diff --git a/application/core/Files/AsynchronousFileReader.php b/application/core/Files/AsynchronousFileReader.php index a241c96f..ef6380ff 100644 --- a/application/core/Files/AsynchronousFileReader.php +++ b/application/core/Files/AsynchronousFileReader.php @@ -41,10 +41,23 @@ class AsynchronousFileReader { foreach($this->sockets as $key => &$socket) { /** @var SocketStructure $socket */ $socket->streamBuffer .= fread($socket->socket, 4096); + + + //$socket->streamBuffer .= fgets($socket->socket, 4096); + //var_dump($socket->streamBuffer); + /* $meta = stream_get_meta_data($socket->socket); + while($meta["unread_bytes"] > 0){ + var_dump("test"); + $socket->streamBuffer .= fgets($socket->socket, 4096); + $meta = stream_get_meta_data($socket->socket); + var_dump($meta); + } + + var_dump($meta); + exit();*/ if (feof($socket->socket) || time() > ($socket->creationTime + self::SOCKET_TIMEOUT)) { fclose($socket->socket); unset($this->sockets[$key]); - $result = ""; $error = 0; if (time() > ($socket->creationTime + self::SOCKET_TIMEOUT)) { @@ -84,15 +97,36 @@ class AsynchronousFileReader { } $header = $this->parseHeader($resultArray[0]); + if (isset($header["transfer-encoding"])) { $result = $this->decode_chunked($resultArray[1]); } else { $result = $resultArray[1]; } - return $result; + return $this->decompressData($header, $result); } + /** + * Checks if the data is Compressed and uncompress it + * + * @param $header + * @param $data + * @return string + */ + private function decompressData($header, $data) { + if (isset($header["content-encoding"])) { + switch($header["content-encoding"]) { + case "gzip": + case "gzip;": + return gzdecode($data); + case "deflate": + case "deflate;": + return gzinflate($data); + } + } + return $data; + } /** * Decode Chunks @@ -134,6 +168,58 @@ class AsynchronousFileReader { } + /** + * Send Data via POST Method + * + * @param $url + * @param $function + * @param $content + * @param string $contentType + * @return bool|null + */ + public function postData($url, $function, $content, $compressed = false, $contentType = 'UTF-8') { + if (!is_callable($function)) { + $this->maniaControl->log("Function is not callable"); + return false; + } + + if (!$url) { + return null; + } + $urlData = parse_url($url); + $port = (isset($urlData['port']) ? $urlData['port'] : 80); + + $socket = @fsockopen($urlData['host'], $port, $errno, $errstr, 4); + if (!$socket) { + return false; + } + + $query = 'POST ' . $urlData['path'] . ' HTTP/1.1' . PHP_EOL; + $query .= 'Host: ' . $urlData['host'] . PHP_EOL; + $query .= 'Accept-Charset: utf-8' . PHP_EOL; + $query .= 'Accept-Encoding: gzip, deflate' . PHP_EOL; + //$query .= 'Content-Encoding: gzip' . PHP_EOL; + $query .= 'Content-Type: text/xml; charset=utf-8;' . PHP_EOL; + $query .= 'Keep-Alive: 300' . PHP_EOL; + $query .= 'Connection: Keep-Alive' . PHP_EOL; + $query .= 'User-Agent: ManiaControl v' . ManiaControl::VERSION . PHP_EOL; + $query .= 'Content-Length: ' . strlen($content) . PHP_EOL . PHP_EOL; + + $query .= $content . PHP_EOL; + + fwrite($socket, $query); + + $success = stream_set_blocking($socket, 0); + if (!$success) { + return false; + } + + $socketStructure = new SocketStructure($url, $socket, $function); + array_push($this->sockets, $socketStructure); + + return true; + } + /** * Load a remote file * diff --git a/application/core/Maps/Map.php b/application/core/Maps/Map.php index 682be850..8627752c 100644 --- a/application/core/Maps/Map.php +++ b/application/core/Maps/Map.php @@ -24,6 +24,7 @@ class Map { public $mapType = ''; public $mapStyle = ''; public $nbCheckpoints = -1; + public $nbLaps = -1; /** @var MXMapInfo $mx */ public $mx = null; public $authorLogin = ''; @@ -57,6 +58,7 @@ class Map { $this->mapType = $mpMap->mapType; $this->mapStyle = $mpMap->mapStyle; $this->nbCheckpoints = $mpMap->nbCheckpoints; + $this->nbLaps = $mpMap->nbLaps; $this->authorNick = $this->authorLogin; } diff --git a/application/core/Maps/MapManager.php b/application/core/Maps/MapManager.php index e6e15069..506fd2fc 100644 --- a/application/core/Maps/MapManager.php +++ b/application/core/Maps/MapManager.php @@ -377,6 +377,7 @@ class MapManager implements CallbackListener { if (array_key_exists($rpcMap->uId, $this->maps)) { $this->currentMap = $this->maps[$rpcMap->uId]; $this->currentMap->nbCheckpoints = $rpcMap->nbCheckpoints; + $this->currentMap->nbLaps = $rpcMap->nbLaps; return true; } $map = $this->initializeMap($rpcMap); diff --git a/application/core/Server/Server.php b/application/core/Server/Server.php index 290afbd1..82edb8cf 100644 --- a/application/core/Server/Server.php +++ b/application/core/Server/Server.php @@ -225,7 +225,7 @@ class Server implements CallbackListener { case 1: return 'Rounds'; case 2: - return 'TimeAttack'; + return 'Timeattack'; case 3: return 'Team'; case 4: @@ -306,7 +306,7 @@ class Server implements CallbackListener { } // Server not yet in given status - Wait for it... $waitBegin = time(); - $maxWaitTime = 20; + $maxWaitTime = 30; $lastStatus = $response->name; $this->maniaControl->log("Waiting for server to reach status {$statusCode}..."); $this->maniaControl->log("Current Status: {$lastStatus}"); diff --git a/application/plugins/Dedimania/Dedimania.php b/application/plugins/Dedimania/Dedimania.php index 5dddf8db..61240816 100644 --- a/application/plugins/Dedimania/Dedimania.php +++ b/application/plugins/Dedimania/Dedimania.php @@ -5,12 +5,16 @@ require_once "DedimaniaData.php"; use ManiaControl\Callbacks\CallbackListener; use ManiaControl\Callbacks\TimerListener; use ManiaControl\ManiaControl; +use ManiaControl\Players\Player; use ManiaControl\Plugins\Plugin; class Dedimania implements CallbackListener, TimerListener, Plugin { /** * Constants */ + const ID = 100; + const VERSION = 0.1; + const MLID_DEDIMANIA = 'Dedimania.ManialinkId'; const XMLRPC_MULTICALL = 'system.multicall'; const DEDIMANIA_URL = 'http://dedimania.net:8081/Dedimania'; const DEDIMANIA_OPENSESSION = 'dedimania.OpenSession'; @@ -21,6 +25,7 @@ class Dedimania implements CallbackListener, TimerListener, Plugin { const DEDIMANIA_UPDATESERVERPLAYERS = 'dedimania.UpdateServerPlayers'; const DEDIMANIA_SETCHALLENGETIMES = 'dedimania.SetChallengeTimes'; const DEDIMANIA_WARNINGSANDTTR2 = 'dedimania.WarningsAndTTR2'; + const USE_COMPRESSION = false; /** * Private Properties @@ -29,6 +34,9 @@ class Dedimania implements CallbackListener, TimerListener, Plugin { private $maniaControl = null; /** @var DedimaniaData $dedimaniaData */ private $dedimaniaData = null; + private $manialink = null; + //private $lastSendManialink = array(); + private $updateManialink = false; /** * Prepares the Plugin @@ -51,41 +59,229 @@ class Dedimania implements CallbackListener, TimerListener, Plugin { return; - $this->openDedimaniaSession(true); - } + $this->maniaControl->timerManager->registerTimerListening($this, 'updateEverySecond', 1000); + $this->maniaControl->timerManager->registerTimerListening($this, 'handleEveryMinute', 1000 * 60); + //TODO parse settings - private function openDedimaniaSession($init = false) { // Open session $serverInfo = $this->maniaControl->server->getInfo(); $serverVersion = $this->maniaControl->client->getVersion(); $packMask = substr($this->maniaControl->server->titleId, 2); - $this->dedimaniaData = new DedimaniaData("abc", "cde", $serverInfo->path, $packMask, $serverVersion); + $this->dedimaniaData = new DedimaniaData(".paragoncanyon", "a3ee654ac8", $serverInfo->path, $packMask, $serverVersion); + $this->openDedimaniaSession(); + } - $url = self::DEDIMANIA_URL; + /** + * Opens the Dedimania Session + */ + private function openDedimaniaSession() { + //$content = gzcompress($this->encode_request(self::DEDIMANIA_OPENSESSION, array($this->dedimaniaData->toArray()))); + $content = $this->encode_request(self::DEDIMANIA_OPENSESSION, array($this->dedimaniaData->toArray())); - //TODO make postFile method in FileReader - $urlData = parse_url($url); + $this->maniaControl->fileReader->postData(self::DEDIMANIA_URL, function ($data, $error) { + $this->maniaControl->log("Try to connect on Dedimania"); + $data = $this->decode($data); - $content = gzcompress($this->encode_request(self::DEDIMANIA_OPENSESSION, array($this->dedimaniaData->toArray()))); - - $query = 'POST ' . $urlData['path'] . ' HTTP/1.1' . PHP_EOL; - $query .= 'Host: ' . $urlData['host'] . PHP_EOL; - $query .= 'Accept-Charset: utf-8;' . PHP_EOL; - $query .= 'Accept-Encoding: gzip;' . PHP_EOL; - $query .= 'Content-Type: text/xml; charset=utf-8;' . PHP_EOL; - $query .= 'Keep-Alive: 300;' . PHP_EOL; - $query .= 'User-Agent: ManiaControl v' . ManiaControl::VERSION . PHP_EOL; - $query .= 'Content-Length: ' . strlen($content) . PHP_EOL . PHP_EOL; - $query .= $content . PHP_EOL; - - - $this->maniaControl->fileReader->loadFile($url, function ($data, $error) { - var_dump($data); var_dump($error); + if (is_array($data)) { + foreach($data as $index => $methodResponse) { + if (xmlrpc_is_fault($methodResponse)) { + $this->handleXmlRpcFault($methodResponse); + } else if ($index <= 0) { + $responseData = $methodResponse[0]; + var_dump($responseData); + $this->dedimaniaData->sessionId = $responseData['SessionId']; + if ($this->dedimaniaData->sessionId != '') { + $this->maniaControl->log("Dedimania connection successfully established."); + $this->fetchDedimaniaRecords(); + } else { + $this->maniaControl->log("Error while opening Dedimania Connection"); + } + } + } + } + }, $content, self::USE_COMPRESSION); + } - }, 'UTF-8', $query); + /** + * Handle 1Second callback + */ + public function updateEverySecond($time) { + $this->updateManialink = false; + //TODO send manialink + } + /** + * Check if the session is alive every minute + * + * @param null $callback + */ + public function handleEveryMinute($callback = null) { + //$this->checkDedimaniaSession(); + } + + /** + * Fetch Dedimania Records + * + * @param bool $reset + */ + private function fetchDedimaniaRecords($reset = true) { + if ($this->dedimaniaData->sessionId == '') { + return false; + } + + if ($reset) { + // Reset records + $this->dedimaniaData->records = array(); + } + + $serverInfo = $this->getServerInfo(); + $playerInfo = $this->getPlayerList(); + $mapInfo = $this->getMapInfo(); + $gameMode = $this->getGameModeString(); + + if (!$serverInfo || !$playerInfo || !$mapInfo || !$gameMode) { + return false; + } + + $data = array($this->dedimaniaData->sessionId, $mapInfo, $gameMode, $serverInfo, $playerInfo); + $content = $this->encode_request(self::DEDIMANIA_GETRECORDS, $data); + + $this->maniaControl->fileReader->postData(self::DEDIMANIA_URL, function ($data, $error) { + $data = $this->decode($data); + + if (is_array($data)) { + foreach($data as $index => $methodResponse) { + if (xmlrpc_is_fault($methodResponse)) { + $this->handleXmlRpcFault($methodResponse); + return false; + } else if ($index <= 0) { + $responseData = $methodResponse[0]; + $this->dedimaniaData->records = $responseData; + } + } + } + $this->updateManialink = true; + return true; + }, $content, self::USE_COMPRESSION); + + return true; + } + + /** + * Checks If a Dedimania Session exists, if not create a new oen + */ + private function checkDedimaniaSession() { + if ($this->dedimaniaData->sessionId == '') { + $this->openDedimaniaSession(); + return; + } + + $content = $this->encode_request(self::DEDIMANIA_CHECKSESSION, array($this->dedimaniaData->sessionId)); + + $this->maniaControl->fileReader->postData(self::DEDIMANIA_URL, function ($data, $error) { + $data = $this->decode($data); + + if (is_array($data)) { + foreach($data as $methodResponse) { + if (xmlrpc_is_fault($methodResponse)) { + $this->handleXmlRpcFault($methodResponse); + } else { + $responseData = $methodResponse[0]; + if (is_bool($responseData)) { + if (!$responseData) { + $this->openDedimaniaSession(); + } + } + } + } + } + }, $content, self::USE_COMPRESSION); + return; + } + + /** + * Build server info Structure for callbacks + */ + private function getServerInfo() { + $server = $this->maniaControl->client->getServerOptions(); + if (!$server) { + return null; + } + + if (count($this->maniaControl->playerManager->getPlayers()) == 0) { + return null; + } + + $playerCount = $this->maniaControl->playerManager->getPlayerCount(); + $spectatorCount = $this->maniaControl->playerManager->getSpectatorCount(); + + return array('SrvName' => $server->name, 'Comment' => $server->comment, 'Private' => (strlen($server->password) > 0), 'NumPlayers' => $playerCount, 'MaxPlayers' => $server->currentMaxPlayers, 'NumSpecs' => $spectatorCount, 'MaxSpecs' => $server->currentMaxSpectators); + } + + /** + * Build simple player list for callbacks + */ + private function getPlayerList($votes = false) { + $client = null; + + $players = $this->maniaControl->playerManager->getPlayers(); + + if (count($players) == 0) { + return null; + } + $playerInfo = array(); + foreach($players as $player) { + /** @var Player $player */ + array_push($playerInfo, array('Login' => $player->login, 'IsSpec' => $player->isSpectator)); + } + return $playerInfo; + } + + /** + * Build map info struct for dedimania requests + */ + private function getMapInfo() { + $map = $this->maniaControl->mapManager->getCurrentMap(); + if (!$map) { + return null; + } + $mapInfo = array(); + $mapInfo['UId'] = $map->uid; + $mapInfo['Name'] = $map->name; + $mapInfo['Author'] = $map->authorLogin; + $mapInfo['Environment'] = $map->environment; + $mapInfo['NbCheckpoints'] = $map->nbCheckpoints; + $mapInfo['NbLaps'] = $map->nbLaps; + return $mapInfo; + } + + /** + * Get Dedimania string representation of the current game mode + * + * @return String + */ + private function getGameModeString() { + $gameMode = $this->maniaControl->server->getGameMode(); + if ($gameMode === null) { + trigger_error("Couldn't retrieve game mode. "); + return null; + } + switch($gameMode) { + case 1: + case 3: + case 5: + { + return 'Rounds'; + } + case 2: + case 4: + { + return 'TA'; + } + } + return null; } /** @@ -100,6 +296,15 @@ class Dedimania implements CallbackListener, TimerListener, Plugin { return xmlrpc_encode_request(self::XMLRPC_MULTICALL, array($paramArray), array('encoding' => 'UTF-8', 'escaping' => 'markup')); } + /** + * Handles xml rpc fault + * + * @param $fault + */ + private function handleXmlRpcFault($fault) { + trigger_error('XmlRpc Fault: ' . $fault['faultString'] . ' (' . $fault['faultCode'] . ')'); + } + /** * Decodes xml rpc response * @@ -114,6 +319,8 @@ class Dedimania implements CallbackListener, TimerListener, Plugin { * Unload the plugin and its resources */ public function unload() { + $this->maniaControl->timerManager->unregisterTimerListenings($this); + $this->maniaControl->callbackManager->unregisterCallbackListener($this); unset($this->maniaControl); } @@ -123,7 +330,7 @@ class Dedimania implements CallbackListener, TimerListener, Plugin { * @return int */ public static function getId() { - // TODO: Implement getId() method. + return self::ID; } /** @@ -132,7 +339,7 @@ class Dedimania implements CallbackListener, TimerListener, Plugin { * @return string */ public static function getName() { - // TODO: Implement getName() method. + return "Dedimania Plugin"; } /** @@ -141,7 +348,7 @@ class Dedimania implements CallbackListener, TimerListener, Plugin { * @return float */ public static function getVersion() { - // TODO: Implement getVersion() method. + return self::VERSION; } /** @@ -150,7 +357,7 @@ class Dedimania implements CallbackListener, TimerListener, Plugin { * @return string */ public static function getAuthor() { - // TODO: Implement getAuthor() method. + return "kremsy and steeffeen"; } /** @@ -159,6 +366,6 @@ class Dedimania implements CallbackListener, TimerListener, Plugin { * @return string */ public static function getDescription() { - // TODO: Implement getDescription() method. + return "Dedimania Plugin for Trackmania"; } } \ No newline at end of file diff --git a/application/plugins/Dedimania/DedimaniaData.php b/application/plugins/Dedimania/DedimaniaData.php index 7de85805..84fb630a 100644 --- a/application/plugins/Dedimania/DedimaniaData.php +++ b/application/plugins/Dedimania/DedimaniaData.php @@ -26,6 +26,8 @@ class DedimaniaData { public $version; public $login; public $code; + public $sessionId = ''; + public $records = array(); public function __construct($serverLogin, $dedimaniaCode, $path, $packmask, Version $serverVersion) { $this->game = "TM2"; @@ -41,8 +43,12 @@ class DedimaniaData { public function toArray() { $array = array(); - foreach(get_object_vars($this) as $key => $value) + foreach(get_object_vars($this) as $key => $value) { + if ($key == 'records' || $key == 'sessionId') { + continue; + } $array[ucfirst($key)] = $value; + } return $array; } } \ No newline at end of file