]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - host_cmd.c
Fix setinfo.
[xonotic/darkplaces.git] / host_cmd.c
index 2c466d1f24063e146f68bab4c221debcf676c6b8..a62b08324be7aa6bad9b80428204e4579b7853b1 100644 (file)
@@ -19,17 +19,36 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 
 #include "quakedef.h"
 */
 
 #include "quakedef.h"
+#include "sv_demo.h"
+#include "image.h"
+
+#include "prvm_cmds.h"
+#include "utf8lib.h"
+
+// for secure rcon authentication
+#include "hmac.h"
+#include "mdfour.h"
+#include <time.h>
 
 int current_skill;
 cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
 cvar_t sv_adminnick = {CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
 
 int current_skill;
 cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
 cvar_t sv_adminnick = {CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
-cvar_t rcon_password = {CVAR_PRIVATE, "rcon_password", "", "password to authenticate rcon commands"};
+cvar_t sv_status_privacy = {CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"};
+cvar_t sv_status_show_qcstatus = {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_SAVE, "sv_namechangetimer", "5", "how often to allow name changes, in seconds (prevents people from using animated names and other tricks"};
+cvar_t rcon_password = {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_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 = {0, "rcon_secure_challengetimeout", "5", "challenge-based secure rcon: time out requests if no challenge came within this time interval"};
 cvar_t rcon_address = {0, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"};
 cvar_t team = {CVAR_USERINFO | CVAR_SAVE, "team", "none", "QW team (4 character limit, example: blue)"};
 cvar_t skin = {CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"};
 cvar_t noaim = {CVAR_USERINFO | CVAR_SAVE, "noaim", "1", "QW option to disable vertical autoaim"};
 cvar_t rcon_address = {0, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"};
 cvar_t team = {CVAR_USERINFO | CVAR_SAVE, "team", "none", "QW team (4 character limit, example: blue)"};
 cvar_t skin = {CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"};
 cvar_t noaim = {CVAR_USERINFO | CVAR_SAVE, "noaim", "1", "QW option to disable vertical autoaim"};
+cvar_t r_fixtrans_auto = {0, "r_fixtrans_auto", "0", "automatically fixtrans textures (when set to 2, it also saves the fixed versions to a fixtrans directory)"};
 qboolean allowcheats = false;
 
 qboolean allowcheats = false;
 
+extern qboolean host_shuttingdown;
+extern cvar_t developer_entityparsing;
+
 /*
 ==================
 Host_Quit_f
 /*
 ==================
 Host_Quit_f
@@ -38,20 +57,27 @@ Host_Quit_f
 
 void Host_Quit_f (void)
 {
 
 void Host_Quit_f (void)
 {
-       Sys_Quit ();
+       if(host_shuttingdown)
+               Con_Printf("shutting down already!\n");
+       else
+               Sys_Quit (0);
 }
 
 }
 
-
 /*
 ==================
 Host_Status_f
 ==================
 */
 /*
 ==================
 Host_Status_f
 ==================
 */
-void Host_Status_f (void)
+static void Host_Status_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
+       char qcstatus[256];
        client_t *client;
        client_t *client;
-       int seconds, minutes, hours = 0, j, players;
+       int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
        void (*print) (const char *fmt, ...);
        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 (cmd_source == src_command)
        {
@@ -69,31 +95,108 @@ void Host_Status_f (void)
        if (!sv.active)
                return;
 
        if (!sv.active)
                return;
 
-       for (players = 0, j = 0;j < svs.maxclients;j++)
-               if (svs.clients[j].active)
+       in = 0;
+       if (Cmd_Argc() == 2)
+       {
+               if (strcmp(Cmd_Argv(1), "1") == 0)
+                       in = 1;
+               else if (strcmp(Cmd_Argv(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 ("hostname"));
                        players++;
        print ("host:     %s\n", Cvar_VariableString ("hostname"));
-       print ("version:  %s build %s\n", gamename, buildstring);
+       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 ("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);
        print ("players:  %i active (%i max)\n\n", players, svs.maxclients);
-       for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++)
+
+       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;
        {
                if (!client->active)
                        continue;
-               seconds = (int)(realtime - client->connecttime);
-               minutes = seconds / 60;
-               if (minutes)
+
+               ++k;
+
+               if (in == 0 || in == 1)
                {
                {
-                       seconds -= (minutes * 60);
-                       hours = minutes / 60;
-                       if (hours)
-                               minutes -= (hours * 60);
+                       seconds = (int)(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
                else
-                       hours = 0;
-               print ("#%-3u %-16.16s  %3i  %2i:%02i:%02i\n", j+1, client->name, client->frags, hours, minutes, seconds);
-               print ("   %s\n", client->netconnection ? client->netconnection->address : "botclient");
+                       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)
+                       {
+                               // LordHavoc: 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
+                       {
+                               // LordHavoc: 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);
+               }
        }
 }
 
        }
 }
 
@@ -105,31 +208,33 @@ Host_God_f
 Sets client to godmode
 ==================
 */
 Sets client to godmode
 ==================
 */
-void Host_God_f (void)
+static void Host_God_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        if (!allowcheats)
        {
                SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
                return;
        }
 
        if (!allowcheats)
        {
                SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
                return;
        }
 
-       host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_GODMODE;
-       if (!((int)host_client->edict->fields.server->flags & FL_GODMODE) )
+       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");
 }
 
                SV_ClientPrint("godmode OFF\n");
        else
                SV_ClientPrint("godmode ON\n");
 }
 
-void Host_Notarget_f (void)
+static void Host_Notarget_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        if (!allowcheats)
        {
                SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
                return;
        }
 
        if (!allowcheats)
        {
                SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
                return;
        }
 
-       host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_NOTARGET;
-       if (!((int)host_client->edict->fields.server->flags & FL_NOTARGET) )
+       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_ClientPrint("notarget OFF\n");
        else
                SV_ClientPrint("notarget ON\n");
@@ -137,24 +242,25 @@ void Host_Notarget_f (void)
 
 qboolean noclip_anglehack;
 
 
 qboolean noclip_anglehack;
 
-void Host_Noclip_f (void)
+static void Host_Noclip_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        if (!allowcheats)
        {
                SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
                return;
        }
 
        if (!allowcheats)
        {
                SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
                return;
        }
 
-       if (host_client->edict->fields.server->movetype != MOVETYPE_NOCLIP)
+       if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_NOCLIP)
        {
                noclip_anglehack = true;
        {
                noclip_anglehack = true;
-               host_client->edict->fields.server->movetype = MOVETYPE_NOCLIP;
+               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_NOCLIP;
                SV_ClientPrint("noclip ON\n");
        }
        else
        {
                noclip_anglehack = false;
                SV_ClientPrint("noclip ON\n");
        }
        else
        {
                noclip_anglehack = false;
-               host_client->edict->fields.server->movetype = MOVETYPE_WALK;
+               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
                SV_ClientPrint("noclip OFF\n");
        }
 }
                SV_ClientPrint("noclip OFF\n");
        }
 }
@@ -166,22 +272,23 @@ Host_Fly_f
 Sets client to flymode
 ==================
 */
 Sets client to flymode
 ==================
 */
-void Host_Fly_f (void)
+static void Host_Fly_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        if (!allowcheats)
        {
                SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
                return;
        }
 
        if (!allowcheats)
        {
                SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
                return;
        }
 
-       if (host_client->edict->fields.server->movetype != MOVETYPE_FLY)
+       if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_FLY)
        {
        {
-               host_client->edict->fields.server->movetype = MOVETYPE_FLY;
+               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_FLY;
                SV_ClientPrint("flymode ON\n");
        }
        else
        {
                SV_ClientPrint("flymode ON\n");
        }
        else
        {
-               host_client->edict->fields.server->movetype = MOVETYPE_WALK;
+               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
                SV_ClientPrint("flymode OFF\n");
        }
 }
                SV_ClientPrint("flymode OFF\n");
        }
 }
@@ -194,7 +301,7 @@ Host_Ping_f
 ==================
 */
 void Host_Pings_f (void); // called by Host_Ping_f
 ==================
 */
 void Host_Pings_f (void); // called by Host_Ping_f
-void Host_Ping_f (void)
+static void Host_Ping_f (void)
 {
        int i;
        client_t *client;
 {
        int i;
        client_t *client;
@@ -225,7 +332,8 @@ void Host_Ping_f (void)
        }
 
        // now call the Pings command also, which will send a report that contains packet loss for the scoreboard (as well as a simpler ping report)
        }
 
        // now call the Pings command also, which will send a report that contains packet loss for the scoreboard (as well as a simpler ping report)
-       Host_Pings_f();
+       // actually, don't, it confuses old clients (resulting in "unknown command pingplreport" flooding the console)
+       //Host_Pings_f();
 }
 
 /*
 }
 
 /*
@@ -245,7 +353,7 @@ map <servername>
 command from the console.  Active clients are kicked off.
 ======================
 */
 command from the console.  Active clients are kicked off.
 ======================
 */
-void Host_Map_f (void)
+static void Host_Map_f (void)
 {
        char level[MAX_QPATH];
 
 {
        char level[MAX_QPATH];
 
@@ -255,12 +363,28 @@ void Host_Map_f (void)
                return;
        }
 
                return;
        }
 
+       // GAME_DELUXEQUAKE - clear warpmark (used by QC)
+       if (gamemode == GAME_DELUXEQUAKE)
+               Cvar_Set("warpmark", "");
+
        cls.demonum = -1;               // stop demo loop in case this fails
 
        CL_Disconnect ();
        Host_ShutdownServer();
 
        cls.demonum = -1;               // stop demo loop in case this fails
 
        CL_Disconnect ();
        Host_ShutdownServer();
 
+       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
        // 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
        key_dest = key_game;
 
        svs.serverflags = 0;                    // haven't completed an episode yet
@@ -268,30 +392,7 @@ void Host_Map_f (void)
        strlcpy(level, Cmd_Argv(1), sizeof(level));
        SV_SpawnServer(level);
        if (sv.active && cls.state == ca_disconnected)
        strlcpy(level, Cmd_Argv(1), sizeof(level));
        SV_SpawnServer(level);
        if (sv.active && cls.state == ca_disconnected)
-               CL_EstablishConnection("local:1");
-
-#ifdef AUTODEMO_BROKEN
-// if cl_autodemo is set, automatically start recording a demo if one isn't being recorded already
-       if (cl_autodemo.integer && !cls.demorecording)
-       {
-               char demofile[MAX_OSPATH];
-
-               dpsnprintf (demofile, sizeof(demofile), "%s_%s.dem", Sys_TimeString (cl_autodemo_nameformat.string), level);
-
-               Con_Printf ("Recording to %s.\n", demofile);
-
-               cls.demofile = FS_Open (demofile, "wb", false, false);
-               if (cls.demofile)
-               {
-                       cls.forcetrack = -1;
-                       FS_Printf (cls.demofile, "%i\n", cls.forcetrack);
-               }
-               else
-                       Con_Print ("ERROR: couldn't open.\n");
-
-               cls.demorecording = true;
-       }
-#endif
+               CL_EstablishConnection("local:1", -2);
 }
 
 /*
 }
 
 /*
@@ -301,7 +402,7 @@ Host_Changelevel_f
 Goes to a new map, taking all clients along
 ==================
 */
 Goes to a new map, taking all clients along
 ==================
 */
-void Host_Changelevel_f (void)
+static void Host_Changelevel_f (void)
 {
        char level[MAX_QPATH];
 
 {
        char level[MAX_QPATH];
 
@@ -316,17 +417,19 @@ void Host_Changelevel_f (void)
                return;
        }
 
                return;
        }
 
+#ifdef CONFIG_MENU
        // remove menu
        // remove menu
+       if (key_dest == key_menu || key_dest == key_menu_grabbed)
+               MR_ToggleMenu(0);
+#endif
        key_dest = key_game;
 
        key_dest = key_game;
 
-       SV_VM_Begin();
        SV_SaveSpawnparms ();
        SV_SaveSpawnparms ();
-       SV_VM_End();
        allowcheats = sv_cheats.integer != 0;
        strlcpy(level, Cmd_Argv(1), sizeof(level));
        SV_SpawnServer(level);
        if (sv.active && cls.state == ca_disconnected)
        allowcheats = sv_cheats.integer != 0;
        strlcpy(level, Cmd_Argv(1), sizeof(level));
        SV_SpawnServer(level);
        if (sv.active && cls.state == ca_disconnected)
-               CL_EstablishConnection("local:1");
+               CL_EstablishConnection("local:1", -2);
 }
 
 /*
 }
 
 /*
@@ -336,7 +439,7 @@ Host_Restart_f
 Restarts the current server for a dead player
 ==================
 */
 Restarts the current server for a dead player
 ==================
 */
-void Host_Restart_f (void)
+static void Host_Restart_f (void)
 {
        char mapname[MAX_QPATH];
 
 {
        char mapname[MAX_QPATH];
 
@@ -351,14 +454,18 @@ void Host_Restart_f (void)
                return;
        }
 
                return;
        }
 
+#ifdef CONFIG_MENU
        // remove menu
        // remove menu
+       if (key_dest == key_menu || key_dest == key_menu_grabbed)
+               MR_ToggleMenu(0);
+#endif
        key_dest = key_game;
 
        allowcheats = sv_cheats.integer != 0;
        strlcpy(mapname, sv.name, sizeof(mapname));
        SV_SpawnServer(mapname);
        if (sv.active && cls.state == ca_disconnected)
        key_dest = key_game;
 
        allowcheats = sv_cheats.integer != 0;
        strlcpy(mapname, sv.name, sizeof(mapname));
        SV_SpawnServer(mapname);
        if (sv.active && cls.state == ca_disconnected)
-               CL_EstablishConnection("local:1");
+               CL_EstablishConnection("local:1", -2);
 }
 
 /*
 }
 
 /*
@@ -379,7 +486,7 @@ void Host_Reconnect_f (void)
                // will still contain its IP address, so get the address...
                InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
                if (temp[0])
                // will still contain its IP address, so get the address...
                InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
                if (temp[0])
-                       CL_EstablishConnection(temp);
+                       CL_EstablishConnection(temp, -1);
                else
                        Con_Printf("Reconnect to what server?  (you have not connected to a server yet)\n");
                return;
                else
                        Con_Printf("Reconnect to what server?  (you have not connected to a server yet)\n");
                return;
@@ -424,14 +531,17 @@ Host_Connect_f
 User command to connect to server
 =====================
 */
 User command to connect to server
 =====================
 */
