Difference between revisions of "Metamod:Source Development"
m |
m |
||
Line 1: | Line 1: | ||
− | This article is an introduction | + | This article is an introduction to coding for Metamod:Source. The main article of importance is [[SourceHook Development]]. Additional information can be found in the sample and stub plugins found in the Metamod:Source source code distribution. |
If you find this article to be a tough read, you may want to skip over to the [[Sample Plugins (Metamod:Source)|Sample Plugins]] article. | If you find this article to be a tough read, you may want to skip over to the [[Sample Plugins (Metamod:Source)|Sample Plugins]] article. | ||
=Requirements= | =Requirements= | ||
− | You must have the Valve [[HL2SDK]], available from your [[Steam]] Menu. For Windows, you must have [[Microsoft Visual Studio]] 8.0 | + | You must have the Valve [[HL2SDK]], available from your [[Steam]] Menu, or Metamod:Source SVN. For Windows, you must have [[Microsoft Visual Studio]] 8.0. Although you can try using 7.1, anything below that is unusable with SourceHook, which requires a full C++ template implementation. For [[Linux]], Valve requires you use at least [[GCC]] 3.4.1. You should also have a copy of the [[Metamod:Source]] source code, available [http://www.sourcemm.net/ here]. |
For [[Microsoft Visual Studio]], you should make sure that you have the following HL2SDK paths in your include settings (Tools -> Options -> Projects, VC++ Directories, Include Files), as well as the "sourcehook" and "sourcemm" folders: | For [[Microsoft Visual Studio]], you should make sure that you have the following HL2SDK paths in your include settings (Tools -> Options -> Projects, VC++ Directories, Include Files), as well as the "sourcehook" and "sourcemm" folders: | ||
Line 16: | Line 16: | ||
*tier1 | *tier1 | ||
*lib\public (this should be in your Library Paths!) | *lib\public (this should be in your Library Paths!) | ||
− | + | ||
=Plugin API= | =Plugin API= | ||
− | In order to write a plugin, you must implement the ISmmPlugin interface, similar to IServerPluginCallbacks. | + | In order to write a plugin, you must implement the ISmmPlugin interface, similar to IServerPluginCallbacks. An example of implementing this can be seen in the <tt>stub_mm</tt> sample plugin/ |
Once you've implemented the interface, you must also have a global singleton of your plugin available. There are a few macros to assist you in properly exposing the interface as a DLL and setting up the API states. | Once you've implemented the interface, you must also have a global singleton of your plugin available. There are a few macros to assist you in properly exposing the interface as a DLL and setting up the API states. | ||
Line 28: | Line 28: | ||
Full documentation of each callback is available in the <tt>ISmmPlugin.h</tt> header file. | Full documentation of each callback is available in the <tt>ISmmPlugin.h</tt> header file. | ||
+ | |||
=SourceHook= | =SourceHook= | ||
Using SourceHook is fully covered in the [[SourceHook Development]] article. | Using SourceHook is fully covered in the [[SourceHook Development]] article. | ||
+ | |||
=Various Macros= | =Various Macros= | ||
Line 42: | Line 44: | ||
*{{bcode|META_UNREGCVAR}}(var) | *{{bcode|META_UNREGCVAR}}(var) | ||
**Unregisters a ConCommandBase pointer. | **Unregisters a ConCommandBase pointer. | ||
+ | |||
=Metamod Events= | =Metamod Events= | ||
Line 49: | Line 52: | ||
*Game Events are simple events that Metamod:Source is already hooking and makes available. These are LevelShutdown and LevelInit right now. | *Game Events are simple events that Metamod:Source is already hooking and makes available. These are LevelShutdown and LevelInit right now. | ||
*Query Events occur when a factory is used. The four main factories (Engine, GameDLL, FileSystem, and Physics) are all overridable. You should simply return a non-NULL result to override, and fill the return code with IFACE_OK if available. There is no way to handle the case of two plugins overriding right now. The final factory is the Metamod Factory, which is the factory that Metamod:Source adds to the runtime space for plugins. By default, it only contains the interfaces for the PluginManager and SourceHook. Plugins can use this to add new interfaces. Other plugins request these interfaces through g_SMAPI->MetaFactory(). | *Query Events occur when a factory is used. The four main factories (Engine, GameDLL, FileSystem, and Physics) are all overridable. You should simply return a non-NULL result to override, and fill the return code with IFACE_OK if available. There is no way to handle the case of two plugins overriding right now. The final factory is the Metamod Factory, which is the factory that Metamod:Source adds to the runtime space for plugins. By default, it only contains the interfaces for the PluginManager and SourceHook. Plugins can use this to add new interfaces. Other plugins request these interfaces through g_SMAPI->MetaFactory(). | ||
+ | |||
=Global Variables= | =Global Variables= | ||
Line 60: | Line 64: | ||
**The SourceHook::ISourceHook * pointer to SourceHook's interface. | **The SourceHook::ISourceHook * pointer to SourceHook's interface. | ||
*{{bcode|g_SMAPI}} | *{{bcode|g_SMAPI}} | ||
− | **The ISmmAPI * pointer to | + | **The ISmmAPI * pointer to Metamod:Source's interface. |
+ | |||
=Compiling= | =Compiling= | ||
− | To see more about compiling, see the Sample | + | To see more about compiling, see the [[Sample Plugins (Metamod:Source)|Sample Plugins]] article. |
Modifying the default Makefiles for your own projects: | Modifying the default Makefiles for your own projects: | ||
Line 79: | Line 84: | ||
*clean - Cleans all build files | *clean - Cleans all build files | ||
*debug - Builds debug version | *debug - Builds debug version | ||
+ | |||
=Interface Searching= | =Interface Searching= | ||
Line 84: | Line 90: | ||
<tt>VInterfaceMatch()</tt> removes the "max" parameter from <tt>InterfaceSearch()</tt> and adds an optional "chop" parameter, which specifices whether or not the interface should be searched from the beginning (default) or from the current version. | <tt>VInterfaceMatch()</tt> removes the "max" parameter from <tt>InterfaceSearch()</tt> and adds an optional "chop" parameter, which specifices whether or not the interface should be searched from the beginning (default) or from the current version. | ||
+ | |||
=VSP Interface Hooking= | =VSP Interface Hooking= | ||
Line 113: | Line 120: | ||
RETURN_META_VALUE(MRES_SUPERCEDE, PLUGIN_CONTINUE); | RETURN_META_VALUE(MRES_SUPERCEDE, PLUGIN_CONTINUE); | ||
}</cpp> | }</cpp> | ||
+ | |||
=User Message Enumeration= | =User Message Enumeration= |
Revision as of 08:47, 9 October 2007
This article is an introduction to coding for Metamod:Source. The main article of importance is SourceHook Development. Additional information can be found in the sample and stub plugins found in the Metamod:Source source code distribution.
If you find this article to be a tough read, you may want to skip over to the Sample Plugins article.
Contents
Requirements
You must have the Valve HL2SDK, available from your Steam Menu, or Metamod:Source SVN. For Windows, you must have Microsoft Visual Studio 8.0. Although you can try using 7.1, anything below that is unusable with SourceHook, which requires a full C++ template implementation. For Linux, Valve requires you use at least GCC 3.4.1. You should also have a copy of the Metamod:Source source code, available here.
For Microsoft Visual Studio, you should make sure that you have the following HL2SDK paths in your include settings (Tools -> Options -> Projects, VC++ Directories, Include Files), as well as the "sourcehook" and "sourcemm" folders:
- dlls
- public
- public\vstdlib
- public\tier1
- public\tier0
- public\engine
- public\dlls
- tier1
- lib\public (this should be in your Library Paths!)
Plugin API
In order to write a plugin, you must implement the ISmmPlugin interface, similar to IServerPluginCallbacks. An example of implementing this can be seen in the stub_mm sample plugin/
Once you've implemented the interface, you must also have a global singleton of your plugin available. There are a few macros to assist you in properly exposing the interface as a DLL and setting up the API states.
- PLUGIN_GLOBALVARS() - Place in header. Declares the global variables that some API calls require (such as g_SHPtr and g_PLAPI).
- PLUGIN_EXPOSE(class, singleton) - Place in .cpp file. Declares the external CreateInterface function which exposes the API.
- PLUGIN_SAVEVARS() - Use first thing in ISmmPlugin::Load(), saves the global variables sent from SourceMM.
Full documentation of each callback is available in the ISmmPlugin.h header file.
SourceHook
Using SourceHook is fully covered in the SourceHook Development article.
Various Macros
- META_CONPRINT(const char *msg)
- META_CONPRINTF(const char *fmt, ...)
- These two functions are equivalent to ConMsg().
- META_LOG(g_PLAPI, const char *msg, ...)
- Logs a message through IVEngineServer::LogPrint(). A newline is automatically added, and msg is formatted as a sprintf() style string. Logging is done by the game server and can be enabled by adding log on to server.cfg or typing it in the console. The log files are found in the logs directory of the particular MOD you are running.
- META_REGCVAR(var)
- Registers a ConCommandBase pointer through Metamod:Source.
- META_UNREGCVAR(var)
- Unregisters a ConCommandBase pointer.
Metamod Events
The Metamod Events System is based on IMetamodListener. By implementing the IMetamodListener class and using g_SMAPI->AddListener, you can watch for certain, low-traffic events. These events are split into three categories:
- Plugin Events let you listen for plugin pauses and unloads. This is important if you're relying on information from another plugin, as you can handle cases where a live plugin has become invalid.
- Game Events are simple events that Metamod:Source is already hooking and makes available. These are LevelShutdown and LevelInit right now.
- Query Events occur when a factory is used. The four main factories (Engine, GameDLL, FileSystem, and Physics) are all overridable. You should simply return a non-NULL result to override, and fill the return code with IFACE_OK if available. There is no way to handle the case of two plugins overriding right now. The final factory is the Metamod Factory, which is the factory that Metamod:Source adds to the runtime space for plugins. By default, it only contains the interfaces for the PluginManager and SourceHook. Plugins can use this to add new interfaces. Other plugins request these interfaces through g_SMAPI->MetaFactory().
Global Variables
These global variables are saved by PLUGIN_EXPOSE and PLUGIN_SAVEVARS. They are declared with PLUGIN_GLOBALVARS.
- g_PLAPI
- ISmmPlugin * pointer to your global class singleton.
- g_PLID
- The internal PluginId of your plugin.
- g_SHPtr
- The SourceHook::ISourceHook * pointer to SourceHook's interface.
- g_SMAPI
- The ISmmAPI * pointer to Metamod:Source's interface.
Compiling
To see more about compiling, see the Sample Plugins article.
Modifying the default Makefiles for your own projects:
- OPT_FLAGS - Optimization flags
- DEBUG_FLAGS - Debug flags
- CPP - C++ Compiler
- OBJECTS - List of C++ files to compile
- LINK - Linker Options
- CFLAGS - Base Compiler Flags
- BINARY - Output Binary Name
Makefile commands:
- clean - Cleans all build files
- debug - Builds debug version
Interface Searching
ISmmAPI::VInterfaceMatch() can be used for searching for an interface.. This simplified version corrects the design flaw in InterfaceSearch() whereby passing an unmodified INTERFACEVERSION string would only search interfaces later than or equal to that version. For example, INTERFACEVERSION_SERVERGAMEDLL being "ServerGameDLL005" would not find a GameDLL using ServerGameDLL004.
VInterfaceMatch() removes the "max" parameter from InterfaceSearch() and adds an optional "chop" parameter, which specifices whether or not the interface should be searched from the beginning (default) or from the current version.
VSP Interface Hooking
Metamod:Source can provide a "virtual" Valve Server Plugin plugin interface. This is useful for providing an IServerPluginCallbacks pointer to certain routines, or hooking functions it has.
Example:
SH_DECL_HOOK2(IServerPluginCallbacks, NetworkIDValidated, SH_NOATTRIB, 0, PLUGIN_RESULT, const char *, const char *); IServerPluginCallbacks *vsp_iface = NULL; bool Plugin::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late) { if ((vsp_iface = ismm->GetVSPInfo(NULL)) == NULL) { ismm->EnableVSPListener(); ismm->AddListener(this, this); } } void Plugin::OnVSPListening(IServerPluginCallbacks *iface) { SH_ADD_HOOK_MEMFUNC(IServerPluginCallbacks, NetworkIDValidated, iface, &g_Plugin, &Plugin::NetworkIDValidated, false); vsp_iface = iface; } PLUGIN_RESULT Plugin::NetworkIDValidated(const char *pszUserName, const char *pszNetworkID) { META_CONPRINTF("%s has been validated with Network ID %s\n", pszUserName, pszNetworkID); RETURN_META_VALUE(MRES_SUPERCEDE, PLUGIN_CONTINUE); }
User Message Enumeration
API functions have also been added for the purpose of enumerating user messages. These serve to replace IServerGameDLL::GetUserMessageInfo() which can crash the server upon passing an invalid message index. The new functions include:
- GetUserMessageCount()
- FindUserMessage()
- GetUserMessage()
Here is a quick example of how to use them:
// Get index of 'SayText' message int msgSayText = g_SMAPI->FindUserMessage("SayText"); // Get number of user messages in GameDLL int count = g_SMAPI->GetUserMessageCount(); const char *name; int size; // Print list of user message names and sizes for (int i = 0; i < count; i++) { name = g_SMAPI->GetUserMessage(i, &size); META_CONPRINTF("Message %d: (name \"%s\") (size %d)\n", i, name, size); }