diff options
Diffstat (limited to 'source/sv_ccmds.c')
-rw-r--r-- | source/sv_ccmds.c | 1158 |
1 files changed, 1158 insertions, 0 deletions
diff --git a/source/sv_ccmds.c b/source/sv_ccmds.c new file mode 100644 index 0000000..e3f6c2a --- /dev/null +++ b/source/sv_ccmds.c @@ -0,0 +1,1158 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "sv_local.h" +#include "mvd_local.h" + +/* +=============================================================================== + +OPERATOR CONSOLE ONLY COMMANDS + +These commands can only be entered from stdin or by a remote operator datagram +=============================================================================== +*/ + +/* +==================== +SV_SetMaster_f + +Specify a list of master servers +==================== +*/ +void SV_SetMaster_f (void) +{ + int i, slot; + + // only dedicated servers send heartbeats + if (!dedicated->integer) + { + Com_Printf ("Only dedicated servers use masters.\n"); + return; + } + + // make sure the server is listed public + Cvar_Set ("public", "1"); + + for (i=0 ; i<MAX_MASTERS ; i++) + memset (&master_adr[i], 0, sizeof(master_adr[0])); + + slot = 0; + for (i=1 ; i<Cmd_Argc() ; i++) + { + if (slot == MAX_MASTERS) { + Com_Printf ("Too many masters.\n"); + break; + } + + if (!NET_StringToAdr (Cmd_Argv(i), &master_adr[slot])) + { + Com_Printf ("Bad address: %s\n", Cmd_Argv(i)); + continue; + } + if (master_adr[slot].port == 0) + master_adr[slot].port = BigShort (PORT_MASTER); + + Com_Printf ("Master server at %s\n", NET_AdrToString (&master_adr[slot])); + + //Com_Printf ("Sending a ping.\n"); + + //Netchan_OutOfBandPrint (NS_SERVER, &master_adr[slot], "ping"); + + slot++; + } + + svs.last_heartbeat = -9999999; +} + +static const char *SV_SetPlayer_g( const char *partial, int state ) { + static int length; + static int index; + client_t *client; + + if( !svs.initialized ) { + return NULL; + } + + if( !state ) { + length = strlen( partial ); + index = 0; + } + + while( index < sv_maxclients->integer ) { + client = &svs.clientpool[index++]; + if( !client->state || client->protocol == -1 ) { + continue; + } + if( !strncmp( partial, client->name, length ) ) { + return client->name; + } + } + + return NULL; +} + + +/* +================== +SV_SetPlayer + +Sets sv_client and sv_player to the player with idnum Cmd_Argv(1) +================== +*/ +qboolean SV_SetPlayer (void) +{ + client_t *cl; + int i; + int idnum; + char *s; + + if (Cmd_Argc() < 2) + return qfalse; + + s = Cmd_Argv(1); + + // numeric values are just slot numbers + for( i = 0; s[i]; i++ ) { + if( !Q_isdigit( s[i] ) ) { + break; + } + } + if( !s[i] ) { + idnum = atoi(s); + if (idnum < 0 || idnum >= sv_maxclients->integer) + { + Com_Printf ("Bad client slot: %i\n", idnum); + return qfalse; + } + + sv_client = &svs.clientpool[idnum]; + sv_player = sv_client->edict; + if (!sv_client->state) + { + Com_Printf ("Client %i is not active\n", idnum); + return qfalse; + } + return qtrue; + } + + // check for a name match + for( i = 0; i < sv_maxclients->integer; i++ ) { + cl = &svs.clientpool[i]; + if( !cl->state ) { + continue; + } + if (!strcmp(cl->name, s)) + { + sv_client = cl; + sv_player = sv_client->edict; + return qtrue; + } + } + + Com_Printf ("Userid %s is not on the server\n", s); + return qfalse; +} + + +/* +=============================================================================== + +SAVEGAME FILES + +=============================================================================== +*/ + +#define GAME_SEARCH_FLAGS ( FS_SEARCH_BYFILTER | FS_SEARCH_SAVEPATH | FS_PATH_GAME | FS_TYPE_REAL ) + +/* +===================== +SV_WipeSavegame + +Delete save/<XXX>/ +===================== +*/ +void SV_WipeSavegame( const char *savename ) { + char name[MAX_QPATH]; + char **list, **s; + + Com_DPrintf( "SV_WipeSaveGame( %s )\n", savename ); + + Com_sprintf( name, sizeof( name ), "save/%s/server.ssv", savename ); + FS_RemoveFile( name ); + + Com_sprintf( name, sizeof( name ), "save/%s/game.ssv", savename ); + FS_RemoveFile( name ); + + Com_sprintf( name, sizeof( name ), "save/%s/*.sav", savename ); + if( ( list = FS_ListFiles( NULL, name, GAME_SEARCH_FLAGS, NULL ) ) != NULL ) { + s = list; + while( *s ) { + FS_RemoveFile( *s ); + s++; + } + FS_FreeFileList( list ); + } + + Com_sprintf( name, sizeof( name ), "save/%s/*.sv2", savename ); + if( ( list = FS_ListFiles( NULL, name, GAME_SEARCH_FLAGS, NULL ) ) != NULL ) { + s = list; + while( *s ) { + FS_RemoveFile( *s ); + s++; + } + FS_FreeFileList( list ); + } + +} + +/* +================ +SV_CopySaveGame +================ +*/ +void SV_CopySaveGame( const char *src, const char *dst ) { + char name[MAX_QPATH], name2[MAX_QPATH]; + int len; + char **list, **s; + + Com_DPrintf( "SV_CopySaveGame(%s, %s)\n", src, dst ); + + SV_WipeSavegame( dst ); + + // copy the savegame over + Com_sprintf( name, sizeof( name ), "save/%s/server.ssv", src ); + Com_sprintf( name2, sizeof( name2 ), "save/%s/server.ssv", dst ); + FS_CopyFile( name, name2 ); + + Com_sprintf( name, sizeof( name ), "save/%s/game.ssv", src ); + Com_sprintf( name2, sizeof( name2 ), "save/%s/game.ssv", dst ); + FS_CopyFile( name, name2 ); + + Com_sprintf( name, sizeof( name ), "save/%s/", src ); + len = strlen( name ); + + Com_sprintf( name, sizeof( name ), "save/%s/*.sav", src ); + if( ( list = FS_ListFiles( NULL, name, GAME_SEARCH_FLAGS, NULL ) ) != NULL ) { + s = list; + while( *s ) { + Com_sprintf( name2, sizeof( name2 ), "save/%s/%s", dst, *s + len ); + FS_CopyFile( *s, name2 ); + s++; + } + FS_FreeFileList( list ); + } + + Com_sprintf( name, sizeof( name ), "save/%s/*.sv2", src ); + if( ( list = FS_ListFiles( NULL, name, GAME_SEARCH_FLAGS, NULL ) ) != NULL ) { + s = list; + while( *s ) { + Com_sprintf( name2, sizeof( name2 ), "save/%s/%s", dst, *s + len ); + FS_CopyFile( *s, name2 ); + s++; + } + FS_FreeFileList( list ); + } +} + + +/* +============== +SV_WriteLevelFile + +============== +*/ +void SV_WriteLevelFile (void) +{ + char name[MAX_OSPATH]; + fileHandle_t f; + + Com_DPrintf("SV_WriteLevelFile()\n"); + + Com_sprintf (name, sizeof(name), "save/current/%s.sv2", sv.name); + FS_FOpenFile( name, &f, FS_MODE_WRITE ); + if (!f) + { + Com_Printf ("Failed to open %s\n", name); + return; + } + + FS_Write (sv.configstrings, sizeof(sv.configstrings), f); + CM_WritePortalState (&sv.cm, f); + FS_FCloseFile (f); + + Com_sprintf (name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name); + ge->WriteLevel (name); +} + +/* +============== +SV_ReadLevelFile + +============== +*/ +void SV_ReadLevelFile (void) +{ + char name[MAX_OSPATH]; + fileHandle_t f; + + Com_DPrintf("SV_ReadLevelFile()\n"); + + Com_sprintf (name, sizeof(name), "save/current/%s.sv2", sv.name); + + FS_FOpenFile( name, &f, FS_MODE_READ|FS_TYPE_REAL ); + if( !f ) { + Com_Printf ("Failed to open %s\n", name); + return; + } + FS_Read( sv.configstrings, sizeof( sv.configstrings ), f ); + *( ( byte * )sv.configstrings + sizeof( sv.configstrings ) - 1 ) = 0; + + CM_ReadPortalState (&sv.cm, f); + FS_FCloseFile (f); + + Com_sprintf (name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name); + ge->ReadLevel (name); +} + +/* +============== +SV_WriteServerFile + +============== +*/ +void SV_WriteServerFile (qboolean autosave) +{ + fileHandle_t f; + cvar_t *var; + char name[MAX_OSPATH], string[128]; + char comment[MAX_QPATH]; // increased from 32 to avoid Com_sprintf warnings + time_t aclock; + struct tm *newtime; + + Com_DPrintf( "SV_WriteServerFile( %i )\n", autosave ); + + FS_FOpenFile( "save/current/server.ssv", &f, FS_MODE_WRITE ); + if( !f ) { + Com_WPrintf( "Couldn't write save/current/server.ssv\n" ); + return; + } + // write the comment field + memset (comment, 0, sizeof(comment)); + + if (!autosave) + { + time (&aclock); + newtime = localtime (&aclock); + Com_sprintf (comment,sizeof(comment), "%2i:%i%i %2i/%2i ", newtime->tm_hour + , newtime->tm_min/10, newtime->tm_min%10, + newtime->tm_mon+1, newtime->tm_mday); + strncat (comment, sv.configstrings[CS_NAME], sizeof(comment)-1-strlen(comment) ); + } + else + { // autosaved + Com_sprintf( comment, sizeof( comment ), "ENTERING %s", sv.configstrings[CS_NAME] ); + } + + // write 32 bytes instead of 64 for compatibility + FS_Write (comment, 32, f); + + // write the mapcmd + FS_Write (svs.mapcmd, sizeof(svs.mapcmd), f); + + // write all CVAR_LATCH cvars + // these will be things like coop, skill, deathmatch, etc + for (var = cvar_vars ; var ; var=var->next) + { + if (!(var->flags & CVAR_LATCH)) + continue; + if (strlen(var->name) >= sizeof(name)-1 + || strlen(var->string) >= sizeof(string)-1) + { + Com_Printf ("Cvar too long: %s = %s\n", var->name, var->string); + continue; + } + memset (name, 0, sizeof(name)); + memset (string, 0, sizeof(string)); + strcpy (name, var->name); + strcpy (string, var->string); + FS_Write (name, sizeof(name), f); + FS_Write (string, sizeof(string), f); + } + + FS_FCloseFile (f); + + // write game state + Com_sprintf (name, sizeof(name), "%s/save/current/game.ssv", FS_Gamedir()); + ge->WriteGame (name, autosave); +} + +/* +============== +SV_ReadServerFile + +============== +*/ +void SV_ReadServerFile (void) +{ + fileHandle_t f; + char name[MAX_OSPATH], string[128]; + char comment[32]; + char mapcmd[MAX_TOKEN_CHARS]; + + Com_DPrintf("SV_ReadServerFile()\n"); + + Com_sprintf (name, sizeof(name), "save/current/server.ssv"); + FS_FOpenFile( name, &f, FS_MODE_READ|FS_TYPE_REAL|FS_PATH_GAME ); + if (!f) + { + Com_Printf ("Couldn't read %s\n", name); + return; + } + // read the comment field + FS_Read (comment, sizeof(comment), f); + + // read the mapcmd + FS_Read (mapcmd, sizeof(mapcmd), f); + + // read all CVAR_LATCH cvars + // these will be things like coop, skill, deathmatch, etc + while (1) + { + if (!FS_Read (name, sizeof(name), f)) + break; + FS_Read (string, sizeof(string), f); + Com_DPrintf ("Set %s = %s\n", name, string); + Cvar_Set (name, string); + } + + FS_FCloseFile (f); + + // start a new game fresh with new cvars + SV_InitGame ( 0 ); + + strcpy (svs.mapcmd, mapcmd); + + // read game state + Com_sprintf (name, sizeof(name), "%s/save/current/game.ssv", FS_Gamedir()); + ge->ReadGame (name); + +} + + +//========================================================= + + +/* +================== +SV_DemoMap_f + +Puts the server in demo mode on a specific map/cinematic +================== +*/ +void SV_DemoMap_f( void ) { + if( Cmd_Argc() != 2 ) { + Com_Printf( "Usage: %s <mapname>\n", Cmd_Argv( 0 ) ); + return; + } + SV_Map( ATR_DEMO, Cmd_Argv( 1 ), qfalse ); +} + +/* +================== +SV_GameMap_f + +Saves the state of the map just being exited and goes to a new map. + +If the initial character of the map string is '*', the next map is +in a new unit, so the current savegame directory is cleared of +map files. + +Example: + +*inter.cin+jail + +Clears the archived maps, plays the inter.cin cinematic, then +goes to map jail.bsp. +================== +*/ +void SV_GameMap_f (void) +{ + char *map; + int i; + client_t *cl; + qboolean *savedInuse; + char expanded[MAX_QPATH]; + + if (Cmd_Argc() != 2) + { + Com_Printf( "Usage: %s <mapname>\n", Cmd_Argv( 0 ) ); + return; + } + + Com_DPrintf("SV_GameMap(%s)\n", Cmd_Argv(1)); + + FS_CreatePath (va("%s/save/current/", FS_Gamedir())); + + // check for clearing the current savegame + map = Cmd_Argv(1); + if (map[0] == '*') + { + // wipe all the *.sav files + SV_WipeSavegame ("current"); + } + else + { + // if not a pcx, demo, or cinematic, check to make sure the level exists + if( !strchr( map, '.' ) && !strchr( map, '$' ) ) { + Com_sprintf( expanded, sizeof( expanded ), "maps/%s.bsp", map ); + if( FS_LoadFile( expanded, NULL ) == -1 ) { + Com_Printf( "Can't find %s\n", expanded ); + return; + } + } + + // save the map just exited + if (sv.state == ss_game) + { + // clear all the client inuse flags before saving so that + // when the level is re-entered, the clients will spawn + // at spawn points instead of occupying body shells + savedInuse = SV_Malloc(sv_maxclients->integer * sizeof(qboolean)); + for (i=0,cl=svs.clientpool ; i<sv_maxclients->integer; i++,cl++) + { + savedInuse[i] = cl->edict->inuse; + cl->edict->inuse = qfalse; + } + + SV_WriteLevelFile (); + + // we must restore these for clients to transfer over correctly + for (i=0,cl=svs.clientpool ; i<sv_maxclients->integer; i++,cl++) + cl->edict->inuse = savedInuse[i]; + Z_Free (savedInuse); + } + } + + // start up the next map + SV_Map (ATR_NONE, Cmd_Argv(1), qfalse ); + + // archive server state + Q_strncpyz( svs.mapcmd, Cmd_Argv( 1 ), sizeof( svs.mapcmd ) ); + + // copy off the level to the autosave slot + if (!dedicated->integer) + { + SV_WriteServerFile (qtrue); + SV_CopySaveGame ("current", "save0"); + } +} + +/* +================== +SV_Map_f + +Goes directly to a given map without any savegame archiving. +For development work +================== +*/ +void SV_Map_f( void ) { + char *map; + char expanded[MAX_QPATH]; + + if( Cmd_Argc() != 2 ) { + Com_Printf( "Usage: %s <mapname>\n", Cmd_Argv( 0 ) ); + return; + } + + // if not a pcx, demo, or cinematic, check to make sure the level exists + map = Cmd_Argv( 1 ); + if( !strchr( map, '.' ) ) { + Com_sprintf( expanded, sizeof( expanded ), "maps/%s.bsp", map ); + if( FS_LoadFile( expanded, NULL ) == -1 ) { + Com_Printf( "Can't find %s\n", expanded ); + return; + } + } + + sv.state = ss_dead; // don't save current level when changing + SV_WipeSavegame( "current" ); + SV_GameMap_f(); +} + +static const char *SV_Map_g( const char *partial, int state ) { + return Com_FileNameGenerator( "maps", ".bsp", partial, qtrue, state ); +} + +/* +===================================================================== + + SAVEGAMES + +===================================================================== +*/ + + +/* +============== +SV_Loadgame_f + +============== +*/ +void SV_Loadgame_f (void) +{ + char name[MAX_QPATH]; + char *dir; + + if (Cmd_Argc() != 2) + { + Com_Printf ("USAGE: loadgame <directory>\n"); + return; + } + + Com_Printf ("Loading game...\n"); + + dir = Cmd_Argv(1); + if (strstr (dir, "..") || strstr (dir, "/") || strstr (dir, "\\") ) + { + Com_Printf ("Bad savedir.\n"); + } + + // make sure the server.ssv file exists + Com_sprintf (name, sizeof(name), "save/%s/server.ssv", Cmd_Argv(1)); + if( FS_LoadFileEx( name, NULL, FS_TYPE_REAL|FS_PATH_GAME ) == -1 ) { + Com_Printf ("No such savegame: %s\n", name); + return; + } + + SV_CopySaveGame (Cmd_Argv(1), "current"); + + SV_ReadServerFile (); + + // go to the map + sv.state = ss_dead; // don't save current level when changing + SV_Map (ATR_NONE, svs.mapcmd, qtrue); +} + + + +/* +============== +SV_Savegame_f + +============== +*/ +void SV_Savegame_f (void) +{ + char *dir; + + if (sv.state != ss_game) + { + Com_Printf ("You must be in a game to save.\n"); + return; + } + + if (Cmd_Argc() != 2) + { + Com_Printf ("USAGE: savegame <directory>\n"); + return; + } + + if (Cvar_VariableValue("deathmatch")) + { + Com_Printf ("Can't savegame in a deathmatch\n"); + return; + } + + if (!strcmp (Cmd_Argv(1), "current")) + { + Com_Printf ("Can't save to 'current'\n"); + return; + } + + if (sv_maxclients->integer == 1 && svs.clientpool[0].edict && svs.clientpool[0].edict->client && svs.clientpool[0].edict->client->ps.stats[STAT_HEALTH] <= 0) + { + Com_Printf ("\nCan't savegame while dead!\n"); + return; + } + + dir = Cmd_Argv(1); + if (strstr (dir, "..") || strstr (dir, "/") || strstr (dir, "\\") ) + { + Com_Printf ("Bad savedir.\n"); + } + + Com_Printf ("Saving game...\n"); + + // archive current level, including all client edicts. + // when the level is reloaded, they will be shells awaiting + // a connecting client + SV_WriteLevelFile (); + + // save server state + SV_WriteServerFile (qfalse); + + // copy it off + SV_CopySaveGame ("current", dir); + + Com_Printf ("Done.\n"); +} + +//=============================================================== + +/* +================== +SV_Kick_f + +Kick a user off of the server +================== +*/ +void SV_Kick_f( void ) { + if( !svs.initialized ) { + Com_Printf( "No server running.\n" ); + return; + } + + if( Cmd_Argc() != 2 ) { + Com_Printf( "Usage: %s <userid>\n", Cmd_Argv( 0 ) ); + return; + } + + if( !SV_SetPlayer() ) + return; + + SV_DropClient( sv_client, "was kicked" ); + sv_client->lastmessage = svs.realtime; // min case there is a funny zombie + + sv_client = NULL; + sv_player = NULL; +} + +static void SV_DumpUdpClients( void ) { + client_t *client; + + Com_Printf( +"num score ping name lastmsg address rate proto\n" +"--- ----- ---- ---------------- ------- --------------------- ----- -----\n" ); + FOR_EACH_CLIENT( client ) { + Com_Printf( "%3i ", client->number ); + if( sv.state == ss_broadcast ) { + Com_Printf( " " ); + } else { + Com_Printf( "%5i ", client->edict->client->ps.stats[STAT_FRAGS] ); + } + + switch( client->state ) { + case cs_zombie: + Com_Printf( "ZMBI " ); + break; + case cs_assigned: + Com_Printf( "ASGN " ); + break; + case cs_connected: + Com_Printf( "CNCT " ); + break; + case cs_primed: + Com_Printf( "PRIM " ); + break; + default: + Com_Printf( "%4i ", client->ping < 9999 ? client->ping : 9999 ); + break; + } + + Com_Printf( "%-16.16s ", client->name ); + Com_Printf( "%7u ", svs.realtime - client->lastmessage ); + Com_Printf( "%-21s ", NET_AdrToString( + &client->netchan->remote_address ) ); + Com_Printf( "%5i ", client->rate ); + Com_Printf( "%2i/%d ", client->protocol, client->netchan->type ); + Com_Printf( "\n" ); + } + +} + +static void SV_DumpUdpVersions( void ) { + client_t *client; + + Com_Printf( +"num name version\n" +"--- ---------------- -----------------------------------------\n" ); + + FOR_EACH_CLIENT( client ) { + Com_Printf( "%3i %-16.16s %-40.40s\n", + client->number, client->name, + client->versionString ? client->versionString : "" ); + } +} + +static void SV_DumpTcpClients( void ) { + tcpClient_t *client; + int count; + + Com_Printf( +"num resource buf lastmsg address state\n" +"--- -------------------- --- ------- --------------------- -----\n" ); + count = 0; + LIST_FOR_EACH( tcpClient_t, client, &svs.tcpClients, entry ) { + Com_Printf( "%3d %-20.20s %3d %7u %-21s ", + count, client->resource ? client->resource : "", + FIFO_Usage( &client->stream.send ), + svs.realtime - client->lastmessage, + NET_AdrToString( &client->stream.address ) ); + + switch( client->state ) { + case cs_zombie: + Com_Printf( "ZMBI " ); + break; + case cs_assigned: + Com_Printf( "ASGN " ); + break; + case cs_connected: + Com_Printf( "CNCT " ); + break; + default: + Com_Printf( "SEND " ); + break; + } + Com_Printf( "\n" ); + + count++; + } +} + +static void SV_DumpTcpVersions( void ) { + tcpClient_t *client; + int count; + + Com_Printf( +"num address user-agent\n" +"--- --------------------- -----------------------------------------\n" ); + + count = 0; + LIST_FOR_EACH( tcpClient_t, client, &svs.tcpClients, entry ) { + Com_Printf( "%3i %-21s %-40.40s\n", + count, NET_AdrToString( &client->stream.address ), + client->agent ? client->agent : "" ); + count++; + } +} + + +/* +================ +SV_Status_f +================ +*/ +static void SV_Status_f( void ) { + if( !svs.initialized ) { + Com_Printf( "No server running.\n" ); + return; + } + + if( sv.name[0] ) { + Com_Printf( "Current map: %s\n\n", sv.name ); + } + + if( LIST_EMPTY( &svs.clients ) ) { + Com_Printf( "No UDP clients.\n" ); + } else { + if( Cmd_Argc() > 1 ) { + SV_DumpUdpVersions(); + } else { + SV_DumpUdpClients(); + } + } + Com_Printf( "\n" ); + + if( LIST_EMPTY( &svs.tcpClients ) ) { + Com_Printf( "No TCP clients.\n" ); + } else { + if( Cmd_Argc() > 1 ) { + SV_DumpTcpVersions(); + } else { + SV_DumpTcpClients(); + } + } + Com_Printf( "\n" ); +} + +/* +================== +SV_ConSay_f +================== +*/ +void SV_ConSay_f( void ) { + client_t *client; + char *s; + + if( !svs.initialized ) { + Com_Printf( "No server running\n" ); + return; + } + + if( Cmd_Argc() < 2 ) { + Com_Printf( "Usage: %s <text>\n", Cmd_Argv( 0 ) ); + return; + } + + s = Cmd_RawArgs(); + FOR_EACH_CLIENT( client ) { + if( client->state != cs_spawned ) + continue; + SV_ClientPrintf( client, PRINT_CHAT, "console: %s\n", s ); + } + + Com_Printf( "console: %s\n", s ); +} + + +/* +================== +SV_Heartbeat_f +================== +*/ +static void SV_Heartbeat_f( void ) { + svs.last_heartbeat = -9999999; +} + + +/* +=========== +SV_Serverinfo_f + + Examine or change the serverinfo string +=========== +*/ +static void SV_Serverinfo_f( void ) { + Com_Printf( "Server info settings:\n" ); + Info_Print( Cvar_Serverinfo() ); +} + + +/* +=========== +SV_DumpUser_f + +Examine all a users info strings +=========== +*/ +static void SV_DumpUser_f( void ) { + if( !svs.initialized ) { + Com_Printf( "No server running\n" ); + return; + } + + if( Cmd_Argc() != 2 ) { + Com_Printf( "Usage: %s <userid>\n", Cmd_Argv( 0 ) ); + return; + } + + if( !SV_SetPlayer() ) + return; + + Com_Printf( "userinfo\n" ); + Com_Printf( "--------\n" ); + Info_Print( sv_client->userinfo ); + if( sv_client->versionString ) { + Com_Printf( "version %s\n", sv_client->versionString ); + } + + sv_client = NULL; + sv_player = NULL; + +} + +/* +================== +SV_Stuff_f + +Stuff raw command string to the client. +================== +*/ +void SV_Stuff_f( void ) { + if( !svs.initialized ) { + Com_Printf( "No server running.\n" ); + return; + } + + if( Cmd_Argc() < 3 ) { + Com_Printf( "Usage: %s <userid> <text>\n", Cmd_Argv( 0 ) ); + return; + } + + if( !SV_SetPlayer() ) + return; + + MSG_WriteByte( svc_stufftext ); + MSG_WriteString( Cmd_RawArgsFrom( 2 ) ); + SV_ClientAddMessage( sv_client, MSG_RELIABLE|MSG_CLEAR ); + + sv_client = NULL; + sv_player = NULL; +} + +/* +================== +SV_Stuff_f + +Stuff raw command string to all clients. +================== +*/ +void SV_Stuffall_f( void ) { + client_t *client; + + if( !svs.initialized ) { + Com_Printf( "No server running.\n" ); + return; + } + + if( Cmd_Argc() < 2 ) { + Com_Printf( "Usage: %s <text>\n", Cmd_Argv( 0 ) ); + return; + } + + MSG_WriteByte( svc_stufftext ); + MSG_WriteString( Cmd_RawArgsFrom( 1 ) ); + + FOR_EACH_CLIENT( client ) { + SV_ClientAddMessage( client, MSG_RELIABLE ); + } + + SZ_Clear( &msg_write ); + +} + +void SVC_GetChallenge( void ); + +static void SV_PickClient_f( void ) { + char *s; + netadr_t address; + + if( !svs.initialized ) { + Com_Printf( "No server running.\n" ); + return; + } + if( sv_maxclients->integer == 1 ) { + Com_Printf( "Single player server running.\n" ); + return; + } + + if ( Cmd_Argc() < 2 ) { + Com_Printf( "Usage: %s <address>\n", Cmd_Argv( 0 ) ); + return; + } + + s = Cmd_Argv( 1 ); + if ( !NET_StringToAdr( s, &address ) ) { + Com_Printf( "Bad client address: %s\n", s ); + return; + } + if ( address.port == 0 ) { + Com_Printf( "Please specify client port explicitly.\n" ); + return; + } + + Netchan_OutOfBandPrint( NS_SERVER, &address, "passive_connect\n" ); +} + + +/* +=============== +SV_KillServer_f + +Kick everyone off, possibly in preparation for a new game + +=============== +*/ +void SV_KillServer_f( void ) { + if( !svs.initialized ) { + Com_Printf( "No server running.\n" ); + return; + } + + SV_Shutdown( "Server was killed.\n", KILL_DROP ); +} + +/* +=============== +SV_ServerCommand_f + +Let the game dll handle a command +=============== +*/ +void SV_ServerCommand_f( void ) { + if( !ge ) { + Com_Printf( "No game loaded.\n" ); + return; + } + + ge->ServerCommand(); +} + +static void SV_Client_m( char *buffer, int bufferSize ) { + if( !sv_client ) { + Q_strncpyz( buffer, "unknown", bufferSize ); + return; + } + + Q_strncpyz( buffer, sv_client->name, bufferSize ); +} + +//=========================================================== + +static const cmdreg_t c_server[] = { + { "heartbeat", SV_Heartbeat_f }, + { "kick", SV_Kick_f, SV_SetPlayer_g }, + { "status", SV_Status_f }, + { "serverinfo", SV_Serverinfo_f }, + { "dumpuser", SV_DumpUser_f, SV_SetPlayer_g }, + { "stuff", SV_Stuff_f, SV_SetPlayer_g }, + { "stuffall", SV_Stuffall_f }, + { "map", SV_Map_f, SV_Map_g }, + { "demomap", SV_DemoMap_f }, + { "gamemap", SV_GameMap_f, SV_Map_g }, + { "setmaster", SV_SetMaster_f }, + { "save", SV_Savegame_f }, + { "load", SV_Loadgame_f }, + { "killserver", SV_KillServer_f }, + { "sv", SV_ServerCommand_f }, + { "pick", SV_PickClient_f }, + + { NULL } +}; + + +/* +================== +SV_InitOperatorCommands +================== +*/ +void SV_InitOperatorCommands( void ) { + Cmd_Register( c_server ); + + if ( dedicated->integer ) + Cmd_AddCommand( "say", SV_ConSay_f ); + + + Cmd_AddMacro ("sv_client", SV_Client_m); + +} + + |