-
Notifications
You must be signed in to change notification settings - Fork 5
Creating a Plugin
The main part of a SvenMod's plugin is IClientPlugin
interface that must be inherited and implemented by a single class, this interface is queried by SvenMod at DLL load and it has several important and useful callbacks when your plugin is going to load/unload or pause/unpause, etc (for more, check the header svenmod/public/IClientPlugin.h
). Then after implementing your interface, it must be exposed as a singleton that may be queried from the automatically exported function CreateInterface
.
Tip: it's better to implement the interface in a separate source file (.cpp).
Include this header in your file:
#include <IClientPlugin.h>
Now we can inherit and implement our interface. This is how may look your inherited class:
class MyPluginClass : IClientPlugin
{
public:
// Always add this in implementation: return SVENMOD_API_VER;
virtual api_version_s GetAPIVersion( void ) = 0;
// Initialize the plugin to run
// Return 'false' if there is an error during startup
virtual bool Load( CreateInterfaceFn pfnSvenModFactory, ISvenModAPI *pSvenModAPI, IPluginHelpers *pPluginHelpers ) = 0;
// Called if the plugin was loaded during SvenMod's initialization after attaching detours (via plugins.txt file, @bGlobalLoad is 'true')
// Otherwise, called right after 'Load' callback (@bGlobalLoad is 'false')
virtual void PostLoad( bool bGlobalLoad ) = 0;
// Called when the plugin should be shutdown
virtual void Unload( void ) = 0;
// Called when the plugin should stop executing
// Return 'false' if it should not pause
virtual bool Pause( void ) = 0;
// Called when the plugin should start executing again (sometime after a Pause() call)
virtual void Unpause( void ) = 0;
// Called on each game frame twice (if @bPostRunCmd is 'false', then none of inputs/commands were processed yet)
virtual void GameFrame( client_state_t state, double frametime, bool bPostRunCmd ) = 0;
// Called to draw 2D paints after rendering the game view, you can handle the callback to influence on other plugins
// @PLUGIN_CONTINUE - don't abort the call sequence
// @PLUGIN_STOP and @PLUGIN_CALL_STOP - don't call the further plugins callbacks
virtual PLUGIN_RESULT Draw( void ) = 0;
// Called to redraw client's HUD, the same callback as @Draw method
virtual PLUGIN_RESULT DrawHUD( float time, int intermission ) = 0;
// Name of the plugin
virtual const char *GetName( void ) = 0;
// Author of the plugin
virtual const char *GetAuthor( void ) = 0;
// Version of the plugin
virtual const char *GetVersion( void ) = 0;
// Description of the plugin
virtual const char *GetDescription( void ) = 0;
// URL of the plugin
virtual const char *GetURL( void ) = 0;
// Build date of the plugin
virtual const char *GetDate( void ) = 0;
// Tag of the plugin to log
virtual const char *GetLogTag( void ) = 0;
};
Implementing your class is easy too:
api_version_s MyPluginClass::GetAPIVersion()
{
return SVENMOD_API_VER;
}
bool MyPluginClass::Load(CreateInterfaceFn pfnSvenModFactory, ISvenModAPI *pSvenModAPI, IPluginHelpers *pPluginHelpers)
{
return true;
}
void MyPluginClass::PostLoad(bool bGlobalLoad)
{
if ( bGlobalLoad )
{
}
else
{
}
}
void MyPluginClass::Unload(void)
{
}
bool MyPluginClass::Pause(void)
{
return true;
}
void CSamplePlugin::Unpause(void)
{
}
void MyPluginClass::GameFrame(client_state_t state, double frametime, bool bPostRunCmd)
{
if (bPostRunCmd) // called from HUD_Frame
{
}
else // called on start of function Host_Frame
{
}
}
PLUGIN_RESULT MyPluginClass::Draw(void)
{
return PLUGIN_CONTINUE;
}
PLUGIN_RESULT MyPluginClass::DrawHUD(float time, int intermission)
{
return PLUGIN_CONTINUE;
}
const char *MyPluginClass::GetName(void)
{
return "My Plugin Name";
}
const char *MyPluginClass::GetAuthor(void)
{
return "Author";
}
const char *MyPluginClass::GetVersion(void)
{
return "1.0";
}
const char *MyPluginClass::GetDescription(void)
{
return "My plugin for SvenMod";
}
const char *MyPluginClass::GetURL(void)
{
return "https://github.com/sw1ft747";
}
const char *MyPluginClass::GetDate(void)
{
return SVENMOD_BUILD_TIMESTAMP;
}
const char *MyPluginClass::GetLogTag(void)
{
return "SAMPLE_PLUGIN";
}
Now we need only to expose our interface and build the project.
Include this header in your file:
#include <interface.h>
The header above will minimize your time to expose the interface.
You can use one of these macros:
// Use this to expose a singleton interface with a global variable you've created.
#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName)
// Use this to expose a singleton interface. This creates the global variable for you automatically.
#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName)
Example (autocreate global variable):
EXPOSE_SINGLE_INTERFACE(MyPluginClass, IClientPlugin, CLIENT_PLUGIN_INTERFACE_VERSION);
Another example (with your global variable):
MyPluginClass g_Plugin;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(MyPluginClass, IClientPlugin, CLIENT_PLUGIN_INTERFACE_VERSION, g_Plugin);
You may look at the sample plugin in SvenMod's folder (svenmod/sample_plugin/sample_plugin.*)
it either has examples with other interfaces/functions (convars, printing, hooking functions).
Now you can build your plugin and load it via SvenMod