implemented+tested RegServerCmd()

renamed internal project console stuff

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40504
This commit is contained in:
David Anderson 2007-02-15 22:17:01 +00:00
parent 81509e1ee4
commit 165b705354
5 changed files with 496 additions and 41 deletions

269
core/CConCmdManager.cpp Normal file
View File

@ -0,0 +1,269 @@
#include "CConCmdManager.h"
#include "sm_srvcmds.h"
CConCmdManager g_ConCmds;
SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int);
struct PlCmdInfo
{
ConCmdInfo *pInfo;
CmdType type;
};
typedef List<PlCmdInfo> CmdList;
char *sm_strdup(const char *str)
{
char *ptr = new char[strlen(str)+1];
strcpy(ptr, str);
return ptr;
}
CConCmdManager::CConCmdManager()
{
m_pCmds = sm_trie_create();
}
CConCmdManager::~CConCmdManager()
{
sm_trie_destroy(m_pCmds);
}
void CConCmdManager::OnSourceModAllInitialized()
{
g_PluginSys.AddPluginsListener(this);
g_RootMenu.AddRootConsoleCommand("cmds", "List console commands", this);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false);
}
void CConCmdManager::OnSourceModShutdown()
{
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &CConCmdManager::SetCommandClient, false);
g_RootMenu.RemoveRootConsoleCommand("cmds", this);
g_PluginSys.RemovePluginsListener(this);
}
void CConCmdManager::OnPluginLoaded(IPlugin *plugin)
{
/* Nothing yet... */
}
void CConCmdManager::OnPluginDestroyed(IPlugin *plugin)
{
CmdList *pList;
if (plugin->GetProperty("CommandList", (void **)&pList, true))
{
CmdList::iterator iter;
for (iter=pList->begin();
iter!=pList->end();
iter++)
{
PlCmdInfo &cmd = (*iter);
if (cmd.type == Cmd_Server
|| cmd.type == Cmd_Console)
{
ConCmdInfo *pInfo = cmd.pInfo;
/* See if there are still hooks */
if (pInfo->srvhooks
&& pInfo->srvhooks->GetFunctionCount())
{
continue;
}
/* Remove the command */
RemoveConCmd(pInfo);
}
}
delete pList;
}
}
void CommandCallback()
{
g_ConCmds.InternalDispatch();
}
void CConCmdManager::SetCommandClient(int client)
{
m_CmdClient = client + 1;
}
void CConCmdManager::InternalDispatch()
{
if (m_CmdClient)
{
return;
}
const char *cmd = engine->Cmd_Argv(0);
ConCmdInfo *pInfo;
if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
{
return;
}
cell_t result = Pl_Continue;
if (pInfo->srvhooks)
{
pInfo->srvhooks->Execute(&result);
}
if (result >= Pl_Handled)
{
if (!pInfo->sourceMod)
{
RETURN_META(MRES_SUPERCEDE);
}
return;
}
}
void CConCmdManager::AddServerCommand(IPluginFunction *pFunction,
const char *name,
const char *description,
int flags)
{
ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
if (!pInfo->srvhooks)
{
pInfo->srvhooks = g_Forwards.CreateForwardEx(NULL, ET_Hook, 1, NULL, Param_Cell);
}
pInfo->srvhooks->AddFunction(pFunction);
/* Add to the plugin */
CmdList *pList;
IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
if (!pPlugin->GetProperty("CommandList", (void **)&pList))
{
pList = new CmdList();
pPlugin->SetProperty("CommandList", pList);
}
PlCmdInfo info;
info.pInfo = pInfo;
info.type = Cmd_Console;
pList->push_back(info);
}
void CConCmdManager::AddToCmdList(ConCmdInfo *info)
{
List<ConCmdInfo *>::iterator iter = m_CmdList.begin();
ConCmdInfo *pInfo;
bool inserted = false;
const char *orig;
if (info->pCmd)
{
orig = info->pCmd->GetName();
}
/* Insert this into the help list, SORTED alphabetically. */
while (iter != m_CmdList.end())
{
const char *cmd = NULL;
pInfo = (*iter);
if (pInfo->pCmd)
{
cmd = pInfo->pCmd->GetName();
}
if (strcmp(orig, cmd) < 0)
{
m_CmdList.insert(iter, info);
inserted = true;
break;
}
iter++;
}
if (!inserted)
{
m_CmdList.push_back(info);
}
}
void CConCmdManager::RemoveConCmd(ConCmdInfo *info)
{
/* Remove console-specific information */
if (info->pCmd)
{
/* Remove from the trie */
sm_trie_delete(m_pCmds, info->pCmd->GetName());
if (info->sourceMod)
{
/* Unlink from SourceMM */
g_SMAPI->UnregisterConCmdBase(g_PLAPI, info->pCmd);
/* Delete the command's memory */
char *new_help = const_cast<char *>(info->pCmd->GetHelpText());
char *new_name = const_cast<char *>(info->pCmd->GetName());
delete [] new_help;
delete [] new_name;
delete info->pCmd;
} else {
/* Remove the external hook */
SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false);
}
}
/* Remove from list */
m_CmdList.remove(info);
/* Free forwards */
if (info->srvhooks)
{
g_Forwards.ReleaseForward(info->srvhooks);
}
}
ConCmdInfo *CConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
{
ConCmdInfo *pInfo;
if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo))
{
pInfo = new ConCmdInfo();
/* Find the commandopan */
ConCommandBase *pBase = icvar->GetCommands();
ConCommand *pCmd = NULL;
while (pBase)
{
if (pBase->IsCommand()
&& (strcmp(pBase->GetName(), name) == 0))
{
pCmd = (ConCommand *)pBase;
break;
}
pBase = const_cast<ConCommandBase *>(pBase->GetNext());
}
if (!pCmd)
{
/* Note that we have to duplicate because the source might not be
* a static string, and these expect static memory.
*/
if (!description)
{
description = "";
}
char *new_name = sm_strdup(name);
char *new_help = sm_strdup(description);
pCmd = new ConCommand(new_name, CommandCallback, new_help, flags);
pInfo->sourceMod = true;
} else {
SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false);
}
pInfo->pCmd = pCmd;
sm_trie_insert(m_pCmds, name, pInfo);
AddToCmdList(pInfo);
}
return pInfo;
}
void CConCmdManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
{
}

