engine: client: support GoldSrc signon and refactor parsing delta entities for current and legacy protocols

This commit is contained in:
Alibek Omarov 2024-10-07 21:44:25 +03:00
parent 609680b328
commit 6ae09d2866
7 changed files with 129 additions and 142 deletions

View file

@ -669,9 +669,9 @@ FRAME PARSING
=========================================================================
*/
static qboolean CL_ParseEntityNumFromPacket( sizebuf_t *msg, int *newnum )
static qboolean CL_ParseEntityNumFromPacket( sizebuf_t *msg, int *newnum, connprotocol_t proto )
{
if( cls.legacymode )
if( proto == PROTO_LEGACY )
{
*newnum = MSG_ReadWord( msg );
if( *newnum == 0 )
@ -694,7 +694,7 @@ CL_FlushEntityPacket
Read and ignore whole entity packet.
=================
*/
static void CL_FlushEntityPacket( sizebuf_t *msg )
static void CL_FlushEntityPacket( sizebuf_t *msg, connprotocol_t proto )
{
int newnum;
entity_state_t from, to;
@ -707,7 +707,7 @@ static void CL_FlushEntityPacket( sizebuf_t *msg )
// read it all, but ignore it
while( 1 )
{
if( !CL_ParseEntityNumFromPacket( msg, &newnum ))
if( !CL_ParseEntityNumFromPacket( msg, &newnum, proto ))
break; // done
if( MSG_CheckOverflow( msg ))
@ -717,6 +717,47 @@ static void CL_FlushEntityPacket( sizebuf_t *msg )
}
}
qboolean CL_ValidateDeltaPacket( uint oldpacket, frame_t *oldframe )
{
int subtracted = ( cls.netchan.incoming_sequence - oldpacket ) & 0xFF;
if( subtracted == 0 )
{
Con_NPrintf( 2, "^3Warning:^1 update too old\n^7\n" );
return false;
}
if( subtracted >= CL_UPDATE_MASK )
{
// we can't use this, it is too old
Con_NPrintf( 2, "^3Warning:^1 delta frame is too old^7\n" );
return false;
}
if(( cls.next_client_entities - oldframe->first_entity ) > ( cls.num_client_entities - NUM_PACKET_ENTITIES ))
{
Con_NPrintf( 2, "^3Warning:^1 delta frame is too old^7\n" );
return false;
}
return true;
}
int CL_UpdateOldEntNum( int oldindex, frame_t *oldframe, entity_state_t **oldent )
{
if( !oldframe )
{
*oldent = NULL;
return MAX_ENTNUMBER;
}
if( oldindex >= oldframe->num_entities )
return MAX_ENTNUMBER;
*oldent = &cls.packet_entities[(oldframe->first_entity + oldindex) % cls.num_client_entities];
return (*oldent)->number;
}
/*
=================
CL_DeltaEntity
@ -787,7 +828,7 @@ An svc_packetentities has just been parsed, deal with the
rest of the data stream.
==================
*/
int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta, connprotocol_t proto )
{
frame_t *newframe, *oldframe;
int oldindex, newnum, oldnum;
@ -803,7 +844,7 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
CL_WriteDemoJumpTime();
// sentinel count. save it for debug checking
if( cls.legacymode )
if( proto == PROTO_LEGACY )
count = MSG_ReadWord( msg );
else count = MSG_ReadUBitLong( msg, MAX_VISIBLE_PACKET_BITS ) + 1;
@ -817,32 +858,12 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
if( delta )
{
int subtracted;
oldpacket = MSG_ReadByte( msg );
subtracted = ( cls.netchan.incoming_sequence - oldpacket ) & 0xFF;
if( subtracted == 0 )
{
Con_NPrintf( 2, "^3Warning:^1 update too old\n^7\n" );
CL_FlushEntityPacket( msg );
return playerbytes;
}
if( subtracted >= CL_UPDATE_MASK )
{
// we can't use this, it is too old
Con_NPrintf( 2, "^3Warning:^1 delta frame is too old^7\n" );
CL_FlushEntityPacket( msg );
return playerbytes;
}
uint oldpacket = MSG_ReadByte( msg );
oldframe = &cl.frames[oldpacket & CL_UPDATE_MASK];
if(( cls.next_client_entities - oldframe->first_entity ) > ( cls.num_client_entities - NUM_PACKET_ENTITIES ))
if( !CL_ValidateDeltaPacket( oldpacket, oldframe ))
{
Con_NPrintf( 2, "^3Warning:^1 delta frame is too old^7\n" );
CL_FlushEntityPacket( msg );
CL_FlushEntityPacket( msg, proto );
return playerbytes;
}
}
@ -860,27 +881,11 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
oldent = NULL;
oldindex = 0;
if( !oldframe )
{
oldnum = MAX_ENTNUMBER;
}
else
{
if( oldindex >= oldframe->num_entities )
{
oldnum = MAX_ENTNUMBER;
}
else
{
oldent = &cls.packet_entities[(oldframe->first_entity+oldindex) % cls.num_client_entities];
oldnum = oldent->number;
}
}
oldnum = CL_UpdateOldEntNum( oldindex, oldframe, &oldent );
while( 1 )
{
if( !CL_ParseEntityNumFromPacket( msg, &newnum ))
if( !CL_ParseEntityNumFromPacket( msg, &newnum, proto ))
break; // done
if( MSG_CheckOverflow( msg ))
@ -892,17 +897,7 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
{
// one or more entities from the old packet are unchanged
CL_DeltaEntity( msg, newframe, oldnum, oldent, false );
oldindex++;
if( oldindex >= oldframe->num_entities )
{
oldnum = MAX_ENTNUMBER;
}
else
{
oldent = &cls.packet_entities[(oldframe->first_entity+oldindex) % cls.num_client_entities];
oldnum = oldent->number;
}
oldnum = CL_UpdateOldEntNum( ++oldindex, oldframe, &oldent );
}
if( oldnum == newnum )
@ -911,17 +906,7 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
bufStart = MSG_GetNumBytesRead( msg );
CL_DeltaEntity( msg, newframe, newnum, oldent, true );
if( player ) playerbytes += MSG_GetNumBytesRead( msg ) - bufStart;
oldindex++;
if( oldindex >= oldframe->num_entities )
{
oldnum = MAX_ENTNUMBER;
}
else
{
oldent = &cls.packet_entities[(oldframe->first_entity+oldindex) % cls.num_client_entities];
oldnum = oldent->number;
}
oldnum = CL_UpdateOldEntNum( ++oldindex, oldframe, &oldent );
continue;
}
@ -940,17 +925,7 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
{
// one or more entities from the old packet are unchanged
CL_DeltaEntity( msg, newframe, oldnum, oldent, false );
oldindex++;
if( oldindex >= oldframe->num_entities )
{
oldnum = MAX_ENTNUMBER;
}
else
{
oldent = &cls.packet_entities[(oldframe->first_entity+oldindex) % cls.num_client_entities];
oldnum = oldent->number;
}
oldnum = CL_UpdateOldEntNum( ++oldindex, oldframe, &oldent );
}
if( newframe->num_entities != count && newframe->num_entities != 0 )
@ -972,7 +947,7 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
cls.signon = SIGNONS;
// Clear loading plaque.
CL_SignonReply ();
CL_SignonReply( proto );
}
return playerbytes;

View file

@ -223,7 +223,7 @@ CL_SignonReply
An svc_signonnum has been received, perform a client side setup
=====================
*/
void CL_SignonReply( void )
void CL_SignonReply( connprotocol_t proto )
{
// g-cont. my favorite message :-)
Con_Reportf( "%s: %i\n", __func__, cls.signon );
@ -231,7 +231,7 @@ void CL_SignonReply( void )
switch( cls.signon )
{
case 1:
CL_ServerCommand( true, "begin" );
CL_ServerCommand( true, proto == PROTO_GOLDSRC ? "sendents" : "begin" );
if( host_developer.value >= DEV_EXTENDED )
Mem_PrintStats();
break;
@ -699,7 +699,13 @@ void CL_WriteUsercmd( sizebuf_t *msg, int from, int to )
t = &cl.commands[to].cmd;
// write it into the buffer
MSG_WriteDeltaUsercmd( msg, f, t );
if( cls.legacymode == PROTO_GOLDSRC )
{
MSG_StartBitWriting( msg );
Delta_WriteGSFields( msg, DT_USERCMD_T, f, t, 0.0f );
MSG_EndBitWriting( msg );
}
else MSG_WriteDeltaUsercmd( msg, f, t );
}
/*
@ -798,6 +804,9 @@ static void CL_WritePacket( void )
// begin a client move command
MSG_BeginClientCmd( &buf, clc_move );
if( cls.legacymode == PROTO_GOLDSRC )
MSG_WriteByte( &buf, 0 );
// save the position for a checksum byte
key = MSG_GetRealBytesWritten( &buf );
MSG_WriteByte( &buf, 0 );
@ -835,8 +844,16 @@ static void CL_WritePacket( void )
// calculate a checksum over the move commands
size = MSG_GetRealBytesWritten( &buf ) - key - 1;
if( cls.legacymode == PROTO_GOLDSRC )
{
size = Q_min( size, 255 );
buf.pData[key - 1] = size;
}
buf.pData[key] = CRC32_BlockSequence( buf.pData + key + 1, size, cls.netchan.outgoing_sequence );
if( cls.legacymode == PROTO_GOLDSRC )
COM_Munge( buf.pData + key + 1, size, cls.netchan.outgoing_sequence );
// message we are constructing.
i = cls.netchan.outgoing_sequence & CL_UPDATE_MASK;
@ -1054,7 +1071,31 @@ static void CL_SendConnectPacket( void )
Info_SetValueForKey( protinfo, "a", Q_buildarch(), sizeof( protinfo ) );
}
if( cls.legacymode )
if( cls.legacymode == PROTO_GOLDSRC )
{
byte send_buf[MAX_PRINT_MSG];
byte steam_cert[512];
sizebuf_t send;
protinfo[0] = 0;
memset( steam_cert, 0, sizeof( steam_cert ));
Info_SetValueForKey( protinfo, "prot", "3", sizeof( protinfo )); // steam auth type
Info_SetValueForKey( protinfo, "raw", "steam", sizeof( protinfo ));
Info_SetValueForKey( protinfo, "cdkey", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", sizeof( protinfo ));
Info_SetValueForStarKey( cls.userinfo, "*hltv", "0", sizeof( cls.userinfo ));
MSG_Init( &send, "GoldSrcConnect", send_buf, sizeof( send_buf ));
MSG_WriteLong( &send, NET_HEADER_OUTOFBANDPACKET );
MSG_WriteStringf( &send, "connect %i %i \"%s\" \"%s\"\n",
PROTOCOL_GOLDSRC_VERSION, cls.challenge, protinfo, cls.userinfo );
MSG_WriteBytes( &send, steam_cert, sizeof( steam_cert ));
NET_SendPacket( NS_CLIENT, MSG_GetNumBytesWritten( &send ), MSG_GetData( &send ), adr );
}
else if( cls.legacymode == PROTO_LEGACY )
{
// set related userinfo keys
if( cl_dlmax.value >= 40000 || cl_dlmax.value < 100 )
@ -1280,15 +1321,16 @@ static void CL_Connect_f( void )
{
const char *s = Cmd_Argv( 2 );
if( !Q_strcmp( s, "current" ) || !Q_strcmp( s, "49" ))
if( !Q_stricmp( s, "current" ) || !Q_strcmp( s, "49" ))
proto = PROTO_CURRENT;
else if( !Q_strcmp( s, "legacy" ) || !Q_strcmp( s, "48" ))
else if( !Q_stricmp( s, "legacy" ) || !Q_strcmp( s, "48" ))
proto = PROTO_LEGACY;
else if( !Q_stricmp( s, "goldsrc" ) || !Q_strcmp( s, "gs" ))
proto = PROTO_GOLDSRC;
else
{
// quake protocol only used for demos
// goldsrc protocol is not supported yet
Con_Printf( "Unknown protocol. Supported are: current, legacy\n" );
Con_Printf( "Unknown protocol. Supported are: 49 (current), 48 (legacy), gs (goldsrc)\n" );
return;
}
}
@ -2055,7 +2097,7 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
Con_Reportf( "%s: %s : %s\n", __func__, NET_AdrToString( from ), c );
// server connection
if( !Q_strcmp( c, "client_connect" ))
if( !Q_strcmp( c, "client_connect" ) || !Q_strcmp( c, S2C_CONNECTION ))
{
if( !CL_IsFromConnectingServer( from ))
return;
@ -2177,7 +2219,7 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
// ping from somewhere
Netchan_OutOfBandPrint( NS_CLIENT, from, "ack" );
}
else if( !Q_strcmp( c, "challenge" ))
else if( !Q_strcmp( c, "challenge" ) || !Q_strcmp( c, S2C_CHALLENGE ))
{
// this message only used during connection
// it doesn't make sense after client_connect
@ -2214,7 +2256,7 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
// dropped the connection but it is still getting packets from us
CL_Disconnect_f();
}
else if( !Q_strcmp( c, "errormsg" ))
else if( !Q_strcmp( c, "errormsg" ) || c[0] == S2C_REJECT || c[0] == S2C_REJECT_BADPASSWORD )
{
char formatted_msg[MAX_VA_STRING];
@ -2222,6 +2264,8 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
return;
args = MSG_ReadString( msg );
if( c[0] == S2C_REJECT || c[0] == S2C_REJECT_BADPASSWORD )
args++; // skip one byte
Q_snprintf( formatted_msg, sizeof( formatted_msg ), "^3Server message^7\n%s", args );
@ -2386,6 +2430,8 @@ static void CL_ReadNetMessage( void )
parsefn = CL_ParseQuakeMessage;
break;
case PROTO_GOLDSRC:
parsefn = CL_ParseGoldSrcServerMessage;
break;
default:
ASSERT( 0 );
return;

View file

@ -216,7 +216,7 @@ CL_ParseSignon
==================
*/
void CL_ParseSignon( sizebuf_t *msg )
void CL_ParseSignon( sizebuf_t *msg, connprotocol_t proto )
{
int i = MSG_ReadByte( msg );
@ -228,7 +228,7 @@ void CL_ParseSignon( sizebuf_t *msg )
}
cls.signon = i;
CL_SignonReply();
CL_SignonReply( proto );
}
/*
@ -2480,7 +2480,7 @@ void CL_ParseServerMessage( sizebuf_t *msg )
cl.paused = ( MSG_ReadOneBit( msg ) != 0 );
break;
case svc_signonnum:
CL_ParseSignon( msg );
CL_ParseSignon( msg, PROTO_CURRENT );
break;
case svc_centerprint:
CL_CenterPrint( MSG_ReadString( msg ), 0.25f );
@ -2523,12 +2523,12 @@ void CL_ParseServerMessage( sizebuf_t *msg )
CL_RegisterUserMessage( msg );
break;
case svc_packetentities:
playerbytes = CL_ParsePacketEntities( msg, false );
playerbytes = CL_ParsePacketEntities( msg, false, PROTO_CURRENT );
cl.frames[cl.parsecountmod].graphdata.players += playerbytes;
cl.frames[cl.parsecountmod].graphdata.entities += MSG_GetNumBytesRead( msg ) - bufStart - playerbytes;
break;
case svc_deltapacketentities:
playerbytes = CL_ParsePacketEntities( msg, true );
playerbytes = CL_ParsePacketEntities( msg, true, PROTO_CURRENT );
cl.frames[cl.parsecountmod].graphdata.players += playerbytes;
cl.frames[cl.parsecountmod].graphdata.entities += MSG_GetNumBytesRead( msg ) - bufStart - playerbytes;
break;

View file

@ -492,7 +492,7 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg )
cl.paused = ( MSG_ReadOneBit( msg ) != 0 );
break;
case svc_signonnum:
CL_ParseSignon( msg );
CL_ParseSignon( msg, PROTO_LEGACY );
break;
case svc_centerprint:
CL_CenterPrint( MSG_ReadString( msg ), 0.25f );
@ -538,12 +538,12 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg )
CL_RegisterUserMessage( msg );
break;
case svc_packetentities:
playerbytes = CL_ParsePacketEntities( msg, false );
playerbytes = CL_ParsePacketEntities( msg, false, PROTO_LEGACY );
cl.frames[cl.parsecountmod].graphdata.players += playerbytes;
cl.frames[cl.parsecountmod].graphdata.entities += MSG_GetNumBytesRead( msg ) - bufStart - playerbytes;
break;
case svc_deltapacketentities:
playerbytes = CL_ParsePacketEntities( msg, true );
playerbytes = CL_ParsePacketEntities( msg, true, PROTO_LEGACY );
cl.frames[cl.parsecountmod].graphdata.players += playerbytes;
cl.frames[cl.parsecountmod].graphdata.entities += MSG_GetNumBytesRead( msg ) - bufStart - playerbytes;
break;

View file

@ -382,7 +382,7 @@ static int CL_ParsePacketEntitiesGS( sizebuf_t *msg, qboolean delta )
cls.signon = SIGNONS;
// Clear loading plaque.
CL_SignonReply ();
CL_SignonReply( PROTO_GOLDSRC );
}
return playerbytes;
@ -516,29 +516,12 @@ CL_ParseGoldSrcServerMessage
dispatch messages
=====================
*/
void CL_ParseGoldSrcServerMessage( sizebuf_t *msg, qboolean normal_message )
void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
{
size_t bufStart, playerbytes;
int cmd, param1, param2;
const char *s;
cls.starting_count = MSG_GetNumBytesRead( msg ); // updates each frame
CL_Parse_Debug( true ); // begin parsing
if( normal_message )
{
// assume no entity/player update this packet
if( cls.state == ca_active )
{
cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].valid = false;
cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].choked = false;
}
else
{
CL_ResetFrame( &cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK] );
}
}
// parse the message
while( 1 )
{
@ -658,7 +641,7 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg, qboolean normal_message )
cl.paused = ( MSG_ReadOneBit( msg ) != 0 );
break;
case svc_signonnum:
CL_ParseSignon( msg );
CL_ParseSignon( msg, PROTO_GOLDSRC );
break;
case svc_centerprint:
CL_CenterPrint( MSG_ReadString( msg ), 0.25f );
@ -771,21 +754,4 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg, qboolean normal_message )
break;
}
}
cl.frames[cl.parsecountmod].graphdata.msgbytes += MSG_GetNumBytesRead( msg ) - cls.starting_count;
CL_Parse_Debug( false ); // done
// we don't know if it is ok to save a demo message until
// after we have parsed the frame
if( !cls.demoplayback )
{
if( cls.demorecording && !cls.demowaiting )
{
CL_WriteDemoMessage( false, cls.starting_count, msg );
}
else if( cls.state != ca_active )
{
CL_WriteDemoMessage( true, cls.starting_count, msg );
}
}
}

