]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
(Round 1) Break up host_cmd.c
authorcloudwalk <cloudwalk@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 22 Jun 2020 15:16:13 +0000 (15:16 +0000)
committercloudwalk <cloudwalk@d7cf8633-e32d-0410-b094-e92efae38249>
Mon, 22 Jun 2020 15:16:13 +0000 (15:16 +0000)
Massive move operation of pretty much every single SV_ function.
There are some functions that are dual server and client in purpose that
need to be broken up, but that will require manual effort other than
mere copying.

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12718 d7cf8633-e32d-0410-b094-e92efae38249

darkplaces-sdl2-vs2017.vcxproj
darkplaces-sdl2-vs2019.vcxproj
host_cmd.c
makefile.inc
prvm_edict.c
quakedef.h
server.h
sv_ccmds.c [new file with mode: 0644]
sv_main.c
sv_save.c [new file with mode: 0644]
sv_user.c

index e1ee97c948a197057f40a0aaf09ac4d4181e3917..2edd2a578133ebfdde9a54e6075db0a862895490 100644 (file)
     <ClCompile Include="snd_sdl.c" />\r
     <ClCompile Include="snd_wav.c" />\r
        <ClInclude Include="snd_xmp.c" />\r
+    <ClCompile Include="sv_ccmds.c" />\r
     <ClCompile Include="sv_demo.c" />\r
     <ClCompile Include="sv_main.c" />\r
     <ClCompile Include="sv_move.c" />\r
     <ClCompile Include="sv_phys.c" />\r
+    <ClCompile Include="sv_save.c" />\r
     <ClCompile Include="sv_send.c" />\r
     <ClCompile Include="sv_user.c" />\r
     <ClCompile Include="svbsp.c" />\r
index 2d7f18644f15dfcfd7f4d94bfe9a7f60f27e088f..0baf4767ea15d885d1461805a804efc52a3248c3 100644 (file)
     <ClCompile Include="snd_sdl.c" />\r
     <ClCompile Include="snd_wav.c" />\r
        <ClCompile Include="snd_xmp.c" />\r
+    <ClCompile Include="sv_ccmds.c" />\r
     <ClCompile Include="sv_demo.c" />\r
     <ClCompile Include="sv_main.c" />\r
     <ClCompile Include="sv_move.c" />\r
     <ClCompile Include="sv_phys.c" />\r
+    <ClCompile Include="sv_save.c" />\r
     <ClCompile Include="sv_send.c" />\r
     <ClCompile Include="sv_user.c" />\r
     <ClCompile Include="svbsp.c" />\r
index 769c50f79c33525eb6125153f6a17938be7c6ef9..5689d11792306cd284e65c9271653a4a2c5d30bf 100644 (file)
@@ -31,11 +31,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include <time.h>
 
 int current_skill;
-cvar_t sv_cheats = {CVAR_SERVER | CVAR_NOTIFY, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
-cvar_t sv_adminnick = {CVAR_SERVER | CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
-cvar_t sv_status_privacy = {CVAR_SERVER | CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"};
-cvar_t sv_status_show_qcstatus = {CVAR_SERVER | CVAR_SAVE, "sv_status_show_qcstatus", "0", "show the 'qcstatus' field in status replies, not the 'frags' field. Turn this on if your mod uses this field, and the 'frags' field on the other hand has no meaningful value."};
-cvar_t sv_namechangetimer = {CVAR_SERVER | CVAR_SAVE, "sv_namechangetimer", "5", "how often to allow name changes, in seconds (prevents people from using animated names and other tricks"};
+extern cvar_t sv_adminnick;
+extern cvar_t sv_status_privacy;
+extern cvar_t sv_status_show_qcstatus;
+extern cvar_t sv_namechangetimer;
 cvar_t rcon_password = {CVAR_CLIENT | CVAR_SERVER | CVAR_PRIVATE, "rcon_password", "", "password to authenticate rcon commands; NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
 cvar_t rcon_secure = {CVAR_CLIENT | CVAR_SERVER | CVAR_NQUSERINFOHACK, "rcon_secure", "0", "force secure rcon authentication (1 = time based, 2 = challenge based); NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password"};
 cvar_t rcon_secure_challengetimeout = {CVAR_CLIENT, "rcon_secure_challengetimeout", "5", "challenge-based secure rcon: time out requests if no challenge came within this time interval"};
@@ -61,408 +60,6 @@ void Host_Quit_f(cmd_state_t *cmd)
                host.state = host_shutdown;
 }
 
-/*
-==================
-SV_Status_f
-==================
-*/
-static void SV_Status_f(cmd_state_t *cmd)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       char qcstatus[256];
-       client_t *client;
-       int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
-       void (*print) (const char *fmt, ...);
-       char ip[48]; // can contain a full length v6 address with [] and a port
-       int frags;
-       char vabuf[1024];
-
-       if (cmd->source == src_command)
-       {
-               // if running a client, try to send over network so the client's status report parser will see the report
-               if (cls.state == ca_connected)
-               {
-                       Cmd_ForwardToServer_f(cmd);
-                       return;
-               }
-               print = Con_Printf;
-       }
-       else
-               print = SV_ClientPrintf;
-
-       if (!sv.active)
-               return;
-
-       in = 0;
-       if (Cmd_Argc(cmd) == 2)
-       {
-               if (strcmp(Cmd_Argv(cmd, 1), "1") == 0)
-                       in = 1;
-               else if (strcmp(Cmd_Argv(cmd, 1), "2") == 0)
-                       in = 2;
-       }
-
-       for (players = 0, i = 0;i < svs.maxclients;i++)
-               if (svs.clients[i].active)
-                       players++;
-       print ("host:     %s\n", Cvar_VariableString (&cvars_all, "hostname", CVAR_SERVER));
-       print ("version:  %s build %s (gamename %s)\n", gamename, buildstring, gamenetworkfiltername);
-       print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
-       print ("map:      %s\n", sv.name);
-       print ("timing:   %s\n", Host_TimingReport(vabuf, sizeof(vabuf)));
-       print ("players:  %i active (%i max)\n\n", players, svs.maxclients);
-
-       if (in == 1)
-               print ("^2IP                                             %%pl ping  time   frags  no   name\n");
-       else if (in == 2)
-               print ("^5IP                                              no   name\n");
-
-       for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
-       {
-               if (!client->active)
-                       continue;
-
-               ++k;
-
-               if (in == 0 || in == 1)
-               {
-                       seconds = (int)(host.realtime - client->connecttime);
-                       minutes = seconds / 60;
-                       if (minutes)
-                       {
-                               seconds -= (minutes * 60);
-                               hours = minutes / 60;
-                               if (hours)
-                                       minutes -= (hours * 60);
-                       }
-                       else
-                               hours = 0;
-                       
-                       packetloss = 0;
-                       if (client->netconnection)
-                               for (j = 0;j < NETGRAPH_PACKETS;j++)
-                                       if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
-                                               packetloss++;
-                       packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
-                       ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
-               }
-
-               if(sv_status_privacy.integer && cmd->source != src_command)
-                       strlcpy(ip, client->netconnection ? "hidden" : "botclient", 48);
-               else
-                       strlcpy(ip, (client->netconnection && *client->netconnection->address) ? client->netconnection->address : "botclient", 48);
-
-               frags = client->frags;
-
-               if(sv_status_show_qcstatus.integer)
-               {
-                       prvm_edict_t *ed = PRVM_EDICT_NUM(i + 1);
-                       const char *str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
-                       if(str && *str)
-                       {
-                               char *p;
-                               const char *q;
-                               p = qcstatus;
-                               for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
-                                       if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
-                                               *p++ = *q;
-                               *p = 0;
-                               if(*qcstatus)
-                                       frags = atoi(qcstatus);
-                       }
-               }
-               
-               if (in == 0) // default layout
-               {
-                       if (sv.protocol == PROTOCOL_QUAKE && svs.maxclients <= 99)
-                       {
-                               // LadyHavoc: this is very touchy because we must maintain ProQuake compatible status output
-                               print ("#%-2u %-16.16s  %3i  %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
-                               print ("   %s\n", ip);
-                       }
-                       else
-                       {
-                               // LadyHavoc: no real restrictions here, not a ProQuake-compatible protocol anyway...
-                               print ("#%-3u %-16.16s %4i  %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
-                               print ("   %s\n", ip);
-                       }
-               }
-               else if (in == 1) // extended layout
-               {
-                       print ("%s%-47s %2i %4i %2i:%02i:%02i %4i  #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, packetloss, ping, hours, minutes, seconds, frags, i+1, client->name);
-               }
-               else if (in == 2) // reduced layout
-               {
-                       print ("%s%-47s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
-               }
-       }
-}
-
-
-/*
-==================
-SV_God_f
-
-Sets client to godmode
-==================
-*/
-static void SV_God_f(cmd_state_t *cmd)
-{
-       prvm_prog_t *prog = SVVM_prog;
-
-       PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_GODMODE;
-       if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_GODMODE) )
-               SV_ClientPrint("godmode OFF\n");
-       else
-               SV_ClientPrint("godmode ON\n");
-}
-
-static void SV_Notarget_f(cmd_state_t *cmd)
-{
-       prvm_prog_t *prog = SVVM_prog;
-
-       PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_NOTARGET;
-       if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_NOTARGET) )
-               SV_ClientPrint("notarget OFF\n");
-       else
-               SV_ClientPrint("notarget ON\n");
-}
-
-qboolean noclip_anglehack;
-
-static void SV_Noclip_f(cmd_state_t *cmd)
-{
-       prvm_prog_t *prog = SVVM_prog;
-
-       if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_NOCLIP)
-       {
-               noclip_anglehack = true;
-               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_NOCLIP;
-               SV_ClientPrint("noclip ON\n");
-       }
-       else
-       {
-               noclip_anglehack = false;
-               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
-               SV_ClientPrint("noclip OFF\n");
-       }
-}
-
-/*
-==================
-SV_Fly_f
-
-Sets client to flymode
-==================
-*/
-static void SV_Fly_f(cmd_state_t *cmd)
-{
-       prvm_prog_t *prog = SVVM_prog;
-
-       if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_FLY)
-       {
-               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_FLY;
-               SV_ClientPrint("flymode ON\n");
-       }
-       else
-       {
-               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
-               SV_ClientPrint("flymode OFF\n");
-       }
-}
-
-
-/*
-==================
-SV_Ping_f
-
-==================
-*/
-static void SV_Ping_f(cmd_state_t *cmd)
-{
-       int i;
-       client_t *client;
-       void (*print) (const char *fmt, ...);
-
-       if (cmd->source == src_command)
-       {
-               // if running a client, try to send over network so the client's ping report parser will see the report
-               if (cls.state == ca_connected)
-               {
-                       Cmd_ForwardToServer_f(cmd);
-                       return;
-               }
-               print = Con_Printf;
-       }
-       else
-               print = SV_ClientPrintf;
-
-       if (!sv.active)
-               return;
-
-       print("Client ping times:\n");
-       for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
-       {
-               if (!client->active)
-                       continue;
-               print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
-       }
-}
-
-// Disable cheats if sv_cheats is turned off
-static void SV_DisableCheats_c(char *value)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       int i = 0;
-
-       if (value[0] == '0' && !value[1])
-       {
-               while (svs.clients[i].edict)
-               {
-                       if (((int)PRVM_serveredictfloat(svs.clients[i].edict, flags) & FL_GODMODE))
-                               PRVM_serveredictfloat(svs.clients[i].edict, flags) = (int)PRVM_serveredictfloat(svs.clients[i].edict, flags) ^ FL_GODMODE;
-                       if (((int)PRVM_serveredictfloat(svs.clients[i].edict, flags) & FL_NOTARGET))
-                               PRVM_serveredictfloat(svs.clients[i].edict, flags) = (int)PRVM_serveredictfloat(svs.clients[i].edict, flags) ^ FL_NOTARGET;
-                       if (PRVM_serveredictfloat(svs.clients[i].edict, movetype) == MOVETYPE_NOCLIP ||
-                               PRVM_serveredictfloat(svs.clients[i].edict, movetype) == MOVETYPE_FLY)
-                       {
-                               noclip_anglehack = false;
-                               PRVM_serveredictfloat(svs.clients[i].edict, movetype) = MOVETYPE_WALK;
-                       }
-                       i++;
-               }
-       }
-}
-
-/*
-===============================================================================
-
-SERVER TRANSITIONS
-
-===============================================================================
-*/
-
-/*
-======================
-SV_Map_f
-
-handle a
-map <servername>
-command from the console.  Active clients are kicked off.
-======================
-*/
-static void SV_Map_f(cmd_state_t *cmd)
-{
-       char level[MAX_QPATH];
-
-       if (Cmd_Argc(cmd) != 2)
-       {
-               Con_Print("map <levelname> : start a new game (kicks off all players)\n");
-               return;
-       }
-
-       // GAME_DELUXEQUAKE - clear warpmark (used by QC)
-       if (gamemode == GAME_DELUXEQUAKE)
-               Cvar_Set(&cvars_all, "warpmark", "");
-
-       cls.demonum = -1;               // stop demo loop in case this fails
-
-       CL_Disconnect ();
-       SV_Shutdown();
-
-       if(svs.maxclients != svs.maxclients_next)
-       {
-               svs.maxclients = svs.maxclients_next;
-               if (svs.clients)
-                       Mem_Free(svs.clients);
-               svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
-       }
-
-#ifdef CONFIG_MENU
-       // remove menu
-       if (key_dest == key_menu || key_dest == key_menu_grabbed)
-               MR_ToggleMenu(0);
-#endif
-       key_dest = key_game;
-
-       svs.serverflags = 0;                    // haven't completed an episode yet
-       strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
-       SV_SpawnServer(level);
-       if (sv.active && cls.state == ca_disconnected)
-               CL_EstablishConnection("local:1", -2);
-}
-
-/*
-==================
-SV_Changelevel_f
-
-Goes to a new map, taking all clients along
-==================
-*/
-static void SV_Changelevel_f(cmd_state_t *cmd)
-{
-       char level[MAX_QPATH];
-
-       if (Cmd_Argc(cmd) != 2)
-       {
-               Con_Print("changelevel <levelname> : continue game on a new level\n");
-               return;
-       }
-       // HACKHACKHACK
-       if (!sv.active) {
-               SV_Map_f(cmd);
-               return;
-       }
-
-#ifdef CONFIG_MENU
-       // remove menu
-       if (key_dest == key_menu || key_dest == key_menu_grabbed)
-               MR_ToggleMenu(0);
-#endif
-       key_dest = key_game;
-
-       SV_SaveSpawnparms ();
-       strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
-       SV_SpawnServer(level);
-       if (sv.active && cls.state == ca_disconnected)
-               CL_EstablishConnection("local:1", -2);
-}
-
-/*
-==================
-SV_Restart_f
-
-Restarts the current server for a dead player
-==================
-*/
-static void SV_Restart_f(cmd_state_t *cmd)
-{
-       char mapname[MAX_QPATH];
-
-       if (Cmd_Argc(cmd) != 1)
-       {
-               Con_Print("restart : restart current level\n");
-               return;
-       }
-       if (!sv.active)
-       {
-               Con_Print("Only the server may restart\n");
-               return;
-       }
-
-#ifdef CONFIG_MENU
-       // remove menu
-       if (key_dest == key_menu || key_dest == key_menu_grabbed)
-               MR_ToggleMenu(0);
-#endif
-       key_dest = key_game;
-
-       strlcpy(mapname, sv.name, sizeof(mapname));
-       SV_SpawnServer(mapname);
-       if (sv.active && cls.state == ca_disconnected)
-               CL_EstablishConnection("local:1", -2);
-}
-
 /*
 ==================
 CL_Reconnect_f
@@ -540,613 +137,56 @@ static void CL_Connect_f(cmd_state_t *cmd)
 }
 
 
-/*
-===============================================================================
-
-LOAD / SAVE GAME
+//============================================================================
 
-===============================================================================
+/*
+======================
+CL_Name_f
+======================
 */
