Plugin development for Fast 3.1.x

It's not a complete documentation, but a basic explanation which should permit to someone having a minimum php programming knowledge to have some chance to understand how Fast and its plugins work. If you want to understand, please read all !...

If you make a plugin which could interest some other people, please announce it on Fast/Dedimania forum, so you will be able to get some advice, tests and feedbacks, or just some halp. If you want to, i can put your plugins at the same place as Fast itself.

Plugin definition

A plugin is just a php file in which is registered the plugin name, this name will be the base name used by Fast to call the plugin callbacks, most issued from the TM server callbacks or some Fast specific events.

You can use plugin.98.howto.php as a starting template. Plugins must have a name like plugin.NN.xxxxxx.php, knowing that they will be read in the plugins directory in alphanumerical order. Habitually the same NN and xxxxxx are used than in what is explain after, but no control is made. Btw, xxxxxx must be unique. In the file should appear:

registerPlugin('xxxxxx',NN);

Once done, all functions xxxxxxEventname(...) will be automatically called for associated events, in the NN order (so this number can have a big impact in some case). Most events names are understandable from their name or from the associated server callbacks. Some others are purely Fast ones, or build by Fast when possible for servers without callbacks (ingame TMS/TMO/TMN servers); read $_func_list comments in fast_main.php (those which have a NeedChalSure are eventually delayed until the map is surely known, because GetCurrentChallengeInfo can be wrong in some cases, and so if the script is just started the current map is uncertain (this was a cause of false records in Fast2 for example).
The event xxxxxxInit() is called at startup to permit to all plugins to init themselves, in priority order, when all plugins files have already been included.

To see how events works/come, try to uncomment registerPlugin() in plugin.98.howto.php , then launch Fast in a terminal, so you will see the various events, which one is called in which case, and in which order. Eventually add prints or parameters to verify what is received in each case... To print, you can use Console("...") or debugPrint("....",$variable) which print also in the log file. You can also look the other existing plugins (look preferably the last ones for simpler examples).

Note: if you make global variables in your plugin, try to name them $xxxxxx_nom (where xxxxxx is the plugin name), to avoid name collisions with other plugins or future changes in Fast itself. Thanks.

Note2: for some complex plugin, perhaps some changes should be made in Fast files because else the plugin would be too complicated, or because of some Fast or TM server bug. In such case contact me to look at the problem and eventually make some Fast changes (post on Dedimania/Fast forum, or PM me in tm-forum or Traxico Team forum)

Send a command to the TM server

To send a method to the server (dedicated or not), use this function :

addCall(action,'TMServerMethod',...)

The actions permit to do some automatic action when the reply is received. It can be another method call, a function callback with the reply content in the num-th parameter. This is for advanced use, most time you will just use null, true or login

'TMServerMethod' is a TM server method, see TMU dedicated methods, TMS/TMO/TMN dedicated methods, or build yourself the method list of the used server using the php script included in the dedicated archive. Note that all ingame servers are more limited than dedicated ones.

There are some other versions of this function (see fast_general.php) : addCallArray(action,addcall_array) where addcall_array is an array('TMServerMethod',...), and two versions permetting to delay the call (milliseconds timeout) before sending it to the server : addCallDelay(delay,action,'TMServerMethod',...) and addCallDelayArray(delay,action,addcall_array).

Available variables

Some global variables are available and are normally kept up to date automatically by Fast. Main ones come directly from the corresponding server methods responses (exemple: http://kheops.unice.fr/slig/tmu/xmlrpc/TMU-dedicated-2007-01-09.html ), and some are the same but with previous values (used to detect changed values).

A Fast game admin can use the chat command '/debug variable' to get a print_r or the variable/array in the terminal and log, usefull to see values at any time. Note: don't put the leading $ (exemple: /debug _players )

Main are (see // Init variables in fast_main.php) :

$_Status , $_old_Status
$_ChallengeInfo , $_old_ChallengeInfo , $_unsure_ChallengeInfo
$_NetworkStats
$_GameInfos
$_NextGameInfos
$_ServerOptions
$_ChallengeList
$_CurrentChallengeIndex
$_players

Direct use of $_PlayerList, $_Ranking and $_PlayerInfo should be avoided : instead use $_players, which is an associative array with logins as index, include all there values, and include also many info used by Fast and plugins ($_players is made and kept up to date by plugin plugin.01.players.php which is mandatory and must stay the 1st plugin).

$_debug : debug level which change what will be written in the
        log. It's not always really logical... consider than 3 will be enough 
        most time to understand and debug.
$_use_cb : true if the server work in callback mode.
$_is_dedicated : true if it's a dedicated server.
$_Game : 'TMU', 'TMS', 'TMO' or 'TMN'
$_currentTime : current time in milliseconds
$_players_round_current : current round number in game (Rounds and Team modes)
$_players_actives : number of active players
$_players_spec : number of specs
$_players_finished : number of player having finished (Rounds, Team and Laps modes)
$_players_positions : array with live infos about players order in current round

There are many other global variables, plugin specific, or too much specific to try to document all here : you will have to search in the plugins... :p

Send translated text in the player game language

Game language of each player is indicated in the TM server infos only for TMU, so except if the language is choosed manually by the player, it's nearly useless for older TM games.

How it works : all the locale.xxxxx.xml.txt file are read and parsed, and must have a xml struct like:

<fast><locale><language><tag>sprintf like string text</tag></language></locale></fast>

there can be set or not several languages, 'language' is 'en', 'fr', etc. and have to be one of the existing Translations name of the game. There can be one or more 'tag', each one being the name used in locale functions to get the wanted la translation. The functions parameters after the tag will be passed as sprintf parameters, so the translation string can use them with %d, %f, %s etc. in correct order (so in the xml please add the string parameters in any !)

localeText() and localeTextArray() return a string, which can be sent to a player using addCall('ChatSendToLogin','text','login') or addCall('ChatSendServerMessageToLogin','text','login'). localeTextArray() permit to create a translation composed of several parts as string to translate, simple string, nomber etc. which will be concatenated, which can make a code nicer than several localeText() concatenated. Tags are translated according to the indicated payer language, and if the tag don't exist in the player language it is searched in the default language one ('en' in the default Fast config).

// get localized string using login language
//   localeText($login,$tag,...)
// set login to null if not related to a player
// tag is the searched tag in the locale file
// other params are sprintf like params
localeText($login,$string)

// get localized string using login language
//   localeTextArray($login,array($tag,...))
// set login to null if not related to a player
// tag is the searched tag in the locale file
// other params in the array are sprintf like params
localeTextArray($login,$string_tag_array)

multiLocaleText() (tmu only) is a litle more complex, it does not return a string but an array in the ChatSendToLanguage()/ChatSendServerMessageToLanguage() needed format. It will permit to send easily a message to all players, in the right language for each player when available, and else in default language.

// get localized array for ChatSendToLanguage TM method for all used languages
//   multiLocaleText(mixed,mixed,...)
// all mixed are concatenated, each mixed can be :
// - an array($tag,...) , where tag is the searched tag in the locale file, and
//   other params in the array are sprintf like params
// - any other value will just be concatenated
multiLocaleText($string_tag_array,...)

Manialinks

First, note that only one manialink can be drawn at the same time on the client game. Fast already use it, and so your plugin can't just send a manialink to player using a addCall(). So no choice : you have to integrate your plugin manialink in the existing Fast system, handled by plugin.10.manialinks.php ! The use of manialinks need first to know how to write a manialink xml string. Next will only explain how to make it work with Fast.

The plugin.10.manialinks.php plugin deal with each player manialink draw/redraw, at begining and at each change.

This plugin build a "<manialinks>...</manialinks>" which will concatenate all "<manialink>...</manialink>" given by other plugins. Each plugin which want to add its own manialink part need to register its callback/hook function, which will be called when needed. Some function are here to help and manage the numerical id used in manialinks for the player interactions, because this id have to be unique and so each plugin have to use different values.

Be carrefull : Fast manialinks are computed individually for each player. Be sure to limit the complexity of your manialink callback and the resources used for each call.

Note also that a manialink draw update make a graphic lag, so it is not usable while the player is driving !

// add a hook/callback which will be called to build a manialink part
// specify $login, or set it to true for all players, or false to set
// it as default for new players. Set $force to 1 to get it visible
// while playing, to 2 to force it visible.
function manialinksAddManialinkHook($login,$id_name,$hook,$force=0)
  $hook is the callback function which will be called by Fast when
	needed to build the player manialink, and have tu return a string
  (like "<manialink>...</manialink>"). $id_name must be unique,
  habitually the plugin name.

  The $hook function should be defined like this :
    function $hook($login,&$pml){ return '<manialink>...</manialink>'; }
	where $login is the login of the player for whom the current manialink is
	built, and $pml is a reference to $_players[$login]['ML'] data.

// remove a hook added with manialinksAddManialinkHook
function manialinksRemoveManialinkHook($login,$id_name)
  remove the hook of a previously added manialink callback


// add a general action name and get its value
manialinksAddAction($name) :
  Used to allocate an id for the manialink answers when the player click
  Give a name including the plugin name, like 'pluginname.xxx'
  The id will be usable in the global array $_ml_act[$name], or in manialinksGetAction($name)
	Habitually used in some xml like "<text action=\''.$_ml_act[$name].'\'>clic it text</text>",

// remove a general action name and value
manialinksRemoveAction($name)

// Get an base value of the specified size for manialink actions for custom use
// (and so avoid having 2 plugins using the same values).
// If login is specified then get it specifically for a user.
// Player action values start at 20000, so if you get a general
// value >20000 then it means that some plugin was too hungry
manialinksGetActionBase($login=null,$size=100)
  Used if a pool of id is wanted, after your plugin is free to use
  from the return value id to  id+size-1

// get a general action value
manialinksGetAction($name)
  return the id for the name (allocated by manialinksAddAction())

Special manialink used for records

Fast also include in plugin.16.ml_times.php a special manialink permitting to show other thing than records, but at the same place. If you want to use it, you can loo, at 2 plugins to see how it works : plugin.18.ml_records.php and plugin.90.ktlc.php. Mainly, it need to add a callback using ml_timesAddTimesMod(), remove it using ml_timesRemoveTimesMod(), and return an array of the right form when the callback is called.
// Add a callback to draw times (or other) in records place
function ml_timesAddTimesMod($name,$hook,$data)

// remove a callback added with ml_timesAddTimesMod()
function ml_timesRemoveTimesMod($name)
The callback ($hook) has the form : callbackname($login,&$data,$num,$min){} , where: $data is the data given previously to the function ml_timesAddTimesMod(), $num is the number of visible entries in the player panel, $min is the minimum number of entries to return. It must return an array ($table in the example) containing :

Various functions

Most usefull functions are in fast_general.php (well many are probably of no use in plugins). Here are some of them:

// formats a time: 1200000 -> 2:00.00
MwTimeToString($MwTime,$msec=true)

// formats a time: -12340 -> -12.34
MwDiffTimeToString($DiffTime)

// Remove colors from strings : $s$fffhello -> hello
stripColors($str)

// Remove links $h $l from strings.  if not force then remove links only if game is known 
//  and not TMU (ie which don't support the links)
stripLinks($str,$force=false)

// return a string with Player name, used for beginning of chat line
authorChat($author)

// verify if login is in admin list
verifyAdmin($login)

// Verify than 'Login' in given array is really of type string and convert them is needed
// because there are problems with pure numeric logins seen as int
loginToString(&$response,$level)

Notes

You can use some utilities xmlrpc for php which can help you to make tests about commands/methods of TM servers, and for TMU using a dedicated server test ingame manialinks, and so verify layout befoire trying to make it in a plugin


Good luck ;)
Slig