]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - host_cmd.c
reworked progs loading so that entvars_t and globalvars_t are no longer
[xonotic/darkplaces.git] / host_cmd.c
index cdda57cc6f43b1f2464cee6cb203f537cda91e88..6f368f03ee0310dd61db62695819f208cc078490 100644 (file)
@@ -22,6 +22,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "sv_demo.h"
 #include "image.h"
 
+#include "utf8lib.h"
+
 // for secure rcon authentication
 #include "hmac.h"
 #include "mdfour.h"
@@ -32,8 +34,9 @@ cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, an
 cvar_t sv_adminnick = {CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
 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 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"};
-cvar_t rcon_secure = {CVAR_NQUSERINFOHACK, "rcon_secure", "0", "force secure rcon authentication; NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password"};
+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)"};
@@ -69,7 +72,7 @@ void Host_Status_f (void)
        client_t *client;
        int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
        void (*print) (const char *fmt, ...);
-       char ip[22];
+       char ip[48]; // can contain a full length v6 address with [] and a port
        int frags;
 
        if (cmd_source == src_command)
@@ -111,9 +114,9 @@ void Host_Status_f (void)
        print ("players:  %i active (%i max)\n\n", players, svs.maxclients);
 
        if (in == 1)
-               print ("^2IP                   %%pl ping  time   frags  no   name\n");
+               print ("^2IP                                             %%pl ping  time   frags  no   name\n");
        else if (in == 2)
-               print ("^5IP                    no   name\n");
+               print ("^5IP                                              no   name\n");
 
        for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
        {
@@ -139,22 +142,23 @@ void Host_Status_f (void)
                        packetloss = 0;
                        if (client->netconnection)
                                for (j = 0;j < NETGRAPH_PACKETS;j++)
-                                       if (client->netconnection->incoming_unreliablesize[j] == NETGRAPH_LOSTPACKET)
+                                       if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
                                                packetloss++;
-                       packetloss = packetloss * 100 / NETGRAPH_PACKETS;
+                       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" , 22);
+                       strlcpy(ip, client->netconnection ? "hidden" : "botclient", 48);
                else
-                       strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 22);
+                       strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 48);
 
                frags = client->frags;
 
-               if(sv_status_show_qcstatus.integer && prog->fieldoffsets.clientstatus >= 0)
+               if(sv_status_show_qcstatus.integer)
                {
-                       const char *str = PRVM_E_STRING(PRVM_EDICT_NUM(i + 1), prog->fieldoffsets.clientstatus);
+                       prvm_edict_t *ed = PRVM_EDICT_NUM(i + 1);
+                       const char *str = PRVM_GetString(PRVM_serveredictstring(ed, clientstatus));
                        if(str && *str)
                        {
                                char *p;
@@ -171,29 +175,26 @@ void Host_Status_f (void)
                
                if (in == 0) // default layout
                {
-                       print ("#%-3u ", i+1);
-                       print ("%-16.16s ", client->name);
-                       print ("%4i  ", frags);
-                       print ("%2i:%02i:%02i\n   ", hours, minutes, seconds);
-                       print ("%s\n", ip);
+                       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
                {
-                       k%2 ? print("^3") : print("^7");
-                       print ("%-21s ", ip);
-                       print ("%2i ", packetloss);
-                       print ("%4i ", ping);
-                       print ("%2i:%02i:%02i ", hours, minutes, seconds);
-                       print ("%4i  ", frags);
-                       print ("#%-3u ", i+1);
-                       print ("^7%s\n", client->name);
+                       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
                {
-                       k%2 ? print("^3") : print("^7");
-                       print ("%-21s ", ip);
-                       print ("#%-3u ", i+1);
-                       print ("^7%s\n", client->name);
+                       print ("%s%-47s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
                }
        }
 
@@ -217,8 +218,8 @@ void Host_God_f (void)
                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");
@@ -232,8 +233,8 @@ void Host_Notarget_f (void)
                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");
@@ -249,16 +250,16 @@ void Host_Noclip_f (void)
                return;
        }
 
-       if (host_client->edict->fields.server->movetype != MOVETYPE_NOCLIP)
+       if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_NOCLIP)
        {
                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;
-               host_client->edict->fields.server->movetype = MOVETYPE_WALK;
+               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
                SV_ClientPrint("noclip OFF\n");
        }
 }
@@ -278,14 +279,14 @@ void Host_Fly_f (void)
                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
        {
-               host_client->edict->fields.server->movetype = MOVETYPE_WALK;
+               PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
                SV_ClientPrint("flymode OFF\n");
        }
 }
