]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - host_cmd.c
a nice improvement to the loading plaque: displaying the name of what's being loaded
[xonotic/darkplaces.git] / host_cmd.c
index c0a38fca9f3799a96fe68a1d1424979764ff29f3..6e3a2abcce66d3dac7f37c2c7d0b494dab799eab 100644 (file)
@@ -26,6 +26,7 @@ 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 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"};
 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)"};
@@ -35,6 +36,7 @@ cvar_t r_fixtrans_auto = {0, "r_fixtrans_auto", "0", "automatically fixtrans tex
 qboolean allowcheats = false;
 
 extern qboolean host_shuttingdown;
+extern cvar_t developer_entityparsing;
 
 /*
 ==================
@@ -57,9 +59,12 @@ Host_Status_f
 */
 void Host_Status_f (void)
 {
+       char qcstatus[256];
        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, ...);
+       char ip[22];
+       int frags;
 
        if (cmd_source == src_command)
        {
@@ -76,9 +81,21 @@ void Host_Status_f (void)
 
        if (!sv.active)
                return;
+       
+       if(cmd_source == src_command)
+               SV_VM_Begin();
+       
+       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, j = 0;j < svs.maxclients;j++)
-               if (svs.clients[j].active)
+       for (players = 0, i = 0;i < svs.maxclients;i++)
+               if (svs.clients[i].active)
                        players++;
        print ("host:     %s\n", Cvar_VariableString ("hostname"));
        print ("version:  %s build %s\n", gamename, buildstring);
@@ -86,27 +103,96 @@ void Host_Status_f (void)
        print ("map:      %s\n", sv.name);
        print ("timing:   %s\n", Host_TimingReport());
        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;
-               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_unreliablesize[j] == NETGRAPH_LOSTPACKET)
+                                               packetloss++;
+                       packetloss = packetloss * 100 / NETGRAPH_PACKETS;
+                       ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
                }
-               else
-                       hours = 0;
-               print ("#%-3u %-16.16s  %3i  %2i:%02i:%02i\n", j+1, client->name, client->frags, hours, minutes, seconds);
+
                if(sv_status_privacy.integer && cmd_source != src_command)
-                       print ("   %s\n", client->netconnection ? "hidden" : "botclient");
+                       strlcpy(ip, client->netconnection ? "hidden" : "botclient" , 22);
                else
-                       print ("   %s\n", client->netconnection ? client->netconnection->address : "botclient");
+                       strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 22);
+
+               frags = client->frags;
+
+               if(sv_status_show_qcstatus.integer && prog->fieldoffsets.clientstatus >= 0)
+               {
+                       const char *str = PRVM_E_STRING(PRVM_EDICT_NUM(i + 1), prog->fieldoffsets.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
+               {
+                       print ("#%-3u ", i+1);
+                       print ("%-16.16s ", client->name);
+                       print ("%4i  ", frags);
+                       print ("%2i:%02i:%02i\n   ", 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);
+               }
+               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);
+               }
        }
+
+       if(cmd_source == src_command)
+               SV_VM_End();
 }
 
 
@@ -277,6 +363,14 @@ void Host_Map_f (void)
        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);
+       }
+
        // remove menu
        key_dest = key_game;
 
@@ -472,7 +566,7 @@ void Host_Savegame_to (const char *name)
        // convert space to _ to make stdio happy
        // LordHavoc: convert control characters to _ as well
        for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
-               if (comment[i] <= ' ')
+               if (ISWHITESPACEORCONTROL(comment[i]))
                        comment[i] = '_';
        comment[SAVEGAME_COMMENT_LENGTH] = '\0';
 
@@ -636,6 +730,9 @@ void Host_Loadgame_f (void)
                return;
        }
 
+       if(developer_entityparsing.integer)
+               Con_Printf("Host_Loadgame_f: loading version\n");
+
        // version
        COM_ParseToken_Simple(&t, false, false);
        version = atoi(com_token);
@@ -646,6 +743,9 @@ void Host_Loadgame_f (void)
                return;
        }
 
+       if(developer_entityparsing.integer)
+               Con_Printf("Host_Loadgame_f: loading description\n");
+
        // description
        COM_ParseToken_Simple(&t, false, false);
 
@@ -660,16 +760,25 @@ void Host_Loadgame_f (void)
        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
        COM_ParseToken_Simple(&t, false, false);
        strlcpy (mapname, com_token, sizeof(mapname));
 
