From 1d6c5f9f4c2e0f79b6af40c5801beffb69a14e85 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 7 Aug 2022 14:31:20 +0300 Subject: [PATCH 01/58] Allow more than 4 players in coop. Fixes #249. --- src/server/init.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/server/init.c b/src/server/init.c index 27c98464a..5e7dc1daf 100644 --- a/src/server/init.c +++ b/src/server/init.c @@ -394,17 +394,15 @@ void SV_InitGame(unsigned mvd_spawn) // init clients if (Cvar_VariableInteger("deathmatch")) { - if (sv_maxclients->integer <= 1) { - Cvar_SetInteger(sv_maxclients, 8, FROM_CODE); - } else if (sv_maxclients->integer > CLIENTNUM_RESERVED) { - Cvar_SetInteger(sv_maxclients, CLIENTNUM_RESERVED, FROM_CODE); - } + if (sv_maxclients->integer <= 1) + Cvar_Set("maxclients", "8"); } else if (Cvar_VariableInteger("coop")) { - if (sv_maxclients->integer <= 1 || sv_maxclients->integer > 4) + if (sv_maxclients->integer <= 1) Cvar_Set("maxclients", "4"); } else { // non-deathmatch, non-coop is one player - Cvar_FullSet("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH, FROM_CODE); + Cvar_Set("maxclients", "1"); } + Cvar_ClampInteger(sv_maxclients, 1, CLIENTNUM_RESERVED); // enable networking if (sv_maxclients->integer > 1) { From 82096ea79e3330b69a2207b920a300e98ce284a8 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 20 Aug 2022 17:27:51 +0300 Subject: [PATCH 02/58] Drop too short packets. --- src/client/main.c | 4 ++++ src/server/main.c | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/client/main.c b/src/client/main.c index c54c23aec..da4a86570 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -1457,6 +1457,10 @@ CL_PacketEvent */ static void CL_PacketEvent(void) { + if (msg_read.cursize < 4) { + return; + } + // // remote command packet // diff --git a/src/server/main.c b/src/server/main.c index 6448f4861..ea13a43ab 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -1492,6 +1492,10 @@ static void SV_PacketEvent(void) netchan_t *netchan; int qport; + if (msg_read.cursize < 4) { + return; + } + // check for connectionless packet (0xffffffff) first // connectionless packets are processed even if the server is down if (*(int *)msg_read.data == -1) { @@ -1513,11 +1517,17 @@ static void SV_PacketEvent(void) // read the qport out of the message so we can fix up // stupid address translating routers if (client->protocol == PROTOCOL_VERSION_DEFAULT) { + if (msg_read.cursize < PACKET_HEADER) { + continue; + } qport = RL16(&msg_read.data[8]); if (netchan->qport != qport) { continue; } } else if (netchan->qport) { + if (msg_read.cursize < PACKET_HEADER - 1) { + continue; + } qport = msg_read.data[8]; if (netchan->qport != qport) { continue; From 2a31352105a5465b3435bf5827a90d306906cb62 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 26 Aug 2022 19:47:25 +0300 Subject: [PATCH 03/58] Fix CL_Ups_m() if PMF_NO_PREDICTION is set. --- src/client/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/main.c b/src/client/main.c index da4a86570..053625829 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -2132,8 +2132,8 @@ static size_t CL_Ups_m(char *buffer, size_t size) { vec3_t vel; - if (!cls.demo.playback && cl.frame.clientNum == cl.clientNum && - cl_predict->integer) { + if (!cls.demo.playback && cl_predict->integer && + !(cl.frame.ps.pmove.pm_flags & PMF_NO_PREDICTION)) { VectorCopy(cl.predicted_velocity, vel); } else { VectorScale(cl.frame.ps.pmove.velocity, 0.125f, vel); From 36bd0f77457eed8f65cb0eb7011c1f8ff5e16386 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 27 Aug 2022 10:56:22 +0300 Subject: [PATCH 04/58] Don't set listener_entnum to -1 if cl.clientNum == -1. This will break sound from player's own entity when playing a MVD without dummy MVD observer spawned and chasing a player. --- src/client/sound/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/sound/main.c b/src/client/sound/main.c index 0951d796b..28484cb77 100644 --- a/src/client/sound/main.c +++ b/src/client/sound/main.c @@ -843,7 +843,7 @@ void S_Update(void) // set listener entity number // other parameters should be already set up by CL_CalcViewValues - if (cls.state != ca_active || cl.clientNum == -1) { + if (cls.state != ca_active) { listener_entnum = -1; } else { listener_entnum = cl.frame.clientNum + 1; From 75376eec303cb2465da11ed47e5c6c796b0f8a8d Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 29 Aug 2022 22:03:27 +0300 Subject: [PATCH 05/58] Drop support for anicent Q2PRO protocol versions. Set minimum Q2PRO protocol version to 1015, which was added more than 13 years ago in r335. --- inc/common/msg.h | 4 ++-- inc/common/protocol.h | 6 +----- src/client/input.c | 2 +- src/client/parse.c | 15 +++++---------- src/common/msg.c | 35 ++++++++++------------------------- src/server/entities.c | 18 +++++------------- src/server/main.c | 9 ++------- src/server/user.c | 6 ++---- 8 files changed, 28 insertions(+), 67 deletions(-) diff --git a/inc/common/msg.h b/inc/common/msg.h index 421f9b00a..448fdeb6a 100644 --- a/inc/common/msg.h +++ b/inc/common/msg.h @@ -103,7 +103,7 @@ void MSG_WriteAngle(float f); void MSG_FlushBits(void); void MSG_WriteBits(int value, int bits); int MSG_WriteDeltaUsercmd(const usercmd_t *from, const usercmd_t *cmd, int version); -int MSG_WriteDeltaUsercmd_Enhanced(const usercmd_t *from, const usercmd_t *cmd, int version); +int MSG_WriteDeltaUsercmd_Enhanced(const usercmd_t *from, const usercmd_t *cmd); #endif void MSG_WriteDir(const vec3_t vector); void MSG_PackEntity(entity_packed_t *out, const entity_state_t *in, bool short_angles); @@ -140,7 +140,7 @@ void MSG_ReadDir(vec3_t vector); int MSG_ReadBits(int bits); void MSG_ReadDeltaUsercmd(const usercmd_t *from, usercmd_t *cmd); void MSG_ReadDeltaUsercmd_Hacked(const usercmd_t *from, usercmd_t *to); -void MSG_ReadDeltaUsercmd_Enhanced(const usercmd_t *from, usercmd_t *to, int version); +void MSG_ReadDeltaUsercmd_Enhanced(const usercmd_t *from, usercmd_t *to); int MSG_ParseEntityBits(int *bits); void MSG_ParseDeltaEntity(const entity_state_t *from, entity_state_t *to, int number, int bits, msgEsFlags_t flags); #if USE_CLIENT diff --git a/inc/common/protocol.h b/inc/common/protocol.h index 1dba310d6..7da828ede 100644 --- a/inc/common/protocol.h +++ b/inc/common/protocol.h @@ -36,11 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PROTOCOL_VERSION_R1Q2_LONG_SOLID 1905 // b7759 #define PROTOCOL_VERSION_R1Q2_CURRENT 1905 // b7759 -#define PROTOCOL_VERSION_Q2PRO_MINIMUM 1011 // r161 -#define PROTOCOL_VERSION_Q2PRO_UCMD 1012 // r179 -#define PROTOCOL_VERSION_Q2PRO_CLIENTNUM_FIX 1013 // r226 -#define PROTOCOL_VERSION_Q2PRO_LONG_SOLID 1014 // r243 -#define PROTOCOL_VERSION_Q2PRO_WATERJUMP_HACK 1015 // r335 +#define PROTOCOL_VERSION_Q2PRO_MINIMUM 1015 // r335 #define PROTOCOL_VERSION_Q2PRO_RESERVED 1016 // r364 #define PROTOCOL_VERSION_Q2PRO_BEAM_ORIGIN 1017 // r1037-8 #define PROTOCOL_VERSION_Q2PRO_SHORT_ANGLES 1018 // r1037-44 diff --git a/src/client/input.c b/src/client/input.c index 6f8a191d3..b589c7e86 100644 --- a/src/client/input.c +++ b/src/client/input.c @@ -1020,7 +1020,7 @@ static void CL_SendBatchedCmd(void) for (j = oldest->cmdNumber + 1; j <= history->cmdNumber; j++) { cmd = &cl.cmds[j & CMD_MASK]; totalMsec += cmd->msec; - bits = MSG_WriteDeltaUsercmd_Enhanced(oldcmd, cmd, cls.protocolVersion); + bits = MSG_WriteDeltaUsercmd_Enhanced(oldcmd, cmd); #if USE_DEBUG if (cl_showpackets->integer == 3) { MSG_ShowDeltaUsercmdBits_Enhanced(bits); diff --git a/src/client/parse.c b/src/client/parse.c index 7d2680b3e..5f4df85dc 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -605,23 +605,18 @@ static void CL_ParseServerData(void) Com_DPrintf("Q2PRO QW mode enabled\n"); PmoveEnableQW(&cl.pmp); } - cl.esFlags |= MSG_ES_UMASK; - if (cls.protocolVersion >= PROTOCOL_VERSION_Q2PRO_LONG_SOLID) { - cl.esFlags |= MSG_ES_LONGSOLID; + i = MSG_ReadByte(); + if (i) { + Com_DPrintf("Q2PRO waterjump hack enabled\n"); + cl.pmp.waterhack = true; } + cl.esFlags |= MSG_ES_UMASK | MSG_ES_LONGSOLID; if (cls.protocolVersion >= PROTOCOL_VERSION_Q2PRO_BEAM_ORIGIN) { cl.esFlags |= MSG_ES_BEAMORIGIN; } if (cls.protocolVersion >= PROTOCOL_VERSION_Q2PRO_SHORT_ANGLES) { cl.esFlags |= MSG_ES_SHORTANGLES; } - if (cls.protocolVersion >= PROTOCOL_VERSION_Q2PRO_WATERJUMP_HACK) { - i = MSG_ReadByte(); - if (i) { - Com_DPrintf("Q2PRO waterjump hack enabled\n"); - cl.pmp.waterhack = true; - } - } cl.pmp.speedmult = 2; cl.pmp.flyhack = true; // fly hack is unconditionally enabled cl.pmp.flyfriction = 4; diff --git a/src/common/msg.c b/src/common/msg.c index 012b63fdf..4d93ac14c 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -345,10 +345,9 @@ MSG_WriteDeltaUsercmd_Enhanced ============= */ int MSG_WriteDeltaUsercmd_Enhanced(const usercmd_t *from, - const usercmd_t *cmd, - int version) + const usercmd_t *cmd) { - int bits, delta, count; + int bits, delta; if (!from) { from = &nullUserCmd; @@ -407,20 +406,14 @@ int MSG_WriteDeltaUsercmd_Enhanced(const usercmd_t *from, MSG_WriteBits(cmd->angles[2], -16); } - if (version >= PROTOCOL_VERSION_Q2PRO_UCMD) { - count = -10; - } else { - count = -16; - } - if (bits & CM_FORWARD) { - MSG_WriteBits(cmd->forwardmove, count); + MSG_WriteBits(cmd->forwardmove, -10); } if (bits & CM_SIDE) { - MSG_WriteBits(cmd->sidemove, count); + MSG_WriteBits(cmd->sidemove, -10); } if (bits & CM_UP) { - MSG_WriteBits(cmd->upmove, count); + MSG_WriteBits(cmd->upmove, -10); } if (bits & CM_BUTTONS) { @@ -1662,11 +1655,9 @@ int MSG_ReadBits(int bits) return value; } -void MSG_ReadDeltaUsercmd_Enhanced(const usercmd_t *from, - usercmd_t *to, - int version) +void MSG_ReadDeltaUsercmd_Enhanced(const usercmd_t *from, usercmd_t *to) { - int bits, count; + int bits; if (from) { memcpy(to, from, sizeof(*to)); @@ -1700,20 +1691,14 @@ void MSG_ReadDeltaUsercmd_Enhanced(const usercmd_t *from, } // read movement - if (version >= PROTOCOL_VERSION_Q2PRO_UCMD) { - count = -10; - } else { - count = -16; - } - if (bits & CM_FORWARD) { - to->forwardmove = MSG_ReadBits(count); + to->forwardmove = MSG_ReadBits(-10); } if (bits & CM_SIDE) { - to->sidemove = MSG_ReadBits(count); + to->sidemove = MSG_ReadBits(-10); } if (bits & CM_UP) { - to->upmove = MSG_ReadBits(count); + to->upmove = MSG_ReadBits(-10); } // read buttons diff --git a/src/server/entities.c b/src/server/entities.c index f9cade80a..67169e448 100644 --- a/src/server/entities.c +++ b/src/server/entities.c @@ -289,20 +289,12 @@ void SV_WriteFrameToClient_Enhanced(client_t *client) if (client->protocol == PROTOCOL_VERSION_Q2PRO) { // delta encode the clientNum - if (client->version < PROTOCOL_VERSION_Q2PRO_CLIENTNUM_FIX) { - if (!oldframe || frame->clientNum != oldframe->clientNum) { - extraflags |= EPS_CLIENTNUM; + if ((oldframe ? oldframe->clientNum : 0) != frame->clientNum) { + extraflags |= EPS_CLIENTNUM; + if (client->version < PROTOCOL_VERSION_Q2PRO_CLIENTNUM_SHORT) { MSG_WriteByte(frame->clientNum); - } - } else { - int clientNum = oldframe ? oldframe->clientNum : 0; - if (clientNum != frame->clientNum) { - extraflags |= EPS_CLIENTNUM; - if (client->version < PROTOCOL_VERSION_Q2PRO_CLIENTNUM_SHORT) { - MSG_WriteByte(frame->clientNum); - } else { - MSG_WriteShort(frame->clientNum); - } + } else { + MSG_WriteShort(frame->clientNum); } } } diff --git a/src/server/main.c b/src/server/main.c index ea13a43ab..915182193 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -1004,16 +1004,11 @@ static void init_pmove_and_es_flags(client_t *newcl) } newcl->pmp.flyhack = true; newcl->pmp.flyfriction = 4; - newcl->esFlags |= MSG_ES_UMASK; - if (newcl->version >= PROTOCOL_VERSION_Q2PRO_LONG_SOLID) { - newcl->esFlags |= MSG_ES_LONGSOLID; - } + newcl->esFlags |= MSG_ES_UMASK | MSG_ES_LONGSOLID; if (newcl->version >= PROTOCOL_VERSION_Q2PRO_BEAM_ORIGIN) { newcl->esFlags |= MSG_ES_BEAMORIGIN; } - if (newcl->version >= PROTOCOL_VERSION_Q2PRO_WATERJUMP_HACK) { - force = 1; - } + force = 1; } newcl->pmp.waterhack = sv_waterjump_hack->integer >= force; } diff --git a/src/server/user.c b/src/server/user.c index 367225150..af2c1c374 100644 --- a/src/server/user.c +++ b/src/server/user.c @@ -336,9 +336,7 @@ void SV_New_f(void) MSG_WriteByte(sv.state); MSG_WriteByte(sv_client->pmp.strafehack); MSG_WriteByte(sv_client->pmp.qwmode); - if (sv_client->version >= PROTOCOL_VERSION_Q2PRO_WATERJUMP_HACK) { - MSG_WriteByte(sv_client->pmp.waterhack); - } + MSG_WriteByte(sv_client->pmp.waterhack); break; } @@ -1190,7 +1188,7 @@ static void SV_NewClientExecuteMove(int c) return; } cmd = &cmds[i][j]; - MSG_ReadDeltaUsercmd_Enhanced(lastcmd, cmd, sv_client->version); + MSG_ReadDeltaUsercmd_Enhanced(lastcmd, cmd); cmd->lightlevel = lightlevel; lastcmd = cmd; } From b5b04efd64325e17a6df46704efa20500c5b71f6 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 30 Aug 2022 00:21:58 +0300 Subject: [PATCH 06/58] =?UTF-8?q?Fix=20broken=20=E2=80=98sv=5Fpad=5Fpacket?= =?UTF-8?q?s=E2=80=99.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also implement for old netchan. --- src/server/send.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/server/send.c b/src/server/send.c index 65bb9bdda..fbe9b6421 100644 --- a/src/server/send.c +++ b/src/server/send.c @@ -759,6 +759,15 @@ static void write_datagram_old(client_t *client) // write at least one reliable message write_reliables_old(client, client->netchan.maxpacketlen - msg_write.cursize); +#if USE_DEBUG + if (sv_pad_packets->integer > 0) { + size_t pad = min(MAX_PACKETLEN - 8, sv_pad_packets->integer); + + while (msg_write.cursize < pad) + MSG_WriteByte(svc_nop); + } +#endif + // send the datagram cursize = client->netchan.Transmit(&client->netchan, msg_write.cursize, @@ -817,15 +826,11 @@ static void write_datagram_new(client_t *client) } #if USE_DEBUG - if (sv_pad_packets->integer) { - size_t pad = msg_write.cursize + sv_pad_packets->integer; + if (sv_pad_packets->integer > 0) { + size_t pad = min(msg_write.maxsize, sv_pad_packets->integer); - if (pad > msg_write.maxsize) { - pad = msg_write.maxsize; - } - for (; pad > 0; pad--) { + while (msg_write.cursize < pad) MSG_WriteByte(svc_nop); - } } #endif From 2938ebcbfc9c5966cce11d74a22bb16f26bd0258 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 30 Aug 2022 17:18:18 +0300 Subject: [PATCH 07/58] Always deflate(Z_FINISH) info big enough buffer. From a description of deflate() it seems like one should always allocate buffer big enough for the worst case compression using deflateBound() in order for deflate(Z_FINISH) to succeed in one call. While it doesn't seem to be the case in practice, and deflate(Z_FINISH) will succeed as long as there is enough space for compressed data, restructure code to always perform compression into statically allocated buffer that's big enough to hold deflateBound(MAX_MSGLEN) bytes. --- src/server/init.c | 2 ++ src/server/main.c | 1 + src/server/send.c | 65 +++++++++++++++++++-------------------------- src/server/server.h | 2 ++ 4 files changed, 32 insertions(+), 38 deletions(-) diff --git a/src/server/init.c b/src/server/init.c index 5e7dc1daf..461d25879 100644 --- a/src/server/init.c +++ b/src/server/init.c @@ -426,6 +426,8 @@ void SV_InitGame(unsigned mvd_spawn) svs.z.zfree = SV_zfree; Q_assert(deflateInit2(&svs.z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 9, Z_DEFAULT_STRATEGY) == Z_OK); + svs.z_buffer_size = ZPACKET_HEADER + deflateBound(&svs.z, MAX_MSGLEN); + svs.z_buffer = SV_Malloc(svs.z_buffer_size); #endif // init game diff --git a/src/server/main.c b/src/server/main.c index 915182193..d582feeca 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -2370,6 +2370,7 @@ void SV_Shutdown(const char *finalmsg, error_type_t type) Z_Free(svs.entities); #if USE_ZLIB deflateEnd(&svs.z); + Z_Free(svs.z_buffer); #endif memset(&svs, 0, sizeof(svs)); diff --git a/src/server/send.c b/src/server/send.c index fbe9b6421..68c54166a 100644 --- a/src/server/send.c +++ b/src/server/send.c @@ -321,15 +321,7 @@ void SV_Multicast(const vec3_t origin, multicast_t to) } #if USE_ZLIB -static size_t max_compressed_len(client_t *client) -{ - if (client->netchan.type == NETCHAN_NEW) - return MAX_MSGLEN - ZPACKET_HEADER; - - return client->netchan.maxpacketlen - ZPACKET_HEADER; -} - -static bool can_compress_message(client_t *client) +static bool can_auto_compress(client_t *client) { if (!client->has_zlib) return false; @@ -349,52 +341,44 @@ static bool can_compress_message(client_t *client) return true; } -static bool compress_message(client_t *client, int flags) +static int compress_message(client_t *client) { - byte buffer[MAX_MSGLEN]; int ret, len; + byte *hdr; if (!client->has_zlib) - return false; + return 0; svs.z.next_in = msg_write.data; svs.z.avail_in = msg_write.cursize; - svs.z.next_out = buffer + ZPACKET_HEADER; - svs.z.avail_out = max_compressed_len(client); + svs.z.next_out = svs.z_buffer + ZPACKET_HEADER; + svs.z.avail_out = svs.z_buffer_size - ZPACKET_HEADER; ret = deflate(&svs.z, Z_FINISH); len = svs.z.total_out; - // prepare for next deflate() or deflateBound() + // prepare for next deflate() deflateReset(&svs.z); if (ret != Z_STREAM_END) { Com_WPrintf("Error %d compressing %zu bytes message for %s\n", ret, msg_write.cursize, client->name); - return false; + return 0; } - buffer[0] = svc_zpacket; - buffer[1] = len & 255; - buffer[2] = (len >> 8) & 255; - buffer[3] = msg_write.cursize & 255; - buffer[4] = (msg_write.cursize >> 8) & 255; + // write the packet header + hdr = svs.z_buffer; + hdr[0] = svc_zpacket; + hdr[1] = len & 255; + hdr[2] = (len >> 8) & 255; + hdr[3] = msg_write.cursize & 255; + hdr[4] = (msg_write.cursize >> 8) & 255; - len += ZPACKET_HEADER; - - SV_DPrintf(0, "%s: comp: %zu into %d\n", - client->name, msg_write.cursize, len); - - // did it compress good enough? - if (len >= msg_write.cursize) - return false; - - client->AddMessage(client, buffer, len, flags & MSG_RELIABLE); - return true; + return len + ZPACKET_HEADER; } #else -#define can_compress_message(client) false -#define compress_message(client, flags) false +#define can_auto_compress(c) false +#define compress_message(c) 0 #endif /* @@ -408,19 +392,24 @@ unless told otherwise. */ void SV_ClientAddMessage(client_t *client, int flags) { - SV_DPrintf(1, "Added %sreliable message to %s: %zu bytes\n", - (flags & MSG_RELIABLE) ? "" : "un", client->name, msg_write.cursize); + int len; if (!msg_write.cursize) { return; } - if ((flags & MSG_COMPRESS_AUTO) && can_compress_message(client)) { + if ((flags & MSG_COMPRESS_AUTO) && can_auto_compress(client)) { flags |= MSG_COMPRESS; } - if (!(flags & MSG_COMPRESS) || !compress_message(client, flags)) { + if ((flags & MSG_COMPRESS) && (len = compress_message(client)) && len < msg_write.cursize) { + client->AddMessage(client, svs.z_buffer, len, flags & MSG_RELIABLE); + SV_DPrintf(0, "Compressed %sreliable message to %s: %zu into %d\n", + (flags & MSG_RELIABLE) ? "" : "un", client->name, msg_write.cursize, len); + } else { client->AddMessage(client, msg_write.data, msg_write.cursize, flags & MSG_RELIABLE); + SV_DPrintf(1, "Added %sreliable message to %s: %zu bytes\n", + (flags & MSG_RELIABLE) ? "" : "un", client->name, msg_write.cursize); } if (flags & MSG_CLEAR) { diff --git a/src/server/server.h b/src/server/server.h index 9dafd9768..ea64cef37 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -479,6 +479,8 @@ typedef struct server_static_s { #if USE_ZLIB z_stream z; // for compressing messages at once + byte *z_buffer; + size_t z_buffer_size; #endif unsigned last_heartbeat; From d0ddca19953db265e3b4c211f6d95a487a8ce991 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 30 Aug 2022 17:37:48 +0300 Subject: [PATCH 08/58] Try to compress frame for protocol 35 clients if it overflows. --- src/server/send.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/server/send.c b/src/server/send.c index 68c54166a..eb8ce7716 100644 --- a/src/server/send.c +++ b/src/server/send.c @@ -729,9 +729,24 @@ static void write_datagram_old(client_t *client) // and the player_state_t client->WriteFrame(client); if (msg_write.cursize > maxsize) { - SV_DPrintf(0, "Frame %d overflowed for %s: %zu > %zu\n", - client->framenum, client->name, msg_write.cursize, maxsize); + size_t size = msg_write.cursize; + int len = 0; + + // try to compress if it has a chance to fit + // assume it can be compressed by at least 20% + if (size - size / 5 < maxsize) + len = compress_message(client); + SZ_Clear(&msg_write); + + if (len > 0 && len <= maxsize) { + SV_DPrintf(0, "Frame %d compressed for %s: %zu into %d\n", + client->framenum, client->name, size, len); + SZ_Write(&msg_write, svs.z_buffer, len); + } else { + SV_DPrintf(0, "Frame %d overflowed for %s: %zu > %zu (comp %d)\n", + client->framenum, client->name, size, maxsize, len); + } } // now write unreliable messages From af0812b8f5f22bc3460076e6a36422f1763c8668 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 30 Aug 2022 17:44:11 +0300 Subject: [PATCH 09/58] Simplify message flushing. --- src/server/user.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/server/user.c b/src/server/user.c index af2c1c374..6f860dfc5 100644 --- a/src/server/user.c +++ b/src/server/user.c @@ -90,14 +90,15 @@ static void SV_CreateBaselines(void) } } -static bool need_flush_msg(size_t size) +static void maybe_flush_msg(size_t size) { size += msg_write.cursize; #if USE_ZLIB if (sv_client->has_zlib) size = ZPACKET_HEADER + deflateBound(&svs.z, size); #endif - return size > sv_client->netchan.maxpacketlen; + if (size > sv_client->netchan.maxpacketlen) + SV_ClientAddMessage(sv_client, MSG_GAMESTATE); } static void write_configstrings(void) @@ -115,9 +116,7 @@ static void write_configstrings(void) length = Q_strnlen(string, MAX_QPATH); // check if this configstring will overflow - if (need_flush_msg(length + 4)) { - SV_ClientAddMessage(sv_client, MSG_GAMESTATE); - } + maybe_flush_msg(length + 4); MSG_WriteByte(svc_configstring); MSG_WriteShort(i); @@ -153,9 +152,7 @@ static void write_baselines(void) for (j = 0; j < SV_BASELINES_PER_CHUNK; j++) { if (base->number) { // check if this baseline will overflow - if (need_flush_msg(64)) { - SV_ClientAddMessage(sv_client, MSG_GAMESTATE); - } + maybe_flush_msg(64); MSG_WriteByte(svc_spawnbaseline); write_baseline(base); From e71417ef1249332d2f587fc7c1a0aac6882c124b Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 30 Aug 2022 17:44:54 +0300 Subject: [PATCH 10/58] =?UTF-8?q?Reduce=20=E2=80=98sv=5Fdebug=201=E2=80=99?= =?UTF-8?q?=20verbosity.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/send.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/send.c b/src/server/send.c index eb8ce7716..3395d5b7d 100644 --- a/src/server/send.c +++ b/src/server/send.c @@ -536,7 +536,7 @@ static void emit_snd(client_t *client, message_packet_t *msg) // check if position needs to be explicitly sent if (!(flags & SND_POS) && !check_entity(client, entnum)) { - SV_DPrintf(0, "Forcing position on entity %d for %s\n", + SV_DPrintf(1, "Forcing position on entity %d for %s\n", entnum, client->name); flags |= SND_POS; // entity is not present in frame } @@ -1009,7 +1009,7 @@ void SV_SendAsyncPackets(void) // make sure all fragments are transmitted first if (netchan->fragment_pending) { cursize = netchan->TransmitNextFragment(netchan); - SV_DPrintf(0, "%s: frag: %zu\n", client->name, cursize); + SV_DPrintf(1, "%s: frag: %zu\n", client->name, cursize); goto calctime; } @@ -1037,7 +1037,7 @@ void SV_SendAsyncPackets(void) if (netchan->message.cursize || netchan->reliable_ack_pending || netchan->reliable_length || retransmit) { cursize = netchan->Transmit(netchan, 0, "", 1); - SV_DPrintf(0, "%s: send: %zu\n", client->name, cursize); + SV_DPrintf(1, "%s: send: %zu\n", client->name, cursize); calctime: SV_CalcSendTime(client, cursize); } From 9455922b92415244a154581c4abdbf492ae1c3e0 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 2 Sep 2022 16:05:53 +0300 Subject: [PATCH 11/58] Never write a packetplayer with CLIENTNUM_NONE number. --- src/common/msg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/msg.c b/src/common/msg.c index 4d93ac14c..5359251df 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -1181,7 +1181,7 @@ void MSG_WriteDeltaPlayerstate_Packet(const player_packed_t *from, int pflags; int statbits; - if (number < 0 || number >= MAX_CLIENTS) + if (number < 0 || number >= CLIENTNUM_NONE) Com_Error(ERR_DROP, "%s: bad number: %d", __func__, number); if (!to) { From 4158a675308a1b3c85c0845419ac2b8dba27cf31 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 2 Sep 2022 16:09:29 +0300 Subject: [PATCH 12/58] Allow maxclients 256, unless MVD server is enabled. --- inc/common/protocol.h | 4 ++-- src/server/init.c | 12 ++++++------ src/server/mvd.c | 3 +++ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/inc/common/protocol.h b/inc/common/protocol.h index 7da828ede..c67f17060 100644 --- a/inc/common/protocol.h +++ b/inc/common/protocol.h @@ -337,8 +337,8 @@ typedef enum { // ============================================================== -#define CLIENTNUM_NONE (MAX_CLIENTS - 1) -#define CLIENTNUM_RESERVED (MAX_CLIENTS - 1) +// a client with this number will never be included in MVD stream +#define CLIENTNUM_NONE (MAX_CLIENTS - 1) // a SOLID_BBOX will never create this value #define PACKED_BSP 31 diff --git a/src/server/init.c b/src/server/init.c index 461d25879..d108e9bee 100644 --- a/src/server/init.c +++ b/src/server/init.c @@ -402,18 +402,13 @@ void SV_InitGame(unsigned mvd_spawn) } else { // non-deathmatch, non-coop is one player Cvar_Set("maxclients", "1"); } - Cvar_ClampInteger(sv_maxclients, 1, CLIENTNUM_RESERVED); + Cvar_ClampInteger(sv_maxclients, 1, MAX_CLIENTS); // enable networking if (sv_maxclients->integer > 1) { NET_Config(NET_SERVER); } - svs.client_pool = SV_Mallocz(sizeof(client_t) * sv_maxclients->integer); - - svs.num_entities = sv_maxclients->integer * UPDATE_BACKUP * MAX_PACKET_ENTITIES; - svs.entities = SV_Mallocz(sizeof(entity_packed_t) * svs.num_entities); - // initialize MVD server if (!mvd_spawn) { SV_MvdInit(); @@ -421,6 +416,11 @@ void SV_InitGame(unsigned mvd_spawn) Cvar_ClampInteger(sv_reserved_slots, 0, sv_maxclients->integer - 1); + svs.client_pool = SV_Mallocz(sizeof(client_t) * sv_maxclients->integer); + + svs.num_entities = sv_maxclients->integer * UPDATE_BACKUP * MAX_PACKET_ENTITIES; + svs.entities = SV_Mallocz(sizeof(entity_packed_t) * svs.num_entities); + #if USE_ZLIB svs.z.zalloc = SV_zalloc; svs.z.zfree = SV_zfree; diff --git a/src/server/mvd.c b/src/server/mvd.c index 428d24cf2..a49649468 100644 --- a/src/server/mvd.c +++ b/src/server/mvd.c @@ -2057,6 +2057,9 @@ void SV_MvdInit(void) return; // do nothing if disabled } + // reserve CLIENTNUM_NONE slot + Cvar_ClampInteger(sv_maxclients, 1, CLIENTNUM_NONE); + // allocate buffers SZ_Init(&mvd.message, SV_Malloc(MAX_MSGLEN), MAX_MSGLEN); SZ_Init(&mvd.datagram, SV_Malloc(MAX_MSGLEN), MAX_MSGLEN); From 10519ab7367b280016df14977017b5cc3c23d445 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 2 Sep 2022 18:20:00 +0300 Subject: [PATCH 13/58] Print a warning for older Q2PRO clients if server has allocated slot 255. --- src/server/user.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/server/user.c b/src/server/user.c index 6f860dfc5..24e26cf57 100644 --- a/src/server/user.c +++ b/src/server/user.c @@ -339,6 +339,16 @@ void SV_New_f(void) SV_ClientAddMessage(sv_client, MSG_RELIABLE | MSG_CLEAR); + if (sv_client->protocol == PROTOCOL_VERSION_Q2PRO && + sv_client->version < PROTOCOL_VERSION_Q2PRO_CLIENTNUM_SHORT && + sv_client->slot == CLIENTNUM_NONE && oldstate == cs_assigned) + { + SV_ClientPrintf(sv_client, PRINT_HIGH, + "WARNING: Server has allocated client slot number 255. " + "This is known to be broken in your Q2PRO client version. " + "Please update your client to latest version.\n"); + } + SV_ClientCommand(sv_client, "\n"); // send version string request From 094d1c79b2d26f398c8cf82ff2fe7c3f14c7441b Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 5 Sep 2022 17:43:50 +0300 Subject: [PATCH 14/58] Don't error out if neither SND_ENT nor SND_POS are set. The server will never send such packet, but this happens in some (possibly edited) demos. --- src/client/parse.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/client/parse.c b/src/client/parse.c index 5f4df85dc..aebfa6549 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -805,8 +805,6 @@ static void CL_ParseStartSoundPacket(void) int flags, channel, entity; flags = MSG_ReadByte(); - if ((flags & (SND_ENT | SND_POS)) == 0) - Com_Error(ERR_DROP, "%s: neither SND_ENT nor SND_POS set", __func__); snd.index = MSG_ReadByte(); if (snd.index == -1) From 36e6c47afd3b86c163a0fca895f9fedd2e91665c Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 14 Sep 2022 20:07:36 +0300 Subject: [PATCH 15/58] =?UTF-8?q?Add=20=E2=80=98sv=5Fmax=5Fdownload=5Fsize?= =?UTF-8?q?=E2=80=99=20cvar.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, maximum download size was limited by MAX_LOADFILE which was 4 MiB originally, later bumped to 16 MiB and finally 64 MiB. This is too much for UDP downloads, which are kept in memory until client stops downloading. Make maximum download size configurable and change it to be just 8 MiB by default. --- doc/server.md | 4 ++++ src/server/main.c | 2 ++ src/server/server.h | 1 + src/server/user.c | 4 +--- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/doc/server.md b/doc/server.md index 1c3775095..3f65126ca 100644 --- a/doc/server.md +++ b/doc/server.md @@ -299,6 +299,10 @@ Default value is 1. Enables downloading of files from any subdirectory other than those listed above. Default value is 0. +#### `sv_max_download_size` +Maximum size of UDP download in bytes. Value of 0 disables the limit. +Default value is 8388608 (8 MiB). + ### MVD/GTV server diff --git a/src/server/main.c b/src/server/main.c index d582feeca..cdba8f288 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -81,6 +81,7 @@ cvar_t *sv_min_rate; cvar_t *sv_max_rate; cvar_t *sv_calcpings_method; cvar_t *sv_changemapcmd; +cvar_t *sv_max_download_size; cvar_t *sv_strafejump_hack; cvar_t *sv_waterjump_hack; @@ -2210,6 +2211,7 @@ void SV_Init(void) sv_max_rate->changed(sv_max_rate); sv_calcpings_method = Cvar_Get("sv_calcpings_method", "2", 0); sv_changemapcmd = Cvar_Get("sv_changemapcmd", "", 0); + sv_max_download_size = Cvar_Get("sv_max_download_size", "8388608", 0); sv_strafejump_hack = Cvar_Get("sv_strafejump_hack", "1", CVAR_LATCH); sv_waterjump_hack = Cvar_Get("sv_waterjump_hack", "0", CVAR_LATCH); diff --git a/src/server/server.h b/src/server/server.h index ea64cef37..266d8551c 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -535,6 +535,7 @@ extern cvar_t *sv_novis; extern cvar_t *sv_lan_force_rate; extern cvar_t *sv_calcpings_method; extern cvar_t *sv_changemapcmd; +extern cvar_t *sv_max_download_size; extern cvar_t *sv_strafejump_hack; #if USE_PACKETDUP diff --git a/src/server/user.c b/src/server/user.c index 24e26cf57..6d15673ab 100644 --- a/src/server/user.c +++ b/src/server/user.c @@ -582,11 +582,9 @@ static void SV_BeginDownload_f(void) } maxdownloadsize = MAX_LOADFILE; -#if 0 - if (sv_max_download_size->integer) { + if (sv_max_download_size->integer > 0) { maxdownloadsize = Cvar_ClampInteger(sv_max_download_size, 1, MAX_LOADFILE); } -#endif if (downloadsize == 0) { Com_DPrintf("Refusing empty download of %s to %s\n", name, sv_client->name); From da573dbf21ba35c7f2aa430608182f911673cf56 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 17 Nov 2022 19:09:44 +0300 Subject: [PATCH 16/58] Allow max packet entities to be configured. --- doc/server.md | 6 ++++++ src/server/entities.c | 2 +- src/server/main.c | 2 ++ src/server/server.h | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/server.md b/doc/server.md index 3f65126ca..6a9af4a35 100644 --- a/doc/server.md +++ b/doc/server.md @@ -131,6 +131,12 @@ Otherwise clients will be unable to connect. If set to 0, server will skip cinematics even if they exist. Default value is 1. +#### `sv_max_packet_entities` +Maximum number of entities in client frame. 0 means unlimited. Default +value is 128. Some non-standard maps with large open areas may need this +value increased. Consider however that default Quake 2 client can only +render 128 entities maximum. Other clients may support more. + #### `sv_reserved_slots` Number of client slots reserved for clients who know `sv_reserved_password` or `sv_password`. Must be less than `maxclients` value. Default value is 0 diff --git a/src/server/entities.c b/src/server/entities.c index 67169e448..a3c0e0890 100644 --- a/src/server/entities.c +++ b/src/server/entities.c @@ -585,7 +585,7 @@ void SV_BuildClientFrame(client_t *client) svs.next_entity++; - if (++frame->num_entities == MAX_PACKET_ENTITIES) { + if (++frame->num_entities == sv_max_packet_entities->integer) { break; } } diff --git a/src/server/main.c b/src/server/main.c index cdba8f288..4b694f6b8 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -82,6 +82,7 @@ cvar_t *sv_max_rate; cvar_t *sv_calcpings_method; cvar_t *sv_changemapcmd; cvar_t *sv_max_download_size; +cvar_t *sv_max_packet_entities; cvar_t *sv_strafejump_hack; cvar_t *sv_waterjump_hack; @@ -2212,6 +2213,7 @@ void SV_Init(void) sv_calcpings_method = Cvar_Get("sv_calcpings_method", "2", 0); sv_changemapcmd = Cvar_Get("sv_changemapcmd", "", 0); sv_max_download_size = Cvar_Get("sv_max_download_size", "8388608", 0); + sv_max_packet_entities = Cvar_Get("sv_max_packet_entities", STRINGIFY(MAX_PACKET_ENTITIES), 0); sv_strafejump_hack = Cvar_Get("sv_strafejump_hack", "1", CVAR_LATCH); sv_waterjump_hack = Cvar_Get("sv_waterjump_hack", "0", CVAR_LATCH); diff --git a/src/server/server.h b/src/server/server.h index 266d8551c..7936d7fd7 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -536,6 +536,7 @@ extern cvar_t *sv_lan_force_rate; extern cvar_t *sv_calcpings_method; extern cvar_t *sv_changemapcmd; extern cvar_t *sv_max_download_size; +extern cvar_t *sv_max_packet_entities; extern cvar_t *sv_strafejump_hack; #if USE_PACKETDUP From 4c7bbc405da85acc4daf592e4f6273139269b4f3 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 30 Sep 2022 23:45:41 +0300 Subject: [PATCH 17/58] Fix compilation without USE_ZLIB. --- src/server/send.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/server/send.c b/src/server/send.c index 3395d5b7d..aca5ee25b 100644 --- a/src/server/send.c +++ b/src/server/send.c @@ -376,9 +376,15 @@ static int compress_message(client_t *client) return len + ZPACKET_HEADER; } + +static byte *get_compressed_data(void) +{ + return svs.z_buffer; +} #else #define can_auto_compress(c) false #define compress_message(c) 0 +#define get_compressed_data() NULL #endif /* @@ -403,7 +409,7 @@ void SV_ClientAddMessage(client_t *client, int flags) } if ((flags & MSG_COMPRESS) && (len = compress_message(client)) && len < msg_write.cursize) { - client->AddMessage(client, svs.z_buffer, len, flags & MSG_RELIABLE); + client->AddMessage(client, get_compressed_data(), len, flags & MSG_RELIABLE); SV_DPrintf(0, "Compressed %sreliable message to %s: %zu into %d\n", (flags & MSG_RELIABLE) ? "" : "un", client->name, msg_write.cursize, len); } else { @@ -742,7 +748,7 @@ static void write_datagram_old(client_t *client) if (len > 0 && len <= maxsize) { SV_DPrintf(0, "Frame %d compressed for %s: %zu into %d\n", client->framenum, client->name, size, len); - SZ_Write(&msg_write, svs.z_buffer, len); + SZ_Write(&msg_write, get_compressed_data(), len); } else { SV_DPrintf(0, "Frame %d overflowed for %s: %zu > %zu (comp %d)\n", client->framenum, client->name, size, maxsize, len); From c28d351c7cc1c6e60fd972ca19fcb38921dbd9e7 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 8 Oct 2022 23:49:38 +0300 Subject: [PATCH 18/58] Optimize old_origin update for RF_FRAMELERP entities. No point sending if it matches from->origin, same with new entities. --- src/common/msg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/msg.c b/src/common/msg.c index 5359251df..75b8c278c 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -599,7 +599,8 @@ void MSG_WriteDeltaEntity(const entity_packed_t *from, bits |= U_SOUND; if (to->renderfx & RF_FRAMELERP) { - bits |= U_OLDORIGIN; + if (!VectorCompare(to->old_origin, from->origin)) + bits |= U_OLDORIGIN; } else if (to->renderfx & RF_BEAM) { if (flags & MSG_ES_BEAMORIGIN) { if (!VectorCompare(to->old_origin, from->old_origin)) From daad65a043994e55731bb5082d2bf263a8499f90 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 8 Nov 2022 22:32:03 +0300 Subject: [PATCH 19/58] Optimize stats parsing. --- src/common/msg.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/common/msg.c b/src/common/msg.c index 75b8c278c..16b721b1a 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -1983,9 +1983,11 @@ void MSG_ParseDeltaPlayerstate_Default(const player_state_t *from, // parse stats statbits = MSG_ReadLong(); - for (i = 0; i < MAX_STATS; i++) - if (statbits & (1U << i)) - to->stats[i] = MSG_ReadShort(); + if (statbits) { + for (i = 0; i < MAX_STATS; i++) + if (statbits & (1U << i)) + to->stats[i] = MSG_ReadShort(); + } } From b3ed024e0f2f1f58509f8a41de430b8318b76adb Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 15 Dec 2022 20:21:52 +0300 Subject: [PATCH 20/58] Simplify code. --- src/client/entities.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/client/entities.c b/src/client/entities.c index f5c82917f..c84954712 100644 --- a/src/client/entities.c +++ b/src/client/entities.c @@ -34,18 +34,12 @@ FRAME PARSING ========================================================================= */ +// returns true if origin/angles update has been optimized out static inline bool entity_is_optimized(const entity_state_t *state) { - if (cls.serverProtocol != PROTOCOL_VERSION_Q2PRO) - return false; - - if (state->number != cl.frame.clientNum + 1) - return false; - - if (cl.frame.ps.pmove.pm_type >= PM_DEAD) - return false; - - return true; + return cls.serverProtocol == PROTOCOL_VERSION_Q2PRO + && state->number == cl.frame.clientNum + 1 + && cl.frame.ps.pmove.pm_type < PM_DEAD; } static inline void From 7d0cd7d08f0bb677be682491186d27dfd418804f Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 2 Dec 2022 22:56:55 +0300 Subject: [PATCH 21/58] Convert some ERR_DROPs to asserts. --- src/common/msg.c | 50 ++++++++++++++---------------------------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/src/common/msg.c b/src/common/msg.c index 16b721b1a..2f69c6c54 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -295,9 +295,7 @@ MSG_WriteBits */ void MSG_WriteBits(int value, int bits) { - if (bits == 0 || bits < -31 || bits > 31) { - Com_Error(ERR_FATAL, "MSG_WriteBits: bad bits: %d", bits); - } + Q_assert(!(bits == 0 || bits < -31 || bits > 31)); if (bits < 0) { bits = -bits; @@ -440,9 +438,7 @@ void MSG_WriteDir(const vec3_t dir) void MSG_PackEntity(entity_packed_t *out, const entity_state_t *in, bool short_angles) { // allow 0 to accomodate empty baselines - if (in->number < 0 || in->number >= MAX_EDICTS) - Com_Error(ERR_DROP, "%s: bad number: %d", __func__, in->number); - + Q_assert(in->number >= 0 && in->number < MAX_EDICTS); out->number = in->number; out->origin[0] = COORD2SHORT(in->origin[0]); out->origin[1] = COORD2SHORT(in->origin[1]); @@ -481,11 +477,8 @@ void MSG_WriteDeltaEntity(const entity_packed_t *from, uint32_t bits, mask; if (!to) { - if (!from) - Com_Error(ERR_DROP, "%s: NULL", __func__); - - if (from->number < 1 || from->number >= MAX_EDICTS) - Com_Error(ERR_DROP, "%s: bad number: %d", __func__, from->number); + Q_assert(from); + Q_assert(from->number > 0 && from->number < MAX_EDICTS); bits = U_REMOVE; if (from->number & 0xff00) @@ -503,8 +496,7 @@ void MSG_WriteDeltaEntity(const entity_packed_t *from, return; // remove entity } - if (to->number < 1 || to->number >= MAX_EDICTS) - Com_Error(ERR_DROP, "%s: bad number: %d", __func__, to->number); + Q_assert(to->number > 0 && to->number < MAX_EDICTS); if (!from) from = &nullEntityState; @@ -775,8 +767,7 @@ void MSG_WriteDeltaPlayerstate_Default(const player_packed_t *from, const player int pflags; int statbits; - if (!to) - Com_Error(ERR_DROP, "%s: NULL", __func__); + Q_assert(to); if (!from) from = &nullPlayerState; @@ -938,8 +929,7 @@ int MSG_WriteDeltaPlayerstate_Enhanced(const player_packed_t *from, int pflags, eflags; int statbits; - if (!to) - Com_Error(ERR_DROP, "%s: NULL", __func__); + Q_assert(to); if (!from) from = &nullPlayerState; @@ -1182,6 +1172,7 @@ void MSG_WriteDeltaPlayerstate_Packet(const player_packed_t *from, int pflags; int statbits; + // this can happen with client GTV if (number < 0 || number >= CLIENTNUM_NONE) Com_Error(ERR_DROP, "%s: bad number: %d", __func__, number); @@ -1627,9 +1618,7 @@ int MSG_ReadBits(int bits) { bool sgn = false; - if (bits == 0 || bits < -25 || bits > 25) { - Com_Error(ERR_FATAL, "MSG_ReadBits: bad bits: %d", bits); - } + Q_assert(!(bits == 0 || bits < -25 || bits > 25)); if (bits < 0) { bits = -bits; @@ -1765,13 +1754,8 @@ void MSG_ParseDeltaEntity(const entity_state_t *from, int bits, msgEsFlags_t flags) { - if (!to) { - Com_Error(ERR_DROP, "%s: NULL", __func__); - } - - if (number < 1 || number >= MAX_EDICTS) { - Com_Error(ERR_DROP, "%s: bad entity number: %d", __func__, number); - } + Q_assert(to); + Q_assert(number > 0 && number < MAX_EDICTS); // set everything to the state we are delta'ing from if (!from) { @@ -1889,9 +1873,7 @@ void MSG_ParseDeltaPlayerstate_Default(const player_state_t *from, int i; int statbits; - if (!to) { - Com_Error(ERR_DROP, "%s: NULL", __func__); - } + Q_assert(to); // clear to old value before delta parsing if (!from) { @@ -2004,9 +1986,7 @@ void MSG_ParseDeltaPlayerstate_Enhanced(const player_state_t *from, int i; int statbits; - if (!to) { - Com_Error(ERR_DROP, "%s: NULL", __func__); - } + Q_assert(to); // clear to old value before delta parsing if (!from) { @@ -2139,9 +2119,7 @@ void MSG_ParseDeltaPlayerstate_Packet(const player_state_t *from, int i; int statbits; - if (!to) { - Com_Error(ERR_DROP, "%s: NULL", __func__); - } + Q_assert(to); // clear to old value before delta parsing if (!from) { From 4c8e851355aa79c901b90c47485916eb45273c29 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 3 Dec 2022 02:10:36 +0300 Subject: [PATCH 22/58] Add more asserts. --- src/common/msg.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/common/msg.c b/src/common/msg.c index 2f69c6c54..99591e9a4 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -203,7 +203,9 @@ MSG_WriteDeltaUsercmd */ int MSG_WriteDeltaUsercmd(const usercmd_t *from, const usercmd_t *cmd, int version) { - int bits, buttons = cmd->buttons & BUTTON_MASK; + int bits, buttons; + + Q_assert(cmd); if (!from) { from = &nullUserCmd; @@ -232,6 +234,8 @@ int MSG_WriteDeltaUsercmd(const usercmd_t *from, const usercmd_t *cmd, int versi MSG_WriteByte(bits); + buttons = cmd->buttons & BUTTON_MASK; + if (version >= PROTOCOL_VERSION_R1Q2_UCMD) { if (bits & CM_BUTTONS) { if ((bits & CM_FORWARD) && !(cmd->forwardmove % 5)) { @@ -347,6 +351,8 @@ int MSG_WriteDeltaUsercmd_Enhanced(const usercmd_t *from, { int bits, delta; + Q_assert(cmd); + if (!from) { from = &nullUserCmd; } @@ -1507,6 +1513,8 @@ void MSG_ReadDeltaUsercmd(const usercmd_t *from, usercmd_t *to) { int bits; + Q_assert(to); + if (from) { memcpy(to, from, sizeof(*to)); } else { @@ -1549,6 +1557,8 @@ void MSG_ReadDeltaUsercmd_Hacked(const usercmd_t *from, usercmd_t *to) { int bits, buttons = 0; + Q_assert(to); + if (from) { memcpy(to, from, sizeof(*to)); } else { From 09d5110f1c9f7d1ed48b6c0cd2cba3f4a86f7b6d Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 3 Dec 2022 22:35:58 +0300 Subject: [PATCH 23/58] Simplify code. --- src/common/msg.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/common/msg.c b/src/common/msg.c index 99591e9a4..26d8d69de 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -236,19 +236,17 @@ int MSG_WriteDeltaUsercmd(const usercmd_t *from, const usercmd_t *cmd, int versi buttons = cmd->buttons & BUTTON_MASK; - if (version >= PROTOCOL_VERSION_R1Q2_UCMD) { - if (bits & CM_BUTTONS) { - if ((bits & CM_FORWARD) && !(cmd->forwardmove % 5)) { - buttons |= BUTTON_FORWARD; - } - if ((bits & CM_SIDE) && !(cmd->sidemove % 5)) { - buttons |= BUTTON_SIDE; - } - if ((bits & CM_UP) && !(cmd->upmove % 5)) { - buttons |= BUTTON_UP; - } - MSG_WriteByte(buttons); + if (version >= PROTOCOL_VERSION_R1Q2_UCMD && (bits & CM_BUTTONS)) { + if ((bits & CM_FORWARD) && !(cmd->forwardmove % 5)) { + buttons |= BUTTON_FORWARD; } + if ((bits & CM_SIDE) && !(cmd->sidemove % 5)) { + buttons |= BUTTON_SIDE; + } + if ((bits & CM_UP) && !(cmd->upmove % 5)) { + buttons |= BUTTON_UP; + } + MSG_WriteByte(buttons); } if (bits & CM_ANGLE1) @@ -280,10 +278,8 @@ int MSG_WriteDeltaUsercmd(const usercmd_t *from, const usercmd_t *cmd, int versi } } - if (version < PROTOCOL_VERSION_R1Q2_UCMD) { - if (bits & CM_BUTTONS) - MSG_WriteByte(cmd->buttons); - } + if (version < PROTOCOL_VERSION_R1Q2_UCMD && (bits & CM_BUTTONS)) + MSG_WriteByte(cmd->buttons); if (bits & CM_IMPULSE) MSG_WriteByte(cmd->impulse); From b03779e5bb2cbf0cc4fcf2b1ae48e18bf2593e75 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 6 Dec 2022 19:18:42 +0300 Subject: [PATCH 24/58] Add more asserts. --- src/common/msg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/msg.c b/src/common/msg.c index 26d8d69de..19b7a2ea4 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -1655,6 +1655,8 @@ void MSG_ReadDeltaUsercmd_Enhanced(const usercmd_t *from, usercmd_t *to) { int bits; + Q_assert(to); + if (from) { memcpy(to, from, sizeof(*to)); } else { From 9c39456da247af28427bf12b500c4301fa60ddb9 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 28 Dec 2022 13:14:51 +0300 Subject: [PATCH 25/58] Simplify condition. --- src/common/msg.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/common/msg.c b/src/common/msg.c index 19b7a2ea4..0f09a6af7 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -596,12 +596,8 @@ void MSG_WriteDeltaEntity(const entity_packed_t *from, if (!VectorCompare(to->old_origin, from->origin)) bits |= U_OLDORIGIN; } else if (to->renderfx & RF_BEAM) { - if (flags & MSG_ES_BEAMORIGIN) { - if (!VectorCompare(to->old_origin, from->old_origin)) - bits |= U_OLDORIGIN; - } else { + if (!(flags & MSG_ES_BEAMORIGIN) || !VectorCompare(to->old_origin, from->old_origin)) bits |= U_OLDORIGIN; - } } // From 96b9d4f4f255632432bfb418fc28d0f16ab38b0c Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 28 Dec 2022 14:33:26 +0300 Subject: [PATCH 26/58] Clamp after converting to int. --- src/common/msg.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/msg.c b/src/common/msg.c index 0f09a6af7..430b338b7 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -719,12 +719,14 @@ void MSG_WriteDeltaEntity(const entity_packed_t *from, static inline int OFFSET2CHAR(float x) { - return clamp(x, -32, 127.0f / 4) * 4; + int v = x * 4; + return clamp(v, -128, 127); } static inline int BLEND2BYTE(float x) { - return clamp(x, 0, 1) * 255; + int v = x * 255; + return clamp(v, 0, 255); } void MSG_PackPlayer(player_packed_t *out, const player_state_t *in) From e3bef54560862ef02dcf49da0331b685a9ddf8bc Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 28 Dec 2022 14:34:15 +0300 Subject: [PATCH 27/58] Simplify MSG_PackPlayer(). --- src/common/msg.c | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/common/msg.c b/src/common/msg.c index 430b338b7..c044e3323 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -734,27 +734,15 @@ void MSG_PackPlayer(player_packed_t *out, const player_state_t *in) int i; out->pmove = in->pmove; - out->viewangles[0] = ANGLE2SHORT(in->viewangles[0]); - out->viewangles[1] = ANGLE2SHORT(in->viewangles[1]); - out->viewangles[2] = ANGLE2SHORT(in->viewangles[2]); - out->viewoffset[0] = OFFSET2CHAR(in->viewoffset[0]); - out->viewoffset[1] = OFFSET2CHAR(in->viewoffset[1]); - out->viewoffset[2] = OFFSET2CHAR(in->viewoffset[2]); - out->kick_angles[0] = OFFSET2CHAR(in->kick_angles[0]); - out->kick_angles[1] = OFFSET2CHAR(in->kick_angles[1]); - out->kick_angles[2] = OFFSET2CHAR(in->kick_angles[2]); - out->gunoffset[0] = OFFSET2CHAR(in->gunoffset[0]); - out->gunoffset[1] = OFFSET2CHAR(in->gunoffset[1]); - out->gunoffset[2] = OFFSET2CHAR(in->gunoffset[2]); - out->gunangles[0] = OFFSET2CHAR(in->gunangles[0]); - out->gunangles[1] = OFFSET2CHAR(in->gunangles[1]); - out->gunangles[2] = OFFSET2CHAR(in->gunangles[2]); + for (i = 0; i < 3; i++) out->viewangles[i] = ANGLE2SHORT(in->viewangles[i]); + for (i = 0; i < 3; i++) out->viewoffset[i] = OFFSET2CHAR(in->viewoffset[i]); + for (i = 0; i < 3; i++) out->kick_angles[i] = OFFSET2CHAR(in->kick_angles[i]); + for (i = 0; i < 3; i++) out->gunoffset[i] = OFFSET2CHAR(in->gunoffset[i]); + for (i = 0; i < 3; i++) out->gunangles[i] = OFFSET2CHAR(in->gunangles[i]); out->gunindex = in->gunindex; out->gunframe = in->gunframe; - out->blend[0] = BLEND2BYTE(in->blend[0]); - out->blend[1] = BLEND2BYTE(in->blend[1]); - out->blend[2] = BLEND2BYTE(in->blend[2]); - out->blend[3] = BLEND2BYTE(in->blend[3]); + for (i = 0; i < 4; i++) + out->blend[i] = BLEND2BYTE(in->blend[i]); out->fov = (int)in->fov; out->rdflags = in->rdflags; for (i = 0; i < MAX_STATS; i++) From cb40cd90ab3fd9eb71f0802ce0c67c334847160e Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 24 Jan 2023 21:00:44 +0300 Subject: [PATCH 28/58] Document entity/player state writing flags. --- inc/common/msg.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/inc/common/msg.h b/inc/common/msg.h index 448fdeb6a..3fde44ed4 100644 --- a/inc/common/msg.h +++ b/inc/common/msg.h @@ -58,25 +58,25 @@ typedef struct { } player_packed_t; typedef enum { - MSG_PS_IGNORE_GUNINDEX = (1 << 0), - MSG_PS_IGNORE_GUNFRAMES = (1 << 1), - MSG_PS_IGNORE_BLEND = (1 << 2), - MSG_PS_IGNORE_VIEWANGLES = (1 << 3), - MSG_PS_IGNORE_DELTAANGLES = (1 << 4), - MSG_PS_IGNORE_PREDICTION = (1 << 5), // mutually exclusive with IGNORE_VIEWANGLES - MSG_PS_FORCE = (1 << 7), - MSG_PS_REMOVE = (1 << 8) + MSG_PS_IGNORE_GUNINDEX = (1 << 0), // ignore gunindex + MSG_PS_IGNORE_GUNFRAMES = (1 << 1), // ignore gunframe/gunoffset/gunangles + MSG_PS_IGNORE_BLEND = (1 << 2), // ignore blend + MSG_PS_IGNORE_VIEWANGLES = (1 << 3), // ignore viewangles + MSG_PS_IGNORE_DELTAANGLES = (1 << 4), // ignore delta_angles + MSG_PS_IGNORE_PREDICTION = (1 << 5), // mutually exclusive with IGNORE_VIEWANGLES + MSG_PS_FORCE = (1 << 7), // send even if unchanged (MVD stream only) + MSG_PS_REMOVE = (1 << 8), // player is removed (MVD stream only) } msgPsFlags_t; typedef enum { - MSG_ES_FORCE = (1 << 0), - MSG_ES_NEWENTITY = (1 << 1), - MSG_ES_FIRSTPERSON = (1 << 2), - MSG_ES_LONGSOLID = (1 << 3), - MSG_ES_UMASK = (1 << 4), - MSG_ES_BEAMORIGIN = (1 << 5), - MSG_ES_SHORTANGLES = (1 << 6), - MSG_ES_REMOVE = (1 << 7) + MSG_ES_FORCE = (1 << 0), // send even if unchanged + MSG_ES_NEWENTITY = (1 << 1), // send old_origin + MSG_ES_FIRSTPERSON = (1 << 2), // ignore origin/angles + MSG_ES_LONGSOLID = (1 << 3), // higher precision bbox encoding + MSG_ES_UMASK = (1 << 4), // client has 16-bit mask MSB fix + MSG_ES_BEAMORIGIN = (1 << 5), // client has RF_BEAM old_origin fix + MSG_ES_SHORTANGLES = (1 << 6), // higher precision angles encoding + MSG_ES_REMOVE = (1 << 7), // entity is removed (MVD stream only) } msgEsFlags_t; extern sizebuf_t msg_write; From c28285c4d9de4fd1db2ca8c38effeafc16a45538 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 8 Aug 2023 16:56:48 +0300 Subject: [PATCH 29/58] Add missing svc_setting debug print. --- src/common/msg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/msg.c b/src/common/msg.c index c044e3323..66ca95ac1 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -2393,6 +2393,7 @@ const char *MSG_ServerCommandString(int cmd) S(zpacket) S(zdownload) S(gamestate) + S(setting) #undef S } } From 29e6075f8707be5b530cb1f0a78e0b17757c8912 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 22 Aug 2023 13:40:31 +0300 Subject: [PATCH 30/58] Add MAX_PACKETENTITY_BYTES macro. --- inc/common/msg.h | 2 ++ src/client/demo.c | 2 +- src/server/user.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/inc/common/msg.h b/inc/common/msg.h index 3fde44ed4..7055b2c65 100644 --- a/inc/common/msg.h +++ b/inc/common/msg.h @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/protocol.h" #include "common/sizebuf.h" +#define MAX_PACKETENTITY_BYTES 64 // rough estimate + // entity and player states are pre-quantized before sending to make delta // comparsion easier typedef struct { diff --git a/src/client/demo.c b/src/client/demo.c index 1f5dc89f0..4011e082f 100644 --- a/src/client/demo.c +++ b/src/client/demo.c @@ -428,7 +428,7 @@ static void CL_Record_f(void) if (!ent->number) continue; - if (msg_write.cursize + 64 > size) { + if (msg_write.cursize + MAX_PACKETENTITY_BYTES > size) { if (!CL_WriteDemoMessage(&msg_write)) return; } diff --git a/src/server/user.c b/src/server/user.c index 6d15673ab..2940ed4c4 100644 --- a/src/server/user.c +++ b/src/server/user.c @@ -152,7 +152,7 @@ static void write_baselines(void) for (j = 0; j < SV_BASELINES_PER_CHUNK; j++) { if (base->number) { // check if this baseline will overflow - maybe_flush_msg(64); + maybe_flush_msg(MAX_PACKETENTITY_BYTES); MSG_WriteByte(svc_spawnbaseline); write_baseline(base); From 67bbe50a057b756ed8f71ddffa3806398b43865b Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 27 Aug 2023 13:58:56 +0300 Subject: [PATCH 31/58] Define U_SKIN32, etc masks. --- inc/common/protocol.h | 4 ++++ src/common/msg.c | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/inc/common/protocol.h b/inc/common/protocol.h index c67f17060..4b3f098b0 100644 --- a/inc/common/protocol.h +++ b/inc/common/protocol.h @@ -335,6 +335,10 @@ typedef enum { #define U_SOUND (1<<26) #define U_SOLID (1<<27) +#define U_SKIN32 (U_SKIN8 | U_SKIN16) // used for laser colors +#define U_EFFECTS32 (U_EFFECTS8 | U_EFFECTS16) +#define U_RENDERFX32 (U_RENDERFX8 | U_RENDERFX16) + // ============================================================== // a client with this number will never be included in MVD stream diff --git a/src/common/msg.c b/src/common/msg.c index 66ca95ac1..90a67c9e6 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -655,21 +655,21 @@ void MSG_WriteDeltaEntity(const entity_packed_t *from, else if (bits & U_FRAME16) MSG_WriteShort(to->frame); - if ((bits & (U_SKIN8 | U_SKIN16)) == (U_SKIN8 | U_SKIN16)) //used for laser colors + if ((bits & U_SKIN32) == U_SKIN32) MSG_WriteLong(to->skinnum); else if (bits & U_SKIN8) MSG_WriteByte(to->skinnum); else if (bits & U_SKIN16) MSG_WriteShort(to->skinnum); - if ((bits & (U_EFFECTS8 | U_EFFECTS16)) == (U_EFFECTS8 | U_EFFECTS16)) + if ((bits & U_EFFECTS32) == U_EFFECTS32) MSG_WriteLong(to->effects); else if (bits & U_EFFECTS8) MSG_WriteByte(to->effects); else if (bits & U_EFFECTS16) MSG_WriteShort(to->effects); - if ((bits & (U_RENDERFX8 | U_RENDERFX16)) == (U_RENDERFX8 | U_RENDERFX16)) + if ((bits & U_RENDERFX32) == U_RENDERFX32) MSG_WriteLong(to->renderfx); else if (bits & U_RENDERFX8) MSG_WriteByte(to->renderfx); @@ -1783,21 +1783,21 @@ void MSG_ParseDeltaEntity(const entity_state_t *from, if (bits & U_FRAME16) to->frame = MSG_ReadShort(); - if ((bits & (U_SKIN8 | U_SKIN16)) == (U_SKIN8 | U_SKIN16)) //used for laser colors + if ((bits & U_SKIN32) == U_SKIN32) to->skinnum = MSG_ReadLong(); else if (bits & U_SKIN8) to->skinnum = MSG_ReadByte(); else if (bits & U_SKIN16) to->skinnum = MSG_ReadWord(); - if ((bits & (U_EFFECTS8 | U_EFFECTS16)) == (U_EFFECTS8 | U_EFFECTS16)) + if ((bits & U_EFFECTS32) == U_EFFECTS32) to->effects = MSG_ReadLong(); else if (bits & U_EFFECTS8) to->effects = MSG_ReadByte(); else if (bits & U_EFFECTS16) to->effects = MSG_ReadWord(); - if ((bits & (U_RENDERFX8 | U_RENDERFX16)) == (U_RENDERFX8 | U_RENDERFX16)) + if ((bits & U_RENDERFX32) == U_RENDERFX32) to->renderfx = MSG_ReadLong(); else if (bits & U_RENDERFX8) to->renderfx = MSG_ReadByte(); From 93b600da25e19fb22f5d5766cb7e138529824fc8 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 2 Dec 2022 22:58:19 +0300 Subject: [PATCH 32/58] Force set entity number to prevent assertion failure. --- src/server/mvd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/mvd.c b/src/server/mvd.c index a49649468..123f3c576 100644 --- a/src/server/mvd.c +++ b/src/server/mvd.c @@ -583,8 +583,8 @@ static void build_gamestate(void) continue; } + ent->s.number = i; MSG_PackEntity(&mvd.entities[i], &ent->s, false); - mvd.entities[i].number = i; } } From 1b22d84c1abbfa1e530746ee972475ae77bf4ec9 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 19 Aug 2023 20:14:39 +0300 Subject: [PATCH 33/58] Simplify MSG_PackEntity(). Pack angles using ANGLE2SHORT() unconditionally. Simply downscaling packed value to a byte works. --- inc/common/msg.h | 2 +- src/client/demo.c | 12 ++++++------ src/client/gtv.c | 4 ++-- src/common/msg.c | 30 +++++++++++------------------- src/server/entities.c | 2 +- src/server/mvd.c | 4 ++-- src/server/mvd/client.c | 2 +- src/server/user.c | 2 +- 8 files changed, 25 insertions(+), 33 deletions(-) diff --git a/inc/common/msg.h b/inc/common/msg.h index 7055b2c65..f0646605b 100644 --- a/inc/common/msg.h +++ b/inc/common/msg.h @@ -108,7 +108,7 @@ int MSG_WriteDeltaUsercmd(const usercmd_t *from, const usercmd_t *cmd, int v int MSG_WriteDeltaUsercmd_Enhanced(const usercmd_t *from, const usercmd_t *cmd); #endif void MSG_WriteDir(const vec3_t vector); -void MSG_PackEntity(entity_packed_t *out, const entity_state_t *in, bool short_angles); +void MSG_PackEntity(entity_packed_t *out, const entity_state_t *in); void MSG_WriteDeltaEntity(const entity_packed_t *from, const entity_packed_t *to, msgEsFlags_t flags); void MSG_PackPlayer(player_packed_t *out, const player_state_t *in); void MSG_WriteDeltaPlayerstate_Default(const player_packed_t *from, const player_packed_t *to); diff --git a/src/client/demo.c b/src/client/demo.c index 4011e082f..800c7a0af 100644 --- a/src/client/demo.c +++ b/src/client/demo.c @@ -112,8 +112,8 @@ static void emit_packet_entities(server_frame_t *from, server_frame_t *to) // not changed at all. Note that players are always 'newentities', // this updates their old_origin always and prevents warping in case // of packet loss. - MSG_PackEntity(&oldpack, oldent, false); - MSG_PackEntity(&newpack, newent, false); + MSG_PackEntity(&oldpack, oldent); + MSG_PackEntity(&newpack, newent); MSG_WriteDeltaEntity(&oldpack, &newpack, newent->number <= cl.maxclients ? MSG_ES_NEWENTITY : 0); oldindex++; @@ -123,8 +123,8 @@ static void emit_packet_entities(server_frame_t *from, server_frame_t *to) if (newnum < oldnum) { // this is a new entity, send it from the baseline - MSG_PackEntity(&oldpack, &cl.baselines[newnum], false); - MSG_PackEntity(&newpack, newent, false); + MSG_PackEntity(&oldpack, &cl.baselines[newnum]); + MSG_PackEntity(&newpack, newent); MSG_WriteDeltaEntity(&oldpack, &newpack, MSG_ES_FORCE | MSG_ES_NEWENTITY); newindex++; continue; @@ -132,7 +132,7 @@ static void emit_packet_entities(server_frame_t *from, server_frame_t *to) if (newnum > oldnum) { // the old entity isn't present in the new message - MSG_PackEntity(&oldpack, oldent, false); + MSG_PackEntity(&oldpack, oldent); MSG_WriteDeltaEntity(&oldpack, NULL, MSG_ES_FORCE); oldindex++; continue; @@ -434,7 +434,7 @@ static void CL_Record_f(void) } MSG_WriteByte(svc_spawnbaseline); - MSG_PackEntity(&pack, ent, false); + MSG_PackEntity(&pack, ent); MSG_WriteDeltaEntity(NULL, &pack, MSG_ES_FORCE); } diff --git a/src/client/gtv.c b/src/client/gtv.c index fa8fc275f..be421d336 100644 --- a/src/client/gtv.c +++ b/src/client/gtv.c @@ -46,7 +46,7 @@ static void build_gamestate(void) continue; } - MSG_PackEntity(&cls.gtv.entities[i], &ent->current, false); + MSG_PackEntity(&cls.gtv.entities[i], &ent->current); } } @@ -155,7 +155,7 @@ void CL_GTV_EmitFrame(void) } // quantize - MSG_PackEntity(&newes, &ent->current, false); + MSG_PackEntity(&newes, &ent->current); MSG_WriteDeltaEntity(oldes, &newes, flags); diff --git a/src/common/msg.c b/src/common/msg.c index 90a67c9e6..f2bd466d0 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -437,7 +437,7 @@ void MSG_WriteDir(const vec3_t dir) MSG_WriteByte(best); } -void MSG_PackEntity(entity_packed_t *out, const entity_state_t *in, bool short_angles) +void MSG_PackEntity(entity_packed_t *out, const entity_state_t *in) { // allow 0 to accomodate empty baselines Q_assert(in->number >= 0 && in->number < MAX_EDICTS); @@ -445,17 +445,9 @@ void MSG_PackEntity(entity_packed_t *out, const entity_state_t *in, bool short_a out->origin[0] = COORD2SHORT(in->origin[0]); out->origin[1] = COORD2SHORT(in->origin[1]); out->origin[2] = COORD2SHORT(in->origin[2]); - if (short_angles) { - out->angles[0] = ANGLE2SHORT(in->angles[0]); - out->angles[1] = ANGLE2SHORT(in->angles[1]); - out->angles[2] = ANGLE2SHORT(in->angles[2]); - } else { - // pack angles8 akin to angles16 to make delta compression happy when - // precision suddenly changes between entity updates - out->angles[0] = ANGLE2BYTE(in->angles[0]) << 8; - out->angles[1] = ANGLE2BYTE(in->angles[1]) << 8; - out->angles[2] = ANGLE2BYTE(in->angles[2]) << 8; - } + out->angles[0] = ANGLE2SHORT(in->angles[0]); + out->angles[1] = ANGLE2SHORT(in->angles[1]); + out->angles[2] = ANGLE2SHORT(in->angles[2]); out->old_origin[0] = COORD2SHORT(in->old_origin[0]); out->old_origin[1] = COORD2SHORT(in->old_origin[1]); out->old_origin[2] = COORD2SHORT(in->old_origin[2]); @@ -522,11 +514,11 @@ void MSG_WriteDeltaEntity(const entity_packed_t *from, if (to->angles[2] != from->angles[2]) bits |= U_ANGLE3 | U_ANGLE16; } else { - if (to->angles[0] != from->angles[0]) + if ((to->angles[0] ^ from->angles[0]) & 0xff00) bits |= U_ANGLE1; - if (to->angles[1] != from->angles[1]) + if ((to->angles[1] ^ from->angles[1]) & 0xff00) bits |= U_ANGLE2; - if (to->angles[2] != from->angles[2]) + if ((to->angles[2] ^ from->angles[2]) & 0xff00) bits |= U_ANGLE3; } @@ -683,7 +675,7 @@ void MSG_WriteDeltaEntity(const entity_packed_t *from, if (bits & U_ORIGIN3) MSG_WriteShort(to->origin[2]); - if ((flags & MSG_ES_SHORTANGLES) && (bits & U_ANGLE16)) { + if (bits & U_ANGLE16) { if (bits & U_ANGLE1) MSG_WriteShort(to->angles[0]); if (bits & U_ANGLE2) @@ -692,11 +684,11 @@ void MSG_WriteDeltaEntity(const entity_packed_t *from, MSG_WriteShort(to->angles[2]); } else { if (bits & U_ANGLE1) - MSG_WriteByte(to->angles[0] >> 8); + MSG_WriteChar(to->angles[0] >> 8); if (bits & U_ANGLE2) - MSG_WriteByte(to->angles[1] >> 8); + MSG_WriteChar(to->angles[1] >> 8); if (bits & U_ANGLE3) - MSG_WriteByte(to->angles[2] >> 8); + MSG_WriteChar(to->angles[2] >> 8); } if (bits & U_OLDORIGIN) { diff --git a/src/server/entities.c b/src/server/entities.c index a3c0e0890..3ae9014ad 100644 --- a/src/server/entities.c +++ b/src/server/entities.c @@ -549,7 +549,7 @@ void SV_BuildClientFrame(client_t *client) // add it to the circular client_entities array state = &svs.entities[svs.next_entity % svs.num_entities]; - MSG_PackEntity(state, &es, Q2PRO_SHORTANGLES(client, e)); + MSG_PackEntity(state, &ent->s); #if USE_FPS // fix old entity origins for clients not running at diff --git a/src/server/mvd.c b/src/server/mvd.c index 123f3c576..6faa34291 100644 --- a/src/server/mvd.c +++ b/src/server/mvd.c @@ -584,7 +584,7 @@ static void build_gamestate(void) } ent->s.number = i; - MSG_PackEntity(&mvd.entities[i], &ent->s, false); + MSG_PackEntity(&mvd.entities[i], &ent->s); } } @@ -798,7 +798,7 @@ static void emit_frame(void) } // quantize - MSG_PackEntity(&newes, &ent->s, false); + MSG_PackEntity(&newes, &ent->s); MSG_WriteDeltaEntity(oldes, &newes, flags); diff --git a/src/server/mvd/client.c b/src/server/mvd/client.c index 6a5db9c2b..4ce3ac365 100644 --- a/src/server/mvd/client.c +++ b/src/server/mvd/client.c @@ -1826,7 +1826,7 @@ static void emit_base_frame(mvd_t *mvd) if (!(ent->svflags & SVF_MONSTER)) continue; // entity never seen ent->s.number = i; - MSG_PackEntity(&es, &ent->s, false); + MSG_PackEntity(&es, &ent->s); MSG_WriteDeltaEntity(NULL, &es, entity_flags(mvd, ent)); } MSG_WriteShort(0); diff --git a/src/server/user.c b/src/server/user.c index 2940ed4c4..ce3368f27 100644 --- a/src/server/user.c +++ b/src/server/user.c @@ -75,7 +75,7 @@ static void SV_CreateBaselines(void) } base = *chunk + (i & SV_BASELINES_MASK); - MSG_PackEntity(base, &ent->s, Q2PRO_SHORTANGLES(sv_client, i)); + MSG_PackEntity(base, &ent->s); #if USE_MVD_CLIENT if (sv.state == ss_broadcast) { From bc701e74de1c6445518c5676e452067a817f0da6 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 21 Sep 2022 02:17:57 +0300 Subject: [PATCH 34/58] Don't allow server message to underflow. --- src/client/parse.c | 61 ++++++++++++++-------------------------------- 1 file changed, 18 insertions(+), 43 deletions(-) diff --git a/src/client/parse.c b/src/client/parse.c index aebfa6549..1f174b60e 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -90,10 +90,6 @@ static void CL_ParsePacketEntities(server_frame_t *oldframe, Com_Error(ERR_DROP, "%s: bad number: %d", __func__, newnum); } - if (msg_read.readcount > msg_read.cursize) { - Com_Error(ERR_DROP, "%s: read past end of message", __func__); - } - if (!newnum) { break; } @@ -287,14 +283,10 @@ static void CL_ParseFrame(int extrabits) // read areabits length = MSG_ReadByte(); if (length) { - if (length < 0 || msg_read.readcount + length > msg_read.cursize) { - Com_Error(ERR_DROP, "%s: read past end of message", __func__); - } if (length > sizeof(frame.areabits)) { Com_Error(ERR_DROP, "%s: invalid areabits length", __func__); } - memcpy(frame.areabits, msg_read.data + msg_read.readcount, length); - msg_read.readcount += length; + memcpy(frame.areabits, MSG_ReadData(length), length); frame.areabytes = length; } else { frame.areabytes = 0; @@ -463,7 +455,7 @@ static void CL_ParseGamestate(void) { int index, bits; - while (msg_read.readcount < msg_read.cursize) { + while (1) { index = MSG_ReadShort(); if (index == MAX_CONFIGSTRINGS) { break; @@ -471,7 +463,7 @@ static void CL_ParseGamestate(void) CL_ParseConfigstring(index); } - while (msg_read.readcount < msg_read.cursize) { + while (1) { index = MSG_ParseEntityBits(&bits); if (!index) { break; @@ -807,8 +799,6 @@ static void CL_ParseStartSoundPacket(void) flags = MSG_ReadByte(); snd.index = MSG_ReadByte(); - if (snd.index == -1) - Com_Error(ERR_DROP, "%s: read past end of message", __func__); if (flags & SND_VOLUME) snd.volume = MSG_ReadByte() / 255.0f; @@ -1063,13 +1053,7 @@ static void CL_ParseDownload(int cmd) Com_Error(ERR_DROP, "%s: bad size: %d", __func__, size); } - if (msg_read.readcount + size > msg_read.cursize) { - Com_Error(ERR_DROP, "%s: read past end of message", __func__); - } - - data = msg_read.data + msg_read.readcount; - msg_read.readcount += size; - + data = MSG_ReadData(size); CL_HandleDownload(data, size, percent, decompressed_size); } @@ -1077,7 +1061,7 @@ static void CL_ParseZPacket(void) { #if USE_ZLIB sizebuf_t temp; - byte buffer[MAX_MSGLEN]; + byte buffer[MAX_MSGLEN], *data; int ret, inlen, outlen; if (msg_read.data != msg_read_buffer) { @@ -1086,10 +1070,7 @@ static void CL_ParseZPacket(void) inlen = MSG_ReadWord(); outlen = MSG_ReadWord(); - - if (inlen == -1 || outlen == -1 || msg_read.readcount + inlen > msg_read.cursize) { - Com_Error(ERR_DROP, "%s: read past end of message", __func__); - } + data = MSG_ReadData(inlen); if (outlen > MAX_MSGLEN) { Com_Error(ERR_DROP, "%s: invalid output length", __func__); @@ -1097,7 +1078,7 @@ static void CL_ParseZPacket(void) inflateReset(&cls.z); - cls.z.next_in = msg_read.data + msg_read.readcount; + cls.z.next_in = data; cls.z.avail_in = (uInt)inlen; cls.z.next_out = buffer; cls.z.avail_out = (uInt)outlen; @@ -1106,8 +1087,6 @@ static void CL_ParseZPacket(void) Com_Error(ERR_DROP, "%s: inflate() failed with error %d", __func__, ret); } - msg_read.readcount += inlen; - temp = msg_read; SZ_Init(&msg_read, buffer, outlen); msg_read.cursize = outlen; @@ -1181,21 +1160,19 @@ void CL_ParseServerMessage(void) } #endif + msg_read.allowunderflow = false; + // // parse the message // while (1) { - if (msg_read.readcount > msg_read.cursize) { - Com_Error(ERR_DROP, "%s: read past end of server message", __func__); - } - readcount = msg_read.readcount; - - if ((cmd = MSG_ReadByte()) == -1) { - SHOWNET(1, "%3zu:END OF MESSAGE\n", msg_read.readcount - 1); + if (readcount == msg_read.cursize) { + SHOWNET(1, "%3zu:END OF MESSAGE\n", readcount); break; } + cmd = MSG_ReadByte(); extrabits = cmd >> SVCMD_BITS; cmd &= SVCMD_MASK; @@ -1208,7 +1185,7 @@ void CL_ParseServerMessage(void) // other commands switch (cmd) { default: -badbyte: + badbyte: Com_Error(ERR_DROP, "%s: illegible server message: %d", __func__, cmd); break; @@ -1355,19 +1332,18 @@ void CL_SeekDemoMessage(void) } #endif + msg_read.allowunderflow = false; + // // parse the message // while (1) { - if (msg_read.readcount > msg_read.cursize) { - Com_Error(ERR_DROP, "%s: read past end of server message", __func__); - } - - if ((cmd = MSG_ReadByte()) == -1) { - SHOWNET(1, "%3zu:END OF MESSAGE\n", msg_read.readcount - 1); + if (msg_read.readcount == msg_read.cursize) { + SHOWNET(1, "%3zu:END OF MESSAGE\n", msg_read.readcount); break; } + cmd = MSG_ReadByte(); extrabits = cmd >> SVCMD_BITS; cmd &= SVCMD_MASK; @@ -1429,7 +1405,6 @@ void CL_SeekDemoMessage(void) case svc_layout: CL_ParseLayout(); break; - } } } From 863399ef5d78d1f495bfc7516c944cd2c76e6d3f Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 26 Aug 2023 16:14:16 +0300 Subject: [PATCH 35/58] Use MSG_ReadWord() where appropriate. --- src/client/demo.c | 6 +++--- src/client/parse.c | 14 +++++++------- src/common/msg.c | 2 +- src/server/mvd/parse.c | 10 +++++----- src/server/save.c | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/client/demo.c b/src/client/demo.c index 800c7a0af..0137eef26 100644 --- a/src/client/demo.c +++ b/src/client/demo.c @@ -1111,7 +1111,7 @@ demoInfo_t *CL_GetDemoInfo(const char *path, demoInfo_t *info) if (c != svc_configstring) { break; } - index = MSG_ReadShort(); + index = MSG_ReadWord(); if (index < 0 || index >= MAX_CONFIGSTRINGS) { goto fail; } @@ -1127,13 +1127,13 @@ demoInfo_t *CL_GetDemoInfo(const char *path, demoInfo_t *info) if (MSG_ReadLong() != PROTOCOL_VERSION_MVD) { goto fail; } - MSG_ReadShort(); + MSG_ReadWord(); MSG_ReadLong(); MSG_ReadString(NULL, 0); clientNum = MSG_ReadShort(); while (1) { - index = MSG_ReadShort(); + index = MSG_ReadWord(); if (index == MAX_CONFIGSTRINGS) { break; } diff --git a/src/client/parse.c b/src/client/parse.c index 1f174b60e..b9c098fd2 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -456,7 +456,7 @@ static void CL_ParseGamestate(void) int index, bits; while (1) { - index = MSG_ReadShort(); + index = MSG_ReadWord(); if (index == MAX_CONFIGSTRINGS) { break; } @@ -548,7 +548,7 @@ static void CL_ParseServerData(void) if (i) { Com_Error(ERR_DROP, "'Enhanced' R1Q2 servers are not supported"); } - i = MSG_ReadShort(); + i = MSG_ReadWord(); // for some reason, R1Q2 servers always report the highest protocol // version they support, while still using the lower version // client specified in the 'connect' packet. oh well... @@ -573,7 +573,7 @@ static void CL_ParseServerData(void) } cl.pmp.speedmult = 2; } else if (cls.serverProtocol == PROTOCOL_VERSION_Q2PRO) { - i = MSG_ReadShort(); + i = MSG_ReadWord(); if (!Q2PRO_SUPPORTED(i)) { Com_Error(ERR_DROP, "Q2PRO server reports unsupported protocol version %d.\n" @@ -782,7 +782,7 @@ static void CL_ParseMuzzleFlashPacket(int mask) { int entity, weapon; - entity = MSG_ReadShort(); + entity = MSG_ReadWord(); if (entity < 1 || entity >= MAX_EDICTS) Com_Error(ERR_DROP, "%s: bad entity", __func__); @@ -817,7 +817,7 @@ static void CL_ParseStartSoundPacket(void) if (flags & SND_ENT) { // entity relative - channel = MSG_ReadShort(); + channel = MSG_ReadWord(); entity = channel >> 3; if (entity < 0 || entity >= MAX_EDICTS) Com_Error(ERR_DROP, "%s: bad entity: %d", __func__, entity); @@ -1217,7 +1217,7 @@ void CL_ParseServerMessage(void) continue; case svc_configstring: - index = MSG_ReadShort(); + index = MSG_ReadWord(); CL_ParseConfigstring(index); break; @@ -1377,7 +1377,7 @@ void CL_SeekDemoMessage(void) break; case svc_configstring: - index = MSG_ReadShort(); + index = MSG_ReadWord(); CL_ParseConfigstring(index); break; diff --git a/src/common/msg.c b/src/common/msg.c index f2bd466d0..ddab4c8d2 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -1718,7 +1718,7 @@ int MSG_ParseEntityBits(int *bits) } if (total & U_NUMBER16) - number = MSG_ReadShort(); + number = MSG_ReadWord(); else number = MSG_ReadByte(); diff --git a/src/server/mvd/parse.c b/src/server/mvd/parse.c index 6a1a1372d..8cdf69498 100644 --- a/src/server/mvd/parse.c +++ b/src/server/mvd/parse.c @@ -290,7 +290,7 @@ static void MVD_UnicastString(mvd_t *mvd, bool reliable, mvd_player_t *player) data = msg_read.data + msg_read.readcount - 1; readcount = msg_read.readcount - 1; - index = MSG_ReadShort(); + index = MSG_ReadWord(); length = MSG_ReadString(string, sizeof(string)); if (index < 0 || index >= MAX_CONFIGSTRINGS) { @@ -495,7 +495,7 @@ static void MVD_ParseSound(mvd_t *mvd, int extrabits) offset = MSG_ReadByte(); // entity relative - sendchan = MSG_ReadShort(); + sendchan = MSG_ReadWord(); entnum = sendchan >> 3; if (entnum < 0 || entnum >= MAX_EDICTS) { MVD_Destroyf(mvd, "%s: bad entnum: %d", __func__, entnum); @@ -606,7 +606,7 @@ static void MVD_ParseConfigstring(mvd_t *mvd) size_t maxlen; char *s; - index = MSG_ReadShort(); + index = MSG_ReadWord(); if (index < 0 || index >= MAX_CONFIGSTRINGS) { MVD_Destroyf(mvd, "%s: bad index: %d", __func__, index); } @@ -935,7 +935,7 @@ static void MVD_ParseServerData(mvd_t *mvd, int extrabits) } // parse minor protocol version - protocol = MSG_ReadShort(); + protocol = MSG_ReadWord(); if (!MVD_SUPPORTED(protocol)) { MVD_Destroyf(mvd, "Unsupported MVD protocol version: %d.\n" "Current version is %d.\n", protocol, PROTOCOL_VERSION_MVD_CURRENT); @@ -955,7 +955,7 @@ static void MVD_ParseServerData(mvd_t *mvd, int extrabits) // parse configstrings while (1) { - index = MSG_ReadShort(); + index = MSG_ReadWord(); if (index == MAX_CONFIGSTRINGS) { break; } diff --git a/src/server/save.c b/src/server/save.c index 9f31d2d29..a76c3545d 100644 --- a/src/server/save.c +++ b/src/server/save.c @@ -410,7 +410,7 @@ static int read_level_file(void) // read all configstrings while (1) { - index = MSG_ReadShort(); + index = MSG_ReadWord(); if (index == MAX_CONFIGSTRINGS) break; From dc00fc5b5e4990d15b9f5db68f61be67de3b9baa Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sat, 1 Oct 2022 20:11:20 +0300 Subject: [PATCH 36/58] Always validate clientNum in CL_ParseServerData(). --- src/client/parse.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/client/parse.c b/src/client/parse.c index b9c098fd2..0f3882d41 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -628,12 +628,12 @@ static void CL_ParseServerData(void) Com_SetColor(COLOR_ALT); Com_Printf("%s\n", levelname); Com_SetColor(COLOR_NONE); + } - // make sure clientNum is in range - if (!VALIDATE_CLIENTNUM(cl.clientNum)) { - Com_WPrintf("Serverdata has invalid playernum %d\n", cl.clientNum); - cl.clientNum = -1; - } + // make sure clientNum is in range + if (!VALIDATE_CLIENTNUM(cl.clientNum)) { + Com_WPrintf("Serverdata has invalid playernum %d\n", cl.clientNum); + cl.clientNum = -1; } } From 405415a70160dad49823b1df309354ae5e942868 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Fri, 25 Nov 2022 19:48:35 +0300 Subject: [PATCH 37/58] Don't crash dedicated server if UDP port fails to open after startup. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously changing e.g. ‘net_ip’ to bad value would crash the server. Just print a warning and give server operator a chance to correct it. --- src/common/net/net.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/common/net/net.c b/src/common/net/net.c index abd8d3a5d..8c0ca7762 100644 --- a/src/common/net/net.c +++ b/src/common/net/net.c @@ -1204,12 +1204,10 @@ static void NET_OpenServer(void) return; } -#if USE_CLIENT - if (!dedicated->integer) { + if (saved_port || !dedicated->integer) { Com_WPrintf("Couldn't open server UDP port.\n"); return; } -#endif Com_Error(ERR_FATAL, "Couldn't open dedicated server UDP port"); } From bdfa23a76b1f2fd636bf8bab460c833352d26977 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 25 Dec 2022 17:47:21 +0300 Subject: [PATCH 38/58] Allow manual reconnect when downloading. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always fully disconnect and reconnect when user types ‘reconnect’ at console. --- src/client/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/main.c b/src/client/main.c index 053625829..76b3ef249 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -1046,7 +1046,7 @@ The server is changing levels */ static void CL_Reconnect_f(void) { - if (cls.state >= ca_precached) { + if (cls.state >= ca_precached || Cmd_From() != FROM_STUFFTEXT) { CL_Disconnect(ERR_RECONNECT); } From 2affa761ef2a6d8184862393f0f69dac760da849 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 25 Dec 2022 17:58:31 +0300 Subject: [PATCH 39/58] Prevent confusing message when reconnecting to loopback. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't print ‘can't reconnect to loopback’ if local server is running. Client will auto reconnect anyway. --- src/client/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/main.c b/src/client/main.c index 76b3ef249..7611e722d 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -1071,7 +1071,7 @@ static void CL_Reconnect_f(void) Com_Printf("No server to reconnect to.\n"); return; } - if (cls.serverAddress.type == NA_LOOPBACK) { + if (cls.serverAddress.type == NA_LOOPBACK && !sv_running->integer) { Com_Printf("Can not reconnect to loopback.\n"); return; } From f1720a7ef80cfa95487bfd87ad64080981dfb047 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 25 Dec 2022 18:25:35 +0300 Subject: [PATCH 40/58] =?UTF-8?q?Disallow=20=E2=80=98reconnect=E2=80=99=20?= =?UTF-8?q?when=20playing=20a=20demo.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/client/main.c b/src/client/main.c index 7611e722d..e0725a86e 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -1046,6 +1046,11 @@ The server is changing levels */ static void CL_Reconnect_f(void) { + if (cls.demo.playback) { + Com_Printf("No server to reconnect to.\n"); + return; + } + if (cls.state >= ca_precached || Cmd_From() != FROM_STUFFTEXT) { CL_Disconnect(ERR_RECONNECT); } @@ -1053,9 +1058,6 @@ static void CL_Reconnect_f(void) if (cls.state >= ca_connected) { cls.state = ca_connected; - if (cls.demo.playback) { - return; - } if (cls.download.file) { return; // if we are downloading, we don't change! } From 65405cb15cc21e013add632ee39681ea8bf12bfe Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 25 Dec 2022 19:08:57 +0300 Subject: [PATCH 41/58] =?UTF-8?q?Respect=20=E2=80=98cl=5Fprotocol=E2=80=99?= =?UTF-8?q?=20when=20reconnecting.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Requested in #260. --- src/client/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/client/main.c b/src/client/main.c index e0725a86e..c7c6e7df5 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -1080,6 +1080,11 @@ static void CL_Reconnect_f(void) Com_Printf("Reconnecting...\n"); + cls.serverProtocol = cl_protocol->integer; + if (!cls.serverProtocol) { + cls.serverProtocol = PROTOCOL_VERSION_Q2PRO; + } + cls.state = ca_challenging; cls.connect_time -= CONNECT_FAST; cls.connect_count = 0; From c02b26ef40e051f5f44cc6cae05e8f72c6653a37 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 25 Dec 2022 19:30:03 +0300 Subject: [PATCH 42/58] =?UTF-8?q?Remove=20ability=20to=20specify=20protoco?= =?UTF-8?q?l=20via=20=E2=80=98connect=E2=80=99=20second=20argument.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was undocumented and obscure feature. Just use ‘cl_protocol’. --- src/client/main.c | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/src/client/main.c b/src/client/main.c index c7c6e7df5..95c6c4273 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -483,12 +483,6 @@ static void CL_Connect_c(genctx_t *ctx, int argnum) if (argnum == 1) { CL_RecentIP_g(ctx); Com_Address_g(ctx); - } else if (argnum == 2) { - if (!ctx->partial[0] || (ctx->partial[0] == '3' && !ctx->partial[1])) { - Prompt_AddMatch(ctx, "34"); - Prompt_AddMatch(ctx, "35"); - Prompt_AddMatch(ctx, "36"); - } } } @@ -502,8 +496,6 @@ static void CL_Connect_f(void) { char *server, *p; netadr_t address; - int protocol; - int argc = Cmd_Argc(); if (fs_shareware->integer) { @@ -511,23 +503,14 @@ static void CL_Connect_f(void) return; } - if (argc < 2) { -usage: - Com_Printf("Usage: %s [34|35|36]\n", Cmd_Argv(0)); + if (Cmd_Argc() < 2) { + Com_Printf("Usage: %s \n", Cmd_Argv(0)); return; } - if (argc > 2) { - protocol = atoi(Cmd_Argv(2)); - if (protocol < PROTOCOL_VERSION_DEFAULT || - protocol > PROTOCOL_VERSION_Q2PRO) { - goto usage; - } - } else { - protocol = cl_protocol->integer; - if (!protocol) { - protocol = PROTOCOL_VERSION_Q2PRO; - } + if (Cmd_Argc() > 2) { + Com_Printf("Second argument to `%s' is now ignored. " + "Set protocol via `cl_protocol' variable.\n", Cmd_Argv(0)); } server = Cmd_Argv(1); @@ -556,7 +539,7 @@ static void CL_Connect_f(void) CL_Disconnect(ERR_RECONNECT); cls.serverAddress = address; - cls.serverProtocol = protocol; + cls.serverProtocol = cl_protocol->integer; cls.protocolVersion = 0; cls.passive = false; cls.state = ca_challenging; @@ -1081,10 +1064,6 @@ static void CL_Reconnect_f(void) Com_Printf("Reconnecting...\n"); cls.serverProtocol = cl_protocol->integer; - if (!cls.serverProtocol) { - cls.serverProtocol = PROTOCOL_VERSION_Q2PRO; - } - cls.state = ca_challenging; cls.connect_time -= CONNECT_FAST; cls.connect_count = 0; @@ -1303,6 +1282,10 @@ static void CL_ConnectionlessPacket(void) } } + if (!cls.serverProtocol) { + cls.serverProtocol = PROTOCOL_VERSION_Q2PRO; + } + // choose supported protocol switch (cls.serverProtocol) { case PROTOCOL_VERSION_Q2PRO: From 54bb23efe82642e43d1f0af69fe55b689a59a357 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 25 Dec 2022 20:26:58 +0300 Subject: [PATCH 43/58] Reset minor protocol version in CL_ParseServerData(). --- src/client/main.c | 1 - src/client/parse.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/main.c b/src/client/main.c index 95c6c4273..8de5e3042 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -540,7 +540,6 @@ static void CL_Connect_f(void) cls.serverAddress = address; cls.serverProtocol = cl_protocol->integer; - cls.protocolVersion = 0; cls.passive = false; cls.state = ca_challenging; cls.connect_time -= CONNECT_FAST; diff --git a/src/client/parse.c b/src/client/parse.c index 0f3882d41..59a55fbac 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -612,6 +612,8 @@ static void CL_ParseServerData(void) cl.pmp.speedmult = 2; cl.pmp.flyhack = true; // fly hack is unconditionally enabled cl.pmp.flyfriction = 4; + } else { + cls.protocolVersion = 0; } if (cinematic) { From 104538816ec844544ab689b02fbe2f46b2a8aa2d Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 25 Dec 2022 20:29:51 +0300 Subject: [PATCH 44/58] Fix passing minor protocol version to MSG_WriteDeltaUsercmd(). Pass non-zero value only when using R1Q2 protocol. --- src/client/input.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/client/input.c b/src/client/input.c index b589c7e86..d3761100f 100644 --- a/src/client/input.c +++ b/src/client/input.c @@ -872,6 +872,7 @@ static void CL_SendDefaultCmd(void) size_t cursize q_unused, checksumIndex; usercmd_t *cmd, *oldcmd; client_history_t *history; + int version; // archive this packet history = &cl.history[cls.netchan.outgoing_sequence & CMD_MASK]; @@ -895,9 +896,12 @@ static void CL_SendDefaultCmd(void) // save the position for a checksum byte checksumIndex = 0; + version = 0; if (cls.serverProtocol <= PROTOCOL_VERSION_DEFAULT) { checksumIndex = msg_write.cursize; SZ_GetSpace(&msg_write, 1); + } else if (cls.serverProtocol == PROTOCOL_VERSION_R1Q2) { + version = cls.protocolVersion; } // let the server know what the last frame we @@ -911,17 +915,17 @@ static void CL_SendDefaultCmd(void) // send this and the previous cmds in the message, so // if the last packet was dropped, it can be recovered cmd = &cl.cmds[(cl.cmdNumber - 2) & CMD_MASK]; - MSG_WriteDeltaUsercmd(NULL, cmd, cls.protocolVersion); + MSG_WriteDeltaUsercmd(NULL, cmd, version); MSG_WriteByte(cl.lightlevel); oldcmd = cmd; cmd = &cl.cmds[(cl.cmdNumber - 1) & CMD_MASK]; - MSG_WriteDeltaUsercmd(oldcmd, cmd, cls.protocolVersion); + MSG_WriteDeltaUsercmd(oldcmd, cmd, version); MSG_WriteByte(cl.lightlevel); oldcmd = cmd; cmd = &cl.cmds[cl.cmdNumber & CMD_MASK]; - MSG_WriteDeltaUsercmd(oldcmd, cmd, cls.protocolVersion); + MSG_WriteDeltaUsercmd(oldcmd, cmd, version); MSG_WriteByte(cl.lightlevel); if (cls.serverProtocol <= PROTOCOL_VERSION_DEFAULT) { From 9539330a4de30757d394754e07d64bc41da2cae2 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 26 Dec 2022 13:12:27 +0300 Subject: [PATCH 45/58] Set full read buffer maxsize. --- src/client/parse.c | 2 +- src/server/save.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/parse.c b/src/client/parse.c index 59a55fbac..cd4cddfe6 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -1090,7 +1090,7 @@ static void CL_ParseZPacket(void) } temp = msg_read; - SZ_Init(&msg_read, buffer, outlen); + SZ_Init(&msg_read, buffer, sizeof(buffer)); msg_read.cursize = outlen; CL_ParseServerMessage(); diff --git a/src/server/save.c b/src/server/save.c index a76c3545d..a19eca38e 100644 --- a/src/server/save.c +++ b/src/server/save.c @@ -235,7 +235,7 @@ static int read_binary_file(const char *name) if (FS_Read(msg_read_buffer, len, f) != len) goto fail; - SZ_Init(&msg_read, msg_read_buffer, len); + SZ_Init(&msg_read, msg_read_buffer, sizeof(msg_read_buffer)); msg_read.cursize = len; FS_CloseFile(f); From 2c61087028cc4e92f1a9f20ffc9ba1bceb6b0689 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 28 Dec 2022 15:50:45 +0300 Subject: [PATCH 46/58] Fix logging loopback packets. --- src/common/net/net.c | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/src/common/net/net.c b/src/common/net/net.c index 8c0ca7762..9cdf8b2a2 100644 --- a/src/common/net/net.c +++ b/src/common/net/net.c @@ -450,6 +450,8 @@ static void NET_LogPacket(const netadr_t *address, const char *prefix, FS_FPrintf(net_logFile, "\n"); } +#else +#define NET_LogPacket(adr, pre, data, len) (void)0 #endif //============================================================================= @@ -545,11 +547,8 @@ static void NET_GetLoopPackets(netsrc_t sock, void (*packet_cb)(void)) memcpy(msg_read_buffer, loopmsg->data, loopmsg->datalen); -#if USE_DEBUG - if (net_log_enable->integer > 1) { - NET_LogPacket(&net_from, "LP recv", loopmsg->data, loopmsg->datalen); - } -#endif + NET_LogPacket(&net_from, "LP recv", loopmsg->data, loopmsg->datalen); + if (sock == NS_CLIENT) { net_rate_rcvd += loopmsg->datalen; } @@ -579,11 +578,8 @@ static bool NET_SendLoopPacket(netsrc_t sock, const void *data, memcpy(msg->data, data, len); msg->datalen = len; -#if USE_DEBUG - if (net_log_enable->integer > 1) { - NET_LogPacket(to, "LP send", data, len); - } -#endif + NET_LogPacket(to, "LP send", data, len); + if (sock == NS_CLIENT) { net_rate_sent += len; } @@ -855,10 +851,7 @@ static void NET_GetUdpPackets(qsocket_t sock, void (*packet_cb)(void)) break; } -#if USE_DEBUG - if (net_log_enable->integer) - NET_LogPacket(&net_from, "UDP recv", msg_read_buffer, ret); -#endif + NET_LogPacket(&net_from, "UDP recv", msg_read_buffer, ret); net_rate_rcvd += ret; net_bytes_rcvd += ret; @@ -953,10 +946,7 @@ bool NET_SendPacket(netsrc_t sock, const void *data, Com_WPrintf("%s: short send to %s\n", __func__, NET_AdrToString(to)); -#if USE_DEBUG - if (net_log_enable->integer) - NET_LogPacket(to, "UDP send", data, ret); -#endif + NET_LogPacket(to, "UDP send", data, ret); net_rate_sent += ret; net_bytes_sent += ret; @@ -1658,11 +1648,9 @@ neterr_t NET_RunStream(netstream_t *s) e->canread = false; } else { FIFO_Commit(&s->recv, ret); -#if USE_DEBUG - if (net_log_enable->integer) { - NET_LogPacket(&s->address, "TCP recv", data, ret); - } -#endif + + NET_LogPacket(&s->address, "TCP recv", data, ret); + net_rate_rcvd += ret; net_bytes_rcvd += ret; @@ -1693,11 +1681,9 @@ neterr_t NET_RunStream(netstream_t *s) e->canwrite = false; } else { FIFO_Decommit(&s->send, ret); -#if USE_DEBUG - if (net_log_enable->integer) { - NET_LogPacket(&s->address, "TCP send", data, ret); - } -#endif + + NET_LogPacket(&s->address, "TCP send", data, ret); + net_rate_sent += ret; net_bytes_sent += ret; From c2795fad635a4d0702125c544f451768bb756296 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 1 Jan 2023 12:57:27 +0300 Subject: [PATCH 47/58] Provide better error message if server didn't set map. --- src/client/precache.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/client/precache.c b/src/client/precache.c index d50d4063d..4f0301600 100644 --- a/src/client/precache.c +++ b/src/client/precache.c @@ -224,14 +224,15 @@ Registers main BSP file and inline models */ void CL_RegisterBspModels(void) { - int ret; - char *name; - int i; + char *name = cl.configstrings[CS_MODELS + 1]; + int i, ret; - ret = BSP_Load(cl.configstrings[CS_MODELS + 1], &cl.bsp); + if (!name[0]) { + Com_Error(ERR_DROP, "%s: no map set", __func__); + } + ret = BSP_Load(name, &cl.bsp); if (cl.bsp == NULL) { - Com_Error(ERR_DROP, "Couldn't load %s: %s", - cl.configstrings[CS_MODELS + 1], BSP_ErrorString(ret)); + Com_Error(ERR_DROP, "Couldn't load %s: %s", name, BSP_ErrorString(ret)); } if (cl.bsp->checksum != atoi(cl.configstrings[CS_MAPCHECKSUM])) { From 6b0e5928370f8be26b0126c106c0d4c8f488bf72 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 1 Jan 2023 14:06:54 +0300 Subject: [PATCH 48/58] Disallow CL_Skins_f(), etc, until precached. --- src/client/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/main.c b/src/client/main.c index 8de5e3042..60154f934 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -1143,7 +1143,7 @@ static void CL_Skins_f(void) char *s; clientinfo_t *ci; - if (cls.state < ca_loading) { + if (cls.state < ca_precached) { Com_Printf("Must be in a level to load skins.\n"); return; } @@ -1170,7 +1170,7 @@ static void cl_noskins_changed(cvar_t *self) char *s; clientinfo_t *ci; - if (cls.state < ca_loading) { + if (cls.state < ca_precached) { return; } @@ -1185,7 +1185,7 @@ static void cl_noskins_changed(cvar_t *self) static void cl_vwep_changed(cvar_t *self) { - if (cls.state < ca_loading) { + if (cls.state < ca_precached) { return; } @@ -1198,7 +1198,7 @@ static void CL_Name_g(genctx_t *ctx) int i; char buffer[MAX_CLIENT_NAME]; - if (cls.state < ca_loading) { + if (cls.state < ca_precached) { return; } From b3d9021cb89affca334fb9bd69d128eca2a147e3 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 4 Jan 2023 00:08:20 +0300 Subject: [PATCH 49/58] Sort file lists. --- src/client/client.h | 4 ++-- src/common/common.c | 2 +- src/server/server.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/client.h b/src/client/client.h index 04d23f017..2f1f00d6a 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -28,19 +28,19 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/cvar.h" #include "common/field.h" #include "common/files.h" -#include "common/pmove.h" #include "common/math.h" #include "common/msg.h" #include "common/net/chan.h" #include "common/net/net.h" +#include "common/pmove.h" #include "common/prompt.h" #include "common/protocol.h" #include "common/sizebuf.h" #include "common/zone.h" -#include "system/system.h" #include "refresh/refresh.h" #include "server/server.h" +#include "system/system.h" #include "client/client.h" #include "client/input.h" diff --git a/src/common/common.c b/src/common/common.c index ab20f15a7..ca109d8a7 100644 --- a/src/common/common.c +++ b/src/common/common.c @@ -36,8 +36,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/math.h" #include "common/mdfour.h" #include "common/msg.h" -#include "common/net/net.h" #include "common/net/chan.h" +#include "common/net/net.h" #include "common/pmove.h" #include "common/prompt.h" #include "common/protocol.h" diff --git a/src/server/server.h b/src/server/server.h index 7936d7fd7..3e535bcb7 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -29,8 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/error.h" #include "common/files.h" #include "common/msg.h" -#include "common/net/net.h" #include "common/net/chan.h" +#include "common/net/net.h" #include "common/pmove.h" #include "common/prompt.h" #include "common/protocol.h" From 39d85ecf104bb6c57d5c63dd6ca88523ec012325 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 17 Jan 2023 16:00:23 +0300 Subject: [PATCH 50/58] More strictly validate server/client commands. Only allow extrabits on few specific commands and only when protocol version allows it. Prevents possible covert information exchange. --- src/client/parse.c | 7 +++++++ src/server/user.c | 20 +++++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/client/parse.c b/src/client/parse.c index cd4cddfe6..89ba58591 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -1175,6 +1175,9 @@ void CL_ParseServerMessage(void) } cmd = MSG_ReadByte(); + if (cmd & ~SVCMD_MASK && (cls.serverProtocol < PROTOCOL_VERSION_R1Q2 || (cmd & SVCMD_MASK) != svc_frame)) + goto badbyte; + extrabits = cmd >> SVCMD_BITS; cmd &= SVCMD_MASK; @@ -1346,6 +1349,9 @@ void CL_SeekDemoMessage(void) } cmd = MSG_ReadByte(); + if (cmd & ~SVCMD_MASK && (cls.serverProtocol < PROTOCOL_VERSION_R1Q2 || (cmd & SVCMD_MASK) != svc_frame)) + goto badbyte; + extrabits = cmd >> SVCMD_BITS; cmd &= SVCMD_MASK; @@ -1358,6 +1364,7 @@ void CL_SeekDemoMessage(void) // other commands switch (cmd) { default: + badbyte: Com_Error(ERR_DROP, "%s: illegible server message: %d", __func__, cmd); break; diff --git a/src/server/user.c b/src/server/user.c index ce3368f27..ecbc47280 100644 --- a/src/server/user.c +++ b/src/server/user.c @@ -1511,7 +1511,16 @@ void SV_ExecuteClientMessage(client_t *client) if (c == -1) break; - switch (c & SVCMD_MASK) { + if (client->protocol == PROTOCOL_VERSION_Q2PRO) { + switch (c & SVCMD_MASK) { + case clc_move_nodelta: + case clc_move_batched: + SV_NewClientExecuteMove(c); + goto nextcmd; + } + } + + switch (c) { default: badbyte: SV_DropClient(client, "unknown command byte"); @@ -1539,14 +1548,6 @@ void SV_ExecuteClientMessage(client_t *client) SV_ParseClientSetting(); break; - case clc_move_nodelta: - case clc_move_batched: - if (client->protocol != PROTOCOL_VERSION_Q2PRO) - goto badbyte; - - SV_NewClientExecuteMove(c); - break; - case clc_userinfo_delta: if (client->protocol != PROTOCOL_VERSION_Q2PRO) goto badbyte; @@ -1555,6 +1556,7 @@ void SV_ExecuteClientMessage(client_t *client) break; } +nextcmd: if (client->state <= cs_zombie) break; // disconnect command } From 4c406f725865627d8b6623ae03fce6a6f8485ef8 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 24 Jan 2023 12:23:23 +0300 Subject: [PATCH 51/58] Add MSG_WriteLong64/MSG_ReadLong64. --- inc/common/msg.h | 2 ++ src/common/msg.c | 27 +++++++++++++++++++++++++++ src/server/save.c | 16 +++++----------- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/inc/common/msg.h b/inc/common/msg.h index f0646605b..5bd04e5cf 100644 --- a/inc/common/msg.h +++ b/inc/common/msg.h @@ -98,6 +98,7 @@ void MSG_WriteChar(int c); void MSG_WriteByte(int c); void MSG_WriteShort(int c); void MSG_WriteLong(int c); +void MSG_WriteLong64(int64_t c); void MSG_WriteString(const char *s); void MSG_WritePos(const vec3_t pos); void MSG_WriteAngle(float f); @@ -133,6 +134,7 @@ int MSG_ReadByte(void); int MSG_ReadShort(void); int MSG_ReadWord(void); int MSG_ReadLong(void); +int64_t MSG_ReadLong64(void); size_t MSG_ReadString(char *dest, size_t size); size_t MSG_ReadStringLine(char *dest, size_t size); #if USE_CLIENT diff --git a/src/common/msg.c b/src/common/msg.c index ddab4c8d2..f820687d3 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -147,6 +147,19 @@ void MSG_WriteLong(int c) WL32(buf, c); } +/* +============= +MSG_WriteLong64 +============= +*/ +void MSG_WriteLong64(int64_t c) +{ + byte *buf; + + buf = SZ_GetSpace(&msg_write, 8); + WL64(buf, c); +} + /* ============= MSG_WriteString @@ -1407,6 +1420,20 @@ int MSG_ReadLong(void) return c; } +int64_t MSG_ReadLong64(void) +{ + byte *buf = MSG_ReadData(8); + int64_t c; + + if (!buf) { + c = -1; + } else { + c = RL64(buf); + } + + return c; +} + size_t MSG_ReadString(char *dest, size_t size) { int c; diff --git a/src/server/save.c b/src/server/save.c index a19eca38e..b6f1257ee 100644 --- a/src/server/save.c +++ b/src/server/save.c @@ -36,17 +36,13 @@ static int write_server_file(bool autosave) char name[MAX_OSPATH]; cvar_t *var; int ret; - uint64_t timestamp; // write magic MSG_WriteLong(SAVE_MAGIC1); MSG_WriteLong(SAVE_VERSION); - timestamp = (uint64_t)time(NULL); - // write the comment field - MSG_WriteLong(timestamp & 0xffffffff); - MSG_WriteLong(timestamp >> 32); + MSG_WriteLong64(time(NULL)); MSG_WriteByte(autosave); MSG_WriteString(sv.configstrings[CS_NAME]); @@ -250,7 +246,7 @@ char *SV_GetSaveInfo(const char *dir) { char name[MAX_QPATH], date[MAX_QPATH]; size_t len; - uint64_t timestamp; + int64_t timestamp; int autosave, year; time_t t; struct tm *tm; @@ -268,8 +264,7 @@ char *SV_GetSaveInfo(const char *dir) return NULL; // read the comment field - timestamp = (uint64_t)MSG_ReadLong(); - timestamp |= (uint64_t)MSG_ReadLong() << 32; + timestamp = MSG_ReadLong64(); autosave = MSG_ReadByte(); MSG_ReadString(name, sizeof(name)); @@ -282,7 +277,7 @@ char *SV_GetSaveInfo(const char *dir) year = tm ? tm->tm_year : -1; // format savegame date - t = (time_t)timestamp; + t = timestamp; len = 0; if ((tm = localtime(&t)) != NULL) { if (tm->tm_year == year) @@ -324,8 +319,7 @@ static int read_server_file(void) memset(&cmd, 0, sizeof(cmd)); // read the comment field - MSG_ReadLong(); - MSG_ReadLong(); + MSG_ReadLong64(); if (MSG_ReadByte()) cmd.loadgame = 2; // autosave else From 0db8e3007dd6d061e7ab81d3946937fa0b3635a7 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 24 Jan 2023 12:25:39 +0300 Subject: [PATCH 52/58] Use WL16() for writing packet headers. --- src/client/gtv.c | 10 +++------- src/server/main.c | 1 - src/server/mvd.c | 11 ++++------- src/server/mvd/client.c | 4 +--- src/server/send.c | 6 ++---- src/server/server.h | 1 + 6 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/client/gtv.c b/src/client/gtv.c index be421d336..32f9c88f2 100644 --- a/src/client/gtv.c +++ b/src/client/gtv.c @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., // #include "client.h" +#include "common/intreadwrite.h" #include "server/mvd/protocol.h" static byte gtv_recv_buffer[MAX_GTC_MSGLEN]; @@ -200,10 +201,8 @@ static void write_stream(void *data, size_t len) static void write_message(gtv_serverop_t op) { byte header[3]; - size_t len = msg_write.cursize + 1; - header[0] = len & 255; - header[1] = (len >> 8) & 255; + WL16(header, msg_write.cursize + 1); header[2] = op; write_stream(header, sizeof(header)); @@ -274,7 +273,6 @@ void CL_GTV_Suspend(void) void CL_GTV_Transmit(void) { byte header[3]; - size_t total; if (cls.gtv.state != ca_active) return; @@ -294,9 +292,7 @@ void CL_GTV_Transmit(void) return; // build message header - total = cls.gtv.message.cursize + 1; - header[0] = total & 255; - header[1] = (total >> 8) & 255; + WL16(header, cls.gtv.message.cursize + 1); header[2] = GTS_STREAM_DATA; // send frame to client diff --git a/src/server/main.c b/src/server/main.c index 4b694f6b8..fcc4c35f1 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -18,7 +18,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "client/input.h" -#include "common/intreadwrite.h" pmoveParams_t sv_pmp; diff --git a/src/server/mvd.c b/src/server/mvd.c index 6faa34291..21493be0f 100644 --- a/src/server/mvd.c +++ b/src/server/mvd.c @@ -1057,9 +1057,8 @@ void SV_MvdEndFrame(void) } // build message header - total = mvd.message.cursize + msg_write.cursize + mvd.datagram.cursize + 1; - header[0] = total & 255; - header[1] = (total >> 8) & 255; + total = mvd.message.cursize + msg_write.cursize + mvd.datagram.cursize; + WL16(header, total + 1); header[2] = GTS_STREAM_DATA; // send frame to clients @@ -1078,7 +1077,7 @@ void SV_MvdEndFrame(void) // write frame to demofile if (mvd.recording) { - rec_frame(total - 1); + rec_frame(total); } // clear frame @@ -1416,10 +1415,8 @@ static void write_stream(gtv_client_t *client, void *data, size_t len) static void write_message(gtv_client_t *client, gtv_serverop_t op) { byte header[3]; - size_t len = msg_write.cursize + 1; - header[0] = len & 255; - header[1] = (len >> 8) & 255; + WL16(header, msg_write.cursize + 1); header[2] = op; write_stream(client, header, sizeof(header)); diff --git a/src/server/mvd/client.c b/src/server/mvd/client.c index 4ce3ac365..10e8a415e 100644 --- a/src/server/mvd/client.c +++ b/src/server/mvd/client.c @@ -816,10 +816,8 @@ static void write_stream(gtv_t *gtv, void *data, size_t len) static void write_message(gtv_t *gtv, gtv_clientop_t op) { byte header[3]; - size_t len = msg_write.cursize + 1; - header[0] = len & 255; - header[1] = (len >> 8) & 255; + WL16(header, msg_write.cursize + 1); header[2] = op; write_stream(gtv, header, sizeof(header)); diff --git a/src/server/send.c b/src/server/send.c index aca5ee25b..2f593f904 100644 --- a/src/server/send.c +++ b/src/server/send.c @@ -369,10 +369,8 @@ static int compress_message(client_t *client) // write the packet header hdr = svs.z_buffer; hdr[0] = svc_zpacket; - hdr[1] = len & 255; - hdr[2] = (len >> 8) & 255; - hdr[3] = msg_write.cursize & 255; - hdr[4] = (msg_write.cursize >> 8) & 255; + WL16(&hdr[1], len); + WL16(&hdr[3], msg_write.cursize); return len + ZPACKET_HEADER; } diff --git a/src/server/server.h b/src/server/server.h index 3e535bcb7..9e603c137 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/cvar.h" #include "common/error.h" #include "common/files.h" +#include "common/intreadwrite.h" #include "common/msg.h" #include "common/net/chan.h" #include "common/net/net.h" From e019b6ba481bfe85193c6908e7f60b94f0113dc0 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 26 Jul 2023 13:47:58 +0300 Subject: [PATCH 53/58] Add more asserts. --- src/server/send.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server/send.c b/src/server/send.c index 2f593f904..7be7ed450 100644 --- a/src/server/send.c +++ b/src/server/send.c @@ -34,6 +34,7 @@ void SV_FlushRedirect(int redirected, char *outputbuf, size_t len) byte buffer[MAX_PACKETLEN_DEFAULT]; if (redirected == RD_PACKET) { + Q_assert(len <= sizeof(buffer) - 10); memcpy(buffer, "\xff\xff\xff\xffprint\n", 10); memcpy(buffer + 10, outputbuf, len); NET_SendPacket(NS_SERVER, buffer, len + 10, &net_from); @@ -728,6 +729,7 @@ static void write_datagram_old(client_t *client) maxsize -= msg->cursize; } } + Q_assert(maxsize <= client->netchan.maxpacketlen); // send over all the relevant entity_state_t // and the player_state_t From 067ef44fd3e41c581914f1dbe5f222d8fa619b73 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 3 Aug 2023 13:47:21 +0300 Subject: [PATCH 54/58] =?UTF-8?q?Disallow=20client=20=E2=80=98rate?= =?UTF-8?q?=E2=80=99=20less=20than=201500=20bytes/sec.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Default limit of 100 is just too low. Misconfigured clients using rate 100 can take minutes to enter a server, raise minimum rate to 1500 to prevent this. --- doc/server.md | 9 +++++++++ src/server/main.c | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/doc/server.md b/doc/server.md index 6a9af4a35..ff0785b05 100644 --- a/doc/server.md +++ b/doc/server.md @@ -163,6 +163,15 @@ When enabled, do not enforce any rate limits on clients whose IP is from private address space (`127.x.x.x`, `10.x.x.x`, `192.168.x.x`, `172.16.x.x`). Default value is 0 (disabled). +#### `sv_min_rate`` +Server clamps minimum value of `rate` userinfo parameter to this value. +Default value is 1500 bytes/sec. This parameter can't be greater than +`sv_max_rate` value or less than 1500 bytes/sec. + +#### `sv_max_rate`` +Server clamps maximum value of `rate` userinfo parameter to this value. +Default value is 15000 bytes/sec. + #### `sv_calcpings_method` Specifies the way client pings are calculated. Default ping calculation algorithm is very client frame and packet rate dependent, and may give diff --git a/src/server/main.c b/src/server/main.c index fcc4c35f1..64bffc09d 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -2087,7 +2087,7 @@ static void init_rate_limits(void) static void sv_rate_changed(cvar_t *self) { - Cvar_ClampInteger(sv_min_rate, 100, Cvar_ClampInteger(sv_max_rate, 1000, INT_MAX)); + Cvar_ClampInteger(sv_min_rate, 1500, Cvar_ClampInteger(sv_max_rate, 1500, INT_MAX)); } void sv_sec_timeout_changed(cvar_t *self) @@ -2205,7 +2205,7 @@ void SV_Init(void) sv_pad_packets = Cvar_Get("sv_pad_packets", "0", 0); #endif sv_lan_force_rate = Cvar_Get("sv_lan_force_rate", "0", CVAR_LATCH); - sv_min_rate = Cvar_Get("sv_min_rate", "100", CVAR_LATCH); + sv_min_rate = Cvar_Get("sv_min_rate", "1500", CVAR_LATCH); sv_max_rate = Cvar_Get("sv_max_rate", "15000", CVAR_LATCH); sv_max_rate->changed = sv_min_rate->changed = sv_rate_changed; sv_max_rate->changed(sv_max_rate); From 8ae0b668022f2f512818f957dcd06fa5a404cfc5 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Wed, 9 Aug 2023 20:02:51 +0300 Subject: [PATCH 55/58] =?UTF-8?q?Indent=20some=20=E2=80=98cl=5Fshownet?= =?UTF-8?q?=E2=80=99=20prints.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/parse.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/client/parse.c b/src/client/parse.c index 89ba58591..8e58c5d7a 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -306,6 +306,7 @@ static void CL_ParseFrame(int extrabits) MSG_ParseDeltaPlayerstate_Enhanced(from, &frame.ps, bits, extraflags); #if USE_DEBUG if (cl_shownet->integer > 2 && (bits || extraflags)) { + Com_LPrintf(PRINT_DEVELOPER, " "); MSG_ShowDeltaPlayerstateBits_Enhanced(bits, extraflags); Com_LPrintf(PRINT_DEVELOPER, "\n"); } @@ -331,6 +332,7 @@ static void CL_ParseFrame(int extrabits) MSG_ParseDeltaPlayerstate_Default(from, &frame.ps, bits); #if USE_DEBUG if (cl_shownet->integer > 2 && bits) { + Com_LPrintf(PRINT_DEVELOPER, " "); MSG_ShowDeltaPlayerstateBits_Default(bits); Com_LPrintf(PRINT_DEVELOPER, "\n"); } @@ -442,6 +444,7 @@ static void CL_ParseBaseline(int index, int bits) } #if USE_DEBUG if (cl_shownet->integer > 2) { + Com_LPrintf(PRINT_DEVELOPER, " baseline: %i ", index); MSG_ShowDeltaEntityBits(bits); Com_LPrintf(PRINT_DEVELOPER, "\n"); } From ce62ba40e7e39309b947dab470de3bf5f6de8839 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 10 Aug 2023 12:33:43 +0300 Subject: [PATCH 56/58] Shorten code. --- src/server/user.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/server/user.c b/src/server/user.c index ecbc47280..3fdcaac21 100644 --- a/src/server/user.c +++ b/src/server/user.c @@ -50,10 +50,9 @@ static void SV_CreateBaselines(void) // clear baselines from previous level for (i = 0; i < SV_BASELINES_CHUNKS; i++) { base = sv_client->baselines[i]; - if (!base) { - continue; + if (base) { + memset(base, 0, sizeof(*base) * SV_BASELINES_PER_CHUNK); } - memset(base, 0, sizeof(*base) * SV_BASELINES_PER_CHUNK); } for (i = 1; i < sv_client->pool->num_edicts; i++) { From 53d88120bead3ed79b79e99b06c10e5e6d7c8d99 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Tue, 12 Sep 2023 16:08:58 +0300 Subject: [PATCH 57/58] Pre-quantize SV_StartSound() parameters. --- src/server/game.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/server/game.c b/src/server/game.c index 4a8153bbd..e810cba74 100644 --- a/src/server/game.c +++ b/src/server/game.c @@ -501,7 +501,7 @@ static void SV_StartSound(const vec3_t origin, edict_t *edict, int channel, int soundindex, float volume, float attenuation, float timeofs) { - int i, ent, flags, sendchan; + int i, ent, vol, att, ofs, flags, sendchan; vec3_t origin_v; client_t *client; byte mask[VIS_MAX_BYTES]; @@ -520,7 +520,9 @@ static void SV_StartSound(const vec3_t origin, edict_t *edict, if (soundindex < 0 || soundindex >= MAX_SOUNDS) Com_Error(ERR_DROP, "%s: soundindex = %d", __func__, soundindex); - attenuation = min(attenuation, 255.0f / 64); + vol = volume * 255; + att = min(attenuation * 64, 255); // need to clip due to check above + ofs = timeofs * 1000; ent = NUM_FOR_EDICT(edict); @@ -528,11 +530,11 @@ static void SV_StartSound(const vec3_t origin, edict_t *edict, // always send the entity number for channel overrides flags = SND_ENT; - if (volume != DEFAULT_SOUND_PACKET_VOLUME) + if (vol != 255) flags |= SND_VOLUME; - if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION) + if (att != 64) flags |= SND_ATTENUATION; - if (timeofs) + if (ofs) flags |= SND_OFFSET; // send origin for invisible entities @@ -556,11 +558,11 @@ static void SV_StartSound(const vec3_t origin, edict_t *edict, MSG_WriteByte(soundindex); if (flags & SND_VOLUME) - MSG_WriteByte(volume * 255); + MSG_WriteByte(vol); if (flags & SND_ATTENUATION) - MSG_WriteByte(attenuation * 64); + MSG_WriteByte(att); if (flags & SND_OFFSET) - MSG_WriteByte(timeofs * 1000); + MSG_WriteByte(ofs); MSG_WriteShort(sendchan); MSG_WritePos(origin); @@ -636,9 +638,9 @@ static void SV_StartSound(const vec3_t origin, edict_t *edict, msg->cursize = 0; msg->flags = flags; msg->index = soundindex; - msg->volume = volume * 255; - msg->attenuation = attenuation * 64; - msg->timeofs = timeofs * 1000; + msg->volume = vol; + msg->attenuation = att; + msg->timeofs = ofs; msg->sendchan = sendchan; for (i = 0; i < 3; i++) { msg->pos[i] = COORD2SHORT(origin[i]); @@ -652,8 +654,7 @@ static void SV_StartSound(const vec3_t origin, edict_t *edict, // clear multicast buffer SZ_Clear(&msg_write); - SV_MvdStartSound(ent, channel, flags, soundindex, - volume * 255, attenuation * 64, timeofs * 1000); + SV_MvdStartSound(ent, channel, flags, soundindex, vol, att, ofs); } static void PF_StartSound(edict_t *entity, int channel, From 8b123241614eb5dc534bf012078988d35938a79a Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Mon, 9 Oct 2023 19:59:52 +0300 Subject: [PATCH 58/58] Use U_SKIN32, etc in more places. --- src/common/msg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/msg.c b/src/common/msg.c index f820687d3..39f1074bb 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -546,7 +546,7 @@ void MSG_WriteDeltaEntity(const entity_packed_t *from, if (to->skinnum != from->skinnum) { if (to->skinnum & mask) - bits |= U_SKIN8 | U_SKIN16; + bits |= U_SKIN32; else if (to->skinnum & 0x0000ff00) bits |= U_SKIN16; else @@ -562,7 +562,7 @@ void MSG_WriteDeltaEntity(const entity_packed_t *from, if (to->effects != from->effects) { if (to->effects & mask) - bits |= U_EFFECTS8 | U_EFFECTS16; + bits |= U_EFFECTS32; else if (to->effects & 0x0000ff00) bits |= U_EFFECTS16; else @@ -571,7 +571,7 @@ void MSG_WriteDeltaEntity(const entity_packed_t *from, if (to->renderfx != from->renderfx) { if (to->renderfx & mask) - bits |= U_RENDERFX8 | U_RENDERFX16; + bits |= U_RENDERFX32; else if (to->renderfx & 0x0000ff00) bits |= U_RENDERFX16; else