NAME

SpadsPluginApi - SPADS Plugin API

SYNOPSIS

  package MyPlugin;
  
  use SpadsPluginApi;
  
  no warnings 'redefine';
  
  my $pluginVersion='0.1';
  my $requiredSpadsVersion='0.11';
  
  sub getVersion { return $pluginVersion; }
  sub getRequiredSpadsVersion { return $requiredSpadsVersion; }
  
  sub new {
    my $class=shift;
    my $self = {};
    bless($self,$class);
    slog("MyPlugin plugin loaded (version $pluginVersion)",3);
    return $self;
  }
  
  1;

DESCRIPTION

SpadsPluginApi is a Perl module implementing the plugin API for SPADS. This API allows anyone to add new features as well as customize existing SPADS features (such as balancing algorithms, battle status presentation, players skills management, command aliases...).

This API relies on plugin callback functions (implemented by SPADS plugins), which can in turn call plugin API functions (implemented by SPADS core) and access shared SPADS data.

CALLBACK FUNCTIONS

The callback functions are called from SPADS core and implemented by SPADS plugins. SPADS plugins are actually Perl classes, which are instanciated as objects. So even if not indicated below, all callback functions receive a reference to the plugin object as first parameter (except the constructor, which receives the class name as first parameter).

Mandatory callbacks

To be valid, a SPADS plugin must implement at least these 3 callbacks:

new()

This is the plugin constructor, it is called when SPADS (re)loads the plugin. It must return the plugin object.

getVersion()

returns the plugin version number (example: "0.1").

getRequiredSpadsVersion()

returns the required minimum SPADS version number (example: "0.11").

Configuration callback

SPADS plugins can use the core SPADS configuration system to manage their own configuration parameters. This way, all configuration management tools in place (parameter values checking, !reloadconf etc.) can be reused by the plugins. To do so, the plugin must implement following configuration callback:

getParams()

This callback must return a reference to an array containing 2 elements. The first element is a reference to a hash containing the global plugin settings declarations, the second one is the same but for plugin preset settings declarations. These hashes use setting names as keys, and references to array of allowed types as values. The types must match the keys of %paramTypes defined in SpadsConf.pm.

Example of implementation:

  my %globalPluginParams = ( MyGlobalSetting1 => ['integer'],
                             MyGlobalSetting2 => ['ipAddr']);
  my %presetPluginParams = ( MyPresetSetting1 => ['readableDir','null'],
                             MyPresetSetting2 => ['bool'] );
  
  sub getParams { return [\%globalPluginParams,\%presetPluginParams]; }

Dependencies callback

SPADS plugins can use data and functions from other plugins (dependencies). But this can only work if the plugin dependencies are loaded before the plugin itself. That's why following callback should be used by such dependent plugins to declare their dependencies, which will allow SPADS to perform the check for them. Also, SPADS will automatically unload dependent plugins when one of their dependencies is unloaded.

getDependencies()

returns the dependencies plugins names list

Example of implementation:

  sub getDependencies { return ('SpringForumInterface','MailAlerts'); }

Event-based callbacks

Following callbacks are triggered by events from various sources (SPADS, Spring lobby, Spring server...):

onBattleClosed()
onBattleOpened()
onGameEnd(\%endGameData)
onJoinBattleRequest($userName,$ipAddr)

This callback must return:

0 if the user is allowed to join the battle

1 if the user isn't allowed to join the battle (without explicit reason)

"<explicit reason string>" if the user isn't allowed to join the battle, with explicit reason

onLobbyConnected($lobbyInterface)
onLobbyDisconnected()
onPresetApplied($oldPresetName,$newPresetName)
onPrivateMsg($userName,$message)

This callback must return:

0 if the message can be treated by other plugins and SPADS core

1 if the message must not be treated by other plugins and SPADS core (this prevents logging)

onReloadConf($keepSettings)

This callback must return:

0 if an error occured while reloading the plugin configuration

1 if the plugin configuration has been reloaded correctly

onSettingChange($settingName,$oldValue,$newValue)
onSpringStart($springPid)
onSpringStop($springPid)
onUnload()

