2
0
mirror of https://github.com/rehlds/metamod-r.git synced 2024-12-27 07:05:34 +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 "model.h"
#define REHLDS_API_VERSION_MAJOR 2
#define REHLDS_API_VERSION_MINOR 13
#define REHLDS_API_VERSION_MAJOR 3
#define REHLDS_API_VERSION_MINOR 1
//Steam_NotifyClientConnect hook
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;
//EV_PlayReliableEvent hook
typedef IVoidHookChain<IGameClient *, int, short unsigned int, float, struct event_args_s *> IRehldsHook_EV_PlayReliableEvent;
typedef IVoidHookChainRegistry<IGameClient *, int, short unsigned int, float, struct event_args_s *> IRehldsHookRegistry_EV_PlayReliableEvent;
typedef IVoidHookChain<IGameClient *, int, unsigned short, float, struct event_args_s *> IRehldsHook_EV_PlayReliableEvent;
typedef IVoidHookChainRegistry<IGameClient *, int, unsigned short, float, struct event_args_s *> IRehldsHookRegistry_EV_PlayReliableEvent;
//SV_StartSound hook
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)();
void(*Log)(const char* prefix, const char* msg);
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);
void(*MSG_StartBitWriting)(sizebuf_t *buf);
void(*MSG_WriteBits)(uint32 data, int numbits);
@ -280,7 +280,9 @@ struct RehldsFuncs_t {
void(*Steam_NotifyClientDisconnect)(IGameClient* cl);
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);
void (* SV_UpdateUserInfo)(IGameClient *pGameClient);
void(*SV_UpdateUserInfo)(IGameClient *pGameClient);
bool(*StripUnprintableAndSpace)(char *pch);
void(*Cmd_RemoveCmd)(char *cmd_name);
};
class IRehldsApi {

View File

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

View File

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

View File

@ -20,7 +20,7 @@ void META_CONS(const char *fmt, ...)
unsigned int len;
va_start(ap, fmt);
len = Q_vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
len = Q_vsnprintf(buf, sizeof buf, fmt, ap);
va_end(ap);
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
// 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).
g_regCvars->disable(index);

View File

@ -1,125 +1,82 @@
#include "precompiled.h"
// Init values. It would probably be more "proper" to use containers and
// constructors, rather than arrays and init-functions.
void MRegCmd::init(int idx)
MRegCmd::MRegCmd(char* cmd_name, REG_CMD_FN cmd_handler, MPlugin* cmd_plugin) : pfnCmd(cmd_handler), status(RG_VALID), plugid(cmd_plugin->index)
{
index = idx;
name = NULL;
pfnCmd = NULL;
name = _strdup(name);
}
bool MRegCmd::call() const
{
pfnCmd();
return status != RG_INVALID;
}
void MRegCmd::disable()
{
pfnCmd = [](){};
plugid = 0;
status = RG_INVALID;
}
// Try to call the function. Relies on OS-specific routine to attempt
// 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()
char* MRegCmd::getname() const
{
bool ret;
// 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()
return ret;
return name;
}
MRegCmdList::MRegCmdList() : mlist(0), size(REG_CMD_GROWSIZE), endlist(0)
MRegCmdList::MRegCmdList() : m_list()
{
mlist = (MRegCmd *)Q_malloc(size * sizeof(MRegCmd));
// 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
MRegCmd *MRegCmdList::find(const char *name) const
{
for (int i = 0; i < endlist; i++)
for (auto icmd : m_list)
{
if (!Q_stricmp(mlist[i].name, findname))
return &mlist[i];
if (!Q_stricmp(icmd->name, name))
return icmd;
}
RETURN_ERRNO(NULL, ME_NOTFOUND);
return nullptr;
}
// 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)
MRegCmd *MRegCmdList::add(char *addname, REG_CMD_FN cmd_handler, MPlugin* cmd_plugin)
{
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++;
auto icmd = new MRegCmd(addname, cmd_handler, cmd_plugin);
m_list.push_back(icmd);
return icmd;
}
// Disable any functions belonging to the given plugin (by index id).
void MRegCmdList::disable(int plugin_id) const
void MRegCmdList::remove(char* cmd_name)
{
for (int i = 0; i < size; i++)
{
if (mlist[i].plugid == plugin_id)
mlist[i].status = RG_INVALID;
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).
void MRegCmdList::remove(int owner_plugin_id)
{
for (auto it = m_list.begin(), end = m_list.end(); it != end; ++it) {
auto icmd = *it;
if (icmd->plugid == owner_plugin_id) {
if (g_RehldsFuncs) {
g_RehldsFuncs->Cmd_RemoveCmd(icmd->name);
m_list.erase(it);
}
else {
icmd->disable();
}
}
}
}
@ -127,28 +84,19 @@ void MRegCmdList::disable(int plugin_id) const
void MRegCmdList::show() const
{
int n = 0, a = 0;
MRegCmd *icmd;
MPlugin *iplug;
char bplug[18 + 1]; // +1 for term null
META_CONS("Registered plugin commands:");
META_CONS(" %*s %-*s %-s", WIDTH_MAX_REG, "", sizeof(bplug) - 1, "plugin", "command");
for (int i = 0; i < endlist; i++)
META_CONS(" %*s %-*s %-s", WIDTH_MAX_REG, "", sizeof bplug - 1, "plugin", "command");
for (auto icmd : m_list)
{
icmd = &mlist[i];
if (icmd->status == RG_VALID)
{
iplug = g_plugins->find(icmd->plugid);
if (iplug)
{
Q_strncpy(bplug, iplug->desc, sizeof bplug - 1);
bplug[sizeof bplug - 1] = '\0';
}
else
{
Q_strncpy(bplug, "(unknown)", sizeof bplug - 1);
bplug[sizeof bplug - 1] = '\0';
}
auto iplug = g_plugins->find(icmd->plugid);
Q_strncpy(bplug, iplug ? iplug->desc : "(unknown)", sizeof bplug - 1);
bplug[sizeof bplug - 1] = '\0';
}
else
{
@ -156,27 +104,23 @@ void MRegCmdList::show() const
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)
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.
void MRegCmdList::show(int plugin_id) const
{
int n = 0;
MRegCmd *icmd;
META_CONS("Registered commands:");
for (int i = 0; i < endlist; i++)
for (auto icmd : m_list)
{
icmd = &mlist[i];
if (icmd->plugid != plugin_id)
continue;

View File

@ -24,21 +24,23 @@ enum REG_STATUS { RG_INVALID, RG_VALID };
// Pointer to function registered by AddServerCommand.
typedef void (*REG_CMD_FN)();
class MPlugin;
// An individual registered function/command.
class MRegCmd {
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
REG_CMD_FN pfnCmd; // pointer to the function
int plugid; // index id of corresponding plugin
REG_STATUS status; // whether corresponding plugin is loaded
void init(int idx); // init values, as not using constructors
bool call(); // try to call the function
private:
int index; // 1-based
friend class MRegCmdList;
};
// A list of registered commands.
@ -46,21 +48,15 @@ class MRegCmdList {
public:
MRegCmdList();
MRegCmd *find(const char *findname) const; // find by MRegCmd->name
MRegCmd *add(const char *addname);
void disable(int plugin_id) const; // change status to Invalid
MRegCmd *find(const char *name) const; // find by MRegCmd->name
MRegCmd *add(char *name, REG_CMD_FN cmd_handler, MPlugin* cmd_plugin);
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(int plugin_id) const; // list given plugin's funcs to console
private:
MRegCmd *mlist; // malloc'd array of registered commands
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);
std::vector<MRegCmd *> m_list;
};
// An individual registered cvar.

View File

@ -12,14 +12,17 @@
#include <signal.h> // sigaction, etc
#endif
#include <vector>
#include <array>
#include "jitasm.h"
#define CreateInterface mm_CreateInterface
#include <extdll.h>
#include <studio.h>
#include <studioapi.h>
#include <vector>
#include <array>
#include <rehlds_api.h>
#include "osdep.h"
#include "api_info.h"
@ -32,7 +35,6 @@
#include "engine_api.h"
#include "game_support.h"
#include "h_export.h"
#include "linkent.h"
#include "meta_eiface.h"
#include "mreg.h"
#include "meta_api.h"
@ -47,3 +49,7 @@
#include "enginecallbacks.h"
#include "utils.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"
// "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()
{
META_DEBUG(5, ("called: meta_command_handler; arg0=%s args='%s'", CMD_ARGV(0), CMD_ARGS()));
const char *cmd = CMD_ARGV(0);
if (!cmd)
{
META_ERROR("Null command name in meta_command_handler() ??");
return;
}
MRegCmd *icmd = g_regCmds->find(cmd);
if (!icmd)
{
META_ERROR("Couldn't find registered plugin command: %s", cmd);
return;
}
if (icmd->call() != true)
if (!icmd->call())
{
META_CONS("[metamod: command '%s' unavailable; plugin unloaded]", cmd);
}
}
// Replacement for engine routine AddServerCommand; called by plugins.
// 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
@ -85,11 +33,8 @@ void EXT_FUNC meta_AddServerCommand(char *cmd_name, void (*function)())
META_DEBUG(4, ("called: meta_AddServerCommand; cmd_name=%s, function=%d", cmd_name, function));
// try to find which plugin is registering this command
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);
if (!iplug) {
META_ERROR("Failed to find memloc for regcmd '%s'", cmd_name);
}
// See if this command was previously registered, ie a "reloaded" plugin.
@ -97,25 +42,15 @@ void EXT_FUNC meta_AddServerCommand(char *cmd_name, void (*function)())
if (!icmd)
{
// If not found, add.
icmd = g_regCmds->add(cmd_name);
icmd = g_regCmds->add(cmd_name, function, iplug);
if (!icmd)
{
// error details logged in add()
return;
}
// 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;
}