]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - host_cmd.c
autocvars: update to current value after loading a savegame, to always fulfill the...
[xonotic/darkplaces.git] / host_cmd.c
index 5c1f0969d90e16558938df19c185739895a1e628..202e4d98837545e80f451366f854af79b9a7cca2 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,7 +34,7 @@ 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_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)"};
@@ -142,7 +144,7 @@ void Host_Status_f (void)
                                for (j = 0;j < NETGRAPH_PACKETS;j++)
                                        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);
                }
 
@@ -172,8 +174,14 @@ void Host_Status_f (void)
                
                if (in == 0) // default layout
                {
-                       print ("#%-3u %-16.16s  %3i  %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
-                       print ("  %s\n", ip);
+                       // LordHavoc: we must use multiple prints for ProQuake compatibility
+                       print ("#%-3u ", i+1);
+                       print ("%-16.16s ", client->name);
+                       print ("%4i  ", frags);
+                       print ("%2i:%02i:%02i\n   ", hours, minutes, seconds);
+                       print ("%s\n", ip);
+//                     print ("#%-3u %-16.16s  %3i  %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
+//                     print ("   %s\n", ip);
                }
                else if (in == 1) // extended layout
                {
@@ -366,6 +374,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
@@ -399,6 +409,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();
@@ -434,6 +446,8 @@ 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;
@@ -514,7 +528,7 @@ void Host_Connect_f (void)
                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));
 }
@@ -533,9 +547,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)
@@ -619,6 +635,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
 
@@ -685,6 +746,7 @@ void Host_Savegame_f (void)
 Host_Loadgame_f
 ===============
 */
+
 void Host_Loadgame_f (void)
 {
        char filename[MAX_QPATH];
@@ -695,10 +757,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)
        {
@@ -716,6 +780,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
@@ -829,6 +895,9 @@ void Host_Loadgame_f (void)
                }
        }
 
+       // unlink all entities
+       World_UnlinkAll(&sv.world);
+
 // load the edicts out of the savegame file
        end = t;
        for (;;)
@@ -855,6 +924,9 @@ void Host_Loadgame_f (void)
 
                        // parse the global vars
                        PRVM_ED_ParseGlobals (start);
+
+                       // restore the autocvar globals
+                       Cvar_UpdateAllAutoCvars();
                }
                else
                {
@@ -930,7 +1002,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);
@@ -945,6 +1017,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"))
                                        ;
@@ -1027,7 +1139,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;
@@ -2344,25 +2456,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] || rcon_secure.integer)
+       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;
        }
 
-       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)
        {
@@ -2384,9 +2491,9 @@ 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, (void*)rcon_password.string, n);
                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);
        }
@@ -2406,7 +2513,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;
 
@@ -2416,14 +2524,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;
@@ -2462,13 +2564,13 @@ void Host_Rcon_f (void) // credit: taken from QuakeWorld
                        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)
+               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);
@@ -2477,7 +2579,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);
                }
        }
 }
@@ -2722,7 +2824,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)
@@ -2736,11 +2838,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_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)
@@ -2754,7 +2863,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);
                }
        }
@@ -2764,6 +2876,7 @@ void Host_Pings_f (void)
 
 void Host_PingPLReport_f(void)
 {
+       char *errbyte;
        int i;
        int l = Cmd_Argc();
        if (l > cl.maxclients)
@@ -2771,7 +2884,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;
        }
 }