dvander@498: #include "CConCmdManager.h" dvander@498: #include "sm_srvcmds.h" dvander@498: dvander@498: CConCmdManager g_ConCmds; dvander@498: dvander@498: SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false); dvander@498: SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int); dvander@498: dvander@498: struct PlCmdInfo dvander@498: { dvander@498: ConCmdInfo *pInfo; dvander@498: CmdType type; dvander@498: }; dvander@498: dvander@498: typedef List CmdList; dvander@498: dvander@498: char *sm_strdup(const char *str) dvander@498: { dvander@498: char *ptr = new char[strlen(str)+1]; dvander@498: strcpy(ptr, str); dvander@498: return ptr; dvander@498: } dvander@498: dvander@498: CConCmdManager::CConCmdManager() dvander@498: { dvander@498: m_pCmds = sm_trie_create(); dvander@498: } dvander@498: dvander@498: CConCmdManager::~CConCmdManager() dvander@498: { dvander@498: sm_trie_destroy(m_pCmds); dvander@498: } dvander@498: dvander@498: void CConCmdManager::OnSourceModAllInitialized() dvander@498: { dvander@498: g_PluginSys.AddPluginsListener(this); dvander@498: g_RootMenu.AddRootConsoleCommand("cmds", "List console commands", this); dvander@498: SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false); dvander@498: } dvander@498: dvander@498: void CConCmdManager::OnSourceModShutdown() dvander@498: { dvander@500: /* All commands should already be removed by the time we're done */ dvander@498: SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false); dvander@498: g_RootMenu.RemoveRootConsoleCommand("cmds", this); dvander@498: g_PluginSys.RemovePluginsListener(this); dvander@498: } dvander@498: dvander@498: void CConCmdManager::OnPluginLoaded(IPlugin *plugin) dvander@498: { dvander@498: /* Nothing yet... */ dvander@498: } dvander@498: dvander@498: void CConCmdManager::OnPluginDestroyed(IPlugin *plugin) dvander@498: { dvander@498: CmdList *pList; dvander@500: List removed; dvander@498: if (plugin->GetProperty("CommandList", (void **)&pList, true)) dvander@498: { dvander@498: CmdList::iterator iter; dvander@498: for (iter=pList->begin(); dvander@498: iter!=pList->end(); dvander@498: iter++) dvander@498: { dvander@498: PlCmdInfo &cmd = (*iter); dvander@498: if (cmd.type == Cmd_Server dvander@498: || cmd.type == Cmd_Console) dvander@498: { dvander@498: ConCmdInfo *pInfo = cmd.pInfo; dvander@500: /* See if this is being removed */ dvander@500: if (removed.find(pInfo) != removed.end()) dvander@500: { dvander@500: continue; dvander@500: } dvander@498: /* See if there are still hooks */ dvander@498: if (pInfo->srvhooks dvander@498: && pInfo->srvhooks->GetFunctionCount()) dvander@498: { dvander@498: continue; dvander@498: } dvander@500: if (pInfo->conhooks dvander@500: && pInfo->conhooks->GetFunctionCount()) dvander@500: { dvander@500: continue; dvander@500: } dvander@498: /* Remove the command */ dvander@498: RemoveConCmd(pInfo); dvander@500: removed.push_back(pInfo); dvander@498: } dvander@498: } dvander@498: delete pList; dvander@498: } dvander@498: } dvander@498: dvander@498: void CommandCallback() dvander@498: { dvander@498: g_ConCmds.InternalDispatch(); dvander@498: } dvander@498: dvander@498: void CConCmdManager::SetCommandClient(int client) dvander@498: { dvander@498: m_CmdClient = client + 1; dvander@498: } dvander@498: dvander@500: ResultType CConCmdManager::DispatchClientCommand(int client, ResultType type) dvander@500: { dvander@500: const char *cmd = engine->Cmd_Argv(0); dvander@500: int args = engine->Cmd_Argc() - 1; dvander@500: dvander@500: ConCmdInfo *pInfo; dvander@500: if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo)) dvander@500: { dvander@500: cell_t result = type; dvander@500: if (pInfo->conhooks && pInfo->conhooks->GetFunctionCount()) dvander@500: { dvander@500: pInfo->conhooks->PushCell(client); dvander@500: pInfo->conhooks->PushCell(args); dvander@500: pInfo->conhooks->Execute(&result); dvander@500: } dvander@500: if (result >= Pl_Stop) dvander@500: { dvander@500: return Pl_Stop; dvander@500: } dvander@500: type = (ResultType)result; dvander@500: } dvander@500: dvander@500: return type; dvander@500: } dvander@500: dvander@498: void CConCmdManager::InternalDispatch() dvander@498: { dvander@500: /** dvander@500: * Note: Console commands will EITHER go through IServerGameDLL::ClientCommand, dvander@500: * OR this dispatch. They will NEVER go through both. dvander@500: * -- dvander@500: * Whether or not it goes through the callback is determined by FCVAR_GAMEDLL dvander@500: */ dvander@498: dvander@498: const char *cmd = engine->Cmd_Argv(0); dvander@498: ConCmdInfo *pInfo; dvander@498: if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo)) dvander@498: { dvander@498: return; dvander@498: } dvander@498: dvander@498: cell_t result = Pl_Continue; dvander@500: int args = engine->Cmd_Argc() - 1; dvander@500: dvander@500: if (m_CmdClient == 0) dvander@498: { dvander@500: if (pInfo->srvhooks && pInfo->srvhooks->GetFunctionCount()) dvander@500: { dvander@500: pInfo->srvhooks->PushCell(args); dvander@500: pInfo->srvhooks->Execute(&result); dvander@500: } dvander@500: dvander@500: /* Check if there's an early stop */ dvander@500: if (result >= Pl_Stop) dvander@500: { dvander@500: if (!pInfo->sourceMod) dvander@500: { dvander@500: RETURN_META(MRES_SUPERCEDE); dvander@500: } dvander@500: return; dvander@500: } dvander@500: } dvander@500: dvander@500: /* Execute console command hooks */ dvander@500: if (pInfo->conhooks && pInfo->conhooks->GetFunctionCount()) dvander@500: { dvander@500: pInfo->conhooks->PushCell(m_CmdClient); dvander@500: pInfo->conhooks->PushCell(args); dvander@500: pInfo->conhooks->Execute(&result); dvander@498: } dvander@498: dvander@498: if (result >= Pl_Handled) dvander@498: { dvander@498: if (!pInfo->sourceMod) dvander@498: { dvander@498: RETURN_META(MRES_SUPERCEDE); dvander@498: } dvander@498: return; dvander@498: } dvander@498: } dvander@498: dvander@500: void CConCmdManager::AddConsoleCommand(IPluginFunction *pFunction, dvander@500: const char *name, dvander@500: const char *description, dvander@500: int flags) dvander@500: { dvander@500: ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags); dvander@500: dvander@500: if (!pInfo->conhooks) dvander@500: { dvander@500: pInfo->conhooks = g_Forwards.CreateForwardEx(NULL, ET_Hook, 2, NULL, Param_Cell, Param_Cell); dvander@500: } dvander@500: dvander@500: pInfo->conhooks->AddFunction(pFunction); dvander@500: dvander@500: /* Add to the plugin */ dvander@500: CmdList *pList; dvander@500: IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext()); dvander@500: if (!pPlugin->GetProperty("CommandList", (void **)&pList)) dvander@500: { dvander@500: pList = new CmdList(); dvander@500: pPlugin->SetProperty("CommandList", pList); dvander@500: } dvander@500: PlCmdInfo info; dvander@500: info.pInfo = pInfo; dvander@500: info.type = Cmd_Console; dvander@500: pList->push_back(info); dvander@500: } dvander@500: dvander@498: void CConCmdManager::AddServerCommand(IPluginFunction *pFunction, dvander@498: const char *name, dvander@498: const char *description, dvander@498: int flags) dvander@498: dvander@498: { dvander@498: ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags); dvander@498: dvander@498: if (!pInfo->srvhooks) dvander@498: { dvander@498: pInfo->srvhooks = g_Forwards.CreateForwardEx(NULL, ET_Hook, 1, NULL, Param_Cell); dvander@498: } dvander@498: dvander@498: pInfo->srvhooks->AddFunction(pFunction); dvander@498: dvander@498: /* Add to the plugin */ dvander@498: CmdList *pList; dvander@498: IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext()); dvander@498: if (!pPlugin->GetProperty("CommandList", (void **)&pList)) dvander@498: { dvander@498: pList = new CmdList(); dvander@498: pPlugin->SetProperty("CommandList", pList); dvander@498: } dvander@498: PlCmdInfo info; dvander@498: info.pInfo = pInfo; dvander@500: info.type = Cmd_Server; dvander@498: pList->push_back(info); dvander@498: } dvander@498: dvander@498: void CConCmdManager::AddToCmdList(ConCmdInfo *info) dvander@498: { dvander@498: List::iterator iter = m_CmdList.begin(); dvander@498: ConCmdInfo *pInfo; dvander@498: bool inserted = false; dvander@498: const char *orig; dvander@498: dvander@498: if (info->pCmd) dvander@498: { dvander@498: orig = info->pCmd->GetName(); dvander@498: } dvander@498: dvander@498: /* Insert this into the help list, SORTED alphabetically. */ dvander@498: while (iter != m_CmdList.end()) dvander@498: { dvander@498: const char *cmd = NULL; dvander@498: pInfo = (*iter); dvander@498: if (pInfo->pCmd) dvander@498: { dvander@498: cmd = pInfo->pCmd->GetName(); dvander@498: } dvander@498: if (strcmp(orig, cmd) < 0) dvander@498: { dvander@498: m_CmdList.insert(iter, info); dvander@498: inserted = true; dvander@498: break; dvander@498: } dvander@498: iter++; dvander@498: } dvander@498: dvander@498: if (!inserted) dvander@498: { dvander@498: m_CmdList.push_back(info); dvander@498: } dvander@498: } dvander@498: dvander@498: void CConCmdManager::RemoveConCmd(ConCmdInfo *info) dvander@498: { dvander@498: /* Remove console-specific information */ dvander@498: if (info->pCmd) dvander@498: { dvander@498: /* Remove from the trie */ dvander@498: sm_trie_delete(m_pCmds, info->pCmd->GetName()); dvander@498: dvander@498: if (info->sourceMod) dvander@498: { dvander@498: /* Unlink from SourceMM */ dvander@498: g_SMAPI->UnregisterConCmdBase(g_PLAPI, info->pCmd); dvander@498: /* Delete the command's memory */ dvander@498: char *new_help = const_cast(info->pCmd->GetHelpText()); dvander@498: char *new_name = const_cast(info->pCmd->GetName()); dvander@498: delete [] new_help; dvander@498: delete [] new_name; dvander@498: delete info->pCmd; dvander@498: } else { dvander@498: /* Remove the external hook */ dvander@498: SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false); dvander@498: } dvander@498: } dvander@498: dvander@498: /* Remove from list */ dvander@498: m_CmdList.remove(info); dvander@498: dvander@498: /* Free forwards */ dvander@498: if (info->srvhooks) dvander@498: { dvander@498: g_Forwards.ReleaseForward(info->srvhooks); dvander@498: } dvander@498: } dvander@498: dvander@498: ConCmdInfo *CConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags) dvander@498: { dvander@498: ConCmdInfo *pInfo; dvander@498: if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo)) dvander@498: { dvander@498: pInfo = new ConCmdInfo(); dvander@498: /* Find the commandopan */ dvander@498: ConCommandBase *pBase = icvar->GetCommands(); dvander@498: ConCommand *pCmd = NULL; dvander@498: while (pBase) dvander@498: { dvander@498: if (pBase->IsCommand() dvander@498: && (strcmp(pBase->GetName(), name) == 0)) dvander@498: { dvander@498: pCmd = (ConCommand *)pBase; dvander@498: break; dvander@498: } dvander@498: pBase = const_cast(pBase->GetNext()); dvander@498: } dvander@498: dvander@498: if (!pCmd) dvander@498: { dvander@498: /* Note that we have to duplicate because the source might not be dvander@498: * a static string, and these expect static memory. dvander@498: */ dvander@498: if (!description) dvander@498: { dvander@498: description = ""; dvander@498: } dvander@498: char *new_name = sm_strdup(name); dvander@498: char *new_help = sm_strdup(description); dvander@498: pCmd = new ConCommand(new_name, CommandCallback, new_help, flags); dvander@498: pInfo->sourceMod = true; dvander@498: } else { dvander@498: SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false); dvander@498: } dvander@498: dvander@498: pInfo->pCmd = pCmd; dvander@498: dvander@498: sm_trie_insert(m_pCmds, name, pInfo); dvander@498: AddToCmdList(pInfo); dvander@498: } dvander@498: dvander@498: return pInfo; dvander@498: } dvander@498: dvander@498: void CConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argcount) dvander@498: { dvander@499: if (argcount >= 3) dvander@499: { dvander@499: const char *text = engine->Cmd_Argv(2); dvander@499: int id = atoi(text); dvander@499: CPlugin *pPlugin = g_PluginSys.GetPluginByOrder(id); dvander@499: dvander@499: if (!pPlugin) dvander@499: { dvander@499: g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id); dvander@499: return; dvander@499: } dvander@499: dvander@499: CmdList *pList; dvander@499: if (!pPlugin->GetProperty("CommandList", (void **)&pList)) dvander@499: { dvander@499: g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename()); dvander@499: return; dvander@499: } dvander@499: if (!pList->size()) dvander@499: { dvander@499: g_RootMenu.ConsolePrint("[SM] No commands found for %s", pPlugin->GetFilename()); dvander@499: return; dvander@499: } dvander@499: dvander@499: CmdList::iterator iter; dvander@499: const char *type = NULL; dvander@499: const char *name; dvander@499: const char *help; dvander@499: g_RootMenu.ConsolePrint(" %-17.16s %-8.7s %s", "[Name]", "[Type]", "[Help]"); dvander@499: for (iter=pList->begin(); dvander@499: iter!=pList->end(); dvander@499: iter++) dvander@499: { dvander@499: PlCmdInfo &cmd = (*iter); dvander@499: if (cmd.type == Cmd_Server) dvander@499: { dvander@499: type = "server"; dvander@499: } else if (cmd.type == Cmd_Console) { dvander@499: type = "console"; dvander@499: } else if (cmd.type == Cmd_Client) { dvander@499: type = "client"; dvander@499: } dvander@499: name = cmd.pInfo->pCmd->GetName(); dvander@499: help = cmd.pInfo->pCmd->GetHelpText(); dvander@500: g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help); dvander@499: } dvander@499: dvander@499: return; dvander@499: } dvander@499: dvander@499: g_RootMenu.ConsolePrint("[SM] Usage: sm cmds "); dvander@498: }