From 037b6f399743cea2d02a1046cfcec9ca4294c265 Mon Sep 17 00:00:00 2001 From: cloudwalk Date: Tue, 30 Jun 2020 17:28:21 +0000 Subject: [PATCH] (Round 6) Break up host_cmd.c Introduce cl_cmd.c. Move several commands and cvars to it. Renamed Cmd_ForwardToServer prefixes to CL_ and moved them to cl_cmd.c git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12762 d7cf8633-e32d-0410-b094-e92efae38249 --- cl_cmd.c | 512 +++++++++++++++++++++++++++++++++++++++++++++++++++ cl_main.c | 2 + cl_parse.c | 18 +- client.h | 14 ++ cmd.c | 159 +--------------- cmd.h | 9 - host_cmd.c | 342 +--------------------------------- keys.c | 2 +- makefile.inc | 1 + netconn.c | 2 +- sv_ccmds.c | 10 +- 11 files changed, 549 insertions(+), 522 deletions(-) create mode 100644 cl_cmd.c diff --git a/cl_cmd.c b/cl_cmd.c new file mode 100644 index 00000000..8378e46d --- /dev/null +++ b/cl_cmd.c @@ -0,0 +1,512 @@ +/* +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" + +// for secure rcon authentication +#include "hmac.h" +#include "mdfour.h" +#include + +cvar_t rcon_password = {CVAR_CLIENT | CVAR_SERVER | 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_CLIENT | CVAR_SERVER, "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 = {CVAR_CLIENT, "rcon_secure_challengetimeout", "5", "challenge-based secure rcon: time out requests if no challenge came within this time interval"}; +cvar_t rcon_address = {CVAR_CLIENT, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"}; +cvar_t cl_name = {CVAR_CLIENT | CVAR_SAVE | CVAR_USERINFO, "name", "player", "change your player name"}; +cvar_t cl_topcolor = {CVAR_CLIENT | CVAR_SAVE | CVAR_USERINFO, "topcolor", "0", "change the color of your shirt"}; +cvar_t cl_bottomcolor = {CVAR_CLIENT | CVAR_SAVE | CVAR_USERINFO, "bottomcolor", "0", "change the color of your pants"}; + +/* +=================== +CL_ForwardToServer + +Sends an entire command string over to the server, unprocessed +=================== +*/ +void CL_ForwardToServer (const char *s) +{ + char temp[128]; + if (cls.state != ca_connected) + { + Con_Printf("Can't \"%s\", not connected\n", s); + return; + } + + if (!cls.netcon) + return; + + // LadyHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my + // attention, it has been eradicated from here, its only (former) use in + // all of darkplaces. + if (cls.protocol == PROTOCOL_QUAKEWORLD) + MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd); + else + MSG_WriteByte(&cls.netcon->message, clc_stringcmd); + if ((!strncmp(s, "say ", 4) || !strncmp(s, "say_team ", 9)) && cl_locs_enable.integer) + { + // say/say_team commands can replace % character codes with status info + while (*s) + { + if (*s == '%' && s[1]) + { + // handle proquake message macros + temp[0] = 0; + switch (s[1]) + { + case 'l': // current location + CL_Locs_FindLocationName(temp, sizeof(temp), cl.movement_origin); + break; + case 'h': // current health + dpsnprintf(temp, sizeof(temp), "%i", cl.stats[STAT_HEALTH]); + break; + case 'a': // current armor + dpsnprintf(temp, sizeof(temp), "%i", cl.stats[STAT_ARMOR]); + break; + case 'x': // current rockets + dpsnprintf(temp, sizeof(temp), "%i", cl.stats[STAT_ROCKETS]); + break; + case 'c': // current cells + dpsnprintf(temp, sizeof(temp), "%i", cl.stats[STAT_CELLS]); + break; + // silly proquake macros + case 'd': // loc at last death + CL_Locs_FindLocationName(temp, sizeof(temp), cl.lastdeathorigin); + break; + case 't': // current time + dpsnprintf(temp, sizeof(temp), "%.0f:%.0f", floor(cl.time / 60), cl.time - floor(cl.time / 60) * 60); + break; + case 'r': // rocket launcher status ("I have RL", "I need rockets", "I need RL") + if (!(cl.stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER)) + dpsnprintf(temp, sizeof(temp), "I need RL"); + else if (!cl.stats[STAT_ROCKETS]) + dpsnprintf(temp, sizeof(temp), "I need rockets"); + else + dpsnprintf(temp, sizeof(temp), "I have RL"); + break; + case 'p': // powerup status (outputs "quad" "pent" and "eyes" according to status) + if (cl.stats[STAT_ITEMS] & IT_QUAD) + { + if (temp[0]) + strlcat(temp, " ", sizeof(temp)); + strlcat(temp, "quad", sizeof(temp)); + } + if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) + { + if (temp[0]) + strlcat(temp, " ", sizeof(temp)); + strlcat(temp, "pent", sizeof(temp)); + } + if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) + { + if (temp[0]) + strlcat(temp, " ", sizeof(temp)); + strlcat(temp, "eyes", sizeof(temp)); + } + break; + case 'w': // weapon status (outputs "SSG:NG:SNG:GL:RL:LG" with the text between : characters omitted if you lack the weapon) + if (cl.stats[STAT_ITEMS] & IT_SUPER_SHOTGUN) + strlcat(temp, "SSG", sizeof(temp)); + strlcat(temp, ":", sizeof(temp)); + if (cl.stats[STAT_ITEMS] & IT_NAILGUN) + strlcat(temp, "NG", sizeof(temp)); + strlcat(temp, ":", sizeof(temp)); + if (cl.stats[STAT_ITEMS] & IT_SUPER_NAILGUN) + strlcat(temp, "SNG", sizeof(temp)); + strlcat(temp, ":", sizeof(temp)); + if (cl.stats[STAT_ITEMS] & IT_GRENADE_LAUNCHER) + strlcat(temp, "GL", sizeof(temp)); + strlcat(temp, ":", sizeof(temp)); + if (cl.stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER) + strlcat(temp, "RL", sizeof(temp)); + strlcat(temp, ":", sizeof(temp)); + if (cl.stats[STAT_ITEMS] & IT_LIGHTNING) + strlcat(temp, "LG", sizeof(temp)); + break; + default: + // not a recognized macro, print it as-is... + temp[0] = s[0]; + temp[1] = s[1]; + temp[2] = 0; + break; + } + // write the resulting text + SZ_Write(&cls.netcon->message, (unsigned char *)temp, (int)strlen(temp)); + s += 2; + continue; + } + MSG_WriteByte(&cls.netcon->message, *s); + s++; + } + MSG_WriteByte(&cls.netcon->message, 0); + } + else // any other command is passed on as-is + SZ_Write(&cls.netcon->message, (const unsigned char *)s, (int)strlen(s) + 1); +} + +void CL_ForwardToServer_f (cmd_state_t *cmd) +{ + const char *s; + char vabuf[1024]; + if (!strcasecmp(Cmd_Argv(cmd, 0), "cmd")) + { + // we want to strip off "cmd", so just send the args + s = Cmd_Argc(cmd) > 1 ? Cmd_Args(cmd) : ""; + } + else + { + // we need to keep the command name, so send Cmd_Argv(cmd, 0), a space and then Cmd_Args(cmd) + s = va(vabuf, sizeof(vabuf), "%s %s", Cmd_Argv(cmd, 0), Cmd_Argc(cmd) > 1 ? Cmd_Args(cmd) : ""); + } + // don't send an empty forward message if the user tries "cmd" by itself + if (!s || !*s) + return; + CL_ForwardToServer(s); +} + +/* +================== +CL_Color_f +================== +*/ +cvar_t cl_color = {CVAR_READONLY | CVAR_CLIENT | CVAR_SAVE, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"}; + +// Ignore the callbacks so this two-to-three way synchronization doesn't cause an infinite loop. +static void CL_Color_c(cvar_t *var) +{ + char vabuf[1024]; + + Cvar_Set_NoCallback(&cl_topcolor, va(vabuf, sizeof(vabuf), "%i", ((var->integer >> 4) & 15))); + Cvar_Set_NoCallback(&cl_bottomcolor, va(vabuf, sizeof(vabuf), "%i", (var->integer & 15))); +} + +static void CL_Topcolor_c(cvar_t *var) +{ + char vabuf[1024]; + + Cvar_Set_NoCallback(&cl_color, va(vabuf, sizeof(vabuf), "%i", var->integer*16 + cl_bottomcolor.integer)); +} + +static void CL_Bottomcolor_c(cvar_t *var) +{ + char vabuf[1024]; + + Cvar_Set_NoCallback(&cl_color, va(vabuf, sizeof(vabuf), "%i", cl_topcolor.integer*16 + var->integer)); +} + +static void CL_Color_f(cmd_state_t *cmd) +{ + int top, bottom; + + if (Cmd_Argc(cmd) == 1) + { + if (cmd->source == src_command) + { + Con_Printf("\"color\" is \"%i %i\"\n", cl_topcolor.integer, cl_bottomcolor.integer); + Con_Print("color <0-15> [0-15]\n"); + } + return; + } + + if (Cmd_Argc(cmd) == 2) + top = bottom = atoi(Cmd_Argv(cmd, 1)); + else + { + top = atoi(Cmd_Argv(cmd, 1)); + bottom = atoi(Cmd_Argv(cmd, 2)); + } + /* + * This is just a convenient way to change topcolor and bottomcolor + * We can't change cl_color from here directly because topcolor and + * bottomcolor may be changed separately and do not call this function. + * So it has to be changed when the userinfo strings are updated, which + * happens twice here. Perhaps find a cleaner way? + */ + + top = top >= 0 ? top : cl_topcolor.integer; + bottom = bottom >= 0 ? bottom : cl_bottomcolor.integer; + + top &= 15; + bottom &= 15; + + // LadyHavoc: allowing skin colormaps 14 and 15 by commenting this out + //if (top > 13) + // top = 13; + //if (bottom > 13) + // bottom = 13; + + if (cmd->source == src_command) + { + Cvar_SetValueQuick(&cl_topcolor, top); + Cvar_SetValueQuick(&cl_bottomcolor, bottom); + return; + } +} + +/* +==================== +CL_Packet_f + +packet + +Contents allows \n escape character +==================== +*/ +static void CL_Packet_f(cmd_state_t *cmd) // credit: taken from QuakeWorld +{ + char send[2048]; + int i, l; + const char *in; + char *out; + lhnetaddress_t address; + lhnetsocket_t *mysocket; + + if (Cmd_Argc(cmd) != 3) + { + Con_Printf ("packet \n"); + return; + } + + if (!LHNETADDRESS_FromString (&address, Cmd_Argv(cmd, 1), sv_netport.integer)) + { + Con_Printf ("Bad address\n"); + return; + } + + in = Cmd_Argv(cmd, 2); + out = send+4; + send[0] = send[1] = send[2] = send[3] = -1; + + l = (int)strlen (in); + for (i=0 ; i= send + sizeof(send) - 1) + break; + if (in[i] == '\\' && in[i+1] == 'n') + { + *out++ = '\n'; + i++; + } + else if (in[i] == '\\' && in[i+1] == '0') + { + *out++ = '\0'; + i++; + } + else if (in[i] == '\\' && in[i+1] == 't') + { + *out++ = '\t'; + i++; + } + else if (in[i] == '\\' && in[i+1] == 'r') + { + *out++ = '\r'; + i++; + } + else if (in[i] == '\\' && in[i+1] == '"') + { + *out++ = '\"'; + i++; + } + else + *out++ = in[i]; + } + + mysocket = NetConn_ChooseClientSocketForAddress(&address); + if (!mysocket) + mysocket = NetConn_ChooseServerSocketForAddress(&address); + if (mysocket) + NetConn_Write(mysocket, send, out - send, &address); +} + +/* +===================== +CL_PQRcon_f + +ProQuake rcon support +===================== +*/ +static void CL_PQRcon_f(cmd_state_t *cmd) +{ + int n; + const char *e; + lhnetsocket_t *mysocket; + + if (Cmd_Argc(cmd) == 1) + { + Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0)); + return; + } + + if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0) + { + Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n"); + return; + } + + e = strchr(rcon_password.string, ' '); + n = e ? e-rcon_password.string : (int)strlen(rcon_password.string); + + if (cls.netcon) + cls.rcon_address = cls.netcon->peeraddress; + else + { + if (!rcon_address.string[0]) + { + Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n"); + return; + } + LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer); + } + mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address); + if (mysocket) + { + sizebuf_t buf; + unsigned char bufdata[64]; + buf.data = bufdata; + SZ_Clear(&buf); + MSG_WriteLong(&buf, 0); + MSG_WriteByte(&buf, CCREQ_RCON); + SZ_Write(&buf, (const unsigned char*)rcon_password.string, n); + MSG_WriteByte(&buf, 0); // terminate the (possibly partial) string + MSG_WriteString(&buf, Cmd_Args(cmd)); + StoreBigLong(buf.data, NETFLAG_CTL | (buf.cursize & NETFLAG_LENGTH_MASK)); + NetConn_Write(mysocket, buf.data, buf.cursize, &cls.rcon_address); + SZ_Clear(&buf); + } +} + +/* +===================== +CL_Rcon_f + + Send the rest of the command line over as + an unconnected command. +===================== +*/ +static void CL_Rcon_f(cmd_state_t *cmd) // credit: taken from QuakeWorld +{ + int i, n; + const char *e; + lhnetsocket_t *mysocket; + + if (Cmd_Argc(cmd) == 1) + { + Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0)); + return; + } + + if (!rcon_password.string || !rcon_password.string[0]) + { + Con_Printf ("You must set rcon_password before issuing an rcon command.\n"); + return; + } + + e = strchr(rcon_password.string, ' '); + n = e ? e-rcon_password.string : (int)strlen(rcon_password.string); + + if (cls.netcon) + cls.rcon_address = cls.netcon->peeraddress; + else + { + if (!rcon_address.string[0]) + { + Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n"); + return; + } + LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer); + } + mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address); + if (mysocket && Cmd_Args(cmd)[0]) + { + // simply put together the rcon packet and send it + if(Cmd_Argv(cmd, 0)[0] == 's' || rcon_secure.integer > 1) + { + if(cls.rcon_commands[cls.rcon_ringpos][0]) + { + char s[128]; + LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true); + Con_Printf("rcon to %s (for command %s) failed: too many buffered commands (possibly increase MAX_RCONS)\n", s, cls.rcon_commands[cls.rcon_ringpos]); + cls.rcon_commands[cls.rcon_ringpos][0] = 0; + --cls.rcon_trying; + } + for (i = 0;i < MAX_RCONS;i++) + if(cls.rcon_commands[i][0]) + if (!LHNETADDRESS_Compare(&cls.rcon_address, &cls.rcon_addresses[i])) + break; + ++cls.rcon_trying; + if(i >= MAX_RCONS) + NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &cls.rcon_address); // otherwise we'll request the challenge later + strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(cmd), sizeof(cls.rcon_commands[cls.rcon_ringpos])); + cls.rcon_addresses[cls.rcon_ringpos] = cls.rcon_address; + cls.rcon_timeout[cls.rcon_ringpos] = host.realtime + rcon_secure_challengetimeout.value; + cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS; + } + else if(rcon_secure.integer > 0) + { + char buf[1500]; + char argbuf[1500]; + dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args(cmd)); + memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24); + if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n)) + { + buf[40] = ' '; + strlcpy(buf + 41, argbuf, sizeof(buf) - 41); + NetConn_Write(mysocket, buf, 41 + (int)strlen(buf + 41), &cls.rcon_address); + } + } + else + { + char buf[1500]; + memcpy(buf, "\377\377\377\377", 4); + dpsnprintf(buf+4, sizeof(buf)-4, "rcon %.*s %s", n, rcon_password.string, Cmd_Args(cmd)); + NetConn_WriteString(mysocket, buf, &cls.rcon_address); + } + } +} + +static void CL_RCon_ClearPassword_c(cvar_t *var) +{ + // whenever rcon_secure is changed to 0, clear rcon_password for + // security reasons (prevents a send-rcon-password-as-plaintext + // attack based on NQ protocol session takeover and svc_stufftext) + if(var->integer <= 0) + Cvar_SetQuick(&rcon_password, ""); +} + +void CL_InitCommands(void) +{ + Cvar_RegisterVariable(&cl_color); + Cvar_RegisterCallback(&cl_color, CL_Color_c); + Cvar_RegisterVariable(&cl_topcolor); + Cvar_RegisterCallback(&cl_topcolor, CL_Topcolor_c); + Cvar_RegisterVariable(&cl_bottomcolor); + Cvar_RegisterCallback(&cl_bottomcolor, CL_Bottomcolor_c); + Cvar_RegisterVariable(&rcon_address); + Cvar_RegisterVariable(&rcon_secure); + Cvar_RegisterCallback(&rcon_secure, CL_RCon_ClearPassword_c); + Cvar_RegisterVariable(&rcon_secure_challengetimeout); + + Cmd_AddCommand(CMD_CLIENT | CMD_CLIENT_FROM_SERVER, "cmd", CL_ForwardToServer_f, "send a console commandline to the server (used by some mods)"); + Cmd_AddCommand(CMD_CLIENT, "color", CL_Color_f, "change your player shirt and pants colors"); + Cmd_AddCommand(CMD_CLIENT, "rcon", CL_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's); note: if rcon_secure is set, client and server clocks must be synced e.g. via NTP"); + Cmd_AddCommand(CMD_CLIENT, "srcon", CL_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's); this always works as if rcon_secure is set; note: client and server clocks must be synced e.g. via NTP"); + Cmd_AddCommand(CMD_CLIENT, "pqrcon", CL_PQRcon_f, "sends a command to a proquake server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's)"); + Cmd_AddCommand(CMD_CLIENT, "packet", CL_Packet_f, "send a packet to the specified address:port containing a text string"); + +} diff --git a/cl_main.c b/cl_main.c index 3026f669..e3a06b95 100644 --- a/cl_main.c +++ b/cl_main.c @@ -2759,6 +2759,8 @@ void CL_Init (void) // // register our commands // + CL_InitCommands(); + Cvar_RegisterVariable (&cl_upspeed); Cvar_RegisterVariable (&cl_forwardspeed); Cvar_RegisterVariable (&cl_backspeed); diff --git a/cl_parse.c b/cl_parse.c index aa7a311c..685233bc 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -1116,9 +1116,9 @@ static void CL_BeginDownloads(qboolean aborteddownload) { Con_Printf("Downloading new CSQC code to dlcache/%s.%i.%i\n", csqc_progname.string, csqc_progsize.integer, csqc_progcrc.integer); if(cl_serverextension_download.integer == 2 && FS_HasZlib()) - Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "download %s deflate", csqc_progname.string)); + CL_ForwardToServer(va(vabuf, sizeof(vabuf), "download %s deflate", csqc_progname.string)); else - Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "download %s", csqc_progname.string)); + CL_ForwardToServer(va(vabuf, sizeof(vabuf), "download %s", csqc_progname.string)); return; } } @@ -1197,7 +1197,7 @@ static void CL_BeginDownloads(qboolean aborteddownload) cl.loadfinished = true; // now issue the spawn to move on to signon 2 like normal if (cls.netcon) - Cmd_ForwardStringToServer("prespawn"); + CL_ForwardToServer("prespawn"); } } } @@ -1267,7 +1267,7 @@ static void CL_BeginDownloads(qboolean aborteddownload) cl.loadfinished = true; // now issue the spawn to move on to signon 2 like normal if (cls.netcon) - Cmd_ForwardStringToServer("prespawn"); + CL_ForwardToServer("prespawn"); } } aborteddownload = false; @@ -1285,7 +1285,7 @@ static void CL_BeginDownloads(qboolean aborteddownload) // regarding the * check: don't try to download submodels if (cl_serverextension_download.integer && cls.netcon && cl.model_name[cl.downloadmodel_current][0] != '*' && !sv.active) { - Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "download %s", cl.model_name[cl.downloadmodel_current])); + CL_ForwardToServer(va(vabuf, sizeof(vabuf), "download %s", cl.model_name[cl.downloadmodel_current])); // we'll try loading again when the download finishes return; } @@ -1309,7 +1309,7 @@ static void CL_BeginDownloads(qboolean aborteddownload) cl.loadfinished = true; // now issue the spawn to move on to signon 2 like normal if (cls.netcon) - Cmd_ForwardStringToServer("prespawn"); + CL_ForwardToServer("prespawn"); } } } @@ -1338,7 +1338,7 @@ static void CL_BeginDownloads(qboolean aborteddownload) Con_Printf("Sound %s not found\n", soundname); if (cl_serverextension_download.integer && cls.netcon && !sv.active) { - Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "download %s", soundname)); + CL_ForwardToServer(va(vabuf, sizeof(vabuf), "download %s", soundname)); // we'll try loading again when the download finishes return; } @@ -1360,7 +1360,7 @@ static void CL_BeginDownloads(qboolean aborteddownload) // now issue the spawn to move on to signon 2 like normal if (cls.netcon) - Cmd_ForwardStringToServer("prespawn"); + CL_ForwardToServer("prespawn"); } } @@ -1537,7 +1537,7 @@ static void CL_DownloadBegin_f(cmd_state_t *cmd) // check further encodings here } - Cmd_ForwardStringToServer("sv_startdownload"); + CL_ForwardToServer("sv_startdownload"); } static void CL_StopDownload_f(cmd_state_t *cmd) diff --git a/client.h b/client.h index c154d991..aa017ba5 100644 --- a/client.h +++ b/client.h @@ -1593,6 +1593,20 @@ float CL_KeyState (kbutton_t *key); const char *Key_KeynumToString (int keynum, char *buf, size_t buflength); int Key_StringToKeynum (const char *str); +// +// cl_cmd.c +// +/// adds the string as a clc_stringcmd to the client message. +/// (used when there is no reason to generate a local command to do it) +void CL_ForwardToServer (const char *s); + +/// adds the current command line as a clc_stringcmd to the client message. +/// things like godmode, noclip, etc, are commands directed to the server, +/// so when they are typed in at the console, they will need to be forwarded. +void CL_ForwardToServer_f (cmd_state_t *cmd); +void CL_InitCommands(void); + + // // cl_demo.c // diff --git a/cmd.c b/cmd.c index 64a90e26..ff7c138f 100644 --- a/cmd.c +++ b/cmd.c @@ -1527,7 +1527,6 @@ void Cmd_Init(void) // register our commands // // client-only commands - Cmd_AddCommand(CMD_CLIENT | CMD_CLIENT_FROM_SERVER, "cmd", Cmd_ForwardToServer_f, "send a console commandline to the server (used by some mods)"); Cmd_AddCommand(CMD_SHARED, "wait", Cmd_Wait_f, "make script execution wait for next rendered frame"); Cmd_AddCommand(CMD_CLIENT, "cprint", Cmd_Centerprint_f, "print something at the screen center"); @@ -1701,7 +1700,7 @@ void Cmd_AddCommand(int flags, const char *cmd_name, xcommand_t function, const if(cmd == &cmd_client && (flags & CMD_SERVER_FROM_CLIENT) && !(flags & CMD_CLIENT)) { save = function; - function = Cmd_ForwardToServer_f; + function = CL_ForwardToServer_f; } // fail if the command is a variable name if (Cvar_FindVar(cmd->cvars, cmd_name, ~0)) @@ -2105,162 +2104,6 @@ done: Cbuf_Unlock(cmd); } - -/* -=================== -Cmd_ForwardStringToServer - -Sends an entire command string over to the server, unprocessed -=================== -*/ -void Cmd_ForwardStringToServer (const char *s) -{ - char temp[128]; - if (cls.state != ca_connected) - { - Con_Printf("Can't \"%s\", not connected\n", s); - return; - } - - if (!cls.netcon) - return; - - // LadyHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my - // attention, it has been eradicated from here, its only (former) use in - // all of darkplaces. - if (cls.protocol == PROTOCOL_QUAKEWORLD) - MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd); - else - MSG_WriteByte(&cls.netcon->message, clc_stringcmd); - if ((!strncmp(s, "say ", 4) || !strncmp(s, "say_team ", 9)) && cl_locs_enable.integer) - { - // say/say_team commands can replace % character codes with status info - while (*s) - { - if (*s == '%' && s[1]) - { - // handle proquake message macros - temp[0] = 0; - switch (s[1]) - { - case 'l': // current location - CL_Locs_FindLocationName(temp, sizeof(temp), cl.movement_origin); - break; - case 'h': // current health - dpsnprintf(temp, sizeof(temp), "%i", cl.stats[STAT_HEALTH]); - break; - case 'a': // current armor - dpsnprintf(temp, sizeof(temp), "%i", cl.stats[STAT_ARMOR]); - break; - case 'x': // current rockets - dpsnprintf(temp, sizeof(temp), "%i", cl.stats[STAT_ROCKETS]); - break; - case 'c': // current cells - dpsnprintf(temp, sizeof(temp), "%i", cl.stats[STAT_CELLS]); - break; - // silly proquake macros - case 'd': // loc at last death - CL_Locs_FindLocationName(temp, sizeof(temp), cl.lastdeathorigin); - break; - case 't': // current time - dpsnprintf(temp, sizeof(temp), "%.0f:%.0f", floor(cl.time / 60), cl.time - floor(cl.time / 60) * 60); - break; - case 'r': // rocket launcher status ("I have RL", "I need rockets", "I need RL") - if (!(cl.stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER)) - dpsnprintf(temp, sizeof(temp), "I need RL"); - else if (!cl.stats[STAT_ROCKETS]) - dpsnprintf(temp, sizeof(temp), "I need rockets"); - else - dpsnprintf(temp, sizeof(temp), "I have RL"); - break; - case 'p': // powerup status (outputs "quad" "pent" and "eyes" according to status) - if (cl.stats[STAT_ITEMS] & IT_QUAD) - { - if (temp[0]) - strlcat(temp, " ", sizeof(temp)); - strlcat(temp, "quad", sizeof(temp)); - } - if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) - { - if (temp[0]) - strlcat(temp, " ", sizeof(temp)); - strlcat(temp, "pent", sizeof(temp)); - } - if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) - { - if (temp[0]) - strlcat(temp, " ", sizeof(temp)); - strlcat(temp, "eyes", sizeof(temp)); - } - break; - case 'w': // weapon status (outputs "SSG:NG:SNG:GL:RL:LG" with the text between : characters omitted if you lack the weapon) - if (cl.stats[STAT_ITEMS] & IT_SUPER_SHOTGUN) - strlcat(temp, "SSG", sizeof(temp)); - strlcat(temp, ":", sizeof(temp)); - if (cl.stats[STAT_ITEMS] & IT_NAILGUN) - strlcat(temp, "NG", sizeof(temp)); - strlcat(temp, ":", sizeof(temp)); - if (cl.stats[STAT_ITEMS] & IT_SUPER_NAILGUN) - strlcat(temp, "SNG", sizeof(temp)); - strlcat(temp, ":", sizeof(temp)); - if (cl.stats[STAT_ITEMS] & IT_GRENADE_LAUNCHER) - strlcat(temp, "GL", sizeof(temp)); - strlcat(temp, ":", sizeof(temp)); - if (cl.stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER) - strlcat(temp, "RL", sizeof(temp)); - strlcat(temp, ":", sizeof(temp)); - if (cl.stats[STAT_ITEMS] & IT_LIGHTNING) - strlcat(temp, "LG", sizeof(temp)); - break; - default: - // not a recognized macro, print it as-is... - temp[0] = s[0]; - temp[1] = s[1]; - temp[2] = 0; - break; - } - // write the resulting text - SZ_Write(&cls.netcon->message, (unsigned char *)temp, (int)strlen(temp)); - s += 2; - continue; - } - MSG_WriteByte(&cls.netcon->message, *s); - s++; - } - MSG_WriteByte(&cls.netcon->message, 0); - } - else // any other command is passed on as-is - SZ_Write(&cls.netcon->message, (const unsigned char *)s, (int)strlen(s) + 1); -} - -/* -=================== -Cmd_ForwardToServer - -Sends the entire command line over to the server -=================== -*/ -void Cmd_ForwardToServer_f (cmd_state_t *cmd) -{ - const char *s; - char vabuf[1024]; - if (!strcasecmp(Cmd_Argv(cmd, 0), "cmd")) - { - // we want to strip off "cmd", so just send the args - s = Cmd_Argc(cmd) > 1 ? Cmd_Args(cmd) : ""; - } - else - { - // we need to keep the command name, so send Cmd_Argv(cmd, 0), a space and then Cmd_Args(cmd) - s = va(vabuf, sizeof(vabuf), "%s %s", Cmd_Argv(cmd, 0), Cmd_Argc(cmd) > 1 ? Cmd_Args(cmd) : ""); - } - // don't send an empty forward message if the user tries "cmd" by itself - if (!s || !*s) - return; - Cmd_ForwardStringToServer(s); -} - - /* ================ Cmd_CheckParm diff --git a/cmd.h b/cmd.h index 72f481b6..40b71958 100644 --- a/cmd.h +++ b/cmd.h @@ -241,15 +241,6 @@ int Cmd_CheckParm (cmd_state_t *cmd, const char *parm); /// The text can come from the command buffer, a remote client, or stdin. void Cmd_ExecuteString (cmd_state_t *cmd, const char *text, cmd_source_t src, qboolean lockmutex); -/// adds the string as a clc_stringcmd to the client message. -/// (used when there is no reason to generate a local command to do it) -void Cmd_ForwardStringToServer (const char *s); - -/// adds the current command line as a clc_stringcmd to the client message. -/// things like godmode, noclip, etc, are commands directed to the server, -/// so when they are typed in at the console, they will need to be forwarded. -void Cmd_ForwardToServer_f (cmd_state_t *cmd); - /// quotes a string so that it can be used as a command argument again; /// quoteset is a string that contains one or more of ", \, $ and specifies /// the characters to be quoted (you usually want to either pass "\"\\" or diff --git a/host_cmd.c b/host_cmd.c index a7109dd4..01790dd7 100644 --- a/host_cmd.c +++ b/host_cmd.c @@ -25,22 +25,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "prvm_cmds.h" #include "utf8lib.h" -// for secure rcon authentication -#include "hmac.h" -#include "mdfour.h" -#include - extern cvar_t sv_adminnick; extern cvar_t sv_status_privacy; extern cvar_t sv_status_show_qcstatus; extern cvar_t sv_namechangetimer; -cvar_t rcon_password = {CVAR_CLIENT | CVAR_SERVER | 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_CLIENT | CVAR_SERVER, "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 = {CVAR_CLIENT, "rcon_secure_challengetimeout", "5", "challenge-based secure rcon: time out requests if no challenge came within this time interval"}; -cvar_t rcon_address = {CVAR_CLIENT, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"}; -cvar_t cl_name = {CVAR_CLIENT | CVAR_SAVE | CVAR_USERINFO, "name", "player", "change your player name"}; -cvar_t cl_topcolor = {CVAR_CLIENT | CVAR_SAVE | CVAR_USERINFO, "topcolor", "0", "change the color of your shirt"}; -cvar_t cl_bottomcolor = {CVAR_CLIENT | CVAR_SAVE | CVAR_USERINFO, "bottomcolor", "0", "change the color of your pants"}; cvar_t cl_team = {CVAR_CLIENT | CVAR_USERINFO | CVAR_SAVE, "team", "none", "QW team (4 character limit, example: blue)"}; cvar_t cl_skin = {CVAR_CLIENT | CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"}; cvar_t cl_playermodel = {CVAR_CLIENT | CVAR_SERVER | CVAR_USERINFO | CVAR_SAVE, "playermodel", "", "current player model in Nexuiz/Xonotic"}; @@ -172,85 +160,6 @@ static void CL_Playerskin_f(cmd_state_t *cmd) } } -/* -================== -CL_Color_f -================== -*/ -cvar_t cl_color = {CVAR_READONLY | CVAR_CLIENT | CVAR_SAVE, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"}; - -// Ignore the callbacks so this two-to-three way synchronization doesn't cause an infinite loop. -static void CL_Color_c(cvar_t *var) -{ - char vabuf[1024]; - - Cvar_Set_NoCallback(&cl_topcolor, va(vabuf, sizeof(vabuf), "%i", ((var->integer >> 4) & 15))); - Cvar_Set_NoCallback(&cl_bottomcolor, va(vabuf, sizeof(vabuf), "%i", (var->integer & 15))); -} - -static void CL_Topcolor_c(cvar_t *var) -{ - char vabuf[1024]; - - Cvar_Set_NoCallback(&cl_color, va(vabuf, sizeof(vabuf), "%i", var->integer*16 + cl_bottomcolor.integer)); -} - -static void CL_Bottomcolor_c(cvar_t *var) -{ - char vabuf[1024]; - - Cvar_Set_NoCallback(&cl_color, va(vabuf, sizeof(vabuf), "%i", cl_topcolor.integer*16 + var->integer)); -} - -static void CL_Color_f(cmd_state_t *cmd) -{ - int top, bottom; - - if (Cmd_Argc(cmd) == 1) - { - if (cmd->source == src_command) - { - Con_Printf("\"color\" is \"%i %i\"\n", cl_topcolor.integer, cl_bottomcolor.integer); - Con_Print("color <0-15> [0-15]\n"); - } - return; - } - - if (Cmd_Argc(cmd) == 2) - top = bottom = atoi(Cmd_Argv(cmd, 1)); - else - { - top = atoi(Cmd_Argv(cmd, 1)); - bottom = atoi(Cmd_Argv(cmd, 2)); - } - /* - * This is just a convenient way to change topcolor and bottomcolor - * We can't change cl_color from here directly because topcolor and - * bottomcolor may be changed separately and do not call this function. - * So it has to be changed when the userinfo strings are updated, which - * happens twice here. Perhaps find a cleaner way? - */ - - top = top >= 0 ? top : cl_topcolor.integer; - bottom = bottom >= 0 ? bottom : cl_bottomcolor.integer; - - top &= 15; - bottom &= 15; - - // LadyHavoc: allowing skin colormaps 14 and 15 by commenting this out - //if (top > 13) - // top = 13; - //if (bottom > 13) - // bottom = 13; - - if (cmd->source == src_command) - { - Cvar_SetValueQuick(&cl_topcolor, top); - Cvar_SetValueQuick(&cl_bottomcolor, bottom); - return; - } -} - cvar_t cl_rate = {CVAR_CLIENT | CVAR_SAVE | CVAR_USERINFO, "rate", "20000", "change your connection speed"}; cvar_t cl_rate_burstsize = {CVAR_CLIENT | CVAR_SAVE | CVAR_USERINFO, "rate_burstsize", "1024", "internal storage cvar for current rate control burst size (changed by rate_burstsize command)"}; @@ -282,7 +191,7 @@ static void CL_PModel_f(cmd_state_t *cmd) return; Cvar_SetValue (&cvars_all, "_cl_pmodel", i); if (cls.state == ca_connected) - Cmd_ForwardToServer_f(cmd); + CL_ForwardToServer_f(cmd); return; } @@ -310,9 +219,9 @@ static void CL_SendCvar_f(cmd_state_t *cmd) // LadyHavoc: if there is no such cvar or if it is private, send a // reply indicating that it has no value if(!c || (c->flags & CVAR_PRIVATE)) - Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s", cvarname)); + CL_ForwardToServer(va(vabuf, sizeof(vabuf), "sentcvar %s", cvarname)); else - Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s \"%s\"", c->name, c->string)); + CL_ForwardToServer(va(vabuf, sizeof(vabuf), "sentcvar %s \"%s\"", c->name, c->string)); return; } if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand)) @@ -332,165 +241,10 @@ static void CL_SendCvar_f(cmd_state_t *cmd) host_client = old; } -/* -===================== -CL_PQRcon_f - -ProQuake rcon support -===================== -*/ -static void CL_PQRcon_f(cmd_state_t *cmd) -{ - int n; - const char *e; - lhnetsocket_t *mysocket; - - if (Cmd_Argc(cmd) == 1) - { - Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0)); - return; - } - - if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0) - { - Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n"); - return; - } - - e = strchr(rcon_password.string, ' '); - n = e ? e-rcon_password.string : (int)strlen(rcon_password.string); - - if (cls.netcon) - cls.rcon_address = cls.netcon->peeraddress; - else - { - if (!rcon_address.string[0]) - { - Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n"); - return; - } - LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer); - } - mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address); - if (mysocket) - { - sizebuf_t buf; - unsigned char bufdata[64]; - buf.data = bufdata; - SZ_Clear(&buf); - MSG_WriteLong(&buf, 0); - MSG_WriteByte(&buf, CCREQ_RCON); - SZ_Write(&buf, (const unsigned char*)rcon_password.string, n); - MSG_WriteByte(&buf, 0); // terminate the (possibly partial) string - MSG_WriteString(&buf, Cmd_Args(cmd)); - StoreBigLong(buf.data, NETFLAG_CTL | (buf.cursize & NETFLAG_LENGTH_MASK)); - NetConn_Write(mysocket, buf.data, buf.cursize, &cls.rcon_address); - SZ_Clear(&buf); - } -} - //============================================================================= // QuakeWorld commands -/* -===================== -CL_Rcon_f - - Send the rest of the command line over as - an unconnected command. -===================== -*/ -static void CL_Rcon_f(cmd_state_t *cmd) // credit: taken from QuakeWorld -{ - int i, n; - const char *e; - lhnetsocket_t *mysocket; - - if (Cmd_Argc(cmd) == 1) - { - Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0)); - return; - } - - if (!rcon_password.string || !rcon_password.string[0]) - { - Con_Printf ("You must set rcon_password before issuing an rcon command.\n"); - return; - } - - e = strchr(rcon_password.string, ' '); - n = e ? e-rcon_password.string : (int)strlen(rcon_password.string); - - if (cls.netcon) - cls.rcon_address = cls.netcon->peeraddress; - else - { - if (!rcon_address.string[0]) - { - Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n"); - return; - } - LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer); - } - mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address); - if (mysocket && Cmd_Args(cmd)[0]) - { - // simply put together the rcon packet and send it - if(Cmd_Argv(cmd, 0)[0] == 's' || rcon_secure.integer > 1) - { - if(cls.rcon_commands[cls.rcon_ringpos][0]) - { - char s[128]; - LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true); - Con_Printf("rcon to %s (for command %s) failed: too many buffered commands (possibly increase MAX_RCONS)\n", s, cls.rcon_commands[cls.rcon_ringpos]); - cls.rcon_commands[cls.rcon_ringpos][0] = 0; - --cls.rcon_trying; - } - for (i = 0;i < MAX_RCONS;i++) - if(cls.rcon_commands[i][0]) - if (!LHNETADDRESS_Compare(&cls.rcon_address, &cls.rcon_addresses[i])) - break; - ++cls.rcon_trying; - if(i >= MAX_RCONS) - NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &cls.rcon_address); // otherwise we'll request the challenge later - strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(cmd), sizeof(cls.rcon_commands[cls.rcon_ringpos])); - cls.rcon_addresses[cls.rcon_ringpos] = cls.rcon_address; - cls.rcon_timeout[cls.rcon_ringpos] = host.realtime + rcon_secure_challengetimeout.value; - cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS; - } - else if(rcon_secure.integer > 0) - { - char buf[1500]; - char argbuf[1500]; - dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args(cmd)); - memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24); - if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n)) - { - buf[40] = ' '; - strlcpy(buf + 41, argbuf, sizeof(buf) - 41); - NetConn_Write(mysocket, buf, 41 + (int)strlen(buf + 41), &cls.rcon_address); - } - } - else - { - char buf[1500]; - memcpy(buf, "\377\377\377\377", 4); - dpsnprintf(buf+4, sizeof(buf)-4, "rcon %.*s %s", n, rcon_password.string, Cmd_Args(cmd)); - NetConn_WriteString(mysocket, buf, &cls.rcon_address); - } - } -} - -static void CL_RCon_ClearPassword_c(cvar_t *var) -{ - // whenever rcon_secure is changed to 0, clear rcon_password for - // security reasons (prevents a send-rcon-password-as-plaintext - // attack based on NQ protocol session takeover and svc_stufftext) - if(var->integer <= 0) - Cvar_SetQuick(&rcon_password, ""); -} - /* ================== CL_FullServerinfo_f @@ -590,81 +344,6 @@ static void CL_SetInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld CL_SetInfo(Cmd_Argv(cmd, 1), Cmd_Argv(cmd, 2), true, false, false, false); } -/* -==================== -CL_Packet_f - -packet - -Contents allows \n escape character -==================== -*/ -static void CL_Packet_f(cmd_state_t *cmd) // credit: taken from QuakeWorld -{ - char send[2048]; - int i, l; - const char *in; - char *out; - lhnetaddress_t address; - lhnetsocket_t *mysocket; - - if (Cmd_Argc(cmd) != 3) - { - Con_Printf ("packet \n"); - return; - } - - if (!LHNETADDRESS_FromString (&address, Cmd_Argv(cmd, 1), sv_netport.integer)) - { - Con_Printf ("Bad address\n"); - return; - } - - in = Cmd_Argv(cmd, 2); - out = send+4; - send[0] = send[1] = send[2] = send[3] = -1; - - l = (int)strlen (in); - for (i=0 ; i= send + sizeof(send) - 1) - break; - if (in[i] == '\\' && in[i+1] == 'n') - { - *out++ = '\n'; - i++; - } - else if (in[i] == '\\' && in[i+1] == '0') - { - *out++ = '\0'; - i++; - } - else if (in[i] == '\\' && in[i+1] == 't') - { - *out++ = '\t'; - i++; - } - else if (in[i] == '\\' && in[i+1] == 'r') - { - *out++ = '\r'; - i++; - } - else if (in[i] == '\\' && in[i+1] == '"') - { - *out++ = '\"'; - i++; - } - else - *out++ = in[i]; - } - - mysocket = NetConn_ChooseClientSocketForAddress(&address); - if (!mysocket) - mysocket = NetConn_ChooseServerSocketForAddress(&address); - if (mysocket) - NetConn_Write(mysocket, send, out - send, &address); -} - static void CL_PingPLReport_f(cmd_state_t *cmd) { char *errbyte; @@ -696,12 +375,6 @@ void Host_InitCommands (void) Cvar_RegisterVariable(&cl_name); Cvar_RegisterAlias(&cl_name, "_cl_name"); - Cvar_RegisterVariable(&cl_color); - Cvar_RegisterCallback(&cl_color, CL_Color_c); - Cvar_RegisterVariable(&cl_topcolor); - Cvar_RegisterCallback(&cl_topcolor, CL_Topcolor_c); - Cvar_RegisterVariable(&cl_bottomcolor); - Cvar_RegisterCallback(&cl_bottomcolor, CL_Bottomcolor_c); Cvar_RegisterVariable(&cl_rate); Cvar_RegisterAlias(&cl_rate, "_cl_rate"); Cvar_RegisterVariable(&cl_rate_burstsize); @@ -713,27 +386,18 @@ void Host_InitCommands (void) Cvar_RegisterVariable(&cl_playerskin); Cvar_RegisterAlias(&cl_playerskin, "_cl_playerskin"); Cvar_RegisterVariable(&rcon_password); - Cvar_RegisterVariable(&rcon_address); - Cvar_RegisterVariable(&rcon_secure); - Cvar_RegisterCallback(&rcon_secure, CL_RCon_ClearPassword_c); - Cvar_RegisterVariable(&rcon_secure_challengetimeout); Cvar_RegisterVariable(&r_fixtrans_auto); Cvar_RegisterVariable(&cl_team); Cvar_RegisterVariable(&cl_skin); Cvar_RegisterVariable(&cl_noaim); - Cmd_AddCommand(CMD_CLIENT, "color", CL_Color_f, "change your player shirt and pants colors"); Cmd_AddCommand(CMD_USERINFO, "pmodel", CL_PModel_f, "(Nehahra-only) change your player model choice"); Cmd_AddCommand(CMD_USERINFO, "playermodel", CL_Playermodel_f, "change your player model"); Cmd_AddCommand(CMD_USERINFO, "playerskin", CL_Playerskin_f, "change your player skin number"); Cmd_AddCommand(CMD_CLIENT, "sendcvar", CL_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC"); - Cmd_AddCommand(CMD_CLIENT, "rcon", CL_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's); note: if rcon_secure is set, client and server clocks must be synced e.g. via NTP"); - Cmd_AddCommand(CMD_CLIENT, "srcon", CL_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's); this always works as if rcon_secure is set; note: client and server clocks must be synced e.g. via NTP"); - Cmd_AddCommand(CMD_CLIENT, "pqrcon", CL_PQRcon_f, "sends a command to a proquake server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's)"); Cmd_AddCommand(CMD_CLIENT, "fullinfo", CL_FullInfo_f, "allows client to modify their userinfo"); Cmd_AddCommand(CMD_CLIENT, "setinfo", CL_SetInfo_f, "modifies your userinfo"); - Cmd_AddCommand(CMD_CLIENT, "packet", CL_Packet_f, "send a packet to the specified address:port containing a text string"); Cmd_AddCommand(CMD_CLIENT, "fixtrans", Image_FixTransparentPixels_f, "change alpha-zero pixels in an image file to sensible values, and write out a new TGA (warning: SLOW)"); // commands that are only sent by server to client for execution diff --git a/keys.c b/keys.c index 1392be63..7363590e 100644 --- a/keys.c +++ b/keys.c @@ -1307,7 +1307,7 @@ Key_Message (cmd_state_t *cmd, int key, int ascii) if(chat_mode < 0) Cmd_ExecuteString(cmd, chat_buffer, src_command, true); // not Cbuf_AddText to allow semiclons in args; however, this allows no variables then. Use aliases! else - Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "%s %s", chat_mode ? "say_team" : "say ", chat_buffer)); + CL_ForwardToServer(va(vabuf, sizeof(vabuf), "%s %s", chat_mode ? "say_team" : "say ", chat_buffer)); key_dest = key_game; chat_bufferpos = Key_ClearEditLine(false); diff --git a/makefile.inc b/makefile.inc index fd5792d6..87b97c89 100644 --- a/makefile.inc +++ b/makefile.inc @@ -80,6 +80,7 @@ OBJ_COMMON= \ crypto.o \ cd_shared.o \ cl_collision.o \ + cl_cmd.o \ cl_demo.o \ cl_input.o \ cl_main.o \ diff --git a/netconn.c b/netconn.c index 4731a0db..aac264d1 100755 --- a/netconn.c +++ b/netconn.c @@ -1549,7 +1549,7 @@ static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_ // reset move sequence numbering on this new connection cls.servermovesequence = 0; if (cls.protocol == PROTOCOL_QUAKEWORLD) - Cmd_ForwardStringToServer("new"); + CL_ForwardToServer("new"); if (cls.protocol == PROTOCOL_QUAKE) { // write a keepalive (clc_nop) as it seems to greatly improve the diff --git a/sv_ccmds.c b/sv_ccmds.c index abb6f736..0c2674c6 100644 --- a/sv_ccmds.c +++ b/sv_ccmds.c @@ -413,7 +413,7 @@ static void SV_Pause_f(cmd_state_t *cmd) // if running a client, try to send over network so the pause is handled by the server if (cls.state == ca_connected) { - Cmd_ForwardToServer_f(cmd); + CL_ForwardToServer_f(cmd); return; } print = Con_Printf; @@ -465,7 +465,7 @@ static void SV_Say(cmd_state_t *cmd, qboolean teamonly) } else { - Cmd_ForwardToServer_f(cmd); + CL_ForwardToServer_f(cmd); return; } } @@ -540,7 +540,7 @@ static void SV_Tell_f(cmd_state_t *cmd) fromServer = true; else { - Cmd_ForwardToServer_f(cmd); + CL_ForwardToServer_f(cmd); return; } } @@ -668,7 +668,7 @@ static void SV_Ping_f(cmd_state_t *cmd) // 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_f(cmd); + CL_ForwardToServer_f(cmd); return; } print = Con_Printf; @@ -830,7 +830,7 @@ static void SV_Status_f(cmd_state_t *cmd) // 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_f(cmd); + CL_ForwardToServer_f(cmd); return; } print = Con_Printf; -- 2.39.2