/* * SV_SendClientMessages */ void SV_SendClientMessages( void ) { int i; client_t *client; // send a message to each connected client for( i = 0, client = svs.clients; i < sv_maxclients->integer; i++, client++ ) { if( client->state == CS_FREE || client->state == CS_ZOMBIE ) continue; if( client->edict && ( client->edict->r.svflags & SVF_FAKECLIENT ) ) { client->lastSentFrameNum = sv.framenum; continue; } if( !client->tvclient ) { SV_UpdateActivity(); } if( client->state == CS_SPAWNED ) { if( !SV_SendClientDatagram( client ) ) { Com_Printf( "Error sending message to %s: %s\n", client->name, NET_ErrorString() ); if( client->reliable ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error sending message: %s\n", NET_ErrorString() ); } } } else { // send pending reliable commands, or send heartbeats for not timing out if( client->reliableSequence > client->reliableAcknowledge || svs.realtime - client->lastPacketSentTime > 1000 ) { SV_InitClientMessage( client, &tmpMessage, NULL, 0 ); SV_AddReliableCommandsToMessage( client, &tmpMessage ); if( !SV_SendMessageToClient( client, &tmpMessage ) ) { Com_Printf( "Error sending message to %s: %s\n", client->name, NET_ErrorString() ); if( client->reliable ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error sending message: %s\n", NET_ErrorString() ); } } } } } }
/* * SV_AutoUpdateFromWeb */ void SV_AutoUpdateFromWeb( qboolean checkOnly ) { static const char *autoUpdateBaseUrl = APP_UPDATE_URL APP_SERVER_UPDATE_DIRECTORY; char checksumString1[32], checksumString2[32]; unsigned int checksum; qboolean success; int length, filenum; qbyte *data; const char *token, *ptr; char path[MAX_QPATH]; int downloadCount = 0, downloadFailed = 0; char newVersionTag[MAX_QPATH]; qboolean newVersion = qfalse; if( !dedicated->integer ) return; assert( svs.mapcmd[0] ); if( !checkOnly ) SV_UpdateActivity(); Com_Printf( "\n" ); Com_Printf( "========== Starting Auto Update ===========\n" ); Com_Printf( "Checking for updates\n" ); // download the update file list success = SV_WebDownload( autoUpdateBaseUrl, APP_SERVER_UPDATE_FILE, qtrue, qtrue ); // set as last updated today if( !checkOnly ) Cvar_ForceSet( "sv_lastAutoUpdate", va( "%i", (int)Com_DaysSince1900() ) ); if( !success ) // no update to do goto done; // read the file list if( ( length = FS_FOpenBaseFile( APP_SERVER_UPDATE_FILE, &filenum, FS_READ ) ) == -1 ) { Com_Printf( "WARNING: Couldn't find %s\n", path ); goto done; } if( !length ) { FS_FCloseFile( filenum ); goto done; } data = Mem_TempMalloc( length + 1 ); FS_Read( data, length, filenum ); FS_FCloseFile( filenum ); FS_RemoveBaseFile( APP_SERVER_UPDATE_FILE ); ptr = (const char *)data; // first token is always the current release version token = COM_ParseExt( &ptr, qtrue ); if( !token[0] ) goto cancel; // compare versions Q_strncpyz( newVersionTag, token, sizeof( newVersionTag ) ); if( atof( newVersionTag ) > atof( va( "%4.3f", APP_VERSION ) ) ) newVersion = qtrue; while( ptr ) { // we got what should be a checksum token = COM_ParseExt( &ptr, qtrue ); if( !token[0] ) goto cancel; // copy checksum reported by server Q_strncpyz( checksumString1, token, sizeof( checksumString1 ) ); // get filename token = COM_ParseExt( &ptr, qtrue ); if( !token[0] ) goto cancel; // filename should never begin with a slash if( token[0] == '/' ) token++; Q_strncpyz( path, token, sizeof( path ) ); // we got what should be a file path if( !COM_ValidateRelativeFilename( path ) ) { Com_Printf( "WARNING: Invalid filename %s\n", path ); continue; } checksum = FS_ChecksumBaseFile( path ); Q_snprintfz( checksumString2, sizeof( checksumString2 ), "%u", checksum ); // if same checksum no need to update if( !strcmp( checksumString1, checksumString2 ) ) continue; // if it's a pack file and the file exists it can't be replaced, so skip if( FS_CheckPakExtension( path ) && checksum ) { Com_Printf( "WARNING: Purity check failed for: %s\n", path ); Com_Printf( "WARNING: This file has been locally modified. It is highly \n" ); Com_Printf( "WARNING: recommended to restore the original file.\n" ); Com_Printf( "WARNING: Reinstalling \""APPLICATION"\" might be convenient.\n" ); continue; } if( checkOnly ) { Com_Printf( "File update available : %s\n", path ); continue; } if( developer->integer ) Com_Printf( "Downloading update of %s (checksum %s local checksum %s)\n", path, checksumString1, checksumString2 ); else Com_Printf( "Updating %s\n", path ); if( !SV_WebDownload( autoUpdateBaseUrl, path, qtrue, qtrue ) ) { Com_Printf( "Failed to update %s\n", path ); downloadFailed++; } downloadCount++; } cancel: Mem_TempFree( data ); done: if( newVersion ) { if( downloadCount ) { if( downloadFailed ) Com_Printf( "This version of "APPLICATION" was updated incompletely\n" ); else Com_Printf( "This version of "APPLICATION" was updated successfully\n\n" ); } Com_Printf( "****** Version %s of "APPLICATION" is available. ******\n", newVersionTag ); Com_Printf( "****** Please download the new version at "APP_URL" ******\n" ); } else if( downloadCount ) { if( downloadFailed ) Com_Printf( APPLICATION" was updated incompletely\n" ); else Com_Printf( APPLICATION" was updated successfully\n" ); } else if( !checkOnly ) { if( downloadFailed ) Com_Printf( "At least one file failed to update\n" ); else Com_Printf( APPLICATION" is up to date\n" ); } Com_Printf( "========== Auto Update Finished ===========\n" ); Com_Printf( "\n" ); // update the map list, which also does a filesystem rescan ML_Update(); // if there are any new filesystem entries, restart if( FS_GetNotifications() & FS_NOTIFT_NEWPAKS ) { if( sv.state != ss_dead ) { // restart the current map, SV_Map also rescans the filesystem Com_Printf( "The server will now restart...\n\n" ); // start the default map if current map isn't available Cbuf_ExecuteText( EXEC_APPEND, va( "map %s\n", svs.mapcmd[0] ? svs.mapcmd : sv_defaultmap->string ) ); } } }
/* * SV_SpawnServer * Change the server to a new map, taking all connected clients along with it. */ static void SV_SpawnServer( const char *server, qboolean devmap ) { unsigned checksum; int i; if( devmap ) Cvar_ForceSet( "sv_cheats", "1" ); Cvar_FixCheatVars(); Com_Printf( "------- Server Initialization -------\n" ); Com_Printf( "SpawnServer: %s\n", server ); svs.spawncount++; // any partially connected client will be restarted Com_SetServerState( ss_dead ); // wipe the entire per-level structure memset( &sv, 0, sizeof( sv ) ); SV_ResetClientFrameCounters(); svs.realtime = 0; svs.gametime = 0; SV_UpdateActivity(); Q_strncpyz( sv.mapname, server, sizeof( sv.mapname ) ); SV_SetServerConfigStrings(); sv.nextSnapTime = 1000; Q_snprintfz( sv.configstrings[CS_WORLDMODEL], sizeof( sv.configstrings[CS_WORLDMODEL] ), "maps/%s.bsp", server ); CM_LoadMap( svs.cms, sv.configstrings[CS_WORLDMODEL], qfalse, &checksum ); Q_snprintfz( sv.configstrings[CS_MAPCHECKSUM], sizeof( sv.configstrings[CS_MAPCHECKSUM] ), "%i", checksum ); // reserve the first modelIndexes for inline models for( i = 1; i < CM_NumInlineModels( svs.cms ); i++ ) Q_snprintfz( sv.configstrings[CS_MODELS + i], sizeof( sv.configstrings[CS_MODELS + i] ), "*%i", i ); // set serverinfo variable Cvar_FullSet( "mapname", sv.mapname, CVAR_SERVERINFO | CVAR_READONLY, qtrue ); // // spawn the rest of the entities on the map // // precache and static commands can be issued during // map initialization sv.state = ss_loading; Com_SetServerState( sv.state ); // set purelist SV_ReloadPureList(); // load and spawn all other entities ge->InitLevel( sv.mapname, CM_EntityString( svs.cms ), CM_EntityStringLen( svs.cms ), 0, svs.gametime, svs.realtime ); // run two frames to allow everything to settle ge->RunFrame( svc.snapFrameTime, svs.gametime ); ge->RunFrame( svc.snapFrameTime, svs.gametime ); SV_CreateBaseline(); // create a baseline for more efficient communications // all precaches are complete sv.state = ss_game; Com_SetServerState( sv.state ); Com_Printf( "-------------------------------------\n" ); }
/* * SV_ParseClientMessage * The current message is parsed for the given client */ void SV_ParseClientMessage( client_t *client, msg_t *msg ) { int c; char *s; qboolean move_issued; unsigned int cmdNum; if( !msg ) return; SV_UpdateActivity(); // only allow one move command move_issued = qfalse; while( 1 ) { if( msg->readcount > msg->cursize ) { Com_Printf( "SV_ParseClientMessage: badread\n" ); SV_DropClient( client, DROP_TYPE_GENERAL, "Error: Bad message" ); return; } c = MSG_ReadByte( msg ); if( c == -1 ) break; switch( c ) { default: Com_Printf( "SV_ParseClientMessage: unknown command char\n" ); SV_DropClient( client, DROP_TYPE_GENERAL, "Error: Unknown command char" ); return; case clc_nop: break; case clc_move: { if( move_issued ) return; // someone is trying to cheat... move_issued = qtrue; SV_ParseMoveCommand( client, msg ); } break; case clc_svcack: { if( client->reliable ) { Com_Printf( "SV_ParseClientMessage: svack from reliable client\n" ); SV_DropClient( client, DROP_TYPE_GENERAL, "Error: svack from reliable client" ); return; } cmdNum = MSG_ReadLong( msg ); if( cmdNum < client->reliableAcknowledge || cmdNum > client->reliableSent ) { //SV_DropClient( client, DROP_TYPE_GENERAL, "Error: bad server command acknowledged" ); return; } client->reliableAcknowledge = cmdNum; } break; case clc_clientcommand: if( !client->reliable ) { cmdNum = MSG_ReadLong( msg ); if( cmdNum <= client->clientCommandExecuted ) { s = MSG_ReadString( msg ); // read but ignore continue; } client->clientCommandExecuted = cmdNum; } s = MSG_ReadString( msg ); SV_ExecuteUserCommand( client, s ); if( client->state == CS_ZOMBIE ) return; // disconnect command break; case clc_extension: if( 1 ) { int ext, len; ext = MSG_ReadByte( msg ); // extension id MSG_ReadByte( msg ); // version number len = MSG_ReadShort( msg ); // command length switch( ext ) { default: // unsupported MSG_SkipData( msg, len ); break; } } break; } } }