/* ================= SV_Netchan_TransmitNextFragment ================= */ void SV_Netchan_TransmitNextFragment( client_t *client ) { Netchan_TransmitNextFragment( &client->netchan ); while ( !client->netchan.unsentFragments && client->netchan_start_queue ) { // make sure the netchan queue has been properly initialized (you never know) //% if (!client->netchan_end_queue) { //% Com_Error(ERR_DROP, "netchan queue is not properly initialized in SV_Netchan_TransmitNextFragment\n"); //% } // the last fragment was transmitted, check wether we have queued messages netchan_buffer_t* netbuf = client->netchan_start_queue; // pop from queue client->netchan_start_queue = netbuf->next; if ( !client->netchan_start_queue ) { client->netchan_end_queue = NULL; } if ( !SV_GameIsSinglePlayer() ) { SV_Netchan_Encode( client, &netbuf->msg, netbuf->lastClientCommandString ); } Netchan_Transmit( &client->netchan, netbuf->msg.cursize, netbuf->msg.data ); Z_Free( netbuf ); } }
/* ================ SV_TransitionGameState NERVE - SMF ================ */ static qboolean SV_TransitionGameState(gamestate_t new_gs, gamestate_t old_gs, int delay) { if (!SV_GameIsSinglePlayer() && !SV_GameIsCoop()) { // we always do a warmup before starting match if (old_gs == GS_INTERMISSION && new_gs == GS_PLAYING) { new_gs = GS_WARMUP; } } // check if its a valid state transition if (!SV_CheckTransitionGameState(new_gs, old_gs)) { return qfalse; } if (new_gs == GS_RESET) { new_gs = GS_WARMUP; } Cvar_Set("gamestate", va("%i", new_gs)); return qtrue; }
/* ================ CL_Netchan_Transmit ================ */ void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) { #if defined RTCW_SP // int i; MSG_WriteByte( msg, clc_EOF ); // for(i=CL_ENCODE_START;i<msg->cursize;i++) { // chksum[i-CL_ENCODE_START] = msg->data[i]; // } // Huff_Compress( msg, CL_ENCODE_START ); #if DO_NET_ENCODE CL_Netchan_Encode( msg ); #endif Netchan_Transmit( chan, msg->cursize, msg->data ); #elif defined RTCW_MP MSG_WriteByte( msg, clc_EOF ); CL_Netchan_Encode( msg ); Netchan_Transmit( chan, msg->cursize, msg->data ); #else MSG_WriteByte( msg, clc_EOF ); CL_WriteBinaryMessage( msg ); if ( !SV_GameIsSinglePlayer() ) { CL_Netchan_Encode( msg ); } Netchan_Transmit( chan, msg->cursize, msg->data ); #endif // RTCW_XX }
/* =============== SV_BoundMaxClients =============== */ void SV_BoundMaxClients( int minimum ) { // get the current maxclients value #ifdef __MACOS__ Cvar_Get( "sv_maxclients", "16", 0 ); //DAJ HOG #else Cvar_Get( "sv_maxclients", "20", 0 ); // NERVE - SMF - changed to 20 from 8 #endif // START xkan, 10/03/2002 // allow many bots in single player. note that this pretty much means all previous // settings will be ignored (including the one set through "seta sv_maxclients <num>" // in user profile's wolfconfig_mp.cfg). also that if the user subsequently start // the server in multiplayer mode, the number of clients will still be the number // set here, which may be wrong - we can certainly just set it to a sensible number // when it is not in single player mode in the else part of the if statement when // necessary if ( SV_GameIsSinglePlayer() || SV_GameIsCoop() ) { Cvar_Set( "sv_maxclients", "64" ); } // END xkan, 10/03/2002 sv_maxclients->modified = qfalse; if ( sv_maxclients->integer < minimum ) { Cvar_Set( "sv_maxclients", va( "%i", minimum ) ); } else if ( sv_maxclients->integer > MAX_CLIENTS ) { Cvar_Set( "sv_maxclients", va( "%i", MAX_CLIENTS ) ); } }
/* ================ CL_Netchan_Transmit ================ */ void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) { MSG_WriteByte( msg, clc_EOF ); CL_WriteBinaryMessage( msg ); if ( !SV_GameIsSinglePlayer() ) { CL_Netchan_Encode( msg ); } Netchan_Transmit( chan, msg->cursize, msg->data ); }
void SV_MasterHeartbeat( const char *hbname ) { static netadr_t adr[MAX_MASTER_SERVERS]; int i; if ( SV_GameIsSinglePlayer() ) { return; // no heartbeats for SP } // "dedicated 1" is for lan play, "dedicated 2" is for inet public play if ( !com_dedicated || com_dedicated->integer != 2 ) { return; // only dedicated servers send heartbeats } // if not time yet, don't send anything if ( svs.time < svs.nextHeartbeatTime ) { return; } svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC; // send to group masters for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) { if ( !sv_master[i]->string[0] ) { continue; } // see if we haven't already resolved the name // resolving usually causes hitches on win95, so only // do it when needed if ( sv_master[i]->modified ) { sv_master[i]->modified = qfalse; Com_Printf( "Resolving %s\n", sv_master[i]->string ); if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) { // if the address failed to resolve, clear it // so we don't take repeated dns hits Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); Cvar_Set( sv_master[i]->name, "" ); sv_master[i]->modified = qfalse; continue; } if ( !strstr( ":", sv_master[i]->string ) ) { adr[i].port = BigShort( PORT_MASTER ); } Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string, adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3], BigShort( adr[i].port ) ); } Com_Printf( "Sending heartbeat to %s\n", sv_master[i]->string ); // this command should be changed if the server info / status format // ever incompatably changes NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", hbname ); } }
/* ================= SVC_GameCompleteStatus NERVE - SMF - Send serverinfo cvars, etc to master servers when game complete. Useful for tracking global player stats. ================= */ void SVC_GameCompleteStatus( netadr_t from ) { char player[1024]; char status[MAX_MSGLEN]; int i; client_t *cl; playerState_t *ps; int statusLength; int playerLength; char infostring[MAX_INFO_STRING]; // ignore if we are in single player if ( SV_GameIsSinglePlayer() ) { return; } //bani - bugtraq 12534 if ( !SV_VerifyChallenge( Cmd_Argv( 1 ) ) ) { return; } strcpy( infostring, Cvar_InfoString( CVAR_SERVERINFO | CVAR_SERVERINFO_NOUPDATE ) ); // echo back the parameter to status. so master servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", Cmd_Argv( 1 ) ); // add "demo" to the sv_keywords if restricted if ( Cvar_VariableValue( "fs_restrict" ) ) { char keywords[MAX_INFO_STRING]; Com_sprintf( keywords, sizeof( keywords ), "ettest %s", Info_ValueForKey( infostring, "sv_keywords" ) ); Info_SetValueForKey( infostring, "sv_keywords", keywords ); } status[0] = 0; statusLength = 0; for ( i = 0 ; i < sv_maxclients->integer ; i++ ) { cl = &svs.clients[i]; if ( cl->state >= CS_CONNECTED ) { ps = SV_GameClientNum( i ); Com_sprintf( player, sizeof( player ), "%i %i \"%s\"\n", ps->persistant[PERS_SCORE], cl->ping, cl->name ); playerLength = strlen( player ); if ( statusLength + playerLength >= sizeof( status ) ) { break; // can't hold any more } strcpy( status + statusLength, player ); statusLength += playerLength; } } NET_OutOfBandPrint( NS_SERVER, from, "gameCompleteStatus\n%s\n%s", infostring, status ); }
/* =============== SV_BotLibSetup =============== */ int SV_BotLibSetup(void) { static cvar_t *bot_norcd; static cvar_t *bot_frameroutingupdates; #ifdef PRE_RELEASE_DEMO return 0; #endif if(!bot_enable) { return 0; } if(!botlib_export) { Com_Printf(S_COLOR_RED "Error: SV_BotLibSetup without SV_BotInitBotLib\n"); return -1; } #if defined(USE_BOTLIB) // RF, set RCD calculation status bot_norcd = Cvar_Get("bot_norcd", "0", 0); botlib_export->BotLibVarSet("bot_norcd", bot_norcd->string); // RF, set AAS routing max per frame if(SV_GameIsSinglePlayer()) { bot_frameroutingupdates = Cvar_Get("bot_frameroutingupdates", "9999999", 0); } else { // more restrictive in multiplayer bot_frameroutingupdates = Cvar_Get("bot_frameroutingupdates", "1000", 0); } botlib_export->BotLibVarSet("bot_frameroutingupdates", bot_frameroutingupdates->string); // START Arnout changes, 28-08-2002. // added single player return botlib_export->BotLibSetup((SV_GameIsSinglePlayer() || SV_GameIsCoop())); // END Arnout changes, 28-08-2002. #endif }
/* ================= Netchan_SV_Process ================= */ qboolean SV_Netchan_Process( client_t *client, msg_t *msg ) { int ret; ret = Netchan_Process( &client->netchan, msg ); if ( !ret ) { return qfalse; } if ( !SV_GameIsSinglePlayer() ) { SV_Netchan_Decode( client, msg ); } return qtrue; }
/* ================= CL_Netchan_Process ================= */ qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) { int ret; ret = Netchan_Process( chan, msg ); if ( !ret ) { return qfalse; } if ( !SV_GameIsSinglePlayer() ) { CL_Netchan_Decode( msg ); } newsize += msg->cursize; return qtrue; }
/* ================= SV_MasterGameCompleteStatus NERVE - SMF - Sends gameCompleteStatus messages to all master servers ================= */ void SV_MasterGameCompleteStatus() { static netadr_t adr[MAX_MASTER_SERVERS]; int i; if ( SV_GameIsSinglePlayer() ) { return; // no master game status for SP } // "dedicated 1" is for lan play, "dedicated 2" is for inet public play if ( !com_dedicated || com_dedicated->integer != 2 ) { return; // only dedicated servers send master game status } // send to group masters for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) { if ( !sv_master[i]->string[0] ) { continue; } // see if we haven't already resolved the name // resolving usually causes hitches on win95, so only // do it when needed if ( sv_master[i]->modified ) { sv_master[i]->modified = qfalse; Com_Printf( "Resolving %s\n", sv_master[i]->string ); if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) { // if the address failed to resolve, clear it // so we don't take repeated dns hits Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); Cvar_Set( sv_master[i]->name, "" ); sv_master[i]->modified = qfalse; continue; } if ( !strstr( ":", sv_master[i]->string ) ) { adr[i].port = BigShort( PORT_MASTER ); } Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string, adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3], BigShort( adr[i].port ) ); } Com_Printf( "Sending gameCompleteStatus to %s\n", sv_master[i]->string ); // this command should be changed if the server info / status format // ever incompatably changes SVC_GameCompleteStatus( adr[i] ); } }
/* ================= CL_Netchan_Process ================= */ qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) { int ret; #if defined RTCW_SP // int i; // static int newsize = 0; #endif // RTCW_XX ret = Netchan_Process( chan, msg ); if ( !ret ) { return qfalse; } #if (defined RTCW_SP && DO_NET_ENCODE) || defined RTCW_MP CL_Netchan_Decode( msg ); #elif defined RTCW_ET if ( !SV_GameIsSinglePlayer() ) { CL_Netchan_Decode( msg ); } #endif #if defined RTCW_SP // Huff_Decompress( msg, CL_DECODE_START ); // for(i=CL_DECODE_START+msg->readcount;i<msg->cursize;i++) { // if (msg->data[i] != chksum[i-(CL_DECODE_START+msg->readcount)]) { // Com_Error(ERR_DROP,"bad %d v %d\n", msg->data[i], chksum[i-(CL_DECODE_START+msg->readcount)]); // } // } #endif // RTCW_XX newsize += msg->cursize; #if defined RTCW_SP // Com_Printf("saved %d to %d (%d%%)\n", (oldsize>>3), newsize, 100-(newsize*100/(oldsize>>3))); #endif // RTCW_XX return qtrue; }
/* =============== SV_Netchan_Transmit TTimo show_bug.cgi?id=462 if there are some unsent fragments (which may happen if the snapshots and the gamestate are fragmenting, and collide on send for instance) then buffer them and make sure they get sent in correct order ================ */ void SV_Netchan_Transmit( client_t *client, msg_t *msg ) { //int length, const byte *data ) { MSG_WriteByte( msg, svc_EOF ); SV_WriteBinaryMessage( msg, client ); if ( client->netchan.unsentFragments ) { netchan_buffer_t *netbuf; //Com_DPrintf("SV_Netchan_Transmit: there are unsent fragments remaining\n"); netbuf = (netchan_buffer_t *)Z_Malloc( sizeof( netchan_buffer_t ) ); // store the msg, we can't store it encoded, as the encoding depends on stuff we still have to finish sending MSG_Copy( &netbuf->msg, netbuf->msgBuffer, sizeof( netbuf->msgBuffer ), msg ); // copy the command, since the command number used for encryption is // already compressed in the buffer, and receiving a new command would // otherwise lose the proper encryption key strcpy( netbuf->lastClientCommandString, client->lastClientCommandString ); // insert it in the queue, the message will be encoded and sent later //% *client->netchan_end_queue = netbuf; //% client->netchan_end_queue = &(*client->netchan_end_queue)->next; netbuf->next = NULL; if ( !client->netchan_start_queue ) { client->netchan_start_queue = netbuf; } else { client->netchan_end_queue->next = netbuf; } client->netchan_end_queue = netbuf; // emit the next fragment of the current message for now Netchan_TransmitNextFragment( &client->netchan ); } else { if ( !SV_GameIsSinglePlayer() ) { SV_Netchan_Encode( client, msg, client->lastClientCommandString ); } Netchan_Transmit( &client->netchan, msg->cursize, msg->data ); } }
/* ================= SV_SendServerCommand Sends a reliable command string to be interpreted by the client game module: "cp", "print", "chat", etc A NULL client will broadcast to all clients ================= */ void QDECL SV_SendServerCommand( client_t *cl, const char *fmt, ... ) { va_list argptr; byte message[MAX_MSGLEN]; client_t *client; int j; va_start( argptr,fmt ); #if defined RTCW_SP vsprintf( (char *)message, fmt,argptr ); #else Q_vsnprintf( (char *)message, sizeof( message ), fmt, argptr ); #endif // RTCW_XX va_end( argptr ); #if !defined RTCW_SP // do not forward server command messages that would be too big to clients // ( q3infoboom / q3msgboom stuff ) if ( strlen( (char *)message ) > 1022 ) { return; } #endif // RTCW_XX if ( cl != NULL ) { SV_AddServerCommand( cl, (char *)message ); return; } // hack to echo broadcast prints to console if ( com_dedicated->integer && !strncmp( (char *)message, "print", 5 ) ) { Com_Printf( "broadcast: %s\n", SV_ExpandNewlines( (char *)message ) ); } // send the data to all relevent clients for ( j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++ ) { if ( client->state < CS_PRIMED ) { continue; } // Ridah, don't need to send messages to AI #if !defined RTCW_ET if ( client->gentity && client->gentity->r.svFlags & SVF_CASTAI ) { #else if ( client->gentity && client->gentity->r.svFlags & SVF_BOT ) { #endif // RTCW_XX continue; } // done. SV_AddServerCommand( client, (char *)message ); } } /* ============================================================================== MASTER SERVER FUNCTIONS ============================================================================== */ /* ================ SV_MasterHeartbeat Send a message to the masters every few minutes to let it know we are alive, and log information. We will also have a heartbeat sent when a server changes from empty to non-empty, and full to non-full, but not on every player enter or exit. ================ */ #define HEARTBEAT_MSEC 300 * 1000 #if !defined RTCW_ET #define HEARTBEAT_GAME "Wolfenstein-1" #else //#define HEARTBEAT_GAME "Wolfenstein-1" //#define HEARTBEAT_DEAD "WolfFlatline-1" // NERVE - SMF #define HEARTBEAT_GAME "EnemyTerritory-1" #endif // RTCW_XX #if defined RTCW_SP void SV_MasterHeartbeat( void ) { #else #if !defined RTCW_ET #define HEARTBEAT_DEAD "WolfFlatline-1" // NERVE - SMF #else #define HEARTBEAT_DEAD "ETFlatline-1" // NERVE - SMF #endif // RTCW_XX void SV_MasterHeartbeat( const char *hbname ) { #endif // RTCW_XX static netadr_t adr[MAX_MASTER_SERVERS]; int i; #if defined RTCW_MP // DHM - Nerve :: Update Server doesn't send heartbeat #ifdef UPDATE_SERVER return; #endif #endif // RTCW_XX #if defined RTCW_ET if ( SV_GameIsSinglePlayer() ) { return; // no heartbeats for SP } #endif // RTCW_XX // "dedicated 1" is for lan play, "dedicated 2" is for inet public play if ( !com_dedicated || com_dedicated->integer != 2 ) { return; // only dedicated servers send heartbeats } // if not time yet, don't send anything if ( svs.time < svs.nextHeartbeatTime ) { return; } svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC; // send to group masters for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) { if ( !sv_master[i]->string[0] ) { continue; } // see if we haven't already resolved the name // resolving usually causes hitches on win95, so only // do it when needed if ( sv_master[i]->modified ) { sv_master[i]->modified = qfalse; Com_Printf( "Resolving %s\n", sv_master[i]->string ); if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) { // if the address failed to resolve, clear it // so we don't take repeated dns hits Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); Cvar_Set( sv_master[i]->name, "" ); sv_master[i]->modified = qfalse; continue; } if ( !strstr( ":", sv_master[i]->string ) ) { adr[i].port = rtcw::Endian::be( PORT_MASTER ); } Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string, adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3], rtcw::Endian::be( adr[i].port ) ); } Com_Printf( "Sending heartbeat to %s\n", sv_master[i]->string ); // this command should be changed if the server info / status format // ever incompatably changes #if defined RTCW_SP NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", HEARTBEAT_GAME ); #else NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", hbname ); #endif // RTCW_XX } } #if !defined RTCW_SP /* ================= SV_MasterGameCompleteStatus NERVE - SMF - Sends gameCompleteStatus messages to all master servers ================= */ void SV_MasterGameCompleteStatus() { static netadr_t adr[MAX_MASTER_SERVERS]; int i; #if defined RTCW_ET if ( SV_GameIsSinglePlayer() ) { return; // no master game status for SP } #endif // RTCW_XX // "dedicated 1" is for lan play, "dedicated 2" is for inet public play if ( !com_dedicated || com_dedicated->integer != 2 ) { return; // only dedicated servers send master game status } // send to group masters for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) { if ( !sv_master[i]->string[0] ) { continue; } // see if we haven't already resolved the name // resolving usually causes hitches on win95, so only // do it when needed if ( sv_master[i]->modified ) { sv_master[i]->modified = qfalse; Com_Printf( "Resolving %s\n", sv_master[i]->string ); if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) { // if the address failed to resolve, clear it // so we don't take repeated dns hits Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); Cvar_Set( sv_master[i]->name, "" ); sv_master[i]->modified = qfalse; continue; } if ( !strstr( ":", sv_master[i]->string ) ) { adr[i].port = rtcw::Endian::be ( PORT_MASTER ); } Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string, adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3], rtcw::Endian::be ( adr[i].port ) ); } Com_Printf( "Sending gameCompleteStatus to %s\n", sv_master[i]->string ); // this command should be changed if the server info / status format // ever incompatably changes SVC_GameCompleteStatus( adr[i] ); } } #endif // RTCW_XX /* ================= SV_MasterShutdown Informs all masters that this server is going down ================= */ void SV_MasterShutdown( void ) { // send a hearbeat right now svs.nextHeartbeatTime = -9999; #if defined RTCW_SP SV_MasterHeartbeat(); #else SV_MasterHeartbeat( HEARTBEAT_DEAD ); // NERVE - SMF - changed to flatline #endif // RTCW_XX // send it again to minimize chance of drops #if defined RTCW_SP svs.nextHeartbeatTime = -9999; SV_MasterHeartbeat(); #else // svs.nextHeartbeatTime = -9999; // SV_MasterHeartbeat( HEARTBEAT_DEAD ); #endif // RTCW_XX // when the master tries to poll the server, it won't respond, so // it will be removed from the list }
/* =============== SV_MasterHeartbeat =============== */ void SV_MasterHeartbeat( const char *hbname ) { static netadr_t adr[MAX_MASTER_SERVERS][2]; int i, res, netenabled; // Update Server doesn't send heartbeat #if defined (UPDATE_SERVER) return; #endif netenabled = Cvar_VariableIntegerValue("net_enabled"); if ( SV_GameIsSinglePlayer() ) { return; // no heartbeats for SP } // "dedicated 1" is for lan play, "dedicated 2" is for inet public play if (!com_dedicated || com_dedicated->integer != 2 || !(netenabled & (NET_ENABLEV4 | NET_ENABLEV6))) { return; // only dedicated servers send heartbeats } // if not time yet, don't send anything if ( svs.time < svs.nextHeartbeatTime ) { return; } svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC; // send to group masters for (i = 0; i < MAX_MASTER_SERVERS; i++) { if(!sv_master[i]->string[0]) { continue; } // see if we haven't already resolved the name // resolving usually causes hitches on win95, so only // do it when needed if(sv_master[i]->modified || (adr[i][0].type == NA_BAD && adr[i][1].type == NA_BAD)) { sv_master[i]->modified = false; if(netenabled & NET_ENABLEV4) { Com_Printf("Resolving %s (IPv4)\n", sv_master[i]->string); res = NET_StringToAdr(sv_master[i]->string, &adr[i][0], NA_IP); if(res == 2) { // if no port was specified, use the default master port adr[i][0].port = BigShort(PORT_MASTER); } if(res) { Com_Printf( "%s resolved to %s\n", sv_master[i]->string, NET_AdrToStringwPort(adr[i][0])); } else { Com_Printf( "%s has no IPv4 address.\n", sv_master[i]->string); } } if(netenabled & NET_ENABLEV6) { Com_Printf("Resolving %s (IPv6)\n", sv_master[i]->string); res = NET_StringToAdr(sv_master[i]->string, &adr[i][1], NA_IP6); if(res == 2) { // if no port was specified, use the default master port adr[i][1].port = BigShort(PORT_MASTER); } if(res) { Com_Printf( "%s resolved to %s\n", sv_master[i]->string, NET_AdrToStringwPort(adr[i][1])); } else { Com_Printf( "%s has no IPv6 address.\n", sv_master[i]->string); } } if(adr[i][0].type == NA_BAD && adr[i][1].type == NA_BAD) { // if the address failed to resolve, clear it // so we don't take repeated dns hits Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); Cvar_Set( sv_master[i]->name, "" ); sv_master[i]->modified = false; continue; } } Com_Printf( "Sending heartbeat to the official OpenWolf servers list %s\n", sv_master[i]->string ); // this command should be changed if the server info / status format // ever incompatably changes if(adr[i][0].type != NA_BAD) { NET_OutOfBandPrint( NS_SERVER, adr[i][0], "heartbeat %s\n", HEARTBEAT_GAME ); } if(adr[i][1].type != NA_BAD) { NET_OutOfBandPrint( NS_SERVER, adr[i][1], "heartbeat %s\n", HEARTBEAT_GAME ); } } #if defined (USE_PHP) // Send to the main master. if (svs.queryDone) { pthread_exit(&svs.thQuery); svs.queryDone = 0; } else { SV_PHPMaster(); } #endif }
/* ================ SVC_Info Responds with a short info message that should be enough to determine if a user is interested in a server to do a full status ================ */ void SVC_Info(netadr_t from) { int i, count; char *gamedir, infostring[MAX_INFO_STRING], *antilag, *weaprestrict, *balancedteams; // ignore if we are in single player if(SV_GameIsSinglePlayer()) { return; } //bani - bugtraq 12534 if(!SV_VerifyChallenge(Cmd_Argv(1))) { return; } /* * Check whether Cmd_Argv(1) has a sane length. This was not done in the original Quake3 version which led * to the Infostring bug discovered by Luigi Auriemma. See http://aluigi.altervista.org/ for the advisory. */ // A maximum challenge length of 128 should be more than plenty. if(strlen(Cmd_Argv(1)) > 128) { return; } #if defined (UPDATE_SERVER) return; #endif // don't count privateclients count = 0; for(i = sv_privateClients->integer; i < sv_maxclients->integer; i++) { if(svs.clients[i].state >= CS_CONNECTED) { count++; } } infostring[0] = 0; // echo back the parameter to status. so servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey(infostring, "challenge", Cmd_Argv(1)); Info_SetValueForKey(infostring, "protocol", va("%i", com_protocol->integer)); Info_SetValueForKey(infostring, "hostname", sv_hostname->string); Info_SetValueForKey(infostring, "serverload", va("%i", svs.serverLoad)); Info_SetValueForKey(infostring, "mapname", sv_mapname->string); Info_SetValueForKey(infostring, "clients", va("%i", count)); 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, "gametype", Cvar_VariableString("g_gametype")); Info_SetValueForKey(infostring, "pure", va("%i", sv_pure->integer)); #ifdef USE_VOIP if (sv_voip->integer) { Info_SetValueForKey( infostring, "voip", va("%i", sv_voip->integer ) ); } #endif if(sv_minPing->integer) { Info_SetValueForKey(infostring, "minPing", va("%i", sv_minPing->integer)); } if(sv_maxPing->integer) { Info_SetValueForKey(infostring, "maxPing", va("%i", sv_maxPing->integer)); } gamedir = Cvar_VariableString("fs_game"); if(*gamedir) { Info_SetValueForKey(infostring, "game", gamedir); } Info_SetValueForKey(infostring, "sv_allowAnonymous", va("%i", sv_allowAnonymous->integer)); // Rafael gameskill // Info_SetValueForKey (infostring, "gameskill", va ("%i", sv_gameskill->integer)); // done Info_SetValueForKey(infostring, "friendlyFire", va("%i", sv_friendlyFire->integer)); // NERVE - SMF Info_SetValueForKey(infostring, "maxlives", va("%i", sv_maxlives->integer ? 1 : 0)); // NERVE - SMF Info_SetValueForKey(infostring, "needpass", va("%i", sv_needpass->integer ? 1 : 0)); Info_SetValueForKey(infostring, "gamename", GAMENAME_STRING); // Arnout: to be able to filter out Quake servers // TTimo antilag = Cvar_VariableString("g_antilag"); if(antilag) { Info_SetValueForKey(infostring, "g_antilag", antilag); } weaprestrict = Cvar_VariableString("g_heavyWeaponRestriction"); if(weaprestrict) { Info_SetValueForKey(infostring, "weaprestrict", weaprestrict); } balancedteams = Cvar_VariableString("g_balancedteams"); if(balancedteams) { Info_SetValueForKey(infostring, "balancedteams", balancedteams); } NET_OutOfBandPrint(NS_SERVER, from, "infoResponse\n%s", infostring); }
/* ================ SVC_Info Responds with a short info message that should be enough to determine if a user is interested in a server to do a full status ================ */ void SVC_Info( netadr_t from ) { int i, count; const char *gamedir; char infostring[MAX_INFO_STRING]; #if defined RTCW_MP const char *antilag; // DHM - Nerve #ifdef UPDATE_SERVER return; #endif #endif // RTCW_XX #if defined RTCW_ET const char *antilag; const char *weaprestrict; const char *balancedteams; // ignore if we are in single player if ( SV_GameIsSinglePlayer() ) { return; } #endif // RTCW_XX #if !defined RTCW_ET // ignore if we are in single player if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) { return; } #endif // RTCW_XX #if defined RTCW_ET //bani - bugtraq 12534 if ( !SV_VerifyChallenge( Cmd_Argv( 1 ) ) ) { return; } #endif // RTCW_XX // don't count privateclients count = 0; for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { count++; } } infostring[0] = 0; // echo back the parameter to status. so servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", Cmd_Argv( 1 ) ); Info_SetValueForKey( infostring, "protocol", va( "%i", PROTOCOL_VERSION ) ); Info_SetValueForKey( infostring, "hostname", sv_hostname->string ); #if defined RTCW_ET Info_SetValueForKey( infostring, "serverload", va( "%i", svs.serverLoad ) ); #endif // RTCW_XX Info_SetValueForKey( infostring, "mapname", sv_mapname->string ); Info_SetValueForKey( infostring, "clients", va( "%i", count ) ); Info_SetValueForKey( infostring, "sv_maxclients", va( "%i", sv_maxclients->integer - sv_privateClients->integer ) ); #if !defined RTCW_ET Info_SetValueForKey( infostring, "gametype", va( "%i", sv_gametype->integer ) ); #else Info_SetValueForKey( infostring, "gametype", Cvar_VariableString( "g_gametype" ) ); #endif // RTCW_XX Info_SetValueForKey( infostring, "pure", va( "%i", sv_pure->integer ) ); if ( sv_minPing->integer ) { Info_SetValueForKey( infostring, "minPing", va( "%i", sv_minPing->integer ) ); } if ( sv_maxPing->integer ) { Info_SetValueForKey( infostring, "maxPing", va( "%i", sv_maxPing->integer ) ); } gamedir = Cvar_VariableString( "fs_game" ); if ( *gamedir ) { Info_SetValueForKey( infostring, "game", gamedir ); } Info_SetValueForKey( infostring, "sv_allowAnonymous", va( "%i", sv_allowAnonymous->integer ) ); // Rafael gameskill #if !defined RTCW_ET Info_SetValueForKey( infostring, "gameskill", va( "%i", sv_gameskill->integer ) ); #else // Info_SetValueForKey (infostring, "gameskill", va ("%i", sv_gameskill->integer)); #endif // RTCW_XX // done #if !defined RTCW_SP Info_SetValueForKey( infostring, "friendlyFire", va( "%i", sv_friendlyFire->integer ) ); // NERVE - SMF Info_SetValueForKey( infostring, "maxlives", va( "%i", sv_maxlives->integer ? 1 : 0 ) ); // NERVE - SMF #if !defined RTCW_ET Info_SetValueForKey( infostring, "tourney", va( "%i", sv_tourney->integer ) ); // NERVE - SMF #endif // RTCW_XX #if defined RTCW_ET Info_SetValueForKey( infostring, "needpass", va( "%i", sv_needpass->integer ? 1 : 0 ) ); #endif // RTCW_XX Info_SetValueForKey( infostring, "gamename", GAMENAME_STRING ); // Arnout: to be able to filter out Quake servers // TTimo antilag = Cvar_VariableString( "g_antilag" ); if ( antilag ) { Info_SetValueForKey( infostring, "g_antilag", antilag ); } #endif // RTCW_XX #if defined RTCW_ET weaprestrict = Cvar_VariableString( "g_heavyWeaponRestriction" ); if ( weaprestrict ) { Info_SetValueForKey( infostring, "weaprestrict", weaprestrict ); } balancedteams = Cvar_VariableString( "g_balancedteams" ); if ( balancedteams ) { Info_SetValueForKey( infostring, "balancedteams", balancedteams ); } #endif // RTCW_XX NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring ); }
void SV_UpdateConfigStrings( void ) { int len, i, index; client_t *client; int maxChunkSize = MAX_STRING_CHARS - 24; for ( index = 0; index < MAX_CONFIGSTRINGS; index++ ) { if ( !sv.configstringsmodified[ index ] ) { continue; } sv.configstringsmodified[ index ] = qfalse; // send it to all the clients if we aren't // spawning a new server if ( sv.state == SS_GAME || sv.restarting ) { // send the data to all relevent clients for ( i = 0, client = svs.clients; i < sv_maxclients->integer; i++, client++ ) { if ( client->state < CS_PRIMED ) { continue; } // do not always send server info to all clients if ( index == CS_SERVERINFO && client->gentity && ( client->gentity->r.svFlags & SVF_NOSERVERINFO ) ) { continue; } // RF, don't send to bot/AI // Gordon: Note: might want to re-enable later for bot support // RF, re-enabled // Arnout: removed hardcoded gametype // Arnout: added coop if ( ( SV_GameIsSinglePlayer() || SV_GameIsCoop() ) && client->gentity && ( client->gentity->r.svFlags & SVF_BOT ) ) { continue; } len = strlen( sv.configstrings[ index ] ); if ( len >= maxChunkSize ) { int sent = 0; int remaining = len; char *cmd; char buf[ MAX_STRING_CHARS ]; while ( remaining > 0 ) { if ( sent == 0 ) { cmd = "bcs0"; } else if ( remaining < maxChunkSize ) { cmd = "bcs2"; } else { cmd = "bcs1"; } Q_strncpyz( buf, &sv.configstrings[ index ][ sent ], maxChunkSize ); SV_SendServerCommand( client, "%s %i \"%s\"\n", cmd, index, buf ); sent += ( maxChunkSize - 1 ); remaining -= ( maxChunkSize - 1 ); } } else { // standard cs, just send it SV_SendServerCommand( client, "cs %i \"%s\"\n", index, sv.configstrings[ index ] ); } } } } }
/* ================== SV_Map_f Restart the server on a different map ================== */ static void SV_Map_f(void) { char *cmd, *map, smapname[MAX_QPATH], mapname[MAX_QPATH], expanded[MAX_QPATH], *cl_profileStr = Cvar_VariableString("cl_profile"); qboolean killBots, cheat, buildScript; int savegameTime = -1; map = Cmd_Argv(1); if(!map) { return; } if(!com_gameInfo.spEnabled) { if(!Q_stricmp(Cmd_Argv(0), "spdevmap") || !Q_stricmp(Cmd_Argv(0), "spmap")) { Com_Printf("Single Player is not enabled.\n"); return; } } buildScript = (qboolean)Cvar_VariableIntegerValue("com_buildScript"); if(SV_GameIsSinglePlayer()) { if(!buildScript && sv_reloading->integer && sv_reloading->integer != RELOAD_NEXTMAP) { // game is in 'reload' mode, don't allow starting new maps yet. return; } // Trap a savegame load if(strstr(map, ".sav")) { // open the savegame, read the mapname, and copy it to the map string char savemap[MAX_QPATH], savedir[MAX_QPATH]; byte *buffer; int size, csize; if(com_gameInfo.usesProfiles && cl_profileStr[0]) { Com_sprintf(savedir, sizeof(savedir), "profiles/%s/save/", cl_profileStr); } else { Q_strncpyz(savedir, "save/", sizeof(savedir)); } if(!(strstr(map, savedir) == map)) { Com_sprintf(savemap, sizeof(savemap), "%s%s", savedir, map); } else { strcpy(savemap, map); } size = FS_ReadFile(savemap, NULL); if(size < 0) { Com_Printf("Can't find savegame %s\n", savemap); return; } //buffer = Hunk_AllocateTempMemory(size); FS_ReadFile(savemap, (void **)&buffer); if(Q_stricmp(savemap, va("%scurrent.sav", savedir)) != 0) { // copy it to the current savegame file FS_WriteFile(va("%scurrent.sav", savedir), buffer, size); // make sure it is the correct size csize = FS_ReadFile(va("%scurrent.sav", savedir), NULL); if(csize != size) { Hunk_FreeTempMemory(buffer); FS_Delete(va("%scurrent.sav", savedir)); // TTimo #ifdef __linux__ Com_Error(ERR_DROP, "Unable to save game.\n\nPlease check that you have at least 5mb free of disk space in your home directory."); #else Com_Error(ERR_DROP, "Insufficient free disk space.\n\nPlease free at least 5mb of free space on game drive."); #endif return; } } // set the cvar, so the game knows it needs to load the savegame once the clients have connected Cvar_Set("savegame_loading", "1"); // set the filename Cvar_Set("savegame_filename", savemap); // the mapname is at the very start of the savegame file Q_strncpyz(savemap, (char *)(buffer + sizeof(int)), sizeof(savemap)); // skip the version Q_strncpyz(smapname, savemap, sizeof(smapname)); map = smapname; savegameTime = *(int *)(buffer + sizeof(int) + MAX_QPATH); if(savegameTime >= 0) { svs.time = savegameTime; } Hunk_FreeTempMemory(buffer); } else { Cvar_Set("savegame_loading", "0"); // make sure it's turned off // set the filename Cvar_Set("savegame_filename", ""); } } else { Cvar_Set("savegame_loading", "0"); // make sure it's turned off // set the filename Cvar_Set("savegame_filename", ""); } // make sure the level exists before trying to change, so that // a typo at the server console won't end the game Com_sprintf(expanded, sizeof(expanded), "maps/%s.bsp", map); if(FS_ReadFile(expanded, NULL) == -1) { Com_Printf("Can't find map %s\n", expanded); return; } Cvar_Set("gamestate", va("%i", GS_INITIALIZE)); // NERVE - SMF - reset gamestate on map/devmap Cvar_Set("g_currentRound", "0"); // NERVE - SMF - reset the current round Cvar_Set("g_nextTimeLimit", "0"); // NERVE - SMF - reset the next time limit // START Mad Doctor I changes, 8/14/2002. Need a way to force load a single player map as single player if(!Q_stricmp(Cmd_Argv(0), "spdevmap") || !Q_stricmp(Cmd_Argv(0), "spmap")) { // This is explicitly asking for a single player load of this map Cvar_Set("g_gametype", va("%i", com_gameInfo.defaultSPGameType)); // force latched values to get set Cvar_Get("g_gametype", va("%i", com_gameInfo.defaultSPGameType), CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH, "test"); // enable bot support for AI Cvar_Set("bot_enable", "1"); } // Rafael gameskill // Cvar_Get ("g_gameskill", "3", CVAR_SERVERINFO | CVAR_LATCH); // done cmd = Cmd_Argv(0); if(!Q_stricmp(cmd, "devmap")) { cheat = qtrue; killBots = qtrue; } else if(!Q_stricmp(Cmd_Argv(0), "spdevmap")) { cheat = qtrue; killBots = qtrue; } else { cheat = qfalse; killBots = qfalse; } // save the map name here cause on a map restart we reload the q3config.cfg // and thus nuke the arguments of the map command Q_strncpyz(mapname, map, sizeof(mapname)); // start up the map SV_SpawnServer(mapname, killBots); // set the cheat value // if the level was started with "map <levelname>", then // cheats will not be allowed. If started with "devmap <levelname>" // then cheats will be allowed if(cheat) { Cvar_Set("sv_cheats", "1"); } else { Cvar_Set("sv_cheats", "0"); } }
/* ================ SV_SpawnServer Change the server to a new map, taking all connected clients along with it. This is NOT called for map_restart ================ */ void SV_SpawnServer( char *server, qboolean killBots ) { int i; int checksum; qboolean isBot; const char *p; // shut down the existing game if it is running SV_ShutdownGameProgs(); Com_Printf( "------ Server Initialization ------\n" ); Com_Printf( "Server: %s\n", server ); // if not running a dedicated server CL_MapLoading will connect the client to the server // also print some status stuff CL_MapLoading(); // make sure all the client stuff is unloaded CL_ShutdownAll(); // clear the whole hunk because we're (re)loading the server Hunk_Clear(); // clear collision map data // (SA) NOTE: TODO: used in missionpack CM_ClearMap(); // wipe the entire per-level structure SV_ClearServer(); // MrE: main zone should be pretty much emtpy at this point // except for file system data and cached renderer data Z_LogHeap(); // allocate empty config strings for ( i = 0; i < MAX_CONFIGSTRINGS; i++ ) { sv.configstrings[ i ] = CopyString( "" ); sv.configstringsmodified[ i ] = qfalse; } // init client structures and svs.numSnapshotEntities if ( !Cvar_VariableValue( "sv_running" ) ) { SV_Startup(); } else { // check for maxclients change if ( sv_maxclients->modified ) { SV_ChangeMaxClients(); } #ifdef USE_HUB_SERVER // if sv_owHubHost was changed, resolve the address again if ( sv_owHubHost->modified ) { sv_owHubHost->modified = qfalse; SV_ResolveowHubHost(); } #endif } // clear pak references FS_ClearPakReferences( 0 ); // allocate the snapshot entities on the hunk svs.snapshotEntities = Hunk_Alloc( sizeof( entityState_t ) * svs.numSnapshotEntities, h_high ); svs.nextSnapshotEntities = 0; // toggle the server bit so clients can detect that a // server has changed svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT; // set nextmap to the same map, but it may be overriden // by the game startup or another console command Cvar_Set( "nextmap", "map_restart 0" ); // Cvar_Set( "nextmap", va("map %s", server) ); // Ridah // DHM - Nerve :: We want to use the completion bar in multiplayer as well // Arnout: just always use it // if( !SV_GameIsSinglePlayer() ) { SV_SetExpectedHunkUsage( va( "maps/%s.bsp", server ) ); // } else { // just set it to a negative number,so the cgame knows not to draw the percent bar // Cvar_Set( "com_expectedhunkusage", "-1" ); // } // make sure we are not paused Cvar_Set( "cl_paused", "0" ); #if !defined( DO_LIGHT_DEDICATED ) // get a new checksum feed and restart the file system srand( Sys_Milliseconds() ); sv.checksumFeed = ( ( ( int ) rand() << 16 ) ^ rand() ) ^ Sys_Milliseconds(); // DO_LIGHT_DEDICATED // only comment out when you need a new pure checksum string and it's associated random feed //Com_DPrintf("SV_SpawnServer checksum feed: %p\n", sv.checksumFeed); #else // DO_LIGHT_DEDICATED implementation below // we are not able to randomize the checksum feed since the feed is used as key for pure_checksum computations // files.c 1776 : pack->pure_checksum = Com_BlockChecksumKey( fs_headerLongs, 4 * fs_numHeaderLongs, LittleLong(fs_checksumFeed) ); // we request a fake randomized feed, files.c knows the answer srand( Sys_Milliseconds() ); sv.checksumFeed = FS_RandChecksumFeed(); #endif FS_Restart( sv.checksumFeed ); CM_LoadMap( va( "maps/%s.bsp", server ), qfalse, &checksum ); // set serverinfo visible name Cvar_Set( "mapname", server ); Cvar_Set( "sv_mapChecksum", va( "%i", checksum ) ); sv_newGameShlib = Cvar_Get( "sv_newGameShlib", "", CVAR_TEMP ); // 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 ) ); // clear physics interaction links SV_ClearWorld(); // media configstring setting should be done during // the loading stage, so connected clients don't have // to load during actual gameplay sv.state = SS_LOADING; Cvar_Set( "sv_serverRestarting", "1" ); // load and spawn all other entities SV_InitGameProgs(); // don't allow a map_restart if game is modified // Arnout: there isn't any check done against this, obsolete // sv_gametype->modified = qfalse; // run a few frames to allow everything to settle for ( i = 0; i < GAME_INIT_FRAMES; i++ ) { VM_Call( gvm, GAME_RUN_FRAME, svs.time ); SV_BotFrame( svs.time ); svs.time += FRAMETIME; } // create a baseline for more efficient communications SV_CreateBaseline(); for ( i = 0; i < sv_maxclients->integer; i++ ) { // send the new gamestate to all connected clients if ( svs.clients[ i ].state >= CS_CONNECTED ) { char *denied; if ( svs.clients[ i ].netchan.remoteAddress.type == NA_BOT ) { if ( killBots || SV_GameIsSinglePlayer() || SV_GameIsCoop() ) { SV_DropClient( &svs.clients[ i ], "" ); continue; } isBot = qtrue; } else { isBot = qfalse; } // connect the client again denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse, isBot ) ); // firstTime = qfalse if ( denied ) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropClient( &svs.clients[ i ], denied ); } else { if ( !isBot ) { // when we get the next packet from a connected client, // the new gamestate will be sent svs.clients[ i ].state = CS_CONNECTED; } 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 = -1; client->nextSnapshotTime = svs.time; // generate a snapshot immediately VM_Call( gvm, GAME_CLIENT_BEGIN, i ); } } } } // run another frame to allow things to look at all the players VM_Call( gvm, GAME_RUN_FRAME, svs.time ); SV_BotFrame( svs.time ); svs.time += FRAMETIME; if ( sv_pure->integer ) { // the server sends these to the clients so they will only // load pk3s also loaded at the server p = FS_LoadedPakChecksums(); Cvar_Set( "sv_paks", p ); if ( strlen( p ) == 0 ) { Com_Printf( "WARNING: sv_pure set but no PK3 files loaded\n" ); } p = FS_LoadedPakNames(); Cvar_Set( "sv_pakNames", p ); // if a dedicated pure server we need to touch the cgame because it could be in a // seperate pk3 file and the client will need to load the latest cgame.qvm if ( com_dedicated->integer ) { SV_TouchCGame(); } } else { Cvar_Set( "sv_paks", "" ); Cvar_Set( "sv_pakNames", "" ); } // the server sends these to the clients so they can figure // out which pk3s should be auto-downloaded // NOTE: we consider the referencedPaks as 'required for operation' // we want the server to reference the mp_bin pk3 that the client is expected to load from SV_TouchCGameDLL(); p = FS_ReferencedPakChecksums(); Cvar_Set( "sv_referencedPaks", p ); p = FS_ReferencedPakNames(); Cvar_Set( "sv_referencedPakNames", p ); // save systeminfo and serverinfo strings cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString_Big( CVAR_SYSTEMINFO ) ); SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO | CVAR_SERVERINFO_NOUPDATE ) ); cvar_modifiedFlags &= ~CVAR_SERVERINFO; // any media configstring setting now should issue a warning // and any configstring changes should be reliably transmitted // to all clients sv.state = SS_GAME; // send a heartbeat now so the master will get up to date info SV_Heartbeat_f(); Hunk_SetMark(); SV_UpdateConfigStrings(); Cvar_Set( "sv_serverRestarting", "0" ); Com_Printf( "-----------------------------------\n" ); }
/* ================ SV_MapRestart_f Completely restarts a level, but doesn't send a new gamestate to the clients. This allows fair starts with variable load times. ================ */ static void SV_MapRestart_f(void) { int i, delay = 0; client_t *client; char *denied; qboolean isBot; gamestate_t new_gs, old_gs; // NERVE - SMF // make sure we aren't restarting twice in the same frame if(com_frameTime == sv.serverId) { return; } // make sure server is running if(!com_sv_running->integer) { Com_Printf("Server is not running.\n"); return; } if(Cmd_Argc() > 1) { delay = atoi(Cmd_Argv(1)); } if(delay) { sv.restartTime = svs.time + delay * 1000; SV_SetConfigstring(CS_WARMUP, va("%i", sv.restartTime)); return; } // NERVE - SMF - read in gamestate or just default to GS_PLAYING old_gs = (gamestate_t)atoi(Cvar_VariableString("gamestate")); if(SV_GameIsSinglePlayer() || SV_GameIsCoop()) { new_gs = GS_PLAYING; } else { if(Cmd_Argc() > 2) { new_gs = (gamestate_t)atoi(Cmd_Argv(2)); } else { new_gs = GS_PLAYING; } } if(!SV_TransitionGameState(new_gs, old_gs, delay)) { return; } // check for changes in variables that can't just be restarted // check for maxclients change if(sv_maxclients->modified) { char mapname[MAX_QPATH]; Com_Printf("sv_maxclients variable change -- restarting.\n"); // restart the map the slow way Q_strncpyz(mapname, Cvar_VariableString("mapname"), sizeof(mapname)); SV_SpawnServer(mapname, qfalse); return; } // Check for loading a saved game if(Cvar_VariableIntegerValue("savegame_loading")) { // open the current savegame, and find out what the time is, everything else we can ignore char savemap[MAX_QPATH], *cl_profileStr = Cvar_VariableString("cl_profile"); byte *buffer; int size, savegameTime; if(com_gameInfo.usesProfiles) { Com_sprintf(savemap, sizeof(savemap), "profiles/%s/save/current.sav", cl_profileStr); } else { Q_strncpyz(savemap, "save/current.sav", sizeof(savemap)); } size = FS_ReadFile(savemap, NULL); if(size < 0) { Com_Printf("Can't find savegame %s\n", savemap); return; } //buffer = Hunk_AllocateTempMemory(size); FS_ReadFile(savemap, (void **)&buffer); // the mapname is at the very start of the savegame file savegameTime = *(int *)(buffer + sizeof(int) + MAX_QPATH); if(savegameTime >= 0) { svs.time = savegameTime; } Hunk_FreeTempMemory(buffer); } // done. // toggle the server bit so clients can detect that a // map_restart has happened svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT; // generate a new serverid // TTimo - don't update restartedserverId there, otherwise we won't deal correctly with multiple map_restart sv.serverId = com_frameTime; Cvar_Set("sv_serverid", va("%i", sv.serverId)); // reset all the vm data in place without changing memory allocation // note that we do NOT set sv.state = SS_LOADING, so configstrings that // had been changed from their default values will generate broadcast updates sv.state = SS_LOADING; sv.restarting = qtrue; Cvar_Set("sv_serverRestarting", "1"); SV_RestartGameProgs(); // run a few frames to allow everything to settle for(i = 0; i < GAME_INIT_FRAMES; i++) { VM_Call(gvm, GAME_RUN_FRAME, svs.time); svs.time += FRAMETIME; } // create a baseline for more efficient communications // Gordon: meh, this wont work here as the client doesn't know it has happened // SV_CreateBaseline (); sv.state = SS_GAME; sv.restarting = qfalse; // connect and begin all the clients for(i = 0; i < sv_maxclients->integer; i++) { client = &svs.clients[i]; // send the new gamestate to all connected clients if(client->state < CS_CONNECTED) { continue; } if(client->netchan.remoteAddress.type == NA_BOT) { if(SV_GameIsSinglePlayer() || SV_GameIsCoop()) { continue; // dont carry across bots in single player } isBot = qtrue; } else { isBot = qfalse; } // add the map_restart command SV_AddServerCommand(client, "map_restart\n"); // connect the client again, without the firstTime flag denied = (char*)VM_ExplicitArgPtr(gvm, VM_Call(gvm, GAME_CLIENT_CONNECT, i, qfalse, isBot)); if(denied) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropClient(client, denied); if((!SV_GameIsSinglePlayer()) || (!isBot)) { Com_Printf("SV_MapRestart_f(%d): dropped client %i - denied!\n", delay, i); // bk010125 } continue; } client->state = CS_ACTIVE; SV_ClientEnterWorld(client, &client->lastUsercmd); } // run another frame to allow things to look at all the players VM_Call(gvm, GAME_RUN_FRAME, svs.time); svs.time += FRAMETIME; Cvar_Set("sv_serverRestarting", "0"); }