diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 92196219d04bc350227974eba9b7f5cd78c67f7a..5668ab6b3ed70c2c8ae4cb77b8e68e5843b2fef1 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -90,6 +90,7 @@ SINT8 nodetoplayer[MAXNETNODES];
 SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen)
 UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen
 boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
+tic_t servermaxping = 800; // server's max ping. Defaults to 800
 static tic_t nettics[MAXNETNODES]; // what tic the client have received
 static tic_t supposedtics[MAXNETNODES]; // nettics prevision for smaller packet
 static UINT8 nodewaiting[MAXNETNODES];
@@ -4208,10 +4209,12 @@ static void HandlePacketFromPlayer(SINT8 node)
 			//Update client ping table from the server.
 			if (client)
 			{
-				INT32 i;
+				UINT8 i;
 				for (i = 0; i < MAXPLAYERS; i++)
 					if (playeringame[i])
 						playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i];
+
+				servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS];
 			}
 
 			break;
@@ -4733,6 +4736,14 @@ void TryRunTics(tic_t realtics)
 	}
 }
 
+/*
+Ping Update except better:
+We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out.
+If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave.
+*/
+
+static INT32 pingtimeout[MAXPLAYERS];
+
 static inline void PingUpdate(void)
 {
 	INT32 i;
@@ -4753,6 +4764,8 @@ static inline void PingUpdate(void)
 					laggers[i] = true;
 				numlaggers++;
 			}
+			else
+				pingtimeout[i] = 0;
 		}
 
 		//kick lagging players... unless everyone but the server's ping sucks.
@@ -4763,12 +4776,27 @@ static inline void PingUpdate(void)
 			{
 				if (playeringame[i] && laggers[i])
 				{
-					char buf[2];
+					pingtimeout[i]++;
+					if (pingtimeout[i] > cv_pingtimeout.value)
+// ok your net has been bad for too long, you deserve to die.
+					{
+						char buf[2];
 
-					buf[0] = (char)i;
-					buf[1] = KICK_MSG_PING_HIGH;
-					SendNetXCmd(XD_KICK, &buf, 2);
+						pingtimeout[i] = 0;
+
+						buf[0] = (char)i;
+						buf[1] = KICK_MSG_PING_HIGH;
+						SendNetXCmd(XD_KICK, &buf, 2);
+					}
 				}
+				/*
+					you aren't lagging,
+					but you aren't free yet.
+					In case you'll keep spiking,
+					we just make the timer go back down. (Very unstable net must still get kicked).
+				*/
+				else
+					pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1);
 			}
 		}
 	}
@@ -4783,10 +4811,13 @@ static inline void PingUpdate(void)
 		realpingtable[i] = 0; //Reset each as we go.
 	}
 
+	// send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked.
+	netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value;
+
 	//send out our ping packets
 	for (i = 0; i < MAXNETNODES; i++)
 		if (nodeingame[i])
-			HSendPacket(i, true, 0, sizeof(INT32) * MAXPLAYERS);
+			HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1));
 
 	pingmeasurecount = 1; //Reset count
 }
@@ -4816,7 +4847,7 @@ void NetUpdate(void)
 
 	if (server)
 	{
-		if (netgame && !(gametime % 255))
+		if (netgame && !(gametime % 35)) // update once per second.
 			PingUpdate();
 		// update node latency values so we can take an average later.
 		for (i = 0; i < MAXPLAYERS; i++)
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index d7c210895ab5c2d0a376d384d31d586898925e6e..b85bbfab6d412e1edf19064c442c65c584c0f632 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -423,9 +423,9 @@ typedef struct
 		serverrefuse_pak serverrefuse;      //       65025 bytes (somehow I feel like those values are garbage...)
 		askinfo_pak askinfo;                //          61 bytes
 		msaskinfo_pak msaskinfo;            //          22 bytes
-		plrinfo playerinfo[MAXPLAYERS];     //        1152 bytes (I'd say 36~38)
-		plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes (welp they ARE)
-		UINT32 pingtable[MAXPLAYERS];       //         128 bytes
+		plrinfo playerinfo[MAXPLAYERS];     //         576 bytes(?)
+		plrconfig playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?)
+		UINT32 pingtable[MAXPLAYERS+1];     //          68 bytes
 	} u; // This is needed to pack diff packet types data together
 } ATTRPACK doomdata_t;
 
