From 0d5424c5b4d5afeb904bf98735a3ae42a2670794 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 18 Apr 2005 23:33:44 +0000 Subject: [PATCH] Sample plugin can now do anything server plugins can (except NetworkIDValidated callback) --HG-- extra : convert_revision : svn%3Ac2935e3e-5518-0410-8daf-afa5dab7d4e3/trunk%4023 --- sourcemm/sample_mm/SamplePlugin.cpp | 160 ++++++++++++++++++++++++---- sourcemm/sample_mm/SamplePlugin.h | 47 ++++++++ sourcemm/sample_mm/meta_hooks.h | 20 ++++ sourcemm/sample_mm/sample_mm.vcproj | 3 + 4 files changed, 211 insertions(+), 19 deletions(-) create mode 100644 sourcemm/sample_mm/meta_hooks.h diff --git a/sourcemm/sample_mm/SamplePlugin.cpp b/sourcemm/sample_mm/SamplePlugin.cpp index 5c71555..ad07c5f 100644 --- a/sourcemm/sample_mm/SamplePlugin.cpp +++ b/sourcemm/sample_mm/SamplePlugin.cpp @@ -5,46 +5,168 @@ SamplePlugin g_SamplePlugin; PLUGIN_EXPOSE(SamplePlugin, g_SamplePlugin); -SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, 0, bool, char const *, char const *, char const *, char const *, bool, bool); +//This has all of the necessary hook declarations. Read it! +#include "meta_hooks.h" -bool LevelInit_handler( char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background ); +bool SamplePlugin::LevelInit(const char *pMapName, const char *pMapEntities, const char *pOldLevel, const char *pLandmarkName, bool loadGame, bool background) +{ + META_LOG(g_PLAPI, "LevelInit() called: pMapName=%s", pMapName); + RETURN_META_VALUE(MRES_IGNORED, true); +} + +void SamplePlugin::ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) +{ + META_LOG(g_PLAPI, "ServerActivate() called: edictCount=%d, clientMax=%d", edictCount, clientMax); + RETURN_META(MRES_IGNORED); +} + +void SamplePlugin::GameFrame(bool simulating) +{ + META_LOG(g_PLAPI, "GameFrame() called: simulating=%d", simulating); + RETURN_META(MRES_IGNORED); +} + +void SamplePlugin::LevelShutdown( void ) +{ + META_LOG(g_PLAPI, "LevelShutdown() called"); + RETURN_META(MRES_IGNORED); +} + +void SamplePlugin::ClientActive(edict_t *pEntity, bool bLoadGame) +{ + META_LOG(g_PLAPI, "ClientActive called: pEntity=%d", pEntity ? m_Engine->IndexOfEdict(pEntity) : 0); + RETURN_META(MRES_IGNORED); +} + +void SamplePlugin::ClientDisconnect(edict_t *pEntity) +{ + META_LOG(g_PLAPI, "ClientDisconnect called: pEntity=%d", pEntity ? m_Engine->IndexOfEdict(pEntity) : 0); + RETURN_META(MRES_IGNORED); +} + +void SamplePlugin::ClientPutInServer(edict_t *pEntity, char const *playername) +{ + META_LOG(g_PLAPI, "ClientActivate called: pEntity=%d, playername=%s", pEntity ? m_Engine->IndexOfEdict(pEntity) : 0, playername); + RETURN_META(MRES_IGNORED); +} + +void SamplePlugin::SetCommandClient(int index) +{ + META_LOG(g_PLAPI, "SetCommandClient() called: index=%d", index); + RETURN_META(MRES_IGNORED); +} + +void SamplePlugin::ClientSettingsChanged(edict_t *pEdict) +{ + META_LOG(g_PLAPI, "ClientSettingsChanged called: pEdict=%d", pEdict ? m_Engine->IndexOfEdict(pEdict) : 0); + RETURN_META(MRES_IGNORED); +} + +bool SamplePlugin::ClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen) +{ + META_LOG(g_PLAPI, "ClientConnect called: pEntity=%d, pszName=%s, pszAddress=%s", pEntity ? m_Engine->IndexOfEdict(pEntity) : 0, pszName, pszAddress); + RETURN_META_VALUE(MRES_IGNORED, true); +} + +void SamplePlugin::ClientCommand(edict_t *pEntity) +{ + META_LOG(g_PLAPI, "ClientCommand called: pEntity=%d (commandString=%s)", pEntity ? m_Engine->IndexOfEdict(pEntity) : 0, m_Engine->Cmd_Args() ? m_Engine->Cmd_Args() : ""); + RETURN_META(MRES_IGNORED); +} bool SamplePlugin::Load(PluginId id, ISmmAPI *ismm, factories *list, char *error, size_t maxlen) { PLUGIN_SAVEVARS(); - IServerGameDLL *isgd = (IServerGameDLL *)((ismm->serverFactory())(INTERFACEVERSION_SERVERGAMEDLL, NULL)); + m_ServerDll = (IServerGameDLL *)((ismm->serverFactory())(INTERFACEVERSION_SERVERGAMEDLL, NULL)); + m_Engine = (IVEngineServer *)((ismm->engineFactory())(INTERFACEVERSION_VENGINESERVER, NULL)); + m_ServerClients = (IServerGameClients *)((ismm->serverFactory())(INTERFACEVERSION_SERVERGAMECLIENTS, NULL)); - if (!isgd) + if (!m_ServerDll) { snprintf(error, maxlen, "Could not find interface %s", INTERFACEVERSION_SERVERGAMEDLL); return false; } + if (!m_Engine) + { + snprintf(error, maxlen, "Could not find interface %s", INTERFACEVERSION_VENGINESERVER); + return false; + } + if (!m_ServerClients) + { + snprintf(error, maxlen, "Could not find interface %s", INTERFACEVERSION_SERVERGAMECLIENTS); + return false; + } - SH_ADD_HOOK_STATICFUNC(IServerGameDLL, LevelInit, isgd, LevelInit_handler, false); + //We're hooking the following things as POST, in order to seem like Server Plugins. + //However, I don't actually know if Valve has done server plugins as POST or not. + //Change the last parameter to 'false' in order to change this to PRE. + //SH_ADD_HOOK_MEMFUNC means "SourceHook, Add Hook, Member Function". + + //Hook LevelInit to our function + SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, m_ServerDll, &g_SamplePlugin, LevelInit, true); + //Hook ServerActivate to our function + SH_ADD_HOOK_MEMFUNC(IServerGameDLL, ServerActivate, m_ServerDll, &g_SamplePlugin, ServerActivate, true); + //Hook GameFrame to our function + SH_ADD_HOOK_MEMFUNC(IServerGameDLL, GameFrame, m_ServerDll, &g_SamplePlugin, GameFrame, true); + //Hook LevelShutdown to our function -- this makes more sense as pre I guess + SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, m_ServerDll, &g_SamplePlugin, LevelShutdown, false); + //Hook ClientActivate to our function + SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientActive, m_ServerClients, &g_SamplePlugin, ClientActive, true); + //Hook ClientDisconnect to our function + SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, m_ServerClients, &g_SamplePlugin, ClientDisconnect, true); + //Hook ClientPutInServer to our function + SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, m_ServerClients, &g_SamplePlugin, ClientPutInServer, true); + //Hook SetCommandClient to our function + SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, m_ServerClients, &g_SamplePlugin, SetCommandClient, true); + //Hook ClientSettingsChanged to our function + SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientSettingsChanged, m_ServerClients, &g_SamplePlugin, ClientSettingsChanged, true); + + //The following functions are pre handled, because that's how they are in IServerPluginCallbacks + + //Hook ClientConnect to our function + SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientConnect, m_ServerClients, &g_SamplePlugin, ClientConnect, false); + //Hook ClientCommand to our function + SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientCommand, m_ServerClients, &g_SamplePlugin, ClientCommand, false); + + //Now, importantly, SourceHook has modified the layout of the above classes. + //If we want to call them in their original state (i.e., without invoking hooks) + // we must request a "Call Class" from SourceHook. + m_ServerClients_CC = SH_GET_CALLCLASS(IServerGameClients, m_ServerClients); + m_ServerDll = SH_GET_CALLCLASS(IServerGameDLL, m_ServerDll); return true; } bool SamplePlugin::Unload(char *error, size_t maxlen) { + //Make sure we remove any hooks we did... this may not be necessary since + // SourceHook is capable of unloading plugins' hooks itself, but just to be safe. + + SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelInit, m_ServerDll, &g_SamplePlugin, LevelInit, true); + SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, ServerActivate, m_ServerDll, &g_SamplePlugin, ServerActivate, true); + SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, GameFrame, m_ServerDll, &g_SamplePlugin, GameFrame, true); + SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, m_ServerDll, &g_SamplePlugin, LevelShutdown, false); + SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientActive, m_ServerClients, &g_SamplePlugin, ClientActive, true); + SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, m_ServerClients, &g_SamplePlugin, ClientDisconnect, true); + SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, m_ServerClients, &g_SamplePlugin, ClientPutInServer, true); + SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, m_ServerClients, &g_SamplePlugin, SetCommandClient, true); + SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientSettingsChanged, m_ServerClients, &g_SamplePlugin, ClientSettingsChanged, true); + SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientConnect, m_ServerClients, &g_SamplePlugin, ClientConnect, false); + SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientCommand, m_ServerClients, &g_SamplePlugin, ClientCommand, false); + + //Lastly, release the call classes we requested + SH_RELEASE_CALLCLASS(m_ServerDll_CC); + SH_RELEASE_CALLCLASS(m_ServerClients_CC); + return true; } void SamplePlugin::AllPluginsLoaded() { - //we don't really care -} - -bool LevelInit_handler( char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background ) -{ - FILE *fp = fopen("c:\\dump.txt", "at"); - if (!fp) - RETURN_META_VALUE(MRES_IGNORED, false); - - fprintf(fp, "Map name: %s (old level: %s)\n", pMapName?pMapName:"", pOldLevel?pOldLevel:""); - - fclose(fp); - - RETURN_META_VALUE(MRES_IGNORED, false); + //we don't really need this for anything other than interplugin communication + //and that's not used in this plugin. + //If we really wanted, we could override the factories so other plugins can request + // interfaces we make. In this callback, the plugin could be assured that either + // the interfaces it requires were either loaded in another plugin or not. } diff --git a/sourcemm/sample_mm/SamplePlugin.h b/sourcemm/sample_mm/SamplePlugin.h index c16af64..d8a2448 100644 --- a/sourcemm/sample_mm/SamplePlugin.h +++ b/sourcemm/sample_mm/SamplePlugin.h @@ -52,6 +52,53 @@ public: { return "SAMPLE"; } +public: + //These functions are from IServerPluginCallbacks + //Note, the parameters might be a little different to match the actual calls! + + //Called on LevelInit. Server plugins only have pMapName + bool LevelInit(const char *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background); + + //Called on ServerActivate. Same definition as server plugins + void ServerActivate(edict_t *pEdictList, int edictCount, int clientMax); + + //Called on a game tick. Same definition as server plugins + void GameFrame(bool simulating); + + //Called on level shutdown. Same definition as server plugins + void LevelShutdown(void); + + //Client is activate (whatever that means). We get an extra parameter... + // "If bLoadGame is true, don't spawn the player because its state is already setup." + void ClientActive(edict_t *pEntity, bool bLoadGame); + + //Client disconnects - same as server plugins + void ClientDisconnect(edict_t *pEntity); + + //Client is put in server - same as server plugins + void ClientPutInServer(edict_t *pEntity, char const *playername); + + //Sets the client index - same as server plugins + void SetCommandClient(int index); + + //Called on client settings changed (duh) - same as server plugins + void ClientSettingsChanged(edict_t *pEdict); + + //Called on client connect. Unlike server plugins, we return whether the + // connection is allowed rather than set it through a pointer in the first parameter. + // You can still supercede the GameDLL through RETURN_META_VALUE(MRES_SUPERCEDE, true/false) + bool ClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen); + + //Called when a client uses a command. Unlike server plugins, it's void. + // You can still supercede the gamedll through RETURN_META(MRES_SUPERCEDE). + void ClientCommand(edict_t *pEntity); + +private: + IVEngineServer *m_Engine; + IServerGameDLL *m_ServerDll; + IServerGameClients *m_ServerClients; + IServerGameDLL *m_ServerDll_CC; + IServerGameClients *m_ServerClients_CC; }; extern SamplePlugin g_SamplePlugin; diff --git a/sourcemm/sample_mm/meta_hooks.h b/sourcemm/sample_mm/meta_hooks.h new file mode 100644 index 0000000..dfce933 --- /dev/null +++ b/sourcemm/sample_mm/meta_hooks.h @@ -0,0 +1,20 @@ +#ifndef _INCLUDE_META_HOOKS_H +#define _INCLUDE_META_HOOKS_H + +//Declare the hooks we will be using. Hooking will not compile without these. +//The macro naming scheme is SH_DECL_HOOKn[_void]. +//If you have 5 parameters, it would be HOOK5. If the function is void, add _void. +//It stands for "SourceHook, Declare Hook". +SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, 0, bool, char const *, char const *, char const *, char const *, bool, bool); +SH_DECL_HOOK3_void(IServerGameDLL, ServerActivate, SH_NOATTRIB, 0, edict_t *, int, int); +SH_DECL_HOOK1_void(IServerGameDLL, GameFrame, SH_NOATTRIB, 0, bool); +SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, 0); +SH_DECL_HOOK2_void(IServerGameClients, ClientActive, SH_NOATTRIB, 0, edict_t *, bool); +SH_DECL_HOOK1_void(IServerGameClients, ClientDisconnect, SH_NOATTRIB, 0, edict_t *); +SH_DECL_HOOK2_void(IServerGameClients, ClientPutInServer, SH_NOATTRIB, 0, edict_t *, char const *); +SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, 0, int); +SH_DECL_HOOK1_void(IServerGameClients, ClientSettingsChanged, SH_NOATTRIB, 0, edict_t *); +SH_DECL_HOOK5(IServerGameClients, ClientConnect, SH_NOATTRIB, 0, bool, edict_t *, const char*, const char *, char *, int); +SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *); + +#endif //_INCLUDE_META_HOOKS_H diff --git a/sourcemm/sample_mm/sample_mm.vcproj b/sourcemm/sample_mm/sample_mm.vcproj index 8aa65e4..5a23df0 100644 --- a/sourcemm/sample_mm/sample_mm.vcproj +++ b/sourcemm/sample_mm/sample_mm.vcproj @@ -124,6 +124,9 @@ Name="Header Files" Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"> + +