diff --git a/core/Callbacks/Callbacks.php b/core/Callbacks/Callbacks.php index 2eb37e74..76046218 100644 --- a/core/Callbacks/Callbacks.php +++ b/core/Callbacks/Callbacks.php @@ -17,6 +17,8 @@ interface Callbacks { const AFTERINIT = 'Callbacks.AfterInit'; const ONSHUTDOWN = 'Callbacks.OnShutdown'; const ONRESTART = 'Callbacks.OnRestart'; + const PRELOOP = 'Callbacks.PreLoop'; + const AFTERLOOP = 'Callbacks.AfterLoop'; /** Script Callback: CallbackName, CallbackData */ const SCRIPTCALLBACK = 'Callbacks.ScriptCallback'; diff --git a/core/ManiaControl.php b/core/ManiaControl.php index d919f0a0..4c6c90bc 100644 --- a/core/ManiaControl.php +++ b/core/ManiaControl.php @@ -52,7 +52,7 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener, /* * Constants */ - const VERSION = '0.222'; + const VERSION = '0.223'; const API_VERSION = '2013-04-16'; const MIN_DEDIVERSION = '2017-05-03_21_00'; const SCRIPT_TIMEOUT = 40; @@ -694,6 +694,9 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener, private function loop() { $loopStart = microtime(true); + //Trigger a Callback on entering the Loop + $this->getCallbackManager()->triggerCallback(Callbacks::PRELOOP, $loopStart); + // Extend script timeout if (!defined('PHP_UNIT_TEST')) { set_time_limit(self::SCRIPT_TIMEOUT); @@ -712,6 +715,9 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener, if ($sleepTime > 0) { usleep($sleepTime); } + + //Trigger a Callback after the Loop + $this->getCallbackManager()->triggerCallback(Callbacks::AFTERLOOP, $loopDuration); } /** diff --git a/plugins/MCTeam/Dedimania/DedimaniaPlugin.php b/plugins/MCTeam/Dedimania/DedimaniaPlugin.php index 4754e553..7c748b22 100644 --- a/plugins/MCTeam/Dedimania/DedimaniaPlugin.php +++ b/plugins/MCTeam/Dedimania/DedimaniaPlugin.php @@ -36,7 +36,7 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene * Constants */ const ID = 8; - const VERSION = 0.2; + const VERSION = 0.3; const AUTHOR = 'MCTeam'; const NAME = 'Dedimania Plugin'; const MLID_DEDIMANIA = 'Dedimania.ManialinkId'; @@ -112,7 +112,7 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene $this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::TM_ONFINISHLINE, $this, 'handleFinishCallback'); $this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::TM_ONLAPFINISH, $this, 'handleFinishCallback'); $this->maniaControl->getCallbackManager()->registerCallbackListener(SettingManager::CB_SETTING_CHANGED, $this, 'handleSettingChanged'); - + $this->maniaControl->getCallbackManager()->registerCallbackListener(Callbacks::AFTERLOOP, $this, 'handleAfterLoop'); $this->maniaControl->getTimerManager()->registerTimerListening($this, 'updateEverySecond', 1000); $this->maniaControl->getTimerManager()->registerTimerListening($this, 'handleEveryMinute', 1000 * 60); @@ -152,6 +152,8 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene /** * Handle 1 Second Callback + * + * @internal */ public function updateEverySecond() { if (!$this->webHandler->doesManiaLinkNeedUpdate()) { @@ -211,6 +213,8 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene /** * Handle 1 Minute Callback + * + * @internal */ public function handleEveryMinute() { if ($this->webHandler->getDedimaniaData()->sessionId == "") { @@ -222,6 +226,7 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene /** * Handle PlayerConnect callback * + * @internal * @param Player $player */ public function handlePlayerConnect(Player $player) { @@ -231,6 +236,7 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene /** * Handle Player Disconnect Callback * + * @internal * @param Player $player */ public function handlePlayerDisconnect(Player $player) { @@ -239,6 +245,8 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene /** * Handle Begin Map Callback + * + * @internal */ public function handleBeginMap() { $this->webHandler->getDedimaniaData()->unsetRecords(); @@ -248,6 +256,8 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene /** * Handle EndMap Callback + * + * @internal */ public function handleMapEnd() { $this->webHandler->submitChallengeTimes(); @@ -256,6 +266,7 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene /** * Handle Checkpoint Callback * + * @internal * @param OnWayPointEventStructure $callback */ public function handleCheckpointCallback(OnWayPointEventStructure $structure) { @@ -273,6 +284,7 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene /** * Handle Finish Callback * + * @internal * @param \ManiaControl\Callbacks\Structures\TrackMania\OnWayPointEventStructure $structure */ public function handleFinishCallback(OnWayPointEventStructure $structure) { @@ -531,6 +543,7 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene /** * Handle PlayerManialinkPageAnswer callback * + * @internal * @param array $callback */ public function handleManialinkPageAnswer(array $callback) { @@ -544,9 +557,19 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene } } + /** + * Process Dedimania Webreqests at end of Loop + * + * @internal + */ + public function handleAfterLoop(){ + $this->webHandler->callDedimania(); + } + /** * Shows a ManiaLink list with the local records. * + * @api * @param array $chat * @param Player $player */ @@ -628,6 +651,7 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene /** * Function to retrieve the dedimania records on the current map * + * @api * @return RecordData[] */ public function getDedimaniaRecords() { @@ -636,6 +660,7 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene /** * Update the RecordWidget variables + * */ private function updateRecordWidget() { $width = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_WIDGET_WIDTH); @@ -647,6 +672,12 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene $this->webHandler->maniaLinkUpdateNeeded(); } + /** + * Handle settings Changed + * + * @internal + * @param \ManiaControl\Settings\Setting $setting + */ public function handleSettingChanged(Setting $setting) { if (!$setting->belongsToClass($this)) { return; @@ -654,6 +685,7 @@ class DedimaniaPlugin implements CallbackListener, CommandListener, TimerListene $this->updateRecordWidget(); } + /** * @see \ManiaControl\Plugins\Plugin::unload() */ diff --git a/plugins/MCTeam/Dedimania/DedimaniaWebHandler.php b/plugins/MCTeam/Dedimania/DedimaniaWebHandler.php index 7a26f681..cc0fe65d 100644 --- a/plugins/MCTeam/Dedimania/DedimaniaWebHandler.php +++ b/plugins/MCTeam/Dedimania/DedimaniaWebHandler.php @@ -11,7 +11,6 @@ use ManiaControl\Players\Player; /** * ManiaControl Dedimania Webhandler Class for Dedimania Plugin - * Notice its not completely finished yet * * @author ManiaControl Team * @copyright 2014-2017 ManiaControl Team @@ -34,6 +33,8 @@ class DedimaniaWebHandler implements TimerListener { /** @var \MCTeam\Dedimania\DedimaniaData $dedimaniaData */ private $dedimaniaData; + private $requests = array(); + private $requestIndex = 0; private $maniaLinkNeedsUpdate = false; public function __construct($maniaControl) { @@ -46,6 +47,7 @@ class DedimaniaWebHandler implements TimerListener { * @param bool $updateRecords */ public function openDedimaniaSession($updateRecords = false) { + //TODO updateRecords $content = $this->encodeRequest(self::DEDIMANIA_OPEN_SESSION, array($this->dedimaniaData->toArray())); Logger::logInfo("Try to connect on Dedimania"); @@ -54,7 +56,7 @@ class DedimaniaWebHandler implements TimerListener { if (!$data || $error) { Logger::logError("Dedimania Error while opening session: '{$error}' Line 42"); } - + $data = $this->decode($data); if (!is_array($data) || empty($data)) { @@ -87,6 +89,7 @@ class DedimaniaWebHandler implements TimerListener { $asyncHttpRequest->postData(); } + /** * Fetch Dedimania Records * @@ -110,71 +113,16 @@ class DedimaniaWebHandler implements TimerListener { if (!$serverInfo || !$playerInfo || !$mapInfo || !$gameMode) { $data = array($this->dedimaniaData->sessionId, $mapInfo, $gameMode, $serverInfo, $playerInfo); - if($serverInfo){ // No Players + if ($serverInfo) { // No Players Logger::logError("Dedimania Records could not be fetched, debuginfo:" . json_encode($data)); } return false; } - $data = array($this->dedimaniaData->sessionId, $mapInfo, $gameMode, $serverInfo, $playerInfo); - $content = $this->encodeRequest(self::DEDIMANIA_GET_RECORDS, $data); + $data = array($this->dedimaniaData->sessionId, $mapInfo, $gameMode, $serverInfo, $playerInfo); Logger::logInfo("Try to fetch Dedimania Records"); - - $asyncHttpRequest = new AsyncHttpRequest($this->maniaControl, self::DEDIMANIA_URL); - $asyncHttpRequest->setCallable(function ($data, $error) { - if (!$data || $error) { - Logger::logError("Dedimania Error while fetching records '{$error}'"); - return; - } - - $data = $this->decode($data); - - //Data[0][0] can be false in error case like map has no checkpoints - if (!is_array($data) || empty($data) || !isset($data[0]) || !isset($data[0][0]) || $data[0][0] == false) { - Logger::logError("Dedimania Get Records, invalid response or no checkpoints"); - Logger::log(json_encode($data)); - return; - } - - $methodResponse = $data[0]; - if (xmlrpc_is_fault($methodResponse)) { - $this->handleXmlRpcFault($methodResponse, self::DEDIMANIA_GET_RECORDS); - return; - } - - $responseData = $methodResponse[0]; - - if (!isset($responseData['Players']) || !isset($responseData['Records'])) { - $this->maniaControl->getErrorHandler()->triggerDebugNotice('Invalid Dedimania response! ' . json_encode($responseData)); - return; - } - - $this->dedimaniaData->serverMaxRank = $responseData['ServerMaxRank']; - - foreach ($responseData['Players'] as $player) { - $dediPlayer = new DedimaniaPlayer($player); - $this->dedimaniaData->addPlayer($dediPlayer); - } - foreach ($responseData['Records'] as $key => $record) { - $this->dedimaniaData->records[$key] = new RecordData($record); - } - - Logger::logInfo(count($this->dedimaniaData->records) . " Dedimania Records Fetched succesfully!"); - - $this->maniaLinkNeedsUpdate = true; - $this->maniaControl->getCallbackManager()->triggerCallback(DedimaniaPlugin::CB_DEDIMANIA_UPDATED, $this->dedimaniaData->records); //TODO - - //3 Minutes - $this->maniaControl->getTimerManager()->registerOneTimeListening($this, function () { - $this->updatePlayerList(); - }, 1000 * 60 * 3); - }); - - $asyncHttpRequest->setContent($content); - $asyncHttpRequest->setCompression(true); - $asyncHttpRequest->setTimeout(500); - $asyncHttpRequest->postData(); + $this->addRequest(self::DEDIMANIA_GET_RECORDS, $data); return true; } @@ -187,41 +135,7 @@ class DedimaniaWebHandler implements TimerListener { return; } - $content = $this->encodeRequest(self::DEDIMANIA_CHECK_SESSION, array($this->dedimaniaData->sessionId)); - - $asyncHttpRequest = new AsyncHttpRequest($this->maniaControl, self::DEDIMANIA_URL); - $asyncHttpRequest->setCallable(function ($data, $error) { - if ($error) { - //Reopen session in Timeout case - $this->openDedimaniaSession(); - Logger::logError("Dedimania Error while checking session (opening new Session): " . $error); - } - - $data = $this->decode($data); - if (!is_array($data) || empty($data)) { - return; - } - - $methodResponse = $data[0]; - if (xmlrpc_is_fault($methodResponse)) { - $this->handleXmlRpcFault($methodResponse, self::DEDIMANIA_CHECK_SESSION); - Logger::logError("Dedimania Check Session failed, opening a new Session!"); - $this->openDedimaniaSession(); - return; - } - - $responseData = $methodResponse[0]; - if (is_bool($responseData)) { - if (!$responseData) { - Logger::logError("Dedimania Check Session failed, opening a new Session!"); - $this->openDedimaniaSession(); - } - } - }); - $asyncHttpRequest->setContent($content); - $asyncHttpRequest->setCompression(true); - $asyncHttpRequest->setTimeout(500); - $asyncHttpRequest->postData(); + $this->addRequest(self::DEDIMANIA_CHECK_SESSION, array($this->dedimaniaData->sessionId)); } /** @@ -276,43 +190,10 @@ class DedimaniaWebHandler implements TimerListener { xmlrpc_set_type($replays['VReplay'], 'base64'); xmlrpc_set_type($replays['Top1GReplay'], 'base64'); - $data = array($this->dedimaniaData->sessionId, $this->getMapInfo(), $gameMode, $times, $replays); - $content = $this->encodeRequest(self::DEDIMANIA_SET_CHALLENGE_TIMES, $data); + $data = array($this->dedimaniaData->sessionId, $this->getMapInfo(), $gameMode, $times, $replays); Logger::logInfo("Dedimania Submitting Map Times at End-Map Start"); - - - $asyncHttpRequest = new AsyncHttpRequest($this->maniaControl, self::DEDIMANIA_URL); - $asyncHttpRequest->setCallable(function ($data, $error) { - if ($error) { - Logger::logError("Dedimania Error while submitting times: " . $error); - return; - } - - $data = $this->decode($data); - if (!is_array($data) || empty($data)) { - return; - } - - $methodResponse = $data[0]; - if (xmlrpc_is_fault($methodResponse)) { - $this->handleXmlRpcFault($methodResponse, self::DEDIMANIA_SET_CHALLENGE_TIMES); - return; - } - - // Called method response - if (!$methodResponse[0]) { - Logger::logError("Records Plugin: Submitting dedimania records failed."); - Logger::logError(json_encode($data)); - } else { - Logger::logInfo("Dedimania Times succesfully submitted"); - } - - }); - $asyncHttpRequest->setContent($content); - $asyncHttpRequest->setCompression(false); - $asyncHttpRequest->setTimeout(500); - $asyncHttpRequest->postData(); + $this->addRequest(self::DEDIMANIA_SET_CHALLENGE_TIMES, $data); } /** @@ -326,45 +207,8 @@ class DedimaniaWebHandler implements TimerListener { } // Send Dedimania request - $data = array($this->dedimaniaData->sessionId, $player->login, $player->rawNickname, $player->path, $player->isSpectator); - $content = $this->encodeRequest(self::DEDIMANIA_PLAYERCONNECT, $data); - - $asyncHttpRequest = new AsyncHttpRequest($this->maniaControl, self::DEDIMANIA_URL); - $asyncHttpRequest->setCallable(function ($data, $error) use (&$player) { - if ($error) { - $this->checkDedimaniaSession(); //TODO Verify why? - return; - } - - $data = $this->decode($data); - if (!is_array($data) || empty($data)) { - return; - } - - $methodResponse = $data[0]; - if (xmlrpc_is_fault($methodResponse)) { - $this->handleXmlRpcFault($methodResponse, self::DEDIMANIA_PLAYERCONNECT); - return; - } - - $responseData = $methodResponse[0]; - $dediPlayer = new DedimaniaPlayer($responseData); - $this->dedimaniaData->addPlayer($dediPlayer); - - // Fetch records if he is the first who joined the server - if ($this->maniaControl->getPlayerManager()->getPlayerCount(false) === 1) { - $this->fetchDedimaniaRecords(true); - } - - Logger::logInfo("Dedimania Player added " . $dediPlayer->login); - - $this->maniaLinkNeedsUpdate = true; //TODO handle update for only one player instead of everyone as soon splitted - }); - - $asyncHttpRequest->setContent($content); - $asyncHttpRequest->setCompression(true); - $asyncHttpRequest->setTimeout(500); - $asyncHttpRequest->postData(); + $data = array($this->dedimaniaData->sessionId, $player->login, $player->rawNickname, $player->path, $player->isSpectator); + $this->addRequest(self::DEDIMANIA_PLAYERCONNECT, $data); } /** @@ -380,33 +224,8 @@ class DedimaniaWebHandler implements TimerListener { $this->dedimaniaData->removePlayer($player->login); // Send Dedimania request - $data = array($this->dedimaniaData->sessionId, $player->login, ''); - $content = $this->encodeRequest(self::DEDIMANIA_PLAYERDISCONNECT, $data); - - $asyncHttpRequest = new AsyncHttpRequest($this->maniaControl, self::DEDIMANIA_URL); - $asyncHttpRequest->setCallable(function ($data, $error) { - if ($error) { - $this->checkDedimaniaSession(); - return; - } - - $data = $this->decode($data); - if (!is_array($data) || empty($data)) { - return; - } - - $methodResponse = $data[0]; - if (xmlrpc_is_fault($methodResponse)) { - $this->handleXmlRpcFault($methodResponse, self::DEDIMANIA_PLAYERDISCONNECT); - } - - Logger::logInfo("Debug: Dedimania Player removed"); - }); - - $asyncHttpRequest->setContent($content); - $asyncHttpRequest->setCompression(true); - $asyncHttpRequest->setTimeout(500); - $asyncHttpRequest->postData(); + $data = array($this->dedimaniaData->sessionId, $player->login, ''); + $this->addRequest(self::DEDIMANIA_PLAYERDISCONNECT, $data); } @@ -422,8 +241,29 @@ class DedimaniaWebHandler implements TimerListener { } // Send Dedimania request - $data = array($this->dedimaniaData->sessionId, $serverInfo, $votesInfo, $playerList); - $content = $this->encodeRequest(self::DEDIMANIA_UPDATE_SERVER_PLAYERS, $data); + $data = array($this->dedimaniaData->sessionId, $serverInfo, $votesInfo, $playerList); + $this->addRequest(self::DEDIMANIA_UPDATE_SERVER_PLAYERS, $data); + + } + + /** Adds a Request to the Dedimania Queue */ + private function addRequest($method, $params) { + $this->requests[$this->requestIndex] = array('methodName' => $method, 'params' => $params); + $this->requestIndex++; + } + + /** + * Process Dedimania Calls + */ + public function callDedimania() { + if (empty($this->requests)) { + return; + } + + $this->addRequest(self::DEDIMANIA_WARNINGSANDTTR2, array()); + + $content = xmlrpc_encode_request(self::XMLRPC_MULTICALL, array($this->requests), array('encoding' => 'UTF-8', 'escaping' => 'markup')); + $asyncHttpRequest = new AsyncHttpRequest($this->maniaControl, self::DEDIMANIA_URL); $asyncHttpRequest->setCallable(function ($data, $error) { @@ -434,23 +274,94 @@ class DedimaniaWebHandler implements TimerListener { } $data = $this->decode($data); - if (!is_array($data) || empty($data)) { + if (!is_array($data) || empty($data) || !isset($data[0]) || !isset($data[0][0]) || $data[0][0] == false) { return; } - $methodResponse = $data[0]; - if (xmlrpc_is_fault($methodResponse)) { - $this->handleXmlRpcFault($methodResponse, self::DEDIMANIA_UPDATE_SERVER_PLAYERS); + + //Get the Errors and Warnings to get the Method Order + $errorsAndWarnings = $data[count($data) - 1][0]; + $methods = $errorsAndWarnings['methods']; + + foreach ($data as $key => $methodResponse) { + $methodName = $methods[$key]['methodName']; + + if (xmlrpc_is_fault($methodResponse)) { + $this->handleXmlRpcFault($methodResponse, $methodName); + } + + $responseParameters = $methodResponse[0]; + + switch ($methodName) { + case self::DEDIMANIA_UPDATE_SERVER_PLAYERS: + Logger::logInfo("Dedimania Playerlist Updated"); + break; + case self::DEDIMANIA_GET_RECORDS: + if (!isset($responseParameters['Players']) || !isset($responseParameters['Records'])) { + $this->maniaControl->getErrorHandler()->triggerDebugNotice('Invalid Dedimania response! ' . json_encode($responseParameters)); + return; + } + + $this->dedimaniaData->serverMaxRank = $responseParameters['ServerMaxRank']; + + foreach ($responseParameters['Players'] as $player) { + $dediPlayer = new DedimaniaPlayer($player); + $this->dedimaniaData->addPlayer($dediPlayer); + } + foreach ($responseParameters['Records'] as $recordKey => $record) { + $this->dedimaniaData->records[$recordKey] = new RecordData($record); + } + + Logger::logInfo(count($this->dedimaniaData->records) . " Dedimania Records Fetched succesfully!"); + + $this->maniaLinkNeedsUpdate = true; + $this->maniaControl->getCallbackManager()->triggerCallback(DedimaniaPlugin::CB_DEDIMANIA_UPDATED, $this->dedimaniaData->records); //TODO + + //3 Minutes + $this->maniaControl->getTimerManager()->registerOneTimeListening($this, function () { + $this->updatePlayerList(); + }, 1000 * 60 * 3); + + break; + case self::DEDIMANIA_CHECK_SESSION: + //TODO Check and reopen if needed + break; + case self::DEDIMANIA_SET_CHALLENGE_TIMES: + //TODO Proper Checks + Logger::logInfo("Dedimania Times succesfully submitted"); + break; + case self::DEDIMANIA_PLAYERCONNECT: + $dediPlayer = new DedimaniaPlayer($responseParameters); + $this->dedimaniaData->addPlayer($dediPlayer); + + // Fetch records if he is the first who joined the server + if ($this->maniaControl->getPlayerManager()->getPlayerCount(false) === 1) { + $this->fetchDedimaniaRecords(true); + } + + Logger::logInfo("Dedimania Player added " . $dediPlayer->login); + + $this->maniaLinkNeedsUpdate = true; //TODO handle update for only one player instead of everyone as soon splitted + break; + case self::DEDIMANIA_PLAYERDISCONNECT: + Logger::logInfo("Debug: Dedimania Player removed"); + break; + case self::DEDIMANIA_WARNINGSANDTTR2: + foreach ($responseParameters['methods'] as $method) { + if ($method['errors']) { + Logger::log('Dedimania Warning or Error: ' . $method['methodName'] . ': ' . json_encode($method['errors'])); + } + } + } } - - - Logger::logInfo("Dedimania Playerlist Updated"); }); $asyncHttpRequest->setContent($content); $asyncHttpRequest->setCompression(true); $asyncHttpRequest->setTimeout(500); $asyncHttpRequest->postData(); + + $this->requests = array(); } /**