Server plugins: Difference between revisions
TomEdwards (talk | contribs) (partial cleanup) |
|||
Line 1: | Line 1: | ||
{{otherlang2 | |||
| ru = Server Plugins:ru | |||
| es = Server Plugins:es | |||
}} | |||
{{toc-right}} | |||
'''Server plugins''' are code libraries that modify the behaviour of [[dedicated server]]s. | |||
{{warning|Plugins ''do not'' work with the GUI dedicated servers available through Steam. Only servers installed through the HLDS Update Tool can use them.}} | |||
== Compiling == | |||
You can find a sample plugin project at <code>src\utils\serverplugin_sample\</code>. The Orange Box version is good for both [[Source 2007]] and [[Source 2009|2009]]. | |||
; Windows | |||
: Build with the Visual Studio project provided; see [[Compiling under VS2008]] or [[Compiling under VS2010]] for help with upgrading | |||
; Linux | |||
: Execute <code>make plugin</code> from <code>src/linux_sdk/</code> | |||
; Mac | |||
: Not possible yet | |||
To debug your plugin, you must launch the server with <code>-allowdebug</code>. | |||
== Installing == | |||
Source automatically loads plugins from files matching <code><game>\addons\*.vdf*</code>. Their format is: | |||
Plugin | |||
{ | { | ||
file <path to plugin> | |||
} | } | ||
* On Windows, do not add the file extension. On Linux, do. | |||
* | * The path value is relative to the engine's binary folder (''not'' the game's), so if you want to load a plugin from TF2's addons folder it will need to read <code>..\tf\addons\plugin</code>. | ||
* | |||
=== Managing === | |||
The following console commands are provided: | |||
=The IServerPlugin interface= | * <code>plugin_print</code> (particularly useful for confirming that your plugin is installed correctly) | ||
* <code>plugin_load</code> | |||
* <code>plugin_unload</code> | |||
* <code>plugin_pause</code> | |||
* <code>plugin_unpause</code> | |||
* <code>plugin_pause_all</code> | |||
* <code>plugin_unpause_all</code> | |||
== Coding == | |||
===The IServerPlugin interface=== | |||
Plugins work by exposing an <code>IServerPluginCallbacks</code> interface to the engine. The engine calls back into this interface when various events happen. The definition of this interface can be found in <code>public/engine/iserverplugin.h</code>. The <code>PLUGIN_RESULT</code> return type of some of the functions allows the plugin to control whether this call is passed onto the underlying mod or not, see the header file for details. | Plugins work by exposing an <code>IServerPluginCallbacks</code> interface to the engine. The engine calls back into this interface when various events happen. The definition of this interface can be found in <code>public/engine/iserverplugin.h</code>. The <code>PLUGIN_RESULT</code> return type of some of the functions allows the plugin to control whether this call is passed onto the underlying mod or not, see the header file for details. | ||
=IServerPluginCallbacks explained= | ===IServerPluginCallbacks explained=== | ||
=Listening to Events= | ==== Management ==== | ||
; <code>bool Load([[CreateInterfaceFn]] interfaceFactory, CreateInterfaceFn gameServerFactory)</code> | |||
: Called when the plugin is loaded by the engine. This can happen either on initialization or when being re-loaded after being unloaded. The two parameters provide the interface factories that the plugin needs to operate. Returns false on error. | |||
; <code>void UnLoad()</code> | |||
: Called when a plugin is unloaded, use this to disable any asynchronous tasks and remove any callbacks you have registered with the engine (for example a game events listener). | |||
; <code>void Pause()</code> | |||
: Called when the operation of the plugin is paused (i.e it will stop receiving callbacks but should not be unloaded). | |||
; <code>void UnPause()</code> | |||
: Called when a plugin is brought out of the paused state. You should re-enable any asynchronous events your plugin uses in this call | |||
; <code>[[char]]* GetPluginDescription()</code> | |||
: This function should return a friendly string describing your plugin. Typically this would be its name and the author. | |||
==== Server events ==== | |||
; <code>void LevelInit(char* pMapName)</code> | |||
: Called on level (map) startup, it is the first function called as a server enters a new level. | |||
; <code>void ServerActivate([[edict_t]] *pEdictList, [[int]] edictCount, int clientMax)</code> | |||
: This is called when the server successfully enters a new level, this will happen after the LevelInit call. This call provides pointers to the list of edicts created on the server and the maxplayer count of the server. | |||
; <code>void GameFrame(bool simulating)</code> | |||
: Called once per server frame (typically 60 times a second). Server performance is very sensitive to the execution time of this function so keep anything you do in this function to a minimum. | |||
; <code>void LevelShutdown()</code> | |||
: Called when a server is changing to a new level or is being shutdown. Remove any map specific allocations in this call. This can be called multiple times during a map change. | |||
; <code>void OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, [[edict_t]] *pPlayerEntity, EQueryCvarValueStatus eStatus, char *pCvarName, char *pCvarValue ) | |||
: Called when a query from <code>[[IServerPluginHelpers]]::StartQueryCvarValue</code> finishes. <code>iCookie</code> is the value requested. | |||
; <code>void OnEdictAllocated(edict_t *edict)</code> | |||
; <code>void OnEdictAllocated(const edict_t *edict)</code> | |||
==== Clients ==== | |||
; <code>[[PLUGIN_RESULT]] ClientConnect(bool *bAllowConnect, [[edict_t]] *pEntity, char *pszName, char *pszAddress, char *reject, int maxrejectlen)</code> | |||
: Called when a client initially connects to a server. Set bAllowConnect to false to stop this user from connecting. | |||
; <code>void ClientPutInServer(edict_t *pEntity, char const *playername)</code> | |||
: Called when a client spawns into a server. This is called before the server spawn function. | |||
; <code>void ClientActive(edict_t *pEntity)</code> | |||
: Called after a client is fully spawned and configured by the Mod. Use this call to change any player specific settings. | |||
; <code>void ClientDisconnect(edict_t *pEntity)</code> | |||
: Called when a client disconnects from the server. | |||
; <code>void SetCommandClient(int index)</code> | |||
: Called by the ConVar code to let you keep track of which client is entering a ConCommand. Use this index in ConCommands if you want to see who is running the command. As ConVar commands don't have an edict associated with them when they run you can use this index to look up the entity that is running the command. The index is one less than the entity index. | |||
; <code>void ClientSettingsChanged(edict_t *pEdict)</code> | |||
: Called when player specific cvars about a player change (for example the users name). Use this function to control what operations a user is allowed to perform to their settings (for example limiting usernames). | |||
; <code>PLUGIN_RESULT ClientCommand(edict_t *pEntity, const CCommand &args)</code> | |||
: Called when a remote client enters a command into the console. This should be used to provide commands to clients (and ConCommand used to implement server side only commands). | |||
; <code>PLUGIN_RESULT NetworkIDValidated(const char *pszUserName, const char *pszNetworkID)</code> | |||
: Called when the server retrieves a clients network ID (i.e Steam ID). Note that a clients network ID is not set on connect, you must wait for this callback before a users network ID is valid. The client name is passed into this function, you need to search the currently connected players to find the associated edict pointer. Note that the clients name can change between connect and id validation, you should use the entity index to track specific players. Note also that this function is NOT called for the local user when running a listen/non-dedicated server. | |||
===Listening to Events=== | |||
The <code>IGameEventManager2</code> (<code>IGameEventManager</code> was used previously) interface allows a plugin to listen to game events. Game events are fired by a Mod when things of interest happen, like a player dying or the bomb being planted. <code>IGameEventManager2::AddListener</code> should be called to subscribe to particular game events. It must be called for each event that your listener wishes to be aware of. FireGameEvent is then called in your plugin when an one of the listened for events happen. The data contained in each event is described by event configuration files, in particular: | The <code>IGameEventManager2</code> (<code>IGameEventManager</code> was used previously) interface allows a plugin to listen to game events. Game events are fired by a Mod when things of interest happen, like a player dying or the bomb being planted. <code>IGameEventManager2::AddListener</code> should be called to subscribe to particular game events. It must be called for each event that your listener wishes to be aware of. FireGameEvent is then called in your plugin when an one of the listened for events happen. The data contained in each event is described by event configuration files, in particular: | ||
Line 100: | Line 109: | ||
* <code><mod dir>/resource/ModEvents.res</code> | * <code><mod dir>/resource/ModEvents.res</code> | ||
=Creating [[ConVar]]s and [[Command]]s= | ===Creating [[ConVar]]s and [[Command]]s=== | ||
[[ConVar]]s let you specify variables that users can use to configure the behavior of your plugin. [[ConCommand]]s allow the creation of commands that your plugin provides. Creating both is easy and self contained, the code snippet below creates a new command <code>empty_version</code> and a new variable <code>plugin_empty</code>. These commands can be run from the server or client, you should use the index provided by <code>SetCommandClient</code> to determine the source of the command. | [[ConVar]]s let you specify variables that users can use to configure the behavior of your plugin. [[ConCommand]]s allow the creation of commands that your plugin provides. Creating both is easy and self contained, the code snippet below creates a new command <code>empty_version</code> and a new variable <code>plugin_empty</code>. These commands can be run from the server or client, you should use the index provided by <code>SetCommandClient</code> to determine the source of the command. | ||
Line 112: | Line 121: | ||
</pre> | </pre> | ||
=Interacting with Clients= | ===Interacting with Clients=== | ||
The engine provides the <code>IServerPluginHelpers</code> interface to allow plugins to message clients. This interface provides a single function, <code>CreateMessage</code> that is called with 3 different dialog options to message users in different ways. | The engine provides the <code>IServerPluginHelpers</code> interface to allow plugins to message clients. This interface provides a single function, <code>CreateMessage</code> that is called with 3 different dialog options to message users in different ways. | ||
Line 122: | Line 131: | ||
Examples of how to use this interface are shown in the sample plugin example. Each message will be displayed for 10 seconds in the HUD and for the menu and text option for up to 200 seconds in GameUI. Only 1 message can be displayed at a time, you have to wait for the current one to expire before a new one can be shown. Messages with a lower <code>"level"</code> field can be used to override current messages (the minimum level value is 0). Details of valid message fields can be found in the sample code. | Examples of how to use this interface are shown in the sample plugin example. Each message will be displayed for 10 seconds in the HUD and for the menu and text option for up to 200 seconds in GameUI. Only 1 message can be displayed at a time, you have to wait for the current one to expire before a new one can be shown. Messages with a lower <code>"level"</code> field can be used to override current messages (the minimum level value is 0). Details of valid message fields can be found in the sample code. | ||
= | ===Release your plugin=== | ||
=Release your plugin= | |||
When ready for public consumption or beta testing, announce your server plugin at a community website like [http://www.sourceplugins.com SourcePlugins]. | When ready for public consumption or beta testing, announce your server plugin at a community website like [http://www.sourceplugins.com SourcePlugins]. | ||
[[Category:Programming]] | [[Category:Programming]] |
Revision as of 09:05, 5 September 2010
Server plugins are code libraries that modify the behaviour of dedicated servers.

Compiling
You can find a sample plugin project at src\utils\serverplugin_sample\
. The Orange Box version is good for both Source 2007 and 2009.
- Windows
- Build with the Visual Studio project provided; see Compiling under VS2008 or Compiling under VS2010 for help with upgrading
- Linux
- Execute
make plugin
fromsrc/linux_sdk/
- Mac
- Not possible yet
To debug your plugin, you must launch the server with -allowdebug
.
Installing
Source automatically loads plugins from files matching <game>\addons\*.vdf*
. Their format is:
Plugin { file <path to plugin> }
- On Windows, do not add the file extension. On Linux, do.
- The path value is relative to the engine's binary folder (not the game's), so if you want to load a plugin from TF2's addons folder it will need to read
..\tf\addons\plugin
.
Managing
The following console commands are provided:
plugin_print
(particularly useful for confirming that your plugin is installed correctly)plugin_load
plugin_unload
plugin_pause
plugin_unpause
plugin_pause_all
plugin_unpause_all
Coding
The IServerPlugin interface
Plugins work by exposing an IServerPluginCallbacks
interface to the engine. The engine calls back into this interface when various events happen. The definition of this interface can be found in public/engine/iserverplugin.h
. The PLUGIN_RESULT
return type of some of the functions allows the plugin to control whether this call is passed onto the underlying mod or not, see the header file for details.
IServerPluginCallbacks explained
Management
bool Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory)
- Called when the plugin is loaded by the engine. This can happen either on initialization or when being re-loaded after being unloaded. The two parameters provide the interface factories that the plugin needs to operate. Returns false on error.
void UnLoad()
- Called when a plugin is unloaded, use this to disable any asynchronous tasks and remove any callbacks you have registered with the engine (for example a game events listener).
void Pause()
- Called when the operation of the plugin is paused (i.e it will stop receiving callbacks but should not be unloaded).
void UnPause()
- Called when a plugin is brought out of the paused state. You should re-enable any asynchronous events your plugin uses in this call
char* GetPluginDescription()
- This function should return a friendly string describing your plugin. Typically this would be its name and the author.
Server events
void LevelInit(char* pMapName)
- Called on level (map) startup, it is the first function called as a server enters a new level.
void ServerActivate(edict_t *pEdictList, int edictCount, int clientMax)
- This is called when the server successfully enters a new level, this will happen after the LevelInit call. This call provides pointers to the list of edicts created on the server and the maxplayer count of the server.
void GameFrame(bool simulating)
- Called once per server frame (typically 60 times a second). Server performance is very sensitive to the execution time of this function so keep anything you do in this function to a minimum.
void LevelShutdown()
- Called when a server is changing to a new level or is being shutdown. Remove any map specific allocations in this call. This can be called multiple times during a map change.
void OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, char *pCvarName, char *pCvarValue )
- Called when a query from
IServerPluginHelpers::StartQueryCvarValue
finishes.iCookie
is the value requested. void OnEdictAllocated(edict_t *edict)
void OnEdictAllocated(const edict_t *edict)
Clients
PLUGIN_RESULT ClientConnect(bool *bAllowConnect, edict_t *pEntity, char *pszName, char *pszAddress, char *reject, int maxrejectlen)
- Called when a client initially connects to a server. Set bAllowConnect to false to stop this user from connecting.
void ClientPutInServer(edict_t *pEntity, char const *playername)
- Called when a client spawns into a server. This is called before the server spawn function.
void ClientActive(edict_t *pEntity)
- Called after a client is fully spawned and configured by the Mod. Use this call to change any player specific settings.
void ClientDisconnect(edict_t *pEntity)
- Called when a client disconnects from the server.
void SetCommandClient(int index)
- Called by the ConVar code to let you keep track of which client is entering a ConCommand. Use this index in ConCommands if you want to see who is running the command. As ConVar commands don't have an edict associated with them when they run you can use this index to look up the entity that is running the command. The index is one less than the entity index.
void ClientSettingsChanged(edict_t *pEdict)
- Called when player specific cvars about a player change (for example the users name). Use this function to control what operations a user is allowed to perform to their settings (for example limiting usernames).
PLUGIN_RESULT ClientCommand(edict_t *pEntity, const CCommand &args)
- Called when a remote client enters a command into the console. This should be used to provide commands to clients (and ConCommand used to implement server side only commands).
PLUGIN_RESULT NetworkIDValidated(const char *pszUserName, const char *pszNetworkID)
- Called when the server retrieves a clients network ID (i.e Steam ID). Note that a clients network ID is not set on connect, you must wait for this callback before a users network ID is valid. The client name is passed into this function, you need to search the currently connected players to find the associated edict pointer. Note that the clients name can change between connect and id validation, you should use the entity index to track specific players. Note also that this function is NOT called for the local user when running a listen/non-dedicated server.
Listening to Events
The IGameEventManager2
(IGameEventManager
was used previously) interface allows a plugin to listen to game events. Game events are fired by a Mod when things of interest happen, like a player dying or the bomb being planted. IGameEventManager2::AddListener
should be called to subscribe to particular game events. It must be called for each event that your listener wishes to be aware of. FireGameEvent is then called in your plugin when an one of the listened for events happen. The data contained in each event is described by event configuration files, in particular:
hl2/resource/serverevents.res
hl2/resource/GameEvents.res
<mod dir>/resource/ModEvents.res
Creating ConVars and Commands
ConVars let you specify variables that users can use to configure the behavior of your plugin. ConCommands allow the creation of commands that your plugin provides. Creating both is easy and self contained, the code snippet below creates a new command empty_version
and a new variable plugin_empty
. These commands can be run from the server or client, you should use the index provided by SetCommandClient
to determine the source of the command.
CON_COMMAND( empty_version, "prints the version of the empty plugin" )
{
Msg( "Version:1.0.0.0\n" );
}
static ConVar empty_cvar("plugin_empty", "0", 0, "Example plugin cvar");
Interacting with Clients
The engine provides the IServerPluginHelpers
interface to allow plugins to message clients. This interface provides a single function, CreateMessage
that is called with 3 different dialog options to message users in different ways.
DIALOG_MSG, this prints a simple message to a client.
DIALOG_MENU, this provides a client with a menu of options.
DIALOG_TEXT, this shows a user a larger text field.
Examples of how to use this interface are shown in the sample plugin example. Each message will be displayed for 10 seconds in the HUD and for the menu and text option for up to 200 seconds in GameUI. Only 1 message can be displayed at a time, you have to wait for the current one to expire before a new one can be shown. Messages with a lower "level"
field can be used to override current messages (the minimum level value is 0). Details of valid message fields can be found in the sample code.
Release your plugin
When ready for public consumption or beta testing, announce your server plugin at a community website like SourcePlugins.