X-Git-Url: http://git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=cl_cmd.c;h=4f844a821f25981c9f86d9053885871e20713f6f;hp=d17c6fda71c1d2224655185dbbb52e9efbe6620c;hb=HEAD;hpb=40bbda3135e611673d899661fd4fd29486a60c2f diff --git a/cl_cmd.c b/cl_cmd.c index d17c6fda..4556d2cf 100644 --- a/cl_cmd.c +++ b/cl_cmd.c @@ -26,20 +26,23 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "image.h" #include -cvar_t cl_name = {CVAR_CLIENT | CVAR_SAVE | CVAR_USERINFO, "name", "player", "change your player name"}; -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)"}; -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_noaim = {CVAR_CLIENT | CVAR_USERINFO | CVAR_SAVE, "noaim", "1", "QW option to disable vertical autoaim"}; -cvar_t cl_pmodel = {CVAR_CLIENT | CVAR_USERINFO | CVAR_SAVE, "pmodel", "0", "current player model number in nehahra"}; -cvar_t r_fixtrans_auto = {CVAR_CLIENT, "r_fixtrans_auto", "0", "automatically fixtrans textures (when set to 2, it also saves the fixed versions to a fixtrans directory)"}; -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)"}; +#include "cl_collision.h" + +/// User-visible names of these CF_USERINFO cvars must be matched in CL_SetInfo()! +cvar_t cl_name = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "_cl_name", "player", "player name"}; +cvar_t cl_rate = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "rate", "20000", "connection speed"}; +cvar_t cl_rate_burstsize = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "rate_burstsize", "1024", "rate control burst size"}; +cvar_t cl_topcolor = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "topcolor", "0", "color of your shirt"}; +cvar_t cl_bottomcolor = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "bottomcolor", "0", "color of your pants"}; +cvar_t cl_team = {CF_CLIENT | CF_USERINFO | CF_ARCHIVE, "team", "none", "QW team (4 character limit, example: blue)"}; +cvar_t cl_skin = {CF_CLIENT | CF_USERINFO | CF_ARCHIVE, "skin", "", "QW player skin name (example: base)"}; +cvar_t cl_noaim = {CF_CLIENT | CF_USERINFO | CF_ARCHIVE, "noaim", "1", "QW option to disable vertical autoaim"}; +cvar_t cl_pmodel = {CF_CLIENT | CF_USERINFO | CF_ARCHIVE, "pmodel", "0", "current player model number in nehahra"}; + +cvar_t r_fixtrans_auto = {CF_CLIENT, "r_fixtrans_auto", "0", "automatically fixtrans textures (when set to 2, it also saves the fixed versions to a fixtrans directory)"}; + +extern cvar_t rcon_secure; +extern cvar_t rcon_secure_challengetimeout; /* =================== @@ -112,40 +115,40 @@ void CL_ForwardToServer (const char *s) if (cl.stats[STAT_ITEMS] & IT_QUAD) { if (temp[0]) - strlcat(temp, " ", sizeof(temp)); - strlcat(temp, "quad", sizeof(temp)); + dp_strlcat(temp, " ", sizeof(temp)); + dp_strlcat(temp, "quad", sizeof(temp)); } if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) { if (temp[0]) - strlcat(temp, " ", sizeof(temp)); - strlcat(temp, "pent", sizeof(temp)); + dp_strlcat(temp, " ", sizeof(temp)); + dp_strlcat(temp, "pent", sizeof(temp)); } if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) { if (temp[0]) - strlcat(temp, " ", sizeof(temp)); - strlcat(temp, "eyes", sizeof(temp)); + dp_strlcat(temp, " ", sizeof(temp)); + dp_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)); + dp_strlcat(temp, "SSG", sizeof(temp)); + dp_strlcat(temp, ":", sizeof(temp)); if (cl.stats[STAT_ITEMS] & IT_NAILGUN) - strlcat(temp, "NG", sizeof(temp)); - strlcat(temp, ":", sizeof(temp)); + dp_strlcat(temp, "NG", sizeof(temp)); + dp_strlcat(temp, ":", sizeof(temp)); if (cl.stats[STAT_ITEMS] & IT_SUPER_NAILGUN) - strlcat(temp, "SNG", sizeof(temp)); - strlcat(temp, ":", sizeof(temp)); + dp_strlcat(temp, "SNG", sizeof(temp)); + dp_strlcat(temp, ":", sizeof(temp)); if (cl.stats[STAT_ITEMS] & IT_GRENADE_LAUNCHER) - strlcat(temp, "GL", sizeof(temp)); - strlcat(temp, ":", sizeof(temp)); + dp_strlcat(temp, "GL", sizeof(temp)); + dp_strlcat(temp, ":", sizeof(temp)); if (cl.stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER) - strlcat(temp, "RL", sizeof(temp)); - strlcat(temp, ":", sizeof(temp)); + dp_strlcat(temp, "RL", sizeof(temp)); + dp_strlcat(temp, ":", sizeof(temp)); if (cl.stats[STAT_ITEMS] & IT_LIGHTNING) - strlcat(temp, "LG", sizeof(temp)); + dp_strlcat(temp, "LG", sizeof(temp)); break; default: // not a recognized macro, print it as-is... @@ -171,7 +174,8 @@ void CL_ForwardToServer (const char *s) void CL_ForwardToServer_f (cmd_state_t *cmd) { const char *s; - char vabuf[1024]; + char vabuf[MAX_INPUTLINE]; + size_t i; if (!strcasecmp(Cmd_Argv(cmd, 0), "cmd")) { // we want to strip off "cmd", so just send the args @@ -180,7 +184,11 @@ void CL_ForwardToServer_f (cmd_state_t *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) : ""); + i = dpsnprintf(vabuf, sizeof(vabuf), "%s", Cmd_Argv(cmd, 0)); + if(Cmd_Argc(cmd) > 1) + // (i + 1) accounts for the added space + dpsnprintf(&vabuf[i], sizeof(vabuf) - (i + 1), " %s", Cmd_Args(cmd)); + s = vabuf; } // don't send an empty forward message if the user tries "cmd" by itself if (!s || !*s) @@ -199,10 +207,10 @@ static void CL_SendCvar_f(cmd_state_t *cmd) cvarname = Cmd_Argv(cmd, 1); if (cls.state == ca_connected) { - c = Cvar_FindVar(&cvars_all, cvarname, CVAR_CLIENT | CVAR_SERVER); + c = Cvar_FindVar(&cvars_all, cvarname, CF_CLIENT | CF_SERVER); // 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)) + if(!c || (c->flags & CF_PRIVATE)) CL_ForwardToServer(va(vabuf, sizeof(vabuf), "sentcvar %s", cvarname)); else CL_ForwardToServer(va(vabuf, sizeof(vabuf), "sentcvar %s \"%s\"", c->name, c->string)); @@ -210,34 +218,76 @@ static void CL_SendCvar_f(cmd_state_t *cmd) } } +/* +================== +CL_Name_f + +The logic from div0-stable's Host_Name_f() is now in SV_Name_f(). +================== +*/ +static void CL_Name_f(cmd_state_t *cmd) +{ + char *newNameSource; + + if (Cmd_Argc(cmd) == 1) + { + Con_Printf("name: \"%s^7\"\n", cl_name.string); + return; + } + + // in the single-arg case any enclosing quotes shall be stripped + newNameSource = (char *)(Cmd_Argc(cmd) == 2 ? Cmd_Argv(cmd, 1) : Cmd_Args(cmd)); + + if (strlen(newNameSource) >= MAX_SCOREBOARDNAME) // may as well truncate before networking + newNameSource[MAX_SCOREBOARDNAME - 1] = '\0'; // this is fine (cbuf stores length) + + Cvar_SetQuick(&cl_name, newNameSource); +} + /* ================== 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)"}; +cvar_t cl_color = {CF_CLIENT | CF_ARCHIVE, "_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. +// HACK: 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))); + void (*callback_save)(cvar_t *); + + callback_save = cl_topcolor.callback; + cl_topcolor.callback = NULL; + Cvar_SetQuick(&cl_topcolor, va(vabuf, sizeof(vabuf), "%i", ((var->integer >> 4) & 15))); + cl_topcolor.callback = callback_save; + + callback_save = cl_bottomcolor.callback; + cl_bottomcolor.callback = NULL; + Cvar_SetQuick(&cl_bottomcolor, va(vabuf, sizeof(vabuf), "%i", (var->integer & 15))); + cl_bottomcolor.callback = callback_save; } 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)); + void (*callback_save)(cvar_t *); + + callback_save = cl_color.callback; + cl_color.callback = NULL; + Cvar_SetQuick(&cl_color, va(vabuf, sizeof(vabuf), "%i", var->integer*16 + cl_bottomcolor.integer)); + cl_color.callback = callback_save; } static void CL_Bottomcolor_c(cvar_t *var) { char vabuf[1024]; + void (*callback_save)(cvar_t *); - Cvar_Set_NoCallback(&cl_color, va(vabuf, sizeof(vabuf), "%i", cl_topcolor.integer*16 + var->integer)); + callback_save = cl_color.callback; + cl_color.callback = NULL; + Cvar_SetQuick(&cl_color, va(vabuf, sizeof(vabuf), "%i", cl_topcolor.integer*16 + var->integer)); + cl_color.callback = callback_save; } static void CL_Color_f(cmd_state_t *cmd) @@ -246,7 +296,7 @@ static void CL_Color_f(cmd_state_t *cmd) if (Cmd_Argc(cmd) == 1) { - if (cmd->source == src_command) + if (cmd->source == src_local) { Con_Printf("\"color\" is \"%i %i\"\n", cl_topcolor.integer, cl_bottomcolor.integer); Con_Print("color <0-15> [0-15]\n"); @@ -281,7 +331,7 @@ static void CL_Color_f(cmd_state_t *cmd) //if (bottom > 13) // bottom = 13; - if (cmd->source == src_command) + if (cmd->source == src_local) { Cvar_SetValueQuick(&cl_topcolor, top); Cvar_SetValueQuick(&cl_bottomcolor, bottom); @@ -289,6 +339,68 @@ static void CL_Color_f(cmd_state_t *cmd) } } +/* +==================== +CL_User_f + +user + +Dump userdata / masterdata for a user +==================== +*/ +static void CL_User_f(cmd_state_t *cmd) // credit: taken from QuakeWorld +{ + int uid; + int i; + + if (Cmd_Argc(cmd) != 2) + { + Con_Printf ("Usage: user \n"); + return; + } + + uid = atoi(Cmd_Argv(cmd, 1)); + + for (i = 0;i < cl.maxclients;i++) + { + if (!cl.scores[i].name[0]) + continue; + if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(cmd, 1))) + { + InfoString_Print(cl.scores[i].qw_userinfo); + return; + } + } + Con_Printf ("User not in server.\n"); +} + +/* +==================== +CL_Users_f + +Dump userids for all current players +==================== +*/ +static void CL_Users_f(cmd_state_t *cmd) // credit: taken from QuakeWorld +{ + int i; + int c; + + c = 0; + Con_Printf ("userid frags name\n"); + Con_Printf ("------ ----- ----\n"); + for (i = 0;i < cl.maxclients;i++) + { + if (cl.scores[i].name[0]) + { + Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name); + c++; + } + } + + Con_Printf ("%i total users\n", c); +} + /* ==================== CL_Packet_f @@ -482,7 +594,7 @@ static void CL_Rcon_f(cmd_state_t *cmd) // credit: taken from QuakeWorld ++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])); + dp_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; @@ -496,7 +608,7 @@ static void CL_Rcon_f(cmd_state_t *cmd) // credit: taken from QuakeWorld 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); + dp_strlcpy(buf + 41, argbuf, sizeof(buf) - 41); NetConn_Write(mysocket, buf, 41 + (int)strlen(buf + 41), &cls.rcon_address); } } @@ -510,15 +622,6 @@ static void CL_Rcon_f(cmd_state_t *cmd) // credit: taken from QuakeWorld } } -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 @@ -536,7 +639,7 @@ static void CL_FullServerinfo_f(cmd_state_t *cmd) // credit: taken from QuakeWor return; } - strlcpy (cl.qw_serverinfo, Cmd_Argv(cmd, 1), sizeof(cl.qw_serverinfo)); + dp_strlcpy (cl.qw_serverinfo, Cmd_Argv(cmd, 1), sizeof(cl.qw_serverinfo)); InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp)); cl.qw_teamplay = atoi(temp); } @@ -570,7 +673,7 @@ static void CL_FullInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld if (len >= sizeof(key)) { len = sizeof(key) - 1; } - strlcpy(key, s, len + 1); + dp_strlcpy(key, s, len + 1); s += len; if (!*s) { @@ -583,7 +686,7 @@ static void CL_FullInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld if (len >= sizeof(value)) { len = sizeof(value) - 1; } - strlcpy(value, s, len + 1); + dp_strlcpy(value, s, len + 1); CL_SetInfo(key, value, false, false, false, false); @@ -640,42 +743,47 @@ void CL_InitCommands(void) { dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp"); + /* In Quake `name` is a command that concatenates its arguments (quotes unnecessary) + * which is expected in most DP-based games. + * In QuakeWorld it's a cvar which requires quotes if spaces are used. + */ Cvar_RegisterVariable(&cl_name); - Cvar_RegisterAlias(&cl_name, "_cl_name"); + if ((0)) // TODO: if (gamemode == GAME_QUAKEWORLD) + Cvar_RegisterVirtual(&cl_name, "name"); + else + Cmd_AddCommand(CF_CLIENT, "name", CL_Name_f, "change your player name"); + Cvar_RegisterVariable(&cl_rate); - Cvar_RegisterAlias(&cl_rate, "_cl_rate"); + Cvar_RegisterVirtual(&cl_rate, "_cl_rate"); Cvar_RegisterVariable(&cl_rate_burstsize); - Cvar_RegisterAlias(&cl_rate_burstsize, "_cl_rate_burstsize"); + Cvar_RegisterVirtual(&cl_rate_burstsize, "_cl_rate_burstsize"); Cvar_RegisterVariable(&cl_pmodel); - Cvar_RegisterAlias(&cl_pmodel, "_cl_pmodel"); + Cvar_RegisterVirtual(&cl_pmodel, "_cl_pmodel"); 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); - Cvar_RegisterVariable(&rcon_password); Cvar_RegisterVariable(&r_fixtrans_auto); Cvar_RegisterVariable(&cl_team); Cvar_RegisterVariable(&cl_skin); Cvar_RegisterVariable(&cl_noaim); - 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"); - 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, "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, "fixtrans", Image_FixTransparentPixels_f, "change alpha-zero pixels in an image file to sensible values, and write out a new TGA (warning: SLOW)"); + Cmd_AddCommand(CF_CLIENT | CF_CLIENT_FROM_SERVER, "cmd", CL_ForwardToServer_f, "send a console commandline to the server (used by some mods)"); + Cmd_AddCommand(CF_CLIENT, "color", CL_Color_f, "change your player shirt and pants colors"); + Cmd_AddCommand(CF_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(CF_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(CF_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(CF_SHARED, "user", CL_User_f, "prints additional information about a player number or name on the scoreboard"); + Cmd_AddCommand(CF_SHARED, "users", CL_Users_f, "prints additional information about all players on the scoreboard"); + Cmd_AddCommand(CF_CLIENT, "packet", CL_Packet_f, "send a packet to the specified address:port containing a text string"); + Cmd_AddCommand(CF_CLIENT, "fullinfo", CL_FullInfo_f, "allows client to modify their userinfo"); + Cmd_AddCommand(CF_CLIENT, "setinfo", CL_SetInfo_f, "modifies your userinfo"); + Cmd_AddCommand(CF_CLIENT, "fixtrans", Image_FixTransparentPixels_f, "change alpha-zero pixels in an image file to sensible values, and write out a new TGA (warning: SLOW)"); + host.hook.CL_SendCvar = CL_SendCvar_f; // commands that are only sent by server to client for execution - Cmd_AddCommand(CMD_CLIENT_FROM_SERVER, "pingplreport", CL_PingPLReport_f, "command sent by server containing client ping and packet loss values for scoreboard, triggered by pings command from client (not used by QW servers)"); - Cmd_AddCommand(CMD_CLIENT_FROM_SERVER, "fullserverinfo", CL_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string"); + Cmd_AddCommand(CF_CLIENT_FROM_SERVER, "pingplreport", CL_PingPLReport_f, "command sent by server containing client ping and packet loss values for scoreboard, triggered by pings command from client (not used by QW servers)"); + Cmd_AddCommand(CF_CLIENT_FROM_SERVER, "fullserverinfo", CL_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string"); }