-void Host_Connect_f (void)
+static void Host_Connect_f (void)
 {
 {
-       if (Cmd_Argc() != 2)
+       if (Cmd_Argc() < 2)
        {
        {
-               Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
+               Con_Print("connect <serveraddress> [<key> <value> ...]: connect to a multiplayer game\n");
                return;
        }
                return;
        }
-       CL_EstablishConnection(Cmd_Argv(1));
+       // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
+       if(rcon_secure.integer <= 0)
+               Cvar_SetQuick(&rcon_password, "");
+       CL_EstablishConnection(Cmd_Argv(1), 2);
 }
 
 
 }
 
 
@@ -445,73 +555,187 @@ LOAD / SAVE GAME
 
 #define        SAVEGAME_VERSION        5
 
 
 #define        SAVEGAME_VERSION        5
 
-/*
-===============
-Host_SavegameComment
-
-Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current
-===============
-*/
-void Host_SavegameComment (char *text)
+void Host_Savegame_to(prvm_prog_t *prog, const char *name)
 {
 {
-       int             i;
-       char    kills[20];
+       qfile_t *f;
+       int             i, k, l, numbuffers, lightstyles = 64;
+       char    comment[SAVEGAME_COMMENT_LENGTH+1];
+       char    line[MAX_INPUTLINE];
+       qboolean isserver;
+       char    *s;
 
 
-       for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
-               text[i] = ' ';
-       // LordHavoc: added min() to prevent overflow
-       memcpy (text, cl.levelname, min(strlen(cl.levelname), SAVEGAME_COMMENT_LENGTH));
-       sprintf (kills,"kills:%3i/%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
-       memcpy (text+22, kills, strlen(kills));
+       // 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
        // LordHavoc: convert control characters to _ as well
        for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
        // convert space to _ to make stdio happy
        // LordHavoc: convert control characters to _ as well
        for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
-               if (text[i] <= ' ')
-                       text[i] = '_';
-       text[SAVEGAME_COMMENT_LENGTH] = '\0';
-}
+               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", 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");
+}
 
 /*
 ===============
 Host_Savegame_f
 ===============
 */
 
 /*
 ===============
 Host_Savegame_f
 ===============
 */
-void Host_Savegame_f (void)
+static void Host_Savegame_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        char    name[MAX_QPATH];
        char    name[MAX_QPATH];
-       qfile_t *f;
-       int             i;
-       char    comment[SAVEGAME_COMMENT_LENGTH+1];
+       qboolean deadflag = false;
 
 
-       if (cls.state != ca_connected || !sv.active)
+       if (!sv.active)
        {
        {
-               Con_Print("Not playing a local game.\n");
+               Con_Print("Can't save - no server running.\n");
                return;
        }
 
                return;
        }
 
-       if (cl.intermission)
-       {
-               Con_Print("Can't save in intermission.\n");
-               return;
-       }
+       deadflag = cl.islocalgame && svs.clients[0].active && PRVM_serveredictfloat(svs.clients[0].edict, deadflag);
 
 
-       for (i = 0;i < svs.maxclients;i++)
+       if (cl.islocalgame)
        {
        {
-               if (svs.clients[i].active)
+               // singleplayer checks
+               if (cl.intermission)
                {
                {
-                       if (i > 0)
-                       {
-                               Con_Print("Can't save multiplayer games.\n");
-                               return;
-                       }
-                       if (svs.clients[i].edict->fields.server->deadflag)
-                       {
-                               Con_Print("Can't savegame with a dead player\n");
-                               return;
-                       }
+                       Con_Print("Can't save in intermission.\n");
+                       return;
+               }
+
+               if (deadflag)
+               {
+                       Con_Print("Can't savegame with a dead player\n");
+                       return;
                }
        }
                }
        }
+       else
+               Con_Print("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() != 2)
        {
 
        if (Cmd_Argc() != 2)
        {
@@ -528,42 +752,7 @@ void Host_Savegame_f (void)
        strlcpy (name, Cmd_Argv(1), sizeof (name));
        FS_DefaultExtension (name, ".sav", sizeof (name));
 
        strlcpy (name, Cmd_Argv(1), sizeof (name));
        FS_DefaultExtension (name, ".sav", sizeof (name));
 
-       Con_Printf("Saving game to %s...\n", name);
-       f = FS_Open (name, "wb", false, false);
-       if (!f)
-       {
-               Con_Print("ERROR: couldn't open.\n");
-               return;
-       }
-
-       FS_Printf(f, "%i\n", SAVEGAME_VERSION);
-       Host_SavegameComment (comment);
-       FS_Printf(f, "%s\n", comment);
-       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);
-
-       // write the light styles
-       for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
-       {
-               if (sv.lightstyles[i][0])
-                       FS_Printf(f, "%s\n", sv.lightstyles[i]);
-               else
-                       FS_Print(f,"m\n");
-       }
-
-       SV_VM_Begin();
-
-       PRVM_ED_WriteGlobals (f);
-       for (i=0 ; i<prog->num_edicts ; i++)
-               PRVM_ED_Write (f, PRVM_EDICT_NUM(i));
-
-       SV_VM_End();
-
-       FS_Close (f);
-       Con_Print("done.\n");
+       Host_Savegame_to(prog, name);
 }
 
 
 }
 
 
@@ -572,20 +761,23 @@ void Host_Savegame_f (void)
 Host_Loadgame_f
 ===============
 */
 Host_Loadgame_f
 ===============
 */
-void Host_Loadgame_f (void)
+
+static void Host_Loadgame_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        char filename[MAX_QPATH];
        char mapname[MAX_QPATH];
        float time;
        const char *start;
        char filename[MAX_QPATH];
        char mapname[MAX_QPATH];
        float time;
        const char *start;
+       const char *end;
        const char *t;
        const char *t;
-       const char *oldt;
        char *text;
        prvm_edict_t *ent;
        char *text;
        prvm_edict_t *ent;
-       int i;
+       int i, k, numbuffers;
        int entnum;
        int version;
        float spawn_parms[NUM_SPAWN_PARMS];
        int entnum;
        int version;
        float spawn_parms[NUM_SPAWN_PARMS];
+       prvm_stringbuffer_t *stringbuffer;
 
        if (Cmd_Argc() != 2)
        {
 
        if (Cmd_Argc() != 2)
        {
@@ -598,6 +790,17 @@ void Host_Loadgame_f (void)
 
        Con_Printf("Loading game from %s...\n", 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);
        cls.demonum = -1;               // stop demo loop in case this fails
 
        t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
@@ -607,8 +810,11 @@ void Host_Loadgame_f (void)
                return;
        }
 
                return;
        }
 
+       if(developer_entityparsing.integer)
+               Con_Printf("Host_Loadgame_f: loading version\n");
+
        // version
        // version
-       COM_ParseTokenConsole(&t);
+       COM_ParseToken_Simple(&t, false, false, true);
        version = atoi(com_token);
        if (version != SAVEGAME_VERSION)
        {
        version = atoi(com_token);
        if (version != SAVEGAME_VERSION)
        {
@@ -617,32 +823,42 @@ void Host_Loadgame_f (void)
                return;
        }
 
                return;
        }
 
+       if(developer_entityparsing.integer)
+               Con_Printf("Host_Loadgame_f: loading description\n");
+
        // description
        // description
