Files
pathfinder/app/main/controller/api/route.php
Mark Friedrich 5c1cdca936 v1.1.2 (#270)
* fixed #194 PHP 5.6 error

* - closed #102 added "set waypoint/destination" context menu to route finder module
- update "Select2" 4.0.0 -> 4.0.3
- update "Font Awesome" 4.6.1 -> 4.6.3

* - added *.js files for develop branch

* - closed #195 fixed "BASE" dir for subDir installations
- fixed "Home" menu link

* -  #195 improved js load path

* - added "clear cache" function for manually cache clearing to /setup #200 #105 #158
- added cache size information to /setup
- added current pathfinder  "VERSION" to /setup
- updated "requireJs" 2.1.20 ->2.2.0
- removed unnecessary page cache timings from static templates (page cache)

* - added "document_root", "port", "protocol" and "PHP framework version" to /setup page
- added new "shattered" wormhole types to "signature table", closed #182, #179

* - added new "delete old signatures" option to "signature reader" dialog, closed #95

* - added new housekeeping cronjob für cached files, closed #200
- added new cache size information to /setup page

* - fixed signature groupId/typeId "overwriting" for already known signatures. closed #207
- improved system search dialog. Added trim(); before "api/signatures-> search" request

* updated README.me

* fixed PHP error "default object from empty value", closed #209

* reduced image file size

* - added local storage (IndexedDB)
- added local storage for map scroll position. closed #69

* - added "notice" panel for upcoming release information
- improved layout for "release dialog" (GitHub API)
- improved pagespeed (removed render blocking javascripts)
- improved map scrollbar configuration
- improved Chrome browser custom scrollbar layout
- removed "sign up" buttons from "map panels", closed #214

* - fixed some session and cookie  bugs

* - added new requirement check for `max_input_vars` to /setup URL, closed #224

* - fixed isWormhole(); bug

* -v1.1.1 added js build files

* - removed IGB support #206
- removed location tracking by IGB

* - added build files for upcoming version 1.1.2
- improved ajax authentication check and "logout" notification | closed #198
- improved logging, added missing log file configuration to pathfinder.ini
- added  logging for "unauthorized" requests | closed #198
- updated js "jQuery" 1.11.3 -> 3.0.0 | #206
- updated js "datatables" plugin 1.10.7 -> 1.10.12 | #206
- updated js "mCustomScrollbar" 3.1.14 -> 3.1.4 | #206

* - fixed some minor bugs in signature table module

* - fixed type "Cataclysmic", closed #241

* - added new setup DB indexing for "system_neighbour"  table to /setup route, #125
- fixed system "TrueSec" rounding in "routes module", closed #109

* - fixed system "TrueSec" rounding in "routes module", closed #109

* - added new wormhole statics for "Thera", closed #240

* - fixed missing statics for constellation "21000062" , closed #232

* - added "static" wormholes for "shattered" systems , closed #180
- added im/export function for "index" tables (*.csv import), as an alternative to the *.sql import, closed #125

* - added new system tooltip for "region name", closed #236
- updated "Bootstrap" JS-library 3.3.0 -> 3.3.5

* - removed console.log(),,,

* minor bugfixes in /setup page

* - added basic support for Russian signatures, closed #256
- added warning notification for invalid signature stings

* - added basic support for Russian signatures, closed #256

* - added requirement check for "PDO", "PDO_MYSQL" to "/setup" route
- imrpved DB PDO connection (added "TIMEOUT", forced "ERRMODE")
- fixed broken "system alias" renaming dialog

* - fixed "system graph" module rendering if there was no data available
- improved "image gallery" initialization on landing page
- added navigation to /setup page
- updated "blueImpGallery" (fixed some bugs after jQuery 3.0 upgrade) 1.15.2 -> 2.21.3
- updated "blueImpGalleryBootstrap"  (fixed some bugs after jQuery 3.0 upgrade)  3.1.1 -> 3.4.2

* - JS build files vor 1.1.2

* Updated pathfinder.css
2016-07-29 20:19:17 +02:00

580 lines
19 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 06.06.15
* Time: 03:34
*/
namespace Controller\Api;
use Model;
/**
* Routes controller
* Class Route
* @package Controller\Api
*/
class Route extends \Controller\AccessController {
/**
* cache time for static jump data (e.g. K-Space stargates)
* @var int
*/
private $staticJumpDataCacheTime = 86400;
/**
* cache time for dynamic jump data (e.g. W-Space systems, Jumpbridges. ...)
* @var int
*/
private $dynamicJumpDataCacheTime = 10;
/**
* array system information grouped by systemId
* @var array
*/
private $nameArray = [];
/**
* array neighbour systems grouped by systemName
* @var array
*/
private $jumpArray = [];
/**
* array withh systemName => systemId matching
* @var array
*/
private $idArray = [];
/**
* set jump data for route search
* -> this function is required for route search! (Don´t forget)
* @param array $mapIds
* @param array $filterData
*/
public function initJumpData($mapIds = [], $filterData = []){
// add static data (e.g. K-Space stargates,..)
$this->setStaticJumpData();
// add map specific data
$this->setDynamicJumpData($mapIds, $filterData);
}
/**
* set static system jump data for this instance
* the data is fixed and should not change
* -> jump data includes JUST "static" connections (Stargates)
* -> this data is equal for EACH route search (does not depend on map data)
*/
private function setStaticJumpData(){
$cacheKey = 'staticJumpData';
$f3 = $this->getF3();
$cacheKeyNamedArray = $cacheKey . '.nameArray';
$cacheKeyJumpArray = $cacheKey . '.jumpArray';
$cacheKeyIdArray = $cacheKey . '.idArray';
if(
$f3->exists($cacheKeyNamedArray) &&
$f3->exists($cacheKeyJumpArray) &&
$f3->exists($cacheKeyIdArray)
){
// get cached values
$this->nameArray = $f3->get($cacheKeyNamedArray);
$this->jumpArray = $f3->get($cacheKeyJumpArray);
$this->idArray = $f3->get($cacheKeyIdArray);
}else{
// nothing cached
$query = "SELECT * FROM system_neighbour";
$rows = $this->getDB()->exec($query, null, $this->staticJumpDataCacheTime);
if(count($rows) > 0){
$this->updateJumpData($rows);
// static data should be cached
$f3->set($cacheKeyNamedArray, $this->nameArray, $this->staticJumpDataCacheTime);
$f3->set($cacheKeyJumpArray, $this->jumpArray, $this->staticJumpDataCacheTime);
$f3->set($cacheKeyIdArray, $this->idArray, $this->staticJumpDataCacheTime);
}
}
}
/**
* set/add dynamic system jump data for specific "mapId"´s
* -> this data is dynamic and could change on any map change
* -> (e.g. new system added, connection added/updated, ...)
* @param array $mapIds
* @param array $filterData
*/
private function setDynamicJumpData($mapIds = [], $filterData = []){
if( !empty($mapIds) ){
// make sure, mapIds are integers (protect against SQL injections)
$mapIds = array_map('intval', $mapIds);
// connection filter --------------------------------------------------------
$whereQuery = "";
$includeScopes = [];
$includeTypes = [];
if( $filterData['stargates'] === true){
// include "stargates" for search
$includeScopes[] = 'stargate';
$includeTypes[] = 'stargate';
}
if( $filterData['jumpbridges'] === true ){
// add jumpbridge connections for search
$includeScopes[] = 'jumpbridge';
$includeTypes[] = 'jumpbridge';
}
if( $filterData['wormholes'] === true ){
// add wormhole connections for search
$includeScopes[] = 'wh';
$includeTypes[] = 'wh_fresh';
if( $filterData['wormholesReduced'] === true ){
$includeTypes[] = 'wh_reduced';
}
if( $filterData['wormholesCritical'] === true ){
$includeTypes[] = 'wh_critical';
}
}
// search connections -------------------------------------------------------
if( !empty($includeScopes) ){
$whereQuery .= " connection.scope IN ('" . implode("', '", $includeScopes) . "') AND ";
if( !empty($includeTypes) ){
$whereQuery .= " connection.type REGEXP '" . implode("|", $includeTypes) . "' AND ";
}
$query = "SELECT
system_src.regionId regionId,
system_src.constellationId constellationId,
system_src.name systemName,
system_src.systemId systemId,
(
SELECT
GROUP_CONCAT( NULLIF(system_tar.name, NULL) SEPARATOR ':')
FROM
connection INNER JOIN
system system_tar ON
system_tar.id = connection.source OR
system_tar.id = connection.target
WHERE
(
connection.source = system_src.id OR
connection.target = system_src.id
) AND
" . $whereQuery . "
connection.active = 1 AND
system_tar.id != system_src.id AND
system_tar.active = 1
) jumpNodes,
system_src.trueSec trueSec
FROM
system system_src INNER JOIN
map ON
map.id = system_src.mapId
WHERE
system_src.mapId IN (" . implode(', ', $mapIds) . ") AND
system_src.active = 1 AND
map.active = 1
HAVING
-- skip systems without neighbors (e.g. WHs)
jumpNodes IS NOT NULL
";
$rows = $this->getDB()->exec($query, null, $this->dynamicJumpDataCacheTime);
if(count($rows) > 0){
// update jump data for this instance
$this->updateJumpData($rows);
}
}
}
}
/**
* update jump data for this instance
* -> data is either coming from CCPs [SDE] OR from map specific data
* @param array $rows
*/
private function updateJumpData($rows = []){
foreach($rows as $row){
$regionId = (int)$row['regionId'];
$constId = (int)$row['constellationId'];
$systemName = strtoupper($row['systemName']);
$systemId = (int)$row['systemId'];
$secStatus = (float)$row['trueSec'];
// fill "nameArray" data ----------------------------------------------------
if( !isset($this->nameArray[$systemId]) ){
$this->nameArray[$systemId][0] = $systemName;
$this->nameArray[$systemId][1] = $regionId;
$this->nameArray[$systemId][2] = $constId;
$this->nameArray[$systemId][3] = $secStatus;
}
// fill "idArray" data ------------------------------------------------------
if( !isset($this->idArray[$systemName]) ){
$this->idArray[$systemName] = $systemId;
}
// fill "jumpArray" data ----------------------------------------------------
if( !is_array($this->jumpArray[$systemName]) ){
$this->jumpArray[$systemName] = [];
}
$this->jumpArray[$systemName] = array_merge( explode(':', strtoupper($row['jumpNodes'])), $this->jumpArray[$systemName] );
// add systemId to end (if not already there)
if(end($this->jumpArray[$systemName]) != $systemId){
array_push($this->jumpArray[$systemName],$systemId);
}
}
}
/**
* get system data by systemId and dataName
* @param $systemId
* @param $option
* @return null
*/
private function getSystemInfoBySystemId($systemId, $option){
$info = null;
switch($option){
case 'systemName':
$info = $this->nameArray[ $systemId ][0];
break;
case 'regionId':
$info = $this->nameArray[ $systemId ][1];
break;
case 'constellationId':
$info = $this->nameArray[ $systemId ][2];
break;
case 'trueSec':
$info = $this->nameArray[ $systemId ][3];
break;
}
return $info;
}
/**
* recursive search function within a undirected graph
* @param $G
* @param $A
* @param $B
* @param int $M
* @return array
*/
private function graph_find_path(&$G, $A, $B, $M = 50000){
// $P will hold the result path at the end.
// Remains empty if no path was found.
$P = array();
// For each Node ID create a "visit information",
// initially set as 0 (meaning not yet visited)
// as soon as we visit a node we will tag it with the "source"
// so we can track the path when we reach the search target
$V = array();
// We are going to keep a list of nodes that are "within reach",
// initially this list will only contain the start node,
// then gradually expand (almost like a flood fill)
$R = array(trim($A));
$A = trim($A);
$B = trim($B);
while(count($R) > 0 && $M > 0){
$M--;
$X = trim(array_shift($R));
if( array_key_exists($X, $G) ){
foreach($G[$X] as $Y){
$Y = trim($Y);
// See if we got a solution
if($Y == $B){
// We did? Construct a result path then
array_push($P, $B);
array_push($P, $X);
while($V[$X] != $A){
array_push($P, trim($V[$X]));
$X = $V[$X];
}
array_push($P, $A);
return array_reverse($P);
}
// First time we visit this node?
if(!array_key_exists($Y, $V)){
// Store the path so we can track it back,
$V[$Y] = $X;
// and add it to the "within reach" list
array_push($R, $Y);
}
}
}
}
return $P;
}
/**
* find a route between two systems (system names)
* $searchDepth for recursive route search (5000 would be best but slow)
* -> in reality there are no routes > 100 jumps between systems
* @param $systemFrom
* @param $systemTo
* @param int $searchDepth
* @return array
*/
public function findRoute($systemFrom, $systemTo, $searchDepth = 5000){
$routeData = [
'routePossible' => false,
'routeJumps' => 0,
'route' => []
];
if(
!empty($systemFrom) &&
!empty($systemTo)
){
$from = strtoupper( $systemFrom );
$to = strtoupper( $systemTo );
// jump counter
$jumpNum = 0;
if( isset($this->jumpArray[$from]) ){
// check if the system we are looking for is a direct neighbour
foreach( $this->jumpArray[$from] as $n ) {
if ($n == $to) {
$jumpNum = 2;
$jumpNode = [
'system' => $n,
'security' => $this->getSystemInfoBySystemId($this->idArray[$n], 'trueSec')
];
$routeData['route'][] = $jumpNode;
break;
}
}
// system is not a direct neighbour -> search recursive its neighbours
if ($jumpNum == 0) {
foreach( $this->graph_find_path( $this->jumpArray, $from, $to, $searchDepth ) as $n ) {
if ($jumpNum > 0) {
$jumpNode = [
'system' => $n,
'security' => $this->getSystemInfoBySystemId($this->idArray[$n], 'trueSec')
];
$routeData['route'][] = $jumpNode;
}
$jumpNum++;
}
}
if ($jumpNum > 0) {
// route found
$routeData['routePossible'] = true;
$jumpNode = [
'system' => $from,
'security' => $this->getSystemInfoBySystemId($this->idArray[$from], 'trueSec')
];
// insert "from" system on top
array_unshift($routeData['route'], $jumpNode);
} else {
// route not found
$routeData['routePossible'] = false;
}
}
// route jumps
$routeData['routeJumps'] = $jumpNum - 1;
}
return $routeData;
}
/**
* get key for route cache
* @param $mapIds
* @param $systemFrom
* @param $systemTo
* @param array $filterData
* @return string
*/
private function getRouteCacheKey($mapIds, $systemFrom, $systemTo, $filterData = []){
$keyParts = [
implode('_', $mapIds),
self::formatHiveKey($systemFrom),
self::formatHiveKey($systemTo)
];
$keyParts += $filterData;
$key = 'route_' . hash('md5', implode('_', $keyParts));
return $key;
}
/**
* search multiple route between two systems
* @param \Base $f3
*/
public function search($f3){
$requestData = (array)$f3->get('POST');
$activeCharacter = $this->getCharacter();
$return = (object) [];
$return->error = [];
$return->routesData = [];
if( !empty($requestData['routeData']) ){
$routesData = (array)$requestData['routeData'];
// map data where access was already checked -> cached data
$validMaps = [];
/**
* @var $map Model\MapModel
*/
$map = Model\BasicModel::getNew('MapModel');
foreach($routesData as $key => $routeData){
// mapIds are optional. If mapIds is empty or not set
// route search is limited to CCPs static data
$mapData = (array)$routeData['mapIds'];
$mapData = array_flip( array_map('intval', $mapData) );
// check map access (filter requested mapIDs and format) --------------------
array_walk($mapData, function(&$item, &$key, $data){
if( isset($data[1][$key]) ){
// character has mas access -> do not check again
$item = $data[1][$key];
}else{
// check map access for current character
$data[0]->getById($key);
if( $data[0]->hasAccess($data[2]) ){
$item = ['id' => $key, 'name' => $data[0]->name];
}else{
$item = false;
}
$data[0]->reset();
}
}, [$map, $validMaps, $activeCharacter]);
// filter maps with NO access right
$mapData = array_filter($mapData);
$mapIds = array_column($mapData, 'id');
// add map data to cache array
$validMaps += $mapData;
// search route with filter options
$filterData = [
'stargates' => (bool) $routeData['stargates'],
'jumpbridges' => (bool) $routeData['jumpbridges'],
'wormholes' => (bool) $routeData['wormholes'],
'wormholesReduced' => (bool) $routeData['wormholesReduced'],
'wormholesCritical' => (bool) $routeData['wormholesCritical']
];
$returnRoutData = [
'systemFromData' => $routeData['systemFromData'],
'systemToData' => $routeData['systemToData'],
'maps' => $mapData,
'mapIds' => $mapIds
];
// add filter options for each route as well
$returnRoutData += $filterData;
if(count($mapIds) > 0){
$systemFrom = $routeData['systemFromData']['name'];
$systemTo = $routeData['systemToData']['name'];
$cacheKey = $this->getRouteCacheKey(
$mapIds,
$systemFrom,
$systemTo,
$filterData
);
if($f3->exists($cacheKey)){
// get data from cache
$returnRoutData = $f3->get($cacheKey);
}else{
// set jump data for following route search
$this->initJumpData($mapIds, $filterData);
// no cached route data found
$foundRoutData = $this->findRoute($systemFrom, $systemTo);
$returnRoutData = array_merge($returnRoutData, $foundRoutData);
// cache if route was found
if(
isset($returnRoutData['routePossible']) &&
$returnRoutData['routePossible'] === true
){
$f3->set($cacheKey, $returnRoutData, $this->dynamicJumpDataCacheTime);
}
}
}
$return->routesData[] = $returnRoutData;
}
}
echo json_encode($return);
}
}