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