diff --git a/engine/client/cl_cam.c b/engine/client/cl_cam.c
index 5932de2b6..f5465ede2 100644
--- a/engine/client/cl_cam.c
+++ b/engine/client/cl_cam.c
@@ -996,7 +996,7 @@ void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd)
 	int i;
 	player_info_t	*s;
 	int end;
-	extern cvar_t cl_demospeed, cl_splitscreen;
+	extern cvar_t cl_demospeed;
 
 	if (cls.state != ca_active)
 		return;
diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c
index 9daf3eb36..5b01cbc7e 100644
--- a/engine/client/cl_ents.c
+++ b/engine/client/cl_ents.c
@@ -4755,7 +4755,6 @@ void CLQW_ParsePlayerinfo (void)
 		//add a new splitscreen autotrack view if we can
 		if (cl.splitclients < MAX_SPLITS && !cl.players[num].spectator)
 		{
-			extern cvar_t cl_splitscreen;
 			if (cl.splitclients < cl_splitscreen.value+1)
 			{
 				for (i = 0; i < cl.splitclients; i++)
diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c
index b63261b9f..eb2105ad4 100644
--- a/engine/client/cl_input.c
+++ b/engine/client/cl_input.c
@@ -59,7 +59,6 @@ usercmd_t cl_pendingcmd[MAX_SPLITS];
 /*kinda a hack...*/
 unsigned int		con_splitmodifier;
 cvar_t	cl_forceseat = CVARAD("in_forceseat", "0", "in_forcesplitclient", "Overrides the device identifiers to control a specific client from any device. This can be used for debugging mods, where you only have one keyboard/mouse.");
-extern cvar_t cl_splitscreen;
 int CL_TargettedSplit(qboolean nowrap)
 {
 	int mod;
@@ -1723,6 +1722,7 @@ void CL_UpdateSeats(void)
 			}
 			if (!*InfoBuf_ValueForKey(info, "skin"))	//give players the same skin by default, because we can. q2 cares for teams. qw might as well (its not like anyone actually uses them thanks to enemy-skin forcing).
 				InfoBuf_SetKey(info, "skin", InfoBuf_ValueForKey(&cls.userinfo[0], "skin"));
+			InfoBuf_SetKey(info, "chat", "");
 
 #ifdef SVNREVISION
 			if (strcmp(STRINGIFY(SVNREVISION), "-"))
@@ -2093,7 +2093,7 @@ static void CL_SendUserinfoUpdate(void)
 void CL_SendCmd (double frametime, qboolean mainloop)
 {
 	sizebuf_t	buf;
-	qbyte		data[MAX_DATAGRAM];
+	qbyte		data[MAX_DATAGRAM*16];
 	int			i, plnum;
 	usercmd_t	*cmd;
 	float wantfps;
@@ -2581,7 +2581,7 @@ CL_InitInput
 */
 void CL_InitInput (void)
 {
-	static char pcmd[MAX_SPLITS][3][5];
+	static char pcmd[MAX_SPLITS][3][6];
 	unsigned int sp, i;
 #define inputnetworkcvargroup "client networking options"
 	cl.splitclients = 1;
diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c
index 4d658faf9..fa94083a0 100644
--- a/engine/client/cl_parse.c
+++ b/engine/client/cl_parse.c
@@ -1737,6 +1737,9 @@ void CL_RequestNextDownload (void)
 
 		cl.sendprespawn = false;
 
+		if (cl_splitscreen.ival && !(cls.fteprotocolextensions & PEXT_SPLITSCREEN))
+			Con_TPrintf(CON_WARNING "Splitscreen requested but not available on this server.\n");
+
 		if (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING)
 			COM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING);
 
@@ -6777,8 +6780,8 @@ static void CL_ParsePrecache(void)
 			model_t *model;
 			CL_CheckOrEnqueDownloadFile(s, s, 0);
 			model = Mod_ForName(Mod_FixName(s, cl.model_name[1]), (i == 1)?MLV_ERROR:MLV_WARN);
-			if (!model)
-				Con_Printf("svc_precache: Mod_ForName(\"%s\") failed\n", s);
+//			if (!model)
+//				Con_Printf("svc_precache: Mod_ForName(\"%s\") failed\n", s);
 			cl.model_precache[i] = model;
 			Q_strncpyz (cl.model_name[i], s, sizeof(cl.model_name[i]));
 
@@ -6796,8 +6799,8 @@ static void CL_ParsePrecache(void)
 			if (S_HaveOutput())
 				CL_CheckOrEnqueDownloadFile(va("sound/%s", s), NULL, 0);
 			sfx = S_PrecacheSound (s);
-			if (!sfx)
-				Con_Printf("svc_precache: S_PrecacheSound(\"%s\") failed\n", s);
+//			if (!sfx)
+//				Con_Printf("svc_precache: S_PrecacheSound(\"%s\") failed\n", s);
 			cl.sound_precache[i] = sfx;
 			Q_strncpyz (cl.sound_name[i], s, sizeof(cl.sound_name[i]));
 		}
@@ -7098,11 +7101,14 @@ void CLQW_ParseServerMessage (void)
  			CLQW_ParseServerData ();
 			break;
 		case svcfte_splitscreenconfig:
+			j = cl.splitclients;
 			cl.splitclients = MSG_ReadByte();
 			for (i = 0; i < cl.splitclients && i < MAX_SPLITS; i++)
 			{
 				cl.playerview[i].playernum = MSG_ReadByte();
 				cl.playerview[i].viewentity = cl.playerview[i].playernum+1;
+				if (i>=j)	//its new.
+					cl.playerview[i].chatstate = 0;
 			}
 			if (i < cl.splitclients)
 			{
diff --git a/engine/client/input.h b/engine/client/input.h
index 50bd555fd..9f19242e9 100644
--- a/engine/client/input.h
+++ b/engine/client/input.h
@@ -66,6 +66,7 @@ void INS_SetupControllerAudioDevices(qboolean enabled);	//creates audio devices
 
 #define DEVID_UNSET ~0u
 
+extern cvar_t	cl_splitscreen;
 extern cvar_t	cl_nodelta;
 extern cvar_t	cl_c2spps;
 extern cvar_t	cl_c2sImpulseBackup;
diff --git a/engine/client/m_multi.c b/engine/client/m_multi.c
index 8434bb38a..efc87a6ac 100644
--- a/engine/client/m_multi.c
+++ b/engine/client/m_multi.c
@@ -991,7 +991,6 @@ void M_Menu_Teamplay_Items_Status_Location_Misc_f (void)
 void M_Menu_Network_f (void)
 {
 #if MAX_SPLITS > 1
-	extern cvar_t cl_splitscreen;
 	static const char *splitopts[] = {
 		"Disabled",
 		"2 Screens",
diff --git a/engine/client/m_single.c b/engine/client/m_single.c
index 6a08fc2d8..cddda30a3 100644
--- a/engine/client/m_single.c
+++ b/engine/client/m_single.c
@@ -316,7 +316,6 @@ void M_Menu_Load_f (void)
 
 #endif
 
-extern cvar_t cl_splitscreen;
 void M_Menu_SinglePlayer_f (void)
 {
 	emenu_t *menu;
diff --git a/engine/client/menu.c b/engine/client/menu.c
index 0bace0c60..fc03548dc 100644
--- a/engine/client/menu.c
+++ b/engine/client/menu.c
@@ -891,9 +891,6 @@ void M_Menu_Keys_f (void)
 	int y;
 	emenu_t *menu;
 	vfsfile_t *bindslist;
-#if MAX_SPLITS > 1
-	extern cvar_t cl_splitscreen;
-#endif
 
 	menu = M_CreateMenu(0);
 	switch(M_GameType())
diff --git a/engine/common/pmove.c b/engine/common/pmove.c
index 9b5a110d6..c2a0ed050 100644
--- a/engine/common/pmove.c
+++ b/engine/common/pmove.c
@@ -1313,7 +1313,7 @@ static void PM_NudgePosition (void)
 		}
 	}
 
-	if (pmove.safeorigin_known)
+	if (pmove.safeorigin_known && PM_TestPlayerPosition(pmove.safeorigin, false))
 		VectorCopy (pmove.safeorigin, pmove.origin);
 	else
 		VectorCopy (base, pmove.origin);
diff --git a/engine/common/pmove.h b/engine/common/pmove.h
index 95eb729af..a9bf87776 100644
--- a/engine/common/pmove.h
+++ b/engine/common/pmove.h
@@ -58,7 +58,7 @@ typedef struct
 
 	// player state
 	vec3_t		origin;
-	vec3_t		safeorigin;
+	vec3_t		safeorigin;	//valid when safeorigin_known. needed for extrasr4's ladders otherwise they bug out.
 	vec3_t		angles;
 	vec3_t		velocity;
 	vec3_t		basevelocity;
diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c
index 8d0d949ea..77f76b770 100644
--- a/engine/common/pr_bgcmd.c
+++ b/engine/common/pr_bgcmd.c
@@ -18,7 +18,7 @@
 static char *cvargroup_progs = "Progs variables";
 
 cvar_t utf8_enable = CVARD("utf8_enable", "0", "When 1, changes the qc builtins to act upon codepoints instead of bytes. Do not use unless com_parseutf8 is also set.");
-cvar_t sv_gameplayfix_nolinknonsolid = CVARD("sv_gameplayfix_nolinknonsolid", "1", "When 0, setorigin et al will not link the entity into the collision nodes (which is faster, especially if you have a lot of non-solid entities. When 1, allows entities to freely switch between .solid values (except for SOLID_BSP) without relinking. A lot of DP mods assume a value of 1 and will bug out otherwise, while 0 will restore a bugs present in various mods.");
+cvar_t sv_gameplayfix_linknonsolid = CVARD("sv_gameplayfix_nolinknonsolid", "1", "When 0, setorigin et al will not link the entity into the collision nodes (which is faster, especially if you have a lot of non-solid entities. When 1, allows entities to freely switch between .solid values (except for SOLID_BSP) without relinking. A lot of DP mods assume a value of 1 and will bug out otherwise, while 0 will restore a bugs present in various mods.");
 cvar_t sv_gameplayfix_blowupfallenzombies = CVARD("sv_gameplayfix_blowupfallenzombies", "0", "Allow findradius to find non-solid entities. This may break certain mods. It is better for mods to use FL_FINDABLE_NONSOLID instead.");
 cvar_t sv_gameplayfix_findradiusdistancetobox = CVARD("sv_gameplayfix_findradiusdistancetobox", "0", "When 1, findradius checks to the nearest part of the entity instead of only its origin, making it find slightly more entities.");
 cvar_t sv_gameplayfix_droptofloorstartsolid = CVARD("sv_gameplayfix_droptofloorstartsolid", "0", "When droptofloor fails, this causes a second attemp, but with traceline instead.");
@@ -86,7 +86,7 @@ void PF_Common_RegisterCvars(void)
 
 	Cvar_Register (&sv_gameplayfix_blowupfallenzombies, cvargroup_progs);
 	Cvar_Register (&sv_gameplayfix_findradiusdistancetobox, cvargroup_progs);
-	Cvar_Register (&sv_gameplayfix_nolinknonsolid, cvargroup_progs);
+	Cvar_Register (&sv_gameplayfix_linknonsolid, cvargroup_progs);
 	Cvar_Register (&sv_gameplayfix_droptofloorstartsolid, cvargroup_progs);
 	Cvar_Register (&dpcompat_findradiusarealinks, cvargroup_progs);
 #ifdef HAVE_LEGACY
diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c
index 9fff66d8d..dd6efe7e8 100644
--- a/engine/server/sv_init.c
+++ b/engine/server/sv_init.c
@@ -1319,7 +1319,6 @@ MSV_OpenUserDatabase();
 #ifndef SERVERONLY
 		/*force coop 1 if splitscreen and not deathmatch*/
 		{
-		extern cvar_t cl_splitscreen;
 		if (cl_splitscreen.value && !deathmatch.value && !coop.value)
 			Cvar_Set(&coop, "1");
 		}
diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c
index 11a249b61..11a896a25 100644
--- a/engine/server/sv_main.c
+++ b/engine/server/sv_main.c
@@ -450,7 +450,7 @@ void SV_FinalMessage (char *message)
 	MSG_WriteByte (&buf, svc_disconnect);
 
 	for (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++)
-		if (cl->state >= cs_spawned)
+		if (cl->state >= cs_spawned && !cl->controlled)
 			if (ISNQCLIENT(cl) || ISQWCLIENT(cl))
 				Netchan_Transmit (&cl->netchan, buf.cursize
 						, buf.data, 10000);
@@ -621,7 +621,8 @@ void SV_DropClient (client_t *drop)
 #ifndef SERVERONLY
 	if (drop->netchan.remote_address.type == NA_LOOPBACK)
 	{
-		Netchan_Transmit(&drop->netchan, 0, "", SV_RateForClient(drop));
+		if (drop->protocol != SCP_BAD)
+			Netchan_Transmit(&drop->netchan, 0, "", SV_RateForClient(drop));
 #ifdef warningmsg
 #pragma warningmsg("This means that we may not see the reason we kicked ourselves.")
 #endif
@@ -2378,6 +2379,7 @@ client_t *SV_AddSplit(client_t *controller, char *info, int id)
 	cl->playerclass = 0;
 	cl->pendingdeltabits = NULL;
 	cl->pendingcsqcbits = NULL;
+	cl->seat = curclients;
 
 	cl->edict = NULL;
 #ifdef Q2SERVER
diff --git a/engine/server/sv_nchan.c b/engine/server/sv_nchan.c
index 18789c401..cb15153bf 100644
--- a/engine/server/sv_nchan.c
+++ b/engine/server/sv_nchan.c
@@ -104,16 +104,9 @@ sizebuf_t *ClientReliable_StartWrite(client_t *cl, int maxsize)
 		return MVDWrite_Begin(dem_all, 0, maxsize);
 #endif
 
-	if (cl->controller)
+	if (cl->seat)
 	{
-		client_t *sp;
-		int pnum = 0;
-		for (sp = cl->controller; sp; sp = sp->controlled)
-		{
-			if (sp == cl)
-				break;
-			pnum++;
-		}
+		int pnum = cl->seat;
 		cl = cl->controller;
 		ClientReliableWrite_Begin (cl, svcfte_choosesplitclient, 2+maxsize);
 		ClientReliableWrite_Byte (cl, pnum);
diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c
index d5ad439a4..9e413f2e9 100644
--- a/engine/server/sv_send.c
+++ b/engine/server/sv_send.c
@@ -1651,8 +1651,7 @@ void SV_SendFixAngle(client_t *client, sizebuf_t *msg, int fixtype, qboolean rol
 		msg = NULL;	//try to keep them vaugely reliable, where feasable.
 	if (!msg)
 		msg = ClientReliable_StartWrite(client, 10);
-
-	if (client->seat)
+	else if (client->seat)
 	{
 		MSG_WriteByte(msg, svcfte_choosesplitclient);
 		MSG_WriteByte(msg, client->seat);
diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c
index 7c237ccbe..4f697e8e1 100644
--- a/engine/server/sv_user.c
+++ b/engine/server/sv_user.c
@@ -4403,7 +4403,7 @@ static void SV_UpdateSeats(client_t *controller)
 		return;	//wait for the clientinfo stuff instead.
 
 	for (curclients = 0, cl = controller; cl; cl = cl->controlled)
-		curclients++;
+		cl->seat = curclients++;
 
 	ClientReliableWrite_Begin(controller, svcfte_splitscreenconfig, 2+curclients);
 	ClientReliableWrite_Byte(controller, curclients);
@@ -4412,11 +4412,11 @@ static void SV_UpdateSeats(client_t *controller)
 		ClientReliableWrite_Byte(controller, cl - svs.clients);
 	}
 
-	for (curclients = 0, cl = controller; cl; cl = cl->controlled, curclients++)
+	/*for (curclients = 0, cl = controller; cl; cl = cl->controlled, curclients++)
 	{
 		SV_SendFixAngle(cl, NULL, FIXANGLE_FIXED, false);
 		cl->edict->v->fixangle = FIXANGLE_NO;	//no point doing it again
-	}
+	}*/
 }
 
 /*
@@ -5375,7 +5375,8 @@ static void Cmd_AddSeat_f(void)
 
 		if (!num || host_client->joinobservelockeduntil > realtime)
 			return;
-		host_client->joinobservelockeduntil = realtime + 2;
+		if (host_client->netchan.remote_address.type != NA_LOOPBACK)
+			host_client->joinobservelockeduntil = realtime + 2;
 
 		for (count = 1, prev = host_client, cl = host_client->controlled; cl; cl = cl->controlled)
 		{
@@ -8251,6 +8252,9 @@ void SV_ExecuteClientMessage (client_t *cl)
 		case clc_stringcmd:
 			s = MSG_ReadString ();
 			SV_ExecuteUserCommand (s, false);
+#ifdef NETPREPARSE
+			NPP_Flush();	//flush it just in case there was an error and we stopped preparsing. This is only really needed while debugging.
+#endif
 
 			host_client = cl;
 			sv_player = cl->edict;
@@ -8819,7 +8823,9 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
 		case clc_stringcmd:
 			s = MSG_ReadString ();
 			SV_ExecuteUserCommand (s, false);
-
+#ifdef NETPREPARSE
+			NPP_Flush();	//flush it just in case there was an error and we stopped preparsing. This is only really needed while debugging.
+#endif
 			host_client = cl;
 			sv_player = cl->edict;
 			if (cl->state < cs_connected)
diff --git a/engine/server/world.c b/engine/server/world.c
index 6cdf95ffe..dbc840f28 100644
--- a/engine/server/world.c
+++ b/engine/server/world.c
@@ -36,7 +36,7 @@ line of sight checks trace->crosscontent, but bullets don't
 size_t areagridsequence;	//used to avoid poking the same ent twice.
 
 extern cvar_t sv_compatiblehulls;
-extern cvar_t sv_gameplayfix_nolinknonsolid;
+extern cvar_t sv_gameplayfix_linknonsolid;
 
 typedef struct
 {
@@ -655,7 +655,7 @@ void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers)
 		w->worldmodel->funcs.FindTouchedLeafs(w->worldmodel, &ent->pvsinfo, ent->v->absmin, ent->v->absmax);
 	}
 
-	if (ent->v->solid == SOLID_NOT && !sv_gameplayfix_nolinknonsolid.ival)
+	if (ent->v->solid == SOLID_NOT && !sv_gameplayfix_linknonsolid.ival)
 		return;
 
 #ifdef USEAREAGRID