Skip to content

Commit

Permalink
more plugin hooks and helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
wootguy committed Oct 28, 2024
1 parent df1a867 commit d780ac9
Show file tree
Hide file tree
Showing 16 changed files with 268 additions and 44 deletions.
2 changes: 1 addition & 1 deletion dlls/CBaseEntity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ CBaseEntity* CBaseEntity::Create(const char* szName, const Vector& vecOrigin, co
edict_t* pent;
CBaseEntity* pEntity;

pent = CREATE_NAMED_ENTITY(MAKE_STRING(szName));
pent = CREATE_NAMED_ENTITY(ALLOC_STRING(szName)); // not using MAKE_STRING in case an unloaded plugin called this func
if (FNullEnt(pent))
{
ALERT(at_console, UTIL_VarArgs("NULL Ent '%s' in Create!\n", szName));
Expand Down
6 changes: 3 additions & 3 deletions dlls/CBasePlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1687,7 +1687,7 @@ void CBasePlayer::LeaveObserver()
//
void CBasePlayer::PlayerUse ( void )
{
CALL_HOOKS_VOID(&HLCOOP_PLUGIN_HOOKS::pfnPlayerUse, this);
CALL_HOOKS_VOID(pfnPlayerUse, this);

if ( IsObserver() )
return;
Expand Down Expand Up @@ -2225,7 +2225,7 @@ void CBasePlayer::UpdateStatusBar()

void CBasePlayer::PreThink(void)
{
CALL_HOOKS_VOID(&HLCOOP_PLUGIN_HOOKS::pfnPlayerPreThink, this);
CALL_HOOKS_VOID(pfnPlayerPreThink, this);

int buttonsChanged = (m_afButtonLast ^ pev->button); // These buttons have changed this frame

Expand Down Expand Up @@ -2997,7 +2997,7 @@ void CBasePlayer :: UpdatePlayerSound ( void )

void CBasePlayer::PostThink()
{
CALL_HOOKS_VOID(&HLCOOP_PLUGIN_HOOKS::pfnPlayerPostThink, this);
CALL_HOOKS_VOID(pfnPlayerPostThink, this);

if ( g_fGameOver )
goto pt_end; // intermission or finale
Expand Down
2 changes: 1 addition & 1 deletion dlls/CWorld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ void CWorld::Precache(void)
CVAR_SET_FLOAT("mp_defaultteam", 0);
}

CALL_HOOKS_VOID(&HLCOOP_PLUGIN_HOOKS::pfnMapInit);
CALL_HOOKS_VOID(pfnMapInit);
}


Expand Down
17 changes: 16 additions & 1 deletion dlls/PluginHooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ struct HLCOOP_PLUGIN_HOOKS {
// called after map entities are spawned
HOOK_RETURN_DATA (*pfnMapActivate)();

// called when the server is about to load a new map
HOOK_RETURN_DATA (*pfnMapChange)();

// called before the player PreThink function
HOOK_RETURN_DATA (*pfnPlayerPreThink)(CBasePlayer* pPlayer);

Expand All @@ -36,13 +39,25 @@ struct HLCOOP_PLUGIN_HOOKS {
// called when a client connects to the server. Return 0 to reject the connection with the given reason.
HOOK_RETURN_DATA (*pfnClientConnect)(edict_t* pEntity, const char* pszName, const char* pszAddress, char szRejectReason[128]);

// called when a client disconnects from the server.
HOOK_RETURN_DATA(*pfnClientDisconnect)(edict_t* pEntity);

// called when a player is fully connected to the server and is about to spawn
HOOK_RETURN_DATA (*pfnClientPutInServer)(CBasePlayer* pPlayer);

// called when a player changes model, name, colors, etc.
HOOK_RETURN_DATA (*pfnClientUserInfoChanged)(edict_t* pPlayer, char* infobuffer);
};

EXPORT void RegisterPlugin(void* plugin, HLCOOP_PLUGIN_HOOKS* hooks, const char* name);

// must call this instead of registering cvars directly or else the game crashes when the plugin unloads
// and any cvar is used
EXPORT cvar_t* RegisterPluginCVar(void* plugin, char* name, char* strDefaultValue, int intDefaultValue, int flags);

EXPORT void RegisterPlugin(void* plugin, HLCOOP_PLUGIN_HOOKS* hooks, const char* name);
// must call this instead of registering commands directly or else the game crashes when the plugin unloads
// and the registered command is used
EXPORT void RegisterPluginCommand(void* plugin, char* cmd, void (*function)(void));

// boilerplate for PluginInit functions
// must be inline so that plugins don't reference the game definition of HLCOOP_API_VERSION
Expand Down
97 changes: 92 additions & 5 deletions dlls/PluginManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,25 @@
PluginManager g_pluginManager;

#define MAX_PLUGIN_CVARS 256
#define MAX_PLUGIN_COMMANDS 256

struct ExternalCvar {
int pluginId;
cvar_t cvar;
};

struct ExternalCommand {
int pluginId;
char name[64];
void (*function)(void);
};

ExternalCvar g_plugin_cvars[MAX_PLUGIN_CVARS];
int g_plugin_cvar_count = 0;

ExternalCommand g_plugin_commands[MAX_PLUGIN_COMMANDS];
int g_plugin_command_count = 0;

int g_plugin_id = 0;

#ifdef _WIN32
Expand Down Expand Up @@ -193,8 +203,11 @@ void PluginManager::UpdateServerPlugins(bool forceUpdate) {
{
lineNum++;

line = trimSpaces(line);
if (line.empty() || line[0] == '/' || line[0] == '#') {
int endPos = line.find_first_of("#/");
if (endPos != -1)
line = trimSpaces(line.substr(0, endPos));

if (line.empty()) {
continue;
}

Expand Down Expand Up @@ -302,7 +315,7 @@ void PluginManager::ReloadPlugins() {
void PluginManager::ListPlugins(edict_t* plr) {
std::vector<std::string> lines;

bool isAdmin = !plr;
bool isAdmin = !plr || AdminLevel(plr) > ADMIN_NO;

lines.push_back(UTIL_VarArgs("\n %-20s %-8s %-44s\n", "Name", "Type", isAdmin ? "File path" : ""));
lines.push_back("--------------------------------------------------------------------------------\n");
Expand All @@ -311,7 +324,7 @@ void PluginManager::ListPlugins(edict_t* plr) {
const Plugin& plugin = plugins[i];

const char* type = plugin.isMapPlugin ? "MAP" : "SERVER";

if (isAdmin) {
lines.push_back(UTIL_VarArgs("%2d) %-20s %-8s %-44s\n", i + 1, plugin.name, type, plugin.fpath.c_str()));
}
Expand All @@ -320,6 +333,12 @@ void PluginManager::ListPlugins(edict_t* plr) {
lines.push_back(UTIL_VarArgs("%2d) %-20s %-8s\n", i + 1, plugin.name, type));
}
}

if (isAdmin) {
lines.push_back(UTIL_VarArgs("\n Registered %d cvars, %d commands\n",
g_plugin_cvar_count, g_plugin_command_count));
}

lines.push_back("--------------------------------------------------------------------------------\n");

for (std::string& line : lines) {
Expand All @@ -332,6 +351,16 @@ void PluginManager::ListPlugins(edict_t* plr) {
}
}

Plugin* PluginManager::FindPlugin(int id) {
for (Plugin& plugin : plugins) {
if (plugin.id == id) {
return &plugin;
}
}

return NULL;
}

ENTITYINIT PluginManager::GetCustomEntityInitFunc(const char* pname) {
for (const Plugin& plugin : plugins) {
ENTITYINIT initFunc = (ENTITYINIT)GetProcAddress((HMODULE)plugin.h_module, pname);
Expand All @@ -358,7 +387,7 @@ cvar_t* RegisterPluginCVar(void* pluginptr, char* name, char* strDefaultValue, i

cvar_t* existing = CVAR_GET_POINTER(name);
if (existing) {
g_engfuncs.pfnServerPrint(UTIL_VarArgs("Plugin cvar already registered: %s\n", name));
//g_engfuncs.pfnServerPrint(UTIL_VarArgs("Plugin cvar already registered: %s\n", name));

// update the owner of the cvar
for (int i = 0; i < MAX_PLUGIN_CVARS; i++) {
Expand Down Expand Up @@ -386,6 +415,64 @@ cvar_t* RegisterPluginCVar(void* pluginptr, char* name, char* strDefaultValue, i
return CVAR_GET_POINTER(name);
}

void ExternalPluginCommand() {
const char* cmd = CMD_ARGV(0);

ExternalCommand* ecmd = NULL;

for (int i = 0; i < g_plugin_command_count; i++) {
if (!strcmp(g_plugin_commands[i].name, cmd)) {
ecmd = &g_plugin_commands[i];
break;
}
}

if (!ecmd) {
// should never happen
g_engfuncs.pfnServerPrint(UTIL_VarArgs("Unrecognized external plugin command: %s\n", cmd));
return;
}

Plugin* plugin = g_pluginManager.FindPlugin(ecmd->pluginId);

if (!plugin) {
g_engfuncs.pfnServerPrint(UTIL_VarArgs("Command from unloaded plugin can't be called: %s\n", cmd));
return;
}

ecmd->function();
}

void RegisterPluginCommand(void* pluginptr, char* cmd, void (*function)(void)) {
if (!pluginptr) {
return;
}

Plugin* plugin = (Plugin*)pluginptr;

if (g_plugin_command_count >= MAX_PLUGIN_COMMANDS) {
ALERT(at_error, "Plugin command limit exceeded! Failed to register: %s\n", cmd);
return;
}

for (int i = 0; i < g_plugin_command_count; i++) {
if (!strcmp(g_plugin_commands[i].name, cmd)) {
//g_engfuncs.pfnServerPrint(UTIL_VarArgs("Plugin command already registered: %s\n", cmd));
g_plugin_commands[i].pluginId = plugin->id;
g_plugin_commands[i].function = function;
return;
}
}

ExternalCommand& ecmd = g_plugin_commands[g_plugin_command_count];
ecmd.pluginId = plugin->id;
ecmd.function = function;
strcpy_safe(ecmd.name, cmd, sizeof(ecmd.name));
g_plugin_command_count++;

g_engfuncs.pfnAddServerCommand(ecmd.name, ExternalPluginCommand);
}

void RegisterPlugin(void* pluginptr, HLCOOP_PLUGIN_HOOKS* hooks, const char* name) {
if (!pluginptr) {
return;
Expand Down
6 changes: 4 additions & 2 deletions dlls/PluginManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ struct Plugin {
typedef void(*ENTITYINIT)(struct entvars_s*);

#define CALL_HOOKS_VOID(...) \
if (g_pluginManager.CallHooks(__VA_ARGS__).code & HOOKBIT_OVERRIDE) { return; }
if (g_pluginManager.CallHooks(&HLCOOP_PLUGIN_HOOKS::__VA_ARGS__).code & HOOKBIT_OVERRIDE) { return; }

#define CALL_HOOKS(type, ...) { \
HOOK_RETURN_DATA ret = g_pluginManager.CallHooks(__VA_ARGS__); \
HOOK_RETURN_DATA ret = g_pluginManager.CallHooks(&HLCOOP_PLUGIN_HOOKS::__VA_ARGS__); \
if (ret.code & HOOKBIT_OVERRIDE) { \
return (type)ret.data; \
} \
Expand Down Expand Up @@ -51,6 +51,8 @@ class PluginManager {
// print loaded server and map plugins to console or client
void ListPlugins(edict_t* plr);

Plugin* FindPlugin(int id);

template<typename Func, typename... Args>
HOOK_RETURN_DATA CallHooks(Func hookFunction, Args&&... args) {
HOOK_RETURN_DATA totalRet = {0, 0};
Expand Down
1 change: 0 additions & 1 deletion dlls/Scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ void Scheduler::RemoveTimers(const char* owner) {
for (int i = 0; i < (int)functions.size(); i++) {
if (strcmp(functions[i].owner, owner)) {
newFuncs.push_back(functions[i]);
return;
}
}

Expand Down
14 changes: 7 additions & 7 deletions dlls/TextMenu.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,24 @@ class TextMenu {

// use this to create menus for each player.
// When creating a menu for all players, pass NULL for player.
static TextMenu* init(edict_t* player, TextMenuCallback callback);
EXPORT static TextMenu* init(edict_t* player, TextMenuCallback callback);

static TextMenu* init(edict_t* player, EntityTextMenuCallback callback, CBaseEntity* ent);
EXPORT static TextMenu* init(edict_t* player, EntityTextMenuCallback callback, CBaseEntity* ent);

void SetTitle(std::string title);
EXPORT void SetTitle(std::string title);

void AddItem(std::string displayText, std::string optionData);
EXPORT void AddItem(std::string displayText, std::string optionData);

// set player to NULL to send to all players.
// This should be the same target as was used with initMenuForPlayer
// paging not supported yet
void Open(uint8_t duration, uint8_t page, edict_t* player);
EXPORT void Open(uint8_t duration, uint8_t page, edict_t* player);

// don't call directly. This is triggered by global hook functions
void handleMenuMessage(int msg_dest, edict_t* ed);
EXPORT void handleMenuMessage(int msg_dest, edict_t* ed);

// don't call directly. This is triggered by global hook functions
void handleMenuselectCmd(edict_t* pEntity, int selection);
EXPORT void handleMenuselectCmd(edict_t* pEntity, int selection);

private:
void initAnon(TextMenuCallback callback);
Expand Down
3 changes: 3 additions & 0 deletions dlls/cbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,9 @@ void SaveReadFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseD
restoreHelper.ReadFields( pname, pBaseData, pFields, fieldCount );
}

EHANDLE::EHANDLE(edict_t* pent) {
Set(pent);
}

edict_t * EHANDLE::GetEdict( void )
{
Expand Down
2 changes: 2 additions & 0 deletions dlls/cbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ class EXPORT EHANDLE
edict_t *m_pent;
int m_serialnumber;
public:
EHANDLE() : m_pent(0), m_serialnumber(0) {}
EHANDLE(edict_t* pent);
edict_t *GetEdict( void );
CBaseEntity *GetEntity( void );
edict_t *Set( edict_t *pent );
Expand Down
12 changes: 9 additions & 3 deletions dlls/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ called when a player connects to a server
*/
BOOL ClientConnect( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] )
{
CALL_HOOKS(BOOL, &HLCOOP_PLUGIN_HOOKS::pfnClientConnect, pEntity, pszName, pszAddress, szRejectReason);
CALL_HOOKS(BOOL, pfnClientConnect, pEntity, pszName, pszAddress, szRejectReason);

return g_pGameRules->ClientConnected( pEntity, pszName, pszAddress, szRejectReason );

Expand All @@ -113,6 +113,8 @@ GLOBALS ASSUMED SET: g_fGameOver
*/
void ClientDisconnect( edict_t *pEntity )
{
CALL_HOOKS_VOID(pfnClientDisconnect, pEntity);

if (mp_debugmsg.value) {
writeNetworkMessageHistory(std::string(STRING(pEntity->v.netname))
+ " dropped on map " + STRING(gpGlobals->mapname));
Expand Down Expand Up @@ -283,7 +285,7 @@ void ClientPutInServer( edict_t *pEntity )
// Reset interpolation during first frame
pPlayer->pev->effects |= EF_NOINTERP;

CALL_HOOKS_VOID(&HLCOOP_PLUGIN_HOOKS::pfnClientPutInServer, pPlayer);
CALL_HOOKS_VOID(pfnClientPutInServer, pPlayer);

// Allocate a CBasePlayer for pev, and call spawn
pPlayer->Spawn();
Expand All @@ -308,6 +310,8 @@ void ClientUserInfoChanged( edict_t *pEntity, char *infobuffer )
if ( !pEntity->pvPrivateData )
return;

CALL_HOOKS_VOID(pfnClientUserInfoChanged, pEntity, infobuffer);

// msg everyone if someone changes their name, and it isn't the first time (changing no name to current name)
if ( pEntity->v.netname && STRING(pEntity->v.netname)[0] != 0 && !FStrEq( STRING(pEntity->v.netname), g_engfuncs.pfnInfoKeyValue( infobuffer, "name" )) )
{
Expand Down Expand Up @@ -421,6 +425,8 @@ void ServerDeactivate( void )
// below and try keenrace instead.
// SHA-1: 0c95b51652eda12e0b268631d1421634614c661f
// fix physics breaking after long uptime

CALL_HOOKS_VOID(pfnMapChange);
}

#include "lagcomp.h"
Expand Down Expand Up @@ -689,7 +695,7 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )

LoadAdminList();

CALL_HOOKS_VOID(&HLCOOP_PLUGIN_HOOKS::pfnMapActivate);
CALL_HOOKS_VOID(pfnMapActivate);
}

/*
Expand Down
2 changes: 1 addition & 1 deletion dlls/client_commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ void ClientCommand(edict_t* pEntity)
return;
}

CALL_HOOKS_VOID(&HLCOOP_PLUGIN_HOOKS::pfnClientCommand, pPlayer);
CALL_HOOKS_VOID(pfnClientCommand, pPlayer);

TextMenuClientCommandHook(pEntity);

Expand Down
2 changes: 1 addition & 1 deletion dlls/mstream.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once
#include <stdint.h>

class mstream
class EXPORT mstream
{
public:

Expand Down
Loading

0 comments on commit d780ac9

Please sign in to comment.