X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=host_cmd.c;h=ee974ac607e302c71c4ff19eb8208a44c98a40a8;hb=5090f6da276d673f6ee0b149ee2f83ef82f62902;hp=ee5fdcdf0240b9a002130291732e1bcf282f551e;hpb=028f30e0d2b47fe698ac49e2a777c49b55393627;p=xonotic%2Fdarkplaces.git diff --git a/host_cmd.c b/host_cmd.c index ee5fdcdf..ee974ac6 100644 --- a/host_cmd.c +++ b/host_cmd.c @@ -19,15 +19,19 @@ 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; @@ -46,7 +50,6 @@ void Host_Quit_f (void) Sys_Quit (0); } - /* ================== Host_Status_f @@ -81,6 +84,7 @@ void Host_Status_f (void) print ("version: %s build %s\n", gamename, buildstring); print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol)); print ("map: %s\n", sv.name); + print ("timing: %s\n", Host_TimingReport()); print ("players: %i active (%i max)\n\n", players, svs.maxclients); for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++) { @@ -98,7 +102,10 @@ void Host_Status_f (void) 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"); + if(sv_status_privacy.integer && cmd_source != src_command) + print (" %s\n", client->netconnection ? "hidden" : "botclient"); + else + print (" %s\n", client->netconnection ? client->netconnection->address : "botclient"); } } @@ -261,6 +268,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 (); @@ -428,32 +439,101 @@ LOAD / SAVE GAME #define SAVEGAME_VERSION 5 -/* -=============== -Host_SavegameComment - -Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current -=============== -*/ -void Host_SavegameComment (char *text) +void Host_Savegame_to (const char *name) { - int i; - char kills[20]; + qfile_t *f; + int i, lightstyles = 64; + char comment[SAVEGAME_COMMENT_LENGTH+1]; + qboolean isserver; - for (i=0 ; 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 ; ifields.server->deadflag) { - if (i > 0) - { - Con_Print("Can't save multiplayer games.\n"); - return; - } - if (svs.clients[i].edict->fields.server->deadflag) - { - Con_Print("Can't savegame with a dead player\n"); - return; - } + Con_Print("Can't savegame with a dead player\n"); + return; } } + else + Con_Print("Warning: saving a multiplayer game may have strange results when restored (to properly resume, all players must join in the same player slots and then the game can be reloaded).\n"); if (Cmd_Argc() != 2) { @@ -511,42 +583,9 @@ void Host_Savegame_f (void) strlcpy (name, Cmd_Argv(1), sizeof (name)); FS_DefaultExtension (name, ".sav", sizeof (name)); - Con_Printf("Saving game to %s...\n", name); - f = FS_Open (name, "wb", false, false); - if (!f) - { - Con_Print("ERROR: couldn't open.\n"); - return; - } - - FS_Printf(f, "%i\n", SAVEGAME_VERSION); - Host_SavegameComment (comment); - FS_Printf(f, "%s\n", comment); - for (i=0 ; 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"); } @@ -561,8 +600,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; @@ -581,6 +620,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); @@ -591,7 +637,7 @@ void Host_Loadgame_f (void) } // version - COM_ParseToken_Simple(&t, false); + COM_ParseToken_Simple(&t, false, false); version = atoi(com_token); if (version != SAVEGAME_VERSION) { @@ -601,25 +647,25 @@ void Host_Loadgame_f (void) } // description - COM_ParseToken_Simple(&t, false); + COM_ParseToken_Simple(&t, false, false); for (i = 0;i < NUM_SPAWN_PARMS;i++) { - COM_ParseToken_Simple(&t, false); + COM_ParseToken_Simple(&t, false, false); spawn_parms[i] = atof(com_token); } // skill - COM_ParseToken_Simple(&t, false); + COM_ParseToken_Simple(&t, false, false); // this silliness is so we can load 1.06 save files, which have float skill values current_skill = (int)(atof(com_token) + 0.5); Cvar_SetValue ("skill", (float)current_skill); // mapname - COM_ParseToken_Simple(&t, false); + COM_ParseToken_Simple(&t, false, false); strlcpy (mapname, com_token, sizeof(mapname)); // time - COM_ParseToken_Simple(&t, false); + COM_ParseToken_Simple(&t, false, false); time = atof(com_token); allowcheats = sv_cheats.integer != 0; @@ -636,16 +682,20 @@ void Host_Loadgame_f (void) // load the light styles + SV_VM_Begin(); + // -1 is the globals + entnum = -1; + for (i = 0;i < MAX_LIGHTSTYLES;i++) { // light style - oldt = t; - COM_ParseToken_Simple(&t, false); + 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])); @@ -655,28 +705,27 @@ void Host_Loadgame_f (void) // (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); + 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; - while (COM_ParseToken_Simple(&t, false)) + while (COM_ParseToken_Simple(&t, false, false)) if (!strcmp(com_token, "}")) break; - if (!COM_ParseToken_Simple(&start, false)) + if (!COM_ParseToken_Simple(&start, false, false)) { // end of file break; @@ -712,6 +761,7 @@ void Host_Loadgame_f (void) SV_LinkEdict (ent, false); } + end = t; entnum++; } Mem_Free(text); @@ -722,10 +772,63 @@ void Host_Loadgame_f (void) for (i = 0;i < NUM_SPAWN_PARMS;i++) svs.clients[0].spawn_parms[i] = spawn_parms[i]; + // 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 (!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")) + ; + } + } + SV_VM_End(); // make sure we're connected to loopback - if (cls.state == ca_disconnected || !(cls.state == ca_connected && cls.netcon != NULL && LHNETADDRESS_GetAddressType(&cls.netcon->peeraddress) == LHNETADDRESSTYPE_LOOP)) + if (sv.active && cls.state == ca_disconnected) CL_EstablishConnection("local:1"); } @@ -776,6 +879,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 { @@ -831,6 +943,7 @@ void Host_Name_f (void) MSG_WriteByte (&sv.reliable_datagram, svc_updatename); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteString (&sv.reliable_datagram, host_client->name); + SV_WriteNetnameIntoDemo(host_client); } } @@ -1063,11 +1176,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); @@ -1426,9 +1539,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"); @@ -1451,8 +1561,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"); } @@ -1519,7 +1629,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 { @@ -1543,6 +1652,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 + } + } } //=========================================================================== @@ -1606,7 +1729,7 @@ void Host_Kick_f (void) if (Cmd_Argc() > 2) { message = Cmd_Args(); - COM_ParseToken_Simple(&message, false); + COM_ParseToken_Simple(&message, false, false); if (byNumber) { message++; // skip the # @@ -1810,7 +1933,7 @@ Host_Viewmodel_f void Host_Viewmodel_f (void) { prvm_edict_t *e; - model_t *m; + dp_model_t *m; if (!sv.active) return; @@ -1841,7 +1964,7 @@ void Host_Viewframe_f (void) { prvm_edict_t *e; int f; - model_t *m; + dp_model_t *m; if (!sv.active) return; @@ -1861,7 +1984,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); @@ -1877,7 +2000,7 @@ Host_Viewnext_f void Host_Viewnext_f (void) { prvm_edict_t *e; - model_t *m; + dp_model_t *m; if (!sv.active) return; @@ -1904,7 +2027,7 @@ Host_Viewprev_f void Host_Viewprev_f (void) { prvm_edict_t *e; - model_t *m; + dp_model_t *m; if (!sv.active) return; @@ -1951,7 +2074,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