Files
pathfinder/app/main/controller/api/route.php

406 lines
12 KiB
PHP

<?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
* @var int
*/
private $jumpDataCacheTime = 0;
/**
* 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 = [];
function __construct() {
parent::__construct();
// set cache time for static jump data
$this->jumpDataCacheTime = 60 * 60 * 24;
// set static system jump data
$this->setSystemJumpData();
}
/**
* set static system jump data for this instance
* the data is fixed and should not change
*/
private function setSystemJumpData(){
$cacheKey = 'staticJumpData';
$cacheKeyNamedArray = $cacheKey . '.nameArray';
$cacheKeyJumpArray = $cacheKey . '.jumpArray';
$cacheKeyIdArray = $cacheKey . '.idArray';
if(
$this->f3->exists($cacheKeyNamedArray) &&
$this->f3->exists($cacheKeyJumpArray) &&
$this->f3->exists($cacheKeyIdArray)
){
// get cached values
$this->nameArray = $this->f3->get($cacheKeyNamedArray);
$this->jumpArray = $this->f3->get($cacheKeyJumpArray);
$this->idArray = $this->f3->get($cacheKeyIdArray);
}else{
// nothing cached
$pfDB = $this->getDB('PF');
$query = "SELECT * FROM system_neighbour";
$rows = $pfDB->exec($query, null, $this->jumpDataCacheTime);
foreach($rows as $row){
$regionId = $row['regionId'];
$constId = $row['constellationId'];
$systemName = strtoupper($row['systemName']);
$systemId = $row['systemId'];
$secStatus = $row['trueSec'];
$this->nameArray[$systemId][0] = $systemName;
$this->nameArray[$systemId][1] = $regionId;
$this->nameArray[$systemId][2] = $constId;
$this->nameArray[$systemId][3] = $secStatus;
$this->idArray[strtoupper($systemName)] = $systemId;
$this->jumpArray[$systemName]= explode(":", strtoupper($row['jumpNodes']));
array_push($this->jumpArray[$systemName],$systemId);
}
$this->f3->set($cacheKeyNamedArray, $this->nameArray, $this->jumpDataCacheTime);
$this->f3->set($cacheKeyJumpArray, $this->jumpArray, $this->jumpDataCacheTime);
$this->f3->set($cacheKeyIdArray, $this->idArray, $this->jumpDataCacheTime);
}
}
/**
* 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;
}
/**
* This function is just for setting up the cache table 'system_neighbour' which is used
* for system jump calculation. Call this function manually if CCP adds Systems/Stargates
*/
private function setupSystemJumpTable(){
$pfDB = $this->getDB('PF');
$ccpDB = $this->getDB('CCP');
$query = "SELECT
map_sys.solarSystemID system_id,
map_sys.regionID region_id,
map_sys.constellationID constellation_id,
map_sys.solarSystemName system_name,
ROUND( map_sys.security, 2) system_security,
(
SELECT
GROUP_CONCAT( NULLIF(map_sys_inner.solarSystemName, NULL) SEPARATOR ':')
FROM
mapsolarsystemjumps map_jump INNER JOIN
mapsolarsystems map_sys_inner ON
map_sys_inner.solarSystemID = map_jump.toSolarSystemID
WHERE
map_jump.fromSolarSystemID = map_sys.solarSystemID
) system_neighbours
FROM
mapsolarsystems map_sys
HAVING
-- skip systems without neighbors (e.g. WHs)
system_neighbours IS NOT NULL
";
$rows = $ccpDB->exec($query);
if(count($rows) > 0){
// switch DB back to pathfinder DB
// clear cache table
$query = "TRUNCATE system_neighbour";
$pfDB->exec($query);
foreach($rows as $row){
$pfDB->exec("
INSERT INTO
system_neighbour(
regionId,
constellationId,
systemName,
systemId,
jumpNodes,
trueSec
)
VALUES(
:regionId,
:constellationId,
:systemName,
:systemId,
:jumpNodes,
:trueSec
)",
[
':regionId' => $row['region_id'],
':constellationId' => $row['constellation_id'],
':systemName' => $row['system_name'],
':systemId' => $row['system_id'],
':jumpNodes' => $row['system_neighbours'],
':trueSec' => $row['system_security']
]);
}
}
}
/**
* 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;
}
/**
* search multiple route between two systems
* @param $f3
*/
public function search($f3){
$routesData = $data = (array)$f3->get('POST.routeData');
$return = (object) [];
$return->error = [];
$return->routesData = [];
foreach($routesData as $routeData){
$cacheKey = self::formatHiveKey($routeData['systemFrom']) . '_' . self::formatHiveKey($routeData['systemTo']);
if($f3->exists($cacheKey)){
// get data from cache
$return->routesData[] = $f3->get($cacheKey);
}else{
// no cached route data found
$foundRoutData = $this->findRoute($routeData['systemFrom'], $routeData['systemTo']);
$f3->set($cacheKey, $foundRoutData, $this->jumpDataCacheTime);
$return->routesData[] = $foundRoutData;
}
}
echo json_encode($return);
}
}