]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cmd.c
Initial implementation of cvar aliases, with slowmo as a starting example
[xonotic/darkplaces.git] / cmd.c
diff --git a/cmd.c b/cmd.c
index 28185b16116569e8bb533280e1f8b589cb30685d..43794e8b1c4b96d52643b50a8f11d6dd6934b901 100644 (file)
--- a/cmd.c
+++ b/cmd.c
@@ -27,6 +27,9 @@ cmd_state_t cmd_clientfromserver;
 cmd_state_t cmd_server;
 cmd_state_t cmd_serverfromclient;
 
+cmd_userdefined_t cmd_userdefined_all;
+cmd_userdefined_t cmd_userdefined_null;
+
 typedef struct cmd_iter_s {
        cmd_state_t *cmd;
 }
@@ -193,7 +196,7 @@ void Cbuf_AddText (cmd_state_t *cmd, const char *text)
        l = (int)strlen(text);
 
        Cbuf_Lock(cmd);
-       if (cmd->text.cursize + l >= (size_t)cmd->text.maxsize)
+       if (cmd->text.maxsize - cmd->text.cursize <= l)
                Con_Print("Cbuf_AddText: overflow\n");
        else
                SZ_Write(&cmd->text, (const unsigned char *)text, l);
@@ -234,14 +237,13 @@ Cbuf_Execute_Deferred --blub
 */
 static void Cbuf_Execute_Deferred (cmd_state_t *cmd)
 {
-       static double oldrealtime = 0;
        cmddeferred_t *defcmd, *prev;
        double eat;
-       if (realtime - oldrealtime < 0 || realtime - oldrealtime > 1800) oldrealtime = realtime;
-       eat = realtime - oldrealtime;
+       if (realtime - cmd->deferred_oldrealtime < 0 || realtime - cmd->deferred_oldrealtime > 1800) cmd->deferred_oldrealtime = realtime;
+       eat = realtime - cmd->deferred_oldrealtime;
        if (eat < (1.0 / 120.0))
                return;
-       oldrealtime = realtime;
+       cmd->deferred_oldrealtime = realtime;
        prev = NULL;
        defcmd = cmd->deferred_list;
        while(defcmd)
@@ -325,7 +327,7 @@ void Cbuf_Execute (cmd_state_t *cmd)
                // better than CRASHING on overlong input lines that may SOMEHOW enter the buffer
                if(i >= MAX_INPUTLINE)
                {
-                       Con_Printf("Warning: console input buffer had an overlong line. Ignored.\n");
+                       Con_Warnf("Warning: console input buffer had an overlong line. Ignored.\n");
                        line[0] = 0;
                }
                else
@@ -395,6 +397,8 @@ void Cbuf_Frame(cmd_state_t *cmd)
 ==============================================================================
 */
 
+extern qboolean host_init;
+
 /*
 ===============
 Cmd_StuffCmds_f
@@ -411,6 +415,10 @@ static void Cmd_StuffCmds_f (cmd_state_t *cmd)
        // this is for all commandline options combined (and is bounds checked)
        char    build[MAX_INPUTLINE];
 
+       // come back later so we don't crash
+       if(host_init)
+               return;
+
        if (Cmd_Argc (cmd) != 1)
        {
                Con_Print("stuffcmds : execute command line parameters\n");
@@ -522,6 +530,7 @@ static void Cmd_Exec(cmd_state_t *cmd, const char *filename)
 "sys_ticrate 0.01388889\n"
 "r_shadow_gloss 1\n"
 "r_shadow_bumpscale_basetexture 0\n"
+"csqc_polygons_defaultmaterial_nocullface 0\n"
                                );
                        break;
                case GAME_NEHAHRA:
@@ -546,6 +555,7 @@ static void Cmd_Exec(cmd_state_t *cmd, const char *filename)
 "sys_ticrate 0.01388889\n"
 "r_shadow_gloss 1\n"
 "r_shadow_bumpscale_basetexture 0\n"
+"csqc_polygons_defaultmaterial_nocullface 0\n"
                                );
                        break;
                // 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.
@@ -574,6 +584,7 @@ static void Cmd_Exec(cmd_state_t *cmd, const char *filename)
 "sys_ticrate 0.02\n"
 "r_shadow_gloss 1\n"
 "r_shadow_bumpscale_basetexture 0\n"
+"csqc_polygons_defaultmaterial_nocullface 0\n"
                                );
                        break;
                // rogue mission pack has a guardian boss that does not wake up if findradius returns one of the entities around its spawn area
@@ -599,6 +610,7 @@ static void Cmd_Exec(cmd_state_t *cmd, const char *filename)
 "sys_ticrate 0.01388889\n"
 "r_shadow_gloss 1\n"
 "r_shadow_bumpscale_basetexture 0\n"
+"csqc_polygons_defaultmaterial_nocullface 0\n"
                                );
                        break;
                case GAME_TENEBRAE:
@@ -623,6 +635,7 @@ static void Cmd_Exec(cmd_state_t *cmd, const char *filename)
 "sys_ticrate 0.01388889\n"
 "r_shadow_gloss 2\n"
 "r_shadow_bumpscale_basetexture 4\n"
+"csqc_polygons_defaultmaterial_nocullface 0\n"
                                );
                        break;
                case GAME_NEXUIZ:
@@ -647,6 +660,14 @@ static void Cmd_Exec(cmd_state_t *cmd, const char *filename)
 "sys_ticrate 0.01388889\n"
 "sv_gameplayfix_q2airaccelerate 1\n"
 "sv_gameplayfix_stepmultipletimes 1\n"
+"csqc_polygons_defaultmaterial_nocullface 1\n"
+                               );
+                       break;
+               case GAME_XONOTIC:
+               case GAME_VORETOURNAMENT:
+                       // compatibility for versions prior to 2020-05-25, this can be overridden in newer versions to get the default behavior and be consistent with FTEQW engine
+                       Cbuf_InsertText(cmd, "\n"
+"csqc_polygons_defaultmaterial_nocullface 1\n"
                                );
                        break;
                // Steel Storm: Burning Retribution csqc misinterprets CSQC_InputEvent if type is a value other than 0 or 1
@@ -671,6 +692,7 @@ static void Cmd_Exec(cmd_state_t *cmd, const char *filename)
 "sv_gameplayfix_downtracesupportsongroundflag 1\n"
 "sys_ticrate 0.01388889\n"
 "cl_csqc_generatemousemoveevents 0\n"
+"csqc_polygons_defaultmaterial_nocullface 1\n"
                                );
                        break;
                default:
@@ -693,6 +715,7 @@ static void Cmd_Exec(cmd_state_t *cmd, const char *filename)
 "sv_gameplayfix_swiminbmodels 1\n"
 "sv_gameplayfix_downtracesupportsongroundflag 1\n"
 "sys_ticrate 0.01388889\n"
+"csqc_polygons_defaultmaterial_nocullface 0\n"
                                );
                        break;
                }
@@ -765,7 +788,7 @@ static void Cmd_Toggle_f(cmd_state_t *cmd)
        else
        { // Correct Arguments Specified
                // Acquire Potential CVar
-               cvar_t* cvCVar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 1), cmd->cvars_flagsmask);
+               cvar_t* cvCVar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 1), cmd->cvars_flagsmask, false);
 
                if(cvCVar != NULL)
                { // Valid CVar
@@ -843,7 +866,7 @@ static void Cmd_Alias_f (cmd_state_t *cmd)
        if (Cmd_Argc(cmd) == 1)
        {
                Con_Print("Current alias commands:\n");
-               for (a = cmd->alias ; a ; a=a->next)
+               for (a = cmd->userdefined->alias ; a ; a=a->next)
                        Con_Printf("%s : %s", a->name, a->value);
                return;
        }
@@ -856,7 +879,7 @@ static void Cmd_Alias_f (cmd_state_t *cmd)
        }
 
        // if the alias already exists, reuse it
-       for (a = cmd->alias ; a ; a=a->next)
+       for (a = cmd->userdefined->alias ; a ; a=a->next)
        {
                if (!strcmp(s, a->name))
                {
@@ -872,12 +895,12 @@ static void Cmd_Alias_f (cmd_state_t *cmd)
                a = (cmdalias_t *)Z_Malloc (sizeof(cmdalias_t));
                strlcpy (a->name, s, sizeof (a->name));
                // insert it at the right alphanumeric position
-               for( prev = NULL, current = cmd->alias ; current && strcmp( current->name, a->name ) < 0 ; prev = current, current = current->next )
+               for( prev = NULL, current = cmd->userdefined->alias ; current && strcmp( current->name, a->name ) < 0 ; prev = current, current = current->next )
                        ;
                if( prev ) {
                        prev->next = a;
                } else {
-                       cmd->alias = a;
+                       cmd->userdefined->alias = a;
                }
                a->next = current;
        }
@@ -924,14 +947,14 @@ static void Cmd_UnAlias_f (cmd_state_t *cmd)
        {
                s = Cmd_Argv(cmd, i);
                p = NULL;
-               for(a = cmd->alias; a; p = a, a = a->next)
+               for(a = cmd->userdefined->alias; a; p = a, a = a->next)
                {
                        if(!strcmp(s, a->name))
                        {
                                if (a->initstate) // we can not remove init aliases
                                        continue;
-                               if(a == cmd->alias)
-                                       cmd->alias = a->next;
+                               if(a == cmd->userdefined->alias)
+                                       cmd->userdefined->alias = a->next;
                                if(p)
                                        p->next = a->next;
                                Z_Free(a->value);
@@ -1013,7 +1036,7 @@ static const char *Cmd_GetDirectCvarValue(cmd_state_t *cmd, const char *varname,
                }
        }
 
-       if((cvar = Cvar_FindVar(cmd->cvars, varname, cmd->cvars_flagsmask)) && !(cvar->flags & CVAR_PRIVATE))
+       if((cvar = Cvar_FindVar(cmd->cvars, varname, cmd->cvars_flagsmask, false)) && !(cvar->flags & CVAR_PRIVATE))
                return cvar->string;
 
        return NULL;
@@ -1104,9 +1127,9 @@ static const char *Cmd_GetCvarValue(cmd_state_t *cmd, const char *var, size_t va
        {
                // empty cvar name?
                if(alias)
-                       Con_Printf("Warning: Could not expand $ in alias %s\n", alias->name);
+                       Con_Warnf("Warning: Could not expand $ in alias %s\n", alias->name);
                else
-                       Con_Printf("Warning: Could not expand $\n");
+                       Con_Warnf("Warning: Could not expand $\n");
                return "$";
        }
 
@@ -1152,9 +1175,9 @@ static const char *Cmd_GetCvarValue(cmd_state_t *cmd, const char *var, size_t va
                if(required)
                {
                        if(alias)
-                               Con_Printf("Error: Could not expand $%s in alias %s\n", varname, alias->name);
+                               Con_Errorf("Error: Could not expand $%s in alias %s\n", varname, alias->name);
                        else
-                               Con_Printf("Error: Could not expand $%s\n", varname);
+                               Con_Errorf("Error: Could not expand $%s\n", varname);
                        return NULL;
                }
                else if(optional)
@@ -1164,9 +1187,9 @@ static const char *Cmd_GetCvarValue(cmd_state_t *cmd, const char *var, size_t va
                else
                {
                        if(alias)
-                               Con_Printf("Warning: Could not expand $%s in alias %s\n", varname, alias->name);
+                               Con_Warnf("Warning: Could not expand $%s in alias %s\n", varname, alias->name);
                        else
-                               Con_Printf("Warning: Could not expand $%s\n", varname);
+                               Con_Warnf("Warning: Could not expand $%s\n", varname);
                        dpsnprintf(varval, sizeof(varval), "$%s", varname);
                        return varval;
                }
@@ -1371,7 +1394,14 @@ static void Cmd_List_f (cmd_state_t *cmd)
        }
 
        count = 0;
-       for (func = cmd->functions; func; func = func->next)
+       for (func = cmd->userdefined->csqc_functions; func; func = func->next)
+       {
+               if (partial && (ispattern ? !matchpattern_with_separator(func->name, partial, false, "", false) : strncmp(partial, func->name, len)))
+                       continue;
+               Con_Printf("%s : %s\n", func->name, func->description);
+               count++;
+       }
+       for (func = cmd->engine_functions; func; func = func->next)
        {
                if (partial && (ispattern ? !matchpattern_with_separator(func->name, partial, false, "", false) : strncmp(partial, func->name, len)))
                        continue;
@@ -1418,10 +1448,19 @@ static void Cmd_Apropos_f(cmd_state_t *cmd)
                if (!matchpattern_with_separator(cvar->name, partial, true, "", false))
                if (!matchpattern_with_separator(cvar->description, partial, true, "", false))
                        continue;
-               Con_Printf ("cvar ^3%s^7 is \"%s\" [\"%s\"] %s\n", cvar->name, cvar->string, cvar->defstring, cvar->description);
+               Con_Printf ("cvar ");
+               Cvar_PrintHelp(cvar, true);
                count++;
        }
-       for (func = cmd->functions; func; func = func->next)
+       for (func = cmd->userdefined->csqc_functions; func; func = func->next)
+       {
+               if (!matchpattern_with_separator(func->name, partial, true, "", false))
+                       if (!matchpattern_with_separator(func->description, partial, true, "", false))
+                               continue;
+               Con_Printf("command ^2%s^7: %s\n", func->name, func->description);
+               count++;
+       }
+       for (func = cmd->engine_functions; func; func = func->next)
        {
                if (!matchpattern_with_separator(func->name, partial, true, "", false))
                if (!matchpattern_with_separator(func->description, partial, true, "", false))
@@ -1429,7 +1468,7 @@ static void Cmd_Apropos_f(cmd_state_t *cmd)
                Con_Printf("command ^2%s^7: %s\n", func->name, func->description);
                count++;
        }
-       for (alias = cmd->alias; alias; alias = alias->next)
+       for (alias = cmd->userdefined->alias; alias; alias = alias->next)
        {
                // procede here a bit differently as an alias value always got a final \n
                if (!matchpattern_with_separator(alias->name, partial, true, "", false))
@@ -1462,15 +1501,19 @@ void Cmd_Init(void)
        // client console can see server cvars because the user may start a server
        cmd_client.cvars = &cvars_all;
        cmd_client.cvars_flagsmask = CVAR_CLIENT | CVAR_SERVER;
+       cmd_client.userdefined = &cmd_userdefined_all;
        // stuffcmd from server has access to the reasonable client things, but it probably doesn't need to access the client's server-only cvars
        cmd_clientfromserver.cvars = &cvars_all;
        cmd_clientfromserver.cvars_flagsmask = CVAR_CLIENT;
+       cmd_clientfromserver.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 = CVAR_SERVER;
+       cmd_server.userdefined = &cmd_userdefined_all;
        // 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.userdefined = &cmd_userdefined_null;
 }
 
 void Cmd_Init_Commands(qboolean dedicated_server)
@@ -1480,6 +1523,7 @@ void Cmd_Init_Commands(qboolean dedicated_server)
 //
        // client-only commands
        Cmd_AddCommand(&cmd_client, "cmd", Cmd_ForwardToServer_f, "send a console commandline to the server (used by some mods)");
+       Cmd_AddCommand(&cmd_clientfromserver, "cmd", Cmd_ForwardToServer_f, "send a console commandline to the server (used by some mods)");
        Cmd_AddCommand(&cmd_client, "wait", Cmd_Wait_f, "make script execution wait for next rendered frame");
        Cmd_AddCommand(&cmd_client, "cprint", Cmd_Centerprint_f, "print something at the screen center");
 
@@ -1668,43 +1712,69 @@ void Cmd_AddCommand(cmd_state_t *cmd, const char *cmd_name, xcommand_t function,
        cmd_function_t *prev, *current;
 
 // fail if the command is a variable name
-       if (Cvar_FindVar(cmd->cvars, cmd_name, ~0))
+       if (Cvar_FindVar(cmd->cvars, cmd_name, ~0, true))
        {
                Con_Printf("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
                return;
        }
 
-       // fail if the command already exists in this interpreter
-       for (func = cmd->functions; func; func = func->next)
-               if (!strcmp(cmd_name, func->name))
-                       break;
-
-       if (func)
+       if (function)
        {
-               // command already defined...
-               if (function)
-                       Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name);
-               else    //[515]: csqc
-                       func->csqcfunc = true;
+               // fail if the command already exists in this interpreter
+               for (func = cmd->engine_functions; func; func = func->next)
+               {
+                       if (!strcmp(cmd_name, func->name))
+                       {
+                               Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name);
+                               return;
+                       }
+               }
+
+               func = (cmd_function_t *)Mem_Alloc(cmd->mempool, sizeof(cmd_function_t));
+               func->name = cmd_name;
+               func->function = function;
+               func->description = description;
+               func->next = cmd->engine_functions;
+
+               // 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)
+                       ;
+               if (prev) {
+                       prev->next = func;
+               }
+               else {
+                       cmd->engine_functions = func;
+               }
+               func->next = current;
        }
        else
        {
+               // mark csqcfunc if the function already exists in the csqc_functions list
+               for (func = cmd->userdefined->csqc_functions; func; func = func->next)
+               {
+                       if (!strcmp(cmd_name, func->name))
+                       {
+                               func->csqcfunc = true; //[515]: csqc
+                               return;
+                       }
+               }
+
+
                func = (cmd_function_t *)Mem_Alloc(cmd->mempool, sizeof(cmd_function_t));
                func->name = cmd_name;
                func->function = function;
                func->description = description;
-               if (!function)                  //[515]: csqc
-                       func->csqcfunc = true;
-               func->next = cmd->functions;
+               func->csqcfunc = true; //[515]: csqc
+               func->next = cmd->userdefined->csqc_functions;
 
                // insert it at the right alphanumeric position
-               for (prev = NULL, current = cmd->functions; current && strcmp(current->name, func->name) < 0; prev = current, current = current->next)
+               for (prev = NULL, current = cmd->userdefined->csqc_functions; current && strcmp(current->name, func->name) < 0; prev = current, current = current->next)
                        ;
                if (prev) {
                        prev->next = func;
                }
                else {
-                       cmd->functions = func;
+                       cmd->userdefined->csqc_functions = func;
                }
                func->next = current;
        }
@@ -1719,7 +1789,11 @@ qboolean Cmd_Exists (cmd_state_t *cmd, const char *cmd_name)
 {
        cmd_function_t  *func;
 
-       for (func=cmd->functions ; func ; func=func->next)
+       for (func = cmd->userdefined->csqc_functions; func; func = func->next)
+               if (!strcmp(cmd_name, func->name))
+                       return true;
+
+       for (func=cmd->engine_functions ; func ; func=func->next)
                if (!strcmp (cmd_name,func->name))
                        return true;
 
@@ -1743,7 +1817,11 @@ const char *Cmd_CompleteCommand (cmd_state_t *cmd, const char *partial)
                return NULL;
 
 // check functions
-       for (func = cmd->functions; func; func = func->next)
+       for (func = cmd->userdefined->csqc_functions; func; func = func->next)
+               if (!strncasecmp(partial, func->name, len))
+                       return func->name;
+
+       for (func = cmd->engine_functions; func; func = func->next)
                if (!strncasecmp(partial, func->name, len))
                        return func->name;
 
@@ -1772,7 +1850,11 @@ 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->functions; func; func = func->next)
+       for (func = cmd->userdefined->csqc_functions; func; func = func->next)
+               if (!strncasecmp(partial, func->name, len))
+                       h++;
+
+       for (func = cmd->engine_functions; func; func = func->next)
                if (!strncasecmp(partial, func->name, len))
                        h++;
 
@@ -1798,8 +1880,11 @@ 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 alias list and print all matches
-       for (func = cmd->functions; func; func = func->next)
+       // Loop through the functions lists and print all matches
+       for (func = cmd->userdefined->csqc_functions; func; func = func->next)
+               if (!strncasecmp(partial, func->name, len))
+                       buf[bpos++] = func->name;
+       for (func = cmd->engine_functions; func; func = func->next)
                if (!strncasecmp(partial, func->name, len))
                        buf[bpos++] = func->name;
 
@@ -1813,7 +1898,10 @@ 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->functions; func; func = func->next)
+       for (func = cmd->userdefined->csqc_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)
                if (!strncasecmp(partial, func->name, len))
                        Con_Printf("^2%s^7: %s\n", func->name, func->description);
 }
@@ -1838,7 +1926,7 @@ const char *Cmd_CompleteAlias (cmd_state_t *cmd, const char *partial)
                return NULL;
 
        // Check functions
-       for (alias = cmd->alias; alias; alias = alias->next)
+       for (alias = cmd->userdefined->alias; alias; alias = alias->next)
                if (!strncasecmp(partial, alias->name, len))
                        return alias->name;
 
@@ -1851,7 +1939,7 @@ void Cmd_CompleteAliasPrint (cmd_state_t *cmd, const char *partial)
        cmdalias_t *alias;
        size_t len = strlen(partial);
        // Loop through the alias list and print all matches
-       for (alias = cmd->alias; alias; alias = alias->next)
+       for (alias = cmd->userdefined->alias; alias; alias = alias->next)
                if (!strncasecmp(partial, alias->name, len))
                        Con_Printf("^5%s^7: %s", alias->name, alias->value);
 }
@@ -1880,7 +1968,7 @@ int Cmd_CompleteAliasCountPossible (cmd_state_t *cmd, const char *partial)
                return 0;
 
        // Loop through the command list and count all partial matches
-       for (alias = cmd->alias; alias; alias = alias->next)
+       for (alias = cmd->userdefined->alias; alias; alias = alias->next)
                if (!strncasecmp(partial, alias->name, len))
                        h++;
 
@@ -1907,7 +1995,7 @@ const char **Cmd_CompleteAliasBuildList (cmd_state_t *cmd, const char *partial)
        len = strlen(partial);
        buf = (const char **)Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
        // Loop through the alias list and print all matches
-       for (alias = cmd->alias; alias; alias = alias->next)
+       for (alias = cmd->userdefined->alias; alias; alias = alias->next)
                if (!strncasecmp(partial, alias->name, len))
                        buf[bpos++] = alias->name;
 
@@ -1915,11 +2003,18 @@ const char **Cmd_CompleteAliasBuildList (cmd_state_t *cmd, const char *partial)
        return buf;
 }
 
-void Cmd_ClearCsqcFuncs (cmd_state_t *cmd)
+// TODO: Make this more generic?
+void Cmd_ClearCSQCCommands (cmd_state_t *cmd)
 {
        cmd_function_t *func;
-       for (func = cmd->functions; func; func = func->next)
-               func->csqcfunc = false;
+       cmd_function_t **next = &cmd->userdefined->csqc_functions;
+       
+       while(*next)
+       {
+               func = *next;
+               *next = func->next;
+               Z_Free(func);
+       }
 }
 
 /*
@@ -1930,6 +2025,8 @@ A complete command line has been parsed, so try to execute it
 FIXME: lookupnoadd the token to speed search?
 ============
 */
+extern hook_t *csqc_concmd;
+
 void Cmd_ExecuteString (cmd_state_t *cmd, const char *text, cmd_source_t src, qboolean lockmutex)
 {
        int oldpos;
@@ -1947,12 +2044,20 @@ void Cmd_ExecuteString (cmd_state_t *cmd, const char *text, cmd_source_t src, qb
                goto done; // no tokens
 
 // check functions
-       for (func = cmd->functions; func; func=func->next)
+       for (func = cmd->userdefined->csqc_functions; func; func = func->next)
        {
-               if (!strcasecmp (cmd->argv[0], func->name))
+               if (!strcasecmp(cmd->argv[0], func->name))
                {
-                       if (func->csqcfunc && CL_VM_ConsoleCommand (text))      //[515]: csqc
+                       if (func->csqcfunc && Hook_Call(csqc_concmd, text)->bval)       //[515]: csqc
                                goto done;
+                       break;
+               }
+       }
+
+       for (func = cmd->engine_functions; func; func=func->next)
+       {
+               if (!strcasecmp (cmd->argv[0], func->name))
+               {
                        switch (src)
                        {
                        case src_command:
@@ -1975,12 +2080,12 @@ void Cmd_ExecuteString (cmd_state_t *cmd, const char *text, cmd_source_t src, qb
        // 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);
+               Con_Printf("Client \"%s\" tried to execute \"%s\"\n", host_client->name, text);
                goto done;
        }
 
 // check alias
-       for (a=cmd->alias ; a ; a=a->next)
+       for (a=cmd->userdefined->alias ; a ; a=a->next)
        {
                if (!strcasecmp (cmd->argv[0], a->name))
                {
@@ -1990,9 +2095,13 @@ void Cmd_ExecuteString (cmd_state_t *cmd, const char *text, cmd_source_t src, qb
        }
 
 // check cvars
-       if (!Cvar_Command(cmd) && host_framecount > 0)
-               Con_Printf("Unknown command \"%s\"\n", Cmd_Argv(cmd, 0));
-
+       if (!Cvar_Command(cmd) && host_framecount > 0) {
+               if (cmd == &cmd_clientfromserver) {
+                       Con_Printf("Server tried to execute \"%s\"\n", Cmd_Argv(cmd, 0));
+               } else {
+                       Con_Printf("Unknown command \"%s\"\n", Cmd_Argv(cmd, 0));
+               }
+       }
 done:
        cmd->tokenizebufferpos = oldpos;
        if (lockmutex)
@@ -2191,9 +2300,11 @@ void Cmd_SaveInitState(void)
                cmd_state_t *cmd = cmd_iter->cmd;
                cmd_function_t *f;
                cmdalias_t *a;
-               for (f = cmd->functions; f; f = f->next)
+               for (f = cmd->userdefined->csqc_functions; f; f = f->next)
+                       f->initstate = true;
+               for (f = cmd->engine_functions; f; f = f->next)
                        f->initstate = true;
-               for (a = cmd->alias; a; a = a->next)
+               for (a = cmd->userdefined->alias; a; a = a->next)
                {
                        a->initstate = true;
                        a->initialvalue = Mem_strdup(zonemempool, a->value);
@@ -2210,7 +2321,19 @@ void Cmd_RestoreInitState(void)
                cmd_state_t *cmd = cmd_iter->cmd;
                cmd_function_t *f, **fp;
                cmdalias_t *a, **ap;
-               for (fp = &cmd->functions; (f = *fp);)
+               for (fp = &cmd->userdefined->csqc_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 (fp = &cmd->engine_functions; (f = *fp);)
                {
                        if (f->initstate)
                                fp = &f->next;
@@ -2222,7 +2345,7 @@ void Cmd_RestoreInitState(void)
                                Z_Free(f);
                        }
                }
-               for (ap = &cmd->alias; (a = *ap);)
+               for (ap = &cmd->userdefined->alias; (a = *ap);)
                {
                        if (a->initstate)
                        {