diff --git a/core/CConCmdManager.cpp b/core/CConCmdManager.cpp index b0586dfc..20858ed6 100644 --- a/core/CConCmdManager.cpp +++ b/core/CConCmdManager.cpp @@ -40,6 +40,7 @@ void CConCmdManager::OnSourceModAllInitialized() void CConCmdManager::OnSourceModShutdown() { + /* All commands should already be removed by the time we're done */ SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false); g_RootMenu.RemoveRootConsoleCommand("cmds", this); g_PluginSys.RemovePluginsListener(this); @@ -53,6 +54,7 @@ void CConCmdManager::OnPluginLoaded(IPlugin *plugin) void CConCmdManager::OnPluginDestroyed(IPlugin *plugin) { CmdList *pList; + List removed; if (plugin->GetProperty("CommandList", (void **)&pList, true)) { CmdList::iterator iter; @@ -65,14 +67,25 @@ void CConCmdManager::OnPluginDestroyed(IPlugin *plugin) || cmd.type == Cmd_Console) { ConCmdInfo *pInfo = cmd.pInfo; + /* See if this is being removed */ + if (removed.find(pInfo) != removed.end()) + { + continue; + } /* See if there are still hooks */ if (pInfo->srvhooks && pInfo->srvhooks->GetFunctionCount()) { continue; } + if (pInfo->conhooks + && pInfo->conhooks->GetFunctionCount()) + { + continue; + } /* Remove the command */ RemoveConCmd(pInfo); + removed.push_back(pInfo); } } delete pList; @@ -89,12 +102,39 @@ void CConCmdManager::SetCommandClient(int client) m_CmdClient = client + 1; } +ResultType CConCmdManager::DispatchClientCommand(int client, ResultType type) +{ + const char *cmd = engine->Cmd_Argv(0); + int args = engine->Cmd_Argc() - 1; + + ConCmdInfo *pInfo; + if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo)) + { + cell_t result = type; + if (pInfo->conhooks && pInfo->conhooks->GetFunctionCount()) + { + pInfo->conhooks->PushCell(client); + pInfo->conhooks->PushCell(args); + pInfo->conhooks->Execute(&result); + } + if (result >= Pl_Stop) + { + return Pl_Stop; + } + type = (ResultType)result; + } + + return type; +} + void CConCmdManager::InternalDispatch() { - if (m_CmdClient) - { - return; - } + /** + * Note: Console commands will EITHER go through IServerGameDLL::ClientCommand, + * OR this dispatch. They will NEVER go through both. + * -- + * Whether or not it goes through the callback is determined by FCVAR_GAMEDLL + */ const char *cmd = engine->Cmd_Argv(0); ConCmdInfo *pInfo; @@ -104,9 +144,33 @@ void CConCmdManager::InternalDispatch() } cell_t result = Pl_Continue; - if (pInfo->srvhooks) + int args = engine->Cmd_Argc() - 1; + + if (m_CmdClient == 0) { - pInfo->srvhooks->Execute(&result); + if (pInfo->srvhooks && pInfo->srvhooks->GetFunctionCount()) + { + pInfo->srvhooks->PushCell(args); + pInfo->srvhooks->Execute(&result); + } + + /* Check if there's an early stop */ + if (result >= Pl_Stop) + { + if (!pInfo->sourceMod) + { + RETURN_META(MRES_SUPERCEDE); + } + return; + } + } + + /* Execute console command hooks */ + if (pInfo->conhooks && pInfo->conhooks->GetFunctionCount()) + { + pInfo->conhooks->PushCell(m_CmdClient); + pInfo->conhooks->PushCell(args); + pInfo->conhooks->Execute(&result); } if (result >= Pl_Handled) @@ -119,6 +183,34 @@ void CConCmdManager::InternalDispatch() } } +void CConCmdManager::AddConsoleCommand(IPluginFunction *pFunction, + const char *name, + const char *description, + int flags) +{ + ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags); + + if (!pInfo->conhooks) + { + pInfo->conhooks = g_Forwards.CreateForwardEx(NULL, ET_Hook, 2, NULL, Param_Cell, Param_Cell); + } + + pInfo->conhooks->AddFunction(pFunction); + + /* Add to the plugin */ + CmdList *pList; + IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext()); + if (!pPlugin->GetProperty("CommandList", (void **)&pList)) + { + pList = new CmdList(); + pPlugin->SetProperty("CommandList", pList); + } + PlCmdInfo info; + info.pInfo = pInfo; + info.type = Cmd_Console; + pList->push_back(info); +} + void CConCmdManager::AddServerCommand(IPluginFunction *pFunction, const char *name, const char *description, @@ -144,7 +236,7 @@ void CConCmdManager::AddServerCommand(IPluginFunction *pFunction, } PlCmdInfo info; info.pInfo = pInfo; - info.type = Cmd_Console; + info.type = Cmd_Server; pList->push_back(info); } @@ -310,8 +402,7 @@ void CConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argc } name = cmd.pInfo->pCmd->GetName(); help = cmd.pInfo->pCmd->GetHelpText(); - g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help); - + g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help); } return; diff --git a/core/CConCmdManager.h b/core/CConCmdManager.h index 247b3a64..d924483c 100644 --- a/core/CConCmdManager.h +++ b/core/CConCmdManager.h @@ -38,10 +38,12 @@ struct ConCmdInfo sourceMod = false; pCmd = NULL; srvhooks = NULL; + conhooks = NULL; } bool sourceMod; /**< Determines whether or not concmd was created by a SourceMod plugin */ ConCommand *pCmd; /**< Pointer to the command itself */ IChangeableForward *srvhooks; /**< Hooks on this name as a server command */ + IChangeableForward *conhooks; /**< Hooks on this name as a console command */ }; class CConCmdManager : @@ -63,6 +65,8 @@ public: //IRootConsoleCommand void OnRootConsoleCommand(const char *command, unsigned int argcount); public: void AddServerCommand(IPluginFunction *pFunction, const char *name, const char *description, int flags); + void AddConsoleCommand(IPluginFunction *pFunction, const char *name, const char *description, int flags); + ResultType DispatchClientCommand(int client, ResultType type); private: void InternalDispatch(); ConCmdInfo *AddOrFindCommand(const char *name, const char *description, int flags); diff --git a/core/CPlayerManager.cpp b/core/CPlayerManager.cpp index 69f70bd9..91c74fb2 100644 --- a/core/CPlayerManager.cpp +++ b/core/CPlayerManager.cpp @@ -15,6 +15,7 @@ #include "ForwardSys.h" #include "ShareSys.h" #include "AdminCache.h" +#include "CConCmdManager.h" CPlayerManager g_Players; @@ -51,15 +52,14 @@ void CPlayerManager::OnSourceModAllInitialized() ParamType p1[] = {Param_Cell, Param_String, Param_Cell}; ParamType p2[] = {Param_Cell}; - ParamType p3[] = {Param_Cell, Param_String}; m_clconnect = g_Forwards.CreateForward("OnClientConnect", ET_Event, 3, p1); m_clputinserver = g_Forwards.CreateForward("OnClientPutInServer", ET_Ignore, 1, p2); m_cldisconnect = g_Forwards.CreateForward("OnClientDisconnect", ET_Ignore, 1, p2); m_cldisconnect_post = g_Forwards.CreateForward("OnClientDisconnect_Post", ET_Ignore, 1, p2); - m_clcommand = g_Forwards.CreateForward("OnClientCommand", ET_Hook, 1, p2); + m_clcommand = g_Forwards.CreateForward("OnClientCommand", ET_Hook, 2, NULL, Param_Cell, Param_Cell); m_clinfochanged = g_Forwards.CreateForward("OnClientSettingsChanged", ET_Ignore, 1, p2); - m_clauth = g_Forwards.CreateForward("OnClientAuthorized", ET_Ignore, 2, p3); + m_clauth = g_Forwards.CreateForward("OnClientAuthorized", ET_Ignore, 2, NULL, Param_Cell, Param_String); } void CPlayerManager::OnSourceModShutdown() @@ -197,6 +197,7 @@ bool CPlayerManager::OnClientConnect(edict_t *pEntity, const char *pszName, cons g_SourceMod.SetAuthChecking(true); } + //:todo: this must meta return return (res) ? true : false; } @@ -328,10 +329,26 @@ void CPlayerManager::OnClientDisconnect_Post(edict_t *pEntity) void CPlayerManager::OnClientCommand(edict_t *pEntity) { - cell_t res; + cell_t res = Pl_Continue; + int client = engine->IndexOfEdict(pEntity); - m_clcommand->PushCell(engine->IndexOfEdict(pEntity)); + int args = engine->Cmd_Argc() - 1; + + m_clcommand->PushCell(client); + m_clcommand->PushCell(args); m_clcommand->Execute(&res, NULL); + + if (res >= Pl_Stop) + { + RETURN_META(MRES_SUPERCEDE); + } + + res = g_ConCmds.DispatchClientCommand(client, (ResultType)res); + + if (res >= Pl_Handled) + { + RETURN_META(MRES_SUPERCEDE); + } } void CPlayerManager::OnClientSettingsChanged(edict_t *pEntity) diff --git a/core/smn_console.cpp b/core/smn_console.cpp index 4a9275a7..3651e4bf 100644 --- a/core/smn_console.cpp +++ b/core/smn_console.cpp @@ -342,6 +342,25 @@ static cell_t sm_RegServerCmd(IPluginContext *pContext, const cell_t *params) return 1; } +static cell_t sm_RegConsoleCmd(IPluginContext *pContext, const cell_t *params) +{ + char *name,*help; + IPluginFunction *pFunction; + + pContext->LocalToString(params[1], &name); + pContext->LocalToString(params[3], &help); + pFunction = pContext->GetFunctionById(params[2]); + + if (!pFunction) + { + return pContext->ThrowNativeError("Invalid function id (%X)", params[2]); + } + + g_ConCmds.AddConsoleCommand(pFunction, name, help, params[4]); + + return 1; +} + REGISTER_NATIVES(convarNatives) { {"CreateConVar", sm_CreateConVar}, @@ -363,5 +382,6 @@ REGISTER_NATIVES(convarNatives) {"GetConVarMax", sm_GetConVarMax}, {"ResetConVar", sm_ResetConVar}, {"RegServerCmd", sm_RegServerCmd}, + {"RegConsoleCmd", sm_RegConsoleCmd}, {NULL, NULL} }; diff --git a/plugins/include/console.inc b/plugins/include/console.inc index 1bacbcf6..84125f50 100644 --- a/plugins/include/console.inc +++ b/plugins/include/console.inc @@ -79,7 +79,7 @@ native PrintToConsole(client, const String:format[], {Handle,Float,String,_}:... * @return A Result value. Not handling the command * means that Source will report it as "not found." */ -functag SrvCmd Action:public(); +functag SrvCmd Action:public(argCount); /** * Creates a server-only console command, or hooks an already existing one. @@ -92,7 +92,6 @@ functag SrvCmd Action:public(); */ native RegServerCmd(const String:cmd[], SrvCmd:callback, const String:description[]="", flags=0); -#if 0 /** * Called when a generic console command is invoked. * @@ -100,7 +99,7 @@ native RegServerCmd(const String:cmd[], SrvCmd:callback, const String:descriptio * @return A Result value. Not handling the command * means that Source will report it as "not found." */ -functag ConCmd Action:public(client); +functag ConCmd Action:public(client, argCount); /** * Creates a console command, or hooks an already existing one. @@ -113,6 +112,7 @@ functag ConCmd Action:public(client); */ native RegConsoleCmd(const String:cmd[], ConCmd:callback, const String:description[]="", flags=0); +#if 0 /** * Hooks a specific client-only command. * diff --git a/plugins/include/sourcemod.inc b/plugins/include/sourcemod.inc index fc0cb0a0..e0e7ab00 100644 --- a/plugins/include/sourcemod.inc +++ b/plugins/include/sourcemod.inc @@ -128,7 +128,7 @@ forward OnClientDisconnect_Post(client); * @param client Player index. * @noreturn */ -forward OnClientCommand(client); +forward OnClientCommand(client, argCount); /** * Called whenever the client's settings are changed.