Skip to content

Commit

Permalink
server: simplify gamestate retransmit conditions
Browse files Browse the repository at this point in the history
Removed unnecessary checks for client acknowledging the exact gamestate message number.
  • Loading branch information
Chomenor committed Aug 18, 2024
1 parent 0c26ee3 commit 8135f46
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 63 deletions.
13 changes: 2 additions & 11 deletions code/server/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,6 @@ struct leakyBucket_s {
leakyBucket_t *prev, *next;
};

typedef enum {
GSA_INIT = 0, // gamestate never sent with current sv.serverId
GSA_SENT_ONCE, // gamestate sent once, client can reply with any (messageAcknowledge - gamestateMessageNum) >= 0 and correct serverId
GSA_SENT_MANY, // gamestate sent many times, client must reply with exact gamestateMessageNum == gamestateMessageNum and correct serverId
GSA_ACKED // gamestate acknowledged, no retansmissions needed
} gameStateAck_t;

typedef struct client_s {
clientState_t state;
char userinfo[MAX_INFO_STRING]; // name, etc
Expand All @@ -174,11 +167,8 @@ typedef struct client_s {
sharedEntity_t *gentity; // SV_GentityNum(clientnum)
char name[MAX_NAME_LENGTH]; // extracted from userinfo, high bits masked

gameStateAck_t gamestateAck;
qboolean downloading; // set at "download", reset at gamestate retransmission
// int serverId; // last acknowledged serverId

// downloading
qboolean downloading; // set at "download", reset at gamestate retransmission
char downloadName[MAX_QPATH]; // if not empty string, we are downloading
fileHandle_t download; // file being downloaded
int downloadSize; // total bytes (can't use EOF because of paks)
Expand All @@ -190,6 +180,7 @@ typedef struct client_s {
int downloadBlockSize[MAX_DOWNLOAD_WINDOW];
qboolean downloadEOF; // We have sent the EOF block
int downloadSendTime; // time we last got an ack from the client
qboolean downloadGamestateDropCheck; // perform extra dropped gamestate check after downloads

int deltaMessage; // frame last client usercmd message
int lastPacketTime; // svs.time when packet was last received
Expand Down
102 changes: 51 additions & 51 deletions code/server/sv_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1013,9 +1013,13 @@ This will be sent on the initial connection and upon each new map load.
It will be resent if the client acknowledges a later message but has
the wrong gamestate.
In cases where it's safe to assume client can't drop the gamestate and
use a previously sent one, resetCsUpdated can be set to true to avoid
unnecessary configstring updating.
================
*/
static void SV_SendClientGameState( client_t *client ) {
static void SV_SendClientGameState( client_t *client, qboolean resetCsUpdated ) {
int start;
entityState_t nullstate;
const svEntity_t *svEnt;
Expand All @@ -1028,12 +1032,6 @@ static void SV_SendClientGameState( client_t *client ) {

client->state = CS_PRIMED;

if ( client->gamestateAck == GSA_INIT ) {
client->gamestateAck = GSA_SENT_ONCE;
} else {
client->gamestateAck = GSA_SENT_MANY;
}

client->downloading = qfalse;

client->pureAuthentic = qfalse;
Expand Down Expand Up @@ -1082,7 +1080,9 @@ static void SV_SendClientGameState( client_t *client ) {
MSG_WriteBigString( &msg, sv.configstrings[start] );
}
}
client->csUpdated[ start ] = qfalse;
if ( resetCsUpdated ) {
client->csUpdated[ start ] = qfalse;
}
}

// write the baselines
Expand Down Expand Up @@ -1140,7 +1140,6 @@ void SV_ClientEnterWorld( client_t *client ) {
}

client->state = CS_ACTIVE;
client->gamestateAck = GSA_ACKED;

client->oldServerTime = 0;

Expand Down Expand Up @@ -1230,7 +1229,7 @@ static void SV_DoneDownload_f( client_t *cl ) {
Com_DPrintf( "clientDownload: %s Done\n", cl->name );

// resend the game state to update any clients that entered during the download
SV_SendClientGameState( cl );
SV_SendClientGameState( cl, qtrue );
}


Expand Down Expand Up @@ -1286,7 +1285,7 @@ static void SV_BeginDownload_f( client_t *cl ) {
cl->gentity = NULL;

cl->downloading = qtrue;
cl->gamestateAck = GSA_SENT_MANY; // expect exact messageAcknowledge next time
cl->downloadGamestateDropCheck = qtrue;
}


Expand Down Expand Up @@ -2163,7 +2162,7 @@ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) {
// we didn't get a cp yet, don't assume anything and just send the gamestate all over again
if ( !SVC_RateLimit( &cl->gamestate_rate, 2, 1000 ) ) {
Com_DPrintf( "%s: didn't get cp command, resending gamestate\n", cl->name );
SV_SendClientGameState( cl );
SV_SendClientGameState( cl, qfalse );
}
return;
}
Expand Down Expand Up @@ -2269,42 +2268,12 @@ 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
//
// if the client was downloading, let it stay at whatever serverId and
// gamestate it was at. This allows it to keep downloading even when
// the gamestate changes. After the download is finished, we'll
// notice and send it a new game state
//
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->gamestateAck != GSA_ACKED ) {
// early check for gamestate acknowledge
if ( serverId == sv.serverId ) {
const int delta = cl->messageAcknowledge - cl->gamestateMessageNum;
if ( delta == 0 || ( delta > 0 && cl->gamestateAck == GSA_SENT_ONCE ) ) {
cl->gamestateAck = GSA_ACKED;
// this client has acknowledged the new gamestate so it's
// safe to start sending it the real time again
Com_DPrintf( "%s acknowledged gamestate with delta %i\n", cl->name, delta );
cl->oldServerTime = 0;
}
}
// 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;
}
// else if ( cl->state == CS_PRIMED ) {
// 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
//}

// read optional clientCommand strings
do {
Expand All @@ -2320,12 +2289,25 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
}
} while ( 1 );

if ( cl->gamestateAck != GSA_ACKED ) {
// late check for gamestate resend
if ( cl->state == CS_PRIMED && cl->messageAcknowledge - cl->gamestateMessageNum > 0 ) {
if ( cl->downloading ) {
// waiting for "donedl" command
return;
}

// check for sending initial gamestate
if ( cl->state == CS_CONNECTED ) {
if ( !SVC_RateLimit( &cl->gamestate_rate, 2, 1000 ) ) {
SV_SendClientGameState( cl, qtrue );
}
return;
}

// check for dropped gamestate
if ( cl->state != CS_ACTIVE && serverId != sv.serverId ) {
if ( 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 );
SV_SendClientGameState( cl, qtrue );
}
}
return;
Expand All @@ -2342,4 +2324,22 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
// if ( msg->readcount != msg->cursize ) {
// Com_Printf( "WARNING: Junk at end of packet for client %i\n", cl - svs.clients );
// }

// extra check for dropped gamestate for post-UDP download clients, since after
// download client can have correct serverid but still be awaiting gamestate
if ( cl->downloadGamestateDropCheck ) {
const int gsDelta = cl->messageAcknowledge - cl->gamestateMessageNum;
// either a move command (implied by CS_ACTIVE) or exact acknowledge of
// gamestate message number implies client has the new gamestate
if ( cl->state == CS_ACTIVE || gsDelta == 0 ) {
Com_DPrintf( "%s: acknowledged post-download gamestate (state:%i gsDelta:%i)\n",
cl->name, cl->state, gsDelta );
cl->downloadGamestateDropCheck = qfalse;
} else if ( gsDelta > 20 ) {
Com_DPrintf( "%s: dropped post-download gamestate, resending\n", cl->name );
if ( !SVC_RateLimit( &cl->gamestate_rate, 2, 1000 ) ) {
SV_SendClientGameState( cl, qfalse );
}
}
}
}
2 changes: 1 addition & 1 deletion code/server/sv_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -586,11 +586,11 @@ void SV_SpawnServer( const char *mapname, qboolean killBots ) {
SV_DropClient( &svs.clients[i], denied );
} else {
if ( !isBot ) {
svs.clients[i].gamestateAck = GSA_INIT; // resend gamestate, accept first correct serverId
// 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;
svs.clients[i].downloadGamestateDropCheck = qfalse;
} else {
SV_ClientEnterWorld( &svs.clients[i] );
}
Expand Down

0 comments on commit 8135f46

Please sign in to comment.