/* * vim: set ts=4 : * ====================================================== * Metamod:Source * Copyright (C) 2004-2008 AlliedModders LLC and authors. * All rights reserved. * ====================================================== * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from * the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software in a * product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * * Version: $Id$ */ #ifndef _INCLUDE_ISMM_PLUGIN_H #define _INCLUDE_ISMM_PLUGIN_H /** * @brief Plugin API interface. * @file ISmmPlugin.h These are callbacks that plugins can implement without hooks. * The pure-virtual ISmmPlugin callbacks must be implemented for the load to load. */ #include #include #include #include #ifndef META_NO_HL2SDK #if SOURCE_ENGINE == SE_DOTA #include #else #include #endif #endif class IServerPluginCallbacks; // Interface return status, binary-compatible with HL2SDK's IFACE_OK and IFACE_FAILED. enum { META_IFACE_OK = 0, META_IFACE_FAILED }; namespace SourceMM { class ISmmAPI; /** * @brief Callbacks that a plugin must expose. */ class ISmmPlugin { public: /** * @brief Called to request the plugin's API version. * * This is the first callback invoked, and always remains at the top * of the virtual table. * * @return Plugin API version. */ virtual int GetApiVersion() { return METAMOD_PLAPI_VERSION; } /** * @brief Virtual destructor so GCC doesn't complain. */ virtual ~ISmmPlugin() { } public: /** * @brief Called on plugin load. * * This is called as DLLInit() executes - after the parameters are * known, but before the original GameDLL function is called. * Therefore, you cannot hook it, but you don't need to - Load() is * basically your hook. You can override factories before the engine * and gamedll exchange them. However, take care to note that if your * plugin is unloaded, and the gamedll/engine have cached an interface * you've passed, something will definitely crash. Be careful. * * @param id Internal id of plugin. Saved globally by PLUGIN_SAVEVARS() * @param ismm External API for SourceMM. Saved globally by PLUGIN_SAVEVARS() * @param error Error message buffer * @param maxlength Size of error message buffer * @param late Set to true if your plugin was loaded late (not at server load). * @return True if successful, return false to reject the load. */ virtual bool Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlength, bool late) =0; /** * @brief Called when all plugins have been loaded. * * This is called after DLLInit(), and thus the mod has been mostly initialized. * It is also safe to assume that all other (automatically loaded) plugins are now * ready to start interacting, because they are all loaded. */ virtual void AllPluginsLoaded() { } /** * @brief Called when your plugin is "queried". * * This is useful for rejecting a loaded state. For example, if your * plugin wants to stop operating, it can simply return false and copy * an error message. This will notify other plugins or MM:S of * something bad that happened. MM:S will not cache the return state, * so if you return false, your plugin will not actually be paused or * unloaded. This callback will be called when: * - Another plugin requests it * - Someone types "meta list", it will show up as "REFUSED" * - When Metamod need to re-check the plugin's status * - If the plugin does something like overload a factory, Metamod * will make sure the Query() returns true before calling it. * Also note that this query will only override Metamod when the * plugin is running and not paused. * * @param error Buffer for error message, or NULL if none. * @param maxlen Maximum length of error buffer. * @return Status code - true for okay, false for badness. */ virtual bool QueryRunning(char *error, size_t maxlen) { return true; } /** * @brief Called on plugin unload. You can return false if you know * your plugin is not capable of restoring critical states it modifies. * * @param error Error message buffer * @param maxlen Size of error message buffer * @return True on success, return false to request no unload. */ virtual bool Unload(char *error, size_t maxlen) { return true; } /** * @brief Called on plugin pause. * * @param error Error message buffer * @param maxlen Size of error message buffer * @return True on success, return false to request no pause. */ virtual bool Pause(char *error, size_t maxlen) { return true; } /** * @brief Called on plugin unpause. * * @param error Error message buffer * @param maxlen Size of error message buffer * @return True on success, return false to request no unpause. */ virtual bool Unpause(char *error, size_t maxlen) { return true; } public: /** @brief Return author as string */ virtual const char *GetAuthor() =0; /** @brief Return plugin name as string */ virtual const char *GetName() =0; /** @brief Return a description as string */ virtual const char *GetDescription() =0; /** @brief Return a URL as string */ virtual const char *GetURL() =0; /** @brief Return quick license code as string */ virtual const char *GetLicense() =0; /** @brief Return version as string */ virtual const char *GetVersion() =0; /** @brief Return author as string */ virtual const char *GetDate() =0; /** @brief Return author as string */ virtual const char *GetLogTag() =0; }; /** * @brief Various events that Metamod can fire. */ class IMetamodListener { public: /** * @brief Called when a plugin is loaded. * * @param id Id of the plugin. */ virtual void OnPluginLoad(PluginId id) { } /** * @brief Called when a plugin is unloaded. * * @param id Id of the plugin. */ virtual void OnPluginUnload(PluginId id) { } /** * @brief Called when a plugin is paused. * * @param id Id of the plugin. */ virtual void OnPluginPause(PluginId id) { } /** * @brief Called when a plugin is unpaused. * * @param id Id of the plugin. */ virtual void OnPluginUnpause(PluginId id) { } /** * @brief Called when the level is loaded (after GameInit, before * ServerActivate). * * To override this, hook IServerGameDLL::LevelInit(). * * @param pMapName Name of the map. * @param pMapEntities Lump string of the map entities, in KeyValues * format. * @param pOldLevel Unknown. * @param pLandmarkName Unknown. * @param loadGame Unknown. * @param background Unknown. */ virtual void OnLevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background) { } /** * @brief Called when the level is shut down. May be called more than * once. */ virtual void OnLevelShutdown() { } /** * @brief Called when engineFactory() is used through Metamod:Source's * wrapper. This can be used to provide interfaces to other plugins or * the GameDLL. * * If ret is passed, you should fill it with META_IFACE_OK or META_IFACE_FAILED. * * @param iface Interface string. * @param ret Optional pointer to store return code. * @return Generic pointer to the interface, or NULL if * not found. */ virtual void *OnEngineQuery(const char *iface, int *ret) { if (ret) { *ret = META_IFACE_FAILED; } return NULL; } /** * @brief Called when the physics factory is used through * Metamod:Source's wrapper. This can be used to provide interfaces to * other plugins. * * If ret is passed, you should fill it with META_IFACE_OK or META_IFACE_FAILED. * * @param iface Interface string. * @param ret Optional pointer to store return code. * @return Generic pointer to the interface, or NULL if * not found. */ virtual void *OnPhysicsQuery(const char *iface, int *ret) { if (ret) { *ret = META_IFACE_FAILED; } return NULL; } /** * @brief Called when the filesystem factory is used through * Metamod:Source's wrapper. This can be used to provide interfaces to * other plugins. * * If ret is passed, you should fill it with META_IFACE_OK or META_IFACE_FAILED. * * @param iface Interface string. * @param ret Optional pointer to store return code. * @return Generic pointer to the interface, or NULL if not * found. */ virtual void *OnFileSystemQuery(const char *iface, int *ret) { if (ret) { *ret = META_IFACE_FAILED; } return NULL; } /** * @brief Called when the server DLL's factory is used through * Metamod:Source's wrapper. This can be used to provide interfaces to * other plugins. * * If ret is passed, you should fill it with META_IFACE_OK or META_IFACE_FAILED. * * @param iface Interface string. * @param ret Optional pointer to store return code. * @return Generic pointer to the interface, or NULL if not * found. */ virtual void *OnGameDLLQuery(const char *iface, int *ret) { if (ret) { *ret = META_IFACE_FAILED; } return NULL; } /** * @brief Called when Metamod's own factory is invoked. * This can be used to provide interfaces to other plugins. * * If ret is passed, you should fill it with META_IFACE_OK or META_IFACE_FAILED. * * @param iface Interface string. * @param ret Optional pointer to store return code. * @return Generic pointer to the interface, or NULL if not * found. */ virtual void *OnMetamodQuery(const char *iface, int *ret) { if (ret) { *ret = META_IFACE_FAILED; } return NULL; } /** * @brief Called when Metamod:Source acquires a valid * IServerPluginCallbacks pointer to be used for hooking by plugins. * * This will only be called after a call to ISmmAPI::EnableVSPListener(). * If called before GameInit, this callback will occur before LevelInit. * Otherwise, it will be called on the first call after that. * * This callback is provided to all plugins regardless of which (or how * many) called EnableVSPListener(), but only if at least one did in * fact enable it, and only once for all plugins. That is, a late * loading plugin should use ISmmAPI::GetVSPInfo() before relying on * this callback. * * This callback is never called if Metamod:Source is in VSP mode. * If in VSP mode, a VSP instance is automatically and always available * via ISmmAPI::GetVSPInfo(), which should be called anyway (to handle * late loading cases). * * @param iface Interface pointer. If NULL, then the VSP * listening construct failed to initialize and * is not available. */ virtual void OnVSPListening(IServerPluginCallbacks *iface) { } /** * @brief Called when Metamod:Source is about to remove a concommand or * convar. This can also be called if ISmmAPI::UnregisterConCmdBase is * used by a plugin. * * @param id Id of the plugin that created the concommand or * convar. * @param pCommand Pointer to concommand or convar that is being * removed. */ virtual void OnUnlinkConCommandBase(PluginId id, ConCommandBase *pCommand) { } }; } #if !defined METAMOD_NO_AUTO_NAMESPACE using namespace SourceMM; #endif #define PL_EXPOSURE CreateInterface #define PL_EXPOSURE_C "CreateInterface" /** * @brief Exposes the plugin to the MM:S loader. * * @param name Deprecated - should be a variable name (like name). * @param var Name of the variable that contains the singleton. * This macro automatically takes the address of it, so * you should not pass a pointer to your plugin's * singleton. */ #ifdef META_NO_HL2SDK #define PL_EXPOSURE_FUNC(name, var) \ SMM_API void *PL_EXPOSURE(const char *name, int *code) { \ if (name && !strcmp(name, METAMOD_PLAPI_NAME)) { \ return static_cast(&var); \ } \ return NULL; \ } #else // First param should be actual classname, not iface name, but we don't have that and it doesn't matter here. #define PL_EXPOSURE_FUNC(name, var) EXPOSE_SINGLE_INTERFACE_GLOBALVAR(ISmmPlugin, ISmmPlugin, METAMOD_PLAPI_NAME, var); #endif #define PLUGIN_EXPOSE(name, var) \ ISmmAPI *g_SMAPI = NULL; \ ISmmPlugin *g_PLAPI = NULL; \ PluginId g_PLID = (PluginId)0; \ SourceHook::ISourceHook *g_SHPtr = NULL; \ PL_EXPOSURE_FUNC(name, var) /** * @brief This should be in one of your header files, if you wish * to use values like g_SHPtr in other files. */ #define PLUGIN_GLOBALVARS() \ extern SourceHook::ISourceHook *g_SHPtr; \ extern ISmmAPI *g_SMAPI; \ extern ISmmPlugin *g_PLAPI; \ extern PluginId g_PLID; /** * @brief This should be the first line in your Load callback. */ #define PLUGIN_SAVEVARS() \ g_SMAPI = ismm; \ g_SHPtr = static_cast(ismm->MetaFactory(MMIFACE_SOURCEHOOK, NULL, NULL)); \ g_PLAPI = static_cast(this); \ g_PLID = id; #define META_LOG g_SMAPI->LogMsg #define META_REGCMD(name) g_SMAPI->RegisterConCommandBase(g_PLAPI, name##_command) #define META_REGCVAR(var) g_SMAPI->RegisterConCommandBase(g_PLAPI, var) #define META_UNREGCMD(name) g_SMAPI->UnregisterConCommandBase(g_PLAPI, name##_command) #define META_UNREGCVAR(var) g_SMAPI->UnregisterConCommandBase(g_PLAPI, var) #define META_CONPRINT g_SMAPI->ConPrint #define META_CONPRINTF g_SMAPI->ConPrintf /* Probably should use this up above someday */ #define CONCMD_VARNAME(name) name##_command #if !defined SMM_API #if defined __WIN32__ || defined _WIN32 || defined WIN32 #define SMM_API extern "C" __declspec(dllexport) #elif defined __GNUC__ #define SMM_API extern "C" __attribute__ ((visibility("default"))) #endif #endif //!defined SMM_API /** * @brief Macro for automatically getting a current or newer Valve interface. * * @param v_factory Factory method to use from ISmmAPI (such as engineFactory). * @param v_var Variable name to store into. * @param v_type Interface type (do not include the pointer/asterisk). * @param v_name Interface name. */ #define GET_V_IFACE_CURRENT(v_factory, v_var, v_type, v_name) \ v_var = (v_type *)ismm->VInterfaceMatch(ismm->v_factory(), v_name); \ if (!v_var) \ { \ if (error && maxlen) \ { \ ismm->Format(error, maxlen, "Could not find interface: %s", v_name); \ } \ return false; \ } /** * @brief Same as GET_V_IFACE, except searches for any. * * @param v_factory Factory method to use from ISmmAPI (such as engineFactory). * @param v_var Variable name to store into. * @param v_type Interface type (do not include the pointer/asterisk). * @param v_name Interface name. */ #define GET_V_IFACE_ANY(v_factory, v_var, v_type, v_name) \ v_var = (v_type *)ismm->VInterfaceMatch(ismm->v_factory(), v_name, 0); \ if (!v_var) \ { \ if (error && maxlen) \ { \ ismm->Format(error, maxlen, "Could not find interface: %s", v_name); \ } \ return false; \ } #endif //_INCLUDE_ISMM_PLUGIN_H