From cc1ab60c7b21d6dec88f8fae9123639224223ec7 Mon Sep 17 00:00:00 2001 From: VReaperV Date: Tue, 17 Mar 2026 14:18:48 +0300 Subject: [PATCH] Fix remaining network message overflows --- src/engine/client/cl_main.cpp | 18 ++++++++++++ src/engine/client/cl_parse.cpp | 30 ++++++++++++------- src/engine/qcommon/msg.cpp | 14 +++++++-- src/engine/qcommon/qcommon.h | 4 +-- src/engine/server/sv_client.cpp | 13 +++++++- src/engine/server/sv_snapshot.cpp | 49 +++++++++++++++++++++++++++++-- 6 files changed, 110 insertions(+), 18 deletions(-) diff --git a/src/engine/client/cl_main.cpp b/src/engine/client/cl_main.cpp index f80b735470..f94efe1f07 100644 --- a/src/engine/client/cl_main.cpp +++ b/src/engine/client/cl_main.cpp @@ -420,6 +420,24 @@ void CL_Record(std::string demo_name) MSG_WriteByte( &buf, svc_configstring ); MSG_WriteShort( &buf, i ); MSG_WriteBigString( &buf, cl.gameState[i].c_str() ); + + if ( MAX_MSGLEN - buf.cursize < 128 ) { + // We have too much configstring data to put it all into one msg_t, so split it here + MSG_WriteByte( &buf, svc_partial ); + + int len = LittleLong( clc.serverMessageSequence - 1 ); + FS_Write( &len, 4, clc.demofile ); + + len = LittleLong( buf.cursize ); + FS_Write( &len, 4, clc.demofile ); + FS_Write( buf.data, buf.cursize, clc.demofile ); + + MSG_Init( &buf, bufData, sizeof( bufData ) ); + MSG_Bitstream( &buf ); + MSG_WriteLong( &buf, clc.reliableSequence ); + MSG_WriteByte( &buf, svc_gamestate ); + MSG_WriteLong( &buf, clc.serverCommandSequence ); + } } // baselines diff --git a/src/engine/client/cl_parse.cpp b/src/engine/client/cl_parse.cpp index cf91714f19..1331f8ea5c 100644 --- a/src/engine/client/cl_parse.cpp +++ b/src/engine/client/cl_parse.cpp @@ -67,14 +67,18 @@ MESSAGE PARSING // TODO(kangz) if we can make sure that the baseline entities have the correct entity // number, then we could grab the entity number from old directly, simplifying code a bit. -void CL_DeltaEntity( msg_t *msg, clSnapshot_t *snapshot, int entityNum, const entityState_t &oldEntity) +bool CL_DeltaEntity( msg_t *msg, clSnapshot_t *snapshot, int entityNum, const entityState_t &oldEntity) { entityState_t entity; - MSG_ReadDeltaEntity(msg, &oldEntity, &entity, entityNum); + if ( MSG_ReadDeltaEntity( msg, &oldEntity, &entity, entityNum ) ) { + return true; + } if (entity.number != MAX_GENTITIES - 1) { snapshot->entities.push_back(entity); } + + return false; } /* @@ -136,7 +140,9 @@ void CL_ParsePacketEntities( msg_t *msg, const clSnapshot_t *oldSnapshot, clSnap // (2) there is an entry for an entity in the old snapshot, apply the delta if (oldEntityNum == newEntityNum) { - CL_DeltaEntity(msg, newSnapshot, newEntityNum, oldEntities[oldIndex]); + if ( CL_DeltaEntity( msg, newSnapshot, newEntityNum, oldEntities[oldIndex] ) ) { + break; + } oldIndex ++; if (oldIndex >= oldEntities.size()) { @@ -149,7 +155,9 @@ void CL_ParsePacketEntities( msg_t *msg, const clSnapshot_t *oldSnapshot, clSnap // from the baseline ASSERT_GT(oldEntityNum, newEntityNum); - CL_DeltaEntity(msg, newSnapshot, newEntityNum, cl.entityBaselines[newEntityNum]); + if ( CL_DeltaEntity( msg, newSnapshot, newEntityNum, cl.entityBaselines[newEntityNum] ) ) { + break; + } } } @@ -421,7 +429,11 @@ void CL_ParseGamestate( msg_t *msg ) break; } - if ( cmd == svc_configstring ) + if ( cmd == svc_partial ) { + cl.reading = true; + break; + } + else if ( cmd == svc_configstring ) { i = MSG_ReadShort( msg ); @@ -448,11 +460,6 @@ void CL_ParseGamestate( msg_t *msg ) cl.reading = false; } - else if ( cmd == svc_gamestatePartial ) - { - cl.reading = true; - break; - } else { Sys::Drop( "CL_ParseGamestate: bad command byte" ); @@ -588,6 +595,9 @@ void CL_ParseServerMessage( msg_t *msg ) case svc_download: CL_ParseDownload( msg ); break; + + case svc_partial: + break; } } } diff --git a/src/engine/qcommon/msg.cpp b/src/engine/qcommon/msg.cpp index ed72b56b15..09c2c9ac79 100644 --- a/src/engine/qcommon/msg.cpp +++ b/src/engine/qcommon/msg.cpp @@ -919,6 +919,7 @@ void MSG_WriteDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to, b MSG_WriteBits( msg, from->number, GENTITYNUM_BITS ); MSG_WriteBits( msg, 1, 1 ); + MSG_WriteBits( msg, 0, 1 ); return; } @@ -1061,7 +1062,7 @@ Can go from either a baseline or a previous packet_entity */ extern cvar_t *cl_shownet; -void MSG_ReadDeltaEntity( msg_t *msg, const entityState_t *from, entityState_t *to, int number ) +bool MSG_ReadDeltaEntity( msg_t *msg, const entityState_t *from, entityState_t *to, int number ) { int i, lc; int numFields; @@ -1088,6 +1089,11 @@ void MSG_ReadDeltaEntity( msg_t *msg, const entityState_t *from, entityState_t * // check for a remove if ( MSG_ReadBits( msg, 1 ) == 1 ) { + // partial snapshot; this is not an actual entity, the next entity had to be put into a different msg + if ( MSG_ReadBits( msg, 1 ) == 1 ) { + return true; + } + *to = {}; to->number = MAX_GENTITIES - 1; @@ -1096,7 +1102,7 @@ void MSG_ReadDeltaEntity( msg_t *msg, const entityState_t *from, entityState_t * Log::Notice( "%3i: #%-3i remove", msg->readcount, number ); } - return; + return false; } // check for no delta @@ -1104,7 +1110,7 @@ void MSG_ReadDeltaEntity( msg_t *msg, const entityState_t *from, entityState_t * { *to = *from; to->number = number; - return; + return false; } numFields = ARRAY_LEN( entityStateFields ); @@ -1216,6 +1222,8 @@ void MSG_ReadDeltaEntity( msg_t *msg, const entityState_t *from, entityState_t * Log::Notice( " (%i bits)", endBit - startBit ); } + + return false; } /* diff --git a/src/engine/qcommon/qcommon.h b/src/engine/qcommon/qcommon.h index c3d56f5157..4d692c81cf 100644 --- a/src/engine/qcommon/qcommon.h +++ b/src/engine/qcommon/qcommon.h @@ -93,7 +93,7 @@ void MSG_WriteDeltaUsercmd( msg_t *msg, usercmd_t *from, usercmd_t *to ); void MSG_ReadDeltaUsercmd( msg_t *msg, usercmd_t *from, usercmd_t *to ); void MSG_WriteDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to, bool force ); -void MSG_ReadDeltaEntity( msg_t *msg, const entityState_t *from, entityState_t *to, int number ); +bool MSG_ReadDeltaEntity( msg_t *msg, const entityState_t *from, entityState_t *to, int number ); void MSG_InitNetcodeTables(NetcodeTable playerStateTable, int playerStateSize); void MSG_WriteDeltaPlayerstate( msg_t *msg, const OpaquePlayerState *from, const OpaquePlayerState *to ); @@ -248,12 +248,12 @@ enum svc_ops_e svc_bad, svc_nop, svc_gamestate, - svc_gamestatePartial, svc_configstring, // [short] [string] only in gamestate messages svc_baseline, // only in gamestate messages svc_serverCommand, // [string] to be executed by client game module svc_download, // [short] size [size bytes] svc_snapshot, + svc_partial, svc_EOF }; diff --git a/src/engine/server/sv_client.cpp b/src/engine/server/sv_client.cpp index 3b6f336754..48132b0a52 100644 --- a/src/engine/server/sv_client.cpp +++ b/src/engine/server/sv_client.cpp @@ -368,6 +368,17 @@ void SV_SendClientGameState( client_t *client ) MSG_WriteByte( &msg, svc_configstring ); MSG_WriteShort( &msg, start ); MSG_WriteBigString( &msg, sv.configstrings[ start ] ); + + if ( MAX_MSGLEN - msg.cursize < 128 ) { + // We have too much configstring data to put it all into one msg_t, so split it here + MSG_WriteByte( &msg, svc_partial ); + SV_SendMessageToClient( &msg, client ); + + MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) ); + MSG_WriteLong( &msg, client->lastClientCommand ); + MSG_WriteByte( &msg, svc_gamestate ); + MSG_WriteLong( &msg, client->reliableSequence ); + } } } @@ -388,7 +399,7 @@ void SV_SendClientGameState( client_t *client ) if ( MAX_MSGLEN - msg.cursize < 128 ) { // We have too many entities to put them all into one msg_t, so split it here - MSG_WriteByte( &msg, svc_gamestatePartial ); + MSG_WriteByte( &msg, svc_partial ); SV_SendMessageToClient( &msg, client ); MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) ); diff --git a/src/engine/server/sv_snapshot.cpp b/src/engine/server/sv_snapshot.cpp index a68baad5e1..08398aa8f2 100644 --- a/src/engine/server/sv_snapshot.cpp +++ b/src/engine/server/sv_snapshot.cpp @@ -68,7 +68,8 @@ SV_EmitPacketEntities Writes a delta update of an entityState_t list to the message. ============= */ -static void SV_EmitPacketEntities( const clientSnapshot_t *from, clientSnapshot_t *to, msg_t *msg ) +static void SV_EmitPacketEntities( const clientSnapshot_t *from, clientSnapshot_t *to, msg_t *msg, + client_t* client, int lastframe, int snapFlags ) { entityState_t *oldent, *newent; int oldindex, newindex; @@ -97,6 +98,42 @@ static void SV_EmitPacketEntities( const clientSnapshot_t *from, clientSnapshot_ while ( newindex < to->num_entities || oldindex < from_num_entities ) { + + if ( MAX_MSGLEN - msg->cursize < 128 ) { + MSG_WriteBits( msg, 0, GENTITYNUM_BITS ); + MSG_WriteBits( msg, 1, 1 ); + MSG_WriteBits( msg, 1, 1 ); + + SV_SendMessageToClient( msg, client ); + + MSG_Init( msg, msg->data, msg->maxsize ); + MSG_WriteByte( msg, svc_snapshot ); + + MSG_WriteLong( msg, sv.time ); + + // what we are delta'ing from + MSG_WriteByte( msg, lastframe ); + + snapFlags = svs.snapFlagServerBit; + + MSG_WriteByte( msg, snapFlags ); + + // send over the areabits + MSG_WriteByte( msg, to->areabytes ); + MSG_WriteData( msg, to->areabits, to->areabytes ); + + { + // delta encode the playerstate + if ( from ) { + MSG_WriteDeltaPlayerstate( msg, &from->ps, &to->ps ); + } else { + MSG_WriteDeltaPlayerstate( msg, nullptr, &to->ps ); + } + } + + MSG_WriteShort( msg, to->num_entities - newindex ); + } + if ( newindex >= to->num_entities ) { newnum = MAX_GENTITIES; @@ -236,7 +273,7 @@ static void SV_WriteSnapshotToClient( client_t *client, msg_t *msg ) } // delta encode the entities - SV_EmitPacketEntities( oldframe, frame, msg ); + SV_EmitPacketEntities( oldframe, frame, msg, client, lastframe, snapFlags ); // padding for rate debugging if ( sv_padPackets.Get() ) @@ -265,6 +302,14 @@ void SV_UpdateServerCommandsToClient( client_t *client, msg_t *msg ) MSG_WriteByte( msg, svc_serverCommand ); MSG_WriteLong( msg, i ); MSG_WriteString( msg, client->reliableCommands[ i & ( MAX_RELIABLE_COMMANDS - 1 ) ] ); + + if ( MAX_MSGLEN - msg->cursize < 128 ) { + MSG_WriteByte( msg, svc_partial ); + SV_SendMessageToClient( msg, client ); + + MSG_Init( msg, msg->data, msg->maxsize ); + MSG_WriteLong( msg, client->lastClientCommand ); + } } client->reliableSent = client->reliableSequence;