@@ -378,6 +379,8 @@ void Host_Map_f (void)
        }
 
        // remove menu
+       if (key_dest == key_menu || key_dest == key_menu_grabbed)
+               MR_ToggleMenu(0);
        key_dest = key_game;
 
        svs.serverflags = 0;                    // haven't completed an episode yet
@@ -385,7 +388,7 @@ void Host_Map_f (void)
        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);
 }
 
 /*
@@ -411,6 +414,8 @@ void Host_Changelevel_f (void)
        }
 
        // remove menu
+       if (key_dest == key_menu || key_dest == key_menu_grabbed)
+               MR_ToggleMenu(0);
        key_dest = key_game;
 
        SV_VM_Begin();
@@ -420,7 +425,7 @@ void Host_Changelevel_f (void)
        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);
 }
 
 /*
@@ -446,13 +451,15 @@ void Host_Restart_f (void)
        }
 
        // remove menu
+       if (key_dest == key_menu || key_dest == key_menu_grabbed)
+               MR_ToggleMenu(0);
        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);
 }
 
 /*
@@ -473,7 +480,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])
-                       CL_EstablishConnection(temp);
+                       CL_EstablishConnection(temp, -1);
                else
                        Con_Printf("Reconnect to what server?  (you have not connected to a server yet)\n");
                return;
@@ -520,15 +527,15 @@ User command to connect to server
 */
 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;
        }
        // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
-       if(!rcon_secure.integer)
+       if(rcon_secure.integer <= 0)
                Cvar_SetQuick(&rcon_password, "");
-       CL_EstablishConnection(Cmd_Argv(1));
+       CL_EstablishConnection(Cmd_Argv(1), 2);
 }
 
 
