diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 8cb6d185df3910eccf36038ff40f3f9c5670d5c2..d1fa8f368a650456fad6357b52674be060e0a99b 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -3572,6 +3572,7 @@ static int lib_gAddPlayer(lua_State *L)
 
 	newplayer->jointime = 0;
 	newplayer->quittime = 0;
+	newplayer->lastinputtime = 0;
 
 	// Read the skin argument (defaults to Sonic)
 	if (!lua_isnoneornil(L, 1))
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index 827e5a405fffb7fa1dda25f58aea049dc244bf38..d0858e0441485095f7b49493b199c7970bd86fd6 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -223,6 +223,7 @@ enum player_e
 	player_blocked,
 	player_jointime,
 	player_quittime,
+	player_lastinputtime,
 	player_ping,
 #ifdef HWRENDER
 	player_fovadd,
@@ -371,6 +372,7 @@ static const char *const player_opt[] = {
 	"blocked",
 	"jointime",
 	"quittime",
+	"lastinputtime",
 	"ping",
 #ifdef HWRENDER
 	"fovadd",
@@ -826,6 +828,9 @@ static int player_get(lua_State *L)
 	case player_quittime:
 		lua_pushinteger(L, plr->quittime);
 		break;
+	case player_lastinputtime:
+		lua_pushinteger(L, plr->lastinputtime);
+		break;
 	case player_ping:
 		lua_pushinteger(L, playerpingtable[plr - players]);
 		break;
@@ -1349,6 +1354,9 @@ static int player_set(lua_State *L)
 	case player_quittime:
 		plr->quittime = (tic_t)luaL_checkinteger(L, 3);
 		break;
+	case player_lastinputtime:
+		plr->lastinputtime = (tic_t)luaL_checkinteger(L, 3);
+		break;
 #ifdef HWRENDER
 	case player_fovadd:
 		plr->fovadd = luaL_checkfixed(L, 3);
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 4571930f8bb75d54ae3bd572aa95d4281532e88b..7804b068f7f8d262bd7fab535539af82675bd639 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -227,6 +227,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
 
 	newplayer->jointime = 0;
 	newplayer->quittime = 0;
+	newplayer->lastinputtime = 0;
 
 	READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME);
 
@@ -1282,19 +1283,21 @@ static void IdleUpdate(void)
 
 	for (i = 1; i < MAXPLAYERS; i++)
 	{
-		if (cv_idletime.value && playeringame[i] && playernode[i] != UINT8_MAX && !players[i].quittime && !players[i].spectator && !IsPlayerAdmin(i))
+		if (cv_idletime.value && playeringame[i] && playernode[i] != UINT8_MAX && !players[i].quittime && !players[i].spectator && !players[i].bot && !IsPlayerAdmin(i) && i != serverplayer)
 		{
 			if (players[i].cmd.forwardmove || players[i].cmd.sidemove || players[i].cmd.buttons)
-				players[i].lastinputtime = gametime;
+				players[i].lastinputtime = 0;
+			else
+				players[i].lastinputtime++;
 
-			if (gametime - players[i].lastinputtime > (tic_t)cv_idletime.value * TICRATE * 60)
+			if (players[i].lastinputtime > (tic_t)cv_idletime.value * TICRATE * 60)
 			{
-				players[i].lastinputtime = gametime;
+				players[i].lastinputtime = 0;
 				SendKick(i, KICK_MSG_IDLE | KICK_MSG_KEEP_BODY);
 			}
 		}
 		else
-			players[i].lastinputtime = gametime;
+			players[i].lastinputtime = 0;
 	}
 }