-
-#define        SAVEGAME_VERSION        5
-
-void SV_Savegame_to(prvm_prog_t *prog, const char *name)
+cvar_t cl_name = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
+static void CL_Name_f(cmd_state_t *cmd)
 {
-       qfile_t *f;
-       int             i, k, l, numbuffers, lightstyles = 64;
-       char    comment[SAVEGAME_COMMENT_LENGTH+1];
-       char    line[MAX_INPUTLINE];
-       qboolean isserver;
-       char    *s;
-
-       // first we have to figure out if this can be saved in 64 lightstyles
-       // (for Quake compatibility)
-       for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
-               if (sv.lightstyles[i][0])
-                       lightstyles = i+1;
-
-       isserver = prog == SVVM_prog;
-
-       Con_Printf("Saving game to %s...\n", name);
-       f = FS_OpenRealFile(name, "wb", false);
-       if (!f)
+       prvm_prog_t *prog = SVVM_prog;
+       int i, j;
+       qboolean valid_colors;
+       const char *newNameSource;
+       char newName[sizeof(host_client->name)];
+
+       if (Cmd_Argc (cmd) == 1)
        {
-               Con_Print("ERROR: couldn't open.\n");
+               if (cmd->source == src_command)
+               {
+                       Con_Printf("name: %s\n", cl_name.string);
+               }
                return;
        }
 
-       FS_Printf(f, "%i\n", SAVEGAME_VERSION);
-
-       memset(comment, 0, sizeof(comment));
-       if(isserver)
-               dpsnprintf(comment, sizeof(comment), "%-21.21s kills:%3i/%3i", PRVM_GetString(prog, PRVM_serveredictstring(prog->edicts, message)), (int)PRVM_serverglobalfloat(killed_monsters), (int)PRVM_serverglobalfloat(total_monsters));
-       else
-               dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", prog->name);
-       // convert space to _ to make stdio happy
-       // LadyHavoc: convert control characters to _ as well
-       for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
-               if (ISWHITESPACEORCONTROL(comment[i]))
-                       comment[i] = '_';
-       comment[SAVEGAME_COMMENT_LENGTH] = '\0';
-
-       FS_Printf(f, "%s\n", comment);
-       if(isserver)
-       {
-               for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
-                       FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
-               FS_Printf(f, "%d\n", current_skill);
-               FS_Printf(f, "%s\n", sv.name);
-               FS_Printf(f, "%f\n",sv.time);
-       }
+       if (Cmd_Argc (cmd) == 2)
+               newNameSource = Cmd_Argv(cmd, 1);
        else
-       {
-               for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
-                       FS_Printf(f, "(dummy)\n");
-               FS_Printf(f, "%d\n", 0);
-               FS_Printf(f, "%s\n", "(dummy)");
-               FS_Printf(f, "%f\n", host.realtime);
-       }
-
-       // write the light styles
-       for (i=0 ; i<lightstyles ; i++)
-       {
-               if (isserver && sv.lightstyles[i][0])
-                       FS_Printf(f, "%s\n", sv.lightstyles[i]);
-               else
-                       FS_Print(f,"m\n");
-       }
+               newNameSource = Cmd_Args(cmd);
 
-       PRVM_ED_WriteGlobals (prog, f);
-       for (i=0 ; i<prog->num_edicts ; i++)
-       {
-               FS_Printf(f,"// edict %d\n", i);
-               //Con_Printf("edict %d...\n", i);
-               PRVM_ED_Write (prog, f, PRVM_EDICT_NUM(i));
-       }
+       strlcpy(newName, newNameSource, sizeof(newName));
 
-#if 1
-       FS_Printf(f,"/*\n");
-       FS_Printf(f,"// DarkPlaces extended savegame\n");
-       // darkplaces extension - extra lightstyles, support for color lightstyles
-       for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
-               if (isserver && sv.lightstyles[i][0])
-                       FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
-
-       // darkplaces extension - model precaches
-       for (i=1 ; i<MAX_MODELS ; i++)
-               if (sv.model_precache[i][0])
-                       FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
-
-       // darkplaces extension - sound precaches
-       for (i=1 ; i<MAX_SOUNDS ; i++)
-               if (sv.sound_precache[i][0])
-                       FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
-
-       // darkplaces extension - save buffers
-       numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
-       for (i = 0; i < numbuffers; i++)
+       if (cmd->source == src_command)
        {
-               prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
-               if(stringbuffer && (stringbuffer->flags & STRINGBUFFER_SAVED))
+               Cvar_Set (&cvars_all, "_cl_name", newName);
+               if (strlen(newNameSource) >= sizeof(newName)) // overflowed
                {
-                       FS_Printf(f,"sv.buffer %i %i \"string\"\n", i, stringbuffer->flags & STRINGBUFFER_QCFLAGS);
-                       for(k = 0; k < stringbuffer->num_strings; k++)
-                       {
-                               if (!stringbuffer->strings[k])
-                                       continue;
-                               // Parse the string a bit to turn special characters
-                               // (like newline, specifically) into escape codes
-                               s = stringbuffer->strings[k];
-                               for (l = 0;l < (int)sizeof(line) - 2 && *s;)
-                               {       
-                                       if (*s == '\n')
-                                       {
-                                               line[l++] = '\\';
-                                               line[l++] = 'n';
-                                       }
-                                       else if (*s == '\r')
-                                       {
-                                               line[l++] = '\\';
-                                               line[l++] = 'r';
-                                       }
-                                       else if (*s == '\\')
-                                       {
-                                               line[l++] = '\\';
-                                               line[l++] = '\\';
-                                       }
-                                       else if (*s == '"')
-                                       {
-                                               line[l++] = '\\';
-                                               line[l++] = '"';
-                                       }
-                                       else
-                                               line[l++] = *s;
-                                       s++;
-                               }
-                               line[l] = '\0';
-                               FS_Printf(f,"sv.bufstr %i %i \"%s\"\n", i, k, line);
-                       }
+                       Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
+                       Con_Printf("name: %s\n", cl_name.string);
                }
+               return;
        }
-       FS_Printf(f,"*/\n");
-#endif
-
-       FS_Close (f);
-       Con_Print("done.\n");
-}
-
-/*
-===============
-SV_Savegame_f
-===============
-*/
-static void SV_Savegame_f(cmd_state_t *cmd)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       char    name[MAX_QPATH];
-       qboolean deadflag = false;
 
-       if (!sv.active)
+       if (host.realtime < host_client->nametime)
        {
-               Con_Print("Can't save - no server running.\n");
+               SV_ClientPrintf("You can't change name more than once every %.1f seconds!\n", max(0.0f, sv_namechangetimer.value));
                return;
        }
 
-       deadflag = cl.islocalgame && svs.clients[0].active && PRVM_serveredictfloat(svs.clients[0].edict, deadflag);
-
-       if (cl.islocalgame)
-       {
-               // singleplayer checks
-               if (cl.intermission)
-               {
-                       Con_Print("Can't save in intermission.\n");
-                       return;
-               }
-
-               if (deadflag)
-               {
-                       Con_Print("Can't savegame with a dead player\n");
-                       return;
-               }
-       }
-       else
-               Con_Warn("Warning: saving a multiplayer game may have strange results when restored (to properly resume, all players must join in the same player slots and then the game can be reloaded).\n");
-
-       if (Cmd_Argc(cmd) != 2)
-       {
-               Con_Print("save <savename> : save a game\n");
-               return;
-       }
-
-       if (strstr(Cmd_Argv(cmd, 1), ".."))
-       {
-               Con_Print("Relative pathnames are not allowed.\n");
-               return;
-       }
-
-       strlcpy (name, Cmd_Argv(cmd, 1), sizeof (name));
-       FS_DefaultExtension (name, ".sav", sizeof (name));
-
-       SV_Savegame_to(prog, name);
-}
-
-
-/*
-===============
-SV_Loadgame_f
-===============
-*/
-
-static void SV_Loadgame_f(cmd_state_t *cmd)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       char filename[MAX_QPATH];
-       char mapname[MAX_QPATH];
-       float time;
-       const char *start;
-       const char *end;
-       const char *t;
-       char *text;
-       prvm_edict_t *ent;
-       int i, k, numbuffers;
-       int entnum;
-       int version;
-       float spawn_parms[NUM_SPAWN_PARMS];
-       prvm_stringbuffer_t *stringbuffer;
-
-       if (Cmd_Argc(cmd) != 2)
-       {
-               Con_Print("load <savename> : load a game\n");
-               return;
-       }
-
-       strlcpy (filename, Cmd_Argv(cmd, 1), sizeof(filename));
-       FS_DefaultExtension (filename, ".sav", sizeof (filename));
-
-       Con_Printf("Loading game from %s...\n", filename);
-
-       // stop playing demos
-       if (cls.demoplayback)
-               CL_Disconnect ();
-
-#ifdef CONFIG_MENU
-       // remove menu
-       if (key_dest == key_menu || key_dest == key_menu_grabbed)
-               MR_ToggleMenu(0);
-#endif
-       key_dest = key_game;
-
-       cls.demonum = -1;               // stop demo loop in case this fails
-
-       t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
-       if (!text)
-       {
-               Con_Print("ERROR: couldn't open.\n");
-               return;
-       }
-
-       if(developer_entityparsing.integer)
-               Con_Printf("SV_Loadgame_f: loading version\n");
-
-       // version
-       COM_ParseToken_Simple(&t, false, false, true);
-       version = atoi(com_token);
-       if (version != SAVEGAME_VERSION)
-       {
-               Mem_Free(text);
-               Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
-               return;
-       }
-
-       if(developer_entityparsing.integer)
-               Con_Printf("SV_Loadgame_f: loading description\n");
-
-       // description
-       COM_ParseToken_Simple(&t, false, false, true);
-
-       for (i = 0;i < NUM_SPAWN_PARMS;i++)
-       {
-               COM_ParseToken_Simple(&t, false, false, true);
-               spawn_parms[i] = atof(com_token);
-       }
-       // skill
-       COM_ParseToken_Simple(&t, false, false, true);
-// this silliness is so we can load 1.06 save files, which have float skill values
-       current_skill = (int)(atof(com_token) + 0.5);
-       Cvar_SetValue (&cvars_all, "skill", (float)current_skill);
-
-       if(developer_entityparsing.integer)
-               Con_Printf("SV_Loadgame_f: loading mapname\n");
-
-       // mapname
-       COM_ParseToken_Simple(&t, false, false, true);
-       strlcpy (mapname, com_token, sizeof(mapname));
-
-       if(developer_entityparsing.integer)
-               Con_Printf("SV_Loadgame_f: loading time\n");
-
-       // time
-       COM_ParseToken_Simple(&t, false, false, true);
-       time = atof(com_token);
-
-       if(developer_entityparsing.integer)
-               Con_Printf("SV_Loadgame_f: spawning server\n");
-
-       SV_SpawnServer (mapname);
-       if (!sv.active)
-       {
-               Mem_Free(text);
-               Con_Print("Couldn't load map\n");
-               return;
-       }
-       sv.paused = true;               // pause until all clients connect
-       sv.loadgame = true;
-
-       if(developer_entityparsing.integer)
-               Con_Printf("SV_Loadgame_f: loading light styles\n");
-
-// load the light styles
-
-       // -1 is the globals
-       entnum = -1;
-
-       for (i = 0;i < MAX_LIGHTSTYLES;i++)
-       {
-               // light style
-               start = t;
-               COM_ParseToken_Simple(&t, false, false, true);
-               // if this is a 64 lightstyle savegame produced by Quake, stop now
-               // we have to check this because darkplaces may save more than 64
-               if (com_token[0] == '{')
-               {
-                       t = start;
-                       break;
-               }
-               strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
-       }
-
-       if(developer_entityparsing.integer)
-               Con_Printf("SV_Loadgame_f: skipping until globals\n");
-
-       // now skip everything before the first opening brace
-       // (this is for forward compatibility, so that older versions (at
-       // least ones with this fix) can load savegames with extra data before the
-       // first brace, as might be produced by a later engine version)
-       for (;;)
-       {
-               start = t;
-               if (!COM_ParseToken_Simple(&t, false, false, true))
-                       break;
-               if (com_token[0] == '{')
-               {
-                       t = start;
-                       break;
-               }
-       }
-
-       // unlink all entities
-       World_UnlinkAll(&sv.world);
-
-// load the edicts out of the savegame file
-       end = t;
-       for (;;)
-       {
-               start = t;
-               while (COM_ParseToken_Simple(&t, false, false, true))
-                       if (!strcmp(com_token, "}"))
-                               break;
-               if (!COM_ParseToken_Simple(&start, false, false, true))
-               {
-                       // end of file
-                       break;
-               }
-               if (strcmp(com_token,"{"))
-               {
-                       Mem_Free(text);
-                       Host_Error ("First token isn't a brace");
-               }
-
-               if (entnum == -1)
-               {
-                       if(developer_entityparsing.integer)
-                               Con_Printf("SV_Loadgame_f: loading globals\n");
-
-                       // parse the global vars
-                       PRVM_ED_ParseGlobals (prog, start);
-
-                       // restore the autocvar globals
-                       Cvar_UpdateAllAutoCvars(prog->console_cmd->cvars);
-               }
-               else
-               {
-                       // parse an edict
-                       if (entnum >= MAX_EDICTS)
-                       {
-                               Mem_Free(text);
-                               Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
-                       }
-                       while (entnum >= prog->max_edicts)
-                               PRVM_MEM_IncreaseEdicts(prog);
-                       ent = PRVM_EDICT_NUM(entnum);
-                       memset(ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
-                       ent->priv.server->free = false;
-
-                       if(developer_entityparsing.integer)
-                               Con_Printf("SV_Loadgame_f: loading edict %d\n", entnum);
-
-                       PRVM_ED_ParseEdict (prog, start, ent);
-
-                       // link it into the bsp tree
-                       if (!ent->priv.server->free && !VectorCompare(PRVM_serveredictvector(ent, absmin), PRVM_serveredictvector(ent, absmax)))
-                               SV_LinkEdict(ent);
-               }
-
-               end = t;
-               entnum++;
-       }
-
-       prog->num_edicts = entnum;
-       sv.time = time;
-
-       for (i = 0;i < NUM_SPAWN_PARMS;i++)
-               svs.clients[0].spawn_parms[i] = spawn_parms[i];
-
-       if(developer_entityparsing.integer)
-               Con_Printf("SV_Loadgame_f: skipping until extended data\n");
-
-       // read extended data if present
-       // the extended data is stored inside a /* */ comment block, which the
-       // parser intentionally skips, so we have to check for it manually here
-       if(end)
-       {
-               while (*end == '\r' || *end == '\n')
-                       end++;
-               if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
-               {
-                       if(developer_entityparsing.integer)
-                               Con_Printf("SV_Loadgame_f: loading extended data\n");
-
-                       Con_Printf("Loading extended DarkPlaces savegame\n");
-                       t = end + 2;
-                       memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
-                       memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
-                       memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
-                       BufStr_Flush(prog);
-
-                       while (COM_ParseToken_Simple(&t, false, false, true))
-                       {
-                               if (!strcmp(com_token, "sv.lightstyles"))
-                               {
-                                       COM_ParseToken_Simple(&t, false, false, true);
-                                       i = atoi(com_token);
-                                       COM_ParseToken_Simple(&t, false, false, true);
-                                       if (i >= 0 && i < MAX_LIGHTSTYLES)
-                                               strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
-                                       else
-                                               Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
-                               }
-                               else if (!strcmp(com_token, "sv.model_precache"))
-                               {
-                                       COM_ParseToken_Simple(&t, false, false, true);
-                                       i = atoi(com_token);
-                                       COM_ParseToken_Simple(&t, false, false, true);
-                                       if (i >= 0 && i < MAX_MODELS)
-                                       {
-                                               strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
-                                               sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.worldname : NULL);
-                                       }
-                                       else
-                                               Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
-                               }
-                               else if (!strcmp(com_token, "sv.sound_precache"))
-                               {
-                                       COM_ParseToken_Simple(&t, false, false, true);
-                                       i = atoi(com_token);
-                                       COM_ParseToken_Simple(&t, false, false, true);
-                                       if (i >= 0 && i < MAX_SOUNDS)
-                                               strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
-                                       else
-                                               Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
-                               }
-                               else if (!strcmp(com_token, "sv.buffer"))
-                               {
-                                       if (COM_ParseToken_Simple(&t, false, false, true))
-                                       {
-                                               i = atoi(com_token);
-                                               if (i >= 0)
-                                               {
-                                                       k = STRINGBUFFER_SAVED;
-                                                       if (COM_ParseToken_Simple(&t, false, false, true))
-                                                               k |= atoi(com_token);
-                                                       if (!BufStr_FindCreateReplace(prog, i, k, "string"))
-                                                               Con_Errorf("failed to create stringbuffer %i\n", i);
-                                               }
-                                               else
-                                                       Con_Printf("unsupported stringbuffer index %i \"%s\"\n", i, com_token);
-                                       }
-                                       else
-                                               Con_Printf("unexpected end of line when parsing sv.buffer (expected buffer index)\n");
-                               }
-                               else if (!strcmp(com_token, "sv.bufstr"))
-                               {
-                                       if (!COM_ParseToken_Simple(&t, false, false, true))
-                                               Con_Printf("unexpected end of line when parsing sv.bufstr\n");
-                                       else
-                                       {
-                                               i = atoi(com_token);
-                                               stringbuffer = BufStr_FindCreateReplace(prog, i, STRINGBUFFER_SAVED, "string");
-                                               if (stringbuffer)
-                                               {
-                                                       if (COM_ParseToken_Simple(&t, false, false, true))
-                                                       {
-                                                               k = atoi(com_token);
-                                                               if (COM_ParseToken_Simple(&t, false, false, true))
-                                                                       BufStr_Set(prog, stringbuffer, k, com_token);
-                                                               else
-                                                                       Con_Printf("unexpected end of line when parsing sv.bufstr (expected string)\n");
-                                                       }
-                                                       else
-                                                               Con_Printf("unexpected end of line when parsing sv.bufstr (expected strindex)\n");
-                                               }
-                                               else
-                                                       Con_Errorf("failed to create stringbuffer %i \"%s\"\n", i, com_token);
-                                       }
-                               }       
-                               // skip any trailing text or unrecognized commands
-                               while (COM_ParseToken_Simple(&t, true, false, true) && strcmp(com_token, "\n"))
-                                       ;
-                       }
-               }
-       }
-       Mem_Free(text);
-
-       // remove all temporary flagged string buffers (ones created with BufStr_FindCreateReplace)
-       numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
-       for (i = 0; i < numbuffers; i++)
-       {
-               if ( (stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i)) )
-                       if (stringbuffer->flags & STRINGBUFFER_TEMP)
-                               BufStr_Del(prog, stringbuffer);
-       }
-
-       if(developer_entityparsing.integer)
-               Con_Printf("SV_Loadgame_f: finished\n");
-
-       // make sure we're connected to loopback
-       if (sv.active && cls.state == ca_disconnected)
-               CL_EstablishConnection("local:1", -2);
-}
-
-//============================================================================
-
-/*
-======================
-CL_Name_f
-======================
-*/
-cvar_t cl_name = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
-static void CL_Name_f(cmd_state_t *cmd)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       int i, j;
-       qboolean valid_colors;
-       const char *newNameSource;
-       char newName[sizeof(host_client->name)];
-
-       if (Cmd_Argc (cmd) == 1)
-       {
-               if (cmd->source == src_command)
-               {
-                       Con_Printf("name: %s\n", cl_name.string);
-               }
-               return;
-       }
-
-       if (Cmd_Argc (cmd) == 2)
-               newNameSource = Cmd_Argv(cmd, 1);
-       else
-               newNameSource = Cmd_Args(cmd);
-
-       strlcpy(newName, newNameSource, sizeof(newName));
-
-       if (cmd->source == src_command)
-       {
-               Cvar_Set (&cvars_all, "_cl_name", newName);
-               if (strlen(newNameSource) >= sizeof(newName)) // overflowed
-               {
-                       Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
-                       Con_Printf("name: %s\n", cl_name.string);
-               }
-               return;
-       }
-
-       if (host.realtime < host_client->nametime)
-       {
-               SV_ClientPrintf("You can't change name more than once every %.1f seconds!\n", max(0.0f, sv_namechangetimer.value));
-               return;
-       }
-
-       host_client->nametime = host.realtime + max(0.0f, sv_namechangetimer.value);
+       host_client->nametime = host.realtime + max(0.0f, sv_namechangetimer.value);
 
        // point the string back at updateclient->name to keep it safe
        strlcpy (host_client->name, newName, sizeof (host_client->name));
