2
0
mirror of https://github.com/rehlds/rehlds.git synced 2025-03-13 05:50:20 +03:00
rehlds/rehlds/engine/cmd.cpp

1200 lines
24 KiB
C++

/*
*
* 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
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#include "precompiled.h"
int cmd_argc;
char *cmd_argv[80];
// Complete arguments string
char *cmd_args;
sizebuf_t cmd_text;
cmd_source_t cmd_source;
qboolean cmd_wait;
cmdalias_t *cmd_alias;
//int trashtest;
//int *trashspot;
cmd_function_t *cmd_functions;
char *const cmd_null_string = "";
/* <4aad> ../engine/cmd.c:47 */
void Cmd_Wait_f(void)
{
cmd_wait = 1;
}
/* <4d6b> ../engine/cmd.c:69 */
void Cbuf_Init(void)
{
SZ_Alloc("cmd_text", &cmd_text, MAX_CMD_BUFFER);
}
/* <4d80> ../engine/cmd.c:83 */
// As new commands are generated from the console or keybindings,
// the text is added to the end of the command buffer.
void Cbuf_AddText(char *text)
{
int len = Q_strlen(text);
if (cmd_text.cursize + len >= cmd_text.maxsize)
{
Con_Printf(__FUNCTION__ ": overflow\n");
return;
}
SZ_Write(&cmd_text, text, len);
}
/* <4dcf> ../engine/cmd.c:109 */
// When a command wants to issue other commands immediately, the text is
// inserted at the beginning of the buffer, before any remaining unexecuted
// commands.
void Cbuf_InsertText(char *text)
{
#ifndef REHLDS_FIXES
char *temp = NULL;
#endif // REHLDS_FIXES
int addLen = Q_strlen(text);
int currLen = cmd_text.cursize;
if (cmd_text.cursize + addLen >= cmd_text.maxsize)
{
Con_Printf(__FUNCTION__ ": overflow\n");
return;
}
if (currLen)
{
#ifdef REHLDS_FIXES
memmove(cmd_text.data + addLen, cmd_text.data, currLen);
#else // REHLDS_FIXES
temp = (char *)Z_Malloc(currLen); // TODO: Optimize: better use memmove without need for a temp buffer
Q_memcpy(temp, cmd_text.data, currLen);
SZ_Clear(&cmd_text);
#endif // REHLDS_FIXES
}
Cbuf_AddText(text);
#ifndef REHLDS_FIXES
if (currLen)
{
SZ_Write(&cmd_text, temp, currLen);
Z_Free(temp);
}
#endif // REHLDS_FIXES
}
/* <4f05> ../engine/cmd.c:148 */
void Cbuf_InsertTextLines(char *text)
{
#ifndef REHLDS_FIXES
char *temp = NULL;
#endif // REHLDS_FIXES
int addLen = Q_strlen(text);
int currLen = cmd_text.cursize;
if (cmd_text.cursize + addLen + 2 >= cmd_text.maxsize)
{
Con_Printf(__FUNCTION__ ": overflow\n");
return;
}
if (currLen)
{
#ifdef REHLDS_FIXES
memmove(cmd_text.data + addLen, cmd_text.data, currLen);
#else // REHLDS_FIXES
temp = (char *)Z_Malloc(currLen);
Q_memcpy(temp, cmd_text.data, currLen);
SZ_Clear(&cmd_text);
#endif // REHLDS_FIXES
}
Cbuf_AddText("\n"); // TODO: Why we need leading \n, if there is no commands in the start?
Cbuf_AddText(text);
Cbuf_AddText("\n");
#ifndef REHLDS_FIXES
if (currLen)
{
SZ_Write(&cmd_text, temp, currLen);
Z_Free(temp);
}
#endif // REHLDS_FIXES
}
/* <5d96> ../engine/cmd.c:193 */
// Pulls off \n terminated lines of text from the command buffer and sends
// them through Cmd_ExecuteString. Stops when the buffer is empty.
// Normally called once per frame, but may be explicitly invoked.
// Do not call inside a command function!
void Cbuf_Execute(void)
{
int i;
char *text;
char line[MAX_CMD_LINE];
int quotes;
while (cmd_text.cursize)
{
// find a \n or ; line break
text = (char *)cmd_text.data;
quotes = 0;
for (i = 0; i < cmd_text.cursize; i++)
{
if (text[i] == '"')
quotes++;
if (!(quotes & 1) && text[i] == ';')
break; // don't break if inside a quoted string
if (text[i] == '\n')
break;
}
#ifdef REHLDS_FIXES
// save `i` if we truncate command
int len;
if (i > MAX_CMD_LINE - 1)
len = MAX_CMD_LINE - 1;
else
len = i;
Q_memcpy(line, text, len);
line[len] = 0;
#else // REHLDS_FIXES
if (i > MAX_CMD_LINE - 1)
{
i = MAX_CMD_LINE - 1;
}
Q_memcpy(line, text, i);
line[i] = 0;
#endif // REHLDS_FIXES
// delete the text from the command buffer and move remaining commands down
// this is necessary because commands (exec, alias) can insert data at the
// beginning of the text buffer
if (i == cmd_text.cursize)
{
cmd_text.cursize = 0;
}
else
{
i++;
cmd_text.cursize -= i;
#ifdef REHLDS_FIXES
// dst overlaps src
memmove(text, text + i, cmd_text.cursize);
#else // REHLDS_FIXES
Q_memcpy(text, text + i, cmd_text.cursize);
#endif // REHLDS_FIXES
}
// execute the command line
Cmd_ExecuteString(line, src_command);
if (cmd_wait)
{
// skip out while text still remains in buffer, leaving it
// for next frame
cmd_wait = FALSE;
break;
}
}
}
/* <4e74> ../engine/cmd.c:271 */
void Cmd_StuffCmds_f(void)
{
int i;
int s;
char *build;
if (Cmd_Argc() != 1)
{
Con_Printf("stuffcmds : execute command line parameters\n");
return;
}
if (com_argc <= 1)
{
return;
}
// Get total length for the command line parameters
s = 0;
for (i = 1; i < com_argc; i++)
{
if (com_argv[i])
{
s += Q_strlen(com_argv[i]) + 1;
}
}
if (s == 0)
{
return;
}
// Create buffer able to get all arguments
build = (char *)Z_Malloc(s + com_argc * 2);
build[0] = 0;
// Iterate thru arguments searching for ones starting with +
for (i = 1; i < com_argc; i++)
{
if (com_argv[i] && com_argv[i][0] == '+')
{
// Add command or cvar
Q_strcat(build, &com_argv[i][1]);
// Then add all following parameters till we meet argument with + or -, which means next command/cvar/parameter
i++;
while (com_argv[i] && com_argv[i][0] != '+' && com_argv[i][0] != '-')
{
Q_strcat(build, " ");
Q_strcat(build, com_argv[i]);
i++;
}
// End up with new line which split commands for command processor
Q_strcat(build, "\n");
i--;
}
}
if (build[0] != 0)
{
Cbuf_InsertText(build);
}
// Free buffers
Z_Free(build);
}
/* <5e43> ../engine/cmd.c:347 */
void Cmd_Exec_f(void)
{
const char *pszFileName;
const char *pszFileExt;
char *pszFileData;
int nAddLen;
FileHandle_t hFile;
if (Cmd_Argc() != 2)
{
Con_Printf("exec <filename> : execute a script file\n");
return;
}
pszFileName = Cmd_Argv(1);
if (!pszFileName || pszFileName[0] == 0)
{
return;
}
if (Q_strstr(pszFileName, "\\")
|| Q_strstr(pszFileName, ":")
|| Q_strstr(pszFileName, "~")
|| Q_strstr(pszFileName, "..")
|| *pszFileName == '/')
{
Con_Printf("exec %s: invalid path.\n", pszFileName);
return;
}
pszFileExt = COM_FileExtension((char *)pszFileName);
if (Q_stricmp(pszFileExt, "cfg") && Q_stricmp(pszFileExt, "rc"))
{
Con_Printf("exec %s: not a .cfg or .rc file\n", pszFileName);
return;
}
hFile = FS_OpenPathID(pszFileName, "rb", "GAMECONFIG");
if (!hFile)
{
hFile = FS_OpenPathID(pszFileName, "rb", "GAME");
}
if (!hFile)
{
hFile = FS_Open(pszFileName, "rb");
}
if (!hFile)
{
if (!Q_strstr(pszFileName, "autoexec.cfg")
&& !Q_strstr(pszFileName, "userconfig.cfg")
&& !Q_strstr(pszFileName, "hw/opengl.cfg")
&& !Q_strstr(pszFileName, "joystick.cfg")
&& !Q_strstr(pszFileName, "game.cfg"))
{
Con_Printf("couldn't exec %s\n", pszFileName);
}
return;
}
nAddLen = FS_Size(hFile);
pszFileData = (char *)Mem_Malloc(nAddLen + 1);
if (!pszFileData)
{
Con_Printf("exec: not enough space for %s", pszFileName);
FS_Close(hFile);
return;
}
FS_Read(pszFileData, nAddLen, 1, hFile);
pszFileData[nAddLen] = 0;
FS_Close(hFile);
Con_DPrintf("execing %s\n", pszFileName);
if (cmd_text.cursize + nAddLen + 2 < cmd_text.maxsize)
{
Cbuf_InsertTextLines(pszFileData);
}
else
{
char *pszDataPtr = pszFileData;
while (true)
{
Cbuf_Execute(); // TODO: This doesn't obey the rule to first execute commands from the file, and then the others in the buffer
pszDataPtr = COM_ParseLine(pszDataPtr);
if (com_token[0] == 0)
{
break;
}
Cbuf_InsertTextLines(com_token);
}
}
Mem_Free(pszFileData);
}
/* <4ac1> ../engine/cmd.c:493 */
void Cmd_Echo_f(void)
{
int i;
int c = Cmd_Argc();
for (i = 1; i < c; i++)
{
Con_Printf("%s ", Cmd_Argv(i));
}
Con_Printf("\n");
}
/* <4c38> ../engine/cmd.c:510 */
char *CopyString(char *in)
{
char *out = (char *)Z_Malloc(Q_strlen(in) + 1);
Q_strcpy(out, in);
return out;
}
/* <4c63> ../engine/cmd.c:521 */
void Cmd_Alias_f(void)
{
cmdalias_t *a;
const char *s;
char cmd[MAX_CMD_LINE];
int i, c;
if (Cmd_Argc() == 1)
{
// Output all aliases
Con_Printf("Current alias commands:\n");
for (a = cmd_alias; a; a = a->next)
{
Con_Printf("%s : %s", a->name, a->value); // Don't need \n here, because each alias value is appended with it
}
return;
}
s = Cmd_Argv(1);
if (Q_strlen(s) >= MAX_ALIAS_NAME)
{
Con_Printf("Alias name is too long\n");
return;
}
if (Cvar_FindVar(s))
{
Con_Printf("Alias name is invalid\n");
return;
}
SetCStrikeFlags(); // TODO: Do this once somewhere at the server start
if ((g_bIsCStrike || g_bIsCZero) &&
(!Q_stricmp(s, "cl_autobuy")
|| !Q_stricmp(s, "cl_rebuy")
|| !Q_stricmp(s, "gl_ztrick")
|| !Q_stricmp(s, "gl_ztrick_old")
|| !Q_stricmp(s, "gl_d3dflip")))
{
Con_Printf("Alias name is invalid\n");
return;
}
// Say hello to my little friend! (c)
if (g_bIsTFC && (!Q_stricmp(s, "_special") || !Q_stricmp(s, "special")))
{
Con_Printf("Alias name is invalid\n");
return;
}
// Gather arguments into one string
cmd[0] = 0;
c = Cmd_Argc();
for (i = 2; i <= c; i++)
{
Q_strncat(cmd, Cmd_Argv(i), MAX_CMD_LINE - 2 - Q_strlen(cmd)); // always have a space for \n or ' ' and \0
if (i != c)
{
Q_strcat(cmd, " ");
}
}
Q_strcat(cmd, "\n");
// Search for existing alias
for (a = cmd_alias; a; a = a->next)
{
if (!Q_stricmp(a->name, s))
{
if (!Q_strcmp(a->value, cmd))
{
// Same value on the alias, return
return;
}
// Release value, will realloc
Z_Free(a->value);
break;
}
}
if (!a)
{
// Alloc new alias
a = (cmdalias_t *)Z_Malloc(sizeof(cmdalias_t));
a->next = cmd_alias;
cmd_alias = a;
Q_strncpy(a->name, s, ARRAYSIZE(a->name) - 1);
a->name[ARRAYSIZE(a->name) - 1] = 0;
}
a->value = CopyString(cmd);
}
/* <5079> ../engine/cmd.c:632 */
struct cmd_function_s *Cmd_GetFirstCmd(void)
{
return cmd_functions;
}
/* <5091> ../engine/cmd.c:646 */
void Cmd_Init(void)
{
Cmd_AddCommand("stuffcmds", Cmd_StuffCmds_f);
Cmd_AddCommand("exec", Cmd_Exec_f);
Cmd_AddCommand("echo", Cmd_Echo_f);
Cmd_AddCommand("alias", Cmd_Alias_f);
Cmd_AddCommand("cmd", Cmd_ForwardToServer);
Cmd_AddCommand("wait", Cmd_Wait_f);
Cmd_AddCommand("cmdlist", Cmd_CmdList_f);
}
/* <5521> ../engine/cmd.c:663 */
void Cmd_Shutdown(void)
{
for (int i = 0; i < cmd_argc; i++)
{
Z_Free(cmd_argv[i]);
}
memset(cmd_argv, 0, sizeof(cmd_argv));
cmd_argc = 0;
cmd_args = NULL;
cmd_functions = NULL; // TODO: Check that memory from functions is released too
}
/* <5536> ../engine/cmd.c:677 */
int Cmd_Argc(void)
{
#ifndef SWDS
g_engdstAddrs->Cmd_Argc();
#endif
return cmd_argc;
}
/* <5547> ../engine/cmd.c:689 */
const char *Cmd_Argv(int arg)
{
#ifndef SWDS
g_engdstAddrs->Cmd_Argv(&arg);
#endif
if (arg >= 0 && arg < cmd_argc)
{
return cmd_argv[arg];
}
return ""; // TODO: Possibly better to return NULL here, but require to check all usages
}
/* <5565> ../engine/cmd.c:703 */
const char *Cmd_Args(void)
{
#ifndef SWDS
g_engdstAddrs->Cmd_Args();
#endif
return cmd_args;
}
/* <5575> ../engine/cmd.c:715 */
/*
============
Cmd_TokenizeString
Parses the given string into command line tokens.
Takes a null terminated string. Does not need to be \n terminated.
Breaks the string up into arg tokens.
============
*/
void Cmd_TokenizeString(char *text)
{
int i;
int arglen;
// clear args from the last string
for (i = 0; i < cmd_argc; i++)
{
Z_Free(cmd_argv[i]);
cmd_argv[i] = NULL;
}
cmd_argc = 0;
cmd_args = NULL;
while (true)
{
// Skip whitespace up to a \n
while (*text && *text <= ' ' && *text != '\n')
{
++text;
}
// A newline separates commands in the buffer
if (*text == '\n' || *text == 0)
{
break;
}
// Store complete args string pointer
if (cmd_argc == 1)
{
cmd_args = text;
}
// Break into token
text = COM_Parse(text);
// If nothing was parsed
if (!text)
{
return;
}
arglen = Q_strlen(com_token) + 1;
if (arglen >= 516) // TODO: What is that magic number?
{
return;
}
cmd_argv[cmd_argc] = (char *)Z_Malloc(arglen);
Q_strcpy(cmd_argv[cmd_argc++], com_token);
if (cmd_argc >= MAX_CMD_TOKENS)
{
// Will not fit any more
return;
}
}
}
/* <55be> ../engine/cmd.c:773 */
NOXREF cmd_function_t *Cmd_FindCmd(char *cmd_name)
{
NOXREFCHECK;
cmd_function_t *cmd;
for (cmd = cmd_functions; cmd; cmd = cmd->next)
{
if (!Q_stricmp(cmd_name, cmd->name))
{
return cmd;
}
}
return NULL;
}
/* <5611> ../engine/cmd.c:791 */
NOXREF cmd_function_t *Cmd_FindCmdPrev(char *cmd_name)
{
NOXREFCHECK;
cmd_function_t *cmd = NULL;
if (cmd_functions == NULL)
{
return NULL;
}
for (cmd = cmd_functions; cmd->next; cmd = cmd->next)
{
if (!Q_stricmp(cmd_name, cmd->next->name))
{
return cmd;
}
}
return NULL;
}
void Cmd_InsertCommand(cmd_function_t *cmd)
{
cmd_function_t *c, **p;
// Commands list is alphabetically sorted, search where to push
c = cmd_functions;
p = &cmd_functions;
while (c)
{
if (Q_stricmp(c->name, cmd->name) > 0)
{
// Current command name is bigger, insert before it
cmd->next = c;
*p = cmd;
return;
}
p = &c->next;
c = c->next;
}
// All commands in the list are lower then the new one
cmd->next = NULL;
*p = cmd;
}
/* <5664> ../engine/cmd.c:812 */
// Use this for engine inside call only, not from user code, because it doesn't alloc string for the name.
void Cmd_AddCommand(char *cmd_name, xcommand_t function)
{
cmd_function_t *cmd;
if (host_initialized)
{
Sys_Error(__FUNCTION__ " after host_initialized");
}
// Check in variables list
if (Cvar_FindVar(cmd_name) != NULL)
{
Con_Printf(__FUNCTION__ ": \"%s\" already defined as a var\n", cmd_name);
return;
}
// Check if this command is already defined
if (Cmd_Exists(cmd_name))
{
Con_Printf(__FUNCTION__ ": \"%s\" already defined\n", cmd_name);
return;
}
// Create cmd_function
cmd = (cmd_function_t *)Hunk_Alloc(sizeof(cmd_function_t));
cmd->name = cmd_name;
cmd->function = function ? function : Cmd_ForwardToServer;
cmd->flags = 0;
Cmd_InsertCommand(cmd);
}
/* <5700> ../engine/cmd.c:891 */
// Use this for call from user code, because it alloc string for the name.
void Cmd_AddMallocCommand(char *cmd_name, xcommand_t function, int flag)
{
cmd_function_t *cmd;
// Check in variables list
if (Cvar_FindVar(cmd_name) != NULL)
{
Con_Printf(__FUNCTION__ ": \"%s\" already defined as a var\n", cmd_name);
return;
}
// Check if this command is already defined
if (Cmd_Exists(cmd_name))
{
Con_Printf(__FUNCTION__ ": \"%s\" already defined\n", cmd_name);
return;
}
// Create cmd_function
cmd = (cmd_function_t *)Mem_Malloc(sizeof(cmd_function_t));
cmd->name = CopyString(cmd_name); // alloc string, so it will not dissapear on side modules unloading and to maintain the same name during run
cmd->function = function ? function : Cmd_ForwardToServer;
cmd->flags = flag;
Cmd_InsertCommand(cmd);
}
/* <5776> ../engine/cmd.c:934 */
NOXREF void Cmd_AddHUDCommand(char *cmd_name, xcommand_t function)
{
NOXREFCHECK;
Cmd_AddMallocCommand(cmd_name, function, FCMD_HUD_COMMAND);
}
/* <5824> ../engine/cmd.c:946 */
NOXREF void Cmd_AddWrapperCommand(char *cmd_name, xcommand_t function)
{
NOXREFCHECK;
Cmd_AddMallocCommand(cmd_name, function, FCMD_WRAPPER_COMMAND);
}
/* <58d2> ../engine/cmd.c:960 */
void Cmd_AddGameCommand(char *cmd_name, xcommand_t function)
{
Cmd_AddMallocCommand(cmd_name, function, FCMD_GAME_COMMAND);
}
/* <5980> ../engine/cmd.c:970 */
void Cmd_RemoveMallocedCmds(int flag)
{
cmd_function_t *c, **p;
c = cmd_functions;
p = &cmd_functions;
while (c)
{
if (c->flags & flag)
{
*p = c->next;
Z_Free(c->name);
Mem_Free(c);
c = *p;
continue;
}
p = &c->next;
c = c->next;
}
}
/* <59f9> ../engine/cmd.c:1002 */
NOXREF void Cmd_RemoveHudCmds(void)
{
NOXREFCHECK;
Cmd_RemoveMallocedCmds(FCMD_HUD_COMMAND);
}
/* <5a4c> ../engine/cmd.c:1012 */
void Cmd_RemoveGameCmds(void)
{
Cmd_RemoveMallocedCmds(FCMD_GAME_COMMAND);
}
/* <5a9f> ../engine/cmd.c:1022 */
void Cmd_RemoveWrapperCmds(void)
{
Cmd_RemoveMallocedCmds(FCMD_WRAPPER_COMMAND);
}
/* <5af2> ../engine/cmd.c:1035 */
qboolean Cmd_Exists(char *cmd_name)
{
cmd_function_t *cmd = cmd_functions;
while (cmd)
{
if (!Q_stricmp(cmd_name, cmd->name))
{
return TRUE;
}
cmd = cmd->next;
}
return FALSE;
}
/* <5b30> ../engine/cmd.c:1055 */
NOXREF char *Cmd_CompleteCommand(char *search, int forward)
{
NOXREFCHECK;
// TODO: We have a command name length limit here: prepare for unforeseen consequences!
static char lastpartial[256];
char partial[256];
cmd_function_t *cmd;
int len;
char *pPartial;
Q_strncpy(partial, search, 255);
partial[255] = 0;
len = Q_strlen(partial);
// Trim tail spaces
for (pPartial = partial + len - 1; pPartial >= partial && *pPartial == ' '; pPartial--, len--)
{
*pPartial = 0;
}
if (!len)
{
return NULL;
}
if (!Q_stricmp(partial, lastpartial))
{
// Same partial, find this then next/prev cvar, if any.
// TODO: But where it match for entered by user partial? Because we store full name
cmd = Cmd_FindCmd(partial);
if (cmd)
{
cmd = forward == 1 ? cmd->next : Cmd_FindCmdPrev(cmd->name);
if (cmd)
{
Q_strncpy(lastpartial, cmd->name, 255);
lastpartial[255] = 0;
return cmd->name;
}
}
}
// Find first matching cvar
for (cmd = cmd_functions; cmd != NULL; cmd = cmd->next)
{
if (!Q_strnicmp(partial, cmd->name, len))
{
// Store matched cvar name
Q_strncpy(lastpartial, cmd->name, 255);
lastpartial[255] = 0;
return cmd->name;
}
}
return NULL;
}
bool ValidateCmd_API(const char* cmd, cmd_source_t src, IGameClient* client) {
return true;
}
/* <5d4e> ../engine/cmd.c:1133 */
void Cmd_ExecuteString(char *text, cmd_source_t src)
{
cmd_source = src;
Cmd_TokenizeString(text);
if (!Cmd_Argc())
{
return;
}
IGameClient* cl = (src == src_client) ? GetRehldsApiClient(host_client) : NULL;
if (!g_RehldsHookchains.m_ValidateCommand.callChain(ValidateCmd_API, cmd_argv[0], src, cl))
return;
// Search in functions
cmd_function_t *cmd = cmd_functions;
while (cmd)
{
if (!Q_stricmp(cmd_argv[0], cmd->name))
{
cmd->function();
if (g_pcls.demorecording && (cmd->flags & FCMD_HUD_COMMAND) && !g_pcls.spectator)
{
CL_RecordHUDCommand(cmd->name);
}
return;
}
cmd = cmd->next;
}
// Search in aliases
cmdalias_t *a = cmd_alias;
while (a)
{
if (!Q_stricmp(cmd_argv[0], a->name))
{
Cbuf_InsertText(a->value);
return;
}
a = a->next;
}
// Search in cvars
if (!Cvar_Command() && g_pcls.state >= ca_connected)
{
// Send to a server if nothing processed locally and connected
Cmd_ForwardToServer();
}
}
/* <5c15> ../engine/cmd.c:1181 */
qboolean Cmd_ForwardToServerInternal(sizebuf_t *pBuf)
{
const char *cmd_name = Cmd_Argv(0);
if (g_pcls.state <= ca_disconnected)
{
if (Q_stricmp(cmd_name, "setinfo"))
{
Con_Printf("Can't \"%s\", not connected\n", cmd_name);
}
return FALSE;
}
if (g_pcls.demoplayback || g_bIsDedicatedServer)
{
return FALSE;
}
char tempData[4096];
sizebuf_t tempBuf;
tempBuf.buffername = __FUNCTION__ "::tempBuf";
tempBuf.data = (byte *)tempData;
tempBuf.maxsize = 4096;
tempBuf.cursize = 0;
tempBuf.flags = SIZEBUF_ALLOW_OVERFLOW;
MSG_WriteByte(&tempBuf, clc_stringcmd);
if (Q_stricmp(cmd_name, "cmd"))
{
SZ_Print(&tempBuf, cmd_name);
SZ_Print(&tempBuf, " ");
}
SZ_Print(&tempBuf, Cmd_Argc() <= 1 ? "\n" : Cmd_Args());
if (tempBuf.flags & SIZEBUF_OVERFLOWED)
{
return FALSE;
}
if (tempBuf.cursize + pBuf->cursize <= pBuf->maxsize)
{
SZ_Write(pBuf, tempBuf.data, tempBuf.cursize);
return TRUE;
}
return FALSE;
}
/* <5cfd> ../engine/cmd.c:1247 */
void Cmd_ForwardToServer(void)
{
if (Q_stricmp(Cmd_Argv(0), "cmd") || Q_stricmp(Cmd_Argv(1), "dlfile"))
{
Cmd_ForwardToServerInternal(&g_pcls.netchan.message);
}
}
/* <5f1b> ../engine/cmd.c:1258 */
qboolean Cmd_ForwardToServerUnreliable(void)
{
return Cmd_ForwardToServerInternal(&g_pcls.datagram);
}
/* <5f35> ../engine/cmd.c:1273 */
// Returns the position (1 to argc-1) in the command's argument list
// where the given parameter apears, or 0 if not present.
NOXREF int Cmd_CheckParm(char *parm)
{
NOXREFCHECK;
if (!parm)
{
Sys_Error(__FUNCTION__ ": NULL");
}
int c = Cmd_Argc();
for (int i = 1; i < c; i++)
{
if (!Q_stricmp(Cmd_Argv(i), parm))
{
return i;
}
}
return 0;
}
/* <4b17> ../engine/cmd.c:1301 */
void Cmd_CmdList_f(void)
{
cmd_function_t *cmd;
int iCmds;
int iArgs;
const char *partial, *arg1;
int ipLen;
char szTemp[MAX_PATH];
FileHandle_t f;
FileHandle_t fp;
qboolean bLogging;
iCmds = 0;
partial = NULL;
f = NULL;
fp = NULL;
bLogging = FALSE;
iArgs = Cmd_Argc();
if (iArgs > 1)
{
arg1 = Cmd_Argv(1);
if (!Q_stricmp(arg1, "?"))
{
Con_Printf("CmdList : List all commands\nCmdList [Partial] : List commands starting with 'Partial'\nCmdList log [Partial] : Logs commands to file \"cmdlist.txt\" in the gamedir.\n");
return;
}
if (!Q_stricmp(arg1, "log"))
{
// Open log
int i;
for (i = 0; i < 100; i++)
{
Q_snprintf(szTemp, ARRAYSIZE(szTemp) - 1, "cmdlist%02d.txt", i);
szTemp[ARRAYSIZE(szTemp) - 1] = 0;
fp = FS_Open(szTemp, "r");
if (!fp)
{
break;
}
FS_Close(fp);
}
if (i >= 100)
{
Con_Printf("Can't cmdlist! Too many existing cmdlist output files in the gamedir!\n");
return;
}
f = FS_Open(szTemp, "wt");
if (!f)
{
Con_Printf("Couldn't open \"%s\" for writing!\n", szTemp);
return;
}
bLogging = TRUE;
// Get next argument into partial, if present
if (iArgs >= 2)
{
partial = Cmd_Argv(2);
ipLen = Q_strlen(partial);
}
}
else
{
partial = arg1;
ipLen = Q_strlen(partial);
}
}
// Print commands
Con_Printf("Command List\n--------------\n");
for (cmd = cmd_functions; cmd; cmd = cmd->next)
{
if (partial && Q_strnicmp(cmd->name, partial, ipLen))
{
continue;
}
Con_Printf("%s\n", cmd->name);
if (bLogging)
{
FS_FPrintf(f, "%s\n", cmd->name);
}
iCmds++;
}
if (partial && *partial)
{
Con_Printf("--------------\n%3i Commands for [%s]\nCmdList ? for syntax\n", iCmds, partial);
}
else
{
Con_Printf("--------------\n%3i Total Commands\nCmdList ? for syntax\n", iCmds);
}
// Close log
if (bLogging)
{
FS_Close(f);
Con_Printf("cmdlist logged to %s\n", szTemp);
}
}