+       if(developer_entityparsing.integer)
+               Con_Printf("Host_Loadgame_f: loading time\n");
+
        // time
        COM_ParseToken_Simple(&t, false, false);
        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)
        {
@@ -680,6 +789,9 @@ void Host_Loadgame_f (void)
        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
 
        SV_VM_Begin();
@@ -701,6 +813,9 @@ void Host_Loadgame_f (void)
                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
@@ -738,6 +853,9 @@ void Host_Loadgame_f (void)
 
                if (entnum == -1)
                {
+                       if(developer_entityparsing.integer)
+                               Con_Printf("Host_Loadgame_f: loading globals\n");
+
                        // parse the global vars
                        PRVM_ED_ParseGlobals (start);
                }
@@ -754,6 +872,10 @@ void Host_Loadgame_f (void)
                        ent = PRVM_EDICT_NUM(entnum);
                        memset (ent->fields.server, 0, prog->progs->entityfields * 4);
                        ent->priv.server->free = false;
+
+                       if(developer_entityparsing.integer)
+                               Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
+
                        PRVM_ED_ParseEdict (start, ent);
 
                        // link it into the bsp tree
@@ -764,7 +886,6 @@ void Host_Loadgame_f (void)
                end = t;
                entnum++;
        }
-       Mem_Free(text);
 
        prog->num_edicts = entnum;
        sv.time = time;
@@ -772,58 +893,71 @@ void Host_Loadgame_f (void)
        for (i = 0;i < NUM_SPAWN_PARMS;i++)
                svs.clients[0].spawn_parms[i] = spawn_parms[i];
 
+       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
-       while (*end == '\r' || *end == '\n')
-               end++;
-       if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\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));
-               while (COM_ParseToken_Simple(&t, false, false))
+       if(end)
+       {
+               while (*end == '\r' || *end == '\n')
+                       end++;
+               if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
                {
-                       if (!strcmp(com_token, "sv.lightstyles"))
-                       {
-                               COM_ParseToken_Simple(&t, false, false);
-                               i = atoi(com_token);
-                               COM_ParseToken_Simple(&t, false, false);
-                               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"))
+                       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));
+                       while (COM_ParseToken_Simple(&t, false, false))
                        {
-                               COM_ParseToken_Simple(&t, false, false);
-                               i = atoi(com_token);
-                               COM_ParseToken_Simple(&t, false, false);
-                               if (i >= 0 && i < MAX_MODELS)
+                               if (!strcmp(com_token, "sv.lightstyles"))
                                {
-                                       strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
-                                       sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, false);
+                                       COM_ParseToken_Simple(&t, false, false);
+                                       i = atoi(com_token);
+                                       COM_ParseToken_Simple(&t, false, false);
+                                       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
-                                       Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
-                       }
-                       else if (!strcmp(com_token, "sv.sound_precache"))
-                       {
-                               COM_ParseToken_Simple(&t, false, false);
-                               i = atoi(com_token);
-                               COM_ParseToken_Simple(&t, false, false);
-                               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.model_precache"))
+                               {
+                                       COM_ParseToken_Simple(&t, false, false);
+                                       i = atoi(com_token);
+                                       COM_ParseToken_Simple(&t, false, false);
+                                       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);
+                                       }
+                                       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);
+                                       i = atoi(com_token);
+                                       COM_ParseToken_Simple(&t, false, false);
+                                       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);
+                               }
+                               // skip any trailing text or unrecognized commands
+                               while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
+                                       ;
                        }
-                       // skip any trailing text or unrecognized commands
-                       while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
-                               ;
                }
        }
+       Mem_Free(text);
+
+       if(developer_entityparsing.integer)
+               Con_Printf("Host_Loadgame_f: finished\n");
 
        SV_VM_End();
 
@@ -922,6 +1056,12 @@ void Host_Name_f (void)
                                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++;
@@ -937,7 +1077,7 @@ void Host_Name_f (void)
        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);
+                       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);
@@ -1944,7 +2084,7 @@ void Host_Viewmodel_f (void)
        if (!e)
                return;
 
-       m = Mod_ForName (Cmd_Argv(1), false, true, false);
+       m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
        if (!m || !m->loaded || !m->Draw)
        {
                Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
@@ -2169,24 +2309,21 @@ static void MaxPlayers_f(void)
 
        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;
+               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);
 
-       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
@@ -2219,7 +2356,7 @@ void Host_Rcon_f (void) // credit: taken from QuakeWorld
 
        for (i = 0;rcon_password.string[i];i++)
        {
-               if (rcon_password.string[i] <= ' ')
+               if (ISWHITESPACE(rcon_password.string[i]))
                {
                        Con_Printf("rcon_password is not allowed to have any whitespace.\n");
                        return;
@@ -2642,6 +2779,7 @@ void Host_InitCommands (void)
        Cvar_RegisterVariable(&sv_cheats);
        Cvar_RegisterVariable(&sv_adminnick);
        Cvar_RegisterVariable(&sv_status_privacy);
+       Cvar_RegisterVariable(&sv_status_show_qcstatus);
 }
 
 void Host_NoOperation_f(void)