X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=cmd.c;h=0a42ca4a093d2ad9d59882ea45e6a78d52114efc;hb=4c064a2e39e32f099e2069c426fa2ae837fcf738;hp=3fb741e0a3d6ba10380caade324fa99df721fe3e;hpb=872ba9d240c7105d8b7cee22e6719de8cc3d0615;p=xonotic%2Fdarkplaces.git diff --git a/cmd.c b/cmd.c index 3fb741e0..0a42ca4a 100644 --- a/cmd.c +++ b/cmd.c @@ -20,12 +20,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // cmd.c -- Quake script command processing module #include "quakedef.h" +#include "thread.h" typedef struct cmdalias_s { struct cmdalias_s *next; char name[MAX_ALIAS_NAME]; char *value; + qboolean initstate; // indicates this command existed at init + char *initialvalue; // backup copy of value at init } cmdalias_t; static cmdalias_t *cmd_alias; @@ -57,7 +60,7 @@ typedef struct cmddeferred_s { struct cmddeferred_s *next; char *value; - double time; + double delay; } cmddeferred_t; static cmddeferred_t *cmd_deferred_list = NULL; @@ -73,13 +76,12 @@ static void Cmd_Defer_f (void) { if(Cmd_Argc() == 1) { - double time = Sys_DoubleTime(); cmddeferred_t *next = cmd_deferred_list; if(!next) Con_Printf("No commands are pending.\n"); while(next) { - Con_Printf("-> In %9.2f: %s\n", next->time-time, next->value); + Con_Printf("-> In %9.2f: %s\n", next->delay, next->value); next = next->next; } } else if(Cmd_Argc() == 2 && !strcasecmp("clear", Cmd_Argv(1))) @@ -97,7 +99,7 @@ static void Cmd_Defer_f (void) cmddeferred_t *defcmd = (cmddeferred_t*)Mem_Alloc(tempmempool, sizeof(*defcmd)); size_t len = strlen(value); - defcmd->time = Sys_DoubleTime() + atof(Cmd_Argv(1)); + defcmd->delay = atof(Cmd_Argv(1)); defcmd->value = (char*)Mem_Alloc(tempmempool, len+1); memcpy(defcmd->value, value, len+1); defcmd->next = NULL; @@ -173,6 +175,10 @@ static void Cmd_Centerprint_f (void) static sizebuf_t cmd_text; static unsigned char cmd_text_buf[CMDBUFSIZE]; +void *cmd_text_mutex = NULL; + +#define Cbuf_LockThreadMutex() (cmd_text_mutex ? Thread_LockMutex(cmd_text_mutex),1 : 0) +#define Cbuf_UnlockThreadMutex() (cmd_text_mutex ? Thread_UnlockMutex(cmd_text_mutex),1 : 0) /* ============ @@ -185,15 +191,14 @@ void Cbuf_AddText (const char *text) { int l; - l = (int)strlen (text); + l = (int)strlen(text); + Cbuf_LockThreadMutex(); if (cmd_text.cursize + l >= cmd_text.maxsize) - { Con_Print("Cbuf_AddText: overflow\n"); - return; - } - - SZ_Write (&cmd_text, (const unsigned char *)text, (int)strlen (text)); + else + SZ_Write(&cmd_text, (const unsigned char *)text, l); + Cbuf_UnlockThreadMutex(); } @@ -208,29 +213,19 @@ FIXME: actually change the command buffer to do less copying */ void Cbuf_InsertText (const char *text) { - char *temp; - int templen; - - // copy off any commands still remaining in the exec buffer - templen = cmd_text.cursize; - if (templen) - { - temp = (char *)Mem_Alloc (tempmempool, templen); - memcpy (temp, cmd_text.data, templen); - SZ_Clear (&cmd_text); - } + size_t l = strlen(text); + Cbuf_LockThreadMutex(); + // we need to memmove the existing text and stuff this in before it... + if (cmd_text.cursize + l >= (size_t)cmd_text.maxsize) + Con_Print("Cbuf_InsertText: overflow\n"); else - temp = NULL; - - // add the entire text of the file - Cbuf_AddText (text); - - // add the copied off data - if (temp != NULL) { - SZ_Write (&cmd_text, (const unsigned char *)temp, templen); - Mem_Free (temp); + // we don't have a SZ_Prepend, so... + memmove(cmd_text.data + l, cmd_text.data, cmd_text.cursize); + cmd_text.cursize += l; + memcpy(cmd_text.data, text, l); } + Cbuf_UnlockThreadMutex(); } /* @@ -238,15 +233,22 @@ void Cbuf_InsertText (const char *text) Cbuf_Execute_Deferred --blub ============ */ -void Cbuf_Execute_Deferred (void) +static void Cbuf_Execute_Deferred (void) { + static double oldrealtime = 0; cmddeferred_t *cmd, *prev; - double time = Sys_DoubleTime(); + double eat; + if (realtime - oldrealtime < 0 || realtime - oldrealtime > 1800) oldrealtime = realtime; + eat = realtime - oldrealtime; + if (eat < (1.0 / 120.0)) + return; + oldrealtime = realtime; prev = NULL; cmd = cmd_deferred_list; while(cmd) { - if(cmd->time <= time) + cmd->delay -= eat; + if(cmd->delay <= 0) { Cbuf_AddText(cmd->value); Cbuf_AddText(";\n"); @@ -287,7 +289,6 @@ void Cbuf_Execute (void) // LordHavoc: making sure the tokenizebuffer doesn't get filled up by repeated crashes cmd_tokenizebufferpos = 0; - Cbuf_Execute_Deferred(); while (cmd_text.cursize) { // find a \n or ; line break @@ -360,11 +361,11 @@ void Cbuf_Execute (void) ) { Cmd_PreprocessString( line, preprocessed, sizeof(preprocessed), NULL ); - Cmd_ExecuteString (preprocessed, src_command); + Cmd_ExecuteString (preprocessed, src_command, false); } else { - Cmd_ExecuteString (line, src_command); + Cmd_ExecuteString (line, src_command, false); } if (cmd_wait) @@ -376,6 +377,17 @@ void Cbuf_Execute (void) } } +void Cbuf_Frame(void) +{ + Cbuf_Execute_Deferred(); + if (cmd_text.cursize) + { + SV_LockThreadMutex(); + Cbuf_Execute(); + SV_UnlockThreadMutex(); + } +} + /* ============================================================================== @@ -395,7 +407,7 @@ quake -nosound +cmd amlev1 =============== */ qboolean host_stuffcmdsrun = false; -void Cmd_StuffCmds_f (void) +static void Cmd_StuffCmds_f (void) { int i, j, l; // this is for all commandline options combined (and is bounds checked) @@ -448,24 +460,11 @@ void Cmd_StuffCmds_f (void) Cbuf_InsertText (build); } - -/* -=============== -Cmd_Exec_f -=============== -*/ -static void Cmd_Exec_f (void) +static void Cmd_Exec(const char *filename) { char *f; - const char *filename; + qboolean isdefaultcfg = strlen(filename) >= 11 && !strcmp(filename + strlen(filename) - 11, "default.cfg"); - if (Cmd_Argc () != 2) - { - Con_Print("exec : execute a script file\n"); - return; - } - - filename = Cmd_Argv(1); if (!strcmp(filename, "config.cfg")) { filename = CONFIGFILENAME; @@ -484,7 +483,7 @@ static void Cmd_Exec_f (void) // if executing default.cfg for the first time, lock the cvar defaults // it may seem backwards to insert this text BEFORE the default.cfg // but Cbuf_InsertText inserts before, so this actually ends up after it. - if (strlen(filename) >= 11 && !strcmp(filename + strlen(filename) - 11, "default.cfg")) + if (isdefaultcfg) Cbuf_InsertText("\ncvar_lockdefaults\n"); // insert newline after the text to make sure the last line is terminated (some text editors omit the trailing newline) @@ -492,6 +491,59 @@ static void Cmd_Exec_f (void) Cbuf_InsertText ("\n"); Cbuf_InsertText (f); Mem_Free(f); + + if (isdefaultcfg) + { + // special defaults for specific games go here, these execute before default.cfg + // Nehahra pushable crates malfunction in some levels if this is on + // Nehahra NPC AI is confused by blowupfallenzombies + if (gamemode == GAME_NEHAHRA) + Cbuf_InsertText("\nsv_gameplayfix_upwardvelocityclearsongroundflag 0\nsv_gameplayfix_blowupfallenzombies 0\n\n"); + // hipnotic mission pack has issues in their 'friendly monster' ai, which seem to attempt to attack themselves for some reason when findradius() returns non-solid entities. + // hipnotic mission pack has issues with bobbing water entities 'jittering' between different heights on alternate frames at the default 0.0138889 ticrate, 0.02 avoids this issue + // hipnotic mission pack has issues in their proximity mine sticking code, which causes them to bounce off. + if (gamemode == GAME_HIPNOTIC) + Cbuf_InsertText("\nsv_gameplayfix_blowupfallenzombies 0\nsys_ticrate 0.02\nsv_gameplayfix_slidemoveprojectiles 0\n\n"); + // rogue mission pack has a guardian boss that does not wake up if findradius returns one of the entities around its spawn area + if (gamemode == GAME_ROGUE) + Cbuf_InsertText("\nsv_gameplayfix_findradiusdistancetobox 0\n\n"); + if (gamemode == GAME_NEXUIZ) + Cbuf_InsertText("\nsv_gameplayfix_q2airaccelerate 1\nsv_gameplayfix_stepmultipletimes 1\n\n"); + if (gamemode == GAME_TENEBRAE) + Cbuf_InsertText("\nr_shadow_gloss 2\nr_shadow_bumpscale_basetexture 4\n\n"); + // Steel Storm: Burning Retribution csqc misinterprets CSQC_InputEvent if type is a value other than 0 or 1 + if (gamemode == GAME_STEELSTORM) + Cbuf_InsertText("\ncl_csqc_generatemousemoveevents 0\n\n"); + } +} + +/* +=============== +Cmd_Exec_f +=============== +*/ +static void Cmd_Exec_f (void) +{ + fssearch_t *s; + int i; + + if (Cmd_Argc () != 2) + { + Con_Print("exec : execute a script file\n"); + return; + } + + s = FS_Search(Cmd_Argv(1), true, true); + if(!s || !s->numfilenames) + { + Con_Printf("couldn't exec %s\n",Cmd_Argv(1)); + return; + } + + for(i = 0; i < s->numfilenames; ++i) + Cmd_Exec(s->filenames[i]); + + FS_FreeSearch(s); } @@ -694,6 +746,8 @@ static void Cmd_UnAlias_f (void) { if(!strcmp(s, a->name)) { + if (a->initstate) // we can not remove init aliases + continue; if(a == cmd_alias) cmd_alias = a->next; if(p) @@ -724,6 +778,7 @@ typedef struct cmd_function_s xcommand_t consolefunction; xcommand_t clientfunction; qboolean csqcfunc; + qboolean initstate; // indicates this command existed at init } cmd_function_t; static int cmd_argc; @@ -740,6 +795,7 @@ static const char *Cmd_GetDirectCvarValue(const char *varname, cmdalias_t *alias cvar_t *cvar; long argno; char *endptr; + char vabuf[1024]; if(is_multiple) *is_multiple = false; @@ -757,7 +813,7 @@ static const char *Cmd_GetDirectCvarValue(const char *varname, cmdalias_t *alias } else if(!strcmp(varname, "#")) { - return va("%d", Cmd_Argc()); + return va(vabuf, sizeof(vabuf), "%d", Cmd_Argc()); } else if(varname[strlen(varname) - 1] == '-') { @@ -801,63 +857,69 @@ static const char *Cmd_GetDirectCvarValue(const char *varname, cmdalias_t *alias return NULL; } -qboolean Cmd_QuoteString(char *out, size_t outlen, const char *in, const char *quoteset) +qboolean Cmd_QuoteString(char *out, size_t outlen, const char *in, const char *quoteset, qboolean putquotes) { qboolean quote_quot = !!strchr(quoteset, '"'); qboolean quote_backslash = !!strchr(quoteset, '\\'); qboolean quote_dollar = !!strchr(quoteset, '$'); + if(putquotes) + { + if(outlen <= 2) + { + *out++ = 0; + return false; + } + *out++ = '"'; --outlen; + --outlen; + } + while(*in) { if(*in == '"' && quote_quot) { if(outlen <= 2) - { - *out++ = 0; - return false; - } + goto fail; *out++ = '\\'; --outlen; *out++ = '"'; --outlen; } else if(*in == '\\' && quote_backslash) { if(outlen <= 2) - { - *out++ = 0; - return false; - } + goto fail; *out++ = '\\'; --outlen; *out++ = '\\'; --outlen; } else if(*in == '$' && quote_dollar) { if(outlen <= 2) - { - *out++ = 0; - return false; - } + goto fail; *out++ = '$'; --outlen; *out++ = '$'; --outlen; } else { if(outlen <= 1) - { - *out++ = 0; - return false; - } + goto fail; *out++ = *in; --outlen; } ++in; } + if(putquotes) + *out++ = '"'; *out++ = 0; return true; +fail: + if(putquotes) + *out++ = '"'; + *out++ = 0; + return false; } static const char *Cmd_GetCvarValue(const char *var, size_t varlen, cmdalias_t *alias) { - static char varname[MAX_INPUTLINE]; - static char varval[MAX_INPUTLINE]; + static char varname[MAX_INPUTLINE]; // cmd_mutex + static char varval[MAX_INPUTLINE]; // cmd_mutex const char *varstr; char *varfunc; static char asis[] = "asis"; // just to suppress const char warnings @@ -907,7 +969,7 @@ static char asis[] = "asis"; // just to suppress const char warnings { // quote it so it can be used inside double quotes // we just need to replace " by \", and of course, double backslashes - Cmd_QuoteString(varval, sizeof(varval), varstr, "\"\\"); + Cmd_QuoteString(varval, sizeof(varval), varstr, "\"\\", false); return varval; } else if(!strcmp(varfunc, "asis")) @@ -1046,8 +1108,8 @@ Called for aliases and fills in the alias into the cbuffer */ static void Cmd_ExecuteAlias (cmdalias_t *alias) { - static char buffer[ MAX_INPUTLINE ]; - static char buffer2[ MAX_INPUTLINE ]; + static char buffer[ MAX_INPUTLINE ]; // cmd_mutex + static char buffer2[ MAX_INPUTLINE ]; // cmd_mutex Cmd_PreprocessString( alias->value, buffer, sizeof(buffer) - 2, alias ); // insert at start of command buffer, so that aliases execute in order // (fixes bug introduced by Black on 20050705) @@ -1055,7 +1117,7 @@ static void Cmd_ExecuteAlias (cmdalias_t *alias) // Note: Cbuf_PreprocessString will be called on this string AGAIN! So we // have to make sure that no second variable expansion takes place, otherwise // alias parameters containing dollar signs can have bad effects. - Cmd_QuoteString(buffer2, sizeof(buffer2), buffer, "$"); + Cmd_QuoteString(buffer2, sizeof(buffer2), buffer, "$", false); Cbuf_InsertText( buffer2 ); } @@ -1117,6 +1179,7 @@ static void Cmd_Apropos_f(void) const char *partial; int count; qboolean ispattern; + char vabuf[1024]; if (Cmd_Argc() > 1) partial = Cmd_Args(); @@ -1128,7 +1191,7 @@ static void Cmd_Apropos_f(void) ispattern = partial && (strchr(partial, '*') || strchr(partial, '?')); if(!ispattern) - partial = va("*%s*", partial); + partial = va(vabuf, sizeof(vabuf), "*%s*", partial); count = 0; for (cvar = cvar_vars; cvar; cvar = cvar->next) @@ -1171,6 +1234,9 @@ void Cmd_Init (void) cmd_text.data = cmd_text_buf; cmd_text.maxsize = sizeof(cmd_text_buf); cmd_text.cursize = 0; + + if (Thread_HasThreads()) + cmd_text_mutex = Thread_CreateMutex(); } void Cmd_Init_Commands (void) @@ -1218,6 +1284,14 @@ Cmd_Shutdown */ void Cmd_Shutdown(void) { + if (cmd_text_mutex) + { + // we usually have this locked when we get here from Host_Quit_f + Cbuf_UnlockThreadMutex(); + Thread_DestroyMutex(cmd_text_mutex); + } + cmd_text_mutex = NULL; + Mem_FreePool(&cmd_mempool); } @@ -1586,7 +1660,6 @@ void Cmd_ClearCsqcFuncs (void) cmd->csqcfunc = false; } -qboolean CL_VM_ConsoleCommand (const char *cmd); /* ============ Cmd_ExecuteString @@ -1595,7 +1668,7 @@ A complete command line has been parsed, so try to execute it FIXME: lookupnoadd the token to speed search? ============ */ -void Cmd_ExecuteString (const char *text, cmd_source_t src) +void Cmd_ExecuteString (const char *text, cmd_source_t src, qboolean lockmutex) { int oldpos; int found; @@ -1610,10 +1683,7 @@ void Cmd_ExecuteString (const char *text, cmd_source_t src) // execute the command line if (!Cmd_Argc()) - { - cmd_tokenizebufferpos = oldpos; - return; // no tokens - } + goto done; // no tokens // check functions for (cmd=cmd_functions ; cmd ; cmd=cmd->next) @@ -1621,7 +1691,7 @@ void Cmd_ExecuteString (const char *text, cmd_source_t src) if (!strcasecmp (cmd_argv[0],cmd->name)) { if (cmd->csqcfunc && CL_VM_ConsoleCommand (text)) //[515]: csqc - return; + goto done; switch (src) { case src_command: @@ -1645,8 +1715,7 @@ void Cmd_ExecuteString (const char *text, cmd_source_t src) if (cmd->clientfunction) { cmd->clientfunction (); - cmd_tokenizebufferpos = oldpos; - return; + goto done; } break; } @@ -1659,8 +1728,7 @@ command_found: if (cmd_source == src_client) { Con_Printf("player \"%s\" tried to %s\n", host_client->name, text); - cmd_tokenizebufferpos = oldpos; - return; + goto done; } // check alias @@ -1669,21 +1737,18 @@ command_found: if (!strcasecmp (cmd_argv[0], a->name)) { Cmd_ExecuteAlias(a); - cmd_tokenizebufferpos = oldpos; - return; + goto done; } } if(found) // if the command was hooked and found, all is good - { - cmd_tokenizebufferpos = oldpos; - return; - } + goto done; // check cvars if (!Cvar_Command () && host_framecount > 0) Con_Printf("Unknown command \"%s\"\n", Cmd_Argv(0)); +done: cmd_tokenizebufferpos = oldpos; } @@ -1825,6 +1890,7 @@ Sends the entire command line over to the server void Cmd_ForwardToServer (void) { const char *s; + char vabuf[1024]; if (!strcasecmp(Cmd_Argv(0), "cmd")) { // we want to strip off "cmd", so just send the args @@ -1833,7 +1899,7 @@ void Cmd_ForwardToServer (void) else { // we need to keep the command name, so send Cmd_Argv(0), a space and then Cmd_Args() - s = va("%s %s", Cmd_Argv(0), Cmd_Argc() > 1 ? Cmd_Args() : ""); + s = va(vabuf, sizeof(vabuf), "%s %s", Cmd_Argv(0), Cmd_Argc() > 1 ? Cmd_Args() : ""); } // don't send an empty forward message if the user tries "cmd" by itself if (!s || !*s) @@ -1868,3 +1934,61 @@ int Cmd_CheckParm (const char *parm) return 0; } + + +void Cmd_SaveInitState(void) +{ + cmd_function_t *f; + cmdalias_t *a; + for (f = cmd_functions;f;f = f->next) + f->initstate = true; + for (a = cmd_alias;a;a = a->next) + { + a->initstate = true; + a->initialvalue = Mem_strdup(zonemempool, a->value); + } + Cvar_SaveInitState(); +} + +void Cmd_RestoreInitState(void) +{ + cmd_function_t *f, **fp; + cmdalias_t *a, **ap; + for (fp = &cmd_functions;(f = *fp);) + { + if (f->initstate) + fp = &f->next; + else + { + // destroy this command, it didn't exist at init + Con_DPrintf("Cmd_RestoreInitState: Destroying command %s\n", f->name); + *fp = f->next; + Z_Free(f); + } + } + for (ap = &cmd_alias;(a = *ap);) + { + if (a->initstate) + { + // restore this alias, it existed at init + if (strcmp(a->value ? a->value : "", a->initialvalue ? a->initialvalue : "")) + { + Con_DPrintf("Cmd_RestoreInitState: Restoring alias %s\n", a->name); + if (a->value) + Z_Free(a->value); + a->value = Mem_strdup(zonemempool, a->initialvalue); + } + ap = &a->next; + } + else + { + // free this alias, it didn't exist at init... + Con_DPrintf("Cmd_RestoreInitState: Destroying alias %s\n", a->name); + *ap = a->next; + if (a->value) + Z_Free(a->value); + Z_Free(a); + } + } + Cvar_RestoreInitState(); +}