Home | History | Annotate | Download | only in server
      1 /*
      2 Copyright (C) 1996-1997 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 
     21 #include "qwsvdef.h"
     22 
     23 qboolean	sv_allow_cheats;
     24 
     25 int fp_messages=4, fp_persecond=4, fp_secondsdead=10;
     26 char fp_msg[255] = { 0 };
     27 extern cvar_t cl_warncmd;
     28 	extern		redirect_t	sv_redirected;
     29 
     30 
     31 /*
     32 ===============================================================================
     33 
     34 OPERATOR CONSOLE ONLY COMMANDS
     35 
     36 These commands can only be entered from stdin or by a remote operator datagram
     37 ===============================================================================
     38 */
     39 
     40 /*
     41 ====================
     42 SV_SetMaster_f
     43 
     44 Make a master server current
     45 ====================
     46 */
     47 void SV_SetMaster_f (void)
     48 {
     49 	char	data[2];
     50 	int		i;
     51 
     52 	memset (&master_adr, 0, sizeof(master_adr));
     53 
     54 	for (i=1 ; i<Cmd_Argc() ; i++)
     55 	{
     56 		if (!strcmp(Cmd_Argv(i), "none") || !NET_StringToAdr (Cmd_Argv(i), &master_adr[i-1]))
     57 		{
     58 			Con_Printf ("Setting nomaster mode.\n");
     59 			return;
     60 		}
     61 		if (master_adr[i-1].port == 0)
     62 			master_adr[i-1].port = BigShort (27000);
     63 
     64 		Con_Printf ("Master server at %s\n", NET_AdrToString (master_adr[i-1]));
     65 
     66 		Con_Printf ("Sending a ping.\n");
     67 
     68 		data[0] = A2A_PING;
     69 		data[1] = 0;
     70 		NET_SendPacket (2, data, master_adr[i-1]);
     71 	}
     72 
     73 	svs.last_heartbeat = -99999;
     74 }
     75 
     76 
     77 /*
     78 ==================
     79 SV_Quit_f
     80 ==================
     81 */
     82 void SV_Quit_f (void)
     83 {
     84 	SV_FinalMessage ("server shutdown\n");
     85 	Con_Printf ("Shutting down.\n");
     86 	SV_Shutdown ();
     87 	Sys_Quit ();
     88 }
     89 
     90 /*
     91 ============
     92 SV_Logfile_f
     93 ============
     94 */
     95 void SV_Logfile_f (void)
     96 {
     97 	char	name[MAX_OSPATH];
     98 
     99 	if (sv_logfile)
    100 	{
    101 		Con_Printf ("File logging off.\n");
    102 		fclose (sv_logfile);
    103 		sv_logfile = NULL;
    104 		return;
    105 	}
    106 
    107 	sprintf (name, "%s/qconsole.log", com_gamedir);
    108 	Con_Printf ("Logging text to %s.\n", name);
    109 	sv_logfile = fopen (name, "w");
    110 	if (!sv_logfile)
    111 		Con_Printf ("failed.\n");
    112 }
    113 
    114 
    115 /*
    116 ============
    117 SV_Fraglogfile_f
    118 ============
    119 */
    120 void SV_Fraglogfile_f (void)
    121 {
    122 	char	name[MAX_OSPATH];
    123 	int		i;
    124 
    125 	if (sv_fraglogfile)
    126 	{
    127 		Con_Printf ("Frag file logging off.\n");
    128 		fclose (sv_fraglogfile);
    129 		sv_fraglogfile = NULL;
    130 		return;
    131 	}
    132 
    133 	// find an unused name
    134 	for (i=0 ; i<1000 ; i++)
    135 	{
    136 		sprintf (name, "%s/frag_%i.log", com_gamedir, i);
    137 		sv_fraglogfile = fopen (name, "r");
    138 		if (!sv_fraglogfile)
    139 		{	// can't read it, so create this one
    140 			sv_fraglogfile = fopen (name, "w");
    141 			if (!sv_fraglogfile)
    142 				i=1000;	// give error
    143 			break;
    144 		}
    145 		fclose (sv_fraglogfile);
    146 	}
    147 	if (i==1000)
    148 	{
    149 		Con_Printf ("Can't open any logfiles.\n");
    150 		sv_fraglogfile = NULL;
    151 		return;
    152 	}
    153 
    154 	Con_Printf ("Logging frags to %s.\n", name);
    155 }
    156 
    157 
    158 /*
    159 ==================
    160 SV_SetPlayer
    161 
    162 Sets host_client and sv_player to the player with idnum Cmd_Argv(1)
    163 ==================
    164 */
    165 qboolean SV_SetPlayer (void)
    166 {
    167 	client_t	*cl;
    168 	int			i;
    169 	int			idnum;
    170 
    171 	idnum = atoi(Cmd_Argv(1));
    172 
    173 	for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
    174 	{
    175 		if (!cl->state)
    176 			continue;
    177 		if (cl->userid == idnum)
    178 		{
    179 			host_client = cl;
    180 			sv_player = host_client->edict;
    181 			return true;
    182 		}
    183 	}
    184 	Con_Printf ("Userid %i is not on the server\n", idnum);
    185 	return false;
    186 }
    187 
    188 
    189 /*
    190 ==================
    191 SV_God_f
    192 
    193 Sets client to godmode
    194 ==================
    195 */
    196 void SV_God_f (void)
    197 {
    198 	if (!sv_allow_cheats)
    199 	{
    200 		Con_Printf ("You must run the server with -cheats to enable this command.\n");
    201 		return;
    202 	}
    203 
    204 	if (!SV_SetPlayer ())
    205 		return;
    206 
    207 	sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE;
    208 	if (!((int)sv_player->v.flags & FL_GODMODE) )
    209 		SV_ClientPrintf (host_client, PRINT_HIGH, "godmode OFF\n");
    210 	else
    211 		SV_ClientPrintf (host_client, PRINT_HIGH, "godmode ON\n");
    212 }
    213 
    214 
    215 void SV_Noclip_f (void)
    216 {
    217 	if (!sv_allow_cheats)
    218 	{
    219 		Con_Printf ("You must run the server with -cheats to enable this command.\n");
    220 		return;
    221 	}
    222 
    223 	if (!SV_SetPlayer ())
    224 		return;
    225 
    226 	if (sv_player->v.movetype != MOVETYPE_NOCLIP)
    227 	{
    228 		sv_player->v.movetype = MOVETYPE_NOCLIP;
    229 		SV_ClientPrintf (host_client, PRINT_HIGH, "noclip ON\n");
    230 	}
    231 	else
    232 	{
    233 		sv_player->v.movetype = MOVETYPE_WALK;
    234 		SV_ClientPrintf (host_client, PRINT_HIGH, "noclip OFF\n");
    235 	}
    236 }
    237 
    238 
    239 /*
    240 ==================
    241 SV_Give_f
    242 ==================
    243 */
    244 void SV_Give_f (void)
    245 {
    246 	char	*t;
    247 	int		v;
    248 
    249 	if (!sv_allow_cheats)
    250 	{
    251 		Con_Printf ("You must run the server with -cheats to enable this command.\n");
    252 		return;
    253 	}
    254 
    255 	if (!SV_SetPlayer ())
    256 		return;
    257 
    258 	t = Cmd_Argv(2);
    259 	v = atoi (Cmd_Argv(3));
    260 
    261 	switch (t[0])
    262 	{
    263 	case '2':
    264 	case '3':
    265 	case '4':
    266 	case '5':
    267 	case '6':
    268 	case '7':
    269 	case '8':
    270 	case '9':
    271 		sv_player->v.items = (int)sv_player->v.items | IT_SHOTGUN<< (t[0] - '2');
    272 		break;
    273 
    274 	case 's':
    275 		sv_player->v.ammo_shells = v;
    276 		break;
    277 	case 'n':
    278 		sv_player->v.ammo_nails = v;
    279 		break;
    280 	case 'r':
    281 		sv_player->v.ammo_rockets = v;
    282 		break;
    283 	case 'h':
    284 		sv_player->v.health = v;
    285 		break;
    286 	case 'c':
    287 		sv_player->v.ammo_cells = v;
    288 		break;
    289 	}
    290 }
    291 
    292 
    293 /*
    294 ======================
    295 SV_Map_f
    296 
    297 handle a
    298 map <mapname>
    299 command from the console or progs.
    300 ======================
    301 */
    302 void SV_Map_f (void)
    303 {
    304 	char	level[MAX_QPATH];
    305 	char	expanded[MAX_QPATH];
    306 	FILE	*f;
    307 
    308 	if (Cmd_Argc() != 2)
    309 	{
    310 		Con_Printf ("map <levelname> : continue game on a new level\n");
    311 		return;
    312 	}
    313 	strcpy (level, Cmd_Argv(1));
    314 
    315 #if 0
    316 	if (!strcmp (level, "e1m8"))
    317 	{	// QuakeWorld can't go to e1m8
    318 		SV_BroadcastPrintf (PRINT_HIGH, "can't go to low grav level in QuakeWorld...\n");
    319 		strcpy (level, "e1m5");
    320 	}
    321 #endif
    322 
    323 	// check to make sure the level exists
    324 	sprintf (expanded, "maps/%s.bsp", level);
    325 	COM_FOpenFile (expanded, &f);
    326 	if (!f)
    327 	{
    328 		Con_Printf ("Can't find %s\n", expanded);
    329 		return;
    330 	}
    331 	fclose (f);
    332 
    333 	SV_BroadcastCommand ("changing\n");
    334 	SV_SendMessagesToAll ();
    335 
    336 	SV_SpawnServer (level);
    337 
    338 	SV_BroadcastCommand ("reconnect\n");
    339 }
    340 
    341 
    342 /*
    343 ==================
    344 SV_Kick_f
    345 
    346 Kick a user off of the server
    347 ==================
    348 */
    349 void SV_Kick_f (void)
    350 {
    351 	int			i;
    352 	client_t	*cl;
    353 	int			uid;
    354 
    355 	uid = atoi(Cmd_Argv(1));
    356 
    357 	for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++)
    358 	{
    359 		if (!cl->state)
    360 			continue;
    361 		if (cl->userid == uid)
    362 		{
    363 			SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked\n", cl->name);
    364 			// print directly, because the dropped client won't get the
    365 			// SV_BroadcastPrintf message
    366 			SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game\n");
    367 			SV_DropClient (cl);
    368 			return;
    369 		}
    370 	}
    371 
    372 	Con_Printf ("Couldn't find user number %i\n", uid);
    373 }
    374 
    375 
    376 /*
    377 ================
    378 SV_Status_f
    379 ================
    380 */
    381 void SV_Status_f (void)
    382 {
    383 	int			i, j, l;
    384 	client_t	*cl;
    385 	float		cpu, avg, pak;
    386 	char		*s;
    387 
    388 
    389 	cpu = (svs.stats.latched_active+svs.stats.latched_idle);
    390 	if (cpu)
    391 		cpu = 100*svs.stats.latched_active/cpu;
    392 	avg = 1000*svs.stats.latched_active / STATFRAMES;
    393 	pak = (float)svs.stats.latched_packets/ STATFRAMES;
    394 
    395 	Con_Printf ("net address      : %s\n",NET_AdrToString (net_local_adr));
    396 	Con_Printf ("cpu utilization  : %3i%%\n",(int)cpu);
    397 	Con_Printf ("avg response time: %i ms\n",(int)avg);
    398 	Con_Printf ("packets/frame    : %5.2f (%d)\n", pak, num_prstr);
    399 
    400 // min fps lat drp
    401 	if (sv_redirected != RD_NONE) {
    402 		// most remote clients are 40 columns
    403 		//           0123456789012345678901234567890123456789
    404 		Con_Printf ("name               userid frags\n");
    405         Con_Printf ("  address          rate ping drop\n");
    406 		Con_Printf ("  ---------------- ---- ---- -----\n");
    407 		for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
    408 		{
    409 			if (!cl->state)
    410 				continue;
    411 
    412 			Con_Printf ("%-16.16s  ", cl->name);
    413 
    414 			Con_Printf ("%6i %5i", cl->userid, (int)cl->edict->v.frags);
    415 			if (cl->spectator)
    416 				Con_Printf(" (s)\n");
    417 			else
    418 				Con_Printf("\n");
    419 
    420 			s = NET_BaseAdrToString ( cl->netchan.remote_address);
    421 			Con_Printf ("  %-16.16s", s);
    422 			if (cl->state == cs_connected)
    423 			{
    424 				Con_Printf ("CONNECTING\n");
    425 				continue;
    426 			}
    427 			if (cl->state == cs_zombie)
    428 			{
    429 				Con_Printf ("ZOMBIE\n");
    430 				continue;
    431 			}
    432 			Con_Printf ("%4i %4i %5.2f\n"
    433 				, (int)(1000*cl->netchan.frame_rate)
    434 				, (int)SV_CalcPing (cl)
    435 				, 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence);
    436 		}
    437 	} else {
    438 		Con_Printf ("frags userid address         name            rate ping drop  qport\n");
    439 		Con_Printf ("----- ------ --------------- --------------- ---- ---- ----- -----\n");
    440 		for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
    441 		{
    442 			if (!cl->state)
    443 				continue;
    444 			Con_Printf ("%5i %6i ", (int)cl->edict->v.frags,  cl->userid);
    445 
    446 			s = NET_BaseAdrToString ( cl->netchan.remote_address);
    447 			Con_Printf ("%s", s);
    448 			l = 16 - strlen(s);
    449 			for (j=0 ; j<l ; j++)
    450 				Con_Printf (" ");
    451 
    452 			Con_Printf ("%s", cl->name);
    453 			l = 16 - strlen(cl->name);
    454 			for (j=0 ; j<l ; j++)
    455 				Con_Printf (" ");
    456 			if (cl->state == cs_connected)
    457 			{
    458 				Con_Printf ("CONNECTING\n");
    459 				continue;
    460 			}
    461 			if (cl->state == cs_zombie)
    462 			{
    463 				Con_Printf ("ZOMBIE\n");
    464 				continue;
    465 			}
    466 			Con_Printf ("%4i %4i %3.1f %4i"
    467 				, (int)(1000*cl->netchan.frame_rate)
    468 				, (int)SV_CalcPing (cl)
    469 				, 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence
    470 				, cl->netchan.qport);
    471 			if (cl->spectator)
    472 				Con_Printf(" (s)\n");
    473 			else
    474 				Con_Printf("\n");
    475 
    476 
    477 		}
    478 	}
    479 	Con_Printf ("\n");
    480 }
    481 
    482 /*
    483 ==================
    484 SV_ConSay_f
    485 ==================
    486 */
    487 void SV_ConSay_f(void)
    488 {
    489 	client_t *client;
    490 	int		j;
    491 	char	*p;
    492 	char	text[1024];
    493 
    494 	if (Cmd_Argc () < 2)
    495 		return;
    496 
    497 	Q_strcpy (text, "console: ");
    498 	p = Cmd_Args();
    499 
    500 	if (*p == '"')
    501 	{
    502 		p++;
    503 		p[Q_strlen(p)-1] = 0;
    504 	}
    505 
    506 	Q_strcat(text, p);
    507 
    508 	for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++)
    509 	{
    510 		if (client->state != cs_spawned)
    511 			continue;
    512 		SV_ClientPrintf(client, PRINT_CHAT, "%s\n", text);
    513 	}
    514 }
    515 
    516 
    517 /*
    518 ==================
    519 SV_Heartbeat_f
    520 ==================
    521 */
    522 void SV_Heartbeat_f (void)
    523 {
    524 	svs.last_heartbeat = -9999;
    525 }
    526 
    527 void SV_SendServerInfoChange(char *key, char *value)
    528 {
    529 	if (!sv.state)
    530 		return;
    531 
    532 	MSG_WriteByte (&sv.reliable_datagram, svc_serverinfo);
    533 	MSG_WriteString (&sv.reliable_datagram, key);
    534 	MSG_WriteString (&sv.reliable_datagram, value);
    535 }
    536 
    537 /*
    538 ===========
    539 SV_Serverinfo_f
    540 
    541   Examine or change the serverinfo string
    542 ===========
    543 */
    544 char *CopyString(char *s);
    545 void SV_Serverinfo_f (void)
    546 {
    547 	cvar_t	*var;
    548 
    549 	if (Cmd_Argc() == 1)
    550 	{
    551 		Con_Printf ("Server info settings:\n");
    552 		Info_Print (svs.info);
    553 		return;
    554 	}
    555 
    556 	if (Cmd_Argc() != 3)
    557 	{
    558 		Con_Printf ("usage: serverinfo [ <key> <value> ]\n");
    559 		return;
    560 	}
    561 
    562 	if (Cmd_Argv(1)[0] == '*')
    563 	{
    564 		Con_Printf ("Star variables cannot be changed.\n");
    565 		return;
    566 	}
    567 	Info_SetValueForKey (svs.info, Cmd_Argv(1), Cmd_Argv(2), MAX_SERVERINFO_STRING);
    568 
    569 	// if this is a cvar, change it too
    570 	var = Cvar_FindVar (Cmd_Argv(1));
    571 	if (var)
    572 	{
    573 		Z_Free (var->string);	// free the old value string
    574 		var->string = CopyString (Cmd_Argv(2));
    575 		var->value = Q_atof (var->string);
    576 	}
    577 
    578 	SV_SendServerInfoChange(Cmd_Argv(1), Cmd_Argv(2));
    579 }
    580 
    581 
    582 /*
    583 ===========
    584 SV_Serverinfo_f
    585 
    586   Examine or change the serverinfo string
    587 ===========
    588 */
    589 char *CopyString(char *s);
    590 void SV_Localinfo_f (void)
    591 {
    592 	if (Cmd_Argc() == 1)
    593 	{
    594 		Con_Printf ("Local info settings:\n");
    595 		Info_Print (localinfo);
    596 		return;
    597 	}
    598 
    599 	if (Cmd_Argc() != 3)
    600 	{
    601 		Con_Printf ("usage: localinfo [ <key> <value> ]\n");
    602 		return;
    603 	}
    604 
    605 	if (Cmd_Argv(1)[0] == '*')
    606 	{
    607 		Con_Printf ("Star variables cannot be changed.\n");
    608 		return;
    609 	}
    610 	Info_SetValueForKey (localinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_LOCALINFO_STRING);
    611 }
    612 
    613 
    614 /*
    615 ===========
    616 SV_User_f
    617 
    618 Examine a users info strings
    619 ===========
    620 */
    621 void SV_User_f (void)
    622 {
    623 	if (Cmd_Argc() != 2)
    624 	{
    625 		Con_Printf ("Usage: info <userid>\n");
    626 		return;
    627 	}
    628 
    629 	if (!SV_SetPlayer ())
    630 		return;
    631 
    632 	Info_Print (host_client->userinfo);
    633 }
    634 
    635 /*
    636 ================
    637 SV_Gamedir
    638 
    639 Sets the fake *gamedir to a different directory.
    640 ================
    641 */
    642 void SV_Gamedir (void)
    643 {
    644 	char			*dir;
    645 
    646 	if (Cmd_Argc() == 1)
    647 	{
    648 		Con_Printf ("Current *gamedir: %s\n", Info_ValueForKey (svs.info, "*gamedir"));
    649 		return;
    650 	}
    651 
    652 	if (Cmd_Argc() != 2)
    653 	{
    654 		Con_Printf ("Usage: sv_gamedir <newgamedir>\n");
    655 		return;
    656 	}
    657 
    658 	dir = Cmd_Argv(1);
    659 
    660 	if (strstr(dir, "..") || strstr(dir, "/")
    661 		|| strstr(dir, "\\") || strstr(dir, ":") )
    662 	{
    663 		Con_Printf ("*Gamedir should be a single filename, not a path\n");
    664 		return;
    665 	}
    666 
    667 	Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING);
    668 }
    669 
    670 /*
    671 ================
    672 SV_Floodport_f
    673 
    674 Sets the gamedir and path to a different directory.
    675 ================
    676 */
    677 
    678 void SV_Floodprot_f (void)
    679 {
    680 	int arg1, arg2, arg3;
    681 
    682 	if (Cmd_Argc() == 1)
    683 	{
    684 		if (fp_messages) {
    685 			Con_Printf ("Current floodprot settings: \nAfter %d msgs per %d seconds, silence for %d seconds\n",
    686 				fp_messages, fp_persecond, fp_secondsdead);
    687 			return;
    688 		} else
    689 			Con_Printf ("No floodprots enabled.\n");
    690 	}
    691 
    692 	if (Cmd_Argc() != 4)
    693 	{
    694 		Con_Printf ("Usage: floodprot <# of messages> <per # of seconds> <seconds to silence>\n");
    695 		Con_Printf ("Use floodprotmsg to set a custom message to say to the flooder.\n");
    696 		return;
    697 	}
    698 
    699 	arg1 = atoi(Cmd_Argv(1));
    700 	arg2 = atoi(Cmd_Argv(2));
    701 	arg3 = atoi(Cmd_Argv(3));
    702 
    703 	if (arg1<=0 || arg2 <= 0 || arg3<=0) {
    704 		Con_Printf ("All values must be positive numbers\n");
    705 		return;
    706 	}
    707 
    708 	if (arg1 > 10) {
    709 		Con_Printf ("Can only track up to 10 messages.\n");
    710 		return;
    711 	}
    712 
    713 	fp_messages	= arg1;
    714 	fp_persecond = arg2;
    715 	fp_secondsdead = arg3;
    716 }
    717 
    718 void SV_Floodprotmsg_f (void)
    719 {
    720 	if (Cmd_Argc() == 1) {
    721 		Con_Printf("Current msg: %s\n", fp_msg);
    722 		return;
    723 	} else if (Cmd_Argc() != 2) {
    724 		Con_Printf("Usage: floodprotmsg \"<message>\"\n");
    725 		return;
    726 	}
    727 	sprintf(fp_msg, "%s", Cmd_Argv(1));
    728 }
    729 
    730 /*
    731 ================
    732 SV_Gamedir_f
    733 
    734 Sets the gamedir and path to a different directory.
    735 ================
    736 */
    737 char	gamedirfile[MAX_OSPATH];
    738 void SV_Gamedir_f (void)
    739 {
    740 	char			*dir;
    741 
    742 	if (Cmd_Argc() == 1)
    743 	{
    744 		Con_Printf ("Current gamedir: %s\n", com_gamedir);
    745 		return;
    746 	}
    747 
    748 	if (Cmd_Argc() != 2)
    749 	{
    750 		Con_Printf ("Usage: gamedir <newdir>\n");
    751 		return;
    752 	}
    753 
    754 	dir = Cmd_Argv(1);
    755 
    756 	if (strstr(dir, "..") || strstr(dir, "/")
    757 		|| strstr(dir, "\\") || strstr(dir, ":") )
    758 	{
    759 		Con_Printf ("Gamedir should be a single filename, not a path\n");
    760 		return;
    761 	}
    762 
    763 	COM_Gamedir (dir);
    764 	Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING);
    765 }
    766 
    767 /*
    768 ================
    769 SV_Snap
    770 ================
    771 */
    772 void SV_Snap (int uid)
    773 {
    774 	client_t *cl;
    775 	char		pcxname[80];
    776 	char		checkname[MAX_OSPATH];
    777 	int			i;
    778 
    779 	for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++)
    780 	{
    781 		if (!cl->state)
    782 			continue;
    783 		if (cl->userid == uid)
    784 			break;
    785 	}
    786 	if (i >= MAX_CLIENTS) {
    787 		Con_Printf ("userid not found\n");
    788 		return;
    789 	}
    790 
    791 	sprintf(pcxname, "%d-00.pcx", uid);
    792 
    793 	sprintf(checkname, "%s/snap", gamedirfile);
    794 	Sys_mkdir(gamedirfile);
    795 	Sys_mkdir(checkname);
    796 
    797 	for (i=0 ; i<=99 ; i++)
    798 	{
    799 		pcxname[strlen(pcxname) - 6] = i/10 + '0';
    800 		pcxname[strlen(pcxname) - 5] = i%10 + '0';
    801 		sprintf (checkname, "%s/snap/%s", gamedirfile, pcxname);
    802 		if (Sys_FileTime(checkname) == -1)
    803 			break;	// file doesn't exist
    804 	}
    805 	if (i==100)
    806 	{
    807 		Con_Printf ("Snap: Couldn't create a file, clean some out.\n");
    808 		return;
    809 	}
    810 	strcpy(cl->uploadfn, checkname);
    811 
    812 	memcpy(&cl->snap_from, &net_from, sizeof(net_from));
    813 	if (sv_redirected != RD_NONE)
    814 		cl->remote_snap = true;
    815 	else
    816 		cl->remote_snap = false;
    817 
    818 	ClientReliableWrite_Begin (cl, svc_stufftext, 24);
    819 	ClientReliableWrite_String (cl, "cmd snap");
    820 	Con_Printf ("Requesting snap from user %d...\n", uid);
    821 }
    822 
    823 /*
    824 ================
    825 SV_Snap_f
    826 ================
    827 */
    828 void SV_Snap_f (void)
    829 {
    830 	int			uid;
    831 
    832 	if (Cmd_Argc() != 2)
    833 	{
    834 		Con_Printf ("Usage:  snap <userid>\n");
    835 		return;
    836 	}
    837 
    838 	uid = atoi(Cmd_Argv(1));
    839 
    840 	SV_Snap(uid);
    841 }
    842 
    843 /*
    844 ================
    845 SV_Snap
    846 ================
    847 */
    848 void SV_SnapAll_f (void)
    849 {
    850 	client_t *cl;
    851 	int			i;
    852 
    853 	for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++)
    854 	{
    855 		if (cl->state < cs_connected || cl->spectator)
    856 			continue;
    857 		SV_Snap(cl->userid);
    858 	}
    859 }
    860 
    861 /*
    862 ==================
    863 SV_InitOperatorCommands
    864 ==================
    865 */
    866 void SV_InitOperatorCommands (void)
    867 {
    868 	if (COM_CheckParm ("-cheats"))
    869 	{
    870 		sv_allow_cheats = true;
    871 		Info_SetValueForStarKey (svs.info, "*cheats", "ON", MAX_SERVERINFO_STRING);
    872 	}
    873 
    874 	Cmd_AddCommand ("logfile", SV_Logfile_f);
    875 	Cmd_AddCommand ("fraglogfile", SV_Fraglogfile_f);
    876 
    877 	Cmd_AddCommand ("snap", SV_Snap_f);
    878 	Cmd_AddCommand ("snapall", SV_SnapAll_f);
    879 	Cmd_AddCommand ("kick", SV_Kick_f);
    880 	Cmd_AddCommand ("status", SV_Status_f);
    881 
    882 	Cmd_AddCommand ("map", SV_Map_f);
    883 	Cmd_AddCommand ("setmaster", SV_SetMaster_f);
    884 
    885 	Cmd_AddCommand ("say", SV_ConSay_f);
    886 	Cmd_AddCommand ("heartbeat", SV_Heartbeat_f);
    887 	Cmd_AddCommand ("quit", SV_Quit_f);
    888 	Cmd_AddCommand ("god", SV_God_f);
    889 	Cmd_AddCommand ("give", SV_Give_f);
    890 	Cmd_AddCommand ("noclip", SV_Noclip_f);
    891 	Cmd_AddCommand ("serverinfo", SV_Serverinfo_f);
    892 	Cmd_AddCommand ("localinfo", SV_Localinfo_f);
    893 	Cmd_AddCommand ("user", SV_User_f);
    894 	Cmd_AddCommand ("gamedir", SV_Gamedir_f);
    895 	Cmd_AddCommand ("sv_gamedir", SV_Gamedir);
    896 	Cmd_AddCommand ("floodprot", SV_Floodprot_f);
    897 	Cmd_AddCommand ("floodprotmsg", SV_Floodprotmsg_f);
    898 
    899 	cl_warncmd.value = 1;
    900 }
    901