From 4e0533c7fe901a899026aee4edb97fd02f61a537 Mon Sep 17 00:00:00 2001 From: Exodus4D Date: Thu, 5 Jan 2017 22:15:12 +0100 Subject: [PATCH] - part 1/2 WIP added WebSocket extension, #420 - part 1/2 added "secure routes" to route finder module, #311 --- app/environment.ini | 9 ++ app/main/controller/accesscontroller.php | 32 ++++- app/main/controller/api/connection.php | 36 +++-- app/main/controller/api/map.php | 129 ++++++++++++++---- app/main/controller/api/route.php | 47 ++++++- app/main/controller/api/system.php | 53 +++++--- app/main/controller/controller.php | 32 +++-- app/main/controller/setup.php | 63 +++++++++ app/main/lib/config.php | 17 +++ app/main/lib/socket.php | 159 +++++++++++++++++++++++ app/main/model/mapmodel.php | 58 +++++++-- app/pathfinder.ini | 4 +- 12 files changed, 556 insertions(+), 83 deletions(-) create mode 100644 app/main/lib/socket.php diff --git a/app/environment.ini b/app/environment.ini index d12081bf..e9f99c42 100644 --- a/app/environment.ini +++ b/app/environment.ini @@ -45,6 +45,11 @@ SMTP_PASS = root SMTP_FROM = pathfinder@localhost.com SMTP_ERROR = pathfinder@localhost.com +; TCP Socket configuration (optional) (advanced) +;SOCKET_HOST = localhost +;SOCKET_PORT = 5555 + + [ENVIRONMENT.PRODUCTION] ; path to index.php (Default: leave blank == "auto-detect") ; -> e.g. set pathfinder/ if your URL looks like https://www.[YOUR_DOMAIN]/pathfinder (subfolder) @@ -83,3 +88,7 @@ SMTP_PASS = SMTP_FROM = registration@pathfinder-w.space SMTP_ERROR = admin@pathfinder-w.space + +; TCP Socket configuration (optional) (advanced) +;SOCKET_HOST = localhost +;SOCKET_PORT = 5555 \ No newline at end of file diff --git a/app/main/controller/accesscontroller.php b/app/main/controller/accesscontroller.php index 63bd33a5..9e74634c 100644 --- a/app/main/controller/accesscontroller.php +++ b/app/main/controller/accesscontroller.php @@ -7,7 +7,9 @@ */ namespace Controller; -use Controller\Api as Api; + +use lib\Config; +use lib\Socket; use Model; class AccessController extends Controller { @@ -40,4 +42,32 @@ class AccessController extends Controller { } } + /** + * broadcast map data to clients + * -> send over TCP Socket + * @param Model\MapModel $map + * @return int (number of active connections for this map) + */ + protected function broadcastMapData(Model\MapModel $map){ + $mapData = $this->getFormattedMapData($map); + return (int)(new Socket( Config::getSocketUri() ))->sendData('mapUpdate', $mapData); + } + + /** + * get formatted Map Data + * @param Model\MapModel $map + * @return array + */ + protected function getFormattedMapData(Model\MapModel $map){ + $mapData = $map->getData(); + + return [ + 'config' => $mapData->mapData, + 'data' => [ + 'systems' => $mapData->systems, + 'connections' => $mapData->connections, + ] + ]; + } + } \ No newline at end of file diff --git a/app/main/controller/api/connection.php b/app/main/controller/api/connection.php index b217e2bd..2ef66ba5 100644 --- a/app/main/controller/api/connection.php +++ b/app/main/controller/api/connection.php @@ -86,6 +86,9 @@ class Connection extends Controller\AccessController { $connection->save(); $newConnectionData = $connection->getData(); + + // broadcast map changes + $this->broadcastMapData($connection->mapId); } } } @@ -100,18 +103,31 @@ class Connection extends Controller\AccessController { * @throws \Exception */ public function delete(\Base $f3){ - $connectionIds = $f3->get('POST.connectionIds'); - $activeCharacter = $this->getCharacter(); + $mapId = (int)$f3->get('POST.mapId'); + $connectionIds = (array)$f3->get('POST.connectionIds'); - /** - * @var Model\ConnectionModel $connection - */ - $connection = Model\BasicModel::getNew('ConnectionModel'); - foreach($connectionIds as $connectionId){ - $connection->getById($connectionId); - $connection->delete( $activeCharacter ); + if($mapId){ + $activeCharacter = $this->getCharacter(); + + /** + * @var Model\MapModel $map + */ + $map = Model\BasicModel::getNew('MapModel'); + $map->getById($mapId); + + if( $map->hasAccess($activeCharacter) ){ + foreach($connectionIds as $connectionId){ + if( $connection = $map->getConnectionById($connectionId) ){ + $connection->delete( $activeCharacter ); + + $connection->reset(); + } + } + + // broadcast map changes + $this->broadcastMapData($map); + } - $connection->reset(); } echo json_encode([]); diff --git a/app/main/controller/api/map.php b/app/main/controller/api/map.php index a7579464..549dec20 100644 --- a/app/main/controller/api/map.php +++ b/app/main/controller/api/map.php @@ -9,6 +9,7 @@ namespace Controller\Api; use Controller; use lib\Config; +use lib\Socket; use Model; /** @@ -396,10 +397,10 @@ class Map extends Controller\AccessController { $maxShared = max($f3->get('PATHFINDER.MAP.PRIVATE.MAX_SHARED') - 1, 0); $accessCharacters = array_slice($accessCharacters, 0, $maxShared); - if($accessCharacters){ - // clear map access. In case something has removed from access list - $map->clearAccess(); + // clear map access. In case something has removed from access list + $map->clearAccess(); + if($accessCharacters){ /** * @var $tempCharacter Model\CharacterModel */ @@ -439,10 +440,10 @@ class Map extends Controller\AccessController { $maxShared = max($f3->get('PATHFINDER.MAP.CORPORATION.MAX_SHARED') - 1, 0); $accessCorporations = array_slice($accessCorporations, 0, $maxShared); - if($accessCorporations){ - // clear map access. In case something has removed from access list - $map->clearAccess(); + // clear map access. In case something has removed from access list + $map->clearAccess(); + if($accessCorporations){ /** * @var $tempCorporation Model\CorporationModel */ @@ -482,10 +483,10 @@ class Map extends Controller\AccessController { $maxShared = max($f3->get('PATHFINDER.MAP.ALLIANCE.MAX_SHARED') - 1, 0); $accessAlliances = array_slice($accessAlliances, 0, $maxShared); - if($accessAlliances){ - // clear map access. In case something has removed from access list - $map->clearAccess(); + // clear map access. In case something has removed from access list + $map->clearAccess(); + if($accessAlliances){ /** * @var $tempAlliance Model\AllianceModel */ @@ -512,7 +513,16 @@ class Map extends Controller\AccessController { } // reload the same map model (refresh) // this makes sure all data is up2date - $map->getById( $map->id, 0 ); + $map->getById( $map->_id, 0 ); + + + $charactersData = $map->getCharactersData(); + $characterIds = array_map(function ($data){ + return $data->id; + }, $charactersData); + + // broadcast map Access -> and send map Data + $this->broadcastMapAccess($map, $characterIds); $return->mapData = $map->getData(); }else{ @@ -546,11 +556,79 @@ class Map extends Controller\AccessController { */ $map = Model\BasicModel::getNew('MapModel'); $map->getById($mapData['id']); - $map->delete( $activeCharacter ); + $map->delete( $activeCharacter, function($mapId){ + $this->broadcastMapDeleted($mapId); + }); echo json_encode([]); } + /** + * broadcast characters with map access rights to WebSocket server + * -> if characters with map access found -> broadcast mapData to them + * @param Model\MapModel $map + * @param array $characterIds + * @return int + */ + protected function broadcastMapAccess($map, $characterIds){ + $connectionCount = 0; + + $mapAccess = [ + 'id' => $map->_id, + 'characterIds' => $characterIds + ]; + $charCount = (int)(new Socket( Config::getSocketUri() ))->sendData('mapAccess', $mapAccess); + + if($charCount > 0){ + // map has active connections that should receive map Data + $connectionCount = $this->broadcastMapData($map); + } + + return $connectionCount; + } + + /** + * broadcast map delete information to clients + * @param int $mapId + * @return bool|string + */ + protected function broadcastMapDeleted($mapId){ + return (new Socket( Config::getSocketUri() ))->sendData('mapDeleted', $mapId); + } + + /** + * get map access tokens for current character + * -> send access tokens via TCP Socket for WebSocket auth + * @param \Base $f3 + */ + public function getAccessData(\Base $f3){ + $return = (object) []; + + $activeCharacter = $this->getCharacter(); + $maps = $activeCharacter->getMaps(); + + $return->data = [ + 'id' => $activeCharacter->_id, + 'token' => bin2hex(random_bytes(16)), // token for character access + 'mapData' => [] + ]; + + if($maps){ + foreach($maps as $map){ + $return->data['mapData'][] = [ + 'id' => $map->_id, + 'token' => bin2hex(random_bytes(16)) // token for map access + ]; + } + } + + // send Access Data to WebSocket Server and get response (status) + // if 'OK' -> Socket exists + $return->status = (new Socket( Config::getSocketUri() ))->sendData('mapConnectionAccess', $return->data); + + echo json_encode( $return ); + } + /** * update map data * -> function is called continuously (trigger) by any active client @@ -603,6 +681,7 @@ class Map extends Controller\AccessController { // loop current user maps and check for changes foreach($maps as $map){ + $mapChanged = false; // update system data --------------------------------------------------------------------- foreach($systems as $i => $systemData){ @@ -631,6 +710,8 @@ class Map extends Controller\AccessController { $system->updatedCharacterId = $activeCharacter; $system->save(); + $mapChanged = true; + // a system belongs to ONE map -> speed up for multiple maps unset($systemData[$i]); } @@ -663,17 +744,23 @@ class Map extends Controller\AccessController { $connection->setData($connectionData); $connection->save(); + $mapChanged = true; + // a connection belongs to ONE map -> speed up for multiple maps unset($connectionData[$i]); } } } + + if($mapChanged){ + $this->broadcastMapData($map); + } } } } // format map Data for return - $return->mapData = self::getFormattedMapData($maps); + $return->mapData = $this->getFormattedMapsData($maps); // cache time(s) per user should be equal or less than this function is called // prevent request flooding @@ -696,23 +783,13 @@ class Map extends Controller\AccessController { /** * get formatted map data - * @param $mapModels + * @param Model\MapModel[] $mapModels * @return array */ - public static function getFormattedMapData($mapModels){ + protected function getFormattedMapsData($mapModels){ $mapData = []; - foreach($mapModels as &$mapModel){ - /** - * @var $mapModel Model\MapModel - */ - $allMapData = $mapModel->getData(); - $mapData[] = [ - 'config' => $allMapData->mapData, - 'data' => [ - 'systems' => $allMapData->systems, - 'connections' => $allMapData->connections, - ] - ]; + foreach($mapModels as $mapModel){ + $mapData[] = $this->getFormattedMapData($mapModel); } return $mapData; diff --git a/app/main/controller/api/route.php b/app/main/controller/api/route.php index 73ac4463..b09ec224 100644 --- a/app/main/controller/api/route.php +++ b/app/main/controller/api/route.php @@ -53,13 +53,17 @@ class Route extends Controller\AccessController { * -> this function is required for route search! (Don´t forget) * @param array $mapIds * @param array $filterData + * @param array $keepSystems */ - public function initJumpData($mapIds = [], $filterData = []){ + public function initJumpData($mapIds = [], $filterData = [], $keepSystems = []){ // add static data (e.g. K-Space stargates,..) $this->setStaticJumpData(); // add map specific data $this->setDynamicJumpData($mapIds, $filterData); + + // filter jump data (e.g. remove some systems (0.0, LS) + $this->filterJumpData($filterData, $keepSystems); } /** @@ -256,6 +260,35 @@ class Route extends Controller\AccessController { } } + /** + * filter systems (remove some systems) e.g. WH,LS,0.0 for "safer search" + * @param array $filterData + * @param array $keepSystems + */ + private function filterJumpData($filterData = [], $keepSystems = []){ + if($filterData['safer']){ + // remove all systems (TrueSec < 0.5) from search arrays + $this->jumpArray = array_filter($this->jumpArray, function($jumpData) use($keepSystems) { + + // systemId is always last entry + $systemId = end($jumpData); + $systemNameData = $this->nameArray[$systemId]; + $systemSec = $systemNameData[3]; + + if($systemSec < 0.45 && !in_array($systemId, $keepSystems)){ + // remove system from nameArray as well + unset($this->nameArray[$systemId]); + // remove system from idArray as well + $systemName = $systemNameData[0]; + unset($this->idArray[$systemName]); + return false; + }else{ + return true; + } + }); + } + } + /** * get system data by systemId and dataName * @param $systemId @@ -504,7 +537,7 @@ class Route extends Controller\AccessController { array_walk($mapData, function(&$item, &$key, $data){ if( isset($data[1][$key]) ){ - // character has mas access -> do not check again + // character has map access -> do not check again $item = $data[1][$key]; }else{ // check map access for current character @@ -534,7 +567,8 @@ class Route extends Controller\AccessController { 'wormholes' => (bool) $routeData['wormholes'], 'wormholesReduced' => (bool) $routeData['wormholesReduced'], 'wormholesCritical' => (bool) $routeData['wormholesCritical'], - 'wormholesEOL' => (bool) $routeData['wormholesEOL'] + 'wormholesEOL' => (bool) $routeData['wormholesEOL'], + 'safer' => (bool) $routeData['safer'] ]; $returnRoutData = [ @@ -553,8 +587,9 @@ class Route extends Controller\AccessController { count($mapIds) > 0 ){ $systemFrom = $routeData['systemFromData']['name']; + $systemFromId = (int)$routeData['systemFromData']['systemId']; $systemTo = $routeData['systemToData']['name']; - + $systemToId = (int)$routeData['systemToData']['systemId']; $cacheKey = $this->getRouteCacheKey( $mapIds, @@ -571,7 +606,9 @@ class Route extends Controller\AccessController { $searchDepth = $f3->get('PATHFINDER.ROUTE.SEARCH_DEPTH'); // set jump data for following route search - $this->initJumpData($mapIds, $filterData); + // --> don´t filter some systems (e.g. systemFrom, systemTo) even if they are are WH,LS,0.0 + $keepSystems = [$systemFromId, $systemToId]; + $this->initJumpData($mapIds, $filterData, $keepSystems); // no cached route data found $foundRoutData = $this->findRoute($systemFrom, $systemTo, $searchDepth); diff --git a/app/main/controller/api/system.php b/app/main/controller/api/system.php index 73546f1e..abf15e1a 100644 --- a/app/main/controller/api/system.php +++ b/app/main/controller/api/system.php @@ -273,6 +273,9 @@ class System extends Controller\AccessController { $newSystemModel->getById( $systemModel->id, 0); $newSystemModel->clearCacheData(); $newSystemData = $newSystemModel->getData(); + + // broadcast map changes + $this->broadcastMapData($newSystemModel->mapId); } } @@ -408,29 +411,41 @@ class System extends Controller\AccessController { * @param \Base $f3 */ public function delete(\Base $f3){ + $mapId = (int)$f3->get('POST.mapId'); $systemIds = (array)$f3->get('POST.systemIds'); - $activeCharacter = $this->getCharacter(); - /** - * @var Model\SystemModel $system - */ - $system = Model\BasicModel::getNew('SystemModel'); - foreach($systemIds as $systemId){ - $system->getById($systemId); - if( $system->hasAccess($activeCharacter) ){ - // check whether system should be deleted OR set "inactive" - if( - empty($system->alias) && - empty($system->description) - ){ - $system->erase(); - }else{ - // keep data -> set "inactive" - $system->setActive(false); - $system->save(); + if($mapId){ + $activeCharacter = $this->getCharacter(); + + /** + * @var Model\MapModel $map + */ + $map = Model\BasicModel::getNew('MapModel'); + $map->getById($mapId); + + if( $map->hasAccess($activeCharacter) ){ + foreach($systemIds as $systemId){ + if( $system = $map->getSystemById($systemId) ){ + // check whether system should be deleted OR set "inactive" + if( + empty($system->alias) && + empty($system->description) + ){ + $system->erase(); + }else{ + // keep data -> set "inactive" + $system->setActive(false); + $system->save(); + } + + $system->reset(); + } } - $system->reset(); + + // broadcast map changes + $this->broadcastMapData($map); } + } echo json_encode([]); diff --git a/app/main/controller/controller.php b/app/main/controller/controller.php index c31b0069..1ca35685 100644 --- a/app/main/controller/controller.php +++ b/app/main/controller/controller.php @@ -10,6 +10,7 @@ namespace Controller; use Controller\Api as Api; use Controller\Ccp\Sso as Sso; use lib\Config; +use lib\Socket; use Model; use DB; @@ -447,10 +448,6 @@ class Controller { return $user; } - public function getCharacterSessionData(){ - - } - /** * log out current character * @param \Base $f3 @@ -458,14 +455,16 @@ class Controller { public function logout(\Base $f3){ $params = (array)$f3->get('POST'); - // ---------------------------------------------------------- - // delete server side cookie validation data - // for the active character - if( - $params['clearCookies'] === '1' && - ( $activeCharacter = $this->getCharacter()) - ){ - $activeCharacter->logout(); + if( $activeCharacter = $this->getCharacter() ){ + + if($params['clearCookies'] === '1'){ + // delete server side cookie validation data + // for the active character + $activeCharacter->logout(); + } + + // broadcast logout information to webSocket server + (new Socket( Config::getSocketUri() ))->sendData('characterLogout', $activeCharacter->_id); } // destroy session login data ------------------------------- @@ -808,6 +807,15 @@ class Controller { return Config::getEnvironmentData($key); } + /** + * health check for ICP socket -> ping request + * @param $ttl + * @return bool|string + */ + static function checkTcpSocket($ttl){ + return (new Socket( Config::getSocketUri(), $ttl ))->sendData('healthCheck'); + } + /** * get required MySQL variable value * @param $key diff --git a/app/main/controller/setup.php b/app/main/controller/setup.php index b589aac8..d748cee6 100644 --- a/app/main/controller/setup.php +++ b/app/main/controller/setup.php @@ -189,6 +189,9 @@ class Setup extends Controller { // set database connection information $f3->set('checkDatabase', $this->checkDatabase($f3, $fixColumns)); + // set socket information + $f3->set('socketInformation', $this->getSocketInformation()); + // set index information $f3->set('indexInformation', $this->getIndexData()); @@ -905,6 +908,66 @@ class Setup extends Controller { return $checkTables; } + /** + * get Socket information (TCP (internal)), (WebSocket (clients)) + * @return array + */ + protected function getSocketInformation(){ + // $ttl for health check + $ttl = 600; + + $socketInformation = [ + 'tcpSocket' => [ + 'label' => 'Socket (intern) [TCP]', + 'online' => (self::checkTcpSocket($ttl) == '1'), + 'data' => [ + [ + 'label' => 'HOST', + 'value' => Config::getEnvironmentData('SOCKET_HOST'), + 'check' => !empty( Config::getEnvironmentData('SOCKET_HOST') ) + ],[ + 'label' => 'PORT', + 'value' => Config::getEnvironmentData('SOCKET_PORT'), + 'check' => !empty( Config::getEnvironmentData('SOCKET_PORT') ) + ],[ + 'label' => 'URI', + 'value' => Config::getSocketUri(), + 'check' => !empty( Config::getSocketUri() ) + ],[ + 'label' => 'timeout (ms)', + 'value' => $ttl, + 'check' => !empty( $ttl ) + ] + ] + ], + 'webSocket' => [ + 'label' => 'WebSocket (clients) [HTTP]', + 'online' => true, + 'data' => [ + [ + 'label' => 'HOST', + 'value' => 'pathfinder.local', + 'check' => true + ],[ + 'label' => 'PORT', + 'value' => 80, + 'check' => true + ],[ + 'label' => 'URI', + 'value' => 'ws://pathfinder.local/ws/map/update', + 'check' => true + ],[ + 'label' => 'timeout (ms)', + 'value' => $ttl, + 'check' => !empty( $ttl ) + ] + ] + ] + ]; + + return $socketInformation; + } + /** get indexed (cache) data information * @return array */ diff --git a/app/main/lib/config.php b/app/main/lib/config.php index 804a5218..dc95bcc9 100644 --- a/app/main/lib/config.php +++ b/app/main/lib/config.php @@ -179,5 +179,22 @@ class Config extends \Prefab { return Util::arrayChangeKeyCaseRecursive( $f3->get($hiveKey) ); } + /** + * get URI for TCP socket + * @return bool|string + */ + static function getSocketUri(){ + $uri = false; + + if( + ( $ip = self::getEnvironmentData('SOCKET_HOST') ) && + ( $port = self::getEnvironmentData('SOCKET_PORT') ) + ){ + $uri = 'tcp://' . $ip . ':' . $port; + } + + return $uri; + } + } \ No newline at end of file diff --git a/app/main/lib/socket.php b/app/main/lib/socket.php new file mode 100644 index 00000000..57bffd0c --- /dev/null +++ b/app/main/lib/socket.php @@ -0,0 +1,159 @@ + The total timeout for a request is ($tll * $maxRetries) + * @var int + */ + protected $ttl = (self::DEFAULT_TTL_MAX / self::DEFAULT_RETRY_MAX); + + /** + * max retry count for message send + * @var int + */ + protected $maxRetries = self::DEFAULT_RETRY_MAX; + + public function __construct($uri, $ttl = self::DEFAULT_TTL_MAX, $maxRetries = self::DEFAULT_RETRY_MAX){ + $this->setSocketUri($uri); + $this->setTtl($ttl, $maxRetries); + $this->initSocket(); + } + + /** + * @param mixed $socketUri + */ + public function setSocketUri($socketUri){ + $this->socketUri = $socketUri; + } + + /** + * @param int $ttl + * @param int $maxRetries + */ + public function setTtl(int $ttl, int $maxRetries){ + if( + $ttl > 0 && + $maxRetries > 0 + ){ + $this->maxRetries = $maxRetries; + $this->ttl = round($ttl / $maxRetries); + } + } + + /** + * init socket + */ + public function initSocket(){ + if($this->socketUri){ + $context = new \ZMQContext(); + $this->socket = $context->getSocket(\ZMQ::SOCKET_REQ); + } + } + + /** + * send data to socket and listen for response + * -> "Request" => "Response" setup + * @param $task + * @param $load + * @return bool|string + */ + public function sendData($task, $load = ''){ + $response = false; + + if( !$this->socket ){ + // Socket not active (e.g. URI missing) + return $response; + } + + // add task, and wrap data + $send = [ + 'task' => $task, + 'load' => $load + ]; + + $this->socket->setSockOpt(\ZMQ::SOCKOPT_LINGER, 0); + + + $this->socket->connect($this->socketUri); + $this->socket->send(json_encode($send)); + + $readable = []; + $writable = []; + + $poller = new \ZMQPoll(); + $poller->add($this->socket, \ZMQ::POLL_IN); + + $retriesLeft = $this->maxRetries; + while($retriesLeft){ + /* Amount of events retrieved */ + $events = 0; + + try{ + /* Poll until there is something to do */ + $events = $poller->poll($readable, $writable, $this->ttl); + $errors = $poller->getLastErrors(); + + if(count($errors) > 0){ + foreach($errors as $error){ + LogController::getLogger('SOCKET_ERROR')->write(sprintf(self::ERROR_POLLING, $error)); + } + } + }catch(\ZMQPollException $e){ + LogController::getLogger('SOCKET_ERROR')->write(sprintf(self::ERROR_POLLING_FAILED, $e->getMessage() )); + } + + if($events > 0){ + try{ + $response = $this->socket->recv(); + // everything OK -> stop loop + break; + }catch(\ZMQException $e){ + LogController::getLogger('SOCKET_ERROR')->write(sprintf(self::ERROR_RECV_FAILED, $e->getMessage() )); + } + }elseif(--$retriesLeft <= 0){ + // retry limit exceeded + LogController::getLogger('SOCKET_ERROR')->write(sprintf(self::ERROR_OFFLINE, $this->socketUri, $this->maxRetries, $this->ttl)); + break; + } + } + + $this->socket->disconnect($this->socketUri); + + return $response; + } + + +} \ No newline at end of file diff --git a/app/main/model/mapmodel.php b/app/main/model/mapmodel.php index 159a9413..53796b12 100644 --- a/app/main/model/mapmodel.php +++ b/app/main/model/mapmodel.php @@ -15,6 +15,11 @@ class MapModel extends BasicModel { protected $table = 'map'; + /** + * cache key prefix for getCharactersData(); + */ + const DATA_CACHE_KEY_CHARACTER = 'CHARACTERS'; + protected $fieldConf = [ 'active' => [ 'type' => Schema::DT_BOOL, @@ -157,7 +162,7 @@ class MapModel extends BasicModel { // map access $mapData->access = (object) []; - $mapData->access->user = []; + $mapData->access->character = []; $mapData->access->corporation = []; $mapData->access->alliance = []; @@ -233,6 +238,16 @@ class MapModel extends BasicModel { $self->clearCacheData(); } + /** + * see parent + */ + public function clearCacheData(){ + parent::clearCacheData(); + + // clear character data with map access as well! + parent::clearCacheDataWithPrefix(self::DATA_CACHE_KEY_CHARACTER); + } + /** * get blank system model pre-filled with default SDE data * -> check for "inactive" systems on this map first! @@ -356,6 +371,24 @@ class MapModel extends BasicModel { return $systemData; } + /** + * search for a connection by id + * @param int $id + * @return null|ConnectionModel + */ + public function getConnectionById($id){ + /** + * @var $connection ConnectionModel + */ + $connection = $this->rel('connections'); + $result = $connection->findone([ + 'active = 1 AND mapId = :mapId AND id = :id', + ':mapId' => $this->id, + ':id' => $id + ]); + return is_object($result) ? $result : null; + } + /** * get all connections in this map * @return ConnectionModel[] @@ -535,7 +568,7 @@ class MapModel extends BasicModel { * get all character models that are currently online "viewing" this map * @return CharacterModel[] */ - private function getActiveCharacters(){ + private function getAllCharacters(){ $characters = []; if($this->isPrivate()){ @@ -563,17 +596,17 @@ class MapModel extends BasicModel { } /** - * get data for all characters that are currently online "viewing" this map + * get data for ALL characters with map access * -> The result of this function is cached! * @return \stdClass[] */ - private function getCharactersData(){ + public function getCharactersData(){ // check if there is cached data - $charactersData = $this->getCacheData('CHARACTERS'); + $charactersData = $this->getCacheData(self::DATA_CACHE_KEY_CHARACTER); if(is_null($charactersData)){ $charactersData = []; - $characters = $this->getActiveCharacters(); + $characters = $this->getAllCharacters(); foreach($characters as $character){ $charactersData[] = $character->getData(true); @@ -581,7 +614,7 @@ class MapModel extends BasicModel { // cache active characters (if found) if(!empty($charactersData)){ - $this->updateCacheData($charactersData, 'CHARACTERS', 5); + $this->updateCacheData($charactersData, self::DATA_CACHE_KEY_CHARACTER, 5); } } @@ -631,13 +664,20 @@ class MapModel extends BasicModel { /** * delete this map and all dependencies * @param CharacterModel $characterModel + * @param null $callback */ - public function delete(CharacterModel $characterModel){ + public function delete(CharacterModel $characterModel, $callback = null){ + if( !$this->dry() ){ // check if character has access if($this->hasAccess($characterModel)){ // all map related tables will be deleted on cascade - $this->erase(); + if( + $this->erase() && + is_callable($callback) + ){ + $callback($this->_id); + } } } } diff --git a/app/pathfinder.ini b/app/pathfinder.ini index 354453d5..31990e26 100644 --- a/app/pathfinder.ini +++ b/app/pathfinder.ini @@ -116,7 +116,7 @@ EXECUTION_LIMIT = 50 ; map user update ping (ajax) (ms) [PATHFINDER.TIMER.UPDATE_SERVER_USER_DATA] DELAY = 5000 -EXECUTION_LIMIT = 300 +EXECUTION_LIMIT = 500 ; update client user data (ms) [PATHFINDER.TIMER.UPDATE_CLIENT_USER_DATA] @@ -149,6 +149,8 @@ SESSION_SUSPECT = session_suspect DELETE_ACCOUNT = account_delete ; unauthorized request (HTTP 401) UNAUTHORIZED = unauthorized +; TCP socket errors +SOCKET_ERROR = socket_error ; debug log for development DEBUG = debug