This callback is called when the plugin is unloaded. If the plugin added handlers for SPADS command, lobby commands, or Spring commands, then they must be removed here. If the plugin handles persistent data, then these data must be serialized here.

onVoteRequest($source,$user,\@command,\%remainingVoters)

This callback is called each time a vote is requested by a player.

\%remainingVoters is a reference to a hash containing the players allowed to vote. This hash is indexed by player names. The plugin can filter these players by simply removing the corresponding entries from the hash.

This callback must return 0 to prevent the vote call from happening, or 1 to allow it.

onVoteStart($user,\@command)
onVoteStop($voteResult)

$voteResult values: -1 (vote failed), 0 (vote cancelled), 1 (vote passed)

postSpadsCommand($command,$source,$user,\@params,$commandResult)

This callback is called each time a SPADS command has been executed.

If $commandResult is defined and set to 0, then it means the command failed.

preGameCheck($force,$checkOnly,$automatic)

This callback is called each time a game is going to be launched, to allow plugins to perform pre-game checks and prevent the game from starting if needed.

$force is 1 if the game is being launched using !forceStart command, 0 else

$checkOnly is 1 if the callback is being called in the context of a vote call, 0 else

$automatic is 1 if the game is being launched automatically through autoStart functionality, 0 else

The return value must be the reason for preventing the game from starting (for example "too many players for current map"), or 1 if no reason can be given, or undef to allow the game to start.

preSpadsCommand($command,$source,$user,\@params)

This callback is called each time a SPADS command is going to be executed.

It must return 0 to prevent the command from being treated by other plugins and executed by SPADS core, or 1 to allow it.

Customization callbacks

Following callbacks are called by SPADS during specific operations to allow plugins to customize features (more callbacks can be added on request):

addStartScriptTags(\%additionalData)

This callback is called when a Spring start script is generated, just before launching the game. It allows plugins to declare additional scrip tags which will be written in the start script.

\%additionalData is a reference to a hash which must be updated by adding the desired keys/values. For example a plugin can add a modoption named "hiddenoption" with value "test" like this: $additionalData{"game/modoptions/hiddenoption"}="test" . For tags to be added in player sections, the special key "playerData" must be used. This special key must point to a hash associating each account ID to a hash containing the tags to add in the corresponding player section.

balanceBattle(\%players,\%bots,$clanMode,$nbTeams,$teamSize)

This callback is called each time SPADS needs to balance a battle and evaluate the resulting balance quality. It allows plugins to replace the built-in balance algorithm.

\%players is a reference to a hash containing the players in the battle lobby. This hash is indexed by player names, and the values are references to a hash containing player data. For balancing, you should only need to access the players skill as follows: $players->{<playerName>}->{skill}

\%bots is a reference to a hash containing the bots in the battle lobby. This hash has exact same structure has \%players.

$clanMode is the current clan mode which must be applied to the balance. Clan modes are specified here. <maxUnbalance> thresholds are automatically managed by SPADS, plugins don't need to handle them. So basically, plugins only need to check if tag and/or pref clan modes are enabled and apply them to their balance algorithm.

$nbTeams and $teamSize are the target battle structue computed by SPADS. The number of entities to balance is the number of entries in \%players + number of entries in \%bots. The number of entities to balance is always > $nbTeams*($teamSize-1), and <= $nbTeams*$teamSize.

If the plugin is able to balance the battle, it must update the \%players and \%bots hash references with the team and id information. Assigned player teams must be written in $players->{<playerName>}->{battleStatus}->{team} , and assigned player ids must be written in $players->{<playerName>}->{battleStatus}->{id} . \%bots works the same way. The return value is the unbalance indicator, defined as follows: standardDeviationOfTeamSkills * 100 / averageTeamSkill.

If the plugin is unable to balance the battle, it must not update \%players and \%bots. The callback must return undef or a negative value so that SPADS knows it has to use another plugin or internal balance algorithm instead.

canBalanceNow()

This callback allows plugins to delay the battle balance operation. It is called each time a battle balance operation is required (either automatic if autoBalance is enabled, either manual if !balance command is called). If the plugin is ready for balance, it must return 1. Else, it can delay the operation by returning 0 (the balance algorithm won't be launched as long as the plugin didn't return 1).