@@ -545,9 +552,11 @@ LOAD / SAVE GAME
 void Host_Savegame_to (const char *name)
 {
        qfile_t *f;
-       int             i, lightstyles = 64;
+       int             i, k, l, lightstyles = 64;
        char    comment[SAVEGAME_COMMENT_LENGTH+1];
+       char    line[MAX_INPUTLINE];
        qboolean isserver;
+       char    *s;
 
        // first we have to figure out if this can be saved in 64 lightstyles
        // (for Quake compatibility)
@@ -569,7 +578,7 @@ void Host_Savegame_to (const char *name)
 
        memset(comment, 0, sizeof(comment));
        if(isserver)
-               dpsnprintf(comment, sizeof(comment), "%-21.21s kills:%3i/%3i", PRVM_GetString(prog->edicts->fields.server->message), (int)prog->globals.server->killed_monsters, (int)prog->globals.server->total_monsters);
+               dpsnprintf(comment, sizeof(comment), "%-21.21s kills:%3i/%3i", PRVM_GetString(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)", PRVM_NAME);
        // convert space to _ to make stdio happy
@@ -631,6 +640,51 @@ void Host_Savegame_to (const char *name)
        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
+       for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); i++)
+       {
+               prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
+               if(stringbuffer && (stringbuffer->flags & STRINGBUFFER_SAVED))
+               {
+                       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
 
@@ -662,7 +716,7 @@ void Host_Savegame_f (void)
                        return;
                }
 
-               if (svs.clients[0].active && svs.clients[0].edict->fields.server->deadflag)
+               if (svs.clients[0].active && PRVM_serveredictfloat(svs.clients[0].edict, deadflag))
                {
                        Con_Print("Can't savegame with a dead player\n");
                        return;
@@ -697,6 +751,7 @@ void Host_Savegame_f (void)
 Host_Loadgame_f
 ===============
 */
+
 void Host_Loadgame_f (void)
 {
        char filename[MAX_QPATH];
@@ -707,10 +762,12 @@ void Host_Loadgame_f (void)
        const char *t;
        char *text;
        prvm_edict_t *ent;
-       int i;
+       int i, k;
        int entnum;
        int version;
        float spawn_parms[NUM_SPAWN_PARMS];
+       prvm_stringbuffer_t *stringbuffer;
+       size_t alloclen;
 
        if (Cmd_Argc() != 2)
        {
@@ -728,6 +785,8 @@ void Host_Loadgame_f (void)
                CL_Disconnect ();
 
        // remove menu
+       if (key_dest == key_menu || key_dest == key_menu_grabbed)
+               MR_ToggleMenu(0);
        key_dest = key_game;
 
        cls.demonum = -1;               // stop demo loop in case this fails
@@ -841,6 +900,9 @@ void Host_Loadgame_f (void)
                }
        }
 
+       // unlink all entities
+       World_UnlinkAll(&sv.world);
+
 // load the edicts out of the savegame file
        end = t;
        for (;;)
@@ -867,6 +929,9 @@ void Host_Loadgame_f (void)
 
                        // parse the global vars
                        PRVM_ED_ParseGlobals (start);
+
+                       // restore the autocvar globals
+                       Cvar_UpdateAllAutoCvars();
                }
                else
                {
@@ -879,7 +944,7 @@ void Host_Loadgame_f (void)
                        while (entnum >= prog->max_edicts)
                                PRVM_MEM_IncreaseEdicts();
                        ent = PRVM_EDICT_NUM(entnum);
-                       memset (ent->fields.server, 0, prog->progs->entityfields * 4);
+                       memset(ent->fields.vp, 0, prog->entityfields * 4);
                        ent->priv.server->free = false;
 
                        if(developer_entityparsing.integer)
@@ -889,7 +954,7 @@ void Host_Loadgame_f (void)
 
                        // link it into the bsp tree
                        if (!ent->priv.server->free)
-                               SV_LinkEdict (ent, false);
+                               SV_LinkEdict(ent);
                }
 
                end = t;
@@ -942,7 +1007,7 @@ void Host_Loadgame_f (void)
                                        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.modelname : NULL);
+                                               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);
@@ -957,6 +1022,46 @@ void Host_Loadgame_f (void)
                                        else
                                                Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
                                }
+                               else if (!strcmp(com_token, "sv.bufstr"))
+                               {
+                                       COM_ParseToken_Simple(&t, false, false);
+                                       i = atoi(com_token);
+                                       COM_ParseToken_Simple(&t, false, false);
+                                       k = atoi(com_token);
+                                       COM_ParseToken_Simple(&t, false, false);
+                                       stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
+                                       // VorteX: nasty code, cleanup required
+                                       // create buffer at this index
+                                       if(!stringbuffer) 
+                                               stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecordAtIndex(&prog->stringbuffersarray, i);
+                                       if (!stringbuffer)
+                                               Con_Printf("cant write string %i into buffer %i\n", k, i);
+                                       else
+                                       {
+                                               // code copied from VM_bufstr_set
+                                               // expand buffer
+                                               if (stringbuffer->max_strings <= i)
+                                               {
+                                                       char **oldstrings = stringbuffer->strings;
+                                                       stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
+                                                       while (stringbuffer->max_strings <= i)
+                                                               stringbuffer->max_strings *= 2;
+                                                       stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
+                                                       if (stringbuffer->num_strings > 0)
+                                                               memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
+                                                       if (oldstrings)
+                                                               Mem_Free(oldstrings);
+                                               }
+                                               // allocate string
+                                               stringbuffer->num_strings = max(stringbuffer->num_strings, k + 1);
+                                               if(stringbuffer->strings[k])
+                                                       Mem_Free(stringbuffer->strings[k]);
+                                               stringbuffer->strings[k] = NULL;
+                                               alloclen = strlen(com_token) + 1;
+                                               stringbuffer->strings[k] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
+                                               memcpy(stringbuffer->strings[k], com_token, alloclen);
+                                       }
+                               }       
                                // skip any trailing text or unrecognized commands
                                while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
                                        ;