-       // this is a little hard to parse, as : is a separator in COM_ParseToken,
-       // so use the console parser instead
-       COM_ParseTokenConsole(&t);
+       COM_ParseToken_Simple(&t, false, false, true);
 
        for (i = 0;i < NUM_SPAWN_PARMS;i++)
        {
 
        for (i = 0;i < NUM_SPAWN_PARMS;i++)
        {
-               COM_ParseTokenConsole(&t);
+               COM_ParseToken_Simple(&t, false, false, true);
                spawn_parms[i] = atof(com_token);
        }
        // skill
                spawn_parms[i] = atof(com_token);
        }
        // skill
-       COM_ParseTokenConsole(&t);
+       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 ("skill", (float)current_skill);
 
 // 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 ("skill", (float)current_skill);
 
+       if(developer_entityparsing.integer)
+               Con_Printf("Host_Loadgame_f: loading mapname\n");
+
        // mapname
        // mapname
-       COM_ParseTokenConsole(&t);
+       COM_ParseToken_Simple(&t, false, false, true);
        strlcpy (mapname, com_token, sizeof(mapname));
 
        strlcpy (mapname, com_token, sizeof(mapname));
 
+       if(developer_entityparsing.integer)
+               Con_Printf("Host_Loadgame_f: loading time\n");
+
        // time
        // time
-       COM_ParseTokenConsole(&t);
+       COM_ParseToken_Simple(&t, false, false, true);
        time = atof(com_token);
 
        allowcheats = sv_cheats.integer != 0;
 
        time = atof(com_token);
 
        allowcheats = sv_cheats.integer != 0;
 
+       if(developer_entityparsing.integer)
+               Con_Printf("Host_Loadgame_f: spawning server\n");
+
        SV_SpawnServer (mapname);
        if (!sv.active)
        {
        SV_SpawnServer (mapname);
        if (!sv.active)
        {
@@ -653,49 +869,60 @@ void Host_Loadgame_f (void)
        sv.paused = true;               // pause until all clients connect
        sv.loadgame = true;
 
        sv.paused = true;               // pause until all clients connect
        sv.loadgame = true;
 
+       if(developer_entityparsing.integer)
+               Con_Printf("Host_Loadgame_f: loading light styles\n");
+
 // load the light styles
 
 // load the light styles
 
+       // -1 is the globals
+       entnum = -1;
+
        for (i = 0;i < MAX_LIGHTSTYLES;i++)
        {
                // light style
        for (i = 0;i < MAX_LIGHTSTYLES;i++)
        {
                // light style
-               oldt = t;
-               COM_ParseTokenConsole(&t);
+               start = t;
+               COM_ParseToken_Simple(&t, false, false, true);
                // if this is a 64 lightstyle savegame produced by Quake, stop now
                // if this is a 64 lightstyle savegame produced by Quake, stop now
-               // we have to check this because darkplaces saves 256 lightstyle savegames
+               // we have to check this because darkplaces may save more than 64
                if (com_token[0] == '{')
                {
                if (com_token[0] == '{')
                {
-                       t = oldt;
+                       t = start;
                        break;
                }
                strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
        }
 
                        break;
                }
                strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
        }
 
+       if(developer_entityparsing.integer)
+               Con_Printf("Host_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)
        // 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(;;)
+       for (;;)
        {
        {
-               oldt = t;
-               COM_ParseTokenConsole(&t);
+               start = t;
+               if (!COM_ParseToken_Simple(&t, false, false, true))
+                       break;
                if (com_token[0] == '{')
                {
                if (com_token[0] == '{')
                {
-                       t = oldt;
+                       t = start;
                        break;
                }
        }
 
                        break;
                }
        }
 
+       // unlink all entities
+       World_UnlinkAll(&sv.world);
+
 // load the edicts out of the savegame file
 // load the edicts out of the savegame file
-       SV_VM_Begin();
-       // -1 is the globals
-       entnum = -1;
+       end = t;
        for (;;)
        {
                start = t;
        for (;;)
        {
                start = t;
-               while (COM_ParseTokenConsole(&t))
+               while (COM_ParseToken_Simple(&t, false, false, true))
                        if (!strcmp(com_token, "}"))
                                break;
                        if (!strcmp(com_token, "}"))
                                break;
-               if (!COM_ParseTokenConsole(&start))
+               if (!COM_ParseToken_Simple(&start, false, false, true))
                {
                        // end of file
                        break;
                {
                        // end of file
                        break;
@@ -708,8 +935,14 @@ void Host_Loadgame_f (void)
 
                if (entnum == -1)
                {
 
                if (entnum == -1)
                {
+                       if(developer_entityparsing.integer)
+                               Con_Printf("Host_Loadgame_f: loading globals\n");
+
                        // parse the global vars
                        // parse the global vars
-                       PRVM_ED_ParseGlobals (start);
+                       PRVM_ED_ParseGlobals (prog, start);
+
+                       // restore the autocvar globals
+                       Cvar_UpdateAllAutoCvars();
                }
                else
                {
                }
                else
                {
@@ -720,21 +953,24 @@ void Host_Loadgame_f (void)
                                Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
                        }
                        while (entnum >= prog->max_edicts)
                                Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
                        }
                        while (entnum >= prog->max_edicts)
-                               //SV_IncreaseEdicts();
-                               PRVM_MEM_IncreaseEdicts();
+                               PRVM_MEM_IncreaseEdicts(prog);
                        ent = PRVM_EDICT_NUM(entnum);
                        ent = PRVM_EDICT_NUM(entnum);
-                       memset (ent->fields.server, 0, prog->progs->entityfields * 4);
+                       memset(ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
                        ent->priv.server->free = false;
                        ent->priv.server->free = false;
-                       PRVM_ED_ParseEdict (start, ent);
+
+                       if(developer_entityparsing.integer)
+                               Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
+
+                       PRVM_ED_ParseEdict (prog, start, ent);
 
                        // link it into the bsp tree
                        if (!ent->priv.server->free)
 
                        // link it into the bsp tree
                        if (!ent->priv.server->free)
-                               SV_LinkEdict (ent, false);
+                               SV_LinkEdict(ent);
                }
 
                }
 
+               end = t;
                entnum++;
        }
                entnum++;
        }
-       Mem_Free(text);
 
        prog->num_edicts = entnum;
        sv.time = time;
 
        prog->num_edicts = entnum;
        sv.time = time;
@@ -742,11 +978,130 @@ void Host_Loadgame_f (void)
        for (i = 0;i < NUM_SPAWN_PARMS;i++)
                svs.clients[0].spawn_parms[i] = spawn_parms[i];
 
        for (i = 0;i < NUM_SPAWN_PARMS;i++)
                svs.clients[0].spawn_parms[i] = spawn_parms[i];
 
-       SV_VM_End();
+       if(developer_entityparsing.integer)
+               Con_Printf("Host_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("Host_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_Printf("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_Printf("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("Host_Loadgame_f: finished\n");
 
        // make sure we're connected to loopback
 
        // make sure we're connected to loopback
-       if (cls.state == ca_disconnected || !(cls.state == ca_connected && cls.netcon != NULL && LHNETADDRESS_GetAddressType(&cls.netcon->peeraddress) == LHNETADDRESSTYPE_LOOP))
-               CL_EstablishConnection("local:1");
+       if (sv.active && cls.state == ca_disconnected)
+               CL_EstablishConnection("local:1", -2);
 }
 
 //============================================================================
 }
 
 //============================================================================
@@ -757,54 +1112,128 @@ Host_Name_f
 ======================
 */
 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
 ======================
 */
 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
-void Host_Name_f (void)
+static void Host_Name_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        int i, j;
        int i, j;
+       qboolean valid_colors;
+       const char *newNameSource;
        char newName[sizeof(host_client->name)];
 
        if (Cmd_Argc () == 1)
        {
        char newName[sizeof(host_client->name)];
 
        if (Cmd_Argc () == 1)
        {
-               Con_Printf("\"name\" is \"%s\"\n", cl_name.string);
+               if (cmd_source == src_command)
+               {
+                       Con_Printf("name: %s\n", cl_name.string);
+               }
                return;
        }
 
        if (Cmd_Argc () == 2)
                return;
        }
 
        if (Cmd_Argc () == 2)
-               strlcpy (newName, Cmd_Argv(1), sizeof (newName));
+               newNameSource = Cmd_Argv(1);
        else
        else
-               strlcpy (newName, Cmd_Args(), sizeof (newName));
+               newNameSource = Cmd_Args();
 
 
-       for (i = 0, j = 0;newName[i];i++)
-               if (newName[i] != '\r' && newName[i] != '\n')
-                       newName[j++] = newName[i];
-       newName[j] = 0;
+       strlcpy(newName, newNameSource, sizeof(newName));
 
        if (cmd_source == src_command)
        {
                Cvar_Set ("_cl_name", newName);
 
        if (cmd_source == src_command)
        {
                Cvar_Set ("_cl_name", newName);
-               CL_SetInfo("name", newName, true, false, false, false);
+               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;
        }
 
                return;
        }
 
-       if (sv.time < host_client->nametime)
+       if (realtime < host_client->nametime)
        {
        {
-               SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
+               SV_ClientPrintf("You can't change name more than once every %.1f seconds!\n", max(0.0f, sv_namechangetimer.value));
                return;
        }
 
                return;
        }
 
-       host_client->nametime = sv.time + 5;
+       host_client->nametime = 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));
 
        // point the string back at updateclient->name to keep it safe
        strlcpy (host_client->name, newName, sizeof (host_client->name));
-       host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
+
+       for (i = 0, j = 0;host_client->name[i];i++)
+               if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
+                       host_client->name[j++] = host_client->name[i];
+       host_client->name[j] = 0;
+
+       if(host_client->name[0] == 1 || host_client->name[0] == 2)
+       // may interfere with chat area, and will needlessly beep; so let's add a ^7
+       {
+               memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
+               host_client->name[sizeof(host_client->name) - 1] = 0;
+               host_client->name[0] = STRING_COLOR_TAG;
+               host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
+       }
+
+       u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
+       if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
+       {
+               size_t l;
+               l = strlen(host_client->name);
+               if(l < sizeof(host_client->name) - 1)
+               {
+                       // duplicate the color tag to escape it
+                       host_client->name[i] = STRING_COLOR_TAG;
+                       host_client->name[i+1] = 0;
+                       //Con_DPrintf("abuse detected, adding another trailing color tag\n");
+               }
+               else
+               {
+                       // remove the last character to fix the color code
+                       host_client->name[l-1] = 0;
+                       //Con_DPrintf("abuse detected, removing a trailing color tag\n");
+               }
+       }
+
+       // find the last color tag offset and decide if we need to add a reset tag
+       for (i = 0, j = -1;host_client->name[i];i++)
+       {
+               if (host_client->name[i] == STRING_COLOR_TAG)
+               {
+                       if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
+                       {
+                               j = i;
+                               // if this happens to be a reset  tag then we don't need one
+                               if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
+                                       j = -1;
+                               i++;
+                               continue;
+                       }
+                       if (host_client->name[i+1] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(host_client->name[i+2]) && isxdigit(host_client->name[i+3]) && isxdigit(host_client->name[i+4]))
+                       {
+                               j = i;
+                               i += 4;
+                               continue;
+                       }
+                       if (host_client->name[i+1] == STRING_COLOR_TAG)
+                       {
+                               i++;
+                               continue;
+                       }
+               }
+       }
+       // does not end in the default color string, so add it
+       if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
+               memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
+
+       PRVM_serveredictstring(host_client->edict, netname) = PRVM_SetEngineString(prog, host_client->name);
        if (strcmp(host_client->old_name, host_client->name))
        {
        if (strcmp(host_client->old_name, host_client->name))
        {
-               if (host_client->spawned)
-                       SV_BroadcastPrintf("%s changed name to %s\n", host_client->old_name, host_client->name);
+               if (host_client->begun)
+                       SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
                strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
                // send notification to all clients
                MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
                MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
                MSG_WriteString (&sv.reliable_datagram, host_client->name);
                strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
                // send notification to all clients
                MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
                MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
                MSG_WriteString (&sv.reliable_datagram, host_client->name);
+               SV_WriteNetnameIntoDemo(host_client);
        }
 }
 
        }
 }
 