changeUserAccessLevel($userName,\%userData,$isAuthenticated,$currentAccessLevel)

This callback is called by SPADS each time it needs to get the access level of a user. It allows plugins to overwrite this level. Don't call the getUserAccessLevel($user) function from this callback, or the program will be locked in recusrive loop! (and it would give you the same value as $currentAccessLevel anyway).

\%userData is a reference to a hash containing the lobby data of the user

$isAuthenticated indicates if the user has been authenticated (0: lobby server in LAN mode and not authenticated at autohost level, 1: authenticated by lobby server only, 2: authenticated by autohost)

The callback must return the new access level value if changed, or undef if not changed.

filterRotationMaps(\@rotationMaps)

This callback is called by SPADS each time a new map must be picked up for rotation. It allows plugins to remove some maps from the rotation maps list just before the new map is picked up.

\@rotationMaps is a reference to an array containing the names of the maps currently allowed for rotation.

The callback must return a reference to a new array containing the filtered map names.

setMapStartBoxes(\@boxes,$mapName,$nbTeams,$nbExtraBox)

This callback allows plugins to set map start boxes (for "Choose in game" start position type).

\@boxes is a reference to an array containing the start boxes definitions. A start box definition is a string containing the box coordinates separated by spaces, in following order: left, top, right, bottom (0,0 is top left corner and 200,200 is bottom right corner). If the array already contains box definitions, it means SPADS already knows boxes for this map. In this case the plugin can choose to override them by replacing the array content, or simply leave it unmodified.

$nbExtraBox is the number of extra box required. Usually this is 0, unless a special game mode is enabled such as King Of The Hill.

The callback must return 1 to prevent start boxes from being replaced by other plugins, 0 else.

setVoteMsg($reqYesVotes,$maxReqYesVotes,$reqNoVotes,$maxReqNoVotes,$nbRequiredManualVotes)

This callback allows plugins to customize the vote status messages.

