2
0
mirror of https://github.com/FWGS/xash3d-fwgs synced 2025-01-11 02:45:46 +01:00
xash3d-fwgs/engine/server/sv_cmds.c
Gleb Mazovetskiy 5e0a0765ce Trim all trailing whitespace
The `.editorconfig` file in this repo is configured to trim all trailing
whitespace regardless of whether the line is modified.

Trims all trailing whitespace in the repository to make the codebase easier
to work with in editors that respect `.editorconfig`.

`git blame` becomes less useful on these lines but it already isn't very useful.

Commands:

```
find . -type f -name '*.h' -exec sed --in-place 's/[[:space:]]\+$//' {} \+
find . -type f -name '*.c' -exec sed --in-place 's/[[:space:]]\+$//' {} \+
```
2021-01-04 20:55:10 +03:00

1027 lines
20 KiB
C

/*
sv_cmds.c - server console commands
Copyright (C) 2007 Uncle Mike
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 3 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.
*/
#include "common.h"
#include "server.h"
extern convar_t *con_gamemaps;
/*
=================
SV_ClientPrintf
Sends text across to be displayed if the level passes
=================
*/
void SV_ClientPrintf( sv_client_t *cl, char *fmt, ... )
{
char string[MAX_SYSPATH];
va_list argptr;
if( FBitSet( cl->flags, FCL_FAKECLIENT ))
return;
va_start( argptr, fmt );
Q_vsprintf( string, fmt, argptr );
va_end( argptr );
MSG_BeginServerCmd( &cl->netchan.message, svc_print );
MSG_WriteString( &cl->netchan.message, string );
}
/*
=================
SV_BroadcastPrintf
Sends text to all active clients
=================
*/
void SV_BroadcastPrintf( sv_client_t *ignore, char *fmt, ... )
{
char string[MAX_SYSPATH];
va_list argptr;
sv_client_t *cl;
int i;
va_start( argptr, fmt );
Q_vsprintf( string, fmt, argptr );
va_end( argptr );
if( sv.state == ss_active )
{
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
{
if( FBitSet( cl->flags, FCL_FAKECLIENT ))
continue;
if( cl == ignore || cl->state != cs_spawned )
continue;
MSG_BeginServerCmd( &cl->netchan.message, svc_print );
MSG_WriteString( &cl->netchan.message, string );
}
}
if( Host_IsDedicated() )
{
// echo to console
Con_DPrintf( "%s", string );
}
}
/*
=================
SV_BroadcastCommand
Sends text to all active clients
=================
*/
void SV_BroadcastCommand( const char *fmt, ... )
{
char string[MAX_SYSPATH];
va_list argptr;
if( sv.state == ss_dead )
return;
va_start( argptr, fmt );
Q_vsprintf( string, fmt, argptr );
va_end( argptr );
MSG_BeginServerCmd( &sv.reliable_datagram, svc_stufftext );
MSG_WriteString( &sv.reliable_datagram, string );
}
/*
==================
SV_SetPlayer
Sets sv_client and sv_player to the player with idnum Cmd_Argv(1)
==================
*/
static sv_client_t *SV_SetPlayer( void )
{
const char *s;
sv_client_t *cl;
int i, idnum;
if( !svs.clients || sv.background )
return NULL;
if( svs.maxclients == 1 || Cmd_Argc() < 2 )
{
// special case for local client
return svs.clients;
}
s = Cmd_Argv( 1 );
// numeric values are just slot numbers
if( Q_isdigit( s ) || (s[0] == '-' && Q_isdigit( s + 1 )))
{
idnum = Q_atoi( s );
if( idnum < 0 || idnum >= svs.maxclients )
{
Con_Printf( "Bad client slot: %i\n", idnum );
return NULL;
}
cl = &svs.clients[idnum];
if( !cl->state )
{
Con_Printf( "Client %i is not active\n", idnum );
return NULL;
}
return cl;
}
// check for a name match
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
{
if( !cl->state ) continue;
if( !Q_strcmp( cl->name, s ))
return cl;
}
Con_Printf( "Userid %s is not on the server\n", s );
return NULL;
}
/*
==================
SV_ValidateMap
check map for typically errors
==================
*/
qboolean SV_ValidateMap( const char *pMapName, qboolean check_spawn )
{
char *spawn_entity;
int flags;
// determine spawn entity classname
if( !check_spawn || (int)sv_maxclients->value <= 1 )
spawn_entity = GI->sp_entity;
else spawn_entity = GI->mp_entity;
flags = SV_MapIsValid( pMapName, spawn_entity, NULL );
if( FBitSet( flags, MAP_INVALID_VERSION ))
{
Con_Printf( S_ERROR "map %s is invalid or not supported\n", pMapName );
return false;
}
if( !FBitSet( flags, MAP_IS_EXIST ))
{
Con_Printf( S_ERROR "map %s doesn't exist\n", pMapName );
return false;
}
if( check_spawn && !FBitSet( flags, MAP_HAS_SPAWNPOINT ))
{
Con_Printf( S_ERROR "map %s doesn't have a valid spawnpoint\n", pMapName );
return false;
}
return true;
}
/*
==================
SV_Map_f
Goes directly to a given map without any savegame archiving.
For development work
==================
*/
void SV_Map_f( void )
{
char mapname[MAX_QPATH];
if( Cmd_Argc() != 2 )
{
Con_Printf( S_USAGE "map <mapname>\n" );
return;
}
// hold mapname to other place
Q_strncpy( mapname, Cmd_Argv( 1 ), sizeof( mapname ));
COM_StripExtension( mapname );
if( !SV_ValidateMap( mapname, true ))
return;
Cvar_DirectSet( sv_hostmap, mapname );
COM_LoadLevel( mapname, false );
}
/*
==================
SV_MapBackground_f
Set background map (enable physics in menu)
==================
*/
void SV_MapBackground_f( void )
{
char mapname[MAX_QPATH];
if( Cmd_Argc() != 2 )
{
Con_Printf( S_USAGE "map_background <mapname>\n" );
return;
}
if( SV_Active() && !sv.background )
{
if( GameState->nextstate == STATE_RUNFRAME )
Con_Printf( S_ERROR "can't set background map while game is active\n" );
return;
}
// hold mapname to other place
Q_strncpy( mapname, Cmd_Argv( 1 ), sizeof( mapname ));
COM_StripExtension( mapname );
if( !SV_ValidateMap( mapname, false ))
return;
// background map is always run as singleplayer
Cvar_FullSet( "maxplayers", "1", FCVAR_LATCH );
Cvar_FullSet( "deathmatch", "0", FCVAR_LATCH );
Cvar_FullSet( "coop", "0", FCVAR_LATCH );
COM_LoadLevel( mapname, true );
}
/*
==================
SV_NextMap_f
Change map for next in alpha-bethical ordering
For development work
==================
*/
void SV_NextMap_f( void )
{
char nextmap[MAX_QPATH];
int i, next;
search_t *t;
t = FS_Search( "maps\\*.bsp", true, CVAR_TO_BOOL( con_gamemaps )); // only in gamedir
if( !t ) t = FS_Search( "maps/*.bsp", true, CVAR_TO_BOOL( con_gamemaps )); // only in gamedir
if( !t )
{
Con_Printf( "next map can't be found\n" );
return;
}
for( i = 0; i < t->numfilenames; i++ )
{
const char *ext = COM_FileExtension( t->filenames[i] );
if( Q_stricmp( ext, "bsp" ))
continue;
COM_FileBase( t->filenames[i], nextmap );
if( Q_stricmp( sv_hostmap->string, nextmap ))
continue;
next = ( i + 1 ) % t->numfilenames;
COM_FileBase( t->filenames[next], nextmap );
Cvar_DirectSet( sv_hostmap, nextmap );
// found current point, check for valid
if( SV_ValidateMap( nextmap, true ))
{
// found and valid
COM_LoadLevel( nextmap, false );
Mem_Free( t );
return;
}
// jump to next map
}
Con_Printf( "failed to load next map\n" );
Mem_Free( t );
}
/*
==============
SV_NewGame_f
==============
*/
void SV_NewGame_f( void )
{
if( Cmd_Argc() != 1 )
{
Con_Printf( S_USAGE "newgame\n" );
return;
}
COM_NewGame( GI->startmap );
}
/*
==============
SV_HazardCourse_f
==============
*/
void SV_HazardCourse_f( void )
{
if( Cmd_Argc() != 1 )
{
Con_Printf( S_USAGE "hazardcourse\n" );
return;
}
// special case for Gunman Chronicles: playing avi-file
if( FS_FileExists( va( "media/%s.avi", GI->trainmap ), false ))
{
Cbuf_AddText( va( "wait; movie %s\n", GI->trainmap ));
Host_EndGame( true, DEFAULT_ENDGAME_MESSAGE );
}
else COM_NewGame( GI->trainmap );
}
/*
==============
SV_Load_f
==============
*/
void SV_Load_f( void )
{
char path[MAX_QPATH];
if( Cmd_Argc() != 2 )
{
Con_Printf( S_USAGE "load <savename>\n" );
return;
}
Q_snprintf( path, sizeof( path ), DEFAULT_SAVE_DIRECTORY "%s.sav", Cmd_Argv( 1 ));
SV_LoadGame( path );
}
/*
==============
SV_QuickLoad_f
==============
*/
void SV_QuickLoad_f( void )
{
Cbuf_AddText( "echo Quick Loading...; wait; load quick" );
}
/*
==============
SV_Save_f
==============
*/
void SV_Save_f( void )
{
switch( Cmd_Argc( ))
{
case 1:
SV_SaveGame( "new" );
break;
case 2:
SV_SaveGame( Cmd_Argv( 1 ));
break;
default:
Con_Printf( S_USAGE "save <savename>\n" );
break;
}
}
/*
==============
SV_QuickSave_f
==============
*/
void SV_QuickSave_f( void )
{
Cbuf_AddText( "echo Quick Saving...; wait; save quick" );
}
/*
==============
SV_DeleteSave_f
==============
*/
void SV_DeleteSave_f( void )
{
if( Cmd_Argc() != 2 )
{
Con_Printf( S_USAGE "killsave <name>\n" );
return;
}
// delete save and saveshot
FS_Delete( va( DEFAULT_SAVE_DIRECTORY "%s.sav", Cmd_Argv( 1 )));
FS_Delete( va( DEFAULT_SAVE_DIRECTORY "%s.bmp", Cmd_Argv( 1 )));
}
/*
==============
SV_AutoSave_f
==============
*/
void SV_AutoSave_f( void )
{
if( Cmd_Argc() != 1 )
{
Con_Printf( S_USAGE "autosave\n" );
return;
}
SV_SaveGame( "autosave" );
}
/*
==================
SV_Restart_f
restarts current level
==================
*/
void SV_Restart_f( void )
{
// because restart can be multiple issued
if( sv.state != ss_active )
return;
COM_LoadLevel( sv.name, sv.background );
}
/*
==================
SV_Reload_f
continue from latest savedgame
==================
*/
void SV_Reload_f( void )
{
// because reload can be multiple issued
if( GameState->nextstate != STATE_RUNFRAME )
return;
if( !SV_LoadGame( SV_GetLatestSave( )))
COM_LoadLevel( sv_hostmap->string, false );
}
/*
==================
SV_ChangeLevel_f
classic change level
==================
*/
void SV_ChangeLevel_f( void )
{
if( Cmd_Argc() != 2 )
{
Con_Printf( S_USAGE "changelevel <mapname>\n" );
return;
}
SV_QueueChangeLevel( Cmd_Argv( 1 ), NULL );
}
/*
==================
SV_ChangeLevel2_f
smooth change level
==================
*/
void SV_ChangeLevel2_f( void )
{
if( Cmd_Argc() != 3 )
{
Con_Printf( S_USAGE "changelevel2 <mapname> <landmark>\n" );
return;
}
SV_QueueChangeLevel( Cmd_Argv( 1 ), Cmd_Argv( 2 ));
}
/*
==================
SV_Kick_f
Kick a user off of the server
==================
*/
void SV_Kick_f( void )
{
sv_client_t *cl;
const char *param, *clientId;
if( Cmd_Argc() != 2 )
{
Con_Printf( S_USAGE "kick <#id|name> [reason]\n" );
return;
}
param = Cmd_Argv( 1 );
if( *param == '#' && Q_isdigit( param + 1 ) )
cl = SV_ClientById( Q_atoi( param + 1 ) );
else cl = SV_ClientByName( param );
if( !cl )
{
Con_Printf( "Client is not on the server\n" );
return;
}
if( NET_IsLocalAddress( cl->netchan.remote_address ))
{
Con_Printf( "The local player cannot be kicked!\n" );
return;
}
param = Cmd_Argv( 2 );
clientId = SV_GetClientIDString( cl );
if( *param )
{
Log_Printf( "Kick: \"%s<%i><%s><>\" was kicked by \"Console\" (message \"%s\")\n", cl->name, cl->userid, clientId, param );
SV_BroadcastPrintf( cl, "%s was kicked with message: \"%s\"\n", cl->name, param );
SV_ClientPrintf( cl, "You were kicked from the game with message: \"%s\"\n", param );
}
else
{
Log_Printf( "Kick: \"%s<%i><%s><>\" was kicked by \"Console\"\n", cl->name, cl->userid, clientId );
SV_BroadcastPrintf( cl, "%s was kicked\n", cl->name );
SV_ClientPrintf( cl, "You were kicked from the game\n" );
}
if( cl->useragent[0] )
{
if( *param )
Netchan_OutOfBandPrint( NS_SERVER, cl->netchan.remote_address, "errormsg\nKicked with message:\n%s\n", param );
else
Netchan_OutOfBandPrint( NS_SERVER, cl->netchan.remote_address, "errormsg\nYou were kicked from the game\n" );
}
SV_DropClient( cl, false );
}
/*
==================
SV_EntPatch_f
==================
*/
void SV_EntPatch_f( void )
{
const char *mapname;
if( Cmd_Argc() < 2 )
{
if( sv.state != ss_dead )
{
mapname = sv.name;
}
else
{
Con_Printf( S_USAGE "entpatch <mapname>\n" );
return;
}
}
else mapname = Cmd_Argv( 1 );
SV_WriteEntityPatch( mapname );
}
/*
================
SV_Status_f
================
*/
void SV_Status_f( void )
{
sv_client_t *cl;
int i;
if( !svs.clients || sv.background )
{
Con_Printf( "^3no server running.\n" );
return;
}
Con_Printf( "map: %s\n", sv.name );
Con_Printf( "num score ping name lastmsg address port \n" );
Con_Printf( "--- ----- ------- --------------- ------- --------------------- ------\n" );
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
{
int j, l;
char *s;
if( !cl->state ) continue;
Con_Printf( "%3i ", i );
Con_Printf( "%5i ", (int)cl->edict->v.frags );
if( cl->state == cs_connected ) Con_Printf( "Connect" );
else if( cl->state == cs_zombie ) Con_Printf( "Zombie " );
else if( FBitSet( cl->flags, FCL_FAKECLIENT )) Con_Printf( "Bot " );
else Con_Printf( "%7i ", SV_CalcPing( cl ));
Con_Printf( "%s", cl->name );
l = 24 - Q_strlen( cl->name );
for( j = 0; j < l; j++ ) Con_Printf( " " );
Con_Printf( "%g ", ( host.realtime - cl->netchan.last_received ));
s = NET_BaseAdrToString( cl->netchan.remote_address );
Con_Printf( "%s", s );
l = 22 - Q_strlen( s );
for( j = 0; j < l; j++ ) Con_Printf( " " );
Con_Printf( "%5i", cl->netchan.qport );
Con_Printf( "\n" );
}
Con_Printf( "\n" );
}
/*
==================
SV_ConSay_f
==================
*/
void SV_ConSay_f( void )
{
const char *p;
char text[MAX_SYSPATH];
if( Cmd_Argc() < 2 ) return;
if( !svs.clients || sv.background )
{
Con_Printf( "^3no server running.\n" );
return;
}
p = Cmd_Args();
Q_strncpy( text, *p == '"' ? p + 1 : p, MAX_SYSPATH );
if( *p == '"' )
{
text[Q_strlen(text) - 1] = 0;
}
Log_Printf( "Server say: \"%s\"\n", text );
Q_snprintf( text, sizeof( text ), "%s: %s", Cvar_VariableString( "hostname" ), p );
SV_BroadcastPrintf( NULL, "%s\n", text );
}
/*
==================
SV_Heartbeat_f
==================
*/
void SV_Heartbeat_f( void )
{
svs.last_heartbeat = MAX_HEARTBEAT;
}
/*
===========
SV_ServerInfo_f
Examine or change the serverinfo string
===========
*/
void SV_ServerInfo_f( void )
{
convar_t *var;
if( Cmd_Argc() == 1 )
{
Con_Printf( "Server info settings:\n" );
Info_Print( svs.serverinfo );
Con_Printf( "Total %i symbols\n", Q_strlen( svs.serverinfo ));
return;
}
if( Cmd_Argc() != 3 )
{
Con_Printf( S_USAGE "serverinfo [ <key> <value> ]\n");
return;
}
if( Cmd_Argv(1)[0] == '*' )
{
Con_Printf( "Star variables cannot be changed.\n" );
return;
}
// if this is a cvar, change it too
var = Cvar_FindVar( Cmd_Argv( 1 ));
if( var )
{
freestring( var->string ); // free the old value string
var->string = copystring( Cmd_Argv( 2 ));
var->value = Q_atof( var->string );
}
Info_SetValueForStarKey( svs.serverinfo, Cmd_Argv( 1 ), Cmd_Argv( 2 ), MAX_SERVERINFO_STRING );
SV_BroadcastCommand( "fullserverinfo \"%s\"\n", SV_Serverinfo( ));
}
/*
===========
SV_LocalInfo_f
Examine or change the localinfo string
===========
*/
void SV_LocalInfo_f( void )
{
if( Cmd_Argc() == 1 )
{
Con_Printf( "Local info settings:\n" );
Info_Print( svs.localinfo );
Con_Printf( "Total %i symbols\n", Q_strlen( svs.localinfo ));
return;
}
if( Cmd_Argc() != 3 )
{
Con_Printf( S_USAGE "localinfo [ <key> <value> ]\n");
return;
}
if( Cmd_Argv(1)[0] == '*' )
{
Con_Printf( "Star variables cannot be changed.\n" );
return;
}
Info_SetValueForStarKey( svs.localinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_LOCALINFO_STRING );
}
/*
===========
SV_ClientInfo_f
Examine all a users info strings
===========
*/
void SV_ClientInfo_f( void )
{
sv_client_t *cl;
if( Cmd_Argc() != 2 )
{
Con_Printf( S_USAGE "clientinfo <userid>\n" );
return;
}
if(( cl = SV_SetPlayer( )) == NULL )
return;
Con_Printf( "userinfo\n" );
Con_Printf( "--------\n" );
Info_Print( cl->userinfo );
}
/*
===========
SV_ClientUserAgent_f
Examine useragent strings
===========
*/
void SV_ClientUserAgent_f( void )
{
sv_client_t *cl;
if( Cmd_Argc() != 2 )
{
Con_Printf( S_USAGE "clientuseragent <userid>\n" );
return;
}
if(( cl = SV_SetPlayer( )) == NULL )
return;
Con_Printf( "useragent\n" );
Con_Printf( "---------\n" );
Info_Print( cl->useragent );
}
/*
===============
SV_KillServer_f
Kick everyone off, possibly in preparation for a new game
===============
*/
void SV_KillServer_f( void )
{
Host_ShutdownServer();
}
/*
===============
SV_PlayersOnly_f
disable plhysics but players
===============
*/
void SV_PlayersOnly_f( void )
{
if( !Cvar_VariableInteger( "sv_cheats" )) return;
sv.playersonly ^= 1;
SV_BroadcastPrintf( NULL, "%s game physic\n", sv.playersonly ? "Freeze" : "Resume" );
}
/*
===============
SV_EdictUsage_f
===============
*/
void SV_EdictUsage_f( void )
{
int active;
if( sv.state != ss_active )
{
Con_Printf( "^3no server running.\n" );
return;
}
active = pfnNumberOfEntities();
Con_Printf( "%5i edicts is used\n", active );
Con_Printf( "%5i edicts is free\n", GI->max_edicts - active );
Con_Printf( "%5i total\n", GI->max_edicts );
}
/*
===============
SV_EntityInfo_f
===============
*/
void SV_EntityInfo_f( void )
{
edict_t *ent;
int i;
if( sv.state != ss_active )
{
Con_Printf( "^3no server running.\n" );
return;
}
for( i = 0; i < svgame.numEntities; i++ )
{
ent = EDICT_NUM( i );
if( !SV_IsValidEdict( ent )) continue;
Con_Printf( "%5i origin: %.f %.f %.f", i, ent->v.origin[0], ent->v.origin[1], ent->v.origin[2] );
if( ent->v.classname )
Con_Printf( ", class: %s", STRING( ent->v.classname ));
if( ent->v.globalname )
Con_Printf( ", global: %s", STRING( ent->v.globalname ));
if( ent->v.targetname )
Con_Printf( ", name: %s", STRING( ent->v.targetname ));
if( ent->v.target )
Con_Printf( ", target: %s", STRING( ent->v.target ));
if( ent->v.model )
Con_Printf( ", model: %s", STRING( ent->v.model ));
Con_Printf( "\n" );
}
}
/*
==================
SV_InitHostCommands
commands that create server
is available always
==================
*/
void SV_InitHostCommands( void )
{
Cmd_AddCommand( "map", SV_Map_f, "start new level" );
if( host.type == HOST_NORMAL )
{
Cmd_AddCommand( "newgame", SV_NewGame_f, "begin new game" );
Cmd_AddCommand( "hazardcourse", SV_HazardCourse_f, "starting a Hazard Course" );
Cmd_AddCommand( "map_background", SV_MapBackground_f, "set background map" );
Cmd_AddCommand( "load", SV_Load_f, "load a saved game file" );
Cmd_AddCommand( "loadquick", SV_QuickLoad_f, "load a quick-saved game file" );
Cmd_AddCommand( "reload", SV_Reload_f, "continue from latest save or restart level" );
Cmd_AddCommand( "killsave", SV_DeleteSave_f, "delete a saved game file and saveshot" );
Cmd_AddCommand( "nextmap", SV_NextMap_f, "load next level" );
}
}
/*
==================
SV_InitOperatorCommands
==================
*/
void SV_InitOperatorCommands( void )
{
Cmd_AddCommand( "heartbeat", SV_Heartbeat_f, "send a heartbeat to the master server" );
Cmd_AddCommand( "kick", SV_Kick_f, "kick a player off the server by number or name" );
Cmd_AddCommand( "status", SV_Status_f, "print server status information" );
Cmd_AddCommand( "localinfo", SV_LocalInfo_f, "examine or change the localinfo string" );
Cmd_AddCommand( "serverinfo", SV_ServerInfo_f, "examine or change the serverinfo string" );
Cmd_AddCommand( "clientinfo", SV_ClientInfo_f, "print user infostring (player num required)" );
Cmd_AddCommand( "clientuseragent", SV_ClientUserAgent_f, "print user agent (player num required)" );
Cmd_AddCommand( "playersonly", SV_PlayersOnly_f, "freezes time, except for players" );
Cmd_AddCommand( "restart", SV_Restart_f, "restarting current level" );
Cmd_AddCommand( "entpatch", SV_EntPatch_f, "write entity patch to allow external editing" );
Cmd_AddCommand( "edict_usage", SV_EdictUsage_f, "show info about edicts usage" );
Cmd_AddCommand( "entity_info", SV_EntityInfo_f, "show more info about edicts" );
Cmd_AddCommand( "shutdownserver", SV_KillServer_f, "shutdown current server" );
Cmd_AddCommand( "changelevel", SV_ChangeLevel_f, "change level" );
Cmd_AddCommand( "changelevel2", SV_ChangeLevel2_f, "smooth change level" );
if( host.type == HOST_NORMAL )
{
Cmd_AddCommand( "save", SV_Save_f, "save the game to a file" );
Cmd_AddCommand( "savequick", SV_QuickSave_f, "save the game to the quicksave" );
Cmd_AddCommand( "autosave", SV_AutoSave_f, "save the game to 'autosave' file" );
}
else if( host.type == HOST_DEDICATED )
{
Cmd_AddCommand( "say", SV_ConSay_f, "send a chat message to everyone on the server" );
}
}
/*
==================
SV_KillOperatorCommands
==================
*/
void SV_KillOperatorCommands( void )
{
Cmd_RemoveCommand( "heartbeat" );
Cmd_RemoveCommand( "kick" );
Cmd_RemoveCommand( "status" );
Cmd_RemoveCommand( "localinfo" );
Cmd_RemoveCommand( "serverinfo" );
Cmd_RemoveCommand( "clientinfo" );
Cmd_RemoveCommand( "playersonly" );
Cmd_RemoveCommand( "restart" );
Cmd_RemoveCommand( "entpatch" );
Cmd_RemoveCommand( "edict_usage" );
Cmd_RemoveCommand( "entity_info" );
Cmd_RemoveCommand( "shutdownserver" );
Cmd_RemoveCommand( "changelevel" );
Cmd_RemoveCommand( "changelevel2" );
if( host.type == HOST_NORMAL )
{
Cmd_RemoveCommand( "save" );
Cmd_RemoveCommand( "savequick" );
Cmd_RemoveCommand( "autosave" );
}
else if( host.type == HOST_DEDICATED )
{
Cmd_RemoveCommand( "say" );
}
}