-
Notifications
You must be signed in to change notification settings - Fork 8
Developpers : First plugin
First of all we recomend any developer wto use an IDE. This will allow you to have code completation and not need relay on documentation that we didn't generate. The IDE will find the documentations we have provide it inside the code and will allow you develop faster & with less errors.
We do strongly recomend Netbeans or PHPStorm as the 2 best IDE's out there.
If you have created plugins for ManiaLive before you will see that expansion isn't much different, the main differences will be due to all the eXpansion layer that will allow your plugins to be much more interactive and easy to configure by admins.
We will consider that you will release you plugin under the "oliverde8" name, and that the plugin that we will do will be called test. This means our plugin needs to be placed under here libraries/ManiaLivePlugins/oliverde8/test
. Our working namespace will be ManiaLivePlugins\oliverde8\test
Here we will create 2 files. One of them is the plugin that will contain all the functional part of what you intend to do. This file will be called Test.php
. The second file is the file that will contain all the information for your plugin to be able to interact with expansion even when it isn't still loaded. This interections are about letting now the system which game modes and titles your plugin support and declaring all the configuration that you plugin needs.
We will work on the details on the MetaData in another tutorial for now we will just create a MetaData that does nothing. Let's create a MetaData.php file.
namespace ManiaLivePlugins\oliverde8\test;
class MetaData extends ManiaLivePlugins\eXpansion\Core\types\MetaData{
//@todo work on easy to configure settings & configure dependencies
}
Once this is done we can create our plugin. So let's create the test.php file
and put this content in it
namespace ManiaLivePlugins\oliverde8\test;
class test extends ManiaLivePlugins\eXpansion\Core\types\BasicPlugin{
public function exp_onLoad()
{
}
public function exp_onReady()
{
}
public function exp_onUnLoad()
{
}
}
We now should have a working plugin that does nothing. To test it out, restart expansion on your "dev" server and check the plugin list. You should see a a plugin named ManiaLivePlugins\oliverde8\test\test
.
It is important to extend BasicPlugin of eXpansion and not the Plugin type from ManiaLive. Our plugin type extends the ManiaLive plugin types and by doing so will allow the plugin to have ingame configuration, translations and most importantly it will allow the plugin management system that loads & unloads the plugin depending on the game mode and title to work.
I would like to note that ManiaLive plugins do work with little modifications on eXPansion but they will not be supported by the plugin management system and the in game configuration.
There is 2 way to for listening to events, the first methods will allow you to start listening to events without having to write to much code. Those events are let say the base events, the events that will always exist. Events like onBeginMap, onPlayerConnect is part of those events.
In short this will allow us to listen to Dedicated server events and to manialive events, as well as to some new events eXpansion has added to the system. We start by the basic Dedicated events.
Those are the events that you will find in any other controller. To start to listen to those events in the exp_onReady() function you will need to call
$this->enableDedicatedEvents();
Once you activate this the dedicated event methods of you plugin will be called. Yes you have defined no methods, but empty methods have been defined for you. Here is the list of all methods the dedicated events will call with their parameters :
/**
* Method called when a Player join the server
* @param string $login
* @param bool $isSpectator
*/
function onPlayerConnect($login, $isSpectator);
/**
* Method called when a Player quit the server
* @param string $login
*/
function onPlayerDisconnect($login, $disconnectionReason);
/**
* Method called when a Player chat on the server
* @param int $playerUid
* @param string $login
* @param string $text
* @param bool $isRegistredCmd
*/
function onPlayerChat($playerUid, $login, $text, $isRegistredCmd);
/**
* Method called when a Answer to a Manialink Page
* difference with previous TM: this is not called if the player doesn't answer, and thus '0' is also a valid answer.
* @param int $playerUid
* @param string $login
* @param int $answer
*/
function onPlayerManialinkPageAnswer($playerUid, $login, $answer,array $entries);
/**
* Method called when the dedicated Method Echo is called
* @param string $internal
* @param string $public
*/
function onEcho($internal, $public);
/**
* Method called when the server starts
*/
function onServerStart();
/**
* Method called when the server stops
*/
function onServerStop();
/**
* Method called when the Race Begin
*/
function onBeginMatch();
/**
* Method called when the Race Ended
* struct of SPlayerRanking is a part of the structure of Maniaplanet\DedicatedServer\Structures\Player object
* struct SPlayerRanking
* {
* string Login;
* string NickName;
* int PlayerId;
* int Rank;
* [for legacy TrackMania modes also:
* int BestTime;
* int[] BestCheckpoints;
* int Score;
* int NbrLapsFinished;
* double LadderScore;
* ]
* }
* @param SPlayerRanking[] $rankings
* @param int|SMapInfo $winnerTeamOrMap Winner team if API version >= 2012-06-19, else the map
*/
function onEndMatch($rankings, $winnerTeamOrMap);
/**
* Method called when a map begin
* @param SMapInfo $map
* @param bool $warmUp
* @param bool $matchContinuation
*/
function onBeginMap($map, $warmUp, $matchContinuation);
/**
* Method called when a map end
* @param SPlayerRanking[] $rankings
* @param SMapInfo $map
* @param bool $wasWarmUp
* @param bool $matchContinuesOnNextMap
* @param bool $restartMap
*/
function onEndMap($rankings, $map, $wasWarmUp, $matchContinuesOnNextMap, $restartMap);
/**
* Method called on Round beginning
*/
function onBeginRound();
/**
* Method called on Round ending
*/
function onEndRound();
/**
* Method called when the server status change
* @param int StatusCode
* @param string StatsName
*/
function onStatusChanged($statusCode, $statusName);
/**
* Method called when a player cross a checkPoint
* @param int $playerUid
* @param string $login
* @param int $timeOrScore
* @param int $curLap
* @param int $checkpointIndex
*/
function onPlayerCheckpoint($playerUid, $login, $timeOrScore, $curLap, $checkpointIndex);
/**
* Method called when a player finish a round
* @param int $playerUid
* @param string $login
* @param int $timeOrScore
*/
function onPlayerFinish($playerUid, $login, $timeOrScore);
/**
* Method called when there is an incoherence with a player data
* @param int $playerUid
* @param string $login
*/
function onPlayerIncoherence($playerUid, $login);
/**
* Method called when a bill is updated
* @param int $billId
* @param int $state
* @param string $stateName
* @param int $transactionId
*/
function onBillUpdated($billId, $state, $stateName, $transactionId);
/**
* Method called server receive data
* @param int $playerUid
* @param string $login
* @param base64 $data
*/
function onTunnelDataReceived($playerUid, $login, $data);
/**
* Method called when the map list is modified
* @param int $curMapIndex
* @param int $nextMapIndex
* @param bool $isListModified
*/
function onMapListModified($curMapIndex, $nextMapIndex, $isListModified);
/**
* Method called when player info changed
* @param SPlayerInfo $playerInfo
*/
function onPlayerInfoChanged($playerInfo);
/**
* Method called when the Flow Control is manual
* @param string $transition
*/
function onManualFlowControlTransition($transition);
/**
* Method called when a vote change of State
* @param string $stateName can be NewVote, VoteCancelled, votePassed, voteFailed
* @param string $login the login of the player who start the vote if empty the server start the vote
* @param string $cmdName the command used for the vote
* @param string $cmdParam the parameters of the vote
*/
function onVoteUpdated($stateName, $login, $cmdName, $cmdParam);
/**
* @param string
* @param string
*/
function onModeScriptCallback($param1, $param2);
/**
* Method called when the player in parameter has changed its allies
* @param string $login
*/
function onPlayerAlliesChanged($login);
Among easy events we might listen to is the controllers run events. We have basically 3 events. preLoop, postLoop and onTick.
First of all let understand how the communication with the dedicated works. Manialive is in a infinite loop, at the begining of the loop it will trigger the PreeLoop even. Once the event has been sent, manialive will get all the events the dedicated return. As it gets those events it will trigger new events.
Once all the events has been triggerd, the postLoop event is triggered. This post loop announces the end of the retrival of a pack of the dedicated server events. Between the postLoop and PreLoop Manialive will sleep for a bit in order not to overload the dedicated and to keep your cpu cool.
The definition of those 3 functions are :
function onInit();
function onRun();
function onPreLoop();
function onPostLoop();
function onTerminate();
To start listening to those events you will need to enable their listeners :
$this->enableApplicationEvents();
Since Shootmania we have the script game modes that has appeared in ManiaPlanet, expansion distinguish it self by handling those events as well as the legacy events. We are transforming those Script events to easy to use manialive events.
To use those events you will need to activate script events on your plugin :
$this->enableScriptEvents()
And here is the interface of all the events :
function LibXmlRpc_BeginMatch($number);
function LibXmlRpc_LoadingMap($number);
function LibXmlRpc_BeginMap($number);
function LibXmlRpc_BeginSubmatch($number);
function LibXmlRpc_BeginRound($number);
function LibXmlRpc_BeginTurn($number);
function LibXmlRpc_EndTurn($number);
function LibXmlRpc_EndRound($number);
function LibXmlRpc_EndSubmatch($number);
function LibXmlRpc_EndMap($number);
function LibXmlRpc_EndMatch($number);
function LibXmlRpc_BeginWarmUp();
function LibXmlRpc_EndWarmUp();
/* storm common */
function LibXmlRpc_Rankings($array);
function LibXmlRpc_Scores($MatchScoreClan1, $MatchScoreClan2, $MapScoreClan1, $MapScoreClan2);
function LibXmlRpc_PlayerRanking($rank, $login, $nickName, $teamId, $isSpectator, $isAway, $currentPoints, $zone);
function WarmUp_Status($status);
function LibAFK_IsAFK($login);
function LibAFK_Properties($idleTimelimit, $spawnTimeLimit, $checkInterval, $forceSpec);
/* tm common */
function LibXmlRpc_OnStartLine($login);
function LibXmlRpc_OnWayPoint($login, $blockId, $time, $cpIndex, $isEndBlock, $lapTime, $lapNb, $isLapEnd);
function LibXmlRpc_OnGiveUp($login);
function LibXmlRpc_OnRespawn($login);
function LibXmlRpc_OnStunt($login, $points, $combo, $totalScore, $factor, $stuntname, $angle, $isStraight, $isReversed, $isMasterJump);
The number of events that be thrown and catch is limitless, and some of our plugins will throw their own events. To get an event you will need to use the Manialive Dispatcher. Let first get the instance of the dispacther.
/** @var ManiaLive/libraries/event/Dispatcher $dispatcher **/
$dispatcher = ManiaLive/libraries/event/Dispatcher::getInstance();
I have added phpdoc in order to have autocompletation on the $dispatcher
variable. Now that we have our Dispatcher we can register for any event. Let register for onNewRecord.
$dispatcher->registerManiaLivePlugins\eXpansion\LocalRecords\Events\Event::getClass(), this);
Events are defined by the class name, this allows them to be unique and not have multiple plugins using same events name by mistake.
To understand what function this will call you will need to check the LocalRecords event listener interface ManiaLivePlugins\eXpansion\LocalRecords\Events\Listener
In your custom plugin you may want to trigger your own events, this is basically simple to do but requires some patience.
First of all we will create an Event Class whose job will be to call the methods of the listeners with the correct arguments.
Then we will create a listener interface to allow people that will use our system to know which methods with which parameters they need to have in their plugin.
Finally we will dispatch our event.
But first let us decide what our event will do; we will dispatch an event when the plugin is ready.
namespace ManiaLivePlugins\oliverde8\test\Event;
class Event extends ...{
}
Now we will create 1 event, for that we will create a constant :
const PLUGIN_READY = 0;
We will need to store the parameters of the event somewhere so let us add a parameter.
const PLUGIN_READY = 0;
We need variable to store parameters.
protected $params;
And we need a constructor to get the parameters and the event name
function __construct($onWhat) {
parent::__construct($onWhat);
$params = func_get_args();
array_shift($params);
$this->params = $params;
}
And now we create the dispatcher function :
function fireDo($listener) {
$p = $this->params;
switch ($this->onWhat) {
case self::ON_NEW_READY: $listener->exemple_isReady($p[0], $p[1]);
break;
}
}
We have 2 parameters send with our event.
We will create a simple interface.
namespace ManiaLivePlugins\oliverde8\test\Event;
interface Listener{
/**
* Method called when exemple plugin is ready
*
* @param string $name the name of the plugin
* @param int $randomValue a random value
*/
public function exemple_isReady($name, $randomValue);
}
in exp_onReady we will use the dispatcehr
Another great feature manialive offers and that expansion uses in force and extends is the cached data.
In any plugin we have acces to
$this->storage
This variable contains list of maps, server information, player list and player information and many more important information. If you need an information instead of fetching it throught the dedicated connection try and see if it isn't in the storage already. This will allow your plugin to be faster.
Unecessary connections to the dedicated will slow down all the system. If the information is missing check the next storage class :)
When we started to code eXpansion we have noticed very fast that some important elements weren't stored and that we needed to do some important work on some elements.
The expansion storage variable will allow you to access the current title, the map base some other information :
When all fails and you find no method to get an information or to do an action you will need to directly use the dedicated connection. you will find that the connection is available in all plugins $this->connection