X-Git-Url: http://git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=cmd.c;h=a12fae8ea0c4fa52839d66a46267e73b927936bf;hp=34a5da43e4ee0ebd2fe3ffdaa40678966550e467;hb=97820be04836873cca98fddf52c1901e430d9bee;hpb=1281b657ccad3673df5c7334c1c45b72645c2d08 diff --git a/cmd.c b/cmd.c index 34a5da43..a12fae8e 100644 --- a/cmd.c +++ b/cmd.c @@ -22,9 +22,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quakedef.h" #include "thread.h" -cmd_state_t cmd_client; -cmd_state_t cmd_server; -cmd_state_t cmd_serverfromclient; +cmd_state_t *cmd_local; +cmd_state_t *cmd_serverfromclient; cmd_userdefined_t cmd_userdefined_all; cmd_userdefined_t cmd_userdefined_null; @@ -34,12 +33,7 @@ typedef struct cmd_iter_s { } cmd_iter_t; -static cmd_iter_t cmd_iter_all[] = { - {&cmd_client}, - {&cmd_server}, - {&cmd_serverfromclient}, - {NULL}, -}; +static cmd_iter_t *cmd_iter_all; mempool_t *cbuf_mempool; @@ -88,35 +82,36 @@ static void Cmd_Defer_f (cmd_state_t *cmd) if(Cmd_Argc(cmd) == 1) { - if(List_IsEmpty(&cbuf->deferred)) + if(List_Is_Empty(&cbuf->deferred)) Con_Printf("No commands are pending.\n"); else { - llist_t *pos; - List_ForEach(pos, &cbuf->deferred) - { - current = List_Container(*pos, cmd_input_t, list); + List_For_Each_Entry(current, &cbuf->deferred, cmd_input_t, list) Con_Printf("-> In %9.2f: %s\n", current->delay, current->text); - } } } else if(Cmd_Argc(cmd) == 2 && !strcasecmp("clear", Cmd_Argv(cmd, 1))) { - while(!List_IsEmpty(&cbuf->deferred)) + while(!List_Is_Empty(&cbuf->deferred)) List_Move_Tail(cbuf->deferred.next, &cbuf->free); } else if(Cmd_Argc(cmd) == 3) { const char *text = Cmd_Argv(cmd, 2); current = Cbuf_LinkGet(cbuf, NULL); - current->size = strlen(text); + current->length = strlen(text); current->source = cmd; current->delay = atof(Cmd_Argv(cmd, 1)); - memcpy(current->text, text, current->size + 1); + if(current->size < current->length) + { + current->text = (char *)Mem_Realloc(cbuf_mempool, current->text, current->length + 1); + current->size = current->length; + } + + strlcpy(current->text, text, current->length + 1); List_Move_Tail(¤t->list, &cbuf->deferred); - } else { @@ -182,9 +177,9 @@ static cmd_input_t *Cbuf_LinkGet(cmd_buf_t *cbuf, cmd_input_t *existing) cmd_input_t *ret = NULL; if(existing && existing->pending) ret = existing; - else if(!List_IsEmpty(&cbuf->free)) + else if(!List_Is_Empty(&cbuf->free)) { - ret = List_Container(*cbuf->free.next, cmd_input_t, list); + ret = List_Entry(cbuf->free.next, cmd_input_t, list); ret->length = 0; ret->pending = false; } @@ -199,6 +194,11 @@ static cmd_input_t *Cmd_AllocInputNode(void) return node; } + +// Cloudwalk FIXME: The entire design of this thing is overly complicated. +// We could very much safely have one input node per line whether or not +// the command was terminated. We don't need to split up input nodes per command +// executed. static size_t Cmd_ParseInput (cmd_input_t **output, char **input) { size_t pos, cmdsize = 0, start = 0; @@ -285,18 +285,24 @@ static size_t Cmd_ParseInput (cmd_input_t **output, char **input) if(!*output) *output = Cmd_AllocInputNode(); + // Append, since this input line hasn't closed yet. if((*output)->pending) offset = (*output)->length; (*output)->length += cmdsize; - if((*output)->size < (*output)->length + 1) + if((*output)->size < (*output)->length) { (*output)->text = (char *)Mem_Realloc(cbuf_mempool, (*output)->text, (*output)->length + 1); - (*output)->size = (*output)->length + 1; + (*output)->size = (*output)->length; } strlcpy(&(*output)->text[offset], &(*input)[start], cmdsize + 1); + + /* + * If we were still looking ahead by the time we broke from the loop, the command input + * hasn't terminated yet and we're still expecting more, so keep this node open for appending later. + */ (*output)->pending = !lookahead; } @@ -317,12 +323,14 @@ static void Cbuf_LinkCreate(cmd_state_t *cmd, llist_t *head, cmd_input_t *existi // Slide the pointer down until we reach the end while(*in) { + // Check if the current node is still accepting input (input line hasn't terminated) current = Cbuf_LinkGet(cbuf, existing); newsize = Cmd_ParseInput(¤t, &in); // Valid command if(newsize) { + // current will match existing if the input line hasn't terminated yet if(current != existing) { current->source = cmd; @@ -358,8 +366,8 @@ void Cbuf_AddText (cmd_state_t *cmd, const char *text) Con_Print("Cbuf_AddText: overflow\n"); else { - Cbuf_LinkCreate(cmd, &llist, (List_IsEmpty(&cbuf->start) ? NULL : List_Container(*cbuf->start.prev, cmd_input_t, list)), text); - if(!List_IsEmpty(&llist)) + Cbuf_LinkCreate(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(cbuf->start.prev, cmd_input_t, list)), text); + if(!List_Is_Empty(&llist)) List_Splice_Tail(&llist, &cbuf->start); } Cbuf_Unlock(cbuf); @@ -382,12 +390,12 @@ void Cbuf_InsertText (cmd_state_t *cmd, const char *text) Cbuf_Lock(cbuf); // we need to memmove the existing text and stuff this in before it... - if (cbuf->size + l >= (size_t)cbuf->maxsize) + if (cbuf->size + l >= cbuf->maxsize) Con_Print("Cbuf_InsertText: overflow\n"); else { - Cbuf_LinkCreate(cmd, &llist, List_Container(*cbuf->start.next, cmd_input_t, list), text); - if(!List_IsEmpty(&llist)) + Cbuf_LinkCreate(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(cbuf->start.next, cmd_input_t, list)), text); + if(!List_Is_Empty(&llist)) List_Splice(&llist, &cbuf->start); } @@ -401,7 +409,6 @@ Cbuf_Execute_Deferred --blub */ static void Cbuf_Execute_Deferred (cmd_buf_t *cbuf) { - llist_t *pos; cmd_input_t *current; double eat; @@ -412,14 +419,13 @@ static void Cbuf_Execute_Deferred (cmd_buf_t *cbuf) return; cbuf->deferred_oldtime = host.realtime; - List_ForEach(pos, &cbuf->deferred) + List_For_Each_Entry(current, &cbuf->deferred, cmd_input_t, list) { - current = List_Container(*pos, cmd_input_t, list); current->delay -= eat; if(current->delay <= 0) { - cbuf->size += current->size; - List_Move(pos, &cbuf->start); + cbuf->size += current->length; + List_Move(¤t->list, &cbuf->start); // We must return and come back next frame or the engine will freeze. Fragile... like glass :3 return; } @@ -441,14 +447,14 @@ void Cbuf_Execute (cmd_buf_t *cbuf) // LadyHavoc: making sure the tokenizebuffer doesn't get filled up by repeated crashes cbuf->tokenizebufferpos = 0; - while (!List_IsEmpty(&cbuf->start)) + while (!List_Is_Empty(&cbuf->start)) { /* * 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 */ - current = List_Container(*cbuf->start.next, cmd_input_t, list); + current = List_Entry(cbuf->start.next, cmd_input_t, list); // Recycle memory so using WASD doesn't cause a malloc and free List_Move_Tail(¤t->list, &cbuf->free); @@ -491,8 +497,29 @@ void Cbuf_Execute (cmd_buf_t *cbuf) } } +/* +=================== +Cbuf_Frame_Input + +Add them exactly as if they had been typed at the console +=================== +*/ +static void Cbuf_Frame_Input(void) +{ + char *line; + + while ((line = Sys_ConsoleInput())) + Cbuf_AddText(cmd_local, line); +} + void Cbuf_Frame(cmd_buf_t *cbuf) { + // check for commands typed to the host + Cbuf_Frame_Input(); + +// R_TimeReport("preconsole"); + + // execute commands queued with the defer command Cbuf_Execute_Deferred(cbuf); if (cbuf->size) { @@ -500,6 +527,8 @@ void Cbuf_Frame(cmd_buf_t *cbuf) Cbuf_Execute(cbuf); SV_UnlockThreadMutex(); } + +// R_TimeReport("console"); } /* @@ -1501,7 +1530,7 @@ static void Cmd_List_f (cmd_state_t *cmd) } count = 0; - for (func = cmd->userdefined->csqc_functions; func; func = func->next) + for (func = cmd->userdefined->qc_functions; func; func = func->next) { if (partial && (ispattern ? !matchpattern_with_separator(func->name, partial, false, "", false) : strncmp(partial, func->name, len))) continue; @@ -1559,17 +1588,17 @@ static void Cmd_Apropos_f(cmd_state_t *cmd) Cvar_PrintHelp(cvar, cvar->name, true); count++; } - for (int i = 0; i < cvar->aliasindex; i++) + for (char **cvar_alias = cvar->aliases; cvar_alias && *cvar_alias; cvar_alias++) { - if (matchpattern_with_separator(cvar->aliases[i], partial, true, "", false)) + if (matchpattern_with_separator(*cvar_alias, partial, true, "", false)) { Con_Printf ("cvar "); - Cvar_PrintHelp(cvar, cvar->aliases[i], true); + Cvar_PrintHelp(cvar, *cvar_alias, true); count++; } } } - for (func = cmd->userdefined->csqc_functions; func; func = func->next) + for (func = cmd->userdefined->qc_functions; func; func = func->next) { if (!matchpattern_with_separator(func->name, partial, true, "", false)) if (!matchpattern_with_separator(func->description, partial, true, "", false)) @@ -1597,6 +1626,23 @@ static void Cmd_Apropos_f(cmd_state_t *cmd) Con_Printf("%i result%s\n\n", count, (count > 1) ? "s" : ""); } +static cmd_state_t *Cmd_AddInterpreter(cmd_buf_t *cbuf, cvar_state_t *cvars, int cvars_flagsmask, int cmds_flagsmask, cmd_userdefined_t *userdefined) +{ + cmd_state_t *cmd = (cmd_state_t *)Mem_Alloc(tempmempool, sizeof(cmd_state_t)); + + cmd->mempool = Mem_AllocPool("commands", 0, NULL); + // space for commands and script files + cmd->cbuf = cbuf; + cmd->null_string = ""; + + cmd->cvars = cvars; + cmd->cvars_flagsmask = cvars_flagsmask; + cmd->cmd_flags = cmds_flagsmask; + cmd->userdefined = userdefined; + + return cmd; +} + /* ============ Cmd_Init @@ -1604,7 +1650,6 @@ Cmd_Init */ void Cmd_Init(void) { - cmd_iter_t *cmd_iter; cmd_buf_t *cbuf; cbuf_mempool = Mem_AllocPool("Command buffer", 0, NULL); cbuf = (cmd_buf_t *)Mem_Alloc(cbuf_mempool, sizeof(cmd_buf_t)); @@ -1617,36 +1662,20 @@ void Cmd_Init(void) cbuf->deferred.prev = cbuf->deferred.next = &(cbuf->deferred); cbuf->free.prev = cbuf->free.next = &(cbuf->free); - for (cmd_iter = cmd_iter_all; cmd_iter->cmd; cmd_iter++) - { - cmd_state_t *cmd = cmd_iter->cmd; - cmd->mempool = Mem_AllocPool("commands", 0, NULL); - // space for commands and script files - cmd->cbuf = cbuf; - cmd->null_string = ""; - } - // client console can see server cvars because the user may start a server - cmd_client.cvars = &cvars_all; - cmd_client.cvars_flagsmask = CF_CLIENT | CF_SERVER; - cmd_client.cmd_flags = CF_CLIENT | CF_CLIENT_FROM_SERVER; - cmd_client.auto_flags = CF_SERVER_FROM_CLIENT; - cmd_client.auto_function = CL_ForwardToServer_f; // FIXME: Move this to the client. - cmd_client.userdefined = &cmd_userdefined_all; - // dedicated server console can only see server cvars, there is no client - cmd_server.cvars = &cvars_all; - cmd_server.cvars_flagsmask = CF_SERVER; - cmd_server.cmd_flags = CF_SERVER; - cmd_server.auto_flags = 0; - cmd_server.auto_function = NULL; - cmd_server.userdefined = &cmd_userdefined_all; + // FIXME: Get rid of cmd_iter_all eventually. This is just a hack to reduce the amount of work to make the interpreters dynamic. + cmd_iter_all = (cmd_iter_t *)Mem_Alloc(tempmempool, sizeof(cmd_iter_t) * 3); + + // local console + cmd_iter_all[0].cmd = cmd_local = Cmd_AddInterpreter(cbuf, &cvars_all, CF_CLIENT | CF_SERVER, CF_CLIENT | CF_CLIENT_FROM_SERVER | CF_SERVER_FROM_CLIENT, &cmd_userdefined_all); + cmd_local->Handle = Cmd_CL_Callback; + cmd_local->NotFound = NULL; + // server commands received from clients have no reason to access cvars, cvar expansion seems perilous. - cmd_serverfromclient.cvars = &cvars_null; - cmd_serverfromclient.cvars_flagsmask = 0; - cmd_serverfromclient.cmd_flags = CF_SERVER_FROM_CLIENT | CF_USERINFO; - cmd_serverfromclient.auto_flags = 0; - cmd_serverfromclient.auto_function = NULL; - cmd_serverfromclient.userdefined = &cmd_userdefined_null; + cmd_iter_all[1].cmd = cmd_serverfromclient = Cmd_AddInterpreter(cbuf, &cvars_null, 0, CF_SERVER_FROM_CLIENT | CF_USERINFO, &cmd_userdefined_null); + cmd_serverfromclient->Handle = Cmd_SV_Callback; + cmd_serverfromclient->NotFound = Cmd_SV_NotFound; + cmd_iter_all[2].cmd = NULL; // // register our commands // @@ -1756,6 +1785,7 @@ static void Cmd_TokenizeString (cmd_state_t *cmd, const char *text) cmd->argc = 0; cmd->args = NULL; + cmd->cmdline = NULL; while (1) { @@ -1779,6 +1809,8 @@ static void Cmd_TokenizeString (cmd_state_t *cmd, const char *text) if (!*text) return; + if(!cmd->argc) + cmd->cmdline = text; if (cmd->argc == 1) cmd->args = text; @@ -1812,22 +1844,13 @@ void Cmd_AddCommand(int flags, const char *cmd_name, xcommand_t function, const cmd_function_t *func; cmd_function_t *prev, *current; cmd_state_t *cmd; - xcommand_t save = NULL; - qbool auto_add = false; int i; - for (i = 0; i < 3; i++) + for (i = 0; i < 2; i++) { cmd = cmd_iter_all[i].cmd; - if ((flags & cmd->cmd_flags) || (flags & cmd->auto_flags)) + if (flags & cmd->cmd_flags) { - if((flags & cmd->auto_flags) && cmd->auto_function) - { - save = function; - function = cmd->auto_function; - auto_add = true; - } - // fail if the command is a variable name if (Cvar_FindVar(cmd->cvars, cmd_name, ~0)) { @@ -1842,10 +1865,8 @@ void Cmd_AddCommand(int flags, const char *cmd_name, xcommand_t function, const { if (!strcmp(cmd_name, func->name)) { - if(func->autofunc && !auto_add) - break; Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name); - goto next; + continue; } } @@ -1855,7 +1876,6 @@ void Cmd_AddCommand(int flags, const char *cmd_name, xcommand_t function, const func->function = function; func->description = description; func->next = cmd->engine_functions; - func->autofunc = auto_add; // insert it at the right alphanumeric position for (prev = NULL, current = cmd->engine_functions; current && strcmp(current->name, func->name) < 0; prev = current, current = current->next) @@ -1870,42 +1890,37 @@ void Cmd_AddCommand(int flags, const char *cmd_name, xcommand_t function, const } else { - // mark csqcfunc if the function already exists in the csqc_functions list - for (func = cmd->userdefined->csqc_functions; func; func = func->next) + // mark qcfunc if the function already exists in the qc_functions list + for (func = cmd->userdefined->qc_functions; func; func = func->next) { if (!strcmp(cmd_name, func->name)) { - func->csqcfunc = true; //[515]: csqc + func->qcfunc = true; //[515]: csqc continue; } } func = (cmd_function_t *)Mem_Alloc(cmd->mempool, sizeof(cmd_function_t)); + func->flags = flags; func->name = cmd_name; func->function = function; func->description = description; - func->csqcfunc = true; //[515]: csqc - func->next = cmd->userdefined->csqc_functions; - func->autofunc = false; + func->qcfunc = true; //[515]: csqc + func->next = cmd->userdefined->qc_functions; // insert it at the right alphanumeric position - for (prev = NULL, current = cmd->userdefined->csqc_functions; current && strcmp(current->name, func->name) < 0; prev = current, current = current->next) + for (prev = NULL, current = cmd->userdefined->qc_functions; current && strcmp(current->name, func->name) < 0; prev = current, current = current->next) ; if (prev) { prev->next = func; } else { - cmd->userdefined->csqc_functions = func; + cmd->userdefined->qc_functions = func; } func->next = current; } - if (save) - function = save; } -next: - auto_add = false; - continue; } } @@ -1918,7 +1933,7 @@ qbool Cmd_Exists (cmd_state_t *cmd, const char *cmd_name) { cmd_function_t *func; - for (func = cmd->userdefined->csqc_functions; func; func = func->next) + for (func = cmd->userdefined->qc_functions; func; func = func->next) if (!strcmp(cmd_name, func->name)) return true; @@ -1946,7 +1961,7 @@ const char *Cmd_CompleteCommand (cmd_state_t *cmd, const char *partial) return NULL; // check functions - for (func = cmd->userdefined->csqc_functions; func; func = func->next) + for (func = cmd->userdefined->qc_functions; func; func = func->next) if (!strncasecmp(partial, func->name, len)) return func->name; @@ -1979,7 +1994,7 @@ int Cmd_CompleteCountPossible (cmd_state_t *cmd, const char *partial) return 0; // Loop through the command list and count all partial matches - for (func = cmd->userdefined->csqc_functions; func; func = func->next) + for (func = cmd->userdefined->qc_functions; func; func = func->next) if (!strncasecmp(partial, func->name, len)) h++; @@ -2010,7 +2025,7 @@ const char **Cmd_CompleteBuildList (cmd_state_t *cmd, const char *partial) len = strlen(partial); buf = (const char **)Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *)); // Loop through the functions lists and print all matches - for (func = cmd->userdefined->csqc_functions; func; func = func->next) + for (func = cmd->userdefined->qc_functions; func; func = func->next) if (!strncasecmp(partial, func->name, len)) buf[bpos++] = func->name; for (func = cmd->engine_functions; func; func = func->next) @@ -2027,7 +2042,7 @@ void Cmd_CompleteCommandPrint (cmd_state_t *cmd, const char *partial) cmd_function_t *func; size_t len = strlen(partial); // Loop through the command list and print all matches - for (func = cmd->userdefined->csqc_functions; func; func = func->next) + for (func = cmd->userdefined->qc_functions; func; func = func->next) if (!strncasecmp(partial, func->name, len)) Con_Printf("^2%s^7: %s\n", func->name, func->description); for (func = cmd->engine_functions; func; func = func->next) @@ -2136,7 +2151,7 @@ const char **Cmd_CompleteAliasBuildList (cmd_state_t *cmd, const char *partial) void Cmd_ClearCSQCCommands (cmd_state_t *cmd) { cmd_function_t *func; - cmd_function_t **next = &cmd->userdefined->csqc_functions; + cmd_function_t **next = &cmd->userdefined->qc_functions; while(*next) { @@ -2148,6 +2163,76 @@ void Cmd_ClearCSQCCommands (cmd_state_t *cmd) extern cvar_t sv_cheats; +/* + * Cloudwalk FIXME: This idea sounded great in my head but... + * How do we handle commands that can be received by the client, + * but which the server can also execute locally? + * + * If we create a callback where the engine will forward to server + * but try to execute the command locally if it's dedicated, + * we're back to intermixing client and server code which I'm + * trying to avoid. There's no other way I can think of to + * implement that behavior that doesn't involve an #ifdef, or + * making a mess of hooks. + */ +qbool Cmd_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src) +{ + if (func->function) + func->function(cmd); + else + Con_Printf("Command \"%s\" can not be executed\n", Cmd_Argv(cmd, 0)); + return true; +} + +qbool Cmd_CL_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src) +{ + // TODO: Assign these functions to QC commands directly? + if(func->qcfunc) + { + if(((func->flags & CF_CLIENT) && CL_VM_ConsoleCommand(text)) || + ((func->flags & CF_SERVER) && SV_VM_ConsoleCommand(text))) + return true; + } + if (func->flags & CF_SERVER_FROM_CLIENT) + { + if(host_isclient.integer) + { + CL_ForwardToServer_f(cmd); + return true; + } + else if(!(func->flags & CF_SERVER)) + { + Con_Printf("Cannot execute client commands from a dedicated server console.\n"); + return true; + } + } + return Cmd_Callback(cmd, func, text, src); +} + +qbool Cmd_SV_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src) +{ + if(func->qcfunc && (func->flags & CF_SERVER)) + return SV_VM_ConsoleCommand(text); + else if (src == src_client) + { + if((func->flags & CF_CHEAT) && !sv_cheats.integer) + SV_ClientPrintf("No cheats allowed. The server must have sv_cheats set to 1\n"); + else + func->function(cmd); + return true; + } + return false; +} + +qbool Cmd_SV_NotFound(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src) +{ + if (cmd->source == src_client) + { + Con_Printf("Client \"%s\" tried to execute \"%s\"\n", host_client->name, text); + return true; + } + return false; +} /* ============ Cmd_ExecuteString @@ -2173,13 +2258,12 @@ void Cmd_ExecuteString (cmd_state_t *cmd, const char *text, cmd_source_t src, qb goto done; // no tokens // check functions - for (func = cmd->userdefined->csqc_functions; func; func = func->next) + for (func = cmd->userdefined->qc_functions; func; func = func->next) { if (!strcasecmp(cmd->argv[0], func->name)) { - if (func->csqcfunc && CL_VM_ConsoleCommand(text)) //[515]: csqc + if(cmd->Handle(cmd, func, text, src)) goto done; - break; } } @@ -2187,33 +2271,16 @@ void Cmd_ExecuteString (cmd_state_t *cmd, const char *text, cmd_source_t src, qb { if (!strcasecmp (cmd->argv[0], func->name)) { - switch (src) - { - case src_local: - if (func->function) - func->function(cmd); - else - Con_Printf("Command \"%s\" can not be executed\n", Cmd_Argv(cmd, 0)); + if(cmd->Handle(cmd, func, text, src)) goto done; - case src_client: - if (func->function) - { - if((func->flags & CF_CHEAT) && !sv_cheats.integer) - SV_ClientPrintf("No cheats allowed. The server must have sv_cheats set to 1\n"); - else - func->function(cmd); - goto done; - } - } - break; } } // if it's a client command and no command was found, say so. - if (cmd->source == src_client) + if(cmd->NotFound) { - Con_Printf("Client \"%s\" tried to execute \"%s\"\n", host_client->name, text); - goto done; + if(cmd->NotFound(cmd, func, text, src)) + goto done; } // check alias @@ -2271,7 +2338,7 @@ void Cmd_SaveInitState(void) cmd_state_t *cmd = cmd_iter->cmd; cmd_function_t *f; cmd_alias_t *a; - for (f = cmd->userdefined->csqc_functions; f; f = f->next) + for (f = cmd->userdefined->qc_functions; f; f = f->next) f->initstate = true; for (f = cmd->engine_functions; f; f = f->next) f->initstate = true; @@ -2292,7 +2359,7 @@ void Cmd_RestoreInitState(void) cmd_state_t *cmd = cmd_iter->cmd; cmd_function_t *f, **fp; cmd_alias_t *a, **ap; - for (fp = &cmd->userdefined->csqc_functions; (f = *fp);) + for (fp = &cmd->userdefined->qc_functions; (f = *fp);) { if (f->initstate) fp = &f->next; @@ -2343,3 +2410,7 @@ void Cmd_RestoreInitState(void) } Cvar_RestoreInitState(&cvars_all); } + +void Cmd_NoOperation_f(cmd_state_t *cmd) +{ +}