2
0
mirror of https://github.com/rehlds/metamod-r.git synced 2024-12-28 15:45:37 +03:00

Rewritten regcmd code

This commit is contained in:
asmodai 2017-01-07 03:20:55 +03:00
parent d206ad4a13
commit 7e6878b3a1
13 changed files with 205 additions and 222 deletions

View File

@ -0,0 +1,5 @@
#include "precompiled.h"
#include "sys_shared.cpp"
#include "interface.cpp"
#include "crc32c.cpp"

View File

@ -34,8 +34,8 @@
#include "interface.h" #include "interface.h"
#include "model.h" #include "model.h"
#define REHLDS_API_VERSION_MAJOR 2 #define REHLDS_API_VERSION_MAJOR 3
#define REHLDS_API_VERSION_MINOR 13 #define REHLDS_API_VERSION_MINOR 1
//Steam_NotifyClientConnect hook //Steam_NotifyClientConnect hook
typedef IHookChain<qboolean, IGameClient*, const void*, unsigned int> IRehldsHook_Steam_NotifyClientConnect; typedef IHookChain<qboolean, IGameClient*, const void*, unsigned int> IRehldsHook_Steam_NotifyClientConnect;
@ -122,8 +122,8 @@ typedef IVoidHookChain<IGameClient *, struct packet_entities_s *, sizebuf_t *> I
typedef IVoidHookChainRegistry<IGameClient *, struct packet_entities_s *, sizebuf_t *> IRehldsHookRegistry_SV_EmitEvents; typedef IVoidHookChainRegistry<IGameClient *, struct packet_entities_s *, sizebuf_t *> IRehldsHookRegistry_SV_EmitEvents;
//EV_PlayReliableEvent hook //EV_PlayReliableEvent hook
typedef IVoidHookChain<IGameClient *, int, short unsigned int, float, struct event_args_s *> IRehldsHook_EV_PlayReliableEvent; typedef IVoidHookChain<IGameClient *, int, unsigned short, float, struct event_args_s *> IRehldsHook_EV_PlayReliableEvent;
typedef IVoidHookChainRegistry<IGameClient *, int, short unsigned int, float, struct event_args_s *> IRehldsHookRegistry_EV_PlayReliableEvent; typedef IVoidHookChainRegistry<IGameClient *, int, unsigned short, float, struct event_args_s *> IRehldsHookRegistry_EV_PlayReliableEvent;
//SV_StartSound hook //SV_StartSound hook
typedef IVoidHookChain<int , edict_t *, int, const char *, int, float, int, int> IRehldsHook_SV_StartSound; typedef IVoidHookChain<int , edict_t *, int, const char *, int, float, int, int> IRehldsHook_SV_StartSound;
@ -257,7 +257,7 @@ struct RehldsFuncs_t {
cmd_source_t*(*GetCmdSource)(); cmd_source_t*(*GetCmdSource)();
void(*Log)(const char* prefix, const char* msg); void(*Log)(const char* prefix, const char* msg);
DLL_FUNCTIONS *(*GetEntityInterface)(); DLL_FUNCTIONS *(*GetEntityInterface)();
void(*EV_PlayReliableEvent)(IGameClient *cl, int entindex, short unsigned int eventindex, float delay, struct event_args_s *pargs); void(*EV_PlayReliableEvent)(IGameClient *cl, int entindex, unsigned short eventindex, float delay, struct event_args_s *pargs);
int(*SV_LookupSoundIndex)(const char *sample); int(*SV_LookupSoundIndex)(const char *sample);
void(*MSG_StartBitWriting)(sizebuf_t *buf); void(*MSG_StartBitWriting)(sizebuf_t *buf);
void(*MSG_WriteBits)(uint32 data, int numbits); void(*MSG_WriteBits)(uint32 data, int numbits);
@ -281,6 +281,8 @@ struct RehldsFuncs_t {
void(*SV_StartSound)(int recipients, edict_t *entity, int channel, const char *sample, int volume, float attenuation, int flags, int pitch); void(*SV_StartSound)(int recipients, edict_t *entity, int channel, const char *sample, int volume, float attenuation, int flags, int pitch);
bool(*SV_EmitSound2)(edict_t *entity, IGameClient *receiver, int channel, const char *sample, float volume, float attenuation, int flags, int pitch, int emitFlags, const float *pOrigin); bool(*SV_EmitSound2)(edict_t *entity, IGameClient *receiver, int channel, const char *sample, float volume, float attenuation, int flags, int pitch, int emitFlags, const float *pOrigin);
void(*SV_UpdateUserInfo)(IGameClient *pGameClient); void(*SV_UpdateUserInfo)(IGameClient *pGameClient);
bool(*StripUnprintableAndSpace)(char *pch);
void(*Cmd_RemoveCmd)(char *cmd_name);
}; };
class IRehldsApi { class IRehldsApi {

View File

@ -186,6 +186,7 @@
<ClCompile Include="..\src\linkent.cpp" /> <ClCompile Include="..\src\linkent.cpp" />
<ClCompile Include="..\src\linkgame.cpp" /> <ClCompile Include="..\src\linkgame.cpp" />
<ClCompile Include="..\src\log_meta.cpp" /> <ClCompile Include="..\src\log_meta.cpp" />
<ClCompile Include="..\src\metamod_rehlds_api.cpp" />
<ClCompile Include="..\src\meta_eiface.cpp" /> <ClCompile Include="..\src\meta_eiface.cpp" />
<ClCompile Include="..\src\metamod.cpp" /> <ClCompile Include="..\src\metamod.cpp" />
<ClCompile Include="..\src\mlist.cpp" /> <ClCompile Include="..\src\mlist.cpp" />
@ -198,6 +199,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
</ClCompile> </ClCompile>
<ClCompile Include="..\src\public_amalgamation.cpp" />
<ClCompile Include="..\src\reg_support.cpp" /> <ClCompile Include="..\src\reg_support.cpp" />
<ClCompile Include="..\src\sdk_util.cpp" /> <ClCompile Include="..\src\sdk_util.cpp" />
<ClCompile Include="..\src\studioapi.cpp" /> <ClCompile Include="..\src\studioapi.cpp" />
@ -218,6 +220,7 @@
<ClInclude Include="..\src\jitasm.h" /> <ClInclude Include="..\src\jitasm.h" />
<ClInclude Include="..\src\linkent.h" /> <ClInclude Include="..\src\linkent.h" />
<ClInclude Include="..\src\log_meta.h" /> <ClInclude Include="..\src\log_meta.h" />
<ClInclude Include="..\src\metamod_rehlds_api.h" />
<ClInclude Include="..\src\meta_api.h" /> <ClInclude Include="..\src\meta_api.h" />
<ClInclude Include="..\src\meta_eiface.h" /> <ClInclude Include="..\src\meta_eiface.h" />
<ClInclude Include="..\src\metamod.h" /> <ClInclude Include="..\src\metamod.h" />

View File

@ -86,6 +86,12 @@
<ClCompile Include="..\src\callback_jit.cpp"> <ClCompile Include="..\src\callback_jit.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\src\metamod_rehlds_api.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\public_amalgamation.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\src\api_info.h"> <ClInclude Include="..\src\api_info.h">
@ -178,6 +184,9 @@
<ClInclude Include="..\src\jitasm.h"> <ClInclude Include="..\src\jitasm.h">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\src\metamod_rehlds_api.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="metamod.def"> <None Include="metamod.def">

View File

@ -20,7 +20,7 @@ void META_CONS(const char *fmt, ...)
unsigned int len; unsigned int len;
va_start(ap, fmt); va_start(ap, fmt);
len = Q_vsnprintf(buf, sizeof(buf) - 1, fmt, ap); len = Q_vsnprintf(buf, sizeof buf, fmt, ap);
va_end(ap); va_end(ap);
buf[len] = '\n'; buf[len] = '\n';

View File

@ -0,0 +1,70 @@
#include "precompiled.h"
IRehldsApi* g_RehldsApi;
const RehldsFuncs_t* g_RehldsFuncs;
IRehldsHookchains* g_RehldsHookchains;
IRehldsServerStatic* g_RehldsSvs;
bool rehlds_api_try_init(CSysModule* engineModule, char* failureReason)
{
if (!engineModule) {
META_ERROR("Failed to locate engine module\n");
return false;
}
CreateInterfaceFn ifaceFactory = Sys_GetFactory(engineModule);
if (!ifaceFactory) {
sprintf(failureReason, "Failed to locate interface factory in engine module\n");
return false;
}
int retCode = 0;
g_RehldsApi = (IRehldsApi*)ifaceFactory(VREHLDS_HLDS_API_VERSION, &retCode);
if (!g_RehldsApi) {
sprintf(failureReason, "Failed to locate retrieve rehlds api interface from engine module, return code is %d\n", retCode);
return false;
}
int majorVersion = g_RehldsApi->GetMajorVersion();
int minorVersion = g_RehldsApi->GetMinorVersion();
if (majorVersion != REHLDS_API_VERSION_MAJOR) {
sprintf(failureReason, "REHLDS Api major version mismatch; expected %d, real %d\n", REHLDS_API_VERSION_MAJOR, majorVersion);
return false;
}
if (minorVersion < REHLDS_API_VERSION_MINOR) {
sprintf(failureReason, "REHLDS Api minor version mismatch; expected at least %d, real %d\n", REHLDS_API_VERSION_MINOR, majorVersion);
return false;
}
g_RehldsFuncs = g_RehldsApi->GetFuncs();
g_RehldsHookchains = g_RehldsApi->GetHookchains();
g_RehldsSvs = g_RehldsApi->GetServerStatic();
return true;
}
bool meta_init_rehlds_api() {
char failReason[2048];
#ifdef WIN32
CSysModule* engineModule = Sys_LoadModule("swds.dll");
if (!rehlds_api_try_init(engineModule, failReason)) {
engineModule = Sys_LoadModule("filesystem_stdio.dll");
if (!rehlds_api_try_init(engineModule, failReason)) {
META_ERROR("%s", failReason);
return false;
}
}
#else
CSysModule* engineModule = Sys_LoadModule("engine_i486.so");
if (!rehlds_api_try_init(engineModule, failReason)) {
META_ERROR("%s", failReason);
return false;
}
#endif
return true;
}

View File

@ -0,0 +1,8 @@
#pragma once
extern IRehldsApi* g_RehldsApi;
extern const RehldsFuncs_t* g_RehldsFuncs;
extern IRehldsHookchains* g_RehldsHookchains;
extern IRehldsServerStatic* g_RehldsSvs;
extern bool meta_init_rehlds_api();

View File

@ -1068,7 +1068,7 @@ bool MPlugin::unload(PLUG_LOADTIME now, PL_UNLOAD_REASON reason, PL_UNLOAD_REASO
// successful detach, or forced unload // successful detach, or forced unload
// Unmark registered commands for this plugin (by index number). // Unmark registered commands for this plugin (by index number).
g_regCmds->disable(index); g_regCmds->remove(index);
// Unmark registered cvars for this plugin (by index number). // Unmark registered cvars for this plugin (by index number).
g_regCvars->disable(index); g_regCvars->disable(index);

View File

@ -1,125 +1,82 @@
#include "precompiled.h" #include "precompiled.h"
// Init values. It would probably be more "proper" to use containers and MRegCmd::MRegCmd(char* cmd_name, REG_CMD_FN cmd_handler, MPlugin* cmd_plugin) : pfnCmd(cmd_handler), status(RG_VALID), plugid(cmd_plugin->index)
// constructors, rather than arrays and init-functions.
void MRegCmd::init(int idx)
{ {
index = idx; name = _strdup(name);
name = NULL; }
pfnCmd = NULL;
bool MRegCmd::call() const
{
pfnCmd();
return status != RG_INVALID;
}
void MRegCmd::disable()
{
pfnCmd = [](){};
plugid = 0; plugid = 0;
status = RG_INVALID; status = RG_INVALID;
} }
// Try to call the function. Relies on OS-specific routine to attempt char* MRegCmd::getname() const
// calling the function without generating a segfault from an unloaded
// plugin DLL.
// meta_errno values:
// - ME_BADREQ function disabled/invalid
// - ME_ARGUMENT function pointer is null
bool MRegCmd::call()
{ {
bool ret; return name;
// can we expect to call this function?
if (status != RG_VALID)
RETURN_ERRNO(false, ME_BADREQ);
if (!pfnCmd)
RETURN_ERRNO(false, ME_ARGUMENT);
// try to call this function
ret = os_safe_call(pfnCmd);
if (!ret)
{
META_DEBUG(4, ("Plugin reg_cmd '%s' called after unloaded; removed from list", name));
status = RG_INVALID;
pfnCmd = NULL;
// NOTE: we can't free the malloc'd space for the name, as that
// would just re-introduce the segfault problem..
} }
// meta_errno (if failed) is set already in os_safe_call() MRegCmdList::MRegCmdList() : m_list()
return ret; {
} }
MRegCmdList::MRegCmdList() : mlist(0), size(REG_CMD_GROWSIZE), endlist(0) MRegCmd *MRegCmdList::find(const char *name) const
{ {
mlist = (MRegCmd *)Q_malloc(size * sizeof(MRegCmd)); for (auto icmd : m_list)
// initialize array
for (int i = 0; i < size; i++)
mlist[i].init(i + 1); // 1-based index
endlist = 0;
}
// Try to find a registered function with the given name.
// meta_errno values:
// - ME_NOTFOUND couldn't find a matching function
MRegCmd *MRegCmdList::find(const char *findname) const
{ {
for (int i = 0; i < endlist; i++) if (!Q_stricmp(icmd->name, name))
{
if (!Q_stricmp(mlist[i].name, findname))
return &mlist[i];
}
RETURN_ERRNO(NULL, ME_NOTFOUND);
}
// Add the given name to the list and return the instance. This only
// writes the "name" to the new cmd; other fields are writtin by caller
// (meta_AddServerCommand).
// meta_errno values:
// - ME_NOMEM couldn't realloc or malloc for various parts
MRegCmd *MRegCmdList::add(const char *addname)
{
if (endlist == size)
{
// grow array
int newsize = size + REG_CMD_GROWSIZE;
META_DEBUG(6, ("Growing reg cmd list from %d to %d", size, newsize));
MRegCmd *temp = (MRegCmd *) realloc(mlist, newsize * sizeof(MRegCmd));
if (!temp)
{
META_ERROR("Couldn't grow registered command list to %d for '%s': %s", newsize, addname, strerror(errno));
RETURN_ERRNO(NULL, ME_NOMEM);
}
mlist = temp;
size = newsize;
// initialize new (unused) entries
for (int i = endlist; i < size; i++)
mlist[i].init(i + 1); // 1-based
}
MRegCmd *icmd = &mlist[endlist];
// Malloc space separately for the command name, because:
// - Can't point to memory loc in plugin (another segv waiting to
// happen).
// - Can't point to memory in mlist which might get moved later by
// realloc (again, segv).
icmd->name = Q_strdup(addname);
if (!icmd->name)
{
META_ERROR("Couldn't Q_strdup for adding reg cmd name '%s': %s", addname, strerror(errno));
RETURN_ERRNO(NULL, ME_NOMEM);
}
endlist++;
return icmd; return icmd;
} }
return nullptr;
}
MRegCmd *MRegCmdList::add(char *addname, REG_CMD_FN cmd_handler, MPlugin* cmd_plugin)
{
auto icmd = new MRegCmd(addname, cmd_handler, cmd_plugin);
m_list.push_back(icmd);
return icmd;
}
void MRegCmdList::remove(char* cmd_name)
{
for (auto it = m_list.begin(), end = m_list.end(); it != end; ++it) {
auto icmd = *it;
if (!Q_stricmp(icmd->name, cmd_name)) {
if (g_RehldsFuncs) {
g_RehldsFuncs->Cmd_RemoveCmd(cmd_name);
m_list.erase(it);
}
else {
icmd->disable();
}
}
}
}
// Disable any functions belonging to the given plugin (by index id). // Disable any functions belonging to the given plugin (by index id).
void MRegCmdList::disable(int plugin_id) const void MRegCmdList::remove(int owner_plugin_id)
{ {
for (int i = 0; i < size; i++) for (auto it = m_list.begin(), end = m_list.end(); it != end; ++it) {
{ auto icmd = *it;
if (mlist[i].plugid == plugin_id)
mlist[i].status = RG_INVALID; if (icmd->plugid == owner_plugin_id) {
if (g_RehldsFuncs) {
g_RehldsFuncs->Cmd_RemoveCmd(icmd->name);
m_list.erase(it);
}
else {
icmd->disable();
}
}
} }
} }
@ -127,56 +84,43 @@ void MRegCmdList::disable(int plugin_id) const
void MRegCmdList::show() const void MRegCmdList::show() const
{ {
int n = 0, a = 0; int n = 0, a = 0;
MRegCmd *icmd;
MPlugin *iplug;
char bplug[18 + 1]; // +1 for term null char bplug[18 + 1]; // +1 for term null
META_CONS("Registered plugin commands:"); META_CONS("Registered plugin commands:");
META_CONS(" %*s %-*s %-s", WIDTH_MAX_REG, "", sizeof(bplug) - 1, "plugin", "command"); META_CONS(" %*s %-*s %-s", WIDTH_MAX_REG, "", sizeof bplug - 1, "plugin", "command");
for (int i = 0; i < endlist; i++)
for (auto icmd : m_list)
{ {
icmd = &mlist[i];
if (icmd->status == RG_VALID) if (icmd->status == RG_VALID)
{ {
iplug = g_plugins->find(icmd->plugid); auto iplug = g_plugins->find(icmd->plugid);
if (iplug)
{ Q_strncpy(bplug, iplug ? iplug->desc : "(unknown)", sizeof bplug - 1);
Q_strncpy(bplug, iplug->desc, sizeof bplug - 1);
bplug[sizeof bplug - 1] = '\0'; bplug[sizeof bplug - 1] = '\0';
} }
else else
{
Q_strncpy(bplug, "(unknown)", sizeof bplug - 1);
bplug[sizeof bplug - 1] = '\0';
}
}
else
{ {
Q_strncpy(bplug, "(unloaded)", sizeof bplug - 1); Q_strncpy(bplug, "(unloaded)", sizeof bplug - 1);
bplug[sizeof bplug - 1] = '\0'; bplug[sizeof bplug - 1] = '\0';
} }
META_CONS(" [%*d] %-*s %-s", WIDTH_MAX_REG, icmd->index, sizeof(bplug) - 1, bplug, icmd->name); META_CONS(" [%*d] %-*s %-s", WIDTH_MAX_REG, ++n, sizeof bplug - 1, bplug, icmd->name);
if (icmd->status == RG_VALID) if (icmd->status == RG_VALID)
a++; a++;
n++;
} }
META_CONS("%d commands, %d available (%d allocated)", n, a, size); META_CONS("%d commands, %d available", n, a);
} }
// List all the registered commands for the given plugin id. // List all the registered commands for the given plugin id.
void MRegCmdList::show(int plugin_id) const void MRegCmdList::show(int plugin_id) const
{ {
int n = 0; int n = 0;
MRegCmd *icmd;
META_CONS("Registered commands:"); META_CONS("Registered commands:");
for (int i = 0; i < endlist; i++) for (auto icmd : m_list)
{ {
icmd = &mlist[i];
if (icmd->plugid != plugin_id) if (icmd->plugid != plugin_id)
continue; continue;

View File

@ -24,21 +24,23 @@ enum REG_STATUS { RG_INVALID, RG_VALID };
// Pointer to function registered by AddServerCommand. // Pointer to function registered by AddServerCommand.
typedef void (*REG_CMD_FN)(); typedef void (*REG_CMD_FN)();
class MPlugin;
// An individual registered function/command. // An individual registered function/command.
class MRegCmd { class MRegCmd {
public: public:
friend class MRegCmdList; MRegCmd(char* cmd_name, REG_CMD_FN cmd_handler, MPlugin* cmd_plugin);
bool call() const; // try to call the function
void disable();
char* getname() const;
private:
char *name; // space is malloc'd char *name; // space is malloc'd
REG_CMD_FN pfnCmd; // pointer to the function REG_CMD_FN pfnCmd; // pointer to the function
int plugid; // index id of corresponding plugin int plugid; // index id of corresponding plugin
REG_STATUS status; // whether corresponding plugin is loaded REG_STATUS status; // whether corresponding plugin is loaded
void init(int idx); // init values, as not using constructors friend class MRegCmdList;
bool call(); // try to call the function
private:
int index; // 1-based
}; };
// A list of registered commands. // A list of registered commands.
@ -46,21 +48,15 @@ class MRegCmdList {
public: public:
MRegCmdList(); MRegCmdList();
MRegCmd *find(const char *findname) const; // find by MRegCmd->name MRegCmd *find(const char *name) const; // find by MRegCmd->name
MRegCmd *add(const char *addname); MRegCmd *add(char *name, REG_CMD_FN cmd_handler, MPlugin* cmd_plugin);
void disable(int plugin_id) const; // change status to Invalid void remove(char* cmd_name);
void remove(int owner_plugin_id); // change status to Invalid
void show() const; // list all funcs to console void show() const; // list all funcs to console
void show(int plugin_id) const; // list given plugin's funcs to console void show(int plugin_id) const; // list given plugin's funcs to console
private: private:
MRegCmd *mlist; // malloc'd array of registered commands std::vector<MRegCmd *> m_list;
int size; // current size of list
int endlist; // index of last used entry
// Private; to satisfy -Weffc++ "has pointer data members but does
// not override" copy/assignment constructor.
void operator=(const MRegCmdList &src);
MRegCmdList(const MRegCmdList &src);
}; };
// An individual registered cvar. // An individual registered cvar.

View File

@ -12,14 +12,17 @@
#include <signal.h> // sigaction, etc #include <signal.h> // sigaction, etc
#endif #endif
#include <vector>
#include <array>
#include "jitasm.h" #include "jitasm.h"
#define CreateInterface mm_CreateInterface
#include <extdll.h> #include <extdll.h>
#include <studio.h> #include <studio.h>
#include <studioapi.h> #include <studioapi.h>
#include <rehlds_api.h>
#include <vector>
#include <array>
#include "osdep.h" #include "osdep.h"
#include "api_info.h" #include "api_info.h"
@ -32,7 +35,6 @@
#include "engine_api.h" #include "engine_api.h"
#include "game_support.h" #include "game_support.h"
#include "h_export.h" #include "h_export.h"
#include "linkent.h"
#include "meta_eiface.h" #include "meta_eiface.h"
#include "mreg.h" #include "mreg.h"
#include "meta_api.h" #include "meta_api.h"
@ -47,3 +49,7 @@
#include "enginecallbacks.h" #include "enginecallbacks.h"
#include "utils.h" #include "utils.h"
#include "callback_jit.h" #include "callback_jit.h"
#include "metamod_rehlds_api.h"
#undef CreateInterface
#include "linkent.h"

View File

@ -0,0 +1,5 @@
#include "precompiled.h"
#include "sys_shared.cpp"
#include "interface.cpp"
#include "crc32c.cpp"

View File

@ -1,74 +1,22 @@
#include "precompiled.h" #include "precompiled.h"
// "Register" support.
//
// This code is necessary to support the different "register" engine
// functions that can be called by plugins, so that the plugins can be
// safely unloaded without incurring segmentation faults.
//
// The problem was that, when a plugin called, say:
// REG_SVR_COMMAND("stub_version", svr_stub_version);
//
// the engine stored the address to the "stub_version" string, as well as,
// of course, the svr_stub_version() function pointer. If the plugin was
// unloaded, and dlclose()'d, the memory locations for both the string and
// function became invalid and unavailable. If the engine tried to
// strcmp() against the string, or tried to call the function pointer, it
// generated a segfault and killed the process.
//
// The solution used here is to create strings and functions allocated
// locally, in metamod.dll, which won't become unavailable or invalid due
// to a dlclose(). These local copies are passed to the engine instead of
// those allocated in the plugin DLLs. Thus, when a plugin is later
// unloaded and dlclose'd, the engine can still reference the memory
// locations of registered strings and functions, without segfaulting.
//
// Now, what happens with a registered string or function after a plugin is
// unloaded not quite as nice as I'd like.
//
// Console commands will print a message along the lines of:
// [metamod: command '%s' unavailable; plugin unloaded]
//
// Cvars though, have their value unaffected. This can be convenient when
// reloading a plugin, as the cvars keep their old values. However it can
// also be confusing, as there is no indication when viewing/setting cvars
// from the console that they no longer have an effect.
//
// Also note, the console commands for listing registered cmds and cvars
// will try to show the name of the associated plugin. However, this only
// works under linux; under windows it will say "(unknown)". Linux
// provides a routine "dladdr()" which can indicate (during Reg request)
// which plugin it's coming from, and this is stored with the cvar entry.
// Windows didn't seem to have a similar routine, and I couldn't find
// another way to get the information..
// Generic command handler, passed to the engine for any AddServerCommand
// calls made by the plugin. It finds the appropriate plugin function
// pointer to call based on CMD_ARGV(0).
void EXT_FUNC meta_command_handler() void EXT_FUNC meta_command_handler()
{ {
META_DEBUG(5, ("called: meta_command_handler; arg0=%s args='%s'", CMD_ARGV(0), CMD_ARGS()));
const char *cmd = CMD_ARGV(0); const char *cmd = CMD_ARGV(0);
if (!cmd)
{
META_ERROR("Null command name in meta_command_handler() ??");
return;
}
MRegCmd *icmd = g_regCmds->find(cmd); MRegCmd *icmd = g_regCmds->find(cmd);
if (!icmd) if (!icmd)
{ {
META_ERROR("Couldn't find registered plugin command: %s", cmd); META_ERROR("Couldn't find registered plugin command: %s", cmd);
return; return;
} }
if (icmd->call() != true) if (!icmd->call())
{ {
META_CONS("[metamod: command '%s' unavailable; plugin unloaded]", cmd); META_CONS("[metamod: command '%s' unavailable; plugin unloaded]", cmd);
} }
} }
// Replacement for engine routine AddServerCommand; called by plugins. // Replacement for engine routine AddServerCommand; called by plugins.
// Rather then handing the engine the plugin's command string and function // Rather then handing the engine the plugin's command string and function
// pointer (both of which are allocated in the plugin DLL), this hands the // pointer (both of which are allocated in the plugin DLL), this hands the
@ -85,10 +33,7 @@ void EXT_FUNC meta_AddServerCommand(char *cmd_name, void (*function)())
META_DEBUG(4, ("called: meta_AddServerCommand; cmd_name=%s, function=%d", cmd_name, function)); META_DEBUG(4, ("called: meta_AddServerCommand; cmd_name=%s, function=%d", cmd_name, function));
// try to find which plugin is registering this command // try to find which plugin is registering this command
if (!iplug) if (!iplug) {
{
// if this isn't supported on this OS, don't log an error
if (meta_errno != ME_OSNOTSUP)
META_ERROR("Failed to find memloc for regcmd '%s'", cmd_name); META_ERROR("Failed to find memloc for regcmd '%s'", cmd_name);
} }
@ -97,25 +42,15 @@ void EXT_FUNC meta_AddServerCommand(char *cmd_name, void (*function)())
if (!icmd) if (!icmd)
{ {
// If not found, add. // If not found, add.
icmd = g_regCmds->add(cmd_name); icmd = g_regCmds->add(cmd_name, function, iplug);
if (!icmd) if (!icmd)
{ {
// error details logged in add()
return; return;
} }
// Only register if not previously registered.. // Only register if not previously registered..
REG_SVR_COMMAND(icmd->name, meta_command_handler); REG_SVR_COMMAND(icmd->getname(), meta_command_handler);
} }
icmd->pfnCmd = function;
icmd->status = RG_VALID;
// Store which plugin this is for, if we know. We can use '0' for
// unknown plugin, since plugin index starts at 1.
if (iplug)
icmd->plugid = iplug->index;
else
icmd->plugid = 0;
} }