@@ -813,16 +1242,20 @@ void Host_Name_f (void)
 Host_Playermodel_f
 ======================
 */
 Host_Playermodel_f
 ======================
 */
-cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
+cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz/Xonotic (changed by playermodel command)"};
 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
-void Host_Playermodel_f (void)
+static void Host_Playermodel_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        int i, j;
        char newPath[sizeof(host_client->playermodel)];
 
        if (Cmd_Argc () == 1)
        {
        int i, j;
        char newPath[sizeof(host_client->playermodel)];
 
        if (Cmd_Argc () == 1)
        {
-               Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
+               if (cmd_source == src_command)
+               {
+                       Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
+               }
                return;
        }
 
                return;
        }
 
@@ -839,24 +1272,22 @@ void Host_Playermodel_f (void)
        if (cmd_source == src_command)
        {
                Cvar_Set ("_cl_playermodel", newPath);
        if (cmd_source == src_command)
        {
                Cvar_Set ("_cl_playermodel", newPath);
-               CL_SetInfo("playermodel", newPath, true, false, false, false);
                return;
        }
 
        /*
                return;
        }
 
        /*
-       if (sv.time < host_client->nametime)
+       if (realtime < host_client->nametime)
        {
                SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
                return;
        }
 
        {
                SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
                return;
        }
 
-       host_client->nametime = sv.time + 5;
+       host_client->nametime = realtime + 5;
        */
 
        // point the string back at updateclient->name to keep it safe
        strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
        */
 
        // point the string back at updateclient->name to keep it safe
        strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
-       if( prog->fieldoffsets.playermodel >= 0 )
-               PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
+       PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(prog, host_client->playermodel);
        if (strcmp(host_client->old_model, host_client->playermodel))
        {
                strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
        if (strcmp(host_client->old_model, host_client->playermodel))
        {
                strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
@@ -872,15 +1303,19 @@ void Host_Playermodel_f (void)
 Host_Playerskin_f
 ======================
 */
 Host_Playerskin_f
 ======================
 */
-cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
-void Host_Playerskin_f (void)
+cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz/Xonotic (changed by playerskin command)"};
+static void Host_Playerskin_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        int i, j;
        char newPath[sizeof(host_client->playerskin)];
 
        if (Cmd_Argc () == 1)
        {
        int i, j;
        char newPath[sizeof(host_client->playerskin)];
 
        if (Cmd_Argc () == 1)
        {
-               Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
+               if (cmd_source == src_command)
+               {
+                       Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
+               }
                return;
        }
 
                return;
        }
 
@@ -897,27 +1332,25 @@ void Host_Playerskin_f (void)
        if (cmd_source == src_command)
        {
                Cvar_Set ("_cl_playerskin", newPath);
        if (cmd_source == src_command)
        {
                Cvar_Set ("_cl_playerskin", newPath);
-               CL_SetInfo("playerskin", newPath, true, false, false, false);
                return;
        }
 
        /*
                return;
        }
 
        /*
-       if (sv.time < host_client->nametime)
+       if (realtime < host_client->nametime)
        {
                SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
                return;
        }
 
        {
                SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
                return;
        }
 
-       host_client->nametime = sv.time + 5;
+       host_client->nametime = realtime + 5;
        */
 
        // point the string back at updateclient->name to keep it safe
        strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
        */
 
        // point the string back at updateclient->name to keep it safe
        strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
-       if( prog->fieldoffsets.playerskin >= 0 )
-               PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
+       PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(prog, host_client->playerskin);
        if (strcmp(host_client->old_skin, host_client->playerskin))
        {
        if (strcmp(host_client->old_skin, host_client->playerskin))
        {
-               //if (host_client->spawned)
+               //if (host_client->begun)
                //      SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
                strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
                /*// send notification to all clients
                //      SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
                strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
                /*// send notification to all clients
@@ -927,13 +1360,14 @@ void Host_Playerskin_f (void)
        }
 }
 
        }
 }
 
-void Host_Version_f (void)
+static void Host_Version_f (void)
 {
        Con_Printf("Version: %s build %s\n", gamename, buildstring);
 }
 
 {
        Con_Printf("Version: %s build %s\n", gamename, buildstring);
 }
 
-void Host_Say(qboolean teamonly)
+static void Host_Say(qboolean teamonly)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        client_t *save;
        int j, quoted;
        const char *p1;
        client_t *save;
        int j, quoted;
        const char *p1;
@@ -970,12 +1404,14 @@ void Host_Say(qboolean teamonly)
                p1++;
        }
        // note this uses the chat prefix \001
                p1++;
        }
        // note this uses the chat prefix \001
-       if (!fromServer)
-               dpsnprintf (text, sizeof(text), "\001%s" STRING_COLOR_DEFAULT_STR ": %s", host_client->name, p1);
+       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))
        else if(*(sv_adminnick.string))
-               dpsnprintf (text, sizeof(text), "\001<%s" STRING_COLOR_DEFAULT_STR "> %s", sv_adminnick.string, p1);
+               dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
        else
        else
-               dpsnprintf (text, sizeof(text), "\001<%s" STRING_COLOR_DEFAULT_STR "> %s", hostname.string, p1);
+               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)))
        {
        p2 = text + strlen(text);
        while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
        {
@@ -989,7 +1425,7 @@ void Host_Say(qboolean teamonly)
        // 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++)
        // 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 || host_client->edict->fields.server->team == save->edict->fields.server->team))
+               if (host_client->active && (!teamonly || PRVM_serveredictfloat(host_client->edict, team) == PRVM_serveredictfloat(save->edict, team)))
                        SV_ClientPrint(text);
        host_client = save;
 
                        SV_ClientPrint(text);
        host_client = save;
 
@@ -998,19 +1434,19 @@ void Host_Say(qboolean teamonly)
 }
 
 
 }
 
 
-void Host_Say_f(void)
+static void Host_Say_f(void)
 {
        Host_Say(false);
 }
 
 
 {
        Host_Say(false);
 }
 
 
-void Host_Say_Team_f(void)
+static void Host_Say_Team_f(void)
 {
        Host_Say(true);
 }
 
 
 {
        Host_Say(true);
 }
 
 
-void Host_Tell_f(void)
+static void Host_Tell_f(void)
 {
        const char *playername_start = NULL;
        size_t playername_length = 0;
 {
        const char *playername_start = NULL;
        size_t playername_length = 0;
@@ -1037,11 +1473,11 @@ void Host_Tell_f(void)
 
        // note this uses the chat prefix \001
        if (!fromServer)
 
        // note this uses the chat prefix \001
        if (!fromServer)
-               sprintf (text, "\001%s tells you: ", host_client->name);
+               dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
        else if(*(sv_adminnick.string))
        else if(*(sv_adminnick.string))
-               sprintf (text, "\001<%s tells you> ", sv_adminnick.string);
+               dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
        else
        else
-               sprintf (text, "\001<%s tells you> ", hostname.string);
+               dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
 
        p1 = Cmd_Args();
        p2 = p1 + strlen(p1);
 
        p1 = Cmd_Args();
        p2 = p1 + strlen(p1);
@@ -1145,8 +1581,9 @@ Host_Color_f
 ==================
 */
 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
 ==================
 */
 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
-void Host_Color(int changetop, int changebottom)
+static void Host_Color(int changetop, int changebottom)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        int top, bottom, playercolor;
 
        // get top and bottom either from the provided values or the current values
        int top, bottom, playercolor;
 
        // get top and bottom either from the provided values or the current values
@@ -1167,37 +1604,26 @@ void Host_Color(int changetop, int changebottom)
        if (cmd_source == src_command)
        {
                Cvar_SetValueQuick(&cl_color, playercolor);
        if (cmd_source == src_command)
        {
                Cvar_SetValueQuick(&cl_color, playercolor);
-               if (changetop >= 0)
-                       CL_SetInfo("topcolor", va("%i", top), true, false, false, false);
-               if (changebottom >= 0)
-                       CL_SetInfo("bottomcolor", va("%i", bottom), true, false, false, false);
-               if (cls.protocol != PROTOCOL_QUAKEWORLD && cls.netcon)
-               {
-                       MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
-                       MSG_WriteString(&cls.netcon->message, va("color %i %i", top, bottom));
-               }
                return;
        }
 
        if (cls.protocol == PROTOCOL_QUAKEWORLD)
                return;
 
                return;
        }
 
        if (cls.protocol == PROTOCOL_QUAKEWORLD)
                return;
 
-       if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
+       if (host_client->edict && PRVM_serverfunction(SV_ChangeTeam))
        {
                Con_DPrint("Calling SV_ChangeTeam\n");
        {
                Con_DPrint("Calling SV_ChangeTeam\n");
-               prog->globals.server->time = sv.time;
-               prog->globals.generic[OFS_PARM0] = playercolor;
-               prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
-               PRVM_ExecuteProgram(prog->funcoffsets.SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
+               prog->globals.fp[OFS_PARM0] = playercolor;
+               PRVM_serverglobalfloat(time) = sv.time;
+               PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
+               prog->ExecuteProgram(prog, PRVM_serverfunction(SV_ChangeTeam), "QC function SV_ChangeTeam is missing");
        }
        else
        {
        }
        else
        {
-               prvm_eval_t *val;
                if (host_client->edict)
                {
                if (host_client->edict)
                {
-                       if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
-                               val->_float = playercolor;
-                       host_client->edict->fields.server->team = bottom + 1;
+                       PRVM_serveredictfloat(host_client->edict, clientcolors) = playercolor;
+                       PRVM_serveredictfloat(host_client->edict, team) = bottom + 1;
                }
                host_client->colors = playercolor;
                if (host_client->old_colors != host_client->colors)
                }
                host_client->colors = playercolor;
                if (host_client->old_colors != host_client->colors)
@@ -1211,14 +1637,17 @@ void Host_Color(int changetop, int changebottom)
        }
 }
 
        }
 }
 
-void Host_Color_f(void)
+static void Host_Color_f(void)
 {
        int             top, bottom;
 
        if (Cmd_Argc() == 1)
        {
 {
        int             top, bottom;
 
        if (Cmd_Argc() == 1)
        {
-               Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
-               Con_Print("color <0-15> [0-15]\n");
+               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;
        }
 
                return;
        }
 
@@ -1232,39 +1661,49 @@ void Host_Color_f(void)
        Host_Color(top, bottom);
 }
 
        Host_Color(top, bottom);
 }
 
-void Host_TopColor_f(void)
+static void Host_TopColor_f(void)
 {
        if (Cmd_Argc() == 1)
        {
 {
        if (Cmd_Argc() == 1)
        {
-               Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
-               Con_Print("topcolor <0-15>\n");
+               if (cmd_source == src_command)
+               {
+                       Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
+                       Con_Print("topcolor <0-15>\n");
+               }
                return;
        }
 
        Host_Color(atoi(Cmd_Argv(1)), -1);
 }
 
                return;
        }
 
        Host_Color(atoi(Cmd_Argv(1)), -1);
 }
 
-void Host_BottomColor_f(void)
+static void Host_BottomColor_f(void)
 {
        if (Cmd_Argc() == 1)
        {
 {
        if (Cmd_Argc() == 1)
        {
-               Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
-               Con_Print("bottomcolor <0-15>\n");
+               if (cmd_source == src_command)
+               {
+                       Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
+                       Con_Print("bottomcolor <0-15>\n");
+               }
                return;
        }
 
        Host_Color(-1, atoi(Cmd_Argv(1)));
 }
 
                return;
        }
 
        Host_Color(-1, atoi(Cmd_Argv(1)));
 }
 
-cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "10000", "internal storage cvar for current rate (changed by rate command)"};
-void Host_Rate_f(void)
+cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
+cvar_t cl_rate_burstsize = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate_burstsize", "1024", "internal storage cvar for current rate control burst size (changed by rate_burstsize command)"};
+static void Host_Rate_f(void)
 {
        int rate;
 
        if (Cmd_Argc() != 2)
        {
 {
        int rate;
 
        if (Cmd_Argc() != 2)
        {
-               Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
-               Con_Print("rate <bytespersecond>\n");
+               if (cmd_source == src_command)
+               {
+                       Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
+                       Con_Print("rate <bytespersecond>\n");
+               }
                return;
        }
 
                return;
        }
 
@@ -1273,29 +1712,50 @@ void Host_Rate_f(void)
        if (cmd_source == src_command)
        {
                Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
        if (cmd_source == src_command)
        {
                Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
-               CL_SetInfo("rate", va("%i", rate), true, false, false, false);
                return;
        }
 
        host_client->rate = rate;
 }
                return;
        }
 
        host_client->rate = rate;
 }
