void Cmd_StuffCmds_f (void)
{
int i, j, l;
- // this is per command, and bounds checked (no buffer overflows)
+ // this is for all commandline options combined (and is bounds checked)
char build[MAX_INPUTLINE];
if (Cmd_Argc () != 1)
return;
}
+ // no reason to run the commandline arguments twice
+ if (host_stuffcmdsrun)
+ return;
+
host_stuffcmdsrun = true;
+ build[0] = 0;
+ l = 0;
for (i = 0;i < com_argc;i++)
{
- if (com_argv[i] && com_argv[i][0] == '+' && (com_argv[i][1] < '0' || com_argv[i][1] > '9'))
+ if (com_argv[i] && com_argv[i][0] == '+' && (com_argv[i][1] < '0' || com_argv[i][1] > '9') && l + strlen(com_argv[i]) - 1 <= sizeof(build) - 1)
{
- l = 0;
j = 1;
while (com_argv[i][j])
build[l++] = com_argv[i][j++];
continue;
if ((com_argv[i][0] == '+' || com_argv[i][0] == '-') && (com_argv[i][1] < '0' || com_argv[i][1] > '9'))
break;
- if (l + strlen(com_argv[i]) + 5 > sizeof(build))
+ if (l + strlen(com_argv[i]) + 4 > sizeof(build) - 1)
break;
build[l++] = ' ';
- build[l++] = '\"';
+ if (strchr(com_argv[i], ' '))
+ build[l++] = '\"';
for (j = 0;com_argv[i][j];j++)
build[l++] = com_argv[i][j];
- build[l++] = '\"';
+ if (strchr(com_argv[i], ' '))
+ build[l++] = '\"';
}
build[l++] = '\n';
- build[l++] = 0;
- Cbuf_InsertText (build);
i--;
}
}
+ // now terminate the combined string and prepend it to the command buffer
+ // we already reserved space for the terminator
+ build[l++] = 0;
+ Cbuf_InsertText (build);
}
struct cmd_function_s *next;
const char *name;
const char *description;
- xcommand_t function;
+ xcommand_t consolefunction;
+ xcommand_t clientfunction;
qboolean csqcfunc;
} cmd_function_t;
Cmd_AddCommand
============
*/
-void Cmd_AddCommand (const char *cmd_name, xcommand_t function, const char *description)
+void Cmd_AddCommand_WithClientCommand (const char *cmd_name, xcommand_t consolefunction, xcommand_t clientfunction, const char *description)
{
cmd_function_t *cmd;
cmd_function_t *prev, *current;
{
if (!strcmp (cmd_name, cmd->name))
{
- if (function)
+ if (consolefunction || clientfunction)
{
Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name);
return;
cmd = (cmd_function_t *)Mem_Alloc(cmd_mempool, sizeof(cmd_function_t));
cmd->name = cmd_name;
- cmd->function = function;
+ cmd->consolefunction = consolefunction;
+ cmd->clientfunction = clientfunction;
cmd->description = description;
- if(!function) //[515]: csqc
+ if(!consolefunction && !clientfunction) //[515]: csqc
cmd->csqcfunc = true;
cmd->next = cmd_functions;
cmd->next = current;
}
+void Cmd_AddCommand (const char *cmd_name, xcommand_t function, const char *description)
+{
+ Cmd_AddCommand_WithClientCommand (cmd_name, function, NULL, description);
+}
+
/*
============
Cmd_Exists
{
if (!strcasecmp (cmd_argv[0],cmd->name))
{
- if(cmd->function && !cmd->csqcfunc)
- cmd->function ();
- else
- if(CL_VM_ConsoleCommand (text)) //[515]: csqc
- return;
+ if (cmd->csqcfunc && CL_VM_ConsoleCommand (text)) //[515]: csqc
+ return;
+ switch (src)
+ {
+ case src_command:
+ if (cmd->consolefunction)
+ cmd->consolefunction ();
+ else if (cmd->clientfunction)
+ {
+ if (cls.state == ca_connected)
+ {
+ // forward remote commands to the server for execution
+ Cmd_ForwardToServer();
+ }
+ else
+ Con_Printf("Can not send command \"%s\", not connected.\n", Cmd_Argv(0));
+ }
else
- if(cmd->function)
- cmd->function ();
- cmd_tokenizebufferpos = oldpos;
- return;
+ Con_Printf("Command \"%s\" can not be executed\n", Cmd_Argv(0));
+ cmd_tokenizebufferpos = oldpos;
+ return;
+ case src_client:
+ if (cmd->clientfunction)
+ {
+ cmd->clientfunction ();
+ cmd_tokenizebufferpos = oldpos;
+ return;
+ }
+ break;
+ }
+ break;
}
}
+ // if it's a client command and no command was found, say so.
+ if (cmd_source == src_client)
+ {
+ Con_Printf("player \"%s\" tried to %s\n", host_client->name, text);
+ return;
+ }
+
// check alias
for (a=cmd_alias ; a ; a=a->next)
{
*/
void Cmd_ForwardStringToServer (const char *s)
{
+ char temp[128];
if (cls.state != ca_connected)
{
Con_Printf("Can't \"%s\", not connected\n", s);
MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
else
MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
- SZ_Write(&cls.netcon->message, (const unsigned char *)s, (int)strlen(s) + 1);
+ 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, 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);
}
/*