@@ -972,7 +1077,7 @@ void Host_Loadgame_f (void)
 
        // make sure we're connected to loopback
        if (sv.active && cls.state == ca_disconnected)
-               CL_EstablishConnection("local:1");
+               CL_EstablishConnection("local:1", -2);
 }
 
 //============================================================================
@@ -1039,7 +1144,7 @@ void Host_Name_f (void)
                host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
        }
 
-       COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
+       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;
@@ -1090,7 +1195,7 @@ void Host_Name_f (void)
        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);
 
-       host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
+       PRVM_serveredictstring(host_client->edict, netname) = PRVM_SetEngineString(host_client->name);
        if (strcmp(host_client->old_name, host_client->name))
        {
                if (host_client->spawned)
@@ -1109,7 +1214,7 @@ void Host_Name_f (void)
 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
 void Host_Playermodel_f (void)
 {
@@ -1150,8 +1255,7 @@ void Host_Playermodel_f (void)
 
        // 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(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));
@@ -1167,7 +1271,7 @@ void Host_Playermodel_f (void)
 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)"};
+cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz/Xonotic (changed by playerskin command)"};
 void Host_Playerskin_f (void)
 {
        int i, j;
@@ -1207,8 +1311,7 @@ void Host_Playerskin_f (void)
 
        // 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(host_client->playerskin);
        if (strcmp(host_client->old_skin, host_client->playerskin))
        {
                //if (host_client->spawned)
@@ -1285,7 +1388,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++)
-               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;
 
@@ -1469,22 +1572,20 @@ void Host_Color(int changetop, int changebottom)
        if (cls.protocol == PROTOCOL_QUAKEWORLD)
                return;
 
-       if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
+       if (host_client->edict && PRVM_clientfunction(SV_ChangeTeam))
        {
                Con_DPrint("Calling SV_ChangeTeam\n");
-               prog->globals.server->time = sv.time;
+               PRVM_serverglobalfloat(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");
+               PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
+               PRVM_ExecuteProgram(PRVM_clientfunction(SV_ChangeTeam), "QC function SV_ChangeTeam is missing");
        }
        else
        {
-               prvm_eval_t *val;
                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)
@@ -1573,15 +1674,15 @@ Host_Kill_f
 */
 void Host_Kill_f (void)
 {
-       if (host_client->edict->fields.server->health <= 0)
+       if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
        {
                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);
+       PRVM_ExecuteProgram (PRVM_serverfunction(ClientKill), "QC function ClientKill is missing");
 }
 
 
@@ -1615,7 +1716,6 @@ cvar_t cl_pmodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_pmodel", "0", "interna
 static void Host_PModel_f (void)
 {
        int i;
-       prvm_eval_t *val;
 
        if (Cmd_Argc () == 1)
        {
@@ -1634,8 +1734,7 @@ static void Host_PModel_f (void)
                return;
        }
 
-       if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
-               val->_float = i;
+       PRVM_serveredictfloat(host_client->edict, pmodel) = i;
 }
 
 //===========================================================================
@@ -1696,32 +1795,32 @@ void Host_Spawn_f (void)
        if (sv.loadgame)
        {
                // loaded games are fully initialized already
-               if (prog->funcoffsets.RestoreGame)
+               if (PRVM_serverfunction(RestoreGame))
                {
                        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);
+                       PRVM_ExecuteProgram(PRVM_serverfunction(RestoreGame), "QC function RestoreGame is missing");
                }
        }
        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++)
-                       (&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;
-               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);
+               PRVM_ExecuteProgram (PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing");
 
                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_ExecuteProgram (PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
        }
 
        if (!host_client->netconnection)
@@ -1761,19 +1860,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);
-       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_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_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_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
@@ -1783,15 +1882,15 @@ void Host_Spawn_f (void)
        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);
        }
        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);
        }
 