@@ -1358,212 +398,6 @@ static void Host_Version_f(cmd_state_t *cmd)
        Con_Printf("Version: %s build %s\n", gamename, buildstring);
 }
 
-static void SV_Say(cmd_state_t *cmd, qboolean teamonly)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       client_t *save;
-       int j, quoted;
-       const char *p1;
-       char *p2;
-       // LadyHavoc: long say messages
-       char text[1024];
-       qboolean fromServer = false;
-
-       if (cmd->source == src_command)
-       {
-               if (cls.state == ca_dedicated)
-               {
-                       fromServer = true;
-                       teamonly = false;
-               }
-               else
-               {
-                       Cmd_ForwardToServer_f(cmd);
-                       return;
-               }
-       }
-
-       if (Cmd_Argc (cmd) < 2)
-               return;
-
-       if (!teamplay.integer)
-               teamonly = false;
-
-       p1 = Cmd_Args(cmd);
-       quoted = false;
-       if (*p1 == '\"')
-       {
-               quoted = true;
-               p1++;
-       }
-       // note this uses the chat prefix \001
-       if (!fromServer && !teamonly)
-               dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
-       else if (!fromServer && teamonly)
-               dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
-       else if(*(sv_adminnick.string))
-               dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
-       else
-               dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
-       p2 = text + strlen(text);
-       while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
-       {
-               if (p2[-1] == '\"' && quoted)
-                       quoted = false;
-               p2[-1] = 0;
-               p2--;
-       }
-       strlcat(text, "\n", sizeof(text));
-
-       // note: save is not a valid edict if fromServer is true
-       save = host_client;
-       for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
-               if (host_client->active && (!teamonly || PRVM_serveredictfloat(host_client->edict, team) == PRVM_serveredictfloat(save->edict, team)))
-                       SV_ClientPrint(text);
-       host_client = save;
-
-       if (cls.state == ca_dedicated)
-               Con_Print(&text[1]);
-}
-
-static void SV_Say_f(cmd_state_t *cmd)
-{
-       SV_Say(cmd, false);
-}
-
-static void SV_Say_Team_f(cmd_state_t *cmd)
-{
-       SV_Say(cmd, true);
-}
-
-static void SV_Tell_f(cmd_state_t *cmd)
-{
-       const char *playername_start = NULL;
-       size_t playername_length = 0;
-       int playernumber = 0;
-       client_t *save;
-       int j;
-       const char *p1, *p2;
-       char text[MAX_INPUTLINE]; // LadyHavoc: FIXME: temporary buffer overflow fix (was 64)
-       qboolean fromServer = false;
-
-       if (cmd->source == src_command)
-       {
-               if (cls.state == ca_dedicated)
-                       fromServer = true;
-               else
-               {
-                       Cmd_ForwardToServer_f(cmd);
-                       return;
-               }
-       }
-
-       if (Cmd_Argc (cmd) < 2)
-               return;
-
-       // note this uses the chat prefix \001
-       if (!fromServer)
-               dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
-       else if(*(sv_adminnick.string))
-               dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
-       else
-               dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
-
-       p1 = Cmd_Args(cmd);
-       p2 = p1 + strlen(p1);
-       // remove the target name
-       while (p1 < p2 && *p1 == ' ')
-               p1++;
-       if(*p1 == '#')
-       {
-               ++p1;
-               while (p1 < p2 && *p1 == ' ')
-                       p1++;
-               while (p1 < p2 && isdigit(*p1))
-               {
-                       playernumber = playernumber * 10 + (*p1 - '0');
-                       p1++;
-               }
-               --playernumber;
-       }
-       else if(*p1 == '"')
-       {
-               ++p1;
-               playername_start = p1;
-               while (p1 < p2 && *p1 != '"')
-                       p1++;
-               playername_length = p1 - playername_start;
-               if(p1 < p2)
-                       p1++;
-       }
-       else
-       {
-               playername_start = p1;
-               while (p1 < p2 && *p1 != ' ')
-                       p1++;
-               playername_length = p1 - playername_start;
-       }
-       while (p1 < p2 && *p1 == ' ')
-               p1++;
-       if(playername_start)
-       {
-               // set playernumber to the right client
-               char namebuf[128];
-               if(playername_length >= sizeof(namebuf))
-               {
-                       if (fromServer)
-                               Con_Print("Host_Tell: too long player name/ID\n");
-                       else
-                               SV_ClientPrint("Host_Tell: too long player name/ID\n");
-                       return;
-               }
-               memcpy(namebuf, playername_start, playername_length);
-               namebuf[playername_length] = 0;
-               for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
-               {
-                       if (!svs.clients[playernumber].active)
-                               continue;
-                       if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
-                               break;
-               }
-       }
-       if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
-       {
-               if (fromServer)
-                       Con_Print("Host_Tell: invalid player name/ID\n");
-               else
-                       SV_ClientPrint("Host_Tell: invalid player name/ID\n");
-               return;
-       }
-       // remove trailing newlines
-       while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
-               p2--;
-       // remove quotes if present
-       if (*p1 == '"')
-       {
-               p1++;
-               if (p2[-1] == '"')
-                       p2--;
-               else if (fromServer)
-                       Con_Print("Host_Tell: missing end quote\n");
-               else
-                       SV_ClientPrint("Host_Tell: missing end quote\n");
-       }
-       while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
-               p2--;
-       if(p1 == p2)
-               return; // empty say
-       for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
-               text[j++] = *p1++;
-       text[j++] = '\n';
-       text[j++] = 0;
-
-       save = host_client;
-       host_client = svs.clients + playernumber;
-       SV_ClientPrint(text);
-       host_client = save;
-}
-
 /*
 ==================
 CL_Color_f
@@ -1621,766 +455,154 @@ static void CL_Color(cmd_state_t *cmd, int changetop, int changebottom)
                        // send notification to all clients
                        MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
                        MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
-                       MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
-               }
-       }
-}
-
-static void CL_Color_f(cmd_state_t *cmd)
-{
-       int             top, bottom;
-
-       if (Cmd_Argc(cmd) == 1)
-       {
-               if (cmd->source == src_command)
-               {
-                       Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
-                       Con_Print("color <0-15> [0-15]\n");
-               }
-               return;
-       }
-
-       if (Cmd_Argc(cmd) == 2)
-               top = bottom = atoi(Cmd_Argv(cmd, 1));
-       else
-       {
-               top = atoi(Cmd_Argv(cmd, 1));
-               bottom = atoi(Cmd_Argv(cmd, 2));
-       }
-       CL_Color(cmd, top, bottom);
-}
-
-static void CL_TopColor_f(cmd_state_t *cmd)
-{
-       if (Cmd_Argc(cmd) == 1)
-       {
-               if (cmd->source == src_command)
-               {
-                       Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
-                       Con_Print("topcolor <0-15>\n");
-               }
-               return;
-       }
-
-       CL_Color(cmd, atoi(Cmd_Argv(cmd, 1)), -1);
-}
-
-static void CL_BottomColor_f(cmd_state_t *cmd)
-{
-       if (Cmd_Argc(cmd) == 1)
-       {
-               if (cmd->source == src_command)
-               {
-                       Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
-                       Con_Print("bottomcolor <0-15>\n");
-               }
-               return;
-       }
-
-       CL_Color(cmd, -1, atoi(Cmd_Argv(cmd, 1)));
-}
-
-cvar_t cl_rate = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
-cvar_t cl_rate_burstsize = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate_burstsize", "1024", "internal storage cvar for current rate control burst size (changed by rate_burstsize command)"};
-static void CL_Rate_f(cmd_state_t *cmd)
-{
-       int rate;
-
-       if (Cmd_Argc(cmd) != 2)
-       {
-               if (cmd->source == src_command)
-               {
-                       Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
-                       Con_Print("rate <bytespersecond>\n");
-               }
-               return;
-       }
-
-       rate = atoi(Cmd_Argv(cmd, 1));
-
-       if (cmd->source == src_command)
-       {
-               Cvar_SetValue (&cvars_all, "_cl_rate", max(NET_MINRATE, rate));
-               return;
-       }
-
-       host_client->rate = rate;
-}
-
-static void CL_Rate_BurstSize_f(cmd_state_t *cmd)
-{
-       int rate_burstsize;
-
-       if (Cmd_Argc(cmd) != 2)
-       {
-               Con_Printf("\"rate_burstsize\" is \"%i\"\n", cl_rate_burstsize.integer);
-               Con_Print("rate_burstsize <bytes>\n");
-               return;
-       }
-
-       rate_burstsize = atoi(Cmd_Argv(cmd, 1));
-
-       if (cmd->source == src_command)
-       {
-               Cvar_SetValue (&cvars_all, "_cl_rate_burstsize", rate_burstsize);
-               return;
-       }
-
-       host_client->rate_burstsize = rate_burstsize;
-}
-
-/*
-==================
-SV_Kill_f
-==================
-*/
-static void SV_Kill_f(cmd_state_t *cmd)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
-       {
-               SV_ClientPrint("Can't suicide -- already dead!\n");
-               return;
-       }
-
-       PRVM_serverglobalfloat(time) = sv.time;
-       PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
-       prog->ExecuteProgram(prog, PRVM_serverfunction(ClientKill), "QC function ClientKill is missing");
-}
-
-
-/*
-==================
-SV_Pause_f
-==================
-*/
-static void SV_Pause_f(cmd_state_t *cmd)
-{
-       void (*print) (const char *fmt, ...);
-       if (cmd->source == src_command)
-       {
-               // if running a client, try to send over network so the pause is handled by the server
-               if (cls.state == ca_connected)
-               {
-                       Cmd_ForwardToServer_f(cmd);
-                       return;
-               }
-               print = Con_Printf;
-       }
-       else
-               print = SV_ClientPrintf;
-
-       if (!pausable.integer)
-       {
-               if (cmd->source == src_client)
-               {
-                       if(cls.state == ca_dedicated || host_client != &svs.clients[0]) // non-admin
-                       {
-                               print("Pause not allowed.\n");
-                               return;
-                       }
-               }
-       }
-       
-       sv.paused ^= 1;
-       if (cmd->source != src_command)
-               SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
-       else if(*(sv_adminnick.string))
-               SV_BroadcastPrintf("%s %spaused the game\n", sv_adminnick.string, sv.paused ? "" : "un");
-       else
-               SV_BroadcastPrintf("%s %spaused the game\n", hostname.string, sv.paused ? "" : "un");
-       // send notification to all clients
-       MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
-       MSG_WriteByte(&sv.reliable_datagram, sv.paused);
-}
-
-/*
-======================
-CL_PModel_f
-LadyHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
-LadyHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
-======================
-*/
-cvar_t cl_pmodel = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_pmodel", "0", "internal storage cvar for current player model number in nehahra (changed by pmodel command)"};
-static void CL_PModel_f(cmd_state_t *cmd)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       int i;
-
-       if (Cmd_Argc (cmd) == 1)
-       {
-               if (cmd->source == src_command)
-               {
-                       Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
-               }
-               return;
-       }
-       i = atoi(Cmd_Argv(cmd, 1));
-
-       if (cmd->source == src_command)
-       {
-               if (cl_pmodel.integer == i)
-                       return;
-               Cvar_SetValue (&cvars_all, "_cl_pmodel", i);
-               if (cls.state == ca_connected)
-                       Cmd_ForwardToServer_f(cmd);
-               return;
-       }
-
-       PRVM_serveredictfloat(host_client->edict, pmodel) = i;
-}
-
-//===========================================================================
-
-
-/*
-==================
-SV_PreSpawn_f
-==================
-*/
-static void SV_PreSpawn_f(cmd_state_t *cmd)
-{
-       if (host_client->prespawned)
-       {
-               Con_Print("prespawn not valid -- already prespawned\n");
-               return;
-       }
-       host_client->prespawned = true;
-
-       if (host_client->netconnection)
-       {
-               SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
-               MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
-               MSG_WriteByte (&host_client->netconnection->message, 2);
-               host_client->sendsignon = 0;            // enable unlimited sends again
-       }
-
-       // reset the name change timer because the client will send name soon
-       host_client->nametime = 0;
-}
-
-/*
-==================
-SV_Spawn_f
-==================
-*/
-static void SV_Spawn_f(cmd_state_t *cmd)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       int i;
-       client_t *client;
-       int stats[MAX_CL_STATS];
-
-       if (!host_client->prespawned)
-       {
-               Con_Print("Spawn not valid -- not yet prespawned\n");
-               return;
-       }
-       if (host_client->spawned)
-       {
-               Con_Print("Spawn not valid -- already spawned\n");
-               return;
-       }
-       host_client->spawned = true;
-
-       // reset name change timer again because they might want to change name
-       // again in the first 5 seconds after connecting
-       host_client->nametime = 0;
-
-       // LadyHavoc: moved this above the QC calls at FrikaC's request
-       // LadyHavoc: commented this out
-       //if (host_client->netconnection)
-       //      SZ_Clear (&host_client->netconnection->message);
-
-       // run the entrance script
-       if (sv.loadgame)
-       {
-               // loaded games are fully initialized already
-               if (PRVM_serverfunction(RestoreGame))
-               {
-                       Con_DPrint("Calling RestoreGame\n");
-                       PRVM_serverglobalfloat(time) = sv.time;
-                       PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
-                       prog->ExecuteProgram(prog, PRVM_serverfunction(RestoreGame), "QC function RestoreGame is missing");
-               }
-       }
-       else
-       {
-               //Con_Printf("SV_Spawn_f: host_client->edict->netname = %s, host_client->edict->netname = %s, host_client->name = %s\n", PRVM_GetString(PRVM_serveredictstring(host_client->edict, netname)), PRVM_GetString(PRVM_serveredictstring(host_client->edict, netname)), host_client->name);
-
-               // copy spawn parms out of the client_t
-               for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
-                       (&PRVM_serverglobalfloat(parm1))[i] = host_client->spawn_parms[i];
-
-               // call the spawn function
-               host_client->clientconnectcalled = true;
-               PRVM_serverglobalfloat(time) = sv.time;
-               PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
-               prog->ExecuteProgram(prog, PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing");
-
-               if (cls.state == ca_dedicated)
-                       Con_Printf("%s connected\n", host_client->name);
-
-               PRVM_serverglobalfloat(time) = sv.time;
-               prog->ExecuteProgram(prog, PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
-       }
-
-       if (!host_client->netconnection)
-               return;
-
-       // send time of update
-       MSG_WriteByte (&host_client->netconnection->message, svc_time);
-       MSG_WriteFloat (&host_client->netconnection->message, sv.time);
-
-       // send all current names, colors, and frag counts
-       for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
-       {
-               if (!client->active)
-                       continue;
-               MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
-               MSG_WriteByte (&host_client->netconnection->message, i);
-               MSG_WriteString (&host_client->netconnection->message, client->name);
-               MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
-               MSG_WriteByte (&host_client->netconnection->message, i);
-               MSG_WriteShort (&host_client->netconnection->message, client->frags);
-               MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
-               MSG_WriteByte (&host_client->netconnection->message, i);
-               MSG_WriteByte (&host_client->netconnection->message, client->colors);
-       }
-
-       // send all current light styles
-       for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
-       {
-               if (sv.lightstyles[i][0])
-               {
-                       MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
-                       MSG_WriteByte (&host_client->netconnection->message, (char)i);
-                       MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
-               }
-       }
-
-       // send some stats
-       MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
-       MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
-       MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_secrets));
-
-       MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
-       MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
-       MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_monsters));
-
-       MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
-       MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
-       MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(found_secrets));
-
-       MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
-       MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
-       MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(killed_monsters));
-
-       // send a fixangle
-       // Never send a roll angle, because savegames can catch the server
-       // in a state where it is expecting the client to correct the angle
-       // and it won't happen if the game was just loaded, so you wind up
-       // with a permanent head tilt
-       if (sv.loadgame)
-       {
-               MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
-               MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[0], sv.protocol);
-               MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[1], sv.protocol);
-               MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
-       }
-       else
-       {
-               MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
-               MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[0], sv.protocol);
-               MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[1], sv.protocol);
-               MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
-       }
-
-       SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
-
-       MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
-       MSG_WriteByte (&host_client->netconnection->message, 3);
-}
-
-/*
-==================
-SV_Begin_f
-==================
-*/
-static void SV_Begin_f(cmd_state_t *cmd)
-{
-       if (!host_client->spawned)
-       {
-               Con_Print("Begin not valid -- not yet spawned\n");
-               return;
-       }
-       if (host_client->begun)
-       {
-               Con_Print("Begin not valid -- already begun\n");
-               return;
-       }
-       host_client->begun = true;
-
-       // LadyHavoc: note: this code also exists in SV_DropClient
-       if (sv.loadgame)
-       {
-               int i;
-               for (i = 0;i < svs.maxclients;i++)
-                       if (svs.clients[i].active && !svs.clients[i].spawned)
-                               break;
-               if (i == svs.maxclients)
-               {
-                       Con_Printf("Loaded game, everyone rejoined - unpausing\n");
-                       sv.paused = sv.loadgame = false; // we're basically done with loading now
-               }
-       }
-}
-
-//===========================================================================
-
-
-/*
-==================
-SV_Kick_f
-
-Kicks a user off of the server
-==================
-*/
-static void SV_Kick_f(cmd_state_t *cmd)
-{
-       const char *who;
-       const char *message = NULL;
-       client_t *save;
-       int i;
-       qboolean byNumber = false;
-
-       if (!sv.active)
-               return;
-
-       save = host_client;
-
-       if (Cmd_Argc(cmd) > 2 && strcmp(Cmd_Argv(cmd, 1), "#") == 0)
-       {
-               i = (int)(atof(Cmd_Argv(cmd, 2)) - 1);
-               if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
-                       return;
-               byNumber = true;
-       }
-       else
-       {
-               for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
-               {
-                       if (!host_client->active)
-                               continue;
-                       if (strcasecmp(host_client->name, Cmd_Argv(cmd, 1)) == 0)
-                               break;
+                       MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
                }
        }
+}
+
+static void CL_Color_f(cmd_state_t *cmd)
+{
+       int             top, bottom;
 
-       if (i < svs.maxclients)
+       if (Cmd_Argc(cmd) == 1)
        {
                if (cmd->source == src_command)
                {
-                       if (cls.state == ca_dedicated)
-                               who = "Console";
-                       else
-                               who = cl_name.string;
-               }
-               else
-                       who = save->name;
-
-               // can't kick yourself!
-               if (host_client == save)
-                       return;
-
-               if (Cmd_Argc(cmd) > 2)
-               {
-                       message = Cmd_Args(cmd);
-                       COM_ParseToken_Simple(&message, false, false, true);
-                       if (byNumber)
-                       {
-                               message++;                                                      // skip the #
-                               while (*message == ' ')                         // skip white space
-                                       message++;
-                               message += strlen(Cmd_Argv(cmd, 2));    // skip the number
-                       }
-                       while (*message && *message == ' ')
-                               message++;
+                       Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
+                       Con_Print("color <0-15> [0-15]\n");
                }
-               if (message)
-                       SV_ClientPrintf("Kicked by %s: %s\n", who, message);
-               else
-                       SV_ClientPrintf("Kicked by %s\n", who);
-               SV_DropClient (false); // kicked
+               return;
        }
 
-       host_client = save;
+       if (Cmd_Argc(cmd) == 2)
+               top = bottom = atoi(Cmd_Argv(cmd, 1));
+       else
+       {
+               top = atoi(Cmd_Argv(cmd, 1));
+               bottom = atoi(Cmd_Argv(cmd, 2));
+       }
+       CL_Color(cmd, top, bottom);
 }
 