+static void Host_Rate_BurstSize_f(void)
+{
+       int rate_burstsize;
+
+       if (Cmd_Argc() != 2)
+       {
+               Con_Printf("\"rate_burstsize\" is \"%i\"\n", cl_rate_burstsize.integer);
+               Con_Print("rate_burstsize <bytes>\n");
+               return;
+       }
+
+       rate_burstsize = atoi(Cmd_Argv(1));
+
+       if (cmd_source == src_command)
+       {
+               Cvar_SetValue ("_cl_rate_burstsize", rate_burstsize);
+               return;
+       }
+
+       host_client->rate_burstsize = rate_burstsize;
+}
 
 /*
 ==================
 Host_Kill_f
 ==================
 */
 
 /*
 ==================
 Host_Kill_f
 ==================
 */
-void Host_Kill_f (void)
+static void Host_Kill_f (void)
 {
 {
-       if (host_client->edict->fields.server->health <= 0)
+       prvm_prog_t *prog = SVVM_prog;
+       if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
        {
                SV_ClientPrint("Can't suicide -- already dead!\n");
                return;
        }
 
        {
                SV_ClientPrint("Can't suicide -- already dead!\n");
                return;
        }
 
-       prog->globals.server->time = sv.time;
-       prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
-       PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
+       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");
 }
 
 
 }
 
 
@@ -1304,18 +1764,44 @@ void Host_Kill_f (void)
 Host_Pause_f
 ==================
 */
 Host_Pause_f
 ==================
 */
-void Host_Pause_f (void)
+static void Host_Pause_f (void)
 {
 {
-       if (!pausable.integer)
-               SV_ClientPrint("Pause not allowed.\n");
+       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 ();
+                       return;
+               }
+               print = Con_Printf;
+       }
        else
        else
+               print = SV_ClientPrintf;
+
+       if (!pausable.integer)
        {
        {
-               sv.paused ^= 1;
-               SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
-               // send notification to all clients
-               MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
-               MSG_WriteByte(&sv.reliable_datagram, sv.paused);
+               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);
 }
 
 /*
 }
 
 /*
@@ -1328,12 +1814,15 @@ LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's
 cvar_t cl_pmodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_pmodel", "0", "internal storage cvar for current player model number in nehahra (changed by pmodel command)"};
 static void Host_PModel_f (void)
 {
 cvar_t cl_pmodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_pmodel", "0", "internal storage cvar for current player model number in nehahra (changed by pmodel command)"};
 static void Host_PModel_f (void)
 {
+       prvm_prog_t *prog = SVVM_prog;
        int i;
        int i;
-       prvm_eval_t *val;
 
        if (Cmd_Argc () == 1)
        {
 
        if (Cmd_Argc () == 1)
        {
-               Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
+               if (cmd_source == src_command)
+               {
+                       Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
+               }
                return;
        }
        i = atoi(Cmd_Argv(1));
                return;
        }
        i = atoi(Cmd_Argv(1));
@@ -1348,8 +1837,7 @@ static void Host_PModel_f (void)
                return;
        }
 
                return;
        }
 
-       if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
-               val->_float = i;
+       PRVM_serveredictfloat(host_client->edict, pmodel) = i;
 }
 
 //===========================================================================
 }
 
 //===========================================================================
@@ -1360,19 +1848,21 @@ static void Host_PModel_f (void)
 Host_PreSpawn_f
 ==================
 */
 Host_PreSpawn_f
 ==================
 */
-void Host_PreSpawn_f (void)
+static void Host_PreSpawn_f (void)
 {
 {
-       if (host_client->spawned)
+       if (host_client->prespawned)
        {
        {
-               Con_Print("prespawn not valid -- already spawned\n");
+               Con_Print("prespawn not valid -- already prespawned\n");
                return;
        }
                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);
 
        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
        }
 
        // reset the name change timer because the client will send name soon
@@ -1384,17 +1874,24 @@ void Host_PreSpawn_f (void)
 Host_Spawn_f
 ==================
 */
 Host_Spawn_f
 ==================
 */
-void Host_Spawn_f (void)
+static void Host_Spawn_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        int i;
        client_t *client;
        int stats[MAX_CL_STATS];
 
        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;
        }
        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
 
        // reset name change timer again because they might want to change name
        // again in the first 5 seconds after connecting
@@ -1409,35 +1906,33 @@ void Host_Spawn_f (void)
        if (sv.loadgame)
        {
                // loaded games are fully initialized already
        if (sv.loadgame)
        {
                // loaded games are fully initialized already
-               // if this is the last client to be connected, unpause
-               sv.paused = false;
-
-               if (prog->funcoffsets.RestoreGame)
+               if (PRVM_serverfunction(RestoreGame))
                {
                        Con_DPrint("Calling RestoreGame\n");
                {
                        Con_DPrint("Calling RestoreGame\n");
-                       prog->globals.server->time = sv.time;
-                       prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
-                       PRVM_ExecuteProgram(prog->funcoffsets.RestoreGame, "QC function RestoreGame is missing");
+                       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
        {
                }
        }
        else
        {
-               //Con_Printf("Host_Spawn_f: host_client->edict->netname = %s, host_client->edict->netname = %s, host_client->name = %s\n", PRVM_GetString(host_client->edict->fields.server->netname), PRVM_GetString(host_client->edict->fields.server->netname), host_client->name);
+               //Con_Printf("Host_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++)
 
                // copy spawn parms out of the client_t
                for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
-                       (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
+                       (&PRVM_serverglobalfloat(parm1))[i] = host_client->spawn_parms[i];
 
                // call the spawn function
                host_client->clientconnectcalled = true;
 
                // call the spawn function
                host_client->clientconnectcalled = true;
-               prog->globals.server->time = sv.time;
-               prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
-               PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
+               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 ((Sys_DoubleTime() - host_client->connecttime) <= sv.time)
-                       Con_Printf("%s entered the game\n", host_client->name);
+               if (cls.state == ca_dedicated)
+                       Con_Printf("%s connected\n", host_client->name);
 
 
-               PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
+               PRVM_serverglobalfloat(time) = sv.time;
+               prog->ExecuteProgram(prog, PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
        }
 
        if (!host_client->netconnection)
        }
 
        if (!host_client->netconnection)
@@ -1477,19 +1972,19 @@ void Host_Spawn_f (void)
        // send some stats
        MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
        MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
        // 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)prog->globals.server->total_secrets);
+       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_WriteByte (&host_client->netconnection->message, svc_updatestat);
        MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
-       MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
+       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_WriteByte (&host_client->netconnection->message, svc_updatestat);
        MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
-       MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_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_WriteByte (&host_client->netconnection->message, svc_updatestat);
        MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
-       MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_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
 
        // send a fixangle
        // Never send a roll angle, because savegames can catch the server
@@ -1499,16 +1994,15 @@ void Host_Spawn_f (void)
        if (sv.loadgame)
        {
                MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
        if (sv.loadgame)
        {
                MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
-               MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
-               MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
+               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);
                MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
-               sv.loadgame = false; // we're basically done with loading now
        }
        else
        {
                MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
        }
        else
        {
                MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
-               MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
-               MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
+               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);
        }
 
                MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
        }
 
@@ -1523,9 +2017,33 @@ void Host_Spawn_f (void)
 Host_Begin_f
 ==================
 */
 Host_Begin_f
 ==================
 */
-void Host_Begin_f (void)
+static void Host_Begin_f (void)
 {
 {
-       host_client->spawned = true;
+       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;
+
+       // LordHavoc: 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
+               }
+       }
 }
 
 //===========================================================================
 }
 
 //===========================================================================
@@ -1538,9 +2056,9 @@ Host_Kick_f
 Kicks a user off of the server
 ==================
 */
 Kicks a user off of the server
 ==================
 */
