/host_cmd.c
C | 3109 lines | 2480 code | 278 blank | 351 comment | 609 complexity | deafedcc4a2cfded86908db65dc97312 MD5 | raw file
Possible License(s): GPL-2.0
Large files files are truncated, but you can click here to view the full file
- /*
- Copyright (C) 1996-1997 Id Software, Inc.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
- #include "quakedef.h"
- #include "sv_demo.h"
- #include "image.h"
- #include "prvm_cmds.h"
- #include "utf8lib.h"
- // for secure rcon authentication
- #include "hmac.h"
- #include "mdfour.h"
- #include <time.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 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 sv_namechangetimer = {CVAR_SAVE, "sv_namechangetimer", "5", "how often to allow name changes, in seconds (prevents people from using animated names and other tricks"};
- 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)"};
- 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;
- /*
- ==================
- Host_Quit_f
- ==================
- */
- void Host_Quit_f (void)
- {
- if(host_shuttingdown)
- Con_Printf("shutting down already!\n");
- else
- Sys_Quit (0);
- }
- /*
- ==================
- Host_Status_f
- ==================
- */
- static void Host_Status_f (void)
- {
- prvm_prog_t *prog = SVVM_prog;
- char qcstatus[256];
- client_t *client;
- int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
- void (*print) (const char *fmt, ...);
- char ip[48]; // can contain a full length v6 address with [] and a port
- int frags;
- char vabuf[1024];
- 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
- if (cls.state == ca_connected)
- {
- Cmd_ForwardToServer ();
- return;
- }
- print = Con_Printf;
- }
- else
- print = SV_ClientPrintf;
- 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, i = 0;i < svs.maxclients;i++)
- if (svs.clients[i].active)
- players++;
- print ("host: %s\n", Cvar_VariableString ("hostname"));
- print ("version: %s build %s (gamename %s)\n", gamename, buildstring, gamenetworkfiltername);
- 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(vabuf, sizeof(vabuf)));
- print ("players: %i active (%i max)\n\n", players, svs.maxclients);
- 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;
- ++k;
- if (in == 0 || in == 1)
- {
- 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_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
- packetloss++;
- packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / 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", 48);
- else
- strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 48);
- frags = client->frags;
- if(sv_status_show_qcstatus.integer)
- {
- prvm_edict_t *ed = PRVM_EDICT_NUM(i + 1);
- const char *str = PRVM_GetString(prog, PRVM_serveredictstring(ed, 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
- {
- if (sv.protocol == PROTOCOL_QUAKE && svs.maxclients <= 99)
- {
- // LordHavoc: this is very touchy because we must maintain ProQuake compatible status output
- print ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
- print (" %s\n", ip);
- }
- else
- {
- // LordHavoc: no real restrictions here, not a ProQuake-compatible protocol anyway...
- print ("#%-3u %-16.16s %4i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
- print (" %s\n", ip);
- }
- }
- else if (in == 1) // extended layout
- {
- print ("%s%-47s %2i %4i %2i:%02i:%02i %4i #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, packetloss, ping, hours, minutes, seconds, frags, i+1, client->name);
- }
- else if (in == 2) // reduced layout
- {
- print ("%s%-47s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
- }
- }
- }
- /*
- ==================
- Host_God_f
- Sets client to godmode
- ==================
- */
- static void Host_God_f (void)
- {
- prvm_prog_t *prog = SVVM_prog;
- if (!allowcheats)
- {
- SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
- return;
- }
- PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_GODMODE;
- if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_GODMODE) )
- SV_ClientPrint("godmode OFF\n");
- else
- SV_ClientPrint("godmode ON\n");
- }
- static void Host_Notarget_f (void)
- {
- prvm_prog_t *prog = SVVM_prog;
- if (!allowcheats)
- {
- SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
- return;
- }
- PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_NOTARGET;
- if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_NOTARGET) )
- SV_ClientPrint("notarget OFF\n");
- else
- SV_ClientPrint("notarget ON\n");
- }
- qboolean noclip_anglehack;
- static void Host_Noclip_f (void)
- {
- prvm_prog_t *prog = SVVM_prog;
- if (!allowcheats)
- {
- SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
- return;
- }
- if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_NOCLIP)
- {
- noclip_anglehack = true;
- PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_NOCLIP;
- SV_ClientPrint("noclip ON\n");
- }
- else
- {
- noclip_anglehack = false;
- PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
- SV_ClientPrint("noclip OFF\n");
- }
- }
- /*
- ==================
- Host_Fly_f
- Sets client to flymode
- ==================
- */
- static void Host_Fly_f (void)
- {
- prvm_prog_t *prog = SVVM_prog;
- if (!allowcheats)
- {
- SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
- return;
- }
- if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_FLY)
- {
- PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_FLY;
- SV_ClientPrint("flymode ON\n");
- }
- else
- {
- PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
- SV_ClientPrint("flymode OFF\n");
- }
- }
- /*
- ==================
- Host_Ping_f
- ==================
- */
- void Host_Pings_f (void); // called by Host_Ping_f
- static void Host_Ping_f (void)
- {
- int i;
- client_t *client;
- void (*print) (const char *fmt, ...);
- if (cmd_source == src_command)
- {
- // if running a client, try to send over network so the client's ping report parser will see the report
- if (cls.state == ca_connected)
- {
- Cmd_ForwardToServer ();
- return;
- }
- print = Con_Printf;
- }
- else
- print = SV_ClientPrintf;
- if (!sv.active)
- return;
- print("Client ping times:\n");
- for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
- {
- if (!client->active)
- continue;
- print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
- }
- // now call the Pings command also, which will send a report that contains packet loss for the scoreboard (as well as a simpler ping report)
- // actually, don't, it confuses old clients (resulting in "unknown command pingplreport" flooding the console)
- //Host_Pings_f();
- }
- /*
- ===============================================================================
- SERVER TRANSITIONS
- ===============================================================================
- */
- /*
- ======================
- Host_Map_f
- handle a
- map <servername>
- command from the console. Active clients are kicked off.
- ======================
- */
- static void Host_Map_f (void)
- {
- char level[MAX_QPATH];
- if (Cmd_Argc() != 2)
- {
- Con_Print("map <levelname> : start a new game (kicks off all players)\n");
- 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);
- }
- #ifdef CONFIG_MENU
- // remove menu
- if (key_dest == key_menu || key_dest == key_menu_grabbed)
- MR_ToggleMenu(0);
- #endif
- key_dest = key_game;
- svs.serverflags = 0; // haven't completed an episode yet
- allowcheats = sv_cheats.integer != 0;
- strlcpy(level, Cmd_Argv(1), sizeof(level));
- SV_SpawnServer(level);
- if (sv.active && cls.state == ca_disconnected)
- CL_EstablishConnection("local:1", -2);
- }
- /*
- ==================
- Host_Changelevel_f
- Goes to a new map, taking all clients along
- ==================
- */
- static void Host_Changelevel_f (void)
- {
- char level[MAX_QPATH];
- if (Cmd_Argc() != 2)
- {
- Con_Print("changelevel <levelname> : continue game on a new level\n");
- return;
- }
- // HACKHACKHACK
- if (!sv.active) {
- Host_Map_f();
- return;
- }
- #ifdef CONFIG_MENU
- // remove menu
- if (key_dest == key_menu || key_dest == key_menu_grabbed)
- MR_ToggleMenu(0);
- #endif
- key_dest = key_game;
- SV_SaveSpawnparms ();
- allowcheats = sv_cheats.integer != 0;
- strlcpy(level, Cmd_Argv(1), sizeof(level));
- SV_SpawnServer(level);
- if (sv.active && cls.state == ca_disconnected)
- CL_EstablishConnection("local:1", -2);
- }
- /*
- ==================
- Host_Restart_f
- Restarts the current server for a dead player
- ==================
- */
- static void Host_Restart_f (void)
- {
- char mapname[MAX_QPATH];
- if (Cmd_Argc() != 1)
- {
- Con_Print("restart : restart current level\n");
- return;
- }
- if (!sv.active)
- {
- Con_Print("Only the server may restart\n");
- return;
- }
- #ifdef CONFIG_MENU
- // remove menu
- if (key_dest == key_menu || key_dest == key_menu_grabbed)
- MR_ToggleMenu(0);
- #endif
- key_dest = key_game;
- allowcheats = sv_cheats.integer != 0;
- strlcpy(mapname, sv.name, sizeof(mapname));
- SV_SpawnServer(mapname);
- if (sv.active && cls.state == ca_disconnected)
- CL_EstablishConnection("local:1", -2);
- }
- /*
- ==================
- Host_Reconnect_f
- This command causes the client to wait for the signon messages again.
- This is sent just before a server changes levels
- ==================
- */
- void Host_Reconnect_f (void)
- {
- char temp[128];
- // if not connected, reconnect to the most recent server
- if (!cls.netcon)
- {
- // if we have connected to a server recently, the userinfo
- // will still contain its IP address, so get the address...
- InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
- if (temp[0])
- CL_EstablishConnection(temp, -1);
- else
- Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
- return;
- }
- // if connected, do something based on protocol
- if (cls.protocol == PROTOCOL_QUAKEWORLD)
- {
- // quakeworld can just re-login
- if (cls.qw_downloadmemory) // don't change when downloading
- return;
- S_StopAllSounds();
- if (cls.state == ca_connected && cls.signon < SIGNONS)
- {
- Con_Printf("reconnecting...\n");
- MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
- MSG_WriteString(&cls.netcon->message, "new");
- }
- }
- else
- {
- // netquake uses reconnect on level changes (silly)
- if (Cmd_Argc() != 1)
- {
- Con_Print("reconnect : wait for signon messages again\n");
- return;
- }
- if (!cls.signon)
- {
- Con_Print("reconnect: no signon, ignoring reconnect\n");
- return;
- }
- cls.signon = 0; // need new connection messages
- }
- }
- /*
- =====================
- Host_Connect_f
- User command to connect to server
- =====================
- */
- static void Host_Connect_f (void)
- {
- if (Cmd_Argc() < 2)
- {
- Con_Print("connect <serveraddress> [<key> <value> ...]: connect to a multiplayer game\n");
- return;
- }
- // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
- if(rcon_secure.integer <= 0)
- Cvar_SetQuick(&rcon_password, "");
- CL_EstablishConnection(Cmd_Argv(1), 2);
- }
- /*
- ===============================================================================
- LOAD / SAVE GAME
- ===============================================================================
- */
- #define SAVEGAME_VERSION 5
- void Host_Savegame_to(prvm_prog_t *prog, const char *name)
- {
- qfile_t *f;
- int i, k, l, numbuffers, 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)
- for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
- if (sv.lightstyles[i][0])
- lightstyles = i+1;
- isserver = prog == SVVM_prog;
- Con_Printf("Saving game to %s...\n", name);
- f = FS_OpenRealFile(name, "wb", false);
- if (!f)
- {
- Con_Print("ERROR: couldn't open.\n");
- return;
- }
- FS_Printf(f, "%i\n", SAVEGAME_VERSION);
- memset(comment, 0, sizeof(comment));
- if(isserver)
- dpsnprintf(comment, sizeof(comment), "%-21.21s kills:%3i/%3i", PRVM_GetString(prog, PRVM_serveredictstring(prog->edicts, message)), (int)PRVM_serverglobalfloat(killed_monsters), (int)PRVM_serverglobalfloat(total_monsters));
- else
- dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", prog->name);
- // convert space to _ to make stdio happy
- // LordHavoc: convert control characters to _ as well
- for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
- if (ISWHITESPACEORCONTROL(comment[i]))
- comment[i] = '_';
- comment[SAVEGAME_COMMENT_LENGTH] = '\0';
- FS_Printf(f, "%s\n", comment);
- if(isserver)
- {
- for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
- FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
- FS_Printf(f, "%d\n", current_skill);
- FS_Printf(f, "%s\n", sv.name);
- FS_Printf(f, "%f\n",sv.time);
- }
- else
- {
- for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
- FS_Printf(f, "(dummy)\n");
- FS_Printf(f, "%d\n", 0);
- FS_Printf(f, "%s\n", "(dummy)");
- FS_Printf(f, "%f\n", realtime);
- }
- // write the light styles
- for (i=0 ; i<lightstyles ; i++)
- {
- if (isserver && sv.lightstyles[i][0])
- FS_Printf(f, "%s\n", sv.lightstyles[i]);
- else
- FS_Print(f,"m\n");
- }
- PRVM_ED_WriteGlobals (prog, f);
- for (i=0 ; i<prog->num_edicts ; i++)
- {
- FS_Printf(f,"// edict %d\n", i);
- //Con_Printf("edict %d...\n", i);
- PRVM_ED_Write (prog, 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 ; i<MAX_LIGHTSTYLES ; i++)
- if (isserver && sv.lightstyles[i][0])
- FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
- // darkplaces extension - model precaches
- for (i=1 ; i<MAX_MODELS ; i++)
- if (sv.model_precache[i][0])
- FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
- // darkplaces extension - sound precaches
- 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
- numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
- for (i = 0; i < numbuffers; i++)
- {
- prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
- if(stringbuffer && (stringbuffer->flags & STRINGBUFFER_SAVED))
- {
- FS_Printf(f,"sv.buffer %i %i \"string\"\n", i, stringbuffer->flags & STRINGBUFFER_QCFLAGS);
- 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
- FS_Close (f);
- Con_Print("done.\n");
- }
- /*
- ===============
- Host_Savegame_f
- ===============
- */
- static void Host_Savegame_f (void)
- {
- prvm_prog_t *prog = SVVM_prog;
- char name[MAX_QPATH];
- qboolean deadflag = false;
- if (!sv.active)
- {
- Con_Print("Can't save - no server running.\n");
- return;
- }
- deadflag = cl.islocalgame && svs.clients[0].active && PRVM_serveredictfloat(svs.clients[0].edict, deadflag);
- if (cl.islocalgame)
- {
- // singleplayer checks
- if (cl.intermission)
- {
- Con_Print("Can't save in intermission.\n");
- return;
- }
- if (deadflag)
- {
- 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)
- {
- Con_Print("save <savename> : save a game\n");
- return;
- }
- if (strstr(Cmd_Argv(1), ".."))
- {
- Con_Print("Relative pathnames are not allowed.\n");
- return;
- }
- strlcpy (name, Cmd_Argv(1), sizeof (name));
- FS_DefaultExtension (name, ".sav", sizeof (name));
- Host_Savegame_to(prog, name);
- }
- /*
- ===============
- Host_Loadgame_f
- ===============
- */
- static void Host_Loadgame_f (void)
- {
- prvm_prog_t *prog = SVVM_prog;
- char filename[MAX_QPATH];
- char mapname[MAX_QPATH];
- float time;
- const char *start;
- const char *end;
- const char *t;
- char *text;
- prvm_edict_t *ent;
- int i, k, numbuffers;
- int entnum;
- int version;
- float spawn_parms[NUM_SPAWN_PARMS];
- prvm_stringbuffer_t *stringbuffer;
- if (Cmd_Argc() != 2)
- {
- Con_Print("load <savename> : load a game\n");
- return;
- }
- strlcpy (filename, Cmd_Argv(1), sizeof(filename));
- FS_DefaultExtension (filename, ".sav", sizeof (filename));
- Con_Printf("Loading game from %s...\n", filename);
- // stop playing demos
- if (cls.demoplayback)
- CL_Disconnect ();
- #ifdef CONFIG_MENU
- // remove menu
- if (key_dest == key_menu || key_dest == key_menu_grabbed)
- MR_ToggleMenu(0);
- #endif
- key_dest = key_game;
- cls.demonum = -1; // stop demo loop in case this fails
- t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
- if (!text)
- {
- Con_Print("ERROR: couldn't open.\n");
- return;
- }
- if(developer_entityparsing.integer)
- Con_Printf("Host_Loadgame_f: loading version\n");
- // version
- COM_ParseToken_Simple(&t, false, false, true);
- version = atoi(com_token);
- if (version != SAVEGAME_VERSION)
- {
- Mem_Free(text);
- Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
- return;
- }
- if(developer_entityparsing.integer)
- Con_Printf("Host_Loadgame_f: loading description\n");
- // description
- COM_ParseToken_Simple(&t, false, false, true);
- for (i = 0;i < NUM_SPAWN_PARMS;i++)
- {
- COM_ParseToken_Simple(&t, false, false, true);
- spawn_parms[i] = atof(com_token);
- }
- // skill
- COM_ParseToken_Simple(&t, false, false, true);
- // 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, false, true);
- 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, true);
- 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)
- {
- Mem_Free(text);
- Con_Print("Couldn't load map\n");
- return;
- }
- 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
- // -1 is the globals
- entnum = -1;
- for (i = 0;i < MAX_LIGHTSTYLES;i++)
- {
- // light style
- start = t;
- COM_ParseToken_Simple(&t, false, false, true);
- // if this is a 64 lightstyle savegame produced by Quake, stop now
- // we have to check this because darkplaces may save more than 64
- if (com_token[0] == '{')
- {
- 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 (;;)
- {
- start = t;
- if (!COM_ParseToken_Simple(&t, false, false, true))
- break;
- if (com_token[0] == '{')
- {
- t = start;
- break;
- }
- }
- // unlink all entities
- World_UnlinkAll(&sv.world);
- // load the edicts out of the savegame file
- end = t;
- for (;;)
- {
- start = t;
- while (COM_ParseToken_Simple(&t, false, false, true))
- if (!strcmp(com_token, "}"))
- break;
- if (!COM_ParseToken_Simple(&start, false, false, true))
- {
- // end of file
- break;
- }
- if (strcmp(com_token,"{"))
- {
- Mem_Free(text);
- Host_Error ("First token isn't a brace");
- }
- if (entnum == -1)
- {
- if(developer_entityparsing.integer)
- Con_Printf("Host_Loadgame_f: loading globals\n");
- // parse the global vars
- PRVM_ED_ParseGlobals (prog, start);
- // restore the autocvar globals
- Cvar_UpdateAllAutoCvars();
- }
- else
- {
- // parse an edict
- if (entnum >= MAX_EDICTS)
- {
- Mem_Free(text);
- Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
- }
- while (entnum >= prog->max_edicts)
- PRVM_MEM_IncreaseEdicts(prog);
- ent = PRVM_EDICT_NUM(entnum);
- memset(ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
- ent->priv.server->free = false;
- if(developer_entityparsing.integer)
- Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
- PRVM_ED_ParseEdict (prog, start, ent);
- // link it into the bsp tree
- if (!ent->priv.server->free)
- SV_LinkEdict(ent);
- }
- end = t;
- entnum++;
- }
- prog->num_edicts = entnum;
- sv.time = time;
- 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));
- BufStr_Flush(prog);
- while (COM_ParseToken_Simple(&t, false, false, true))
- {
- if (!strcmp(com_token, "sv.lightstyles"))
- {
- COM_ParseToken_Simple(&t, false, false, true);
- i = atoi(com_token);
- COM_ParseToken_Simple(&t, false, false, true);
- 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, true);
- i = atoi(com_token);
- COM_ParseToken_Simple(&t, false, false, true);
- 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.worldname : 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, true);
- i = atoi(com_token);
- COM_ParseToken_Simple(&t, false, false, true);
- 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.buffer"))
- {
- if (COM_ParseToken_Simple(&t, false, false, true))
- {
- i = atoi(com_token);
- if (i >= 0)
- {
- k = STRINGBUFFER_SAVED;
- if (COM_ParseToken_Simple(&t, false, false, true))
- k |= atoi(com_token);
- if (!BufStr_FindCreateReplace(prog, i, k, "string"))
- Con_Printf("failed to create stringbuffer %i\n", i);
- }
- else
- Con_Printf("unsupported stringbuffer index %i \"%s\"\n", i, com_token);
- }
- else
- Con_Printf("unexpected end of line when parsing sv.buffer (expected buffer index)\n");
- }
- else if (!strcmp(com_token, "sv.bufstr"))
- {
- if (!COM_ParseToken_Simple(&t, false, false, true))
- Con_Printf("unexpected end of line when parsing sv.bufstr\n");
- else
- {
- i = atoi(com_token);
- stringbuffer = BufStr_FindCreateReplace(prog, i, STRINGBUFFER_SAVED, "string");
- if (stringbuffer)
- {
- if (COM_ParseToken_Simple(&t, false, false, true))
- {
- k = atoi(com_token);
- if (COM_ParseToken_Simple(&t, false, false, true))
- BufStr_Set(prog, stringbuffer, k, com_token);
- else
- Con_Printf("unexpected end of line when parsing sv.bufstr (expected string)\n");
- }
- else
- Con_Printf("unexpected end of line when parsing sv.bufstr (expected strindex)\n");
- }
- else
- Con_Printf("failed to create stringbuffer %i \"%s\"\n", i, com_token);
- }
- }
- // skip any trailing text or unrecognized commands
- while (COM_ParseToken_Simple(&t, true, false, true) && strcmp(com_token, "\n"))
- ;
- }
- }
- }
- Mem_Free(text);
- // remove all temporary flagged string buffers (ones created with BufStr_FindCreateReplace)
- numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
- for (i = 0; i < numbuffers; i++)
- {
- if ( (stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i)) )
- if (stringbuffer->flags & STRINGBUFFER_TEMP)
- BufStr_Del(prog, stringbuffer);
- }
- if(developer_entityparsing.integer)
- Con_Printf("Host_Loadgame_f: finished\n");
- // make sure we're connected to loopback
- if (sv.active && cls.state == ca_disconnected)
- CL_EstablishConnection("local:1", -2);
- }
- //============================================================================
- /*
- ======================
- Host_Name_f
- ======================
- */
- cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
- static void Host_Name_f (void)
- {
- prvm_prog_t *prog = SVVM_prog;
- int i, j;
- qboolean valid_colors;
- const char *newNameSource;
- char newName[sizeof(host_client->name)];
- if (Cmd_Argc () == 1)
- {
- if (cmd_source == src_command)
- {
- Con_Printf("name: %s\n", cl_name.string);
- }
- return;
- }
- if (Cmd_Argc () == 2)
- newNameSource = Cmd_Argv(1);
- else
- newNameSource = Cmd_Args();
- 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;
- }
- if (realtime < host_client->nametime)
- {
- SV_ClientPrintf("You can't change name more than once every %.1f seconds!\n", max(0.0f, sv_namechangetimer.value));
- return;
- }
- host_client->nametime = realtime + max(0.0f, sv_namechangetimer.value);
- // 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;
- }
- 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;
- l = strlen(host_client->name);
- if(l < sizeof(host_client->name) - 1)
- {
- // duplicate the color tag to escape it
- host_client->name[i] = STRING_COLOR_TAG;
- host_client->name[i+1] = 0;
- //Con_DPrintf("abuse detected, adding another trailing color tag\n");
- }
- else
- {
- // remove the last character to fix the color code
- host_client->name[l-1] = 0;
- //Con_DPrintf("abuse detected, removing a trailing color tag\n");
- }
- }
- // find the last color tag offset and decide if we need to add a reset tag
- for (i = 0, j = -1;host_client->name[i];i++)
- {
- if (host_client->name[i] == STRING_COLOR_TAG)
- {
- if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
- {
- j = i;
- // if this happens to be a reset tag then we don't need one
- if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
- j = -1;
- 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++;
- continue;
- }
- }
- }
- // does not end in the default color string, so add it
- if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
- memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
- PRVM_serveredictstring(host_client->edict, netname) = PRVM_SetEngineString(prog, host_client->name);
- if (strcmp(host_client->old_name, host_client->name))
- {
- if (host_client->begun)
- 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);
- }
- }
- /*
- ======================
- Host_Playermodel_f
- ======================
- */
- cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz/Xonotic (changed by playermodel command)"};
- // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
- static void Host_Playermodel_f (void)
- {
- prvm_prog_t *prog = SVVM_prog;
- int i, j;
- char newPath[sizeof(host_client->playermodel)];
- if (Cmd_Argc () == 1)
- {
- if (cmd_source == src_command)
- {
- Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
- }
- return;
- }
- if (Cmd_Argc () == 2)
- strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
- else
- strlcpy (newPath, Cmd_Args(), sizeof (newPath));
- for (i = 0, j = 0;newPath[i];i++)
- if (newPath[i] != '\r' && newPath[i] != '\n')
- newPath[j++] = newPath[i];
- newPath[j] = 0;
- if (cmd_source == src_command)
- {
- Cvar_Set ("_cl_playermodel", newPath);
- return;
- }
- /*
- if (realtime < host_client->nametime)
- {
- SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
- return;
- }
- host_client->nametime = realtime + 5;
- */
- // point the string back at updateclient->name to keep it safe
- strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
- PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(prog, host_client->playermodel);
- if (strcmp(host_client->old_model, host_client->playermodel))
- {
- strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
- /*// send notification to all clients
- MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
- MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
- MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
- }
- }
- /*
- ======================
- Host_Playerskin_f
- ======================
- */
- cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz/Xonotic (changed by playerskin command)"};
- static void Host_Playerskin_f (void)
- {
- prvm_prog_t *prog = SVVM_prog;
- int i, j;
- char newPath[sizeof(host_client->playerskin)];
- if (Cmd_Argc () == 1)
- {
- if (cmd_source == src_command)
- {
- Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
- }
- return;
- }
- if (Cmd_Argc () == 2)
- strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
- else
- strlcpy (newPath, Cmd_Args(), sizeof (newPath));
- for (i = 0, j = 0;newPath[i];i++)
- if (newPath[i] != '\r' && newPath[i] != '\n')
- newPath[j++] = newPath[i];
- newPath[j] = 0;
- if (cmd_source == src_command)
- {
- Cvar_Set ("_cl_playerskin", newPath);
- return;
- }
- /*
- if (realtime < host_client->nametime)
- {
- SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
- return;
- }
- host_client->nametime = realtime + 5;
- */
- // point the string back at updateclient->name to keep it safe
- strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
- PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(prog, host_client->playerskin);
- if (strcmp(host_client->old_skin, host_client->playerskin))
- {
- //if (host_client->begun)
- // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
- strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
- /*// send notification to all clients
- MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
- MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
- MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
- }
- }
- static void Host_Version_f (void)
- {
- Con_Printf("Version: %s build %s\n", gamename, buildstring);
- }
- static void Host_Say(qboolean teamonly)
- {
- prvm_prog_t *prog = SVVM_prog;
- client_t *save;
- int j, quoted;
- const char *p1;
- char *p2;
- // LordHavoc: long say messages
- char text[1024];
- qboolean fromServer = false;
- if (cmd_source == src_command)
- {
- if (cls.state == ca_dedicated)
- {
- fromServer = true;
- teamonly = false;
- }
- else
- {
- Cmd_ForwardToServer ();
- return;
- }
- }
- if (Cmd_Argc () < 2)
- return;
- if (!teamplay.integer)
- teamonly = false;
- p1 = Cmd_Args();
- quoted = false;
- if (*p1 == '\"')
- {
- quoted = true;
- p1++;
- }
- // note this uses the chat prefix \001
- if (!fromServer && !teamonly)
- dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
- else if (!fromServer && teamonly)
- dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
- else if(*(sv_adminnick.string))
- dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
- else
- dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
- p2 = text + strlen(text);
- while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
- {
- if (p2[-1] == '\"' && quoted)
- quoted = false;
- p2[-1] = 0;
- p2--;
- }
- strlcat(text, "\n", sizeof(text));
- // note: save is not a valid edict if fromServer is true
- save = host_client;
- for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
- if (host_client->active && (!teamonly || PRVM_serveredictfloat(host_client->edict, team) == PRVM_serveredictfloat(save->edict, team)))
- SV_ClientPrint(text);
- host_client = save;
- if (cls.state == ca_dedicated)
- Con_Print(&text[1]);
- }
- static void Host_Say_f(void)
- {
- Host_Say(false);
- }
- static void Host_Say_Team_f(void)
- {
- Host_Say(true);
- }
- static void Host_Tell_f(void)
- {
- const char *playername_start = NULL;
- size_t playername_length = 0;
- int playernumber = 0;
- client_t *save;
- int j;
- const char *p1, *p2;
- char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
- qboolean fromServer = false;
- if (cmd_source == src_command)
- {
- if (cls.state == ca_dedicated)
- fromServer = true;
- else
- {
- Cmd_ForwardToServer ();
- return;
- }
- }
- if (Cmd_Argc () < 2)
- return;
- // note this uses the chat prefix \001
- if (!fromServer)
- dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
- else if(*(sv_adminnick.string))
- dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
- else
- dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
- p1 = Cmd_Args();
- p2 = p1 + strlen(p1);
- // remove the target name
- while (p1 < p2 && *p1 == ' ')
- p1++;
- if(*p1 == '#')
- {
- ++p1;
- while (p1 < p2 && *p1 == ' ')
- p1++;
- while (p1 < p2 && isdigit(*p1))
- {
- playernumber = playernumber * 10 + (*p1 - '0');
- p1++;
- }
- --playernumber;
- }
- else if(*p1 == '"')
- {
- ++p1;
- playername_start = p1;
- while (p1 < p2 && *p1 != '"')
- p1++;
- playername_length = p1 - playername_start;
- if(p1 < p2)
- p1++;
- }
- else
- {
- playername_start = p1;
- while (p1 < p2 && *p1 != ' ')
- p1++;
- playername_length = p1 - playername_start;
- }
- while (p1 < p2 && *p1 == ' ')
- p1++;
- if(playername_start)
- {
- // set playernumber to the right client
- char namebuf[128];
- if(playername_length >= sizeof(namebuf))
- {
- if (fromServer)
- Con_Print("Host_Tell: too long player name/ID\n");
- else
- SV_ClientPrint("Host_Tell: too long player name/ID\n");
- return;
- }
- memcpy(namebuf, playername_start, playername_length);
- namebuf[playername_length] = 0;
- for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
- {
- if (!svs.clients[playernumber].active)
- continue;
- if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
- break;
- }
- }
- if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
- {
- if (fromServer)
- Con_Print("Host_Tell: invalid player name/ID\n");
- else
- SV_ClientPrint("Host_Tell: invalid player name/ID\n");
- return;
- }
- // remove trailing newlines
- while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
- p2--;
- // remove quotes if present
- if (*p1 == '"')
- {
- p1++;
- if (p2[-1] == '"')
- p2--;
- else if (fromServer)
- Con_Print("Host_Tell: missing end quote\n");
- else
- SV_ClientPrint("Host_Tell: missing end quote\n");
- }
- while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
- p2--;
- if(p1 == p2)
- return; // empty say
- for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
- text[j++] = *p1++;
- text[j++] = '\n';
- text[j++] = 0;
- save = host_client;
- host_client = svs.clients + playernumber;
- SV_ClientPrint(text);
- host_client = save;
- }
- /*
- ==================
- Host_Color_f
- ==================
- */
- cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
- static void Host_Color(int changetop, int changebottom)
- {
- prvm_prog_t *prog = SVVM_prog;
- int top, bottom, playercolor;
- // get top and bottom either from the provided values or the current values
- // (allows changing only top or bottom, or both at once)
- top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
- bottom = changebottom >= 0 ? changebottom : cl_color.integer;
- top &= 15;
- bottom &= 15;
- // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
- //if (top > 13)
- // top = 13;
- //if (bottom > 13)
- // bottom = 13;
- playercolor = top*16 + bottom;
- if (cmd_source == src_command)
- {
- Cvar_SetValueQuick(&cl_color, playercolor);
- return;
- }
- if (cls.protocol == PROTOCOL_QUAKEWORLD)
- return;
- if (host_client->edict && PRVM_serverfunction(SV_ChangeTeam))
- {
- Con_DPrint("Calling SV_ChangeTeam\n");
- prog->globals.fp[OFS_PARM0] = playercolor;
- PRVM_serverglobalfloat(time) = sv.time;
- PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
- prog->ExecuteProgram(prog, PRVM_serverfunction(SV_ChangeTeam), "QC function SV_ChangeTeam is missing");
- }
- else
- {
- if (host_client->edict)
- {
- PRVM_serveredictfloat(host_client->edict, clientcolors) = playercolor;
- PRVM_serveredictfloat(host_client->edict, team) = bottom + 1;
- }
- host_client->colors = playercolor;
- if (host_client->old_colors != host_client->colors)
- {
- host_client->old_colors = host_client->colors;
- // send notification to all clients
- MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
- MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
- MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
- }
- }
- }
- static void Host_Color_f(void)
- {
- int top, bottom;
- if (Cmd_Argc() == 1)
- {
- if (cmd_source == src_command)
- {
- Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
- Con_Print("color <0-15> [0-15]\n");
- }
- return;
- }
- if (Cmd_Argc() == 2)
- top = bottom = atoi(Cmd_Argv(1));
- else
- {
- top = atoi(Cmd_Argv(1));
- bottom = atoi(Cmd_Argv(2));
- }
- Host_Color(top, bottom);
- }
- static void Host_TopColor_f(void)
- {
- if (Cmd_Argc() == 1)
- {
- if (cmd_source == src_command)
- {
- Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
- Con_Print("topcolor <0-15>\n");
- }
- return;
- }
- Host_Color(atoi(Cmd_Argv(1)), -1);
- }
- static void Host_BottomColor_f(void)
- {
- if (Cmd_Argc() == 1)
- {
- if (cmd_source == src_command)
- {
- Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
- Con_Print("bottomcolor <0-15>\n");
- }
- return;
- }
- Host_Color(-1, atoi(Cmd_Argv(1)));
- }
- cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
- cvar_t cl_rate_burstsize = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate_burstsize", "1024", "internal storage cvar for current rate control burst size (changed by rate_burstsize command)"};
- static void Host_Rate_f(void)
- {
- int rate;
- if (Cmd_Argc() != 2)
- {
- if (cmd_source == src_command)
- {
- Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
- Con_Print("rate <bytespersecond>\n");
- }
- return;
- }
- rate = atoi(Cmd_Argv(1));
- if (cmd_source == src_command)
- {
- Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
- return;
- }
- host_client->rate = rate;
- }
- static void Host_Rate_BurstSize_f(void)
- {
- int rate_burstsize;
- if (Cmd_Argc() != 2)
- {
- Con_Printf("\"rate_burstsize\" is \"%i\"\n", cl_rate_burstsize.integer);
- Con_Print("rate_burstsize <bytes>\n");
- return;
- }
- rate_burstsize = atoi(Cmd_Argv(1));
- if (cmd_source == src_command)
- {
- Cvar_SetValue ("_cl_rate_burstsize", rate_burstsize);
- return;
- }
- host_client->rate_burstsize = rate_burstsize;
- }
- /*
- ==================
- Host_Kill_f
- ==================
- */
- static void Host_Kill_f (void)
- {
- prvm_prog_t *prog = SVVM_prog;
- if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
- {
- SV_ClientPrint("Can't suicide -- already dead!\n");
- return;
- }
- PRVM_serverglobalfloat(time) = sv.time;
- PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
- prog->ExecuteProgram(prog, PRVM_serverfunction(ClientKill), "QC function ClientKill is missing");
- }
- /*
- ==================
- Host_Pause_f
- ==================
- */
- static void Host_Pause_f (void)
- {
- void (*print) (const char *fmt, ...);
- if (cmd_source == src_command)
- {
- // if running a client, try to send over network so the pause is handled by the server
- if (cls.state == ca_connected)
- {
- Cmd_ForwardToServer ();
- return;
- }
- print = Con_Printf;
- }
- else
- print = SV_ClientPrintf;
- if (!pausable.integer)
- {
- if (cmd_source == src_client)
- {
- if(cls.state == ca_dedicated || host_client != &svs.clients[0]) // non-admin
- {
- print("Pause not allowed.\n");
- return;
- }
- }
- }
-
- sv.paused ^= 1;
- if (cmd_source != src_command)
- SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
- else if(*(sv_adminnick.string))
- SV_BroadcastPrintf("%s %spaused the game\n", sv_adminnick.string, sv.paused ? "" : "un");
- else
- SV_BroadcastPrintf("%s %spaused the game\n", hostname.string, sv.paused ? "" : "un");
- // send notification to all clients
- MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
- MSG_WriteByte(&sv.reliable_datagram, sv.paused);
- }
- /*
- ======================
- Host_PModel_f
- LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
- LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
- ======================
- */
- cvar_t cl_pmodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_pmodel", "0", "internal storage cvar for current player model number in nehahra (changed by pmodel command)"};
- static void Host_PModel_f (void)
- {
- prvm_prog_t *prog = SVVM_prog;
- int i;
- if (Cmd_Argc () == 1)
- {
- if (cmd_source == src_command)
- {
- Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
- }
- return;
- }
- i = atoi(Cmd_Argv(1));
- if (cmd_source == src_command)
- {
- if (cl_pmodel.integer == i)
- return;
- Cvar_SetValue ("_cl_pmodel", i);
- if (cls.state == ca_connected)
- Cmd_ForwardToServer ();
- return;
- }
- PRVM_serveredictfloat(host_client->edict, pmodel) = i;
- }
- //===========================================================================
- /*
- ==================
- Host_PreSpawn_f
- ==================
- */
- static void Host_PreSpawn_f (void)
- {
- if (host_client->prespawned)
- {
- Con_Print("prespawn not valid -- already prespawned\n");
- return;
- }
- host_client->prespawned = true;
- if (host_client->netconnection)
- {
- SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
- MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
- MSG_WriteByte (&host_client->netconnection->message, 2);
- host_client->sendsignon = 0; // enable unlimited sends again
- }
- // reset the name change timer because the client will send name soon
- host_client->nametime = 0;
- }
- /*
- ==================
- Host_S…
Large files files are truncated, but you can click here to view the full file