Skip to content

Commit

Permalink
server: do not issue new serverId at map restart
Browse files Browse the repository at this point in the history
  • Loading branch information
ec- committed Jun 10, 2024
1 parent c6fe6cf commit 9fd02e4
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 56 deletions.
2 changes: 2 additions & 0 deletions code/server/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ typedef struct {
serverState_t state;
qboolean restarting; // if true, send configstring changes during SS_LOADING
int serverId; // changes each server start
int restartedServerId; // changes each map restart
int checksumFeed; // the feed key that we use to compute the pure checksum strings
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475
// the serverId associated with the current checksumFeed (always <= serverId)
Expand Down Expand Up @@ -169,6 +170,7 @@ typedef struct client_s {

qboolean gamestateAcked; // set to qtrue when serverId = sv.serverId & messageAcknowledge = gamestateMessageNum
qboolean downloading; // set at "download", reset at gamestate retransmission
// int serverId; // last acknowledged serverId

// downloading
char downloadName[MAX_QPATH]; // if not empty string, we are downloading
Expand Down
7 changes: 3 additions & 4 deletions code/server/sv_ccmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ static void SV_MapRestart_f( void ) {
int delay;

// make sure we aren't restarting twice in the same frame
if ( com_frameTime == sv.serverId ) {
if ( com_frameTime == sv.restartedServerId ) {
return;
}

Expand Down Expand Up @@ -288,9 +288,8 @@ static void SV_MapRestart_f( void ) {
// map_restart has happened
svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT;

// generate a new serverid
sv.serverId = com_frameTime;
Cvar_SetIntegerValue( "sv_serverid", sv.serverId );
// generate a new restartedServerid
sv.restartedServerId = com_frameTime;

// if a map_restart occurs while a client is changing maps, we need
// to give them the correct time so that when they finish loading
Expand Down
74 changes: 42 additions & 32 deletions code/server/sv_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1114,18 +1114,28 @@ SV_ClientEnterWorld
==================
*/
void SV_ClientEnterWorld( client_t *client ) {
int clientNum;
sharedEntity_t *ent;
qboolean isBot;
int clientNum;

isBot = client->netchan.remoteAddress.type == NA_BOT;

SV_PrintClientStateChange( client, CS_ACTIVE );
if ( !isBot ) {
SV_PrintClientStateChange( client, CS_ACTIVE );
} else {
// client->serverId = sv.serverId;
}

client->state = CS_ACTIVE;

client->gamestateAcked = qtrue;
client->oldServerTime = 0;

// resend all configstrings using the cs commands since these are
// no longer sent when the client is CS_PRIMED
SV_UpdateConfigstrings( client );
if ( !isBot ) {
SV_UpdateConfigstrings( client );
}

// set up the entity for the client
clientNum = client - svs.clients;
Expand Down Expand Up @@ -1263,6 +1273,7 @@ static void SV_BeginDownload_f( client_t *cl ) {
cl->gentity = NULL;

cl->downloading = qtrue;
cl->gamestateAcked = qfalse;
}


Expand Down Expand Up @@ -1961,6 +1972,7 @@ Also called by bot code
qboolean SV_ExecuteClientCommand( client_t *cl, const char *s ) {
const ucmd_t *ucmd;
qboolean bFloodProtect;
qboolean isBot;

Cmd_TokenizeString( s );

Expand All @@ -1972,7 +1984,8 @@ qboolean SV_ExecuteClientCommand( client_t *cl, const char *s ) {

// We don't do this when the client hasn't been active yet since it's
// normal to spam a lot of commands when downloading
bFloodProtect = cl->netchan.remoteAddress.type != NA_BOT && cl->state >= CS_ACTIVE;
isBot = cl->netchan.remoteAddress.type == NA_BOT ? qtrue: qfalse;
bFloodProtect = !isBot && cl->state >= CS_ACTIVE;

// see if it is a server level command
for ( ucmd = ucmds; ucmd->name; ucmd++ ) {
Expand All @@ -1992,6 +2005,11 @@ qboolean SV_ExecuteClientCommand( client_t *cl, const char *s ) {
}
}

// if ( !isBot && ( !cl->gamestateAcked || sv.serverId != cl->serverId ) ) {
// Com_Printf( "%s: ignoring pre map_restart / outdated client command '%s'\n", cl->name, s );
// return qtrue;
// }

#ifndef DEDICATED
if ( !com_cl_running->integer && bFloodProtect && SV_FloodProtect( cl ) ) {
#else
Expand Down Expand Up @@ -2249,6 +2267,8 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {

cl->justConnected = qfalse;

// cl->serverId = serverId;

// if this is a usercmd from a previous gamestate,
// ignore it or retransmit the current gamestate
//
Expand All @@ -2257,33 +2277,30 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
// the gamestate changes. After the download is finished, we'll
// notice and send it a new game state
//
if ( cl->state == CS_ACTIVE ) {
if ( serverId != sv.serverId ) {
Com_DPrintf( "%s: ignoring pre map_restart / outdated client message\n", cl->name );
return;
}
} else if ( cl->state == CS_CONNECTED ) {
if ( cl->state == CS_CONNECTED ) {
if ( !cl->downloading ) {
// send initial gamestate, client may not acknowledge it in next command but start downloading after SV_ClientCommand()
if ( !SVC_RateLimit( &cl->gamestate_rate, 2, 1000 ) ) {
SV_SendClientGameState( cl );
}
return;
}
} else if ( !cl->gamestateAcked ) {
// eary check for gamestate acknowledge
if ( serverId == sv.serverId && cl->messageAcknowledge == cl->gamestateMessageNum ) {
cl->gamestateAcked = qtrue;
// this client has acknowledged the new gamestate so it's
// safe to start sending it the real time again
Com_DPrintf( "%s acknowledged gamestate\n", cl->name );
cl->oldServerTime = 0;
}
}
// else if ( cl->state == CS_PRIMED ) {
// in case of download intention client replies with (messageAcknowledge - gamestateMessageNum) >= 0 and (serverId == sv.serverId)
// in case of download intention client replies with (messageAcknowledge - gamestateMessageNum) >= 0 and (serverId == sv.serverId), sv.serverId can drift away later
// in case of lost gamestate client replies with (messageAcknowledge - gamestateMessageNum) > 0 and (serverId == sv.serverId)
// in case of disconnect/etc. client replies with any serverId
//}

// this client has acknowledged the new gamestate so it's
// safe to start sending it the real time again
if ( cl->oldServerTime && serverId == sv.serverId ) {
Com_DPrintf( "%s acknowledged gamestate\n", cl->name );
cl->oldServerTime = 0;
}

// read optional clientCommand strings
do {
c = MSG_ReadByte( msg );
Expand All @@ -2298,22 +2315,15 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
}
} while ( 1 );

if ( cl->state == CS_PRIMED && !cl->gamestateAcked ) {
// not downloading and gamestate is not yet acknowledged
const int gsDelta = cl->messageAcknowledge - cl->gamestateMessageNum;
if ( gsDelta == 0 && serverId == sv.serverId ) {
// exact match, acknowledge
cl->gamestateAcked = qtrue;
} else {
// dropped gamestate or invalid serverId
if ( gsDelta > 0 ) {
Com_DPrintf( "%s: %s gamestate, resending\n", cl->name, serverId != sv.serverId ? "outdated" : "dropped" );
if ( !SVC_RateLimit( &cl->gamestate_rate, 2, 1000 ) ) {
SV_SendClientGameState( cl );
}
if ( !cl->gamestateAcked ) {
// late check for gamestate resend
if ( cl->state == CS_PRIMED && cl->messageAcknowledge - cl->gamestateMessageNum > 0 ) {
Com_DPrintf( "%s: dropped gamestate, resending\n", cl->name );
if ( !SVC_RateLimit( &cl->gamestate_rate, 2, 1000 ) ) {
SV_SendClientGameState( cl );
}
return;
}
return;
}

// read the usercmd_t
Expand Down
26 changes: 6 additions & 20 deletions code/server/sv_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -507,12 +507,13 @@ void SV_SpawnServer( const char *mapname, qboolean killBots ) {
// set serverinfo visible name
Cvar_Set( "mapname", mapname );

Cvar_Set( "sv_mapChecksum", va( "%i",checksum ) );
Cvar_SetIntegerValue( "sv_mapChecksum", checksum );

// serverid should be different each time
sv.serverId = com_frameTime;
sv.restartedServerId = sv.serverId;
sv.checksumFeedServerId = sv.serverId;
Cvar_Set( "sv_serverid", va( "%i", sv.serverId ) );
Cvar_SetIntegerValue( "sv_serverid", sv.serverId );

// clear physics interaction links
SV_ClearWorld();
Expand Down Expand Up @@ -567,29 +568,14 @@ void SV_SpawnServer( const char *mapname, qboolean killBots ) {
// was connected before the level change
SV_DropClient( &svs.clients[i], denied );
} else {

svs.clients[i].gamestateAcked = qfalse;

if ( !isBot ) {
svs.clients[i].gamestateAcked = qfalse;
// when we get the next packet from a connected client,
// the new gamestate will be sent
svs.clients[i].state = CS_CONNECTED;
svs.clients[i].gentity = NULL;
}
else {
client_t *client;
sharedEntity_t *ent;

client = &svs.clients[i];
client->state = CS_ACTIVE;
ent = SV_GentityNum( i );
ent->s.number = i;
client->gentity = ent;

client->deltaMessage = client->netchan.outgoingSequence - ( PACKET_BACKUP + 1 ); // force delta reset
client->lastSnapshotTime = svs.time - 9999; // generate a snapshot immediately

VM_Call( gvm, 1, GAME_CLIENT_BEGIN, i );
} else {
SV_ClientEnterWorld( &svs.clients[i] );
}
}
}
Expand Down

0 comments on commit 9fd02e4

Please sign in to comment.