@@ -1837,7 +1936,7 @@ Kicks a user off of the server
 */
 void Host_Kick_f (void)
 {
-       char *who;
+       const char *who;
        const char *message = NULL;
        client_t *save;
        int i;
@@ -1925,7 +2024,6 @@ void Host_Give_f (void)
 {
        const char *t;
        int v;
-       prvm_eval_t *val;
 
        if (!allowcheats)
        {
@@ -1954,114 +2052,91 @@ void Host_Give_f (void)
                        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
-                                       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')
-                               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')
-                               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')
-                               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')
-                               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':
-               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)
                {
-                       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
                {
-                       host_client->edict->fields.server->ammo_nails = v;
+                       PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
                }
                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)
                {
-                       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
                {
-                       host_client->edict->fields.server->ammo_rockets = v;
+                       PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
                }
                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':
-               host_client->edict->fields.server->health = v;
+               PRVM_serveredictfloat(host_client->edict, health) = v;
                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
                {
-                       host_client->edict->fields.server->ammo_cells = v;
+                       PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
                }
                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;
        }
@@ -2075,7 +2150,7 @@ prvm_edict_t      *FindViewthing (void)
        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(PRVM_serveredictstring(e, classname)), "viewthing"))
                        return e;
        }
        Con_Print("No viewthing on map\n");
@@ -2108,8 +2183,8 @@ void Host_Viewmodel_f (void)
                return;
        }
 
-       e->fields.server->frame = 0;
-       cl.model_precache[(int)e->fields.server->modelindex] = m;
+       PRVM_serveredictfloat(e, frame) = 0;
+       cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
 }
 
 /*
@@ -2131,13 +2206,13 @@ void Host_Viewframe_f (void)
        SV_VM_End();
        if (!e)
                return;
-       m = cl.model_precache[(int)e->fields.server->modelindex];
+       m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
 
        f = atoi(Cmd_Argv(1));
        if (f >= m->numframes)
                f = m->numframes-1;
 
-       e->fields.server->frame = f;
+       PRVM_serveredictfloat(e, frame) = f;
 }
 
 
@@ -2167,13 +2242,13 @@ void Host_Viewnext_f (void)
        SV_VM_End();
        if (!e)
                return;
-       m = cl.model_precache[(int)e->fields.server->modelindex];
+       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));
 }
 
 /*
@@ -2195,13 +2270,13 @@ void Host_Viewprev_f (void)
        if (!e)
                return;
 
-       m = cl.model_precache[(int)e->fields.server->modelindex];
+       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));
 }
 
 /*
@@ -2303,7 +2378,7 @@ void Host_SendCvar_f (void)
                        Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
                return;
        }
-       if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
+       if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
                return;
 
        old = host_client;
@@ -2356,25 +2431,20 @@ ProQuake rcon support
 */
 void Host_PQRcon_f (void)
 {
-       int i;
+       int n;
+       const char *e;
        lhnetaddress_t to;
        lhnetsocket_t *mysocket;
        char peer_address[64];
 
-       if (!rcon_password.string || !rcon_password.string[0])
+       if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
        {
-               Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
+               Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
                return;
        }
 
-       for (i = 0;rcon_password.string[i];i++)
-       {
-               if (ISWHITESPACE(rcon_password.string[i]))
-               {
-                       Con_Printf("rcon_password is not allowed to have any whitespace.\n");
-                       return;
-               }
-       }
+       e = strchr(rcon_password.string, ' ');
+       n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
 
        if (cls.netcon)
        {
@@ -2396,9 +2466,10 @@ void Host_PQRcon_f (void)
                SZ_Clear(&net_message);
                MSG_WriteLong (&net_message, 0);
                MSG_WriteByte (&net_message, CCREQ_RCON);
-               MSG_WriteString (&net_message, rcon_password.string);
+               SZ_Write(&net_message, (const unsigned char*)rcon_password.string, n);
+               MSG_WriteByte (&net_message, 0); // terminate the (possibly partial) string
                MSG_WriteString (&net_message, Cmd_Args());
-               *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+               StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
                NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
                SZ_Clear (&net_message);
        }
@@ -2418,7 +2489,8 @@ Host_Rcon_f
 */
 void Host_Rcon_f (void) // credit: taken from QuakeWorld
 {
-       int i;
+       int i, n;
+       const char *e;
        lhnetaddress_t to;
        lhnetsocket_t *mysocket;
 
@@ -2428,14 +2500,8 @@ void Host_Rcon_f (void) // credit: taken from QuakeWorld
                return;
        }
 
-       for (i = 0;rcon_password.string[i];i++)
-       {
-               if (ISWHITESPACE(rcon_password.string[i]))
-               {
-                       Con_Printf("rcon_password is not allowed to have any whitespace.\n");
-                       return;
-               }
-       }
+       e = strchr(rcon_password.string, ' ');
+       n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
 
        if (cls.netcon)
                to = cls.netcon->peeraddress;
@@ -2449,16 +2515,38 @@ void Host_Rcon_f (void) // credit: taken from QuakeWorld
                LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
        }
        mysocket = NetConn_ChooseClientSocketForAddress(&to);