-/*
-===============================================================================
-
-DEBUGGING TOOLS
-
-===============================================================================
-*/
-
-/*
-==================
-SV_Give_f
-==================
-*/
-static void SV_Give_f(cmd_state_t *cmd)
+static void CL_TopColor_f(cmd_state_t *cmd)
 {
-       prvm_prog_t *prog = SVVM_prog;
-       const char *t;
-       int v;
-
-       t = Cmd_Argv(cmd, 1);
-       v = atoi (Cmd_Argv(cmd, 2));
-
-       switch (t[0])
+       if (Cmd_Argc(cmd) == 1)
        {
-       case '0':
-       case '1':
-       case '2':
-       case '3':
-       case '4':
-       case '5':
-       case '6':
-       case '7':
-       case '8':
-       case '9':
-               // MED 01/04/97 added hipnotic give stuff
-               if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
-               {
-                       if (t[0] == '6')
-                       {
-                               if (t[1] == 'a')
-                                       PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_PROXIMITY_GUN;
-                               else
-                                       PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | IT_GRENADE_LAUNCHER;
-                       }
-                       else if (t[0] == '9')
-                               PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_LASER_CANNON;
-                       else if (t[0] == '0')
-                               PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_MJOLNIR;
-                       else if (t[0] >= '2')
-                               PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
-               }
-               else
-               {
-                       if (t[0] >= '2')
-                               PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
-               }
-               break;
-
-       case 's':
-               if (gamemode == GAME_ROGUE)
-                       PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
-
-               PRVM_serveredictfloat(host_client->edict, ammo_shells) = v;
-               break;
-       case 'n':
-               if (gamemode == GAME_ROGUE)
-               {
-                       PRVM_serveredictfloat(host_client->edict, ammo_nails1) = v;
-                       if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
-                               PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
-               }
-               else
-               {
-                       PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
-               }
-               break;
-       case 'l':
-               if (gamemode == GAME_ROGUE)
-               {
-                       PRVM_serveredictfloat(host_client->edict, ammo_lava_nails) = v;
-                       if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
-                               PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
-               }
-               break;
-       case 'r':
-               if (gamemode == GAME_ROGUE)
-               {
-                       PRVM_serveredictfloat(host_client->edict, ammo_rockets1) = v;
-                       if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
-                               PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
-               }
-               else
-               {
-                       PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
-               }
-               break;
-       case 'm':
-               if (gamemode == GAME_ROGUE)
-               {
-                       PRVM_serveredictfloat(host_client->edict, ammo_multi_rockets) = v;
-                       if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
-                               PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
-               }
-               break;
-       case 'h':
-               PRVM_serveredictfloat(host_client->edict, health) = v;
-               break;
-       case 'c':
-               if (gamemode == GAME_ROGUE)
-               {
-                       PRVM_serveredictfloat(host_client->edict, ammo_cells1) = v;
-                       if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
-                               PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
-               }
-               else
-               {
-                       PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
-               }
-               break;
-       case 'p':
-               if (gamemode == GAME_ROGUE)
+               if (cmd->source == src_command)
                {
-                       PRVM_serveredictfloat(host_client->edict, ammo_plasma) = v;
-                       if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
-                               PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
+                       Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
+                       Con_Print("topcolor <0-15>\n");
                }
-               break;
+               return;
        }
+
+       CL_Color(cmd, atoi(Cmd_Argv(cmd, 1)), -1);
 }
 
-static prvm_edict_t    *FindViewthing(prvm_prog_t *prog)
+static void CL_BottomColor_f(cmd_state_t *cmd)
 {
-       int             i;
-       prvm_edict_t    *e;
-
-       for (i=0 ; i<prog->num_edicts ; i++)
+       if (Cmd_Argc(cmd) == 1)
        {
-               e = PRVM_EDICT_NUM(i);
-               if (!strcmp (PRVM_GetString(prog, PRVM_serveredictstring(e, classname)), "viewthing"))
-                       return e;
+               if (cmd->source == src_command)
+               {
+                       Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
+                       Con_Print("bottomcolor <0-15>\n");
+               }
+               return;
        }
-       Con_Print("No viewthing on map\n");
-       return NULL;
+
+       CL_Color(cmd, -1, atoi(Cmd_Argv(cmd, 1)));
 }
 
-/*
-==================
-SV_Viewmodel_f
-==================
-*/
-static void SV_Viewmodel_f(cmd_state_t *cmd)
+cvar_t cl_rate = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
+cvar_t cl_rate_burstsize = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate_burstsize", "1024", "internal storage cvar for current rate control burst size (changed by rate_burstsize command)"};
+static void CL_Rate_f(cmd_state_t *cmd)
 {
-       prvm_prog_t *prog = SVVM_prog;
-       prvm_edict_t    *e;
-       dp_model_t      *m;
-
-       if (!sv.active)
-               return;
+       int rate;
 
-       e = FindViewthing(prog);
-       if (e)
+       if (Cmd_Argc(cmd) != 2)
        {
-               m = Mod_ForName (Cmd_Argv(cmd, 1), false, true, NULL);
-               if (m && m->loaded && m->Draw)
+               if (cmd->source == src_command)
                {
-                       PRVM_serveredictfloat(e, frame) = 0;
-                       cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
+                       Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
+                       Con_Print("rate <bytespersecond>\n");
                }
-               else
-                       Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(cmd, 1));
+               return;
        }
-}
-
-/*
-==================
-SV_Viewframe_f
-==================
-*/
-static void SV_Viewframe_f(cmd_state_t *cmd)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       prvm_edict_t    *e;
-       int             f;
-       dp_model_t      *m;
 
-       if (!sv.active)
-               return;
+       rate = atoi(Cmd_Argv(cmd, 1));
 
-       e = FindViewthing(prog);
-       if (e)
+       if (cmd->source == src_command)
        {
-               m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
-
-               f = atoi(Cmd_Argv(cmd, 1));
-               if (f >= m->numframes)
-                       f = m->numframes-1;
-
-               PRVM_serveredictfloat(e, frame) = f;
+               Cvar_SetValue (&cvars_all, "_cl_rate", max(NET_MINRATE, rate));
+               return;
        }
-}
 
-
-static void PrintFrameName (dp_model_t *m, int frame)
-{
-       if (m->animscenes)
-               Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
-       else
-               Con_Printf("frame %i\n", frame);
+       host_client->rate = rate;
 }
 
-/*
-==================
-SV_Viewnext_f
-==================
-*/
-static void SV_Viewnext_f(cmd_state_t *cmd)
+static void CL_Rate_BurstSize_f(cmd_state_t *cmd)
 {
-       prvm_prog_t *prog = SVVM_prog;
-       prvm_edict_t    *e;
-       dp_model_t      *m;
-
-       if (!sv.active)
-               return;
+       int rate_burstsize;
 
-       e = FindViewthing(prog);
-       if (e)
+       if (Cmd_Argc(cmd) != 2)
        {
-               m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
+               Con_Printf("\"rate_burstsize\" is \"%i\"\n", cl_rate_burstsize.integer);
+               Con_Print("rate_burstsize <bytes>\n");
+               return;
+       }
 
-               PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) + 1;
-               if (PRVM_serveredictfloat(e, frame) >= m->numframes)
-                       PRVM_serveredictfloat(e, frame) = m->numframes - 1;
+       rate_burstsize = atoi(Cmd_Argv(cmd, 1));
 
-               PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
+       if (cmd->source == src_command)
+       {
+               Cvar_SetValue (&cvars_all, "_cl_rate_burstsize", rate_burstsize);
+               return;
        }
+
+       host_client->rate_burstsize = rate_burstsize;
 }
 
 /*
-==================
-SV_Viewprev_f
-==================
+======================
+CL_PModel_f
+LadyHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
+LadyHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
+======================
 */
-static void SV_Viewprev_f(cmd_state_t *cmd)
+cvar_t cl_pmodel = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_pmodel", "0", "internal storage cvar for current player model number in nehahra (changed by pmodel command)"};
+static void CL_PModel_f(cmd_state_t *cmd)
 {
        prvm_prog_t *prog = SVVM_prog;
-       prvm_edict_t    *e;
-       dp_model_t      *m;
+       int i;
 
-       if (!sv.active)
+       if (Cmd_Argc (cmd) == 1)
+       {
+               if (cmd->source == src_command)
+               {
+                       Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
+               }
                return;
+       }
+       i = atoi(Cmd_Argv(cmd, 1));
 
-       e = FindViewthing(prog);
-       if (e)
+       if (cmd->source == src_command)
        {
-               m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
-
-               PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) - 1;
-               if (PRVM_serveredictfloat(e, frame) < 0)
-                       PRVM_serveredictfloat(e, frame) = 0;
-
-               PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
+               if (cl_pmodel.integer == i)
+                       return;
+               Cvar_SetValue (&cvars_all, "_cl_pmodel", i);
+               if (cls.state == ca_connected)
+                       Cmd_ForwardToServer_f(cmd);
+               return;
        }
+
+       PRVM_serveredictfloat(host_client->edict, pmodel) = i;
 }
 
