diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml
index ba7af51e..22cd6899 100644
--- a/.idea/codeStyleSettings.xml
+++ b/.idea/codeStyleSettings.xml
@@ -54,6 +54,7 @@
+
@@ -72,5 +73,4 @@
-
-
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
index 0218f4cb..f7589596 100644
--- a/.idea/encodings.xml
+++ b/.idea/encodings.xml
@@ -3,5 +3,4 @@
-
-
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 8ee67934..42ad540f 100644
--- a/README.md
+++ b/README.md
@@ -34,11 +34,11 @@ http://www.maniacontrol.com
## REQUIREMENTS:
- MySQL Database
- PHP 5.4+
-- Needed extensions:
+- Needed extensions (on ManiaControl startup you will see if you have them activated):
- php_mysqli
- php_curl
- - php_xmlrpc (TM only)
-
+ - php_xmlrpc (TM only, recomended for SM)
+ - php_zlib
### How to report bugs or request features?:
- Write a mail to bugs(at)maniacontrol(dot)com
diff --git a/changelog.txt b/changelog.txt
new file mode 100644
index 00000000..8e134a80
--- /dev/null
+++ b/changelog.txt
@@ -0,0 +1,60 @@
+###v0.163###
+#Additions
+- completely reworked the filereader (new version, way more flexible), old methods are still working but deprecated
+- added pause command and vote command for other gamemodes than elite (especially Chase/Combo)
+- added Scriptcallbacks SCORESREADY / SCORES
+- added SSL support as well as http Redirections of the FileReader
+
+# Bug fixes
+- Banning of not connected Players now possible
+
+###v0.162###
+#Additions
+- added typhinting ladderStat in Player object
+- added optional AsynchronousFileReader Parameter for loadFile and postFile to set additional Headers
+- added ServerLogin header for Mania-Exchange downloads
+
+###v0.161###
+#Additions
+- added admin chatcommand //uptime which displays the time since when the server is running
+- updated playerhitstructure with new properties
+
+#Bug Fixes
+- fixed some z positions to be in front of overlays (especially in Trackmania)
+- fixed limit problem on maniaexchange list
+
+###v0.16###
+#Additions
+- added changelog
+- added CommunicationManager which acts like a communication interface you can connect to and interact with ManiaControl (also thanks to TGYoshi for some help)
+ - You can call ManiaControl from a Website or from ManiaControl itself
+- added "//removerights login" command
+- added new EchoManager which handles Interactions between different Controllers
+ - It is possible to send an Echo command via the Method sendEcho, as message Parameter strings, objects or arrays can get used
+ - An EchoListener can be added, callable by closure or implicit use (like on callbacks)
+ - The Listener always returns either an Stringer or an Object back as Param (arrays get converted into objects)
+ - On sending an Echo Message from another controller you need to provide an string to the dedicated method or an json_encoded array or object
+ - 4 Echos are Implemented by ManiaControl (ManiaControl.Restart, ManiaControl.AuthenticationManager.GrandLevel, ManiaControl.AuthenticationManager.RevokeLevel, ManiaControl.PlayerManager.WarnPlayer)
+- added Method getServerLoginByIndex to Server object
+- added to PlayerManager's Method PlayerManager the Parameter "withoutBots" (default on true)
+- added Method getSpectators() in PlayerManager
+- added Method restartMap(), skipToMapByMxId(), skipToMapByUid() into MapActions
+- added some missing PHP Docs
+- added some depency libraries as they are used by the Socket Handler
+- added additional Callback which gets triggered on ManiaControl Restart
+- added class name to the BillData object
+- updated some depency libraries
+
+#Bug Fixes
+- fixed TriggerDebugNotice Method
+- Exception fix on kicking Players
+- updated FaultException List
+
+###v0.157###
+- labelline improvements
+ - new usage examples:
+ $positions = array($posX + 5, $posX + 18, $posX + 70);
+ $texts = array($index, $admin->nickname, $admin->login);
+ $this->maniaControl->getManialinkManager()->labelLine($playerFrame, array($positions, $texts));
+- improvements on Billmanager, added receiver to the BillData
+- increased timeout time (fixes crashes on speedball)
diff --git a/core/Admin/ActionsMenu.php b/core/Admin/ActionsMenu.php
index 78aa78b9..c5afda03 100644
--- a/core/Admin/ActionsMenu.php
+++ b/core/Admin/ActionsMenu.php
@@ -290,7 +290,7 @@ class ActionsMenu implements CallbackListener, ManialinkPageAnswerListener {
unset($this->playerMenuItems[$order]);
}
} else {
- if (isset($this->playerMenuItems[$order])) {
+ if (isset($this->adminMenuItems[$order])) {
unset($this->adminMenuItems[$order]);
}
}
diff --git a/core/Admin/AdminLists.php b/core/Admin/AdminLists.php
index ff144ad2..3266a384 100644
--- a/core/Admin/AdminLists.php
+++ b/core/Admin/AdminLists.php
@@ -133,9 +133,9 @@ class AdminLists implements ManialinkPageAnswerListener, CallbackListener {
$lineQuad->setZ(0.001);
}
- $array = array($index => $posX + 5, $admin->nickname => $posX + 18, $admin->login => $posX + 70);
- $this->maniaControl->getManialinkManager()->labelLine($playerFrame, $array);
-
+ $positions = array($posX + 5, $posX + 18, $posX + 70);
+ $texts = array($index, $admin->nickname, $admin->login);
+ $this->maniaControl->getManialinkManager()->labelLine($playerFrame, array($positions, $texts));
// Level Quad
$rightQuad = new Quad_BgRaceScore2();
diff --git a/core/Admin/AuthCommands.php b/core/Admin/AuthCommands.php
index 7b40b40c..15590e04 100644
--- a/core/Admin/AuthCommands.php
+++ b/core/Admin/AuthCommands.php
@@ -32,6 +32,8 @@ class AuthCommands implements CommandListener {
$this->maniaControl->getCommandManager()->registerCommandListener('addsuperadmin', $this, 'command_AddSuperAdmin', true, 'Add Player to the AdminList as SuperAdmin.');
$this->maniaControl->getCommandManager()->registerCommandListener('addadmin', $this, 'command_AddAdmin', true, 'Add Player to the AdminList as Admin.');
$this->maniaControl->getCommandManager()->registerCommandListener('addmod', $this, 'command_AddModerator', true, 'Add Player to the AdminList as Moderator.');
+
+ $this->maniaControl->getCommandManager()->registerCommandListener('removerights', $this, 'command_RemoveRights', true, 'Remove Player from the AdminList.');
}
/**
@@ -159,4 +161,53 @@ class AuthCommands implements CommandListener {
$message = "Usage Example: '//addmod login'";
return $this->maniaControl->getChat()->sendUsageInfo($message, $player);
}
+
+
+ /**
+ * Handle //removerights command
+ *
+ * @param array $chatCallback
+ * @param Player $player
+ */
+ public function command_RemoveRights(array $chatCallback, Player $player) {
+ if (!AuthenticationManager::checkRight($player, AuthenticationManager::AUTH_LEVEL_ADMIN)) {
+ $this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
+ return;
+ }
+ $text = $chatCallback[1][2];
+ $commandParts = explode(' ', $text);
+ if (!array_key_exists(1, $commandParts)) {
+ $this->sendRemoveRightsUsageInfo($player);
+ return;
+ }
+ $target = $this->maniaControl->getPlayerManager()->getPlayer($commandParts[1]);
+ if (!$target) {
+ $this->maniaControl->getChat()->sendError("Player '{$commandParts[1]}' not found!", $player);
+ return;
+ }
+
+ if ($target->authLevel == AuthenticationManager::AUTH_LEVEL_MASTERADMIN) {
+ $this->maniaControl->getChat()->sendError("You can't remove an MasterAdmin from the Adminlists", $player);
+ return;
+ }
+
+ $success = $this->maniaControl->getAuthenticationManager()->grantAuthLevel($target, AuthenticationManager::AUTH_LEVEL_PLAYER);
+ if (!$success) {
+ $this->maniaControl->getChat()->sendError('Error occurred.', $player);
+ return;
+ }
+ $message = $player->getEscapedNickname() . ' removed ' . $target->getEscapedNickname() . ' from the Adminlists!';
+ $this->maniaControl->getChat()->sendSuccess($message);
+ }
+
+ /**
+ * Send usage example for //removerights command
+ *
+ * @param Player $player
+ * @return bool
+ */
+ private function sendRemoveRightsUsageInfo(Player $player) {
+ $message = "Usage Example: '//addadmin login'";
+ return $this->maniaControl->getChat()->sendUsageInfo($message, $player);
+ }
}
diff --git a/core/Admin/AuthenticationManager.php b/core/Admin/AuthenticationManager.php
index 8aa8961b..8d4b5ab6 100644
--- a/core/Admin/AuthenticationManager.php
+++ b/core/Admin/AuthenticationManager.php
@@ -4,6 +4,10 @@ namespace ManiaControl\Admin;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
+use ManiaControl\Callbacks\EchoListener;
+use ManiaControl\Communication\CommunicationAnswer;
+use ManiaControl\Communication\CommunicationListener;
+use ManiaControl\Communication\CommunicationMethods;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
@@ -17,7 +21,7 @@ use ManiaControl\Settings\Setting;
* @copyright 2014-2015 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
-class AuthenticationManager implements CallbackListener {
+class AuthenticationManager implements CallbackListener, EchoListener, CommunicationListener {
/*
* Constants
*/
@@ -33,6 +37,8 @@ class AuthenticationManager implements CallbackListener {
const AUTH_NAME_MASTERADMIN = 'MasterAdmin';
const CB_AUTH_LEVEL_CHANGED = 'AuthenticationManager.AuthLevelChanged';
+ const ECHO_GRANT_LEVEL = 'ManiaControl.AuthenticationManager.GrandLevel';
+ const ECHO_REVOKE_LEVEL = 'ManiaControl.AuthenticationManager.RevokeLevel';
/*
* Private properties
*/
@@ -52,6 +58,56 @@ class AuthenticationManager implements CallbackListener {
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONINIT, $this, 'handleOnInit');
+
+ // Echo Grant Level Command (Usage: sendEcho json_encode("player" => "loginName", "level" => "AUTH_LEVEL_NUMBER")
+ $this->maniaControl->getEchoManager()->registerEchoListener(self::ECHO_GRANT_LEVEL, $this, function ($params) {
+ if (property_exists($params, 'level') && property_exists($params, 'player')) {
+ $player = $this->maniaControl->getPlayerManager()->getPlayer($params->player);
+ if ($player) {
+ $this->grantAuthLevel($player, $params->level);
+ }
+ }
+ });
+
+ // Echo Revoke Level Command (Usage: sendEcho json_encode("player" => "loginName")
+ $this->maniaControl->getEchoManager()->registerEchoListener(self::ECHO_REVOKE_LEVEL, $this, function ($params) {
+ if (property_exists($params, 'player')) {
+ $player = $this->maniaControl->getPlayerManager()->getPlayer($params->player);
+ if ($player) {
+ $this->maniaControl->getAuthenticationManager()->grantAuthLevel($player, self::AUTH_LEVEL_PLAYER);
+ }
+ }
+ });
+
+
+ //Communication Listenings
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::GRANT_AUTH_LEVEL, $this, function ($data) {
+ if (!is_object($data) || !property_exists($data, 'level') || !property_exists($data, 'login')) {
+ return new CommunicationAnswer("No valid level or player login provided!", true);
+ }
+
+ $player = $this->maniaControl->getPlayerManager()->getPlayer($data->login);
+ if ($player) {
+ $success = $this->grantAuthLevel($player, $data->level);
+ return new CommunicationAnswer(array("success" => $success));
+ } else {
+ return new CommunicationAnswer("Player not found!", true);
+ }
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::REVOKE_AUTH_LEVEL, $this, function ($data) {
+ if (!is_object($data) || !property_exists($data, 'login')) {
+ return new CommunicationAnswer("No valid player login provided!", true);
+ }
+
+ $player = $this->maniaControl->getPlayerManager()->getPlayer($data->login);
+ if ($player) {
+ $success = $this->maniaControl->getAuthenticationManager()->grantAuthLevel($player, self::AUTH_LEVEL_PLAYER);
+ return new CommunicationAnswer(array("success" => $success));
+ } else {
+ return new CommunicationAnswer("Player not found!", true);
+ }
+ });
}
/**
@@ -83,12 +139,12 @@ class AuthenticationManager implements CallbackListener {
*/
public static function getAuthLevelInt($authLevelParam) {
if (is_object($authLevelParam) && property_exists($authLevelParam, 'authLevel')) {
- return (int)$authLevelParam->authLevel;
+ return (int) $authLevelParam->authLevel;
}
if (is_string($authLevelParam)) {
return self::getAuthLevel($authLevelParam);
}
- return (int)$authLevelParam;
+ return (int) $authLevelParam;
}
/**
@@ -98,7 +154,7 @@ class AuthenticationManager implements CallbackListener {
* @return int
*/
public static function getAuthLevel($authLevelName) {
- $authLevelName = (string)$authLevelName;
+ $authLevelName = (string) $authLevelName;
switch ($authLevelName) {
case self::AUTH_NAME_MASTERADMIN:
return self::AUTH_LEVEL_MASTERADMIN;
@@ -189,7 +245,7 @@ class AuthenticationManager implements CallbackListener {
}
$success = true;
foreach ($loginElements as $loginElement) {
- $login = (string)$loginElement;
+ $login = (string) $loginElement;
$adminStatement->bind_param('si', $login, $masterAdminLevel);
$adminStatement->execute();
if ($adminStatement->error) {
@@ -271,7 +327,7 @@ class AuthenticationManager implements CallbackListener {
if (!$player || !is_numeric($authLevel)) {
return false;
}
- $authLevel = (int)$authLevel;
+ $authLevel = (int) $authLevel;
if ($authLevel >= self::AUTH_LEVEL_MASTERADMIN) {
return false;
}
diff --git a/core/Bills/BillData.php b/core/Bills/BillData.php
index 2438335f..25b0c94a 100644
--- a/core/Bills/BillData.php
+++ b/core/Bills/BillData.php
@@ -15,28 +15,34 @@ class BillData {
/*
* Public properties
*/
- public $function = null;
- public $pay = false;
- public $player = null;
+ public $function = null;
+ public $pay = false;
+ public $player = null;
public $receiverLogin = null;
- public $amount = 0;
- public $creationTime = -1;
+ public $amount = 0;
+ public $creationTime = -1;
+ public $message = "";
+ public $class = "";
/**
* Construct new Bill Data Model
*
+ * @param string $class
* @param callable $function
* @param Player|string $player
* @param int $amount
* @param bool $pay
* @param string $receiverLogin
+ * @param string $message
*/
- public function __construct(callable $function, $player, $amount, $pay = false, $receiverLogin = null) {
+ public function __construct($class, callable $function, $player, $amount, $pay = false, $receiverLogin = null, $message = '') {
+ $this->class = $class;
$this->function = $function;
$this->player = $player;
$this->amount = $amount;
$this->pay = $pay;
$this->receiverLogin = $receiverLogin;
+ $this->message = $message;
$this->creationTime = time();
}
}
diff --git a/core/Bills/BillManager.php b/core/Bills/BillManager.php
index 2169136f..c06b0704 100644
--- a/core/Bills/BillManager.php
+++ b/core/Bills/BillManager.php
@@ -6,6 +6,7 @@ use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
+use Maniaplanet\DedicatedServer\InvalidArgumentException;
use Maniaplanet\DedicatedServer\Structures\Bill;
/**
@@ -24,7 +25,7 @@ class BillManager implements CallbackListener {
const PAYED_FROM_SERVER = 3;
const PLAYER_REFUSED_DONATION = 4;
const ERROR_WHILE_TRANSACTION = 5;
- const CB_BILL_PAID = 'Billmanager.BillPaid';
+ const CB_BILL_PAID = 'Billmanager.BillPaid';
/*
* Private properties
@@ -57,11 +58,21 @@ class BillManager implements CallbackListener {
* @return bool
*/
public function sendBill(callable $function, Player $player, $amount, $message, $receiver = '') {
- $billId = $this->maniaControl->getClient()->sendBill($player->login, $amount, $message, $receiver);
- $this->openBills[$billId] = new BillData($function, $player, $amount);
+ //Get the Caller Class
+ $backTrace = debug_backtrace();
+ $class = $backTrace[1]['class'];
+
+ try {
+ $billId = $this->maniaControl->getClient()->sendBill($player->login, intval($amount), $message, $receiver);
+ } catch (InvalidArgumentException $e) {
+ //TODO better error handling, maybe call the user func with ERROR_WHILE_TRANSACTION
+ return false;
+ }
+ $this->openBills[$billId] = new BillData($class, $function, $player, $amount, false, $receiver, $message);
return true;
}
+
/**
* Send planets from the server to a player
*
@@ -72,8 +83,17 @@ class BillManager implements CallbackListener {
* @return bool
*/
public function sendPlanets(callable $function, $receiverLogin, $amount, $message) {
- $billId = $this->maniaControl->getClient()->pay($receiverLogin, $amount, $message);
- $this->openBills[$billId] = new BillData($function, $receiverLogin, $amount, true);
+ //Get the Caller Class
+ $backTrace = debug_backtrace();
+ $class = $backTrace[1]['class'];
+
+ try {
+ $billId = $this->maniaControl->getClient()->pay($receiverLogin, intval($amount), $message);
+ } catch (InvalidArgumentException $e) {
+ return false;
+ }
+
+ $this->openBills[$billId] = new BillData($class, $function, $receiverLogin, $amount, true, $receiverLogin, $message);
return true;
}
diff --git a/core/Callbacks/CallbackManager.php b/core/Callbacks/CallbackManager.php
index cd0c6f63..e57aa76e 100644
--- a/core/Callbacks/CallbackManager.php
+++ b/core/Callbacks/CallbackManager.php
@@ -153,8 +153,6 @@ class CallbackManager {
return $this->removeCallbackListener($this->callbackListenings, $listener);
}
- //TODO better name (used only in customvotesPlugin)
-
/**
* Remove the Callback Listener from the given Listeners Array
*
@@ -212,6 +210,9 @@ class CallbackManager {
// Manage Timings
$this->maniaControl->getTimerManager()->manageTimings();
+ // Manage Socket Tickets
+ $this->maniaControl->getCommunicationManager()->tick();
+
// Server Callbacks
if (!$this->maniaControl->getClient()) {
return;
diff --git a/core/Callbacks/Callbacks.php b/core/Callbacks/Callbacks.php
index e450879a..a458083c 100644
--- a/core/Callbacks/Callbacks.php
+++ b/core/Callbacks/Callbacks.php
@@ -18,6 +18,7 @@ interface Callbacks {
const ONINIT = 'Callbacks.OnInit';
const AFTERINIT = 'Callbacks.AfterInit';
const ONSHUTDOWN = 'Callbacks.OnShutdown';
+ const ONRESTART = 'Callbacks.OnRestart';
/** Script Callback: CallbackName, CallbackData */
const SCRIPTCALLBACK = 'Callbacks.ScriptCallback';
@@ -49,6 +50,8 @@ interface Callbacks {
const ENDTURNSTOP = 'Callbacks.EndTurnStop';
/** EndRound Callback: RoundNumber */
const ENDROUND = 'Callbacks.EndRound';
+ /** EndRound Callback: RoundNumber */
+ const ENDROUNDSTOP = 'Callbacks.EndRoundStop';
/** EndSubmatch Callback: SubmatchNumber */
const ENDSUBMATCH = 'Callbacks.EndSubmatch';
/** EndMap Callback: Map */
@@ -67,6 +70,12 @@ interface Callbacks {
/** EndWarmup Callback */
const ENDWARMUP = 'Callbacks.EndWarmUp';
+ /** Scores Callback (returned after LibXmlRpc_PlayerRanking): Scores */
+ const SCORESREADY = 'Callbacks.ScoresReady';
+
+ /** Scores Callback (returned after LibXmlRpc_PlayerRanking in SM, or LibXmlRpc_TeamsScores in Trackmania): Scores */
+ const SCORES = 'Callbacks.Scores';
+
/** PlayerRanking Callback, returned after LibXmlRpc_PlayerRanking
* try to avoid to use this, just use the Get function of the RankingsManager instead
* param1 Player $player
@@ -80,8 +89,6 @@ interface Callbacks {
*/
/** RankingsUpdated Callback: SortedRankings */
const RANKINGSUPDATED = 'Callbacks.RankingsUpdated';
- /** Scores Callback (returned after LibXmlRpc_PlayerRanking): Scores */
- const SCORES = 'Callbacks.Scores';
/** Returns the AFKStatus of an Player, returned after param1 Scores */ //returned after TODO
const AFKSTATUS = 'Callbacks.AfkStatus';
diff --git a/core/Callbacks/EchoListener.php b/core/Callbacks/EchoListener.php
new file mode 100644
index 00000000..a0f87e2b
--- /dev/null
+++ b/core/Callbacks/EchoListener.php
@@ -0,0 +1,12 @@
+
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
+interface EchoListener {
+}
diff --git a/core/Callbacks/EchoManager.php b/core/Callbacks/EchoManager.php
new file mode 100644
index 00000000..8b5deb71
--- /dev/null
+++ b/core/Callbacks/EchoManager.php
@@ -0,0 +1,150 @@
+
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
+class EchoManager implements CallbackListener, EchoListener {
+ /*
+ * Private properties
+ */
+ /** @var ManiaControl $maniaControl */
+ private $maniaControl = null;
+ /** @var Listening[] $echoListenings */
+ private $echoListenings = array();
+
+ /**
+ * Create a new Echo Handler Instance
+ *
+ * @param ManiaControl $maniaControl
+ */
+ public function __construct(ManiaControl $maniaControl) {
+ $this->maniaControl = $maniaControl;
+
+ $this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_ECHO, $this, 'handleEchos');
+ }
+
+ /**
+ * Sends an Echo Message
+ *
+ * @param string $name
+ * @param mixed $data (can be array, object or string)
+ * @return bool
+ * @throws \Maniaplanet\DedicatedServer\InvalidArgumentException
+ */
+ public function sendEcho($name, $data) {
+ if (is_string($data)) {
+ $success = $this->maniaControl->getClient()->dedicatedEcho($data, $name);
+ } else {
+ $success = $this->maniaControl->getClient()->dedicatedEcho(json_encode($data), $name);
+ }
+
+ return $success;
+ }
+
+ /**
+ * Register a new Echo Listener
+ *
+ * @param string $callbackName
+ * @param EchoListener $listener
+ * @param string $method
+ * @return bool
+ */
+ public function registerEchoListener($echoName, EchoListener $listener, $method) {
+ if (!Listening::checkValidCallback($listener, $method)) {
+ $listenerClass = get_class($listener);
+ trigger_error("Given Listener '{$listenerClass}' can't handle Callback '{$echoName}': No callable Method '{$method}'!");
+ return false;
+ }
+
+ if (!array_key_exists($echoName, $this->echoListenings)) {
+ $this->echoListenings[$echoName] = array();
+ }
+
+ $listening = new Listening($listener, $method);
+ array_push($this->echoListenings[$echoName], $listening);
+ return true;
+ }
+
+ /**
+ * Unregister a Echo Listener
+ *
+ * @param EchoListener $listener
+ * @return bool
+ */
+ public function unregisterEchoListener(EchoListener $listener) {
+ return $this->removeEchoListener($this->echoListenings, $listener);
+ }
+
+ /**
+ * Remove the Echo Listener from the given Listeners Array
+ *
+ * @param Listening[] $listeningsArray
+ * @param EchoListener $listener
+ * @return bool
+ */
+ private function removeEchoListener(array &$listeningsArray, EchoListener $listener) {
+ $removed = false;
+ foreach ($listeningsArray as &$listenings) {
+ foreach ($listenings as $key => &$listening) {
+ if ($listening->listener === $listener) {
+ unset($listenings[$key]);
+ $removed = true;
+ }
+ }
+ }
+ return $removed;
+ }
+
+
+ /**
+ * Trigger a specific Callback
+ *
+ * @param mixed $callback
+ */
+ public function triggerEchoCallback($callbackName) {
+ if (!array_key_exists($callbackName, $this->echoListenings)) {
+ return;
+ }
+
+
+ $params = func_get_args();
+ $params = array_slice($params, 1, null, true);
+
+ //var_dump($params);
+ foreach ($this->echoListenings[$callbackName] as $listening) {
+ /** @var Listening $listening */
+ $listening->triggerCallbackWithParams($params);
+ }
+ }
+
+ /**
+ * Handle the given Callback
+ *
+ * @param array $callback
+ */
+ public function handleEchos($param) {
+ $name = $param[1][0];
+ if (is_object($decode = json_decode($param[1][1]))) {
+ $message = $decode;
+ } else {
+ $message = $param[1][1];
+ }
+
+ switch ($name) {
+ case 'ManiaControl.Restart':
+ $this->maniaControl->restart($message);
+ break;
+ default:
+ $this->triggerEchoCallback($name, $message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/Callbacks/LibXmlRpcCallbacks.php b/core/Callbacks/LibXmlRpcCallbacks.php
index 8b306580..2ff97ad1 100644
--- a/core/Callbacks/LibXmlRpcCallbacks.php
+++ b/core/Callbacks/LibXmlRpcCallbacks.php
@@ -82,6 +82,9 @@ class LibXmlRpcCallbacks implements CallbackListener {
case 'LibXmlRpc_EndRound':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ENDROUND, $data[0]);
break;
+ case 'LibXmlRpc_EndRoundStop':
+ $this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ENDROUNDSTOP, $data[0]);
+ break;
case 'LibXmlRpc_EndSubmatch':
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ENDSUBMATCH, $data[0]);
break;
@@ -146,6 +149,12 @@ class LibXmlRpcCallbacks implements CallbackListener {
$player = $this->maniaControl->getPlayerManager()->getPlayer($data[0]);
$this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::ONPLAYERREQUESTRESPAWN, $player);
break;
+ case 'LibXmlRpc_Scores':
+ $this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::SCORES, $data);
+ break;
+ case 'LibXmlRpc_ScoresReady':
+ $this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::SCORESREADY, $data);
+ break;
}
}
diff --git a/core/Callbacks/Listening.php b/core/Callbacks/Listening.php
index 26722661..310981ec 100644
--- a/core/Callbacks/Listening.php
+++ b/core/Callbacks/Listening.php
@@ -57,9 +57,10 @@ class Listening {
* Trigger the Listener's Method with the given Array of Params
*
* @param array $params
+ * @return mixed
*/
public function triggerCallbackWithParams(array $params) {
- call_user_func_array($this->getUserFunction(), $params);
+ return call_user_func_array($this->getUserFunction(), $params);
}
/**
diff --git a/core/Callbacks/ShootManiaCallbacks.php b/core/Callbacks/ShootManiaCallbacks.php
index 7f01e3e3..4aa5cf82 100644
--- a/core/Callbacks/ShootManiaCallbacks.php
+++ b/core/Callbacks/ShootManiaCallbacks.php
@@ -52,9 +52,6 @@ class ShootManiaCallbacks implements CallbackListener {
case 'LibXmlRpc_Rankings':
$this->maniaControl->getServer()->getRankingManager()->updateRankings($data[0]);
break;
- case 'LibXmlRpc_Scores':
- $this->maniaControl->getCallbackManager()->triggerCallback(Callbacks::SCORES, $data);
- break;
case 'LibAFK_IsAFK':
$this->triggerAfkStatus($data[0]);
break;
diff --git a/core/Callbacks/Structures/ArmorEmptyStructure.php b/core/Callbacks/Structures/ArmorEmptyStructure.php
index 09108816..325b4aa8 100644
--- a/core/Callbacks/Structures/ArmorEmptyStructure.php
+++ b/core/Callbacks/Structures/ArmorEmptyStructure.php
@@ -4,7 +4,13 @@ namespace ManiaControl\Callbacks\Structures;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
-
+/**
+ * Structure Class for the ArmorEmpty Callback
+ *
+ * @author ManiaControl Team
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
class ArmorEmptyStructure {
/*
* Private properties
diff --git a/core/Callbacks/Structures/CaptureStructure.php b/core/Callbacks/Structures/CaptureStructure.php
index 12a853c0..88bb0d9f 100644
--- a/core/Callbacks/Structures/CaptureStructure.php
+++ b/core/Callbacks/Structures/CaptureStructure.php
@@ -5,6 +5,13 @@ namespace ManiaControl\Callbacks\Structures;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
+/**
+ * Structure Class for the Capture Callback
+ *
+ * @author ManiaControl Team
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
class CaptureStructure {
/*
* Private properties
@@ -42,7 +49,7 @@ class CaptureStructure {
$playerArray = array();
foreach ($this->playerArray as $login) {
$player = $this->maniaControl->getPlayerManager()->getPlayer($login);
- if($player){
+ if ($player) {
$playerArray[$login] = $player;
}
}
diff --git a/core/Callbacks/Structures/EliteBeginTurnStructure.php b/core/Callbacks/Structures/EliteBeginTurnStructure.php
index ffe5f787..f4b9ba14 100644
--- a/core/Callbacks/Structures/EliteBeginTurnStructure.php
+++ b/core/Callbacks/Structures/EliteBeginTurnStructure.php
@@ -5,6 +5,13 @@ namespace ManiaControl\Callbacks\Structures;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
+/**
+ * Structure Class for the EliteBeginTurn Callback
+ *
+ * @author ManiaControl Team
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
class EliteBeginTurnStructure {
/*
* Private properties
@@ -22,8 +29,8 @@ class EliteBeginTurnStructure {
* @param array $data
*/
public function __construct(ManiaControl $maniaControl, array $data) {
- $this->maniaControl = $maniaControl;
- $this->attackerLogin = $data[0];
+ $this->maniaControl = $maniaControl;
+ $this->attackerLogin = $data[0];
$this->defenderLogins = $data[1];
}
diff --git a/core/Callbacks/Structures/NearMissStructure.php b/core/Callbacks/Structures/NearMissStructure.php
index fe9cad94..bd9e8703 100644
--- a/core/Callbacks/Structures/NearMissStructure.php
+++ b/core/Callbacks/Structures/NearMissStructure.php
@@ -5,6 +5,13 @@ namespace ManiaControl\Callbacks\Structures;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
+/**
+ * Structure Class for the NearMiss Callback
+ *
+ * @author ManiaControl Team
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
class NearMissStructure {
/*
* Private properties
diff --git a/core/Callbacks/Structures/PlayerHitStructure.php b/core/Callbacks/Structures/PlayerHitStructure.php
index ae739a5b..ea278112 100644
--- a/core/Callbacks/Structures/PlayerHitStructure.php
+++ b/core/Callbacks/Structures/PlayerHitStructure.php
@@ -1,16 +1,17 @@
- * @copyright 2014-2015 ManiaControl Team
- * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
- */
+
namespace ManiaControl\Callbacks\Structures;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
+/**
+ * Structure Class for the Player Hit Callback
+ *
+ * @author ManiaControl Team
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
class PlayerHitStructure {
/*
* Private properties
@@ -20,7 +21,11 @@ class PlayerHitStructure {
private $damage;
private $shooterPoints;
private $weapon;
- private $hitDistance = 0;
+ private $hitDistance;
+ private $shooterPosition = 0;
+ private $victimPosition = 0;
+ private $shooterAimDirection = 0;
+ private $victimAimDirection = 0;
/** @var ManiaControl $maniaControl */
private $maniaControl;
@@ -38,10 +43,23 @@ class PlayerHitStructure {
$this->damage = $data[2];
$this->weapon = $data[3];
$this->shooterPoints = $data[4];
-
- //TODO remove key check in some months (hitDistance got implemented 2014-10-16)
- if (array_key_exists(5, $data)) {
- $this->hitDistance = $data[5];
+ $this->hitDistance = $data[5];
+
+ //TODO remove key check in some months (got implemented 2015-05-03)
+ if (array_key_exists(6, $data)) {
+ $this->shooterPosition = $data[6];
+ }
+
+ if (array_key_exists(7, $data)) {
+ $this->victimPosition = $data[7];
+ }
+
+ if (array_key_exists(8, $data)) {
+ $this->shooterAimDirection = $data[8];
+ }
+
+ if (array_key_exists(9, $data)) {
+ $this->victimAimDirection = $data[9];
}
}
diff --git a/core/Chat.php b/core/Chat.php
index f102617d..434ad7eb 100644
--- a/core/Chat.php
+++ b/core/Chat.php
@@ -3,6 +3,11 @@
namespace ManiaControl;
use ManiaControl\Admin\AuthenticationManager;
+use ManiaControl\Callbacks\CallbackListener;
+use ManiaControl\Callbacks\CallbackManager;
+use ManiaControl\Communication\CommunicationAnswer;
+use ManiaControl\Communication\CommunicationListener;
+use ManiaControl\Communication\CommunicationMethods;
use ManiaControl\Players\Player;
use Maniaplanet\DedicatedServer\Xmlrpc\UnknownPlayerException;
@@ -13,7 +18,7 @@ use Maniaplanet\DedicatedServer\Xmlrpc\UnknownPlayerException;
* @copyright 2014-2015 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
-class Chat {
+class Chat implements CallbackListener, CommunicationListener {
/*
* Constants
*/
@@ -22,12 +27,13 @@ class Chat {
const SETTING_FORMAT_SUCCESS = 'Success Format';
const SETTING_FORMAT_ERROR = 'Error Format';
const SETTING_FORMAT_USAGEINFO = 'UsageInfo Format';
-
+ const CHAT_BUFFER_SIZE = 200;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
+ private $chatBuffer = array();
/**
* Construct chat utility
@@ -43,6 +49,15 @@ class Chat {
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_FORMAT_SUCCESS, '$0f0');
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_FORMAT_ERROR, '$f30');
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_FORMAT_USAGEINFO, '$f80');
+
+ //Callbacks
+ $this->maniaControl->getCallbackManager()->registerCallbackListener(CallbackManager::CB_MP_PLAYERCHAT, $this, 'onPlayerChat');
+
+ //Socket Listenings
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::SEND_CHAT_MESSAGE, $this, "communcationSendChat");
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::GET_SERVER_CHAT, $this, function ($data) {
+ return new CommunicationAnswer($this->chatBuffer);
+ });
}
/**
@@ -226,4 +241,92 @@ class Chat {
$format = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_FORMAT_USAGEINFO);
return $this->sendChat($format . $message, $login, $prefix);
}
+
+
+ /**
+ * Handles SendChat Communication Request
+ *
+ * @param $data
+ * @return array
+ */
+ public function communcationSendChat($data) {
+ if (!is_object($data) || !property_exists($data, "message")) {
+ return new CommunicationAnswer("You have to provide a valid message", true);
+ }
+
+ $prefix = true;
+ if (property_exists($data, "prefix")) {
+ $prefix = $data->prefix;
+ }
+
+ $login = null;
+ if (property_exists($data, "login")) {
+ $login = $data->login;
+ }
+
+ $adminLevel = 0;
+ if (property_exists($data, "adminLevel")) {
+ $adminLevel = $data->adminLevel;
+ }
+
+ $type = "default";
+ if (property_exists($data, "type")) {
+ $type = $data->type;
+ }
+
+ switch ($type) {
+ case "information":
+ if ($adminLevel) {
+ $this->sendInformationToAdmins($data->message, $adminLevel, $prefix);
+ } else {
+ $this->sendInformation($data->message, $login, $prefix);
+ }
+ break;
+ case "success":
+ if ($adminLevel) {
+ $this->sendInformationToAdmins($data->message, $adminLevel, $prefix);
+ } else {
+ $this->sendSuccess($data->message, $login, $prefix);
+ }
+ break;
+ case "error":
+ if ($adminLevel) {
+ $this->sendErrorToAdmins($data->message, $adminLevel, $prefix);
+ } else {
+ $this->sendError($data->message, $login, $prefix);
+ }
+ break;
+ case "usage":
+ $this->sendUsageInfo($data->message, $login, $prefix);
+ break;
+ default:
+ if ($adminLevel) {
+ $this->sendMessageToAdmins($data->message, $adminLevel, $prefix);
+ } else {
+ $this->sendChat($data->message, $login, $prefix);
+ }
+ }
+
+ return new CommunicationAnswer();
+ }
+
+
+ /**
+ * Stores the ChatMessage in the Buffer
+ *
+ * @param $data
+ */
+ public function onPlayerChat($data) {
+ $login = $data[1][1];
+ $player = $this->maniaControl->getPlayerManager()->getPlayer($login);
+
+ $nickname = "";
+ if ($player) {
+ $nickname = $player->nickname;
+ }
+ array_push($this->chatBuffer, array("user" => $login, "nickname" => $nickname, "message" => $data[1][2]));
+ if (count($this->chatBuffer) > self::CHAT_BUFFER_SIZE) {
+ array_shift($this->chatBuffer);
+ }
+ }
}
diff --git a/core/Commands/HelpManager.php b/core/Commands/HelpManager.php
index 4dac4125..6ccaa7b4 100644
--- a/core/Commands/HelpManager.php
+++ b/core/Commands/HelpManager.php
@@ -19,15 +19,14 @@ use ManiaControl\Players\Player;
* @copyright 2014-2015 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
-
class HelpManager implements CommandListener, CallbackListener {
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
- private $maniaControl = null;
+ private $maniaControl = null;
private $playerCommands = array();
- private $adminCommands = array();
+ private $adminCommands = array();
/**
* Construct a new Commands Manager
@@ -58,14 +57,14 @@ class HelpManager implements CommandListener, CallbackListener {
* @param Player $player
*/
public function command_adminHelp(array $chatCallback, Player $player) {
- // Parse list from array
- $message = $this->parseHelpList($this->adminCommands);
-
- // Show message when it's not empty
- if($message != NULL){
- $message = 'Supported Admin Commands: ' . $message;
- $this->maniaControl->getChat()->sendChat($message, $player);
- }
+ // Parse list from array
+ $message = $this->parseHelpList($this->adminCommands);
+
+ // Show message when it's not empty
+ if ($message != null) {
+ $message = 'Supported Admin Commands: ' . $message;
+ $this->maniaControl->getChat()->sendChat($message, $player);
+ }
}
/**
@@ -75,14 +74,14 @@ class HelpManager implements CommandListener, CallbackListener {
* @param Player $player
*/
public function command_playerHelp(array $chatCallback, Player $player) {
- // Parse list from array
+ // Parse list from array
$message = $this->parseHelpList($this->playerCommands);
-
- // Show message when it's not empty
- if($message != NULL){
- $message = 'Supported Player Commands: ' . $message;
- $this->maniaControl->getChat()->sendChat($message, $player);
- }
+
+ // Show message when it's not empty
+ if ($message != null) {
+ $message = 'Supported Player Commands: ' . $message;
+ $this->maniaControl->getChat()->sendChat($message, $player);
+ }
}
/**
@@ -92,23 +91,24 @@ class HelpManager implements CommandListener, CallbackListener {
* @param Player $player
*/
public function command_playerHelpAll(array $chatCallback, Player $player) {
- $this->parseHelpList($this->playerCommands, true, $player);
+ $this->parseHelpList($this->playerCommands, true, $player);
}
/**
- * Parse list with commands from array
- * @param array $commands
- * @param bool $isHelpAll
- * @param Player $player
- * @return string|void
- */
+ * Parse list with commands from array
+ *
+ * @param array $commands
+ * @param bool $isHelpAll
+ * @param Player $player
+ * @return string|void
+ */
private function parseHelpList(array $commands, $isHelpAll = false, Player $player = null) {
$showCommands = array();
$registeredMethods = array();
- $message = '';
+ $message = '';
foreach (array_reverse($commands) as $command) {
if (array_key_exists($command['Method'], $registeredMethods)) {
- if($showCommands[$registeredMethods[$command['Method']]]['Description'] === $command['Description']) {
+ if ($showCommands[$registeredMethods[$command['Method']]]['Description'] === $command['Description']) {
$name = $registeredMethods[$command['Method']];
$showCommands[$name]['Name'] .= '|' . $command['Name'];
} else {
@@ -125,20 +125,17 @@ class HelpManager implements CommandListener, CallbackListener {
return strcmp($commandA['Name'], $commandB['Name']);
});
- if(!$isHelpAll){
+ if (!$isHelpAll) {
foreach ($showCommands as $command) {
$message .= $command['Name'] . ',';
}
$message = substr($message, 0, -1);
-
- return $message;
- }else{
- if($player != NULL){
+ } else {
+ if ($player != null) {
$this->showHelpAllList($showCommands, $player);
}
}
-
- return;
+ return $message;
}
/**
@@ -221,7 +218,7 @@ class HelpManager implements CommandListener, CallbackListener {
* @param Player $player
*/
public function command_adminHelpAll(array $chatCallback, Player $player) {
- $this->parseHelpList($this->adminCommands, true, $player);
+ $this->parseHelpList($this->adminCommands, true, $player);
}
/**
diff --git a/core/Communication/Communication.php b/core/Communication/Communication.php
new file mode 100644
index 00000000..d8ae80c9
--- /dev/null
+++ b/core/Communication/Communication.php
@@ -0,0 +1,107 @@
+
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
+class Communication {
+ private $socket;
+ private $ip;
+ private $port;
+ private $encryptionPassword;
+
+ private $buffer = "";
+ private $messageQueue = array();
+
+ public function __construct($ip, $port, $encryptionPassword) {
+ $this->ip = $ip;
+ $this->port = $port;
+ $this->encryptionPassword = $encryptionPassword;
+ }
+
+ /** Create an Connection */
+ public function createConnection() {
+ $errno = null;
+ $errstr = null;
+ $this->socket = @fsockopen($this->ip, $this->port, $errno, $errstr, 2);
+
+ //socket_set_nonblock($this->socket);
+ stream_set_blocking($this->socket, 0);
+
+ if ($errno != 0 || !$this->socket) {
+ var_dump($errstr);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Call an Method Asynchronously
+ *
+ * @param callable $function
+ * @param $method
+ * @param string $data
+ */
+ public function call(callable $function, $method, $data = "") {
+ if (!$this->socket) {
+ call_user_func($function, true, "You need to create an Communication before using it");
+ return null;
+ }
+
+ $data = json_encode(array("method" => $method, "data" => $data));
+
+ $data = openssl_encrypt($data, CommunicationManager::ENCRYPTION_METHOD, $this->encryptionPassword, OPENSSL_RAW_DATA, CommunicationManager::ENCRYPTION_IV);
+
+ array_push($this->messageQueue, $function);
+
+ // Write Request on Socket
+ fwrite($this->socket, strlen($data) . "\n" . $data);
+ }
+
+ /**
+ * Process data on every Tick
+ */
+ public function tick() {
+ //Check if the connection is enabled
+ if (!$this->socket) {
+ return;
+ }
+
+ $data = fgets($this->socket, 1024); // reads as much as possible OR nothing at all
+ if (strlen($data) > 0) { // got new data
+ $this->buffer .= $data; // append new data to buffer
+ // handle the data the exact same way
+ $arr = explode("\n", $this->buffer, 2);
+ while (count($arr) == 2 && strlen($arr[1]) >= (int) $arr[0]) {
+ // received full message
+ $len = (int) $arr[0];
+ $msg = substr($arr[1], 0, $len); // clip msg
+ $this->buffer = substr($this->buffer, strlen((string) $len) + 1 /* newline */ + $len); // clip buffer
+
+ // Decode Message
+ $data = openssl_decrypt($msg, CommunicationManager::ENCRYPTION_METHOD, $this->encryptionPassword, OPENSSL_RAW_DATA, CommunicationManager::ENCRYPTION_IV);
+ $data = json_decode($data);
+
+ // Received something!
+ //Call Function with Data
+ call_user_func(array_shift($this->messageQueue), $data->error, $data->data);
+
+ // next msg
+ $arr = explode("\n", $this->buffer, 2);
+ }
+ }
+ }
+
+ /** Closes the connection, don't call yourself, let it do the Communication Manager */
+ public function closeConnection() {
+ if ($this->socket) {
+ fclose($this->socket);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/Communication/CommunicationAnswer.php b/core/Communication/CommunicationAnswer.php
new file mode 100644
index 00000000..0df63d2e
--- /dev/null
+++ b/core/Communication/CommunicationAnswer.php
@@ -0,0 +1,25 @@
+
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
+class CommunicationAnswer {
+ /** Properties are Public for serialization */
+ public $error;
+ public $data;
+
+ /**
+ * @param string $data
+ * @param bool $error
+ */
+ public function __construct($data = "", $error = false) {
+ $this->data = $data;
+ $this->error = $error;
+ }
+}
\ No newline at end of file
diff --git a/core/Communication/CommunicationListener.php b/core/Communication/CommunicationListener.php
new file mode 100644
index 00000000..5c910134
--- /dev/null
+++ b/core/Communication/CommunicationListener.php
@@ -0,0 +1,13 @@
+
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
+interface CommunicationListener {
+}
\ No newline at end of file
diff --git a/core/Communication/CommunicationManager.php b/core/Communication/CommunicationManager.php
new file mode 100644
index 00000000..52548a3d
--- /dev/null
+++ b/core/Communication/CommunicationManager.php
@@ -0,0 +1,311 @@
+
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
+class CommunicationManager implements CallbackListener {
+ /** Constants */
+ const SETTING_SOCKET_ENABLED = "Activate Socket";
+ const SETTING_SOCKET_PASSWORD = "Password for the Socket Connection";
+ const SETTING_SOCKET_PORT = "Socket Port for Server ";
+
+ const ENCRYPTION_IV = "kZ2Kt0CzKUjN2MJX";
+ const ENCRYPTION_METHOD = "aes-192-cbc";
+
+ /** @var ManiaControl $maniaControl */
+ private $maniaControl = null;
+
+ /** @var LoopInterface $loop */
+ private $loop = null;
+
+ /** @var Listening[] $communicationListenings */
+ private $communicationListenings = array();
+
+ /** @var Server $socket */
+ private $socket = null;
+ /** @var Communication[] $communcations */
+ private $communications = array();
+
+ /**
+ * Create a new Communication Handler Instance
+ *
+ * @param ManiaControl $maniaControl
+ */
+ public function __construct(ManiaControl $maniaControl) {
+ $this->maniaControl = $maniaControl;
+
+ $this->maniaControl->getCallbackManager()->registerCallbackListener(SettingManager::CB_SETTING_CHANGED, $this, 'updateSettings');
+ $this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::AFTERINIT, $this, 'initCommunicationManager');
+ $this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONRESTART, $this, 'onShutDown');
+ $this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONSHUTDOWN, $this, 'onShutDown');
+ }
+
+ /**
+ * Creates a Communication to another ManiaControl
+ *
+ * @param $ip
+ * @param $port
+ * @return \ManiaControl\Communication\Communication
+ */
+ public function createCommunication($ip, $port, $encryptionKey) {
+ $communication = new Communication($ip, $port, $encryptionKey);
+ $communication->createConnection();
+
+ $this->communications[] = $communication;
+ return $communication;
+ }
+
+
+ /**
+ * Closes a opened Communication
+ * Does not necessarily need be called, all connections get destroyed on ManiaControl Shutdown
+ *
+ * @param Communication $communication
+ * @return bool
+ */
+ public function closeCommunication($communication) {
+ $key = array_search($communication, $this->communications);
+ if (isset($this->communications[$key])) {
+ $this->communications[$key]->closeConnection();
+ unset($this->communications[$key]);
+ return true;
+ }
+ return false;
+ }
+
+ /** Close all Sockets on maniaControl Shutdown */
+ public function onShutDown() {
+ if ($this->socket && $this->socket->master) {
+ //Stop the Socket Listening
+ $this->socket->shutdown();
+ $this->socket = null;
+ }
+
+ foreach ($this->communications as $communication) {
+ $this->closeCommunication($communication);
+ }
+ }
+
+ /**
+ * Register a new Communication Listener
+ *
+ * @param string $callbackName
+ * @param CommunicationListener $listener
+ * @param string $method
+ * @return bool
+ */
+ public function registerCommunicationListener($echoName, CommunicationListener $listener, $method) {
+ if (!Listening::checkValidCallback($listener, $method)) {
+ $listenerClass = get_class($listener);
+ trigger_error("Given Listener '{$listenerClass}' can't handle Callback '{$echoName}': No callable Method '{$method}'!");
+ return false;
+ }
+
+ if (!array_key_exists($echoName, $this->communicationListenings)) {
+ $this->communicationListenings[$echoName] = new Listening($listener, $method);
+ } else {
+ //TODO say which is already listening and other stuff
+ trigger_error("Only one Listener can listen on a specific Communication Message");
+ }
+
+ return true;
+ }
+
+ /**
+ * Trigger a specific Callback
+ *
+ * @param mixed $callback
+ */
+ public function triggerCommuncationCallback($callbackName) {
+ if (!array_key_exists($callbackName, $this->communicationListenings)) {
+ return null;
+ }
+
+ $params = func_get_args();
+ $params = array_slice($params, 1, null, true);
+
+ $listening = $this->communicationListenings[$callbackName];
+ /** @var Listening $listening */
+ return $listening->triggerCallbackWithParams($params);
+ }
+
+
+ /**
+ * Unregister a Communication Listener
+ *
+ * @param CommunicationListener $listener
+ * @return bool
+ */
+ public function unregisterCommunicationListener(CommunicationListener $listener) {
+ return $this->removeCommunicationListener($this->communicationListenings, $listener);
+ }
+
+
+ /**
+ * Remove the Communication Listener from the given Listeners Array
+ *
+ * @param Listening[] $listeningsArray
+ * @param CommunicationListener $listener
+ * @return bool
+ */
+ private function removeCommunicationListener(array &$listeningsArray, CommunicationListener $listener) {
+ $removed = false;
+ foreach ($listeningsArray as &$listening) {
+ if ($listening->listener === $listener) {
+ unset($listening);
+ $removed = true;
+ }
+ }
+ return $removed;
+ }
+
+ /**
+ * Inits the Communication Manager after ManiaControl Startup
+ */
+ public function initCommunicationManager() {
+ $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_SOCKET_ENABLED, false);
+ $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_SOCKET_PASSWORD, "");
+
+ $servers = $this->maniaControl->getServer()->getAllServers();
+ foreach ($servers as $server) {
+ $this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_SOCKET_PORT . $server->login, 31500 + $server->index);
+ }
+
+
+ $this->createListeningSocket();
+ }
+
+ /**
+ * Update Setting
+ *
+ * @param Setting $setting
+ */
+ public function updateSettings(Setting $setting) {
+ if (!$setting->belongsToClass($this)) {
+ return;
+ }
+
+ $socketEnabled = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SOCKET_ENABLED);
+
+ if ($socketEnabled && !$this->socket) {
+ $this->createListeningSocket();
+ }
+
+ if (!$socketEnabled) {
+ $this->socket = null;
+ }
+ }
+
+ /**
+ * Creates The Socket
+ */
+ private function createListeningSocket() {
+ $socketEnabled = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SOCKET_ENABLED);
+ if ($socketEnabled) {
+
+ Logger::log("[CommunicationManager] Trying to create Socket");
+
+ // Check for MySQLi
+ $message = '[CommunicationManager] Checking for installed openssl ... ';
+ if (!extension_loaded('openssl')) {
+ Logger::log($message . 'NOT FOUND!');
+ Logger::log(" -- You don't have openssl installed! Check: http://www.php.net/manual/en/openssl.installation.php");
+ return;
+ } else {
+ Logger::log($message . 'FOUND!');
+ }
+
+ $serverLogin = $this->maniaControl->getServer()->login;
+ $socketPort = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SOCKET_PORT . $serverLogin);
+ $password = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SOCKET_PASSWORD);
+
+ try {
+ $this->loop = Factory::create();
+ $this->socket = new Server($this->loop);
+
+ $this->socket->on('error', function ($e) {
+ Logger::log("[CommunicationManager] Socket Error" . $e);
+ });
+
+ $this->socket->on('connection', function (Connection $connection) use ($password) {
+ $buffer = '';
+ $connection->on('data', function ($data) use (&$buffer, &$connection, $password) {
+ $buffer .= $data;
+ $arr = explode("\n", $buffer, 2);
+ while (count($arr) == 2 && strlen($arr[1]) >= (int) $arr[0]) {
+ // received full message
+ $len = (int) $arr[0];
+ $msg = substr($arr[1], 0, $len); // clip msg
+ $buffer = substr($buffer, strlen((string) $len) + 1 /* newline */ + $len); // clip buffer
+
+ // Decode Message
+ $data = openssl_decrypt($msg, self::ENCRYPTION_METHOD, $password, OPENSSL_RAW_DATA, self::ENCRYPTION_IV);
+ $data = json_decode($data);
+
+ if ($data == null) {
+ $data = array("error" => true, "data" => "Data is not provided as an valid AES-196-encrypted encrypted JSON");
+ } else if (!property_exists($data, "method") || !property_exists($data, "data")) {
+ $data = array("error" => true, "data" => "Invalid Message");
+ } else {
+ $answer = $this->triggerCommuncationCallback($data->method, $data->data);
+ //Prepare Response
+ if (!$answer) {
+ $data = new CommunicationAnswer("No listener or response on the given Message", true);
+ } else {
+ $data = $answer;
+ }
+ }
+
+ //Encode, Encrypt and Send Response
+ $data = json_encode($data);
+
+ $data = openssl_encrypt($data, self::ENCRYPTION_METHOD, $password, OPENSSL_RAW_DATA, self::ENCRYPTION_IV);
+ $connection->write(strlen($data) . "\n" . $data);
+
+ // next msg
+ $arr = explode("\n", $buffer, 2);
+ }
+ });
+ });
+ //TODO check if port is closed
+ $this->socket->listen($socketPort, $this->maniaControl->getServer()->ip);
+
+ Logger::log("[CommunicationManager] Socket " . $this->maniaControl->getServer()->ip . ":" . $this->socket->getPort() . " Successfully created!");
+ } catch (ConnectionException $e) {
+ Logger::log("[CommunicationManager] Exception: " . $e->getMessage());
+ }
+ }
+ }
+
+
+ /**
+ * Processes Data on every ManiaControl Tick, don't call this Method
+ */
+ public function tick() {
+ if ($this->loop) {
+ $this->loop->tick();
+ }
+
+ foreach ($this->communications as $communication) {
+ $communication->tick();
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/Communication/CommunicationMethods.php b/core/Communication/CommunicationMethods.php
new file mode 100644
index 00000000..c4384a34
--- /dev/null
+++ b/core/Communication/CommunicationMethods.php
@@ -0,0 +1,173 @@
+
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
+interface CommunicationMethods {
+ /** Restarts Mania Control
+ * Optional Params
+ * - message
+ */
+ const RESTART_MANIA_CONTROL = "ManiaControl.Restart";
+
+ /** Grands an Authentication Level on a Player
+ * Required Parameters
+ * - login (login of the player)
+ * - level (integer, 0-3 possible, @see AuthenticationManager)
+ */
+ const GRANT_AUTH_LEVEL = "AuthenticationManager.GrandLevel";
+
+ /** Revokes an Authentication Level on a Player
+ * Required Parameters
+ * - login (login of the player)
+ */
+ const REVOKE_AUTH_LEVEL = "AuthenticationManager.RevokeLevel";
+
+ /** Provides the Server Options
+ * no Parameters
+ */
+ const GET_SERVER_OPTIONS = "ServerOptions.GetServerOptions";
+
+ /** Set Server Options
+ * Required Parameter
+ * - scriptSettings (array(optionName1 => value1, optionName2 => value2...))
+ */
+ const SET_SERVER_OPTIONS = "ServerOptions.SetServerOptions";
+
+ /** Provides the ModeScriptSettings
+ * no Parameters
+ */
+ const GET_SCRIPT_SETTINGS = "ScriptSettings.GetScriptSettings";
+
+ /** Set ModeScriptSettings
+ * Required Parameter
+ * - scriptSettings (array(settingName1 => value1, settingName2 => value2...))
+ */
+ const SET_SCRIPT_SETTINGS = "ScriptSettings.SetScriptSettings";
+
+ /** Restarts the Current Map
+ * no Parameters
+ */
+ const RESTART_MAP = "MapActions.RestartMap";
+
+ /** Skips the Current Map
+ * no Parameters
+ */
+ const SKIP_MAP = "MapActions.SkipMap";
+
+ /** Skips to a Specific Map by MxId or MapUid
+ * Required Parameters
+ * - mxId (integer)
+ * OR
+ * - mapUid (string)
+ */
+ const SKIP_TO_MAP = "MapActions.SkipToMap";
+
+ /** Adds a Map from Mania Exchange to the Server
+ * Required Parameters
+ * - mxId (integer)
+ * (no success returning yet because of asynchronously of adding)
+ */
+ const ADD_MAP = "MapManager.AddMap";
+
+ /** Removes a Map from the Server
+ * Required Parameters
+ * - mapUid (string)
+ * Optional Parameters
+ * - displayMessage (default true)
+ * - eraseMapFile (default false)
+ */
+ const REMOVE_MAP = "MapManager.RemoveMap";
+
+ /** Updates a Map over Mania Exchange
+ * Required Parameters
+ * - mapUid
+ * (no success returning yet because of asynchronously of adding)
+ */
+ const UPDATE_MAP = "MapManager.UpdateMap";
+
+ /** Gets the current Map
+ * Required Parameters
+ * - mxId (integer)
+ * OR
+ * - mapUid (string)
+ */
+ const GET_CURRENT_MAP = "MapManager.GetCurrentMap";
+
+ /** Gets the specific Map
+ * no Parameters
+ */
+ const GET_MAP = "MapManager.GetMap";
+
+ /** Gets the current Map List
+ * no Parameters
+ */
+ const GET_MAP_LIST = "MapManager.GetMapList";
+
+ /** Gets Mania Control PlayerList
+ * no Parameters
+ */
+ const GET_PLAYER_LIST = "PlayerManager.GetPlayerList";
+
+ /** Warns a Player
+ * Required Params
+ * - login
+ */
+ const WARN_PLAYER = "PlayerActions.WarnPlayer";
+
+ /** Mutes a Player
+ * Required Params
+ * - login
+ */
+ const MUTE_PLAYER = "PlayerActions.MutePlayer";
+
+ /** UnMutes a Player
+ * Required Params
+ * - login
+ */
+ const UNMUTE_PLAYER = "PlayerActions.UnMutePlayer";
+
+ /** UnMutes a Player
+ * Required Params
+ * - login
+ * Optional Params
+ * - message
+ */
+ const KICK_PLAYER = "PlayerActions.KickPlayer";
+
+ /** Forces a player to Spectator
+ * Required Params
+ * - login
+ */
+ const FORCE_PLAYER_TO_SPEC = "PlayerActions.ForcePlayerToSpec";
+
+ /** Forces a player to Spectator
+ * Required Params
+ * - login
+ * Optional Params
+ * - teamId (integer, id of the team the player should get forced into it)
+ */
+ const FORCE_PLAYER_TO_PLAY = "PlayerActions.ForcePlayerToPlay";
+
+ /** Returns the last 200 lines of the chat (inclusive player logins and nicknames)
+ * No Params
+ */
+ const GET_SERVER_CHAT = "Chat.GetServerChat";
+
+ /** Sends a ChatMessage to the Server
+ * Required Params:
+ * - message
+ * Optional Params
+ * - prefix (use custom prefix or false for no prefix)
+ * - login (login of a receiver if the message don't get sent to all)
+ * - adminLevel (minimum Admin Level if the Message should get sent to an Admin)
+ * - type (type of the message (information, error, success or usage)
+ */
+ const SEND_CHAT_MESSAGE = "Chat.SendChatMessage";
+}
\ No newline at end of file
diff --git a/core/Communication/usage_documentation.txt b/core/Communication/usage_documentation.txt
new file mode 100644
index 00000000..3dc28b14
--- /dev/null
+++ b/core/Communication/usage_documentation.txt
@@ -0,0 +1,53 @@
+The CommuncationListening of the Communcation Manager can be enabled in the ingame Settings.
+
+There the following settings are existing:
+-- Enable Socket Listening (Let the CommunicationManager listen for incoming calls)
+-- Passsword (Password which get used to encrypt and decrypt the messages for the openssl connection)
+-- Listening port for every server (this is the port the CommunicationManager listens at)
+
+
+For the description of the available implemented communcation Methods check the CommunicationMethods interface.
+
+If you need methods which are not implemented, or additional Parameters, feel free to contact us.
+
+Sample ManiaControl Implementation (for ManiaControl to ManiaControl connections)
+
+##php code begin
+ $communication = $this->maniaControl->getCommunicationManager()->createCommunication(IP/Domain, PORT, 'YOUR_PASSWORD');
+ $communication->call(function($data){
+ var_dump($data);
+ }, CommunicationMethods::GET_SERVER_CHAT);
+##php code end
+
+Sample Web Implementation (to call ManiaControl from a website)
+##php code begin
+ $errno = null;
+ $errstr = null;
+ $socket = fsockopen("xx.xxx.xx.xx", xxxxx, $errno, $errstr, 2);
+ echo "ok?" . $errno . " - " . $errstr . "\n";
+
+ $data = array("method" => "getServerChat", "data" => "");
+
+ // Encode and Encrypt the Data
+ $data = json_encode(array("method" => "getServerChat", "data" => ""));
+ $data = openssl_encrypt($data, 'aes-192-cbc', 'YOUR_PASSWORD', OPENSSL_RAW_DATA, 'kZ2Kt0CzKUjN2MJX');
+
+ // Write the Data on the Socket
+ fwrite($socket, strlen($data) . "\n" . $data);
+
+
+ // Read Answer Data
+ $len = (int)fgets($socket);
+ echo $len;
+ $buff = '';
+ while (!feof($socket) && strlen($buff) < $len) {
+ $buff .= fgets($socket, $len - strlen($buff) + 1);
+ }
+
+ // Decrypt and Decode the Response Data
+ $data = openssl_decrypt($buff, 'aes-192-cbc', 'YOUR_PASSWORD', OPENSSL_RAW_DATA, 'kZ2Kt0CzKUjN2MJX');
+ echo json_decode($data);
+
+ //Close the Socket
+ fclose($socket);
+##php code end
\ No newline at end of file
diff --git a/core/Configurator/Configurator.php b/core/Configurator/Configurator.php
index 20ddb826..b58c6a2e 100644
--- a/core/Configurator/Configurator.php
+++ b/core/Configurator/Configurator.php
@@ -199,11 +199,11 @@ class Configurator implements CallbackListener, CommandListener, ManialinkPageAn
$frame = new Frame();
$manialink->add($frame);
- $frame->setPosition($menuPosX, $menuPosY, 10);
+ $frame->setPosition($menuPosX, $menuPosY, 34);
$backgroundQuad = new Quad();
$frame->add($backgroundQuad);
- $backgroundQuad->setZ(-10)->setSize($menuWidth, $menuHeight)->setStyles($quadStyle, $quadSubstyle);
+ $backgroundQuad->setZ(-2)->setSize($menuWidth, $menuHeight)->setStyles($quadStyle, $quadSubstyle);
$menuItemsFrame = new Frame();
$frame->add($menuItemsFrame);
@@ -211,7 +211,7 @@ class Configurator implements CallbackListener, CommandListener, ManialinkPageAn
$itemsBackgroundQuad = new Quad();
$menuItemsFrame->add($itemsBackgroundQuad);
- $backgroundQuad->setZ(-9);
+ $backgroundQuad->setZ(-1);
$itemsBackgroundQuad->setSize($menuListWidth, $menuHeight)->setStyles($quadStyle, $quadSubstyle);
$menusFrame = new Frame();
diff --git a/core/Configurator/ScriptSettings.php b/core/Configurator/ScriptSettings.php
index 6dc4204d..8d317693 100644
--- a/core/Configurator/ScriptSettings.php
+++ b/core/Configurator/ScriptSettings.php
@@ -14,6 +14,9 @@ use FML\Script\Script;
use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
+use ManiaControl\Communication\CommunicationAnswer;
+use ManiaControl\Communication\CommunicationListener;
+use ManiaControl\Communication\CommunicationMethods;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
@@ -26,7 +29,7 @@ use Maniaplanet\DedicatedServer\Xmlrpc\GameModeException;
* @copyright 2014-2015 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
-class ScriptSettings implements ConfiguratorMenu, CallbackListener {
+class ScriptSettings implements ConfiguratorMenu, CallbackListener, CommunicationListener {
/*
* Constants
*/
@@ -61,6 +64,10 @@ class ScriptSettings implements ConfiguratorMenu, CallbackListener {
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_CHANGE_SCRIPT_SETTINGS, AuthenticationManager::AUTH_LEVEL_ADMIN);
+
+ //TODO remove to somewhere cleaner
+ //Communication Listenings
+ $this->initalizeCommunicationListenings();
}
/**
@@ -149,8 +156,7 @@ class ScriptSettings implements ConfiguratorMenu, CallbackListener {
* Handle Begin Map Callback
*/
public function onBeginMap() {
- if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_LOAD_DEFAULT_SETTINGS_MAP_BEGIN)
- ) {
+ if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_LOAD_DEFAULT_SETTINGS_MAP_BEGIN)) {
$this->loadSettingsFromDatabase();
}
}
@@ -282,8 +288,7 @@ class ScriptSettings implements ConfiguratorMenu, CallbackListener {
* @see \ManiaControl\Configurators\ConfiguratorMenu::saveConfigData()
*/
public function saveConfigData(array $configData, Player $player) {
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SCRIPT_SETTINGS)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SCRIPT_SETTINGS)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
@@ -327,6 +332,7 @@ class ScriptSettings implements ConfiguratorMenu, CallbackListener {
$this->maniaControl->getConfigurator()->showMenu($player, $this);
}
+
/**
* Apply the Array of new Script Settings
*
@@ -406,6 +412,63 @@ class ScriptSettings implements ConfiguratorMenu, CallbackListener {
if (is_bool($value)) {
return ($value ? 'True' : 'False');
}
- return (string)$value;
+ return (string) $value;
+ }
+
+ /**
+ * Initializes the communication Listenings
+ */
+ private function initalizeCommunicationListenings() {
+ //Communication Listenings
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::GET_SCRIPT_SETTINGS, $this, function ($data) {
+ try {
+ $scriptSettings = $this->maniaControl->getClient()->getModeScriptSettings();
+ } catch (GameModeException $e) {
+ return new CommunicationAnswer($e->getMessage(), true);
+ }
+
+ return new CommunicationAnswer($scriptSettings);
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::SET_SCRIPT_SETTINGS, $this, function ($data) {
+ if (!is_object($data) || !property_exists($data, "scriptSettings")) {
+ return new CommunicationAnswer("No valid ScriptSettings provided!", true);
+ }
+
+ try {
+ $scriptSettings = $this->maniaControl->getClient()->getModeScriptSettings();
+ } catch (GameModeException $e) {
+ return new CommunicationAnswer($e->getMessage(), true);
+ }
+
+ $newSettings = array();
+ foreach ($data->scriptSettings as $name => $value) {
+ if (!isset($scriptSettings[$name])) {
+ var_dump('no setting ' . $name);
+ continue;
+ }
+
+ if ($value == $scriptSettings[$name]) {
+ // Not changed
+ continue;
+ }
+
+ $newSettings[$name] = $value;
+ settype($newSettings[$name], gettype($scriptSettings[$name]));
+ }
+
+ //No new Settings
+ if (empty($newSettings)) {
+ return new CommunicationAnswer(array("success" => true));
+ }
+
+ //Trigger Scriptsettings Changed Callback
+ $this->maniaControl->getCallbackManager()->triggerCallback(self::CB_SCRIPTSETTINGS_CHANGED);
+
+ //Set the Settings
+ $success = $this->maniaControl->getClient()->setModeScriptSettings($newSettings);
+
+ return new CommunicationAnswer(array("success" => $success));
+ });
}
}
diff --git a/core/Files/AsyncHttpRequest.php b/core/Files/AsyncHttpRequest.php
new file mode 100644
index 00000000..40eb57ac
--- /dev/null
+++ b/core/Files/AsyncHttpRequest.php
@@ -0,0 +1,207 @@
+
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
+class AsyncHttpRequest {
+ /*
+ * Constants
+ */
+ const CONTENT_TYPE_JSON = 'application/json';
+
+ /*
+ * Private properties
+ */
+ /** @var ManiaControl $maniaControl */
+ private $maniaControl;
+
+ private $url;
+ private $function;
+ private $content;
+ private $compression = false;
+ private $contentType = 'text/xml; charset=UTF-8;';
+ private $headers = array();
+
+ public function __construct($maniaControl, $url) {
+ $this->maniaControl = $maniaControl;
+ $this->url = $url;
+ }
+
+ /**
+ * Create a new cURL Request for the given URL
+ *
+ * @param string $url
+ * @return Request
+ */
+ private function newRequest($url) {
+ $request = new Request($url);
+ $request->getOptions()->set(CURLOPT_TIMEOUT, 60)->set(CURLOPT_HEADER, false)// don't display response header
+ ->set(CURLOPT_CRLF, true)// linux line feed
+ ->set(CURLOPT_ENCODING, '')// accept encoding
+ ->set(CURLOPT_USERAGENT, 'ManiaControl v' . ManiaControl::VERSION)// user-agent
+ ->set(CURLOPT_RETURNTRANSFER, true)//
+ ->set(CURLOPT_FOLLOWLOCATION, true)// support redirect
+ ->set(CURLOPT_SSL_VERIFYPEER, false);
+ return $request;
+ }
+
+ /**
+ * Carry out a GetData Request
+ *
+ * @param int $keepAlive
+ */
+ public function getData($keepAlive = 0) {
+ array_push($this->headers, 'Content-Type: ' . $this->contentType);
+ if ($keepAlive) {
+ array_push($this->headers, 'Keep-Alive: ' . $keepAlive);
+ array_push($this->headers, 'Connection: Keep-Alive');
+ }
+
+ $request = $this->newRequest($this->url);
+ $request->getOptions()->set(CURLOPT_AUTOREFERER, true)// accept link reference
+ ->set(CURLOPT_HTTPHEADER, $this->headers); // headers
+
+ $this->processRequest($request);
+ }
+
+
+ /**
+ * Carry out a PostData Request
+ */
+ public function postData() {
+ array_push($this->headers, 'Content-Type: ' . $this->contentType);
+ array_push($this->headers, 'Keep-Alive: timeout=600, max=2000');
+ array_push($this->headers, 'Connection: Keep-Alive');
+
+ $content = str_replace(array("\r", "\n"), '', $this->content);
+ if ($this->compression) {
+ $content = zlib_encode($content, 31);
+ array_push($this->headers, 'Content-Encoding: gzip');
+ }
+
+
+ $request = $this->newRequest($this->url);
+ $request->getOptions()->set(CURLOPT_POST, true)// post method
+ ->set(CURLOPT_POSTFIELDS, $content)// post content field
+ ->set(CURLOPT_HTTPHEADER, $this->headers) // headers
+ ;
+
+ $this->processRequest($request);
+ }
+
+ /**
+ * Processes the Request
+ *
+ * @param Request $request
+ */
+ private function processRequest(Request $request) {
+ $request->addListener('complete', function (Event $event) {
+ $error = null;
+ $content = null;
+ if ($event->response->hasError()) {
+ $error = $event->response->getError()->getMessage();
+ } else {
+ $content = $event->response->getContent();
+ }
+ call_user_func($this->function, $content, $error);
+ });
+
+ $fileReader = $this->maniaControl->getFileReader();
+ $fileReader->addRequest($request);
+ }
+
+ /**
+ * @param $url
+ * @return $this
+ */
+ public function setURL($url) {
+ $this->url = $url;
+ return $this;
+ }
+
+ /**
+ * @param callable $function
+ * @return $this
+ */
+ public function setCallable($function) {
+ $this->function = $function;
+ return $this;
+ }
+
+
+ /**
+ * @return mixed
+ */
+ public function getContent() {
+ return $this->content;
+ }
+
+ /**
+ * @param mixed $content
+ * @return $this
+ */
+ public function setContent($content) {
+ $this->content = $content;
+ return $this;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function getCompression() {
+ return $this->compression;
+ }
+
+ /**
+ * @param boolean $compression
+ * @return $this
+ */
+ public function setCompression($compression) {
+ $this->compression = $compression;
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getHeaders() {
+ return $this->headers;
+ }
+
+ /**
+ * @param array $headers
+ * @return $this
+ */
+ public function setHeaders($headers) {
+ if(is_array($headers)){
+ $this->headers = $headers;
+ }
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getContentType() {
+ return $this->contentType;
+ }
+
+ /**
+ * @param string $contentType
+ * @return $this
+ */
+ public function setContentType($contentType) {
+ $this->contentType = $contentType;
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/core/Files/AsynchronousFileReader.php b/core/Files/AsynchronousFileReader.php
index 4df0a3a1..41b9bd72 100644
--- a/core/Files/AsynchronousFileReader.php
+++ b/core/Files/AsynchronousFileReader.php
@@ -2,7 +2,6 @@
namespace ManiaControl\Files;
-use cURL\Event;
use cURL\Request;
use ManiaControl\ManiaControl;
@@ -38,10 +37,10 @@ class AsynchronousFileReader {
public static function newRequestTest($url) {
$request = new Request($url);
- $request->getOptions()->set(CURLOPT_TIMEOUT, 60)->set(CURLOPT_HEADER, false) // don't display response header
- ->set(CURLOPT_CRLF, true) // linux line feed
- ->set(CURLOPT_ENCODING, '') // accept encoding
- ->set(CURLOPT_USERAGENT, 'ManiaControl v' . ManiaControl::VERSION) // user-agent
+ $request->getOptions()->set(CURLOPT_TIMEOUT, 60)->set(CURLOPT_HEADER, false)// don't display response header
+ ->set(CURLOPT_CRLF, true)// linux line feed
+ ->set(CURLOPT_ENCODING, '')// accept encoding
+ ->set(CURLOPT_USERAGENT, 'ManiaControl v' . ManiaControl::VERSION)// user-agent
->set(CURLOPT_RETURNTRANSFER, true); // return instead of output content
return $request;
}
@@ -66,93 +65,15 @@ class AsynchronousFileReader {
* @param callable $function
* @param string $contentType
* @param int $keepAlive
+ * @param array $headers Additional Headers
+ * @deprecated @see ManiaControl\Files\AsyncHttpRequest
*/
- public function loadFile($url, callable $function, $contentType = 'UTF-8', $keepAlive = 0) {
- $headers = array();
- array_push($headers, 'Content-Type: ' . $contentType);
- if ($keepAlive) {
- array_push($headers, 'Keep-Alive: ' . $keepAlive);
- array_push($headers, 'Connection: Keep-Alive');
- }
-
- $request = $this->newRequest($url);
- $request->getOptions()->set(CURLOPT_AUTOREFERER, true) // accept link reference
- ->set(CURLOPT_HTTPHEADER, $headers); // headers
-
- $request->addListener('complete', function (Event $event) use (&$function) {
- $error = null;
- $content = null;
- if ($event->response->hasError()) {
- $error = $event->response->getError()->getMessage();
- } else {
- $content = $event->response->getContent();
- }
- call_user_func($function, $content, $error);
- });
-
- $this->addRequest($request);
+ public function loadFile($url, callable $function, $contentType = 'UTF-8', $keepAlive = 0, $headers = array()) {
+ $httpRequest = new AsyncHttpRequest($this->maniaControl, $url);
+ $httpRequest->setCallable($function)->setContentType($contentType)->setHeaders($headers);
+ $httpRequest->getData($keepAlive);
}
- /**
- * Create a new cURL Request for the given URL
- *
- * @param string $url
- * @return Request
- */
- protected function newRequest($url) {
- $request = new Request($url);
- $request->getOptions()->set(CURLOPT_TIMEOUT, 60)->set(CURLOPT_HEADER, false) // don't display response header
- ->set(CURLOPT_CRLF, true) // linux line feed
- ->set(CURLOPT_ENCODING, '') // accept encoding
- ->set(CURLOPT_USERAGENT, 'ManiaControl v' . ManiaControl::VERSION) // user-agent
- ->set(CURLOPT_RETURNTRANSFER, true); // return instead of output content
- return $request;
- }
-
- //TODO remove, they are just for testing dedimania
-
- /**
- * Add a Request to the queue
- *
- * @param Request $request
- */
- protected function addRequest(Request $request) {
- array_push($this->requests, $request);
- }
-
- public function postDataTest(Request $request, $url, callable $function, $content, $compression = false, $contentType = 'text/xml; charset=UTF-8;') {
-
- $headers = array();
- array_push($headers, 'Content-Type: ' . $contentType);
- array_push($headers, 'Keep-Alive: timeout=600, max=2000');
- array_push($headers, 'Connection: Keep-Alive');
-
- $content = str_replace(array("\r", "\n"), '', $content);
- if ($compression) {
- $content = zlib_encode($content, 31);
- array_push($headers, 'Content-Encoding: gzip');
- }
-
- $request->getOptions()->set(CURLOPT_POST, true) // post method
- ->set(CURLOPT_POSTFIELDS, $content) // post content field
- ->set(CURLOPT_HTTPHEADER, $headers) // headers
- ;
- $request->addListener('complete', function (Event $event) use (&$function) {
- $error = null;
- $content = null;
- if ($event->response->hasError()) {
- $error = $event->response->getError()->getMessage();
- } else {
- $content = $event->response->getContent();
- }
-
- call_user_func($function, $content, $error);
- });
-
- $this->addRequest($request);
- }
-
-
/**
* Send Data via POST Method
*
@@ -161,36 +82,21 @@ class AsynchronousFileReader {
* @param string $content
* @param bool $compression
* @param string $contentType
+ * @param array $headers Additional Headers
+ * @deprecated @see ManiaControl\Files\AsyncHttpRequest
*/
- public function postData($url, callable $function, $content, $compression = false, $contentType = 'text/xml; charset=UTF-8;') {
+ public function postData($url, callable $function, $content, $compression = false, $contentType = 'text/xml; charset=UTF-8;', $headers = array()) {
+ $httpRequest = new AsyncHttpRequest($this->maniaControl, $url);
+ $httpRequest->setCallable($function)->setContent($content)->setCompression($compression)->setContentType($contentType)->setHeaders($headers);
+ $httpRequest->postData();
+ }
- $headers = array();
- array_push($headers, 'Content-Type: ' . $contentType);
- array_push($headers, 'Keep-Alive: timeout=600, max=2000');
- array_push($headers, 'Connection: Keep-Alive');
-
- $content = str_replace(array("\r", "\n"), '', $content);
- if ($compression) {
- $content = zlib_encode($content, 31);
- array_push($headers, 'Content-Encoding: gzip');
- }
-
- $request = $this->newRequest($url);
- $request->getOptions()->set(CURLOPT_POST, true) // post method
- ->set(CURLOPT_POSTFIELDS, $content) // post content field
- ->set(CURLOPT_HTTPHEADER, $headers) // headers
- ;
- $request->addListener('complete', function (Event $event) use (&$function) {
- $error = null;
- $content = null;
- if ($event->response->hasError()) {
- $error = $event->response->getError()->getMessage();
- } else {
- $content = $event->response->getContent();
- }
- call_user_func($function, $content, $error);
- });
-
- $this->addRequest($request);
+ /**
+ * Add a Request to the queue, DO NOT CALL MANUALLY!
+ *
+ * @param Request $request
+ */
+ public function addRequest(Request $request) {
+ array_push($this->requests, $request);
}
}
diff --git a/core/ManiaControl.php b/core/ManiaControl.php
index 086112a3..0f0b4cbc 100644
--- a/core/ManiaControl.php
+++ b/core/ManiaControl.php
@@ -8,10 +8,15 @@ use ManiaControl\Bills\BillManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Callbacks\Callbacks;
+use ManiaControl\Callbacks\EchoManager;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\Callbacks\TimerManager;
use ManiaControl\Commands\CommandListener;
use ManiaControl\Commands\CommandManager;
+use ManiaControl\Communication\CommunicationAnswer;
+use ManiaControl\Communication\CommunicationListener;
+use ManiaControl\Communication\CommunicationManager;
+use ManiaControl\Communication\CommunicationMethods;
use ManiaControl\Configurator\Configurator;
use ManiaControl\Database\Database;
use ManiaControl\Files\AsynchronousFileReader;
@@ -38,11 +43,11 @@ use Maniaplanet\DedicatedServer\Xmlrpc\TransportException;
* @copyright 2014-2015 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
-class ManiaControl implements CallbackListener, CommandListener, TimerListener {
+class ManiaControl implements CallbackListener, CommandListener, TimerListener, CommunicationListener {
/*
* Constants
*/
- const VERSION = '0.155';
+ const VERSION = '0.163';
const API_VERSION = '2013-04-16';
const MIN_DEDIVERSION = '2014-04-02_18_00';
const SCRIPT_TIMEOUT = 10;
@@ -164,6 +169,10 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener {
*/
private $requestQuitMessage = null;
+ /** @var EchoManager $echoManager */
+ private $echoManager = null;
+ private $communicationManager = null;
+
/**
* Construct a new ManiaControl instance
*/
@@ -176,6 +185,8 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener {
// Load ManiaControl Modules
$this->callbackManager = new CallbackManager($this);
+ $this->echoManager = new EchoManager($this);
+ $this->communicationManager = new CommunicationManager($this);
$this->timerManager = new TimerManager($this);
$this->database = new Database($this);
$this->fileReader = new AsynchronousFileReader($this);
@@ -194,6 +205,7 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener {
$this->pluginManager = new PluginManager($this);
$this->updateManager = new UpdateManager($this);
+
$this->getErrorHandler()->init();
// Permissions
@@ -207,6 +219,18 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener {
// Check connection every 30 seconds
$this->getTimerManager()->registerTimerListening($this, 'checkConnection', 1000 * 30);
+
+ // Communication Methods
+ $this->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::RESTART_MANIA_CONTROL, $this, function ($data) {
+ //Delay Shutdown to send answer first
+ $this->getTimerManager()->registerOneTimeListening($this, function () use ($data) {
+ if (is_object($data) && property_exists($data, "message")) {
+ $this->restart($data->message);
+ }
+ $this->restart();
+ }, 3000);
+ return new CommunicationAnswer();
+ });
}
/**
@@ -278,6 +302,24 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener {
return $this->callbackManager;
}
+ /**
+ * Return the echo manager
+ *
+ * @return EchoManager
+ */
+ public function getEchoManager() {
+ return $this->echoManager;
+ }
+
+ /**
+ * Return the socket manager
+ *
+ * @return CommunicationManager
+ */
+ public function getCommunicationManager() {
+ return $this->communicationManager;
+ }
+
/**
* Return the chat
*
@@ -465,6 +507,9 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener {
* @param string $message
*/
public function restart($message = null) {
+ // Trigger callback on Restart
+ $this->getCallbackManager()->triggerCallback(Callbacks::ONRESTART);
+
// Announce restart
try {
$this->getChat()->sendInformation('Restarting ManiaControl...');
diff --git a/core/ManiaExchange/ManiaExchangeList.php b/core/ManiaExchange/ManiaExchangeList.php
index 24c8f71c..9d061dd4 100644
--- a/core/ManiaExchange/ManiaExchangeList.php
+++ b/core/ManiaExchange/ManiaExchangeList.php
@@ -130,14 +130,18 @@ class ManiaExchangeList implements CallbackListener, ManialinkPageAnswerListener
}
}
- // search for matching maps
- $this->maniaControl->getMapManager()->getMXManager()->fetchMapsAsync(function (array $maps) use (&$player) {
+ //Search the Maps
+ $mxSearch = new ManiaExchangeMapSearch($this->maniaControl);
+ $mxSearch->setAuthorName($author);
+ $mxSearch->setEnvironments($environment);
+ $mxSearch->setMapName($searchString);
+ $mxSearch->fetchMapsAsync(function (array $maps) use (&$player) {
if (!$maps) {
$this->maniaControl->getChat()->sendError('No maps found, or MX is down!', $player->login);
return;
}
$this->showManiaExchangeList($maps, $player);
- }, $searchString, $author, $environment);
+ });
}
/**
diff --git a/core/ManiaExchange/ManiaExchangeManager.php b/core/ManiaExchange/ManiaExchangeManager.php
index 869efbec..f220ab3f 100644
--- a/core/ManiaExchange/ManiaExchangeManager.php
+++ b/core/ManiaExchange/ManiaExchangeManager.php
@@ -9,7 +9,7 @@ use ManiaControl\Maps\MapManager;
use Maniaplanet\DedicatedServer\Xmlrpc\GameModeException;
/**
- * Mania Exchange Info Searcher Class
+ * Mania Exchange Manager Class
*
* @author ManiaControl Team
* @copyright 2014-2015 ManiaControl Team
@@ -18,8 +18,9 @@ use Maniaplanet\DedicatedServer\Xmlrpc\GameModeException;
class ManiaExchangeManager {
/*
* Constants
+ * @deprecated SEARCH Constants
*/
- //Search others
+ //Search orders (prior parameter) http://api.mania-exchange.com/documents/enums#orderings
const SEARCH_ORDER_NONE = -1;
const SEARCH_ORDER_TRACK_NAME = 0;
const SEARCH_ORDER_AUTHOR = 1;
@@ -47,7 +48,7 @@ class ManiaExchangeManager {
* Private properties
*/
/** @var ManiaControl $maniaControl */
- private $maniaControl = null;
+ private $maniaControl = null;
private $mxIdUidVector = array();
/**
@@ -269,9 +270,9 @@ class ManiaExchangeManager {
/**
* @deprecated
- * @see \ManiaControl\ManiaExchange\ManiaExchangeManager::fetchMapsAsync()
+ * @see \ManiaControl\ManiaExchange\ManiaExchangeMapSearch
*/
- public function getMapsAsync(callable $function, $name = '', $author = '', $env = '', $maxMapsReturned = 100, $searchOrder = self::SEARCH_ORDER_UPDATED_NEWEST) {
+ public function getMapsAsync(callable $function, $name = '', $author = '', $env = '', $maxMapsReturned = 100, $searchOrder = ManiaExchangeMapSearch::SEARCH_ORDER_UPDATED_NEWEST) {
$this->fetchMapsAsync($function, $name, $author, $env, $maxMapsReturned, $searchOrder);
return true;
}
@@ -285,73 +286,22 @@ class ManiaExchangeManager {
* @param string $env
* @param int $maxMapsReturned
* @param int $searchOrder
+ *
+ * @deprecated
+ * @see \ManiaControl\ManiaExchange\ManiaExchangeMapSearch
*/
- public function fetchMapsAsync(callable $function, $name = '', $author = '', $env = '', $maxMapsReturned = 100, $searchOrder = self::SEARCH_ORDER_UPDATED_NEWEST) {
- // TODO: remove $env because it's not really used?
+ public function fetchMapsAsync(callable $function, $name = '', $author = '', $env = '', $maxMapsReturned = 100, $sortOrder = ManiaExchangeMapSearch::SEARCH_ORDER_UPDATED_NEWEST) {
+ $mapSearch = new ManiaExchangeMapSearch($this->maniaControl);
+ $mapSearch->setMapName($name);
+ $mapSearch->setAuthorName($author);
+ $mapSearch->setMapLimit($maxMapsReturned);
+ $mapSearch->setPrioritySortOrder($sortOrder);
- // Get Title Id
- $titleId = $this->maniaControl->getServer()->titleId;
- $titlePrefix = $this->maniaControl->getMapManager()->getCurrentMap()->getGame();
-
- // compile search URL
- $url = 'http://' . $titlePrefix . '.mania-exchange.com/tracksearch2/search?api=on';
-
- $game = explode('@', $titleId);
- $envNumber = $this->getEnvironment($game[0]);
- if ($env || $envNumber > -1) {
- $url .= '&environments=' . $envNumber;
- }
- if ($name) {
- $url .= '&trackname=' . str_replace(" ", "%20", $name);
- }
- if ($author) {
- $url .= '&author=' . $author;
+ if($env){
+ $mapSearch->setEnvironments($env);
}
- $url .= '&priord=' . $searchOrder;
- $url .= '&limit=' . $maxMapsReturned;
-
- if ($titlePrefix !== "tm") {
- $url .= '&minexebuild=' . self::MIN_EXE_BUILD;
- }
-
- // Get MapTypes
- try {
- $scriptInfos = $this->maniaControl->getClient()->getModeScriptInfo();
- $mapTypes = $scriptInfos->compatibleMapTypes;
- $url .= '&mtype=' . $mapTypes;
- } catch (GameModeException $e) {
- }
-
- $this->maniaControl->getFileReader()->loadFile($url, function ($mapInfo, $error) use (&$function, $titlePrefix) {
- if ($error) {
- trigger_error($error);
- return;
- }
-
- $mxMapList = json_decode($mapInfo);
-
- if (!isset($mxMapList->results)) {
- trigger_error('Cannot decode searched JSON data');
- return;
- }
-
- $mxMapList = $mxMapList->results;
-
- if ($mxMapList === null) {
- trigger_error('Cannot decode searched JSON data');
- return;
- }
-
- $maps = array();
- foreach ($mxMapList as $map) {
- if (!empty($map)) {
- array_push($maps, new MXMapInfo($titlePrefix, $map));
- }
- }
-
- call_user_func($function, $maps);
- }, AsynchronousFileReader::CONTENT_TYPE_JSON);
+ $mapSearch->fetchMapsAsync($function);
}
/**
diff --git a/core/ManiaExchange/ManiaExchangeMapSearch.php b/core/ManiaExchange/ManiaExchangeMapSearch.php
new file mode 100644
index 00000000..2da8a713
--- /dev/null
+++ b/core/ManiaExchange/ManiaExchangeMapSearch.php
@@ -0,0 +1,437 @@
+
+ * @copyright 2014-2015 ManiaControl Team
+ * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
+ */
+class ManiaExchangeMapSearch {
+ //Search orders (prior parameter) http://api.mania-exchange.com/documents/enums#orderings
+ const SEARCH_ORDER_NONE = -1;
+ const SEARCH_ORDER_TRACK_NAME = 0;
+ const SEARCH_ORDER_AUTHOR = 1;
+ const SEARCH_ORDER_UPLOADED_NEWEST = 2;
+ const SEARCH_ORDER_UPLOADED_OLDEST = 3;
+ const SEARCH_ORDER_UPDATED_NEWEST = 4;
+ const SEARCH_ORDER_UPDATED_OLDEST = 5;
+ const SEARCH_ORDER_ACTIVITY_LATEST = 6;
+ const SEARCH_ORDER_ACTIVITY_OLDEST = 7;
+ const SEARCH_ORDER_AWARDS_MOST = 8;
+ const SEARCH_ORDER_AWARDS_LEAST = 9;
+ const SEARCH_ORDER_COMMENTS_MOST = 10;
+ const SEARCH_ORDER_COMMENTS_LEAST = 11;
+ const SEARCH_ORDER_DIFFICULTY_EASIEST = 12;
+ const SEARCH_ORDER_DIFFICULTY_HARDEST = 13;
+ const SEARCH_ORDER_LENGTH_SHORTEST = 14;
+ const SEARCH_ORDER_LENGTH_LONGEST = 15;
+ const SEARCH_ORDER_TRACK_VALUE_LTH = 24;
+ const SEARCH_ORDER_TRACK_VALUE_HTL = 25;
+ const SEARCH_ORDER_ONLINE_RATING_LTH = 26;
+ const SEARCH_ORDER_ONLINE_RATING_HTL = 27;
+
+ //Special Search Orders (mode parameter): http://api.mania-exchange.com/documents/enums#modes
+ const SEARCH_ORDER_SPECIAL_DEFAULT = 0;
+ const SEARCH_ORDER_SPECIAL_USER_TRACKS = 1;
+ const SEARCH_ORDER_SPECIAL_LATEST_TRACKS = 2;
+ const SEARCH_ORDER_SPECIAL_RECENTLY_AWARDED = 3;
+ const SEARCH_ORDER_SPECIAL_BEST_OF_WEEK_AWARDS = 4;
+ const SEARCH_ORDER_SPECIAL_BEST_OF_MONTH_AWARDS = 5;
+ const SEARCH_ORDER_SPECIAL_MX_SUPPORTER_TRACKS = 10;
+ const SEARCH_ORDER_SPECIAL_DUO_ACCOUNT_TRACKS = 11;
+ const SEARCH_ORDER_SPECIAL_MOST_COMPETITIVE_WEEK = 19;
+ const SEARCH_ORDER_SPECIAL_MOST_COMPETITIVE_MONTH = 20;
+ const SEARCH_ORDER_SPECIAL_BEST_ONLINE_RATING_WEEK = 21;
+ const SEARCH_ORDER_SPECIAL_BEST_ONLINE_RATING_MONTH = 22;
+
+ //Private Properties
+ private $url = "";
+ private $titlePrefix = "";
+
+ private $mode = null;
+ private $mapName = null;
+ private $authorName = null;
+ private $mod = null;
+ private $authorId = null;
+ private $maniaScriptType = null;
+ private $titlePack = null;
+ private $replayType = null;
+ private $style = null;
+ private $length = null;
+ private $lengthOperator = null;
+ private $priorityOrder = null;
+ private $secondaryOrder = null;
+ private $environments = null;
+ private $vehicles = null;
+ private $page = null;
+ private $mapLimit = null;
+ private $unreleased = null;
+ private $mapGroup = null;
+ private $commentsMinLength = null;
+ private $customScreenshot = null;
+ private $minExeBuild = null;
+ private $envMix = null;
+ private $ghostBlocks = null;
+ private $embeddedObjects = null;
+
+ /** @var ManiaControl $maniaControl */
+ private $maniaControl = null;
+
+ //TODO use class by mxlist
+
+ /**
+ * Construct map manager
+ *
+ * @param \ManiaControl\ManiaControl $maniaControl
+ */
+ public function __construct(ManiaControl $maniaControl) {
+ $this->maniaControl = $maniaControl;
+
+
+ $titleId = $this->maniaControl->getServer()->titleId;
+ $this->titlePrefix = $this->maniaControl->getMapManager()->getCurrentMap()->getGame();
+
+ $this->url = 'https://' . $this->titlePrefix . '.mania-exchange.com/tracksearch2/search?api=on';
+
+ //Set some defaults:
+ $this->mapLimit = 100;
+ $this->priorityOrder = self::SEARCH_ORDER_UPDATED_NEWEST;
+
+ //Set Min Exe Build Default for games which are not Trackmania
+ if ($this->titlePrefix !== "tm") {
+ $this->minExeBuild = ManiaExchangeManager::MIN_EXE_BUILD;
+ }
+
+ //Set MapTypes
+ try {
+ $scriptInfos = $this->maniaControl->getClient()->getModeScriptInfo();
+ $mapTypes = $scriptInfos->compatibleMapTypes;
+ $this->maniaScriptType = $mapTypes;
+ } catch (GameModeException $e) {
+ }
+
+ //Set Environments on Trackmania
+ $game = explode('@', $titleId);
+ $envNumber = $this->getEnvironment($game[0]); //TODO enviroment as constant
+ if ($envNumber > -1) {
+ $this->environments = $envNumber;
+ }
+ }
+
+ /**
+ * Fetch a MapList Asynchronously
+ *
+ * @param callable $function
+ */
+ public function fetchMapsAsync(callable $function) {
+ // compile search URL
+ $parameters = "";
+
+ if ($this->mode) {
+ $parameters .= "&mode=" . $this->mode;
+ }
+ if ($this->mapName) {
+ $parameters .= "&trackname=" . urlencode($this->mapName);
+ }
+ if ($this->authorName) {
+ $parameters .= "&author=" . urlencode($this->authorName);
+ }
+ if ($this->mod) {
+ $parameters .= "&mod=" . urlencode($this->mod);
+ }
+ if ($this->authorId) {
+ $parameters .= "&authorid= " . $this->authorId;
+ }
+ if ($this->maniaScriptType) {
+ $parameters .= "&mtype=" . urlencode($this->maniaScriptType);
+ }
+ if ($this->titlePack) {
+ $parameters .= "&tpack=" . urlencode($this->titlePack);
+ }
+ if ($this->replayType) {
+ $parameters .= "&rytpe=" . $this->replayType;
+ }
+ if ($this->style) {
+ $parameters .= "&style=" . $this->style;
+ }
+ if ($this->length) {
+ $parameters .= "&length=" . $this->length;
+ }
+ if ($this->lengthOperator) {
+ $parameters .= "&lengthop=" . $this->lengthOperator;
+ }
+ if ($this->priorityOrder) {
+ $parameters .= "&priord=" . $this->priorityOrder;
+ }
+ if ($this->secondaryOrder) {
+ $parameters .= "&secord=" . $this->secondaryOrder;
+ }
+ if ($this->environments) {
+ $parameters .= "&environemtns=" . $this->environments;
+ }
+ if ($this->vehicles) {
+ $parameters .= "&vehicles=" . $this->vehicles;
+ }
+ if ($this->page) {
+ $parameters .= "&page=" . $this->page;
+ }
+ if ($this->mapLimit) {
+ $parameters .= "&limit=" . $this->mapLimit;
+ }
+ if (isset($this->unreleased)) {
+ $parameters .= "&unreleased=" . (int) $this->unreleased;
+ }
+ if ($this->mapGroup) {
+ $parameters .= "&mapgroup=" . $this->mapGroup;
+ }
+ if ($this->commentsMinLength) {
+ $parameters .= "&commentsminlength=" . $this->commentsMinLength;
+ }
+ if (isset($this->customScreenshot)) {
+ $parameters .= "&customscreenshot=" . $this->customScreenshot;
+ }
+ if ($this->minExeBuild) {
+ $parameters .= "&minexebuild=" . urlencode($this->minExeBuild);
+ }
+ if (isset($this->envMix)) {
+ $parameters .= "&envmix=" . (int) $this->envMix;
+ }
+ if (isset($this->ghostBlocks)) {
+ $parameters .= "&ghostblocks=" . (int) $this->ghostBlocks;
+ }
+ if (isset($this->embeddedObjects)) {
+ $parameters .= "&embeddedobjects=" . (int) $this->embeddedObjects;
+ }
+
+ $this->maniaControl->getFileReader()->loadFile($this->url . $parameters, function ($mapInfo, $error) use (&$function) {
+ if ($error) {
+ trigger_error($error);
+ return;
+ }
+
+ $mxMapList = json_decode($mapInfo);
+
+ if (!isset($mxMapList->results)) {
+ trigger_error('Cannot decode searched JSON data');
+ return;
+ }
+
+ $mxMapList = $mxMapList->results;
+
+ if ($mxMapList === null) {
+ trigger_error('Cannot decode searched JSON data');
+ return;
+ }
+
+ $maps = array();
+ foreach ($mxMapList as $map) {
+ if (!empty($map)) {
+ array_push($maps, new MXMapInfo($this->titlePrefix, $map));
+ }
+ }
+
+ call_user_func($function, $maps);
+ }, AsynchronousFileReader::CONTENT_TYPE_JSON);
+ }
+
+
+ /**
+ * Get the Current Environment by String
+ *
+ * @param string $env
+ * @return int
+ */
+ private function getEnvironment($env) {
+ switch ($env) {
+ case 'TMCanyon':
+ return 1;
+ case 'TMStadium':
+ return 2;
+ case 'TMValley':
+ return 3;
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * @param int $mode
+ */
+ public function setMode($mode) {
+ $this->mode = $mode;
+ }
+
+ /**
+ * @param string $mapName
+ */
+ public function setMapName($mapName) {
+ $this->mapName = $mapName;
+ }
+
+ /**
+ * @param string $authorName
+ */
+ public function setAuthorName($authorName) {
+ $this->authorName = $authorName;
+ }
+
+ /**
+ * @param int $authorId
+ */
+ public function setAuthorId($authorId) {
+ $this->authorId = $authorId;
+ }
+
+ /**
+ * @param string $maniaScriptType
+ */
+ public function setManiaScriptType($maniaScriptType) {
+ $this->maniaScriptType = $maniaScriptType;
+ }
+
+ /**
+ * @param string $mod
+ */
+ public function setMod($mod) {
+ $this->mod = $mod;
+ }
+
+ /**
+ * @param string $titlePack
+ */
+ public function setTitlePack($titlePack) {
+ $this->titlePack = $titlePack;
+ }
+
+ /**
+ * @param int $replayType
+ */
+ public function setReplayType($replayType) {
+ $this->replayType = $replayType;
+ }
+
+ /**
+ * @param int $length
+ */
+ public function setLength($length) {
+ $this->length = $length;
+ }
+
+ /**
+ * @param int $style
+ */
+ public function setStyle($style) {
+ $this->style = $style;
+ }
+
+ /**
+ * @param int $lengthOperator
+ */
+ public function setLengthOperator($lengthOperator) {
+ $this->lengthOperator = $lengthOperator;
+ }
+
+ /**
+ * @param int $secondaryOrder
+ */
+ public function setSecondarySortOrder($secondaryOrder) {
+ $this->secondaryOrder = $secondaryOrder;
+ }
+
+ /**
+ * @param int $priorityOrder
+ */
+ public function setPrioritySortOrder($priorityOrder) {
+ $this->priorityOrder = $priorityOrder;
+ }
+
+ /**
+ * @param string $environments
+ */
+ public function setEnvironments($environments) {
+ $this->environments = $environments;
+ }
+
+ /**
+ * @param int $page
+ */
+ public function setPage($page) {
+ $this->page = $page;
+ }
+
+ /**
+ * @param string $vehicles
+ */
+ public function setVehicles($vehicles) {
+ $this->vehicles = $vehicles;
+ }
+
+ /**
+ * @param bool $unreleased
+ */
+ public function setUnreleased($unreleased) {
+ $this->unreleased = $unreleased;
+ }
+
+ /**
+ * @param int $mapGroup
+ */
+ public function setMapGroup($mapGroup) {
+ $this->mapGroup = $mapGroup;
+ }
+
+ /**
+ * @param int $commentsMinLength
+ */
+ public function setCommentsMinLength($commentsMinLength) {
+ $this->commentsMinLength = $commentsMinLength;
+ }
+
+ /**
+ * @param bool $customScreenshot
+ */
+ public function setCustomScreenshot($customScreenshot) {
+ $this->customScreenshot = $customScreenshot;
+ }
+
+ /**
+ * @param bool $envMix
+ */
+ public function setEnvMix($envMix) {
+ $this->envMix = $envMix;
+ }
+
+ /**
+ * @param string $minExeBuild
+ */
+ public function setMinExeBuild($minExeBuild) {
+ $this->minExeBuild = $minExeBuild;
+ }
+
+ /**
+ * @param bool $ghostBlocks
+ */
+ public function setGhostBlocks($ghostBlocks) {
+ $this->ghostBlocks = $ghostBlocks;
+ }
+
+ /**
+ * @param bool $embeddedObjects
+ */
+ public function setEmbeddedObjects($embeddedObjects) {
+ $this->embeddedObjects = $embeddedObjects;
+ }
+
+ /**
+ * @param null $mapLimit
+ */
+ public function setMapLimit($mapLimit) {
+ $this->mapLimit = $mapLimit;
+ }
+}
\ No newline at end of file
diff --git a/core/Manialinks/CustomUIManager.php b/core/Manialinks/CustomUIManager.php
index 7ee5059b..8eb6a561 100644
--- a/core/Manialinks/CustomUIManager.php
+++ b/core/Manialinks/CustomUIManager.php
@@ -10,7 +10,7 @@ use ManiaControl\Players\Player;
use ManiaControl\Players\PlayerManager;
/**
- * Class managing the Custom UI in TrackMania
+ * Class managing the Custom UI in ManiaPlanet
*
* @author ManiaControl Team
* @copyright 2014-2015 ManiaControl Team
@@ -28,7 +28,7 @@ class CustomUIManager implements CallbackListener, TimerListener {
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
/** @var customUI $customUI */
- private $customUI = null;
+ private $customUI = null;
private $updateManialink = false;
/**
diff --git a/core/Manialinks/ManialinkManager.php b/core/Manialinks/ManialinkManager.php
index 6dd978ed..1261d6cd 100644
--- a/core/Manialinks/ManialinkManager.php
+++ b/core/Manialinks/ManialinkManager.php
@@ -262,7 +262,7 @@ class ManialinkManager implements ManialinkPageAnswerListener, CallbackListener
}
} catch (UnknownPlayerException $e) {
return false;
- } catch (FaultException $e){
+ } catch (FaultException $e) {
//TODO added 17.01.2015, remove later:
$this->maniaControl->getErrorHandler()->triggerDebugNotice("Fault Exception: ManiaLink Manager, Message: " . $e->getMessage());
return false;
@@ -349,8 +349,11 @@ class ManialinkManager implements ManialinkPageAnswerListener, CallbackListener
return $success;
}
+
/**
* Adds a line of labels
+ * LabelLine should be an array with the following structure: array(array(positions), array(texts))
+ * or array($text1 => $pos1, $text2 => $pos2 ...)
*
* @param Frame $frame
* @param array $labelStrings
@@ -366,21 +369,49 @@ class ManialinkManager implements ManialinkPageAnswerListener, CallbackListener
$profile = (isset($properties['profile']) ? $properties['profile'] : false);
$labels = array();
- foreach ($labelStrings as $text => $x) {
- $label = new Label_Text();
- $frame->add($label);
- $label->setHAlign($hAlign);
- $label->setX($x);
- $label->setStyle($style);
- $label->setTextSize($textSize);
- $label->setText($text);
- $label->setTextColor($textColor);
- if ($profile) {
- $label->addPlayerProfileFeature($profile);
+ //If you call LabelLine with array(array(positions), array(texts))
+ if (count($labelStrings) == 2 && array_key_exists(0, $labelStrings) && array_key_exists(1, $labelStrings) && array_key_exists(0, $labelStrings[0]) && array_key_exists(0, $labelStrings[1])) {
+ $positions = $labelStrings[0];
+ $texts = $labelStrings[1];
+
+ if (count($positions) != count($texts)) {
+ trigger_error("LabelLine Position length is not equal to Text Length", E_USER_ERROR);
}
- array_push($labels, $label);
+ foreach ($positions as $key => $x) {
+ $label = new Label_Text();
+ $frame->add($label);
+ $label->setHAlign($hAlign);
+ $label->setX($x);
+ $label->setStyle($style);
+ $label->setTextSize($textSize);
+ $label->setText($texts[$key]);
+ $label->setTextColor($textColor);
+
+ if ($profile) {
+ $label->addPlayerProfileFeature($profile);
+ }
+
+ array_push($labels, $label);
+ }
+ } else {
+ foreach ($labelStrings as $text => $x) {
+ $label = new Label_Text();
+ $frame->add($label);
+ $label->setHAlign($hAlign);
+ $label->setX($x);
+ $label->setStyle($style);
+ $label->setTextSize($textSize);
+ $label->setText($text);
+ $label->setTextColor($textColor);
+
+ if ($profile) {
+ $label->addPlayerProfileFeature($profile);
+ }
+
+ array_push($labels, $label);
+ }
}
return $labels;
diff --git a/core/Manialinks/StyleManager.php b/core/Manialinks/StyleManager.php
index 74144cd9..999f730b 100644
--- a/core/Manialinks/StyleManager.php
+++ b/core/Manialinks/StyleManager.php
@@ -161,6 +161,13 @@ class StyleManager {
$frame = new Frame();
$frame->setSize($width, $height)->setZ(45); //TODO place before scoreboards
+ //TODO remove: (just temporary fix for tm bug)
+ if ($this->maniaControl->getMapManager()->getCurrentMap()->getGame() === 'tm'
+ ) {
+ $frame->setSize($width, $height)->setZ(32);
+ }
+
+
// Background Quad
$backgroundQuad = new Quad();
$frame->add($backgroundQuad);
diff --git a/core/Maps/MapActions.php b/core/Maps/MapActions.php
index 5807121e..0eac0749 100644
--- a/core/Maps/MapActions.php
+++ b/core/Maps/MapActions.php
@@ -2,6 +2,9 @@
namespace ManiaControl\Maps;
+use ManiaControl\Communication\CommunicationAnswer;
+use ManiaControl\Communication\CommunicationListener;
+use ManiaControl\Communication\CommunicationMethods;
use ManiaControl\ManiaControl;
use Maniaplanet\DedicatedServer\Xmlrpc\ChangeInProgressException;
@@ -12,7 +15,7 @@ use Maniaplanet\DedicatedServer\Xmlrpc\ChangeInProgressException;
* @copyright 2014-2015 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
-class MapActions {
+class MapActions implements CommunicationListener {
/*
* Private properties
*/
@@ -26,12 +29,79 @@ class MapActions {
*/
public function __construct(ManiaControl $maniaControl) {
$this->maniaControl = $maniaControl;
+
+ //Communication Listenings
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::SKIP_MAP, $this, function ($data) {
+ $success = $this->skipMap();
+ return new CommunicationAnswer(array("success" => $success));
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::RESTART_MAP, $this, function ($data) {
+ $success = $this->restartMap();
+ return new CommunicationAnswer(array("success" => $success));
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::SKIP_TO_MAP, $this, function ($data) {
+ if (!is_object($data)) {
+ return new CommunicationAnswer("Error in provided Data", true);
+ }
+
+ if (property_exists($data, "mxId")) {
+ $success = $this->skipToMapByMxId($data->mxId);
+ } else if (property_exists($data, "mapUid")) {
+ $success = $this->skipToMapByUid($data->mapUid);
+ } else {
+ return new CommunicationAnswer("No mxId or mapUid provided.", true);
+ }
+
+ return new CommunicationAnswer(array("success" => $success));
+ });
+ }
+
+ /**
+ * Skips to a Map by its given UID
+ *
+ * @param String $uid
+ * @return bool
+ */
+ public function skipToMapByUid($uid) {
+ //TODO message
+ //Check if Map exists
+ $map = $this->maniaControl->getMapManager()->getMapByUid($uid);
+ if (!$map) {
+ return false;
+ }
+
+ try {
+ $this->maniaControl->getClient()->jumpToMapIdent($uid);
+ } catch (ChangeInProgressException $e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Skips to a Map by its given MxId
+ *
+ * @param int $mxId
+ * @return bool
+ */
+ public function skipToMapByMxId($mxId) {
+ $map = $this->maniaControl->getMapManager()->getMapByMxId($mxId);
+ if (!$map) {
+ return false;
+ }
+ return $this->skipToMapByUid($map->uid);
}
/**
* Skip the current Map
+ *
+ * @return bool
*/
public function skipMap() {
+ //TODO message
+
// Force an EndMap on the MapQueue to set the next Map
$this->maniaControl->getMapManager()->getMapQueue()->endMap(null);
@@ -42,6 +112,27 @@ class MapActions {
try {
$this->maniaControl->getClient()->nextMap();
} catch (ChangeInProgressException $e) {
+ return false;
}
+
+ return true;
+ }
+
+ /**
+ * Restarts the Current Map
+ *
+ * @return bool
+ */
+ public function restartMap() {
+ //TODO message
+
+ //Restarts the Current Map
+ try {
+ $this->maniaControl->getClient()->restartMap();
+ } catch (ChangeInProgressException $e) {
+ return false;
+ }
+
+ return true;
}
}
diff --git a/core/Maps/MapCommands.php b/core/Maps/MapCommands.php
index c2a04c61..59767037 100644
--- a/core/Maps/MapCommands.php
+++ b/core/Maps/MapCommands.php
@@ -436,8 +436,18 @@ class MapCommands implements CommandListener, ManialinkPageAnswerListener, Callb
*/
private function showMapListKarma($best, Player $player) {
/** @var \MCTeam\KarmaPlugin $karmaPlugin */
- $karmaPlugin = $this->maniaControl->getPluginManager()->getPlugin(MapList::DEFAULT_KARMA_PLUGIN);
+ $karmaPlugin = $this->maniaControl->getPluginManager()->getPlugin(MapList::DEFAULT_KARMA_PLUGIN);
+ $displayMxKarma = $this->maniaControl->getSettingManager()->getSettingValue($karmaPlugin, $karmaPlugin::SETTING_WIDGET_DISPLAY_MX);
+
if ($karmaPlugin) {
+ //Sort by Mx Karma in Maplist
+ if ($displayMxKarma) { //TODO
+
+ //Sort by Local Karma in Maplist
+ } else {
+
+ }
+
$maps = $this->maniaControl->getMapManager()->getMaps();
$mapList = array();
foreach ($maps as $map) {
diff --git a/core/Maps/MapList.php b/core/Maps/MapList.php
index 022cd1bb..728d74f5 100644
--- a/core/Maps/MapList.php
+++ b/core/Maps/MapList.php
@@ -122,7 +122,7 @@ class MapList implements ManialinkPageAnswerListener, CallbackListener {
$height = $this->maniaControl->getManialinkManager()->getStyleManager()->getListWidgetsHeight();
if ($pageIndex < 0) {
- $pageIndex = (int)$player->getCache($this, self::CACHE_CURRENT_PAGE);
+ $pageIndex = (int) $player->getCache($this, self::CACHE_CURRENT_PAGE);
}
$player->setCache($this, self::CACHE_CURRENT_PAGE, $pageIndex);
$queueBuffer = $this->maniaControl->getMapManager()->getMapQueue()->getQueueBuffer();
@@ -441,9 +441,20 @@ class MapList implements ManialinkPageAnswerListener, CallbackListener {
// Display Karma bar
if ($karmaPlugin) {
- $karma = $karmaPlugin->getMapKarma($map);
- $votes = $karmaPlugin->getMapVotes($map);
- if (is_numeric($karma)) {
+ $displayMxKarma = $this->maniaControl->getSettingManager()->getSettingValue($karmaPlugin, $karmaPlugin::SETTING_WIDGET_DISPLAY_MX);
+
+ //Display Mx Karma
+ if ($displayMxKarma && $map->mx) {
+ $karma = $map->mx->ratingVoteAverage / 100;
+ $votes = array("count" => $map->mx->ratingVoteCount);
+
+ //Display Local Karma
+ } else {
+ $karma = $karmaPlugin->getMapKarma($map);
+ $votes = $karmaPlugin->getMapVotes($map);
+ }
+
+ if (is_numeric($karma) && $votes['count'] > 0) {
if ($this->maniaControl->getSettingManager()->getSettingValue($karmaPlugin, $karmaPlugin::SETTING_NEWKARMA)
) {
$karmaText = ' ' . round($karma * 100.) . '% (' . $votes['count'] . ')';
@@ -692,7 +703,7 @@ class MapList implements ManialinkPageAnswerListener, CallbackListener {
default:
if (substr($actionId, 0, strlen(self::ACTION_PAGING_CHUNKS)) === self::ACTION_PAGING_CHUNKS) {
// Paging chunks
- $neededPage = (int)substr($actionId, strlen(self::ACTION_PAGING_CHUNKS));
+ $neededPage = (int) substr($actionId, strlen(self::ACTION_PAGING_CHUNKS));
$this->showMapList($player, null, $neededPage - 1);
}
break;
diff --git a/core/Maps/MapManager.php b/core/Maps/MapManager.php
index 58c1173f..293e66f6 100644
--- a/core/Maps/MapManager.php
+++ b/core/Maps/MapManager.php
@@ -6,6 +6,9 @@ use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Callbacks\Callbacks;
+use ManiaControl\Communication\CommunicationAnswer;
+use ManiaControl\Communication\CommunicationListener;
+use ManiaControl\Communication\CommunicationMethods;
use ManiaControl\Files\FileUtil;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
@@ -30,7 +33,7 @@ use Maniaplanet\DedicatedServer\Xmlrpc\UnavailableFeatureException;
* @copyright 2014-2015 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
-class MapManager implements CallbackListener {
+class MapManager implements CallbackListener, CommunicationListener {
/*
* Constants
*/
@@ -135,34 +138,9 @@ class MapManager implements CallbackListener {
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_AUTOSAVE_MAPLIST, true);
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_MAPLIST_FILE, "MatchSettings/tracklist.txt");
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_WRITE_OWN_MAPLIST_FILE, false);
- }
- /**
- * Initialize necessary database tables
- *
- * @return bool
- */
- private function initTables() {
- $mysqli = $this->maniaControl->getDatabase()->getMysqli();
- $query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_MAPS . "` (
- `index` int(11) NOT NULL AUTO_INCREMENT,
- `mxid` int(11),
- `uid` varchar(50) NOT NULL,
- `name` varchar(150) NOT NULL,
- `authorLogin` varchar(100) NOT NULL,
- `fileName` varchar(100) NOT NULL,
- `environment` varchar(50) NOT NULL,
- `mapType` varchar(50) NOT NULL,
- `changed` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (`index`),
- UNIQUE KEY `uid` (`uid`)
- ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Map Data' AUTO_INCREMENT=1;";
- $result = $mysqli->query($query);
- if ($mysqli->error) {
- trigger_error($mysqli->error, E_USER_ERROR);
- return false;
- }
- return $result;
+ //Initlaize Communication Listenings
+ $this->initalizeCommunicationListenings();
}
/**
@@ -213,14 +191,16 @@ class MapManager implements CallbackListener {
/**
* Update a Map from Mania Exchange
*
- * @param Player $admin
- * @param string $uid
+ * @param Player|null $admin
+ * @param string $uid
*/
- public function updateMap(Player $admin, $uid) {
+ public function updateMap($admin, $uid) {
$this->updateMapTimestamp($uid);
if (!isset($uid) || !isset($this->maps[$uid])) {
- $this->maniaControl->getChat()->sendError("Error updating Map: Unknown UID '{$uid}'!", $admin);
+ if ($admin) {
+ $this->maniaControl->getChat()->sendError("Error updating Map: Unknown UID '{$uid}'!", $admin);
+ }
return;
}
@@ -229,7 +209,11 @@ class MapManager implements CallbackListener {
$mxId = $map->mx->id;
$this->removeMap($admin, $uid, true, false);
- $this->addMapFromMx($mxId, $admin->login, true);
+ if ($admin) {
+ $this->addMapFromMx($mxId, $admin->login, true);
+ } else {
+ $this->addMapFromMx($mxId, null, true);
+ }
}
/**
@@ -263,15 +247,18 @@ class MapManager implements CallbackListener {
/**
* Remove a Map
*
- * @param Player $admin
- * @param string $uid
- * @param bool $eraseFile
- * @param bool $message
+ * @param Player|null $admin
+ * @param string $uid
+ * @param bool $eraseFile
+ * @param bool $message
+ * @return bool
*/
- public function removeMap(Player $admin, $uid, $eraseFile = false, $message = true) {
+ public function removeMap($admin, $uid, $eraseFile = false, $message = true) {
if (!isset($this->maps[$uid])) {
- $this->maniaControl->getChat()->sendError('Map does not exist!', $admin);
- return;
+ if ($admin) {
+ $this->maniaControl->getChat()->sendError('Map does not exist!', $admin);
+ }
+ return false;
}
/** @var Map $map */
@@ -296,26 +283,35 @@ class MapManager implements CallbackListener {
if ($eraseFile) {
// Check if ManiaControl can even write to the maps dir
$mapDir = $this->maniaControl->getClient()->getMapsDirectory();
- if ($this->maniaControl->getServer()->checkAccess($mapDir)
- ) {
+ if ($this->maniaControl->getServer()->checkAccess($mapDir)) {
// Delete map file
if (!@unlink($mapDir . $map->fileName)) {
- $this->maniaControl->getChat()->sendError("Couldn't erase the map file.", $admin);
+ if ($admin) {
+ $this->maniaControl->getChat()->sendError("Couldn't erase the map file.", $admin);
+ }
$eraseFile = false;
}
} else {
- $this->maniaControl->getChat()->sendError("Couldn't erase the map file (no access).", $admin);
+ if ($admin) {
+ $this->maniaControl->getChat()->sendError("Couldn't erase the map file (no access).", $admin);
+ }
$eraseFile = false;
}
}
// Show Message
if ($message) {
- $action = ($eraseFile ? 'erased' : 'removed');
- $message = $admin->getEscapedNickname() . ' ' . $action . ' ' . $map->getEscapedName() . '!';
+ $action = ($eraseFile ? 'erased' : 'removed');
+ if ($admin) {
+ $message = $admin->getEscapedNickname() . ' ' . $action . ' ' . $map->getEscapedName() . '!';
+ } else {
+ $message = $map->getEscapedName() . ' got ' . $action . '!';
+ }
$this->maniaControl->getChat()->sendSuccess($message);
Logger::logInfo($message, true);
}
+
+ return true;
}
/**
@@ -342,6 +338,7 @@ class MapManager implements CallbackListener {
* @param int $mapId
* @param string $login
* @param bool $update
+ * @param bool $displayMessage
*/
public function addMapFromMx($mapId, $login, $update = false) {
if (is_numeric($mapId)) {
@@ -350,8 +347,10 @@ class MapManager implements CallbackListener {
&$login, &$update
) {
if (!$mapInfo || !isset($mapInfo->uploaded)) {
- // Invalid id
- $this->maniaControl->getChat()->sendError('Invalid MX-Id!', $login);
+ if ($login) {
+ // Invalid id
+ $this->maniaControl->getChat()->sendError('Invalid MX-Id!', $login);
+ }
return;
}
@@ -360,14 +359,17 @@ class MapManager implements CallbackListener {
&$login, &$mapInfo, &$update
) {
if (!$file || $error) {
- // Download error
- $this->maniaControl->getChat()->sendError("Download failed: '{$error}'!", $login);
+ if ($login) {
+ // Download error
+ $this->maniaControl->getChat()->sendError("Download failed: '{$error}'!", $login);
+ }
return;
}
$this->processMapFile($file, $mapInfo, $login, $update);
- });
+ }, 'UTF-8', 0, array("X-ManiaPlanet-ServerLogin: " . $this->maniaControl->getServer()->login));
});
}
+ return;
}
/**
@@ -459,19 +461,21 @@ class MapManager implements CallbackListener {
}
$map->lastUpdate = time();
- $player = $this->maniaControl->getPlayerManager()->getPlayer($login);
-
- if (!$update) {
- // Message
- $message = $player->getEscapedNickname() . ' added $<' . $mapInfo->name . '$>!';
- $this->maniaControl->getChat()->sendSuccess($message);
- Logger::logInfo($message, true);
- // Queue requested Map
- $this->maniaControl->getMapManager()->getMapQueue()->addMapToMapQueue($login, $mapInfo->uid);
- } else {
- $message = $player->getEscapedNickname() . ' updated $<' . $mapInfo->name . '$>!';
- $this->maniaControl->getChat()->sendSuccess($message);
- Logger::logInfo($message, true);
+ //TODO messages for communication
+ if ($login) {
+ $player = $this->maniaControl->getPlayerManager()->getPlayer($login);
+ if (!$update) {
+ // Message
+ $message = $player->getEscapedNickname() . ' added $<' . $mapInfo->name . '$>!';
+ $this->maniaControl->getChat()->sendSuccess($message);
+ Logger::logInfo($message, true);
+ // Queue requested Map
+ $this->maniaControl->getMapManager()->getMapQueue()->addMapToMapQueue($login, $mapInfo->uid);
+ } else {
+ $message = $player->getEscapedNickname() . ' updated $<' . $mapInfo->name . '$>!';
+ $this->maniaControl->getChat()->sendSuccess($message);
+ Logger::logInfo($message, true);
+ }
}
}
@@ -522,10 +526,8 @@ class MapManager implements CallbackListener {
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_MAPS_UPDATED);
// Write MapList
- if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_AUTOSAVE_MAPLIST)
- ) {
- if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_WRITE_OWN_MAPLIST_FILE)
- ) {
+ if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_AUTOSAVE_MAPLIST)) {
+ if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_WRITE_OWN_MAPLIST_FILE)) {
$serverLogin = $this->maniaControl->getServer()->login;
$matchSettingsFileName = "MatchSettings/{$serverLogin}.txt";
} else {
@@ -884,4 +886,102 @@ class MapManager implements CallbackListener {
public function getMapsCount() {
return count($this->maps);
}
+
+ /**
+ * Initializes the Communication Listenings
+ */
+ private function initalizeCommunicationListenings() {
+ // Communication Listenings
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::GET_CURRENT_MAP, $this, function ($data) {
+ return new CommunicationAnswer($this->getCurrentMap());
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::GET_MAP_LIST, $this, function ($data) {
+ return new CommunicationAnswer($this->getMaps());
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::GET_MAP, $this, function ($data) {
+ if (!is_object($data)) {
+ return new CommunicationAnswer("Error in provided Data", true);
+ }
+
+ if (property_exists($data, "mxId")) {
+ return new CommunicationAnswer($this->getMapByMxId($data->mxId));
+ } else if (property_exists($data, "mapUid")) {
+ return new CommunicationAnswer($this->getMapByUid($data->mapUid));
+ } else {
+ return new CommunicationAnswer("No mxId or mapUid provided.", true);
+ }
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::ADD_MAP, $this, function ($data) {
+ if (!is_object($data) || !property_exists($data, "mxId")) {
+ return new CommunicationAnswer("No valid mxId provided.", true);
+ }
+
+ $this->addMapFromMx($data->mxId, null);
+
+ return new CommunicationAnswer();
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::REMOVE_MAP, $this, function ($data) {
+ if (!is_object($data) || !property_exists($data, "mapUid")) {
+ return new CommunicationAnswer("No valid mapUid provided.", true);
+ }
+
+ if (!$this->getMapByUid($data->mapUid)) {
+ return new CommunicationAnswer("Map not found.", true);
+ }
+
+ $erase = false;
+ if (property_exists($data, "eraseMapFile")) {
+ $erase = $data->eraseMapFile;
+ }
+ $showMessage = true;
+ if (property_exists($data, "showChatMessage")) {
+ $showMessage = $data->showChatMessage;
+ }
+
+ $success = $this->removeMap(null, $data->mapUid, $erase, $showMessage);
+ return new CommunicationAnswer(array("success" => $success));
+ });
+
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::UPDATE_MAP, $this, function ($data) {
+ if (!is_object($data) || !property_exists($data, "mapUid")) {
+ return new CommunicationAnswer("No valid mapUid provided.", true);
+ }
+
+ $this->updateMap(null, $data->mapUid);
+ return new CommunicationAnswer();
+ });
+ }
+
+ /**
+ * Initialize necessary database tables
+ *
+ * @return bool
+ */
+ private function initTables() {
+ $mysqli = $this->maniaControl->getDatabase()->getMysqli();
+ $query = "CREATE TABLE IF NOT EXISTS `" . self::TABLE_MAPS . "` (
+ `index` int(11) NOT NULL AUTO_INCREMENT,
+ `mxid` int(11),
+ `uid` varchar(50) NOT NULL,
+ `name` varchar(150) NOT NULL,
+ `authorLogin` varchar(100) NOT NULL,
+ `fileName` varchar(100) NOT NULL,
+ `environment` varchar(50) NOT NULL,
+ `mapType` varchar(50) NOT NULL,
+ `changed` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`index`),
+ UNIQUE KEY `uid` (`uid`)
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Map Data' AUTO_INCREMENT=1;";
+ $result = $mysqli->query($query);
+ if ($mysqli->error) {
+ trigger_error($mysqli->error, E_USER_ERROR);
+ return false;
+ }
+ return $result;
+ }
}
diff --git a/core/Maps/MapQueue.php b/core/Maps/MapQueue.php
index 2386f41b..210a6287 100644
--- a/core/Maps/MapQueue.php
+++ b/core/Maps/MapQueue.php
@@ -42,10 +42,10 @@ class MapQueue implements CallbackListener, CommandListener {
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
- private $queuedMaps = array();
- private $nextMap = null;
- private $buffer = array();
- private $nextNoQueue = false;
+ private $queuedMaps = array();
+ private $nextMap = null;
+ private $buffer = array();
+ private $nextNoQueue = false;
/**
* Construct a new map queue instance
@@ -108,8 +108,7 @@ class MapQueue implements CallbackListener, CommandListener {
* @param Player $admin |null
*/
public function clearMapQueue(Player $admin = null) {
- if ($admin && !$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_CLEAR_MAPQUEUE)
- ) {
+ if ($admin && !$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_CLEAR_MAPQUEUE)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
return;
}
@@ -223,8 +222,8 @@ class MapQueue implements CallbackListener, CommandListener {
if (array_key_exists($map->uid, $this->queuedMaps)) {
unset($this->queuedMaps[$map->uid]);
}
-
array_unshift($this->queuedMaps, array($player, $map, true));
+ $this->maniaControl->callbackManager->triggerCallback(self::CB_MAPQUEUE_CHANGED, array('add', $map));
}
}
@@ -308,8 +307,7 @@ class MapQueue implements CallbackListener, CommandListener {
// Check if map is in the buffer
if (in_array($uid, $this->buffer)) {
$this->maniaControl->getChat()->sendError('That map has recently been played!', $login);
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CLEAR_MAPQUEUE)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CLEAR_MAPQUEUE)) {
return;
}
}
@@ -329,10 +327,10 @@ class MapQueue implements CallbackListener, CommandListener {
/**
* Remove a Map from the Map queue
*
- * @param Player $player
- * @param string $uid
+ * @param Player|null $player
+ * @param string $uid
*/
- public function removeFromMapQueue(Player $player, $uid) {
+ public function removeFromMapQueue($player, $uid) {
if (!isset($this->queuedMaps[$uid])) {
return;
}
@@ -340,7 +338,9 @@ class MapQueue implements CallbackListener, CommandListener {
$map = $this->queuedMaps[$uid][1];
unset($this->queuedMaps[$uid]);
- $this->maniaControl->getChat()->sendInformation('$fa0$<$fff' . $map->name . '$> is removed from the Map-Queue by $<$fff' . $player->nickname . '$>.');
+ if ($player) {
+ $this->maniaControl->getChat()->sendInformation('$fa0$<$fff' . $map->name . '$> is removed from the Map-Queue by $<$fff' . $player->nickname . '$>.');
+ }
// Trigger callback
$this->maniaControl->getCallbackManager()->triggerCallback(self::CB_MAPQUEUE_CHANGED, array('remove', $map));
@@ -359,8 +359,7 @@ class MapQueue implements CallbackListener, CommandListener {
}
$this->nextMap = null;
- if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SKIP_MAP_ON_LEAVE)
- ) {
+ if ($this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_SKIP_MAP_ON_LEAVE)) {
// Skip Map if requester has left
foreach ($this->queuedMaps as $queuedMap) {
$player = $queuedMap[0];
@@ -423,8 +422,7 @@ class MapQueue implements CallbackListener, CommandListener {
return;
}
- if (count($this->buffer) >= $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_BUFFERSIZE)
- ) {
+ if (count($this->buffer) >= $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_BUFFERSIZE)) {
array_shift($this->buffer);
}
diff --git a/core/Players/Player.php b/core/Players/Player.php
index a1fe52d9..374643a7 100644
--- a/core/Players/Player.php
+++ b/core/Players/Player.php
@@ -5,6 +5,7 @@ namespace ManiaControl\Players;
use ManiaControl\ManiaControl;
use ManiaControl\Utils\ClassUtil;
use ManiaControl\Utils\Formatter;
+use Maniaplanet\DedicatedServer\Structures\LadderStats;
/**
* Player Model Class
@@ -17,55 +18,56 @@ class Player {
/*
* Public Properties
*/
- public $index = -1;
- public $pid = -1;
- public $login = null;
- public $nickname = null;
+ public $index = -1;
+ public $pid = -1;
+ public $login = null;
+ public $nickname = null;
public $rawNickname = null;
- public $path = null;
- public $authLevel = 0;
- public $language = null;
- public $avatar = null;
- public $allies = array();
- public $clubLink = null;
- public $teamId = -1;
- public $isOfficial = null;
+ public $path = null;
+ public $authLevel = 0;
+ public $language = null;
+ public $avatar = null;
+ public $allies = array();
+ public $clubLink = null;
+ public $teamId = -1;
+ public $isOfficial = null;
public $ladderScore = -1.;
- public $ladderRank = -1;
- public $ladderStats = null;
- public $joinTime = -1;
- public $ipAddress = null;
- public $isConnected = true;
- public $clientVersion = null;
- public $downloadRate = -1;
- public $uploadRate = -1;
- public $skins = null;
+ public $ladderRank = -1;
+ /** @var LadderStats $ladderStats */
+ public $ladderStats = null;
+ public $joinTime = -1;
+ public $ipAddress = null;
+ public $isConnected = true;
+ public $clientVersion = null;
+ public $downloadRate = -1;
+ public $uploadRate = -1;
+ public $skins = null;
public $daysSinceZoneInscription = -1;
//Flags details
- public $forcedSpectatorState = 0;
- public $isReferee = false;
- public $isPodiumReady = false;
- public $isUsingStereoscopy = false;
+ public $forcedSpectatorState = 0;
+ public $isReferee = false;
+ public $isPodiumReady = false;
+ public $isUsingStereoscopy = false;
public $isManagedByAnOtherServer = false;
- public $isServer = false;
- public $hasPlayerSlot = false;
- public $isBroadcasting = false;
- public $hasJoinedGame = false;
+ public $isServer = false;
+ public $hasPlayerSlot = false;
+ public $isBroadcasting = false;
+ public $hasJoinedGame = false;
//SpectatorStatus details
- public $isSpectator = false;
+ public $isSpectator = false;
public $isTemporarySpectator = false;
- public $isPureSpectator = false;
- public $autoTarget = false;
- public $currentTargetId = 0;
+ public $isPureSpectator = false;
+ public $autoTarget = false;
+ public $currentTargetId = 0;
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
private $maniaControl = null;
- private $cache = array();
+ private $cache = array();
/**
* Construct a new Player
@@ -75,7 +77,7 @@ class Player {
*/
public function __construct(ManiaControl $maniaControl, $connected) {
$this->maniaControl = $maniaControl;
- $this->isConnected = (bool)$connected;
+ $this->isConnected = (bool) $connected;
if ($connected) {
$this->joinTime = time();
}
@@ -89,9 +91,9 @@ class Player {
*/
public static function parseLogin($player) {
if (is_object($player) && property_exists($player, 'login')) {
- return (string)$player->login;
+ return (string) $player->login;
}
- return (string)$player;
+ return (string) $player;
}
/**
@@ -235,14 +237,14 @@ class Player {
public function updatePlayerFlags($flags) {
//Detail flags
$this->forcedSpectatorState = $flags % 10; // 0, 1 or 2
- $this->isReferee = (bool)(intval($flags / 10) % 10);
- $this->isPodiumReady = (bool)(intval($flags / 100) % 10);
- $this->isUsingStereoscopy = (bool)(intval($flags / 1000) % 10);
- $this->isManagedByAnOtherServer = (bool)(intval($flags / 10000) % 10);
- $this->isServer = (bool)(intval($flags / 100000) % 10);
- $this->hasPlayerSlot = (bool)(intval($flags / 1000000) % 10);
- $this->isBroadcasting = (bool)(intval($flags / 10000000) % 10);
- $this->hasJoinedGame = (bool)(intval($flags / 100000000) % 10);
+ $this->isReferee = (bool) (intval($flags / 10) % 10);
+ $this->isPodiumReady = (bool) (intval($flags / 100) % 10);
+ $this->isUsingStereoscopy = (bool) (intval($flags / 1000) % 10);
+ $this->isManagedByAnOtherServer = (bool) (intval($flags / 10000) % 10);
+ $this->isServer = (bool) (intval($flags / 100000) % 10);
+ $this->hasPlayerSlot = (bool) (intval($flags / 1000000) % 10);
+ $this->isBroadcasting = (bool) (intval($flags / 10000000) % 10);
+ $this->hasJoinedGame = (bool) (intval($flags / 100000000) % 10);
}
/**
@@ -252,10 +254,10 @@ class Player {
*/
public function updateSpectatorStatus($spectatorStatus) {
//Details spectatorStatus
- $this->isSpectator = (bool)($spectatorStatus % 10);
- $this->isTemporarySpectator = (bool)(intval($spectatorStatus / 10) % 10);
- $this->isPureSpectator = (bool)(intval($spectatorStatus / 100) % 10);
- $this->autoTarget = (bool)(intval($spectatorStatus / 1000) % 10);
+ $this->isSpectator = (bool) ($spectatorStatus % 10);
+ $this->isTemporarySpectator = (bool) (intval($spectatorStatus / 10) % 10);
+ $this->isPureSpectator = (bool) (intval($spectatorStatus / 100) % 10);
+ $this->autoTarget = (bool) (intval($spectatorStatus / 1000) % 10);
$this->currentTargetId = intval($spectatorStatus / 10000);
}
diff --git a/core/Players/PlayerActions.php b/core/Players/PlayerActions.php
index 3f52a771..9250a42c 100644
--- a/core/Players/PlayerActions.php
+++ b/core/Players/PlayerActions.php
@@ -8,6 +8,10 @@ use FML\Controls\Quad;
use FML\Controls\Quads\Quad_Icons64x64_1;
use FML\ManiaLink;
use ManiaControl\Admin\AuthenticationManager;
+use ManiaControl\Callbacks\EchoListener;
+use ManiaControl\Communication\CommunicationAnswer;
+use ManiaControl\Communication\CommunicationListener;
+use ManiaControl\Communication\CommunicationMethods;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Manialinks\ManialinkManager;
@@ -26,7 +30,7 @@ use Maniaplanet\DedicatedServer\Xmlrpc\UnknownPlayerException;
* @copyright 2014-2015 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
-class PlayerActions {
+class PlayerActions implements EchoListener, CommunicationListener {
/*
* Constants
*/
@@ -36,6 +40,7 @@ class PlayerActions {
const SPECTATOR_SPECTATOR = 1;
const SPECTATOR_PLAYER = 2;
const SPECTATOR_BUT_KEEP_SELECTABLE = 3;
+ const ECHO_WARN_PLAYER = 'ManiaControl.PlayerManager.WarnPlayer';
/*
* Permission Setting Constants
@@ -48,6 +53,7 @@ class PlayerActions {
const SETTING_PERMISSION_KICK_PLAYER = 'Kick Player';
const SETTING_PERMISSION_BAN_PLAYER = 'Ban Player';
+
/*
* Private properties
*/
@@ -70,6 +76,75 @@ class PlayerActions {
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_FORCE_PLAYER_PLAY, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_FORCE_PLAYER_TEAM, AuthenticationManager::AUTH_LEVEL_MODERATOR);
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_FORCE_PLAYER_SPEC, AuthenticationManager::AUTH_LEVEL_MODERATOR);
+
+ // Echo Warn Command (Usage: sendEcho json_encode("player" => "loginName")
+ $this->maniaControl->getEchoManager()->registerEchoListener(self::ECHO_WARN_PLAYER, $this, function ($params) {
+ $this->warnPlayer(null, $params->player, false);
+ });
+
+ //Communication Manager Methods
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::WARN_PLAYER, $this, function ($data) {
+ if (!is_object($data) || !property_exists($data, "login")) {
+ return new CommunicationAnswer("You have to provide a valid player Login", true);
+ }
+ $success = $this->warnPlayer(null, $data->login, false);
+ return new CommunicationAnswer(array("success" => $success));
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::MUTE_PLAYER, $this, function ($data) {
+ if (!is_object($data) || !property_exists($data, "login")) {
+ return new CommunicationAnswer("You have to provide a valid player Login", true);
+ }
+ $success = $this->mutePlayer(null, $data->login, false);
+ return new CommunicationAnswer(array("success" => $success));
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::UNMUTE_PLAYER, $this, function ($data) {
+ if (!is_object($data) || !property_exists($data, "login")) {
+ return new CommunicationAnswer("You have to provide a valid player Login", true);
+ }
+ $success = $this->unMutePlayer(null, $data->login, false);
+ return new CommunicationAnswer(array("success" => $success));
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::KICK_PLAYER, $this, function ($data) {
+ if (!is_object($data) || !property_exists($data, "login")) {
+ return new CommunicationAnswer("You have to provide a valid player Login", true);
+ }
+
+ $message = "";
+ if (property_exists($data, "message")) {
+ $message = $data->message;
+ }
+
+ $success = $this->kickPlayer(null, $data->login, $message, false);
+
+ return new CommunicationAnswer(array("success" => $success));
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::FORCE_PLAYER_TO_SPEC, $this, function ($data) {
+ if (!is_object($data) || !property_exists($data, "login")) {
+ return new CommunicationAnswer("You have to provide a valid player Login", true);
+ }
+ //TODO allow parameters like spectator state
+ $success = $this->forcePlayerToSpectator(null, $data->login, self::SPECTATOR_BUT_KEEP_SELECTABLE, true, false);
+ return new CommunicationAnswer(array("success" => $success));
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::FORCE_PLAYER_TO_PLAY, $this, function ($data) {
+ if (!is_object($data) || !property_exists($data, "login")) {
+ return new CommunicationAnswer("You have to provide a valid player Login", true);
+ }
+
+ //TODO allow parameters like spectator state
+ if (property_exists($data, "teamId")) {
+ $success = $this->forcePlayerToTeam(null, $data->login, $data->teamId, false);
+ } else {
+ $success = $this->forcePlayerToPlay(null, $data->login, true, true, false);
+ }
+
+ return new CommunicationAnswer(array("success" => $success));
+ });
}
/**
@@ -78,26 +153,34 @@ class PlayerActions {
* @param string $adminLogin
* @param string $targetLogin
* @param int $teamId
+ * @param bool $calledByAdmin
+ * @return bool
*/
- public function forcePlayerToTeam($adminLogin, $targetLogin, $teamId) {
- $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_FORCE_PLAYER_TEAM)
- ) {
- $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
- return;
+ public function forcePlayerToTeam($adminLogin, $targetLogin, $teamId, $calledByAdmin = true) {
+ if ($calledByAdmin) {
+ $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_FORCE_PLAYER_TEAM)) {
+ $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
+ return false;
+ }
+ if (!$admin) {
+ return false;
+ }
}
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
- if (!$target || !$admin) {
- return;
+ if (!$target) {
+ return false;
}
if ($target->isSpectator) {
try {
- if (!$this->forcePlayerToPlay($adminLogin, $targetLogin, true, false)) {
- return;
+ if (!$this->forcePlayerToPlay($adminLogin, $targetLogin, true, false, $calledByAdmin)) {
+ return false;
}
} catch (FaultException $exception) {
- $this->maniaControl->getChat()->sendException($exception, $admin);
+ if ($calledByAdmin) {
+ $this->maniaControl->getChat()->sendException($exception, $admin);
+ }
}
}
@@ -105,27 +188,43 @@ class PlayerActions {
$this->maniaControl->getClient()->forcePlayerTeam($target->login, $teamId);
} catch (ServerOptionsException $exception) {
$this->forcePlayerToPlay($adminLogin, $targetLogin);
- return;
+ return false;
} catch (UnknownPlayerException $exception) {
- $this->maniaControl->getChat()->sendException($exception, $admin);
- return;
+ if ($calledByAdmin) {
+ $this->maniaControl->getChat()->sendException($exception, $admin);
+ }
+ return false;
} catch (GameModeException $exception) {
- $this->maniaControl->getChat()->sendException($exception, $admin);
- return;
+ if ($calledByAdmin) {
+ $this->maniaControl->getChat()->sendException($exception, $admin);
+ }
+ return false;
}
$chatMessage = false;
- $title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
- if ($teamId === self::TEAM_BLUE) {
- $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' forced ' . $target->getEscapedNickname() . ' into the Blue-Team!';
- } else if ($teamId === self::TEAM_RED) {
- $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' forced ' . $target->getEscapedNickname() . ' into the Red-Team!';
+
+ if ($calledByAdmin) {
+ $title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
+ if ($teamId === self::TEAM_BLUE) {
+ $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' forced ' . $target->getEscapedNickname() . ' into the Blue-Team!';
+ } else if ($teamId === self::TEAM_RED) {
+ $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' forced ' . $target->getEscapedNickname() . ' into the Red-Team!';
+ }
+ } else {
+ if ($teamId === self::TEAM_BLUE) {
+ $chatMessage = $target->getEscapedNickname() . ' got forced into the Blue-Team!';
+ } else if ($teamId === self::TEAM_RED) {
+ $chatMessage = $target->getEscapedNickname() . ' got forced into the Red-Team!';
+ }
}
+
if (!$chatMessage) {
- return;
+ return false;
}
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
+
+ return true;
}
/**
@@ -135,15 +234,18 @@ class PlayerActions {
* @param string $targetLogin
* @param bool $userIsAbleToSelect
* @param bool $displayAnnouncement
+ * @param bool $calledByAdmin
* @return bool
*/
- public function forcePlayerToPlay($adminLogin, $targetLogin, $userIsAbleToSelect = true, $displayAnnouncement = true) {
- $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_FORCE_PLAYER_PLAY)
- ) {
- $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
- return false;
+ public function forcePlayerToPlay($adminLogin, $targetLogin, $userIsAbleToSelect = true, $displayAnnouncement = true, $calledByAdmin = true) {
+ if ($calledByAdmin) {
+ $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_FORCE_PLAYER_PLAY)) {
+ $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
+ return false;
+ }
}
+
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$target) {
return false;
@@ -152,7 +254,9 @@ class PlayerActions {
try {
$this->maniaControl->getClient()->forceSpectator($target->login, self::SPECTATOR_PLAYER);
} catch (ServerOptionsException $exception) {
- $this->maniaControl->getChat()->sendException($exception, $admin);
+ if ($calledByAdmin) {
+ $this->maniaControl->getChat()->sendException($exception, $admin);
+ }
return false;
}
@@ -160,14 +264,22 @@ class PlayerActions {
try {
$this->maniaControl->getClient()->forceSpectator($target->login, self::SPECTATOR_USER_SELECTABLE);
} catch (ServerOptionsException $exception) {
- $this->maniaControl->getChat()->sendException($exception, $admin);
+ if ($calledByAdmin) {
+ $this->maniaControl->getChat()->sendException($exception, $admin);
+ }
return false;
}
}
// Announce force
if ($displayAnnouncement) {
- $chatMessage = $admin->getEscapedNickname() . ' forced ' . $target->getEscapedNickname() . ' to Play!';
+ if ($calledByAdmin) {
+ $chatMessage = $admin->getEscapedNickname() . ' forced ' . $target->getEscapedNickname() . ' to Play!';
+ } else {
+ $chatMessage = $target->getEscapedNickname() . ' got forced to Play!';
+ }
+
+
$this->maniaControl->getChat()->sendInformation($chatMessage);
}
@@ -181,29 +293,43 @@ class PlayerActions {
* @param string $targetLogin
* @param int $spectatorState
* @param bool $releaseSlot
+ * @param bool $calledByAdmin
+ * @return bool
*/
- public function forcePlayerToSpectator($adminLogin, $targetLogin, $spectatorState = self::SPECTATOR_BUT_KEEP_SELECTABLE, $releaseSlot = true) {
- $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_FORCE_PLAYER_SPEC)
- ) {
- $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
- return;
+ public function forcePlayerToSpectator($adminLogin, $targetLogin, $spectatorState = self::SPECTATOR_BUT_KEEP_SELECTABLE, $releaseSlot = true, $calledByAdmin = true) {
+ if ($calledByAdmin) {
+ $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_FORCE_PLAYER_SPEC)) {
+ $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
+ return false;
+ }
+
+ if (!$admin) {
+ return false;
+ }
}
+
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
- if (!$admin || !$target || $target->isSpectator) {
- return;
+ if (!$target || $target->isSpectator) {
+ return false;
}
try {
$this->maniaControl->getClient()->forceSpectator($target->login, $spectatorState);
} catch (ServerOptionsException $exception) {
- $this->maniaControl->getChat()->sendException($exception, $admin->login);
- return;
+ if ($calledByAdmin) {
+ $this->maniaControl->getChat()->sendException($exception, $admin->login);
+ }
+ return false;
}
- $title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
- $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' forced ' . $target->getEscapedNickname() . ' to Spectator!';
+ if ($calledByAdmin) {
+ $title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
+ $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' forced ' . $target->getEscapedNickname() . ' to Spectator!';
+ } else {
+ $chatMessage = $target->getEscapedNickname() . ' got forced to Spectator!';
+ }
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
@@ -215,39 +341,50 @@ class PlayerActions {
} catch (UnknownPlayerException $e) {
}
}
+
+ return true;
}
/**
* UnMute a Player
*
- * @param string $adminLogin
- * @param string $targetLogin
+ * @param $adminLogin
+ * @param $targetLogin
+ * @param bool $calledByAdmin
+ * @return bool
*/
- public function unMutePlayer($adminLogin, $targetLogin) {
- $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_MUTE_PLAYER)
- ) {
- $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
- return;
+ public function unMutePlayer($adminLogin, $targetLogin, $calledByAdmin = true) {
+ if ($calledByAdmin) {
+ $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_MUTE_PLAYER)) {
+ $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
+ return false;
+ }
}
-
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$target) {
- return;
+ return false;
}
try {
$this->maniaControl->getClient()->unIgnore($targetLogin);
} catch (NotInListException $e) {
$this->maniaControl->getChat()->sendError('Player is not ignored!', $adminLogin);
- return;
+ return false;
+ }
+
+ if ($calledByAdmin) {
+ $title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
+ $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' un-muted ' . $target->getEscapedNickname() . '!';
+ } else {
+ $chatMessage = $target->getEscapedNickname() . ' got un-muted!';
}
- $title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
- $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' un-muted ' . $target->getEscapedNickname() . '!';
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
+
+ return true;
}
/**
@@ -255,32 +392,42 @@ class PlayerActions {
*
* @param string $adminLogin
* @param string $targetLogin
+ * @param bool $calledByAdmin
+ * @return bool
*/
- public function mutePlayer($adminLogin, $targetLogin) {
- $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_MUTE_PLAYER)
- ) {
- $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
- return;
+ public function mutePlayer($adminLogin, $targetLogin, $calledByAdmin = true) {
+ if ($calledByAdmin) {
+ $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_MUTE_PLAYER)) {
+ $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
+ return false;
+ }
}
-
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$target) {
- return;
+ return false;
}
try {
$this->maniaControl->getClient()->ignore($targetLogin);
} catch (AlreadyInListException $e) {
$this->maniaControl->getChat()->sendError("Player already ignored!", $adminLogin);
- return;
+ return false;
+ }
+
+ // Announce warning
+ if ($calledByAdmin) {
+ $title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
+ $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' muted ' . $target->getEscapedNickname() . '!';
+ } else {
+ $chatMessage = $target->getEscapedNickname() . ' got muted!';
}
- $title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
- $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' muted ' . $target->getEscapedNickname() . '!';
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
+
+ return true;
}
/**
@@ -288,19 +435,22 @@ class PlayerActions {
*
* @param string $adminLogin
* @param string $targetLogin
+ * @param bool $calledByAdmin
+ * @return bool
*/
- public function warnPlayer($adminLogin, $targetLogin) {
- $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_WARN_PLAYER)
- ) {
- $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
- return;
+ public function warnPlayer($adminLogin, $targetLogin, $calledByAdmin = true) {
+ if ($calledByAdmin) {
+ $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_WARN_PLAYER)) {
+ $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
+ return false;
+ }
}
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$target) {
- return;
+ return false;
}
// Display warning message
@@ -360,49 +510,78 @@ class PlayerActions {
$this->maniaControl->getManialinkManager()->displayWidget($maniaLink, $target);
// Announce warning
- $title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
- $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' warned ' . $target->getEscapedNickname() . '!';
+ if ($calledByAdmin) {
+ $title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
+ $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' warned ' . $target->getEscapedNickname() . '!';
+ } else {
+ $chatMessage = $target->getEscapedNickname() . ' got an administrative warning!';
+ }
+
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::log($chatMessage, true);
+
+ return true;
}
+
/**
* Kick a Player
*
- * @param string $adminLogin
- * @param string $targetLogin
+ * @param $adminLogin
+ * @param $targetLogin
* @param string $message
+ * @param bool $calledByAdmin
+ * @return bool
*/
- public function kickPlayer($adminLogin, $targetLogin, $message = '') {
- $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_KICK_PLAYER)
- ) {
- $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
- return;
- }
- $target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
- if (!$target) {
- return;
+ public function kickPlayer($adminLogin, $targetLogin, $message = '', $calledByAdmin = true) {
+ if ($calledByAdmin) {
+ $admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_KICK_PLAYER)) {
+ $this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
+ return false;
+ }
}
- try {
- if ($target->isFakePlayer()) {
+ $target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
+ if (!$target) {
+ return false;
+ }
+
+ if ($target->isFakePlayer()) {
+ try {
$this->maniaControl->getClient()->disconnectFakePlayer($target->login);
- } else {
- $this->maniaControl->getClient()->kick($target->login, $message);
+ } catch (PlayerStateException $e) {
+ if ($calledByAdmin) {
+ $this->maniaControl->getChat()->sendException($e, $admin);
+ }
+ return false;
+ }
+ } else {
+ try {
+ $this->maniaControl->getClient()->kick($target->login, $message);
+ } catch (UnknownPlayerException $e) {
+ if ($calledByAdmin) {
+ $this->maniaControl->getChat()->sendException($e, $admin);
+ }
+ return false;
}
- } catch (UnknownPlayerException $e) {
- $this->maniaControl->getChat()->sendException($e, $admin);
- return;
}
// Announce kick
- $title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
- $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' kicked ' . $target->getEscapedNickname() . '!';
+ if ($calledByAdmin) {
+ $title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($admin->authLevel);
+ $chatMessage = $title . ' ' . $admin->getEscapedNickname() . ' kicked ' . $target->getEscapedNickname() . '!';
+ } else {
+ $chatMessage = $target->getEscapedNickname() . ' got kicked!';
+ }
+
$this->maniaControl->getChat()->sendInformation($chatMessage);
Logger::logInfo($chatMessage, true);
+
+ return true;
}
+
/**
* Ban a Player
*
@@ -412,17 +591,18 @@ class PlayerActions {
*/
public function banPlayer($adminLogin, $targetLogin, $message = '') {
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_BAN_PLAYER)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_BAN_PLAYER)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
return;
}
+
$target = $this->maniaControl->getPlayerManager()->getPlayer($targetLogin);
if (!$target) {
return;
}
- if ($target->isFakePlayer()) {
+ //Todo Validate (Problem: Not connected player isFakePlayer)
+ if ($target->isOfficial && $target->isFakePlayer()) {
$this->maniaControl->getChat()->sendError('It is not possible to Ban a bot', $admin);
return;
}
@@ -450,8 +630,7 @@ class PlayerActions {
*/
public function unBanPlayer($adminLogin, $targetLogin) {
$admin = $this->maniaControl->getPlayerManager()->getPlayer($adminLogin);
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_BAN_PLAYER)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($admin, self::SETTING_PERMISSION_BAN_PLAYER)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($admin);
return;
}
@@ -485,14 +664,12 @@ class PlayerActions {
}
$authLevelName = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($authLevel);
- if (!$this->maniaControl->getAuthenticationManager()->checkRight($admin, $authLevel + 1)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkRight($admin, $authLevel + 1)) {
$this->maniaControl->getChat()->sendError("You don't have the permission to add a {$authLevelName}!", $admin);
return;
}
- if ($this->maniaControl->getAuthenticationManager()->checkRight($target, $authLevel)
- ) {
+ if ($this->maniaControl->getAuthenticationManager()->checkRight($target, $authLevel)) {
$this->maniaControl->getChat()->sendError("This Player is already {$authLevelName}!", $admin);
return;
}
@@ -523,15 +700,13 @@ class PlayerActions {
return;
}
- if (!$this->maniaControl->getAuthenticationManager()->checkRight($admin, $target->authLevel + 1)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkRight($admin, $target->authLevel + 1)) {
$title = $this->maniaControl->getAuthenticationManager()->getAuthLevelName($target->authLevel);
$this->maniaControl->getChat()->sendError("You can't revoke the Rights of a {$title}!", $admin);
return;
}
- if ($this->maniaControl->getAuthenticationManager()->checkRight($target, AuthenticationManager::AUTH_LEVEL_MASTERADMIN)
- ) {
+ if ($this->maniaControl->getAuthenticationManager()->checkRight($target, AuthenticationManager::AUTH_LEVEL_MASTERADMIN)) {
$this->maniaControl->getChat()->sendError("MasterAdmins can't be removed!", $admin);
return;
}
diff --git a/core/Players/PlayerList.php b/core/Players/PlayerList.php
index c77df989..1ec75615 100644
--- a/core/Players/PlayerList.php
+++ b/core/Players/PlayerList.php
@@ -62,7 +62,7 @@ class PlayerList implements ManialinkPageAnswerListener, CallbackListener, Timer
* Private properties
*/
/** @var ManiaControl $maniaControl */
- private $maniaControl = null;
+ private $maniaControl = null;
private $playersListShown = array();
/**
@@ -163,9 +163,9 @@ class PlayerList implements ManialinkPageAnswerListener, CallbackListener, Timer
$headFrame = new Frame();
$frame->add($headFrame);
$headFrame->setY($posY - 5);
+
$labelLineArray = array('Id' => $posX + 5, 'Nickname' => $posX + 18, 'Login' => $posX + 70, 'Location' => $posX + 101);
- if ($this->maniaControl->getAuthenticationManager()->checkRight($player, AuthenticationManager::AUTH_LEVEL_MODERATOR)
- ) {
+ if ($this->maniaControl->getAuthenticationManager()->checkRight($player, AuthenticationManager::AUTH_LEVEL_MODERATOR)) {
$labelLineArray['Actions'] = $posX + 135;
}
$this->maniaControl->getManialinkManager()->labelLine($headFrame, $labelLineArray);
@@ -195,8 +195,9 @@ class PlayerList implements ManialinkPageAnswerListener, CallbackListener, Timer
$lineQuad->setZ(0.001);
}
- $array = array($index => $posX + 5, $listPlayer->nickname => $posX + 18, $listPlayer->login => $posX + 70, $path => $posX + 101);
- $this->maniaControl->getManialinkManager()->labelLine($playerFrame, $array);
+ $positions = array($posX + 5, $posX + 18, $posX + 70, $posX + 101);
+ $texts = array($index, $listPlayer->nickname, $listPlayer->login, $path);
+ $this->maniaControl->getManialinkManager()->labelLine($playerFrame, array($positions, $texts));
$playerFrame->setY($posY);
@@ -304,8 +305,7 @@ class PlayerList implements ManialinkPageAnswerListener, CallbackListener, Timer
$description = 'View Player Profile of $<' . $listPlayer->nickname . '$>';
$playerQuad->addTooltipLabelFeature($descriptionLabel, $description);
- if ($this->maniaControl->getAuthenticationManager()->checkRight($player, AuthenticationManager::AUTH_LEVEL_MODERATOR)
- ) {
+ if ($this->maniaControl->getAuthenticationManager()->checkRight($player, AuthenticationManager::AUTH_LEVEL_MODERATOR)) {
// Further Player actions Quad
$playerQuad = new Quad_Icons64x64_1();
$playerFrame->add($playerQuad);
@@ -320,10 +320,8 @@ class PlayerList implements ManialinkPageAnswerListener, CallbackListener, Timer
$playerQuad->addTooltipLabelFeature($descriptionLabel, $description);
}
- if ($this->maniaControl->getServer()->isTeamMode()
- ) {
- if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_FORCE_PLAYER_TEAM)
- ) {
+ if ($this->maniaControl->getServer()->isTeamMode()) {
+ if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_FORCE_PLAYER_TEAM)) {
// Force to Red-Team Quad
$redQuad = new Quad_Emblems();
$playerFrame->add($redQuad);
@@ -350,8 +348,7 @@ class PlayerList implements ManialinkPageAnswerListener, CallbackListener, Timer
$description = 'Force $<' . $listPlayer->nickname . '$> to Blue Team!';
$blueQuad->addTooltipLabelFeature($descriptionLabel, $description);
- } else if ($this->maniaControl->getPluginManager()->isPluginActive(self::DEFAULT_CUSTOM_VOTE_PLUGIN)
- ) {
+ } else if ($this->maniaControl->getPluginManager()->isPluginActive(self::DEFAULT_CUSTOM_VOTE_PLUGIN)) {
// Kick Player Vote
$kickQuad = new Quad_UIConstruction_Buttons();
$playerFrame->add($kickQuad);
@@ -365,8 +362,7 @@ class PlayerList implements ManialinkPageAnswerListener, CallbackListener, Timer
$kickQuad->addTooltipLabelFeature($descriptionLabel, $description);
}
} else {
- if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_FORCE_PLAYER_PLAY)
- ) {
+ if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_FORCE_PLAYER_PLAY)) {
// Force to Play
$playQuad = new Quad_Emblems();
$playerFrame->add($playQuad);
@@ -381,8 +377,7 @@ class PlayerList implements ManialinkPageAnswerListener, CallbackListener, Timer
}
}
- if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_FORCE_PLAYER_SPEC)
- ) {
+ if ($this->maniaControl->getAuthenticationManager()->checkPermission($player, PlayerActions::SETTING_PERMISSION_FORCE_PLAYER_SPEC)) {
// Force to Spectator Quad
$spectatorQuad = new Quad_BgRaceScore2();
$playerFrame->add($spectatorQuad);
@@ -395,8 +390,7 @@ class PlayerList implements ManialinkPageAnswerListener, CallbackListener, Timer
// Force to Spectator Description Label
$description = 'Force $<' . $listPlayer->nickname . '$> to Spectator!';
$spectatorQuad->addTooltipLabelFeature($descriptionLabel, $description);
- } else if ($this->maniaControl->getPluginManager()->isPluginActive(self::DEFAULT_CUSTOM_VOTE_PLUGIN)
- ) {
+ } else if ($this->maniaControl->getPluginManager()->isPluginActive(self::DEFAULT_CUSTOM_VOTE_PLUGIN)) {
// Force to Spectator Quad
$spectatorQuad = new Quad_BgRaceScore2();
$playerFrame->add($spectatorQuad);
@@ -451,7 +445,7 @@ class PlayerList implements ManialinkPageAnswerListener, CallbackListener, Timer
// mainframe
$frame = new Frame();
$frame->setSize($width, $height);
- $frame->setPosition($posX + $width / 2, 0);
+ $frame->setPosition($posX + $width / 2, 0, 31);
// Add Close Quad (X)
$closeQuad = new Quad_Icons64x64_1();
@@ -467,14 +461,14 @@ class PlayerList implements ManialinkPageAnswerListener, CallbackListener, Timer
$backgroundQuad->setSize($width, $height);
$backgroundQuad->setImage('https://dl.dropboxusercontent.com/u/105352981/Stuff/CAM%20SM%20BORDER%20PNG.png'); //TODO just a test
//$backgroundQuad->setStyles($quadStyle, $quadSubstyle);
- $backgroundQuad->setZ(0.2);
+ $backgroundQuad->setZ(-0.3);
// Background Quad
$backgroundQuad = new Quad();
$frame->add($backgroundQuad);
$backgroundQuad->setSize($width - 2, $height - 2);
$backgroundQuad->setStyles($quadStyle, $quadSubstyle);
- $backgroundQuad->setZ(0.1);
+ $backgroundQuad->setZ(-0.4);
// Show headline
$label = new Label_Text();
diff --git a/core/Players/PlayerManager.php b/core/Players/PlayerManager.php
index 11e5ec51..568d039b 100644
--- a/core/Players/PlayerManager.php
+++ b/core/Players/PlayerManager.php
@@ -7,6 +7,9 @@ use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\CallbackManager;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\Callbacks\TimerListener;
+use ManiaControl\Communication\CommunicationAnswer;
+use ManiaControl\Communication\CommunicationListener;
+use ManiaControl\Communication\CommunicationMethods;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Statistics\StatisticManager;
@@ -20,7 +23,7 @@ use Maniaplanet\DedicatedServer\Xmlrpc\UnknownPlayerException;
* @copyright 2014-2015 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
-class PlayerManager implements CallbackListener, TimerListener {
+class PlayerManager implements CallbackListener, TimerListener, CommunicationListener {
/*
* Constants
*/
@@ -105,6 +108,12 @@ class PlayerManager implements CallbackListener, TimerListener {
// Player stats
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_JOIN_COUNT);
$this->maniaControl->getStatisticManager()->defineStatMetaData(self::STAT_SERVERTIME, StatisticManager::STAT_TYPE_TIME);
+
+
+ // Communication Listenings
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::GET_PLAYER_LIST, $this, function ($data) {
+ return new CommunicationAnswer($this->players);
+ });
}
/**
@@ -184,6 +193,7 @@ class PlayerManager implements CallbackListener, TimerListener {
return $this->adminLists;
}
+
/**
* Handle OnInit callback
*/
@@ -374,15 +384,24 @@ class PlayerManager implements CallbackListener, TimerListener {
* Get the count of all Players
*
* @param bool $withoutSpectators
+ * @param bool $withoutBots
* @return int
*/
- public function getPlayerCount($withoutSpectators = true) {
- if (!$withoutSpectators) {
- return count($this->players);
- }
+ public function getPlayerCount($withoutSpectators = true, $withoutBots = true) {
$count = 0;
foreach ($this->players as $player) {
- if (!$player->isSpectator) {
+ $valid = true;
+ if ($withoutSpectators) {
+ if ($player->isSpectator) {
+ $valid = false;
+ }
+ }
+ if ($withoutBots) {
+ if ($player->isFakePlayer()) {
+ $valid = false;
+ }
+ }
+ if ($valid) {
$count++;
}
}
@@ -516,6 +535,22 @@ class PlayerManager implements CallbackListener, TimerListener {
return $this->players;
}
+ /**
+ * Get a List of Spectators
+ *
+ * @return Player[]
+ */
+ public function getSpectators() {
+ $spectators = array();
+ foreach ($this->players as $player) {
+ if ($player->isSpectator) {
+ $spectators[] = $player;
+ }
+ }
+
+ return $spectators;
+ }
+
/**
* Get the count of all spectators
*
diff --git a/core/Plugins/PluginManager.php b/core/Plugins/PluginManager.php
index 13f74efc..e51b6c66 100644
--- a/core/Plugins/PluginManager.php
+++ b/core/Plugins/PluginManager.php
@@ -3,6 +3,7 @@
namespace ManiaControl\Plugins;
use ManiaControl\Callbacks\CallbackListener;
+use ManiaControl\Callbacks\EchoListener;
use ManiaControl\Callbacks\TimerListener;
use ManiaControl\Commands\CommandListener;
use ManiaControl\Files\FileUtil;
@@ -141,6 +142,9 @@ class PluginManager {
$plugin->unload();
+ if ($plugin instanceof EchoListener) {
+ $this->maniaControl->getEchoManager()->unregisterEchoListener($plugin);
+ }
if ($plugin instanceof CallbackListener) {
$this->maniaControl->getCallbackManager()->unregisterCallbackListener($plugin);
$this->maniaControl->getCallbackManager()->unregisterScriptCallbackListener($plugin);
diff --git a/core/Server/Commands.php b/core/Server/Commands.php
index f56feaa0..29a6130b 100644
--- a/core/Server/Commands.php
+++ b/core/Server/Commands.php
@@ -40,13 +40,14 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
const SETTING_PERMISSION_CHANGE_SERVERSETTINGS = 'Change ServerSettings';
const COMMAND_EXTEND_WARMUP = 'WarmUp_Extend';
const COMMAND_FORCE_WARMUP = 'Command_ForceWarmUp';
+ const COMMAND_SET_PAUSE = 'Command_SetPause';
/*
* Private properties
*/
/** @var ManiaControl $maniaControl */
- private $maniaControl = null;
- private $serverShutdownTime = -1;
+ private $maniaControl = null;
+ private $serverShutdownTime = -1;
private $serverShutdownEmpty = false;
/**
@@ -113,7 +114,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
try {
$scriptInfos = $this->maniaControl->getClient()->getModeScriptInfo();
foreach ($scriptInfos->commandDescs as $param) {
- if ($param->name === self::COMMAND_FORCE_WARMUP) {
+ if ($param->name === self::COMMAND_FORCE_WARMUP || $param->name === self::COMMAND_SET_PAUSE) {
$pauseExists = true;
break;
}
@@ -163,14 +164,12 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
* @param Player $player
*/
public function commandCancelVote(array $chatCallback, Player $player) {
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CANCEL_VOTE)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CANCEL_VOTE)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
- if ($this->maniaControl->getClient()->cancelVote()
- ) {
+ if ($this->maniaControl->getClient()->cancelVote()) {
$this->maniaControl->getChat()->sendInformation($player->getEscapedNickname() . ' cancelled the Vote!');
} else {
$this->maniaControl->getChat()->sendInformation("There's no vote running currently!", $player);
@@ -187,8 +186,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
* @param Player $player
*/
public function commandExtendWarmup(array $callback, Player $player) {
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_HANDLE_WARMUP)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_HANDLE_WARMUP)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
@@ -207,8 +205,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
* @param Player $player
*/
public function commandEndWarmup(array $callback, Player $player) {
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_HANDLE_WARMUP)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_HANDLE_WARMUP)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
@@ -227,16 +224,27 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
* @param Player $player
*/
public function setPause(array $callback, Player $player) {
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_SET_PAUSE)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_SET_PAUSE)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
+
+ //Normal Gamemodes
try {
$this->maniaControl->getClient()->sendModeScriptCommands(array('Command_ForceWarmUp' => true));
$this->maniaControl->getChat()->sendInformation($player->getEscapedNickname() . ' paused the Game!');
} catch (GameModeException $e) {
}
+
+ try {
+ //Chase and Combo?
+ $this->maniaControl->getClient()->sendModeScriptCommands(array('Command_SetPause' => true));
+ $this->maniaControl->getChat()->sendInformation($player->getEscapedNickname() . ' paused the Game!');
+
+ //Especially for chase, force end of the round to reach a draw
+ $this->maniaControl->getClient()->sendModeScriptCommands(array('Command_ForceEndRound' => true));
+ } catch (GameModeException $ex) {
+ }
}
/**
@@ -246,8 +254,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
// TODO: move empty & delayed shutdown code into server class
// Empty shutdown
if ($this->serverShutdownEmpty) {
- if ($this->maniaControl->getPlayerManager()->getPlayerCount(false) <= 0
- ) {
+ if ($this->maniaControl->getPlayerManager()->getPlayerCount(false) <= 0) {
$this->shutdownServer('empty');
}
}
@@ -277,8 +284,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
* @param Player $player
*/
public function commandSystemInfo(array $chat, Player $player) {
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_SHOW_SYSTEMINFO)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_SHOW_SYSTEMINFO)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
@@ -294,8 +300,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
* @param Player $player
*/
public function commandShutdownServer(array $chat, Player $player) {
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_SHUTDOWN_SERVER)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_SHUTDOWN_SERVER)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
@@ -312,7 +317,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
$this->maniaControl->getChat()->sendInformation("Empty-shutdown cancelled!", $player);
return;
}
- $delay = (int)$param;
+ $delay = (int) $param;
if ($delay <= 0) {
// Cancel shutdown
$this->serverShutdownTime = -1;
@@ -334,8 +339,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
* @param Player $player
*/
public function commandSetServerName(array $chat, Player $player) {
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
@@ -356,8 +360,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
* @param Player $player
*/
public function commandSetPwd(array $chatCallback, Player $player) {
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
@@ -379,8 +382,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
* @param Player $player
*/
public function commandSetSpecPwd(array $chatCallback, Player $player) {
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
@@ -402,8 +404,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
* @param Player $player
*/
public function commandSetMaxPlayers(array $chatCallback, Player $player) {
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
@@ -417,7 +418,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
$this->maniaControl->getChat()->sendUsageInfo('Usage example: //setmaxplayers 16', $player);
return;
}
- $amount = (int)$amount;
+ $amount = (int) $amount;
if ($amount < 0) {
$amount = 0;
}
@@ -433,8 +434,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
* @param Player $player
*/
public function commandSetMaxSpectators(array $chatCallback, Player $player) {
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVERSETTINGS)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
@@ -448,7 +448,7 @@ class Commands implements CallbackListener, CommandListener, ManialinkPageAnswer
$this->maniaControl->getChat()->sendUsageInfo('Usage example: //setmaxspectators 16', $player);
return;
}
- $amount = (int)$amount;
+ $amount = (int) $amount;
if ($amount < 0) {
$amount = 0;
}
diff --git a/core/Server/ScriptManager.php b/core/Server/ScriptManager.php
index 945a8e33..48c2092b 100644
--- a/core/Server/ScriptManager.php
+++ b/core/Server/ScriptManager.php
@@ -4,6 +4,7 @@ namespace ManiaControl\Server;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
+use Maniaplanet\DedicatedServer\Xmlrpc\GameModeException;
/**
* Manager for Game Mode Script related Stuff
@@ -39,13 +40,18 @@ class ScriptManager {
if (!$this->isScriptMode()) {
return false;
}
- $scriptSettings = $this->maniaControl->getClient()->getModeScriptSettings();
+
+ try {
+ $scriptSettings = $this->maniaControl->getClient()->getModeScriptSettings();
+ } catch (GameModeException $e) {
+ return false;
+ }
if (!array_key_exists('S_UseScriptCallbacks', $scriptSettings)) {
return false;
}
- $scriptSettings['S_UseScriptCallbacks'] = (bool)$enable;
+ $scriptSettings['S_UseScriptCallbacks'] = (bool) $enable;
$actionName = ($enable ? 'en' : 'dis');
$this->maniaControl->getClient()->setModeScriptSettings($scriptSettings);
diff --git a/core/Server/Server.php b/core/Server/Server.php
index 6c25b8ba..377cdb24 100644
--- a/core/Server/Server.php
+++ b/core/Server/Server.php
@@ -4,6 +4,7 @@ namespace ManiaControl\Server;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
+use ManiaControl\Commands\CommandListener;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
@@ -17,7 +18,7 @@ use Maniaplanet\DedicatedServer\Xmlrpc\Exception;
* @copyright 2014-2015 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
-class Server implements CallbackListener {
+class Server implements CallbackListener, CommandListener {
/*
* Constants
*/
@@ -85,6 +86,30 @@ class Server implements CallbackListener {
// Callbacks
$this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::ONINIT, $this, 'onInit');
+
+ $this->maniaControl->getCommandManager()->registerCommandListener("uptime", $this, "chatUpTime", true, "Show how long the server is running.");
+ }
+
+ /**
+ * Displays how long the Server is running already in the Chat
+ *
+ * @param array $chatCallback
+ * @param \ManiaControl\Players\Player $player
+ */
+ public function chatUpTime(array $chatCallback, Player $player) {
+ $networkStats = $this->maniaControl->getClient()->getNetworkStats();
+
+ $minutestotal = $networkStats->uptime / 60;
+ $hourstotal = $minutestotal / 60;
+ $days = intval($hourstotal / 24);
+ $hours = intval($hourstotal - 24 * $days);
+ $minutes = intval($minutestotal - 24 * 60 * $days - $hours * 60);
+
+ $days > 1 ? $dayString = 'days' : $dayString = 'day';
+ $hours > 1 ? $hourString = 'hours' : $hourString = 'hour';
+ $minutes > 1 ? $minuteString = 'minutes' : $minuteString = 'minute';
+
+ $this->maniaControl->getChat()->sendChat('Server is running since $<$fff' . $days . '$> ' . $dayString . ', $<$fff' . $hours . '$> ' . $hourString . ' and $<$fff' . $minutes . '$> ' . $minuteString, $player);
}
/**
@@ -226,6 +251,30 @@ class Server implements CallbackListener {
return $servers;
}
+ /** Get Server Login by Index
+ *
+ * @param int $index
+ * @return string
+ */
+ public function getServerLoginByIndex($index) {
+ $mysqli = $this->maniaControl->getDatabase()->getMysqli();
+ $query = "SELECT * FROM `" . self::TABLE_SERVERS . "` WHERE `index`=" . $index . ";";
+ $result = $mysqli->query($query);
+
+ if (!$result) {
+ trigger_error($mysqli->error);
+ return "";
+ }
+
+ if ($result->num_rows != 1) {
+ return "";
+ }
+
+ $row = $result->fetch_object();
+
+ return $row->login;
+ }
+
/**
* Handle OnInit Callback
*/
diff --git a/core/Server/ServerOptionsMenu.php b/core/Server/ServerOptionsMenu.php
index a815792f..76add131 100644
--- a/core/Server/ServerOptionsMenu.php
+++ b/core/Server/ServerOptionsMenu.php
@@ -14,6 +14,9 @@ use ManiaControl\Admin\AuthenticationManager;
use ManiaControl\Callbacks\CallbackListener;
use ManiaControl\Callbacks\Callbacks;
use ManiaControl\Callbacks\TimerListener;
+use ManiaControl\Communication\CommunicationAnswer;
+use ManiaControl\Communication\CommunicationListener;
+use ManiaControl\Communication\CommunicationMethods;
use ManiaControl\Configurator\ConfiguratorMenu;
use ManiaControl\Logger;
use ManiaControl\ManiaControl;
@@ -28,7 +31,7 @@ use Maniaplanet\DedicatedServer\Xmlrpc\ServerOptionsException;
* @copyright 2014-2015 ManiaControl Team
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
*/
-class ServerOptionsMenu implements CallbackListener, ConfiguratorMenu, TimerListener {
+class ServerOptionsMenu implements CallbackListener, ConfiguratorMenu, TimerListener, CommunicationListener {
/*
* Constants
*/
@@ -64,6 +67,10 @@ class ServerOptionsMenu implements CallbackListener, ConfiguratorMenu, TimerList
// Permissions
$this->maniaControl->getAuthenticationManager()->definePermissionLevel(self::SETTING_PERMISSION_CHANGE_SERVER_OPTIONS, AuthenticationManager::AUTH_LEVEL_SUPERADMIN);
+
+ //TODO remove to somewhere cleaner
+ //Communication Listenings
+ $this->initalizeCommunicationListenings();
}
/**
@@ -322,8 +329,7 @@ class ServerOptionsMenu implements CallbackListener, ConfiguratorMenu, TimerList
* @see \ManiaControl\Configurators\ConfiguratorMenu::saveConfigData()
*/
public function saveConfigData(array $configData, Player $player) {
- if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVER_OPTIONS)
- ) {
+ if (!$this->maniaControl->getAuthenticationManager()->checkPermission($player, self::SETTING_PERMISSION_CHANGE_SERVER_OPTIONS)) {
$this->maniaControl->getAuthenticationManager()->sendNotAllowed($player);
return;
}
@@ -376,4 +382,43 @@ class ServerOptionsMenu implements CallbackListener, ConfiguratorMenu, TimerList
return true;
}
+
+
+ /**
+ * Initializes the communication Listenings
+ */
+ private function initalizeCommunicationListenings() {
+ //Communication Listenings
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::GET_SERVER_OPTIONS, $this, function ($data) {
+ return new CommunicationAnswer($this->maniaControl->getClient()->getServerOptions());
+ });
+
+ $this->maniaControl->getCommunicationManager()->registerCommunicationListener(CommunicationMethods::SET_SERVER_OPTIONS, $this, function ($data) {
+ if (!is_object($data) || !property_exists($data, "serverOptions")) {
+ return new CommunicationAnswer("No valid ServerOptions provided!", true);
+ }
+
+ $oldServerOptions = $this->maniaControl->getClient()->getServerOptions();
+ $newServerOptions = new ServerOptions();
+
+ foreach ($data->serverOptions as $name => $value) {
+ $optionName = $name;
+ $newServerOptions->$optionName = $value;
+ settype($newServerOptions->$optionName, gettype($oldServerOptions->$optionName));
+ }
+
+ $this->fillUpMandatoryOptions($newServerOptions, $oldServerOptions);
+
+ try {
+ $success = $this->maniaControl->getClient()->setServerOptions($newServerOptions);
+ } catch (ServerOptionsException $exception) {
+ return new CommunicationAnswer($exception->getMessage(), true);
+ }
+
+ //Trigger Server Options Changed Callback
+ $this->maniaControl->getCallbackManager()->triggerCallback(self::CB_SERVER_OPTIONS_CHANGED, array(self::CB_SERVER_OPTIONS_CHANGED));
+
+ return new CommunicationAnswer(array("success" => $success));
+ });
+ }
}
diff --git a/core/Utils/SystemUtil.php b/core/Utils/SystemUtil.php
index cd068daa..9013755c 100644
--- a/core/Utils/SystemUtil.php
+++ b/core/Utils/SystemUtil.php
@@ -77,6 +77,16 @@ class SystemUtil {
Logger::log($message . 'FOUND!');
}
+ // Check for Zlib
+ $message = 'Checking for installed Zlib ... ';
+ if (!extension_loaded('zlib')) {
+ Logger::log($message . 'NOT FOUND!');
+ Logger::log(" -- You don't have Zlib installed! Check: http://php.net/manual/de/zlib.setup.php");
+ $success = false;
+ } else {
+ Logger::log($message . 'FOUND!');
+ }
+
if (!$success) {
// Missing requirements
self::quit();
diff --git a/libs/Evenement/EventEmitter.php b/libs/Evenement/EventEmitter.php
new file mode 100644
index 00000000..0ef51fc2
--- /dev/null
+++ b/libs/Evenement/EventEmitter.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement;
+
+class EventEmitter implements EventEmitterInterface
+{
+ use EventEmitterTrait;
+}
diff --git a/libs/Evenement/EventEmitterInterface.php b/libs/Evenement/EventEmitterInterface.php
new file mode 100644
index 00000000..9b0b136a
--- /dev/null
+++ b/libs/Evenement/EventEmitterInterface.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement;
+
+interface EventEmitterInterface
+{
+ public function on($event, callable $listener);
+ public function once($event, callable $listener);
+ public function removeListener($event, callable $listener);
+ public function removeAllListeners($event = null);
+ public function listeners($event);
+ public function emit($event, array $arguments = []);
+}
diff --git a/libs/Evenement/EventEmitterTrait.php b/libs/Evenement/EventEmitterTrait.php
new file mode 100644
index 00000000..b4a4a3a4
--- /dev/null
+++ b/libs/Evenement/EventEmitterTrait.php
@@ -0,0 +1,68 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement;
+
+trait EventEmitterTrait
+{
+ protected $listeners = [];
+
+ public function on($event, callable $listener)
+ {
+ if (!isset($this->listeners[$event])) {
+ $this->listeners[$event] = [];
+ }
+
+ $this->listeners[$event][] = $listener;
+ }
+
+ public function once($event, callable $listener)
+ {
+ $onceListener = function () use (&$onceListener, $event, $listener) {
+ $this->removeListener($event, $onceListener);
+
+ call_user_func_array($listener, func_get_args());
+ };
+
+ $this->on($event, $onceListener);
+ }
+
+ public function removeListener($event, callable $listener)
+ {
+ if (isset($this->listeners[$event])) {
+ $index = array_search($listener, $this->listeners[$event], true);
+ if (false !== $index) {
+ unset($this->listeners[$event][$index]);
+ }
+ }
+ }
+
+ public function removeAllListeners($event = null)
+ {
+ if ($event !== null) {
+ unset($this->listeners[$event]);
+ } else {
+ $this->listeners = [];
+ }
+ }
+
+ public function listeners($event)
+ {
+ return isset($this->listeners[$event]) ? $this->listeners[$event] : [];
+ }
+
+ public function emit($event, array $arguments = [])
+ {
+ foreach ($this->listeners($event) as $listener) {
+ call_user_func_array($listener, $arguments);
+ }
+ }
+}
diff --git a/libs/Maniaplanet/DedicatedServer/Connection.php b/libs/Maniaplanet/DedicatedServer/Connection.php
index f88d942a..e7962782 100755
--- a/libs/Maniaplanet/DedicatedServer/Connection.php
+++ b/libs/Maniaplanet/DedicatedServer/Connection.php
@@ -4250,9 +4250,9 @@ class Connection
if(is_string($filename))
{
$filename = $this->stripBom($filename);
- if(mb_check_encoding($filename, 'ascii'))
- return $filename;
- return "\xEF\xBB\xBF".$filename;
+ if(preg_match('/[^\x09\x0A\x0D\x20-\x7E]/', $filename))
+ return "\xEF\xBB\xBF".$filename;
+ return $filename;
}
return array_map(array($this, 'secureUtf8'), $filename);
}
diff --git a/libs/Maniaplanet/DedicatedServer/Xmlrpc/FaultException.php b/libs/Maniaplanet/DedicatedServer/Xmlrpc/FaultException.php
index 785eea78..8953735b 100755
--- a/libs/Maniaplanet/DedicatedServer/Xmlrpc/FaultException.php
+++ b/libs/Maniaplanet/DedicatedServer/Xmlrpc/FaultException.php
@@ -7,12 +7,9 @@
namespace Maniaplanet\DedicatedServer\Xmlrpc;
-class FaultException extends Exception
-{
- static function create($faultString, $faultCode)
- {
- switch($faultString)
- {
+class FaultException extends Exception {
+ static function create($faultString, $faultCode) {
+ switch ($faultString) {
case 'Password incorrect.':
case 'Permission denied.':
return new AuthenticationException($faultString, $faultCode);
@@ -34,6 +31,7 @@ class FaultException extends Exception
return new LockedFeatureException($faultString, $faultCode);
case 'Login or Uid unknown.':
case 'Login unknown.':
+ case 'Payer login unknown.':
return new UnknownPlayerException($faultString, $faultCode);
case 'The player is not a spectator':
case 'The player is not a spectator.':
@@ -72,6 +70,7 @@ class FaultException extends Exception
case 'You cannot change the max spectators count: AllowSpectatorRelays is activated.':
case 'There are too many players':
case 'There are too many spectators':
+ case 'Unknown hideserver value':
return new ServerOptionsException($faultString, $faultCode);
case 'New mode unknown.':
case 'You need to stop the server to change to/from script mode.':
@@ -79,6 +78,7 @@ class FaultException extends Exception
case 'Not in Team mode.':
case 'Not in Rounds or Laps mode.':
case 'The scores must be decreasing.':
+ case 'No current script.':
return new GameModeException($faultString, $faultCode);
case 'Unable to write the black list file.':
case 'Unable to write the guest list file.':
@@ -90,26 +90,55 @@ class FaultException extends Exception
case 'Invalid url.':
return new FileException($faultString, $faultCode);
}
- if(preg_match('~^Unknown setting \'.*\'\.$~iu', $faultString))
+ if (preg_match('~^Unknown setting \'.*\'\.$~iu', $faultString)) {
return new GameModeException($faultString, $faultCode);
- if(preg_match('~^Couldn\'t load \'.*\'\.$~iu', $faultString))
+ }
+ if (preg_match('~^Couldn\'t load \'.*\'\.$~iu', $faultString)) {
return new FileException($faultString, $faultCode);
+ }
return new self($faultString, $faultCode);
}
}
-class AuthenticationException extends FaultException {}
-class UnavailableFeatureException extends FaultException {}
-class LockedFeatureException extends FaultException {}
-class UnknownPlayerException extends FaultException {}
-class PlayerStateException extends FaultException {}
-class AlreadyInListException extends FaultException {}
-class NotInListException extends FaultException {}
-class IndexOutOfBoundException extends FaultException {}
-class NextMapException extends FaultException{}
-class ChangeInProgressException extends FaultException {}
-class InvalidMapException extends FaultException{}
-class GameModeException extends FaultException {}
-class ServerOptionsException extends FaultException {}
-class FileException extends FaultException {}
+class AuthenticationException extends FaultException {
+}
+
+class UnavailableFeatureException extends FaultException {
+}
+
+class LockedFeatureException extends FaultException {
+}
+
+class UnknownPlayerException extends FaultException {
+}
+
+class PlayerStateException extends FaultException {
+}
+
+class AlreadyInListException extends FaultException {
+}
+
+class NotInListException extends FaultException {
+}
+
+class IndexOutOfBoundException extends FaultException {
+}
+
+class NextMapException extends FaultException {
+}
+
+class ChangeInProgressException extends FaultException {
+}
+
+class InvalidMapException extends FaultException {
+}
+
+class GameModeException extends FaultException {
+}
+
+class ServerOptionsException extends FaultException {
+}
+
+class FileException extends FaultException {
+}
diff --git a/libs/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php b/libs/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php
index c6f8106a..a84f2b63 100755
--- a/libs/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php
+++ b/libs/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php
@@ -16,8 +16,8 @@ class GbxRemote
public static $sent;
private $socket;
- private $readTimeout = array('sec' => 5, 'usec' => 0);
- private $writeTimeout = array('sec' => 5, 'usec' => 0);
+ private $readTimeout = array('sec' => 30, 'usec' => 0);
+ private $writeTimeout = array('sec' => 30, 'usec' => 0);
private $requestHandle;
private $callbacksBuffer = array();
private $multicallBuffer = array();
diff --git a/libs/React/EventLoop/ExtEventLoop.php b/libs/React/EventLoop/ExtEventLoop.php
new file mode 100644
index 00000000..b3d16b94
--- /dev/null
+++ b/libs/React/EventLoop/ExtEventLoop.php
@@ -0,0 +1,327 @@
+eventBase = new EventBase($config);
+ $this->nextTickQueue = new NextTickQueue($this);
+ $this->futureTickQueue = new FutureTickQueue($this);
+ $this->timerEvents = new SplObjectStorage();
+
+ $this->createTimerCallback();
+ $this->createStreamCallback();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addReadStream($stream, callable $listener)
+ {
+ $key = (int) $stream;
+
+ if (!isset($this->readListeners[$key])) {
+ $this->readListeners[$key] = $listener;
+ $this->subscribeStreamEvent($stream, Event::READ);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addWriteStream($stream, callable $listener)
+ {
+ $key = (int) $stream;
+
+ if (!isset($this->writeListeners[$key])) {
+ $this->writeListeners[$key] = $listener;
+ $this->subscribeStreamEvent($stream, Event::WRITE);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeReadStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->readListeners[$key])) {
+ unset($this->readListeners[$key]);
+ $this->unsubscribeStreamEvent($stream, Event::READ);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeWriteStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->writeListeners[$key])) {
+ unset($this->writeListeners[$key]);
+ $this->unsubscribeStreamEvent($stream, Event::WRITE);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->streamEvents[$key])) {
+ $this->streamEvents[$key]->free();
+
+ unset(
+ $this->streamFlags[$key],
+ $this->streamEvents[$key],
+ $this->readListeners[$key],
+ $this->writeListeners[$key]
+ );
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addTimer($interval, callable $callback)
+ {
+ $timer = new Timer($this, $interval, $callback, false);
+
+ $this->scheduleTimer($timer);
+
+ return $timer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addPeriodicTimer($interval, callable $callback)
+ {
+ $timer = new Timer($this, $interval, $callback, true);
+
+ $this->scheduleTimer($timer);
+
+ return $timer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function cancelTimer(TimerInterface $timer)
+ {
+ if ($this->isTimerActive($timer)) {
+ $this->timerEvents[$timer]->free();
+ $this->timerEvents->detach($timer);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isTimerActive(TimerInterface $timer)
+ {
+ return $this->timerEvents->contains($timer);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function nextTick(callable $listener)
+ {
+ $this->nextTickQueue->add($listener);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function futureTick(callable $listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function tick()
+ {
+ $this->nextTickQueue->tick();
+
+ $this->futureTickQueue->tick();
+
+ // @-suppression: https://github.com/reactphp/react/pull/234#discussion-diff-7759616R226
+ @$this->eventBase->loop(EventBase::LOOP_ONCE | EventBase::LOOP_NONBLOCK);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->nextTickQueue->tick();
+
+ $this->futureTickQueue->tick();
+
+ $flags = EventBase::LOOP_ONCE;
+ if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) {
+ $flags |= EventBase::LOOP_NONBLOCK;
+ } elseif (!$this->streamEvents && !$this->timerEvents->count()) {
+ break;
+ }
+
+ // @-suppression: https://github.com/reactphp/react/pull/234#discussion-diff-7759616R226
+ @$this->eventBase->loop($flags);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function stop()
+ {
+ $this->running = false;
+ }
+
+ /**
+ * Schedule a timer for execution.
+ *
+ * @param TimerInterface $timer
+ */
+ private function scheduleTimer(TimerInterface $timer)
+ {
+ $flags = Event::TIMEOUT;
+
+ if ($timer->isPeriodic()) {
+ $flags |= Event::PERSIST;
+ }
+
+ $event = new Event($this->eventBase, -1, $flags, $this->timerCallback, $timer);
+ $this->timerEvents[$timer] = $event;
+
+ $event->add($timer->getInterval());
+ }
+
+ /**
+ * Create a new ext-event Event object, or update the existing one.
+ *
+ * @param resource $stream
+ * @param integer $flag Event::READ or Event::WRITE
+ */
+ private function subscribeStreamEvent($stream, $flag)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->streamEvents[$key])) {
+ $event = $this->streamEvents[$key];
+ $flags = ($this->streamFlags[$key] |= $flag);
+
+ $event->del();
+ $event->set($this->eventBase, $stream, Event::PERSIST | $flags, $this->streamCallback);
+ } else {
+ $event = new Event($this->eventBase, $stream, Event::PERSIST | $flag, $this->streamCallback);
+
+ $this->streamEvents[$key] = $event;
+ $this->streamFlags[$key] = $flag;
+ }
+
+ $event->add();
+ }
+
+ /**
+ * Update the ext-event Event object for this stream to stop listening to
+ * the given event type, or remove it entirely if it's no longer needed.
+ *
+ * @param resource $stream
+ * @param integer $flag Event::READ or Event::WRITE
+ */
+ private function unsubscribeStreamEvent($stream, $flag)
+ {
+ $key = (int) $stream;
+
+ $flags = $this->streamFlags[$key] &= ~$flag;
+
+ if (0 === $flags) {
+ $this->removeStream($stream);
+
+ return;
+ }
+
+ $event = $this->streamEvents[$key];
+
+ $event->del();
+ $event->set($this->eventBase, $stream, Event::PERSIST | $flags, $this->streamCallback);
+ $event->add();
+ }
+
+ /**
+ * Create a callback used as the target of timer events.
+ *
+ * A reference is kept to the callback for the lifetime of the loop
+ * to prevent "Cannot destroy active lambda function" fatal error from
+ * the event extension.
+ */
+ private function createTimerCallback()
+ {
+ $this->timerCallback = function ($_, $_, $timer) {
+ call_user_func($timer->getCallback(), $timer);
+
+ if (!$timer->isPeriodic() && $this->isTimerActive($timer)) {
+ $this->cancelTimer($timer);
+ }
+ };
+ }
+
+ /**
+ * Create a callback used as the target of stream events.
+ *
+ * A reference is kept to the callback for the lifetime of the loop
+ * to prevent "Cannot destroy active lambda function" fatal error from
+ * the event extension.
+ */
+ private function createStreamCallback()
+ {
+ $this->streamCallback = function ($stream, $flags) {
+ $key = (int) $stream;
+
+ if (Event::READ === (Event::READ & $flags) && isset($this->readListeners[$key])) {
+ call_user_func($this->readListeners[$key], $stream, $this);
+ }
+
+ if (Event::WRITE === (Event::WRITE & $flags) && isset($this->writeListeners[$key])) {
+ call_user_func($this->writeListeners[$key], $stream, $this);
+ }
+ };
+ }
+}
diff --git a/libs/React/EventLoop/Factory.php b/libs/React/EventLoop/Factory.php
new file mode 100644
index 00000000..9a481e35
--- /dev/null
+++ b/libs/React/EventLoop/Factory.php
@@ -0,0 +1,21 @@
+loop = new EventLoop();
+ $this->nextTickQueue = new NextTickQueue($this);
+ $this->futureTickQueue = new FutureTickQueue($this);
+ $this->timerEvents = new SplObjectStorage();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addReadStream($stream, callable $listener)
+ {
+ $callback = function () use ($stream, $listener) {
+ call_user_func($listener, $stream, $this);
+ };
+
+ $event = new IOEvent($callback, $stream, IOEvent::READ);
+ $this->loop->add($event);
+
+ $this->readEvents[(int) $stream] = $event;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addWriteStream($stream, callable $listener)
+ {
+ $callback = function () use ($stream, $listener) {
+ call_user_func($listener, $stream, $this);
+ };
+
+ $event = new IOEvent($callback, $stream, IOEvent::WRITE);
+ $this->loop->add($event);
+
+ $this->writeEvents[(int) $stream] = $event;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeReadStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->readEvents[$key])) {
+ $this->readEvents[$key]->stop();
+ unset($this->readEvents[$key]);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeWriteStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->writeEvents[$key])) {
+ $this->writeEvents[$key]->stop();
+ unset($this->writeEvents[$key]);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeStream($stream)
+ {
+ $this->removeReadStream($stream);
+ $this->removeWriteStream($stream);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addTimer($interval, callable $callback)
+ {
+ $timer = new Timer($this, $interval, $callback, false);
+
+ $callback = function () use ($timer) {
+ call_user_func($timer->getCallback(), $timer);
+
+ if ($this->isTimerActive($timer)) {
+ $this->cancelTimer($timer);
+ }
+ };
+
+ $event = new TimerEvent($callback, $timer->getInterval());
+ $this->timerEvents->attach($timer, $event);
+ $this->loop->add($event);
+
+ return $timer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addPeriodicTimer($interval, callable $callback)
+ {
+ $timer = new Timer($this, $interval, $callback, true);
+
+ $callback = function () use ($timer) {
+ call_user_func($timer->getCallback(), $timer);
+ };
+
+ $event = new TimerEvent($callback, $interval, $interval);
+ $this->timerEvents->attach($timer, $event);
+ $this->loop->add($event);
+
+ return $timer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function cancelTimer(TimerInterface $timer)
+ {
+ if (isset($this->timerEvents[$timer])) {
+ $this->loop->remove($this->timerEvents[$timer]);
+ $this->timerEvents->detach($timer);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isTimerActive(TimerInterface $timer)
+ {
+ return $this->timerEvents->contains($timer);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function nextTick(callable $listener)
+ {
+ $this->nextTickQueue->add($listener);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function futureTick(callable $listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function tick()
+ {
+ $this->nextTickQueue->tick();
+
+ $this->futureTickQueue->tick();
+
+ $this->loop->run(EventLoop::RUN_ONCE | EventLoop::RUN_NOWAIT);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->nextTickQueue->tick();
+
+ $this->futureTickQueue->tick();
+
+ $flags = EventLoop::RUN_ONCE;
+ if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) {
+ $flags |= EventLoop::RUN_NOWAIT;
+ } elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count()) {
+ break;
+ }
+
+ $this->loop->run($flags);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function stop()
+ {
+ $this->running = false;
+ }
+}
diff --git a/libs/React/EventLoop/LibEventLoop.php b/libs/React/EventLoop/LibEventLoop.php
new file mode 100644
index 00000000..1ab2aa3e
--- /dev/null
+++ b/libs/React/EventLoop/LibEventLoop.php
@@ -0,0 +1,343 @@
+eventBase = event_base_new();
+ $this->nextTickQueue = new NextTickQueue($this);
+ $this->futureTickQueue = new FutureTickQueue($this);
+ $this->timerEvents = new SplObjectStorage();
+
+ $this->createTimerCallback();
+ $this->createStreamCallback();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addReadStream($stream, callable $listener)
+ {
+ $key = (int) $stream;
+
+ if (!isset($this->readListeners[$key])) {
+ $this->readListeners[$key] = $listener;
+ $this->subscribeStreamEvent($stream, EV_READ);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addWriteStream($stream, callable $listener)
+ {
+ $key = (int) $stream;
+
+ if (!isset($this->writeListeners[$key])) {
+ $this->writeListeners[$key] = $listener;
+ $this->subscribeStreamEvent($stream, EV_WRITE);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeReadStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->readListeners[$key])) {
+ unset($this->readListeners[$key]);
+ $this->unsubscribeStreamEvent($stream, EV_READ);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeWriteStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->writeListeners[$key])) {
+ unset($this->writeListeners[$key]);
+ $this->unsubscribeStreamEvent($stream, EV_WRITE);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeStream($stream)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->streamEvents[$key])) {
+ $event = $this->streamEvents[$key];
+
+ event_del($event);
+ event_free($event);
+
+ unset(
+ $this->streamFlags[$key],
+ $this->streamEvents[$key],
+ $this->readListeners[$key],
+ $this->writeListeners[$key]
+ );
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addTimer($interval, callable $callback)
+ {
+ $timer = new Timer($this, $interval, $callback, false);
+
+ $this->scheduleTimer($timer);
+
+ return $timer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addPeriodicTimer($interval, callable $callback)
+ {
+ $timer = new Timer($this, $interval, $callback, true);
+
+ $this->scheduleTimer($timer);
+
+ return $timer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function cancelTimer(TimerInterface $timer)
+ {
+ if ($this->isTimerActive($timer)) {
+ $event = $this->timerEvents[$timer];
+
+ event_del($event);
+ event_free($event);
+
+ $this->timerEvents->detach($timer);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isTimerActive(TimerInterface $timer)
+ {
+ return $this->timerEvents->contains($timer);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function nextTick(callable $listener)
+ {
+ $this->nextTickQueue->add($listener);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function futureTick(callable $listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function tick()
+ {
+ $this->nextTickQueue->tick();
+
+ $this->futureTickQueue->tick();
+
+ event_base_loop($this->eventBase, EVLOOP_ONCE | EVLOOP_NONBLOCK);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->nextTickQueue->tick();
+
+ $this->futureTickQueue->tick();
+
+ $flags = EVLOOP_ONCE;
+ if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) {
+ $flags |= EVLOOP_NONBLOCK;
+ } elseif (!$this->streamEvents && !$this->timerEvents->count()) {
+ break;
+ }
+
+ event_base_loop($this->eventBase, $flags);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function stop()
+ {
+ $this->running = false;
+ }
+
+ /**
+ * Schedule a timer for execution.
+ *
+ * @param TimerInterface $timer
+ */
+ private function scheduleTimer(TimerInterface $timer)
+ {
+ $this->timerEvents[$timer] = $event = event_timer_new();
+
+ event_timer_set($event, $this->timerCallback, $timer);
+ event_base_set($event, $this->eventBase);
+ event_add($event, $timer->getInterval() * self::MICROSECONDS_PER_SECOND);
+ }
+
+ /**
+ * Create a new ext-libevent event resource, or update the existing one.
+ *
+ * @param resource $stream
+ * @param integer $flag EV_READ or EV_WRITE
+ */
+ private function subscribeStreamEvent($stream, $flag)
+ {
+ $key = (int) $stream;
+
+ if (isset($this->streamEvents[$key])) {
+ $event = $this->streamEvents[$key];
+ $flags = $this->streamFlags[$key] |= $flag;
+
+ event_del($event);
+ event_set($event, $stream, EV_PERSIST | $flags, $this->streamCallback);
+ } else {
+ $event = event_new();
+
+ event_set($event, $stream, EV_PERSIST | $flag, $this->streamCallback);
+ event_base_set($event, $this->eventBase);
+
+ $this->streamEvents[$key] = $event;
+ $this->streamFlags[$key] = $flag;
+ }
+
+ event_add($event);
+ }
+
+ /**
+ * Update the ext-libevent event resource for this stream to stop listening to
+ * the given event type, or remove it entirely if it's no longer needed.
+ *
+ * @param resource $stream
+ * @param integer $flag EV_READ or EV_WRITE
+ */
+ private function unsubscribeStreamEvent($stream, $flag)
+ {
+ $key = (int) $stream;
+
+ $flags = $this->streamFlags[$key] &= ~$flag;
+
+ if (0 === $flags) {
+ $this->removeStream($stream);
+
+ return;
+ }
+
+ $event = $this->streamEvents[$key];
+
+ event_del($event);
+ event_set($event, $stream, EV_PERSIST | $flags, $this->streamCallback);
+ event_add($event);
+ }
+
+ /**
+ * Create a callback used as the target of timer events.
+ *
+ * A reference is kept to the callback for the lifetime of the loop
+ * to prevent "Cannot destroy active lambda function" fatal error from
+ * the event extension.
+ */
+ private function createTimerCallback()
+ {
+ $this->timerCallback = function ($_, $_, $timer) {
+ call_user_func($timer->getCallback(), $timer);
+
+ // Timer already cancelled ...
+ if (!$this->isTimerActive($timer)) {
+ return;
+
+ // Reschedule periodic timers ...
+ } elseif ($timer->isPeriodic()) {
+ event_add(
+ $this->timerEvents[$timer],
+ $timer->getInterval() * self::MICROSECONDS_PER_SECOND
+ );
+
+ // Clean-up one shot timers ...
+ } else {
+ $this->cancelTimer($timer);
+ }
+ };
+ }
+
+ /**
+ * Create a callback used as the target of stream events.
+ *
+ * A reference is kept to the callback for the lifetime of the loop
+ * to prevent "Cannot destroy active lambda function" fatal error from
+ * the event extension.
+ */
+ private function createStreamCallback()
+ {
+ $this->streamCallback = function ($stream, $flags) {
+ $key = (int) $stream;
+
+ if (EV_READ === (EV_READ & $flags) && isset($this->readListeners[$key])) {
+ call_user_func($this->readListeners[$key], $stream, $this);
+ }
+
+ if (EV_WRITE === (EV_WRITE & $flags) && isset($this->writeListeners[$key])) {
+ call_user_func($this->writeListeners[$key], $stream, $this);
+ }
+ };
+ }
+}
diff --git a/libs/React/EventLoop/LoopInterface.php b/libs/React/EventLoop/LoopInterface.php
new file mode 100644
index 00000000..d046526c
--- /dev/null
+++ b/libs/React/EventLoop/LoopInterface.php
@@ -0,0 +1,121 @@
+nextTickQueue = new NextTickQueue($this);
+ $this->futureTickQueue = new FutureTickQueue($this);
+ $this->timers = new Timers();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addReadStream($stream, callable $listener)
+ {
+ $key = (int) $stream;
+
+ if (!isset($this->readStreams[$key])) {
+ $this->readStreams[$key] = $stream;
+ $this->readListeners[$key] = $listener;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addWriteStream($stream, callable $listener)
+ {
+ $key = (int) $stream;
+
+ if (!isset($this->writeStreams[$key])) {
+ $this->writeStreams[$key] = $stream;
+ $this->writeListeners[$key] = $listener;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeReadStream($stream)
+ {
+ $key = (int) $stream;
+
+ unset(
+ $this->readStreams[$key],
+ $this->readListeners[$key]
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeWriteStream($stream)
+ {
+ $key = (int) $stream;
+
+ unset(
+ $this->writeStreams[$key],
+ $this->writeListeners[$key]
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeStream($stream)
+ {
+ $this->removeReadStream($stream);
+ $this->removeWriteStream($stream);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addTimer($interval, callable $callback)
+ {
+ $timer = new Timer($this, $interval, $callback, false);
+
+ $this->timers->add($timer);
+
+ return $timer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addPeriodicTimer($interval, callable $callback)
+ {
+ $timer = new Timer($this, $interval, $callback, true);
+
+ $this->timers->add($timer);
+
+ return $timer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function cancelTimer(TimerInterface $timer)
+ {
+ $this->timers->cancel($timer);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isTimerActive(TimerInterface $timer)
+ {
+ return $this->timers->contains($timer);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function nextTick(callable $listener)
+ {
+ $this->nextTickQueue->add($listener);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function futureTick(callable $listener)
+ {
+ $this->futureTickQueue->add($listener);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function tick()
+ {
+ $this->nextTickQueue->tick();
+
+ $this->futureTickQueue->tick();
+
+ $this->timers->tick();
+
+ $this->waitForStreamActivity(0);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->running = true;
+
+ while ($this->running) {
+ $this->nextTickQueue->tick();
+
+ $this->futureTickQueue->tick();
+
+ $this->timers->tick();
+
+ // Next-tick or future-tick queues have pending callbacks ...
+ if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) {
+ $timeout = 0;
+
+ // There is a pending timer, only block until it is due ...
+ } elseif ($scheduledAt = $this->timers->getFirst()) {
+ $timeout = $scheduledAt - $this->timers->getTime();
+ if ($timeout < 0) {
+ $timeout = 0;
+ } else {
+ $timeout *= self::MICROSECONDS_PER_SECOND;
+ }
+
+ // The only possible event is stream activity, so wait forever ...
+ } elseif ($this->readStreams || $this->writeStreams) {
+ $timeout = null;
+
+ // There's nothing left to do ...
+ } else {
+ break;
+ }
+
+ $this->waitForStreamActivity($timeout);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function stop()
+ {
+ $this->running = false;
+ }
+
+ /**
+ * Wait/check for stream activity, or until the next timer is due.
+ */
+ private function waitForStreamActivity($timeout)
+ {
+ $read = $this->readStreams;
+ $write = $this->writeStreams;
+
+ $this->streamSelect($read, $write, $timeout);
+
+ foreach ($read as $stream) {
+ $key = (int) $stream;
+
+ if (isset($this->readListeners[$key])) {
+ call_user_func($this->readListeners[$key], $stream, $this);
+ }
+ }
+
+ foreach ($write as $stream) {
+ $key = (int) $stream;
+
+ if (isset($this->writeListeners[$key])) {
+ call_user_func($this->writeListeners[$key], $stream, $this);
+ }
+ }
+ }
+
+ /**
+ * Emulate a stream_select() implementation that does not break when passed
+ * empty stream arrays.
+ *
+ * @param array &$read An array of read streams to select upon.
+ * @param array &$write An array of write streams to select upon.
+ * @param integer|null $timeout Activity timeout in microseconds, or null to wait forever.
+ *
+ * @return integer The total number of streams that are ready for read/write.
+ */
+ protected function streamSelect(array &$read, array &$write, $timeout)
+ {
+ if ($read || $write) {
+ $except = null;
+
+ return stream_select($read, $write, $except, $timeout === null ? null : 0, $timeout);
+ }
+
+ usleep($timeout);
+
+ return 0;
+ }
+}
diff --git a/libs/React/EventLoop/Tick/FutureTickQueue.php b/libs/React/EventLoop/Tick/FutureTickQueue.php
new file mode 100644
index 00000000..eeffd363
--- /dev/null
+++ b/libs/React/EventLoop/Tick/FutureTickQueue.php
@@ -0,0 +1,59 @@
+eventLoop = $eventLoop;
+ $this->queue = new SplQueue();
+ }
+
+ /**
+ * Add a callback to be invoked on a future tick of the event loop.
+ *
+ * Callbacks are guaranteed to be executed in the order they are enqueued.
+ *
+ * @param callable $listener The callback to invoke.
+ */
+ public function add(callable $listener)
+ {
+ $this->queue->enqueue($listener);
+ }
+
+ /**
+ * Flush the callback queue.
+ */
+ public function tick()
+ {
+ // Only invoke as many callbacks as were on the queue when tick() was called.
+ $count = $this->queue->count();
+
+ while ($count--) {
+ call_user_func(
+ $this->queue->dequeue(),
+ $this->eventLoop
+ );
+ }
+ }
+
+ /**
+ * Check if the next tick queue is empty.
+ *
+ * @return boolean
+ */
+ public function isEmpty()
+ {
+ return $this->queue->isEmpty();
+ }
+}
diff --git a/libs/React/EventLoop/Tick/NextTickQueue.php b/libs/React/EventLoop/Tick/NextTickQueue.php
new file mode 100644
index 00000000..5b8e1de8
--- /dev/null
+++ b/libs/React/EventLoop/Tick/NextTickQueue.php
@@ -0,0 +1,57 @@
+eventLoop = $eventLoop;
+ $this->queue = new SplQueue();
+ }
+
+ /**
+ * Add a callback to be invoked on the next tick of the event loop.
+ *
+ * Callbacks are guaranteed to be executed in the order they are enqueued,
+ * before any timer or stream events.
+ *
+ * @param callable $listener The callback to invoke.
+ */
+ public function add(callable $listener)
+ {
+ $this->queue->enqueue($listener);
+ }
+
+ /**
+ * Flush the callback queue.
+ */
+ public function tick()
+ {
+ while (!$this->queue->isEmpty()) {
+ call_user_func(
+ $this->queue->dequeue(),
+ $this->eventLoop
+ );
+ }
+ }
+
+ /**
+ * Check if the next tick queue is empty.
+ *
+ * @return boolean
+ */
+ public function isEmpty()
+ {
+ return $this->queue->isEmpty();
+ }
+}
diff --git a/libs/React/EventLoop/Timer/Timer.php b/libs/React/EventLoop/Timer/Timer.php
new file mode 100644
index 00000000..f670ab3c
--- /dev/null
+++ b/libs/React/EventLoop/Timer/Timer.php
@@ -0,0 +1,102 @@
+loop = $loop;
+ $this->interval = (float) $interval;
+ $this->callback = $callback;
+ $this->periodic = (bool) $periodic;
+ $this->data = null;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLoop()
+ {
+ return $this->loop;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getInterval()
+ {
+ return $this->interval;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCallback()
+ {
+ return $this->callback;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setData($data)
+ {
+ $this->data = $data;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isPeriodic()
+ {
+ return $this->periodic;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isActive()
+ {
+ return $this->loop->isTimerActive($this);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function cancel()
+ {
+ $this->loop->cancelTimer($this);
+ }
+}
diff --git a/libs/React/EventLoop/Timer/TimerInterface.php b/libs/React/EventLoop/Timer/TimerInterface.php
new file mode 100644
index 00000000..d066f369
--- /dev/null
+++ b/libs/React/EventLoop/Timer/TimerInterface.php
@@ -0,0 +1,62 @@
+timers = new SplObjectStorage();
+ $this->scheduler = new SplPriorityQueue();
+ }
+
+ public function updateTime()
+ {
+ return $this->time = microtime(true);
+ }
+
+ public function getTime()
+ {
+ return $this->time ?: $this->updateTime();
+ }
+
+ public function add(TimerInterface $timer)
+ {
+ $interval = $timer->getInterval();
+ $scheduledAt = $interval + $this->getTime();
+
+ $this->timers->attach($timer, $scheduledAt);
+ $this->scheduler->insert($timer, -$scheduledAt);
+ }
+
+ public function contains(TimerInterface $timer)
+ {
+ return $this->timers->contains($timer);
+ }
+
+ public function cancel(TimerInterface $timer)
+ {
+ $this->timers->detach($timer);
+ }
+
+ public function getFirst()
+ {
+ while ($this->scheduler->count()) {
+ $timer = $this->scheduler->top();
+
+ if ($this->timers->contains($timer)) {
+ return $this->timers[$timer];
+ }
+
+ $this->scheduler->extract();
+ }
+
+ return null;
+ }
+
+ public function isEmpty()
+ {
+ return count($this->timers) === 0;
+ }
+
+ public function tick()
+ {
+ $time = $this->updateTime();
+ $timers = $this->timers;
+ $scheduler = $this->scheduler;
+
+ while (!$scheduler->isEmpty()) {
+ $timer = $scheduler->top();
+
+ if (!isset($timers[$timer])) {
+ $scheduler->extract();
+ $timers->detach($timer);
+
+ continue;
+ }
+
+ if ($timers[$timer] >= $time) {
+ break;
+ }
+
+ $scheduler->extract();
+ call_user_func($timer->getCallback(), $timer);
+
+ if ($timer->isPeriodic() && isset($timers[$timer])) {
+ $timers[$timer] = $scheduledAt = $timer->getInterval() + $time;
+ $scheduler->insert($timer, -$scheduledAt);
+ } else {
+ $timers->detach($timer);
+ }
+ }
+ }
+}
diff --git a/libs/React/Socket/Connection.php b/libs/React/Socket/Connection.php
new file mode 100644
index 00000000..2bdf2180
--- /dev/null
+++ b/libs/React/Socket/Connection.php
@@ -0,0 +1,42 @@
+bufferSize);
+ if ('' !== $data && false !== $data) {
+ $this->emit('data', array($data, $this));
+ }
+
+ if ('' === $data || false === $data || !is_resource($stream) || feof($stream)) {
+ $this->end();
+ }
+ }
+
+ public function handleClose()
+ {
+ if (is_resource($this->stream)) {
+ // http://chat.stackoverflow.com/transcript/message/7727858#7727858
+ stream_socket_shutdown($this->stream, STREAM_SHUT_RDWR);
+ stream_set_blocking($this->stream, false);
+ fclose($this->stream);
+ }
+ }
+
+ public function getRemoteAddress()
+ {
+ return $this->parseAddress(stream_socket_get_name($this->stream, true));
+ }
+
+ private function parseAddress($address)
+ {
+ return trim(substr($address, 0, strrpos($address, ':')), '[]');
+ }
+}
diff --git a/libs/React/Socket/ConnectionException.php b/libs/React/Socket/ConnectionException.php
new file mode 100644
index 00000000..72b10280
--- /dev/null
+++ b/libs/React/Socket/ConnectionException.php
@@ -0,0 +1,7 @@
+loop = $loop;
+ }
+
+ public function listen($port, $host = '127.0.0.1')
+ {
+ if (strpos($host, ':') !== false) {
+ // enclose IPv6 addresses in square brackets before appending port
+ $host = '[' . $host . ']';
+ }
+
+ $this->master = @stream_socket_server("tcp://$host:$port", $errno, $errstr);
+ if (false === $this->master) {
+ $message = "Could not bind to tcp://$host:$port: $errstr";
+ throw new ConnectionException($message, $errno);
+ }
+ stream_set_blocking($this->master, 0);
+
+ $this->loop->addReadStream($this->master, function ($master) {
+ $newSocket = stream_socket_accept($master);
+ if (false === $newSocket) {
+ $this->emit('error', array(new \RuntimeException('Error accepting new connection')));
+
+ return;
+ }
+ $this->handleConnection($newSocket);
+ });
+ }
+
+ public function handleConnection($socket)
+ {
+ stream_set_blocking($socket, 0);
+
+ $client = $this->createConnection($socket);
+
+ $this->emit('connection', array($client));
+ }
+
+ public function getPort()
+ {
+ $name = stream_socket_get_name($this->master, false);
+
+ return (int) substr(strrchr($name, ':'), 1);
+ }
+
+ public function shutdown()
+ {
+ $this->loop->removeStream($this->master);
+ fclose($this->master);
+ $this->removeAllListeners();
+ }
+
+ public function createConnection($socket)
+ {
+ return new Connection($socket, $this->loop);
+ }
+}
diff --git a/libs/React/Socket/ServerInterface.php b/libs/React/Socket/ServerInterface.php
new file mode 100644
index 00000000..3665a165
--- /dev/null
+++ b/libs/React/Socket/ServerInterface.php
@@ -0,0 +1,13 @@
+ 0,
+ 'message' => '',
+ 'file' => '',
+ 'line' => 0,
+ );
+
+ public function __construct($stream, LoopInterface $loop)
+ {
+ $this->stream = $stream;
+ $this->loop = $loop;
+ }
+
+ public function isWritable()
+ {
+ return $this->writable;
+ }
+
+ public function write($data)
+ {
+ if (!$this->writable) {
+ return;
+ }
+
+ $this->data .= $data;
+
+ if (!$this->listening) {
+ $this->listening = true;
+
+ $this->loop->addWriteStream($this->stream, array($this, 'handleWrite'));
+ }
+
+ $belowSoftLimit = strlen($this->data) < $this->softLimit;
+
+ return $belowSoftLimit;
+ }
+
+ public function end($data = null)
+ {
+ if (null !== $data) {
+ $this->write($data);
+ }
+
+ $this->writable = false;
+
+ if ($this->listening) {
+ $this->on('full-drain', array($this, 'close'));
+ } else {
+ $this->close();
+ }
+ }
+
+ public function close()
+ {
+ $this->writable = false;
+ $this->listening = false;
+ $this->data = '';
+
+ $this->emit('close', [$this]);
+ }
+
+ public function handleWrite()
+ {
+ if (!is_resource($this->stream)) {
+ $this->emit('error', array(new \RuntimeException('Tried to write to invalid stream.'), $this));
+
+ return;
+ }
+
+ set_error_handler(array($this, 'errorHandler'));
+
+ $sent = fwrite($this->stream, $this->data);
+
+ restore_error_handler();
+
+ if (false === $sent) {
+ $this->emit('error', array(
+ new \ErrorException(
+ $this->lastError['message'],
+ 0,
+ $this->lastError['number'],
+ $this->lastError['file'],
+ $this->lastError['line']
+ ),
+ $this
+ ));
+
+ return;
+ }
+
+ if (0 === $sent && feof($this->stream)) {
+ $this->emit('error', array(new \RuntimeException('Tried to write to closed stream.'), $this));
+
+ return;
+ }
+
+ $len = strlen($this->data);
+ if ($len >= $this->softLimit && $len - $sent < $this->softLimit) {
+ $this->emit('drain', [$this]);
+ }
+
+ $this->data = (string) substr($this->data, $sent);
+
+ if (0 === strlen($this->data)) {
+ $this->loop->removeWriteStream($this->stream);
+ $this->listening = false;
+
+ $this->emit('full-drain', [$this]);
+ }
+ }
+
+ private function errorHandler($errno, $errstr, $errfile, $errline)
+ {
+ $this->lastError['number'] = $errno;
+ $this->lastError['message'] = $errstr;
+ $this->lastError['file'] = $errfile;
+ $this->lastError['line'] = $errline;
+ }
+}
diff --git a/libs/React/Stream/BufferedSink.php b/libs/React/Stream/BufferedSink.php
new file mode 100644
index 00000000..a6b35c01
--- /dev/null
+++ b/libs/React/Stream/BufferedSink.php
@@ -0,0 +1,59 @@
+deferred = new Deferred();
+
+ $this->on('pipe', array($this, 'handlePipeEvent'));
+ $this->on('error', array($this, 'handleErrorEvent'));
+ }
+
+ public function handlePipeEvent($source)
+ {
+ Util::forwardEvents($source, $this, array('error'));
+ }
+
+ public function handleErrorEvent($e)
+ {
+ $this->deferred->reject($e);
+ }
+
+ public function write($data)
+ {
+ $this->buffer .= $data;
+ $this->deferred->progress($data);
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ parent::close();
+ $this->deferred->resolve($this->buffer);
+ }
+
+ public function promise()
+ {
+ return $this->deferred->promise();
+ }
+
+ public static function createPromise(ReadableStreamInterface $stream)
+ {
+ $sink = new static();
+ $stream->pipe($sink);
+
+ return $sink->promise();
+ }
+}
diff --git a/libs/React/Stream/CompositeStream.php b/libs/React/Stream/CompositeStream.php
new file mode 100644
index 00000000..b2f88b0a
--- /dev/null
+++ b/libs/React/Stream/CompositeStream.php
@@ -0,0 +1,84 @@
+readable = $readable;
+ $this->writable = $writable;
+
+ Util::forwardEvents($this->readable, $this, array('data', 'end', 'error', 'close'));
+ Util::forwardEvents($this->writable, $this, array('drain', 'error', 'close', 'pipe'));
+
+ $this->readable->on('close', array($this, 'close'));
+ $this->writable->on('close', array($this, 'close'));
+
+ $this->on('pipe', array($this, 'handlePipeEvent'));
+ }
+
+ public function handlePipeEvent($source)
+ {
+ $this->pipeSource = $source;
+ }
+
+ public function isReadable()
+ {
+ return $this->readable->isReadable();
+ }
+
+ public function pause()
+ {
+ if ($this->pipeSource) {
+ $this->pipeSource->pause();
+ }
+
+ $this->readable->pause();
+ }
+
+ public function resume()
+ {
+ if ($this->pipeSource) {
+ $this->pipeSource->resume();
+ }
+
+ $this->readable->resume();
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+
+ public function isWritable()
+ {
+ return $this->writable->isWritable();
+ }
+
+ public function write($data)
+ {
+ return $this->writable->write($data);
+ }
+
+ public function end($data = null)
+ {
+ $this->writable->end($data);
+ }
+
+ public function close()
+ {
+ $this->pipeSource = null;
+
+ $this->readable->close();
+ $this->writable->close();
+ }
+}
diff --git a/libs/React/Stream/DuplexStreamInterface.php b/libs/React/Stream/DuplexStreamInterface.php
new file mode 100644
index 00000000..bd370d01
--- /dev/null
+++ b/libs/React/Stream/DuplexStreamInterface.php
@@ -0,0 +1,7 @@
+closed;
+ }
+
+ public function pause()
+ {
+ }
+
+ public function resume()
+ {
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->closed = true;
+ $this->emit('end', array($this));
+ $this->emit('close', array($this));
+ $this->removeAllListeners();
+ }
+}
diff --git a/libs/React/Stream/ReadableStreamInterface.php b/libs/React/Stream/ReadableStreamInterface.php
new file mode 100644
index 00000000..312fc47e
--- /dev/null
+++ b/libs/React/Stream/ReadableStreamInterface.php
@@ -0,0 +1,20 @@
+stream = $stream;
+ if (!is_resource($this->stream) || get_resource_type($this->stream) !== "stream") {
+ throw new InvalidArgumentException('First parameter must be a valid stream resource');
+ }
+
+ stream_set_blocking($this->stream, 0);
+
+ $this->loop = $loop;
+ $this->buffer = new Buffer($this->stream, $this->loop);
+
+ $this->buffer->on('error', function ($error) {
+ $this->emit('error', array($error, $this));
+ $this->close();
+ });
+
+ $this->buffer->on('drain', function () {
+ $this->emit('drain', array($this));
+ });
+
+ $this->resume();
+ }
+
+ public function isReadable()
+ {
+ return $this->readable;
+ }
+
+ public function isWritable()
+ {
+ return $this->writable;
+ }
+
+ public function pause()
+ {
+ $this->loop->removeReadStream($this->stream);
+ }
+
+ public function resume()
+ {
+ if ($this->readable) {
+ $this->loop->addReadStream($this->stream, array($this, 'handleData'));
+ }
+ }
+
+ public function write($data)
+ {
+ if (!$this->writable) {
+ return;
+ }
+
+ return $this->buffer->write($data);
+ }
+
+ public function close()
+ {
+ if (!$this->writable && !$this->closing) {
+ return;
+ }
+
+ $this->closing = false;
+
+ $this->readable = false;
+ $this->writable = false;
+
+ $this->emit('end', array($this));
+ $this->emit('close', array($this));
+ $this->loop->removeStream($this->stream);
+ $this->buffer->removeAllListeners();
+ $this->removeAllListeners();
+
+ $this->handleClose();
+ }
+
+ public function end($data = null)
+ {
+ if (!$this->writable) {
+ return;
+ }
+
+ $this->closing = true;
+
+ $this->readable = false;
+ $this->writable = false;
+
+ $this->buffer->on('close', function () {
+ $this->close();
+ });
+
+ $this->buffer->end($data);
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+
+ public function handleData($stream)
+ {
+ $data = fread($stream, $this->bufferSize);
+
+ $this->emit('data', array($data, $this));
+
+ if (!is_resource($stream) || feof($stream)) {
+ $this->end();
+ }
+ }
+
+ public function handleClose()
+ {
+ if (is_resource($this->stream)) {
+ fclose($this->stream);
+ }
+ }
+
+ public function getBuffer()
+ {
+ return $this->buffer;
+ }
+}
diff --git a/libs/React/Stream/ThroughStream.php b/libs/React/Stream/ThroughStream.php
new file mode 100644
index 00000000..a6a47495
--- /dev/null
+++ b/libs/React/Stream/ThroughStream.php
@@ -0,0 +1,33 @@
+readable->emit('data', array($this->filter($data), $this));
+ }
+
+ public function end($data = null)
+ {
+ if (null !== $data) {
+ $this->readable->emit('data', array($this->filter($data), $this));
+ }
+
+ $this->writable->end($data);
+ }
+}
diff --git a/libs/React/Stream/Util.php b/libs/React/Stream/Util.php
new file mode 100644
index 00000000..c2445a63
--- /dev/null
+++ b/libs/React/Stream/Util.php
@@ -0,0 +1,45 @@
+emit('pipe', array($source));
+
+ $source->on('data', function ($data) use ($source, $dest) {
+ $feedMore = $dest->write($data);
+
+ if (false === $feedMore) {
+ $source->pause();
+ }
+ });
+
+ $dest->on('drain', function () use ($source) {
+ $source->resume();
+ });
+
+ $end = isset($options['end']) ? $options['end'] : true;
+ if ($end && $source !== $dest) {
+ $source->on('end', function () use ($dest) {
+ $dest->end();
+ });
+ }
+ }
+
+ public static function forwardEvents($source, $target, array $events)
+ {
+ foreach ($events as $event) {
+ $source->on($event, function () use ($event, $target) {
+ $target->emit($event, func_get_args());
+ });
+ }
+ }
+}
diff --git a/libs/React/Stream/WritableStream.php b/libs/React/Stream/WritableStream.php
new file mode 100644
index 00000000..9118bdb1
--- /dev/null
+++ b/libs/React/Stream/WritableStream.php
@@ -0,0 +1,40 @@
+write($data);
+ }
+
+ $this->close();
+ }
+
+ public function isWritable()
+ {
+ return !$this->closed;
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->closed = true;
+ $this->emit('end', array($this));
+ $this->emit('close', array($this));
+ $this->removeAllListeners();
+ }
+}
diff --git a/libs/React/Stream/WritableStreamInterface.php b/libs/React/Stream/WritableStreamInterface.php
new file mode 100644
index 00000000..84b93ee6
--- /dev/null
+++ b/libs/React/Stream/WritableStreamInterface.php
@@ -0,0 +1,19 @@
+listenerIds) as $serviceEventName) {
+ foreach ($this->listenerIds as $serviceEventName => $args) {
$this->lazyLoad($serviceEventName);
}
} else {
diff --git a/libs/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/libs/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php
index b796a812..7653ccfb 100644
--- a/libs/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php
+++ b/libs/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php
@@ -31,6 +31,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
private $called;
private $dispatcher;
+ private $wrappedListeners;
/**
* Constructor.
@@ -45,6 +46,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
$this->stopwatch = $stopwatch;
$this->logger = $logger;
$this->called = array();
+ $this->wrappedListeners = array();
}
/**
@@ -68,6 +70,16 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
*/
public function removeListener($eventName, $listener)
{
+ if (isset($this->wrappedListeners[$eventName])) {
+ foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
+ if ($wrappedListener->getWrappedListener() === $listener) {
+ $listener = $wrappedListener;
+ unset($this->wrappedListeners[$eventName][$index]);
+ break;
+ }
+ }
+ }
+
return $this->dispatcher->removeListener($eventName, $listener);
}
@@ -146,7 +158,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
$allListeners = $this->getListeners();
} catch (\Exception $e) {
if (null !== $this->logger) {
- $this->logger->info(sprintf('An exception was thrown while getting the uncalled listeners (%s)', $e->getMessage()), array('exception' => $e));
+ $this->logger->info('An exception was thrown while getting the uncalled listeners.', array('exception' => $e));
}
// unable to retrieve the uncalled listeners
@@ -216,12 +228,15 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
$this->dispatcher->removeListener($eventName, $listener);
$info = $this->getListenerInfo($listener, $eventName);
$name = isset($info['class']) ? $info['class'] : $info['type'];
- $this->dispatcher->addListener($eventName, new WrappedListener($listener, $name, $this->stopwatch));
+ $wrappedListener = new WrappedListener($listener, $name, $this->stopwatch, $this);
+ $this->wrappedListeners[$eventName][] = $wrappedListener;
+ $this->dispatcher->addListener($eventName, $wrappedListener);
}
}
private function postProcess($eventName)
{
+ unset($this->wrappedListeners[$eventName]);
$skipped = false;
foreach ($this->dispatcher->getListeners($eventName) as $listener) {
if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch.
@@ -259,7 +274,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
}
/**
- * Returns information about the listener
+ * Returns information about the listener.
*
* @param object $listener The listener
* @param string $eventName The event name
diff --git a/libs/Symfony/Component/EventDispatcher/Debug/WrappedListener.php b/libs/Symfony/Component/EventDispatcher/Debug/WrappedListener.php
index c501662b..e16627d6 100644
--- a/libs/Symfony/Component/EventDispatcher/Debug/WrappedListener.php
+++ b/libs/Symfony/Component/EventDispatcher/Debug/WrappedListener.php
@@ -25,12 +25,14 @@ class WrappedListener
private $called;
private $stoppedPropagation;
private $stopwatch;
+ private $dispatcher;
- public function __construct($listener, $name, Stopwatch $stopwatch)
+ public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
{
$this->listener = $listener;
$this->name = $name;
$this->stopwatch = $stopwatch;
+ $this->dispatcher = $dispatcher;
$this->called = false;
$this->stoppedPropagation = false;
}
@@ -56,7 +58,7 @@ class WrappedListener
$e = $this->stopwatch->start($this->name, 'event_listener');
- call_user_func($this->listener, $event, $eventName, $dispatcher);
+ call_user_func($this->listener, $event, $eventName, $this->dispatcher ?: $dispatcher);
if ($e->isStarted()) {
$e->stop();
diff --git a/libs/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php b/libs/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php
index afe3ecd1..7e74a37a 100644
--- a/libs/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php
+++ b/libs/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php
@@ -91,8 +91,12 @@ class RegisterListenersPass implements CompilerPassInterface
throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id));
}
+ if ($def->isAbstract()) {
+ throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event subscribers are lazy-loaded.', $id));
+ }
+
// We must assume that the class value has been correctly filled, even if the service is created by a factory
- $class = $def->getClass();
+ $class = $container->getParameterBag()->resolveValue($def->getClass());
$refClass = new \ReflectionClass($class);
$interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
diff --git a/libs/Symfony/Component/EventDispatcher/Event.php b/libs/Symfony/Component/EventDispatcher/Event.php
index dc39b05d..048bf0ac 100644
--- a/libs/Symfony/Component/EventDispatcher/Event.php
+++ b/libs/Symfony/Component/EventDispatcher/Event.php
@@ -77,7 +77,7 @@ class Event
*
* @param EventDispatcherInterface $dispatcher
*
- * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
+ * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
*
* @api
*/
@@ -91,12 +91,14 @@ class Event
*
* @return EventDispatcherInterface
*
- * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
+ * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
*
* @api
*/
public function getDispatcher()
{
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 2.4 and will be removed in 3.0. The event dispatcher instance can be received in the listener call instead.', E_USER_DEPRECATED);
+
return $this->dispatcher;
}
@@ -105,12 +107,14 @@ class Event
*
* @return string
*
- * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call.
+ * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call.
*
* @api
*/
public function getName()
{
+ @trigger_error('The '.__METHOD__.' method is deprecated since version 2.4 and will be removed in 3.0. The event name can be received in the listener call instead.', E_USER_DEPRECATED);
+
return $this->name;
}
@@ -119,7 +123,7 @@ class Event
*
* @param string $name The event name.
*
- * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call.
+ * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call.
*
* @api
*/
diff --git a/libs/Symfony/Component/EventDispatcher/EventDispatcher.php b/libs/Symfony/Component/EventDispatcher/EventDispatcher.php
index 3b032fb0..46c11100 100644
--- a/libs/Symfony/Component/EventDispatcher/EventDispatcher.php
+++ b/libs/Symfony/Component/EventDispatcher/EventDispatcher.php
@@ -68,7 +68,7 @@ class EventDispatcher implements EventDispatcherInterface
return $this->sorted[$eventName];
}
- foreach (array_keys($this->listeners) as $eventName) {
+ foreach ($this->listeners as $eventName => $eventListeners) {
if (!isset($this->sorted[$eventName])) {
$this->sortListeners($eventName);
}
diff --git a/libs/Symfony/Component/EventDispatcher/EventDispatcherInterface.php b/libs/Symfony/Component/EventDispatcher/EventDispatcherInterface.php
index efb7c5be..9d9fc4d4 100644
--- a/libs/Symfony/Component/EventDispatcher/EventDispatcherInterface.php
+++ b/libs/Symfony/Component/EventDispatcher/EventDispatcherInterface.php
@@ -77,7 +77,7 @@ interface EventDispatcherInterface
public function removeSubscriber(EventSubscriberInterface $subscriber);
/**
- * Gets the listeners of a specific event or all listeners.
+ * Gets the listeners of a specific event or all listeners sorted by descending priority.
*
* @param string $eventName The name of the event
*
diff --git a/libs/Symfony/Component/EventDispatcher/GenericEvent.php b/libs/Symfony/Component/EventDispatcher/GenericEvent.php
index a8955ca4..6458180a 100644
--- a/libs/Symfony/Component/EventDispatcher/GenericEvent.php
+++ b/libs/Symfony/Component/EventDispatcher/GenericEvent.php
@@ -71,7 +71,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
return $this->arguments[$key];
}
- throw new \InvalidArgumentException(sprintf('%s not found in %s', $key, $this->getName()));
+ throw new \InvalidArgumentException(sprintf('Argument "%s" not found.', $key));
}
/**
diff --git a/libs/Symfony/Component/EventDispatcher/README.md b/libs/Symfony/Component/EventDispatcher/README.md
index 0fbc35e2..8031f4dd 100644
--- a/libs/Symfony/Component/EventDispatcher/README.md
+++ b/libs/Symfony/Component/EventDispatcher/README.md
@@ -23,5 +23,5 @@ Resources
You can run the unit tests with the following command:
$ cd path/to/Symfony/Component/EventDispatcher/
- $ composer.phar install
+ $ composer install
$ phpunit
diff --git a/libs/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php b/libs/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php
index b9e41949..5ff5be84 100644
--- a/libs/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php
+++ b/libs/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php
@@ -118,10 +118,21 @@ abstract class AbstractEventDispatcherTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo));
$event = new Event();
$return = $this->dispatcher->dispatch(self::preFoo, $event);
- $this->assertEquals('pre.foo', $event->getName());
$this->assertSame($event, $return);
}
+ /**
+ * @group legacy
+ */
+ public function testLegacyDispatch()
+ {
+ $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
+
+ $event = new Event();
+ $return = $this->dispatcher->dispatch(self::preFoo, $event);
+ $this->assertEquals('pre.foo', $event->getName());
+ }
+
public function testDispatchForClosure()
{
$invoked = 0;
@@ -239,8 +250,13 @@ abstract class AbstractEventDispatcherTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
}
- public function testEventReceivesTheDispatcherInstance()
+ /**
+ * @group legacy
+ */
+ public function testLegacyEventReceivesTheDispatcherInstance()
{
+ $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
+
$dispatcher = null;
$this->dispatcher->addListener('test', function ($event) use (&$dispatcher) {
$dispatcher = $event->getDispatcher();
diff --git a/libs/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/libs/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php
index 47dd5da1..24e60024 100644
--- a/libs/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php
+++ b/libs/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\EventDispatcher\Tests\Debug;
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;
@@ -86,6 +87,20 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(), $tdispatcher->getNotCalledListeners());
}
+ public function testGetCalledListenersNested()
+ {
+ $tdispatcher = null;
+ $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+ $dispatcher->addListener('foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) {
+ $tdispatcher = $dispatcher;
+ $dispatcher->dispatch('bar');
+ });
+ $dispatcher->addListener('bar', function (Event $event) {});
+ $dispatcher->dispatch('foo');
+ $this->assertSame($dispatcher, $tdispatcher);
+ $this->assertCount(2, $dispatcher->getCalledListeners());
+ }
+
public function testLogger()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');
@@ -160,6 +175,19 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
$dispatcher->dispatch('foo');
$this->assertTrue($nestedCall);
}
+
+ public function testListenerCanRemoveItselfWhenExecuted()
+ {
+ $eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+ $listener1 = function ($event, $eventName, EventDispatcherInterface $dispatcher) use (&$listener1) {
+ $dispatcher->removeListener('foo', $listener1);
+ };
+ $eventDispatcher->addListener('foo', $listener1);
+ $eventDispatcher->addListener('foo', function () {});
+ $eventDispatcher->dispatch('foo');
+
+ $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed');
+ }
}
class EventSubscriber implements EventSubscriberInterface
diff --git a/libs/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php b/libs/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php
index b291e1ee..0fdd6372 100644
--- a/libs/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php
+++ b/libs/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php
@@ -138,6 +138,58 @@ class RegisterListenersPassTest extends \PHPUnit_Framework_TestCase
$registerListenersPass = new RegisterListenersPass();
$registerListenersPass->process($container);
}
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage The service "foo" must not be abstract as event subscribers are lazy-loaded.
+ */
+ public function testAbstractEventSubscriber()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_subscriber', array());
+ $container->register('event_dispatcher', 'stdClass');
+
+ $registerListenersPass = new RegisterListenersPass();
+ $registerListenersPass->process($container);
+ }
+
+ public function testEventSubscriberResolvableClassName()
+ {
+ $container = new ContainerBuilder();
+
+ $container->setParameter('subscriber.class', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService');
+ $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array());
+ $container->register('event_dispatcher', 'stdClass');
+
+ $registerListenersPass = new RegisterListenersPass();
+ $registerListenersPass->process($container);
+
+ $definition = $container->getDefinition('event_dispatcher');
+ $expected_calls = array(
+ array(
+ 'addSubscriberService',
+ array(
+ 'foo',
+ 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService',
+ ),
+ ),
+ );
+ $this->assertSame($expected_calls, $definition->getMethodCalls());
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage You have requested a non-existent parameter "subscriber.class"
+ */
+ public function testEventSubscriberUnresolvableClassName()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array());
+ $container->register('event_dispatcher', 'stdClass');
+
+ $registerListenersPass = new RegisterListenersPass();
+ $registerListenersPass->process($container);
+ }
}
class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
diff --git a/libs/Symfony/Component/EventDispatcher/Tests/EventTest.php b/libs/Symfony/Component/EventDispatcher/Tests/EventTest.php
index 8f2fb735..4bd26972 100644
--- a/libs/Symfony/Component/EventDispatcher/Tests/EventTest.php
+++ b/libs/Symfony/Component/EventDispatcher/Tests/EventTest.php
@@ -60,6 +60,9 @@ class EventTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($this->event->isPropagationStopped());
}
+ /**
+ * @group legacy
+ */
public function testLegacySetDispatcher()
{
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
@@ -67,18 +70,27 @@ class EventTest extends \PHPUnit_Framework_TestCase
$this->assertSame($this->dispatcher, $this->event->getDispatcher());
}
+ /**
+ * @group legacy
+ */
public function testLegacyGetDispatcher()
{
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
$this->assertNull($this->event->getDispatcher());
}
+ /**
+ * @group legacy
+ */
public function testLegacyGetName()
{
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
$this->assertNull($this->event->getName());
}
+ /**
+ * @group legacy
+ */
public function testLegacySetName()
{
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
diff --git a/libs/Symfony/Component/EventDispatcher/composer.json b/libs/Symfony/Component/EventDispatcher/composer.json
index 6c58d4e4..d7058629 100644
--- a/libs/Symfony/Component/EventDispatcher/composer.json
+++ b/libs/Symfony/Component/EventDispatcher/composer.json
@@ -3,7 +3,7 @@
"type": "library",
"description": "Symfony EventDispatcher Component",
"keywords": [],
- "homepage": "http://symfony.com",
+ "homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
@@ -12,13 +12,14 @@
},
{
"name": "Symfony Community",
- "homepage": "http://symfony.com/contributors"
+ "homepage": "https://symfony.com/contributors"
}
],
"require": {
- "php": ">=5.3.3"
+ "php": ">=5.3.9"
},
"require-dev": {
+ "symfony/phpunit-bridge": "~2.7",
"symfony/dependency-injection": "~2.6",
"symfony/expression-language": "~2.6",
"symfony/config": "~2.0,>=2.0.5",
@@ -30,13 +31,12 @@
"symfony/http-kernel": ""
},
"autoload": {
- "psr-0": { "Symfony\\Component\\EventDispatcher\\": "" }
+ "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" }
},
- "target-dir": "Symfony/Component/EventDispatcher",
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "2.6-dev"
+ "dev-master": "2.7-dev"
}
}
}
diff --git a/libs/Symfony/Component/EventDispatcher/phpunit.xml.dist b/libs/Symfony/Component/EventDispatcher/phpunit.xml.dist
index 9a7728e9..ae0586e0 100644
--- a/libs/Symfony/Component/EventDispatcher/phpunit.xml.dist
+++ b/libs/Symfony/Component/EventDispatcher/phpunit.xml.dist
@@ -7,9 +7,9 @@
bootstrap="vendor/autoload.php"
>
-
-
+
+
./Tests/
diff --git a/libs/curl-easy/cURL/Event.php b/libs/curl-easy/cURL/Event.php
index d98238c7..53804cf6 100644
--- a/libs/curl-easy/cURL/Event.php
+++ b/libs/curl-easy/cURL/Event.php
@@ -5,6 +5,18 @@ use Symfony\Component\EventDispatcher\Event as SymfonyEvent;
class Event extends SymfonyEvent
{
- /** @var Response $response */
- public $response = null;
+ /**
+ * @var Response
+ */
+ public $response;
+
+ /**
+ * @var Request
+ */
+ public $request;
+
+ /**
+ * @var RequestsQueue
+ */
+ public $queue;
}
diff --git a/libs/curl-easy/cURL/Request.php b/libs/curl-easy/cURL/Request.php
index 461d8ad8..199ebfac 100644
--- a/libs/curl-easy/cURL/Request.php
+++ b/libs/curl-easy/cURL/Request.php
@@ -54,7 +54,7 @@ class Request extends EventDispatcher implements RequestInterface
public function getOptions()
{
if (!isset($this->options)) {
- $this->options = new Options;
+ $this->options = new Options();
}
return $this->options;
}
@@ -97,6 +97,8 @@ class Request extends EventDispatcher implements RequestInterface
* This function should be called after initializing a cURL
* session and all the options for the session are set.
*
+ * Warning: it doesn't fire 'complete' event.
+ *
* @return Response
*/
public function send()
@@ -113,32 +115,31 @@ class Request extends EventDispatcher implements RequestInterface
}
return $response;
}
-
- protected function prepareQueue()
- {
- if (!isset($this->queue)) {
- $request = $this;
- $this->queue = new RequestsQueue;
- $this->queue->addListener(
- 'complete',
- function ($event) use ($request) {
- $request->dispatch('complete', $event);
- }
- );
- $this->queue->attach($this);
- }
- }
-
+
+ /**
+ * Creates new RequestsQueue with single Request attached to it
+ * and calls RequestsQueue::socketPerform() method.
+ *
+ * @see RequestsQueue::socketPerform()
+ */
public function socketPerform()
{
- $this->prepareQueue();
+ if (!isset($this->queue)) {
+ $this->queue = new RequestsQueue();
+ $this->queue->attach($this);
+ }
return $this->queue->socketPerform();
}
-
+
+ /**
+ * Calls socketSelect() on previously created RequestsQueue
+ *
+ * @see RequestsQueue::socketSelect()
+ */
public function socketSelect($timeout = 1)
{
if (!isset($this->queue)) {
- throw new Exception('Cannot select without perform before.');
+ throw new Exception('You need to call socketPerform() before.');
}
return $this->queue->socketSelect($timeout);
}
diff --git a/libs/curl-easy/cURL/RequestsQueue.php b/libs/curl-easy/cURL/RequestsQueue.php
index 0758946e..d97661b2 100644
--- a/libs/curl-easy/cURL/RequestsQueue.php
+++ b/libs/curl-easy/cURL/RequestsQueue.php
@@ -2,9 +2,8 @@
namespace cURL;
use Symfony\Component\EventDispatcher\EventDispatcher;
-use Countable;
-class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, Countable
+class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, \Countable
{
/**
* @var Options Default options for new Requests attached to RequestsQueue
@@ -59,7 +58,7 @@ class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, C
public function getDefaultOptions()
{
if (!isset($this->defaultOptions)) {
- $this->defaultOptions = new Options;
+ $this->defaultOptions = new Options();
}
return $this->defaultOptions;
}
@@ -126,7 +125,7 @@ class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, C
unset($this->running[$request->getUID()]);
$this->detach($request);
- $event = new Event;
+ $event = new Event();
$event->request = $request;
$event->response = new Response($request, curl_multi_getcontent($request->getHandle()));
if ($result !== CURLE_OK) {
@@ -134,6 +133,7 @@ class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, C
}
$event->queue = $this;
$this->dispatch('complete', $event);
+ $request->dispatch('complete', $event);
}
return $n;
@@ -168,9 +168,11 @@ class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, C
*/
protected function getRequestsNotRunning()
{
- return array_diff_key($this->queue, $this->running);
+ $map = $this->queue;
+ foreach($this->running as $k => $v) unset($map[$k]);
+ return $map;
}
-
+
/**
* Download available data on socket.
*
@@ -200,12 +202,15 @@ class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, C
$mrc = curl_multi_exec($this->mh, $this->runningCount);
} while ($mrc === CURLM_CALL_MULTI_PERFORM);
$runningAfter = $this->runningCount;
-
- $completed = ($runningAfter < $runningBefore) ? $this->read() : 0;
+
+ if ($runningAfter < $runningBefore) {
+ $this->read();
+ }
$notRunning = $this->getRequestsNotRunning();
} while (count($notRunning) > 0);
-
+ // Why the loop? New requests might be added at runtime on 'complete' event.
+ // So we need to attach them to curl_multi handle immediately.
return $this->count() > 0;
}
diff --git a/plugins/MCTeam/ChatMessagePlugin.php b/plugins/MCTeam/ChatMessagePlugin.php
index f96d13cd..414b8432 100644
--- a/plugins/MCTeam/ChatMessagePlugin.php
+++ b/plugins/MCTeam/ChatMessagePlugin.php
@@ -7,6 +7,7 @@ use ManiaControl\ManiaControl;
use ManiaControl\Players\Player;
use ManiaControl\Plugins\Plugin;
use Maniaplanet\DedicatedServer\Xmlrpc\PlayerStateException;
+use Maniaplanet\DedicatedServer\Xmlrpc\ServerOptionsException;
use Maniaplanet\DedicatedServer\Xmlrpc\UnknownPlayerException;
/**
@@ -407,6 +408,8 @@ class ChatMessagePlugin implements CommandListener, Plugin {
} catch (UnknownPlayerException $exception) {
$this->maniaControl->getChat()->sendException($exception, $player);
} catch (PlayerStateException $exception) {
+ } catch (ServerOptionsException $exception) {
+ //too many spectators
}
}
diff --git a/plugins/MCTeam/CustomVotesPlugin.php b/plugins/MCTeam/CustomVotesPlugin.php
index 953b4a6b..fd0c6a00 100644
--- a/plugins/MCTeam/CustomVotesPlugin.php
+++ b/plugins/MCTeam/CustomVotesPlugin.php
@@ -79,8 +79,8 @@ class CustomVotesPlugin implements CommandListener, CallbackListener, ManialinkP
* Private properties
*/
/** @var ManiaControl $maniaControl */
- private $maniaControl = null;
- private $voteCommands = array();
+ private $maniaControl = null;
+ private $voteCommands = array();
private $voteMenuItems = array();
/** @var CurrentVote $currentVote */
private $currentVote = null;
@@ -229,7 +229,7 @@ class CustomVotesPlugin implements CommandListener, CallbackListener, ManialinkP
$pauseExists = false;
foreach ($scriptInfos->commandDescs as $param) {
- if ($param->name === "Command_ForceWarmUp") {
+ if ($param->name === "Command_ForceWarmUp" || $param->name === "Command_SetPause") {
$pauseExists = true;
break;
}
@@ -251,8 +251,7 @@ class CustomVotesPlugin implements CommandListener, CallbackListener, ManialinkP
$itemQuad->setAction(self::ACTION_START_VOTE . 'skipmap');
$this->addVoteMenuItem($itemQuad, 15, 'Vote for a Map Skip');
- if ($this->maniaControl->getServer()->isTeamMode()
- ) {
+ if ($this->maniaControl->getServer()->isTeamMode()) {
//Menu TeamBalance
$itemQuad = new Quad_Icons128x32_1();
$itemQuad->setSubStyle($itemQuad::SUBSTYLE_RT_Team);
@@ -295,8 +294,7 @@ class CustomVotesPlugin implements CommandListener, CallbackListener, ManialinkP
$itemMarginFactorY = 1.2;
//If game is shootmania lower the icons position by 20
- if ($this->maniaControl->getMapManager()->getCurrentMap()->getGame() === 'sm'
- ) {
+ if ($this->maniaControl->getMapManager()->getCurrentMap()->getGame() === 'sm') {
$posY -= $shootManiaOffset;
}
@@ -544,10 +542,21 @@ class CustomVotesPlugin implements CommandListener, CallbackListener, ManialinkP
break;
case 'pausegame':
try {
+ //Gamemodes like Elite, Speedball
$this->maniaControl->getClient()->sendModeScriptCommands(array('Command_ForceWarmUp' => true));
$this->maniaControl->getChat()->sendInformation('$f8fVote to $fffpause the current Game$f8f has been successful!');
} catch (GameModeException $ex) {
}
+
+ try {
+ //Chase and Combo?
+ $this->maniaControl->getClient()->sendModeScriptCommands(array('Command_SetPause' => true));
+ $this->maniaControl->getChat()->sendInformation('$f8fVote to $fffpause the current Game$f8f has been successful!');
+
+ //Especially for chase, force end of the round to reach a draw
+ $this->maniaControl->getClient()->sendModeScriptCommands(array('Command_ForceEndRound' => true));
+ } catch (GameModeException $ex) {
+ }
break;
case 'replay':
$this->maniaControl->getMapManager()->getMapQueue()->addFirstMapToMapQueue($this->currentVote->voter, $this->maniaControl->getMapManager()->getCurrentMap());
@@ -808,11 +817,11 @@ class CustomVotesPlugin implements CommandListener, CallbackListener, ManialinkP
*/
// TODO: extract classes to own files
class VoteCommand {
- public $index = '';
- public $name = '';
+ public $index = '';
+ public $name = '';
public $neededRatio = 0;
- public $idBased = false;
- public $startText = '';
+ public $idBased = false;
+ public $startText = '';
private $stopCallback = '';
@@ -858,16 +867,16 @@ class CurrentVote {
const VOTE_FOR_ACTION = '1';
const VOTE_AGAINST_ACTION = '-1';
- public $voteCommand = null;
- public $expireTime = 0;
- public $positiveVotes = 0;
- public $neededRatio = 0;
+ public $voteCommand = null;
+ public $expireTime = 0;
+ public $positiveVotes = 0;
+ public $neededRatio = 0;
public $neededPlayerRatio = 0;
- public $voter = null;
- public $map = null;
- public $player = null;
- public $function = null;
- public $stopCallback = "";
+ public $voter = null;
+ public $map = null;
+ public $player = null;
+ public $function = null;
+ public $stopCallback = "";
private $playersVoted = array();
diff --git a/plugins/MCTeam/DonationPlugin.php b/plugins/MCTeam/DonationPlugin.php
index 7dc8ab05..05cf24a7 100644
--- a/plugins/MCTeam/DonationPlugin.php
+++ b/plugins/MCTeam/DonationPlugin.php
@@ -281,6 +281,7 @@ class DonationPlugin implements CallbackListener, CommandListener, Plugin {
return;
}
+ //FIXME if you write "/donate 50 hallo" than comes Message: Donate to Hallo
if (!$receiverName) {
$serverName = $this->maniaControl->getClient()->getServerName();
$message = 'Donate ' . $amount . ' Planets to $<' . $serverName . '$>?';
@@ -519,8 +520,10 @@ class DonationPlugin implements CallbackListener, CommandListener, Plugin {
}
$donatingPlayer = $this->maniaControl->getPlayerManager()->getPlayerByIndex($playerIndex);
- $array = array($index => $posX + 5, $donatingPlayer->nickname => $posX + 18, $donatingPlayer->login => $posX + 70, $donations => $posX + 110);
- $this->maniaControl->getManialinkManager()->labelLine($playerFrame, $array);
+
+ $positions = array($posX + 5, $posX + 18, $posX + 70, $posX + 110);
+ $texts = array($index, $donatingPlayer->nickname, $donatingPlayer->login, $donations);
+ $this->maniaControl->getManialinkManager()->labelLine($playerFrame, array($positions, $texts));
$posY -= 4;
$index++;