-       if (mysocket)
+       if (mysocket && Cmd_Args()[0])
        {
                // simply put together the rcon packet and send it
-               if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer)
+               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(&to, &cls.rcon_addresses[i]))
+                                               break;
+                       ++cls.rcon_trying;
+                       if(i >= MAX_RCONS)
+                               NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // 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] = to;
+                       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, strlen(argbuf), (unsigned char *) rcon_password.string, strlen(rcon_password.string)))
+                       if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
                        {
                                buf[40] = ' ';
                                strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
@@ -2467,7 +2555,7 @@ void Host_Rcon_f (void) // credit: taken from QuakeWorld
                }
                else
                {
-                       NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to);
+                       NetConn_WriteString(mysocket, va("\377\377\377\377rcon %.*s %s", n, rcon_password.string, Cmd_Args()), &to);
                }
        }
 }
@@ -2660,7 +2748,7 @@ void Host_Packet_f (void) // credit: taken from QuakeWorld
 
        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++)
@@ -2712,7 +2800,7 @@ Send back ping and packet loss update for all current players to this player
 */
 void Host_Pings_f (void)
 {
-       int             i, j, ping, packetloss;
+       int             i, j, ping, packetloss, movementloss;
        char temp[128];
 
        if (!host_client->netconnection)
@@ -2726,11 +2814,18 @@ void Host_Pings_f (void)
        for (i = 0;i < svs.maxclients;i++)
        {
                packetloss = 0;
+               movementloss = 0;
                if (svs.clients[i].netconnection)
+               {
                        for (j = 0;j < NETGRAPH_PACKETS;j++)
-                               if (svs.clients[i].netconnection->incoming_unreliablesize[j] == NETGRAPH_LOSTPACKET)
+                               if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
                                        packetloss++;
-               packetloss = packetloss * 100 / NETGRAPH_PACKETS;
+                       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)
@@ -2744,7 +2839,10 @@ void Host_Pings_f (void)
                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);
                }
        }
@@ -2754,6 +2852,7 @@ void Host_Pings_f (void)
 
 void Host_PingPLReport_f(void)
 {
+       char *errbyte;
        int i;
        int l = Cmd_Argc();
        if (l > cl.maxclients)
@@ -2761,7 +2860,11 @@ void Host_PingPLReport_f(void)
        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;
        }
 }
 
@@ -2778,22 +2881,11 @@ void Host_InitCommands (void)
 
        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");
@@ -2825,11 +2917,8 @@ 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");
-       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_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);
@@ -2847,6 +2936,7 @@ void Host_InitCommands (void)
        Cvar_RegisterVariable (&rcon_password);
        Cvar_RegisterVariable (&rcon_address);
        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)");