diff options
Diffstat (limited to 'src/game/g_admin.c')
-rw-r--r-- | src/game/g_admin.c | 9006 |
1 files changed, 2105 insertions, 6901 deletions
diff --git a/src/game/g_admin.c b/src/game/g_admin.c index dc19530..afc40cb 100644 --- a/src/game/g_admin.c +++ b/src/game/g_admin.c @@ -1,6 +1,7 @@ /* =========================================================================== -Copyright (C) 2004-2006 Tony J. White +Copyright (C) 2000-2013 Darklegion Development +Copyright (C) 2015-2019 GrangerHub This file is part of Tremulous. @@ -11,12 +12,12 @@ and Travis Maurer. The functionality of this code mimics the behaviour of the currently inactive project shrubet (http://www.etstats.com/shrubet/index.php?ver=2) -by Ryan Mannion. However, shrubet was a closed-source project and -none of it's code has been copied, only it's functionality. +by Ryan Mannion. However, shrubet was a closed-source project and +none of its code has been copied, only its functionality. Tremulous 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, +published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Tremulous is distributed in the hope that it will be @@ -25,8 +26,8 @@ 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 Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +along with Tremulous; if not, see <https://www.gnu.org/licenses/> + =========================================================================== */ @@ -36,420 +37,270 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA static char g_bfb[ 32000 ]; // note: list ordered alphabetically -g_admin_cmd_t g_admin_cmds[ ] = - { - {"adjustban", G_admin_adjustban, "ban", - "change the duration or reason of a ban. time is specified as numbers " - "followed by units 'w' (weeks), 'd' (days), 'h' (hours) or 'm' (minutes)," - " or seconds if no units are specified. if the duration is" - " preceded by a + or -, the ban duration will be extended or shortened by" - " the specified amount", - "[^3ban#^7] (^5duration^7) (^5reason^7)" +g_admin_cmd_t g_admin_cmds[ ] = + { + {"addlayout", G_admin_addlayout, qfalse, "addlayout", + "place layout elements into the game. the elements are specified by a " + "union of filtered layouts. the syntax is demonstrated by an example: " + "^5reactor,telenode|westside+alien|sewers^7 will place only the " + "reactor and telenodes from the westside layout, and also all alien " + "layout elements from the sewers layout", + "[^3layoutelements^7]" + }, + + {"adjustban", G_admin_adjustban, qfalse, "ban", + "change the IP address mask, duration or reason of a ban. mask is " + "prefixed with '/'. duration is specified as numbers followed by units " + " 'w' (weeks), 'd' (days), 'h' (hours) or 'm' (minutes), or seconds if " + " no unit is specified. if the duration is preceded by a + or -, the " + "ban duration will be extended or shortened by the specified amount", + "[^3ban#^7] (^5/mask^7) (^5duration^7) (^5reason^7)" }, - - {"adminlog", G_admin_adminlog, "adminlog", - "list recent admin activity", - "(^5start id#|name|!command|-skip#^7) (^5search skip#^7)" + + {"adminhelp", G_admin_adminhelp, qtrue, "adminhelp", + "display admin commands available to you or help on a specific command", + "(^5command^7)" }, - {"admintest", G_admin_admintest, "admintest", + {"admintest", G_admin_admintest, qfalse, "admintest", "display your current admin level", "" }, - {"allowbuild", G_admin_denybuild, "denybuild", + {"allowbuild", G_admin_denybuild, qfalse, "denybuild", "restore a player's ability to build", "[^3name|slot#^7]" }, - {"allowweapon", G_admin_denyweapon, "denyweapon", - "restore a player's ability to use a weapon or class", - "[^3name|slot#^7] [^3class|weapon|all^7]" - }, - - {"allready", G_admin_allready, "allready", + {"allready", G_admin_allready, qfalse, "allready", "makes everyone ready in intermission", "" }, - {"ban", G_admin_ban, "ban", + {"ban", G_admin_ban, qfalse, "ban", "ban a player by IP and GUID with an optional expiration time and reason." - " time is specified as numbers followed by units 'w' (weeks), 'd' " + " duration is specified as numbers followed by units 'w' (weeks), 'd' " "(days), 'h' (hours) or 'm' (minutes), or seconds if no units are " "specified", - "[^3name|slot#|IP^7] (^5time^7) (^5reason^7)" - }, - - {"buildlog", G_admin_buildlog, "buildlog", - "display a list of recent builds and deconstructs, optionally specifying" - " a team", - "(^5xnum^7) (^5#skip^7) (^5-name|num^7) (^5a|h^7)" - "\n ^3Example:^7 '!buildlog #10 h' skips 10 events, then shows the previous 10 events affecting human buildables" + "[^3name|slot#|IP(/mask)^7] (^5duration^7) (^5reason^7)" }, - {"cancelvote", G_admin_cancelvote, "cancelvote", - "cancel a vote taking place", + {"builder", G_admin_builder, qtrue, "builder", + "show who built a structure", "" }, - - {"cp", G_admin_cp, "cp", - "display a CP message to users, optionally specifying team(s) to send to", - "(-AHS) [^3message^7]" - }, - {"decon", G_admin_decon, "decon", - "Reverts a decon previously made and ban the user for the time specified in g_deconBanTime", - "[^3name|slot#^7]" - }, - - {"demo", G_admin_demo, "demo", - "turn admin chat off for the caller so it does not appear in demos. " - "this is a toggle use !demo again to turn warnings back on", - "" - }, - - {"denybuild", G_admin_denybuild, "denybuild", - "take away a player's ability to build", - "[^3name|slot#^7]" + {"buildlog", G_admin_buildlog, qfalse, "buildlog", + "show buildable log", + "(^5name|slot#^7) (^5id^7)" }, - {"designate", G_admin_designate, "designate", - "give the player designated builder privileges", - "[^3name|slot#^7]" + {"cancelvote", G_admin_endvote, qfalse, "cancelvote", + "cancel a vote taking place", + "(^5a|h^7)" }, - {"devmap", G_admin_devmap, "devmap", - "load a map with cheats (and optionally force layout)", + {"changemap", G_admin_changemap, qfalse, "changemap", + "load a map (and optionally force layout)", "[^3mapname^7] (^5layout^7)" }, - {"denyweapon", G_admin_denyweapon, "denyweapon", - "take away a player's ability to use a weapon or class", - "[^3name|slot#^7] [^3class|weapon^7]" - }, - - {"drop", G_admin_drop, "drop", - "kick a client from the server without log", - "[^3name|slot#^7] [^3message^7]" - }, - - {"flag", G_admin_flag, "flag", - "add an admin flag to a player, prefix flag with '-' to disallow the flag. " - "console can use this command on admin levels by prefacing a '*' to the admin level value.", - "[^3name|slot#|admin#|*adminlevel^7] (^5+^7|^5-^7)[^3flag^7]" - }, - - {"flaglist", G_admin_flaglist, "flag", - "list all flags understood by this server", - "" - }, - - {"help", G_admin_help, "help", - "display commands available to you or help on a specific command", - "(^5command^7)" + {"denybuild", G_admin_denybuild, qfalse, "denybuild", + "take away a player's ability to build", + "[^3name|slot#^7]" }, - {"info", G_admin_info, "info", - "display the contents of server info files", - "(^5subject^7)" - }, - - {"invisible", G_admin_invisible, "invisible", - "hides a player so they cannot be seen in playerlists", - "" - }, - - {"kick", G_admin_kick, "kick", + {"kick", G_admin_kick, qfalse, "kick", "kick a player with an optional reason", "[^3name|slot#^7] (^5reason^7)" }, - - {"L0", G_admin_L0, "l0", - "Sets a level 1 to level 0", - "[^3name|slot#^7]" - }, - - {"L1", G_admin_L1, "l1", - "Sets a level 0 to level 1", - "[^3name|slot#^7]" - }, - - {"layoutsave", G_admin_layoutsave, "layoutsave", - "save a map layout", - "[^3mapname^7]" - }, - - {"listadmins", G_admin_listadmins, "listadmins", + + {"listadmins", G_admin_listadmins, qtrue, "listadmins", "display a list of all server admins and their levels", - "(^5name|start admin#^7) (^5minimum level to display^7)" + "(^5name^7) (^5start admin#^7)" }, - - {"listlayouts", G_admin_listlayouts, "listlayouts", + + {"listlayouts", G_admin_listlayouts, qtrue, "listlayouts", "display a list of all available layouts for a map", "(^5mapname^7)" }, - {"listplayers", G_admin_listplayers, "listplayers", + {"listplayers", G_admin_listplayers, qtrue, "listplayers", "display a list of players, their client numbers and their levels", "" }, - - {"listmaps", G_admin_listmaps, "listmaps", - "display a list of available maps on the server", - "(^5map name^7)" - }, - - {"lock", G_admin_lock, "lock", + + {"lock", G_admin_lock, qfalse, "lock", "lock a team to prevent anyone from joining it", "[^3a|h^7]" }, - - {"map", G_admin_map, "map", - "load a map (and optionally force layout)", - "[^3mapname^7] (^5layout^7)" - }, - {"maplog", G_admin_maplog, "maplog", - "show recently played maps", - "" - }, - - {"mute", G_admin_mute, "mute", + {"mute", G_admin_mute, qfalse, "mute", "mute a player", - "[^3name|slot#^7] (Duration)" + "[^3name|slot#^7]" }, - - {"namelog", G_admin_namelog, "namelog", + + {"namelog", G_admin_namelog, qtrue, "namelog", "display a list of names used by recently connected players", - "(^5name^7)" + "(^5name|IP(/mask)^7) (start namelog#)" }, - {"nextmap", G_admin_nextmap, "nextmap", + {"nextmap", G_admin_nextmap, qfalse, "nextmap", "go to the next map in the cycle", "" }, - {"nobuild", G_admin_nobuild, "nobuild", - "set nobuild markers to prevent players from building in an area", - "(^5area^7) (^5height^7)" + {"passvote", G_admin_endvote, qfalse, "passvote", + "pass a vote currently taking place", + "(^5a|h^7)" }, - {"passvote", G_admin_passvote, "passvote", - "pass a vote currently taking place", + {"pause", G_admin_pause, qfalse, "pause", + "Pause (or unpause) the game.", "" }, - - {"pause", G_admin_pause, "pause", - "prevent a player from interacting with the game." - " * will pause all players, using no argument will pause game clock", - "(^5name|slot|*^7)" - }, - - {"putteam", G_admin_putteam, "putteam", + {"putteam", G_admin_putteam, qfalse, "putteam", "move a player to a specified team", - "[^3name|slot#^7] [^3h|a|s^7] (^3duration^7)" + "[^3name|slot#^7] [^3h|a|s^7]" }, - {"readconfig", G_admin_readconfig, "readconfig", + {"readconfig", G_admin_readconfig, qfalse, "readconfig", "reloads the admin config file and refreshes permission flags", "" }, - - {"register", G_admin_register, "register", - "Registers your name to protect it from being used by others or updates your admin name to your current name.", - "" - }, - {"rename", G_admin_rename, "rename", + {"rename", G_admin_rename, qfalse, "rename", "rename a player", "[^3name|slot#^7] [^3new name^7]" }, - {"restart", G_admin_restart, "restart", + {"restart", G_admin_restart, qfalse, "restart", "restart the current map (optionally using named layout or keeping/switching teams)", "(^5layout^7) (^5keepteams|switchteams|keepteamslock|switchteamslock^7)" }, - {"revert", G_admin_revert, "revert", - "revert one or more buildlog events, optionally of only one team", - "(^5xnum^7) (^5#ID^7) (^5-name|num^7) (^5a|h^7)" - "\n ^3Example:^7 '!revert x5 h' reverts the last 5 events affecting human buildables" + {"revert", G_admin_revert, qfalse, "revert", + "revert buildables to a given time", + "[^3id^7]" }, - {"rotation", G_admin_listrotation, "rotation", - "display a list of maps that are in the active map rotation", - "" + {"setdevmode", G_admin_setdevmode, qfalse, "setdevmode", + "switch developer mode on or off", + "[^3on|off^7]" }, - {"seen", G_admin_seen, "seen", - "find the last time a player was on the server", - "[^3name|admin#^7]" + {"setivo", G_admin_setivo, qfalse, "setivo", + "set an intermission view override", + "[^3s|a|h^7]" }, - {"setlevel", G_admin_setlevel, "setlevel", + {"setlevel", G_admin_setlevel, qfalse, "setlevel", "sets the admin level of a player", "[^3name|slot#|admin#^7] [^3level^7]" }, - {"showbans", G_admin_showbans, "showbans", - "display a (partial) list of active bans", - "(^5start at ban#^7) (^5name|IP|'-subnet'^7)" + {"setnextmap", G_admin_setnextmap, qfalse, "setnextmap", + "set the next map (and, optionally, a forced layout)", + "[^3mapname^7] (^5layout^7)" }, - {"slap", G_admin_slap, "slap", - "Do damage to a player, and send them flying", - "[^3name|slot^7] (damage)" + {"showbans", G_admin_showbans, qtrue, "showbans", + "display a (partial) list of active bans", + "(^5name|IP(/mask)^7) (^5start at ban#^7)" }, - {"spec999", G_admin_spec999, "spec999", + {"spec999", G_admin_spec999, qfalse, "spec999", "move 999 pingers to the spectator team", - "" - }, - - {"specme", G_admin_putmespec, "specme", - "moves you to the spectators", - "" - }, - - {"subnetban", G_admin_subnetban, "subnetban", - "Add or change a subnet mask on a ban", - "[^3ban#^7] [^5CIDR mask^7]" - "\n ^3Example:^7 '!subnetban 10 16' changes ban #10 to be a ban on XXX.XXX.*.*" - "\n ^3Example:^7 '!subnetban 10 24' changes ban #10 to be a ban on XXX.XXX.XXX.*" - "\n ^3Example:^7 '!subnetban 10 32' changes ban #10 to be a regular (non-subnet) ban" - "\n ^1WARNING:^7 Use of this command may make your admin.dat incompatible with other game.qvms" - }, - - {"suspendban", G_admin_suspendban, "ban", - "suspend a ban for a length of time. time is specified as numbers " - "followed by units 'w' (weeks), 'd' (days), 'h' (hours) or 'm' (minutes)," - " or seconds if no units are specified", - "[^5ban #^7] [^5length^7]" - }, + ""}, - {"time", G_admin_time, "time", + {"time", G_admin_time, qtrue, "time", "show the current local server time", - "" + ""}, + {"transform", G_admin_transform, qfalse, "magic", + "change a human player to a different player model", + "[^3name|slot#^7] [^3player model^7]" }, - {"unban", G_admin_unban, "ban", + {"unban", G_admin_unban, qfalse, "ban", "unbans a player specified by the slot as seen in showbans", "[^3ban#^7]" }, - - {"undesignate", G_admin_designate, "designate", - "revoke designated builder privileges", - "[^3name|slot#^7]" - }, - - {"unflag", G_admin_flag, "flag", - "clears an admin flag from a player. " - "console can use this command on admin levels by prefacing a '*' to the admin level value.", - "[^3name|slot#|admin#|*adminlevel^7] (^5+^7|^5-^7)[^3flag^7]" - }, - {"unlock", G_admin_unlock, "lock", + {"unlock", G_admin_lock, qfalse, "lock", "unlock a locked team", "[^3a|h^7]" }, - - {"unmute", G_admin_mute, "mute", + + {"unmute", G_admin_mute, qfalse, "mute", "unmute a muted player", "[^3name|slot#^7]" }, - {"unpause", G_admin_pause, "pause", - "allow a player to interact with the game." - " * will unpause all players, using no argument will unpause game clock", - "(^5name|slot|*^7)" - }, - - { - "warn", G_admin_warn, "warn", - "Warn a player to cease or face admin intervention", - "[^3name|slot#^7] [reason]" - }, - - {"setdevmode", G_admin_setdevmode, "setdevmode", - "switch developer mode on or off", - "[^3on|off^7]" - }, + {"sm", G_admin_sm, qfalse, "schachtmeister", + "Schachtmeister", + "..." + } + }; - {"hstage", G_admin_hstage, "stage", - "change the stage for humans", - "[^3#^7]" - }, +static size_t adminNumCmds = ARRAY_LEN( g_admin_cmds ); - {"astage", G_admin_astage, "stage", - "change the stage for aliens", - "[^3#^7]" - }, +static int admin_level_maxname = 0; +g_admin_level_t *g_admin_levels = NULL; +g_admin_admin_t *g_admin_admins = NULL; +g_admin_ban_t *g_admin_bans = NULL; +g_admin_command_t *g_admin_commands = NULL; - {"bubble", G_admin_bubble, "bubble", - "continuously spawn bubbles around a player", - "[^3name|slot#^7]" - }, +void G_admin_register_cmds( void ) +{ + int i; - {"scrim", G_admin_scrim, "scrim", - "toggles scrim mode", - "[on|off]", - }, + for( i = 0; i < adminNumCmds; i++ ) + trap_AddCommand( g_admin_cmds[ i ].keyword ); +} - {"give", G_admin_give, "give", - "give funds to a player", - "[^3name|slot#^7] [^3amount^7]" - }, +void G_admin_unregister_cmds( void ) +{ + int i; - {"setrotation", G_admin_setrotation, "setrotation", - "sets the map rotation", - "[^3rotation^7]" - }, + for( i = 0; i < adminNumCmds; i++ ) + trap_RemoveCommand( g_admin_cmds[ i ].keyword ); +} - {"versions", G_admin_versions, "namelog", - "Check what versions of Tremulous players are running.", - "" - }, +void G_admin_cmdlist( gentity_t *ent ) +{ + int i; + char out[ MAX_STRING_CHARS ] = ""; + int len, outlen; - {"showff", G_admin_showff, "showff", - "shows how much friendly damage a player has done this game" - "\nno arguments will list all connected players", - "(^3name|slot^7)" - "\n ^3Example:^7 ^120% ^7means 1/5th of the damage dealt this game was dealt to the team" - }, + outlen = 0; - {"tklog", G_admin_tklog, "tklog", - "list recent teamkill activity", - "(^5start id#|name|-skip#^7) (^5search skip#^7)" - }, + for( i = 0; i < adminNumCmds; i++ ) + { + if( !G_admin_permission( ent, g_admin_cmds[ i ].flag ) ) + continue; - {"sm", G_admin_sm, "schachtmeister", - "Schachtmeister", - "..." + len = strlen( g_admin_cmds[ i ].keyword ) + 1; + if( len + outlen >= sizeof( out ) - 1 ) + { + trap_SendServerCommand( ent - g_entities, va( "cmds%s\n", out ) ); + outlen = 0; } - }; - -static int adminNumCmds = sizeof( g_admin_cmds ) / sizeof( g_admin_cmds[ 0 ] ); - -static int admin_level_maxname = 0; -g_admin_level_t *g_admin_levels[ MAX_ADMIN_LEVELS ]; -g_admin_admin_t *g_admin_admins[ MAX_ADMIN_ADMINS ]; -g_admin_ban_t *g_admin_bans[ MAX_ADMIN_BANS ]; -g_admin_command_t *g_admin_commands[ MAX_ADMIN_COMMANDS ]; -g_admin_namelog_t *g_admin_namelog[ MAX_ADMIN_NAMELOGS ]; + strcpy( out + outlen, va( " %s", g_admin_cmds[ i ].keyword ) ); + outlen += len; + } -int G_admin_parse_time( const char *time ); + trap_SendServerCommand( ent - g_entities, va( "cmds%s\n", out ) ); +} // match a certain flag within these flags -// return state of whether flag was found or not, -// set *perm to indicate whether found flag was + or - static qboolean admin_permission( char *flags, const char *flag, qboolean *perm ) { char *token, *token_p = flags; - qboolean all_found = qfalse; - qboolean base_perm = qfalse; - + qboolean allflags = qfalse; + qboolean p = qfalse; + *perm = qfalse; while( *( token = COM_Parse( &token_p ) ) ) { *perm = qtrue; @@ -459,63 +310,84 @@ static qboolean admin_permission( char *flags, const char *flag, qboolean *perm return qtrue; if( !strcmp( token, ADMF_ALLFLAGS ) ) { - all_found = qtrue; - base_perm = *perm; + allflags = qtrue; + p = *perm; } } + if( allflags ) + *perm = p; + return allflags; +} + +g_admin_cmd_t *G_admin_cmd( const char *cmd ) +{ + return bsearch( cmd, g_admin_cmds, adminNumCmds, sizeof( g_admin_cmd_t ), + cmdcmp ); +} + +g_admin_level_t *G_admin_level( const int l ) +{ + g_admin_level_t *level; - if( all_found && flag[ 0 ] != '.' ) + for( level = g_admin_levels; level; level = level->next ) { - *perm = base_perm; - return qtrue; + if( level->level == l ) + return level; } - return qfalse; + return NULL; } -static int admin_adminlog_index = 0; -g_admin_adminlog_t *g_admin_adminlog[ MAX_ADMIN_ADMINLOGS ]; - -static int admin_tklog_index = 0; -g_admin_tklog_t *g_admin_tklog[ MAX_ADMIN_TKLOGS ]; - -// This function should only be used directly when the client is connecting and thus has no GUID. -// Else, use G_admin_permission() -qboolean G_admin_permission_guid( char *guid, const char* flag ) +g_admin_admin_t *G_admin_admin( const char *guid ) { - int i; - int l = 0; - qboolean perm = qfalse; + g_admin_admin_t *admin; - // Does the admin specifically have this flag granted/denied to them, - // irrespective of their admin level? - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) + for( admin = g_admin_admins; admin; admin = admin->next ) { - if( !Q_stricmp( guid, g_admin_admins[ i ]->guid ) ) - { - if( admin_permission( g_admin_admins[ i ]->flags, flag, &perm ) ) - return perm; - l = g_admin_admins[ i ]->level; - break; - } + if( !Q_stricmp( admin->guid, guid ) ) + return admin; } - // If not, is this flag granted/denied for their admin level? - for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) + return NULL; +} + +g_admin_command_t *G_admin_command( const char *cmd ) +{ + g_admin_command_t *c; + + for( c = g_admin_commands; c; c = c->next ) { - if( g_admin_levels[ i ]->level == l ) - return admin_permission( g_admin_levels[ i ]->flags, flag, &perm ) && - perm; + if( !Q_stricmp( c->command, cmd ) ) + return c; } - return qfalse; -} + return NULL; +} qboolean G_admin_permission( gentity_t *ent, const char *flag ) { - if(!ent) return qtrue; //console always wins + qboolean perm; + g_admin_admin_t *a; + g_admin_level_t *l; + + // console always wins + if( !ent ) + return qtrue; + + if( ( a = ent->client->pers.admin ) ) + { + if( admin_permission( a->flags, flag, &perm ) ) + return perm; + + l = G_admin_level( a->level ); + } + else + l = G_admin_level( 0 ); + + if( l ) + return admin_permission( l->flags, flag, &perm ) && perm; - return G_admin_permission_guid(ent->client->pers.guid, flag); + return qfalse; } qboolean G_admin_name_check( gentity_t *ent, char *name, char *err, int len ) @@ -524,125 +396,94 @@ qboolean G_admin_name_check( gentity_t *ent, char *name, char *err, int len ) gclient_t *client; char testName[ MAX_NAME_LENGTH ] = {""}; char name2[ MAX_NAME_LENGTH ] = {""}; + g_admin_admin_t *admin; int alphaCount = 0; - G_SanitiseString( name, name2, sizeof( name2) ); + G_SanitiseString( name, name2, sizeof( name2 ) ); - if( !Q_stricmp( name2, "UnnamedPlayer" ) ) + if( !strcmp( name2, "unnamedplayer" ) ) return qtrue; - if( !Q_stricmp( name2, "console" ) ) + if( !strcmp( name2, "console" ) ) + { + if( err && len > 0 ) + Q_strncpyz( err, "The name 'console' is not allowed.", len ); + return qfalse; + } + + G_DecolorString( name, testName, sizeof( testName ) ); + if( isdigit( testName[ 0 ] ) ) { - Q_strncpyz( err, va( "The name '%s^7' is invalid here", name2 ), - len ); + if( err && len > 0 ) + Q_strncpyz( err, "Names cannot begin with numbers", len ); + return qfalse; + } + + for( i = 0; testName[ i ]; i++) + { + if( isalpha( testName[ i ] ) ) + alphaCount++; + } + + if( alphaCount == 0 ) + { + if( err && len > 0 ) + Q_strncpyz( err, "Names must contain letters", len ); return qfalse; } for( i = 0; i < level.maxclients; i++ ) { client = &level.clients[ i ]; - if( client->pers.connected != CON_CONNECTING - && client->pers.connected != CON_CONNECTED ) - { + if( client->pers.connected == CON_DISCONNECTED ) continue; - } // can rename ones self to the same name using different colors if( i == ( ent - g_entities ) ) continue; - G_SanitiseString( client->pers.netname, testName, sizeof( testName) ); - if( !Q_stricmp( name2, testName ) ) + G_SanitiseString( client->pers.netname, testName, sizeof( testName ) ); + if( !strcmp( name2, testName ) ) { - Q_strncpyz( err, va( "The name '%s^7' is already in use", name ), - len ); + if( err && len > 0 ) + Com_sprintf( err, len, "The name '%s^7' is already in use", name ); return qfalse; } } - - if( Q_isdigit( name2[ 0 ] ) || name2[ 0 ] == '-' ) - { - Q_strncpyz( err, "Names cannot begin with a number or with a dash. Please choose another.", len ); - return qfalse; - } - - for( i = 0; name2[ i ] !='\0'; i++) - { - if( Q_isalpha( name2[ i ] ) ) - alphaCount++; - if( name2[ i ] == ' ' ) - { - if( name2[ i + 1 ] == '-' ) - { - Q_strncpyz( err, "Names cannot contain a - preceded by a space. Please choose another.", len ); - return qfalse; - } - } - } - - if( alphaCount == 0 ) + for( admin = g_admin_admins; admin; admin = admin->next ) { - Q_strncpyz( err, va( "The name '%s^7' does not include at least one letter. Please choose another.", name ), len ); - return qfalse; - } - - if( !g_adminNameProtect.string[ 0 ] ) - return qtrue; - - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) - { - if( g_admin_admins[ i ]->level < 1 ) + if( admin->level < 1 ) continue; - G_SanitiseString( g_admin_admins[ i ]->name, testName, sizeof( testName) ); - if( !Q_stricmp( name2, testName ) && - Q_stricmp( ent->client->pers.guid, g_admin_admins[ i ]->guid ) ) + G_SanitiseString( admin->name, testName, sizeof( testName ) ); + if( !strcmp( name2, testName ) && ent->client->pers.admin != admin ) { - Q_strncpyz( err, va( "The name '%s^7' belongs to an admin. " - "Please choose another.", name ), len ); + if( err && len > 0 ) + Com_sprintf( err, len, "The name '%s^7' belongs to an admin, " + "please use another name", name ); return qfalse; } } return qtrue; } -static qboolean admin_higher_guid( char *admin_guid, char *victim_guid ) +static qboolean admin_higher_admin( g_admin_admin_t *a, g_admin_admin_t *b ) { - int i; - int alevel = 0; - int alevel2 = 0; - - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) - { - if( !Q_stricmp( admin_guid, g_admin_admins[ i ]->guid ) ) - { - alevel = g_admin_admins[ i ]->level; - - // Novelty Levels should be equivelant to level 1 - if( alevel > 9 ) - alevel = 1; + qboolean perm; - break; - } - } - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) - { - if( !Q_stricmp( victim_guid, g_admin_admins[ i ]->guid ) ) - { - alevel2 = g_admin_admins[ i ]->level; + if( !b ) + return qtrue; - // Novelty Levels should be equivelant to level 1 - if( alevel2 > 9 ) - alevel2 = 1; + if( admin_permission( b->flags, ADMF_IMMUTABLE, &perm ) ) + return !perm; - if( alevel < alevel2 ) - return qfalse; + return b->level <= ( a ? a->level : 0 ); +} - if( strstr( g_admin_admins[ i ]->flags, va( "%s", ADMF_IMMUTABLE ) ) ) - return qfalse; - } - } - return qtrue; +static qboolean admin_higher_guid( char *admin_guid, char *victim_guid ) +{ + return admin_higher_admin( G_admin_admin( admin_guid ), + G_admin_admin( victim_guid ) ); } static qboolean admin_higher( gentity_t *admin, gentity_t *victim ) @@ -651,25 +492,15 @@ static qboolean admin_higher( gentity_t *admin, gentity_t *victim ) // console always wins if( !admin ) return qtrue; - // just in case - if( !victim ) - return qtrue; - return admin_higher_guid( admin->client->pers.guid, - victim->client->pers.guid ); + return admin_higher_admin( admin->client->pers.admin, + victim->client->pers.admin ); } static void admin_writeconfig_string( char *s, fileHandle_t f ) { - char buf[ MAX_STRING_CHARS ]; - - buf[ 0 ] = '\0'; if( s[ 0 ] ) - { - //Q_strcat(buf, sizeof(buf), s); - Q_strncpyz( buf, s, sizeof( buf ) ); - trap_FS_Write( buf, strlen( buf ), f ); - } + trap_FS_Write( s, strlen( s ), f ); trap_FS_Write( "\n", 1, f ); } @@ -677,18 +508,18 @@ static void admin_writeconfig_int( int v, fileHandle_t f ) { char buf[ 32 ]; - Com_sprintf( buf, sizeof(buf), "%d", v ); - if( buf[ 0 ] ) - trap_FS_Write( buf, strlen( buf ), f ); - trap_FS_Write( "\n", 1, f ); + Com_sprintf( buf, sizeof( buf ), "%d\n", v ); + trap_FS_Write( buf, strlen( buf ), f ); } -void admin_writeconfig( void ) +static void admin_writeconfig( void ) { fileHandle_t f; - int len, i; - int t, expiretime; - char levels[ MAX_STRING_CHARS ] = {""}; + int t; + g_admin_admin_t *a; + g_admin_level_t *l; + g_admin_ban_t *b; + g_admin_command_t *c; if( !g_admin.string[ 0 ] ) { @@ -697,96 +528,75 @@ void admin_writeconfig( void ) return; } t = trap_RealTime( NULL ); - len = trap_FS_FOpenFile( g_admin.string, &f, FS_WRITE ); - if( len < 0 ) + if( trap_FS_FOpenFile( g_admin.string, &f, FS_WRITE ) < 0 ) { G_Printf( "admin_writeconfig: could not open g_admin file \"%s\"\n", g_admin.string ); return; } - for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) + for( l = g_admin_levels; l; l = l->next ) { trap_FS_Write( "[level]\n", 8, f ); trap_FS_Write( "level = ", 10, f ); - admin_writeconfig_int( g_admin_levels[ i ]->level, f ); + admin_writeconfig_int( l->level, f ); trap_FS_Write( "name = ", 10, f ); - admin_writeconfig_string( g_admin_levels[ i ]->name, f ); + admin_writeconfig_string( l->name, f ); trap_FS_Write( "flags = ", 10, f ); - admin_writeconfig_string( g_admin_levels[ i ]->flags, f ); + admin_writeconfig_string( l->flags, f ); trap_FS_Write( "\n", 1, f ); } - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) + for( a = g_admin_admins; a; a = a->next ) { // don't write level 0 users - if( g_admin_admins[ i ]->level < 1 ) + if( a->level == 0 ) continue; - //if set dont write admins that havent been seen in a while - expiretime = G_admin_parse_time( g_adminExpireTime.string ); - if( expiretime > 0 ) { - //only expire level 1 people - if( t - expiretime > g_admin_admins[ i ]->seen && g_admin_admins[ i ]->level == 1 ) { - G_Printf("Admin %s has been expired.\n", g_admin_admins[ i ]->name ); - continue; - } - } - trap_FS_Write( "[admin]\n", 8, f ); trap_FS_Write( "name = ", 10, f ); - admin_writeconfig_string( g_admin_admins[ i ]->name, f ); + admin_writeconfig_string( a->name, f ); trap_FS_Write( "guid = ", 10, f ); - admin_writeconfig_string( g_admin_admins[ i ]->guid, f ); + admin_writeconfig_string( a->guid, f ); trap_FS_Write( "level = ", 10, f ); - admin_writeconfig_int( g_admin_admins[ i ]->level, f ); + admin_writeconfig_int( a->level, f ); trap_FS_Write( "flags = ", 10, f ); - admin_writeconfig_string( g_admin_admins[ i ]->flags, f ); - trap_FS_Write( "seen = ", 10, f ); - admin_writeconfig_int( g_admin_admins[ i ]->seen, f ); + admin_writeconfig_string( a->flags, f ); trap_FS_Write( "\n", 1, f ); } - for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) + for( b = g_admin_bans; b; b = b->next ) { // don't write expired bans // if expires is 0, then it's a perm ban - if( g_admin_bans[ i ]->expires != 0 && - ( g_admin_bans[ i ]->expires - t ) < 1 ) + if( b->expires != 0 && b->expires <= t ) continue; trap_FS_Write( "[ban]\n", 6, f ); trap_FS_Write( "name = ", 10, f ); - admin_writeconfig_string( g_admin_bans[ i ]->name, f ); + admin_writeconfig_string( b->name, f ); trap_FS_Write( "guid = ", 10, f ); - admin_writeconfig_string( g_admin_bans[ i ]->guid, f ); + admin_writeconfig_string( b->guid, f ); trap_FS_Write( "ip = ", 10, f ); - admin_writeconfig_string( g_admin_bans[ i ]->ip, f ); + admin_writeconfig_string( b->ip.str, f ); trap_FS_Write( "reason = ", 10, f ); - admin_writeconfig_string( g_admin_bans[ i ]->reason, f ); + admin_writeconfig_string( b->reason, f ); trap_FS_Write( "made = ", 10, f ); - admin_writeconfig_string( g_admin_bans[ i ]->made, f ); + admin_writeconfig_string( b->made, f ); trap_FS_Write( "expires = ", 10, f ); - admin_writeconfig_int( g_admin_bans[ i ]->expires, f ); - if( g_admin_bans[ i ]->suspend > t ) { - trap_FS_Write( "suspend = ", 10, f ); - admin_writeconfig_int( g_admin_bans[ i ]->suspend, f ); - } + admin_writeconfig_int( b->expires, f ); trap_FS_Write( "banner = ", 10, f ); - admin_writeconfig_string( g_admin_bans[ i ]->banner, f ); - trap_FS_Write( "blevel = ", 10, f ); - admin_writeconfig_int( g_admin_bans[ i ]->bannerlevel, f ); + admin_writeconfig_string( b->banner, f ); trap_FS_Write( "\n", 1, f ); } - for( i = 0; i < MAX_ADMIN_COMMANDS && g_admin_commands[ i ]; i++ ) + for( c = g_admin_commands; c; c = c->next ) { - levels[ 0 ] = '\0'; trap_FS_Write( "[command]\n", 10, f ); trap_FS_Write( "command = ", 10, f ); - admin_writeconfig_string( g_admin_commands[ i ]->command, f ); + admin_writeconfig_string( c->command, f ); trap_FS_Write( "exec = ", 10, f ); - admin_writeconfig_string( g_admin_commands[ i ]->exec, f ); + admin_writeconfig_string( c->exec, f ); trap_FS_Write( "desc = ", 10, f ); - admin_writeconfig_string( g_admin_commands[ i ]->desc, f ); + admin_writeconfig_string( c->desc, f ); trap_FS_Write( "flag = ", 10, f ); - admin_writeconfig_string( g_admin_commands[ i ]->flag, f ); + admin_writeconfig_string( c->flag, f ); trap_FS_Write( "\n", 1, f ); } trap_FS_FCloseFile( f ); @@ -794,41 +604,32 @@ void admin_writeconfig( void ) static void admin_readconfig_string( char **cnf, char *s, int size ) { - char * t; + char *t; //COM_MatchToken(cnf, "="); + s[ 0 ] = '\0'; t = COM_ParseExt( cnf, qfalse ); - if( !strcmp( t, "=" ) ) - { - t = COM_ParseExt( cnf, qfalse ); - } - else + if( strcmp( t, "=" ) ) { - G_Printf( "readconfig: warning missing = before " - "\"%s\" on line %d\n", - t, - COM_GetCurrentParseLine() ); + COM_ParseWarning( "expected '=' before \"%s\"", t ); + Q_strncpyz( s, t, size ); } - s[ 0 ] = '\0'; - while( t[ 0 ] ) + while( 1 ) { - if( ( s[ 0 ] == '\0' && strlen( t ) <= size ) - || ( strlen( t ) + strlen( s ) < size ) ) - { - - Q_strcat( s, size, t ); - Q_strcat( s, size, " " ); - } t = COM_ParseExt( cnf, qfalse ); + if( !*t ) + break; + if( strlen( t ) + strlen( s ) >= size ) + break; + if( *s ) + Q_strcat( s, size, " " ); + Q_strcat( s, size, t ); } - // trim the trailing space - if( strlen( s ) > 0 && s[ strlen( s ) - 1 ] == ' ' ) - s[ strlen( s ) - 1 ] = '\0'; } static void admin_readconfig_int( char **cnf, int *v ) { - char * t; + char *t; //COM_MatchToken(cnf, "="); t = COM_ParseExt( cnf, qfalse ); @@ -838,10 +639,7 @@ static void admin_readconfig_int( char **cnf, int *v ) } else { - G_Printf( "readconfig: warning missing = before " - "\"%s\" on line %d\n", - t, - COM_GetCurrentParseLine() ); + COM_ParseWarning( "expected '=' before \"%s\"", t ); } *v = atoi( t ); } @@ -850,413 +648,217 @@ static void admin_readconfig_int( char **cnf, int *v ) // ones to make new installs easier for admins static void admin_default_levels( void ) { - g_admin_level_t * l; - int i; - - for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) - { - G_Free( g_admin_levels[ i ] ); - g_admin_levels[ i ] = NULL; - } - for( i = 0; i <= 5; i++ ) - { - l = G_Alloc( sizeof( g_admin_level_t ) ); - l->level = i; - *l->name = '\0'; - *l->flags = '\0'; - g_admin_levels[ i ] = l; - } + g_admin_level_t *l; + int level = 0; - Q_strncpyz( g_admin_levels[ 0 ]->name, "^4Unknown Player", - sizeof( l->name ) ); - Q_strncpyz( g_admin_levels[ 0 ]->flags, - "listplayers admintest help specme time", + l = g_admin_levels = BG_Alloc( sizeof( g_admin_level_t ) ); + l->level = level++; + Q_strncpyz( l->name, "^4Unknown Player", sizeof( l->name ) ); + Q_strncpyz( l->flags, + "listplayers admintest adminhelp time", sizeof( l->flags ) ); - Q_strncpyz( g_admin_levels[ 1 ]->name, "^5Server Regular", - sizeof( l->name ) ); - Q_strncpyz( g_admin_levels[ 1 ]->flags, - "listplayers admintest help specme time", + l = l->next = BG_Alloc( sizeof( g_admin_level_t ) ); + l->level = level++; + Q_strncpyz( l->name, "^5Server Regular", sizeof( l->name ) ); + Q_strncpyz( l->flags, + "listplayers admintest adminhelp time", sizeof( l->flags ) ); - Q_strncpyz( g_admin_levels[ 2 ]->name, "^6Team Manager", - sizeof( l->name ) ); - Q_strncpyz( g_admin_levels[ 2 ]->flags, - "listplayers admintest help specme time putteam spec999 warn denybuild", + l = l->next = BG_Alloc( sizeof( g_admin_level_t ) ); + l->level = level++; + Q_strncpyz( l->name, "^6Team Manager", sizeof( l->name ) ); + Q_strncpyz( l->flags, + "listplayers admintest adminhelp time putteam spec999", sizeof( l->flags ) ); - Q_strncpyz( g_admin_levels[ 3 ]->name, "^2Junior Admin", - sizeof( l->name ) ); - Q_strncpyz( g_admin_levels[ 3 ]->flags, - "listplayers admintest help specme time putteam spec999 kick mute warn " - "denybuild ADMINCHAT SEESFULLLISTPLAYERS", + l = l->next = BG_Alloc( sizeof( g_admin_level_t ) ); + l->level = level++; + Q_strncpyz( l->name, "^2Junior Admin", sizeof( l->name ) ); + Q_strncpyz( l->flags, + "listplayers admintest adminhelp time putteam spec999 kick mute ADMINCHAT", sizeof( l->flags ) ); - Q_strncpyz( g_admin_levels[ 4 ]->name, "^3Senior Admin", - sizeof( l->name ) ); - Q_strncpyz( g_admin_levels[ 4 ]->flags, - "listplayers admintest help specme time putteam spec999 kick mute showbans " - "ban namelog warn denybuild decon ADMINCHAT SEESFULLLISTPLAYERS", + l = l->next = BG_Alloc( sizeof( g_admin_level_t ) ); + l->level = level++; + Q_strncpyz( l->name, "^3Senior Admin", sizeof( l->name ) ); + Q_strncpyz( l->flags, + "listplayers admintest adminhelp time putteam spec999 kick mute showbans ban " + "namelog ADMINCHAT", sizeof( l->flags ) ); - Q_strncpyz( g_admin_levels[ 5 ]->name, "^1Server Operator", - sizeof( l->name ) ); - Q_strncpyz( g_admin_levels[ 5 ]->flags, - "ALLFLAGS -INCOGNITO -IMMUTABLE -DBUILDER -BANIMMUNITY", + l = l->next = BG_Alloc( sizeof( g_admin_level_t ) ); + l->level = level++; + Q_strncpyz( l->name, "^1Server Operator", sizeof( l->name ) ); + Q_strncpyz( l->flags, + "ALLFLAGS -IMMUTABLE -INCOGNITO", sizeof( l->flags ) ); + admin_level_maxname = 15; } -// return a level for a player entity. -int G_admin_level( gentity_t *ent ) +void G_admin_authlog( gentity_t *ent ) { - int i; - qboolean found = qfalse; + char aflags[ MAX_ADMIN_FLAGS * 2 ]; + g_admin_level_t *level; + int levelNum = 0; if( !ent ) - { - return MAX_ADMIN_LEVELS; - } + return; - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) - { - if( !Q_stricmp( g_admin_admins[ i ]->guid, ent->client->pers.guid ) ) - { + if( ent->client->pers.admin ) + levelNum = ent->client->pers.admin->level; - found = qtrue; - break; - } - } + level = G_admin_level( levelNum ); - if( found ) - { - return g_admin_admins[ i ]->level; - } + Com_sprintf( aflags, sizeof( aflags ), "%s %s", + ent->client->pers.admin->flags, + ( level ) ? level->flags : "" ); - return 0; + G_LogPrintf( "AdminAuth: %i \"%s" S_COLOR_WHITE "\" \"%s" S_COLOR_WHITE + "\" [%d] (%s): %s\n", + (int)( ent - g_entities ), ent->client->pers.netname, + ent->client->pers.admin->name, ent->client->pers.admin->level, + ent->client->pers.guid, aflags ); } -// set a player's adminname -void G_admin_set_adminname( gentity_t *ent ) +static char adminLog[ MAX_STRING_CHARS ]; +static int adminLogLen; +static void admin_log_start( gentity_t *admin, const char *cmd ) { - int i; - qboolean found = qfalse; + const char *name = admin ? admin->client->pers.netname : "console"; - if( !ent ) - { - return; - } - - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) - { - if( !Q_stricmp( g_admin_admins[ i ]->guid, ent->client->pers.guid ) ) - { - found = qtrue; - break; - } - } - - if( found ) - { - Q_strncpyz( ent->client->pers.adminName, g_admin_admins[ i ]->name, sizeof( ent->client->pers.adminName ) ); - } - else - { - Q_strncpyz( ent->client->pers.adminName, "", sizeof( ent->client->pers.adminName ) ); - } + adminLogLen = Q_snprintf( adminLog, sizeof( adminLog ), + "%d \"%s" S_COLOR_WHITE "\" \"%s" S_COLOR_WHITE "\" [%d] (%s): %s", + admin ? admin->s.clientNum : -1, + name, + admin && admin->client->pers.admin ? admin->client->pers.admin->name : name, + admin && admin->client->pers.admin ? admin->client->pers.admin->level : 0, + admin ? admin->client->pers.guid : "", + cmd ); } -// Get an admin's registered name -const char *G_admin_get_adminname( gentity_t *ent ) +static void admin_log( const char *str ) { - int i; - - if( !ent ) - return "console"; - - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) - { - if( !Q_stricmp( g_admin_admins[ i ]->guid, ent->client->pers.guid ) ) - return g_admin_admins[ i ]->name; - } - - return ent->client->pers.netname; + if( adminLog[ 0 ] ) + adminLogLen += Q_snprintf( adminLog + adminLogLen, + sizeof( adminLog ) - adminLogLen, ": %s", str ); } -// Get an admin's name to print as the user of various commands, -// obeying admin stealth settings when necessary -char* G_admin_adminPrintName( gentity_t *ent ) +static void admin_log_abort( void ) { - char *out; - - if( !ent->client->pers.adminLevel ) - { - out = ""; - return out; - } - - if( G_admin_permission( ent, ADMF_ADMINSTEALTH ) ) - { - out = ent->client->pers.adminName; - } - else - { - out = ent->client->pers.netname; - } - - - return out; + adminLog[ 0 ] = '\0'; + adminLogLen = 0; } -static void admin_log( gentity_t *admin, char *cmd, int skiparg ) +static void admin_log_end( const qboolean ok ) { - fileHandle_t f; - int len, i, j; - char string[ MAX_STRING_CHARS ], decoloured[ MAX_STRING_CHARS ]; - int min, tens, sec; - g_admin_admin_t *a; - g_admin_level_t *l; - char flags[ MAX_ADMIN_FLAGS * 2 ]; - gentity_t *victim = NULL; - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ]; - - if( !g_adminLog.string[ 0 ] ) - return ; - - - len = trap_FS_FOpenFile( g_adminLog.string, &f, FS_APPEND ); - if( len < 0 ) - { - G_Printf( "admin_log: error could not open %s\n", g_adminLog.string ); - return ; - } + if( adminLog[ 0 ] ) + G_LogPrintf( "AdminExec: %s: %s\n", ok ? "ok" : "fail", adminLog ); + admin_log_abort( ); +} - sec = (level.time - level.startTime) / 1000; - min = sec / 60; - sec -= min * 60; - tens = sec / 10; - sec -= tens * 10; +struct llist +{ + struct llist *next; +}; +static int admin_search( gentity_t *ent, + const char *cmd, + const char *noun, + qboolean ( *match )( void *, const void * ), + void ( *out )( void *, char * ), + const void *list, + const void *arg, /* this will be used as char* later */ + int start, + const int offset, + const int limit ) +{ + int i; + int count = 0; + int found = 0; + int total; + int next = 0, end = 0; + char str[ MAX_STRING_CHARS ]; + struct llist *l = (struct llist *)list; + + for( total = 0; l; total++, l = l->next ) ; + if( start < 0 ) + start += total; + else + start -= offset; + if( start < 0 || start > total ) + start = 0; - *flags = '\0'; - if( admin ) + ADMBP_begin(); + for( i = 0, l = (struct llist *)list; l; i++, l = l->next ) { - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) + if( match( l, arg ) ) { - if( !Q_stricmp( g_admin_admins[ i ]->guid , admin->client->pers.guid ) ) + if( i >= start && ( limit < 1 || count < limit ) ) { - - a = g_admin_admins[ i ]; - Q_strncpyz( flags, a->flags, sizeof( flags ) ); - for( j = 0; j < MAX_ADMIN_LEVELS && g_admin_levels[ j ]; j++ ) - { - if( g_admin_levels[ j ]->level == a->level ) - { - l = g_admin_levels[ j ]; - Q_strcat( flags, sizeof( flags ), l->flags ); - break; - } - } - break; + out( l, str ); + ADMBP( va( "%-3d %s\n", i + offset, str ) ); + count++; + end = i; + } + else if( count == limit ) + { + if( next == 0 ) + next = i; } - } - } - if( G_SayArgc() > 1 + skiparg ) - { - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - if( G_ClientNumbersFromString( name, pids ) == 1 ) - { - victim = &g_entities[ pids[ 0 ] ]; + found++; } } - if( victim && Q_stricmp( cmd, "attempted" ) ) - { - Com_sprintf( string, sizeof( string ), - "%3i:%i%i: %i: %s: %s (%s): %s: %s: %s: %s: \"%s\"\n", - min, - tens, - sec, - ( admin ) ? admin->s.clientNum : -1, - ( admin ) ? admin->client->pers.guid - : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - ( admin ) ? admin->client->pers.netname : "console", - ( admin ) ? admin->client->pers.adminName : "console", - flags, - cmd, - victim->client->pers.guid, - victim->client->pers.netname, - G_SayConcatArgs( 2 + skiparg ) ); - } - else + if( limit > 0 ) { - Com_sprintf( string, sizeof( string ), - "%3i:%i%i: %i: %s: %s (%s): %s: %s: \"%s\"\n", - min, - tens, - sec, - ( admin ) ? admin->s.clientNum : -1, - ( admin ) ? admin->client->pers.guid - : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - ( admin ) ? admin->client->pers.netname : "console", - ( admin ) ? admin->client->pers.adminName : "console", - flags, - cmd, - G_SayConcatArgs( 1 + skiparg ) ); + ADMBP( va( "^3%s: ^7showing %d of %d %s %d-%d%s%s.", + cmd, count, found, noun, start + offset, end + offset, + *(char *)arg ? " matching " : "", (char *)arg ) ); + if( next ) + ADMBP( va( " use '%s%s%s %d' to see more", cmd, + *(char *)arg ? " " : "", + (char *)arg, + next + offset ) ); } + ADMBP( "\n" ); + ADMBP_end(); + return next + offset; +} - if( g_decolourLogfiles.integer ) - { - G_DecolorString( string, decoloured ); - trap_FS_Write( decoloured, strlen( decoloured ), f ); - } - else +static qboolean admin_match( void *admin, const void *match ) +{ + char n1[ MAX_NAME_LENGTH ], n2[ MAX_NAME_LENGTH ]; + G_SanitiseString( (char *)match, n2, sizeof( n2 ) ); + if( !n2[ 0 ] ) + return qtrue; + G_SanitiseString( ( (g_admin_admin_t *)admin )->name, n1, sizeof( n1 ) ); + return strstr( n1, n2 ) ? qtrue : qfalse; +} +static void admin_out( void *admin, char *str ) +{ + g_admin_admin_t *a = (g_admin_admin_t *)admin; + g_admin_level_t *l = G_admin_level( a->level ); + int lncol = 0, i; + for( i = 0; l && l->name[ i ]; i++ ) { - trap_FS_Write( string, strlen( string ), f ); + if( Q_IsColorString( l->name + i ) ) + lncol += 2; } - trap_FS_FCloseFile( f ); - - if ( !Q_stricmp( cmd, "attempted" ) ) - { - Com_sprintf( string, sizeof( string ), - "%s^7 (%i) %s: %s", - ( admin ) ? admin->client->pers.netname : "console", - ( admin ) ? admin->s.clientNum : -1, - cmd, - G_SayConcatArgs( 1 + skiparg ) ); - G_AdminsPrintf("%s\n",string); - } - - G_LogPrintf("Admin Command: %s^7 (%s): %s %s\n",( admin ) ? admin->client->pers.netname : "console", ( admin ) ? admin->client->pers.adminName : "console", cmd, G_SayConcatArgs( 1 + skiparg )); + Com_sprintf( str, MAX_STRING_CHARS, "%-6d %*s^7 %s", + a->level, admin_level_maxname + lncol - 1, l ? l->name : "(null)", + a->name ); } - -static int admin_listadmins( gentity_t *ent, int start, char *search, int minlevel ) +static int admin_listadmins( gentity_t *ent, int start, char *search ) { - int drawn = 0; - char guid_stub[9]; - char name[ MAX_NAME_LENGTH ] = {""}; - char name2[ MAX_NAME_LENGTH ] = {""}; - char lname[ MAX_NAME_LENGTH ] = {""}; - char lname_fmt[ 5 ]; - int i,j; - gentity_t *vic; - int l = 0; - qboolean dup = qfalse; - - ADMBP_begin(); - - // print out all connected players regardless of level if name searching - for( i = 0; i < level.maxclients && search[ 0 ]; i++ ) - { - vic = &g_entities[ i ]; - - if( vic->client && vic->client->pers.connected != CON_CONNECTED ) - continue; - - l = vic->client->pers.adminLevel; - - G_SanitiseString( vic->client->pers.netname, name, sizeof( name ) ); - if( !strstr( name, search ) ) - continue; - - for( j = 0; j < 8; j++ ) - guid_stub[ j ] = vic->client->pers.guid[ j + 24 ]; - guid_stub[ j ] = '\0'; - - lname[ 0 ] = '\0'; - Q_strncpyz( lname_fmt, "%s", sizeof( lname_fmt ) ); - for( j = 0; j < MAX_ADMIN_LEVELS && g_admin_levels[ j ]; j++ ) - { - if( g_admin_levels[ j ]->level == l ) - { - G_DecolorString( g_admin_levels[ j ]->name, lname ); - Com_sprintf( lname_fmt, sizeof( lname_fmt ), "%%%is", - ( admin_level_maxname + strlen( g_admin_levels[ j ]->name ) - - strlen( lname ) ) ); - Com_sprintf( lname, sizeof( lname ), lname_fmt, - g_admin_levels[ j ]->name ); - break; - } - } - ADMBP( va( "%4i %4i %s^7 (*%s) %s^7\n", - i, - l, - lname, - guid_stub, - vic->client->pers.netname ) ); - drawn++; - } - - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] - && drawn < MAX_ADMIN_LISTITEMS; i++ ) - if( g_admin_admins[ i ]->level >= minlevel ) - { - - if( start ) - { - start--; - continue; - } - - if( search[ 0 ] ) - { - G_SanitiseString( g_admin_admins[ i ]->name, name, sizeof( name ) ); - if( !strstr( name, search ) ) - continue; - - // verify we don't have the same guid/name pair in connected players - // since we don't want to draw the same player twice - dup = qfalse; - for( j = 0; j < level.maxclients; j++ ) - { - vic = &g_entities[ j ]; - if( !vic->client || vic->client->pers.connected != CON_CONNECTED ) - continue; - G_SanitiseString( vic->client->pers.netname, name2, sizeof( name2) ); - if( !Q_stricmp( vic->client->pers.guid, g_admin_admins[ i ]->guid ) - && strstr( name2, search ) ) - { - dup = qtrue; - break; - } - } - if( dup ) - continue; - } - for( j = 0; j < 8; j++ ) - guid_stub[ j ] = g_admin_admins[ i ]->guid[ j + 24 ]; - guid_stub[ j ] = '\0'; - - lname[ 0 ] = '\0'; - Q_strncpyz( lname_fmt, "%s", sizeof( lname_fmt ) ); - for( j = 0; j < MAX_ADMIN_LEVELS && g_admin_levels[ j ]; j++ ) - { - if( g_admin_levels[ j ]->level == g_admin_admins[ i ]->level ) - { - G_DecolorString( g_admin_levels[ j ]->name, lname ); - Com_sprintf( lname_fmt, sizeof( lname_fmt ), "%%%is", - ( admin_level_maxname + strlen( g_admin_levels[ j ]->name ) - - strlen( lname ) ) ); - Com_sprintf( lname, sizeof( lname ), lname_fmt, - g_admin_levels[ j ]->name ); - break; - } - } - ADMBP( va( "%4i %4i %s^7 (*%s) %s^7\n", - ( i + MAX_CLIENTS ), - g_admin_admins[ i ]->level, - lname, - guid_stub, - g_admin_admins[ i ]->name ) ); - drawn++; - } - ADMBP_end(); - return drawn; + return admin_search( ent, "listadmins", "admins", admin_match, admin_out, + g_admin_admins, search, start, MAX_CLIENTS, MAX_ADMIN_LISTITEMS ); } +#define MAX_DURATION_LENGTH 13 void G_admin_duration( int secs, char *duration, int dursize ) { - + // sizeof("12.5 minutes") == 13 if( secs > ( 60 * 60 * 24 * 365 * 50 ) || secs < 0 ) Q_strncpyz( duration, "PERMANENT", dursize ); else if( secs >= ( 60 * 60 * 24 * 365 ) ) @@ -1278,440 +880,200 @@ void G_admin_duration( int secs, char *duration, int dursize ) Com_sprintf( duration, dursize, "%i seconds", secs ); } -qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen ) +static void G_admin_ban_message( + gentity_t *ent, + g_admin_ban_t *ban, + char *creason, + int clen, + char *areason, + int alen ) +{ + if( creason ) + { + char duration[ MAX_DURATION_LENGTH ]; + G_admin_duration( ban->expires - trap_RealTime( NULL ), duration, + sizeof( duration ) ); + // part of this might get cut off on the connect screen + Com_sprintf( creason, clen, + "You have been banned by %s" S_COLOR_WHITE " duration: %s" + " reason: %s", + ban->banner, + duration, + ban->reason ); + } + + if( areason && ent ) + { + // we just want the ban number + int n = 1; + g_admin_ban_t *b = g_admin_bans; + for( ; b && b != ban; b = b->next, n++ ) + ; + Com_sprintf( areason, alen, + S_COLOR_YELLOW "Banned player %s" S_COLOR_YELLOW + " tried to connect from %s (ban #%d)", + ent->client->pers.netname[ 0 ] ? ent->client->pers.netname : ban->name, + ent->client->pers.ip.str, + n ); + } +} + +static qboolean G_admin_ban_matches( g_admin_ban_t *ban, gentity_t *ent ) +{ + return !Q_stricmp( ban->guid, ent->client->pers.guid ) || + ( !G_admin_permission( ent, ADMF_IMMUNITY ) && + G_AddressCompare( &ban->ip, &ent->client->pers.ip ) ); +} + +static g_admin_ban_t *G_admin_match_ban( gentity_t *ent ) { - static char lastConnectIP[ 16 ] = {""}; - static int lastConnectTime = 0; - char guid[ 33 ]; - char ip[ 16 ]; - char *value; - int i; - unsigned int userIP = 0, intIP = 0, tempIP; - int IP[5], k, mask, ipscanfcount; int t; - char notice[51]; - qboolean ignoreIP = qfalse; - - trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) ); - - *reason = '\0'; - - if( !*userinfo ) - return qfalse; - - value = Info_ValueForKey( userinfo, "ip" ); - Q_strncpyz( ip, value, sizeof( ip ) ); - // strip port - value = strchr( ip, ':' ); - if ( value ) - *value = '\0'; - - if( !*ip ) - return qfalse; - - value = Info_ValueForKey( userinfo, "cl_guid" ); - Q_strncpyz( guid, value, sizeof( guid ) ); - + g_admin_ban_t *ban; + t = trap_RealTime( NULL ); - memset( IP, 0, sizeof( IP )); - sscanf(ip, "%i.%i.%i.%i", &IP[4], &IP[3], &IP[2], &IP[1]); - for(k = 4; k >= 1; k--) - { - if(!IP[k]) continue; - userIP |= IP[k] << 8*(k-1); - } - ignoreIP = G_admin_permission_guid( guid , ADMF_BAN_IMMUNITY ); - for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) + if( ent->client->pers.localClient ) + return NULL; + + for( ban = g_admin_bans; ban; ban = ban->next ) { // 0 is for perm ban - if( g_admin_bans[ i ]->expires != 0 && - ( g_admin_bans[ i ]->expires - t ) < 1 ) - continue; - //if suspend time is over continue - if( g_admin_bans[ i ]->suspend >= t ) + if( ban->expires != 0 && ban->expires <= t ) continue; - if( !ignoreIP ) - { - tempIP = userIP; - intIP = 0; - mask = -1; - - memset( IP, 0, sizeof( IP )); - ipscanfcount = sscanf(g_admin_bans[ i ]->ip, "%d.%d.%d.%d/%d", &IP[4], &IP[3], &IP[2], &IP[1], &IP[0]); - - if( ipscanfcount == 4 ) - mask = -1; - else if( ipscanfcount == 5 ) - mask = IP[0]; - else if( ipscanfcount > 0 && ipscanfcount < 4 ) - mask = 8 * ipscanfcount; - else - continue; + if( G_admin_ban_matches( ban, ent ) ) + return ban; + } - for(k = 4; k >= 1; k--) - { - if(!IP[k]) continue; - intIP |= IP[k] << 8*(k-1); - } + return NULL; +} - if(mask > 0 && mask <= 32) - { - tempIP &= ~((1 << (32-mask)) - 1); - intIP &= ~((1 << (32-mask)) - 1); - } +qboolean G_admin_ban_check( gentity_t *ent, char *reason, int rlen ) +{ + g_admin_ban_t *ban; + char warningMessage[ MAX_STRING_CHARS ]; - if( intIP == tempIP || mask == 0 ) - { - char duration[ 32 ]; - G_admin_duration( ( g_admin_bans[ i ]->expires - t ), - duration, sizeof( duration ) ); - - // flood protected - if( t - lastConnectTime >= 300 || - Q_stricmp( lastConnectIP, ip ) ) - { - lastConnectTime = t; - Q_strncpyz( lastConnectIP, ip, sizeof( lastConnectIP ) ); - - G_WarningsPrintf( - "ban", - "Banned player %s^7 (%s^7) tried to connect (ban #%i on %s by %s^7 expires %s reason: %s^7 )\n", - Info_ValueForKey( userinfo, "name" ), - g_admin_bans[ i ]->name, - i+1, - ip, - g_admin_bans[ i ]->banner, - duration, - g_admin_bans[ i ]->reason ); - } - - Com_sprintf( - reason, - rlen, - "You have been banned by %s^7 reason: %s^7 expires: %s %s", - g_admin_bans[ i ]->banner, - g_admin_bans[ i ]->reason, - duration, - notice - ); - G_LogPrintf("Banned player tried to connect from IP %s\n", ip); - return qtrue; - } - } - if( *guid && !Q_stricmp( g_admin_bans[ i ]->guid, guid ) ) - { - char duration[ 32 ]; - G_admin_duration( ( g_admin_bans[ i ]->expires - t ), - duration, sizeof( duration ) ); - Com_sprintf( - reason, - rlen, - "You have been banned by %s^7 reason: %s^7 expires: %s", - g_admin_bans[ i ]->banner, - g_admin_bans[ i ]->reason, - duration - ); - G_Printf("Banned player tried to connect with GUID %s\n", guid); - return qtrue; - } - } - if ( *guid ) + if( ent->client->pers.localClient ) + return qfalse; + + if( ( ban = G_admin_match_ban( ent ) ) ) { - int count = 0; - qboolean valid = qtrue; + G_admin_ban_message( ent, ban, reason, rlen, + warningMessage, sizeof( warningMessage ) ); - while( guid[ count ] != '\0' && valid ) - { - if( (guid[ count ] < '0' || guid[ count ] > '9') && - (guid[ count ] < 'A' || guid[ count ] > 'F') ) - { - valid = qfalse; - } - count++; - } - if( !valid || count != 32 ) - { - Com_sprintf( reason, rlen, "Invalid client data" ); - G_Printf("Player with invalid GUID [%s] connect from IP %s\n", guid, ip); - return qtrue; - } + // don't spam admins + if( ban->warnCount++ < 5 ) + G_AdminMessage( NULL, warningMessage ); + // and don't fill the console + else if( ban->warnCount < 10 ) + trap_Print( va( "%s%s\n", warningMessage, + ban->warnCount + 1 == 10 ? + S_COLOR_WHITE " - future messages for this ban will be suppressed" : + "" ) ); + return qtrue; } + return qfalse; } -qboolean G_admin_cmd_check( gentity_t *ent, qboolean say ) +qboolean G_admin_cmd_check( gentity_t *ent ) { - int i; char command[ MAX_ADMIN_CMD_LEN ]; - char *cmd; - int skip = 0; + g_admin_cmd_t *admincmd; + g_admin_command_t *c; + qboolean success; command[ 0 ] = '\0'; - G_SayArgv( 0, command, sizeof( command ) ); - if( !Q_stricmp( command, "say" ) || - ( G_admin_permission( ent, ADMF_TEAMCHAT_CMD ) && - ( !Q_stricmp( command, "say_team" ) ) ) ) - { - skip = 1; - G_SayArgv( 1, command, sizeof( command ) ); - } + trap_Argv( 0, command, sizeof( command ) ); if( !command[ 0 ] ) return qfalse; - if( command[ 0 ] == '!' ) - { - cmd = &command[ 1 ]; - } - else - { - return qfalse; - } - - // Flood limit. If they're talking too fast, determine that and return. - if( g_floodMinTime.integer ) - if ( G_Flood_Limited( ent ) ) - { - trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" ); - return qtrue; - } + Q_strlwr( command ); + admin_log_start( ent, command ); - if( G_admin_is_restricted( ent, qtrue ) ) - return qtrue; - - for( i = 0; i < MAX_ADMIN_COMMANDS && g_admin_commands[ i ]; i++ ) + if( ( c = G_admin_command( command ) ) ) { - if( Q_stricmp( cmd, g_admin_commands[ i ]->command ) ) - continue; - - if( G_admin_permission( ent, g_admin_commands[ i ]->flag ) ) + admin_log( ConcatArgsPrintable( 1 ) ); + if( ( success = G_admin_permission( ent, c->flag ) ) ) { - trap_SendConsoleCommand( EXEC_APPEND, g_admin_commands[ i ]->exec ); - admin_log( ent, cmd, skip ); - G_admin_adminlog_log( ent, cmd, NULL, skip, qtrue ); + if( G_FloodLimited( ent ) ) + return qtrue; + trap_SendConsoleCommand( EXEC_APPEND, c->exec ); } else { - ADMP( va( "^3!%s: ^7permission denied\n", g_admin_commands[ i ]->command ) ); - admin_log( ent, "attempted", skip - 1 ); - G_admin_adminlog_log( ent, cmd, NULL, skip, qfalse ); + ADMP( va( "^3%s: ^7permission denied\n", c->command ) ); } + admin_log_end( success ); return qtrue; } - for( i = 0; i < adminNumCmds; i++ ) + if( ( admincmd = G_admin_cmd( command ) ) ) { - if( Q_stricmp( cmd, g_admin_cmds[ i ].keyword ) ) - continue; - - if( G_admin_permission( ent, g_admin_cmds[ i ].flag ) ) + if( ( success = G_admin_permission( ent, admincmd->flag ) ) ) { - g_admin_cmds[ i ].handler( ent, skip ); - admin_log( ent, cmd, skip ); - G_admin_adminlog_log( ent, cmd, NULL, skip, qtrue ); + if( G_FloodLimited( ent ) ) + return qtrue; + if( admincmd->silent ) + admin_log_abort( ); + if( !( success = admincmd->handler( ent ) ) ) + admin_log( ConcatArgsPrintable( 1 ) ); } else { - ADMP( va( "^3!%s: ^7permission denied\n", g_admin_cmds[ i ].keyword ) ); - admin_log( ent, "attempted", skip - 1 ); - G_admin_adminlog_log( ent, cmd, NULL, skip, qfalse ); + ADMP( va( "^3%s: ^7permission denied\n", admincmd->keyword ) ); + admin_log( ConcatArgsPrintable( 1 ) ); } + admin_log_end( success ); return qtrue; } return qfalse; } -void G_admin_namelog_cleanup( ) +static void llsort( struct llist **head, int compar( const void *, const void * ) ) { - int i; + struct llist *a, *b, *t, *l; + int i, c = 1, ns, as, bs; - for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) - { - if( g_admin_namelog[ i ]->smj.comment ) - G_Free( g_admin_namelog[ i ]->smj.comment ); - G_Free( g_admin_namelog[ i ] ); - g_admin_namelog[ i ] = NULL; - } -} - -static void dispatchSchachtmeisterIPAQuery( const char *ipa ) -{ - trap_SendConsoleCommand( EXEC_APPEND, va( "smq ipa \"%s\"\n", ipa ) ); -} - -static void schachtmeisterProcess( g_admin_namelog_t *namelog ) -{ - schachtmeisterJudgement_t *j = &namelog->smj; - - if( !j->ratingTime || level.time - j->ratingTime >= 600000 ) - { - if( j->queryTime ) - return; - - j->queryTime = level.time; - goto dispatch; - } - - if( !j->queryTime || level.time - j->queryTime >= 60000 ) - return; - - if( j->dispatchTime && level.time - j->dispatchTime < 5000 ) + if( !*head ) return; - dispatch: - j->dispatchTime = level.time; - dispatchSchachtmeisterIPAQuery( namelog->ip ); -} - -void G_admin_schachtmeisterFrame( void ) -{ - int i; - for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) - schachtmeisterProcess( g_admin_namelog[ i ] ); -} - -void G_admin_namelog_update( gclient_t *client, qboolean disconnect ) -{ - int i, j; - g_admin_namelog_t *namelog; - char n1[ MAX_NAME_LENGTH ]; - char n2[ MAX_NAME_LENGTH ]; - int clientNum = ( client - level.clients ); - - if ( client->sess.invisible == qfalse ) - { - G_admin_seen_update( client->pers.guid ); - } - - G_SanitiseString( client->pers.netname, n1, sizeof( n1 ) ); - for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) + do { - if( disconnect && g_admin_namelog[ i ]->slot != clientNum ) - continue; - - if( !disconnect && !( g_admin_namelog[ i ]->slot == clientNum || - g_admin_namelog[ i ]->slot == -1 ) ) + a = *head, l = *head = NULL; + for( ns = 0; a; ns++, a = b ) { - continue; - } - - if( !Q_stricmp( client->pers.ip, g_admin_namelog[ i ]->ip ) - && !Q_stricmp( client->pers.guid, g_admin_namelog[ i ]->guid ) ) - { - for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES - && g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ ) + b = a; + for( i = as = 0; i < c; i++ ) { - G_SanitiseString( g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) ); - if( !Q_stricmp( n1, n2 ) ) + as++; + if( !( b = b->next ) ) break; } - if( j == MAX_ADMIN_NAMELOG_NAMES ) - j = MAX_ADMIN_NAMELOG_NAMES - 1; - Q_strncpyz( g_admin_namelog[ i ]->name[ j ], client->pers.netname, - sizeof( g_admin_namelog[ i ]->name[ j ] ) ); - g_admin_namelog[ i ]->slot = ( disconnect ) ? -1 : clientNum; - - // if this player is connecting, they are no longer banned - if( !disconnect ) - g_admin_namelog[ i ]->banned = qfalse; - - //check other things like if user was denybuild or muted or denyweapon and restore them - if( !disconnect ) - { - if( g_admin_namelog[ i ]->muted ) - { - client->pers.muted = qtrue; - client->pers.muteExpires = g_admin_namelog[ i ]->muteExpires; - G_AdminsPrintf( "^7%s^7's mute has been restored\n", client->pers.netname ); - g_admin_namelog[ i ]->muted = qfalse; - } - if( g_admin_namelog[ i ]->denyBuild ) - { - client->pers.denyBuild = qtrue; - G_AdminsPrintf( "^7%s^7's Denybuild has been restored\n", client->pers.netname ); - g_admin_namelog[ i ]->denyBuild = qfalse; - } - if( g_admin_namelog[ i ]->denyHumanWeapons > 0 || g_admin_namelog[ i ]->denyAlienClasses > 0 ) - { - if( g_admin_namelog[ i ]->denyHumanWeapons > 0 ) - client->pers.denyHumanWeapons = g_admin_namelog[ i ]->denyHumanWeapons; - if( g_admin_namelog[ i ]->denyAlienClasses > 0 ) - client->pers.denyAlienClasses = g_admin_namelog[ i ]->denyAlienClasses; - - G_AdminsPrintf( "^7%s^7's Denyweapon has been restored\n", client->pers.netname ); - g_admin_namelog[ i ]->denyHumanWeapons = 0; - g_admin_namelog[ i ]->denyAlienClasses = 0; - } - if( g_admin_namelog[ i ]->specExpires > 0 ) - { - client->pers.specExpires = g_admin_namelog[ i ]->specExpires; - G_AdminsPrintf( "^7%s^7's Putteam spectator has been restored\n", client->pers.netname ); - g_admin_namelog[ i ]->specExpires = 0; - } - if( g_admin_namelog[ i ]->voteCount > 0 ) - { - client->pers.voteCount = g_admin_namelog[ i ]->voteCount; - g_admin_namelog[ i ]->voteCount = 0; - } - } - else + for( bs = c; ( b && bs ) || as; l = t ) { - //for mute - if( G_IsMuted( client ) ) - { - g_admin_namelog[ i ]->muted = qtrue; - g_admin_namelog[ i ]->muteExpires = client->pers.muteExpires; - } - //denybuild - if( client->pers.denyBuild ) - { - g_admin_namelog[ i ]->denyBuild = qtrue; - } - //denyweapon humans - if( client->pers.denyHumanWeapons > 0 ) - { - g_admin_namelog[ i ]->denyHumanWeapons = client->pers.denyHumanWeapons; - } - //denyweapon aliens - if( client->pers.denyAlienClasses > 0 ) - { - g_admin_namelog[ i ]->denyAlienClasses = client->pers.denyAlienClasses; - } - //putteam spec - if( client->pers.specExpires > 0 ) - { - g_admin_namelog[ i ]->specExpires = client->pers.specExpires; - } - if( client->pers.voteCount > 0 ) - { - g_admin_namelog[ i ]->voteCount = client->pers.voteCount; - } + if( as && ( !bs || !b || compar( a, b ) <= 0 ) ) + t = a, a = a->next, as--; + else + t = b, b = b->next, bs--; + if( l ) + l->next = t; + else + *head = t; } - - return; } - } - if( i >= MAX_ADMIN_NAMELOGS ) - { - G_Printf( "G_admin_namelog_update: warning, g_admin_namelogs overflow\n" ); - return; - } - namelog = G_Alloc( sizeof( g_admin_namelog_t ) ); - memset( namelog, 0, sizeof( *namelog ) ); - for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES ; j++ ) - namelog->name[ j ][ 0 ] = '\0'; - Q_strncpyz( namelog->ip, client->pers.ip, sizeof( namelog->ip ) ); - Q_strncpyz( namelog->guid, client->pers.guid, sizeof( namelog->guid ) ); - Q_strncpyz( namelog->name[ 0 ], client->pers.netname, - sizeof( namelog->name[ 0 ] ) ); - namelog->slot = ( disconnect ) ? -1 : clientNum; - schachtmeisterProcess( namelog ); - g_admin_namelog[ i ] = namelog; + l->next = NULL; + c *= 2; + } while( ns > 1 ); +} + +static int cmplevel( const void *a, const void *b ) +{ + return ((g_admin_level_t *)b)->level - ((g_admin_level_t *)a)->level; } -qboolean G_admin_readconfig( gentity_t *ent, int skiparg ) +qboolean G_admin_readconfig( gentity_t *ent ) { - g_admin_level_t * l = NULL; + g_admin_level_t *l = NULL; g_admin_admin_t *a = NULL; g_admin_ban_t *b = NULL; g_admin_command_t *c = NULL; @@ -1722,54 +1084,82 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg ) char *t; qboolean level_open, admin_open, ban_open, command_open; int i; + char ip[ 44 ]; G_admin_cleanup(); if( !g_admin.string[ 0 ] ) { - ADMP( "^3!readconfig: g_admin is not set, not loading configuration " + ADMP( "^3readconfig: g_admin is not set, not loading configuration " "from a file\n" ); - admin_default_levels(); return qfalse; } - len = trap_FS_FOpenFile( g_admin.string, &f, FS_READ ) ; + len = trap_FS_FOpenFile( g_admin.string, &f, FS_READ ); if( len < 0 ) { - ADMP( va( "^3!readconfig: ^7could not open admin config file %s\n", - g_admin.string ) ); + G_Printf( "^3readconfig: ^7could not open admin config file %s\n", + g_admin.string ); admin_default_levels(); return qfalse; } - cnf = G_Alloc( len + 1 ); + cnf = BG_Alloc( len + 1 ); cnf2 = cnf; trap_FS_Read( cnf, len, f ); *( cnf + len ) = '\0'; trap_FS_FCloseFile( f ); - t = COM_Parse( &cnf ); + admin_level_maxname = 0; + level_open = admin_open = ban_open = command_open = qfalse; - while( *t ) + COM_BeginParseSession( g_admin.string ); + while( 1 ) { - if( !Q_stricmp( t, "[level]" ) || - !Q_stricmp( t, "[admin]" ) || - !Q_stricmp( t, "[ban]" ) || - !Q_stricmp( t, "[command]" ) ) - { + t = COM_Parse( &cnf ); + if( !*t ) + break; - if( level_open ) - g_admin_levels[ lc++ ] = l; - else if( admin_open ) - g_admin_admins[ ac++ ] = a; - else if( ban_open ) - g_admin_bans[ bc++ ] = b; - else if( command_open ) - g_admin_commands[ cc++ ] = c; - level_open = admin_open = - ban_open = command_open = qfalse; + if( !Q_stricmp( t, "[level]" ) ) + { + if( l ) + l = l->next = BG_Alloc( sizeof( g_admin_level_t ) ); + else + l = g_admin_levels = BG_Alloc( sizeof( g_admin_level_t ) ); + level_open = qtrue; + admin_open = ban_open = command_open = qfalse; + lc++; } - - if( level_open ) + else if( !Q_stricmp( t, "[admin]" ) ) + { + if( a ) + a = a->next = BG_Alloc( sizeof( g_admin_admin_t ) ); + else + a = g_admin_admins = BG_Alloc( sizeof( g_admin_admin_t ) ); + admin_open = qtrue; + level_open = ban_open = command_open = qfalse; + ac++; + } + else if( !Q_stricmp( t, "[ban]" ) ) + { + if( b ) + b = b->next = BG_Alloc( sizeof( g_admin_ban_t ) ); + else + b = g_admin_bans = BG_Alloc( sizeof( g_admin_ban_t ) ); + ban_open = qtrue; + level_open = admin_open = command_open = qfalse; + bc++; + } + else if( !Q_stricmp( t, "[command]" ) ) + { + if( c ) + c = c->next = BG_Alloc( sizeof( g_admin_command_t ) ); + else + c = g_admin_commands = BG_Alloc( sizeof( g_admin_command_t ) ); + command_open = qtrue; + level_open = admin_open = ban_open = qfalse; + cc++; + } + else if( level_open ) { if( !Q_stricmp( t, "level" ) ) { @@ -1778,6 +1168,10 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg ) else if( !Q_stricmp( t, "name" ) ) { admin_readconfig_string( &cnf, l->name, sizeof( l->name ) ); + // max printable name length for formatting + len = Q_PrintStrlen( l->name ); + if( len > admin_level_maxname ) + admin_level_maxname = len; } else if( !Q_stricmp( t, "flags" ) ) { @@ -1785,9 +1179,7 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg ) } else { - ADMP( va( "^3!readconfig: ^7[level] parse error near %s on line %d\n", - t, - COM_GetCurrentParseLine() ) ); + COM_ParseError( "[level] unrecognized token \"%s\"", t ); } } else if( admin_open ) @@ -1808,15 +1200,9 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg ) { admin_readconfig_string( &cnf, a->flags, sizeof( a->flags ) ); } - else if( !Q_stricmp( t, "seen" ) ) - { - admin_readconfig_int( &cnf, &a->seen ); - } else { - ADMP( va( "^3!readconfig: ^7[admin] parse error near %s on line %d\n", - t, - COM_GetCurrentParseLine() ) ); + COM_ParseError( "[admin] unrecognized token \"%s\"", t ); } } @@ -1832,7 +1218,8 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg ) } else if( !Q_stricmp( t, "ip" ) ) { - admin_readconfig_string( &cnf, b->ip, sizeof( b->ip ) ); + admin_readconfig_string( &cnf, ip, sizeof( ip ) ); + G_AddressParse( ip, &b->ip ); } else if( !Q_stricmp( t, "reason" ) ) { @@ -1846,23 +1233,13 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg ) { admin_readconfig_int( &cnf, &b->expires ); } - else if( !Q_stricmp( t, "suspend" ) ) - { - admin_readconfig_int( &cnf, &b->suspend ); - } else if( !Q_stricmp( t, "banner" ) ) { admin_readconfig_string( &cnf, b->banner, sizeof( b->banner ) ); } - else if( !Q_stricmp( t, "blevel" ) ) - { - admin_readconfig_int( &cnf, &b->bannerlevel ); - } else { - ADMP( va( "^3!readconfig: ^7[ban] parse error near %s on line %d\n", - t, - COM_GetCurrentParseLine() ) ); + COM_ParseError( "[ban] unrecognized token \"%s\"", t ); } } else if( command_open ) @@ -1885,761 +1262,305 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg ) } else { - ADMP( va( "^3!readconfig: ^7[command] parse error near %s on line %d\n", - t, - COM_GetCurrentParseLine() ) ); + COM_ParseError( "[command] unrecognized token \"%s\"", t ); } } - - if( !Q_stricmp( t, "[level]" ) ) - { - if( lc >= MAX_ADMIN_LEVELS ) - return qfalse; - l = G_Alloc( sizeof( g_admin_level_t ) ); - l->level = 0; - *l->name = '\0'; - *l->flags = '\0'; - level_open = qtrue; - } - else if( !Q_stricmp( t, "[admin]" ) ) - { - if( ac >= MAX_ADMIN_ADMINS ) - return qfalse; - a = G_Alloc( sizeof( g_admin_admin_t ) ); - *a->name = '\0'; - *a->guid = '\0'; - a->level = 0; - *a->flags = '\0'; - a->seen = 0; - admin_open = qtrue; - } - else if( !Q_stricmp( t, "[ban]" ) ) - { - if( bc >= MAX_ADMIN_BANS ) - return qfalse; - b = G_Alloc( sizeof( g_admin_ban_t ) ); - *b->name = '\0'; - *b->guid = '\0'; - *b->ip = '\0'; - *b->made = '\0'; - b->expires = 0; - b->suspend = 0; - *b->reason = '\0'; - b->bannerlevel = 0; - ban_open = qtrue; - } - else if( !Q_stricmp( t, "[command]" ) ) + else { - if( cc >= MAX_ADMIN_COMMANDS ) - return qfalse; - c = G_Alloc( sizeof( g_admin_command_t ) ); - *c->command = '\0'; - *c->exec = '\0'; - *c->desc = '\0'; - *c->flag = '\0'; - command_open = qtrue; + COM_ParseError( "unexpected token \"%s\"", t ); } - t = COM_Parse( &cnf ); - } - if( level_open ) - { - - g_admin_levels[ lc++ ] = l; } - if( admin_open ) - g_admin_admins[ ac++ ] = a; - if( ban_open ) - g_admin_bans[ bc++ ] = b; - if( command_open ) - g_admin_commands[ cc++ ] = c; - G_Free( cnf2 ); - ADMP( va( "^3!readconfig: ^7loaded %d levels, %d admins, %d bans, %d commands\n", + BG_Free( cnf2 ); + ADMP( va( "^3readconfig: ^7loaded %d levels, %d admins, %d bans, %d commands\n", lc, ac, bc, cc ) ); if( lc == 0 ) admin_default_levels(); else { - char n[ MAX_NAME_LENGTH ] = {""}; - - // max printable name length for formatting - for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) - { - G_DecolorString( l->name, n ); - if( strlen( n ) > admin_level_maxname ) - admin_level_maxname = strlen( n ); - } + llsort( (struct llist **)&g_admin_levels, cmplevel ); + llsort( (struct llist **)&g_admin_admins, cmplevel ); } - // reset adminLevel + + // restore admin mapping for( i = 0; i < level.maxclients; i++ ) + { if( level.clients[ i ].pers.connected != CON_DISCONNECTED ) - level.clients[ i ].pers.adminLevel = G_admin_level( &g_entities[ i ] ); + { + level.clients[ i ].pers.admin = + G_admin_admin( level.clients[ i ].pers.guid ); + if( level.clients[ i ].pers.admin ) + G_admin_authlog( &g_entities[ i ] ); + G_admin_cmdlist( &g_entities[ i ] ); + } + } + return qtrue; } -qboolean G_admin_time( gentity_t *ent, int skiparg ) +qboolean G_admin_time( gentity_t *ent ) { qtime_t qt; - int t; - t = trap_RealTime( &qt ); - ADMP( va( "^3!time: ^7local time is %02i:%02i:%02i\n", + trap_RealTime( &qt ); + ADMP( va( "^3time: ^7local time is %02i:%02i:%02i\n", qt.tm_hour, qt.tm_min, qt.tm_sec ) ); - return qtrue; } -static int G_admin_find_slot( gentity_t *ent, char *namearg, const char *command ) -{ - char name[ MAX_NAME_LENGTH ]; - char testname[ MAX_NAME_LENGTH ]; - char *guid = NULL; - int matches = 0; - int id = -1; - int i; - qboolean numeric = qtrue; - - G_SanitiseString( namearg, name, sizeof( name ) ); - for( i = 0; i < sizeof( name ) && name[ i ] ; i++ ) - { - if( !isdigit( name[ i ] ) ) - { - numeric = qfalse; - break; - } - } - if( numeric ) - { - id = atoi( name ); - - if( id >= 0 && id < level.maxclients ) - { - gentity_t *vic; - - vic = &g_entities[ id ]; - if( vic && vic->client && vic->client->pers.connected != CON_DISCONNECTED ) - return id; - - ADMP( va( "^3!%s:^7 no one connected by that slot number\n", command ) ); - return -1; - } - if( id >= MAX_CLIENTS && id < MAX_CLIENTS + MAX_ADMIN_ADMINS && - g_admin_admins[ id - MAX_CLIENTS ] ) - { - return id; - } - - ADMP( va( "^3!%s:^7 no match for slot or admin number %d\n", command, id ) ); - return -1; - } - - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] && matches < 2; i++ ) - { - G_SanitiseString( g_admin_admins[ i ]->name, testname, sizeof( testname ) ); - if( strstr( testname, name ) ) - { - id = i + MAX_CLIENTS; - guid = g_admin_admins[ i ]->guid; - matches++; - } - } - for( i = 0; i < level.maxclients && matches < 2; i++ ) - { - if( level.clients[ i ].pers.connected == CON_DISCONNECTED ) - continue; - - if( matches && guid && !Q_stricmp( level.clients[ i ].pers.guid, guid ) ) - continue; - - G_SanitiseString( level.clients[ i ].pers.netname, testname, sizeof( testname ) ); - if( strstr( testname, name ) ) - { - id = i; - matches++; - } - } - - if( matches == 0 ) - { - ADMP( va( "^3!%s:^7 no match, use !listplayers or !listadmins to " - "find an appropriate number to use instead of name.\n", command ) ); - return -1; - } - - if( matches == 1 ) - return id; - - ADMP( va( "^3!%s:^7 multiple matches, use the admin number instead:\n", command ) ); - admin_listadmins( ent, 0, name, 0 ); +// this should be in one of the headers, but it is only used here for now +namelog_t *G_NamelogFromString( gentity_t *ent, char *s ); - return -1; -} - -static int G_admin_find_admin_slot( gentity_t *ent, char *namearg, char *command, char *nick, int nick_len ) +/* +for consistency, we should be able to target a disconnected player with setlevel +but we can't use namelog and remain consistent, so the solution would be to make +everyone a real level 0 admin so they can be targeted until the next level +but that seems kind of stupid +*/ +qboolean G_admin_setlevel( gentity_t *ent ) { - gentity_t *vic; - char *guid; - int id; + char name[ MAX_NAME_LENGTH ] = {""}; + char lstr[ 12 ]; // 11 is max strlen() for 32-bit (signed) int + char testname[ MAX_NAME_LENGTH ] = {""}; int i; + gentity_t *vic = NULL; + g_admin_admin_t *a = NULL; + g_admin_level_t *l = NULL; + int na; - if ( nick ) - nick[ 0 ] = '\0'; - - id = G_admin_find_slot( ent, namearg, command ); - if( id < 0 ) - return -1; - - if( id < MAX_CLIENTS ) + if( trap_Argc() < 3 ) { - vic = &g_entities[ id ]; - guid = vic->client->pers.guid; - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) - { - if( !Q_stricmp( guid, g_admin_admins[ i ]->guid ) ) - { - id = i + MAX_CLIENTS; - if( nick ) - Q_strncpyz( nick, vic->client->pers.netname, nick_len ); - break; - } - } - if( id < MAX_CLIENTS ) - { - ADMP( va( "^3!%s:^7 player is not !registered\n", command ) ); - return -1; - } + ADMP( "^3setlevel: ^7usage: setlevel [name|slot#] [level]\n" ); + return qfalse; } - id -= MAX_CLIENTS; - if( nick && !nick[ 0 ] ) - Q_strncpyz( nick, g_admin_admins[ id ]->name, nick_len ); - - return id; -} - -qboolean G_admin_setlevel( gentity_t *ent, int skiparg ) -{ - char lstr[ 11 ]; // 10 is max strlen() for 32-bit int - char adminname[ MAX_NAME_LENGTH ] = {""}; - char testname[ MAX_NAME_LENGTH ] = {""}; - char guid[ 33 ]; - int l, i; - gentity_t *vic = NULL; - qboolean updated = qfalse; - g_admin_admin_t *a; - qboolean found = qfalse; - int id = -1; + trap_Argv( 1, testname, sizeof( testname ) ); + trap_Argv( 2, lstr, sizeof( lstr ) ); - if( G_SayArgc() < 3 + skiparg ) + if( !( l = G_admin_level( atoi( lstr ) ) ) ) { - ADMP( "^3!setlevel: ^7usage: !setlevel [name|slot#] [level]\n" ); + ADMP( "^3setlevel: ^7level is not defined\n" ); return qfalse; } - G_SayArgv( 1 + skiparg, testname, sizeof( testname ) ); - G_SayArgv( 2 + skiparg, lstr, sizeof( lstr ) ); - l = atoi( lstr ); - if( ent && l > ent->client->pers.adminLevel ) + if( ent && l->level > + ( ent->client->pers.admin ? ent->client->pers.admin->level : 0 ) ) { - ADMP( "^3!setlevel: ^7you may not use !setlevel to set a level higher " + ADMP( "^3setlevel: ^7you may not use setlevel to set a level higher " "than your current level\n" ); return qfalse; } - // if admin is activated for the first time on a running server, we need - // to ensure at least the default levels get created - if( !ent && !g_admin_levels[ 0 ] ) - G_admin_readconfig(NULL, 0); + for( na = 0, a = g_admin_admins; a; na++, a = a->next ); - for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) + for( i = 0; testname[ i ] && isdigit( testname[ i ] ); i++ ); + if( !testname[ i ] ) { - if( g_admin_levels[ i ]->level == l ) - { - found = qtrue; - break; - } - } - if( !found ) - { - ADMP( "^3!setlevel: ^7level is not defined\n" ); - return qfalse; - } - - id = G_admin_find_slot( ent, testname, "setlevel" ); - if( id >=0 && id < MAX_CLIENTS ) - { - vic = &g_entities[ id ]; - Q_strncpyz( adminname, vic->client->pers.netname, sizeof( adminname ) ); - Q_strncpyz( guid, vic->client->pers.guid, sizeof( guid ) ); - } - else if( id >= MAX_CLIENTS && id < MAX_CLIENTS + MAX_ADMIN_ADMINS && - g_admin_admins[ id - MAX_CLIENTS ] ) - { - Q_strncpyz( adminname, g_admin_admins[ id - MAX_CLIENTS ]->name, - sizeof( adminname ) ); - Q_strncpyz( guid, g_admin_admins[ id - MAX_CLIENTS ]->guid, - sizeof( guid ) ); - for( i = 0; i < level.maxclients; i++ ) + int id = atoi( testname ); + if( id < MAX_CLIENTS ) { - if( level.clients[ i ].pers.connected == CON_DISCONNECTED ) - continue; - if( !Q_stricmp( level.clients[ i ].pers.guid, guid ) ) + vic = &g_entities[ id ]; + if( !vic || !vic->client || vic->client->pers.connected == CON_DISCONNECTED ) { - vic = &g_entities[ i ]; - Q_strncpyz( adminname, vic->client->pers.netname, sizeof( adminname ) ); + ADMP( va( "^3setlevel: ^7no player connected in slot %d\n", id ) ); + return qfalse; } } - } - else - { - return qfalse; - } - - if( !Q_stricmp( guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ) ) - { - ADMP( va( "^3!setlevel: ^7%s does not have a valid GUID\n", adminname ) ); - return qfalse; - } - if( ent && !admin_higher_guid( ent->client->pers.guid, guid ) ) - { - ADMP( "^3!setlevel: ^7sorry, but your intended victim has a higher" - " admin level than you\n" ); - return qfalse; - } - - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ];i++ ) - { - if( !Q_stricmp( g_admin_admins[ i ]->guid, guid ) ) - { - g_admin_admins[ i ]->level = l; - Q_strncpyz( g_admin_admins[ i ]->name, adminname, - sizeof( g_admin_admins[ i ]->name ) ); - updated = qtrue; - } - } - if( !updated ) - { - if( i == MAX_ADMIN_ADMINS ) + else if( id < na + MAX_CLIENTS ) + for( i = 0, a = g_admin_admins; i < id - MAX_CLIENTS; i++, a = a->next ); + else { - ADMP( "^3!setlevel: ^7too many admins\n" ); + ADMP( va( "^3setlevel: ^7%s not in range 1-%d\n", + testname, na + MAX_CLIENTS - 1 ) ); return qfalse; } - a = G_Alloc( sizeof( g_admin_admin_t ) ); - a->level = l; - Q_strncpyz( a->name, adminname, sizeof( a->name ) ); - Q_strncpyz( a->guid, guid, sizeof( a->guid ) ); - *a->flags = '\0'; - g_admin_admins[ i ] = a; } - - AP( va( - "print \"^3!setlevel: ^7%s^7 was given level %d admin rights by %s\n\"", - adminname, l, ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - if( vic ) - { - vic->client->pers.adminLevel = l; - G_admin_set_adminname( vic ); - } - - if( !g_admin.string[ 0 ] ) - ADMP( "^3!setlevel: ^7WARNING g_admin not set, not saving admin record " - "to a file\n" ); else - admin_writeconfig(); - return qtrue; -} - -static int SortFlags( const void *pa, const void *pb ) -{ - char *a = (char *)pa; - char *b = (char *)pb; - - if( *a == '-' || *a == '+' ) - a++; - if( *b == '-' || *b == '+' ) - b++; - return strcmp(a, b); -} - -#define MAX_USER_FLAGS 200 -const char *G_admin_user_flag( char *oldflags, char *flag, qboolean add, qboolean clear, - char *newflags, int size ) -{ - char *token, *token_p; - char *key; - char head_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ]; - char tail_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ]; - char allflag[ MAX_ADMIN_FLAG_LEN ]; - char newflag[ MAX_ADMIN_FLAG_LEN ]; - int head_count = 0; - int tail_count = 0; - qboolean flagset = qfalse; - int i; + G_SanitiseString( testname, name, sizeof( name ) ); - if( !flag[ 0 ] ) - { - return "invalid admin flag"; - } - - allflag[ 0 ] = '\0'; - token_p = oldflags; - while( *( token = COM_Parse( &token_p ) ) ) + if( vic ) + a = vic->client->pers.admin; + else if( !a ) { - key = token; - if( *key == '-' || *key == '+' ) - key++; + g_admin_admin_t *wa; + int matches = 0; - if( !strcmp( key, flag ) ) + for( wa = g_admin_admins; wa && matches < 2; wa = wa->next ) { - if( flagset ) - continue; - flagset = qtrue; - if( clear ) + G_SanitiseString( wa->name, testname, sizeof( testname ) ); + if( strstr( testname, name ) ) { - // clearing ALLFLAGS will result in clearing any following flags - if( !strcmp( key, ADMF_ALLFLAGS ) ) - break; - else - continue; + a = wa; + matches++; } - Com_sprintf( newflag, sizeof( newflag ), "%s%s", - ( add ) ? "+" : "-", key ); - } - else - { - Q_strncpyz( newflag, token, sizeof( newflag ) ); } - if( !strcmp( key, ADMF_ALLFLAGS ) ) + for( i = 0; i < level.maxclients && matches < 2; i++ ) { - if( !allflag[ 0 ] ) - Q_strncpyz( allflag, newflag, sizeof( allflag ) ); - continue; - } + if( level.clients[ i ].pers.connected == CON_DISCONNECTED ) + continue; - if( !allflag[ 0 ] ) - { - if( head_count < MAX_USER_FLAGS ) + if( matches && level.clients[ i ].pers.admin && + level.clients[ i ].pers.admin == a ) { - Q_strncpyz( head_flags[ head_count ], newflag, - sizeof( head_flags[ head_count ] ) ); - head_count++; + vic = &g_entities[ i ]; + continue; } - } - else - { - if( tail_count < MAX_USER_FLAGS ) + + G_SanitiseString( level.clients[ i ].pers.netname, testname, + sizeof( testname ) ); + if( strstr( testname, name ) ) { - Q_strncpyz( tail_flags[ tail_count ], newflag, - sizeof( tail_flags[ tail_count ] ) ); - tail_count++; + vic = &g_entities[ i ]; + a = vic->client->pers.admin; + matches++; } } - } - if( !flagset && !clear ) - { - if( !strcmp( flag, ADMF_ALLFLAGS ) ) + if( matches == 0 ) { - Com_sprintf( allflag, sizeof( allflag ), "%s%s", - ( add ) ? "" : "-", ADMF_ALLFLAGS ); + ADMP( "^3setlevel:^7 no match. use listplayers or listadmins to " + "find an appropriate number to use instead of name.\n" ); + return qfalse; } - else if( !allflag[ 0 ] ) + if( matches > 1 ) { - if( head_count < MAX_USER_FLAGS ) - { - Com_sprintf( head_flags[ head_count ], sizeof( head_flags[ head_count ] ), - "%s%s", ( add ) ? "" : "-", flag ); - head_count++; - } - } - else - { - if( tail_count < MAX_USER_FLAGS ) - { - Com_sprintf( tail_flags[ tail_count ], sizeof( tail_flags[ tail_count ] ), - "%s%s", ( add ) ? "+" : "-", flag ); - tail_count++; - } + ADMP( "^3setlevel:^7 more than one match. Use the admin number " + "instead:\n" ); + admin_listadmins( ent, 0, name ); + return qfalse; } } - qsort( head_flags, head_count, sizeof( head_flags[ 0 ] ), SortFlags ); - qsort( tail_flags, tail_count, sizeof( tail_flags[ 0 ] ), SortFlags ); - - // rebuild flags - newflags[ 0 ] = '\0'; - for( i = 0; i < head_count; i++ ) + if( ent && !admin_higher_admin( ent->client->pers.admin, a ) ) { - Q_strcat( newflags, size, - va( "%s%s", ( i ) ? " ": "", head_flags[ i ] ) ); - } - if( allflag[ 0 ] ) - { - Q_strcat( newflags, size, - va( "%s%s", ( newflags[ 0 ] ) ? " ": "", allflag ) ); - - for( i = 0; i < tail_count; i++ ) - { - Q_strcat( newflags, size, - va( " %s", tail_flags[ i ] ) ); - } + ADMP( "^3setlevel: ^7sorry, but your intended victim has a higher" + " admin level than you\n" ); + return qfalse; } - return NULL; -} - -typedef struct { - char *flag; - char *description; -} AdminFlagListEntry_t; -static AdminFlagListEntry_t adminFlagList[] = -{ - { ADMF_ACTIVITY, "inactivity rules do not apply" }, - { ADMF_ADMINCHAT, "can see and use admin chat" }, - { ADMF_HIGHADMINCHAT, "can see and use high admin chat" }, - { ADMF_ALLFLAGS, "has all flags and can use any command" }, - { ADMF_BAN_IMMUNITY, "immune from IP bans" }, - { ADMF_CAN_PERM_BAN, "can permanently ban players" }, - { ADMF_DBUILDER, "permanent designated builder" }, - { ADMF_FORCETEAMCHANGE, "team balance rules do not apply" }, - { ADMF_INCOGNITO, "does not show as admin in !listplayers" }, - { ADMF_SEESINCOGNITO, "sees registered name of players flagged with INCOGNITO" }, - { ADMF_IMMUNITY, "cannot be vote kicked or muted" }, - { ADMF_IMMUTABLE, "admin commands cannot be used on them" }, - { ADMF_NOCENSORFLOOD, "no flood protection" }, - { ADMF_NO_VOTE_LIMIT, "vote limitations do not apply" }, - { ADMF_SEESFULLLISTPLAYERS, "sees all info in !listplayers" }, - { ADMF_SPEC_ALLCHAT, "can see team chat as spectator" }, - { ADMF_ADMINSTEALTH, "uses admin stealth" }, - { ADMF_TEAMCHANGEFREE, "keeps credits on team switch" }, - { ADMF_TEAMCHAT_CMD, "can run commands from team chat" }, - { ADMF_UNACCOUNTABLE, "does not need to specify reason for kick/ban" }, - { ADMF_NOSCRIMRESTRICTION, "team joining, vote and chat restrictions during scrims do not apply" }, - { ADMF_NO_BUILD, "can not build" }, - { ADMF_NO_CHAT, "can not talk" }, - { ADMF_NO_VOTE, "can not call votes" }, - { ADMF_NOAUTOBAHN, "ignored by the Autobahn system" } -}; -static int adminNumFlags= sizeof( adminFlagList ) / sizeof( adminFlagList[ 0 ] ); - -#define MAX_LISTCOMMANDS 128 -qboolean G_admin_flaglist( gentity_t *ent, int skiparg ) -{ - qboolean shown[ MAX_LISTCOMMANDS ]; - int i, j; - int count = 0; - - ADMBP_begin(); - - ADMBP( "^3Ability flags:\n" ); - - for( i = 0; i < adminNumFlags; i++ ) + if( vic && vic->client->pers.guidless ) { - ADMBP( va( " %s%-20s ^7%s\n", - ( adminFlagList[ i ].flag[ 0 ] != '.' ) ? "^5" : "^1", - adminFlagList[ i ].flag, - adminFlagList[ i ].description ) ); + ADMP( va( "^3setlevel: ^7%s^7 has no GUID\n", vic->client->pers.netname ) ); + return qfalse; } - ADMBP( "^3Command flags:\n" ); - - memset( shown, 0, sizeof( shown ) ); - for( i = 0; i < adminNumCmds; i++ ) + if( !a && vic ) { - if( i < MAX_LISTCOMMANDS && shown[ i ] ) - continue; - ADMBP( va( " ^5%-20s^7", g_admin_cmds[ i ].flag ) ); - for( j = i; j < adminNumCmds; j++ ) - { - if( !strcmp( g_admin_cmds[ j ].flag, g_admin_cmds[ i ].flag ) ) - { - ADMBP( va( " %s", g_admin_cmds[ j ].keyword ) ); - if( j < MAX_LISTCOMMANDS ) - shown[ j ] = qtrue; - } - } - ADMBP( "\n" ); - count++; + for( a = g_admin_admins; a && a->next; a = a->next ); + if( a ) + a = a->next = BG_Alloc( sizeof( g_admin_admin_t ) ); + else + a = g_admin_admins = BG_Alloc( sizeof( g_admin_admin_t ) ); + vic->client->pers.admin = a; + Q_strncpyz( a->guid, vic->client->pers.guid, sizeof( a->guid ) ); } - ADMBP( va( "^3!flaglist: ^7listed %d abilities and %d command flags\n", - adminNumFlags, count ) ); + a->level = l->level; + if( vic ) + Q_strncpyz( a->name, vic->client->pers.netname, sizeof( a->name ) ); - ADMBP_end(); + admin_log( va( "%d (%s) \"%s" S_COLOR_WHITE "\"", a->level, a->guid, + a->name ) ); + + AP( va( + "print \"^3setlevel: ^7%s^7 was given level %d admin rights by %s\n\"", + a->name, a->level, ( ent ) ? ent->client->pers.netname : "console" ) ); + admin_writeconfig(); + if( vic ) + { + G_admin_authlog( vic ); + G_admin_cmdlist( vic ); + } return qtrue; } -qboolean G_admin_flag( gentity_t *ent, int skiparg ) +static void admin_create_ban( gentity_t *ent, + char *netname, + char *guid, + addr_t *ip, + int seconds, + char *reason ) { - char command[ MAX_ADMIN_CMD_LEN ], *cmd; - char name[ MAX_NAME_LENGTH ]; - char flagbuf[ MAX_ADMIN_FLAG_LEN ]; - char *flag; - int id; - char adminname[ MAX_NAME_LENGTH ] = {""}; - const char *result; - qboolean add = qtrue; - qboolean clear = qfalse; - int admin_level = -1; - int i, level; - - G_SayArgv( skiparg, command, sizeof( command ) ); - cmd = command; - if( *cmd == '!' ) - cmd++; - - if( G_SayArgc() < 2 + skiparg ) - { - ADMP( va( "^3!%s: ^7usage: !%s slot# flag\n", cmd, cmd ) ); - return qfalse; - } - - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - if( name[ 0 ] == '*' ) - { - if( ent ) - { - ADMP( va( "^3!%s: only console can change admin level flags\n", cmd ) ); - return qfalse; - } - id = atoi( name + 1 ); - for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) - { - if( g_admin_levels[ i ]->level == id ) - { - admin_level = i; - break; - } - } - if( admin_level < 0 ) - { - ADMP( va( "^3!%s: admin level %d does not exist\n", cmd, id ) ); - return qfalse; - } - Com_sprintf( adminname, sizeof( adminname ), "admin level %d", id ); - } - else - { - id = G_admin_find_admin_slot( ent, name, cmd, adminname, sizeof( adminname ) ); - if( id < 0 ) - return qfalse; + g_admin_ban_t *b = NULL; + qtime_t qt; + int t; + int i; + char *name; + char disconnect[ MAX_STRING_CHARS ]; - if( ent && !admin_higher_guid( ent->client->pers.guid, g_admin_admins[ id ]->guid ) ) - { - ADMP( va( "^3%s:^7 your intended victim has a higher admin level than you\n", cmd ) ); - return qfalse; - } - } + t = trap_RealTime( &qt ); - if( G_SayArgc() < 3 + skiparg ) + for( b = g_admin_bans; b; b = b->next ) { - flag = ""; - level = 0; - if( admin_level < 0 ) - { - for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) - { - if( g_admin_admins[ id ]->level == g_admin_levels[ i ]->level ) - { - flag = g_admin_levels[ i ]->flags; - level = g_admin_admins[ id ]->level; - break; - } - } - ADMP( va( "^3%s:^7 flags for %s^7 are '^3%s^7'\n", - cmd, adminname, g_admin_admins[ id ]->flags) ); - } - else - { - flag = g_admin_levels[ admin_level ]->flags; - level = g_admin_levels[ admin_level ]->level; - } - ADMP( va( "^3%s:^7 level %d flags are '%s'\n", - cmd, level, flag ) ); - - return qtrue; + if( !b->next ) + break; } - G_SayArgv( 2 + skiparg, flagbuf, sizeof( flagbuf ) ); - flag = flagbuf; - if( flag[ 0 ] == '-' || flag[ 0 ] == '+' ) - { - add = ( flag[ 0 ] == '+' ); - flag++; - } - if( ent && !Q_stricmp( ent->client->pers.guid, g_admin_admins[ id ]->guid ) ) + if( b ) { - ADMP( va( "^3%s:^7 you may not change your own flags (use rcon)\n", cmd ) ); - return qfalse; - } - if( flag[ 0 ] != '.' && !G_admin_permission( ent, flag ) ) - { - ADMP( va( "^3%s:^7 you can only change flags that you also have\n", cmd ) ); - return qfalse; + if( !b->next ) + b = b->next = BG_Alloc( sizeof( g_admin_ban_t ) ); } + else + b = g_admin_bans = BG_Alloc( sizeof( g_admin_ban_t ) ); - if( !Q_stricmp( cmd, "unflag" ) ) - { - clear = qtrue; - } + Q_strncpyz( b->name, netname, sizeof( b->name ) ); + Q_strncpyz( b->guid, guid, sizeof( b->guid ) ); + memcpy( &b->ip, ip, sizeof( b->ip ) ); - if( admin_level < 0 ) - { - result = G_admin_user_flag( g_admin_admins[ id ]->flags, flag, add, clear, - g_admin_admins[ id ]->flags, sizeof( g_admin_admins[ id ]->flags ) ); - } - else - { - result = G_admin_user_flag( g_admin_levels[ admin_level ]->flags, flag, add, clear, - g_admin_levels[ admin_level ]->flags, - sizeof( g_admin_levels[ admin_level ]->flags ) ); - } - if( result ) - { - ADMP( va( "^3!flag: ^7an error occured setting flag '^3%s^7', %s\n", - flag, result ) ); - return qfalse; - } + Com_sprintf( b->made, sizeof( b->made ), "%04i-%02i-%02i %02i:%02i:%02i", + qt.tm_year+1900, qt.tm_mon+1, qt.tm_mday, + qt.tm_hour, qt.tm_min, qt.tm_sec ); - if( !Q_stricmp( cmd, "flag" ) ) - { - G_AdminsPrintf( "^3!%s: ^7%s^7 was %s admin flag '%s' by %s\n", - cmd, adminname, - ( add ) ? "given" : "denied", - flag, - ( ent ) ? ent->client->pers.netname : "console" ); - } + if( ent && ent->client->pers.admin ) + name = ent->client->pers.admin->name; + else if( ent ) + name = ent->client->pers.netname; else - { - G_AdminsPrintf( "^3!%s: ^7admin flag '%s' for %s^7 cleared by %s\n", - cmd, flag, adminname, - ( ent ) ? ent->client->pers.netname : "console" ); - } + name = "console"; - if( !g_admin.string[ 0 ] ) - ADMP( va( "^3!%s: ^7WARNING g_admin not set, not saving admin record " - "to a file\n", cmd ) ); + Q_strncpyz( b->banner, name, sizeof( b->banner ) ); + if( !seconds ) + b->expires = 0; else - admin_writeconfig(); + b->expires = t + seconds; + if( !*reason ) + Q_strncpyz( b->reason, "banned by admin", sizeof( b->reason ) ); + else + Q_strncpyz( b->reason, reason, sizeof( b->reason ) ); - return qtrue; + G_admin_ban_message( NULL, b, disconnect, sizeof( disconnect ), NULL, 0 ); + + for( i = 0; i < level.maxclients; i++ ) + { + if( level.clients[ i ].pers.connected == CON_DISCONNECTED ) + continue; + if( G_admin_ban_matches( b, &g_entities[ i ] ) ) + { + trap_SendServerCommand( i, va( "disconnect \"%s\"", disconnect ) ); + + trap_DropClient( i, va( "has been kicked by %s^7. reason: %s", + b->banner, b->reason ) ); + } + } } int G_admin_parse_time( const char *time ) { int seconds = 0, num = 0; - int i; - for( i = 0; time[ i ]; i++ ) + if( !*time ) + return -1; + while( *time ) { - if( isdigit( time[ i ] ) ) - { - num = num * 10 + time[ i ] - '0'; - continue; - } - if( i == 0 || !isdigit( time[ i - 1 ] ) ) + if( !isdigit( *time ) ) return -1; - switch( time[ i ] ) + while( isdigit( *time ) ) + num = num * 10 + *time++ - '0'; + + if( !*time ) + break; + switch( *time++ ) { case 'w': num *= 7; case 'd': num *= 24; @@ -2653,2093 +1574,776 @@ int G_admin_parse_time( const char *time ) } if( num ) seconds += num; - // overflow - if( seconds < 0 ) - seconds = 0; return seconds; } -static qboolean admin_create_ban( gentity_t *ent, - char *netname, - char *guid, - char *ip, - int seconds, - char *reason ) +qboolean G_admin_setdevmode( gentity_t *ent ) { - g_admin_ban_t *b = NULL; - qtime_t qt; - int t; - int i; - - t = trap_RealTime( &qt ); - b = G_Alloc( sizeof( g_admin_ban_t ) ); - - if( !b ) + char str[ 5 ]; + if( trap_Argc() != 2 ) + { + ADMP( "^3setdevmode: ^7usage: setdevmode [on|off]\n" ); return qfalse; - - Q_strncpyz( b->name, netname, sizeof( b->name ) ); - Q_strncpyz( b->guid, guid, sizeof( b->guid ) ); - Q_strncpyz( b->ip, ip, sizeof( b->ip ) ); - b->suspend = 0; - - //strftime( b->made, sizeof( b->made ), "%m/%d/%y %H:%M:%S", lt ); - Q_strncpyz( b->made, va( "%02i/%02i/%02i %02i:%02i:%02i", - (qt.tm_mon + 1), qt.tm_mday, (qt.tm_year - 100), - qt.tm_hour, qt.tm_min, qt.tm_sec ), - sizeof( b->made ) ); - - Q_strncpyz( b->banner, G_admin_get_adminname( ent ), sizeof( b->banner ) ); - - if( ent ) - b->bannerlevel = G_admin_level( ent ); - else - b->bannerlevel = 0; - - if( !seconds ) - b->expires = 0; - else - b->expires = t + seconds; - if( !*reason ) - Q_strncpyz( b->reason, "banned by admin", sizeof( b->reason ) ); + } + trap_Argv( 1, str, sizeof( str ) ); + if( !Q_stricmp( str, "on" ) ) + { + if( g_cheats.integer ) + { + ADMP( "^3setdevmode: ^7developer mode is already on\n" ); + return qfalse; + } + trap_Cvar_Set( "sv_cheats", "1" ); + trap_Cvar_Update( &g_cheats ); + AP( va( "print \"^3setdevmode: ^7%s ^7has switched developer mode on\n\"", + ent ? ent->client->pers.netname : "console" ) ); + return qtrue; + } + else if( !Q_stricmp( str, "off" ) ) + { + if( !g_cheats.integer ) + { + ADMP( "^3setdevmode: ^7developer mode is already off\n" ); + return qfalse; + } + trap_Cvar_Set( "sv_cheats", "0" ); + trap_Cvar_Update( &g_cheats ); + AP( va( "print \"^3setdevmode: ^7%s ^7has switched developer mode off\n\"", + ent ? ent->client->pers.netname : "console" ) ); + return qtrue; + } else - Q_strncpyz( b->reason, reason, sizeof( b->reason ) ); - for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) - ; - if( i == MAX_ADMIN_BANS ) { - ADMP( "^3!ban: ^7too many bans\n" ); - G_Free( b ); + ADMP( "^3setdevmode: ^7usage: setdevmode [on|off]\n" ); return qfalse; } - g_admin_bans[ i ] = b; - return qtrue; } -qboolean G_admin_kick( gentity_t *ent, int skiparg ) +qboolean G_admin_kick( gentity_t *ent ) { - int pids[ MAX_CLIENTS ]; + int pid; char name[ MAX_NAME_LENGTH ], *reason, err[ MAX_STRING_CHARS ]; int minargc; gentity_t *vic; - char notice[51]; - - trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) ); - minargc = 3 + skiparg; + minargc = 3; if( G_admin_permission( ent, ADMF_UNACCOUNTABLE ) ) - minargc = 2 + skiparg; + minargc = 2; - if( G_SayArgc() < minargc ) + if( trap_Argc() < minargc ) { - ADMP( "^3!kick: ^7usage: !kick [name] [reason]\n" ); + ADMP( "^3kick: ^7usage: kick [name] [reason]\n" ); return qfalse; } - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - reason = G_SayConcatArgs( 2 + skiparg ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) + trap_Argv( 1, name, sizeof( name ) ); + reason = ConcatArgs( 2 ); + if( ( pid = G_ClientNumberFromString( name, err, sizeof( err ) ) ) == -1 ) { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!kick: ^7%s\n", err ) ); + ADMP( va( "^3kick: ^7%s", err ) ); return qfalse; } - if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) + vic = &g_entities[ pid ]; + if( !admin_higher( ent, vic ) ) { - ADMP( "^3!kick: ^7sorry, but your intended victim has a higher admin" + ADMP( "^3kick: ^7sorry, but your intended victim has a higher admin" " level than you\n" ); return qfalse; } - vic = &g_entities[ pids[ 0 ] ]; + if( vic->client->pers.localClient ) + { + ADMP( "^3kick: ^7disconnecting the host would end the game\n" ); + return qfalse; + } + admin_log( va( "%d (%s) \"%s" S_COLOR_WHITE "\": \"%s" S_COLOR_WHITE "\"", + pid, + vic->client->pers.guid, + vic->client->pers.netname, + reason ) ); admin_create_ban( ent, vic->client->pers.netname, - vic->client->pers.guid, - vic->client->pers.ip, G_admin_parse_time( g_adminTempBan.string ), + vic->client->pers.guidless ? "" : vic->client->pers.guid, + &vic->client->pers.ip, + MAX( 1, G_admin_parse_time( g_adminTempBan.string ) ), ( *reason ) ? reason : "kicked by admin" ); - if( g_admin.string[ 0 ] ) - admin_writeconfig(); - - trap_SendServerCommand( pids[ 0 ], - va( "disconnect \"You have been kicked.\n%s^7\nreason:\n%s\n%s\"", - ( ent ) ? va( "admin:\n%s", G_admin_adminPrintName( ent ) ) : "admin\nconsole", - ( *reason ) ? reason : "kicked by admin", notice ) ); - - trap_DropClient( pids[ 0 ], va( "kicked%s^7, reason: %s", - ( ent ) ? va( " by %s", G_admin_adminPrintName( ent ) ) : " by console", - ( *reason ) ? reason : "kicked by admin" ) ); + admin_writeconfig(); return qtrue; } -qboolean G_admin_ban( gentity_t *ent, int skiparg ) +qboolean G_admin_setivo( gentity_t* ent ) { - int seconds; - char search[ MAX_NAME_LENGTH ]; - char secs[ 7 ]; - char *reason; - int minargc; - char duration[ 32 ]; - int logmatch = -1, logmatches = 0; - int i, j; - qboolean exactmatch = qfalse; - char n2[ MAX_NAME_LENGTH ]; - char s2[ MAX_NAME_LENGTH ]; - char guid_stub[ 9 ]; - char notice[51]; - - trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) ); - - if( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) && - G_admin_permission( ent, ADMF_UNACCOUNTABLE ) ) - { - minargc = 2 + skiparg; - } - else if( ( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) || g_adminMaxBan.integer ) || - G_admin_permission( ent, ADMF_UNACCOUNTABLE ) ) - { - minargc = 3 + skiparg; - } - else - { - minargc = 4 + skiparg; - } - if( G_SayArgc() < minargc ) + char arg[ 3 ]; + const char *cn; + gentity_t *spot; + + if( !ent ) { - ADMP( "^3!ban: ^7usage: !ban [name|slot|ip] [time] [reason]\n" ); + ADMP( "^3setivo: ^7the console can't position intermission view overrides\n" ); return qfalse; } - G_SayArgv( 1 + skiparg, search, sizeof( search ) ); - G_SanitiseString( search, s2, sizeof( s2 ) ); - G_SayArgv( 2 + skiparg, secs, sizeof( secs ) ); - seconds = G_admin_parse_time( secs ); - if( seconds <= 0 ) + if( trap_Argc() != 2 ) { - if( g_adminMaxBan.integer && !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) ) - { - ADMP( va( "^3!ban: ^7using your admin level's maximum ban length of %s\n", - g_adminMaxBan.string ) ); - seconds = G_admin_parse_time( g_adminMaxBan.string ); - } - else if( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) ) - { - seconds = 0; - } - else - { - ADMP( "^3!ban: ^7ban time must be positive\n" ); - return qfalse; - } - reason = G_SayConcatArgs( 2 + skiparg ); + ADMP( "^3setivo: ^7usage: setivo {s|a|h}\n" ); + return qfalse; } + trap_Argv( 1, arg, sizeof( arg ) ); + if( !Q_stricmp( arg, "s" ) ) + cn = "info_player_intermission"; + else if( !Q_stricmp( arg, "a" ) ) + cn = "info_alien_intermission"; + else if( !Q_stricmp( arg, "h" ) ) + cn = "info_human_intermission"; else { - reason = G_SayConcatArgs( 3 + skiparg ); - - if( g_adminMaxBan.integer && - seconds > G_admin_parse_time( g_adminMaxBan.string ) && - !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) ) - { - seconds = G_admin_parse_time( g_adminMaxBan.string ); - ADMP( va( "^3!ban: ^7ban length limited to %s for your admin level\n", - g_adminMaxBan.string ) ); - } - } - - for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) - { - // skip players in the namelog who have already been banned - if( g_admin_namelog[ i ]->banned ) - continue; - - // skip disconnected players when banning on slot number - if( g_admin_namelog[ i ]->slot == -1 ) - continue; - - if( !Q_stricmp( va( "%d", g_admin_namelog[ i ]->slot ), s2 ) ) - { - logmatches = 1; - logmatch = i; - exactmatch = qtrue; - break; - } - } - - for( i = 0; - !exactmatch && i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; - i++ ) - { - // skip players in the namelog who have already been banned - if( g_admin_namelog[ i ]->banned ) - continue; - - if( !Q_stricmp( g_admin_namelog[ i ]->ip, s2 ) ) - { - logmatches = 1; - logmatch = i; - exactmatch = qtrue; - break; - } - for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES - && g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ ) - { - G_SanitiseString(g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) ); - if( strstr( n2, s2 ) ) - { - if( logmatch != i ) - logmatches++; - logmatch = i; - } - } - } - - if( !logmatches ) - { - ADMP( "^3!ban: ^7no player found by that name, IP, or slot number\n" ); - return qfalse; - } - else if( logmatches > 1 ) - { - ADMBP_begin(); - ADMBP( "^3!ban: ^7multiple recent clients match name, use IP or slot#:\n" ); - for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) - { - for( j = 0; j < 8; j++ ) - guid_stub[ j ] = g_admin_namelog[ i ]->guid[ j + 24 ]; - guid_stub[ j ] = '\0'; - for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES - && g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ ) - { - G_SanitiseString(g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) ); - if( strstr( n2, s2 ) ) - { - if( g_admin_namelog[ i ]->slot > -1 ) - ADMBP( "^3" ); - ADMBP( va( "%-2s (*%s) %15s ^7'%s^7'\n", - (g_admin_namelog[ i ]->slot > -1) ? - va( "%d", g_admin_namelog[ i ]->slot ) : "-", - guid_stub, - g_admin_namelog[ i ]->ip, - g_admin_namelog[ i ]->name[ j ] ) ); - } - } - } - ADMBP_end(); + ADMP( "^3setivo: ^7the argument must be either s, a or h\n" ); return qfalse; } - - G_admin_duration( ( seconds ) ? seconds : -1, - duration, sizeof( duration ) ); - if( ent && !admin_higher_guid( ent->client->pers.guid, - g_admin_namelog[ logmatch ]->guid ) ) + spot = G_Find( NULL, FOFS( classname ), cn ); + if( !spot ) { - - ADMP( "^3!ban: ^7sorry, but your intended victim has a higher admin" - " level than you\n" ); - return qfalse; + spot = G_Spawn(); + spot->classname = (char *)cn; } + spot->count = 1; - admin_create_ban( ent, - g_admin_namelog[ logmatch ]->name[ 0 ], - g_admin_namelog[ logmatch ]->guid, - g_admin_namelog[ logmatch ]->ip, - seconds, reason ); - - g_admin_namelog[ logmatch ]->banned = qtrue; + BG_GetClientViewOrigin( &ent->client->ps, spot->r.currentOrigin ); + VectorCopy( ent->client->ps.viewangles, spot->r.currentAngles ); - if( !g_admin.string[ 0 ] ) - ADMP( "^3!ban: ^7WARNING g_admin not set, not saving ban to a file\n" ); - else - admin_writeconfig(); - - if( g_admin_namelog[ logmatch ]->slot == -1 ) - { - // client is already disconnected so stop here - AP( va( "print \"^3!ban:^7 %s^7 has been banned by %s^7 " - "duration: %s, reason: %s\n\"", - g_admin_namelog[ logmatch ]->name[ 0 ], - ( ent ) ? G_admin_adminPrintName( ent ) : "console", - duration, - ( *reason ) ? reason : "banned by admin" ) ); - return qtrue; - } - - trap_SendServerCommand( g_admin_namelog[ logmatch ]->slot, - va( "disconnect \"You have been banned.\n" - "admin:\n%s^7\nduration:\n%s\nreason:\n%s\n%s\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console", - duration, - ( *reason ) ? reason : "banned by admin", notice ) ); - - trap_DropClient( g_admin_namelog[ logmatch ]->slot, - va( "banned by %s^7, duration: %s, reason: %s", - ( ent ) ? G_admin_adminPrintName( ent ) : "console", - duration, - ( *reason ) ? reason : "banned by admin" ) ); + ADMP( "^3setivo: ^7intermission view override positioned\n" ); return qtrue; } -// If true then don't let the player join a team, use the chat or run commands. -qboolean G_admin_is_restricted(gentity_t *ent, qboolean sendMessage) +qboolean G_admin_ban( gentity_t *ent ) { - schachtmeisterJudgement_t *j = NULL; - int i; - - // Never restrict admins or whitelisted players. - if (G_admin_permission(ent, ADMF_NOAUTOBAHN) || - G_admin_permission(ent, ADMF_IMMUNITY)) - return qfalse; - - // Find the relevant namelog. - // FIXME: this shouldn't require looping over *all* namelogs. - for (i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[i]; i++) { - if (g_admin_namelog[i]->slot == ent - g_entities) { - j = &g_admin_namelog[i]->smj; - break; - } - } - - // A missing namelog shouldn't happen. - if (!j) - return qfalse; - - // Restrictions concern only unrated players. - if (j->ratingTime) - return qfalse; - - // Don't wait forever, allow up to 15 seconds. - if (level.time - j->queryTime >= 15000) - return qfalse; - - if (sendMessage) - trap_SendServerCommand(ent - g_entities, "print \"Please wait a moment before doing anything.\n\""); - - return qtrue; -} - -static void admin_autobahn(gentity_t *ent, int rating) -{ - // Allow per-GUID exceptions and never autoban admins. - if (G_admin_permission(ent, ADMF_NOAUTOBAHN) || - G_admin_permission(ent, ADMF_IMMUNITY)) - return; - - // Don't do anything if the rating is clear. - if (rating >= g_schachtmeisterClearThreshold.integer) - return; - - // Ban only if the rating is low enough. - if (rating > g_schachtmeisterAutobahnThreshold.integer) { - G_AdminsPrintf("%s^7 (#%d) has rating %d\n", - ent->client->pers.netname, ent - g_entities, - rating); - return; - } - - G_LogAutobahn(ent, NULL, rating, qfalse); - - trap_SendServerCommand(ent - g_entities, va("disconnect \"%s\"\n", - g_schachtmeisterAutobahnMessage.string)); - trap_DropClient(ent - g_entities, "dropped by the Autobahn"); -} - -void G_admin_IPA_judgement( const char *ipa, int rating, const char *comment ) -{ - int i; - for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) - { - if( !strcmp( g_admin_namelog[ i ]->ip, ipa ) ) - { - schachtmeisterJudgement_t *j = &g_admin_namelog[ i ]->smj; - - j->ratingTime = level.time; - j->queryTime = 0; - j->dispatchTime = 0; - - j->rating = rating; - - if( j->comment ) - G_Free( j->comment ); - - if( comment ) - { - j->comment = G_Alloc( strlen( comment ) + 1 ); - strcpy( j->comment, comment ); - } - else - j->comment = NULL; - - if( g_admin_namelog[ i ]->slot != -1 ) - admin_autobahn( g_entities + g_admin_namelog[ i ]->slot, j->rating ); - } - } -} - -qboolean G_admin_adjustban( gentity_t *ent, int skiparg ) -{ - int bnum; - int length; - int expires; - int time = trap_RealTime( NULL ); - char duration[ 32 ] = {""}; - char *reason; - char bs[ 5 ]; + int seconds; + char search[ MAX_NAME_LENGTH ]; char secs[ MAX_TOKEN_CHARS ]; - char mode = '\0'; + char *reason; + char duration[ MAX_DURATION_LENGTH ]; + int i; + addr_t ip; + qboolean ipmatch = qfalse; + namelog_t *match = NULL; + qboolean cidr = qfalse; - if( G_SayArgc() < 3 + skiparg ) + if( trap_Argc() < 2 ) { - ADMP( "^3!adjustban: ^7usage: !adjustban [ban#] [time] [reason]\n" ); + ADMP( "^3ban: ^7usage: ban [name|slot|IP(/mask)] [duration] [reason]\n" ); return qfalse; } - G_SayArgv( 1 + skiparg, bs, sizeof( bs ) ); - bnum = atoi( bs ); - if( bnum < 1 || bnum > MAX_ADMIN_BANS || !g_admin_bans[ bnum - 1] ) + trap_Argv( 1, search, sizeof( search ) ); + trap_Argv( 2, secs, sizeof( secs ) ); + + seconds = G_admin_parse_time( secs ); + if( seconds <= 0 ) { - ADMP( "^3!adjustban: ^7invalid ban#\n" ); - return qfalse; + seconds = 0; + reason = ConcatArgs( 2 ); } - if( g_admin_bans[ bnum - 1 ]->expires == 0 && - !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) ) + else { - ADMP( "^3!adjustban: ^7you cannot modify permanent bans\n" ); - return qfalse; + reason = ConcatArgs( 3 ); } - if( g_admin_bans[ bnum - 1 ]->bannerlevel > G_admin_level( ent ) ) + if( !*reason && !G_admin_permission( ent, ADMF_UNACCOUNTABLE ) ) { - ADMP( "^3!adjustban: ^7you cannot modify Bans made by admins higher than you\n" ); + ADMP( "^3ban: ^7you must specify a reason\n" ); return qfalse; } - if( g_adminMaxBan.integer && - !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) && - g_admin_bans[ bnum - 1 ]->expires - time > G_admin_parse_time( g_adminMaxBan.string ) ) + if( !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) ) { - ADMP( va( "^3!adjustban: ^7your admin level cannot modify bans longer than %s\n", - g_adminMaxBan.string ) ); - return qfalse; - } - G_SayArgv( 2 + skiparg, secs, sizeof( secs ) ); - if( secs[ 0 ] == '+' || secs[ 0 ] == '-' ) - mode = secs[ 0 ]; - length = G_admin_parse_time( &secs[ mode ? 1 : 0 ] ); - if( length < 0 ) - skiparg--; - else - { - if( length ) + int maximum = MAX( 1, G_admin_parse_time( g_adminMaxBan.string ) ); + if( seconds == 0 || seconds > maximum ) { - if( g_admin_bans[ bnum - 1 ]->expires == 0 && mode ) - { - ADMP( "^3!adjustban: ^7new duration must be explicit\n" ); - return qfalse; - } - if( mode == '+' ) - expires = g_admin_bans[ bnum - 1 ]->expires + length; - else if( mode == '-' ) - expires = g_admin_bans[ bnum - 1 ]->expires - length; - else - expires = time + length; - if( expires <= time ) - { - ADMP( "^3!adjustban: ^7ban time must be positive\n" ); - return qfalse; - } - if( g_adminMaxBan.integer && - !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) && - expires - time > G_admin_parse_time( g_adminMaxBan.string ) ) - { - ADMP( va( "^3!adjustban: ^7ban length is limited to %s for your admin level\n", - g_adminMaxBan.string ) ); - length = G_admin_parse_time( g_adminMaxBan.string ); - expires = time + length; - } + ADMP( "^3ban: ^7you may not issue permanent bans\n" ); + seconds = maximum; } - else if( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) ) - expires = 0; + } + + if( G_AddressParse( search, &ip ) ) + { + int max = ip.type == IPv4 ? 32 : 128; + int min; + + cidr = G_admin_permission( ent, ADMF_CAN_IP_BAN ); + if( cidr ) + min = ent ? max / 2 : 1; else + min = max; + + if( ip.mask < min || ip.mask > max ) { - ADMP( "^3!adjustban: ^7ban time must be positive\n" ); + ADMP( va( "^3ban: ^7invalid netmask (%d is not in the range %d-%d)\n", + ip.mask, min, max ) ); return qfalse; } + ipmatch = qtrue; - g_admin_bans[ bnum - 1 ]->expires = expires; - G_admin_duration( ( expires ) ? expires - time : -1, - duration, sizeof( duration ) ); - } - reason = G_SayConcatArgs( 3 + skiparg ); - if( *reason ) - Q_strncpyz( g_admin_bans[ bnum - 1 ]->reason, reason, - sizeof( g_admin_bans[ bnum - 1 ]->reason ) ); - AP( va( "print \"^3!adjustban: ^7ban #%d for %s^7 has been updated by %s^7 " - "%s%s%s%s%s\n\"", - bnum, - g_admin_bans[ bnum - 1 ]->name, - ( ent ) ? G_admin_adminPrintName( ent ) : "console", - ( length >= 0 ) ? "duration: " : "", - duration, - ( length >= 0 && *reason ) ? ", " : "", - ( *reason ) ? "reason: " : "", - reason ) ); - if( ent ) { - Q_strncpyz( g_admin_bans[ bnum - 1 ]->banner, G_admin_get_adminname( ent ), - sizeof( g_admin_bans[ bnum - 1 ]->banner ) ); - g_admin_bans[ bnum - 1 ]->bannerlevel = G_admin_level( ent ); - } - - if( g_admin.string[ 0 ] ) - admin_writeconfig(); - return qtrue; -} - + for( match = level.namelogs; match; match = match->next ) + { + // skip players in the namelog who have already been banned + if( match->banned ) + continue; -qboolean G_admin_subnetban( gentity_t *ent, int skiparg ) -{ - int bnum; - int mask; - unsigned int IPRlow = 0, IPRhigh = 0; - char cIPRlow[ 32 ], cIPRhigh[ 32 ]; - char bs[ 5 ]; - char strmask[ 5 ]; - char exl[2]; - int k, IP[5]; - - if( G_SayArgc() < 3 + skiparg ) - { - ADMP( "^3!subnetban: ^7usage: !subnetban [ban#] [mask]\n" ); - return qfalse; - } - G_SayArgv( 1 + skiparg, bs, sizeof( bs ) ); - bnum = atoi( bs ); - if( bnum < 1 || bnum > MAX_ADMIN_BANS || !g_admin_bans[ bnum - 1] ) - { - ADMP( "^3!subnetban: ^7invalid ban#\n" ); - return qfalse; - } - if( g_admin_bans[ bnum - 1 ]->bannerlevel > G_admin_level( ent ) ) - { - ADMP( "^3!subnetban: ^7you cannot subnetban Bans on bans made by admins higher than you\n" ); - return qfalse; - } + for( i = 0; i < MAX_NAMELOG_ADDRS && match->ip[ i ].str[ 0 ]; i++ ) + { + if( G_AddressCompare( &ip, &match->ip[ i ] ) ) + break; + } + if( i < MAX_NAMELOG_ADDRS && match->ip[ i ].str[ 0 ] ) + break; + } - G_SayArgv( 2 + skiparg, strmask, sizeof( strmask ) ); - mask = atoi( strmask ); - - if( mask >= 0 && mask <= 32) - { - G_SayArgv( 3 + skiparg, exl, sizeof( exl ) ); - if( mask >= 0 && mask < 16 ) + if( !match ) { - if( ent ) + if( cidr ) { - ADMP( "^3!subnetban: ^7Only console may ban such a large network. Regular admins may only ban >=16.\n" ); - return qfalse; + ADMP( "^3ban: ^7no player found by that IP address; banning anyway\n" ); } - if( strcmp(exl, "!") ) + else { - ADMP( "^3!subnetban: ^1WARNING:^7 you are about to ban a large network, use !subnetban [ban] [mask] ! to force^7\n" ); + ADMP( "^3ban: ^7no player found by that IP address\n" ); return qfalse; } } - - memset( IP, 0, sizeof( IP ) ); - sscanf(g_admin_bans[ bnum - 1 ]->ip, "%d.%d.%d.%d/%d", &IP[4], &IP[3], &IP[2], &IP[1], &IP[0]); - for(k = 4; k >= 1; k--) - { - if( IP[k] ) - IPRlow |= IP[k] << 8*(k-1); - } - IPRhigh = IPRlow; - if( mask == 32 ) - { - Q_strncpyz( - g_admin_bans[ bnum - 1 ]->ip, - va("%i.%i.%i.%i", IP[4], IP[3], IP[2], IP[1]), - sizeof( g_admin_bans[ bnum - 1 ]->ip ) - ); - } - else - { - Q_strncpyz( - g_admin_bans[ bnum - 1 ]->ip, - va("%i.%i.%i.%i/%i", IP[4], IP[3], IP[2], IP[1], mask ), - sizeof( g_admin_bans[ bnum - 1 ]->ip ) - ); - IPRlow &= ~((1 << (32-mask)) - 1); - IPRhigh |= ((1 << (32-mask)) - 1); - } } - else + else if( !( match = G_NamelogFromString( ent, search ) ) || match->banned ) { - ADMP( "^3!subnetban: ^7mask is out of range, please use 0-32 inclusive\n" ); + ADMP( "^3ban: ^7no match\n" ); return qfalse; } - if( mask > 0 ) - { - Q_strncpyz( - cIPRlow, - va("%u.%u.%u.%u", (IPRlow & (255 << 24)) >> 24, (IPRlow & (255 << 16)) >> 16, (IPRlow & (255 << 8)) >> 8, IPRlow & 255), - sizeof( cIPRlow ) - ); - Q_strncpyz( - cIPRhigh, - va("%u.%u.%u.%u", (IPRhigh & (255 << 24)) >> 24, (IPRhigh & (255 << 16)) >> 16, (IPRhigh & (255 << 8)) >> 8, IPRhigh & 255), - sizeof( cIPRhigh ) - ); - } - else - { - Q_strncpyz( cIPRlow, "0.0.0.0", sizeof( cIPRlow ) ); - Q_strncpyz( cIPRhigh, "255.255.255.255", sizeof( cIPRhigh ) ); - - } - - AP( va( "print \"^3!subnetban: ^7ban #%d for %s^7 has been updated by %s^7 " - "%s (%s - %s)\n\"", - bnum, - g_admin_bans[ bnum - 1 ]->name, - ( ent ) ? G_admin_adminPrintName( ent ) : "console", - g_admin_bans[ bnum - 1 ]->ip, - cIPRlow, - cIPRhigh) ); - if( ent ) - Q_strncpyz( g_admin_bans[ bnum - 1 ]->banner, G_admin_get_adminname( ent ), - sizeof( g_admin_bans[ bnum - 1 ]->banner ) ); - if( g_admin.string[ 0 ] ) - admin_writeconfig(); - return qtrue; -} - -qboolean G_admin_suspendban( gentity_t *ent, int skiparg ) -{ - int bnum; - int length; - int timenow = 0; - int expires = 0; - char *arg; - char bs[ 5 ]; - char duration[ 32 ]; - qtime_t qt; - if( G_SayArgc() < 3 + skiparg ) - { - ADMP( "^3!suspendban: ^7usage: !suspendban [ban #] [length]\n" ); - return qfalse; - } - G_SayArgv( 1 + skiparg, bs, sizeof( bs ) ); - bnum = atoi( bs ); - if( bnum < 1 || !g_admin_bans[ bnum - 1] ) + if( ent && match && !admin_higher_guid( ent->client->pers.guid, match->guid ) ) { - ADMP( "^3!suspendban: ^7invalid ban #\n" ); + + ADMP( "^3ban: ^7sorry, but your intended victim has a higher admin" + " level than you\n" ); return qfalse; } - if( g_admin_bans[ bnum - 1 ]->bannerlevel > G_admin_level( ent ) ) + if( match && match->slot > -1 && level.clients[ match->slot ].pers.localClient ) { - ADMP( "^3!suspendban: ^7you cannot suspend Bans made by admins higher than you\n" ); + ADMP( "^3ban: ^7disconnecting the host would end the game\n" ); return qfalse; } - arg = G_SayConcatArgs( 2 + skiparg ); - timenow = trap_RealTime( &qt ); - length = G_admin_parse_time( arg ); - - if( length < 0 ) - { - ADMP( "^3!suspendban: ^7invalid length\n" ); - return qfalse; - } - if( length > MAX_ADMIN_BANSUSPEND_DAYS * 24 * 60 * 60 ) - { - length = MAX_ADMIN_BANSUSPEND_DAYS * 24 * 60 * 60; - ADMP( va( "^3!suspendban: ^7maximum ban suspension is %d days\n", - MAX_ADMIN_BANSUSPEND_DAYS ) ); - } else if( g_admin_bans[ bnum - 1 ]->expires > 0 && length + timenow > g_admin_bans[ bnum - 1 ]->expires ) { - length = g_admin_bans[ bnum - 1 ]->expires - timenow; - G_admin_duration( length , duration, sizeof( duration ) ); - ADMP( va( "^3!suspendban: ^7Suspension Duration trimmed to Ban duration: %s\n", - duration ) ); - } + G_admin_duration( ( seconds ) ? seconds : -1, + duration, sizeof( duration ) ); - if ( length > 0 ) - { - expires = timenow + length; - } - if( g_admin_bans[ bnum - 1 ]->suspend == expires ) - { - ADMP( "^3!suspendban: ^7no change\n" ); - return qfalse; - } + AP( va( "print \"^3ban:^7 %s^7 has been banned by %s^7 " + "duration: %s, reason: %s\n\"", + match ? match->name[ match->nameOffset ] : "an IP address", + ( ent ) ? ent->client->pers.netname : "console", + duration, + ( *reason ) ? reason : "banned by admin" ) ); - g_admin_bans[ bnum - 1 ]->suspend = expires; - if ( length > 0 ) + admin_log( va( "%d (%s) \"%s" S_COLOR_WHITE "\": \"%s" S_COLOR_WHITE "\"", + seconds, + match ? match->guid : "", + match ? match->name[ match->nameOffset ] : "IP ban", + reason ) ); + if( ipmatch ) { - G_admin_duration( length , duration, sizeof( duration ) ); - AP( va( "print \"^3!suspendban: ^7ban #%d suspended for %s\n\"", - bnum, duration ) ); + if( match ) + { + admin_create_ban( ent, + match->slot == -1 ? + match->name[ match->nameOffset ] : + level.clients[ match->slot ].pers.netname, + match->guidless ? "" : match->guid, + &ip, + seconds, reason ); + } + else + { + admin_create_ban( ent, "IP ban", "", &ip, seconds, reason ); + } + admin_log( va( "[%s]", ip.str ) ); } else { - AP( va( "print \"^3!suspendban: ^7ban #%d suspension removed\n\"", - bnum ) ); + // ban all IP addresses used by this player + for( i = 0; i < MAX_NAMELOG_ADDRS && match->ip[ i ].str[ 0 ]; i++ ) + { + admin_create_ban( ent, + match->slot == -1 ? + match->name[ match->nameOffset ] : + level.clients[ match->slot ].pers.netname, + match->guidless ? "" : match->guid, + &match->ip[ i ], + seconds, reason ); + admin_log( va( "[%s]", match->ip[ i ].str ) ); + } } + if( match ) + match->banned = qtrue; + if( !g_admin.string[ 0 ] ) - ADMP( "^3!adjustban: ^7WARNING g_admin not set, not saving ban to a file\n" ); + ADMP( "^3ban: ^7WARNING g_admin not set, not saving ban to a file\n" ); else admin_writeconfig(); + return qtrue; } -qboolean G_admin_unban( gentity_t *ent, int skiparg ) +qboolean G_admin_unban( gentity_t *ent ) { int bnum; + int time = trap_RealTime( NULL ); char bs[ 5 ]; - int t; + int i; + g_admin_ban_t *ban; - t = trap_RealTime( NULL ); - if( G_SayArgc() < 2 + skiparg ) + if( trap_Argc() < 2 ) { - ADMP( "^3!unban: ^7usage: !unban [ban#]\n" ); + ADMP( "^3unban: ^7usage: unban [ban#]\n" ); return qfalse; } - G_SayArgv( 1 + skiparg, bs, sizeof( bs ) ); + trap_Argv( 1, bs, sizeof( bs ) ); bnum = atoi( bs ); - if( bnum < 1 || bnum > MAX_ADMIN_BANS || !g_admin_bans[ bnum - 1 ] ) - { - ADMP( "^3!unban: ^7invalid ban#\n" ); - return qfalse; - } - if( g_admin_bans[ bnum - 1 ]->expires == 0 && - !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) ) + for( ban = g_admin_bans, i = 1; ban && i < bnum; ban = ban->next, i++ ) + ; + if( i != bnum || !ban ) { - ADMP( "^3!unban: ^7you cannot remove permanent bans\n" ); + ADMP( "^3unban: ^7invalid ban#\n" ); return qfalse; } - if( g_admin_bans[ bnum - 1 ]->bannerlevel > G_admin_level( ent ) ) + if( ban->expires > 0 && ban->expires - time <= 0 ) { - ADMP( "^3!unban: ^7you cannot Remove Bans made by admins higher than you\n" ); + ADMP( "^3unban: ^7ban already expired\n" ); return qfalse; } - if( g_adminMaxBan.integer && - !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) && - g_admin_bans[ bnum - 1 ]->expires - t > G_admin_parse_time( g_adminMaxBan.string ) ) + if( !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) && + ( ban->expires == 0 || ( ban->expires - time > MAX( 1, + G_admin_parse_time( g_adminMaxBan.string ) ) ) ) ) { - ADMP( va( "^3!unban: ^7your admin level cannot remove bans longer than %s\n", - g_adminMaxBan.string ) ); + ADMP( "^3unban: ^7you cannot remove permanent bans\n" ); return qfalse; } - g_admin_bans[ bnum -1 ]->expires = t; - AP( va( "print \"^3!unban: ^7ban #%d for %s^7 has been removed by %s\n\"", + admin_log( va( "%d (%s) \"%s" S_COLOR_WHITE "\": \"%s" S_COLOR_WHITE "\": [%s]", + ban->expires ? ban->expires - time : 0, ban->guid, ban->name, ban->reason, + ban->ip.str ) ); + AP( va( "print \"^3unban: ^7ban #%d for %s^7 has been removed by %s\n\"", bnum, - g_admin_bans[ bnum - 1 ]->name, - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - if( g_admin.string[ 0 ] ) - admin_writeconfig(); + ban->name, + ( ent ) ? ent->client->pers.netname : "console" ) ); + ban->expires = time; + admin_writeconfig(); return qtrue; } -qboolean G_admin_putteam( gentity_t *ent, int skiparg ) +qboolean G_admin_addlayout( gentity_t *ent ) { - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ], team[ 7 ], err[ MAX_STRING_CHARS ]; - gentity_t *vic; - pTeam_t teamnum = PTE_NONE; - char teamdesc[ 32 ] = {"spectators"}; - char secs[ 7 ]; - int seconds = 0; - qboolean useDuration = qfalse; - - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - G_SayArgv( 2 + skiparg, team, sizeof( team ) ); - if( G_SayArgc() < 3 + skiparg ) - { - ADMP( "^3!putteam: ^7usage: !putteam [name] [h|a|s] (duration)\n" ); - return qfalse; - } - - if( G_ClientNumbersFromString( name, pids ) != 1 ) - { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!putteam: ^7%s\n", err ) ); - return qfalse; - } - if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) - { - ADMP( "^3!putteam: ^7sorry, but your intended victim has a higher " - " admin level than you\n" ); - return qfalse; - } - vic = &g_entities[ pids[ 0 ] ]; + char layout[ MAX_QPATH ]; - if ( vic->client->sess.invisible == qtrue ) + if( trap_Argc( ) != 2 ) { - ADMP( "^3!putteam: ^7invisible players cannot join a team\n" ); + ADMP( "^3addlayout: ^7usage: addlayout <layoutelements>\n" ); return qfalse; } - switch( team[ 0 ] ) - { - case 'a': - teamnum = PTE_ALIENS; - Q_strncpyz( teamdesc, "aliens", sizeof( teamdesc ) ); - break; - case 'h': - teamnum = PTE_HUMANS; - Q_strncpyz( teamdesc, "humans", sizeof( teamdesc ) ); - break; - case 's': - teamnum = PTE_NONE; - break; - default: - ADMP( va( "^3!putteam: ^7unknown team %c\n", team[ 0 ] ) ); - return qfalse; - } - //duration code - if( G_SayArgc() > 3 + skiparg ) { - //can only lock players in spectator - if ( teamnum != PTE_NONE ) - { - ADMP( "^3!putteam: ^7You can only lock a player into the spectators team\n" ); - return qfalse; - } - G_SayArgv( 3 + skiparg, secs, sizeof( secs ) ); - seconds = G_admin_parse_time( secs ); - useDuration = qtrue; - } + trap_Argv( 1, layout, sizeof( layout ) ); - if( vic->client->pers.teamSelection == teamnum && teamnum != PTE_NONE ) - { - ADMP( va( "^3!putteam: ^7%s ^7is already on the %s team\n", vic->client->pers.netname, teamdesc ) ); - return qfalse; - } + G_LayoutLoad( layout ); - if( useDuration == qtrue && seconds > 0 ) { - vic->client->pers.specExpires = level.time + ( seconds * 1000 ); - } - G_ChangeTeam( vic, teamnum ); - - AP( va( "print \"^3!putteam: ^7%s^7 put %s^7 on to the %s team%s\n\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console", - vic->client->pers.netname, teamdesc, - ( seconds ) ? va( " for %i seconds", seconds ) : "" ) ); + AP( va( "print \"^3addlayout: ^7some layout elements have been placed by %s\n\"", + ent ? ent->client->pers.netname : "console" ) ); return qtrue; } -qboolean G_admin_seen(gentity_t *ent, int skiparg ) +qboolean G_admin_adjustban( gentity_t *ent ) { - char name[ MAX_NAME_LENGTH ]; - char search[ MAX_NAME_LENGTH ]; - char sduration[ 32 ]; - qboolean numeric = qtrue; - int i, j; - int id = -1; - int count = 0; - int t; - qtime_t qt; - gentity_t *vic; - qboolean ison; + int bnum; + int length, maximum; + int expires; + int time = trap_RealTime( NULL ); + char duration[ MAX_DURATION_LENGTH ] = {""}; + char *reason; + char bs[ 5 ]; + char secs[ MAX_TOKEN_CHARS ]; + char mode = '\0'; + g_admin_ban_t *ban; + int mask = 0; + int i; + int skiparg = 0; - if( G_SayArgc() < 2 + skiparg ) + if( trap_Argc() < 3 ) { - ADMP( "^3!seen: ^7usage: !seen [name|admin#]\n" ); + ADMP( "^3adjustban: ^7usage: adjustban [ban#] [/mask] [duration] [reason]" + "\n" ); return qfalse; } - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - G_SanitiseString( name, search, sizeof( search ) ); - for( i = 0; i < sizeof( search ) && search[ i ] ; i++ ) + trap_Argv( 1, bs, sizeof( bs ) ); + bnum = atoi( bs ); + for( ban = g_admin_bans, i = 1; ban && i < bnum; ban = ban->next, i++ ); + if( i != bnum || !ban ) { - if( search[ i ] < '0' || search[ i ] > '9' ) - { - numeric = qfalse; - break; - } + ADMP( "^3adjustban: ^7invalid ban#\n" ); + return qfalse; } - - if( numeric ) + maximum = MAX( 1, G_admin_parse_time( g_adminMaxBan.string ) ); + if( !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) && + ( ban->expires == 0 || ban->expires - time > maximum ) ) { - id = atoi( name ); - search[ 0 ] = '\0'; + ADMP( "^3adjustban: ^7you cannot modify permanent bans\n" ); + return qfalse; } - - ADMBP_begin(); - t = trap_RealTime( &qt ); - - for( i = 0; i < level.maxclients && count < 10; i ++ ) + trap_Argv( 2, secs, sizeof( secs ) ); + if( secs[ 0 ] == '/' ) { - vic = &g_entities[ i ]; + int max = ban->ip.type == IPv6 ? 128 : 32; + int min; - if( !vic->client || vic->client->pers.connected != CON_CONNECTED ) - continue; - - G_SanitiseString( vic->client->pers.netname, name, sizeof( name ) ); + if( G_admin_permission( ent, ADMF_CAN_IP_BAN ) ) + min = ent ? max / 2 : 1; + else + min = max; - if( i == id || (search[ 0 ] && strstr( name, search ) ) ) + mask = atoi( secs + 1 ); + if( mask < min || mask > max ) { - if ( vic->client->sess.invisible == qfalse ) - { - ADMBP( va( "^3%4d ^7%s^7 is currently playing\n", i, vic->client->pers.netname ) ); - count++; - } + ADMP( va( "^3adjustban: ^7invalid netmask (%d is not in range %d-%d)\n", + mask, min, max ) ); + return qfalse; } + trap_Argv( 3 + skiparg++, secs, sizeof( secs ) ); } - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] && count < 10; i++ ) + if( secs[ 0 ] == '+' || secs[ 0 ] == '-' ) + mode = secs[ 0 ]; + length = G_admin_parse_time( &secs[ mode ? 1 : 0 ] ); + if( length < 0 ) + skiparg--; + else { - G_SanitiseString( g_admin_admins[ i ]->name, name, sizeof( name ) ); - if( i + MAX_CLIENTS == id || (search[ 0 ] && strstr( name, search ) ) ) + if( length ) { - ison = qfalse; - for( j = 0; j < level.maxclients; j++ ) + if( ban->expires == 0 && mode ) { - vic = &g_entities[ j ]; - if( !vic->client || vic->client->pers.connected != CON_CONNECTED ) - continue; - G_SanitiseString( vic->client->pers.netname, name, sizeof( name ) ); - if( !Q_stricmp( vic->client->pers.guid, g_admin_admins[ i ]->guid ) - && strstr( name, search ) ) - { - if ( vic->client->sess.invisible == qfalse ) - { - ison = qtrue; - break; - } - } - } - - if( ison ) - { - if( id == -1 ) - continue; - ADMBP( va( "^3%4d ^7%s^7 is currently playing\n", - i + MAX_CLIENTS, g_admin_admins[ i ]->name ) ); + ADMP( "^3adjustban: ^7new duration must be explicit\n" ); + return qfalse; } + if( mode == '+' ) + expires = ban->expires + length; + else if( mode == '-' ) + expires = ban->expires - length; else + expires = time + length; + if( expires <= time ) { - G_admin_duration( t - g_admin_admins[ i ]->seen, - sduration, sizeof( sduration ) ); - ADMBP( va( "%4d %s^7 last seen %s%s\n", - i + MAX_CLIENTS, g_admin_admins[ i ]->name , - ( g_admin_admins[ i ]->seen ) ? sduration : "", - ( g_admin_admins[ i ]->seen ) ? " ago" : "time is unknown" ) ); + ADMP( "^3adjustban: ^7ban duration must be positive\n" ); + return qfalse; } - count++; - } - } - - if( search[ 0 ] ) - ADMBP( va( "^3!seen:^7 found %d player%s matching '%s'\n", - count, (count == 1) ? "" : "s", search ) ); - else if ( !count ) - ADMBP( "^3!seen:^7 no one connectd by that slot number\n" ); - - ADMBP_end(); - return qtrue; -} - -void G_admin_seen_update( char *guid ) -{ - int i; - - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ ) - { - if( !Q_stricmp( g_admin_admins[ i ]->guid, guid ) ) - { - qtime_t qt; - - g_admin_admins[ i ]->seen = trap_RealTime( &qt ); - return; } - } -} - -void G_admin_adminlog_cleanup( void ) -{ - int i; - - for( i = 0; i < MAX_ADMIN_ADMINLOGS && g_admin_adminlog[ i ]; i++ ) - { - G_Free( g_admin_adminlog[ i ] ); - g_admin_adminlog[ i ] = NULL; - } - - admin_adminlog_index = 0; -} - -void G_admin_adminlog_log( gentity_t *ent, char *command, char *args, int skiparg, qboolean success ) -{ - g_admin_adminlog_t *adminlog; - int previous; - int count = 1; - int i; - - if( !command ) - return; - - if( !Q_stricmp( command, "adminlog" ) || - !Q_stricmp( command, "admintest" ) || - !Q_stricmp( command, "help" ) || - !Q_stricmp( command, "info" ) || - !Q_stricmp( command, "listadmins" ) || - !Q_stricmp( command, "listplayers" ) || - !Q_stricmp( command, "namelog" ) || - !Q_stricmp( command, "showbans" ) || - !Q_stricmp( command, "seen" ) || - !Q_stricmp( command, "time" ) ) - return; - - previous = admin_adminlog_index - 1; - if( previous < 0 ) - previous = MAX_ADMIN_ADMINLOGS - 1; - - if( g_admin_adminlog[ previous ] ) - count = g_admin_adminlog[ previous ]->id + 1; - - if( g_admin_adminlog[ admin_adminlog_index ] ) - adminlog = g_admin_adminlog[ admin_adminlog_index ]; - else - adminlog = G_Alloc( sizeof( g_admin_adminlog_t ) ); - - memset( adminlog, 0, sizeof( *adminlog ) ); - adminlog->id = count; - adminlog->time = level.time - level.startTime; - adminlog->success = success; - Q_strncpyz( adminlog->command, command, sizeof( adminlog->command ) ); - - if ( args ) - Q_strncpyz( adminlog->args, args, sizeof( adminlog->args ) ); - else - Q_strncpyz( adminlog->args, G_SayConcatArgs( 1 + skiparg ), sizeof( adminlog->args ) ); - - if( ent ) - { - qboolean found = qfalse; - // real admin name - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) + else + length = expires = 0; + if( !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) && + ( length == 0 || length > maximum ) ) { - if( !Q_stricmp( g_admin_admins[ i ]->guid, ent->client->pers.guid ) ) - { - Q_strncpyz( adminlog->name, g_admin_admins[ i ]->name, sizeof( adminlog->name ) ); - found = qtrue; - break; - } + ADMP( "^3adjustban: ^7you may not issue permanent bans\n" ); + expires = time + maximum; } - if( !found ) - Q_strncpyz( adminlog->name, ent->client->pers.netname, sizeof( adminlog->name ) ); - adminlog->level = ent->client->pers.adminLevel; + ban->expires = expires; + G_admin_duration( ( expires ) ? expires - time : -1, duration, + sizeof( duration ) ); } - else + if( mask ) { - Q_strncpyz( adminlog->name, "console", sizeof( adminlog->name ) ); - adminlog->level = 10000; + char *p = strchr( ban->ip.str, '/' ); + if( !p ) + p = ban->ip.str + strlen( ban->ip.str ); + if( mask == ( ban->ip.type == IPv6 ? 128 : 32 ) ) + *p = '\0'; + else + Com_sprintf( p, sizeof( ban->ip.str ) - ( p - ban->ip.str ), "/%d", mask ); + ban->ip.mask = mask; } - - g_admin_adminlog[ admin_adminlog_index ] = adminlog; - admin_adminlog_index++; - if( admin_adminlog_index >= MAX_ADMIN_ADMINLOGS ) - admin_adminlog_index = 0; + reason = ConcatArgs( 3 + skiparg ); + if( *reason ) + Q_strncpyz( ban->reason, reason, sizeof( ban->reason ) ); + admin_log( va( "%d (%s) \"%s" S_COLOR_WHITE "\": \"%s" S_COLOR_WHITE "\": [%s]", + ban->expires ? ban->expires - time : 0, ban->guid, ban->name, ban->reason, + ban->ip.str ) ); + AP( va( "print \"^3adjustban: ^7ban #%d for %s^7 has been updated by %s^7 " + "%s%s%s%s%s%s\n\"", + bnum, + ban->name, + ( ent ) ? ent->client->pers.netname : "console", + ( mask ) ? + va( "netmask: /%d%s", mask, + ( length >= 0 || *reason ) ? ", " : "" ) : "", + ( length >= 0 ) ? "duration: " : "", + duration, + ( length >= 0 && *reason ) ? ", " : "", + ( *reason ) ? "reason: " : "", + reason ) ); + if( ent ) + Q_strncpyz( ban->banner, ent->client->pers.netname, sizeof( ban->banner ) ); + admin_writeconfig(); + return qtrue; } -qboolean G_admin_adminlog( gentity_t *ent, int skiparg ) +qboolean G_admin_putteam( gentity_t *ent ) { - g_admin_adminlog_t *results[ 10 ]; - int result_index = 0; - char *search_cmd = NULL; - char *search_name = NULL; - int index; - int skip = 0; - int skipped = 0; - int checked = 0; - char n1[ MAX_NAME_LENGTH ]; - char fmt_name[ 16 ]; - char argbuf[ 32 ]; - int name_length = 12; - int max_id = 0; - int i; - qboolean match; - - memset( results, 0, sizeof( results ) ); - - index = admin_adminlog_index; - for( i = 0; i < 10; i++ ) - { - int prev; - - prev = index - 1; - if( prev < 0 ) - prev = MAX_ADMIN_ADMINLOGS - 1; - if( !g_admin_adminlog[ prev ] ) - break; - if( g_admin_adminlog[ prev ]->id > max_id ) - max_id = g_admin_adminlog[ prev ]->id; - index = prev; - } - - if( G_SayArgc() > 1 + skiparg ) - { - G_SayArgv( 1 + skiparg, argbuf, sizeof( argbuf ) ); - if( ( *argbuf >= '0' && *argbuf <= '9' ) || *argbuf == '-' ) - { - int id; - - id = atoi( argbuf ); - if( id < 0 ) - id += ( max_id - 9 ); - else if( id <= max_id - MAX_ADMIN_ADMINLOGS ) - id = max_id - MAX_ADMIN_ADMINLOGS + 1; - - if( id + 9 >= max_id ) - id = max_id - 9; - if( id < 1 ) - id = 1; - for( i = 0; i < MAX_ADMIN_ADMINLOGS; i++ ) - { - if( g_admin_adminlog[i] && g_admin_adminlog[ i ]->id == id ) - { - index = i; - break; - } - } - } - else if ( *argbuf == '!' ) - { - search_cmd = argbuf + 1; - } - else - { - search_name = argbuf; - } - - if( G_SayArgc() > 2 + skiparg && ( search_cmd || search_name ) ) - { - char skipbuf[ 4 ]; - G_SayArgv( 2 + skiparg, skipbuf, sizeof( skipbuf ) ); - skip = atoi( skipbuf ); - } - } - - if( search_cmd || search_name ) - { - g_admin_adminlog_t *result_swap[ 10 ]; - - memset( result_swap, 0, sizeof( result_swap ) ); - - index = admin_adminlog_index - 1; - if( index < 0 ) - index = MAX_ADMIN_ADMINLOGS - 1; - - while( g_admin_adminlog[ index ] && - checked < MAX_ADMIN_ADMINLOGS && - result_index < 10 ) - { - match = qfalse; - if( search_cmd ) - { - if( !Q_stricmp( search_cmd, g_admin_adminlog[ index ]->command ) ) - match = qtrue; - } - if( search_name ) - { - G_SanitiseString( g_admin_adminlog[ index ]->name, n1, sizeof( n1 ) ); - if( strstr( n1, search_name ) ) - match = qtrue; - } - - if( match && skip > 0 ) - { - match = qfalse; - skip--; - skipped++; - } - if( match ) - { - result_swap[ result_index ] = g_admin_adminlog[ index ]; - result_index++; - } - - checked++; - index--; - if( index < 0 ) - index = MAX_ADMIN_ADMINLOGS - 1; - } - // search runs backwards, turn it around - for( i = 0; i < result_index; i++ ) - results[ i ] = result_swap[ result_index - i - 1 ]; - } - else - { - while( g_admin_adminlog[ index ] && result_index < 10 ) - { - results[ result_index ] = g_admin_adminlog[ index ]; - result_index++; - index++; - if( index >= MAX_ADMIN_ADMINLOGS ) - index = 0; - } - } + int pid; + char name[ MAX_NAME_LENGTH ], team[ sizeof( "spectators" ) ], + err[ MAX_STRING_CHARS ]; + gentity_t *vic; + team_t teamnum = TEAM_NONE; - for( i = 0; results[ i ] && i < 10; i++ ) + trap_Argv( 1, name, sizeof( name ) ); + trap_Argv( 2, team, sizeof( team ) ); + if( trap_Argc() < 3 ) { - int l; - - G_DecolorString( results[ i ]->name, n1 ); - l = strlen( n1 ); - if( l > name_length ) - name_length = l; - } - ADMBP_begin( ); - for( i = 0; results[ i ] && i < 10; i++ ) - { - char levelbuf[ 8 ]; - int t; - - t = results[ i ]->time / 1000; - G_DecolorString( results[ i ]->name, n1 ); - Com_sprintf( fmt_name, sizeof( fmt_name ), "%%%ds", - ( name_length + strlen( results[ i ]->name ) - strlen( n1 ) ) ); - Com_sprintf( n1, sizeof( n1 ), fmt_name, results[ i ]->name ); - Com_sprintf( levelbuf, sizeof( levelbuf ), "%2d", results[ i ]->level ); - ADMBP( va( "%s%3d %3d:%02d %2s ^7%s^7 %s!%s ^7%s^7\n", - ( results[ i ]->success ) ? "^7" : "^1", - results[ i ]->id, - t / 60, t % 60, - ( results[ i ]->level ) < 10000 ? levelbuf : " -", - n1, - ( results[ i ]->success ) ? "^3" : "^1", - results[ i ]->command, - results[ i ]->args ) ); - } - if( search_cmd || search_name ) - { - ADMBP( va( "^3!adminlog:^7 Showing %d matches for '%s^7'.", - result_index, - argbuf ) ); - if( checked < MAX_ADMIN_ADMINLOGS && g_admin_adminlog[ checked ] ) - ADMBP( va( " run '!adminlog %s^7 %d' to see more", - argbuf, - skipped + result_index ) ); - ADMBP( "\n" ); - } - else if ( results[ 0 ] ) - { - ADMBP( va( "^3!adminlog:^7 Showing %d - %d of %d.\n", - results[ 0 ]->id, - results[ result_index - 1 ]->id, - max_id ) ); - } - else - { - ADMBP( "^3!adminlog:^7 log is empty.\n" ); + ADMP( "^3putteam: ^7usage: putteam [name] [h|a|s]\n" ); + return qfalse; } - ADMBP_end( ); - return qtrue; -} - -qboolean G_admin_map( gentity_t *ent, int skiparg ) -{ - char map[ MAX_QPATH ]; - char layout[ MAX_QPATH ] = { "" }; - - if( G_SayArgc( ) < 2 + skiparg ) + if( ( pid = G_ClientNumberFromString( name, err, sizeof( err ) ) ) == -1 ) { - ADMP( "^3!map: ^7usage: !map [map] (layout)\n" ); + ADMP( va( "^3putteam: ^7%s", err ) ); return qfalse; } - - G_SayArgv( skiparg + 1, map, sizeof( map ) ); - - if( !trap_FS_FOpenFile( va( "maps/%s.bsp", map ), NULL, FS_READ ) ) + vic = &g_entities[ pid ]; + if( !admin_higher( ent, vic ) ) { - ADMP( va( "^3!map: ^7invalid map name '%s'\n", map ) ); + ADMP( "^3putteam: ^7sorry, but your intended victim has a higher " + " admin level than you\n" ); return qfalse; } - - if( G_SayArgc( ) > 2 + skiparg ) + teamnum = G_TeamFromString( team ); + if( teamnum == NUM_TEAMS ) { - G_SayArgv( skiparg + 2, layout, sizeof( layout ) ); - if( !Q_stricmp( layout, "*BUILTIN*" ) || - trap_FS_FOpenFile( va( "layouts/%s/%s.dat", map, layout ), - NULL, FS_READ ) > 0 ) - { - trap_Cvar_Set( "g_layouts", layout ); - } - else - { - ADMP( va( "^3!map: ^7invalid layout name '%s'\n", layout ) ); - return qfalse; - } + ADMP( va( "^3putteam: ^7unknown team %s\n", team ) ); + return qfalse; } + if( vic->client->pers.teamSelection == teamnum ) + return qfalse; + admin_log( va( "%d (%s) \"%s" S_COLOR_WHITE "\"", pid, vic->client->pers.guid, + vic->client->pers.netname ) ); + G_ChangeTeam( vic, teamnum ); - trap_SendConsoleCommand( EXEC_APPEND, va( "map %s", map ) ); - level.restarted = qtrue; - AP( va( "print \"^3!map: ^7map '%s' started by %s^7 %s\n\"", map, - ( ent ) ? G_admin_adminPrintName( ent ) : "console", - ( layout[ 0 ] ) ? va( "(forcing layout '%s')", layout ) : "" ) ); - G_admin_maplog_result( "M" ); + AP( va( "print \"^3putteam: ^7%s^7 put %s^7 on to the %s team\n\"", + ( ent ) ? ent->client->pers.netname : "console", + vic->client->pers.netname, BG_TeamName( teamnum ) ) ); return qtrue; } -qboolean G_admin_devmap( gentity_t *ent, int skiparg ) +qboolean G_admin_changemap( gentity_t *ent ) { char map[ MAX_QPATH ]; char layout[ MAX_QPATH ] = { "" }; - if( G_SayArgc( ) < 2 + skiparg ) + if( trap_Argc( ) < 2 ) { - ADMP( "^3!devmap: ^7usage: !devmap [map] (layout)\n" ); + ADMP( "^3changemap: ^7usage: changemap [map] (layout)\n" ); return qfalse; } - G_SayArgv( skiparg + 1, map, sizeof( map ) ); + trap_Argv( 1, map, sizeof( map ) ); - if( !trap_FS_FOpenFile( va( "maps/%s.bsp", map ), NULL, FS_READ ) ) + if( !G_MapExists( map ) ) { - ADMP( va( "^3!devmap: ^7invalid map name '%s'\n", map ) ); + ADMP( va( "^3changemap: ^7invalid map name '%s'\n", map ) ); return qfalse; } - if( G_SayArgc( ) > 2 + skiparg ) + if( trap_Argc( ) > 2 ) { - G_SayArgv( skiparg + 2, layout, sizeof( layout ) ); - if( !Q_stricmp( layout, "*BUILTIN*" ) || - trap_FS_FOpenFile( va( "layouts/%s/%s.dat", map, layout ), - NULL, FS_READ ) > 0 ) + trap_Argv( 2, layout, sizeof( layout ) ); + if( G_LayoutExists( map, layout ) ) { - trap_Cvar_Set( "g_layouts", layout ); + trap_Cvar_Set( "g_nextLayout", layout ); } else { - ADMP( va( "^3!devmap: ^7invalid layout name '%s'\n", layout ) ); + ADMP( va( "^3changemap: ^7invalid layout name '%s'\n", layout ) ); return qfalse; } } + admin_log( map ); + admin_log( layout ); - trap_SendConsoleCommand( EXEC_APPEND, va( "devmap %s", map ) ); + G_MapConfigs( map ); + trap_SendConsoleCommand( EXEC_APPEND, va( "%smap \"%s\"\n", + ( g_cheats.integer ? "dev" : "" ), map ) ); level.restarted = qtrue; - AP( va( "print \"^3!devmap: ^7map '%s' started by %s^7 with cheats %s\n\"", map, - ( ent ) ? G_admin_adminPrintName( ent ) : "console", + AP( va( "print \"^3changemap: ^7map '%s' started by %s^7 %s\n\"", map, + ( ent ) ? ent->client->pers.netname : "console", ( layout[ 0 ] ) ? va( "(forcing layout '%s')", layout ) : "" ) ); - G_admin_maplog_result( "D" ); return qtrue; } -void G_admin_maplog_update( void ) +qboolean G_admin_mute( gentity_t *ent ) { - char map[ 64 ]; - char maplog[ MAX_CVAR_VALUE_STRING ]; - char *ptr; - int count = 0; - - trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) ); - - Q_strncpyz( maplog, g_adminMapLog.string, sizeof( maplog ) ); - ptr = maplog; - while( *ptr && count < MAX_ADMIN_MAPLOG_LENGTH ) - { - while( *ptr != ' ' && *ptr != '\0' ) ptr++; - - count++; - if( count >= MAX_ADMIN_MAPLOG_LENGTH ) - { - *ptr = '\0'; - } - - if( *ptr == ' ' ) ptr++; - } - - trap_Cvar_Set( "g_adminMapLog", va( "%s%s%s", - map, - ( maplog[0] != '\0' ) ? " " : "", - maplog ) ); -} - -void G_admin_maplog_result( char *flag ) -{ - static int lastTime = 0; - char maplog[ MAX_CVAR_VALUE_STRING ]; - int t; - - if( !flag ) - return; - - // avoid race when called in same frame - if( level.time == lastTime ) - return; - - lastTime = level.time; - - if( g_adminMapLog.string[ 0 ] && - g_adminMapLog.string[ 1 ] == ';' ) - { - // only one result allowed - return; - } - - if ( level.surrenderTeam != PTE_NONE ) - { - if( flag[ 0 ] == 'a' ) - { - if( level.surrenderTeam == PTE_HUMANS ) - flag = "A"; - } - else if( flag[ 0 ] == 'h' ) - { - if( level.surrenderTeam == PTE_ALIENS ) - flag = "H"; - } - } - - t = ( level.time - level.startTime ) / 1000; - Q_strncpyz( maplog, g_adminMapLog.string, sizeof( maplog ) ); - trap_Cvar_Set( "g_adminMapLog", va( "%1s;%03d:%02d;%s", - flag, - t / 60, t % 60, - maplog ) ); -} - - -qboolean G_admin_maplog( gentity_t *ent, int skiparg ) -{ - char maplog[ MAX_CVAR_VALUE_STRING ]; - char *ptr; - int count = 0; - - Q_strncpyz( maplog, g_adminMapLog.string, sizeof( maplog ) ); - - ADMBP_begin( ); - ptr = maplog; - while( *ptr != '\0' && count < MAX_ADMIN_MAPLOG_LENGTH + 1 ) - { - char *end; - const char *result = NULL; - char *clock = NULL; - char *colon; - - end = ptr; - while( *end != ' ' && *end != '\0' ) end++; - if( *end == ' ' ) - { - *end = '\0'; - end++; - } - - if( ptr[ 0 ] && ptr[ 1 ] == ';' ) - { - switch( ptr[ 0 ] ) - { - case 't': - result = "^7tie"; - break; - case 'a': - result = "^1Alien win"; - break; - case 'A': - result = "^1Alien win ^7/ Humans admitted defeat"; - break; - case 'h': - result = "^4Human win"; - break; - case 'H': - result = "^4Human win ^7/ Aliens admitted defeat"; - break; - case 'd': - result = "^5draw vote"; - break; - case 'N': - result = "^6admin loaded next map"; - break; - case 'r': - result = "^2restart vote"; - break; - case 'R': - result = "^6admin restarted map"; - break; - case 'm': - result = "^2map vote"; - break; - case 'M': - result = "^6admin changed map"; - break; - case 'D': - result = "^6admin loaded devmap"; - break; - default: - result = ""; - break; - } - ptr += 2; - colon = strchr( ptr, ';' ); - if ( colon ) - { - clock = ptr; - ptr = colon + 1; - *colon = '\0'; - - // right justification with -6%s doesnt work.. - if( clock[ 0 ] == '0' && clock[ 1 ] != ':' ) - { - if( clock[ 1 ] == '0' && clock[ 2 ] != ':' ) - clock[ 1 ] = ' '; - clock[ 0 ] = ' '; - } - } - } - else if( count == 0 ) - { - result = "^3current map"; - clock = " -:--"; - } - - ADMBP( va( "%s%20s %-6s %s^7\n", - ( count == 0 ) ? "^3" : "^7", - ptr, - ( clock ) ? clock : "", - ( result ) ? result : "" ) ); - - ptr = end; - count++; - } - ADMBP_end( ); - - return qtrue; -} - -qboolean G_admin_layoutsave( gentity_t *ent, int skiparg ) -{ - char layout[ MAX_QPATH ]; - - if( G_SayArgc( ) < 2 + skiparg ) - { - ADMP( "^3!layoutsave: ^7usage: !layoutsave [layout]\n" ); - return qfalse; - } - - G_SayArgv( skiparg + 1, layout, sizeof( layout ) ); - - trap_SendConsoleCommand( EXEC_APPEND, va( "layoutsave %s", layout ) ); - AP( va( "print \"^3!layoutsave: ^7layout saved as '%s' by %s\n\"", layout, - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - return qtrue; -} - -qboolean G_admin_demo( gentity_t *ent, int skiparg ) -{ - if( !ent ) - { - ADMP( "!demo: console can not use demo.\n" ); - return qfalse; - } - - ent->client->pers.ignoreAdminWarnings = !( ent->client->pers.ignoreAdminWarnings ); - - ADMP( va( "^3!demo: ^7your visibility of admin chat is now %s\n", - ( ent->client->pers.ignoreAdminWarnings ) ? "^1disabled" : "^2enabled" ) ); - - return qtrue; -} - -qboolean G_admin_mute( gentity_t *ent, int skiparg ) -{ - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; - char command[ MAX_ADMIN_CMD_LEN ], *cmd; - gentity_t *vic; - char secs[ 7 ]; - int seconds = 0; - - G_SayArgv( skiparg, command, sizeof( command ) ); - cmd = command; - - if( cmd && *cmd == '!' ) - cmd++; - - if( G_SayArgc() < 2 + skiparg ) - { - ADMP( va( "^3!%s: ^7usage: !%s [name|slot#] (duration)\n", cmd, cmd ) ); - return qfalse; - } - - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - - if( G_ClientNumbersFromString( name, pids ) != 1 ) - { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!%s: ^7%s\n", cmd, err ) ); - return qfalse; - } - - if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) - { - ADMP( va( "^3!%s: ^7sorry, but your intended victim has a higher admin" - " level than you\n", cmd ) ); - return qfalse; - } - - vic = &g_entities[ pids[ 0 ] ]; - if( !Q_stricmp( cmd, "unmute" ) ) - { - if( vic->client->pers.muted == qfalse ) - { - ADMP( "^3!unmute: ^7player is not currently muted\n" ); - return qtrue; - } - - vic->client->pers.muteExpires = 0; - vic->client->pers.muted = qfalse; - - CPx( pids[ 0 ], "cp \"^1You have been unmuted\"" ); - AP( va( "print \"^3!unmute: ^7%s^7 has been unmuted by %s\n\"", - vic->client->pers.netname, - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - } else { - // Duration - if( G_SayArgc() > 2 + skiparg ) - { - G_SayArgv( 2 + skiparg, secs, sizeof( secs ) ); - seconds = G_admin_parse_time( secs ); - vic->client->pers.muteExpires = level.time + ( seconds * 1000 ); - } - - vic->client->pers.muted = qtrue; - - CPx( pids[ 0 ], "cp \"^1You've been muted\"" ); - AP( va( "print \"^3!mute: ^7%s^7 has been muted by ^7%s%s\n\"", - vic->client->pers.netname, - ( ent ) ? G_admin_adminPrintName( ent ) : "console", - ( seconds ) ? va( " ^7for %i seconds", seconds ) : "" ) ); - } - - return qtrue; -} - -qboolean G_admin_cp( gentity_t *ent, int skiparg ) -{ - int minargc; - char *s; - - minargc = 2 + skiparg; - - if( G_SayArgc() < minargc ) - { - ADMP( "^3!cp: ^7usage: !cp [message]\n" ); - return qfalse; - } - - s = G_SayConcatArgs( 1 + skiparg ); - G_CP(ent); - return qtrue; -} - -qboolean G_admin_denybuild( gentity_t *ent, int skiparg ) -{ - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; - char command[ MAX_ADMIN_CMD_LEN ], *cmd; - gentity_t *vic; + char name[ MAX_NAME_LENGTH ]; + char command[ MAX_ADMIN_CMD_LEN ]; + namelog_t *vic; - G_SayArgv( skiparg, command, sizeof( command ) ); - cmd = command; - if( cmd && *cmd == '!' ) - cmd++; - if( G_SayArgc() < 2 + skiparg ) + trap_Argv( 0, command, sizeof( command ) ); + if( trap_Argc() < 2 ) { - ADMP( va( "^3!%s: ^7usage: !%s [name|slot#]\n", cmd, cmd ) ); + ADMP( va( "^3%s: ^7usage: %s [name|slot#]\n", command, command ) ); return qfalse; } - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) + trap_Argv( 1, name, sizeof( name ) ); + if( !( vic = G_NamelogFromString( ent, name ) ) ) { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!%s: ^7%s\n", cmd, err ) ); + ADMP( va( "^3%s: ^7no match\n", command ) ); return qfalse; } - if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) + if( ent && !admin_higher_admin( ent->client->pers.admin, + G_admin_admin( vic->guid ) ) ) { - ADMP( va( "^3!%s: ^7sorry, but your intended victim has a higher admin" - " level than you\n", cmd ) ); + ADMP( va( "^3%s: ^7sorry, but your intended victim has a higher admin" + " level than you\n", command ) ); return qfalse; } - vic = &g_entities[ pids[ 0 ] ]; - if( vic->client->pers.denyBuild ) + if( vic->muted ) { - if( !Q_stricmp( cmd, "denybuild" ) ) + if( !Q_stricmp( command, "mute" ) ) { - ADMP( "^3!denybuild: ^7player already has no building rights\n" ); - return qtrue; + ADMP( "^3mute: ^7player is already muted\n" ); + return qfalse; } - vic->client->pers.denyBuild = qfalse; - CPx( pids[ 0 ], "cp \"^1You've regained your building rights\"" ); - AP( va( - "print \"^3!allowbuild: ^7building rights for ^7%s^7 restored by %s\n\"", - vic->client->pers.netname, - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); + vic->muted = qfalse; + if( vic->slot > -1 ) + CPx( vic->slot, "cp \"^1You have been unmuted\"" ); + AP( va( "print \"^3unmute: ^7%s^7 has been unmuted by %s\n\"", + vic->name[ vic->nameOffset ], + ( ent ) ? ent->client->pers.netname : "console" ) ); } else { - if( !Q_stricmp( cmd, "allowbuild" ) ) - { - ADMP( "^3!allowbuild: ^7player already has building rights\n" ); - return qtrue; - } - vic->client->pers.denyBuild = qtrue; - vic->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE; - if( vic->client->ps.stats[ STAT_PCLASS ]== PCL_ALIEN_BUILDER0 || vic->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0_UPG ) + if( !Q_stricmp( command, "unmute" ) ) { - vic->suicideTime = level.time + 1000; + ADMP( "^3unmute: ^7player is not currently muted\n" ); + return qfalse; } - CPx( pids[ 0 ], "cp \"^1You've lost your building rights\"" ); - AP( va( - "print \"^3!denybuild: ^7building rights for ^7%s^7 revoked by ^7%s\n\"", - vic->client->pers.netname, - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); + vic->muted = qtrue; + if( vic->slot > -1 ) + CPx( vic->slot, "cp \"^1You've been muted\"" ); + AP( va( "print \"^3mute: ^7%s^7 has been muted by ^7%s\n\"", + vic->name[ vic->nameOffset ], + ( ent ) ? ent->client->pers.netname : "console" ) ); } + admin_log( va( "%d (%s) \"%s" S_COLOR_WHITE "\"", vic->slot, vic->guid, + vic->name[ vic->nameOffset ] ) ); return qtrue; } -qboolean G_admin_denyweapon( gentity_t *ent, int skiparg ) +qboolean G_admin_denybuild( gentity_t *ent ) { - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; - char command[ MAX_ADMIN_CMD_LEN ], *cmd; - char buffer[ 32 ]; - int weapon = WP_NONE; - int class = PCL_NONE; - char *realname; - gentity_t *vic; - int flag; - qboolean all = qfalse; + char name[ MAX_NAME_LENGTH ]; + char command[ MAX_ADMIN_CMD_LEN ]; + namelog_t *vic; - G_SayArgv( skiparg, command, sizeof( command ) ); - cmd = command; - if( cmd && *cmd == '!' ) - cmd++; - if( G_SayArgc() < 3 + skiparg ) + trap_Argv( 0, command, sizeof( command ) ); + if( trap_Argc() < 2 ) { - ADMP( va( "^3!%s: ^7usage: !%s [name|slot#] [class|weapon]\n", cmd, cmd ) ); + ADMP( va( "^3%s: ^7usage: %s [name|slot#]\n", command, command ) ); return qfalse; } - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) + trap_Argv( 1, name, sizeof( name ) ); + if( !( vic = G_NamelogFromString( ent, name ) ) ) { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!%s: ^7%s\n", cmd, err ) ); + ADMP( va( "^3%s: ^7no match\n", command ) ); return qfalse; } - if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) + if( ent && !admin_higher_admin( ent->client->pers.admin, + G_admin_admin( vic->guid ) ) ) { - ADMP( va( "^3!%s: ^7sorry, but your intended victim has a higher admin" - " level than you\n", cmd ) ); + ADMP( va( "^3%s: ^7sorry, but your intended victim has a higher admin" + " level than you\n", command ) ); return qfalse; } - vic = &g_entities[ pids[ 0 ] ]; - - if( vic == ent && - !Q_stricmp( cmd, "denyweapon" ) ) - { - ADMP( va( "^3!%s: ^7sorry, you cannot %s yourself\n", cmd, cmd ) ); - return qfalse; - } - - G_SayArgv( 2 + skiparg, buffer, sizeof( buffer ) ); - - if( !Q_stricmp( buffer, "all" ) && - !Q_stricmp( cmd, "allowweapon" ) ) - { - if( vic->client->pers.denyHumanWeapons || - vic->client->pers.denyAlienClasses ) - { - vic->client->pers.denyHumanWeapons = 0; - vic->client->pers.denyAlienClasses = 0; - - CPx( pids[ 0 ], "cp \"^1You've regained all weapon and class rights\"" ); - AP( va( "print \"^3!%s: ^7all weapon and class rights for ^7%s^7 restored by %s\n\"", - cmd, - vic->client->pers.netname, - ( ent ) ? ent->client->pers.netname : "console" ) ); - } - else - { - ADMP( va( "^3!%s: ^7player already has all rights\n", cmd ) ); - } - return qtrue; - } - - if( !Q_stricmp( buffer, "all" ) && - !Q_stricmp( cmd, "denyweapon" ) ) - { - all = qtrue; - weapon = WP_NONE; - class = PCL_NONE; - - if( vic->client->pers.denyHumanWeapons == 0xFFFFFF && - vic->client->pers.denyAlienClasses == 0xFFFFFF ) - { - ADMP( va( "^3!%s: ^7player already has no weapon or class rights\n", cmd ) ); - return qtrue; - } - - if( vic->client->pers.teamSelection == PTE_HUMANS ) - { - weapon = vic->client->ps.weapon; - if( weapon < WP_PAIN_SAW || weapon > WP_GRENADE ) - weapon = WP_NONE; - } - if( vic->client->pers.teamSelection == PTE_ALIENS ) - { - class = vic->client->pers.classSelection; - if( class < PCL_ALIEN_LEVEL1 || class > PCL_ALIEN_LEVEL4 ) - class = PCL_NONE; - } - - vic->client->pers.denyHumanWeapons = 0xFFFFFF; - vic->client->pers.denyAlienClasses = 0xFFFFFF; - } - else - { - weapon = BG_FindWeaponNumForName( buffer ); - if( weapon < WP_PAIN_SAW || weapon > WP_GRENADE ) - class = BG_FindClassNumForName( buffer ); - if( ( weapon < WP_PAIN_SAW || weapon > WP_GRENADE ) && - ( class < PCL_ALIEN_LEVEL1 || class > PCL_ALIEN_LEVEL4 ) ) - { - { - ADMP( va( "^3!%s: ^7unknown weapon or class\n", cmd ) ); - return qfalse; - } - } - } - - if( class == PCL_NONE ) + if( vic->denyBuild ) { - realname = BG_FindHumanNameForWeapon( weapon ); - flag = 1 << (weapon - WP_BLASTER); - if( !Q_stricmp( cmd, "denyweapon" ) ) + if( !Q_stricmp( command, "denybuild" ) ) { - if( ( vic->client->pers.denyHumanWeapons & flag ) && !all ) - { - ADMP( va( "^3!%s: ^7player already has no %s rights\n", cmd, realname ) ); - return qtrue; - } - vic->client->pers.denyHumanWeapons |= flag; - if( vic->client->pers.teamSelection == PTE_HUMANS ) - { - if( ( weapon == WP_GRENADE || all ) && - BG_InventoryContainsUpgrade( UP_GRENADE, vic->client->ps.stats ) ) - { - BG_RemoveUpgradeFromInventory( UP_GRENADE, vic->client->ps.stats ); - G_AddCreditToClient( vic->client, (short)BG_FindPriceForUpgrade( UP_GRENADE ), qfalse ); - } - if( BG_InventoryContainsWeapon( weapon, vic->client->ps.stats ) ) - { - int maxAmmo, maxClips; - - BG_RemoveWeaponFromInventory( weapon, vic->client->ps.stats ); - G_AddCreditToClient( vic->client, (short)BG_FindPriceForWeapon( weapon ), qfalse ); - - BG_AddWeaponToInventory( WP_MACHINEGUN, vic->client->ps.stats ); - BG_FindAmmoForWeapon( WP_MACHINEGUN, &maxAmmo, &maxClips ); - vic->client->ps.ammo = maxAmmo; - vic->client->ps.clips = maxClips; - G_ForceWeaponChange( vic, WP_MACHINEGUN ); - vic->client->ps.stats[ STAT_MISC ] = 0; - ClientUserinfoChanged( pids[ 0 ], qfalse ); - } - } - } - else - { - if( !( vic->client->pers.denyHumanWeapons & flag ) ) - { - ADMP( va( "^3!%s: ^7player already has %s rights\n", cmd, realname ) ); - return qtrue; - } - vic->client->pers.denyHumanWeapons &= ~flag; + ADMP( "^3denybuild: ^7player already has no building rights\n" ); + return qfalse; } + vic->denyBuild = qfalse; + if( vic->slot > -1 ) + CPx( vic->slot, "cp \"^1You've regained your building rights\"" ); + AP( va( + "print \"^3allowbuild: ^7building rights for ^7%s^7 restored by %s\n\"", + vic->name[ vic->nameOffset ], + ( ent ) ? ent->client->pers.netname : "console" ) ); } else { - realname = BG_FindHumanNameForClassNum( class ); - flag = 1 << class; - if( !Q_stricmp( cmd, "denyweapon" ) ) + if( !Q_stricmp( command, "allowbuild" ) ) { - if( ( vic->client->pers.denyAlienClasses & flag ) && !all ) - { - ADMP( va( "^3!%s: ^7player already has no %s rights\n", cmd, realname ) ); - return qtrue; - } - vic->client->pers.denyAlienClasses |= flag; - if( vic->client->pers.teamSelection == PTE_ALIENS && - vic->client->pers.classSelection == class ) - { - vec3_t infestOrigin; - short cost; - - G_RoomForClassChange( vic, PCL_ALIEN_LEVEL0, infestOrigin ); - - vic->client->pers.evolveHealthFraction = (float)vic->client->ps.stats[ STAT_HEALTH ] / - (float)BG_FindHealthForClass( class ); - if( vic->client->pers.evolveHealthFraction < 0.0f ) - vic->client->pers.evolveHealthFraction = 0.0f; - else if( vic->client->pers.evolveHealthFraction > 1.0f ) - vic->client->pers.evolveHealthFraction = 1.0f; - - vic->client->pers.classSelection = PCL_ALIEN_LEVEL0; - cost = BG_ClassCanEvolveFromTo( PCL_ALIEN_LEVEL0, class, 9, 0 ); - if( cost < 0 ) cost = 0; - G_AddCreditToClient( vic->client, cost, qfalse ); - ClientUserinfoChanged( pids[ 0 ], qfalse ); - VectorCopy( infestOrigin, vic->s.pos.trBase ); - ClientSpawn( vic, vic, vic->s.pos.trBase, vic->s.apos.trBase ); - } + ADMP( "^3allowbuild: ^7player already has building rights\n" ); + return qfalse; } - else + vic->denyBuild = qtrue; + if( vic->slot > -1 ) { - if( !( vic->client->pers.denyAlienClasses & flag ) ) - { - ADMP( va( "^3!%s: ^7player already has %s rights\n", cmd, realname ) ); - return qtrue; - } - vic->client->pers.denyAlienClasses &= ~flag; + level.clients[ vic->slot ].ps.stats[ STAT_BUILDABLE ] = BA_NONE; + CPx( vic->slot, "cp \"^1You've lost your building rights\"" ); } + AP( va( + "print \"^3denybuild: ^7building rights for ^7%s^7 revoked by ^7%s\n\"", + vic->name[ vic->nameOffset ], + ( ent ) ? ent->client->pers.netname : "console" ) ); } - - if( all ) - realname = "weapon and class"; - - CPx( pids[ 0 ], va( "cp \"^1You've %s your %s rights\"", - ( !Q_stricmp( cmd, "denyweapon" ) ) ? "lost" : "regained", - realname ) ); - AP( va( - "print \"^3!%s: ^7%s rights for ^7%s^7 %s by %s\n\"", - cmd, realname, - vic->client->pers.netname, - ( !Q_stricmp( cmd, "denyweapon" ) ) ? "revoked" : "restored", - ( ent ) ? ent->client->pers.netname : "console" ) ); - + admin_log( va( "%d (%s) \"%s" S_COLOR_WHITE "\"", vic->slot, vic->guid, + vic->name[ vic->nameOffset ] ) ); return qtrue; } -qboolean G_admin_listadmins( gentity_t *ent, int skiparg ) +qboolean G_admin_listadmins( gentity_t *ent ) { - int i, found = 0; + int i; char search[ MAX_NAME_LENGTH ] = {""}; char s[ MAX_NAME_LENGTH ] = {""}; - int start = 0; - qboolean numeric = qtrue; - int drawn = 0; - int minlevel = 1; + int start = MAX_CLIENTS; - if( G_SayArgc() == 3 + skiparg ) + if( trap_Argc() == 3 ) { - G_SayArgv( 2 + skiparg, s, sizeof( s ) ); - if( s[ 0 ] >= '0' && s[ 0] <= '9' ) - { - minlevel = atoi( s ); - if( minlevel < 1 ) - minlevel = 1; - } - } - - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) - { - if( g_admin_admins[ i ]->level >= minlevel ) - found++; - } - if( !found ) - { - if( minlevel > 1 ) - { - ADMP( va( "^3!listadmins: ^7no admins level %i or greater found\n", minlevel ) ); - } - else - { - ADMP( "^3!listadmins: ^7no admins defined\n" ); - } - return qfalse; + trap_Argv( 2, s, sizeof( s ) ); + start = atoi( s ); } - - if( G_SayArgc() >= 2 + skiparg ) + if( trap_Argc() > 1 ) { - G_SayArgv( 1 + skiparg, s, sizeof( s ) ); - for( i = 0; i < sizeof( s ) && s[ i ]; i++ ) + trap_Argv( 1, s, sizeof( s ) ); + i = 0; + if( trap_Argc() == 2 ) { - if( s[ i ] >= '0' && s[ i ] <= '9' ) - continue; - numeric = qfalse; + i = s[ 0 ] == '-'; + for( ; isdigit( s[ i ] ); i++ ); } - if( numeric ) - { + if( i && !s[ i ] ) start = atoi( s ); - if( start > 0 ) - start -= 1; - else if( start < 0 ) - start = found + start; - } else G_SanitiseString( s, search, sizeof( search ) ); } - if( start >= found || start < 0 ) - start = 0; - - drawn = admin_listadmins( ent, start, search, minlevel ); - - if( search[ 0 ] ) - { - if( drawn <= 20 ) - { - ADMP( va( "^3!listadmins:^7 found %d admins level %i or greater matching '%s^7'\n", - drawn, minlevel, search ) ); - } - else - { - ADMP( va( "^3!listadmins:^7 found >20 admins level %i or greater matching '%s^7. Try a more narrow search.'\n", - minlevel, search ) ); - } - } - else - { - ADMBP_begin(); - ADMBP( va( "^3!listadmins:^7 showing admins level %i or greater %d - %d of %d. ", - minlevel, - ( found ) ? ( start + 1 ) : 0, - ( ( start + MAX_ADMIN_LISTITEMS ) > found ) ? - found : ( start + MAX_ADMIN_LISTITEMS ), - found ) ); - if( ( start + MAX_ADMIN_LISTITEMS ) < found ) - { - if( minlevel > 1) - { - ADMBP( va( "run '!listadmins %d %d' to see more", - ( start + MAX_ADMIN_LISTITEMS + 1 ), minlevel ) ); - } - else - { - ADMBP( va( "run '!listadmins %d' to see more", - ( start + MAX_ADMIN_LISTITEMS + 1 ) ) ); - } - } - ADMBP( "\n" ); - ADMBP_end(); - } + admin_listadmins( ent, start, search ); return qtrue; } -qboolean G_admin_listlayouts( gentity_t *ent, int skiparg ) +qboolean G_admin_listlayouts( gentity_t *ent ) { char list[ MAX_CVAR_VALUE_STRING ]; char map[ MAX_QPATH ]; int count = 0; char *s; char layout[ MAX_QPATH ] = { "" }; - int i = 0; - - if( G_SayArgc( ) == 2 + skiparg ) - G_SayArgv( 1 +skiparg, map, sizeof( map ) ); + size_t i = 0; + + if( trap_Argc( ) == 2 ) + trap_Argv( 1, map, sizeof( map ) ); else trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) ); - + count = G_LayoutList( map, list, sizeof( list ) ); ADMBP_begin( ); - ADMBP( va( "^3!listlayouts:^7 %d layouts found for '%s':\n", count, map ) ); + ADMBP( va( "^3listlayouts:^7 %d layouts found for '%s':\n", count, map ) ); s = &list[ 0 ]; while( *s ) { @@ -4762,690 +2366,193 @@ qboolean G_admin_listlayouts( gentity_t *ent, int skiparg ) return qtrue; } -qboolean G_admin_listplayers( gentity_t *ent, int skiparg ) +qboolean G_admin_listplayers( gentity_t *ent ) { - int i, j; - int invisiblePlayers = 0; - gclient_t *p; - char c[ 3 ], t[ 2 ]; // color and team letter - char n[ MAX_NAME_LENGTH ] = {""}; - char n2[ MAX_NAME_LENGTH ] = {""}; - char n3[ MAX_NAME_LENGTH ] = {""}; - char lname[ MAX_NAME_LENGTH ]; - char lname2[ MAX_NAME_LENGTH ]; - char muted[ 2 ], denied[ 2 ], dbuilder[ 2 ], immune[ 2 ], guidless[ 2 ]; - int l; - char lname_fmt[ 5 ]; - - //get amount of invisible players - for( i = 0; i < level.maxclients; i++ ) { - p = &level.clients[ i ]; - if ( p->sess.invisible == qtrue ) - invisiblePlayers++; - } + int i, j; + gclient_t *p; + char c, t; // color and team letter + char *registeredname; + char lname[ MAX_NAME_LENGTH ]; + char muted, denied; + int colorlen; + char namecleaned[ MAX_NAME_LENGTH ]; + char name2cleaned[ MAX_NAME_LENGTH ]; + g_admin_level_t *l; + g_admin_level_t *d = G_admin_level( 0 ); + qboolean hint; + qboolean canset = G_admin_permission( ent, "setlevel" ); ADMBP_begin(); - ADMBP( va( "^3!listplayers^7: %d players connected:\n", - level.numConnectedClients - invisiblePlayers ) ); + ADMBP( va( "^3listplayers: ^7%d players connected:\n", + level.numConnectedClients ) ); for( i = 0; i < level.maxclients; i++ ) { p = &level.clients[ i ]; - - // Ignore invisible players - if ( p->sess.invisible == qtrue ) + if( p->pers.connected == CON_DISCONNECTED ) continue; - - Q_strncpyz( t, "S", sizeof( t ) ); - Q_strncpyz( c, S_COLOR_YELLOW, sizeof( c ) ); - if( p->pers.teamSelection == PTE_HUMANS ) - { - Q_strncpyz( t, "H", sizeof( t ) ); - Q_strncpyz( c, S_COLOR_BLUE, sizeof( c ) ); - } - else if( p->pers.teamSelection == PTE_ALIENS ) - { - Q_strncpyz( t, "A", sizeof( t ) ); - Q_strncpyz( c, S_COLOR_RED, sizeof( c ) ); - } - if( p->pers.connected == CON_CONNECTING ) { - Q_strncpyz( t, "C", sizeof( t ) ); - Q_strncpyz( c, S_COLOR_CYAN, sizeof( c ) ); + t = 'C'; + c = COLOR_YELLOW; } - else if( p->pers.connected != CON_CONNECTED ) + else { - continue; + t = toupper( *( BG_TeamName( p->pers.teamSelection ) ) ); + if( p->pers.teamSelection == TEAM_HUMANS ) + c = COLOR_CYAN; + else if( p->pers.teamSelection == TEAM_ALIENS ) + c = COLOR_RED; + else + c = COLOR_WHITE; } - guidless[ 0 ] = '\0'; - if( !Q_stricmp( p->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ) ) - { - Q_strncpyz( guidless, "G", sizeof( guidless ) ); - } - muted[ 0 ] = '\0'; - if( G_admin_permission( &g_entities[ i ], ADMF_NO_VOTE ) || G_admin_permission( &g_entities[ i ], ADMF_FAKE_NO_VOTE ) ) - { - Q_strncpyz( muted, "V", sizeof( muted ) ); - } - if( p->pers.muted ) - { - Q_strncpyz( muted, "M", sizeof( muted ) ); - } - denied[ 0 ] = '\0'; - if( p->pers.denyBuild ) - { - Q_strncpyz( denied, "B", sizeof( denied ) ); - } - if( p->pers.denyHumanWeapons || p->pers.denyAlienClasses ) - { - Q_strncpyz( denied, "W", sizeof( denied ) ); - } + muted = p->pers.namelog->muted ? 'M' : ' '; + denied = p->pers.namelog->denyBuild ? 'B' : ' '; - dbuilder[ 0 ] = '\0'; - if( p->pers.designatedBuilder ) + l = d; + registeredname = NULL; + hint = canset; + if( p->pers.admin ) { - if( !G_admin_permission( &g_entities[ i ], ADMF_INCOGNITO ) && - G_admin_permission( &g_entities[ i ], ADMF_DBUILDER ) && - G_admin_permission(ent, ADMF_SEESFULLLISTPLAYERS ) ) - { - Q_strncpyz( dbuilder, "P", sizeof( dbuilder ) ); - } - else + if( hint ) + hint = admin_higher( ent, &g_entities[ i ] ); + if( hint || !G_admin_permission( &g_entities[ i ], ADMF_INCOGNITO ) ) { - Q_strncpyz( dbuilder, "D", sizeof( dbuilder ) ); + l = G_admin_level( p->pers.admin->level ); + G_SanitiseString( p->pers.netname, namecleaned, + sizeof( namecleaned ) ); + G_SanitiseString( p->pers.admin->name, + name2cleaned, sizeof( name2cleaned ) ); + if( Q_stricmp( namecleaned, name2cleaned ) ) + registeredname = p->pers.admin->name; } } - immune[ 0 ] = '\0'; - if( G_admin_permission( &g_entities[ i ], ADMF_BAN_IMMUNITY ) && - G_admin_permission(ent, ADMF_SEESFULLLISTPLAYERS ) ) - { - Q_strncpyz( immune, "I", sizeof( immune ) ); - } - if( p->pers.paused ) - { - // use immunity slot for paused player status - Q_strncpyz( immune, "L", sizeof( immune ) ); - } + if( l ) + Q_strncpyz( lname, l->name, sizeof( lname ) ); - l = 0; - G_SanitiseString( p->pers.netname, n2, sizeof( n2 ) ); - n[ 0 ] = '\0'; - for( j = 0; j < MAX_ADMIN_ADMINS && g_admin_admins[ j ]; j++ ) + for( colorlen = j = 0; lname[ j ]; j++ ) { - if( !Q_stricmp( g_admin_admins[ j ]->guid, p->pers.guid ) ) - { - - // don't gather aka or level info if the admin is incognito - if( ent && G_admin_permission( &g_entities[ i ], ADMF_INCOGNITO ) && !G_admin_permission(ent, ADMF_SEESINCOGNITO) ) - { - break; - } - l = g_admin_admins[ j ]->level; - G_SanitiseString( g_admin_admins[ j ]->name, n3, sizeof( n3 ) ); - if( Q_stricmp( n2, n3 ) ) - { - Q_strncpyz( n, g_admin_admins[ j ]->name, sizeof( n ) ); - } - break; - } + if( Q_IsColorString( &lname[ j ] ) ) + colorlen += 2; } - lname[ 0 ] = '\0'; - Q_strncpyz( lname_fmt, "%s", sizeof( lname_fmt ) ); - for( j = 0; j < MAX_ADMIN_LEVELS && g_admin_levels[ j ]; j++ ) - { - if( g_admin_levels[ j ]->level == l ) - { - Q_strncpyz( lname, g_admin_levels[ j ]->name, sizeof( lname ) ); - if( *lname ) - { - G_DecolorString( lname, lname2 ); - Com_sprintf( lname_fmt, sizeof( lname_fmt ), "%%%is", - ( admin_level_maxname + (int)( strlen( lname ) - strlen( lname2 ) ) ) ); - Com_sprintf( lname2, sizeof( lname2 ), lname_fmt, lname ); - } - break; - } - } + ADMBP( va( "%2i ^%c%c %s %s^7 %*s^7 ^1%c%c^7 %s^7 %s%s%s\n", + i, + c, + t, + p->pers.guidless ? "^1---" : va( "^7%-2i^2%c", l ? l->level : 0, hint ? '*' : ' ' ), + p->pers.alternateProtocol == 2 ? "^11" : p->pers.alternateProtocol == 1 ? "^3G" : " ", + admin_level_maxname + colorlen, + lname, + muted, + denied, + p->pers.netname, + ( registeredname ) ? "(a.k.a. " : "", + ( registeredname ) ? registeredname : "", + ( registeredname ) ? S_COLOR_WHITE ")" : "" ) ); - if ( G_admin_permission(ent, ADMF_SEESFULLLISTPLAYERS ) ) { - ADMBP( va( "%2i %s%s^7 %-2i %s^7 ^1%1s%1s%1s%1s%1s^7 %s^7 %s%s^7%s\n", - i, - c, - t, - l, - ( *lname ) ? lname2 : "", - immune, - muted, - dbuilder, - denied, - guidless, - p->pers.netname, - ( *n ) ? "(a.k.a. " : "", - n, - ( *n ) ? ")" : "" - ) ); - } else { - ADMBP( va( "%2i %s%s^7 %-2i %s^7 ^1%1s%1s%1s%1s^7 %s^7 %s%s^7%s\n", - i, - c, - t, - l, - ( *lname ) ? lname2 : "", - immune, - muted, - dbuilder, - denied, - p->pers.netname, - ( *n ) ? "(a.k.a. " : "", - n, - ( *n ) ? ")" : "" - ) ); - } } ADMBP_end(); return qtrue; } -#define MAX_LISTMAPS_MAPS 256 - -static int SortMaps(const void *a, const void *b) +static qboolean ban_matchip( void *ban, const void *ip ) { - return strcmp(*(char **)a, *(char **)b); + return G_AddressCompare( &((g_admin_ban_t *)ban)->ip, (addr_t *)ip ) || + G_AddressCompare( (addr_t *)ip, &((g_admin_ban_t *)ban)->ip ); } - -qboolean G_admin_listmaps( gentity_t *ent, int skiparg ) +static qboolean ban_matchname( void *ban, const void *name ) { - char fileList[ 4096 ] = {""}; - char *fileSort[ MAX_LISTMAPS_MAPS ]; - char search[ 16 ] = {""}; - int numFiles; - int i; - int fileLen = 0; - int count = 0; - char *filePtr; - int rows; + char match[ MAX_NAME_LENGTH ]; - if( G_SayArgc( ) > 1 + skiparg ) - { - G_SayArgv( skiparg + 1, search, sizeof( search ) ); - } - - numFiles = trap_FS_GetFileList( "maps/", ".bsp", - fileList, sizeof( fileList ) ); - filePtr = fileList; - for( i = 0; i < numFiles && count < MAX_LISTMAPS_MAPS; i++, filePtr += fileLen + 1 ) - { - fileLen = strlen( filePtr ); - if (fileLen < 5) - continue; - - filePtr[ fileLen - 4 ] = '\0'; - - if( search[ 0 ] && !strstr( filePtr, search ) ) - continue; - - fileSort[ count ] = filePtr; - count++; - } - - qsort(fileSort, count, sizeof(fileSort[ 0 ]), SortMaps); - - rows = count / 3; - if ( rows * 3 < count ) rows++; - - ADMBP_begin(); - for( i = 0; i < rows; i++ ) - { - ADMBP( va( "^7%20s %20s %20s\n", - fileSort[ i ], - ( rows + i < count ) ? fileSort[ rows + i ] : "", - ( rows * 2 + i < count ) ? fileSort[ rows * 2 + i ] : "" ) ); - } - if ( search[ 0 ] ) - ADMBP( va( "^3!listmaps: ^7found %d maps matching '%s^7'.\n", count, search ) ); - else - ADMBP( va( "^3!listmaps: ^7listing %d maps.\n", count ) ); - - ADMBP_end(); - - return qtrue; + G_SanitiseString( ( (g_admin_ban_t *)ban )->name, match, sizeof( match ) ); + return strstr( match, (const char *)name ) != NULL; } - -qboolean G_admin_listrotation( gentity_t *ent, int skiparg ) +static void ban_out( void *ban, char *str ) { - int i, j, statusColor; - char mapnames[ MAX_STRING_CHARS ]; - char *status = NULL; - - extern mapRotations_t mapRotations; + size_t i; + int colorlen1 = 0; + char duration[ MAX_DURATION_LENGTH ]; + char *d_color = S_COLOR_WHITE; + char date[ 11 ]; + g_admin_ban_t *b = ( g_admin_ban_t * )ban; + int t = trap_RealTime( NULL ); + char *made = b->made; - // Check for an active map rotation - if ( !G_MapRotationActive() || - g_currentMapRotation.integer == NOT_ROTATING ) + for( i = 0; b->name[ i ]; i++ ) { - trap_SendServerCommand( ent-g_entities, "print \"^3!rotation: ^7There is no active map rotation on this server\n\"" ); - return qfalse; + if( Q_IsColorString( &b->name[ i ] ) ) + colorlen1 += 2; } - // Locate the active map rotation and output its contents - for( i = 0; i < mapRotations.numRotations; i++ ) - { - if ( i == g_currentMapRotation.integer ) - { - int currentMap = G_GetCurrentMap( i ); - - ADMBP_begin(); - ADMBP( va( "^3!rotation: ^7%s\n", mapRotations.rotations[ i ].name ) ); - - for( j = 0; j < mapRotations.rotations[ i ].numMaps; j++ ) - { - Q_strncpyz( mapnames, mapRotations.rotations[ i ].maps[ j ].name, sizeof( mapnames ) ); - - if( !Q_stricmp( mapRotations.rotations[ i ].maps[ j ].name, "*VOTE*" ) ) - { - char slotMap[ 64 ]; - int lineLen = 0; - int k; - - trap_Cvar_VariableStringBuffer( "mapname", slotMap, sizeof( slotMap ) ); - mapnames[ 0 ] = '\0'; - for( k = 0; k < mapRotations.rotations[ i ].maps[ j ].numConditions; k++ ) - { - char *thisMap; - int mc = 7; - - if( mapRotations.rotations[ i ].maps[ j ].conditions[ k ].lhs != MCV_VOTE ) - continue; - - thisMap = mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest; - lineLen += strlen( thisMap ) + 1; - - if( currentMap == j && !Q_stricmp( thisMap, slotMap ) ) - mc = 3; - Q_strcat( mapnames, sizeof( mapnames ), va( "^7%s%s^%i%s", - ( k ) ? ", " : "", - ( lineLen > 50 ) ? "\n " : "", - mc, thisMap ) ); - if( lineLen > 50 ) - lineLen = strlen( thisMap ) + 2; - else - lineLen++; - } - - if( currentMap == j ) - { - statusColor = 3; - status = "current slot"; - } - else if( !k ) - { - statusColor = 1; - status = "empty vote"; - } - else - { - statusColor = 7; - status = "vote"; - } - } else if( !Q_stricmp( mapRotations.rotations[ i ].maps[ j ].name, "*RANDOM*" ) ) - { - char slotMap[ 64 ]; - int lineLen = 0; - int k; - - trap_Cvar_VariableStringBuffer( "mapname", slotMap, sizeof( slotMap ) ); - mapnames[ 0 ] = '\0'; - for( k = 0; k < mapRotations.rotations[ i ].maps[ j ].numConditions; k++ ) - { - char *thisMap; - int mc = 7; - - if( mapRotations.rotations[ i ].maps[ j ].conditions[ k ].lhs != MCV_SELECTEDRANDOM ) - continue; - - thisMap = mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest; - lineLen += strlen( thisMap ) + 1; - - if( currentMap == j && !Q_stricmp( thisMap, slotMap ) ) - mc = 3; - Q_strcat( mapnames, sizeof( mapnames ), va( "^7%s%s^%i%s", - ( k ) ? ", " : "", - ( lineLen > 50 ) ? "\n " : "", - mc, thisMap ) ); - if( lineLen > 50 ) - lineLen = strlen( thisMap ) + 2; - else - lineLen++; - } - - if( currentMap == j ) - { - statusColor = 3; - status = "current slot"; - } - else if( !k ) - { - statusColor = 1; - status = "empty Random"; - } - else - { - statusColor = 7; - status = "Random"; - } - } - else if ( currentMap == j ) - { - statusColor = 3; - status = "current slot"; - } - else if ( !G_MapExists( mapRotations.rotations[ i ].maps[ j ].name ) ) - { - statusColor = 1; - status = "missing"; - } - else - { - statusColor = 7; - status = ""; - } - ADMBP( va( " ^%i%-12s %3i %s\n", statusColor, status, j + 1, mapnames ) ); - } - - ADMBP_end(); - - // No maps were found in the active map rotation - if ( mapRotations.rotations[ i ].numMaps < 1 ) - { - trap_SendServerCommand( ent-g_entities, "print \" - ^7Empty!\n\"" ); - return qfalse; - } - } - } + // only print out the the date part of made + date[ 0 ] = '\0'; + for( i = 0; *made && *made != ' ' && i < sizeof( date ) - 1; i++ ) + date[ i ] = *made++; + date[ i ] = 0; - if( g_nextMap.string[0] ) + if( !b->expires || b->expires - t > 0 ) + G_admin_duration( b->expires ? b->expires - t : - 1, + duration, sizeof( duration ) ); + else { - ADMP( va ("^5 Next map overriden by g_nextMap to: %s\n", g_nextMap.string ) ); + Q_strncpyz( duration, "expired", sizeof( duration ) ); + d_color = S_COLOR_CYAN; } - - return qtrue; -} - -qboolean G_admin_showbans( gentity_t *ent, int skiparg ) + Com_sprintf( str, MAX_STRING_CHARS, "%-*s %s%-15s " S_COLOR_WHITE "%-8s %s" + "\n \\__ %s%-*s " S_COLOR_WHITE "%s", + MAX_NAME_LENGTH + colorlen1 - 1, b->name, + ( strchr( b->ip.str, '/' ) ) ? S_COLOR_RED : S_COLOR_WHITE, + b->ip.str, + date, + b->banner, + d_color, + MAX_DURATION_LENGTH - 1, + duration, + b->reason ); +} +qboolean G_admin_showbans( gentity_t *ent ) { - int i, found = 0; - int t; - char duration[ 32 ]; - char sduration[ 32 ]; - char suspended[ 64 ] = { "" }; - char name_fmt[ 32 ] = { "%s" }; - char banner_fmt[ 32 ] = { "%s" }; - int max_name = 1, max_banner = 1; - int secs; - int start = 0; + int i; + int start = 1; char filter[ MAX_NAME_LENGTH ] = {""}; - char date[ 11 ]; - char *made; - int j; - char n1[ MAX_NAME_LENGTH * 2 ] = {""}; - char n2[ MAX_NAME_LENGTH * 2 ] = {""}; - int bannerslevel = 0; - qboolean numeric = qtrue; - char *ip_match = NULL; - int ip_match_len = 0; char name_match[ MAX_NAME_LENGTH ] = {""}; - int show_count = 0; - qboolean subnetfilter = qfalse; + qboolean ipmatch = qfalse; + addr_t ip; - t = trap_RealTime( NULL ); - - for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) + if( trap_Argc() == 3 ) { - if( g_admin_bans[ i ]->expires != 0 - && ( g_admin_bans[ i ]->expires - t ) < 1 ) - { - continue; - } - found++; + trap_Argv( 2, filter, sizeof( filter ) ); + start = atoi( filter ); } - - if( G_SayArgc() >= 2 + skiparg ) + if( trap_Argc() > 1 ) { - G_SayArgv( 1 + skiparg, filter, sizeof( filter ) ); - if( G_SayArgc() >= 3 + skiparg ) - { + trap_Argv( 1, filter, sizeof( filter ) ); + i = 0; + if( trap_Argc() == 2 ) + for( i = filter[ 0 ] == '-'; isdigit( filter[ i ] ); i++ ); + if( !filter[ i ] ) start = atoi( filter ); - G_SayArgv( 2 + skiparg, filter, sizeof( filter ) ); - } - for( i = 0; i < sizeof( filter ) && filter[ i ] ; i++ ) - { - if( ( filter[ i ] < '0' || filter[ i ] > '9' ) - && filter[ i ] != '.' && filter[ i ] != '-' ) - { - numeric = qfalse; - break; - } - } - - if (!numeric) - { - if( filter[ 0 ] != '-' ) - { - G_SanitiseString( filter, name_match, sizeof( name_match) ); - - } - else - { - if( !Q_strncmp( filter, "-sub", 4 ) ) - { - subnetfilter = qtrue; - } - else - { - ADMP( va( "^3!showbans: ^7invalid argument %s\n", filter ) ); - return qfalse; - } - } - } - else if( strchr( filter, '.' ) != NULL ) - { - ip_match = filter; - ip_match_len = strlen(ip_match); - } - else - { - start = atoi( filter ); - filter[0] = '\0'; - } - // showbans 1 means start with ban 0 - if( start > 0 ) - start -= 1; - else if( start < 0 ) - start = found + start; - } - - if( start >= MAX_ADMIN_BANS || start < 0 ) - start = 0; - - for( i = start; i < MAX_ADMIN_BANS && g_admin_bans[ i ] - && show_count < MAX_ADMIN_SHOWBANS; i++ ) - { - qboolean match = qfalse; - - if (!numeric) - { - if( !subnetfilter ) - { - G_SanitiseString( g_admin_bans[ i ]->name, n1, sizeof( n1 ) ); - if (strstr( n1, name_match) ) - match = qtrue; - } - else - { - int mask = -1; - int dummy; - int scanflen = 0; - scanflen = sscanf( g_admin_bans[ i ]->ip, "%d.%d.%d.%d/%d", &dummy, &dummy, &dummy, &dummy, &mask ); - if( scanflen == 5 && mask < 32 ) - { - match = qtrue; - } - } - } - - if ( ( match ) || !ip_match - || Q_strncmp( ip_match, g_admin_bans[ i ]->ip, ip_match_len) == 0 ) - { - G_DecolorString( g_admin_bans[ i ]->name, n1 ); - G_DecolorString( g_admin_bans[ i ]->banner, n2 ); - if( strlen( n1 ) > max_name ) - { - max_name = strlen( n1 ); - } - if( strlen( n2 ) > max_banner ) - max_banner = strlen( n2 ); - - show_count++; - } + else if( !( ipmatch = G_AddressParse( filter, &ip ) ) ) + G_SanitiseString( filter, name_match, sizeof( name_match ) ); } - if( start >= found ) - { - ADMP( va( "^3!showbans: ^7there are %d active bans\n", found ) ); - return qfalse; - } - ADMBP_begin(); - show_count = 0; - for( i = start; i < MAX_ADMIN_BANS && g_admin_bans[ i ] - && show_count < MAX_ADMIN_SHOWBANS; i++ ) - { - if (!numeric) - { - if( !subnetfilter ) - { - G_SanitiseString( g_admin_bans[ i ]->name, n1, sizeof( n1 ) ); - if ( strstr ( n1, name_match ) == NULL ) - continue; - } - else - { - int mask = -1; - int dummy; - int scanflen = 0; - scanflen = sscanf( g_admin_bans[ i ]->ip, "%d.%d.%d.%d/%d", &dummy, &dummy, &dummy, &dummy, &mask ); - if( scanflen != 5 || mask >= 32 ) - { - continue; - } - } - } - else if( ip_match != NULL - && Q_strncmp( ip_match, g_admin_bans[ i ]->ip, ip_match_len ) != 0) - continue; - - // only print out the the date part of made - date[ 0 ] = '\0'; - made = g_admin_bans[ i ]->made; - for( j = 0; made && *made; j++ ) - { - if( ( j + 1 ) >= sizeof( date ) ) - break; - if( *made == ' ' ) - break; - date[ j ] = *made; - date[ j + 1 ] = '\0'; - made++; - } - - if( g_admin_bans[ i ]->expires != 0 - && ( g_admin_bans[ i ]->expires - t ) < 1 ) - { - Com_sprintf( duration, sizeof( duration ), "^1*EXPIRED*^7" ); - } else { - secs = ( g_admin_bans[ i ]->expires - t ); - G_admin_duration( secs, duration, sizeof( duration ) ); - } - - suspended[ 0 ] = '\0'; - if( g_admin_bans[ i ]->suspend > t ) - { - G_admin_duration( g_admin_bans[ i ]->suspend - t, sduration, sizeof( sduration ) ); - Com_sprintf( suspended, sizeof( suspended ), "^3*SUSPENDED*^7 for %s^7", - sduration ); - } - - G_DecolorString( g_admin_bans[ i ]->name, n1 ); - Com_sprintf( name_fmt, sizeof( name_fmt ), "%%%is", - ( max_name + strlen( g_admin_bans[ i ]->name ) - strlen( n1 ) ) ); - Com_sprintf( n1, sizeof( n1 ), name_fmt, g_admin_bans[ i ]->name ); - - G_DecolorString( g_admin_bans[ i ]->banner, n2 ); - Com_sprintf( banner_fmt, sizeof( banner_fmt ), "%%%is", - ( max_banner + strlen( g_admin_bans[ i ]->banner ) - strlen( n2 ) ) ); - Com_sprintf( n2, sizeof( n2 ), banner_fmt, g_admin_bans[ i ]->banner ); - bannerslevel = g_admin_bans[ i ]->bannerlevel; - - ADMBP( va( "%4i %s^7 %-15s %-8s %-10s\n | %-15s^7 Level:%2i\n | %s\n \\__ %s\n", - ( i + 1 ), - n1, - g_admin_bans[ i ]->ip, - date, - duration, - n2, - bannerslevel, - suspended, - g_admin_bans[ i ]->reason ) ); - - show_count++; - } - - if (!numeric || ip_match) - { - char matchmethod[50]; - if( numeric ) - Com_sprintf( matchmethod, sizeof(matchmethod), "IP" ); - else if( !subnetfilter ) - Com_sprintf( matchmethod, sizeof(matchmethod), "name" ); - else - Com_sprintf( matchmethod, sizeof(matchmethod), "ip range size" ); - - - ADMBP( va( "^3!showbans:^7 found %d matching bans by %s. ", - show_count, - matchmethod ) ); - } - else - { - ADMBP( va( "^3!showbans:^7 showing bans %d - %d of %d. ", - ( found ) ? ( start + 1 ) : 0, - ( ( start + MAX_ADMIN_SHOWBANS ) > found ) ? - found : ( start + MAX_ADMIN_SHOWBANS ), - found ) ); - } - - if( ( start + MAX_ADMIN_SHOWBANS ) < found ) - { - ADMBP( va( "run !showbans %d %s to see more", - ( start + MAX_ADMIN_SHOWBANS + 1 ), - (filter[0]) ? filter : "" ) ); - } - ADMBP( "\n" ); - ADMBP_end(); + admin_search( ent, "showbans", "bans", + ipmatch ? ban_matchip : ban_matchname, + ban_out, g_admin_bans, + ipmatch ? (void * )&ip : (void *)name_match, + start, 1, MAX_ADMIN_SHOWBANS ); return qtrue; } -qboolean G_admin_help( gentity_t *ent, int skiparg ) +qboolean G_admin_adminhelp( gentity_t *ent ) { - int i; - int commandsPerLine = 6; - - if( G_SayArgc() < 2 + skiparg ) + g_admin_command_t *c; + if( trap_Argc() < 2 ) { - int j = 0; + size_t i; int count = 0; ADMBP_begin(); @@ -5453,235 +2560,104 @@ qboolean G_admin_help( gentity_t *ent, int skiparg ) { if( G_admin_permission( ent, g_admin_cmds[ i ].flag ) ) { - ADMBP( va( "^3!%-12s", g_admin_cmds[ i ].keyword ) ); - j++; + ADMBP( va( "^3%-12s", g_admin_cmds[ i ].keyword ) ); count++; - } - // show 6 commands per line - if( j == 6 ) - { - ADMBP( "\n" ); - j = 0; + // show 6 commands per line + if( count % 6 == 0 ) + ADMBP( "\n" ); } } - for( i = 0; i < MAX_ADMIN_COMMANDS && g_admin_commands[ i ]; i++ ) + for( c = g_admin_commands; c; c = c->next ) { - if( !G_admin_permission( ent, g_admin_commands[ i ]->flag ) ) + if( !G_admin_permission( ent, c->flag ) ) continue; - ADMBP( va( "^3!%-12s", g_admin_commands[ i ]->command ) ); - j++; + ADMBP( va( "^3%-12s", c->command ) ); count++; // show 6 commands per line - if( j == 6 ) - { + if( count % 6 == 0 ) ADMBP( "\n" ); - j = 0; - } } - - if( count ) + if( count % 6 ) ADMBP( "\n" ); - ADMBP( va( "^3!help: ^7%i available commands\n", count ) ); - ADMBP( "run !help [^3command^7] for help with a specific command.\n" ); - ADMBP( "The following non-standard /commands may also be available to you: \n" ); - count = 1; - - if( ent && g_AllStats.integer ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "allstats" ) ); - count++; - } - if ( ent ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "builder" ) ); - count++; - } - if( ent && g_allowVote.integer && G_admin_permission( ent, ADMF_NO_VOTE ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "callvote" ) ); - count++; - } - if( ent && g_allowVote.integer && G_admin_permission( ent, ADMF_NO_VOTE ) && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "callteamvote" ) ); - count++; - } - if( ent && g_allowShare.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "donate" ) ); - count++; - } - if( g_privateMessages.integer ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "m" ) ); - count++; - } - if( ent && g_markDeconstruct.integer == 2 && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "mark" ) ); - count++; - } - if( ent && g_actionPrefix.string[0] ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "me" ) ); - count++; - } - if( ent && g_actionPrefix.string[0] ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "me_team" ) ); - count++; - } - if( ent && g_actionPrefix.string[0] && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "mt" ) ); - count++; - } - if( ent && g_myStats.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "mystats" ) ); - count++; - } - if( ent && ent->client->pers.designatedBuilder && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "protect" ) ); - count++; - } - if( ent && ent->client->pers.designatedBuilder && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "resign" ) ); - count++; - } - if( g_publicSayadmins.integer || G_admin_permission( ent, ADMF_ADMINCHAT ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "say_admins" ) ); - count++; - } - if( ent && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "say_area" ) ); - count++; - } - if( ent && g_allowShare.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "share" ) ); - count++; - } - if( ent && g_teamStatus.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "teamstatus" ) ); - count++; - } - ADMBP( "\n" ); + ADMBP( va( "^3adminhelp: ^7%i available commands\n", count ) ); + ADMBP( "run adminhelp [^3command^7] for adminhelp with a specific command.\n" ); ADMBP_end(); return qtrue; } else { - //!help param + // adminhelp param char param[ MAX_ADMIN_CMD_LEN ]; - char *cmd; + g_admin_cmd_t *admincmd; + qboolean denied = qfalse; - G_SayArgv( 1 + skiparg, param, sizeof( param ) ); - cmd = ( param[0] == '!' ) ? ¶m[1] : ¶m[0]; + trap_Argv( 1, param, sizeof( param ) ); ADMBP_begin(); - for( i = 0; i < adminNumCmds; i++ ) + if( ( c = G_admin_command( param ) ) ) { - if( !Q_stricmp( cmd, g_admin_cmds[ i ].keyword ) ) + if( G_admin_permission( ent, c->flag ) ) { - if( !G_admin_permission( ent, g_admin_cmds[ i ].flag ) ) - { - ADMBP( va( "^3!help: ^7you have no permission to use '%s'\n", - g_admin_cmds[ i ].keyword ) ); - ADMBP_end(); - return qfalse; - } - ADMBP( va( "^3!help: ^7help for '!%s':\n", - g_admin_cmds[ i ].keyword ) ); - ADMBP( va( " ^3Function: ^7%s\n", g_admin_cmds[ i ].function ) ); - ADMBP( va( " ^3Syntax: ^7!%s %s\n", g_admin_cmds[ i ].keyword, - g_admin_cmds[ i ].syntax ) ); - ADMBP( va( " ^3Flag: ^7'%s'\n", g_admin_cmds[ i ].flag ) ); - ADMBP_end(); + ADMBP( va( "^3adminhelp: ^7help for '%s':\n", c->command ) ); + ADMBP( va( " ^3Description: ^7%s\n", c->desc ) ); + ADMBP( va( " ^3Syntax: ^7%s\n", c->command ) ); + ADMBP( va( " ^3Flag: ^7'%s'\n", c->flag ) ); + ADMBP_end( ); return qtrue; } + denied = qtrue; } - for( i = 0; i < MAX_ADMIN_COMMANDS && g_admin_commands[ i ]; i++ ) + if( ( admincmd = G_admin_cmd( param ) ) ) { - if( !Q_stricmp( cmd, g_admin_commands[ i ]->command ) ) + if( G_admin_permission( ent, admincmd->flag ) ) { - if( !G_admin_permission( ent, g_admin_commands[ i ]->flag ) ) - { - ADMBP( va( "^3!help: ^7you have no permission to use '%s'\n", - g_admin_commands[ i ]->command ) ); - ADMBP_end(); - return qfalse; - } - ADMBP( va( "^3!help: ^7help for '%s':\n", - g_admin_commands[ i ]->command ) ); - ADMBP( va( " ^3Description: ^7%s\n", g_admin_commands[ i ]->desc ) ); - ADMBP( va( " ^3Syntax: ^7!%s\n", g_admin_commands[ i ]->command ) ); + ADMBP( va( "^3adminhelp: ^7help for '%s':\n", admincmd->keyword ) ); + ADMBP( va( " ^3Description: ^7%s\n", admincmd->function ) ); + ADMBP( va( " ^3Syntax: ^7%s %s\n", admincmd->keyword, + admincmd->syntax ) ); + ADMBP( va( " ^3Flag: ^7'%s'\n", admincmd->flag ) ); ADMBP_end(); return qtrue; } + denied = qtrue; } - ADMBP( va( "^3!help: ^7no help found for '%s'\n", cmd ) ); - ADMBP_end(); + ADMBP( va( "^3adminhelp: ^7%s '%s'\n", + denied ? "you do not have permission to use" : "no help found for", + param ) ); + ADMBP_end( ); return qfalse; } } -qboolean G_admin_admintest( gentity_t *ent, int skiparg ) +qboolean G_admin_admintest( gentity_t *ent ) { - int i, l = 0; - qboolean found = qfalse; - qboolean lname = qfalse; + g_admin_level_t *l; if( !ent ) { - ADMP( "^3!admintest: ^7you are on the console.\n" ); + ADMP( "^3admintest: ^7you are on the console.\n" ); return qtrue; } - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) - { - if( !Q_stricmp( g_admin_admins[ i ]->guid, ent->client->pers.guid ) ) - { - found = qtrue; - break; - } - } - if( found ) - { - l = g_admin_admins[ i ]->level; - for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) - { - if( g_admin_levels[ i ]->level != l ) - continue; - if( *g_admin_levels[ i ]->name ) - { - lname = qtrue; - break; - } - } - } - AP( va( "print \"^3!admintest: ^7%s^7 is a level %d admin %s%s^7%s\n\"", + l = G_admin_level( ent->client->pers.admin ? ent->client->pers.admin->level : 0 ); + + AP( va( "print \"^3admintest: ^7%s^7 is a level %d admin %s%s^7%s\n\"", ent->client->pers.netname, - l, - ( lname ) ? "(" : "", - ( lname ) ? g_admin_levels[ i ]->name : "", - ( lname ) ? ")" : "" ) ); + l ? l->level : 0, + l ? "(" : "", + l ? l->name : "", + l ? ")" : "" ) ); return qtrue; } -qboolean G_admin_allready( gentity_t *ent, int skiparg ) +qboolean G_admin_allready( gentity_t *ent ) { int i = 0; gclient_t *cl; if( !level.intermissiontime ) { - ADMP( "^3!allready: ^7this command is only valid during intermission\n" ); + ADMP( "^3allready: ^7this command is only valid during intermission\n" ); return qfalse; } @@ -5691,233 +2667,54 @@ qboolean G_admin_allready( gentity_t *ent, int skiparg ) if( cl->pers.connected != CON_CONNECTED ) continue; - if( cl->pers.teamSelection == PTE_NONE ) + if( cl->pers.teamSelection == TEAM_NONE ) continue; - cl->readyToExit = 1; - } - AP( va( "print \"^3!allready:^7 %s^7 says everyone is READY now\n\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - return qtrue; -} - -qboolean G_admin_cancelvote( gentity_t *ent, int skiparg ) -{ - - if(!level.voteTime && !level.teamVoteTime[ 0 ] && !level.teamVoteTime[ 1 ] ) - { - ADMP( "^3!cancelvote^7: no vote in progress\n" ); - return qfalse; - } - level.voteNo = level.numConnectedClients; - level.voteYes = 0; - CheckVote( ); - level.teamVoteNo[ 0 ] = level.numConnectedClients; - level.teamVoteYes[ 0 ] = 0; - CheckTeamVote( PTE_HUMANS ); - level.teamVoteNo[ 1 ] = level.numConnectedClients; - level.teamVoteYes[ 1 ] = 0; - CheckTeamVote( PTE_ALIENS ); - AP( va( "print \"^3!cancelvote: ^7%s^7 decided that everyone voted No\n\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - return qtrue; -} - -qboolean G_admin_passvote( gentity_t *ent, int skiparg ) -{ - if(!level.voteTime && !level.teamVoteTime[ 0 ] && !level.teamVoteTime[ 1 ] ) - { - ADMP( "^3!passvote^7: no vote in progress\n" ); - return qfalse; + cl->readyToExit = qtrue; } - level.voteYes = level.numConnectedClients; - level.voteNo = 0; - CheckVote( ); - level.teamVoteYes[ 0 ] = level.numConnectedClients; - level.teamVoteNo[ 0 ] = 0; - CheckTeamVote( PTE_HUMANS ); - level.teamVoteYes[ 1 ] = level.numConnectedClients; - level.teamVoteNo[ 1 ] = 0; - CheckTeamVote( PTE_ALIENS ); - AP( va( "print \"^3!passvote: ^7%s^7 decided that everyone voted Yes\n\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); + AP( va( "print \"^3allready:^7 %s^7 says everyone is READY now\n\"", + ( ent ) ? ent->client->pers.netname : "console" ) ); return qtrue; } -static qboolean G_admin_global_pause( gentity_t *ent, int skiparg ) +qboolean G_admin_endvote( gentity_t *ent ) { - if( level.paused ) - { - level.pauseTime = level.time; - ADMP( "^3!pause: ^7Game is already paused, unpause timer reset\n" ); - return qfalse; - } - - level.paused = qtrue; - level.pauseTime = level.time; - - level.pause_speed = g_speed.value; - level.pause_gravity = g_gravity.value; - level.pause_knockback = g_knockback.value; - level.pause_ff = g_friendlyFire.integer; - level.pause_ffb = g_friendlyBuildableFire.integer; - - g_speed.value = 0; - g_gravity.value = 0; - g_knockback.value = 0; - g_friendlyFire.integer = 0; - g_friendlyBuildableFire.integer = 0; - - CP( "cp \"^1Game is PAUSED\"" ); - AP( va( "print \"^3!pause: ^7The game has been paused by %s\n\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - - return qtrue; -} + char teamName[ sizeof( "spectators" ) ] = {"s"}; + char command[ MAX_ADMIN_CMD_LEN ]; + team_t team; + qboolean cancel; + const char *msg; -static qboolean G_admin_global_unpause( gentity_t *ent, int skiparg ) -{ - if( !level.paused ) + trap_Argv( 0, command, sizeof( command ) ); + cancel = !Q_stricmp( command, "cancelvote" ); + if( trap_Argc() == 2 ) + trap_Argv( 1, teamName, sizeof( teamName ) ); + team = G_TeamFromString( teamName ); + if( team == NUM_TEAMS ) { - ADMP( "^3!unpause: ^7Game is not paused\n" ); + ADMP( va( "^3%s: ^7invalid team '%s'\n", command, teamName ) ); return qfalse; } - - level.paused = qfalse; - - g_speed.value = level.pause_speed; - g_gravity.value = level.pause_gravity; - g_knockback.value = level.pause_knockback; - g_friendlyFire.integer = level.pause_ff; - g_friendlyBuildableFire.integer = level.pause_ffb; - - CP( "cp \"^2Game is RESUMED\"" ); - AP( va( "print \"^3!unpause: ^7The game has been resumed by %s\n\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - - return qtrue; -} - -static qboolean is_numeric(const char *str) -{ - const char *p; - - if (!*str) - return qfalse; - - for (p = str; *p; p++) - if (*p < '0' || *p > '9') - return qfalse; - - return qtrue; -} - -qboolean G_admin_pause( gentity_t *ent, int skiparg ) -{ - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; - char command[ MAX_ADMIN_CMD_LEN ], *cmd; - int i; - int count = 0; - gentity_t *vic; - - G_SayArgv( skiparg, command, sizeof( command ) ); - cmd = command; - if( cmd && *cmd == '!' ) - cmd++; - - if( G_SayArgc() < 2 + skiparg ) - { - // global pause - if( !Q_stricmp( cmd, "pause" ) ) - return G_admin_global_pause( ent, skiparg ); - else - return G_admin_global_unpause( ent, skiparg ); - } - if( G_SayArgc() != 2 + skiparg ) + msg = va( "print \"^3%s: ^7%s^7 decided that everyone voted %s\n\"", + command, ( ent ) ? ent->client->pers.netname : "console", + cancel ? "No" : "Yes" ); + if( !level.voteTime[ team ] ) { - ADMP( va( "^3!%s: ^7usage: !%s (^5name|slot|*^7)\n", cmd, cmd ) ); + ADMP( va( "^3%s: ^7no vote in progress\n", command ) ); return qfalse; } - - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - if( !Q_stricmp( name, "*" ) ) - { - for( i = 0; i < MAX_CLIENTS; i++ ) - { - vic = &g_entities[ i ]; - if( vic && vic->client && - vic->client->pers.connected == CON_CONNECTED ) - { - pids[ count ] = i; - count++; - } - } - } + admin_log( BG_TeamName( team ) ); + level.voteNo[ team ] = cancel ? level.numVotingClients[ team ] : 0; + level.voteYes[ team ] = cancel ? 0 : level.numVotingClients[ team ]; + G_CheckVote( team ); + if( team == TEAM_NONE ) + AP( msg ); else - { - if( G_ClientNumbersFromString( name, pids ) != 1 ) - { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!%s: ^7%s\n", cmd, err ) ); - return qfalse; - } - count = 1; - } - - for( i = 0; i < count; i++ ) - { - vic = &g_entities[ pids[ i ] ]; - if ( !vic || !vic->client ) continue; - if( !admin_higher( ent, vic ) ) - { - if( count == 1 ) - ADMP( va( "^3!%s: ^7sorry, but your intended victim has a higher admin" - " level than you\n", cmd ) ); - continue; - } - if( vic->client->pers.paused ) - { - if( !Q_stricmp( cmd, "pause" ) ) - { - if( count == 1 ) - ADMP( "^3!pause: ^7player is already paused\n" ); - continue; - } - vic->client->pers.paused = qfalse; - CPx( pids[ i ], "cp \"^2You've been unpaused\"" ); - if( count == 1 ) - AP( va( "print \"^3!unpause: ^7%s^7 unpaused by %s\n\"", - vic->client->pers.netname, - ( ent ) ? ent->client->pers.netname : "console" ) ); - } - else - { - if( !Q_stricmp( cmd, "unpause" ) ) - { - if( count == 1 ) - ADMP( "^3!unpause: ^7player is already unpaused\n" ); - continue; - } - if( count == 1 && ent == vic) - { - ADMP( "^3!pause: ^7you can not pause yourself\n" ); - continue; - } - vic->client->pers.paused = qtrue; - CPx( pids[ i ], va( "cp \"^1You've been paused by ^7%s\"", - ( ent ) ? ent->client->pers.netname : "console" ) ); - if( count == 1 ) - AP( va( "print \"^3!pause: ^7%s^7 paused by %s\n\"", - vic->client->pers.netname, - ( ent ) ? ent->client->pers.netname : "console" ) ); - } - ClientUserinfoChanged( pids[ i ], qfalse ); - } + G_TeamCommand( team, msg ); return qtrue; } -qboolean G_admin_spec999( gentity_t *ent, int skiparg ) +qboolean G_admin_spec999( gentity_t *ent ) { int i; gentity_t *vic; @@ -5929,146 +2726,186 @@ qboolean G_admin_spec999( gentity_t *ent, int skiparg ) continue; if( vic->client->pers.connected != CON_CONNECTED ) continue; - if( vic->client->pers.teamSelection == PTE_NONE ) + if( vic->client->pers.teamSelection == TEAM_NONE ) continue; if( vic->client->ps.ping == 999 ) { - G_ChangeTeam( vic, PTE_NONE ); - AP( va( "print \"^3!spec999: ^7%s^7 moved ^7%s^7 to spectators\n\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console", + G_ChangeTeam( vic, TEAM_NONE ); + AP( va( "print \"^3spec999: ^7%s^7 moved %s^7 to spectators\n\"", + ( ent ) ? ent->client->pers.netname : "console", vic->client->pers.netname ) ); } } return qtrue; } + +qboolean G_admin_transform( gentity_t *ent ) +{ + int pid; + char name[ MAX_NAME_LENGTH ]; + char modelname[ MAX_NAME_LENGTH ]; + char skin[ MAX_NAME_LENGTH ]; + char err[ MAX_STRING_CHARS ]; + char userinfo[ MAX_INFO_STRING ]; + gentity_t *victim = NULL; + int i; + qboolean found = qfalse; + + if (trap_Argc() < 3) + { + ADMP("^3transform: ^7usage: transform [name|slot#] [model] <skin>\n"); + return qfalse; + } -qboolean G_admin_register(gentity_t *ent, int skiparg ){ - int level = 0; + trap_Argv(1, name, sizeof(name)); + trap_Argv(2, modelname, sizeof(modelname)); - if( !ent ) return qtrue; + strcpy(skin, "default"); + if (trap_Argc() >= 4) + { + trap_Argv(1, skin, sizeof(skin)); + } - level = G_admin_level(ent); + pid = G_ClientNumberFromString(name, err, sizeof(err)); + if (pid == -1) + { + ADMP(va("^3transform: ^7%s", err)); + return qfalse; + } - if( level == 0 ) - level = 1; - - if( !Q_stricmp( ent->client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ) ) + victim = &g_entities[ pid ]; + if (victim->client->pers.connected != CON_CONNECTED) { - ADMP( va( "^3!register: ^7 You cannot register for name protection until you update your client. Please replace your client executable with the one at http://trem.tjw.org/backport/ and reconnect. Updating your client will also allow you to have faster map downloads.\n" ) ); + ADMP("^3transform: ^7sorry, but your intended victim is still connecting\n"); return qfalse; } - if( g_newbieNumbering.integer - && g_newbieNamePrefix.string[ 0 ] - && !Q_stricmpn ( ent->client->pers.netname, g_newbieNamePrefix.string, strlen(g_newbieNamePrefix.string ) ) ) + for ( i = 0; i < level.playerModelCount; i++ ) { - ADMP( va( "^3!register: ^7 You cannot register names that begin with '%s^7'.\n", - g_newbieNamePrefix.string ) ); + if ( !strcmp(modelname, level.playerModel[i]) ) + { + found = qtrue; + break; + } + } + + if (!found) + { + ADMP(va("^3transform: ^7no matching model %s\n", modelname)); return qfalse; } - trap_SendConsoleCommand( EXEC_APPEND,va( "!setlevel %d %d;",ent - g_entities, level) ); - - AP( va( "print \"^3!register: ^7%s^7 is now a protected nickname.\n\"", ent->client->pers.netname) ); - + trap_GetUserinfo(pid, userinfo, sizeof(userinfo)); + AP( va("print \"^3transform: ^7%s^7 has been changed into %s^7 by %s\n\"", + victim->client->pers.netname, modelname, + (ent ? ent->client->pers.netname : "console")) ); + + Info_SetValueForKey( userinfo, "model", modelname ); + Info_SetValueForKey( userinfo, "skin", GetSkin(modelname, skin)); + Info_SetValueForKey( userinfo, "voice", modelname ); + trap_SetUserinfo( pid, userinfo ); + ClientUserinfoChanged( pid, qtrue ); + return qtrue; } -qboolean G_admin_rename( gentity_t *ent, int skiparg ) +qboolean G_admin_rename( gentity_t *ent ) { - int pids[ MAX_CLIENTS ]; + int pid; char name[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; - char oldname[ MAX_NAME_LENGTH ]; char err[ MAX_STRING_CHARS ]; char userinfo[ MAX_INFO_STRING ]; - char *s; gentity_t *victim = NULL; - if( G_SayArgc() < 3 + skiparg ) + if( trap_Argc() < 3 ) { - ADMP( "^3!rename: ^7usage: !rename [name] [newname]\n" ); + ADMP( "^3rename: ^7usage: rename [name] [newname]\n" ); return qfalse; } - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - s = G_SayConcatArgs( 2 + skiparg ); - Q_strncpyz( newname, s, sizeof( newname ) ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) + trap_Argv( 1, name, sizeof( name ) ); + Q_strncpyz( newname, ConcatArgs( 2 ), sizeof( newname ) ); + if( ( pid = G_ClientNumberFromString( name, err, sizeof( err ) ) ) == -1 ) { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!rename: ^7%s\n", err ) ); + ADMP( va( "^3rename: ^7%s", err ) ); return qfalse; } - victim = &g_entities[ pids[ 0 ] ] ; + victim = &g_entities[ pid ]; if( !admin_higher( ent, victim ) ) { - ADMP( "^3!rename: ^7sorry, but your intended victim has a higher admin" + ADMP( "^3rename: ^7sorry, but your intended victim has a higher admin" " level than you\n" ); return qfalse; } if( !G_admin_name_check( victim, newname, err, sizeof( err ) ) ) { - G_admin_print( ent, va( "^3!rename: Invalid name: ^7%s\n", err ) ); + ADMP( va( "^3rename: ^7%s\n", err ) ); + return qfalse; + } + if( victim->client->pers.connected != CON_CONNECTED ) + { + ADMP( "^3rename: ^7sorry, but your intended victim is still connecting\n" ); return qfalse; } - level.clients[ pids[ 0 ] ].pers.nameChanges--; - level.clients[ pids[ 0 ] ].pers.nameChangeTime = 0; - trap_GetUserinfo( pids[ 0 ], userinfo, sizeof( userinfo ) ); - s = Info_ValueForKey( userinfo, "name" ); - Q_strncpyz( oldname, s, sizeof( oldname ) ); + admin_log( va( "%d (%s) \"%s" S_COLOR_WHITE "\"", pid, + victim->client->pers.guid, victim->client->pers.netname ) ); + admin_log( newname ); + trap_GetUserinfo( pid, userinfo, sizeof( userinfo ) ); + AP( va( "print \"^3rename: ^7%s^7 has been renamed to %s^7 by %s\n\"", + victim->client->pers.netname, + newname, + ( ent ) ? ent->client->pers.netname : "console" ) ); Info_SetValueForKey( userinfo, "name", newname ); - trap_SetUserinfo( pids[ 0 ], userinfo ); - ClientUserinfoChanged( pids[ 0 ], qtrue ); - if( strcmp( oldname, level.clients[ pids[ 0 ] ].pers.netname ) ) - AP( va( "print \"^3!rename: ^7%s^7 has been renamed to %s^7 by %s\n\"", - oldname, - level.clients[ pids[ 0 ] ].pers.netname, - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); + trap_SetUserinfo( pid, userinfo ); + ClientUserinfoChanged( pid, qtrue ); return qtrue; } -qboolean G_admin_restart( gentity_t *ent, int skiparg ) +qboolean G_admin_restart( gentity_t *ent ) { - char layout[ MAX_CVAR_VALUE_STRING ] = { "" }; - char teampref[ MAX_CVAR_VALUE_STRING ] = { "" }; - int i; + char layout[ MAX_CVAR_VALUE_STRING ] = { "" }; + char teampref[ MAX_STRING_CHARS ] = { "" }; + char map[ MAX_CVAR_VALUE_STRING ]; + int i; gclient_t *cl; - if( G_SayArgc( ) > 1 + skiparg ) + if( trap_Argc( ) > 1 ) { char map[ MAX_QPATH ]; trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) ); - G_SayArgv( skiparg + 1, layout, sizeof( layout ) ); + trap_Argv( 1, layout, sizeof( layout ) ); - if( Q_stricmp( layout, "keepteams" ) && Q_stricmp( layout, "keepteamslock" ) && Q_stricmp( layout, "switchteams" ) && Q_stricmp( layout, "switchteamslock" ) ) + // Figure out which argument is which + if( Q_stricmp( layout, "keepteams" ) && + Q_stricmp( layout, "keepteamslock" ) && + Q_stricmp( layout, "switchteams" ) && + Q_stricmp( layout, "switchteamslock" ) ) { - if( !Q_stricmp( layout, "*BUILTIN*" ) || - trap_FS_FOpenFile( va( "layouts/%s/%s.dat", map, layout ), - NULL, FS_READ ) > 0 ) + if( G_LayoutExists( map, layout ) ) { - trap_Cvar_Set( "g_layouts", layout ); + trap_Cvar_Set( "g_nextLayout", layout ); } else { - ADMP( va( "^3!restart: ^7layout '%s' does not exist\n", layout ) ); + ADMP( va( "^3restart: ^7layout '%s' does not exist\n", layout ) ); return qfalse; } } - else + else { - strcpy(layout,""); - G_SayArgv( skiparg + 1, teampref, sizeof( teampref ) ); + layout[ 0 ] = '\0'; + trap_Argv( 1, teampref, sizeof( teampref ) ); } } - - if( G_SayArgc( ) > 2 + skiparg ) - { - G_SayArgv( skiparg + 2, teampref, sizeof( teampref ) ); - } - - - if( !Q_stricmp( teampref, "keepteams" ) || !Q_stricmp( teampref, "keepteamslock" ) ) + + if( trap_Argc( ) > 2 ) + trap_Argv( 2, teampref, sizeof( teampref ) ); + + admin_log( layout ); + admin_log( teampref ); + + if( !Q_stricmpn( teampref, "keepteams", 9 ) ) { for( i = 0; i < g_maxclients.integer; i++ ) { @@ -6076,2349 +2913,716 @@ qboolean G_admin_restart( gentity_t *ent, int skiparg ) if( cl->pers.connected != CON_CONNECTED ) continue; - if( cl->pers.teamSelection == PTE_NONE ) + if( cl->pers.teamSelection == TEAM_NONE ) continue; cl->sess.restartTeam = cl->pers.teamSelection; } - } - else if(!Q_stricmp( teampref, "switchteams" ) || !Q_stricmp( teampref, "switchteamslock" )) + } + else if( !Q_stricmpn( teampref, "switchteams", 11 ) ) { for( i = 0; i < g_maxclients.integer; i++ ) { cl = level.clients + i; - if( cl->pers.connected != CON_CONNECTED ) - continue; - if( cl->pers.teamSelection == PTE_NONE ) + if( cl->pers.connected != CON_CONNECTED ) continue; - if( cl->pers.teamSelection == PTE_ALIENS ) - cl->sess.restartTeam = PTE_HUMANS; - else if(cl->pers.teamSelection == PTE_HUMANS ) - cl->sess.restartTeam = PTE_ALIENS; - } + if( cl->pers.teamSelection == TEAM_HUMANS ) + cl->sess.restartTeam = TEAM_ALIENS; + else if(cl->pers.teamSelection == TEAM_ALIENS ) + cl->sess.restartTeam = TEAM_HUMANS; + } } - - if( !Q_stricmp( teampref, "switchteamslock" ) || !Q_stricmp( teampref, "keepteamslock" ) ) - { + + if( !Q_stricmp( teampref, "switchteamslock" ) || + !Q_stricmp( teampref, "keepteamslock" ) ) trap_Cvar_Set( "g_lockTeamsAtStart", "1" ); - } - trap_SendConsoleCommand( EXEC_APPEND, "map_restart" ); - - if(teampref[ 0 ]) - strcpy(teampref,va( "^7(with teams option: '%s^7')", teampref )); - - G_admin_maplog_result( "R" ); - - AP( va( "print \"^3!restart: ^7map restarted by %s %s %s\n\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console", + trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) ); + G_MapConfigs( map ); + trap_SendConsoleCommand( EXEC_APPEND, "map_restart\n" ); + + AP( va( "print \"^3restart: ^7map restarted by %s %s %s\n\"", + ( ent ) ? ent->client->pers.netname : "console", ( layout[ 0 ] ) ? va( "^7(forcing layout '%s^7')", layout ) : "", - teampref ) ); + ( teampref[ 0 ] ) ? va( "^7(with teams option: '%s^7')", teampref ) : "" ) ); return qtrue; } -qboolean G_admin_nobuild( gentity_t *ent, int skiparg ) +qboolean G_admin_nextmap( gentity_t *ent ) { - char buffer[ MAX_STRING_CHARS ]; - int i, tmp; - - if( G_SayArgc() < 2 + skiparg ) - { - ADMP( "^3!nobuild: ^7usage: !nobuild (^5enable / disable / log / remove / save^7)\n" ); + if( level.exited ) return qfalse; - } - - G_SayArgv( 1 + skiparg, buffer, sizeof( buffer ) ); - - if( !Q_stricmp( buffer, "enable" ) || !Q_stricmp( buffer, "Enable" ) ) - { - if( G_SayArgc() < 4 + skiparg ) - { - ADMP( "^3!nobuild: ^7usage: !nobuild enable (^5area^7) (^5height^7)\n" ); - return qfalse; - } - - if( !level.noBuilding ) - { - - level.noBuilding = qtrue; - - // Grab and set the area - G_SayArgv( 2 + skiparg, buffer, sizeof( buffer ) ); - tmp = atoi( buffer ); - level.nbArea = tmp; - - // Grab and set the height - G_SayArgv( 3 + skiparg, buffer, sizeof( buffer ) ); - tmp = atoi( buffer ); - level.nbHeight = tmp; - - ADMP( "^3!nobuild: ^7nobuild is now enabled, please place a buildable to spawn a marker\n" ); - return qtrue; - } - else - { - ADMP( "^3!nobuild: ^7nobuild is already enabled. type !nobuild disable to disable nobuild mode.\n" ); - return qfalse; - } - } - - if( !Q_stricmp( buffer, "disable" ) || !Q_stricmp( buffer, "Disable" ) ) - { - if( level.noBuilding ) - { - level.noBuilding = qfalse; - level.nbArea = 0.0f; - level.nbHeight = 0.0f; - ADMP( "^3!nobuild: ^7nobuild is now disabled\n" ); - return qtrue; - } - else - { - ADMP( "^3!nobuild: ^7nobuild is disabled. type !nobuild enable (^5area^7) (^5height^7) to enable nobuild mode.\n" ); - return qfalse; - } - } - - if( !Q_stricmp( buffer, "log" ) || !Q_stricmp( buffer, "Log" ) ) - { - ADMBP_begin(); - - tmp = 0; - for( i = 0; i < MAX_GENTITIES; i++ ) - { - if( level.nbMarkers[ i ].Marker == NULL ) - continue; - - // This will display a start at 1, and not 0. This is to stop confusion by server ops - ADMBP( va( "^7#%i at origin (^5%f %f %f^7)^7\n", i + 1, level.nbMarkers[ i ].Origin[0], level.nbMarkers[ i ].Origin[1], level.nbMarkers[ i ].Origin[2] ) ); - tmp++; - } - ADMBP( va( "^3!nobuild:^7 displaying %i marker(s)\n", tmp ) ); - - ADMBP_end(); - return qtrue; - } - - if( !Q_stricmp( buffer, "remove" ) || !Q_stricmp( buffer, "Remove" ) ) - { - if( G_SayArgc() < 3 + skiparg ) - { - ADMP( "^3!nobuild: ^7usage: !nobuild remove (^5marker #^7)\n" ); - return qfalse; - } - - G_SayArgv( 2 + skiparg, buffer, sizeof( buffer ) ); - tmp = atoi( buffer ) - 1; - // ^ that was to work with the display number... - - if( level.nbMarkers[ tmp ].Marker == NULL ) - { - ADMP( "^3!nobuild: ^7that is not a valid marker number\n" ); - return qfalse; - } - // Donno why im doing this, but lets clear this... - level.nbMarkers[ tmp ].Marker->noBuild.isNB = qfalse; - level.nbMarkers[ tmp ].Marker->noBuild.Area = 0.0f; - level.nbMarkers[ tmp ].Marker->noBuild.Height = 0.0f; - - // Free the entitiy and null it out... - G_FreeEntity( level.nbMarkers[ tmp ].Marker ); - level.nbMarkers[ tmp ].Marker = NULL; - - // That is to work with the display number... - ADMP( va( "^3!nobuild:^7 marker %i has been removed\n", tmp + 1 ) ); - return qtrue; - } - - if( !Q_stricmp( buffer, "save" ) || !Q_stricmp( buffer, "Save" ) ) - { - int i, tmp; - - G_NobuildSave( ); - - tmp = 0; - for( i = 0; i < MAX_GENTITIES; i++ ) - { - if( level.nbMarkers[ i ].Marker == NULL ) - continue; - - tmp++; - } - ADMP( va( "^3!nobuild:^7 %i nobuild markers have been saved\n", tmp ) ); - return qtrue; - } - return qfalse; -} - -qboolean G_admin_nextmap( gentity_t *ent, int skiparg ) -{ - AP( va( "print \"^3!nextmap: ^7%s^7 decided to load the next map\n\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - level.lastWin = PTE_NONE; + AP( va( "print \"^3nextmap: ^7%s^7 decided to load the next map\n\"", + ( ent ) ? ent->client->pers.netname : "console" ) ); + level.lastWin = TEAM_NONE; trap_SetConfigstring( CS_WINNER, "Evacuation" ); LogExit( va( "nextmap was run by %s", - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - G_admin_maplog_result( "N" ); + ( ent ) ? ent->client->pers.netname : "console" ) ); return qtrue; } -static const char *displaySchachtmeisterJudgement(const schachtmeisterJudgement_t *j, - g_admin_namelog_t *namelog) -{ - static char buffer[20]; - - if (G_admin_permission_guid(namelog->guid, ADMF_INCOGNITO)) { - Com_sprintf(buffer, sizeof(buffer), "^2 +0"); - } else if (strcmp(namelog->guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") && - (G_admin_permission_guid(namelog->guid, ADMF_NOAUTOBAHN) || - G_admin_permission_guid(namelog->guid, ADMF_IMMUNITY))) { - Com_sprintf(buffer, sizeof(buffer), "^7 N/A "); - } else if (!j->ratingTime) { - Com_sprintf(buffer, sizeof(buffer), "^5 wait"); - } else { - int color; - - if (j->rating >= g_schachtmeisterClearThreshold.integer) - color = 2; - else if (j->rating <= g_schachtmeisterAutobahnThreshold.integer) - color = 1; - else - color = 3; - - Com_sprintf(buffer, sizeof(buffer), "^%i%+5i", color, j->rating); - } - - return buffer; -} - -qboolean G_admin_namelog( gentity_t *ent, int skiparg ) +qboolean G_admin_setnextmap( gentity_t *ent ) { - int i, j; - char search[ MAX_NAME_LENGTH ] = {""}; - char s2[ MAX_NAME_LENGTH ] = {""}; - char n2[ MAX_NAME_LENGTH ] = {""}; - char guid_stub[ 9 ]; - qboolean found = qfalse; - int printed = 0; + int argc = trap_Argc(); + char map[ MAX_QPATH ]; + char layout[ MAX_QPATH ]; - if( G_SayArgc() > 1 + skiparg ) + if( argc < 2 || argc > 3 ) { - G_SayArgv( 1 + skiparg, search, sizeof( search ) ); - G_SanitiseString( search, s2, sizeof( s2 ) ); + ADMP( "^3setnextmap: ^7usage: setnextmap [map] (layout)\n" ); + return qfalse; } - ADMBP_begin(); - for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) - { - if( search[0] ) - { - found = qfalse; - for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && - g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ ) - { - G_SanitiseString( g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) ); - if( strstr( n2, s2 ) ) - { - found = qtrue; - break; - } - } - if( !found ) - continue; - } - printed++; - for( j = 0; j < 8; j++ ) - guid_stub[ j ] = g_admin_namelog[ i ]->guid[ j + 24 ]; - guid_stub[ j ] = '\0'; - if( g_admin_namelog[ i ]->slot > -1 ) - ADMBP( "^3" ); - ADMBP( va( "%-2s (*%s) %15s %s^7", - (g_admin_namelog[ i ]->slot > -1 ) ? - va( "%d", g_admin_namelog[ i ]->slot ) : "-", - guid_stub, g_admin_namelog[ i ]->ip, - displaySchachtmeisterJudgement( &g_admin_namelog[ i ]->smj, g_admin_namelog[ i ] ) ) ); - for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && - g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ ) - { - ADMBP( va( " '%s^7'", g_admin_namelog[ i ]->name[ j ] ) ); - } - ADMBP( "\n" ); - } - ADMBP( va( "^3!namelog:^7 %d recent clients found\n", printed ) ); - ADMBP_end(); - return qtrue; -} -qboolean G_admin_lock( gentity_t *ent, int skiparg ) -{ - char teamName[2] = {""}; - pTeam_t team; + trap_Argv( 1, map, sizeof( map ) ); - if( G_SayArgc() < 2 + skiparg ) + if( !G_MapExists( map ) ) { - ADMP( "^3!lock: ^7usage: !lock [a|h]\n" ); - return qfalse; - } - G_SayArgv( 1 + skiparg, teamName, sizeof( teamName ) ); - if( teamName[ 0 ] == 'a' || teamName[ 0 ] == 'A' ) - team = PTE_ALIENS; - else if( teamName[ 0 ] == 'h' || teamName[ 0 ] == 'H' ) - team = PTE_HUMANS; - else - { - ADMP( va( "^3!lock: ^7invalid team\"%c\"\n", teamName[0] ) ); + ADMP( va( "^3setnextmap: ^7map '%s' does not exist\n", map ) ); return qfalse; } - if( team == PTE_ALIENS ) + if( argc > 2 ) { - if( level.alienTeamLocked ) - { - ADMP( "^3!lock: ^7Alien team is already locked\n" ); - return qfalse; - } - else - level.alienTeamLocked = qtrue; - } - else if( team == PTE_HUMANS ) { - if( level.humanTeamLocked ) + trap_Argv( 2, layout, sizeof( layout ) ); + + if( !G_LayoutExists( map, layout ) ) { - ADMP( "^3!lock: ^7Human team is already locked\n" ); + ADMP( va( "^3setnextmap: ^7layout '%s' does not exist for map '%s'\n", layout, map ) ); return qfalse; } - else - level.humanTeamLocked = qtrue; - } - AP( va( "print \"^3!lock: ^7%s team has been locked by %s\n\"", - ( team == PTE_ALIENS ) ? "Alien" : "Human", - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - return qtrue; -} - -qboolean G_admin_unlock( gentity_t *ent, int skiparg ) -{ - char teamName[2] = {""}; - pTeam_t team; - - if( G_SayArgc() < 2 + skiparg ) - { - ADMP( "^3!unlock: ^7usage: !unlock [a|h]\n" ); - return qfalse; + trap_Cvar_Set( "g_nextLayout", layout ); } - G_SayArgv( 1 + skiparg, teamName, sizeof( teamName ) ); - if( teamName[ 0 ] == 'a' || teamName[ 0 ] == 'A' ) - team = PTE_ALIENS; - else if( teamName[ 0 ] == 'h' || teamName[ 0 ] == 'H' ) - team = PTE_HUMANS; else - { - ADMP( va( "^3!unlock: ^7invalid team\"%c\"\n", teamName[0] ) ); - return qfalse; - } - - if( team == PTE_ALIENS ) - { - if( !level.alienTeamLocked ) - { - ADMP( "^3!unlock: ^7Alien team is not currently locked\n" ); - return qfalse; - } - else - level.alienTeamLocked = qfalse; - } - else if( team == PTE_HUMANS ) { - if( !level.humanTeamLocked ) - { - ADMP( "^3!unlock: ^7Human team is not currently locked\n" ); - return qfalse; - } - else - level.humanTeamLocked = qfalse; - } + trap_Cvar_Set( "g_nextLayout", "" ); + + trap_Cvar_Set( "g_nextMap", map ); - AP( va( "print \"^3!unlock: ^7%s team has been unlocked by %s\n\"", - ( team == PTE_ALIENS ) ? "Alien" : "Human", - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); + AP( va( "print \"^3setnextmap: ^7%s^7 has set the next map to '%s'%s\n\"", + ( ent ) ? ent->client->pers.netname : "console", map, + argc > 2 ? va( " with layout '%s'", layout ) : "" ) ); return qtrue; -} +} -qboolean G_admin_designate( gentity_t *ent, int skiparg ) +static qboolean namelog_matchip( void *namelog, const void *ip ) { - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; - char command[ MAX_ADMIN_CMD_LEN ], *cmd; - gentity_t *vic; - - if( G_SayArgc() < 2 + skiparg ) - { - ADMP( "^3!designate: ^7usage: designate [name|slot#]\n" ); - return qfalse; - } - G_SayArgv( skiparg, command, sizeof( command ) ); - cmd = command; - if( cmd && *cmd == '!' ) - cmd++; - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) - { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!designate: ^7%s\n", err ) ); - return qfalse; - } - if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) && - !Q_stricmp( cmd, "undesignate" ) ) - { - ADMP( "^3!designate: ^7sorry, but your intended victim has a higher admin" - " level than you\n" ); - return qfalse; - } - vic = &g_entities[ pids[ 0 ] ]; - if( vic->client->pers.designatedBuilder == qtrue ) + int i; + namelog_t *n = (namelog_t *)namelog; + for( i = 0; i < MAX_NAMELOG_ADDRS && n->ip[ i ].str[ 0 ]; i++ ) { - if( !Q_stricmp( cmd, "designate" ) ) - { - ADMP( "^3!designate: ^7player is already designated builder\n" ); + if( G_AddressCompare( &n->ip[ i ], (addr_t *)ip ) || + G_AddressCompare( (addr_t *)ip, &n->ip[ i ] ) ) return qtrue; - } - vic->client->pers.designatedBuilder = qfalse; - CPx( pids[ 0 ], "cp \"^1Your designation has been revoked\"" ); - AP( va( - "print \"^3!designate: ^7%s^7's designation has been revoked by %s\n\"", - vic->client->pers.netname, - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - G_CheckDBProtection( ); } - else + return qfalse; +} +static qboolean namelog_matchname( void *namelog, const void *name ) +{ + char match[ MAX_NAME_LENGTH ]; + int i; + namelog_t *n = (namelog_t *)namelog; + for( i = 0; i < MAX_NAMELOG_NAMES && n->name[ i ][ 0 ]; i++ ) { - if( !Q_stricmp( cmd, "undesignate" ) ) - { - ADMP( "^3!undesignate: ^7player is not currently designated builder\n" ); + G_SanitiseString( n->name[ i ], match, sizeof( match ) ); + if( strstr( match, (const char *)name ) ) return qtrue; - } - vic->client->pers.designatedBuilder = qtrue; - CPx( pids[ 0 ], "cp \"^1You've been designated\"" ); - AP( va( "print \"^3!designate: ^7%s^7 has been designated by ^7%s\n\"", - vic->client->pers.netname, - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); } - return qtrue; + return qfalse; } +static void namelog_out( void *namelog, char *str ) +{ + namelog_t *n = (namelog_t *)namelog; + char *p = str; + int l, l2 = MAX_STRING_CHARS, i; + const char *scolor; - //!Warn by Gate (Daniel Evans) -qboolean G_admin_warn( gentity_t *ent, int skiparg ) -{//mostly copy and paste with the proper lines altered from !mute and !kick - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ], *reason, err[ MAX_STRING_CHARS ]; - int minargc; - gentity_t *vic; - - minargc = 3 + skiparg; - if( G_admin_permission( ent, ADMF_UNACCOUNTABLE ) ) - minargc = 2 + skiparg; - - if( G_SayArgc() < minargc ) + if( n->slot > -1 ) { - ADMP( "^3!warn: ^7usage: warn [name] [reason]\n" ); - return qfalse; + scolor = S_COLOR_YELLOW; + l = Q_snprintf( p, l2, "%s%-2d", scolor, n->slot ); + p += l; + l2 -= l; } - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - reason = G_SayConcatArgs( 2 + skiparg ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) - { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!warn: ^7%s\n", err ) ); - return qfalse; - } - if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) + else { - ADMP( "^3!warn: ^7sorry, but your intended victim has a higher admin" - " level than you.\n" ); - return qfalse; + *p++ = '-'; + *p++ = ' '; + *p = '\0'; + l2 -= 2; + scolor = S_COLOR_WHITE; } - - vic = &g_entities[ pids[ 0 ] ]; - //next line is the onscreen warning - CPx( pids[ 0 ],va("cp \"^1You have been warned by an administrator.\n ^3Cease immediately or face admin action!\n^1 %s%s\"",(*reason)? "REASON: " : "" ,(*reason)? reason : "") ); - AP( va( "print \"^3!warn: ^7%s^7 has been warned to cease and desist: %s by %s \n\"", - vic->client->pers.netname, (*reason) ? reason : "his current activity", - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );//console announcement - return qtrue; -} - -qboolean G_admin_putmespec( gentity_t *ent, int skiparg ) -{ - if( !ent ) + + for( i = 0; i < MAX_NAMELOG_ADDRS && n->ip[ i ].str[ 0 ]; i++ ) { - ADMP( "!specme: sorry, but console isn't allowed on the spectators team\n"); - return qfalse; + l = Q_snprintf( p, l2, " %s", n->ip[ i ].str ); + p += l; + l2 -= l; } - if( level.paused ) + for( i = 0; i < MAX_NAMELOG_NAMES && n->name[ i ][ 0 ]; i++ ) { - ADMP("!specme: disabled when game is paused\n"); - return qfalse; - } - - if(ent->client->pers.teamSelection == PTE_NONE) - return qfalse; - - //guard against build timer exploit - if( ent->client->pers.teamSelection != PTE_NONE && ent->client->sess.sessionTeam != TEAM_SPECTATOR && - ( ent->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0 || - ent->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0_UPG || - BG_InventoryContainsWeapon( WP_HBUILD, ent->client->ps.stats ) || - BG_InventoryContainsWeapon( WP_HBUILD2, ent->client->ps.stats ) ) && - ent->client->ps.stats[ STAT_MISC ] > 0 ) - { - ADMP("!specme: You cannot leave your team until the build timer expires"); - return qfalse; + l = Q_snprintf( p, l2, " '" S_COLOR_WHITE "%s%s'%s", n->name[ i ], scolor, + i == n->nameOffset ? "*" : "" ); + p += l; + l2 -= l; } - - G_ChangeTeam( ent, PTE_NONE ); - AP( va("print \"^3!specme: ^7%s^7 decided to join the spectators\n\"", ent->client->pers.netname ) ); - return qtrue; } - -qboolean G_admin_slap( gentity_t *ent, int skiparg ) +qboolean G_admin_namelog( gentity_t *ent ) { - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; - gentity_t *vic; - vec3_t dir; - - if( level.intermissiontime ) return qfalse; + char search[ MAX_NAME_LENGTH ] = {""}; + char s2[ MAX_NAME_LENGTH ] = {""}; + addr_t ip; + qboolean ipmatch = qfalse; + int start = MAX_CLIENTS, i; - if( G_SayArgc() < 2 + skiparg ) + if( trap_Argc() == 3 ) { - ADMP( "^3!slap: ^7usage: !slap [name|slot#]\n" ); - return qfalse; + trap_Argv( 2, search, sizeof( search ) ); + start = atoi( search ); } - - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) + if( trap_Argc() > 1 ) { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!slap: ^7%s\n", err ) ); - return qfalse; + trap_Argv( 1, search, sizeof( search ) ); + i = 0; + if( trap_Argc() == 2 ) + for( i = search[ 0 ] == '-'; isdigit( search[ i ] ); i++ ); + if( !search[ i ] ) + start = atoi( search ); + else if( !( ipmatch = G_AddressParse( search, &ip ) ) ) + G_SanitiseString( search, s2, sizeof( s2 ) ); } - vic = &g_entities[ pids[ 0 ] ]; - if( !vic ) - { - ADMP( "^3!slap: ^7bad target\n" ); - return qfalse; - } - if( vic == ent ) - { - ADMP( "^3!slap: ^7sorry, you cannot slap yourself\n" ); - return qfalse; - } - if( !admin_higher( ent, vic ) ) - { - ADMP( "^3!slap: ^7sorry, but your intended victim has a higher admin" - " level than you\n" ); - return qfalse; - } - if( vic->client->pers.teamSelection == PTE_NONE || - vic->client->pers.classSelection == PCL_NONE ) - { - ADMP( "^3!slap: ^7can't slap spectators\n" ); - return qfalse; - } + admin_search( ent, "namelog", "recent players", + ipmatch ? namelog_matchip : namelog_matchname, namelog_out, level.namelogs, + ipmatch ? (void *)&ip : s2, start, MAX_CLIENTS, MAX_ADMIN_LISTITEMS ); + return qtrue; +} - // knockback in a random direction - dir[0] = crandom(); - dir[1] = crandom(); - dir[2] = random(); - G_Knockback( vic, dir, g_slapKnockback.integer ); +/* +================== +G_NamelogFromString - trap_SendServerCommand( vic-g_entities, - va( "cp \"%s^7 is not amused\n\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); +This is similar to G_ClientNumberFromString but for namelog instead +Returns NULL if not exactly 1 match +================== +*/ +namelog_t *G_NamelogFromString( gentity_t *ent, char *s ) +{ + namelog_t *p, *m = NULL; + int i, found = 0; + char n2[ MAX_NAME_LENGTH ] = {""}; + char s2[ MAX_NAME_LENGTH ] = {""}; - if( g_slapDamage.integer > 0 ) + if( !s[ 0 ] ) + return NULL; + + // if a number is provided, it is a clientnum or namelog id + for( i = 0; s[ i ] && isdigit( s[ i ] ); i++ ); + if( !s[ i ] ) { - int damage; + i = atoi( s ); - if( G_SayArgc() > 2 + skiparg ) + if( i >= 0 && i < level.maxclients ) { - char dmg_str[ MAX_STRING_CHARS ]; - G_SayArgv( 2 + skiparg, dmg_str, sizeof( dmg_str ) ); - damage = atoi(dmg_str); - if( damage < 0 ) damage = 0; + if( level.clients[ i ].pers.connected != CON_DISCONNECTED ) + return level.clients[ i ].pers.namelog; } - else + else if( i >= MAX_CLIENTS ) { - if( g_slapDamage.integer > 100 ) g_slapDamage.integer = 100; - damage = BG_FindHealthForClass( vic->client->ps.stats[ STAT_PCLASS ] ) * - g_slapDamage.integer / 100; - if( damage < 1 ) damage = 1; + for( p = level.namelogs; p; p = p->next ) + { + if( p->id == i ) + break; + } + if( p ) + return p; } - vic->health -= damage; - vic->client->ps.stats[ STAT_HEALTH ] = vic->health; - vic->lastDamageTime = level.time; - if( vic->health <= 0 ) - { - vic->flags |= FL_NO_KNOCKBACK; - vic->enemy = &g_entities[ pids[ 0 ] ]; - vic->die( vic, ent, ent, damage, MOD_SLAP ); - } - else if( vic->pain ) - { - vic->pain( vic, &g_entities[ pids[ 0 ] ], damage ); - } + return NULL; } - return qtrue; -} -qboolean G_admin_drop( gentity_t *ent, int skiparg ) -{ - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; + // check for a name match + G_SanitiseString( s, s2, sizeof( s2 ) ); - if( G_SayArgc() < 2 + skiparg ) + for( p = level.namelogs; p; p = p->next ) { - ADMP( "^3!drop: ^7usage: !drop [name|slot#] [message]\n" ); - return qfalse; - } + for( i = 0; i < MAX_NAMELOG_NAMES && p->name[ i ][ 0 ]; i++ ) + { + G_SanitiseString( p->name[ i ], n2, sizeof( n2 ) ); - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) - { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!drop: ^7%s\n", err ) ); - return qfalse; - } + // if this is an exact match to a current player + if( i == p->nameOffset && p->slot > -1 && !strcmp( s2, n2 ) ) + return p; - if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) - { - ADMP( "^3!drop: ^7sorry, but your intended victim has a higher admin" - " level than you\n" ); - return qfalse; + if( strstr( n2, s2 ) ) + m = p; + } + + if( m == p ) + found++; } - // victim's message - if( G_SayArgc() > 2 + skiparg ) - trap_SendServerCommand( pids[ 0 ], - va( "disconnect \"You have been dropped.\n%s^7\n\"", - G_SayConcatArgs( 2 + skiparg ) ) ); - else - trap_SendServerCommand( pids[ 0 ], va( "disconnect" ) ); + if( found == 1 ) + return m; - // server message - trap_DropClient( pids[ 0 ], va( "disconnected" ) ); + if( found > 1 ) + admin_search( ent, "namelog", "recent players", namelog_matchname, + namelog_out, level.namelogs, s2, 0, MAX_CLIENTS, -1 ); - return qtrue; + return NULL; } -qboolean G_admin_buildlog( gentity_t *ent, int skiparg ) +qboolean G_admin_lock( gentity_t *ent ) { -#define LOG_DISPLAY_LENGTH 10 - buildHistory_t *ptr; - gentity_t *builder = NULL; - int skip = 0, start = 0, lastID = -1, firstID = -1, i, len, matchlen = 0; - pTeam_t team = PTE_NONE; - char message[ MAX_STRING_CHARS ], *teamchar; - char *name, *action, *buildablename, markstring[ MAX_STRING_CHARS ]; - if( !g_buildLogMaxLength.integer ) - { - ADMP( "^3!buildlog: ^7build logging is disabled" ); - return qfalse; - } - if( G_SayArgc( ) >= 2 + skiparg ) - { - for( i = 1; i + skiparg < G_SayArgc( ); i++ ) - { - char argbuf[ 64 ], err[ MAX_STRING_CHARS ]; - int x = 0, pids[ MAX_CLIENTS ]; - G_SayArgv( i + skiparg, argbuf, sizeof argbuf ); - switch( argbuf[ 0 ]) - { - case 'x': - x = 1; - default: - skip = atoi( argbuf + x ); - start = 0; - break; - case '#': - start = atoi( argbuf + 1 ); - skip = 0; - break; - case '-': - if(G_ClientNumbersFromString(argbuf + 1, pids) != 1) - { - G_MatchOnePlayer(pids, err, sizeof(err)); - ADMP(va("^3!revert: ^7%s\n", err)); - return qfalse; - } - builder = g_entities + *pids; - break; - case 'A': - case 'a': - team = PTE_ALIENS; - break; - case 'H': - case 'h': - team = PTE_HUMANS; - break; - } - } - } - // !buildlog can be abused, so let everyone know when it is used - AP( va( "print \"^3!buildlog: ^7%s^7 requested a log of recent building" - " activity\n\"", ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - len = G_CountBuildLog( ); // also clips the log if too long - if( !len ) - { - ADMP( "^3!buildlog: ^7no build log found\n" ); - return qfalse; - } - if( start ) - { - // set skip based on start - for( ptr = level.buildHistory; ptr && ptr->ID != start; - ptr = ptr->next, skip++ ); - if( !ptr ) - { - ADMP( "^3!buildlog: ^7log ID not found\n" ); - skip = 0; - } - } - // ensure skip is a useful value - if( skip > len - LOG_DISPLAY_LENGTH ) - skip = len - LOG_DISPLAY_LENGTH; - *message = '\0'; - // skip to start entry - for( ptr = level.buildHistory, i = len; ptr && i > len - skip; - ptr = ptr->next ) - { - // these checks could perhaps be done more efficiently but they are cheap - // in processor time so I'm not worrying - if( team != PTE_NONE && team != BG_FindTeamForBuildable( ptr->buildable ) ) - continue; - if( builder && builder != ptr->ent ) - continue; - matchlen++; - i--; - } - for( ; i + LOG_DISPLAY_LENGTH > len - skip && i > 0; i--, ptr = ptr->next ) - { - if( !ptr ) - break; // run out of log - *markstring = '\0'; // reinit markstring - // check team - if( ( team != PTE_NONE && team != BG_FindTeamForBuildable( ptr->buildable ) ) - || ( builder && builder != ptr->ent ) ) - { - skip++; // loop an extra time because we skipped one - continue; - } - if( lastID < 0 ) - lastID = ptr->ID; - firstID = ptr->ID; - matchlen++; - // set name to the ent's current name or last recorded name - if( ptr->ent ) - { - if( ptr->ent->client ) - name = ptr->ent->client->pers.netname; - else - name = "<world>"; // any non-client action - } - else - name = ptr->name; - switch( ptr->fate ) - { - case BF_BUILT: - action = "^2built^7 a"; - break; - case BF_DECONNED: - action = "^3DECONSTRUCTED^7 a"; - break; - case BF_DESTROYED: - action = "destroyed a"; - break; - case BF_TEAMKILLED: - action = "^1TEAMKILLED^7 a"; - break; - default: - action = "\0"; // erm - break; - } - // handle buildables removed by markdecon - if( ptr->marked ) - { - buildHistory_t *mark; - int j, markdecon[ BA_NUM_BUILDABLES ], and = 2; - char bnames[ 32 ], *article; - mark = ptr; - // count the number of buildables - memset( markdecon, 0, sizeof( markdecon ) ); - while( ( mark = mark->marked ) ) - markdecon[ mark->buildable ]++; - // reverse order makes grammar easier - for( j = BA_NUM_BUILDABLES; j >= 0; j-- ) - { - buildablename = BG_FindHumanNameForBuildable( j ); - // plural is easy - if( markdecon[ j ] > 1 ) - Com_sprintf( bnames, 32, "%d %ss", markdecon[ j ], buildablename ); - // use an appropriate article - else if( markdecon[ j ] == 1 ) - { - if( BG_FindUniqueTestForBuildable( j ) ) - article = "the"; // if only one - else if( strchr( "aeiouAEIOU", *buildablename ) ) - article = "an"; // if first char is vowel - else - article = "a"; - Com_sprintf( bnames, 32, "%s %s", article, buildablename ); - } - else - continue; // none of this buildable - // C grammar: x, y, and z - // the integer and is 2 initially, the test means it is used on the - // second sprintf only, the reverse order makes this second to last - // the comma is only printed if there is already some markstring i.e. - // not the first time ( which would put it on the end of the string ) - Com_sprintf( markstring, sizeof( markstring ), "%s%s %s%s", bnames, - ( *markstring ) ? "," : "", ( and-- == 1 ) ? "and " : "", markstring ); - } - } - buildablename = BG_FindHumanNameForBuildable( ptr->buildable ); - switch( BG_FindTeamForBuildable( ptr->buildable ) ) - { - case PTE_ALIENS: - teamchar = "^1A"; - break; - case PTE_HUMANS: - teamchar = "^4H"; - break; - default: - teamchar = " "; // space so it lines up neatly - break; - } - // prepend the information to the string as we go back in buildhistory - // so the earliest events are at the top - Com_sprintf( message, MAX_STRING_CHARS, "%3d %s^7 %s^7 %s%s %s%s%s\n%s", - ptr->ID, teamchar, name, action, - ( strchr( "aeiouAEIOU", buildablename[ 0 ] ) ) ? "n" : "", - buildablename, ( markstring[ 0 ] ) ? ", removing " : "", - markstring, message ); - } - for( ; ptr; ptr = ptr->next ) - { - if( builder && builder != ptr->ent ) - continue; - if( team != PTE_NONE && team != BG_FindTeamForBuildable( ptr->buildable ) ) - continue; - matchlen++; - } - if( matchlen ) - ADMP( va( "%s^3!buildlog: showing log entries %d - %d of %d\n", message, - firstID, lastID, matchlen ) ); - else - ADMP( "^3!buildlog: ^7no log entries match those criteria\n" ); - return qtrue; -} + char command[ MAX_ADMIN_CMD_LEN ]; + char teamName[ sizeof( "aliens" ) ]; + team_t team; + qboolean lock, fail = qfalse; -qboolean G_admin_revert( gentity_t *ent, int skiparg ) -{ - int i = 0, j = 0, repeat = 1, ID = 0, len, matchlen=0; - pTeam_t team = PTE_NONE; - qboolean force = qfalse, reached = qfalse; - gentity_t *builder = NULL, *targ; - buildHistory_t *ptr, *tmp, *mark, *prev; - vec3_t dist; - char argbuf[ 64 ], *name, *bname, *action, *article; - len = G_CountBuildLog( ); - if( !len ) - { - ADMP( "^3!revert: ^7no build log found\n" ); - return qfalse; - } - if( G_SayArgc( ) < 2 + skiparg ) + trap_Argv( 0, command, sizeof( command ) ); + if( trap_Argc() < 2 ) { - ADMP( "^3!revert: ^7usage: !revert (^5xnum^7) (^5#ID^7) (^5-name|num^7) (^5a|h^7)\n" ); + ADMP( va( "^3%s: ^7usage: %s [a|h]\n", command, command ) ); return qfalse; } - for( i = 1; i + skiparg < G_SayArgc( ); i++ ) - { - char arg[ 64 ], err[ MAX_STRING_CHARS ]; - int pids[ MAX_CLIENTS ]; - G_SayArgv( i + skiparg, arg, sizeof arg ); - switch( arg[ 0 ]) - { - case 'x': - repeat = atoi( arg + 1 ); - break; - case '#': - ID = atoi( arg + 1 ); - break; - case '-': - if(G_ClientNumbersFromString(arg + 1, pids) != 1) - { - G_MatchOnePlayer(pids, err, sizeof err); - ADMP(va("^3!revert: ^7%s\n", err)); - return qfalse; - } - builder = g_entities + *pids; - break; - case 'A': - case 'a': - team = PTE_ALIENS; - break; - case 'H': - case 'h': - team = PTE_HUMANS; - break; - case '!': - force = qtrue; - break; - default: - ADMP( "^3!revert: ^7usage: !revert (^5xnum^7) (^5#ID^7) (^5-name|num^7) (^5a|h^7)\n" ); - return qfalse; - } - } - if( repeat > 25 ) - { - ADMP( "^3!revert: ^7to avoid flooding, can only revert 25 builds at a time\n" ); - repeat = 25; - } - for( i = 0, ptr = prev = level.buildHistory; repeat > 0; repeat--, j = 0 ) + lock = !Q_stricmp( command, "lock" ); + trap_Argv( 1, teamName, sizeof( teamName ) ); + team = G_TeamFromString( teamName ); + + if( team == TEAM_ALIENS ) { - if( !ptr ) - break; // run out of bhist - if( !reached && ID ) - { - if( ptr->ID == ID ) - reached = qtrue; - else - { - prev = ptr; - ptr = ptr->next; - repeat++; - continue; - } - } - if( ( team != PTE_NONE && - team != BG_FindTeamForBuildable( ptr->buildable ) ) || - ( builder && builder != ptr->ent )) - { - // team doesn't match, so skip this ptr and reset prev - prev = ptr; - ptr = ptr->next; - // we don't want to count this one so counteract the decrement by the for - repeat++; - continue; - } - // get the ent's current or last recorded name - if( ptr->ent ) - { - if( ptr->ent->client ) - name = ptr->ent->client->pers.netname; - else - name = "<world>"; // non-client actions - } + if( level.alienTeamLocked == lock ) + fail = qtrue; else - name = ptr->name; - bname = BG_FindHumanNameForBuildable( ptr->buildable ); - action = ""; - switch( ptr->fate ) - { - case BF_BUILT: - action = "^2build^7"; - for( j = MAX_CLIENTS, targ = g_entities + j; - j < level.num_entities; j++, targ++ ) - { - // easy checks first to save time - if( targ->s.eType != ET_BUILDABLE ) - continue; - if( targ->s.modelindex != ptr->buildable ) - continue; - VectorSubtract( targ->s.pos.trBase, ptr->origin, dist ); -#define FIND_BUILDABLE_TOLERANCE 5 - if( VectorLength( dist ) > FIND_BUILDABLE_TOLERANCE ) - continue; // number is somewhat arbitrary, watch for false pos/neg - // if we didn't continue then it's this one, unlink it but we can't - // free it yet, because the markdecon buildables might not place - trap_UnlinkEntity( targ ); - break; - } - // if there are marked buildables to replace, and we aren't overriding - // space check, check they can fit before acting - if( ptr->marked && !force ) - { - for( mark = ptr->marked; mark; mark = mark->marked ) - if( !G_RevertCanFit( mark ) ) - { - trap_LinkEntity( targ ); // put it back, we failed - // scariest sprintf ever: - Com_sprintf( argbuf, sizeof argbuf, "%s%s%s%s%s%s%s!", - ( repeat > 1 ) ? "x" : "", ( repeat > 1 ) ? va( "%d ", repeat ) : "", - ( ID ) ? "#" : "", ( ID ) ? va( "%d ", ptr->ID ) : "", - ( builder ) ? "-" : "", ( builder ) ? va( "%d ", builder - g_entities ) : "", - ( team == PTE_ALIENS ) ? "a " : ( team == PTE_HUMANS ) ? "h " : "" ); - ADMP( va( "^3!revert: ^7revert aborted: reverting this %s would conflict with " - "another buildable, use ^3!revert %s ^7to override\n", action, argbuf ) ); - return qfalse; - } - } - // Prevent teleport glitch when reverting an occupied hovel - if( targ->s.modelindex == BA_A_HOVEL && - targ->active ) - { - gentity_t *builder = targ->builder; - vec3_t newOrigin; - vec3_t newAngles; - - VectorCopy( targ->s.angles, newAngles ); - newAngles[ ROLL ] = 0; - - VectorCopy( targ->s.origin, newOrigin ); - VectorMA( newOrigin, 1.0f, targ->s.origin2, newOrigin ); - - //prevent lerping - builder->client->ps.eFlags ^= EF_TELEPORT_BIT; - builder->client->ps.eFlags &= ~EF_NODRAW; - G_UnlaggedClear( builder ); - - G_SetOrigin( builder, newOrigin ); - VectorCopy( newOrigin, builder->client->ps.origin ); - G_SetClientViewAngle( builder, newAngles ); - - //client leaves hovel - builder->client->ps.stats[ STAT_STATE ] &= ~SS_HOVELING; - } - - // if we haven't returned yet then we're good to go, free it - G_FreeEntity( targ ); - // put the marked buildables back and mark them again - if( ptr->marked ) // there may be a more efficient way of doing this - { - for( mark = ptr->marked; mark; mark = mark->marked ) - G_SpawnRevertedBuildable( mark, qtrue ); - } - break; - case BF_DECONNED: - if( !action[ 0 ] ) action = "^3deconstruction^7"; - case BF_TEAMKILLED: - if( !action[ 0 ] ) action ="^1TEAMKILL^7"; - case BF_DESTROYED: - if( !action[ 0 ] ) action = "destruction"; - // if we're not overriding and the replacement can't fit, as before - if( !force && !G_RevertCanFit( ptr ) ) - { - Com_sprintf( argbuf, sizeof argbuf, "%s%s%s%s%s%s%s!", - ( repeat > 1 ) ? "x" : "", ( repeat > 1 ) ? va( "%d ", repeat ) : "", - ( ID ) ? "#" : "", ( ID ) ? va( "%d ", ptr->ID ) : "", - ( builder ) ? "-" : "", ( builder ) ? va( "%d ", builder - g_entities ) : "", - ( team == PTE_ALIENS ) ? "a " : ( team == PTE_HUMANS ) ? "h " : "" ); - ADMP( va( "^3!revert: ^7revert aborted: reverting this %s would " - "conflict with another buildable, use ^3!revert %s ^7to override\n", - action, argbuf ) ); - return qfalse; - } - // else replace it but don't mark it ( it might have been marked before - // but it isn't that important ) - G_SpawnRevertedBuildable( ptr, qfalse ); - break; - default: - // if this happens something has gone wrong - ADMP( "^3!revert: ^7incomplete or corrupted build log entry\n" ); - /* quarantine and dispose of the log, it's dangerous - trap_Cvar_Set( "g_buildLogMaxLength", "0" ); - G_CountBuildLog( ); - */ - return qfalse; - } - if( j == level.num_entities ) - { - ADMP( va( "^3!revert: ^7could not find logged buildable #%d\n", ptr->ID )); - prev = ptr; - ptr = ptr->next; - continue; - } - // this is similar to the buildlog stuff - if( BG_FindUniqueTestForBuildable( ptr->buildable ) ) - article = "the"; - else if( strchr( "aeiouAEIOU", *bname ) ) - article = "an"; - else - article = "a"; - AP( va( "print \"%s^7 reverted %s^7'%s %s of %s %s\n\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console", - name, strchr( "Ss", name[ strlen( name ) - 1 ] ) ? "" : "s", - action, article, bname ) ); - matchlen++; - // remove the reverted entry - // ptr moves on, prev just readjusts ->next unless it is about to be - // freed, in which case it is forced to move on too - tmp = ptr; - if( ptr == level.buildHistory ) - prev = level.buildHistory = ptr = ptr->next; - else - prev->next = ptr = ptr->next; - G_Free( tmp ); - } - if( ID && !reached ) - { - ADMP( "^3!revert: ^7no buildlog entry with that ID\n" ); - return qfalse; + level.alienTeamLocked = lock; } - - if( !matchlen ) + else if( team == TEAM_HUMANS ) { - ADMP( "^3!revert: ^7no log entries match those criteria\n" ); - return qfalse; + if( level.humanTeamLocked == lock ) + fail = qtrue; + else + level.humanTeamLocked = lock; } else { - ADMP( va( "^3!revert: ^7reverted %d buildlog events\n", matchlen ) ); - } - - return qtrue; -} - -void G_Unescape( char *input, char *output, int len ); -qboolean G_StringReplaceCvars( char *input, char *output, int len ); - -qboolean G_admin_info( gentity_t *ent, int skiparg ) -{ - fileHandle_t infoFile; - int length; - char filename[ MAX_OSPATH ], message[ MAX_STRING_CHARS ]; - if( G_SayArgc() == 2 + skiparg ) - G_SayArgv( 1 + skiparg, filename, sizeof( filename ) ); - else if( G_SayArgc() == 1 + skiparg ) - Q_strncpyz( filename, "default", sizeof( filename ) ); - else - { - ADMP( "^3!info: ^7usage: ^3!info ^7(^5subject^7)\n" ); + ADMP( va( "^3%s: ^7invalid team: '%s'\n", command, teamName ) ); return qfalse; } - Com_sprintf( filename, sizeof( filename ), "info/info-%s.txt", filename ); - length = trap_FS_FOpenFile( filename, &infoFile, FS_READ ); - if( length <= 0 || !infoFile ) - { - trap_FS_FCloseFile( infoFile ); - ADMP( "^3!info: ^7no relevant information is available\n" ); - return qfalse; - } - else - { - int i; - char *cr; - trap_FS_Read( message, sizeof( message ), infoFile ); - if( length < sizeof( message ) ) - message[ length ] = '\0'; - else - message[ sizeof( message ) - 1 ] = '\0'; - trap_FS_FCloseFile( infoFile ); - // strip carriage returns for windows platforms - while( ( cr = strchr( message, '\r' ) ) ) - memmove( cr, cr + 1, strlen( cr + 1 ) + 1 ); -#define MAX_INFO_PARSE_LOOPS 100 - for( i = 0; i < MAX_INFO_PARSE_LOOPS && - G_StringReplaceCvars( message, message, sizeof( message ) ); i++ ); - G_Unescape( message, message, sizeof( message ) ); - if( i == MAX_INFO_PARSE_LOOPS ) - G_Printf( S_COLOR_YELLOW "WARNING: %s exceeds MAX_INFO_PARSE_LOOPS\n", filename ); - ADMP( va( "%s\n", message ) ); - return qtrue; - } -} - -void G_Unescape( char *input, char *output, int len ) -{ - // \n -> newline, \%c -> %c - // output is terminated at output[len - 1] - // it's OK for input to equal output, because our position in input is always - // equal or greater than our position in output - // however, if output is later in the same string as input, a crash is pretty - // much inevitable - int i, j; - for( i = j = 0; input[i] && j + 1 < len; i++, j++ ) - { - if( input[i] == '\\' ) - { - if( !input[++i] ) - { - output[j] = '\0'; - return; - } - else if( input[i] == 'n' ) - output[j] = '\n'; - else - output[j] = input[i]; - } - else - output[j] = input[i]; - } - output[j] = '\0'; -} -qboolean G_StringReplaceCvars( char *input, char *output, int len ) -{ - int i, outNum = 0; - char cvarName[ 64 ], cvarValue[ MAX_CVAR_VALUE_STRING ]; - char *outputBuffer; - qboolean doneAnything = qfalse; - if( len <= 0 ) - return qfalse; - // use our own internal buffer in case output == input - outputBuffer = G_Alloc( len ); - len -= 1; // fit in a terminator - while( *input && outNum < len ) + if( fail ) { - if( *input == '\\' && input[1] && outNum < len - 1 ) - { - outputBuffer[ outNum++ ] = *input++; - outputBuffer[ outNum++ ] = *input++; - } - else if( *input == '$' ) - { - doneAnything = qtrue; - input++; - if( *input == '{' ) - input++; - for( i = 0; *input && ( isalnum( *input ) || *input == '_' ) && - i < 63; i++ ) - cvarName[ i ] = *input++; - cvarName[ i ] = '\0'; - if( *input == '}' ) - input++; - trap_Cvar_VariableStringBuffer( cvarName, cvarValue, sizeof( cvarValue ) ); - if( cvarValue[ 0 ] ) - { - for( i = 0; cvarValue[ i ] && outNum < len; i++ ) - outputBuffer[ outNum++ ] = cvarValue[ i ]; - } - } - else - outputBuffer[ outNum++ ] = *input++; - } - outputBuffer[ outNum ] = '\0'; - Q_strncpyz( output, outputBuffer, len ); - G_Free( outputBuffer ); - return doneAnything; -} - -/* -================ - G_admin_print + ADMP( va( "^3%s: ^7the %s team is %s locked\n", + command, BG_TeamName( team ), lock ? "already" : "not currently" ) ); - This function facilitates the ADMP define. ADMP() is similar to CP except - that it prints the message to the server console if ent is not defined. -================ -*/ -void G_admin_print( gentity_t *ent, char *m ) -{ - if( ent ) - trap_SendServerCommand( ent - level.gentities, va( "print \"%s\"", m ) ); - else - { - char m2[ MAX_STRING_CHARS ]; - if( !trap_Cvar_VariableIntegerValue( "com_ansiColor" ) ) - { - G_DecolorString( m, m2 ); - G_Printf( m2 ); - } - else - G_Printf( m ); + return qfalse; } -} -void G_admin_buffer_begin() -{ - g_bfb[ 0 ] = '\0'; -} + admin_log( BG_TeamName( team ) ); + AP( va( "print \"^3%s: ^7the %s team has been %slocked by %s\n\"", + command, BG_TeamName( team ), lock ? "" : "un", + ent ? ent->client->pers.netname : "console" ) ); -void G_admin_buffer_end( gentity_t *ent ) -{ - ADMP( g_bfb ); + return qtrue; } -void G_admin_buffer_print( gentity_t *ent, char *m ) +qboolean G_admin_builder( gentity_t *ent ) { - // 1022 - strlen("print 64 \"\"") - 1 - if( strlen( m ) + strlen( g_bfb ) >= 1009 ) - { - ADMP( g_bfb ); - g_bfb[ 0 ] = '\0'; - } - Q_strcat( g_bfb, sizeof( g_bfb ), m ); -} + vec3_t forward, right, up; + vec3_t start, end, dist; + trace_t tr; + gentity_t *traceEnt; + buildLog_t *log; + int i; + qboolean buildlog; + char logid[ 20 ] = {""}; - -void G_admin_cleanup() -{ - int i = 0; - - for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) - { - G_Free( g_admin_levels[ i ] ); - g_admin_levels[ i ] = NULL; - } - for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) - { - G_Free( g_admin_admins[ i ] ); - g_admin_admins[ i ] = NULL; - } - for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) - { - G_Free( g_admin_bans[ i ] ); - g_admin_bans[ i ] = NULL; - } - for( i = 0; i < MAX_ADMIN_COMMANDS && g_admin_commands[ i ]; i++ ) - { - G_Free( g_admin_commands[ i ] ); - g_admin_commands[ i ] = NULL; - } -} - -qboolean G_admin_L0(gentity_t *ent, int skiparg ){ - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ] = {""}; - char testname[ MAX_NAME_LENGTH ] = {""}; - char err[ MAX_STRING_CHARS ]; - qboolean numeric = qtrue; - int i; - int id = -1; - gentity_t *vic; - - if( G_SayArgc() < 2 + skiparg ) + if( !ent ) { - ADMP( "^3!L0: ^7usage: !L0 [name|slot#|admin#]\n" ); + ADMP( "^3builder: ^7console can't aim.\n" ); return qfalse; } - G_SayArgv( 1 + skiparg, testname, sizeof( testname ) ); - G_SanitiseString( testname, name, sizeof( name ) ); - for( i = 0; i < sizeof( name ) && name[ i ] ; i++ ) - { - if( name[ i ] < '0' || name[ i ] > '9' ) - { - numeric = qfalse; - break; - } - } - if( numeric ) - { - id = atoi( name ); - } + buildlog = G_admin_permission( ent, "buildlog" ); + + AngleVectors( ent->client->ps.viewangles, forward, right, up ); + if( ent->client->pers.teamSelection != TEAM_NONE && + ent->client->sess.spectatorState == SPECTATOR_NOT ) + CalcMuzzlePoint( ent, forward, right, up, start ); else - { - if( G_ClientNumbersFromString( name, pids ) != 1 ) - { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!L0: ^7%s\n", err ) ); - return qfalse; - } - id = pids[ 0 ]; - } + VectorCopy( ent->client->ps.origin, start ); + VectorMA( start, 1000, forward, end ); - if (id >= 0 && id < level.maxclients) + trap_Trace( &tr, start, NULL, NULL, end, ent->s.number, MASK_PLAYERSOLID ); + traceEnt = &g_entities[ tr.entityNum ]; + if( tr.fraction < 1.0f && ( traceEnt->s.eType == ET_BUILDABLE ) ) { - vic = &g_entities[ id ]; - if( !vic || !(vic->client) || vic->client->pers.connected != CON_CONNECTED ) + if( !buildlog && + ent->client->pers.teamSelection != TEAM_NONE && + ent->client->pers.teamSelection != traceEnt->buildableTeam ) { - ADMP( "^3!L0:^7 no one connected by that slot number\n" ); + ADMP( "^3builder: ^7structure not owned by your team\n" ); return qfalse; } - if( G_admin_level( vic ) != 1 ) + if( buildlog ) { - ADMP( "^3!L0:^7 intended victim is not level 1\n" ); - return qfalse; - } - } - else if (id >= MAX_CLIENTS && id < MAX_CLIENTS + MAX_ADMIN_ADMINS - && g_admin_admins[ id - MAX_CLIENTS ] ) - { - if( g_admin_admins[ id - MAX_CLIENTS ]->level != 1 ) - { - ADMP( "^3!L0:^7 intended victim is not level 1\n" ); - return qfalse; + for( i = 0 ; buildlog && i < level.numBuildLogs; i++ ) + { + log = &level.buildLog[ ( level.buildId - i - 1 ) % MAX_BUILDLOG ]; + if( log->fate != BF_CONSTRUCT || traceEnt->s.modelindex != log->modelindex ) + continue; + + VectorSubtract( traceEnt->s.pos.trBase, log->origin, dist ); + if( VectorLengthSquared( dist ) < 2.0f ) + Com_sprintf( logid, sizeof( logid ), ", buildlog #%d", + MAX_CLIENTS + level.buildId - i - 1 ); + } } + + ADMP( va( "^3builder: ^7%s%s%s^7%s\n", + BG_Buildable( traceEnt->s.modelindex )->humanName, + traceEnt->builtBy ? " built by " : "", + traceEnt->builtBy ? + traceEnt->builtBy->name[ traceEnt->builtBy->nameOffset ] : + "", + buildlog ? ( logid[ 0 ] ? logid : ", not in buildlog" ) : "" ) ); } else - { - ADMP( "^3!L0:^7 no match. use !listplayers or !listadmins to " - "find an appropriate number to use instead of name.\n" ); - return qfalse; - } - - trap_SendConsoleCommand( EXEC_APPEND, va( "!setlevel %d 0;", id ) ); + ADMP( "^3builder: ^7no structure found under crosshair\n" ); return qtrue; } -qboolean G_admin_L1(gentity_t *ent, int skiparg ){ - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ], *reason, err[ MAX_STRING_CHARS ]; - int minargc; - - minargc = 2 + skiparg; - - if( G_SayArgc() < minargc ) - { - ADMP( "^3!L1: ^7usage: !L1 [name]\n" ); - return qfalse; - } - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - reason = G_SayConcatArgs( 2 + skiparg ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) - { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!L1: ^7%s\n", err ) ); - return qfalse; - } - if( G_admin_level(&g_entities[ pids[ 0 ] ] )>0 ) - { - ADMP( "^3!L1: ^7Sorry, but that person is already higher than level 0.\n" ); - return qfalse; - } - - trap_SendConsoleCommand( EXEC_APPEND,va( "!setlevel %d 1;", pids[ 0 ] ) ); - return qtrue; -} - -qboolean G_admin_invisible( gentity_t *ent, int skiparg ) +qboolean G_admin_pause( gentity_t *ent ) { - if( !ent ) - { - ADMP( "!invisible: console can not become invisible.\n" ); - return qfalse; - } - - if ( ent->client->sess.invisible != qtrue ) + if( !level.pausedTime ) { - // Make the player invisible - G_ChangeTeam( ent, PTE_NONE ); - ent->client->sess.invisible = qtrue; - ClientUserinfoChanged( ent->client->pers.connection->clientNum, qfalse ); - G_admin_namelog_update( ent->client, qtrue ); - trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " disconnected\n\"", ent->client->pers.netname ) ); + AP( va( "print \"^3pause: ^7%s^7 paused the game.\n\"", + ( ent ) ? ent->client->pers.netname : "console" ) ); + level.pausedTime = 1; + trap_SendServerCommand( -1, "cp \"The game has been paused. Please wait.\"" ); } else { - // Make the player visible - ent->client->sess.invisible = qfalse; - ClientUserinfoChanged( ent->client->pers.connection->clientNum, qfalse ); - G_admin_namelog_update( ent->client, qfalse ); - trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " connected\n\"", ent->client->pers.netname ) ); - trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " entered the game\n\"", ent->client->pers.netname ) ); - } - return qtrue; -} + // Prevent accidental pause->unpause race conditions by two admins + if( level.pausedTime < 1000 ) + { + ADMP( "^3pause: ^7Unpausing so soon assumed accidental and ignored.\n" ); + return qfalse; + } -qboolean G_admin_decon( gentity_t *ent, int skiparg ) -{ - int i = 0, j = 0, repeat = 24, pids[ MAX_CLIENTS ], len, matchlen = 0; - pTeam_t team = PTE_NONE; - qboolean force = qfalse, reached = qfalse; - gentity_t *builder = NULL, *targ; - buildHistory_t *ptr, *tmp, *mark, *prev; - vec3_t dist; - char arg[ 64 ], err[ MAX_STRING_CHARS ], *name, *bname, *action, *article, *reason; - len = G_CountBuildLog( ); - - if( !len ) - { - ADMP( "^3!decon: ^7no build log found, aborting...\n" ); - return qfalse; - } + AP( va( "print \"^3pause: ^7%s^7 unpaused the game (Paused for %d sec) \n\"", + ( ent ) ? ent->client->pers.netname : "console", + (int) ( (float) level.pausedTime / 1000.0f ) ) ); + trap_SendServerCommand( -1, "cp \"The game has been unpaused!\"" ); - if( G_SayArgc( ) < 2 + skiparg ) - { - ADMP( "^3!decon: ^7usage: !decon (^5name|num^7)\n" ); - return qfalse; + level.pausedTime = 0; } - G_SayArgv( 1 + skiparg, arg, sizeof( arg ) ); - if( G_ClientNumbersFromString( arg, pids ) != 1 ) - { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!decon: ^7%s\n", err ) ); - return qfalse; - } + return qtrue; +} - builder = g_entities + *pids; - if( builder->client->sess.invisible == qtrue ) - { - ADMP( va( "^3!decon: ^7no connected player by the name or slot #\n" ) ); - return qfalse; +static char *fates[] = +{ + "^2built^7", + "^3deconstructed^7", + "^7replaced^7", + "^3destroyed^7", + "^1TEAMKILLED^7", + "^7unpowered^7", + "removed" +}; +qboolean G_admin_buildlog( gentity_t *ent ) +{ + char search[ MAX_NAME_LENGTH ] = {""}; + char s[ MAX_NAME_LENGTH ] = {""}; + char n[ MAX_NAME_LENGTH ]; + char stamp[ 8 ]; + int id = -1; + int printed = 0; + int time; + int start = MAX_CLIENTS + level.buildId - level.numBuildLogs; + int i = 0, j; + buildLog_t *log; + + if( !level.buildId ) + { + ADMP( "^3buildlog: ^7log is empty\n" ); + return qtrue; } - if( !admin_higher( ent, builder ) ) + if( trap_Argc() == 3 ) { - ADMP( "^3!decon: ^7sorry, but your intended victim has a higher admin" - "level than you\n"); - return qfalse; + trap_Argv( 2, search, sizeof( search ) ); + start = atoi( search ); } - - for( i = 0, ptr = prev = level.buildHistory; repeat > 0; repeat--, j = 0 ) + if( trap_Argc() > 1 ) { - if( !ptr ) - break; - if( builder && builder != ptr->ent ) - { - // team doesn't match, so skip this ptr and reset prev - prev = ptr; - ptr = ptr->next; - // we don't want to count this one so counteract the decrement by the for - repeat++; - continue; - } - // get the ent's current or last recorded name - if( ptr->ent ) - { - if( ptr->ent->client ) - name = ptr->ent->client->pers.netname; - else - name = "<world>"; // non-client actions - } - else - name = ptr->name; - bname = BG_FindHumanNameForBuildable( ptr->buildable ); - action = ""; - switch( ptr->fate ) + trap_Argv( 1, search, sizeof( search ) ); + for( i = search[ 0 ] == '-'; isdigit( search[ i ] ); i++ ); + if( i && !search[ i ] ) { - case BF_BUILT: - prev = ptr; - ptr = ptr->next; - repeat++; - continue; - case BF_DESTROYED: - prev = ptr; - ptr = ptr->next; - repeat++; - case BF_DECONNED: - if( !action[0] ) action = "^3deconstruction^7"; - case BF_TEAMKILLED: - if( !action[0] ) action = "^1TEAMKILL^7"; - // if we're not overriding and the replacement can't fit, as before - if( !G_RevertCanFit( ptr ) ) - { - prev = ptr; - ptr = ptr->next; - repeat++; - continue; - } - // else replace it but don't mark it ( it might have been marked before - // but it isn't that important ) - G_SpawnRevertedBuildable( ptr, qfalse ); - break; - default: - // if this happens something has gone wrong - ADMP( "^3!decon: ^7incomplete or corrupted build log entry\n" ); - /* quarantine and dispose of the log, it's dangerous - trap_Cvar_Set( "g_buildLogMaxLength", "0" ); - G_CountBuildLog( ); - */ + id = atoi( search ); + if( trap_Argc() == 2 && ( id < 0 || id >= MAX_CLIENTS ) ) + { + start = id; + id = -1; + } + else if( id < 0 || id >= MAX_CLIENTS || + level.clients[ id ].pers.connected != CON_CONNECTED ) + { + ADMP( "^3buildlog: ^7invalid client id\n" ); return qfalse; + } } - // this is similar to the buildlog stuff - if( BG_FindUniqueTestForBuildable( ptr->buildable ) ) - article = "the"; - else if( strchr( "aeiouAEIOU", *bname ) ) - article = "an"; else - article = "a"; - AP( va( "print \"%s^7 reverted %s^7'%s %s of %s %s\n\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console", - name, strchr( "Ss", name[ strlen( name ) - 1 ] ) ? "" : "s", - action, article, bname ) ); - matchlen++; - // remove the reverted entry - // ptr moves on, prev just readjusts ->next unles it is about to be - // freed, in which case it is forced to move on too - tmp = ptr; - if( ptr == level.buildHistory ) - prev = level.buildHistory = ptr = ptr->next; - else - prev->next = ptr = ptr->next; - G_Free( tmp ); + G_SanitiseString( search, s, sizeof( s ) ); } + else + start = MAX( -MAX_ADMIN_LISTITEMS, -level.buildId ); - if( !matchlen ) + if( start < 0 ) + start = MAX( level.buildId - level.numBuildLogs, start + level.buildId ); + else + start -= MAX_CLIENTS; + if( start < level.buildId - level.numBuildLogs || start >= level.buildId ) { - ADMP( "^3!decopn: ^7This user doesn't seem to have deconned anything...\n" ); + ADMP( "^3buildlog: ^7invalid build ID\n" ); return qfalse; } - ADMP( va( "^3!decon: ^7reverted %d buildlog events\n", matchlen ) ); - admin_create_ban( ent, - builder->client->pers.netname, - builder->client->pers.guid, - builder->client->pers.ip, G_admin_parse_time( g_deconBanTime.string ), - ( *reason ) ? reason : "^1Decon" ); - if( g_admin.string[ 0 ] ) - admin_writeconfig(); - - trap_SendServerCommand( pids[ 0 ], - va( "disconnect \"You have been kicked.\n%s^7\nreason:\n%s\n%s\"", - ( ent ) ? va( "admin:\n%s", G_admin_adminPrintName( ent ) ) : "admin\nconsole", - ( *reason ) ? reason : "^1Decon" ) ); - - trap_DropClient( pids[ 0 ], va( "kicked%s^7, reason: %s", - ( ent ) ? va( " by %s", G_admin_adminPrintName( ent ) ) : " by console", - ( *reason ) ? reason : "^1Decon" ) ); - - return qtrue; -} + if( ent && ent->client->pers.teamSelection != TEAM_NONE ) + trap_SendServerCommand( -1, + va( "print \"^3buildlog: ^7%s^7 requested a log of recent building activity\n\"", + ent->client->pers.netname ) ); -qboolean G_admin_setdevmode( gentity_t *ent, int skiparg ) -{ - char str[ 5 ]; - - if( G_SayArgc() != 2 + skiparg ) - { - ADMP( "^3!setdevmode: ^7usage: !setdevmode [on|off]\n" ); - return qfalse; - } - G_SayArgv( 1 + skiparg, str, sizeof( str ) ); - - if( !Q_stricmp( str, "on" ) ) + ADMBP_begin(); + for( i = start; i < level.buildId && printed < MAX_ADMIN_LISTITEMS; i++ ) { - if( g_cheats.integer ) + log = &level.buildLog[ i % MAX_BUILDLOG ]; + if( id >= 0 && id < MAX_CLIENTS ) { - ADMP( "^3!setdevmode: ^7developer mode is already on\n" ); - return qfalse; + if( log->actor != level.clients[ id ].pers.namelog ) + continue; } - trap_Cvar_Set( "sv_cheats", "1" ); - trap_Cvar_Update( &g_cheats ); - AP( va( "print \"^3!setdevmode: ^7%s ^7has switched developer mode on\n\"", - ent ? G_admin_adminPrintName( ent ) : "console" ) ); - } - else if( !Q_stricmp( str, "off" ) ) - { - if( !g_cheats.integer ) + else if( s[ 0 ] ) { - ADMP( "^3!setdevmode: ^7developer mode is already off\n" ); - return qfalse; + if( !log->actor ) + continue; + for( j = 0; j < MAX_NAMELOG_NAMES && log->actor->name[ j ][ 0 ]; j++ ) + { + G_SanitiseString( log->actor->name[ j ], n, sizeof( n ) ); + if( strstr( n, s ) ) + break; + } + if( j >= MAX_NAMELOG_NAMES || !log->actor->name[ j ][ 0 ] ) + continue; } - trap_Cvar_Set( "sv_cheats", "0" ); - trap_Cvar_Update( &g_cheats ); - AP( va( "print \"^3!setdevmode: ^7%s ^7has switched developer mode off\n\"", - ent ? G_admin_adminPrintName( ent ) : "console" ) ); - } - else - { - ADMP( "^3!setdevmode: ^7usage: !setdevmode [on|off]\n" ); - return qfalse; - } - + printed++; + time = ( log->time - level.startTime ) / 1000; + Com_sprintf( stamp, sizeof( stamp ), "%3d:%02d", time / 60, time % 60 ); + ADMBP( va( "^2%c^7%-3d %s %s^7%s%s%s %s%s%s\n", + log->actor && log->fate != BF_REPLACE && log->fate != BF_UNPOWER ? + '*' : ' ', + i + MAX_CLIENTS, + log->actor && ( log->fate == BF_REPLACE || log->fate == BF_UNPOWER ) ? + " \\_" : stamp, + BG_Buildable( log->modelindex )->humanName, + log->builtBy && log->fate != BF_CONSTRUCT ? + " (built by " : + "", + log->builtBy && log->fate != BF_CONSTRUCT ? + log->builtBy->name[ log->builtBy->nameOffset ] : + "", + log->builtBy && log->fate != BF_CONSTRUCT ? + "^7)" : + "", + fates[ log->fate ], + log->actor ? " by " : "", + log->actor ? + log->actor->name[ log->actor->nameOffset ] : + "" ) ); + } + ADMBP( va( "^3buildlog: ^7showing %d build logs %d - %d of %d - %d. %s\n", + printed, start + MAX_CLIENTS, i + MAX_CLIENTS - 1, + level.buildId + MAX_CLIENTS - level.numBuildLogs, + level.buildId + MAX_CLIENTS - 1, + i < level.buildId ? va( "run 'buildlog %s%s%d' to see more", + search, search[ 0 ] ? " " : "", i + MAX_CLIENTS ) : "" ) ); + ADMBP_end(); return qtrue; } -qboolean G_admin_hstage( gentity_t *ent, int skiparg ) +qboolean G_admin_revert( gentity_t *ent ) { + char arg[ MAX_TOKEN_CHARS ]; + char time[ MAX_DURATION_LENGTH ]; + int id; + buildLog_t *log; - char lvl_chr[ MAX_STRING_CHARS ]; - int minargc; - int lvl; - - - minargc = 2 + skiparg; - - if( G_SayArgc() < minargc ) + if( trap_Argc() != 2 ) { - ADMP( "^3!hstage: ^7hstage: !hstage [#]\n" ); + ADMP( "^3revert: ^7usage: revert [id]\n" ); return qfalse; } - G_SayArgv( 1 + skiparg, lvl_chr, sizeof( lvl_chr ) ); - - lvl = atoi(lvl_chr); - - lvl -= 1; - trap_SendConsoleCommand( EXEC_APPEND, va( "g_humanStage %i", lvl ) ); - - return qtrue; - -} - -qboolean G_admin_astage( gentity_t *ent, int skiparg ) -{ - - char lvl_chr[ MAX_STRING_CHARS ]; - int minargc; - int lvl; - - - minargc = 2 + skiparg; - - if( G_SayArgc() < minargc ) + trap_Argv( 1, arg, sizeof( arg ) ); + id = atoi( arg ) - MAX_CLIENTS; + if( id < level.buildId - level.numBuildLogs || id >= level.buildId ) { - ADMP( "^3!astage: ^7astage: !astage [#]\n" ); + ADMP( "^3revert: ^7invalid id\n" ); return qfalse; } - G_SayArgv( 1 + skiparg, lvl_chr, sizeof( lvl_chr ) ); - - lvl = atoi(lvl_chr); - - lvl -= 1; - trap_SendConsoleCommand( EXEC_APPEND, va( "g_alienStage %i", lvl ) ); - - return qtrue; - -} - -qboolean G_admin_bubble( gentity_t *ent, int skiparg ) -{ - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; - gentity_t *vic; - - if(g_Bubbles.integer) - { - if( G_SayArgc() < 2 + skiparg ) - { - ADMP( "^3!bubble: ^7usage: !bubble [name|slot#]\n" ); - return qfalse; - } - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) - { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!bubble: ^7%s\n", err ) ); - return qfalse; - } - vic = &g_entities[ pids[ 0 ] ]; - if(vic->client->sess.invisible == qtrue) - { - ADMP( va( "^3!bubble: ^7no connected player by that name or slot #\n" ) ); - return qfalse; - } - if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) - { - ADMP( "^3!bubble: ^7sorry, but your intended victim has a higher admin" - " level than you\n" ); - return qfalse; - } - - - if( vic->client->pers.bubbleTime ) - vic->client->pers.bubbleTime = 0; - else - vic->client->pers.bubbleTime = level.time + 500; - - AP( va( "print \"^3!bubble: ^7bubbles %s for %s^7 by %s\n\"", - ( vic->client->pers.bubbleTime ) ? "enabled" : "disabled", - vic->client->pers.netname, - ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - } - else + log = &level.buildLog[ id % MAX_BUILDLOG ]; + if( !log->actor || log->fate == BF_REPLACE || log->fate == BF_UNPOWER ) { - ADMP( "^3!bubble: ^7sorry, but bubbles have been disabled on this server.\n" ); - return qfalse; + // fixme: then why list them with an id # in build log ? - rez + ADMP( "^3revert: ^7you can only revert direct player actions, " + "indicated by ^2* ^7in buildlog\n" ); + return qfalse; } - return qtrue; -} - -qboolean G_admin_scrim(gentity_t *ent, int skiparg ) -{ - char state[5]; - - if( G_SayArgc() < 2 + skiparg ) - { - ADMP( "^3!scrim: ^7usage: !scrim [on|off]\n" ); - return qfalse; - } - - G_SayArgv( 1 + skiparg, state, sizeof( state ) ); - - if( !Q_stricmp(state, "on") ) - { - if( g_scrimMode.integer != 0 ) - { - ADMP( "^3!scrim: ^7scrim mode is already enabled.\n" ); - return qfalse; - } - AP( va( "print \"^3!scrim: ^7%s ^7turned scrim mode ^2on^7\n\"", ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - trap_Cvar_Set( "g_scrimMode", "1" ); - } - else if( !Q_stricmp(state, "off") ) - { - if( g_scrimMode.integer == 0 ) - { - ADMP( "^3!scrim: ^7scrim mode is already disabled.\n" ); - return qfalse; - } - AP( va( "print \"^3!scrim: ^7%s ^7turned scrim mode ^1off^7\n\"", ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); - trap_Cvar_Set( "g_scrimMode", "0" ); - - } else { - ADMP( "^3!scrim: ^7usage: !scrim [on|off]\n" ); - return qfalse; - } - + G_admin_duration( ( level.time - log->time ) / 1000, time, + sizeof( time ) ); + admin_log( arg ); + AP( va( "print \"^3revert: ^7%s^7 reverted %d %s over the past %s\n\"", + ent ? ent->client->pers.netname : "console", + level.buildId - id, + ( level.buildId - id ) > 1 ? "changes" : "change", + time ) ); + G_BuildLogRevert( id ); return qtrue; } -qboolean G_admin_give(gentity_t *ent, int skiparg) -{ - char arg_name_raw[MAX_NAME_LENGTH]; - char arg_name[MAX_NAME_LENGTH]; - char arg_amount[30]; - int target_id, amount; - gentity_t *target; - const char *currency; - - if (G_SayArgc() < 3 + skiparg) { - ADMP("^3!give: ^7usage: !give [player] [amount]\n"); - return qfalse; - } - - G_SayArgv(1 + skiparg, arg_name_raw, sizeof(arg_name_raw)); - G_SanitiseString(arg_name_raw, arg_name, sizeof(arg_name)); - - if (is_numeric(arg_name)) { - target_id = atoi(arg_name); - - if (target_id < 0 || target_id >= MAX_CLIENTS) { - ADMP(va("^3!give: ^7invalid client number\n")); - return qfalse; - } - } else { - int pids[ MAX_CLIENTS ]; - - if (G_ClientNumbersFromString(arg_name, pids) != 1) { - char error[MAX_STRING_CHARS]; - - G_MatchOnePlayer(pids, error, sizeof(error)); - ADMP(va("^3!give: ^7%s\n", error)); - return qfalse; - } - - target_id = pids[0]; - } - - - target = g_entities + target_id; - - if (!target->client || - target->client->pers.connected != CON_CONNECTED) { - invalid_target: - ADMP("^3!give: ^7invalid target\n"); - return qfalse; - } - - G_SayArgv(2 + skiparg, arg_amount, sizeof(arg_amount)); - amount = atoi(arg_amount); - - switch (target->client->pers.teamSelection) { - case PTE_ALIENS: - if (amount < -9 || amount > 9) { - too_big: - ADMP("^3!give: ^7amount is too big\n"); - return qfalse; - } - - currency = "evo"; - break; - - case PTE_HUMANS: - if (amount < -2000 || amount > 2000) - goto too_big; - - currency = "credit"; - break; - - default: - goto invalid_target; - } - - G_AddCreditToClient(target->client, amount, qtrue); - AP(va("print \"^3!give: ^7%s^7 was given %i %s%s by ^7%s^7\n\"", - target->client->pers.netname, amount, currency, - (abs(amount) != 1 ? "s" : ""), - ent ? G_admin_adminPrintName(ent) : "console")); - - return qtrue; -} - -extern mapRotations_t mapRotations; +/* +================ + G_admin_print -qboolean G_admin_setrotation(gentity_t *ent, int skiparg) + This function facilitates the ADMP define. ADMP() is similar to CP except + that it prints the message to the server console if ent is not defined. +================ +*/ +void G_admin_print( gentity_t *ent, const char *m ) { - char new_rotation[MAX_NAME_LENGTH]; - int i; - - if (G_SayArgc() < 2 + skiparg) - { - ADMP("^3!setrotation: ^7usage: !setrotation [rotation]\n"); - ADMP("Available rotations:\n"); - goto rotationlist; - } - - G_SayArgv(1 + skiparg, new_rotation, sizeof(new_rotation)); - - for( i = 0; i < mapRotations.numRotations; i++ ) - { - if( Q_stricmp( mapRotations.rotations[ i ].name, new_rotation ) == 0 ) - { - G_StartMapRotation(new_rotation, qfalse); - trap_SendServerCommand( -1, va("print \"^3!setrotation: ^7rotation ^3%s ^7was started by %s", - new_rotation, ent ? G_admin_adminPrintName(ent) : "console")); - return qtrue; - } - } - ADMP("^3!setrotation: ^7rotation not found. Available rotations:\n"); - goto rotationlist; - rotationlist: + if( ent ) + trap_SendServerCommand( ent - level.gentities, va( "print \"%s\"", m ) ); + else { - for( i = 0; i < mapRotations.numRotations; i++ ) + char m2[ MAX_STRING_CHARS ]; + if( !trap_Cvar_VariableIntegerValue( "com_ansiColor" ) ) { - ADMP(va(" %s\n", mapRotations.rotations[ i ].name)); + G_DecolorString( m, m2, sizeof( m2 ) ); + trap_Print( m2 ); } - ADMP(va("Number of available rotations: ^3%d\n", mapRotations.numRotations)); + else + trap_Print( m ); } - return qfalse; } -qboolean G_admin_versions(gentity_t *ent, int skiparg) +void G_admin_buffer_begin( void ) { - int i; - - ADMBP_begin(); - - for (i = 0; i < level.maxclients; i++) { - gclient_t *client = level.clients + i; - char userinfo[ MAX_INFO_STRING ], *p; - - if (client->pers.connected == CON_DISCONNECTED) - continue; - - ADMBP(va("%02i ", i)); - - trap_GetUserinfo(i, userinfo, sizeof(userinfo)); - p = Info_ValueForKey(userinfo, "version"); - - if (p[0]) - ADMBP(va("'%s'\n", p)); - else { - p = Info_ValueForKey(userinfo, "cl_voip"); - - if (p[0]) - ADMBP("probably GPP or newer\n"); - else - ADMBP("probably stock 1.1\n"); - } - } - - ADMBP_end(); - return qtrue; -} - -static int calc_ff_pct(statsCounters_t *sc) { - if (sc->dmgdone + sc->structdmgdone <= 0) { - if (sc->ffdmgdone <= 0) - return 0; - else - return 100; - } // else { - // return round((float)sc->ffdmgdone / (sc->ffdmgdone + sc->dmgdone + sc->structdmgdone) * 100); - // } + g_bfb[ 0 ] = '\0'; } -qboolean G_admin_showff(gentity_t *ent, int skiparg) +void G_admin_buffer_end( gentity_t *ent ) { - char arg_name_raw[MAX_NAME_LENGTH]; - char arg_name[MAX_NAME_LENGTH]; - int target_id, ffpct; - gentity_t *target; - statsCounters_t *sc; - - if (G_SayArgc() == 1 + skiparg) { - int i; - char team[4]; - gclient_t *client; - - ADMBP_begin(); - ADMBP("^3!showff:^7 friendly fire damage percentage for all connected players\n"); - - for (i = 0; i < level.maxclients; i++) { - client = &level.clients[i]; - - if (client->pers.connected != CON_CONNECTED) - continue; - - if (client->pers.teamSelection == PTE_HUMANS) - Com_sprintf( team, sizeof( team ), "^4H", team); - else if (client->pers.teamSelection == PTE_ALIENS) - Com_sprintf( team, sizeof( team ), "^1A", team); - else - Com_sprintf( team, sizeof( team ), "^3S", team); - - ffpct = calc_ff_pct(&client->pers.statscounters); - ADMBP(va("%2d %s ^1%3d%% ^7%s^7\n", i, team, ffpct, client->pers.netname)); - } - - ADMBP("^7for detailed information, use ^3!showff player|slot^7\n"); - ADMBP_end(); - return qtrue; - } - - G_SayArgv(1 + skiparg, arg_name_raw, sizeof(arg_name_raw)); - G_SanitiseString(arg_name_raw, arg_name, sizeof(arg_name)); - - if (is_numeric(arg_name)) { - target_id = atoi(arg_name); - - if (target_id < 0 || target_id >= MAX_CLIENTS) { - ADMP(va("^3!showff: ^7invalid client number\n")); - return qfalse; - } - } else { - int pids[MAX_CLIENTS]; - - if (G_ClientNumbersFromString(arg_name, pids) != 1) { - char error[MAX_STRING_CHARS]; - - G_MatchOnePlayer(pids, error, sizeof(error)); - ADMP(va("^3!showff: ^7%s\n", error)); - return qfalse; - } - - target_id = pids[0]; - } - - target = g_entities + target_id; - sc = &target->client->pers.statscounters; - ffpct = calc_ff_pct(sc); - - ADMP(va("^3!showff: ^7detailed FF information for %s^7:\n", - target->client->pers.netname)); - ADMP(va("^7damage to: Enemies: ^1%d^7, structures: ^1%d^7, friendlies: ^1%d\n", - sc->dmgdone, sc->structdmgdone, sc->ffdmgdone)); - ADMP(va("dealt ^1%d%%^7 of their total damage to the team\n", ffpct)); - - return qtrue; + ADMP( g_bfb ); } -void G_admin_tklog_cleanup( void ) +void G_admin_buffer_print( gentity_t *ent, const char *m ) { - int i; - - for( i = 0; i < MAX_ADMIN_TKLOGS && g_admin_tklog[ i ]; i++ ) + // 1022 - strlen("print 64 \"\"") - 1 + if( strlen( m ) + strlen( g_bfb ) >= 1009 ) { - G_Free( g_admin_tklog[ i ] ); - g_admin_tklog[ i ] = NULL; + ADMP( g_bfb ); + g_bfb[ 0 ] = '\0'; } - - admin_tklog_index = 0; + Q_strcat( g_bfb, sizeof( g_bfb ), m ); } -void G_admin_tklog_log( gentity_t *attacker, gentity_t *victim, int meansOfDeath ) -{ - g_admin_tklog_t *tklog; - int previous; - int count = 1; - - if( !attacker ) - return; - - previous = admin_tklog_index - 1; - if( previous < 0 ) - previous = MAX_ADMIN_TKLOGS - 1; - - if( g_admin_tklog[ previous ] ) - count = g_admin_tklog[ previous ]->id + 1; - if( g_admin_tklog[ admin_tklog_index ] ) - tklog = g_admin_tklog[ admin_tklog_index ]; - else - tklog = G_Alloc( sizeof( g_admin_tklog_t ) ); - - memset( tklog, 0, sizeof( g_admin_tklog_t ) ); - tklog->id = count; - tklog->time = level.time - level.startTime; - Q_strncpyz( tklog->name, attacker->client->pers.netname, sizeof( tklog->name ) ); - - if( victim ) - { - Q_strncpyz( tklog->victim, victim->client->pers.netname, sizeof( tklog->victim ) ); - tklog->damage = victim->client->tkcredits[ attacker->s.number ]; - tklog->value = victim->client->ps.stats[ STAT_MAX_HEALTH ]; - } - else - { - Q_strncpyz( tklog->victim, "^3BLEEDING", sizeof( tklog->victim ) ); - tklog->damage = attacker->client->pers.statscounters.spreebleeds; - tklog->value = g_bleedingSpree.integer * 100; - } - - tklog->team = attacker->client->ps.stats[ STAT_PTEAM ]; - if( meansOfDeath == MOD_GRENADE ) - tklog->weapon = WP_GRENADE; - else if( tklog->team == PTE_HUMANS ) - tklog->weapon = attacker->s.weapon; - else - tklog->weapon = attacker->client->ps.stats[ STAT_PCLASS ]; - - g_admin_tklog[ admin_tklog_index ] = tklog; - admin_tklog_index++; - if( admin_tklog_index >= MAX_ADMIN_TKLOGS ) - admin_tklog_index = 0; -} - -qboolean G_admin_tklog( gentity_t *ent, int skiparg ) +void G_admin_cleanup( void ) { - g_admin_tklog_t *results[ 10 ]; - int result_index = 0; - char *search_name = NULL; - int index; - int skip = 0; - int skipped = 0; - int checked = 0; - char n1[ MAX_NAME_LENGTH ]; - char fmt_name[ 16 ]; - char argbuf[ 32 ]; - char *weaponName; - int name_length = 10; - int max_id = 0; - int i; - qboolean match; - - memset( results, 0, sizeof( results ) ); - - index = admin_tklog_index; - for( i = 0; i < 10; i++ ) - { - int prev; - - prev = index - 1; - if( prev < 0 ) - prev = MAX_ADMIN_TKLOGS - 1; - if( !g_admin_tklog[ prev ] ) - break; - if( g_admin_tklog[ prev ]->id > max_id ) - max_id = g_admin_tklog[ prev ]->id; - index = prev; - } - - if( G_SayArgc() > 1 + skiparg ) - { - G_SayArgv( 1 + skiparg, argbuf, sizeof( argbuf ) ); - if( ( *argbuf >= '0' && *argbuf <= '9' ) || *argbuf == '-' ) - { - int id; - - id = atoi( argbuf ); - if( id < 0 ) - id += ( max_id - 9 ); - else if( id <= max_id - MAX_ADMIN_TKLOGS ) - id = max_id - MAX_ADMIN_TKLOGS + 1; - - if( id + 9 >= max_id ) - id = max_id - 9; - if( id < 1 ) - id = 1; - for( i = 0; i < MAX_ADMIN_TKLOGS; i++ ) - { - if( g_admin_tklog[ i ]->id == id ) - { - index = i; - break; - } - } - } - else - { - search_name = argbuf; - } - - if( G_SayArgc() > 2 + skiparg && ( search_name ) ) - { - char skipbuf[ 4 ]; - G_SayArgv( 2 + skiparg, skipbuf, sizeof( skipbuf ) ); - skip = atoi( skipbuf ); - } - } + g_admin_level_t *l; + g_admin_admin_t *a; + g_admin_ban_t *b; + g_admin_command_t *c; + void *n; - if( search_name ) + for( l = g_admin_levels; l; l = n ) { - g_admin_tklog_t *result_swap[ 10 ]; - - memset( &result_swap, 0, sizeof( result_swap ) ); - - index = admin_tklog_index - 1; - if( index < 0 ) - index = MAX_ADMIN_TKLOGS - 1; - - while( g_admin_tklog[ index ] && - checked < MAX_ADMIN_TKLOGS && - result_index < 10 ) - { - match = qfalse; - - G_SanitiseString( g_admin_tklog[ index ]->name, n1, sizeof( n1 ) ); - if( strstr( n1, search_name ) ) - match = qtrue; - - if( match && skip > 0 ) - { - match = qfalse; - skip--; - skipped++; - } - if( match ) - { - result_swap[ result_index ] = g_admin_tklog[ index ]; - result_index++; - } - - checked++; - index--; - if( index < 0 ) - index = MAX_ADMIN_TKLOGS - 1; - } - // search runs backwards, turn it around - for( i = 0; i < result_index; i++ ) - results[ i ] = result_swap[ result_index - i - 1 ]; + n = l->next; + BG_Free( l ); } - else + g_admin_levels = NULL; + for( a = g_admin_admins; a; a = n ) { - while( g_admin_tklog[ index ] && result_index < 10 ) - { - results[ result_index ] = g_admin_tklog[ index ]; - result_index++; - index++; - if( index >= MAX_ADMIN_TKLOGS ) - index = 0; - } + n = a->next; + BG_Free( a ); } - - for( i = 0; results[ i ] && i < 10; i++ ) - { - int l; - - G_DecolorString( results[ i ]->name, n1 ); - l = strlen( n1 ); - if( l > name_length ) - name_length = l; - } - ADMBP_begin( ); - for( i = 0; results[ i ] && i < 10; i++ ) + g_admin_admins = NULL; + for( b = g_admin_bans; b; b = n ) { - int t; - - t = results[ i ]->time / 1000; - - G_DecolorString( results[ i ]->name, n1 ); - Com_sprintf( fmt_name, sizeof( fmt_name ), "%%%ds", - ( name_length + (int)( strlen( results[ i ]->name ) - strlen( n1 ) ) ) ); - Com_sprintf( n1, sizeof( n1 ), fmt_name, results[ i ]->name ); - - if( results[ i ]->team == PTE_HUMANS ) - weaponName = BG_FindNameForWeapon( results[ i ]->weapon ); - else - weaponName = BG_FindNameForClassNum( results[ i ]->weapon ); - - ADMBP( va( "^7%3d %3d:%02d %s^7 %3d / %3d %10s %s^7\n", - results[ i ]->id, - t / 60, t % 60, - n1, - results[ i ]->damage, - results[ i ]->value, - weaponName, - results[ i ]->victim ) ); - } - if( search_name ) - { - ADMBP( va( "^3!tklog:^7 Showing %d matches for '%s^7'.", - result_index, - argbuf ) ); - if( checked < MAX_ADMIN_TKLOGS && g_admin_tklog[ checked ] ) - ADMBP( va( " run '!tklog %s^7 %d' to see more", - argbuf, - skipped + result_index ) ); - ADMBP( "\n" ); - } - else if ( results[ 0 ] ) - { - ADMBP( va( "^3!tklog:^7 Showing %d - %d of %d.\n", - results[ 0 ]->id, - results[ result_index - 1 ]->id, - max_id ) ); + n = b->next; + BG_Free( b ); } - else + g_admin_bans = NULL; + for( c = g_admin_commands; c; c = n ) { - ADMBP( "^3!tklog:^7 log is empty.\n" ); + n = c->next; + BG_Free( c ); } - ADMBP_end( ); - - return qtrue; + g_admin_commands = NULL; + BG_DefragmentMemory( ); } -qboolean G_admin_sm( gentity_t *ent, int skiparg ) +qboolean G_admin_sm( gentity_t *ent ) { const char *s; char feature[ 16 ]; - if( G_SayArgc() < 2 + skiparg ) + if( trap_Argc() < 2 ) { usage: - ADMP( "^3!sm: ^7usage: !sm ipa <IPA>\n" ); + ADMP( "^3sm: ^7usage: sm ipa <IPA>\n" ); return qfalse; } - s = G_SayConcatArgs( 1 + skiparg ); + s = ConcatArgs( 1 ); if( strchr( s, '\n' ) || strchr( s, '\r' ) ) { - ADMP( "^3!sm: ^7invalid character\n" ); + ADMP( "^3sm: ^7invalid character\n" ); return qfalse; } - G_SayArgv( 1 + skiparg, feature, sizeof( feature ) ); + trap_Argv( 1, feature, sizeof( feature ) ); if( !Q_stricmp( feature, "ipa" ) ) { char ipa[ 32 ]; - int parts[ 4 ]; - if( G_SayArgc() > 3 + skiparg ) + if( trap_Argc() > 3 ) { - ADMP( "^3!sm: ^7excessive arguments\n" ); + ADMP( "^3sm: ^7excessive arguments\n" ); goto usage; } - G_SayArgv( 2 + skiparg, ipa, sizeof( ipa ) ); - - // have a well-formed IPv4 address, because Schachtmeister silently drops all invalid requests - - if( sscanf( ipa, "%i.%i.%i.%i", &parts[0], &parts[1], &parts[2], &parts[3] ) != 4 - || parts[0] < 0 || parts[0] > 255 - || parts[1] < 0 || parts[1] > 255 - || parts[2] < 0 || parts[2] > 255 - || parts[3] < 0 || parts[3] > 255 ) - { - ADMP( "^3!sm: ^7invalid IP address\n" ); - return qfalse; - } - - Com_sprintf( ipa, sizeof( ipa ), "%i.%i.%i.%i", parts[0], parts[1], parts[2], parts[3] ); + trap_Argv( 2, ipa, sizeof( ipa ) ); if( rand() % 2 /* FIXME cache hit */ ) { const char *answer = "interesting"; - ADMP( va( "^3!sm: ^7IP address %s is: %s\n", ipa, answer ) ); + ADMP( va( "^3sm: ^7IP address '%s^7' is: %s\n", ipa, answer ) ); return qtrue; } - ADMP( "^3!sm: ^7hmm...\n" ); + ADMP( "^3sm: ^7hmm...\n" ); trap_SendConsoleCommand( EXEC_APPEND, va( "smq ipa \"%s\"\n", ipa ) ); } else goto usage; return qtrue; -} +}
\ No newline at end of file |