diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 7804b068f7f8d262bd7fab535539af82675bd639..de22d601b8b88a27eb955d6a09b97efcac1d0822 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -116,6 +116,8 @@ consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_c
 consvar_t cv_idletime = CVAR_INIT ("idletime", "0", CV_SAVE, CV_Unsigned, NULL);
 consvar_t cv_dedicatedidletime = CVAR_INIT ("dedicatedidletime", "10", CV_SAVE, CV_Unsigned, NULL);
 
+static INT32 D_NumNodes(boolean skiphost);
+
 void ResetNode(INT32 node)
 {
 	memset(&netnodes[node], 0, sizeof(*netnodes));
@@ -1275,6 +1277,7 @@ static void UpdatePingTable(void)
 	}
 }
 
+// Handle idle and disconnected player timers
 static void IdleUpdate(void)
 {
 	INT32 i;
@@ -1297,7 +1300,26 @@ static void IdleUpdate(void)
 			}
 		}
 		else
+		{
 			players[i].lastinputtime = 0;
+
+			if (players[i].quittime && playeringame[i])
+			{
+				players[i].quittime++;
+
+				if (players[i].quittime == 30 * TICRATE && G_TagGametype())
+					P_CheckSurvivors();
+
+				if (server && players[i].quittime >= (tic_t)FixedMul(cv_rejointimeout.value, 60 * TICRATE)
+				&& !(players[i].quittime % TICRATE))
+				{
+					if (D_NumNodes(true) > 0)
+						SendKick(i, KICK_MSG_PLAYER_QUIT);
+					else // If the server is empty, don't send a NetXCmd - that would wake an idling dedicated server
+						CL_RemovePlayer(i, KICK_MSG_PLAYER_QUIT);
+				}
+			}
+		}
 	}
 }
 
@@ -1631,6 +1653,17 @@ INT32 D_NumBots(void)
 	return num;
 }
 
+// Returns the number of currently-connected nodes in a netgame
+// Not necessarily equivalent to D_NumPlayers() minus D_NumBots()
+static INT32 D_NumNodes(boolean skiphost)
+{
+	INT32 num = 0, ix;
+	for (ix = skiphost ? 1 : 0; ix < MAXNETNODES; ix++)
+		if (netnodes[ix].ingame)
+			num++;
+	return num;
+}
+
 
 //
 // Consistancy
diff --git a/src/p_tick.c b/src/p_tick.c
index 1bc7b78bf1a39499576794b8e80d7181f58cd43c..3934890f19a585a8c1c657ea753801c21688dd35 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -690,25 +690,11 @@ void P_Ticker(boolean run)
 {
 	INT32 i;
 
-	// Increment jointime and quittime even if paused
+	// Increment jointime even if paused
 	for (i = 0; i < MAXPLAYERS; i++)
 		if (playeringame[i])
-		{
 			players[i].jointime++;
 
-			if (players[i].quittime)
-			{
-				players[i].quittime++;
-
-				if (players[i].quittime == 30 * TICRATE && G_TagGametype())
-					P_CheckSurvivors();
-
-				if (server && players[i].quittime >= (tic_t)FixedMul(cv_rejointimeout.value, 60 * TICRATE)
-				&& !(players[i].quittime % TICRATE))
-					SendKick(i, KICK_MSG_PLAYER_QUIT);
-			}
-		}
-
 	if (objectplacing)
 	{
 		if (OP_FreezeObjectplace())