+//===========================================================================
+
+//===========================================================================
+
 /*
 ===============================================================================
 
@@ -2498,33 +720,6 @@ static void CL_SendCvar_f(cmd_state_t *cmd)
        host_client = old;
 }
 
-static void SV_MaxPlayers_f(cmd_state_t *cmd)
-{
-       int n;
-
-       if (Cmd_Argc(cmd) != 2)
-       {
-               Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
-               return;
-       }
-
-       if (sv.active)
-       {
-               Con_Print("maxplayers can not be changed while a server is running.\n");
-               Con_Print("It will be changed on next server startup (\"map\" command).\n");
-       }
-
-       n = atoi(Cmd_Argv(cmd, 1));
-       n = bound(1, n, MAX_SCOREBOARD);
-       Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
-
-       svs.maxclients_next = n;
-       if (n == 1)
-               Cvar_Set (&cvars_all, "deathmatch", "0");
-       else
-               Cvar_Set (&cvars_all, "deathmatch", "1");
-}
-
 /*
 =====================
 CL_PQRcon_f
@@ -2675,68 +870,6 @@ static void CL_Rcon_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
        }
 }
 
-/*
-====================
-SV_User_f
-
-user <name or userid>
-
-Dump userdata / masterdata for a user
-====================
-*/
-static void SV_User_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
-{
-       int             uid;
-       int             i;
-
-       if (Cmd_Argc(cmd) != 2)
-       {
-               Con_Printf ("Usage: user <username / userid>\n");
-               return;
-       }
-
-       uid = atoi(Cmd_Argv(cmd, 1));
-
-       for (i = 0;i < cl.maxclients;i++)
-       {
-               if (!cl.scores[i].name[0])
-                       continue;
-               if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(cmd, 1)))
-               {
-                       InfoString_Print(cl.scores[i].qw_userinfo);
-                       return;
-               }
-       }
-       Con_Printf ("User not in server.\n");
-}
-
-/*
-====================
-SV_Users_f
-
-Dump userids for all current players
-====================
-*/
-static void SV_Users_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
-{
-       int             i;
-       int             c;
-
-       c = 0;
-       Con_Printf ("userid frags name\n");
-       Con_Printf ("------ ----- ----\n");
-       for (i = 0;i < cl.maxclients;i++)
-       {
-               if (cl.scores[i].name[0])
-               {
-                       Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
-                       c++;
-               }
-       }
-
-       Con_Printf ("%i total users\n", c);
-}
-
 /*
 ==================
 CL_FullServerinfo_f
@@ -2911,65 +1044,6 @@ static void CL_Packet_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
                NetConn_Write(mysocket, send, out - send, &address);
 }
 
-/*
-====================
-SV_Pings_f
-
-Send back ping and packet loss update for all current players to this player
-====================
-*/
-static void SV_Pings_f(cmd_state_t *cmd)
-{
-       int             i, j, ping, packetloss, movementloss;
-       char temp[128];
-
-       if (!host_client->netconnection)
-               return;
-
-       if (sv.protocol != PROTOCOL_QUAKEWORLD)
-       {
-               MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
-               MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
-       }
-       for (i = 0;i < svs.maxclients;i++)
-       {
-               packetloss = 0;
-               movementloss = 0;
-               if (svs.clients[i].netconnection)
-               {
-                       for (j = 0;j < NETGRAPH_PACKETS;j++)
-                               if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
-                                       packetloss++;
-                       for (j = 0;j < NETGRAPH_PACKETS;j++)
-                               if (svs.clients[i].movement_count[j] < 0)
-                                       movementloss++;
-               }
-               packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
-               movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
-               ping = (int)floor(svs.clients[i].ping*1000+0.5);
-               ping = bound(0, ping, 9999);
-               if (sv.protocol == PROTOCOL_QUAKEWORLD)
-               {
-                       // send qw_svc_updateping and qw_svc_updatepl messages
-                       MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
-                       MSG_WriteShort(&host_client->netconnection->message, ping);
-                       MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
-                       MSG_WriteByte(&host_client->netconnection->message, packetloss);
-               }
-               else
-               {
-                       // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
-                       if(movementloss)
-                               dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
-                       else
-                               dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
-                       MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
-               }
-       }
-       if (sv.protocol != PROTOCOL_QUAKEWORLD)
-               MSG_WriteString(&host_client->netconnection->message, "\n");
-}
-
 static void CL_PingPLReport_f(cmd_state_t *cmd)
 {
        char *errbyte;
@@ -3014,48 +1088,11 @@ void Host_InitCommands (void)
        Cvar_RegisterVariable(&team);
        Cvar_RegisterVariable(&skin);
        Cvar_RegisterVariable(&noaim);
-       Cvar_RegisterVariable(&sv_cheats);
-       Cvar_RegisterCallback(&sv_cheats, SV_DisableCheats_c);
-       Cvar_RegisterVariable(&sv_adminnick);
-       Cvar_RegisterVariable(&sv_status_privacy);
-       Cvar_RegisterVariable(&sv_status_show_qcstatus);
-       Cvar_RegisterVariable(&sv_namechangetimer);
 
        // client commands - this includes server commands because the client can host a server, so they must exist
        Cmd_AddCommand(CMD_SHARED, "quit", Host_Quit_f, "quit the game");
-       Cmd_AddCommand(CMD_SERVER | CMD_SERVER_FROM_CLIENT, "status", SV_Status_f, "print server status information");
-       Cmd_AddCommand(CMD_SHARED | CMD_INITWAIT, "map", SV_Map_f, "kick everyone off the server and start a new level");
-       Cmd_AddCommand(CMD_SHARED, "restart", SV_Restart_f, "restart current level");
-       Cmd_AddCommand(CMD_SHARED, "changelevel", SV_Changelevel_f, "change to another level, bringing along all connected clients");
        Cmd_AddCommand(CMD_SHARED, "version", Host_Version_f, "print engine version");
-       Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "say", SV_Say_f, "send a chat message to everyone on the server");
-       Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "say_team", SV_Say_Team_f, "send a chat message to your team on the server");
-       Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "tell", SV_Tell_f, "send a chat message to only one person on the server");
-       Cmd_AddCommand(CMD_SERVER | CMD_SERVER_FROM_CLIENT, "pause", SV_Pause_f, "pause the game (if the server allows pausing)");
-       Cmd_AddCommand(CMD_SHARED, "kick", SV_Kick_f, "kick a player off the server by number or name");
-       Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "ping", SV_Ping_f, "print ping times of all players on the server");
-       Cmd_AddCommand(CMD_SHARED | CMD_INITWAIT, "load", SV_Loadgame_f, "load a saved game file");
-       Cmd_AddCommand(CMD_SHARED, "save", SV_Savegame_f, "save the game to a file");
-       Cmd_AddCommand(CMD_SHARED, "viewmodel", SV_Viewmodel_f, "change model of viewthing entity in current level");
-       Cmd_AddCommand(CMD_SHARED, "viewframe", SV_Viewframe_f, "change animation frame of viewthing entity in current level");
-       Cmd_AddCommand(CMD_SHARED, "viewnext", SV_Viewnext_f, "change to next animation frame of viewthing entity in current level");
-       Cmd_AddCommand(CMD_SHARED, "viewprev", SV_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
-       Cmd_AddCommand(CMD_SHARED, "maxplayers", SV_MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
-       Cmd_AddCommand(CMD_SHARED, "user", SV_User_f, "prints additional information about a player number or name on the scoreboard");
-       Cmd_AddCommand(CMD_SHARED, "users", SV_Users_f, "prints additional information about all players on the scoreboard");
-
-       // commands that do not have automatic forwarding from cmd_client, these are internal details of the network protocol and not of interest to users (if they know what they are doing they can still use a generic "cmd prespawn" or similar)
-       Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "prespawn", SV_PreSpawn_f, "internal use - signon 1 (client acknowledges that server information has been received)");
-       Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "spawn", SV_Spawn_f, "internal use - signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
-       Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "begin", SV_Begin_f, "internal use - signon 3 (client asks server to start sending entities, and will go to signon 4 (playing) when the first entity update is received)");
-       Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "pings", SV_Pings_f, "internal use - command sent by clients to request updated ping and packetloss of players on scoreboard (originally from QW, but also used on NQ servers)");
-
-       Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "god", SV_God_f, "god mode (invulnerability)");
-       Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "notarget", SV_Notarget_f, "notarget mode (monsters do not see you)");
-       Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "fly", SV_Fly_f, "fly mode (flight)");
-       Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "noclip", SV_Noclip_f, "noclip mode (flight without collisions, move through walls)");
-       Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "give", SV_Give_f, "alter inventory");
-       Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "kill", SV_Kill_f, "die instantly");
+
        Cmd_AddCommand(CMD_CLIENT | CMD_SERVER_FROM_CLIENT, "name", CL_Name_f, "change your player name");
        Cmd_AddCommand(CMD_CLIENT | CMD_SERVER_FROM_CLIENT, "color", CL_Color_f, "change your player shirt and pants colors");
        Cmd_AddCommand(CMD_CLIENT | CMD_SERVER_FROM_CLIENT, "rate", CL_Rate_f, "change your network connection speed");
index 4d9a11c54393832d380a5b15b0aed303d78373d7..ce15fefaeaf804a8b0a325e8de4b43f56c876f52 100644 (file)
@@ -142,10 +142,12 @@ OBJ_COMMON= \
        r_sky.o \
        r_sprites.o \
        sbar.o \
+       sv_ccmds.o \
        sv_demo.o \
        sv_main.o \
        sv_move.o \
        sv_phys.o \
+       sv_save.o \
        sv_send.o \
        sv_user.o \
        svbsp.o \
index e3e524f7fa91ba6502caeb5911dde0e08646525d..875d52c600f1df01e8c4e93b308e506c59cc0aab 100644 (file)
@@ -715,7 +715,6 @@ PRVM_ED_Write
 For savegames
 =============
 */
