From 4c8b24789646dff1e11a8fb4ce27de115737e18b Mon Sep 17 00:00:00 2001 From: kremsy Date: Fri, 7 Feb 2014 17:27:09 +0100 Subject: [PATCH] download maps asynchronously --- .../core/Files/AsynchronousFileReader.php | 49 ++--- application/core/Files/SocketStructure.php | 2 + .../core/ManiaExchange/ManiaExchangeList.php | 2 +- .../ManiaExchange/ManiaExchangeManager.php | 166 ++++++---------- application/core/Maps/MapManager.php | 182 ++++++++++-------- 5 files changed, 184 insertions(+), 217 deletions(-) diff --git a/application/core/Files/AsynchronousFileReader.php b/application/core/Files/AsynchronousFileReader.php index 449a5e98..d7ba1410 100644 --- a/application/core/Files/AsynchronousFileReader.php +++ b/application/core/Files/AsynchronousFileReader.php @@ -10,6 +10,16 @@ use ManiaControl\ManiaControl; * @author kremsy & steeffeen */ class AsynchronousFileReader implements TimerListener { + /** + * Constants + */ + const TIMEOUT_ERROR = 'Timed out while reading data'; + const RESPONSE_ERROR = 'Connection or response error'; + const NO_DATA_ERROR = 'No data returned'; + const INVALID_RESULT_ERROR = 'Invalid Result'; + const SOCKET_TIMEOUT = 10; + + /** * Private Properties */ @@ -27,37 +37,33 @@ class AsynchronousFileReader implements TimerListener { /** * Appends the Data - * @throws \Exception */ public function appendData() { foreach($this->sockets as $key => &$socket) { /** @var SocketStructure $socket */ - $socket->streamBuffer .= fread($socket->socket, 1024); - $info = stream_get_meta_data($socket->socket); + $socket->streamBuffer .= fread($socket->socket, 4096); - if (feof($socket->socket) || $info['timed_out']) { + if (feof($socket->socket) || time() > ($socket->creationTime + self::SOCKET_TIMEOUT)) { fclose($socket->socket); unset($this->sockets[$key]); - if ($info['timed_out']) { - throw new \Exception("Timed out while reading data from " . $socket->url); + $result = array(); + $error = 0; + if (time() > ($socket->creationTime + self::SOCKET_TIMEOUT)) { + $error = self::TIMEOUT_ERROR; + } else if (substr($socket->streamBuffer, 9, 3) != "200") { + $error = self::RESPONSE_ERROR; + } else if ($socket->streamBuffer == '') { + $error = self::NO_DATA_ERROR; + } else { + $result = explode("\r\n\r\n", $socket->streamBuffer, 2); + + if (count($result) < 2) { + $error = self::INVALID_RESULT_ERROR; + } } - if (substr($socket->streamBuffer, 9, 3) != "200") { - throw new \Exception("Connection or response error on " . $socket->url); - } - - if ($socket->streamBuffer == '') { - throw new \Exception("No data returned from " . $socket->url); - } - - $result = explode("\r\n\r\n", $socket->streamBuffer, 2); - - if (count($result) < 2) { - throw new \Exception("Invalid Result"); - } - - call_user_func($socket->function, $result[1]); + call_user_func($socket->function, $result[1], $error); } } } @@ -87,7 +93,6 @@ class AsynchronousFileReader implements TimerListener { if (!$socket) { return false; } - stream_set_timeout($socket, 10); $query = 'GET ' . $urlData['path'] . $urlQuery . ' HTTP/1.0' . PHP_EOL; $query .= 'Host: ' . $urlData['host'] . PHP_EOL; diff --git a/application/core/Files/SocketStructure.php b/application/core/Files/SocketStructure.php index 0f57cca5..3cf30799 100644 --- a/application/core/Files/SocketStructure.php +++ b/application/core/Files/SocketStructure.php @@ -14,11 +14,13 @@ class SocketStructure { public $socket; public $function; public $url; + public $creationTime; public function __construct($url, $socket, $function) { $this->url = $url; $this->socket = $socket; $this->function = $function; + $this->creationTime = time(); $this->streamBuffer = ''; } } \ No newline at end of file diff --git a/application/core/ManiaExchange/ManiaExchangeList.php b/application/core/ManiaExchange/ManiaExchangeList.php index e067d32a..b50980d6 100644 --- a/application/core/ManiaExchange/ManiaExchangeList.php +++ b/application/core/ManiaExchange/ManiaExchangeList.php @@ -107,7 +107,7 @@ class ManiaExchangeList implements CallbackListener, ManialinkPageAnswerListener }; // search for matching maps - $this->maniaControl->mapManager->mxManager->getMapsAssync($function, $searchString, $author, $environment); + $this->maniaControl->mapManager->mxManager->getMapsAsync($function, $searchString, $author, $environment); } /** diff --git a/application/core/ManiaExchange/ManiaExchangeManager.php b/application/core/ManiaExchange/ManiaExchangeManager.php index cba60f3d..b9628bd2 100644 --- a/application/core/ManiaExchange/ManiaExchangeManager.php +++ b/application/core/ManiaExchange/ManiaExchangeManager.php @@ -212,7 +212,7 @@ class ManiaExchangeManager { } /** - * Get the Whole Maplist from MX by Mixed Uid and Id String fetch + * Get the Whole MapList from MX by Mixed Uid and Id String fetch * * @param $string * @return array|null @@ -225,33 +225,44 @@ class ManiaExchangeManager { // compile search URL $url = 'http://api.mania-exchange.com/' . $titlePrefix . '/maps/?ids=' . $string; - try { //FIXME exceptions get not caught here? - $success = $this->maniaControl->fileReader->loadFile($url, function ($mapInfo) use ($titlePrefix, $url) { - $mxMapList = json_decode($mapInfo); - if ($mxMapList === null) { - trigger_error('Cannot decode searched JSON data from ' . $url); - return null; - } + $success = $this->maniaControl->fileReader->loadFile($url, function ($mapInfo, $error) use ($titlePrefix, $url) { + if ($error) { + trigger_error($error); + return null; + } - $maps = array(); - foreach($mxMapList as $map) { - if (!empty($map)) { - array_push($maps, new MXMapInfo($titlePrefix, $map)); - } - } + $mxMapList = json_decode($mapInfo); + if ($mxMapList === null) { + trigger_error('Cannot decode searched JSON data from ' . $url); + return null; + } - $this->updateMapObjectsWithManiaExchangeIds($maps); - return true; - }, "application/json"); - } catch(\Exception $e) { - $this->maniaControl->log("Error while fetching Map Infos" . $e->getTraceAsString()); - return false; - } + $maps = array(); + foreach($mxMapList as $map) { + if (!empty($map)) { + array_push($maps, new MXMapInfo($titlePrefix, $map)); + } + } + + $this->updateMapObjectsWithManiaExchangeIds($maps); + return true; + }, "application/json"); return $success; } - public function getMapsAssync($function, $name = '', $author = '', $env = '', $maxMapsReturned = 100, $searchOrder = self::SEARCH_ORDER_UPDATED_NEWEST) { + /** + * Fetch a MapList Asynchronously + * + * @param $function + * @param string $name + * @param string $author + * @param string $env + * @param int $maxMapsReturned + * @param int $searchOrder + * @return bool + */ + public function getMapsAsync($function, $name = '', $author = '', $env = '', $maxMapsReturned = 100, $searchOrder = self::SEARCH_ORDER_UPDATED_NEWEST) { if (!is_callable($function)) { $this->maniaControl->log("Function is not callable"); return false; @@ -283,105 +294,36 @@ class ManiaExchangeManager { $url .= '&limit=' . $maxMapsReturned; $url .= '&mtype=' . $mapTypes; - try { - $success = $this->maniaControl->fileReader->loadFile($url, function ($mapInfo) use (&$function) { - // Get Title Id - $titleId = $this->maniaControl->server->titleId; - $titlePrefix = strtolower(substr($titleId, 0, 2)); - $mxMapList = json_decode($mapInfo); - if ($mxMapList === null) { - trigger_error('Cannot decode searched JSON data'); - return null; + $fileFunc = function ($mapInfo, $error) use (&$function, $titlePrefix) { + if ($error) { + trigger_error($error); + return null; + } + + $mxMapList = json_decode($mapInfo); + if ($mxMapList === null) { + trigger_error('Cannot decode searched JSON data'); + return null; + } + + $maps = array(); + foreach($mxMapList as $map) { + if (!empty($map)) { + array_push($maps, new MXMapInfo($titlePrefix, $map)); } + } - $maps = array(); - foreach($mxMapList as $map) { - if (!empty($map)) { - array_push($maps, new MXMapInfo($titlePrefix, $map)); - } - } + call_user_func($function, $maps); - call_user_func($function, $maps); + return true; + }; - return true; - }, "application/json"); - } catch(\Exception $e) { - var_dump($e); - return false; - } + $success = $this->maniaControl->fileReader->loadFile($url, $fileFunc, "application/json"); return $success; } - - /** - * Gets a Maplist from Mania Exchange - * - * @param string $name - * @param string $author - * @param string $env - * @param int $maxMapsReturned - * @param int $searchOrder - * @return array null - */ - public function getMaps($name = '', $author = '', $env = '', $maxMapsReturned = 100, $searchOrder = self::SEARCH_ORDER_UPDATED_NEWEST) { - // Get Title Id - $titleId = $this->maniaControl->server->titleId; - $titlePrefix = strtolower(substr($titleId, 0, 2)); - - // Get MapTypes - $scriptInfos = $this->maniaControl->client->getModeScriptInfo(); - - $mapTypes = $scriptInfos->compatibleMapTypes; - - // compile search URL - $url = 'http://' . $titlePrefix . '.mania-exchange.com/tracksearch?api=on'; - - if ($env != '') { - $url .= '&environments=' . $this->getEnvironment($env); - } - if ($name != '') { - $url .= '&trackname=' . str_replace(" ", "%20", $name); - } - if ($author != '') { - $url .= '&author=' . $author; - } - - $url .= '&priord=' . $searchOrder; - $url .= '&limit=' . $maxMapsReturned; - $url .= '&mtype=' . $mapTypes; - - $mapInfo = FileUtil::loadFile($url, "application/json"); - - if ($mapInfo === false) { - $this->error = 'Connection or response error on ' . $url; - return array(); - } elseif ($mapInfo === -1) { - $this->error = 'Timed out while reading data from ' . $url; - return array(); - } elseif ($mapInfo == '') { - if (empty($maps)) { - $this->error = 'No data returned from ' . $url; - return array(); - } - } - - $mxMapList = json_decode($mapInfo); - if ($mxMapList === null) { - trigger_error('Cannot decode searched JSON data from ' . $url); - return null; - } - - $maps = array(); - foreach($mxMapList as $map) { - if (!empty($map)) { - array_push($maps, new MXMapInfo($titlePrefix, $map)); - } - } - return $maps; - } - /** * Gets the Current Environemnt by String * diff --git a/application/core/Maps/MapManager.php b/application/core/Maps/MapManager.php index 5d07a286..49cec79f 100644 --- a/application/core/Maps/MapManager.php +++ b/application/core/Maps/MapManager.php @@ -10,6 +10,7 @@ use ManiaControl\Formatter; use ManiaControl\ManiaControl; use ManiaControl\ManiaExchange\ManiaExchangeList; use ManiaControl\ManiaExchange\ManiaExchangeManager; +use ManiaControl\ManiaExchange\MXMapInfo; use ManiaControl\Players\Player; /** @@ -531,97 +532,114 @@ class MapManager implements CallbackListener { return; } - $url = "http://{$title}.mania-exchange.com/tracks/download/{$mapId}"; - $file = FileUtil::loadFile($url); - if (!$file) { - // Download error - $this->maniaControl->chat->sendError('Download failed!', $login); + $url = "http://{$title}.mania-exchange.com/tracks/download/{$mapId}"; + + //Download the file + $function = function ($file, $error) use (&$login, &$mapInfo, &$mapDir, &$update) { + if (!$file) { + // Download error + $this->maniaControl->chat->sendError('Download failed!', $login); + return; + } + $this->processMapFile($file, $mapInfo, $mapDir, $login, $update); + }; + $this->maniaControl->fileReader->loadFile($url, $function); + } + } + + /** + * Process the MapFile + * + * @param $file + * @param MXMapInfo $mapInfo + * @param $mapDir + * @param $login + * @param $update + */ + private function processMapFile($file, MXMapInfo $mapInfo, $mapDir, $login, $update) { + //Check if map is already on the server + if ($this->getMapByUid($mapInfo->uid) != null) { + // Download error + $this->maniaControl->chat->sendError('Map is already on the server!', $login); + return; + } + + // Save map + $fileName = $mapInfo->id . '_' . $mapInfo->name . '.Map.Gbx'; + $fileName = FileUtil::getClearedFileName($fileName); + + $downloadDirectory = $this->maniaControl->settingManager->getSetting($this, 'MapDownloadDirectory', 'MX'); + + $mapFileName = $downloadDirectory . '/' . $fileName; + + //Check if it can get locally Written + if (is_dir($mapDir)) { + // Create download directory if necessary + if (!is_dir($mapDir . $downloadDirectory) && !mkdir($mapDir . $downloadDirectory)) { + trigger_error("ManiaControl doesn't have to rights to save maps in '{$mapDir}{$downloadDirectory}'."); + $this->maniaControl->chat->sendError("ManiaControl doesn't have the rights to save maps.", $login); return; } - //Check if map is already on the server - if ($this->getMapByUid($mapInfo->uid) != null) { - // Download error - $this->maniaControl->chat->sendError('Map is already on the server!', $login); + $mapDir .= $downloadDirectory . '/'; + + if (!file_put_contents($mapDir . $fileName, $file)) { + // Save error + $this->maniaControl->chat->sendError('Saving map failed!', $login); return; } - - // Save map - $fileName = $mapId . '_' . $mapInfo->name . '.Map.Gbx'; - $fileName = FileUtil::getClearedFileName($fileName); - - $downloadDirectory = $this->maniaControl->settingManager->getSetting($this, 'MapDownloadDirectory', 'MX'); - - $mapFileName = $downloadDirectory . '/' . $fileName; - - //Check if it can get locally Written - if (is_dir($mapDir)) { - // Create download directory if necessary - if (!is_dir($mapDir . $downloadDirectory) && !mkdir($mapDir . $downloadDirectory)) { - trigger_error("ManiaControl doesn't have to rights to save maps in '{$mapDir}{$downloadDirectory}'."); - $this->maniaControl->chat->sendError("ManiaControl doesn't have the rights to save maps.", $login); - return; - } - - $mapDir .= $downloadDirectory . '/'; - - if (!file_put_contents($mapDir . $fileName, $file)) { - // Save error - $this->maniaControl->chat->sendError('Saving map failed!', $login); - return; - } - //Write via Write File Method - } else { - try { - $this->maniaControl->client->writeFileFromString($mapFileName, $file); - } catch(\Exception $e) { - $this->maniaControl->chat->sendError("Map is too big for a remote save.", $login); - return; - } - } - - // Check for valid map + //Write via Write File Method + } else { try { - $this->maniaControl->client->checkMapForCurrentServerParams($mapFileName); + $this->maniaControl->client->writeFileFromString($mapFileName, $file); } catch(\Exception $e) { - trigger_error("Couldn't check if map is valid ('{$mapFileName}'). " . $e->getMessage()); - $this->maniaControl->chat->sendError('Wrong MapType or not validated!', $login); + $this->maniaControl->chat->sendError("Map is too big for a remote save.", $login); return; } - - // Add map to map list - try { - $this->maniaControl->client->insertMap($mapFileName); - } catch(\Exception $e) { - $this->maniaControl->chat->sendError("Couldn't add map to match settings!", $login); - return; - } - - $this->updateFullMapList(); - - //Update Mx MapInfo - $this->maniaControl->mapManager->mxManager->updateMapObjectsWithManiaExchangeIds(array($mapInfo)); - - //Update last updated time - $map = $this->maps[$mapInfo->uid]; - /** @var Map $map */ - $map->lastUpdate = time(); - - $player = $this->maniaControl->playerManager->getPlayer($login); - - if (!$update) { - //Message - $message = '$<' . $player->nickname . '$> added $<' . $mapInfo->name . '$>!'; - $this->maniaControl->chat->sendSuccess($message); - $this->maniaControl->log($message, true); - // Queue requested Map - $this->maniaControl->mapManager->mapQueue->addMapToMapQueue($login, $mapInfo->uid); - } else { - $message = '$<' . $player->nickname . '$> updated $<' . $mapInfo->name . '$>!'; - $this->maniaControl->chat->sendSuccess($message); - $this->maniaControl->log($message, true); - } } - // TODO: add local map by filename + + // Check for valid map + try { + $this->maniaControl->client->checkMapForCurrentServerParams($mapFileName); + } catch(\Exception $e) { + trigger_error("Couldn't check if map is valid ('{$mapFileName}'). " . $e->getMessage()); + $this->maniaControl->chat->sendError('Wrong MapType or not validated!', $login); + return; + } + + // Add map to map list + try { + $this->maniaControl->client->insertMap($mapFileName); + } catch(\Exception $e) { + $this->maniaControl->chat->sendError("Couldn't add map to match settings!", $login); + return; + } + + $this->updateFullMapList(); + + //Update Mx MapInfo + $this->maniaControl->mapManager->mxManager->updateMapObjectsWithManiaExchangeIds(array($mapInfo)); + + //Update last updated time + $map = $this->maps[$mapInfo->uid]; + /** @var Map $map */ + $map->lastUpdate = time(); + + $player = $this->maniaControl->playerManager->getPlayer($login); + + if (!$update) { + //Message + $message = '$<' . $player->nickname . '$> added $<' . $mapInfo->name . '$>!'; + $this->maniaControl->chat->sendSuccess($message); + $this->maniaControl->log($message, true); + // Queue requested Map + $this->maniaControl->mapManager->mapQueue->addMapToMapQueue($login, $mapInfo->uid); + } else { + $message = '$<' . $player->nickname . '$> updated $<' . $mapInfo->name . '$>!'; + $this->maniaControl->chat->sendSuccess($message); + $this->maniaControl->log($message, true); + } } + // TODO: add local map by filename + }