diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c
index d08491417..206582425 100644
--- a/engine/client/cl_parse.c
+++ b/engine/client/cl_parse.c
@@ -5181,6 +5181,7 @@ char *CL_ParseChat(char *text, player_info_t **player, int *msgflags)
 	int check_flood;
 
 	flags = TP_CategorizeMessage (text, &offset, player);
+	*msgflags = flags;
 
 	s = text + offset;
 
@@ -5256,8 +5257,6 @@ char *CL_ParseChat(char *text, player_info_t **player, int *msgflags)
 		}
 	}
 
-	*msgflags = flags;
-
 	return s;
 }
 
diff --git a/engine/client/valid.c b/engine/client/valid.c
index ea3a3057b..90a1e1c08 100644
--- a/engine/client/valid.c
+++ b/engine/client/valid.c
@@ -15,6 +15,10 @@ static f_modified_t *f_modified_list;
 qboolean care_f_modified;
 qboolean f_modified_particles;
 
+void QDECL rulesetcallback(cvar_t *var, char *oldval)
+{
+	Validation_Apply_Ruleset();
+}
 
 cvar_t allow_f_version		= SCVAR("allow_f_version", "1");
 cvar_t allow_f_server		= SCVAR("allow_f_server", "1");
@@ -26,7 +30,7 @@ cvar_t allow_f_fakeshaft	= SCVAR("allow_f_fakeshaft", "1");
 cvar_t allow_f_system		= SCVAR("allow_f_system", "0");
 cvar_t allow_f_cmdline		= SCVAR("allow_f_cmdline", "0");
 cvar_t auth_validateclients	= SCVAR("auth_validateclients", "1");
-cvar_t ruleset			= SCVAR("ruleset", "none");
+cvar_t ruleset			= CVARC("ruleset", "none", rulesetcallback);
 
 
 #define SECURITY_INIT_BAD_CHECKSUM	1
@@ -410,6 +414,7 @@ static ruleset_t rulesets[] =
 	{NULL}
 };
 
