ds@579: /** ds@653: * vim: set ts=4 : ds@579: * =============================================================== ds@579: * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. ds@579: * =============================================================== ds@579: * ds@579: * This file is not open source and may not be copied without explicit ds@579: * written permission of AlliedModders LLC. This file may not be redistributed ds@579: * in whole or significant part. ds@579: * For information, see LICENSE.txt or http://www.sourcemod.net/license.php 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@579: SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false); 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: 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); ds@579: } else { 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 */ ds@579: RemoveConCmd(pInfo); ds@579: removed.push_back(pInfo); ds@579: } ds@579: delete pList; ds@579: } ds@579: } ds@579: ds@579: void CommandCallback() ds@579: { ds@579: g_ConCmds.InternalDispatch(); ds@579: } ds@579: ds@579: void ConCmdManager::SetCommandClient(int client) ds@579: { ds@579: m_CmdClient = client + 1; ds@579: } ds@579: ds@579: ResultType ConCmdManager::DispatchClientCommand(int client, ResultType type) ds@579: { ds@579: const char *cmd = engine->Cmd_Argv(0); ds@579: int args = engine->Cmd_Argc() - 1; 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: ds@579: void ConCmdManager::InternalDispatch() ds@579: { 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: */ ds@579: ds@579: const char *cmd = engine->Cmd_Argv(0); ds@579: ConCmdInfo *pInfo; ds@579: if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo)) ds@579: { ds@579: return; ds@579: } ds@579: ds@579: cell_t result = Pl_Continue; ds@579: int args = engine->Cmd_Argc() - 1; ds@579: ds@579: List::iterator iter; ds@579: CmdHook *pHook; ds@579: ds@579: /* Execute server-only commands if viable */ ds@579: if (m_CmdClient == 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: } ds@579: if (m_CmdClient ds@579: && pHook->pAdmin ds@579: && !CheckAccess(m_CmdClient, 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: */ ds@856: if (m_CmdClient == 0 && !engine->IsDedicatedServer()) ds@856: { ds@856: pHook->pf->PushCell(1); ds@856: } else { ds@856: pHook->pf->PushCell(m_CmdClient); ds@856: } ds@856: 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: 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: ds@579: bool ConCmdManager::CheckAccess(int client, const char *cmd, AdminCmdInfo *pAdmin) ds@579: { ds@579: FlagBits cmdflags = pAdmin->eflags; ds@579: if (cmdflags == 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); ds@579: if (!player) 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: ds@579: /* See if our other flags match */ ds@579: if ((bits & cmdflags) == cmdflags) ds@579: { ds@579: return true; ds@579: } ds@579: ds@579: /* Check for overrides */ 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; iIsFakeClient() ds@579: || player->GetEdict() == NULL) ds@579: { ds@579: return false; ds@579: } ds@579: ds@579: /* If we got here, the command failed... */ ds@579: char buffer[128]; ds@579: if (g_Translator.CoreTrans(client, buffer, sizeof(buffer), "No Access", NULL, NULL) ds@579: != Trans_Okay) ds@579: { ds@579: snprintf(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@895: snprintf(fullbuffer, sizeof(fullbuffer), "[SM] %s.\n", buffer); dvander@895: engine->ClientPrintf(player->GetEdict(), fullbuffer); dvander@895: } else if (replyto == SM_REPLY_CHAT) { dvander@895: char fullbuffer[192]; dvander@895: snprintf(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); ds@579: } else { 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); 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: ds@579: void ConCmdManager::UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits) 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: { ds@579: if (bits) ds@579: { ds@579: pHook->pAdmin->eflags = bits; ds@579: } else { ds@579: pHook->pAdmin->eflags = pHook->pAdmin->flags; ds@579: } ds@579: } ds@579: } ds@579: } else if (type == Override_CommandGroup) { 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: { ds@579: if (bits) ds@579: { ds@579: pHook->pAdmin->eflags = bits; ds@579: } else { ds@579: pHook->pAdmin->eflags = pHook->pAdmin->flags; ds@579: } ds@579: } ds@579: } ds@579: } ds@579: } ds@579: } ds@579: ds@579: void ConCmdManager::RemoveConCmd(ConCmdInfo *info) ds@579: { 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: /* Remove from the trie */ ds@579: sm_trie_delete(m_pCmds, info->pCmd->GetName()); ds@579: ds@579: if (info->sourceMod) ds@579: { ds@579: /* Unlink from SourceMM */ ds@579: g_SMAPI->UnregisterConCmdBase(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; ds@579: } else { ds@579: /* Remove the external hook */ ds@579: SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false); 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: 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@579: ConCommandBase *pBase = icvar->GetCommands(); ds@579: ConCommand *pCmd = NULL; ds@579: while (pBase) ds@579: { ds@579: if (strcmp(pBase->GetName(), name) == 0) ds@579: { ds@579: /* Don't want to return convar with same name */ ds@579: if (!pBase->IsCommand()) ds@579: { ds@579: return NULL; ds@579: } ds@579: ds@579: pCmd = (ConCommand *)pBase; ds@579: break; ds@579: } ds@579: pBase = const_cast(pBase->GetNext()); ds@579: } 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; ds@579: } else { ds@579: SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false); ds@579: } ds@579: ds@579: pInfo->pCmd = pCmd; 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: ds@579: void ConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argcount) ds@579: { ds@579: if (argcount >= 3) ds@579: { ds@579: const char *text = engine->Cmd_Argv(2); dvander@723: ds@579: int id = atoi(text); ds@579: CPlugin *pPlugin = g_PluginSys.GetPluginByOrder(id); ds@579: ds@579: if (!pPlugin) ds@579: { ds@579: g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id); 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(); ds@582: iter++, id++) ds@579: { ds@579: PlCmdInfo &cmd = (*iter); ds@579: if (cmd.type == Cmd_Server) ds@579: { ds@579: type = "server"; ds@579: } else if (cmd.type == Cmd_Console) { ds@579: type = "console"; ds@579: } else if (cmd.type == Cmd_Admin) { 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(); ds@579: } else { 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: } dvander@614: dvander@614: static int g_yam_state = 0; dvander@614: void _YamState(int state) dvander@614: { dvander@614: if (state == 0) dvander@614: { dvander@614: g_RootMenu.ConsolePrint("Welcome to the SourceMod Text Adventure."); dvander@614: g_RootMenu.ConsolePrint("Type sm_text to see the last message."); dvander@614: g_RootMenu.ConsolePrint("Type sm_text to follow a capital word."); dvander@614: g_RootMenu.ConsolePrint("-------------------------------"); dvander@614: g_RootMenu.ConsolePrint("You are at VALVE HEADQUARTERS."); dvander@614: g_RootMenu.ConsolePrint("To your LEFT is BAILOPAN, rearranging the letters to spell"); dvander@614: g_RootMenu.ConsolePrint("\"A HARD VEAL QUEST\". FORWARD is the entrance to the building."); dvander@614: g_RootMenu.ConsolePrint("To your RIGHT is your last chance to flee in terror."); dvander@614: } else if (state == 1) { dvander@614: g_RootMenu.ConsolePrint("BAILOPAN tells you that his name his pronounced"); dvander@614: g_RootMenu.ConsolePrint("\"bye low pahn,\" not \"bay low pan.\" Do you "); dvander@614: g_RootMenu.ConsolePrint("MOCK him, or NOD quietly?"); dvander@614: } else if (state == 2) { dvander@614: g_RootMenu.ConsolePrint("You enter the Valve building. You hear screams coming from within."); dvander@614: g_RootMenu.ConsolePrint("A grotesque figure lumbers up to greet you; it is Gabe Newell."); dvander@614: g_RootMenu.ConsolePrint("\"Welcome,\" he belches, \"to my lair.\""); dvander@614: g_RootMenu.ConsolePrint("Do you SHAKE Gaben's hand, WALK past him, or OFFER a donut?"); dvander@614: } else if (state == 3) { dvander@614: g_RootMenu.ConsolePrint("You walk into the break room. Alfred \"Adolf\" Reynolds and"); dvander@614: g_RootMenu.ConsolePrint("Yahn \"Yeti\" Bernier are discussing something (you overhear "); dvander@614: g_RootMenu.ConsolePrint("the phrase \"and next Steam update, here's what we should break\")."); dvander@614: g_RootMenu.ConsolePrint("Should you DIE in a fire, REPORT a bug, REQUEST a feature, or "); dvander@614: g_RootMenu.ConsolePrint("SPRAY them with butter?"); dvander@614: } dvander@614: g_yam_state = state; dvander@614: } dvander@614: dvander@614: void _IntExt_CallYams() dvander@614: { dvander@614: const char *arg = engine->Cmd_Argv(1); dvander@614: dvander@614: /* should be impossible */ dvander@614: if (!arg || arg[0] == '\0') dvander@614: { dvander@614: _YamState(g_yam_state); dvander@614: } dvander@614: dvander@614: if (g_yam_state == 1) dvander@614: { dvander@614: if (strcasecmp(arg, "mock") == 0) dvander@614: { ds@729: g_RootMenu.ConsolePrint("You mock BAILOPAN's pronunciation. In a fit of rage, "); dvander@614: g_RootMenu.ConsolePrint("he sticks an INT 3 call into your chest, rendering you broken."); dvander@614: g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER."); dvander@614: g_yam_state = 0; dvander@614: } else if (strcasecmp(arg, "nod") == 0) { dvander@614: g_RootMenu.ConsolePrint("You nod quietly, and then slowly back away into the Valve headquarters."); dvander@614: _YamState(2); dvander@614: } else { dvander@614: g_RootMenu.ConsolePrint("Commands are MOCK and NOD."); dvander@614: } dvander@614: } else if (g_yam_state == 3) { dvander@614: if (strcasecmp(arg, "report") == 0) dvander@614: { dvander@614: g_RootMenu.ConsolePrint("You report a bug to Alfred and Yeti. Immediately, both fall asleep."); dvander@614: g_RootMenu.ConsolePrint("You decay in the break room for two years while they sleep."); dvander@614: g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER."); dvander@614: g_yam_state = 0; dvander@614: } else if (strcasecmp(arg, "request") == 0) { dvander@614: g_RootMenu.ConsolePrint("You request a feature to Alfred and Yeti. They both mutter something"); dvander@614: g_RootMenu.ConsolePrint("about it being implemented \"soon.\" Then, by accident, someone sends"); dvander@614: g_RootMenu.ConsolePrint("a message over \"Friends.\" The entire building catches fire."); dvander@614: g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER."); dvander@614: g_yam_state = 0; dvander@614: } else if (strcasecmp(arg, "die") == 0) { dvander@614: g_RootMenu.ConsolePrint("For no reason, you suddenly catch fire. Alfred and Yeti find this"); dvander@614: g_RootMenu.ConsolePrint("deeply disturbing, and cover your flaming corpse with Episode 2"); dvander@617: g_RootMenu.ConsolePrint("advertisements. Coming soon, with Team Fortress 2, and Portal!"); dvander@614: g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER."); dvander@614: g_yam_state = 0; dvander@614: } else if (strcasecmp(arg, "spray") == 0) { dvander@614: g_RootMenu.ConsolePrint("You spray Alfred and Yeti with butter. Like Jack Thompson to an"); dvander@614: g_RootMenu.ConsolePrint("ambulance, Gabe Newell instantly appears and devours them both."); dvander@614: g_RootMenu.ConsolePrint("You run away, just in time, as Gabe Newell explodes, registering "); dvander@614: g_RootMenu.ConsolePrint("a 5.6 earthquake. Outside, world peace is achieved."); dvander@614: g_RootMenu.ConsolePrint("YOU HAVE WON."); dvander@614: g_yam_state = 0; dvander@614: } dvander@614: } else if (g_yam_state == 2) { dvander@614: if (strcasecmp(arg, "shake") == 0) dvander@614: { dvander@614: g_RootMenu.ConsolePrint("You shake Gaben's hand. It is a terrifying and disgusting experience."); dvander@614: g_RootMenu.ConsolePrint("However, you survive, and continue on."); dvander@614: _YamState(3); dvander@614: } else if (strcasecmp(arg, "offer") == 0) { dvander@614: g_RootMenu.ConsolePrint("You offer Gabe Newell one (1) donut. With a gleam in his eyes, "); dvander@614: g_RootMenu.ConsolePrint("he picks you up and devours you whole."); dvander@614: g_RootMenu.ConsolePrint("YOU HAVE DIED. GAME OVER."); dvander@614: g_yam_state = 0; dvander@614: } else if (strcasecmp(arg, "walk") == 0) { dvander@614: g_RootMenu.ConsolePrint("You walk past Gabe Newell. He can't keep up with your pace!"); dvander@614: _YamState(3); dvander@614: } else { dvander@614: g_RootMenu.ConsolePrint("Commands are SHAKE, OFFER, and WALK."); dvander@614: } dvander@614: } else if (g_yam_state == 0) { dvander@614: if (strcasecmp(arg, "left") == 0) dvander@614: { dvander@614: _YamState(1); dvander@614: } else if (strcasecmp(arg, "right") == 0) { dvander@614: g_RootMenu.ConsolePrint("You run away from the Valve headquarters in sheer terror."); dvander@614: g_RootMenu.ConsolePrint("While running, you smash into an unknown person, who turns out to be your soul mate."); dvander@614: g_RootMenu.ConsolePrint("You marry and raise a family of 3 kids."); dvander@614: g_RootMenu.ConsolePrint("Many years later, you look back, and realize this was your best choice."); dvander@614: g_RootMenu.ConsolePrint("YOU HAVE WON."); dvander@614: g_yam_state = 0; dvander@614: } else if (strcasecmp(arg, "forward") == 0) { dvander@614: _YamState(2); dvander@614: } else if (arg[0] != '\0') { dvander@614: g_RootMenu.ConsolePrint("Commands are FORWARD, LEFT, and RIGHT."); dvander@614: } dvander@614: } dvander@614: } dvander@614: dvander@614: void _IntExt_EnableYams() dvander@614: { dvander@614: static ConCommand *pCmd = NULL; dvander@614: if (!pCmd) dvander@614: { dvander@614: pCmd = new ConCommand("sm_text", _IntExt_CallYams, "Fountain of Yams Adventure Game", FCVAR_GAMEDLL); dvander@614: g_RootMenu.ConsolePrint("Something is now enabled..."); dvander@614: } else { dvander@614: g_RootMenu.ConsolePrint("Something is already enabled..."); dvander@614: } dvander@614: }