-extern cvar_t developer_entityparsing;
 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
 {
        ddef_t  *d;
index 50df5e0bdd71871feec9568a2dc98840334e691b..ff021f17091b85fefa58e9eb167d4fe2d664e622 100644 (file)
@@ -406,6 +406,7 @@ extern char engineversion[128];
 extern qboolean noclip_anglehack;
 
 extern cvar_t developer;
+extern cvar_t developer_entityparsing;
 extern cvar_t developer_extra;
 extern cvar_t developer_insane;
 extern cvar_t developer_loadfile;
index 34aa79dfd2f349216a9ce7ebbe94cc1080258501..d83c908bf92725b67448d25ddc7676687ebfafac 100644 (file)
--- a/server.h
+++ b/server.h
@@ -611,7 +611,17 @@ void SV_StopThread(void);
 
 void VM_CustomStats_Clear(void);
 void VM_SV_UpdateCustomStats(client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats);
+
+void SV_InitOperatorCommands(void);
+
 void SV_Savegame_to(prvm_prog_t *prog, const char *name);
+void SV_Savegame_f(cmd_state_t *cmd);
+void SV_Loadgame_f(cmd_state_t *cmd);
+
+void SV_PreSpawn_f(cmd_state_t *cmd);
+void SV_Spawn_f(cmd_state_t *cmd);
+void SV_Begin_f(cmd_state_t *cmd);
+
 void SV_SendServerinfo(client_t *client);
 
 #endif
diff --git a/sv_ccmds.c b/sv_ccmds.c
new file mode 100644 (file)
index 0000000..84340c9
--- /dev/null
@@ -0,0 +1,1233 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#include <quakedef.h>
+#include <server.h>
+
+int current_skill;
+cvar_t sv_cheats = {CVAR_SERVER | CVAR_NOTIFY, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
+cvar_t sv_adminnick = {CVAR_SERVER | CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
+cvar_t sv_status_privacy = {CVAR_SERVER | CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"};
+cvar_t sv_status_show_qcstatus = {CVAR_SERVER | CVAR_SAVE, "sv_status_show_qcstatus", "0", "show the 'qcstatus' field in status replies, not the 'frags' field. Turn this on if your mod uses this field, and the 'frags' field on the other hand has no meaningful value."};
+cvar_t sv_namechangetimer = {CVAR_SERVER | CVAR_SAVE, "sv_namechangetimer", "5", "how often to allow name changes, in seconds (prevents people from using animated names and other tricks"};
+
+/*
+===============================================================================
+
+SERVER TRANSITIONS
+
+===============================================================================
+*/
+
+/*
+======================
+SV_Map_f
+
+handle a
+map <servername>
+command from the console.  Active clients are kicked off.
+======================
+*/
+static void SV_Map_f(cmd_state_t *cmd)
+{
+       char level[MAX_QPATH];
+
+       if (Cmd_Argc(cmd) != 2)
+       {
+               Con_Print("map <levelname> : start a new game (kicks off all players)\n");
+               return;
+       }
+
+       // GAME_DELUXEQUAKE - clear warpmark (used by QC)
+       if (gamemode == GAME_DELUXEQUAKE)
+               Cvar_Set(&cvars_all, "warpmark", "");
+
+       cls.demonum = -1;               // stop demo loop in case this fails
+
+       CL_Disconnect ();
+       SV_Shutdown();
+
+       if(svs.maxclients != svs.maxclients_next)
+       {
+               svs.maxclients = svs.maxclients_next;
+               if (svs.clients)
+                       Mem_Free(svs.clients);
+               svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
+       }
+
+#ifdef CONFIG_MENU
+       // remove menu
+       if (key_dest == key_menu || key_dest == key_menu_grabbed)
+               MR_ToggleMenu(0);
+#endif
+       key_dest = key_game;
+
+       svs.serverflags = 0;                    // haven't completed an episode yet
+       strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
+       SV_SpawnServer(level);
+       if (sv.active && cls.state == ca_disconnected)
+               CL_EstablishConnection("local:1", -2);
+}
+
+/*
+==================
+SV_Changelevel_f
+
+Goes to a new map, taking all clients along
+==================
+*/
+static void SV_Changelevel_f(cmd_state_t *cmd)
+{
+       char level[MAX_QPATH];
+
+       if (Cmd_Argc(cmd) != 2)
+       {
+               Con_Print("changelevel <levelname> : continue game on a new level\n");
+               return;
+       }
+       // HACKHACKHACK
+       if (!sv.active) {
+               SV_Map_f(cmd);
+               return;
+       }
+
+#ifdef CONFIG_MENU
+       // remove menu
+       if (key_dest == key_menu || key_dest == key_menu_grabbed)
+               MR_ToggleMenu(0);
+#endif
+       key_dest = key_game;
+
+       SV_SaveSpawnparms ();
+       strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
+       SV_SpawnServer(level);
+       if (sv.active && cls.state == ca_disconnected)
+               CL_EstablishConnection("local:1", -2);
+}
+
+/*
+==================
+SV_Restart_f
+
+Restarts the current server for a dead player
+==================
+*/
+static void SV_Restart_f(cmd_state_t *cmd)
+{
+       char mapname[MAX_QPATH];
+
+       if (Cmd_Argc(cmd) != 1)
+       {
+               Con_Print("restart : restart current level\n");
+               return;
+       }
+       if (!sv.active)
+       {
+               Con_Print("Only the server may restart\n");
+               return;
+       }
+
+#ifdef CONFIG_MENU
+       // remove menu
+       if (key_dest == key_menu || key_dest == key_menu_grabbed)
+               MR_ToggleMenu(0);
+#endif
+       key_dest = key_game;
+
+       strlcpy(mapname, sv.name, sizeof(mapname));
+       SV_SpawnServer(mapname);
+       if (sv.active && cls.state == ca_disconnected)
+               CL_EstablishConnection("local:1", -2);
+}
+
+//===========================================================================
+
+// Disable cheats if sv_cheats is turned off
+static void SV_DisableCheats_c(char *value)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       int i = 0;
+
+       if (value[0] == '0' && !value[1])
+       {
+               while (svs.clients[i].edict)
+               {
+                       if (((int)PRVM_serveredictfloat(svs.clients[i].edict, flags) & FL_GODMODE))
+                               PRVM_serveredictfloat(svs.clients[i].edict, flags) = (int)PRVM_serveredictfloat(svs.clients[i].edict, flags) ^ FL_GODMODE;
+                       if (((int)PRVM_serveredictfloat(svs.clients[i].edict, flags) & FL_NOTARGET))
+                               PRVM_serveredictfloat(svs.clients[i].edict, flags) = (int)PRVM_serveredictfloat(svs.clients[i].edict, flags) ^ FL_NOTARGET;
+                       if (PRVM_serveredictfloat(svs.clients[i].edict, movetype) == MOVETYPE_NOCLIP ||
+                               PRVM_serveredictfloat(svs.clients[i].edict, movetype) == MOVETYPE_FLY)
+                       {
+                               noclip_anglehack = false;
+                               PRVM_serveredictfloat(svs.clients[i].edict, movetype) = MOVETYPE_WALK;
+                       }
+                       i++;
+               }
+       }
+}
+
+/*
+==================
+SV_God_f
+
+Sets client to godmode
+==================
+*/
+static void SV_God_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+
+       PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_GODMODE;
+       if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_GODMODE) )
+               SV_ClientPrint("godmode OFF\n");
+       else
+               SV_ClientPrint("godmode ON\n");
+}
+
+qboolean noclip_anglehack;
+
+static void SV_Noclip_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+
+       if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_NOCLIP)
+       {
+               noclip_anglehack = true;
+               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_NOCLIP;
+               SV_ClientPrint("noclip ON\n");
+       }
+       else
+       {
+               noclip_anglehack = false;
+               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
+               SV_ClientPrint("noclip OFF\n");
+       }
+}
+
+/*
+==================
+SV_Give_f
+==================
+*/
+static void SV_Give_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       const char *t;
+       int v;
+
+       t = Cmd_Argv(cmd, 1);
+       v = atoi (Cmd_Argv(cmd, 2));
+
+       switch (t[0])
+       {
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+               // MED 01/04/97 added hipnotic give stuff
+               if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
+               {
+                       if (t[0] == '6')
+                       {
+                               if (t[1] == 'a')
+                                       PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_PROXIMITY_GUN;
+                               else
+                                       PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | IT_GRENADE_LAUNCHER;
+                       }
+                       else if (t[0] == '9')
+                               PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_LASER_CANNON;
+                       else if (t[0] == '0')
+                               PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_MJOLNIR;
+                       else if (t[0] >= '2')
+                               PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
+               }
+               else
+               {
+                       if (t[0] >= '2')
+                               PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
+               }
+               break;
+
+       case 's':
+               if (gamemode == GAME_ROGUE)
+                       PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
+
+               PRVM_serveredictfloat(host_client->edict, ammo_shells) = v;
+               break;
+       case 'n':
+               if (gamemode == GAME_ROGUE)
+               {
+                       PRVM_serveredictfloat(host_client->edict, ammo_nails1) = v;
+                       if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
+                               PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
+               }
+               else
+               {
+                       PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
+               }
+               break;
+       case 'l':
+               if (gamemode == GAME_ROGUE)
+               {
+                       PRVM_serveredictfloat(host_client->edict, ammo_lava_nails) = v;
+                       if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
+                               PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
+               }
+               break;
+       case 'r':
+               if (gamemode == GAME_ROGUE)
+               {
+                       PRVM_serveredictfloat(host_client->edict, ammo_rockets1) = v;
+                       if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
+                               PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
+               }
+               else
+               {
+                       PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
+               }
+               break;
+       case 'm':
+               if (gamemode == GAME_ROGUE)
+               {
+                       PRVM_serveredictfloat(host_client->edict, ammo_multi_rockets) = v;
+                       if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
+                               PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
+               }
+               break;
+       case 'h':
+               PRVM_serveredictfloat(host_client->edict, health) = v;
+               break;
+       case 'c':
+               if (gamemode == GAME_ROGUE)
+               {
+                       PRVM_serveredictfloat(host_client->edict, ammo_cells1) = v;
+                       if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
+                               PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
+               }
+               else
+               {
+                       PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
+               }
+               break;
+       case 'p':
+               if (gamemode == GAME_ROGUE)
+               {
+                       PRVM_serveredictfloat(host_client->edict, ammo_plasma) = v;
+                       if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
+                               PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
+               }
+               break;
+       }
+}
+
+/*
+==================
+SV_Fly_f
+
+Sets client to flymode
+==================
+*/
+static void SV_Fly_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+
+       if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_FLY)
+       {
+               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_FLY;
+               SV_ClientPrint("flymode ON\n");
+       }
+       else
+       {
+               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
+               SV_ClientPrint("flymode OFF\n");
+       }
+}
+
+static void SV_Notarget_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+
+       PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_NOTARGET;
+       if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_NOTARGET) )
+               SV_ClientPrint("notarget OFF\n");
+       else
+               SV_ClientPrint("notarget ON\n");
+}
+
+/*
+==================
+SV_Kill_f
+==================
+*/
+static void SV_Kill_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
+       {
+               SV_ClientPrint("Can't suicide -- already dead!\n");
+               return;
+       }
+
+       PRVM_serverglobalfloat(time) = sv.time;
+       PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
+       prog->ExecuteProgram(prog, PRVM_serverfunction(ClientKill), "QC function ClientKill is missing");
+}
+
+/*
+==================
+SV_Pause_f
+==================
+*/
+static void SV_Pause_f(cmd_state_t *cmd)
+{
+       void (*print) (const char *fmt, ...);
+       if (cmd->source == src_command)
+       {
+               // if running a client, try to send over network so the pause is handled by the server
+               if (cls.state == ca_connected)
+               {
+                       Cmd_ForwardToServer_f(cmd);
+                       return;
+               }
+               print = Con_Printf;
+       }
+       else
+               print = SV_ClientPrintf;
+
+       if (!pausable.integer)
+       {
+               if (cmd->source == src_client)
+               {
+                       if(cls.state == ca_dedicated || host_client != &svs.clients[0]) // non-admin
+                       {
+                               print("Pause not allowed.\n");
+                               return;
+                       }
+               }
+       }
+       
+       sv.paused ^= 1;
+       if (cmd->source != src_command)
+               SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
+       else if(*(sv_adminnick.string))
+               SV_BroadcastPrintf("%s %spaused the game\n", sv_adminnick.string, sv.paused ? "" : "un");
+       else
+               SV_BroadcastPrintf("%s %spaused the game\n", hostname.string, sv.paused ? "" : "un");
+       // send notification to all clients
+       MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
+       MSG_WriteByte(&sv.reliable_datagram, sv.paused);
+}
+
+static void SV_Say(cmd_state_t *cmd, qboolean teamonly)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       client_t *save;
+       int j, quoted;
+       const char *p1;
+       char *p2;
+       // LadyHavoc: long say messages
+       char text[1024];
+       qboolean fromServer = false;
+
+       if (cmd->source == src_command)
+       {
+               if (cls.state == ca_dedicated)
+               {
+                       fromServer = true;
+                       teamonly = false;
+               }
+               else
+               {
+                       Cmd_ForwardToServer_f(cmd);
+                       return;
+               }
+       }
+
+       if (Cmd_Argc (cmd) < 2)
+               return;
+
+       if (!teamplay.integer)
+               teamonly = false;
+
+       p1 = Cmd_Args(cmd);
+       quoted = false;
+       if (*p1 == '\"')
+       {
+               quoted = true;
+               p1++;
+       }
+       // note this uses the chat prefix \001
+       if (!fromServer && !teamonly)
+               dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
+       else if (!fromServer && teamonly)
+               dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
+       else if(*(sv_adminnick.string))
+               dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
+       else
+               dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
+       p2 = text + strlen(text);
+       while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
+       {
+               if (p2[-1] == '\"' && quoted)
+                       quoted = false;
+               p2[-1] = 0;
+               p2--;
+       }
+       strlcat(text, "\n", sizeof(text));
+
+       // note: save is not a valid edict if fromServer is true
+       save = host_client;
+       for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
+               if (host_client->active && (!teamonly || PRVM_serveredictfloat(host_client->edict, team) == PRVM_serveredictfloat(save->edict, team)))
+                       SV_ClientPrint(text);
+       host_client = save;
+
+       if (cls.state == ca_dedicated)
+               Con_Print(&text[1]);
+}
+
+static void SV_Say_f(cmd_state_t *cmd)
+{
+       SV_Say(cmd, false);
+}
+
+static void SV_Say_Team_f(cmd_state_t *cmd)
+{
+       SV_Say(cmd, true);
+}
+
+static void SV_Tell_f(cmd_state_t *cmd)
+{
+       const char *playername_start = NULL;
+       size_t playername_length = 0;
+       int playernumber = 0;
+       client_t *save;
+       int j;
+       const char *p1, *p2;
+       char text[MAX_INPUTLINE]; // LadyHavoc: FIXME: temporary buffer overflow fix (was 64)
+       qboolean fromServer = false;
+
+       if (cmd->source == src_command)
+       {
+               if (cls.state == ca_dedicated)
+                       fromServer = true;
+               else
+               {
+                       Cmd_ForwardToServer_f(cmd);
+                       return;
+               }
+       }
+
+       if (Cmd_Argc (cmd) < 2)
+               return;
+
+       // note this uses the chat prefix \001
+       if (!fromServer)
+               dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
+       else if(*(sv_adminnick.string))
+               dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
+       else
+               dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
+
+       p1 = Cmd_Args(cmd);
+       p2 = p1 + strlen(p1);
+       // remove the target name
+       while (p1 < p2 && *p1 == ' ')
+               p1++;
+       if(*p1 == '#')
+       {
+               ++p1;
+               while (p1 < p2 && *p1 == ' ')
+                       p1++;
+               while (p1 < p2 && isdigit(*p1))
+               {
+                       playernumber = playernumber * 10 + (*p1 - '0');
+                       p1++;
+               }
+               --playernumber;
+       }
+       else if(*p1 == '"')
+       {
+               ++p1;
+               playername_start = p1;
+               while (p1 < p2 && *p1 != '"')
+                       p1++;
+               playername_length = p1 - playername_start;
+               if(p1 < p2)
+                       p1++;
+       }
+       else
+       {
+               playername_start = p1;
+               while (p1 < p2 && *p1 != ' ')
+                       p1++;
+               playername_length = p1 - playername_start;
+       }
+       while (p1 < p2 && *p1 == ' ')
+               p1++;
+       if(playername_start)
+       {
+               // set playernumber to the right client
+               char namebuf[128];
+               if(playername_length >= sizeof(namebuf))
+               {
+                       if (fromServer)
+                               Con_Print("Host_Tell: too long player name/ID\n");
+                       else
+                               SV_ClientPrint("Host_Tell: too long player name/ID\n");
+                       return;
+               }
+               memcpy(namebuf, playername_start, playername_length);
+               namebuf[playername_length] = 0;
+               for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
+               {
+                       if (!svs.clients[playernumber].active)
+                               continue;
+                       if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
+                               break;
+               }
+       }
+       if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
+       {
+               if (fromServer)
+                       Con_Print("Host_Tell: invalid player name/ID\n");
+               else
+                       SV_ClientPrint("Host_Tell: invalid player name/ID\n");
+               return;
+       }
+       // remove trailing newlines
+       while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
+               p2--;
+       // remove quotes if present
+       if (*p1 == '"')
+       {
+               p1++;
+               if (p2[-1] == '"')
+                       p2--;
+               else if (fromServer)
+                       Con_Print("Host_Tell: missing end quote\n");
+               else
+                       SV_ClientPrint("Host_Tell: missing end quote\n");
+       }
+       while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
+               p2--;
+       if(p1 == p2)
+               return; // empty say
+       for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
+               text[j++] = *p1++;
+       text[j++] = '\n';
+       text[j++] = 0;
+
+       save = host_client;
+       host_client = svs.clients + playernumber;
+       SV_ClientPrint(text);
+       host_client = save;
+}
+
+/*
+==================
+SV_Ping_f
+
+==================
+*/
+static void SV_Ping_f(cmd_state_t *cmd)
+{
+       int i;
+       client_t *client;
+       void (*print) (const char *fmt, ...);
+
+       if (cmd->source == src_command)
+       {
+               // if running a client, try to send over network so the client's ping report parser will see the report
+               if (cls.state == ca_connected)
+               {
+                       Cmd_ForwardToServer_f(cmd);
+                       return;
+               }
+               print = Con_Printf;
+       }
+       else
+               print = SV_ClientPrintf;
+
+       if (!sv.active)
+               return;
+
+       print("Client ping times:\n");
+       for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
+       {
+               if (!client->active)
+                       continue;
+               print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
+       }
+}
+
+/*
+====================
+SV_Pings_f
+
+Send back ping and packet loss update for all current players to this player
+====================
+*/
+static void SV_Pings_f(cmd_state_t *cmd)
+{
+       int             i, j, ping, packetloss, movementloss;
+       char temp[128];
+
+       if (!host_client->netconnection)
+               return;
+
+       if (sv.protocol != PROTOCOL_QUAKEWORLD)
+       {
+               MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
+               MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
+       }
+       for (i = 0;i < svs.maxclients;i++)
+       {
+               packetloss = 0;
+               movementloss = 0;
+               if (svs.clients[i].netconnection)
+               {
+                       for (j = 0;j < NETGRAPH_PACKETS;j++)
+                               if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
+                                       packetloss++;
+                       for (j = 0;j < NETGRAPH_PACKETS;j++)
+                               if (svs.clients[i].movement_count[j] < 0)
+                                       movementloss++;
+               }
+               packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
+               movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
+               ping = (int)floor(svs.clients[i].ping*1000+0.5);
+               ping = bound(0, ping, 9999);
+               if (sv.protocol == PROTOCOL_QUAKEWORLD)
+               {
+                       // send qw_svc_updateping and qw_svc_updatepl messages
+                       MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
+                       MSG_WriteShort(&host_client->netconnection->message, ping);
+                       MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
+                       MSG_WriteByte(&host_client->netconnection->message, packetloss);
+               }
+               else
+               {
+                       // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
+                       if(movementloss)
+                               dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
+                       else
+                               dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
+                       MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
+               }
+       }
+       if (sv.protocol != PROTOCOL_QUAKEWORLD)
+               MSG_WriteString(&host_client->netconnection->message, "\n");
+}
+
+/*
+====================
+SV_User_f
+
+user <name or userid>
+
+Dump userdata / masterdata for a user
+====================
+*/
+static void SV_User_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
+{
+       int             uid;
+       int             i;
+
+       if (Cmd_Argc(cmd) != 2)
+       {
+               Con_Printf ("Usage: user <username / userid>\n");
+               return;
+       }
+
+       uid = atoi(Cmd_Argv(cmd, 1));
+
+       for (i = 0;i < cl.maxclients;i++)
+       {
+               if (!cl.scores[i].name[0])
+                       continue;
+               if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(cmd, 1)))
+               {
+                       InfoString_Print(cl.scores[i].qw_userinfo);
+                       return;
+               }
+       }
+       Con_Printf ("User not in server.\n");
+}
+
+/*
+====================
+SV_Users_f
+
+Dump userids for all current players
+====================
+*/
+static void SV_Users_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
+{
+       int             i;
+       int             c;
+
+       c = 0;
+       Con_Printf ("userid frags name\n");
+       Con_Printf ("------ ----- ----\n");
+       for (i = 0;i < cl.maxclients;i++)
+       {
+               if (cl.scores[i].name[0])
+               {
+                       Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
+                       c++;
+               }
+       }
+
+       Con_Printf ("%i total users\n", c);
+}
+
+/*
+==================
+SV_Status_f
+==================
+*/
+static void SV_Status_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       char qcstatus[256];
+       client_t *client;
+       int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
+       void (*print) (const char *fmt, ...);
+       char ip[48]; // can contain a full length v6 address with [] and a port
+       int frags;
+       char vabuf[1024];
+
+       if (cmd->source == src_command)
+       {
+               // if running a client, try to send over network so the client's status report parser will see the report
+               if (cls.state == ca_connected)
+               {
+                       Cmd_ForwardToServer_f(cmd);
+                       return;
+               }
+               print = Con_Printf;
+       }
+       else
+               print = SV_ClientPrintf;
+
+       if (!sv.active)
+               return;
+
+       in = 0;
+       if (Cmd_Argc(cmd) == 2)
+       {
+               if (strcmp(Cmd_Argv(cmd, 1), "1") == 0)
+                       in = 1;
+               else if (strcmp(Cmd_Argv(cmd, 1), "2") == 0)
+                       in = 2;
+       }
+
+       for (players = 0, i = 0;i < svs.maxclients;i++)
+               if (svs.clients[i].active)
+                       players++;
+       print ("host:     %s\n", Cvar_VariableString (&cvars_all, "hostname", CVAR_SERVER));
+       print ("version:  %s build %s (gamename %s)\n", gamename, buildstring, gamenetworkfiltername);
+       print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
+       print ("map:      %s\n", sv.name);
+       print ("timing:   %s\n", Host_TimingReport(vabuf, sizeof(vabuf)));
+       print ("players:  %i active (%i max)\n\n", players, svs.maxclients);
+
+       if (in == 1)
+               print ("^2IP                                             %%pl ping  time   frags  no   name\n");
+       else if (in == 2)
+               print ("^5IP                                              no   name\n");
+
+       for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
+       {
+               if (!client->active)
+                       continue;
+
+               ++k;
+
+               if (in == 0 || in == 1)
+               {
+                       seconds = (int)(host.realtime - client->connecttime);
+                       minutes = seconds / 60;
+                       if (minutes)
+                       {
+                               seconds -= (minutes * 60);
+                               hours = minutes / 60;
+                               if (hours)
+                                       minutes -= (hours * 60);
+                       }
+                       else
+                               hours = 0;
+                       
+                       packetloss = 0;
+                       if (client->netconnection)
+                               for (j = 0;j < NETGRAPH_PACKETS;j++)
+                                       if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
+                                               packetloss++;
+                       packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
+                       ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
+               }
+
+               if(sv_status_privacy.integer && cmd->source != src_command)
+                       strlcpy(ip, client->netconnection ? "hidden" : "botclient", 48);
+               else
+                       strlcpy(ip, (client->netconnection && *client->netconnection->address) ? client->netconnection->address : "botclient", 48);
+
+               frags = client->frags;
+
+               if(sv_status_show_qcstatus.integer)
+               {
+                       prvm_edict_t *ed = PRVM_EDICT_NUM(i + 1);
+                       const char *str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
+                       if(str && *str)
+                       {
+                               char *p;
+                               const char *q;
+                               p = qcstatus;
+                               for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
+                                       if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
+                                               *p++ = *q;
+                               *p = 0;
+                               if(*qcstatus)
+                                       frags = atoi(qcstatus);
+                       }
+               }
+               
+               if (in == 0) // default layout
+               {
+                       if (sv.protocol == PROTOCOL_QUAKE && svs.maxclients <= 99)
+                       {
+                               // LadyHavoc: this is very touchy because we must maintain ProQuake compatible status output
+                               print ("#%-2u %-16.16s  %3i  %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
+                               print ("   %s\n", ip);
+                       }
+                       else
+                       {
+                               // LadyHavoc: no real restrictions here, not a ProQuake-compatible protocol anyway...
+                               print ("#%-3u %-16.16s %4i  %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
+                               print ("   %s\n", ip);
+                       }
+               }
+               else if (in == 1) // extended layout
+               {
+                       print ("%s%-47s %2i %4i %2i:%02i:%02i %4i  #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, packetloss, ping, hours, minutes, seconds, frags, i+1, client->name);
+               }
+               else if (in == 2) // reduced layout
+               {
+                       print ("%s%-47s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
+               }
+       }
+}
+
+/*
+==================
+SV_Kick_f
+
+Kicks a user off of the server
+==================
+*/
+static void SV_Kick_f(cmd_state_t *cmd)
+{
+       const char *who;
+       const char *message = NULL;
+       client_t *save;
+       int i;
+       qboolean byNumber = false;
+
+       if (!sv.active)
+               return;
+
+       save = host_client;
+
+       if (Cmd_Argc(cmd) > 2 && strcmp(Cmd_Argv(cmd, 1), "#") == 0)
+       {
+               i = (int)(atof(Cmd_Argv(cmd, 2)) - 1);
+               if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
+                       return;
+               byNumber = true;
+       }
+       else
+       {
+               for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+               {
+                       if (!host_client->active)
+                               continue;
+                       if (strcasecmp(host_client->name, Cmd_Argv(cmd, 1)) == 0)
+                               break;
+               }
+       }
+
+       if (i < svs.maxclients)
+       {
+               if (cmd->source == src_command)
+               {
+                       if (cls.state == ca_dedicated)
+                               who = "Console";
+                       else
+                               who = cl_name.string;
+               }
+               else
+                       who = save->name;
+
+               // can't kick yourself!
+               if (host_client == save)
+                       return;
+
+               if (Cmd_Argc(cmd) > 2)
+               {
+                       message = Cmd_Args(cmd);
+                       COM_ParseToken_Simple(&message, false, false, true);
+                       if (byNumber)
+                       {
+                               message++;                                                      // skip the #
+                               while (*message == ' ')                         // skip white space
+                                       message++;
+                               message += strlen(Cmd_Argv(cmd, 2));    // skip the number
+                       }
+                       while (*message && *message == ' ')
+                               message++;
+               }
+               if (message)
+                       SV_ClientPrintf("Kicked by %s: %s\n", who, message);
+               else
+                       SV_ClientPrintf("Kicked by %s\n", who);
+               SV_DropClient (false); // kicked
+       }
+
+       host_client = save;
+}
+
+static void SV_MaxPlayers_f(cmd_state_t *cmd)
+{
+       int n;
+
+       if (Cmd_Argc(cmd) != 2)
+       {
+               Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
+               return;
+       }
+
+       if (sv.active)
+       {
+               Con_Print("maxplayers can not be changed while a server is running.\n");
+               Con_Print("It will be changed on next server startup (\"map\" command).\n");
+       }
+
+       n = atoi(Cmd_Argv(cmd, 1));
+       n = bound(1, n, MAX_SCOREBOARD);
+       Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
+
+       svs.maxclients_next = n;
+       if (n == 1)
+               Cvar_Set (&cvars_all, "deathmatch", "0");
+       else
+               Cvar_Set (&cvars_all, "deathmatch", "1");
+}
+
+/*
+===============================================================================
+
+DEBUGGING TOOLS
+
+===============================================================================
+*/
+
+static prvm_edict_t    *FindViewthing(prvm_prog_t *prog)
+{
+       int             i;
+       prvm_edict_t    *e;
+
+       for (i=0 ; i<prog->num_edicts ; i++)
+       {
+               e = PRVM_EDICT_NUM(i);
+               if (!strcmp (PRVM_GetString(prog, PRVM_serveredictstring(e, classname)), "viewthing"))
+                       return e;
+       }
+       Con_Print("No viewthing on map\n");
+       return NULL;
+}
+
+/*
+==================
+SV_Viewmodel_f
+==================
+*/
+static void SV_Viewmodel_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       prvm_edict_t    *e;
+       dp_model_t      *m;
+
+       if (!sv.active)
+               return;
+
+       e = FindViewthing(prog);
+       if (e)
+       {
+               m = Mod_ForName (Cmd_Argv(cmd, 1), false, true, NULL);
+               if (m && m->loaded && m->Draw)
+               {
+                       PRVM_serveredictfloat(e, frame) = 0;
+                       cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
+               }
+               else
+                       Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(cmd, 1));
+       }
+}
+
+/*
+==================
+SV_Viewframe_f
+==================
+*/
+static void SV_Viewframe_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       prvm_edict_t    *e;
+       int             f;
+       dp_model_t      *m;
+
+       if (!sv.active)
+               return;
+
+       e = FindViewthing(prog);
+       if (e)
+       {
+               m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
+
+               f = atoi(Cmd_Argv(cmd, 1));
+               if (f >= m->numframes)
+                       f = m->numframes-1;
+
+               PRVM_serveredictfloat(e, frame) = f;
+       }
+}
+
+static void PrintFrameName (dp_model_t *m, int frame)
+{
+       if (m->animscenes)
+               Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
+       else
+               Con_Printf("frame %i\n", frame);
+}
+
+/*
+==================
+SV_Viewnext_f
+==================
+*/
+static void SV_Viewnext_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       prvm_edict_t    *e;
+       dp_model_t      *m;
+
+       if (!sv.active)
+               return;
+
+       e = FindViewthing(prog);
+       if (e)
+       {
+               m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
+
+               PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) + 1;
+               if (PRVM_serveredictfloat(e, frame) >= m->numframes)
+                       PRVM_serveredictfloat(e, frame) = m->numframes - 1;
+
+               PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
+       }
+}
+
+/*
+==================
+SV_Viewprev_f
+==================
+*/
+static void SV_Viewprev_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       prvm_edict_t    *e;
+       dp_model_t      *m;
+
+       if (!sv.active)
+               return;
+
+       e = FindViewthing(prog);
+       if (e)
+       {
+               m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
+
+               PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) - 1;
+               if (PRVM_serveredictfloat(e, frame) < 0)
+                       PRVM_serveredictfloat(e, frame) = 0;
+
+               PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
+       }
+}
+
+void SV_InitOperatorCommands(void)
+{
+       Cvar_RegisterVariable(&sv_cheats);
+       Cvar_RegisterCallback(&sv_cheats, SV_DisableCheats_c);
+       Cvar_RegisterVariable(&sv_adminnick);
+       Cvar_RegisterVariable(&sv_status_privacy);
+       Cvar_RegisterVariable(&sv_status_show_qcstatus);
+       Cvar_RegisterVariable(&sv_namechangetimer);
+       
+       Cmd_AddCommand(CMD_SERVER | CMD_SERVER_FROM_CLIENT, "status", SV_Status_f, "print server status information");
+       Cmd_AddCommand(CMD_SHARED | CMD_INITWAIT, "map", SV_Map_f, "kick everyone off the server and start a new level");
+       Cmd_AddCommand(CMD_SHARED, "restart", SV_Restart_f, "restart current level");
+       Cmd_AddCommand(CMD_SHARED, "changelevel", SV_Changelevel_f, "change to another level, bringing along all connected clients");
+       Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "say", SV_Say_f, "send a chat message to everyone on the server");
+       Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "say_team", SV_Say_Team_f, "send a chat message to your team on the server");
+       Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "tell", SV_Tell_f, "send a chat message to only one person on the server");
+       Cmd_AddCommand(CMD_SERVER | CMD_SERVER_FROM_CLIENT, "pause", SV_Pause_f, "pause the game (if the server allows pausing)");
+       Cmd_AddCommand(CMD_SHARED, "kick", SV_Kick_f, "kick a player off the server by number or name");
+       Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "ping", SV_Ping_f, "print ping times of all players on the server");
+       Cmd_AddCommand(CMD_SHARED | CMD_INITWAIT, "load", SV_Loadgame_f, "load a saved game file");
+       Cmd_AddCommand(CMD_SHARED, "save", SV_Savegame_f, "save the game to a file");
+       Cmd_AddCommand(CMD_SHARED, "viewmodel", SV_Viewmodel_f, "change model of viewthing entity in current level");
+       Cmd_AddCommand(CMD_SHARED, "viewframe", SV_Viewframe_f, "change animation frame of viewthing entity in current level");
+       Cmd_AddCommand(CMD_SHARED, "viewnext", SV_Viewnext_f, "change to next animation frame of viewthing entity in current level");
+       Cmd_AddCommand(CMD_SHARED, "viewprev", SV_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
+       Cmd_AddCommand(CMD_SHARED, "maxplayers", SV_MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
+       Cmd_AddCommand(CMD_SHARED, "user", SV_User_f, "prints additional information about a player number or name on the scoreboard");
+       Cmd_AddCommand(CMD_SHARED, "users", SV_Users_f, "prints additional information about all players on the scoreboard");
+
+       // commands that do not have automatic forwarding from cmd_client, these are internal details of the network protocol and not of interest to users (if they know what they are doing they can still use a generic "cmd prespawn" or similar)
+       Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "prespawn", SV_PreSpawn_f, "internal use - signon 1 (client acknowledges that server information has been received)");
+       Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "spawn", SV_Spawn_f, "internal use - signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
+       Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "begin", SV_Begin_f, "internal use - signon 3 (client asks server to start sending entities, and will go to signon 4 (playing) when the first entity update is received)");
+       Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "pings", SV_Pings_f, "internal use - command sent by clients to request updated ping and packetloss of players on scoreboard (originally from QW, but also used on NQ servers)");
+
+       Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "god", SV_God_f, "god mode (invulnerability)");
+       Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "notarget", SV_Notarget_f, "notarget mode (monsters do not see you)");
+       Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "fly", SV_Fly_f, "fly mode (flight)");
+       Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "noclip", SV_Noclip_f, "noclip mode (flight without collisions, move through walls)");
+       Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "give", SV_Give_f, "alter inventory");
+       Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "kill", SV_Kill_f, "die instantly");
+}
\ No newline at end of file
index 19f455370c1fd85086bda56d75c90d4e27721680..f810e5b91a0a9fdf93e0af0736cfc549e2684fff 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -626,6 +626,8 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_mapformat_is_quake2);
        Cvar_RegisterVariable (&sv_mapformat_is_quake3);
 