@@ -487,6 +487,7 @@ extern tic_t jointimeout;
 extern UINT16 pingmeasurecount;
 extern UINT32 realpingtable[MAXPLAYERS];
 extern UINT32 playerpingtable[MAXPLAYERS];
+extern tic_t servermaxping;
 
 extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend, cv_noticedownload, cv_downloadspeed;
 
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index d750c81f2db596c8ef98b55fb23e649e27183a3b..f52008aa91d6e3423d33ae36c9bb3b7e8538d422 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -341,6 +341,14 @@ consvar_t cv_nettimeout = {"nettimeout", "350", CV_CALL|CV_SAVE, nettimeout_cons
 static CV_PossibleValue_t jointimeout_cons_t[] = {{5*TICRATE, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
 consvar_t cv_jointimeout = {"jointimeout", "350", CV_CALL|CV_SAVE, jointimeout_cons_t, JoinTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_maxping = {"maxping", "0", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+static CV_PossibleValue_t pingtimeout_cons_t[] = {{8, "MIN"}, {120, "MAX"}, {0, NULL}};
+consvar_t cv_pingtimeout = {"pingtimeout", "10", CV_SAVE, pingtimeout_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+// show your ping on the HUD next to framerate. Defaults to warning only (shows up if your ping is > maxping)
+static CV_PossibleValue_t showping_cons_t[] = {{0, "Off"}, {1, "Always"}, {2, "Warning"}, {0, NULL}};
+consvar_t cv_showping = {"showping", "Warning", CV_SAVE, showping_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
 // Intermission time Tails 04-19-2002
 static CV_PossibleValue_t inttime_cons_t[] = {{0, "MIN"}, {3600, "MAX"}, {0, NULL}};
 consvar_t cv_inttime = {"inttime", "10", CV_NETVAR, inttime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -577,6 +585,8 @@ void D_RegisterServerCommands(void)
 	CV_RegisterVar(&cv_skipmapcheck);
 	CV_RegisterVar(&cv_sleep);
 	CV_RegisterVar(&cv_maxping);
+	CV_RegisterVar(&cv_pingtimeout);
+	CV_RegisterVar(&cv_showping);
 
 #ifdef SEENAMES
 	 CV_RegisterVar(&cv_allowseenames);
diff --git a/src/d_netcmd.h b/src/d_netcmd.h
index 4821aff46951fa22388e8e4076721116ef4b0a11..b88d8c7bbeedaf4246f68c680b0863d4d6a4a773 100644
--- a/src/d_netcmd.h
+++ b/src/d_netcmd.h
@@ -106,6 +106,9 @@ extern consvar_t cv_ringslinger, cv_soundtest;
 extern consvar_t cv_specialrings, cv_powerstones, cv_matchboxes, cv_competitionboxes;
 
 extern consvar_t cv_maxping;
+extern consvar_t cv_pingtimeout;
+extern consvar_t cv_showping;
+
 
 extern consvar_t cv_skipmapcheck;
 
diff --git a/src/djgppdos/i_video.c b/src/djgppdos/i_video.c
index 6a7641174491d47793895c24677c6221c1f9e73b..56570b7bf8daf871fca8db3a03909823c33ad605 100644
--- a/src/djgppdos/i_video.c
+++ b/src/djgppdos/i_video.c
@@ -98,6 +98,9 @@ void I_FinishUpdate (void)
 	if (cv_ticrate.value)
 		SCR_DisplayTicRate();
 
+	if (cv_showping.value && netgame && consoleplayer != serverplayer)
+		SCR_DisplayLocalPing();
+
 	//blast it to the screen
 	// this code sucks
 	//memcpy(dascreen,screens[0],screenwidth*screenheight);
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 90f4ceedfde7a4a65856ff1d7467c354d93d65ce..d022abd58ce5660d51ba43ef791c99487c2b6e83 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -2322,7 +2322,7 @@ void HU_Erase(void)
 //
 // HU_drawPing
 //
-void HU_drawPing(INT32 x, INT32 y, INT32 ping, boolean notext)
+void HU_drawPing(INT32 x, INT32 y, UINT32 ping, boolean notext)
 {
 	UINT8 numbars = 1; // how many ping bars do we draw?
 	UINT8 barcolor = 35; // color we use for the bars (green, yellow or red)
diff --git a/src/hu_stuff.h b/src/hu_stuff.h
index 55b61d4b7a995d6ffb4f9fd85958ed0d25d2ba66..0860abb48c1c99ef3ccc9d5befa3ab9aef340c85 100644
--- a/src/hu_stuff.h
+++ b/src/hu_stuff.h
@@ -113,7 +113,7 @@ void HU_Drawer(void);
 char HU_dequeueChatChar(void);
 void HU_Erase(void);
 void HU_clearChatChars(void);
-void HU_drawPing(INT32 x, INT32 y, INT32 ping, boolean notext);	// Lat': Ping drawer for scoreboard.
+void HU_drawPing(INT32 x, INT32 y, UINT32 ping, boolean notext);	// Lat': Ping drawer for scoreboard.
 void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer);
 void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer);
 void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer);
diff --git a/src/m_menu.c b/src/m_menu.c
index 47ca7ccd4c4dd9dc93e82acc8a20637616687f9d..13f21cd95414a06723d989c325459f939fbc5558 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -1224,32 +1224,33 @@ static menuitem_t OP_VideoOptionsMenu[] =
 	                      NULL, "HUD Transparency",          &cv_translucenthud,   66},
 	{IT_STRING | IT_CVAR, NULL, "Score/Time/Rings",          &cv_timetic,          71},
 	{IT_STRING | IT_CVAR, NULL, "Show Powerups",             &cv_powerupdisplay,   76},
+	{IT_STRING | IT_CVAR, NULL, "Local ping display",		&cv_showping,			81}, // shows ping next to framerate if we want to.
 #ifdef SEENAMES
-	{IT_STRING | IT_CVAR, NULL, "Show player names",         &cv_seenames,         81},
+	{IT_STRING | IT_CVAR, NULL, "Show player names",         &cv_seenames,         86},
 #endif
 
-	{IT_HEADER, NULL, "Console", NULL, 90},
-	{IT_STRING | IT_CVAR, NULL, "Background color",          &cons_backcolor,      96},
-	{IT_STRING | IT_CVAR, NULL, "Text Size",                 &cv_constextsize,    101},
-
-	{IT_HEADER, NULL, "Chat", NULL, 110},
-	{IT_STRING | IT_CVAR, NULL, "Chat Mode",            		 	 &cv_consolechat,  116},
-	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Width",    &cv_chatwidth,     121},
-	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Height",   &cv_chatheight,    126},
-	{IT_STRING | IT_CVAR, NULL, "Message Fadeout Time",              &cv_chattime,    131},
-	{IT_STRING | IT_CVAR, NULL, "Chat Notifications",           	 &cv_chatnotifications,  136},
-	{IT_STRING | IT_CVAR, NULL, "Spam Protection",           		 &cv_chatspamprotection,  141},
-	{IT_STRING | IT_CVAR, NULL, "Chat background tint",           	 &cv_chatbacktint,  146},
-
-	{IT_HEADER, NULL, "Level", NULL, 155},
-	{IT_STRING | IT_CVAR, NULL, "Draw Distance",             &cv_drawdist,        161},
-	{IT_STRING | IT_CVAR, NULL, "Weather Draw Dist.",        &cv_drawdist_precip, 166},
-	{IT_STRING | IT_CVAR, NULL, "NiGHTS Hoop Draw Dist.",    &cv_drawdist_nights, 171},
-
-	{IT_HEADER, NULL, "Diagnostic", NULL, 180},
-	{IT_STRING | IT_CVAR, NULL, "Show FPS",                  &cv_ticrate,         186},
-	{IT_STRING | IT_CVAR, NULL, "Clear Before Redraw",       &cv_homremoval,      191},
-	{IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"",       &cv_showfocuslost,   196},
+	{IT_HEADER, NULL, "Console", NULL, 95},
+	{IT_STRING | IT_CVAR, NULL, "Background color",          &cons_backcolor,      101},
+	{IT_STRING | IT_CVAR, NULL, "Text Size",                 &cv_constextsize,    106},
+
+	{IT_HEADER, NULL, "Chat", NULL, 115},
+	{IT_STRING | IT_CVAR, NULL, "Chat Mode",            		 	 &cv_consolechat,  121},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Width",    &cv_chatwidth,     126},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Height",   &cv_chatheight,    131},
+	{IT_STRING | IT_CVAR, NULL, "Message Fadeout Time",              &cv_chattime,    136},
+	{IT_STRING | IT_CVAR, NULL, "Chat Notifications",           	 &cv_chatnotifications,  141},
+	{IT_STRING | IT_CVAR, NULL, "Spam Protection",           		 &cv_chatspamprotection,  146},
+	{IT_STRING | IT_CVAR, NULL, "Chat background tint",           	 &cv_chatbacktint,  151},
+
+	{IT_HEADER, NULL, "Level", NULL, 160},
+	{IT_STRING | IT_CVAR, NULL, "Draw Distance",             &cv_drawdist,        166},
+	{IT_STRING | IT_CVAR, NULL, "Weather Draw Dist.",        &cv_drawdist_precip, 171},
+	{IT_STRING | IT_CVAR, NULL, "NiGHTS Hoop Draw Dist.",    &cv_drawdist_nights, 176},
+
+	{IT_HEADER, NULL, "Diagnostic", NULL, 184},
+	{IT_STRING | IT_CVAR, NULL, "Show FPS",                  &cv_ticrate,         190},
+	{IT_STRING | IT_CVAR, NULL, "Clear Before Redraw",       &cv_homremoval,      195},
+	{IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"",       &cv_showfocuslost,   200},
 };
 
 static menuitem_t OP_VideoModeMenu[] =
diff --git a/src/screen.c b/src/screen.c
index f1d91de66b2017ea8347e494293b5ec0848f2466..65953230355dd6969cae2d6e103b633e6d04013f 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -428,6 +428,17 @@ void SCR_DisplayTicRate(void)
 	lasttic = ontic;
 }
 
+void SCR_DisplayLocalPing(void)
+{
+	UINT32 ping = playerpingtable[consoleplayer];	// consoleplayer's ping is everyone's ping in a splitnetgame :P
+	if (cv_showping.value == 1 || (cv_showping.value == 2 && servermaxping && ping > servermaxping))	// only show 2 (warning) if our ping is at a bad level
+	{
+		INT32 dispy = cv_ticrate.value ? 181 : 190;
+		HU_drawPing(307, dispy, ping, true);
+	}
+}
+
+
 void SCR_ClosedCaptions(void)
 {
 	UINT8 i;
diff --git a/src/screen.h b/src/screen.h
index 79f21e8e470bd4832d0c20c5d0b04c586b0635b0..6f03612c46b86b08bec2b6c68b131a9e16371b9b 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -167,5 +167,6 @@ FUNCMATH boolean SCR_IsAspectCorrect(INT32 width, INT32 height);
 // move out to main code for consistency
 void SCR_DisplayTicRate(void);
 void SCR_ClosedCaptions(void);
+void SCR_DisplayLocalPing(void);
 #undef DNWH
 #endif //__SCREEN_H__
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index fb0f4b2ba3eaea01e6881a77f2717672860cb21f..f22c79ecaabbd420bca602fe5ad9f594058cf292 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -1180,6 +1180,9 @@ void I_FinishUpdate(void)
 	{
 		if (cv_ticrate.value)
 			SCR_DisplayTicRate();
+
+		if (cv_showping.value && netgame && consoleplayer != serverplayer)
+			SCR_DisplayLocalPing();
 	}
 
 	if (rendermode == render_soft && screens[0])
diff --git a/src/win32/win_vid.c b/src/win32/win_vid.c
index 11c7a6744b4132baff1a48a4a2b72ec8d43736d8..39a60cd93b936ba3e524d18c4614a8834128a8a4 100644
--- a/src/win32/win_vid.c
+++ b/src/win32/win_vid.c
@@ -371,6 +371,9 @@ void I_FinishUpdate(void)
 	if (cv_ticrate.value)
 		SCR_DisplayTicRate();
 
+	if (cv_showping.value && netgame && consoleplayer != serverplayer)
+		SCR_DisplayLocalPing();
+
 	//
 	if (bDIBMode)
 	{