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}">
+
+