From 413e2fb9023139165accf5186c8c20cb6ec81f6f Mon Sep 17 00:00:00 2001 From: Eugene Date: Fri, 28 Jun 2024 00:09:30 +0300 Subject: [PATCH] server: cache sv_maxclients value to avoid potential crashes caused by VMs changing it at runtime --- code/server/server.h | 4 +- code/server/sv_bot.c | 12 ++--- code/server/sv_ccmds.c | 42 ++++++++--------- code/server/sv_client.c | 36 +++++++-------- code/server/sv_game.c | 14 +++--- code/server/sv_init.c | 97 ++++++++++++++++++++++----------------- code/server/sv_main.c | 45 +++++++++--------- code/server/sv_snapshot.c | 6 +-- 8 files changed, 135 insertions(+), 121 deletions(-) diff --git a/code/server/server.h b/code/server/server.h index a92b8506d..d75b0ae55 100644 --- a/code/server/server.h +++ b/code/server/server.h @@ -66,7 +66,8 @@ typedef struct snapshotFrame_s { typedef struct { serverState_t state; qboolean restarting; // if true, send configstring changes during SS_LOADING - int pure; + int pure; // fixed at level spawn + int maxclients; // fixed at level spawn 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 @@ -340,7 +341,6 @@ void SV_UpdateConfigstrings( client_t *client ); void SV_SetUserinfo( int index, const char *val ); void SV_GetUserinfo( int index, char *buffer, int bufferSize ); -void SV_ChangeMaxClients( void ); void SV_SpawnServer( const char *mapname, qboolean killBots ); diff --git a/code/server/sv_bot.c b/code/server/sv_bot.c index cfb5776e6..503de8d59 100644 --- a/code/server/sv_bot.c +++ b/code/server/sv_bot.c @@ -49,13 +49,13 @@ int SV_BotAllocateClient( void ) { client_t *cl; // find a client slot - for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { + for ( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { if ( cl->state == CS_FREE ) { break; } } - if ( i == sv_maxclients->integer ) { + if ( i == sv.maxclients ) { return -1; } @@ -82,7 +82,7 @@ SV_BotFreeClient void SV_BotFreeClient( int clientNum ) { client_t *cl; - if ( (unsigned) clientNum >= sv_maxclients->integer ) { + if ( (unsigned) clientNum >= sv.maxclients ) { Com_Error( ERR_DROP, "SV_BotFreeClient: bad clientNum: %i", clientNum ); } @@ -440,7 +440,7 @@ SV_BotClientCommand ================== */ static void BotClientCommand( int client, const char *command ) { - if ( (unsigned) client < sv_maxclients->integer ) { + if ( (unsigned) client < sv.maxclients ) { SV_ExecuteClientCommand( &svs.clients[client], command ); } } @@ -592,7 +592,7 @@ SV_BotGetConsoleMessage */ int SV_BotGetConsoleMessage( int client, char *buf, int size ) { - if ( (unsigned) client < sv_maxclients->integer ) { + if ( (unsigned) client < sv.maxclients ) { client_t* cl; int index; @@ -647,7 +647,7 @@ SV_BotGetSnapshotEntity ================== */ int SV_BotGetSnapshotEntity( int client, int sequence ) { - if ( (unsigned) client < sv_maxclients->integer ) { + if ( (unsigned) client < sv.maxclients ) { const client_t* cl = &svs.clients[client]; const clientSnapshot_t* frame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK]; if ( (unsigned) sequence >= frame->num_entities ) { diff --git a/code/server/sv_ccmds.c b/code/server/sv_ccmds.c index ec57c23f2..bd0229d24 100644 --- a/code/server/sv_ccmds.c +++ b/code/server/sv_ccmds.c @@ -65,17 +65,17 @@ client_t *SV_GetPlayerByHandle( void ) { int plid = atoi(s); // Check for numeric playerid match - if(plid >= 0 && plid < sv_maxclients->integer) + if(plid >= 0 && plid < sv.maxclients) { cl = &svs.clients[plid]; - if(cl->state) + if (cl->state >= CS_CONNECTED) return cl; } } // check for a name match - for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) { + for ( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { if ( cl->state < CS_CONNECTED ) { continue; } @@ -128,7 +128,7 @@ static client_t *SV_GetPlayerByNum( void ) { } } idnum = atoi( s ); - if ( idnum < 0 || idnum >= sv_maxclients->integer ) { + if ( idnum < 0 || idnum >= sv.maxclients ) { Com_Printf( "Bad client slot: %i\n", idnum ); return NULL; } @@ -294,7 +294,7 @@ static void SV_MapRestart_f( void ) { // 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 // they don't violate the backwards time check in cl_cgame.c - for ( i = 0; i < sv_maxclients->integer; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { if ( svs.clients[i].state == CS_PRIMED ) { svs.clients[i].oldServerTime = sv.restartTime; } @@ -322,7 +322,7 @@ static void SV_MapRestart_f( void ) { sv.restarting = qfalse; // connect and begin all the clients - for ( i = 0 ; i < sv_maxclients->integer ; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { client = &svs.clients[i]; // send the new gamestate to all connected clients @@ -359,7 +359,7 @@ static void SV_MapRestart_f( void ) { VM_Call( gvm, 1, GAME_RUN_FRAME, sv.time ); svs.time += 100; - for ( i = 0 ; i < sv_maxclients->integer ; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { client = &svs.clients[i]; if ( client->state >= CS_PRIMED ) { // accept usercmds starting from current server time only @@ -395,24 +395,24 @@ static void SV_Kick_f( void ) { cl = SV_GetPlayerByHandle(); if ( !cl ) { - if ( !Q_stricmp(Cmd_Argv(1), "all") ) { - for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) { - if ( !cl->state ) { + if ( !Q_stricmp( Cmd_Argv( 1 ), "all" ) ) { + for ( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { + if ( cl->state < CS_CONNECTED ) { continue; } - if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { + if ( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { continue; } SV_DropClient( cl, "was kicked" ); cl->lastPacketTime = svs.time; // in case there is a funny zombie } } - else if ( !Q_stricmp(Cmd_Argv(1), "allbots") ) { - for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) { - if ( !cl->state ) { + else if ( !Q_stricmp( Cmd_Argv( 1 ), "allbots" ) ) { + for ( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { + if ( cl->state < CS_CONNECTED ) { continue; } - if( cl->netchan.remoteAddress.type != NA_BOT ) { + if ( cl->netchan.remoteAddress.type != NA_BOT ) { continue; } SV_DropClient( cl, "was kicked" ); @@ -421,8 +421,8 @@ static void SV_Kick_f( void ) { } return; } - if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { - Com_Printf("Cannot kick host player\n"); + if ( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { + Com_Printf( "Cannot kick host player\n" ); return; } @@ -447,7 +447,7 @@ static void SV_KickBots_f( void ) { return; } - for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { + for( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { if ( cl->state < CS_CONNECTED ) { continue; } @@ -477,7 +477,7 @@ static void SV_KickAll_f( void ) { return; } - for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { + for( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { if ( cl->state < CS_CONNECTED ) { continue; } @@ -1216,7 +1216,7 @@ static void SV_Status_f( void ) { Com_Memset( al, 0, sizeof( al ) ); // first pass: save and determine max.lengths of name/address fields - for ( i = 0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++ ) + for ( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { if ( cl->state == CS_FREE ) continue; @@ -1260,7 +1260,7 @@ static void SV_Status_f( void ) { Com_Printf( " -----\n" ); #endif - for ( i = 0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++ ) + for ( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { if ( cl->state == CS_FREE ) continue; diff --git a/code/server/sv_client.c b/code/server/sv_client.c index 5abe0db53..8d1ea5e46 100644 --- a/code/server/sv_client.c +++ b/code/server/sv_client.c @@ -380,7 +380,7 @@ static int seqs[ MAX_CLIENTS ]; static void SV_SaveSequences( void ) { int i; - for ( i = 0; i < sv_maxclients->integer; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { seqs[i] = svs.clients[i].reliableSequence; } } @@ -390,7 +390,7 @@ static void SV_InjectLocation( const char *tld, const char *country ) { const char *cmd; char *str; int i, n; - for ( i = 0; i < sv_maxclients->integer; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { if ( seqs[i] != svs.clients[i].reliableSequence ) { for ( n = seqs[i]; n != svs.clients[i].reliableSequence + 1; n++ ) { cmd = svs.clients[i].reliableCommands[n & (MAX_RELIABLE_COMMANDS-1)]; @@ -503,7 +503,7 @@ void SV_DirectConnect( const netadr_t *from ) { } // check for concurrent connections - for ( i = 0, n = 0; i < sv_maxclients->integer; i++ ) { + for ( i = 0, n = 0; i < sv.maxclients; i++ ) { const netadr_t *addr = &svs.clients[ i ].netchan.remoteAddress; if ( addr->type != NA_BOT && NET_CompareBaseAdr( addr, from ) ) { if ( svs.clients[ i ].state >= CS_CONNECTED && !svs.clients[ i ].justConnected ) { @@ -643,7 +643,7 @@ void SV_DirectConnect( const netadr_t *from ) { // quick reject newcl = NULL; - for ( i = 0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++ ) { + for ( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { if ( NET_CompareAdr( from, &cl->netchan.remoteAddress ) ) { int elapsed = svs.time - cl->lastConnectTime; if ( elapsed < ( sv_reconnectlimit->integer * 1000 ) && elapsed >= 0 ) { @@ -664,7 +664,7 @@ void SV_DirectConnect( const netadr_t *from ) { } // if there is already a slot for this ip, reuse it - for ( i = 0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++ ) { + for ( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { if ( cl->state == CS_FREE ) { continue; } @@ -713,7 +713,7 @@ void SV_DirectConnect( const netadr_t *from ) { // select least used free slot n = 0; newcl = NULL; - for ( i = startIndex; i < sv_maxclients->integer ; i++ ) { + for ( i = startIndex; i < sv.maxclients; i++ ) { cl = &svs.clients[i]; if ( cl->state == CS_FREE && ( newcl == NULL || svs.time - cl->lastDisconnectTime > n ) ) { n = svs.time - cl->lastDisconnectTime; @@ -724,16 +724,16 @@ void SV_DirectConnect( const netadr_t *from ) { if ( !newcl ) { if ( NET_IsLocalAddress( from ) ) { count = 0; - for ( i = startIndex; i < sv_maxclients->integer ; i++ ) { + for ( i = startIndex; i < sv.maxclients; i++ ) { cl = &svs.clients[i]; if (cl->netchan.remoteAddress.type == NA_BOT) { count++; } } // if they're all bots - if (count >= sv_maxclients->integer - startIndex) { - SV_DropClient(&svs.clients[sv_maxclients->integer - 1], "only bots on server"); - newcl = &svs.clients[sv_maxclients->integer - 1]; + if (count >= sv.maxclients - startIndex) { + SV_DropClient(&svs.clients[sv.maxclients - 1], "only bots on server"); + newcl = &svs.clients[sv.maxclients - 1]; } else { Com_Error( ERR_DROP, "server is full on local connect" ); @@ -824,12 +824,12 @@ void SV_DirectConnect( const netadr_t *from ) { // if this was the first client on the server, or the last client // the server can hold, send a heartbeat to the master. count = 0; - for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { + for ( i = 0, cl = svs.clients ; i < sv.maxclients; i++, cl++) { if ( svs.clients[i].state >= CS_CONNECTED ) { count++; } } - if ( count == 1 || count == sv_maxclients->integer ) { + if ( count == 1 || count == sv.maxclients ) { SV_Heartbeat_f(); } } @@ -915,12 +915,12 @@ void SV_DropClient( client_t *drop, const char *reason ) { // if this was the last client on the server, send a heartbeat // to the master so it is known the server is empty // send a heartbeat now so the master will get up to date info - for ( i = 0 ; i < sv_maxclients->integer ; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { break; } } - if ( i == sv_maxclients->integer ) { + if ( i == sv.maxclients ) { SV_Heartbeat_f(); } } @@ -1506,7 +1506,7 @@ int SV_SendQueuedMessages( void ) int i, retval = -1, nextFragT; client_t *cl; - for( i = 0; i < sv_maxclients->integer; i++ ) + for( i = 0; i < sv.maxclients; i++ ) { cl = &svs.clients[i]; @@ -1538,7 +1538,7 @@ int SV_SendDownloadMessages( void ) int i, numDLs = 0; client_t *cl; - for( i = 0; i < sv_maxclients->integer; i++ ) + for( i = 0; i < sv.maxclients; i++ ) { cl = &svs.clients[ i ]; if ( cl->state >= CS_CONNECTED && *cl->downloadName ) @@ -1878,7 +1878,7 @@ void SV_PrintLocations_f( client_t *client ) { max_ctrylength = 7; // strlen( "country" ) // first pass: save and determine max.lengths of name/address fields - for ( i = 0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++ ) + for ( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { if ( cl->state == CS_FREE ) continue; @@ -1902,7 +1902,7 @@ void SV_PrintLocations_f( client_t *client ) { Com_sprintf( line, sizeof( line ), "-- %s -- %s\n", filln, fillc ); s = Q_stradd( s, line ); - for ( i = 0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++ ) + for ( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { if ( cl->state == CS_FREE ) continue; diff --git a/code/server/sv_game.c b/code/server/sv_game.c index 95eff21c3..ac35d1ce0 100644 --- a/code/server/sv_game.c +++ b/code/server/sv_game.c @@ -83,10 +83,10 @@ static void SV_GameSendServerCommand( int clientNum, const char *text ) { if ( clientNum == -1 ) { SV_SendServerCommand( NULL, "%s", text ); } else { - if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) { + if ( clientNum < 0 || clientNum >= sv.maxclients ) { return; } - SV_SendServerCommand( svs.clients + clientNum, "%s", text ); + SV_SendServerCommand( svs.clients + clientNum, "%s", text ); } } @@ -99,10 +99,10 @@ Disconnects the client with a message =============== */ static void SV_GameDropClient( int clientNum, const char *reason ) { - if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) { + if ( clientNum < 0 || clientNum >= sv.maxclients ) { return; } - SV_DropClient( svs.clients + clientNum, reason ); + SV_DropClient( svs.clients + clientNum, reason ); } @@ -292,7 +292,7 @@ SV_GetUsercmd =============== */ static void SV_GetUsercmd( int clientNum, usercmd_t *cmd ) { - if ( (unsigned) clientNum < sv_maxclients->integer ) { + if ( (unsigned) clientNum < sv.maxclients ) { *cmd = svs.clients[ clientNum ].lastUsercmd; } else { Com_Error( ERR_DROP, "%s(): bad clientNum: %i", __func__, clientNum ); @@ -559,7 +559,7 @@ static intptr_t SV_GameSystemCalls( intptr_t *args ) { case BOTLIB_USER_COMMAND: { unsigned clientNum = args[1]; - if ( clientNum < sv_maxclients->integer ) + if ( clientNum < sv.maxclients ) { SV_ClientThink( &svs.clients[ clientNum ], VMA(2) ); } @@ -1020,7 +1020,7 @@ static void SV_InitGameVM( qboolean restart ) { // a previous level // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=522 // now done before GAME_INIT call - for ( i = 0 ; i < sv_maxclients->integer ; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { svs.clients[i].gentity = NULL; } diff --git a/code/server/sv_init.c b/code/server/sv_init.c index e2afb93d8..739f3acd6 100644 --- a/code/server/sv_init.c +++ b/code/server/sv_init.c @@ -129,7 +129,7 @@ void SV_SetConfigstring (int index, const char *val) { if ( sv.state == SS_GAME || sv.restarting ) { // send the data to all relevant clients - for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) { + for (i = 0, client = svs.clients; i < sv.maxclients; i++, client++) { if ( client->state < CS_ACTIVE ) { if ( client->state == CS_PRIMED ) client->csUpdated[ index ] = qtrue; @@ -174,8 +174,8 @@ SV_SetUserinfo =============== */ void SV_SetUserinfo( int index, const char *val ) { - if ( index < 0 || index >= sv_maxclients->integer ) { - Com_Error (ERR_DROP, "SV_SetUserinfo: bad index %i", index); + if ( index < 0 || index >= sv.maxclients ) { + Com_Error( ERR_DROP, "%s: bad index %i", __func__, index ); } if ( !val ) { @@ -196,10 +196,10 @@ SV_GetUserinfo */ void SV_GetUserinfo( int index, char *buffer, int bufferSize ) { if ( bufferSize < 1 ) { - Com_Error( ERR_DROP, "SV_GetUserinfo: bufferSize == %i", bufferSize ); + Com_Error( ERR_DROP, "%s: bufferSize == %i", __func__, bufferSize ); } - if ( index < 0 || index >= sv_maxclients->integer ) { - Com_Error (ERR_DROP, "SV_GetUserinfo: bad index %i", index); + if ( index < 0 || index >= sv.maxclients ) { + Com_Error( ERR_DROP, "%s: bad index %i", __func__, index ); } Q_strncpyz( buffer, svs.clients[ index ].userinfo, bufferSize ); } @@ -239,15 +239,19 @@ static void SV_CreateBaseline( void ) { SV_BoundMaxClients =============== */ -static void SV_BoundMaxClients( int minimum ) { +static int SV_BoundMaxClients( int minimum ) { // get the current maxclients value Cvar_Get( "sv_maxclients", "8", 0 ); - sv_maxclients->modified = qfalse; - if ( sv_maxclients->integer < minimum ) { - Cvar_Set( "sv_maxclients", va("%i", minimum) ); + Cvar_SetIntegerValue( "sv_maxclients", minimum ); + sv_maxclients->modified = qfalse; + return minimum; } + + sv_maxclients->modified = qfalse; + + return sv_maxclients->integer; } @@ -263,6 +267,20 @@ static void SV_SetSnapshotParams( void ) } +/* +=============== +SV_AllocClients +=============== +*/ +static void SV_AllocClients( int count ) +{ + svs.clients = Z_TagMalloc( count * sizeof( client_t ), TAG_CLIENTS ); + Com_Memset( svs.clients, 0x0, count * sizeof( client_t ) ); + sv.maxclients = count; + SV_SetSnapshotParams(); +} + + /* =============== SV_Startup @@ -277,11 +295,9 @@ static void SV_Startup( void ) { if ( svs.initialized ) { Com_Error( ERR_FATAL, "SV_Startup: svs.initialized" ); } - SV_BoundMaxClients( 1 ); - svs.clients = Z_TagMalloc( sv_maxclients->integer * sizeof( client_t ), TAG_CLIENTS ); - Com_Memset( svs.clients, 0, sv_maxclients->integer * sizeof( client_t ) ); - SV_SetSnapshotParams(); + SV_AllocClients( sv_maxclients->integer ); + svs.initialized = qtrue; // Don't respect sv_killserver unless a server is actually running @@ -303,37 +319,37 @@ static void SV_Startup( void ) { SV_ChangeMaxClients ================== */ -void SV_ChangeMaxClients( void ) { - int oldMaxClients; - int i; - client_t *oldClients; +static void SV_ChangeMaxClients( void ) { + client_t *oldClients; + int maxclients; int count; + int i; // get the highest client number in use count = 0; - for ( i = 0 ; i < sv_maxclients->integer ; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { - if (i > count) + if ( i > count ) { count = i; + } } } count++; - oldMaxClients = sv_maxclients->integer; // never go below the highest client number in use - SV_BoundMaxClients( count ); + maxclients = SV_BoundMaxClients( count ); + // if still the same - if ( sv_maxclients->integer == oldMaxClients ) { + if ( maxclients == sv.maxclients ) { return; } oldClients = Hunk_AllocateTempMemory( count * sizeof(client_t) ); // copy the clients to hunk memory - for ( i = 0 ; i < count ; i++ ) { + for ( i = 0; i < count; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { oldClients[i] = svs.clients[i]; - } - else { + } else { Com_Memset(&oldClients[i], 0, sizeof(client_t)); } } @@ -342,11 +358,10 @@ void SV_ChangeMaxClients( void ) { Z_Free( svs.clients ); // allocate new clients - svs.clients = Z_TagMalloc( sv_maxclients->integer * sizeof(client_t), TAG_CLIENTS ); - Com_Memset( svs.clients, 0, sv_maxclients->integer * sizeof(client_t) ); + SV_AllocClients( maxclients ); // copy the clients over - for ( i = 0 ; i < count ; i++ ) { + for ( i = 0; i < count; i++ ) { if ( oldClients[i].state >= CS_CONNECTED ) { svs.clients[i] = oldClients[i]; } @@ -354,8 +369,6 @@ void SV_ChangeMaxClients( void ) { // free the old clients on the hunk Hunk_FreeTempMemory( oldClients ); - - SV_SetSnapshotParams(); } @@ -367,7 +380,7 @@ SV_ClearServer static void SV_ClearServer( void ) { int i; - for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { + for ( i = 0; i < MAX_CONFIGSTRINGS; i++ ) { if ( sv.configstrings[i] ) { Z_Free( sv.configstrings[i] ); } @@ -463,17 +476,17 @@ void SV_SpawnServer( const char *mapname, qboolean killBots ) { // try to reset level time if server is empty if ( !sv_levelTimeReset->integer && !sv.restartTime ) { - for ( i = 0; i < sv_maxclients->integer; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { break; } } - if ( i == sv_maxclients->integer ) { + if ( i == sv.maxclients ) { sv.time = 0; } } - for ( i = 0; i < sv_maxclients->integer; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { // save when the server started for each client already connected if ( svs.clients[i].state >= CS_CONNECTED && sv_levelTimeReset->integer ) { svs.clients[i].oldServerTime = sv.time; @@ -482,9 +495,12 @@ void SV_SpawnServer( const char *mapname, qboolean killBots ) { } } + // preserve maxclients + i = sv.maxclients; // wipe the entire per-level structure SV_ClearServer(); - for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { + sv.maxclients = i; + for ( i = 0; i < MAX_CONFIGSTRINGS; i++ ) { sv.configstrings[i] = CopyString(""); } @@ -537,8 +553,7 @@ void SV_SpawnServer( const char *mapname, qboolean killBots ) { sv_pure->modified = qfalse; // run a few frames to allow everything to settle - for ( i = 0; i < 3; i++ ) - { + for ( i = 0; i < 3; i++ ) { sv.time += 100; VM_Call( gvm, 1, GAME_RUN_FRAME, sv.time ); SV_BotFrame( sv.time ); @@ -547,7 +562,7 @@ void SV_SpawnServer( const char *mapname, qboolean killBots ) { // create a baseline for more efficient communications SV_CreateBaseline(); - for ( i = 0; i < sv_maxclients->integer; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { // send the new gamestate to all connected clients if ( svs.clients[i].state >= CS_CONNECTED ) { const char *denied; @@ -822,7 +837,7 @@ static void SV_FinalMessage( const char *message ) { // send it twice, ignoring rate for ( j = 0 ; j < 2 ; j++ ) { - for (i=0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++) { + for ( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++) { if (cl->state >= CS_CONNECTED ) { // don't send a disconnect to a local client if ( cl->netchan.remoteAddress.type != NA_LOOPBACK ) { @@ -876,7 +891,7 @@ void SV_Shutdown( const char *finalmsg ) { if ( svs.clients ) { int index; - for ( index = 0; index < sv_maxclients->integer; index++ ) + for ( index = 0; index < sv.maxclients; index++ ) SV_FreeClient( &svs.clients[ index ] ); Z_Free( svs.clients ); diff --git a/code/server/sv_main.c b/code/server/sv_main.c index 59f96224e..5b4696107 100644 --- a/code/server/sv_main.c +++ b/code/server/sv_main.c @@ -207,7 +207,7 @@ void QDECL SV_SendServerCommand( client_t *cl, const char *fmt, ... ) { } // send the data to all relevant clients - for ( j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++ ) { + for ( j = 0, client = svs.clients; j < sv.maxclients; j++, client++ ) { if ( len <= 1022 || client->longstr ) { SV_AddServerCommand( client, message ); } @@ -696,7 +696,7 @@ static void SVC_Status( const netadr_t *from ) { status[0] = '\0'; statusLength = strlen( infostring ) + 16; // strlen( "statusResponse\n\n" ) - for ( i = 0 ; i < sv_maxclients->integer ; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { cl = &svs.clients[i]; if ( cl->state >= CS_CONNECTED ) { @@ -763,7 +763,7 @@ static void SVC_Info( const netadr_t *from ) { // don't count privateclients count = humans = 0; - for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) { + for ( i = sv_privateClients->integer; i < sv.maxclients; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { count++; if (svs.clients[i].netchan.remoteAddress.type != NA_BOT) { @@ -782,14 +782,13 @@ static void SVC_Info( const netadr_t *from ) { Info_SetValueForKey( infostring, "hostname", sv_hostname->string ); Info_SetValueForKey( infostring, "mapname", sv_mapname->string ); Info_SetValueForKey( infostring, "clients", va("%i", count) ); - Info_SetValueForKey(infostring, "g_humanplayers", va("%i", humans)); - Info_SetValueForKey( infostring, "sv_maxclients", - va("%i", sv_maxclients->integer - sv_privateClients->integer ) ); - Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) ); - Info_SetValueForKey( infostring, "pure", va("%i", sv.pure) ); - Info_SetValueForKey(infostring, "g_needpass", va("%d", Cvar_VariableIntegerValue("g_needpass"))); + Info_SetValueForKey( infostring, "g_humanplayers", va( "%i", humans ) ); + Info_SetValueForKey( infostring, "sv_maxclients", va( "%i", sv.maxclients - sv_privateClients->integer ) ); + Info_SetValueForKey( infostring, "gametype", va( "%i", sv_gametype->integer ) ); + Info_SetValueForKey( infostring, "pure", va( "%i", sv.pure ) ); + Info_SetValueForKey( infostring, "g_needpass", va( "%d", Cvar_VariableIntegerValue( "g_needpass" ) ) ); gamedir = Cvar_VariableString( "fs_game" ); - if( *gamedir ) { + if ( *gamedir != '\0' ) { Info_SetValueForKey( infostring, "game", gamedir ); } @@ -995,8 +994,8 @@ void SV_PacketEvent( const netadr_t *from, msg_t *msg ) { qport = MSG_ReadShort( msg ) & 0xffff; // find which client the message is from - for (i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { - if (cl->state == CS_FREE) { + for ( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { + if ( cl->state == CS_FREE ) { continue; } if ( !NET_CompareBaseAdr( from, &cl->netchan.remoteAddress ) ) { @@ -1004,23 +1003,23 @@ void SV_PacketEvent( const netadr_t *from, msg_t *msg ) { } // it is possible to have multiple clients from a single IP // address, so they are differentiated by the qport variable - if (cl->netchan.qport != qport) { + if ( cl->netchan.qport != qport ) { continue; } // make sure it is a valid, in sequence packet - if (SV_Netchan_Process(cl, msg)) { + if ( SV_Netchan_Process( cl, msg ) ) { // the IP port can't be used to differentiate clients, because // some address translating routers periodically change UDP // port assignments - if (cl->netchan.remoteAddress.port != from->port) { + if ( cl->netchan.remoteAddress.port != from->port ) { Com_Printf( "SV_PacketEvent: fixing up a translated port\n" ); cl->netchan.remoteAddress.port = from->port; } // zombie clients still need to do the Netchan_Process // to make sure they don't need to retransmit the final // reliable message, but they don't do any other processing - if (cl->state != CS_ZOMBIE) { + if ( cl->state != CS_ZOMBIE ) { cl->lastPacketTime = svs.time; // don't timeout SV_ExecuteClientMessage( cl, msg ); } @@ -1044,7 +1043,7 @@ static void SV_CalcPings( void ) { int delta; playerState_t *ps; - for (i=0 ; i < sv_maxclients->integer ; i++) { + for ( i = 0; i < sv.maxclients; i++ ) { cl = &svs.clients[i]; if ( cl->state != CS_ACTIVE ) { cl->ping = 999; @@ -1106,7 +1105,7 @@ static void SV_CheckTimeouts( void ) { droppoint = svs.time - 1000 * sv_timeout->integer; zombiepoint = svs.time - 1000 * sv_zombietime->integer; - for ( i = 0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++ ) { + for ( i = 0, cl = svs.clients ; i < sv.maxclients; i++, cl++ ) { if ( cl->state == CS_FREE ) { continue; } @@ -1163,7 +1162,7 @@ static qboolean SV_CheckPaused( void ) { // only pause if there is just a single client connected count = 0; - for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { + for ( i = 0, cl = svs.clients ; i < sv.maxclients; i++, cl++ ) { if ( cl->state >= CS_CONNECTED && cl->netchan.remoteAddress.type != NA_BOT ) { count++; } @@ -1233,7 +1232,7 @@ void SV_TrackCvarChanges( void ) if ( sv.state == SS_DEAD || !svs.clients ) return; - for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { + for ( i = 0, cl = svs.clients; i < sv.maxclients; i++, cl++ ) { if ( cl->state >= CS_CONNECTED ) { SV_UserinfoChanged( cl, qfalse, qfalse ); // do not update userinfo, do not run filter } @@ -1253,7 +1252,7 @@ static void SV_Restart( const char *reason ) { if ( svs.clients ) { // check if we can reset map time without full server shutdown - for ( i = 0; i < sv_maxclients->integer; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { sv_shutdown = qtrue; break; @@ -1341,13 +1340,13 @@ void SV_Frame( int msec ) { // try to do silent restart earlier if possible if ( sv.time > (12*3600*1000) && ( sv_levelTimeReset->integer == 0 || sv.time > 0x40000000 ) ) { if ( svs.clients ) { - for ( i = 0; i < sv_maxclients->integer; i++ ) { + for ( i = 0; i < sv.maxclients; i++ ) { // FIXME: deal with bots (reconnect?) if ( svs.clients[i].state != CS_FREE && svs.clients[i].netchan.remoteAddress.type != NA_BOT ) { break; } } - if ( i == sv_maxclients->integer ) { + if ( i == sv.maxclients ) { SV_Restart( "Restarting server" ); return; } diff --git a/code/server/sv_snapshot.c b/code/server/sv_snapshot.c index dffa2b039..37f59cecd 100644 --- a/code/server/sv_snapshot.c +++ b/code/server/sv_snapshot.c @@ -756,10 +756,10 @@ void SV_SendClientMessages( void ) svs.msgTime = Sys_Milliseconds(); // send a message to each connected client - for ( i = 0; i < sv_maxclients->integer; i++ ) + for ( i = 0; i < sv.maxclients; i++ ) { c = &svs.clients[ i ]; - + if ( c->state == CS_FREE ) continue; // not connected @@ -780,7 +780,7 @@ void SV_SendClientMessages( void ) c->rateDelayed = qtrue; continue; // Drop this snapshot if the packet queue is still full or delta compression will break } - + if ( SV_RateMsec( c ) > 0 ) { // Not enough time since last packet passed through the line