+static qboolean ruleset_locked;
 void RulesetLatch(cvar_t *cvar)
 {
 	cvar->flags |= CVAR_RULESETLATCH;
@@ -417,6 +422,7 @@ void RulesetLatch(cvar_t *cvar)
 
 void Validation_DelatchRulesets(void)
 {	//game has come to an end, allow the ruleset to be changed
+	ruleset_locked = false;
 	if (Cvar_ApplyLatches(CVAR_RULESETLATCH))
 		Con_DPrintf("Ruleset deactivated\n");
 }
@@ -428,6 +434,9 @@ qboolean Validation_GetCurrentRulesetName(char *rsnames, int resultbuflen, qbool
 	ruleset_t *rs;
 	int i;
 
+	if (enforcechosenrulesets)
+		ruleset_locked = true;
+
 	rs = rulesets;
 	*rsnames = '\0';
 
@@ -504,7 +513,8 @@ void Validation_AllChecks(void)
 		playername[0] = 0;
 	else
 	{
-		memset(playername, ' ', 15-localpnamelen-1);
+		//pad the left side to compensate for the player name prefix the server will add in the final svc_print
+		memset(playername, ' ', 15-localpnamelen);
 		playername[15-localpnamelen] = 0;
 	}
 
@@ -528,16 +538,26 @@ void Validation_Apply_Ruleset(void)
 	int i;
 	char *rulesetname = ruleset.string;
 
+	if (ruleset_locked)
+	{
+		if (ruleset.modified)
+		{
+			Con_Printf("Cannot change rulesets after the current ruleset has been announced\n");
+			ruleset.modified = false;
+		}
+		return;
+	}
+	ruleset.modified = false;
+
 	if  (!strcmp(rulesetname, "smackdown"))	//officially, smackdown cannot authorise this, thus we do not use that name. however, imported configs tend to piss people off.
 		rulesetname = "strict";
 
-#ifdef warningmsg
-#pragma warningmsg("fixme: the following line should not be needed. ensure this is the case")
-#endif
-	Validation_DelatchRulesets();	//make sure there's no old one
-
 	if (!*rulesetname || !strcmp(rulesetname, "none") || !strcmp(rulesetname, "default"))
+	{
+		if (Cvar_ApplyLatches(CVAR_RULESETLATCH))
+			Con_DPrintf("Ruleset deactivated\n");
 		return;	//no ruleset is set
+	}
 
 	for (rs = rulesets; rs->rulesetname; rs++)
 	{
@@ -547,6 +567,8 @@ void Validation_Apply_Ruleset(void)
 	if (!rs->rulesetname)
 	{
 		Con_Printf("Cannot apply ruleset %s - not recognised\n", rulesetname);
+		if (Cvar_ApplyLatches(CVAR_RULESETLATCH))
+			Con_DPrintf("Ruleset deactivated\n");
 		return;
 	}
 	
diff --git a/engine/common/cvar.c b/engine/common/cvar.c
index 1a98909ae..fc1cdbd39 100644
--- a/engine/common/cvar.c
+++ b/engine/common/cvar.c
@@ -881,8 +881,8 @@ qboolean Cvar_ApplyLatchFlag(cvar_t *var, char *value, int flag)
 #ifdef warningmsg
 #pragma warningmsg("this means the callback will never be called")
 #endif
-		latch = var->string;
-		var->string = NULL;
+		latch = Z_StrDup(var->string);
+//		var->string = NULL;
 	}
 #ifdef warningmsg
 #pragma warningmsg("set or forceset?")
@@ -924,16 +924,15 @@ void Cvar_ForceCheatVars(qboolean semicheats, qboolean absolutecheats)
 		if (!(var->flags & (CVAR_CHEAT|CVAR_SEMICHEAT)))
 			continue;
 
+		if (var->flags & CVAR_RULESETLATCH)
+		{
+			Con_Printf("Hello\n");
+		}
+
 		latch = var->latched_string;
 		var->latched_string = NULL;
 		if (!latch)
-		{
-#ifdef warningmsg
-#pragma warningmsg("this means the callback will never be called")
-#endif
-			latch = var->string;
-			var->string = NULL;
-		}
+			latch = Z_StrDup(var->string);
 
 		if (var->flags & CVAR_CHEAT)
 		{
@@ -944,7 +943,9 @@ void Cvar_ForceCheatVars(qboolean semicheats, qboolean absolutecheats)
 		}
 		if (var->flags & CVAR_SEMICHEAT)
 		{
-			if (!semicheats)
+			if (var->flags & CVAR_RULESETLATCH)
+				;	//this is too problematic. the ruleset should cover it.
+			else if (!semicheats)
 				Cvar_ForceSet(var, "");
 			else
 				Cvar_ForceSet(var, latch);
diff --git a/engine/common/protocol.h b/engine/common/protocol.h
index 6f2b6a219..75e161749 100644
--- a/engine/common/protocol.h
+++ b/engine/common/protocol.h
@@ -283,6 +283,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define	svcfte_setangledelta		85	// [angle3] add this to the current viewangles
 #define svcfte_updateentities		86
 #define svcfte_brushedit			87	// networked brush editing, paired with clcfte_brushedit.
+#define	svcfte_updateseats			88	// byte count, byte playernum[count]
 
 
 //fitz svcs
diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c
index 5c38aab4e..b0d1c8c7a 100644
--- a/engine/gl/gl_backend.c
+++ b/engine/gl/gl_backend.c
@@ -656,12 +656,14 @@ static void BE_ApplyAttributes(unsigned int bitstochange, unsigned int bitstoend
 				}
 				break;
 			case VATTR_COLOUR:
-				if (shaderstate.sourcevbo->colours[0].gl.addr)
+				if (!shaderstate.sourcevbo->colours[0].gl.addr)
 				{
-					GL_SelectVBO(shaderstate.sourcevbo->colours[0].gl.vbo);
-					qglVertexAttribPointer(VATTR_COLOUR, 4, shaderstate.colourarraytype, ((shaderstate.colourarraytype==GL_FLOAT)?GL_FALSE:GL_TRUE), 0, shaderstate.sourcevbo->colours[0].gl.addr);
-					break;
+					shaderstate.sha_attr &= ~(1u<<i);
+					qglDisableVertexAttribArray(i);
+					continue;
 				}
+				GL_SelectVBO(shaderstate.sourcevbo->colours[0].gl.vbo);
+				qglVertexAttribPointer(VATTR_COLOUR, 4, shaderstate.colourarraytype, ((shaderstate.colourarraytype==GL_FLOAT)?GL_FALSE:GL_TRUE), 0, shaderstate.sourcevbo->colours[0].gl.addr);
 				break;
 #if MAXRLIGHTMAPS > 1
 			case VATTR_COLOUR2:
@@ -738,22 +740,22 @@ static void BE_ApplyAttributes(unsigned int bitstochange, unsigned int bitstoend
 				qglVertexAttribPointer(VATTR_TNORMALS, 3, GL_FLOAT, GL_FALSE, 0, shaderstate.sourcevbo->tvector.gl.addr);
 				break;
 			case VATTR_BONENUMS:
-				/*if (!shaderstate.sourcevbo->bonenums.gl.vbo && !shaderstate.sourcevbo->bonenums.gl.addr)
+				if (!shaderstate.sourcevbo->bonenums.gl.vbo && !shaderstate.sourcevbo->bonenums.gl.addr)
 				{
 					shaderstate.sha_attr &= ~(1u<<i);
 					qglDisableVertexAttribArray(i);
 					continue;
-				}*/
+				}
 				GL_SelectVBO(shaderstate.sourcevbo->bonenums.gl.vbo);
 				qglVertexAttribPointer(VATTR_BONENUMS, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, shaderstate.sourcevbo->bonenums.gl.addr);
 				break;
 			case VATTR_BONEWEIGHTS:
-				/*if (!shaderstate.sourcevbo->boneweights.gl.vbo && !shaderstate.sourcevbo->boneweights.gl.addr)
+				if (!shaderstate.sourcevbo->boneweights.gl.vbo && !shaderstate.sourcevbo->boneweights.gl.addr)
 				{
 					shaderstate.sha_attr &= ~(1u<<i);
 					qglDisableVertexAttribArray(i);
 					continue;
-				}*/
+				}
 				GL_SelectVBO(shaderstate.sourcevbo->boneweights.gl.vbo);
 				qglVertexAttribPointer(VATTR_BONEWEIGHTS, 4, GL_FLOAT, GL_FALSE, 0, shaderstate.sourcevbo->boneweights.gl.addr);
 				break;
diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c
index 96ec3ce33..519a5e975 100644
--- a/engine/gl/gl_shader.c
+++ b/engine/gl/gl_shader.c
@@ -4693,6 +4693,9 @@ void Shader_DefaultBSPLM(const char *shortname, shader_t *s, const void *args)
 			);
 
 	Shader_DefaultScript(shortname, s, builtin);
+
+	if (r_lightprepass.ival)
+		s->flags |= SHADER_HASNORMALMAP;
 }
 
 void Shader_DefaultCinematic(const char *shortname, shader_t *s, const void *args)
