From 57d81ddb21464680d51854be890225c8bf321517 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Wed, 21 Apr 2021 19:58:14 -0400
Subject: [PATCH] Kart cmd->latency port

Nev3r was talking about something that would've been drastically improved with this, and it is really simple, so I ported it :)
---
 src/d_ticcmd.h      |  3 +++
 src/g_demo.c        | 12 ++++++++++++
 src/g_game.c        |  8 ++++++++
 src/lua_playerlib.c |  6 ++++++
 4 files changed, 29 insertions(+)

diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h
index 2a5ef09818..04cbeef19f 100644
--- a/src/d_ticcmd.h
+++ b/src/d_ticcmd.h
@@ -21,6 +21,8 @@
 #pragma interface
 #endif
 
+#define MAXPREDICTTICS 12
+
 // Button/action code definitions.
 typedef enum
 {
@@ -63,6 +65,7 @@ typedef struct
 	INT16 angleturn; // <<16 for angle delta - saved as 1 byte into demos
 	INT16 aiming; // vertical aiming, see G_BuildTicCmd
 	UINT16 buttons;
+	UINT8 latency; // Netgames: how many tics ago was this ticcmd generated from this player's end?
 } ATTRPACK ticcmd_t;
 
 #if defined(_MSC_VER)
diff --git a/src/g_demo.c b/src/g_demo.c
index 593fd77234..16477ab48c 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -109,6 +109,7 @@ demoghost *ghosts = NULL;
 #define ZT_ANGLE   0x04
 #define ZT_BUTTONS 0x08
 #define ZT_AIMING  0x10
+#define ZT_LATENCY 0x20
 #define DEMOMARKER 0x80 // demoend
 #define METALDEATH 0x44
 #define METALSNICE 0x69
@@ -181,6 +182,8 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
 		oldcmd.buttons = (oldcmd.buttons & (BT_CAMLEFT|BT_CAMRIGHT)) | (READUINT16(demo_p) & ~(BT_CAMLEFT|BT_CAMRIGHT));
 	if (ziptic & ZT_AIMING)
 		oldcmd.aiming = READINT16(demo_p);
+	if (ziptic & ZT_LATENCY)
+		oldcmd.latency = READUINT8(demo_p);
 
 	G_CopyTiccmd(cmd, &oldcmd, 1);
 	players[playernum].angleturn = cmd->angleturn;
@@ -238,6 +241,13 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
 		ziptic |= ZT_AIMING;
 	}
 
+	if (cmd->latency != oldcmd.latency)
+	{
+		WRITEUINT8(demo_p,cmd->latency);
+		oldcmd.latency = cmd->latency;
+		ziptic |= ZT_LATENCY;
+	}
+
 	*ziptic_p = ziptic;
 
 	// attention here for the ticcmd size!
@@ -679,6 +689,8 @@ void G_GhostTicker(void)
 			g->p += 2;
 		if (ziptic & ZT_AIMING)
 			g->p += 2;
+		if (ziptic & ZT_LATENCY)
+			g->p++;
 
 		// Grab ghost data.
 		ziptic = READUINT8(g->p);
diff --git a/src/g_game.c b/src/g_game.c
index 13423ce77d..c6b3292b02 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1693,6 +1693,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 		cmd->angleturn = origangle + extra;
 		*myangle += extra << 16;
 		*myaiming += (cmd->aiming - origaiming) << 16;
+
+		// Send leveltime when this tic was generated to the server for control lag calculations.
+		// Only do this when in a level. Also do this after the hook, so that it can't overwrite this.
+		cmd->latency = (leveltime & 0xFF); 
 	}
 
 	//Reset away view if a command is given.
@@ -1724,6 +1728,7 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n)
 		dest[i].angleturn = SHORT(src[i].angleturn);
 		dest[i].aiming = (INT16)SHORT(src[i].aiming);
 		dest[i].buttons = (UINT16)SHORT(src[i].buttons);
+		dest[i].latency = src[i].latency;
 	}
 	return dest;
 }
@@ -2306,6 +2311,9 @@ void G_Ticker(boolean run)
 
 			players[i].cmd.angleturn &= ~TICCMD_RECEIVED;
 			players[i].cmd.angleturn |= received;
+
+			// Use the leveltime sent in the player's ticcmd to determine control lag
+			players[i].cmd.latency = min(((leveltime & 0xFF) - players[i].cmd.latency) & 0xFF, MAXPREDICTTICS-1);
 		}
 	}
 
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index 0eb54808fb..d37d46c423 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -795,6 +795,7 @@ static int power_len(lua_State *L)
 }
 
 #define NOFIELD luaL_error(L, LUA_QL("ticcmd_t") " has no field named " LUA_QS, field)
+#define NOSET luaL_error(L, LUA_QL("ticcmd_t") " field " LUA_QS " should not be set directly.", field)
 
 static int ticcmd_get(lua_State *L)
 {
@@ -813,6 +814,8 @@ static int ticcmd_get(lua_State *L)
 		lua_pushinteger(L, cmd->aiming);
 	else if (fastcmp(field,"buttons"))
 		lua_pushinteger(L, cmd->buttons);
+	else if (fastcmp(field,"latency"))
+		lua_pushinteger(L, cmd->latency);
 	else
 		return NOFIELD;
 
@@ -839,6 +842,8 @@ static int ticcmd_set(lua_State *L)
 		cmd->aiming = (INT16)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"buttons"))
 		cmd->buttons = (UINT16)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"latency"))
+		return NOSET;
 	else
 		return NOFIELD;
 
@@ -846,6 +851,7 @@ static int ticcmd_set(lua_State *L)
 }
 
 #undef NOFIELD
+#undef NOSET
 
 int LUA_PlayerLib(lua_State *L)
 {
-- 
GitLab