home *** CD-ROM | disk | FTP | other *** search
/ Magazyn Amiga 14 / MA_Cover_14.iso / source / c / q1source_amy / qw / server / sv_ccmds.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-12-21  |  17.2 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. 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.