@@ -5656,21 +5659,29 @@ void Shader_DoReload(void)
 	int oldsort;
 	qboolean resort = false;
 
-	if (shader_rescan_needed && ruleset_allow_shaders.ival)
+	if (shader_rescan_needed)
 	{
 		Shader_FlushCache();
 
-		COM_EnumerateFiles("materials/*.mtr", Shader_InitCallback, NULL);
-		COM_EnumerateFiles("shaders/*.shader", Shader_InitCallback, NULL);
-		COM_EnumerateFiles("scripts/*.shader", Shader_InitCallback, NULL);
-		COM_EnumerateFiles("scripts/*.rscript", Shader_InitCallback, NULL);
+		if (ruleset_allow_shaders.ival)
+		{
+			COM_EnumerateFiles("materials/*.mtr", Shader_InitCallback, NULL);
+			COM_EnumerateFiles("shaders/*.shader", Shader_InitCallback, NULL);
+			COM_EnumerateFiles("scripts/*.shader", Shader_InitCallback, NULL);
+			COM_EnumerateFiles("scripts/*.rscript", Shader_InitCallback, NULL);
+		}
 
 		shader_reload_needed = true;
 		shader_rescan_needed = false;
-	}
 
-	if (!shader_reload_needed)
-		return;
+		Con_DPrintf("Rescanning shaders\n");
+	}
+	else
+	{
+		if (!shader_reload_needed)
+			return;
+		Con_DPrintf("Reloading shaders\n");
+	}
 	shader_reload_needed = false;
 	Font_InvalidateColour();
 	Shader_ReloadGenerics();
diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c
index cc43d56b3..13f189bfd 100644
--- a/engine/server/pr_cmds.c
+++ b/engine/server/pr_cmds.c
@@ -9258,7 +9258,7 @@ BuiltinList_t BuiltinList[] = {				//nq	qw		h2		ebfs
 	{"walkmove",		PF_walkmove,		32,		32,		32,		0,	D("float(float yaw, float dist, optional float settraceglobals)", "Attempt to walk the entity at a given angle for a given distance.\nif settraceglobals is set, the trace_* globals will be set, showing the results of the movement.\nThis function will trigger touch events.")},
 //	{"qtest_flymove",	NULL,	33},	// float(vector dir) flymove = #33;
 //qbism super8's 'private'sound #33
-	{"droptofloor",		PF_droptofloor,		34,		34,		34,		0,	D("float()", "Instantly moves the entity downwards until it hits the ground. If the entity would need to drop more than 'pr_droptofloorunits' quake units, its position will be considered invalid and the builtin will abort.")},
+	{"droptofloor",		PF_droptofloor,		34,		34,		34,		0,	D("float()", "Instantly moves the entity downwards until it hits the ground. If the entity is in solid or would need to drop more than 'pr_droptofloorunits' quake units, its position will be considered invalid and the builtin will abort, returning FALSE, otherwise TRUE.")},
 	{"lightstyle",		PF_lightstyle,		35,		35,		35,		0,	D("void(float lightstyle, string stylestring, optional vector rgb)", "Specifies an auto-animating string that specifies the light intensity for entities using that lightstyle.\na is off, z is fully lit. Should be lower case only.\nrgb will recolour all lights using that lightstyle.\n")},
 	{"rint",			PF_rint,			36,		36,		36,		0,	D("float(float)", "Rounds the given float up or down to the closest integeral value. X.5 rounds away from 0")},
 	{"floor",			PF_floor,			37,		37,		37,		0,	D("float(float)", "Rounds the given float downwards, even when negative.")},
diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c
index 90a68e70f..12cd38429 100644
--- a/engine/server/sv_user.c
+++ b/engine/server/sv_user.c
@@ -5609,21 +5609,27 @@ void SV_ExecuteUserCommand (char *s, qboolean fromQC)
 
 	Cmd_ExecLevel=1;
 
-	if (!fromQC && host_client->controlled && atoi(Cmd_Argv(0))>0)	//now see if it's meant to be from a slave client
-	{
-		int pnum = atoi(Cmd_Argv(0));
-		client_t *sp;
-		for (sp = host_client; sp; sp = sp->controlled)
+	if (!fromQC && host_client->controlled)	//now see if it's meant to be from a slave client
+	{	//'cmd 2 say hi' should 
+		char *a=Cmd_Argv(0), *e;
+		int pnum = strtoul(a, &e, 10);
+		
+		if (e == a+1 && pnum >= 1 && pnum <= MAX_SPLITS)
 		{
-			if (!--pnum)
+			client_t *sp;
+			for (sp = host_client; sp; sp = sp->controlled)
 			{
-				host_client = sp;
-				break;
+				if (!--pnum)
+				{
+					host_client = sp;
+					break;
+				}
 			}
+
+			sv_player = host_client->edict;
+			s = Cmd_Args();
+			Cmd_ShiftArgs(1, false);
 		}
-		sv_player = host_client->edict;
-		s = Cmd_Args();
-		Cmd_ShiftArgs(1, false);
 	}
 
 #ifdef Q2SERVER