81
core/CConCmdManager.h Normal file
View File

@ -0,0 +1,81 @@
/**
* vim: set ts=4 :
* ===============================================================
* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
* ===============================================================
*
* This file is not open source and may not be copied without explicit
* written permission of AlliedModders LLC. This file may not be redistributed
* in whole or significant part.
* For information, see LICENSE.txt or http://www.sourcemod.net/license.php
*
* Version: $Id$
*/
#ifndef _INCLUDE_SOURCEMOD_CCONCMDMANAGER_H_
#define _INCLUDE_SOURCEMOD_CCONCMDMANAGER_H_
#include "sm_globals.h"
#include "sourcemm_api.h"
#include "ForwardSys.h"
#include "sm_trie.h"
#include <sh_list.h>
#include <IRootConsoleMenu.h>
using namespace SourceHook;
enum CmdType
{
Cmd_Server,
Cmd_Console,
Cmd_Client
};
struct ConCmdInfo
{
ConCmdInfo()
{
sourceMod = false;
pCmd = NULL;
srvhooks = NULL;
}
bool sourceMod; /**< Determines whether or not concmd was created by a SourceMod plugin */
ConCommand *pCmd; /**< Pointer to the command itself */
IChangeableForward *srvhooks; /**< Hooks on this name as a server command */
};
class CConCmdManager :
public SMGlobalClass,
public IRootConsoleCommand,
public IPluginsListener
{
friend void CommandCallback();
public:
CConCmdManager();
~CConCmdManager();
public: //SMGlobalClass
void OnSourceModAllInitialized();
void OnSourceModShutdown();
public: //IPluginsListener
void OnPluginLoaded(IPlugin *plugin);
void OnPluginDestroyed(IPlugin *plugin);
public: //IRootConsoleCommand
void OnRootConsoleCommand(const char *command, unsigned int argcount);
public:
void AddServerCommand(IPluginFunction *pFunction, const char *name, const char *description, int flags);
private:
void InternalDispatch();
ConCmdInfo *AddOrFindCommand(const char *name, const char *description, int flags);
void SetCommandClient(int client);
void AddToCmdList(ConCmdInfo *info);
void RemoveConCmd(ConCmdInfo *info);
private:
Trie *m_pCmds;
List<ConCmdInfo *> m_CmdList;
int m_CmdClient;
};
extern CConCmdManager g_ConCmds;
#endif // _INCLUDE_SOURCEMOD_CCONCMDMANAGER_H_

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Version="8.00"
Name="sourcemod_mm"
ProjectGUID="{E39527CD-7CAB-4420-97CC-DA1B93B260BC}"
RootNamespace="sourcemod_mm"
@ -183,6 +183,10 @@
RelativePath="..\AdminCache.cpp"
>
</File>
<File
RelativePath="..\CConCmdManager.cpp"
>
</File>
<File
RelativePath="..\CConVarManager.cpp"
>
@ -231,46 +235,6 @@
RelativePath="..\sm_trie.cpp"
>
</File>
<File
RelativePath="..\smn_admin.cpp"
>
</File>
<File
RelativePath="..\smn_convar.cpp"
>
</File>
<File
RelativePath="..\smn_datapacks.cpp"
>
</File>
<File
RelativePath="..\smn_filesystem.cpp"
>
</File>
<File
RelativePath="..\smn_float.cpp"
>
</File>
<File
RelativePath="..\smn_handles.cpp"
>
</File>
<File
RelativePath="..\smn_lang.cpp"
>
</File>
<File
RelativePath="..\smn_player.cpp"
>
</File>
<File
RelativePath="..\smn_string.cpp"
>
</File>
<File
RelativePath="..\smn_textparse.cpp"
>
</File>
<File
RelativePath="..\sourcemm_api.cpp"
>
@ -289,6 +253,10 @@
RelativePath="..\AdminCache.h"
>
</File>
<File
RelativePath="..\CConCmdManager.h"
>
</File>
<File
RelativePath="..\CConVarManager.h"
>
@ -658,6 +626,50 @@
>
</File>
</Filter>
<Filter
Name="Natives"
>
<File
RelativePath="..\smn_admin.cpp"
>
</File>
<File
RelativePath="..\smn_console.cpp"
>
</File>
<File
RelativePath="..\smn_datapacks.cpp"
>
</File>
<File
RelativePath="..\smn_filesystem.cpp"
>
</File>
<File
RelativePath="..\smn_float.cpp"
>
</File>
<File
RelativePath="..\smn_handles.cpp"
>
</File>
<File
RelativePath="..\smn_lang.cpp"
>
</File>
<File
RelativePath="..\smn_player.cpp"
>
</File>
<File
RelativePath="..\smn_string.cpp"
>
</File>
<File
RelativePath="..\smn_textparse.cpp"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>

