- part 1/2 WIP added WebSocket extension, #420
- part 1/2 added "secure routes" to route finder module, #311
This commit is contained in:
@@ -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
|
||||
@@ -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,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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([]);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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([]);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
159
app/main/lib/socket.php
Normal file
159
app/main/lib/socket.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus
|
||||
* Date: 09.12.2016
|
||||
* Time: 16:21
|
||||
*/
|
||||
|
||||
namespace lib;
|
||||
|
||||
use controller\LogController;
|
||||
|
||||
class Socket {
|
||||
|
||||
const DEFAULT_TTL_MAX = 3000;
|
||||
const DEFAULT_RETRY_MAX = 3;
|
||||
|
||||
const ERROR_OFFLINE = 'Server seems to be offline. uri: "%s" | retries: %s | timeout: %sms';
|
||||
const ERROR_POLLING = 'Error polling object: %s';
|
||||
const ERROR_POLLING_FAILED = 'Polling failed: %s';
|
||||
const ERROR_RECV_FAILED = 'Receive failed: %s';
|
||||
const ERROR_SEND_FAILED = 'Send failed: %s';
|
||||
|
||||
/**
|
||||
* TCP Socket object
|
||||
* @var \ZMQSocket
|
||||
*/
|
||||
protected $socket;
|
||||
|
||||
/**
|
||||
* TCP URI for connections
|
||||
* @var
|
||||
*/
|
||||
protected $socketUri;
|
||||
|
||||
/**
|
||||
* Socket timeout (ms)
|
||||
* -> 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user