+       SV_InitOperatorCommands();
+
        sv_mempool = Mem_AllocPool("server", 0, NULL);
 }
 
diff --git a/sv_save.c b/sv_save.c
new file mode 100644 (file)
index 0000000..f7f26d6
--- /dev/null
+++ b/sv_save.c
@@ -0,0 +1,577 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+#include "prvm_cmds.h"
+
+/*
+===============================================================================
+
+LOAD / SAVE GAME
+
+===============================================================================
+*/
+
+#define        SAVEGAME_VERSION        5
+
+void SV_Savegame_to(prvm_prog_t *prog, const char *name)
+{
+       qfile_t *f;
+       int             i, k, l, numbuffers, lightstyles = 64;
+       char    comment[SAVEGAME_COMMENT_LENGTH+1];
+       char    line[MAX_INPUTLINE];
+       qboolean isserver;
+       char    *s;
+
+       // first we have to figure out if this can be saved in 64 lightstyles
+       // (for Quake compatibility)
+       for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
+               if (sv.lightstyles[i][0])
+                       lightstyles = i+1;
+
+       isserver = prog == SVVM_prog;
+
+       Con_Printf("Saving game to %s...\n", name);
+       f = FS_OpenRealFile(name, "wb", false);
+       if (!f)
+       {
+               Con_Print("ERROR: couldn't open.\n");
+               return;
+       }
+
+       FS_Printf(f, "%i\n", SAVEGAME_VERSION);
+
+       memset(comment, 0, sizeof(comment));
+       if(isserver)
+               dpsnprintf(comment, sizeof(comment), "%-21.21s kills:%3i/%3i", PRVM_GetString(prog, PRVM_serveredictstring(prog->edicts, message)), (int)PRVM_serverglobalfloat(killed_monsters), (int)PRVM_serverglobalfloat(total_monsters));
+       else
+               dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", prog->name);
+       // convert space to _ to make stdio happy
+       // LadyHavoc: convert control characters to _ as well
+       for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
+               if (ISWHITESPACEORCONTROL(comment[i]))
+                       comment[i] = '_';
+       comment[SAVEGAME_COMMENT_LENGTH] = '\0';
+
+       FS_Printf(f, "%s\n", comment);
+       if(isserver)
+       {
+               for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+                       FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
+               FS_Printf(f, "%d\n", current_skill);
+               FS_Printf(f, "%s\n", sv.name);
+               FS_Printf(f, "%f\n",sv.time);
+       }
+       else
+       {
+               for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+                       FS_Printf(f, "(dummy)\n");
+               FS_Printf(f, "%d\n", 0);
+               FS_Printf(f, "%s\n", "(dummy)");
+               FS_Printf(f, "%f\n", host.realtime);
+       }
+
+       // write the light styles
+       for (i=0 ; i<lightstyles ; i++)
+       {
+               if (isserver && sv.lightstyles[i][0])
+                       FS_Printf(f, "%s\n", sv.lightstyles[i]);
+               else
+                       FS_Print(f,"m\n");
+       }
+
+       PRVM_ED_WriteGlobals (prog, f);
+       for (i=0 ; i<prog->num_edicts ; i++)
+       {
+               FS_Printf(f,"// edict %d\n", i);
+               //Con_Printf("edict %d...\n", i);
+               PRVM_ED_Write (prog, f, PRVM_EDICT_NUM(i));
+       }
+
+#if 1
+       FS_Printf(f,"/*\n");
+       FS_Printf(f,"// DarkPlaces extended savegame\n");
+       // darkplaces extension - extra lightstyles, support for color lightstyles
+       for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+               if (isserver && sv.lightstyles[i][0])
+                       FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
+
+       // darkplaces extension - model precaches
+       for (i=1 ; i<MAX_MODELS ; i++)
+               if (sv.model_precache[i][0])
+                       FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
+
+       // darkplaces extension - sound precaches
+       for (i=1 ; i<MAX_SOUNDS ; i++)
+               if (sv.sound_precache[i][0])
+                       FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
+
+       // darkplaces extension - save buffers
+       numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
+       for (i = 0; i < numbuffers; i++)
+       {
+               prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
+               if(stringbuffer && (stringbuffer->flags & STRINGBUFFER_SAVED))
+               {
+                       FS_Printf(f,"sv.buffer %i %i \"string\"\n", i, stringbuffer->flags & STRINGBUFFER_QCFLAGS);
+                       for(k = 0; k < stringbuffer->num_strings; k++)
+                       {
+                               if (!stringbuffer->strings[k])
+                                       continue;
+                               // Parse the string a bit to turn special characters
+                               // (like newline, specifically) into escape codes
+                               s = stringbuffer->strings[k];
+                               for (l = 0;l < (int)sizeof(line) - 2 && *s;)
+                               {       
+                                       if (*s == '\n')
+                                       {
+                                               line[l++] = '\\';
+                                               line[l++] = 'n';
+                                       }
+                                       else if (*s == '\r')
+                                       {
+                                               line[l++] = '\\';
+                                               line[l++] = 'r';
+                                       }
+                                       else if (*s == '\\')
+                                       {
+                                               line[l++] = '\\';
+                                               line[l++] = '\\';
+                                       }
+                                       else if (*s == '"')
+                                       {
+                                               line[l++] = '\\';
+                                               line[l++] = '"';
+                                       }
+                                       else
+                                               line[l++] = *s;
+                                       s++;
+                               }
+                               line[l] = '\0';
+                               FS_Printf(f,"sv.bufstr %i %i \"%s\"\n", i, k, line);
+                       }
+               }
+       }
+       FS_Printf(f,"*/\n");
+#endif
+
+       FS_Close (f);
+       Con_Print("done.\n");
+}
+
+/*
+===============
+SV_Savegame_f
+===============
+*/
+void SV_Savegame_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       char    name[MAX_QPATH];
+       qboolean deadflag = false;
+
+       if (!sv.active)
+       {
+               Con_Print("Can't save - no server running.\n");
+               return;
+       }
+
+       deadflag = cl.islocalgame && svs.clients[0].active && PRVM_serveredictfloat(svs.clients[0].edict, deadflag);
+
+       if (cl.islocalgame)
+       {
+               // singleplayer checks
+               if (cl.intermission)
+               {
+                       Con_Print("Can't save in intermission.\n");
+                       return;
+               }
+
+               if (deadflag)
+               {
+                       Con_Print("Can't savegame with a dead player\n");
+                       return;
+               }
+       }
+       else
+               Con_Warn("Warning: saving a multiplayer game may have strange results when restored (to properly resume, all players must join in the same player slots and then the game can be reloaded).\n");
+
+       if (Cmd_Argc(cmd) != 2)
+       {
+               Con_Print("save <savename> : save a game\n");
+               return;
+       }
+
+       if (strstr(Cmd_Argv(cmd, 1), ".."))
+       {
+               Con_Print("Relative pathnames are not allowed.\n");
+               return;
+       }
+
+       strlcpy (name, Cmd_Argv(cmd, 1), sizeof (name));
+       FS_DefaultExtension (name, ".sav", sizeof (name));
+
+       SV_Savegame_to(prog, name);
+}
+
+/*
+===============
+SV_Loadgame_f
+===============
+*/
+void SV_Loadgame_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       char filename[MAX_QPATH];
+       char mapname[MAX_QPATH];
+       float time;
+       const char *start;
+       const char *end;
+       const char *t;
+       char *text;
+       prvm_edict_t *ent;
+       int i, k, numbuffers;
+       int entnum;
+       int version;
+       float spawn_parms[NUM_SPAWN_PARMS];
+       prvm_stringbuffer_t *stringbuffer;
+
+       if (Cmd_Argc(cmd) != 2)
+       {
+               Con_Print("load <savename> : load a game\n");
+               return;
+       }
+
+       strlcpy (filename, Cmd_Argv(cmd, 1), sizeof(filename));
+       FS_DefaultExtension (filename, ".sav", sizeof (filename));
+
+       Con_Printf("Loading game from %s...\n", filename);
+
+       // stop playing demos
+       if (cls.demoplayback)
+               CL_Disconnect ();
+
+#ifdef CONFIG_MENU
+       // remove menu
+       if (key_dest == key_menu || key_dest == key_menu_grabbed)
+               MR_ToggleMenu(0);
+#endif
+       key_dest = key_game;
+
+       cls.demonum = -1;               // stop demo loop in case this fails
+
+       t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
+       if (!text)
+       {
+               Con_Print("ERROR: couldn't open.\n");
+               return;
+       }
+
+       if(developer_entityparsing.integer)
+               Con_Printf("SV_Loadgame_f: loading version\n");
+
+       // version
+       COM_ParseToken_Simple(&t, false, false, true);
+       version = atoi(com_token);
+       if (version != SAVEGAME_VERSION)
+       {
+               Mem_Free(text);
+               Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
+               return;
+       }
+
+       if(developer_entityparsing.integer)
+               Con_Printf("SV_Loadgame_f: loading description\n");
+
+       // description
+       COM_ParseToken_Simple(&t, false, false, true);
+
+       for (i = 0;i < NUM_SPAWN_PARMS;i++)
+       {
+               COM_ParseToken_Simple(&t, false, false, true);
+               spawn_parms[i] = atof(com_token);
+       }
+       // skill
+       COM_ParseToken_Simple(&t, false, false, true);
+// this silliness is so we can load 1.06 save files, which have float skill values
+       current_skill = (int)(atof(com_token) + 0.5);
+       Cvar_SetValue (&cvars_all, "skill", (float)current_skill);
+
+       if(developer_entityparsing.integer)
+               Con_Printf("SV_Loadgame_f: loading mapname\n");
+
+       // mapname
+       COM_ParseToken_Simple(&t, false, false, true);
+       strlcpy (mapname, com_token, sizeof(mapname));
+
+       if(developer_entityparsing.integer)
+               Con_Printf("SV_Loadgame_f: loading time\n");
+
+       // time
+       COM_ParseToken_Simple(&t, false, false, true);
+       time = atof(com_token);
+
+       if(developer_entityparsing.integer)
+               Con_Printf("SV_Loadgame_f: spawning server\n");
+
+       SV_SpawnServer (mapname);
+       if (!sv.active)
+       {
+               Mem_Free(text);
+               Con_Print("Couldn't load map\n");
+               return;
+       }
+       sv.paused = true;               // pause until all clients connect
+       sv.loadgame = true;
+
+       if(developer_entityparsing.integer)
+               Con_Printf("SV_Loadgame_f: loading light styles\n");
+
+// load the light styles
+
+       // -1 is the globals
+       entnum = -1;
+
+       for (i = 0;i < MAX_LIGHTSTYLES;i++)
+       {
+               // light style
+               start = t;
+               COM_ParseToken_Simple(&t, false, false, true);
+               // if this is a 64 lightstyle savegame produced by Quake, stop now
+               // we have to check this because darkplaces may save more than 64
+               if (com_token[0] == '{')
+               {
+                       t = start;
+                       break;
+               }
+               strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
+       }
+
+       if(developer_entityparsing.integer)
+               Con_Printf("SV_Loadgame_f: skipping until globals\n");
+
+       // now skip everything before the first opening brace
+       // (this is for forward compatibility, so that older versions (at
+       // least ones with this fix) can load savegames with extra data before the
+       // first brace, as might be produced by a later engine version)
+       for (;;)
+       {
+               start = t;
+               if (!COM_ParseToken_Simple(&t, false, false, true))
+                       break;
+               if (com_token[0] == '{')
+               {
+                       t = start;
+                       break;
+               }
+       }
+
+       // unlink all entities
+       World_UnlinkAll(&sv.world);
+
+// load the edicts out of the savegame file
+       end = t;
+       for (;;)
+       {
+               start = t;
+               while (COM_ParseToken_Simple(&t, false, false, true))
+                       if (!strcmp(com_token, "}"))
+                               break;
+               if (!COM_ParseToken_Simple(&start, false, false, true))
+               {
+                       // end of file
+                       break;
+               }
+               if (strcmp(com_token,"{"))
+               {
+                       Mem_Free(text);
+                       Host_Error ("First token isn't a brace");
+               }
+
+               if (entnum == -1)
+               {
+                       if(developer_entityparsing.integer)
+                               Con_Printf("SV_Loadgame_f: loading globals\n");
+
+                       // parse the global vars
+                       PRVM_ED_ParseGlobals (prog, start);
+
+                       // restore the autocvar globals
+                       Cvar_UpdateAllAutoCvars(prog->console_cmd->cvars);
+               }
+               else
+               {
+                       // parse an edict
+                       if (entnum >= MAX_EDICTS)
+                       {
+                               Mem_Free(text);
+                               Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
+                       }
+                       while (entnum >= prog->max_edicts)
+                               PRVM_MEM_IncreaseEdicts(prog);
+                       ent = PRVM_EDICT_NUM(entnum);
+                       memset(ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
+                       ent->priv.server->free = false;
+
+                       if(developer_entityparsing.integer)
+                               Con_Printf("SV_Loadgame_f: loading edict %d\n", entnum);
+
+                       PRVM_ED_ParseEdict (prog, start, ent);
+
+                       // link it into the bsp tree
+                       if (!ent->priv.server->free && !VectorCompare(PRVM_serveredictvector(ent, absmin), PRVM_serveredictvector(ent, absmax)))
+                               SV_LinkEdict(ent);
+               }
+
+               end = t;
+               entnum++;
+       }
+
+       prog->num_edicts = entnum;
+       sv.time = time;
+
+       for (i = 0;i < NUM_SPAWN_PARMS;i++)
+               svs.clients[0].spawn_parms[i] = spawn_parms[i];
+
+       if(developer_entityparsing.integer)
+               Con_Printf("SV_Loadgame_f: skipping until extended data\n");
+
+       // read extended data if present
+       // the extended data is stored inside a /* */ comment block, which the
+       // parser intentionally skips, so we have to check for it manually here
+       if(end)
+       {
+               while (*end == '\r' || *end == '\n')
+                       end++;
+               if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
+               {
+                       if(developer_entityparsing.integer)
+                               Con_Printf("SV_Loadgame_f: loading extended data\n");
+
+                       Con_Printf("Loading extended DarkPlaces savegame\n");
+                       t = end + 2;
+                       memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
+                       memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
+                       memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
+                       BufStr_Flush(prog);
+
+                       while (COM_ParseToken_Simple(&t, false, false, true))
+                       {
+                               if (!strcmp(com_token, "sv.lightstyles"))
+                               {
+                                       COM_ParseToken_Simple(&t, false, false, true);
+                                       i = atoi(com_token);
+                                       COM_ParseToken_Simple(&t, false, false, true);
+                                       if (i >= 0 && i < MAX_LIGHTSTYLES)
+                                               strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
+                                       else
+                                               Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
+                               }
+                               else if (!strcmp(com_token, "sv.model_precache"))
+                               {
+                                       COM_ParseToken_Simple(&t, false, false, true);
+                                       i = atoi(com_token);
+                                       COM_ParseToken_Simple(&t, false, false, true);
+                                       if (i >= 0 && i < MAX_MODELS)
+                                       {
+                                               strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
+                                               sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.worldname : NULL);
+                                       }
+                                       else
+                                               Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
+                               }
+                               else if (!strcmp(com_token, "sv.sound_precache"))
+                               {
+                                       COM_ParseToken_Simple(&t, false, false, true);
+                                       i = atoi(com_token);
+                                       COM_ParseToken_Simple(&t, false, false, true);
+                                       if (i >= 0 && i < MAX_SOUNDS)
+                                               strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
+                                       else
+                                               Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
+                               }
+                               else if (!strcmp(com_token, "sv.buffer"))
+                               {
+                                       if (COM_ParseToken_Simple(&t, false, false, true))
+                                       {
+                                               i = atoi(com_token);
+                                               if (i >= 0)
+                                               {
+                                                       k = STRINGBUFFER_SAVED;
+                                                       if (COM_ParseToken_Simple(&t, false, false, true))
+                                                               k |= atoi(com_token);
+                                                       if (!BufStr_FindCreateReplace(prog, i, k, "string"))
+                                                               Con_Errorf("failed to create stringbuffer %i\n", i);
+                                               }
+                                               else
+                                                       Con_Printf("unsupported stringbuffer index %i \"%s\"\n", i, com_token);
+                                       }
+                                       else
+                                               Con_Printf("unexpected end of line when parsing sv.buffer (expected buffer index)\n");
+                               }
+                               else if (!strcmp(com_token, "sv.bufstr"))
+                               {
+                                       if (!COM_ParseToken_Simple(&t, false, false, true))
+                                               Con_Printf("unexpected end of line when parsing sv.bufstr\n");
+                                       else
+                                       {
+                                               i = atoi(com_token);
+                                               stringbuffer = BufStr_FindCreateReplace(prog, i, STRINGBUFFER_SAVED, "string");
+                                               if (stringbuffer)
+                                               {
+                                                       if (COM_ParseToken_Simple(&t, false, false, true))
+                                                       {
+                                                               k = atoi(com_token);
+                                                               if (COM_ParseToken_Simple(&t, false, false, true))
+                                                                       BufStr_Set(prog, stringbuffer, k, com_token);
+                                                               else
+                                                                       Con_Printf("unexpected end of line when parsing sv.bufstr (expected string)\n");
+                                                       }
+                                                       else
+                                                               Con_Printf("unexpected end of line when parsing sv.bufstr (expected strindex)\n");
+                                               }
+                                               else
+                                                       Con_Errorf("failed to create stringbuffer %i \"%s\"\n", i, com_token);
+                                       }
+                               }       
+                               // skip any trailing text or unrecognized commands
+                               while (COM_ParseToken_Simple(&t, true, false, true) && strcmp(com_token, "\n"))
+                                       ;
+                       }
+               }
+       }
+       Mem_Free(text);
+
+       // remove all temporary flagged string buffers (ones created with BufStr_FindCreateReplace)
+       numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
+       for (i = 0; i < numbuffers; i++)
+       {
+               if ( (stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i)) )
+                       if (stringbuffer->flags & STRINGBUFFER_TEMP)
+                               BufStr_Del(prog, stringbuffer);
+       }
+
+       if(developer_entityparsing.integer)
+               Con_Printf("SV_Loadgame_f: finished\n");
+
+       // make sure we're connected to loopback
+       if (sv.active && cls.state == ca_disconnected)
+               CL_EstablishConnection("local:1", -2);
+}
index e7e68c71cd8fe07f33bc20ffb7f6ff097b121498..67dbdcc5a943902b8668e0cb5ea664f1bb5045b8 100644 (file)
--- a/sv_user.c
+++ b/sv_user.c
@@ -26,6 +26,209 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 static usercmd_t cmd;
 extern cvar_t sv_autodemo_perclient;
 