View File

@ -15,6 +15,7 @@
#include "sourcemm_api.h"
#include "HandleSys.h"
#include "CConVarManager.h"
#include "CConCmdManager.h"
static cell_t sm_CreateConVar(IPluginContext *pContext, const cell_t *params)
{
@ -322,6 +323,25 @@ static cell_t sm_ResetConVar(IPluginContext *pContext, const cell_t *params)
return 1;
}
static cell_t sm_RegServerCmd(IPluginContext *pContext, const cell_t *params)
{
char *name,*help;
IPluginFunction *pFunction;
pContext->LocalToString(params[1], &name);
pContext->LocalToString(params[3], &help);
pFunction = pContext->GetFunctionById(params[2]);
if (!pFunction)
{
return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
}
g_ConCmds.AddServerCommand(pFunction, name, help, params[4]);
return 1;
}
REGISTER_NATIVES(convarNatives)
{
{"CreateConVar", sm_CreateConVar},
@ -342,5 +362,6 @@ REGISTER_NATIVES(convarNatives)
{"GetConVarMin", sm_GetConVarMin},
{"GetConVarMax", sm_GetConVarMax},
{"ResetConVar", sm_ResetConVar},
{"RegServerCmd", sm_RegServerCmd},
{NULL, NULL}
};

View File

@ -73,6 +73,78 @@ native PrintToServer(const String:format[], {Handle,Float,String,_}:...);
*/
native PrintToConsole(client, const String:format[], {Handle,Float,String,_}:...);
/**
* Called when a server-only command is invoked.
*
* @return A Result value. Not handling the command
* means that Source will report it as "not found."
*/
functag SrvCmd Action:public();
/**
* Creates a server-only console command, or hooks an already existing one.
*
* @param cmd Name of the command to hook or create.
* @param callback A function to use as a callback for when the command is invoked.
* @param description Optional description to use for command creation.
* @param flags Optional flags to use for command creation.
* @noreturn
*/
native RegServerCmd(const String:cmd[], SrvCmd:callback, const String:description[]="", flags=0);
#if 0
/**
* Called when a generic console command is invoked.
*
* @param client Index of the client, or 0 from the server.
* @return A Result value. Not handling the command
* means that Source will report it as "not found."
*/
functag ConCmd Action:public(client);
/**
* Creates a console command, or hooks an already existing one.
*
* @param cmd Name of the command to hook or create.
* @param callback A function to use as a callback for when the command is invoked.
* @param description Optional description to use for command creation.
* @param flags Optional flags to use for command creation.
* @noreturn
*/
native RegConsoleCmd(const String:cmd[], ConCmd:callback, const String:description[]="", flags=0);
/**
* Hooks a specific client-only command.
*
* @param cmd String to match against the beginning of each client command.
* @param callback A function to use as a callback for when the command is invoked.
* @param description Optional description string to use for help.
* @noreturn
*/
native RegClientCmd(const String:cmd[], ConCmd:callback, const String:description[]="");
/**
* Creates a console command as an administrative command. If the command does not exist,
* it is created. This command cannot be used to create duplicate admin commands.
*
* @param cmd String containing command to register.
* @param callback A function to use as a callback for when the command is invoked.
* @param adminflags Administrative flags (bitstring) to use for permissions.
* @param group String containing the command group to use. If empty,
* the plugin's filename will be used instead.
* @param description Optional description to use for help.
* @param flags Optional console flags.
* @noreturn
* @error If this command has already been registered as an admin command.
*/
native RegAdminCmd(const String:cmd[],
ConCmd:callback,
adminflags,
const String:group[]="",
const String:description[]="",
flags=0);
#endif
/**
* Creates a new console variable.
*