-void Host_Kick_f (void)
+static void Host_Kick_f (void)
 {
 {
-       char *who;
+       const char *who;
        const char *message = NULL;
        client_t *save;
        int i;
        const char *message = NULL;
        client_t *save;
        int i;
@@ -1549,7 +2067,6 @@ void Host_Kick_f (void)
        if (!sv.active)
                return;
 
        if (!sv.active)
                return;
 
-       SV_VM_Begin();
        save = host_client;
 
        if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
        save = host_client;
 
        if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
@@ -1589,7 +2106,7 @@ void Host_Kick_f (void)
                if (Cmd_Argc() > 2)
                {
                        message = Cmd_Args();
                if (Cmd_Argc() > 2)
                {
                        message = Cmd_Args();
-                       COM_ParseTokenConsole(&message);
+                       COM_ParseToken_Simple(&message, false, false, true);
                        if (byNumber)
                        {
                                message++;                                                      // skip the #
                        if (byNumber)
                        {
                                message++;                                                      // skip the #
@@ -1608,7 +2125,6 @@ void Host_Kick_f (void)
        }
 
        host_client = save;
        }
 
        host_client = save;
-       SV_VM_End();
 }
 
 /*
 }
 
 /*
@@ -1624,11 +2140,11 @@ DEBUGGING TOOLS
 Host_Give_f
 ==================
 */
 Host_Give_f
 ==================
 */
-void Host_Give_f (void)
+static void Host_Give_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        const char *t;
        int v;
        const char *t;
        int v;
-       prvm_eval_t *val;
 
        if (!allowcheats)
        {
 
        if (!allowcheats)
        {
@@ -1652,125 +2168,102 @@ void Host_Give_f (void)
        case '8':
        case '9':
                // MED 01/04/97 added hipnotic give stuff
        case '8':
        case '9':
                // MED 01/04/97 added hipnotic give stuff
-               if (gamemode == GAME_HIPNOTIC)
+               if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
                {
                        if (t[0] == '6')
                        {
                                if (t[1] == 'a')
                {
                        if (t[0] == '6')
                        {
                                if (t[1] == 'a')
-                                       host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
+                                       PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_PROXIMITY_GUN;
                                else
                                else
-                                       host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
+                                       PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | IT_GRENADE_LAUNCHER;
                        }
                        else if (t[0] == '9')
                        }
                        else if (t[0] == '9')
-                               host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
+                               PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_LASER_CANNON;
                        else if (t[0] == '0')
                        else if (t[0] == '0')
-                               host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
+                               PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_MJOLNIR;
                        else if (t[0] >= '2')
                        else if (t[0] >= '2')
-                               host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (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')
                }
                else
                {
                        if (t[0] >= '2')
-                               host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
+                               PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
                }
                break;
 
        case 's':
                }
                break;
 
        case 's':
-               if (gamemode == GAME_ROGUE && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_shells1)))
-                       val->_float = v;
+               if (gamemode == GAME_ROGUE)
+                       PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
 
 
-               host_client->edict->fields.server->ammo_shells = v;
+               PRVM_serveredictfloat(host_client->edict, ammo_shells) = v;
                break;
        case 'n':
                if (gamemode == GAME_ROGUE)
                {
                break;
        case 'n':
                if (gamemode == GAME_ROGUE)
                {
-                       if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_nails1)))
-                       {
-                               val->_float = v;
-                               if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
-                                       host_client->edict->fields.server->ammo_nails = v;
-                       }
+                       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
                {
                }
                else
                {
-                       host_client->edict->fields.server->ammo_nails = v;
+                       PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
                }
                break;
        case 'l':
                if (gamemode == GAME_ROGUE)
                {
                }
                break;
        case 'l':
                if (gamemode == GAME_ROGUE)
                {
-                       val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_lava_nails);
-                       if (val)
-                       {
-                               val->_float = v;
-                               if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
-                                       host_client->edict->fields.server->ammo_nails = v;
-                       }
+                       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)
                {
                }
                break;
        case 'r':
                if (gamemode == GAME_ROGUE)
                {
-                       val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_rockets1);
-                       if (val)
-                       {
-                               val->_float = v;
-                               if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
-                                       host_client->edict->fields.server->ammo_rockets = v;
-                       }
+                       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
                {
                }
                else
                {
-                       host_client->edict->fields.server->ammo_rockets = v;
+                       PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
                }
                break;
        case 'm':
                if (gamemode == GAME_ROGUE)
                {
                }
                break;
        case 'm':
                if (gamemode == GAME_ROGUE)
                {
-                       val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_multi_rockets);
-                       if (val)
-                       {
-                               val->_float = v;
-                               if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
-                                       host_client->edict->fields.server->ammo_rockets = v;
-                       }
+                       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':
                }
                break;
        case 'h':
-               host_client->edict->fields.server->health = v;
+               PRVM_serveredictfloat(host_client->edict, health) = v;
                break;
        case 'c':
                if (gamemode == GAME_ROGUE)
                {
                break;
        case 'c':
                if (gamemode == GAME_ROGUE)
                {
-                       val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_cells1);
-                       if (val)
-                       {
-                               val->_float = v;
-                               if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
-                                       host_client->edict->fields.server->ammo_cells = v;
-                       }
+                       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
                {
                }
                else
                {
-                       host_client->edict->fields.server->ammo_cells = v;
+                       PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
                }
                break;
        case 'p':
                if (gamemode == GAME_ROGUE)
                {
                }
                break;
        case 'p':
                if (gamemode == GAME_ROGUE)
                {
-                       val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_plasma);
-                       if (val)
-                       {
-                               val->_float = v;
-                               if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
-                                       host_client->edict->fields.server->ammo_cells = v;
-                       }
+                       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;
        }
 }
 
                }
                break;
        }
 }
 
-prvm_edict_t   *FindViewthing (void)
+static prvm_edict_t    *FindViewthing(prvm_prog_t *prog)
 {
        int             i;
        prvm_edict_t    *e;
 {
        int             i;
        prvm_edict_t    *e;
@@ -1778,7 +2271,7 @@ prvm_edict_t      *FindViewthing (void)
        for (i=0 ; i<prog->num_edicts ; i++)
        {
                e = PRVM_EDICT_NUM(i);
        for (i=0 ; i<prog->num_edicts ; i++)
        {
                e = PRVM_EDICT_NUM(i);
-               if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
+               if (!strcmp (PRVM_GetString(prog, PRVM_serveredictstring(e, classname)), "viewthing"))
                        return e;
        }
        Con_Print("No viewthing on map\n");
                        return e;
        }
        Con_Print("No viewthing on map\n");
@@ -1790,29 +2283,27 @@ prvm_edict_t    *FindViewthing (void)
 Host_Viewmodel_f
 ==================
 */
 Host_Viewmodel_f
 ==================
 */
-void Host_Viewmodel_f (void)
+static void Host_Viewmodel_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        prvm_edict_t    *e;
        prvm_edict_t    *e;
-       model_t *m;
+       dp_model_t      *m;
 
        if (!sv.active)
                return;
 
 
        if (!sv.active)
                return;
 
-       SV_VM_Begin();
-       e = FindViewthing ();
-       SV_VM_End();
-       if (!e)
-               return;
-
-       m = Mod_ForName (Cmd_Argv(1), false, true, false);
-       if (!m || !m->loaded || !m->Draw)
+       e = FindViewthing(prog);
+       if (e)
        {
        {
-               Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
-               return;
+               m = Mod_ForName (Cmd_Argv(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(1));
        }
        }
-
-       e->fields.server->frame = 0;
-       cl.model_precache[(int)e->fields.server->modelindex] = m;
 }
 
 /*
 }
 
 /*
@@ -1820,31 +2311,31 @@ void Host_Viewmodel_f (void)
 Host_Viewframe_f
 ==================
 */
 Host_Viewframe_f
 ==================
 */
-void Host_Viewframe_f (void)
+static void Host_Viewframe_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        prvm_edict_t    *e;
        int             f;
        prvm_edict_t    *e;
        int             f;
-       model_t *m;
+       dp_model_t      *m;
 
        if (!sv.active)
                return;
 
 
        if (!sv.active)
                return;
 
-       SV_VM_Begin();
-       e = FindViewthing ();
-       SV_VM_End();
-       if (!e)
-               return;
-       m = cl.model_precache[(int)e->fields.server->modelindex];
+       e = FindViewthing(prog);
+       if (e)
+       {
+               m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
 
 
-       f = atoi(Cmd_Argv(1));
-       if (f >= m->numframes)
-               f = m->numframes-1;
+               f = atoi(Cmd_Argv(1));
+               if (f >= m->numframes)
+                       f = m->numframes-1;
 
 
-       e->fields.server->frame = f;
+               PRVM_serveredictfloat(e, frame) = f;
+       }
 }
 
 
 }
 
 
-void PrintFrameName (model_t *m, int frame)
+static void PrintFrameName (dp_model_t *m, int frame)
 {
        if (m->animscenes)
                Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
 {
        if (m->animscenes)
                Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
@@ -1857,26 +2348,26 @@ void PrintFrameName (model_t *m, int frame)
 Host_Viewnext_f
 ==================
 */
 Host_Viewnext_f
 ==================
 */
-void Host_Viewnext_f (void)
+static void Host_Viewnext_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        prvm_edict_t    *e;
        prvm_edict_t    *e;
-       model_t *m;
+       dp_model_t      *m;
 
        if (!sv.active)
                return;
 
 
        if (!sv.active)
                return;
 
-       SV_VM_Begin();
-       e = FindViewthing ();
-       SV_VM_End();
-       if (!e)
-               return;
-       m = cl.model_precache[(int)e->fields.server->modelindex];
+       e = FindViewthing(prog);
+       if (e)
+       {
+               m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
 
 
-       e->fields.server->frame = e->fields.server->frame + 1;
-       if (e->fields.server->frame >= m->numframes)
-               e->fields.server->frame = m->numframes - 1;
+               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)e->fields.server->frame);
+               PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
+       }
 }
 
 /*
 }
 
 /*
@@ -1884,27 +2375,26 @@ void Host_Viewnext_f (void)
 Host_Viewprev_f
 ==================
 */
 Host_Viewprev_f
 ==================
 */
-void Host_Viewprev_f (void)
+static void Host_Viewprev_f (void)
 {
 {
+       prvm_prog_t *prog = SVVM_prog;
        prvm_edict_t    *e;
        prvm_edict_t    *e;
-       model_t *m;
+       dp_model_t      *m;
 
        if (!sv.active)
                return;
 
 
        if (!sv.active)
                return;
 
-       SV_VM_Begin();
-       e = FindViewthing ();
-       SV_VM_End();
-       if (!e)
-               return;
-
-       m = cl.model_precache[(int)e->fields.server->modelindex];
+       e = FindViewthing(prog);
+       if (e)
+       {
+               m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
 
 
-       e->fields.server->frame = e->fields.server->frame - 1;
-       if (e->fields.server->frame < 0)
-               e->fields.server->frame = 0;
+               PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) - 1;
+               if (PRVM_serveredictfloat(e, frame) < 0)
+                       PRVM_serveredictfloat(e, frame) = 0;
 
 
-       PrintFrameName (m, (int)e->fields.server->frame);
+               PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
+       }
 }
 
 /*
 }
 
 /*
@@ -1921,11 +2411,11 @@ DEMO LOOP CONTROL
 Host_Startdemos_f
 ==================
 */
 Host_Startdemos_f
 ==================
 */
-void Host_Startdemos_f (void)
+static void Host_Startdemos_f (void)
 {
        int             i, c;
 
 {
        int             i, c;
 
-       if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-demolooponly"))
+       if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
                return;
 
        c = Cmd_Argc() - 1;
                return;
 
        c = Cmd_Argc() - 1;
@@ -1934,7 +2424,7 @@ void Host_Startdemos_f (void)
                Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
                c = MAX_DEMOS;
        }
                Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
                c = MAX_DEMOS;
        }
