X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=host_cmd.c;h=949c4802cda10906b1ab76b66150a95ceac4a2f1;hb=69bf6601c7604abc7afd9085c687c326c7a805e3;hp=b69389c1d91ae43fd3145b51ba49274daf8eab13;hpb=e41d0c1d5a96671a8337222ad74edb76c27e969f;p=xonotic%2Fdarkplaces.git diff --git a/host_cmd.c b/host_cmd.c index b69389c1..949c4802 100644 --- a/host_cmd.c +++ b/host_cmd.c @@ -19,18 +19,30 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" +#include "sv_demo.h" +#include "image.h" + +// for secure rcon authentication +#include "hmac.h" +#include "mdfour.h" +#include 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 rcon_password = {CVAR_PRIVATE, "rcon_password", "", "password to authenticate rcon commands"}; +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_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; /* ================== @@ -46,7 +58,6 @@ void Host_Quit_f (void) Sys_Quit (0); } - /* ================== Host_Status_f @@ -54,9 +65,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) { @@ -73,33 +87,118 @@ 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); 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++) + + 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); + + 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(); } @@ -261,11 +360,23 @@ 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 (); 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; @@ -414,6 +525,9 @@ void Host_Connect_f (void) Con_Print("connect : connect to a multiplayer game\n"); return; } + // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command + if(!rcon_secure.integer) + Cvar_SetQuick(&rcon_password, ""); CL_EstablishConnection(Cmd_Argv(1)); } @@ -428,32 +542,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 +686,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 +703,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 +723,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); @@ -590,8 +739,11 @@ void Host_Loadgame_f (void) return; } + if(developer_entityparsing.integer) + Con_Printf("Host_Loadgame_f: loading version\n"); + // version - COM_ParseToken_Simple(&t, false); + COM_ParseToken_Simple(&t, false, false); version = atoi(com_token); if (version != SAVEGAME_VERSION) { @@ -600,30 +752,42 @@ void Host_Loadgame_f (void) return; } + if(developer_entityparsing.integer) + Con_Printf("Host_Loadgame_f: loading description\n"); + // 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); + if(developer_entityparsing.integer) + Con_Printf("Host_Loadgame_f: loading mapname\n"); + // mapname - COM_ParseToken_Simple(&t, false); + 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); + 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) { @@ -634,49 +798,58 @@ 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; - 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])); } + 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); + 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; @@ -689,6 +862,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); } @@ -705,6 +881,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 @@ -712,9 +892,9 @@ void Host_Loadgame_f (void) SV_LinkEdict (ent, false); } + end = t; entnum++; } - Mem_Free(text); prog->num_edicts = entnum; sv.time = time; @@ -722,10 +902,76 @@ 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, 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")) + ; + } + } + } + Mem_Free(text); + + if(developer_entityparsing.integer) + Con_Printf("Host_Loadgame_f: finished\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"); } @@ -741,27 +987,30 @@ void Host_Name_f (void) { int i, j; qboolean valid_colors; + const char *newNameSource; char newName[sizeof(host_client->name)]; if (Cmd_Argc () == 1) { - Con_Printf("\"name\" is \"%s\"\n", cl_name.string); + Con_Printf("name: %s\n", cl_name.string); return; } if (Cmd_Argc () == 2) - strlcpy (newName, Cmd_Argv(1), sizeof (newName)); + newNameSource = Cmd_Argv(1); else - strlcpy (newName, Cmd_Args(), sizeof (newName)); + newNameSource = Cmd_Args(); - for (i = 0, j = 0;newName[i];i++) - if (newName[i] != '\r' && newName[i] != '\n') - newName[j++] = newName[i]; - newName[j] = 0; + strlcpy(newName, newNameSource, sizeof(newName)); if (cmd_source == src_command) { Cvar_Set ("_cl_name", newName); + if (strlen(newNameSource) >= sizeof(newName)) // overflowed + { + Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1)); + Con_Printf("name: %s\n", cl_name.string); + } return; } @@ -776,6 +1025,20 @@ void Host_Name_f (void) // point the string back at updateclient->name to keep it safe strlcpy (host_client->name, newName, sizeof (host_client->name)); + for (i = 0, j = 0;host_client->name[i];i++) + if (host_client->name[i] != '\r' && host_client->name[i] != '\n') + 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 { @@ -810,6 +1073,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++; @@ -825,12 +1094,13 @@ 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); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteString (&sv.reliable_datagram, host_client->name); + SV_WriteNetnameIntoDemo(host_client); } } @@ -1063,11 +1333,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 +1696,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 +1718,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 +1786,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 +1809,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 +1886,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 +2090,7 @@ Host_Viewmodel_f void Host_Viewmodel_f (void) { prvm_edict_t *e; - model_t *m; + dp_model_t *m; if (!sv.active) return; @@ -1821,7 +2101,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)); @@ -1841,7 +2121,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 +2141,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 +2157,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 +2184,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 +2231,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