home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 113 / EnigmaAmiga113CD.iso / software / sviluppo / quakeworld_src / server / sv_ccmds.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-17  |  17.9 KB  |  901 lines

  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. extern char gamedirfile[MAX_OSPATH]; /*phx*/
  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.