/* * SV_Download_f * Download command issued from server */ static void SV_Download_f( void ) { qboolean success; char *s; char url[MAX_STRING_CHARS], filepath[MAX_QPATH], writepath[MAX_QPATH]; if( Cmd_Argc() != 2 ) { Com_Printf( "Usage: %s <url>\n", Cmd_Argv( 0 ) ); Com_Printf( "Downloads .pk3 or .pak from URL to gamedir and adds it to the server\n" ); Com_Printf( "Note, server will not function properly while downloading\n" ); return; } s = Cmd_Argv( 1 ); if( !Com_GlobMatch( "*://*", s, qfalse ) ) Q_strncpyz( url, "http://", sizeof( url ) ); else url[0] = 0; Q_strncatz( url, s, sizeof( url ) ); s = strrchr( url, '/' ); if( !s ) { Com_Printf( "%s: invalid URL\n", Cmd_Argv( 0 ) ); return; } Q_strncpyz( filepath, va( "%s/%s", FS_GameDirectory(), s + 1 ), sizeof( filepath ) ); Q_strncpyz( writepath, va( "%s.tmp", filepath ), sizeof( writepath ) ); if( !FS_CheckPakExtension( writepath ) ) { Com_Printf( "Missing or invalid archive extension. Only download of pack files is supported\n" ); return; } Com_Printf( "download url: %s\n", url ); webDownloadPercentPrint = 0; success = Web_Get( url, NULL, writepath, qtrue, 60 * 30, 60, SV_WebDownloadProgress, qfalse ); if( !success ) { Com_Printf( "Server web download failed\n" ); return; } if( !FS_MoveBaseFile( writepath, filepath ) ) { Com_Printf( "Couldn't rename the downloaded file. Download failed\n" ); return; } Com_Printf( "Download successful\n" ); // update the map list, which also does a filesystem rescan ML_Update(); }
/* * CL_DownloadComplete * * Checks downloaded file's checksum, renames it and adds to the filesystem. */ static void CL_DownloadComplete( void ) { unsigned checksum = 0; int length; qboolean updateMapList = qfalse; FS_FCloseFile( cls.download.filenum ); cls.download.filenum = 0; // verify checksum if( FS_CheckPakExtension( cls.download.name ) ) { if( !FS_IsPakValid( cls.download.tempname, &checksum ) ) { Com_Printf( "Downloaded file is not a valid pack file. Removing\n" ); FS_RemoveBaseFile( cls.download.tempname ); return; } } else { length = FS_LoadBaseFile( cls.download.tempname, NULL, NULL, 0 ); if( length < 0 ) { Com_Printf( "Error: Couldn't load downloaded file\n" ); return; } checksum = FS_ChecksumBaseFile( cls.download.tempname ); } if( cls.download.checksum != checksum ) { Com_Printf( "Downloaded file has wrong checksum. Removing: %u %u %s\n", cls.download.checksum, checksum, cls.download.tempname ); FS_RemoveBaseFile( cls.download.tempname ); return; } if( !FS_MoveBaseFile( cls.download.tempname, cls.download.name ) ) { Com_Printf( "Failed to rename the downloaded file\n" ); return; } if( FS_CheckPakExtension( cls.download.name ) ) updateMapList = qtrue; else if( !Q_stricmp( COM_FileExtension( cls.download.name ), ".bsp" ) ) updateMapList = qtrue; // Maplist hook so we also know when a new map is added if( updateMapList ) ML_Update (); cls.download.successCount++; cls.download.timeout = 0; }
/* * SV_AutoUpdateComplete_f */ static void SV_AutoUpdateComplete_f( void ) { // update the map list, which also does a filesystem rescan ML_Update(); if( FS_GetNotifications() & FS_NOTIFY_NEWPAKS ) { // force restart svc.lastActivity = 0; } }
/* * ML_MapListCmd * Handler for console command "maplist" */ static void ML_MapListCmd( void ) { char *pattern; mapinfo_t *map; int argc = Cmd_Argc(); unsigned int i; struct trie_dump_s *dump = NULL; if( argc > 2 ) { Com_Printf( "Usage: %s [rebuild]\n", Cmd_Argv(0) ); return; } pattern = (argc == 2 ? Cmd_Argv( 1 ) : NULL); if( pattern && !*pattern ) pattern = NULL; if( pattern ) { if( !strcmp( pattern, "rebuild" ) ) { Com_Printf( "Rebuilding map list...\n" ); ML_Restart( true ); return; } if( !strcmp( pattern, "update" ) ) { Com_Printf( "Updating map list...\n" ); ML_Update(); return; } } Trie_DumpIf( mlist_filenames_trie, "", TRIE_DUMP_VALUES, ML_PatternMatchesMap, pattern, &dump ); for( i = 0; i < dump->size; i++ ) { map = ( mapinfo_t * )( dump->key_value_vector[i].value ); Com_Printf( "%s: %s\n", map->filename, map->fullname ); } Trie_FreeDump( dump ); Com_Printf( "%d map(s) %s\n", i, pattern ? "matching" : "total" ); }
/* * SV_Map_f * * User command to change the map * map: restart game, and start map * devmap: restart game, enable cheats, and start map * gamemap: just start the map */ static void SV_Map_f( void ) { char *map; char mapname[MAX_CONFIGSTRING_CHARS]; qboolean found = qfalse; if( Cmd_Argc() < 2 ) { Com_Printf( "Usage: %s <map>\n", Cmd_Argv( 0 ) ); return; } // if map "<map>" is used Cmd_Args() will return the "" as well. if( Cmd_Argc() == 2 ) map = Cmd_Argv( 1 ); else map = Cmd_Args(); Com_DPrintf( "SV_GameMap(%s)\n", map ); // applies to fullnames and filenames (whereas + strlen( "maps/" ) wouldnt) if( strlen( map ) >= MAX_CONFIGSTRING_CHARS ) { Com_Printf( "Map name too long.\n" ); return; } Q_strncpyz( mapname, map, sizeof( mapname ) ); if( ML_ValidateFilename( mapname ) ) { COM_StripExtension( mapname ); if( ML_FilenameExists( mapname ) ) { found = qtrue; } else { ML_Update(); if( ML_FilenameExists( mapname ) ) found = qtrue; } } if( !found ) { if( ML_ValidateFullname( map ) ) { Q_strncpyz( mapname, ML_GetFilename( map ), sizeof( mapname ) ); if( *mapname ) found = qtrue; } if( !found ) { Com_Printf( "Couldn't find map: %s\n", map ); return; } } if( FS_GetNotifications() & FS_NOTIFT_NEWPAKS ) { FS_RemoveNotifications( FS_NOTIFT_NEWPAKS ); sv.state = ss_dead; // don't save current level when changing } else if( !Q_stricmp( Cmd_Argv( 0 ), "map" ) || !Q_stricmp( Cmd_Argv( 0 ), "devmap" ) ) { sv.state = ss_dead; // don't save current level when changing } // start up the next map SV_Map( mapname, !Q_stricmp( Cmd_Argv( 0 ), "devmap" ) ); // archive server state Q_strncpyz( svs.mapcmd, mapname, sizeof( svs.mapcmd ) ); }
/* * 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 ) ); } } }