-       Con_Printf("%i demo(s) in loop\n", c);
+       Con_DPrintf("%i demo(s) in loop\n", c);
 
        for (i=1 ; i<c+1 ; i++)
                strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
 
        for (i=1 ; i<c+1 ; i++)
                strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
@@ -1960,7 +2450,7 @@ Host_Demos_f
 Return to looping demos
 ==================
 */
 Return to looping demos
 ==================
 */
-void Host_Demos_f (void)
+static void Host_Demos_f (void)
 {
        if (cls.state == ca_dedicated)
                return;
 {
        if (cls.state == ca_dedicated)
                return;
@@ -1977,7 +2467,7 @@ Host_Stopdemo_f
 Return to looping demos
 ==================
 */
 Return to looping demos
 ==================
 */
-void Host_Stopdemo_f (void)
+static void Host_Stopdemo_f (void)
 {
        if (!cls.demoplayback)
                return;
 {
        if (!cls.demoplayback)
                return;
@@ -1985,19 +2475,29 @@ void Host_Stopdemo_f (void)
        Host_ShutdownServer ();
 }
 
        Host_ShutdownServer ();
 }
 
-void Host_SendCvar_f (void)
+static void Host_SendCvar_f (void)
 {
        int             i;
        cvar_t  *c;
 {
        int             i;
        cvar_t  *c;
+       const char *cvarname;
        client_t *old;
        client_t *old;
+       char vabuf[1024];
 
        if(Cmd_Argc() != 2)
                return;
 
        if(Cmd_Argc() != 2)
                return;
-       if(!(c = Cvar_FindVar(Cmd_Argv(1))) || (c->flags & CVAR_PRIVATE))
+       cvarname = Cmd_Argv(1);
+       if (cls.state == ca_connected)
+       {
+               c = Cvar_FindVar(cvarname);
+               // LordHavoc: if there is no such cvar or if it is private, send a
+               // reply indicating that it has no value
+               if(!c || (c->flags & CVAR_PRIVATE))
+                       Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s", cvarname));
+               else
+                       Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s \"%s\"", c->name, c->string));
                return;
                return;
-       if (cls.state != ca_dedicated)
-               Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"\n", c->name, c->string));
-       if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
+       }
+       if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
                return;
 
        old = host_client;
                return;
 
        old = host_client;
@@ -2009,7 +2509,7 @@ void Host_SendCvar_f (void)
                if(svs.clients[i].active && svs.clients[i].netconnection)
                {
                        host_client = &svs.clients[i];
                if(svs.clients[i].active && svs.clients[i].netconnection)
                {
                        host_client = &svs.clients[i];
-                       Host_ClientCommands(va("sendcvar %s\n", c->name));
+                       Host_ClientCommands("sendcvar %s\n", cvarname);
                }
        host_client = old;
 }
                }
        host_client = old;
 }
@@ -2020,30 +2520,84 @@ static void MaxPlayers_f(void)
 
        if (Cmd_Argc() != 2)
        {
 
        if (Cmd_Argc() != 2)
        {
-               Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients);
+               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");
                return;
        }
 
        if (sv.active)
        {
                Con_Print("maxplayers can not be changed while a server is running.\n");
-               return;
+               Con_Print("It will be changed on next server startup (\"map\" command).\n");
        }
 
        n = atoi(Cmd_Argv(1));
        n = bound(1, n, MAX_SCOREBOARD);
        Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
 
        }
 
        n = atoi(Cmd_Argv(1));
        n = bound(1, n, MAX_SCOREBOARD);
        Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
 
-       if (svs.clients)
-               Mem_Free(svs.clients);
-       svs.maxclients = n;
-       svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
+       svs.maxclients_next = n;
        if (n == 1)
                Cvar_Set ("deathmatch", "0");
        else
                Cvar_Set ("deathmatch", "1");
 }
 
        if (n == 1)
                Cvar_Set ("deathmatch", "0");
        else
                Cvar_Set ("deathmatch", "1");
 }
 
+/*
+=====================
+Host_PQRcon_f
+
+ProQuake rcon support
+=====================
+*/
+static void Host_PQRcon_f (void)
+{
+       int n;
+       const char *e;
+       lhnetsocket_t *mysocket;
+
+       if (Cmd_Argc() == 1)
+       {
+               Con_Printf("%s: Usage: %s command\n", Cmd_Argv(0), Cmd_Argv(0));
+               return;
+       }
+
+       if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
+       {
+               Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
+               return;
+       }
+
+       e = strchr(rcon_password.string, ' ');
+       n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
+
+       if (cls.netcon)
+               cls.rcon_address = cls.netcon->peeraddress;
+       else
+       {
+               if (!rcon_address.string[0])
+               {
+                       Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
+                       return;
+               }
+               LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer);
+       }
+       mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address);
+       if (mysocket)
+       {
+               sizebuf_t buf;
+               unsigned char bufdata[64];
+               buf.data = bufdata;
+               SZ_Clear(&buf);
+               MSG_WriteLong(&buf, 0);
+               MSG_WriteByte(&buf, CCREQ_RCON);
+               SZ_Write(&buf, (const unsigned char*)rcon_password.string, n);
+               MSG_WriteByte(&buf, 0); // terminate the (possibly partial) string
+               MSG_WriteString(&buf, Cmd_Args());
+               StoreBigLong(buf.data, NETFLAG_CTL | (buf.cursize & NETFLAG_LENGTH_MASK));
+               NetConn_Write(mysocket, buf.data, buf.cursize, &cls.rcon_address);
+               SZ_Clear(&buf);
+       }
+}
+
 //=============================================================================
 
 // QuakeWorld commands
 //=============================================================================
 
 // QuakeWorld commands
@@ -2056,29 +2610,29 @@ Host_Rcon_f
   an unconnected command.
 =====================
 */
   an unconnected command.
 =====================
 */
-void Host_Rcon_f (void) // credit: taken from QuakeWorld
+static void Host_Rcon_f (void) // credit: taken from QuakeWorld
 {
 {
-       int i;
-       lhnetaddress_t to;
+       int i, n;
+       const char *e;
        lhnetsocket_t *mysocket;
 
        lhnetsocket_t *mysocket;
 
-       if (!rcon_password.string || !rcon_password.string[0])
+       if (Cmd_Argc() == 1)
        {
        {
-               Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
+               Con_Printf("%s: Usage: %s command\n", Cmd_Argv(0), Cmd_Argv(0));
                return;
        }
 
                return;
        }
 
-       for (i = 0;rcon_password.string[i];i++)
+       if (!rcon_password.string || !rcon_password.string[0])
        {
        {
-               if (rcon_password.string[i] <= ' ')
-               {
-                       Con_Printf("rcon_password is not allowed to have any whitespace.\n");
-                       return;
-               }
+               Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
+               return;
        }
 
        }
 
+       e = strchr(rcon_password.string, ' ');
+       n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
+
        if (cls.netcon)
        if (cls.netcon)
-               to = cls.netcon->peeraddress;
+               cls.rcon_address = cls.netcon->peeraddress;
        else
        {
                if (!rcon_address.string[0])
        else
        {
                if (!rcon_address.string[0])
@@ -2086,13 +2640,54 @@ void Host_Rcon_f (void) // credit: taken from QuakeWorld
                        Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
                        return;
                }
                        Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
                        return;
                }
-               LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
+               LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer);
        }
        }
-       mysocket = NetConn_ChooseClientSocketForAddress(&to);
-       if (mysocket)
+       mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address);
+       if (mysocket && Cmd_Args()[0])
        {
                // simply put together the rcon packet and send it
        {
                // simply put together the rcon packet and send it
-               NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to);
+               if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
+               {
+                       if(cls.rcon_commands[cls.rcon_ringpos][0])
+                       {
+                               char s[128];
+                               LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
+                               Con_Printf("rcon to %s (for command %s) failed: too many buffered commands (possibly increase MAX_RCONS)\n", s, cls.rcon_commands[cls.rcon_ringpos]);
+                               cls.rcon_commands[cls.rcon_ringpos][0] = 0;
+                               --cls.rcon_trying;
+                       }
+                       for (i = 0;i < MAX_RCONS;i++)
+                               if(cls.rcon_commands[i][0])
+                                       if (!LHNETADDRESS_Compare(&cls.rcon_address, &cls.rcon_addresses[i]))
+                                               break;
+                       ++cls.rcon_trying;
+                       if(i >= MAX_RCONS)
+                               NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &cls.rcon_address); // otherwise we'll request the challenge later
+                       strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
+                       cls.rcon_addresses[cls.rcon_ringpos] = cls.rcon_address;
+                       cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
+                       cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
+               }
+               else if(rcon_secure.integer > 0)
+               {
+                       char buf[1500];
+                       char argbuf[1500];
+                       dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
+                       memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
+                       if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
+                       {
+                               buf[40] = ' ';
+                               strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
+                               NetConn_Write(mysocket, buf, 41 + (int)strlen(buf + 41), &cls.rcon_address);
+                       }
+               }
+               else
+               {
+                       char buf[1500];
+                       memcpy(buf, "\377\377\377\377", 4);
+                       dpsnprintf(buf+4, sizeof(buf)-4, "rcon %.*s %s",  n, rcon_password.string, Cmd_Args());
+                       NetConn_WriteString(mysocket, buf, &cls.rcon_address);
+               }
        }
 }
 
        }
 }
 
@@ -2105,7 +2700,7 @@ user <name or userid>
 Dump userdata / masterdata for a user
 ====================
 */
 Dump userdata / masterdata for a user
 ====================
 */
