ds@1952: /** ds@1952: * vim: set ts=4 : ds@1952: * ============================================================================= ds@1952: * SourceMod ds@1952: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. ds@1952: * ============================================================================= ds@1952: * ds@1952: * This program is free software; you can redistribute it and/or modify it under ds@1952: * the terms of the GNU General Public License, version 3.0, as published by the ds@1952: * Free Software Foundation. ds@1952: * ds@1952: * This program is distributed in the hope that it will be useful, but WITHOUT ds@1952: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ds@1952: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ds@1952: * details. ds@1952: * ds@1952: * You should have received a copy of the GNU General Public License along with ds@1952: * this program. If not, see . ds@1952: * ds@1952: * As a special exception, AlliedModders LLC gives you permission to link the ds@1952: * code of this program (as well as its derivative works) to "Half-Life 2," the ds@1952: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software ds@1952: * by the Valve Corporation. You must obey the GNU General Public License in ds@1952: * all respects for all other code used. Additionally, AlliedModders LLC grants ds@1952: * this exception to all derivative works. AlliedModders LLC defines further ds@1952: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), ds@1952: * or . ds@1952: * ds@1952: * Version: $Id$ ds@1952: */ ds@1952: ds@1952: #include "ConCmdManager.h" ds@1952: #include "sm_srvcmds.h" ds@1952: #include "AdminCache.h" ds@1952: #include "sm_stringutil.h" ds@1952: #include "PlayerManager.h" ds@1952: #include "Translator.h" ds@1952: #include "HalfLife2.h" ds@1952: #include "ChatTriggers.h" ds@1952: ds@1952: ConCmdManager g_ConCmds; ds@1952: ds@1952: #if defined ORANGEBOX_BUILD ds@1952: SH_DECL_HOOK1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &); ds@1952: #else ds@1952: SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false); ds@1952: #endif ds@1952: ds@1952: SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int); ds@1952: ds@1952: struct PlCmdInfo ds@1952: { ds@1952: ConCmdInfo *pInfo; ds@1952: CmdHook *pHook; ds@1952: CmdType type; ds@1952: }; ds@1952: typedef List CmdList; ds@1952: ds@1952: void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info); ds@1952: ds@1952: ConCmdManager::ConCmdManager() : m_Strings(1024) ds@1952: { ds@1952: m_pCmds = sm_trie_create(); ds@1952: m_pCmdGrps = sm_trie_create(); ds@1952: m_CmdClient = 0; ds@1952: } ds@1952: ds@1952: ConCmdManager::~ConCmdManager() ds@1952: { ds@1952: sm_trie_destroy(m_pCmds); ds@1952: sm_trie_destroy(m_pCmdGrps); ds@1952: } ds@1952: ds@1952: void ConCmdManager::OnSourceModAllInitialized() ds@1952: { ds@1952: g_PluginSys.AddPluginsListener(this); ds@1952: g_RootMenu.AddRootConsoleCommand("cmds", "List console commands", this); ds@1952: SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false); ds@1952: } ds@1952: ds@1952: void ConCmdManager::OnSourceModShutdown() ds@1952: { ds@1952: /* All commands should already be removed by the time we're done */ ds@1952: SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false); ds@1952: g_RootMenu.RemoveRootConsoleCommand("cmds", this); ds@1952: } ds@1952: ds@1952: void ConCmdManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe) ds@1952: { ds@1952: /* Whoa, first get its information struct */ ds@1952: ConCmdInfo *pInfo; ds@1952: ds@1952: if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo)) ds@1952: { ds@1952: return; ds@1952: } ds@1952: ds@1952: RemoveConCmds(pInfo->srvhooks); ds@1952: RemoveConCmds(pInfo->conhooks); ds@1952: ds@1952: RemoveConCmd(pInfo, name, is_read_safe, false); ds@1952: } ds@1952: ds@1952: void ConCmdManager::RemoveConCmds(List &cmdlist) ds@1952: { ds@1952: List::iterator iter = cmdlist.begin(); ds@1952: ds@1952: while (iter != cmdlist.end()) ds@1952: { ds@1952: CmdHook *pHook = (*iter); ds@1952: IPluginContext *pContext = pHook->pf->GetParentContext(); ds@1952: IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext()); ds@1952: CmdList *pList = NULL; ds@1952: ds@1952: //gaben ds@1952: if (!pPlugin->GetProperty("CommandList", (void **)&pList, false) || !pList) ds@1952: { ds@1952: continue; ds@1952: } ds@1952: ds@1952: CmdList::iterator p_iter = pList->begin(); ds@1952: while (p_iter != pList->end()) ds@1952: { ds@1952: PlCmdInfo &cmd = (*p_iter); ds@1952: if (cmd.pHook == pHook) ds@1952: { ds@1952: p_iter = pList->erase(p_iter); ds@1952: } ds@1952: else ds@1952: { ds@1952: p_iter++; ds@1952: } ds@1952: } ds@1952: ds@1952: delete pHook->pAdmin; ds@1952: delete pHook; ds@1952: ds@1952: iter = cmdlist.erase(iter); ds@1952: } ds@1952: } ds@1952: ds@1952: void ConCmdManager::RemoveConCmds(List &cmdlist, IPluginContext *pContext) ds@1952: { ds@1952: List::iterator iter = cmdlist.begin(); ds@1952: CmdHook *pHook; ds@1952: ds@1952: while (iter != cmdlist.end()) ds@1952: { ds@1952: pHook = (*iter); ds@1952: if (pHook->pf->GetParentContext() == pContext) ds@1952: { ds@1952: delete pHook->pAdmin; ds@1952: delete pHook; ds@1952: iter = cmdlist.erase(iter); ds@1952: } ds@1952: else ds@1952: { ds@1952: iter++; ds@1952: } ds@1952: } ds@1952: } ds@1952: ds@1952: void ConCmdManager::OnPluginDestroyed(IPlugin *plugin) ds@1952: { ds@1952: CmdList *pList; ds@1952: List removed; ds@1952: if (plugin->GetProperty("CommandList", (void **)&pList, true)) ds@1952: { ds@1952: IPluginContext *pContext = plugin->GetBaseContext(); ds@1952: CmdList::iterator iter; ds@1952: /* First pass! ds@1952: * Only bother if there's an actual command list on this plugin... ds@1952: */ ds@1952: for (iter=pList->begin(); ds@1952: iter!=pList->end(); ds@1952: iter++) ds@1952: { ds@1952: PlCmdInfo &cmd = (*iter); ds@1952: ConCmdInfo *pInfo = cmd.pInfo; ds@1952: ds@1952: /* Has this chain already been fully cleaned/removed? */ ds@1952: if (removed.find(pInfo) != removed.end()) ds@1952: { ds@1952: continue; ds@1952: } ds@1952: ds@1952: /* Remove any hooks from us on this command */ ds@1952: RemoveConCmds(pInfo->conhooks, pContext); ds@1952: RemoveConCmds(pInfo->srvhooks, pContext); ds@1952: ds@1952: /* See if there are still hooks */ ds@1952: if (pInfo->srvhooks.size()) ds@1952: { ds@1952: continue; ds@1952: } ds@1952: if (pInfo->conhooks.size()) ds@1952: { ds@1952: continue; ds@1952: } ds@1952: ds@1952: /* Remove the command, it should be safe now */ ds@1952: RemoveConCmd(pInfo, pInfo->pCmd->GetName(), true, true); ds@1952: removed.push_back(pInfo); ds@1952: } ds@1952: delete pList; ds@1952: } ds@1952: } ds@1952: #if defined ORANGEBOX_BUILD ds@1952: void CommandCallback(const CCommand &command) ds@1952: { ds@1952: #else ds@1952: void CommandCallback() ds@1952: { ds@1952: CCommand command; ds@1952: #endif ds@1952: ds@1952: g_HL2.PushCommandStack(&command); ds@1952: ds@1952: g_ConCmds.InternalDispatch(command); ds@1952: ds@1952: g_HL2.PopCommandStack(); ds@1952: } ds@1952: ds@1952: void ConCmdManager::SetCommandClient(int client) ds@1952: { ds@1952: m_CmdClient = client + 1; ds@1952: } ds@1952: ds@1952: ResultType ConCmdManager::DispatchClientCommand(int client, const char *cmd, int args, ResultType type) ds@1952: { ds@1952: ConCmdInfo *pInfo; ds@1952: if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo)) ds@1952: { ds@1952: cell_t result = type; ds@1952: cell_t tempres = result; ds@1952: List::iterator iter; ds@1952: CmdHook *pHook; ds@1952: for (iter=pInfo->conhooks.begin(); ds@1952: iter!=pInfo->conhooks.end(); ds@1952: iter++) ds@1952: { ds@1952: pHook = (*iter); ds@1952: if (!pHook->pf->IsRunnable()) ds@1952: { ds@1952: continue; ds@1952: } ds@1952: if (pHook->pAdmin && !CheckAccess(client, cmd, pHook->pAdmin)) ds@1952: { ds@1952: if (result < Pl_Handled) ds@1952: { ds@1952: result = Pl_Handled; ds@1952: } ds@1952: continue; ds@1952: } ds@1952: pHook->pf->PushCell(client); ds@1952: pHook->pf->PushCell(args); ds@1952: if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE) ds@1952: { ds@1952: if (tempres > result) ds@1952: { ds@1952: result = tempres; ds@1952: } ds@1952: if (result == Pl_Stop) ds@1952: { ds@1952: break; ds@1952: } ds@1952: } ds@1952: } ds@1952: type = (ResultType)result; ds@1952: } ds@1952: ds@1952: return type; ds@1952: } ds@1952: ds@1952: void ConCmdManager::InternalDispatch(const CCommand &command) ds@1952: { ds@1952: int client = m_CmdClient; ds@1952: ds@1952: if (client) ds@1952: { ds@1952: CPlayer *pPlayer = g_Players.GetPlayerByIndex(client); ds@1952: if (!pPlayer || !pPlayer->IsConnected()) ds@1952: { ds@1952: return; ds@1952: } ds@1952: } ds@1952: ds@1952: /** ds@1952: * Note: Console commands will EITHER go through IServerGameDLL::ClientCommand, ds@1952: * OR this dispatch. They will NEVER go through both. ds@1952: * -- ds@1952: * Whether or not it goes through the callback is determined by FCVAR_GAMEDLL ds@1952: */ ds@1952: const char *cmd = g_HL2.CurrentCommandName(); ds@1952: ds@1952: ConCmdInfo *pInfo; ds@1952: if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo)) ds@1952: { ds@1952: return; ds@1952: } ds@1952: ds@1952: /* This is a hack to prevent say triggers from firing on messages that were ds@1952: * blocked because of flooding. We won't remove this, but the hack will get ds@1952: * "nicer" when we expose explicit say hooks. ds@1952: */ ds@1952: if (g_ChatTriggers.WasFloodedMessage()) ds@1952: { ds@1952: return; ds@1952: } ds@1952: ds@1952: cell_t result = Pl_Continue; ds@1952: int args = command.ArgC() - 1; ds@1952: ds@1952: List::iterator iter; ds@1952: CmdHook *pHook; ds@1952: ds@1952: /* Execute server-only commands if viable */ ds@1952: if (client == 0 && pInfo->srvhooks.size()) ds@1952: { ds@1952: cell_t tempres = result; ds@1952: for (iter=pInfo->srvhooks.begin(); ds@1952: iter!=pInfo->srvhooks.end(); ds@1952: iter++) ds@1952: { ds@1952: pHook = (*iter); ds@1952: if (!pHook->pf->IsRunnable()) ds@1952: { ds@1952: continue; ds@1952: } ds@1952: pHook->pf->PushCell(args); ds@1952: if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE) ds@1952: { ds@1952: if (tempres > result) ds@1952: { ds@1952: result = tempres; ds@1952: } ds@1952: if (result == Pl_Stop) ds@1952: { ds@1952: break; ds@1952: } ds@1952: } ds@1952: } ds@1952: ds@1952: /* Check if there's an early stop */ ds@1952: if (result >= Pl_Stop) ds@1952: { ds@1952: if (!pInfo->sourceMod) ds@1952: { ds@1952: RETURN_META(MRES_SUPERCEDE); ds@1952: } ds@1952: return; ds@1952: } ds@1952: } ds@1952: ds@1952: /* Execute console commands */ ds@1952: if (pInfo->conhooks.size()) ds@1952: { ds@1952: cell_t tempres = result; ds@1952: for (iter=pInfo->conhooks.begin(); ds@1952: iter!=pInfo->conhooks.end(); ds@1952: iter++) ds@1952: { ds@1952: pHook = (*iter); ds@1952: if (!pHook->pf->IsRunnable()) ds@1952: { ds@1952: continue; ds@1952: } ds@1952: if (client ds@1952: && pHook->pAdmin ds@1952: && !CheckAccess(client, cmd, pHook->pAdmin)) ds@1952: { ds@1952: if (result < Pl_Handled) ds@1952: { ds@1952: result = Pl_Handled; ds@1952: } ds@1952: continue; ds@1952: } ds@1952: ds@1952: /* On a listen server, sometimes the server host's client index can be set as 0. ds@1952: * So index 1 is passed to the command callback to correct this potential problem. ds@1952: */ dvander@1996: if (!engine->IsDedicatedServer()) ds@1952: { dvander@1996: client = g_Players.ListenClient(); ds@1952: } ds@1952: dvander@1996: pHook->pf->PushCell(client); ds@1952: pHook->pf->PushCell(args); dvander@1996: ds@1952: if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE) ds@1952: { ds@1952: if (tempres > result) ds@1952: { ds@1952: result = tempres; ds@1952: } ds@1952: if (result == Pl_Stop) ds@1952: { ds@1952: break; ds@1952: } ds@1952: } ds@1952: } ds@1952: } ds@1952: ds@1952: if (result >= Pl_Handled) ds@1952: { ds@1952: if (!pInfo->sourceMod) ds@1952: { ds@1952: RETURN_META(MRES_SUPERCEDE); ds@1952: } ds@1952: return; ds@1952: } ds@1952: } ds@1952: ds@1952: bool ConCmdManager::CheckCommandAccess(int client, const char *cmd, FlagBits cmdflags) ds@1952: { ds@1952: if (cmdflags == 0 || client == 0) ds@1952: { ds@1952: return true; ds@1952: } ds@1952: ds@1952: /* If running listen server, then client 1 is the server host and should have 'root' access */ ds@1952: if (client == 1 && !engine->IsDedicatedServer()) ds@1952: { ds@1952: return true; ds@1952: } ds@1952: ds@1952: CPlayer *player = g_Players.GetPlayerByIndex(client); ds@1952: if (!player ds@1952: || player->GetEdict() == NULL ds@1952: || player->IsFakeClient()) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: AdminId adm = player->GetAdminId(); ds@1952: if (adm != INVALID_ADMIN_ID) ds@1952: { ds@1952: FlagBits bits = g_Admins.GetAdminFlags(adm, Access_Effective); ds@1952: ds@1952: /* root knows all, WHOA */ ds@1952: if ((bits & ADMFLAG_ROOT) == ADMFLAG_ROOT) ds@1952: { ds@1952: return true; ds@1952: } ds@1952: ds@1952: /* Check for overrides ds@1952: * :TODO: is it worth optimizing this? ds@1952: */ ds@1952: unsigned int groups = g_Admins.GetAdminGroupCount(adm); ds@1952: GroupId gid; ds@1952: OverrideRule rule; ds@1952: bool override = false; ds@1952: for (unsigned int i=0; ieflags)) ds@1952: { ds@1952: return true; ds@1952: } ds@1952: ds@1952: edict_t *pEdict = engine->PEntityOfEntIndex(client); ds@1952: ds@1952: /* If we got here, the command failed... */ ds@1952: char buffer[128]; dvander@2055: if (!CoreTranslate(buffer, sizeof(buffer), "%T", 2, NULL, "No Access", &client)) ds@1952: { ds@1952: UTIL_Format(buffer, sizeof(buffer), "You do not have access to this command"); ds@1952: } ds@1952: ds@1952: unsigned int replyto = g_ChatTriggers.GetReplyTo(); ds@1952: if (replyto == SM_REPLY_CONSOLE) ds@1952: { ds@1952: char fullbuffer[192]; ds@1952: UTIL_Format(fullbuffer, sizeof(fullbuffer), "[SM] %s.\n", buffer); ds@1952: engine->ClientPrintf(pEdict, fullbuffer); ds@1952: } ds@1952: else if (replyto == SM_REPLY_CHAT) ds@1952: { ds@1952: char fullbuffer[192]; ds@1952: UTIL_Format(fullbuffer, sizeof(fullbuffer), "[SM] %s.", buffer); ds@1952: g_HL2.TextMsg(client, HUD_PRINTTALK, fullbuffer); ds@1952: } ds@1952: ds@1952: return false; ds@1952: } ds@1952: ds@1952: bool ConCmdManager::AddConsoleCommand(IPluginFunction *pFunction, ds@1952: const char *name, ds@1952: const char *description, ds@1952: int flags) ds@1952: { ds@1952: ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags); ds@1952: ds@1952: if (!pInfo) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: CmdHook *pHook = new CmdHook(); ds@1952: ds@1952: pHook->pf = pFunction; ds@1952: if (description && description[0]) ds@1952: { ds@1952: pHook->helptext.assign(description); ds@1952: } ds@1952: pInfo->conhooks.push_back(pHook); ds@1952: ds@1952: /* Add to the plugin */ ds@1952: CmdList *pList; ds@1952: IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext()); ds@1952: if (!pPlugin->GetProperty("CommandList", (void **)&pList)) ds@1952: { ds@1952: pList = new CmdList(); ds@1952: pPlugin->SetProperty("CommandList", pList); ds@1952: } ds@1952: PlCmdInfo info; ds@1952: info.pInfo = pInfo; ds@1952: info.type = Cmd_Console; ds@1952: info.pHook = pHook; ds@1952: AddToPlCmdList(pList, info); ds@1952: ds@1952: return true; ds@1952: } ds@1952: ds@1952: bool ConCmdManager::AddAdminCommand(IPluginFunction *pFunction, ds@1952: const char *name, ds@1952: const char *group, ds@1952: int adminflags, ds@1952: const char *description, ds@1952: int flags) ds@1952: { ds@1952: ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags); ds@1952: ds@1952: if (!pInfo) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: CmdHook *pHook = new CmdHook(); ds@1952: AdminCmdInfo *pAdmin = new AdminCmdInfo(); ds@1952: ds@1952: pHook->pf = pFunction; ds@1952: if (description && description[0]) ds@1952: { ds@1952: pHook->helptext.assign(description); ds@1952: } ds@1952: pHook->pAdmin = pAdmin; ds@1952: ds@1952: void *object; ds@1952: int grpid; ds@1952: if (!sm_trie_retrieve(m_pCmdGrps, group, (void **)&object)) ds@1952: { ds@1952: grpid = m_Strings.AddString(group); ds@1952: sm_trie_insert(m_pCmdGrps, group, (void *)grpid); ds@1952: } ds@1952: else ds@1952: { ds@1952: grpid = (int)object; ds@1952: } ds@1952: ds@1952: pAdmin->cmdGrpId = grpid; ds@1952: pAdmin->flags = adminflags; ds@1952: ds@1952: /* First get the command group override, if any */ ds@1952: bool override = g_Admins.GetCommandOverride(group, ds@1952: Override_CommandGroup, ds@1952: &(pAdmin->eflags)); ds@1952: ds@1952: /* Next get the command override, if any */ ds@1952: if (g_Admins.GetCommandOverride(name, ds@1952: Override_Command, ds@1952: &(pAdmin->eflags))) ds@1952: { ds@1952: override = true; ds@1952: } ds@1952: ds@1952: /* Assign normal flags if there were no overrides */ ds@1952: if (!override) ds@1952: { ds@1952: pAdmin->eflags = pAdmin->flags; ds@1952: } ds@1952: ds@1952: /* Finally, add the hook */ ds@1952: pInfo->conhooks.push_back(pHook); ds@1952: pInfo->admin = *(pHook->pAdmin); ds@1952: pInfo->is_admin_set = true; ds@1952: ds@1952: /* Now add to the plugin */ ds@1952: CmdList *pList; ds@1952: IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext()); ds@1952: if (!pPlugin->GetProperty("CommandList", (void **)&pList)) ds@1952: { ds@1952: pList = new CmdList(); ds@1952: pPlugin->SetProperty("CommandList", pList); ds@1952: } ds@1952: PlCmdInfo info; ds@1952: info.pInfo = pInfo; ds@1952: info.type = Cmd_Admin; ds@1952: info.pHook = pHook; ds@1952: AddToPlCmdList(pList, info); ds@1952: ds@1952: return true; ds@1952: } ds@1952: ds@1952: bool ConCmdManager::AddServerCommand(IPluginFunction *pFunction, ds@1952: const char *name, ds@1952: const char *description, ds@1952: int flags) ds@1952: ds@1952: { ds@1952: ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags); ds@1952: ds@1952: if (!pInfo) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: CmdHook *pHook = new CmdHook(); ds@1952: ds@1952: pHook->pf = pFunction; ds@1952: if (description && description[0]) ds@1952: { ds@1952: pHook->helptext.assign(description); ds@1952: } ds@1952: ds@1952: pInfo->srvhooks.push_back(pHook); ds@1952: ds@1952: /* Add to the plugin */ ds@1952: CmdList *pList; ds@1952: IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext()); ds@1952: if (!pPlugin->GetProperty("CommandList", (void **)&pList)) ds@1952: { ds@1952: pList = new CmdList(); ds@1952: pPlugin->SetProperty("CommandList", pList); ds@1952: } ds@1952: PlCmdInfo info; ds@1952: info.pInfo = pInfo; ds@1952: info.type = Cmd_Server; ds@1952: info.pHook = pHook; ds@1952: AddToPlCmdList(pList, info); ds@1952: ds@1952: return true; ds@1952: } ds@1952: ds@1952: void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info) ds@1952: { ds@1952: CmdList::iterator iter = pList->begin(); ds@1952: bool inserted = false; ds@1952: const char *orig = NULL; ds@1952: ds@1952: orig = info.pInfo->pCmd->GetName(); ds@1952: ds@1952: /* Insert this into the help list, SORTED alphabetically. */ ds@1952: while (iter != pList->end()) ds@1952: { ds@1952: PlCmdInfo &obj = (*iter); ds@1952: const char *cmd = obj.pInfo->pCmd->GetName(); ds@1952: if (strcmp(orig, cmd) < 0) ds@1952: { ds@1952: pList->insert(iter, info); ds@1952: inserted = true; ds@1952: break; ds@1952: } ds@1952: iter++; ds@1952: } ds@1952: ds@1952: if (!inserted) ds@1952: { ds@1952: pList->push_back(info); ds@1952: } ds@1952: } ds@1952: ds@1952: void ConCmdManager::AddToCmdList(ConCmdInfo *info) ds@1952: { ds@1952: List::iterator iter = m_CmdList.begin(); ds@1952: ConCmdInfo *pInfo; ds@1952: bool inserted = false; ds@1952: const char *orig = NULL; ds@1952: ds@1952: orig = info->pCmd->GetName(); ds@1952: ds@1952: /* Insert this into the help list, SORTED alphabetically. */ ds@1952: while (iter != m_CmdList.end()) ds@1952: { ds@1952: const char *cmd = NULL; ds@1952: pInfo = (*iter); ds@1952: cmd = pInfo->pCmd->GetName(); ds@1952: if (strcmp(orig, cmd) < 0) ds@1952: { ds@1952: m_CmdList.insert(iter, info); ds@1952: inserted = true; ds@1952: break; ds@1952: } ds@1952: iter++; ds@1952: } ds@1952: ds@1952: if (!inserted) ds@1952: { ds@1952: m_CmdList.push_back(info); ds@1952: } ds@1952: } ds@1952: ds@1952: void ConCmdManager::UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits, bool remove) ds@1952: { ds@1952: ConCmdInfo *pInfo; ds@1952: ds@1952: if (type == Override_Command) ds@1952: { ds@1952: if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo)) ds@1952: { ds@1952: return; ds@1952: } ds@1952: ds@1952: List::iterator iter; ds@1952: CmdHook *pHook; ds@1952: ds@1952: for (iter=pInfo->conhooks.begin(); iter!=pInfo->conhooks.end(); iter++) ds@1952: { ds@1952: pHook = (*iter); ds@1952: if (pHook->pAdmin) ds@1952: { ds@1952: if (!remove) ds@1952: { ds@1952: pHook->pAdmin->eflags = bits; ds@1952: } else { ds@1952: pHook->pAdmin->eflags = pHook->pAdmin->flags; ds@1952: } ds@1952: pInfo->admin = *(pHook->pAdmin); ds@1952: } ds@1952: } ds@1952: pInfo->is_admin_set = true; ds@1952: } ds@1952: else if (type == Override_CommandGroup) ds@1952: { ds@1952: void *object; ds@1952: if (!sm_trie_retrieve(m_pCmdGrps, cmd, &object)) ds@1952: { ds@1952: return; ds@1952: } ds@1952: int grpid = (int)object; ds@1952: ds@1952: /* This is bad :( loop through all commands */ ds@1952: List::iterator iter; ds@1952: CmdHook *pHook; ds@1952: for (iter=m_CmdList.begin(); iter!=m_CmdList.end(); iter++) ds@1952: { ds@1952: pInfo = (*iter); ds@1952: for (List::iterator citer=pInfo->conhooks.begin(); ds@1952: citer!=pInfo->conhooks.end(); ds@1952: citer++) ds@1952: { ds@1952: pHook = (*citer); ds@1952: if (pHook->pAdmin && pHook->pAdmin->cmdGrpId == grpid) ds@1952: { ds@1952: if (remove) ds@1952: { ds@1952: pHook->pAdmin->eflags = bits; ds@1952: } else { ds@1952: pHook->pAdmin->eflags = pHook->pAdmin->flags; ds@1952: } ds@1952: pInfo->admin = *(pHook->pAdmin); ds@1952: } ds@1952: } ds@1952: } ds@1952: pInfo->is_admin_set = true; ds@1952: } ds@1952: } ds@1952: ds@1952: void ConCmdManager::RemoveConCmd(ConCmdInfo *info, const char *name, bool is_read_safe, bool untrack) ds@1952: { ds@1952: /* Remove from the trie */ ds@1952: sm_trie_delete(m_pCmds, name); ds@1952: ds@1952: /* Remove console-specific information ds@1952: * This should always be true as of right now ds@1952: */ ds@1952: if (info->pCmd) ds@1952: { ds@1952: if (info->sourceMod) ds@1952: { ds@1952: /* Unlink from SourceMM */ ds@1952: g_SMAPI->UnregisterConCommandBase(g_PLAPI, info->pCmd); ds@1952: /* Delete the command's memory */ ds@1952: char *new_help = const_cast(info->pCmd->GetHelpText()); ds@1952: char *new_name = const_cast(info->pCmd->GetName()); ds@1952: delete [] new_help; ds@1952: delete [] new_name; ds@1952: delete info->pCmd; ds@1952: } ds@1952: else ds@1952: { ds@1952: if (is_read_safe) ds@1952: { ds@1952: /* Remove the external hook */ ds@1952: SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false); ds@1952: } ds@1952: if (untrack) ds@1952: { ds@1952: UntrackConCommandBase(info->pCmd, this); ds@1952: } ds@1952: } ds@1952: } ds@1952: ds@1952: /* Remove from list */ ds@1952: m_CmdList.remove(info); ds@1952: ds@1952: delete info; ds@1952: } ds@1952: ds@1952: bool ConCmdManager::LookForSourceModCommand(const char *cmd) ds@1952: { ds@1952: ConCmdInfo *pInfo; ds@1952: ds@1952: if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo)) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: return pInfo->sourceMod && (pInfo->conhooks.size() > 0); ds@1952: } ds@1952: ds@1952: bool ConCmdManager::LookForCommandAdminFlags(const char *cmd, FlagBits *pFlags) ds@1952: { ds@1952: ConCmdInfo *pInfo; ds@1952: ds@1952: if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo)) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: *pFlags = pInfo->admin.eflags; ds@1952: ds@1952: return pInfo->is_admin_set; ds@1952: } ds@1952: ds@1952: ConCmdInfo *ConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags) ds@1952: { ds@1952: ConCmdInfo *pInfo; ds@1952: if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo)) ds@1952: { ds@1952: pInfo = new ConCmdInfo(); ds@1952: /* Find the commandopan */ ds@1952: ConCommandBase *pBase = icvar->GetCommands(); ds@1952: ConCommand *pCmd = NULL; ds@1952: while (pBase) ds@1952: { ds@1952: if (strcmp(pBase->GetName(), name) == 0) ds@1952: { ds@1952: /* Don't want to return convar with same name */ ds@1952: if (!pBase->IsCommand()) ds@1952: { ds@1952: return NULL; ds@1952: } ds@1952: ds@1952: pCmd = (ConCommand *)pBase; ds@1952: break; ds@1952: } ds@1952: pBase = const_cast(pBase->GetNext()); ds@1952: } ds@1952: ds@1952: if (!pCmd) ds@1952: { ds@1952: /* Note that we have to duplicate because the source might not be ds@1952: * a static string, and these expect static memory. ds@1952: */ ds@1952: if (!description) ds@1952: { ds@1952: description = ""; ds@1952: } ds@1952: char *new_name = sm_strdup(name); ds@1952: char *new_help = sm_strdup(description); ds@1952: pCmd = new ConCommand(new_name, CommandCallback, new_help, flags); ds@1952: pInfo->sourceMod = true; ds@1952: } ds@1952: else ds@1952: { ds@1952: TrackConCommandBase(pCmd, this); ds@1952: SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false); ds@1952: } ds@1952: ds@1952: pInfo->pCmd = pCmd; ds@1952: pInfo->is_admin_set = false; ds@1952: ds@1952: sm_trie_insert(m_pCmds, name, pInfo); ds@1952: AddToCmdList(pInfo); ds@1952: } ds@1952: ds@1952: return pInfo; ds@1952: } ds@1952: ds@1952: void ConCmdManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command) ds@1952: { ds@1952: if (command.ArgC() >= 3) ds@1952: { ds@1952: const char *text = command.Arg(2); ds@1952: ds@1952: CPlugin *pPlugin = g_PluginSys.FindPluginByConsoleArg(text); ds@1952: ds@1952: if (!pPlugin) ds@1952: { ds@1952: g_RootMenu.ConsolePrint("[SM] Plugin \"%s\" was not found.", text); ds@1952: return; ds@1952: } ds@1952: ds@1952: const sm_plugininfo_t *plinfo = pPlugin->GetPublicInfo(); ds@1952: const char *plname = IS_STR_FILLED(plinfo->name) ? plinfo->name : pPlugin->GetFilename(); ds@1952: ds@1952: CmdList *pList; ds@1952: if (!pPlugin->GetProperty("CommandList", (void **)&pList)) ds@1952: { ds@1952: g_RootMenu.ConsolePrint("[SM] No commands found for: %s", plname); ds@1952: return; ds@1952: } ds@1952: if (!pList->size()) ds@1952: { ds@1952: g_RootMenu.ConsolePrint("[SM] No commands found for: %s", plname); ds@1952: return; ds@1952: } ds@1952: ds@1952: CmdList::iterator iter; ds@1952: const char *type = NULL; ds@1952: const char *name; ds@1952: const char *help; ds@1952: g_RootMenu.ConsolePrint("[SM] Listing %d commands for: %s", pList->size(), plname); ds@1952: g_RootMenu.ConsolePrint(" %-17.16s %-8.7s %s", "[Name]", "[Type]", "[Help]"); ds@1952: for (iter=pList->begin(); ds@1952: iter!=pList->end(); ds@1952: iter++) ds@1952: { ds@1952: PlCmdInfo &cmd = (*iter); ds@1952: if (cmd.type == Cmd_Server) ds@1952: { ds@1952: type = "server"; ds@1952: } ds@1952: else if (cmd.type == Cmd_Console) ds@1952: { ds@1952: type = "console"; ds@1952: } ds@1952: else if (cmd.type == Cmd_Admin) ds@1952: { ds@1952: type = "admin"; ds@1952: } ds@1952: name = cmd.pInfo->pCmd->GetName(); ds@1952: if (cmd.pHook->helptext.size()) ds@1952: { ds@1952: help = cmd.pHook->helptext.c_str(); ds@1952: } ds@1952: else ds@1952: { ds@1952: help = cmd.pInfo->pCmd->GetHelpText(); ds@1952: } ds@1952: g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help); ds@1952: } ds@1952: ds@1952: return; ds@1952: } ds@1952: ds@1952: g_RootMenu.ConsolePrint("[SM] Usage: sm cmds "); ds@1952: }