2
0
mirror of https://github.com/rehlds/metamod-r.git synced 2025-02-14 07:39:03 +03:00
metamod-r/metamod/src/reg_support.cpp

200 lines
7.1 KiB
C++
Raw Normal View History

2016-07-26 07:22:47 +07:00
#include "precompiled.h"
2016-07-04 12:07:29 +06:00
// "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..
2016-07-04 12:07:29 +06:00
// 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).
2017-01-06 22:33:36 +03:00
void EXT_FUNC meta_command_handler()
2016-07-26 07:22:47 +07:00
{
2016-07-04 12:07:29 +06:00
META_DEBUG(5, ("called: meta_command_handler; arg0=%s args='%s'", CMD_ARGV(0), CMD_ARGS()));
2016-07-26 23:31:47 +07:00
const char *cmd = CMD_ARGV(0);
if (!cmd)
{
META_ERROR("Null command name in meta_command_handler() ??");
2016-07-04 12:07:29 +06:00
return;
}
2016-07-26 23:31:47 +07:00
MRegCmd *icmd = g_regCmds->find(cmd);
if (!icmd)
{
META_ERROR("Couldn't find registered plugin command: %s", cmd);
2016-07-04 12:07:29 +06:00
return;
}
2016-07-26 23:31:47 +07:00
2016-07-30 02:03:01 +03:00
if (icmd->call() != true)
2016-07-26 23:31:47 +07:00
{
2016-07-04 12:07:29 +06:00
META_CONS("[metamod: command '%s' unavailable; plugin unloaded]", cmd);
2016-07-26 23:31:47 +07:00
}
2016-07-04 12:07:29 +06:00
}
// 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
// engine a command string and function pointer allocated locally (in the
// metamod DLL).
//
2016-07-26 23:31:47 +07:00
// The string handed to the engine is just a Q_strdup() of the plugin's
2016-07-04 12:07:29 +06:00
// string. The function pointer handed to the engine is actually a pointer
// to a generic command-handler function (see above).
2017-01-06 22:33:36 +03:00
void EXT_FUNC meta_AddServerCommand(char *cmd_name, void (*function)())
2016-07-26 07:22:47 +07:00
{
2017-01-07 01:24:40 +03:00
MPlugin *iplug = g_plugins->find_memloc(function);
2016-07-04 12:07:29 +06:00
META_DEBUG(4, ("called: meta_AddServerCommand; cmd_name=%s, function=%d", cmd_name, function));
// try to find which plugin is registering this command
2017-01-07 01:24:40 +03:00
if (!iplug)
2016-07-26 23:31:47 +07:00
{
2016-07-04 12:07:29 +06:00
// if this isn't supported on this OS, don't log an error
2016-07-26 07:22:47 +07:00
if (meta_errno != ME_OSNOTSUP)
META_ERROR("Failed to find memloc for regcmd '%s'", cmd_name);
2016-07-04 12:07:29 +06:00
}
// See if this command was previously registered, ie a "reloaded" plugin.
2017-01-07 01:24:40 +03:00
auto icmd = g_regCmds->find(cmd_name);
2016-07-26 23:31:47 +07:00
if (!icmd)
{
2016-07-04 12:07:29 +06:00
// If not found, add.
icmd = g_regCmds->add(cmd_name);
2016-07-26 23:31:47 +07:00
if (!icmd)
{
2016-07-04 12:07:29 +06:00
// error details logged in add()
return;
}
2016-07-26 23:31:47 +07:00
2016-07-04 12:07:29 +06:00
// Only register if not previously registered..
REG_SVR_COMMAND(icmd->name, meta_command_handler);
}
icmd->pfnCmd = function;
icmd->status = RG_VALID;
2016-07-04 12:07:29 +06:00
// Store which plugin this is for, if we know. We can use '0' for
// unknown plugin, since plugin index starts at 1.
2016-07-26 07:22:47 +07:00
if (iplug)
2016-07-04 12:07:29 +06:00
icmd->plugid = iplug->index;
else
icmd->plugid = 0;
}
// Replacement for engine routine CVarRegister; called by plugins. Rather
// then handing the engine the plugin's cvar structure (which is allocated
// in the plugin DLL), this hands the engine a cvar structure allocated
// locally (in the metamod DLL).
//
// The cvar handed to the engine is globally allocated in the metamod.dll;
2016-07-26 23:31:47 +07:00
// the "name" and "string" fields are Q_strdup()'s of the plugin's strings.
2016-07-04 12:07:29 +06:00
// Note that, once this is done, the cvar_t allocated in the plugin is no
// longer used for _anything_. As long as everything sets/gets the cvar
// values via the engine functions, this will work fine. If the plugin
// code tries to _directly_ read/set the fields of its own cvar structures,
// it will fail to work properly.
2017-01-06 22:33:36 +03:00
void EXT_FUNC meta_CVarRegister(cvar_t *pCvar)
2016-07-26 07:22:47 +07:00
{
2017-01-07 01:24:40 +03:00
MPlugin *iplug = g_plugins->find_memloc(pCvar);
2016-07-04 12:07:29 +06:00
META_DEBUG(4, ("called: meta_CVarRegister; name=%s", pCvar->name));
// try to find which plugin is registering this cvar
2017-01-07 01:24:40 +03:00
if (!iplug)
2016-07-26 23:31:47 +07:00
{
META_DEBUG(1, ("Failed to find memloc for regcvar '%s'", pCvar->name));
2016-07-04 12:07:29 +06:00
}
// See if this cvar was previously registered, ie a "reloaded" plugin.
2017-01-07 01:24:40 +03:00
auto icvar = g_regCvars->find(pCvar->name);
2016-07-26 23:31:47 +07:00
if (!icvar)
{
2016-07-04 12:07:29 +06:00
// If not found, add.
icvar = g_regCvars->add(pCvar->name);
2016-07-26 23:31:47 +07:00
if (!icvar)
{
2016-07-04 12:07:29 +06:00
// error details logged in add()
return;
}
2016-07-26 23:31:47 +07:00
2016-07-04 12:07:29 +06:00
// Reset to given value
icvar->set(pCvar);
CVAR_REGISTER(icvar->data);
}
2016-07-26 23:31:47 +07:00
2016-07-04 12:07:29 +06:00
// Note: if not a new cvar, then we don't set the values, and just keep
// the pre-existing value.
2016-07-26 07:22:47 +07:00
icvar->status = RG_VALID;
2016-07-26 23:31:47 +07:00
2016-07-04 12:07:29 +06:00
// Store which plugin this is for, if we know. Use '0' for unknown
// plugin, as plugin index starts at 1.
2016-07-26 07:22:47 +07:00
if (iplug)
2016-07-04 12:07:29 +06:00
icvar->plugid = iplug->index;
else
icvar->plugid = 0;
}
// Replacement for engine routine RegUserMsg; called by plugins. Rather
// than handing the engine the plugin's string (which is allocated in the
// plugin DLL), this hands the engine a string allocated from the stack.
//
// Note that while the above two functions maintain a list of registered
// strings/funcs/cvars from the plugins, this is not done here as I
// couldn't identify a need for it. Since the engine function merely maps
// a string to an int (msgid) for subsequent use in MessageBegin/etc,
// there's no function that needs to be provided and caught (like the
// commands) nor any useful actions to perform upon plugin unload (like the
// commands and cvars). This merely provides differently located storage
// for the string.
2017-01-06 22:33:36 +03:00
int EXT_FUNC meta_RegUserMsg(const char *pszName, int iSize)
{
2016-07-26 23:31:47 +07:00
char *cp = Q_strdup(pszName);
return REG_USER_MSG(cp, iSize);
2016-07-04 12:07:29 +06:00
}
// Intercept and record queries
2017-01-06 22:33:36 +03:00
void EXT_FUNC meta_QueryClientCvarValue(const edict_t *player, const char *cvarName)
2016-07-04 13:11:20 +06:00
{
2016-07-30 02:03:01 +03:00
g_players.set_player_cvar_query(player, cvarName);
2017-01-06 22:33:36 +03:00
g_engfuncs.pfnQueryClientCvarValue(player, cvarName);
2016-07-04 12:07:29 +06:00
}