$reqYesVotes is the total number of "yes" votes required for vote to pass (if away-voters don't vote).

$reqNoVotes is the total number of "no" votes required for vote to fail (if away-voters don't vote).

$maxReqYesVotes is the maximum total number of "yes" votes required for vote to pass (if all away-voters come back and vote).

$maxReqNoVotes is the maximum total number of "no" votes required for vote to fail (if all away-voters come back and vote).

$nbRequiredManualVotes is the minimum number of manual votes required for vote to be taken into account.

The callback must return a list containing following 2 elements: the lobby vote message, and the in-game vote message (undef values can be used to keep default messages).

updateCmdAliases(\%aliases)

This callback allows plugins to add new SPADS command aliases by adding new entries in the \%aliases hash reference. This hash is indexed by alias names and the values are references to an array containing the associated command. For example, a plugin can add an alias "!cvmap ..." for "!callVote map ..." like this: $aliases->{cvmap}=['callVote','map']

"%<N>%" can be used as placeholders for original alias command parameters. For example, a plugin can add an alias "!iprank <playerName>" for "!chrank <playerName> ip" like this: $aliases->{iprank}=['chrank','%1%','ip']

updatePlayerSkill(\%playerSkill,$accountId,$modName,$gameType)

This callback is called by SPADS each time it needs to get or update the skill of a player (on battle join, on game type change...). This allows plugins to replace the built-in skill estimations (rank, TrueSkill...) with custom skill estimations (ELO, Glicko ...).

\%playerSkill is a reference to a hash containing the skill data of the player. The plugin must update the skill entry as follows: $playerSkill->{skill}=<skillValue>

$accountId is the account ID of the player for whom skill value is requested.

$modName is the currently hosted MOD (example: "Balanced Annihilation V7.72")

$gameType is the current game type ("Duel", "Team", "FFA" or "TeamFFA")

The return value is the skill update status: 0 (skill not updated by the plugin), 1 (skill updated by the plugin), 2 (skill updated by the plugin in degraded mode)

updateGameStatusInfo(\%playerStatus,$accessLevel)

This callback is called by SPADS for each player in game when the !status command is called, to allow plugins to update and/or add data which will be presented to the user.

\%playerStatus is a reference to the hash containing current player status data. The plugin must update existing data or add new data in this hash. For example: $playerStatus->{myPluginData}=<myPluginValue>

$accessLevel is the autohost access level of the user issuing the !status command.

The return value must be a reference to an array containing the names of the status information updated or added by the plugin.

updateStatusInfo(\%playerStatus,$accountId,$modName,$gameType,$accessLevel)

This callback is called by SPADS for each player in the battle lobby when the !status command is called, to allow plugins to update and/or add data which will be presented to the user.

\%playerStatus is a reference to the hash containing current player status data. The plugin must update existing data or add new data in this hash. For example: $playerStatus->{myPluginData}=<myPluginValue>

$accountId is the account ID of the player for whom status data update is requested.

$modName is the currently hosted MOD (example: "Balanced Annihilation V7.72")

$gameType is the current game type ("Duel", "Team", "FFA" or "TeamFFA")

$accessLevel is the autohost access level of the user issuing the !status command.

The return value must be a reference to an array containing the names of the status information updated or added by the plugin.

Event loop callback

SPADS uses the asynchronous programming paradigm, so it is based on a main event loop. The following callback is called during each iteration of this event loop:

eventLoop()

Warning: this callback is called very frequently (during each iteration of SPADS main event loop), so performing complex operations here can be very intensive on the CPU. It is recommended to use timers (addTimer/removeTimer functions) instead for all time related operations (timeouts, scheduled actions, regular serialization of persistent data to avoid data loss...). This callback shouldn't be blocking, otherwise SPADS may become unstable.

API FUNCTIONS

The API functions are implemented by SPADS core and can be called by SPADS plugins.

Accessors

getCurrentVote()
getLobbyInterface()
getLobbyState()

This accessor returns an integer describing current lobby state (0: not connected, 1: connecting, 2: connected, 3: just logged in, 4: lobby data received, 5: opening battle, 6: battle opened)

getRunningBattle()
getSpadsConf()
getSpadsConfFull()
getSpringInterface()
getSpringPid()
getSpringServerType()
getTimestamps()

Plugin management

getPlugin($pluginName=caller())

This function returns the plugin object matching the plugin name given as parameter $pluginName. If no parameter is provided, the plugin name of the plugin calling the function is used.

getPluginConf($pluginName=caller())

This function returns the plugin configuration for the plugin named $pluginName. If no parameter is provided, the plugin name of the plugin calling the function is used. The return value is a reference to a hash using plugin settings names as keys and plugin settings values as values.

Handlers management

addLobbyCommandHandler(\%handlers,$priority=caller())

This function allows plugins to set up their own handlers for Spring lobby commands received by SPADS from lobby server.

\%handlers is a reference to a hash which contains the handlers to be added: each entry associates a lobby command (in uppercase) to a handler function implemented by the plugin. For example, with { JOINBATTLEREQUEST => \&hLobbyJoinBattleRequest } , the plugin has to implement the function hLobbyJoinBattleRequest. The parameters passed to the handlers are the command tokens: the command name followed by command parameters. Refer to Spring lobby protocol specifications for more information.

$priority is the priority of the handlers. Lowest priority number actually means higher priority. If not provided, the plugin name is used as priority, which means it is executed after handlers having priority < 1000, and before handlers having priority > 1000. Usually you don't need to provide priority, unless you use data managed by other handlers.

addSpadsCommandHandler(\%handlers,$replace=0)

This function allows plugins to add or replace SPADS command handlers.

\%handlers is a reference to a hash which contains the handlers to be added or replaced: each entry associates a SPADS command to a handler function implemented by the plugin. For example, with { myCommand => \&hSpadsMyCommand } , the plugin has to implement the function hSpadsMyCommand. The parameters passed to the handlers are: $source,$userName,\@params,$checkOnly.

$source indicates the way the command has been called ("pv": private lobby message, "battle": battle lobby message, "chan": master lobby channel message, "game": in game message)

$userName is the name of the user issuing the command

\@params is a reference to an array containing the command parameters

$checkOnly indicates that the command must not be executed but only checked for consistency (this mode is used for !callVote command)

If the command cannot be executed (invalid syntax ...) the handler must return 0. If the command is correct but requires automatic parameter adjustments (automatic case correction or name completion for example), a string containing the adjusted command must be returned. If it can be executed directly without any adjustement, 1 must be returned.

$replace indicates if the handlers provided can replace existing ones: 0 means add handlers only if there is no handler for the given command (default), 1 means add or replace if existing.

addSpringCommandHandler(\%handlers,$priority=caller())

This function allows plugins to set up their own handlers for Spring AutoHost commands received by SPADS from Spring server.

\%handlers is a reference to a hash which contains the handlers to be added: each entry associates a Spring AutoHost command to a handler function implemented by the plugin. The Spring AutoHost command names must match the values of %commandCodes defined in SpringAutoHostInterface.pm. For example, with { SERVER_STARTED => \&hSpringServerStarted } , the plugin has to implement the function hSpringServerStarted. The parameters passed to the handlers are the command tokens: the command name followed by command parameters. Refer to Spring autohost protocol specifications (from source comments) for more information.

$priority is the priority of the handlers. Lowest priority number actually means higher priority. If not provided, the plugin name is used as priority, which means it is executed after handlers having priority < 1000, and before handlers having priority > 1000. Usually you don't need to provide priority, unless you use data managed by other handlers.

removeLobbyCommandHandler(\@commands,$priority=caller())

This function must be called by plugins which added lobby command handlers previously using addLobbyCommandHandler function, when these handlers are no longer required (for example in the onUnload callback, when the plugin is unloaded).

\@commands is a reference to an array containing the lobby command names (in uppercase) for which the handlers must be removed.

$priority is the priority of the handlers to remove. It must be the same as the priority used when adding the handlers. If not provided, the plugin name is used as priority. Usually you don't need to provide priority, unless you use data managed by other handlers.

removeSpadsCommandHandler(\@commands)

This function must be called by plugins which added SPADS command handlers previously using addSpadsCommandHandler function, when these handlers are no longer required (for example in the onUnload callback, when the plugin is unloaded).

\@commands is a reference to an array containing the SPADS command names (in uppercase) for which the handlers must be removed.

removeSpringCommandHandler(\@commands,$priority=caller())

This function must be called by plugins which added Spring AutoHost command handlers previously using addSpringCommandHandler function, when these handlers are no longer required (for example in the onUnload callback, when the plugin is unloaded).

\@commands is a reference to an array containing the Spring AutoHost command names for which the handlers must be removed.

$priority is the priority of the handlers to remove. It must be the same as the priority used when adding the handlers. If not provided, the plugin name is used as priority. Usually you don't need to provide priority, unless you use data managed by other handlers.

SPADS operations

applyPreset($presetName)
cancelCloseBattle()
cancelQuit($reason)
closeBattle($reason)
getUserAccessLevel($user)
loadArchives()
queueLobbyCommand(\@lobbyCommand)
quit($type,$reason)
rehost($reason)
slog($message,$level)

This function uses SPADS logging system to write a message in main SPADS log file.

$message is the log message

$level is the log level of the message: 0 (critical), 1 (error), 2 (warning), 3 (notice), 4 (info), 5 (debug)

AutoHost messaging system

answer($message)
broadcastMsg($message)
invalidSyntax($user,$lowerCaseCommand,$cause='')
sayBattle($message)
sayBattleAndGame($message)
sayBattleUser($user,$message)
sayChan($channel,$message)
sayGame($message)
sayPrivate($user,$message)

Time utils

getDirModifTime($directory)
secToDayAge($seconds)
secToTime($seconds)

Data formatting

formatArray
formatFloat($float)
formatInteger($integer)
formatList

Forking processes

forkProcess(\&processFunction,\&endProcessCallback,$preventQueuing=1)

This function allows plugins to fork a process from main SPADS process, for parallel processing. It returns the PID of the forked process on success, -1 if the fork request has been queued, or 0 if the fork request failed.

\&processFunction is a reference to a function containing the code to be executed in the forked process (no parameter is passed to this function). This function can call exit to end the forked process with a specific exit code. If it returns without calling exit, then the exit code 0 will be used.

\&endProcessCallback is a reference to a function containing the code to be executed in main SPADS process, once the forked process exited. Following parameters are passed to this function: $exitCode (exit code of the forked process), $signalNb (signal number responsible for forked process termination if any), $hasCoreDump (boolean flag indicating if a core dump occured in the forked process), $pid (PID of the forked process that just exited).

$preventQueuing is an optional boolean parameter (default value: 1) indicating if the fork request must not be queued (i.e., the fork request will fail instead of being queued if too many forked processes are already running)

forkCall(\&processFunction,\&endProcessCallback,$preventQueuing=0)

This function allows plugins to call a function asynchronously and retrieve the data returned by this function (this is done internally by forking a process to execute the function and use a socketpair to transmit the result back to the parent process). It returns the PID of the forked process on success, -1 if the fork request has been queued, or 0 on error.

\&processFunction is a reference to a function containing the code to be executed in the forked process (no parameter is passed to this function). This function must not call exit, it should use return instead to return values (scalars, arrays, hashes...) that will be passed to the callback.

\&endProcessCallback is a reference to a function containing the code to be executed in main SPADS process, once the forked function call (\&processFunction) returned. The values returned by the forked function call will be passed as parameters to this callback.

$preventQueuing is an optional boolean parameter (default value: 0) indicating if the fork request must not be queued (i.e., the fork request will fail instead of being queued if too many forked processes are already running)

Timers management

addTimer($name,$delay,$interval,\&callback)

This function allows plugins to add timed events (timers) in order to delay and/or repeat code execution. It returns 1 if the timer has correctly been added, 0 else.

$name is a unique name given by the plugin for this timer.

$delay is the delay in seconds before executing the \&callback function.

$interval is the interval in seconds between each execution of the \&callback function. If this value is set to 0, the \&callback function will be executed only once.

\&callback is a reference to a function containing the code to be executed when the timed event occurs. This callback must not be blocking, otherwise SPADS may become unstable.

removeTimer($name)

This function must be used by plugins to remove timed events (timers) added previously with the addTimer function. It returns 1 if the timer could be removed, 0 else. Note: Non-repeating timers (i.e. having null interval value) are automatically removed once they are triggered.

$name is the unique timer name given by the plugin when the timer was added using the addTimer function.

Sockets management

addSocket(\$socketObject,\&readCallback)

This function allows plugins to add sockets to SPADS asynchronous network system. It returns 1 if the socket has correctly been added, 0 else.

\$socketObject is a reference to a socket object created by the plugin

\&readCallback is a reference to a plugin function containing the code to read the data received on the socket. This function will be called automatically every time data are received on the socket, with the socket object as unique parameter. It must not block, and only unbuffered Perl functions must be used to read data from the socket (sysread() or recv() for example).

removeSocket(\$socketObject)

This function allows plugins to remove sockets from SPADS asynchronous network system. It returns 1 if the socket has correctly been removed, 0 else.

\$socketObject is a reference to a socket object previously added by the plugin

SHARED DATA

Constants

Following constants are directly accessible from plugin modules:

$spadsVersion
$spadsDir

Variables

Following variables are directly accessible from plugin modules, but it is strongly recommended to use the accessors from the API instead:

$::autohost
%::conf
%::currentVote
$::lobby
$::lobbyState
$::p_runningBattle
%::plugins
$::spads
%::spadsHandlers
$::springPid
$::springServerType
%::timestamps

SEE ALSO

SPADS plugin development tutorials

Commented SPADS plugin templates: Simple plugin, Configurable plugin, New command plugin

SPADS documentation, especially regarding plugins management: pluginsDir setting, autoLoadPlugins setting, plugin command

Spring lobby protocol specifications

Spring autohost protocol specifications (from source comments)

Introduction to Perl

COPYRIGHT

Copyright (C) 2013 Yann Riou <yaribzh@gmail.com>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.