-void Host_User_f (void) // credit: taken from QuakeWorld
+static void Host_User_f (void) // credit: taken from QuakeWorld
 {
        int             uid;
        int             i;
 {
        int             uid;
        int             i;
@@ -2138,7 +2733,7 @@ Host_Users_f
 Dump userids for all current players
 ====================
 */
 Dump userids for all current players
 ====================
 */
-void Host_Users_f (void) // credit: taken from QuakeWorld
+static void Host_Users_f (void) // credit: taken from QuakeWorld
 {
        int             i;
        int             c;
 {
        int             i;
        int             c;
@@ -2166,7 +2761,7 @@ Sent by server when serverinfo changes
 ==================
 */
 // TODO: shouldn't this be a cvar instead?
 ==================
 */
 // TODO: shouldn't this be a cvar instead?
-void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
+static void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
 {
        char temp[512];
        if (Cmd_Argc() != 2)
 {
        char temp[512];
        if (Cmd_Argc() != 2)
@@ -2188,11 +2783,10 @@ Allow clients to change userinfo
 ==================
 Casey was here :)
 */
 ==================
 Casey was here :)
 */
-void Host_FullInfo_f (void) // credit: taken from QuakeWorld
+static void Host_FullInfo_f (void) // credit: taken from QuakeWorld
 {
        char key[512];
        char value[512];
 {
        char key[512];
        char value[512];
-       char *o;
        const char *s;
 
        if (Cmd_Argc() != 2)
        const char *s;
 
        if (Cmd_Argc() != 2)
@@ -2206,27 +2800,33 @@ void Host_FullInfo_f (void) // credit: taken from QuakeWorld
                s++;
        while (*s)
        {
                s++;
        while (*s)
        {
-               o = key;
-               while (*s && *s != '\\')
-                       *o++ = *s++;
-               *o = 0;
-
+               size_t len = strcspn(s, "\\");
+               if (len >= sizeof(key)) {
+                       len = sizeof(key) - 1;
+               }
+               strlcpy(key, s, len + 1);
+               s += len;
                if (!*s)
                {
                        Con_Printf ("MISSING VALUE\n");
                        return;
                }
                if (!*s)
                {
                        Con_Printf ("MISSING VALUE\n");
                        return;
                }
+               ++s; // Skip over backslash.
 
 
-               o = value;
-               s++;
-               while (*s && *s != '\\')
-                       *o++ = *s++;
-               *o = 0;
-
-               if (*s)
-                       s++;
+               len = strcspn(s, "\\");
+               if (len >= sizeof(value)) {
+                       len = sizeof(value) - 1;
+               }
+               strlcpy(value, s, len + 1);
 
                CL_SetInfo(key, value, false, false, false, false);
 
                CL_SetInfo(key, value, false, false, false, false);
+
+               s += len;
+               if (!*s)
+               {
+                       break;
+               }
+               ++s; // Skip over backslash.
        }
 }
 
        }
 }
 
@@ -2237,7 +2837,7 @@ CL_SetInfo_f
 Allow clients to change userinfo
 ==================
 */
 Allow clients to change userinfo
 ==================
 */
-void Host_SetInfo_f (void) // credit: taken from QuakeWorld
+static void Host_SetInfo_f (void) // credit: taken from QuakeWorld
 {
        if (Cmd_Argc() == 1)
        {
 {
        if (Cmd_Argc() == 1)
        {
@@ -2261,7 +2861,7 @@ packet <destination> <contents>
 Contents allows \n escape character
 ====================
 */
 Contents allows \n escape character
 ====================
 */
-void Host_Packet_f (void) // credit: taken from QuakeWorld
+static void Host_Packet_f (void) // credit: taken from QuakeWorld
 {
        char send[2048];
        int i, l;
 {
        char send[2048];
        int i, l;
@@ -2284,7 +2884,7 @@ void Host_Packet_f (void) // credit: taken from QuakeWorld
 
        in = Cmd_Argv(2);
        out = send+4;
 
        in = Cmd_Argv(2);
        out = send+4;
-       send[0] = send[1] = send[2] = send[3] = 0xff;
+       send[0] = send[1] = send[2] = send[3] = -1;
 
        l = (int)strlen (in);
        for (i=0 ; i<l ; i++)
 
        l = (int)strlen (in);
        for (i=0 ; i<l ; i++)
@@ -2321,6 +2921,8 @@ void Host_Packet_f (void) // credit: taken from QuakeWorld
        }
 
        mysocket = NetConn_ChooseClientSocketForAddress(&address);
        }
 
        mysocket = NetConn_ChooseClientSocketForAddress(&address);
+       if (!mysocket)
+               mysocket = NetConn_ChooseServerSocketForAddress(&address);
        if (mysocket)
                NetConn_Write(mysocket, send, out - send, &address);
 }
        if (mysocket)
                NetConn_Write(mysocket, send, out - send, &address);
 }
@@ -2334,7 +2936,7 @@ Send back ping and packet loss update for all current players to this player
 */
 void Host_Pings_f (void)
 {
 */
 void Host_Pings_f (void)
 {
-       int             i, j, ping, packetloss;
+       int             i, j, ping, packetloss, movementloss;
        char temp[128];
 
        if (!host_client->netconnection)
        char temp[128];
 
        if (!host_client->netconnection)
@@ -2348,9 +2950,18 @@ void Host_Pings_f (void)
        for (i = 0;i < svs.maxclients;i++)
        {
                packetloss = 0;
        for (i = 0;i < svs.maxclients;i++)
        {
                packetloss = 0;
+               movementloss = 0;
                if (svs.clients[i].netconnection)
                if (svs.clients[i].netconnection)
-                       for (j = 0;j < 100;j++)
-                               packetloss += svs.clients[i].netconnection->packetlost[j];
+               {
+                       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)
                ping = (int)floor(svs.clients[i].ping*1000+0.5);
                ping = bound(0, ping, 9999);
                if (sv.protocol == PROTOCOL_QUAKEWORLD)
@@ -2364,7 +2975,10 @@ void Host_Pings_f (void)
                else
                {
                        // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
                else
                {
                        // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
-                       dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
+                       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);
                }
        }
                        MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
                }
        }
@@ -2372,8 +2986,9 @@ void Host_Pings_f (void)
                MSG_WriteString(&host_client->netconnection->message, "\n");
 }
 
                MSG_WriteString(&host_client->netconnection->message, "\n");
 }
 
-void Host_PingPLReport_f(void)
+static void Host_PingPLReport_f(void)
 {
 {
+       char *errbyte;
        int i;
        int l = Cmd_Argc();
        if (l > cl.maxclients)
        int i;
        int l = Cmd_Argc();
        if (l > cl.maxclients)
@@ -2381,7 +2996,11 @@ void Host_PingPLReport_f(void)
        for (i = 0;i < l;i++)
        {
                cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
        for (i = 0;i < l;i++)
        {
                cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
-               cl.scores[i].qw_packetloss = atoi(Cmd_Argv(1+i*2+1));
+               cl.scores[i].qw_packetloss = strtol(Cmd_Argv(1+i*2+1), &errbyte, 0);
+               if(errbyte && *errbyte == ',')
+                       cl.scores[i].qw_movementloss = atoi(errbyte + 1);
+               else
+                       cl.scores[i].qw_movementloss = 0;
        }
 }
 
        }
 }
 
@@ -2394,26 +3013,15 @@ Host_InitCommands
 */
 void Host_InitCommands (void)
 {
 */
 void Host_InitCommands (void)
 {
-       dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\%s", engineversion);
+       dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
 
        Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
        Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
 
        Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
        Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
-       if (gamemode == GAME_NEHAHRA)
-       {
-               Cmd_AddCommand_WithClientCommand ("max", NULL, Host_God_f, "god mode (invulnerability)");
-               Cmd_AddCommand_WithClientCommand ("monster", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
-               Cmd_AddCommand_WithClientCommand ("scrag", NULL, Host_Fly_f, "fly mode (flight)");
-               Cmd_AddCommand_WithClientCommand ("wraith", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
-               Cmd_AddCommand_WithClientCommand ("gimme", NULL, Host_Give_f, "alter inventory");
-       }
-       else
-       {
-               Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
-               Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
-               Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
-               Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
-               Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
-       }
+       Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
+       Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
+       Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
+       Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
+       Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
        Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
        Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
        Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
        Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
        Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
        Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
@@ -2424,7 +3032,7 @@ void Host_InitCommands (void)
        Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
        Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
        Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
        Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
        Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
        Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
-       Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
+       Cmd_AddCommand_WithClientCommand ("pause", Host_Pause_f, Host_Pause_f, "pause the game (if the server allows pausing)");
        Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
        Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
        Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
        Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
        Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
        Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
@@ -2445,11 +3053,10 @@ void Host_InitCommands (void)
        Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
        Cvar_RegisterVariable (&cl_rate);
        Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
        Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
        Cvar_RegisterVariable (&cl_rate);
        Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
-       if (gamemode == GAME_NEHAHRA)
-       {
-               Cvar_RegisterVariable (&cl_pmodel);
-               Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "change your player model choice (Nehahra specific)");
-       }
+       Cvar_RegisterVariable (&cl_rate_burstsize);
+       Cmd_AddCommand_WithClientCommand ("rate_burstsize", Host_Rate_BurstSize_f, Host_Rate_BurstSize_f, "change your network connection speed");
+       Cvar_RegisterVariable (&cl_pmodel);
+       Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "(Nehahra-only) change your player model choice");
 
        // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
        Cvar_RegisterVariable (&cl_playermodel);
 
        // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
        Cvar_RegisterVariable (&cl_playermodel);
@@ -2462,11 +3069,15 @@ void Host_InitCommands (void)
        Cmd_AddCommand_WithClientCommand ("begin", NULL, Host_Begin_f, "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 ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
 
        Cmd_AddCommand_WithClientCommand ("begin", NULL, Host_Begin_f, "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 ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
 
-       Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");       // By [515]
+       Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
 
        Cvar_RegisterVariable (&rcon_password);
        Cvar_RegisterVariable (&rcon_address);
 
        Cvar_RegisterVariable (&rcon_password);
        Cvar_RegisterVariable (&rcon_address);
-       Cmd_AddCommand ("rcon", Host_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's)");
+       Cvar_RegisterVariable (&rcon_secure);
+       Cvar_RegisterVariable (&rcon_secure_challengetimeout);
+       Cmd_AddCommand ("rcon", Host_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's); note: if rcon_secure is set, client and server clocks must be synced e.g. via NTP");
+       Cmd_AddCommand ("srcon", Host_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's); this always works as if rcon_secure is set; note: client and server clocks must be synced e.g. via NTP");
+       Cmd_AddCommand ("pqrcon", Host_PQRcon_f, "sends a command to a proquake server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's)");
        Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
        Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
        Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
        Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
        Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
        Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
@@ -2479,11 +3090,20 @@ void Host_InitCommands (void)
        Cmd_AddCommand_WithClientCommand ("pings", NULL, Host_Pings_f, "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 ("pingplreport", Host_PingPLReport_f, "command sent by server containing client ping and packet loss values for scoreboard, triggered by pings command from client (not used by QW servers)");
 
        Cmd_AddCommand_WithClientCommand ("pings", NULL, Host_Pings_f, "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 ("pingplreport", Host_PingPLReport_f, "command sent by server containing client ping and packet loss values for scoreboard, triggered by pings command from client (not used by QW servers)");
 
+       Cmd_AddCommand ("fixtrans", Image_FixTransparentPixels_f, "change alpha-zero pixels in an image file to sensible values, and write out a new TGA (warning: SLOW)");
+       Cvar_RegisterVariable (&r_fixtrans_auto);
+
        Cvar_RegisterVariable (&team);
        Cvar_RegisterVariable (&skin);
        Cvar_RegisterVariable (&noaim);
 
        Cvar_RegisterVariable(&sv_cheats);
        Cvar_RegisterVariable(&sv_adminnick);
        Cvar_RegisterVariable (&team);
        Cvar_RegisterVariable (&skin);
        Cvar_RegisterVariable (&noaim);
 
        Cvar_RegisterVariable(&sv_cheats);
        Cvar_RegisterVariable(&sv_adminnick);
+       Cvar_RegisterVariable(&sv_status_privacy);
+       Cvar_RegisterVariable(&sv_status_show_qcstatus);
+       Cvar_RegisterVariable(&sv_namechangetimer);
 }
 
 }
 
+void Host_NoOperation_f(void)
+{
+}