View file

@ -431,7 +431,7 @@ static void CL_ParseQuakeEntityData( sizebuf_t *msg, int bits )
cls.signon = SIGNONS;
// Clear loading plaque.
CL_SignonReply ();
CL_SignonReply( PROTO_QUAKE );
}
// alloc next slot to store update

View file

@ -754,7 +754,7 @@ qboolean CL_PrecacheResources( void );
void CL_SetupOverviewParams( void );
void CL_UpdateFrameLerp( void );
int CL_IsDevOverviewMode( void );
void CL_SignonReply( void );
void CL_SignonReply( connprotocol_t proto );
void CL_ClearState( void );
//
@ -871,7 +871,7 @@ void CL_UpdateUserPings( sizebuf_t *msg );
void CL_ParseParticles( sizebuf_t *msg );
void CL_ParseRestoreSoundPacket( sizebuf_t *msg );
void CL_ParseBaseline( sizebuf_t *msg, connprotocol_t proto );
void CL_ParseSignon( sizebuf_t *msg );
void CL_ParseSignon( sizebuf_t *msg, connprotocol_t proto );
void CL_ParseRestore( sizebuf_t *msg );
void CL_ParseStaticDecal( sizebuf_t *msg );
void CL_ParseAddAngle( sizebuf_t *msg );
@ -912,7 +912,7 @@ void CL_LegacyPrecache_f( void );
//
// cl_parse_gs.c
//
void CL_ParseGoldSrcServerMessage( sizebuf_t *msg, qboolean normal_message );
void CL_ParseGoldSrcServerMessage( sizebuf_t *msg );
//
// cl_scrn.c
@ -980,7 +980,7 @@ struct channel_s;
struct rawchan_s;
qboolean CL_ValidateDeltaPacket( uint oldpacket, frame_t *oldframe );
int CL_UpdateOldEntNum( int oldindex, frame_t *oldframe, entity_state_t **oldent );
int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta );
int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta, connprotocol_t proto );
qboolean CL_AddVisibleEntity( cl_entity_t *ent, int entityType );
void CL_ResetLatchedVars( cl_entity_t *ent, qboolean full_reset );
qboolean CL_GetEntitySpatialization( struct channel_s *ch );