+/*
+==================
+SV_PreSpawn_f
+==================
+*/
+void SV_PreSpawn_f(cmd_state_t *cmd)
+{
+       if (host_client->prespawned)
+       {
+               Con_Print("prespawn not valid -- already prespawned\n");
+               return;
+       }
+       host_client->prespawned = true;
+
+       if (host_client->netconnection)
+       {
+               SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
+               MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
+               MSG_WriteByte (&host_client->netconnection->message, 2);
+               host_client->sendsignon = 0;            // enable unlimited sends again
+       }
+
+       // reset the name change timer because the client will send name soon
+       host_client->nametime = 0;
+}
+
+/*
+==================
+SV_Spawn_f
+==================
+*/
+void SV_Spawn_f(cmd_state_t *cmd)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       int i;
+       client_t *client;
+       int stats[MAX_CL_STATS];
+
+       if (!host_client->prespawned)
+       {
+               Con_Print("Spawn not valid -- not yet prespawned\n");
+               return;
+       }
+       if (host_client->spawned)
+       {
+               Con_Print("Spawn not valid -- already spawned\n");
+               return;
+       }
+       host_client->spawned = true;
+
+       // reset name change timer again because they might want to change name
+       // again in the first 5 seconds after connecting
+       host_client->nametime = 0;
+
+       // LadyHavoc: moved this above the QC calls at FrikaC's request
+       // LadyHavoc: commented this out
+       //if (host_client->netconnection)
+       //      SZ_Clear (&host_client->netconnection->message);
+
+       // run the entrance script
+       if (sv.loadgame)
+       {
+               // loaded games are fully initialized already
+               if (PRVM_serverfunction(RestoreGame))
+               {
+                       Con_DPrint("Calling RestoreGame\n");
+                       PRVM_serverglobalfloat(time) = sv.time;
+                       PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
+                       prog->ExecuteProgram(prog, PRVM_serverfunction(RestoreGame), "QC function RestoreGame is missing");
+               }
+       }
+       else
+       {
+               //Con_Printf("SV_Spawn_f: host_client->edict->netname = %s, host_client->edict->netname = %s, host_client->name = %s\n", PRVM_GetString(PRVM_serveredictstring(host_client->edict, netname)), PRVM_GetString(PRVM_serveredictstring(host_client->edict, netname)), host_client->name);
+
+               // copy spawn parms out of the client_t
+               for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
+                       (&PRVM_serverglobalfloat(parm1))[i] = host_client->spawn_parms[i];
+
+               // call the spawn function
+               host_client->clientconnectcalled = true;
+               PRVM_serverglobalfloat(time) = sv.time;
+               PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
+               prog->ExecuteProgram(prog, PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing");
+
+               if (cls.state == ca_dedicated)
+                       Con_Printf("%s connected\n", host_client->name);
+
+               PRVM_serverglobalfloat(time) = sv.time;
+               prog->ExecuteProgram(prog, PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
+       }
+
+       if (!host_client->netconnection)
+               return;
+
+       // send time of update
+       MSG_WriteByte (&host_client->netconnection->message, svc_time);
+       MSG_WriteFloat (&host_client->netconnection->message, sv.time);
+
+       // send all current names, colors, and frag counts
+       for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
+       {
+               if (!client->active)
+                       continue;
+               MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
+               MSG_WriteByte (&host_client->netconnection->message, i);
+               MSG_WriteString (&host_client->netconnection->message, client->name);
+               MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
+               MSG_WriteByte (&host_client->netconnection->message, i);
+               MSG_WriteShort (&host_client->netconnection->message, client->frags);
+               MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
+               MSG_WriteByte (&host_client->netconnection->message, i);
+               MSG_WriteByte (&host_client->netconnection->message, client->colors);
+       }
+
+       // send all current light styles
+       for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+       {
+               if (sv.lightstyles[i][0])
+               {
+                       MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
+                       MSG_WriteByte (&host_client->netconnection->message, (char)i);
+                       MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
+               }
+       }
+
+       // send some stats
+       MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
+       MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
+       MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_secrets));
+
+       MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
+       MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
+       MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_monsters));
+
+       MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
+       MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
+       MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(found_secrets));
+
+       MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
+       MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
+       MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(killed_monsters));
+
+       // send a fixangle
+       // Never send a roll angle, because savegames can catch the server
+       // in a state where it is expecting the client to correct the angle
+       // and it won't happen if the game was just loaded, so you wind up
+       // with a permanent head tilt
+       if (sv.loadgame)
+       {
+               MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
+               MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[0], sv.protocol);
+               MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[1], sv.protocol);
+               MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
+       }
+       else
+       {
+               MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
+               MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[0], sv.protocol);
+               MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[1], sv.protocol);
+               MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
+       }
+
+       SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
+
+       MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
+       MSG_WriteByte (&host_client->netconnection->message, 3);
+}
+
+/*
+==================
+SV_Begin_f
+==================
+*/
+void SV_Begin_f(cmd_state_t *cmd)
+{
+       if (!host_client->spawned)
+       {
+               Con_Print("Begin not valid -- not yet spawned\n");
+               return;
+       }
+       if (host_client->begun)
+       {
+               Con_Print("Begin not valid -- already begun\n");
+               return;
+       }
+       host_client->begun = true;
+
+       // LadyHavoc: note: this code also exists in SV_DropClient
+       if (sv.loadgame)
+       {
+               int i;
+               for (i = 0;i < svs.maxclients;i++)
+                       if (svs.clients[i].active && !svs.clients[i].spawned)
+                               break;
+               if (i == svs.maxclients)
+               {
+                       Con_Printf("Loaded game, everyone rejoined - unpausing\n");
+                       sv.paused = sv.loadgame = false; // we're basically done with loading now
+               }
+       }
+}
+
 /*
 ===============
 SV_SetIdealPitch