X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=host_cmd.c;h=b80cf5638085f0632361ad9d8784379c0179a934;hb=c8a7a9405df85fe36fc92c145410040df80b0581;hp=21c8690ab492e94af922be38f158dbb85dd6dd04;hpb=40a9038baf65f593ddbf4117c618242953dedefa;p=xonotic%2Fdarkplaces.git diff --git a/host_cmd.c b/host_cmd.c index 21c8690a..b80cf563 100644 --- a/host_cmd.c +++ b/host_cmd.c @@ -20,18 +20,22 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quakedef.h" #include "sv_demo.h" +#include "image.h" int current_skill; cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"}; cvar_t sv_adminnick = {CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"}; +cvar_t sv_status_privacy = {CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"}; 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)"}; cvar_t skin = {CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"}; cvar_t noaim = {CVAR_USERINFO | CVAR_SAVE, "noaim", "1", "QW option to disable vertical autoaim"}; +cvar_t r_fixtrans_auto = {0, "r_fixtrans_auto", "0", "automatically fixtrans textures (when set to 2, it also saves the fixed versions to a fixtrans directory)"}; qboolean allowcheats = false; extern qboolean host_shuttingdown; +extern cvar_t developer_entityparsing; /* ================== @@ -55,9 +59,10 @@ Host_Status_f void Host_Status_f (void) { 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]; + if (cmd_source == src_command) { // if running a client, try to send over network so the client's status report parser will see the report @@ -73,9 +78,18 @@ void Host_Status_f (void) if (!sv.active) return; + + 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); @@ -83,23 +97,73 @@ 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); } + + if(sv_status_privacy.integer && cmd_source != src_command) + strlcpy(ip, client->netconnection ? "hidden" : "botclient" , 22); else - hours = 0; - print ("#%-3u %-16.16s %3i %2i:%02i:%02i\n", j+1, client->name, client->frags, hours, minutes, seconds); - print (" %s\n", client->netconnection ? client->netconnection->address : "botclient"); + strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 22); + + if (in == 0) // default layout + { + print ("#%-3u ", i+1); + print ("%-16.16s ", client->name); + print ("%3i ", client->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 ", client->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); + } } } @@ -262,6 +326,10 @@ void Host_Map_f (void) return; } + // GAME_DELUXEQUAKE - clear warpmark (used by QC) + if (gamemode == GAME_DELUXEQUAKE) + Cvar_Set("warpmark", ""); + cls.demonum = -1; // stop demo loop in case this fails CL_Disconnect (); @@ -429,6 +497,102 @@ LOAD / SAVE GAME #define SAVEGAME_VERSION 5 +void Host_Savegame_to (const char *name) +{ + qfile_t *f; + int i, lightstyles = 64; + char comment[SAVEGAME_COMMENT_LENGTH+1]; + qboolean isserver; + + // first we have to figure out if this can be saved in 64 lightstyles + // (for Quake compatibility) + for (i=64 ; iedicts->fields.server->message), (int)prog->globals.server->killed_monsters, (int)prog->globals.server->total_monsters); + else + dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", PRVM_NAME); + // convert space to _ to make stdio happy + // LordHavoc: convert control characters to _ as well + for (i=0 ; inum_edicts ; i++) + { + FS_Printf(f,"// edict %d\n", i); + //Con_Printf("edict %d...\n", i); + PRVM_ED_Write (f, PRVM_EDICT_NUM(i)); + } + +#if 1 + FS_Printf(f,"/*\n"); + FS_Printf(f,"// DarkPlaces extended savegame\n"); + // darkplaces extension - extra lightstyles, support for color lightstyles + for (i=0 ; iedicts->fields.server->message), (int)prog->globals.server->killed_monsters, (int)prog->globals.server->total_monsters); - // convert space to _ to make stdio happy - // LordHavoc: convert control characters to _ as well - for (i=0 ; inum_edicts ; i++) - PRVM_ED_Write (f, PRVM_EDICT_NUM(i)); - + Host_Savegame_to(name); SV_VM_End(); - - FS_Close (f); - Con_Print("done.\n"); } @@ -539,8 +658,8 @@ void Host_Loadgame_f (void) char mapname[MAX_QPATH]; float time; const char *start; + const char *end; const char *t; - const char *oldt; char *text; prvm_edict_t *ent; int i; @@ -559,6 +678,13 @@ void Host_Loadgame_f (void) Con_Printf("Loading game from %s...\n", filename); + // stop playing demos + if (cls.demoplayback) + CL_Disconnect (); + + // remove menu + key_dest = key_game; + cls.demonum = -1; // stop demo loop in case this fails t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL); @@ -568,6 +694,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); @@ -578,6 +707,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); @@ -592,16 +724,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) { @@ -612,42 +753,51 @@ 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(); + // -1 is the globals + entnum = -1; + for (i = 0;i < MAX_LIGHTSTYLES;i++) { // light style - oldt = t; + start = t; COM_ParseToken_Simple(&t, false, false); // if this is a 64 lightstyle savegame produced by Quake, stop now - // we have to check this because darkplaces saves 256 lightstyle savegames + // we have to check this because darkplaces may save more than 64 if (com_token[0] == '{') { - t = oldt; + t = start; break; } strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i])); } + if(developer_entityparsing.integer) + Con_Printf("Host_Loadgame_f: skipping until globals\n"); + // now skip everything before the first opening brace // (this is for forward compatibility, so that older versions (at // least ones with this fix) can load savegames with extra data before the // first brace, as might be produced by a later engine version) - for(;;) + for (;;) { - oldt = t; - COM_ParseToken_Simple(&t, false, false); + start = t; + if (!COM_ParseToken_Simple(&t, false, false)) + break; if (com_token[0] == '{') { - t = oldt; + t = start; break; } } // load the edicts out of the savegame file - SV_VM_Begin(); - // -1 is the globals - entnum = -1; + end = t; for (;;) { start = t; @@ -667,6 +817,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); } @@ -683,6 +836,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 @@ -690,9 +847,9 @@ void Host_Loadgame_f (void) SV_LinkEdict (ent, false); } + end = t; entnum++; } - Mem_Free(text); prog->num_edicts = entnum; sv.time = time; @@ -700,6 +857,72 @@ 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 + if(end) + { + while (*end == '\r' || *end == '\n') + end++; + if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n')) + { + if(developer_entityparsing.integer) + Con_Printf("Host_Loadgame_f: loading extended data\n"); + + Con_Printf("Loading extended DarkPlaces savegame\n"); + t = end + 2; + memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles)); + memset(sv.model_precache[0], 0, sizeof(sv.model_precache)); + memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache)); + while (COM_ParseToken_Simple(&t, false, false)) + { + 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")) + { + 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, false); + } + 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")) + ; + } + } + } + Mem_Free(text); + + if(developer_entityparsing.integer) + Con_Printf("Host_Loadgame_f: finished\n"); + SV_VM_End(); // make sure we're connected to loopback @@ -754,6 +977,15 @@ void Host_Name_f (void) host_client->name[j++] = host_client->name[i]; host_client->name[j] = 0; + if(host_client->name[0] == 1 || host_client->name[0] == 2) + // may interfere with chat area, and will needlessly beep; so let's add a ^7 + { + memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2); + host_client->name[sizeof(host_client->name) - 1] = 0; + host_client->name[0] = STRING_COLOR_TAG; + host_client->name[1] = '0' + STRING_COLOR_DEFAULT; + } + 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 { @@ -803,7 +1035,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); @@ -1042,11 +1274,11 @@ void Host_Tell_f(void) // note this uses the chat prefix \001 if (!fromServer) - sprintf (text, "\001%s tells you: ", host_client->name); + dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name); else if(*(sv_adminnick.string)) - sprintf (text, "\001<%s tells you> ", sv_adminnick.string); + dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string); else - sprintf (text, "\001<%s tells you> ", hostname.string); + dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string); p1 = Cmd_Args(); p2 = p1 + strlen(p1); @@ -1405,9 +1637,6 @@ void Host_Spawn_f (void) if (sv.loadgame) { // loaded games are fully initialized already - // if this is the last client to be connected, unpause - sv.paused = false; - if (prog->funcoffsets.RestoreGame) { Con_DPrint("Calling RestoreGame\n"); @@ -1430,8 +1659,8 @@ void Host_Spawn_f (void) prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict); PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing"); - if (svs.maxclients > 1 || cls.state == ca_dedicated) - Con_Printf("%s entered the game\n", host_client->name); + if (cls.state == ca_dedicated) + Con_Printf("%s connected\n", host_client->name); PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing"); } @@ -1498,7 +1727,6 @@ void Host_Spawn_f (void) 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, 0, sv.protocol); - sv.loadgame = false; // we're basically done with loading now } else { @@ -1522,6 +1750,20 @@ Host_Begin_f void Host_Begin_f (void) { host_client->spawned = true; + + // LordHavoc: note: this code also exists in SV_DropClient + if (sv.loadgame) + { + int i; + for (i = 0;i < svs.maxclients;i++) + if (svs.clients[i].active && !svs.clients[i].spawned) + break; + if (i == svs.maxclients) + { + Con_Printf("Loaded game, everyone rejoined - unpausing\n"); + sv.paused = sv.loadgame = false; // we're basically done with loading now + } + } } //=========================================================================== @@ -1789,7 +2031,7 @@ Host_Viewmodel_f void Host_Viewmodel_f (void) { prvm_edict_t *e; - model_t *m; + dp_model_t *m; if (!sv.active) return; @@ -1820,7 +2062,7 @@ void Host_Viewframe_f (void) { prvm_edict_t *e; int f; - model_t *m; + dp_model_t *m; if (!sv.active) return; @@ -1840,7 +2082,7 @@ void Host_Viewframe_f (void) } -void PrintFrameName (model_t *m, int frame) +void PrintFrameName (dp_model_t *m, int frame) { if (m->animscenes) Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name); @@ -1856,7 +2098,7 @@ Host_Viewnext_f void Host_Viewnext_f (void) { prvm_edict_t *e; - model_t *m; + dp_model_t *m; if (!sv.active) return; @@ -1883,7 +2125,7 @@ Host_Viewprev_f void Host_Viewprev_f (void) { prvm_edict_t *e; - model_t *m; + dp_model_t *m; if (!sv.active) return; @@ -1930,7 +2172,7 @@ void Host_Startdemos_f (void) Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS); c = MAX_DEMOS; } - Con_Printf("%i demo(s) in loop\n", c); + Con_DPrintf("%i demo(s) in loop\n", c); for (i=1 ; i