diff --git a/app/cron.ini b/app/cron.ini index 44f44724..c0fdfc7c 100644 --- a/app/cron.ini +++ b/app/cron.ini @@ -29,9 +29,6 @@ deleteEolConnections = Cron\MapUpdate->deleteEolConnections, @f ; delete expired wh connections deleteExpiredConnections = Cron\MapUpdate->deleteExpiredConnections, @hourly -; disable character log data -deactivateLogData = Cron\CharacterUpdate->deactivateLogData, @instant - ; delete character log data deleteLogData = Cron\CharacterUpdate->deleteLogData, @instant diff --git a/app/environment.ini b/app/environment.ini index 8dc3624e..672cf564 100644 --- a/app/environment.ini +++ b/app/environment.ini @@ -1,7 +1,7 @@ ; Environment Config [ENVIRONMENT] -; project environment (DEVELOP, PRODUCTION). +; project environment (DEVELOP || PRODUCTION). ; This effects: DB connection, Mail-Server, SSO, CREST configurations in this file ; configuration below SERVER = DEVELOP @@ -34,7 +34,7 @@ CCP_SSO_SECRET_KEY = ; CCP ESI API CCP_ESI_URL = https://esi.tech.ccp.is CCP_ESI_DATASOURCE = singularity -CCP_ESI_SCOPES = esi-location.read_location.v1,esi-location.read_ship_type.v1,esi-ui.write_waypoint.v1,esi-ui.open_window.v1 +CCP_ESI_SCOPES = esi-location.read_online.v1,esi-location.read_location.v1,esi-location.read_ship_type.v1,esi-ui.write_waypoint.v1,esi-ui.open_window.v1 CCP_ESI_SCOPES_ADMIN = esi-corporations.read_corporation_membership.v1 ; SMTP settings (optional) @@ -80,7 +80,7 @@ CCP_SSO_SECRET_KEY = ; CCP ESI API CCP_ESI_URL = https://esi.tech.ccp.is CCP_ESI_DATASOURCE = tranquility -CCP_ESI_SCOPES = esi-location.read_location.v1,esi-location.read_ship_type.v1,esi-ui.write_waypoint.v1,esi-ui.open_window.v1 +CCP_ESI_SCOPES = esi-location.read_online.v1,esi-location.read_location.v1,esi-location.read_ship_type.v1,esi-ui.write_waypoint.v1,esi-ui.open_window.v1 CCP_ESI_SCOPES_ADMIN = esi-corporations.read_corporation_membership.v1 ; SMTP settings (optional) diff --git a/app/main/controller/api/user.php b/app/main/controller/api/user.php index 72b999c0..e49eed7a 100644 --- a/app/main/controller/api/user.php +++ b/app/main/controller/api/user.php @@ -58,7 +58,8 @@ class User extends Controller\Controller{ [ 'ID' => $characterModel->_id, 'NAME' => $characterModel->name, - 'TIME' => (new \DateTime())->getTimestamp() + 'TIME' => (new \DateTime())->getTimestamp(), + 'UPDATE_RETRY' => 0 ] ]; diff --git a/app/main/cron/characterupdate.php b/app/main/cron/characterupdate.php index 6cadf0d6..7ac9ff76 100644 --- a/app/main/cron/characterupdate.php +++ b/app/main/cron/characterupdate.php @@ -13,22 +13,15 @@ use Model; class CharacterUpdate { - const CHARACTER_LOG_ACTIVE = 300; - const CHARACTER_LOG_INACTIVE = 300; + const CHARACTER_LOG_INACTIVE = 300; /** - * get "active" time for character log data in seconds - * -> get default value in case of nothing found in *.ini file - * @param \Base $f3 - * @return int + * max count of "inactive" character log data that will be checked for offline status */ - protected function getCharacterLogActiveTime(\Base $f3){ - $logActiveTime = (int)$f3->get('PATHFINDER.CACHE.CHARACTER_LOG_ACTIVE'); - return ($logActiveTime >= 0) ? $logActiveTime : self::CHARACTER_LOG_ACTIVE; - } + const CHARACTERS_UPDATE_LOGS_MAX = 20; /** - * get "incactive" time for character log data in seconeds + * get "inactive" time for character log data in seconds * @param \Base $f3 * @return int */ @@ -37,38 +30,6 @@ class CharacterUpdate { return ($logInactiveTime >= 0) ? $logInactiveTime : self::CHARACTER_LOG_INACTIVE; } - /** - * set character log data as inactive in case of not changed within X seconds - * >> php index.php "/cron/deactivateLogData" - * @param \Base $f3 - */ - public function deactivateLogData(\Base $f3){ - DB\Database::instance()->getDB('PF'); - $logActiveTime = $this->getCharacterLogActiveTime($f3); - - /** - * @var $characterLogModel Model\CharacterLogModel - */ - $characterLogModel = Model\BasicModel::getNew('CharacterLogModel', 0); - - // find expired character logs - $characterLogs = $characterLogModel->find([ - 'active = :active AND TIMESTAMPDIFF(SECOND, updated, NOW() ) > :lifetime', - ':active' => 1, - ':lifetime' => $logActiveTime - ]); - - if(is_object($characterLogs)){ - foreach($characterLogs as $characterLog){ - /** - * @var $characterLog Model\CharacterLogModel - */ - $characterLog->setActive(false); - $characterLog->save(); - } - } - } - /** * delete all character log data that were set to "active = 0" after X seconds of no changes * -> see deactivateLogData() @@ -84,11 +45,13 @@ class CharacterUpdate { */ $characterLogModel = Model\BasicModel::getNew('CharacterLogModel', 0); - // find expired character logs + // find character logs that were not checked recently and update $characterLogs = $characterLogModel->find([ - 'active = :active AND TIMESTAMPDIFF(SECOND, updated, NOW() ) > :lifetime', - ':active' => 0, + 'TIMESTAMPDIFF(SECOND, updated, NOW() ) > :lifetime', ':lifetime' => $logInactiveTime + ], [ + 'order' => 'updated asc', + 'limit' => self::CHARACTERS_UPDATE_LOGS_MAX ]); if(is_object($characterLogs)){ @@ -96,7 +59,11 @@ class CharacterUpdate { /** * @var $characterLog Model\CharacterLogModel */ - $characterLog->erase(); + // force characterLog as "updated" even if no changes were made + $characterLog->characterId->updateLog([ + 'markUpdated' => true, + 'suppressHTTPErrors' => true + ]); } } } diff --git a/app/main/model/alliancemodel.php b/app/main/model/alliancemodel.php index b762fa49..d5bca132 100644 --- a/app/main/model/alliancemodel.php +++ b/app/main/model/alliancemodel.php @@ -85,12 +85,26 @@ class AllianceModel extends BasicModel { /** * get all characters in this alliance + * @param array $characterIds + * @param array $options * @return array */ - public function getCharacters(){ + public function getCharacters($characterIds = [], $options = []){ $characters = []; + $filter = ['active = ?', 1]; + + if( !empty($characterIds) ){ + $filter[0] .= ' AND id IN (?)'; + $filter[] = $characterIds; + } + + $this->filter('allianceCharacters', $filter); + + if($options['hasLog']){ + // just characters with active log data + $this->has('allianceCharacters.characterLog', ['active = ?', 1]); + } - $this->filter('allianceCharacters', ['active = ?', 1]); if($this->allianceCharacters){ foreach($this->allianceCharacters as $character){ diff --git a/app/main/model/characterlogmodel.php b/app/main/model/characterlogmodel.php index ff2fa427..9dc32431 100644 --- a/app/main/model/characterlogmodel.php +++ b/app/main/model/characterlogmodel.php @@ -116,7 +116,6 @@ class CharacterLogModel extends BasicModel { public function getData(){ $logData = (object) []; - $logData->active = $this->active; $logData->system = (object) []; $logData->system->id = (int)$this->systemId; $logData->system->name = $this->systemName; @@ -140,55 +139,12 @@ class CharacterLogModel extends BasicModel { * @return int */ public function set_systemId($systemId){ - if($this->systemId != $systemId){ - $this->setActive(true); - } - if($systemId > 0){ $this->updateCharacterSessionLocation($systemId); } return $systemId; } - /** - * setter for $shipTypeId - * @param int $shipTypeId - * @return int - */ - public function set_shipTypeId($shipTypeId){ - if($this->shipTypeId != $shipTypeId){ - $this->setActive(true); - } - - return $shipTypeId; - } - - /** - * setter for $shipId - * @param int $shipId - * @return int - */ - public function set_shipId($shipId){ - if($this->shipId != $shipId){ - $this->setActive(true); - } - - return $shipId; - } - - /** - * setter for $stationId - * @param int $stationId - * @return int - */ - public function set_stationId($stationId){ - if($this->stationId != $stationId){ - $this->setActive(true); - } - - return $stationId; - } - /** * Event "Hook" function * return false will stop any further action diff --git a/app/main/model/charactermodel.php b/app/main/model/charactermodel.php index 93d1bf6e..ba0ef1ae 100644 --- a/app/main/model/charactermodel.php +++ b/app/main/model/charactermodel.php @@ -621,87 +621,144 @@ class CharacterModel extends BasicModel { * @return $this */ public function updateLog($additionalOptions = []){ - $deleteLog = true; + $deleteLog = false; + $invalidResponse = false; //check if log update is enabled for this user if( $this->logLocation ){ // Try to pull data from API if( $accessToken = $this->getAccessToken() ){ - $locationData = self::getF3()->ccpClient->getCharacterLocationData($this->_id, $accessToken, $additionalOptions); + $onlineData = self::getF3()->ccpClient->getCharacterOnlineData($this->_id, $accessToken, $additionalOptions); - if( !empty($locationData['system']['id']) ){ - // character is currently in-game + // check whether character is currently ingame online + if(is_bool($onlineData['online'])){ + if($onlineData['online'] === true){ + $locationData = self::getF3()->ccpClient->getCharacterLocationData($this->_id, $accessToken, $additionalOptions); - // IDs for "systemId", "stationId and "shipTypeId" that require more data - $lookupIds = []; + if( !empty($locationData['system']['id']) ){ + // character is currently in-game - if( !$characterLog = $this->getLog() ){ - // create new log - $characterLog = $this->rel('characterLog'); - $characterLog->characterId = $this->_id; - } + // IDs for "systemId", "stationId and "shipTypeId" that require more data + $lookupIds = []; - // get current log data and modify on change - $logData = json_decode(json_encode( $characterLog->getData()), true); + if( !$characterLog = $this->getLog() ){ + // create new log + $characterLog = $this->rel('characterLog'); + $characterLog->characterId = $this->_id; + } - if( - empty($logData['system']['name']) || - $logData['system']['id'] !== $locationData['system']['id'] - ){ - // system changed -> request "system name" for current system - $lookupIds[] = $locationData['system']['id']; - } + // get current log data and modify on change + $logData = json_decode(json_encode( $characterLog->getData()), true); - if( !empty($locationData['station']['id']) ){ - if( - empty($logData['station']['name']) || - $logData['station']['id'] !== $locationData['station']['id'] - ){ - // station changed -> request "station name" for current station - $lookupIds[] = $locationData['station']['id']; + if( + empty($logData['system']['name']) || + $logData['system']['id'] !== $locationData['system']['id'] + ){ + // system changed -> request "system name" for current system + $lookupIds[] = $locationData['system']['id']; + } + + if( !empty($locationData['station']['id']) ){ + if( + empty($logData['station']['name']) || + $logData['station']['id'] !== $locationData['station']['id'] + ){ + // station changed -> request "station name" for current station + $lookupIds[] = $locationData['station']['id']; + } + }else{ + unset($logData['station']); + } + + $logData = array_replace_recursive($logData, $locationData); + + // get current ship data + $shipData = self::getF3()->ccpClient->getCharacterShipData($this->_id, $accessToken, $additionalOptions); + + if( !empty($shipData['ship']['typeId']) ){ + if( + empty($logData['ship']['typeName']) || + $logData['ship']['typeId'] !== $shipData['ship']['typeId'] + ){ + // ship changed -> request "station name" for current station + $lookupIds[] = $shipData['ship']['typeId']; + } + + // "shipName"/"shipId" could have changed... + $logData = array_replace_recursive($logData, $shipData); + }else{ + // ship data should never be empty -> keep current one + //unset($logData['ship']); + $invalidResponse = true; + } + + if( !empty($lookupIds) ){ + // get "more" information for some Ids (e.g. name) + $universeData = self::getF3()->ccpClient->getUniverseNamesData($lookupIds, $additionalOptions); + + if( !empty($universeData) ){ + $logData = array_replace_recursive($logData, $universeData); + }else{ + // this is important! universe data is a MUST HAVE! + $deleteLog = true; + } + } + + if( !$deleteLog ){ + // mark log as "updated" even if no changes were made + if($additionalOptions['markUpdated'] === true){ + $characterLog->touch('updated'); + } + + $characterLog->setData($logData); + $characterLog->save(); + + $this->characterLog = $characterLog; + } + }else{ + // systemId should always exists + $invalidResponse = true; } }else{ - unset($logData['station']); + // user is in-game offline + $deleteLog = true; } + }else{ + // online status request failed + $invalidResponse = true; + } + }else{ + // access token request failed + $deleteLog = true; + } + }else{ + // character deactivated location logging + $deleteLog = true; + } - $logData = array_replace_recursive($logData, $locationData); + if( $user = $this->getUser() ){ + // Session data does not exists in CLI mode (Cronjob) + if( $sessionCharacterData = $user->getSessionCharacterData($this->id, false) ){ + $updateRetry = (int)$sessionCharacterData['UPDATE_RETRY']; + $newRetry = $updateRetry; + if($invalidResponse){ + $newRetry++; - // get current ship data - $shipData = self::getF3()->ccpClient->getCharacterShipData($this->_id, $accessToken, $additionalOptions); - - if( !empty($shipData['ship']['typeId']) ){ - if( - empty($logData['ship']['typeName']) || - $logData['ship']['typeId'] !== $shipData['ship']['typeId'] - ){ - // ship changed -> request "station name" for current station - $lookupIds[] = $shipData['ship']['typeId']; - } - - // "shipName"/"shipId" could have changed... - $logData = array_replace_recursive($logData, $shipData); - }else{ - unset($logData['ship']); + if($newRetry >= 3){ + // no proper character log data (3 fails in a row)) + $newRetry = 0; + $deleteLog = true; } + }else{ + // reset retry counter + $newRetry = 0; + } - if( !empty($lookupIds) ){ - // get "more" information for some Ids (e.g. name) - $universeData = self::getF3()->ccpClient->getUniverseNamesData($lookupIds, $additionalOptions); - - if( !empty($universeData) ){ - $deleteLog = false; - $logData = array_replace_recursive($logData, $universeData); - } - }else{ - $deleteLog = false; - } - - if( !$deleteLog ){ - $characterLog->setData($logData); - $characterLog->save(); - - $this->characterLog = $characterLog; - } + if($updateRetry !== $newRetry){ + // update retry counter + $sessionCharacterData['UPDATE_RETRY'] = $newRetry; + $sessionCharacters = self::mergeSessionCharacterData([$sessionCharacterData]); + self::getF3()->set(User::SESSION_KEY_CHARACTERS, $sessionCharacters); } } } diff --git a/app/main/model/corporationmodel.php b/app/main/model/corporationmodel.php index 35de4b83..c60021c8 100644 --- a/app/main/model/corporationmodel.php +++ b/app/main/model/corporationmodel.php @@ -157,9 +157,10 @@ class CorporationModel extends BasicModel { /** * get all characters in this corporation * @param array $characterIds + * @param array $options * @return CharacterModel[] */ - public function getCharacters( $characterIds = []){ + public function getCharacters($characterIds = [], $options = []){ $characters = []; $filter = ['active = ?', 1]; @@ -170,6 +171,11 @@ class CorporationModel extends BasicModel { $this->filter('corporationCharacters', $filter); + if($options['hasLog']){ + // just characters with active log data + $this->has('corporationCharacters.characterLog', ['active = ?', 1]); + } + if($this->corporationCharacters){ foreach($this->corporationCharacters as $character){ $characters[] = $character; diff --git a/app/main/model/mapmodel.php b/app/main/model/mapmodel.php index 281f94a3..b952bd88 100644 --- a/app/main/model/mapmodel.php +++ b/app/main/model/mapmodel.php @@ -573,12 +573,25 @@ class MapModel extends BasicModel { /** * get all (private) characters for this map + * @param array $characterIds + * @param array $options * @return CharacterModel[] */ - private function getCharacters(){ + private function getCharacters($characterIds = [], $options = []){ $characters = []; + $filter = ['active = ?', 1]; - $this->filter('mapCharacters', ['active = ?', 1]); + if( !empty($characterIds) ){ + $filter[0] .= ' AND id IN (?)'; + $filter[] = $characterIds; + } + + $this->filter('mapCharacters', $filter); + + if($options['hasLog']){ + // just characters with active log data + $this->has('mapCharacters.characterLog', ['active = ?', 1]); + } if($this->mapCharacters){ foreach($this->mapCharacters as $characterMapModel){ @@ -591,9 +604,10 @@ class MapModel extends BasicModel { /** * get all character models that are currently online "viewing" this map + * @param array $options filter options * @return CharacterModel[] */ - private function getAllCharacters(){ + private function getAllCharacters($options = []){ $characters = []; if($this->isPrivate()){ @@ -607,13 +621,13 @@ class MapModel extends BasicModel { $corporations = $this->getCorporations(); foreach($corporations as $corporation){ - $characters = array_merge($characters, $corporation->getCharacters()); + $characters = array_merge($characters, $corporation->getCharacters([], $options)); } }elseif($this->isAlliance()){ $alliances = $this->getAlliances(); foreach($alliances as $alliance){ - $characters = array_merge($characters, $alliance->getCharacters()); + $characters = array_merge($characters, $alliance->getCharacters([], $options)); } } @@ -623,15 +637,16 @@ class MapModel extends BasicModel { /** * get data for ALL characters with map access * -> The result of this function is cached! - * @return \stdClass[] + * @param array $options filter options + * @return \stdClass */ - public function getCharactersData(){ + public function getCharactersData($options = []){ // check if there is cached data $charactersData = $this->getCacheData(self::DATA_CACHE_KEY_CHARACTER); if(is_null($charactersData)){ $charactersData = []; - $characters = $this->getAllCharacters(); + $characters = $this->getAllCharacters($options); foreach($characters as $character){ $charactersData[] = $character->getData(true); @@ -844,7 +859,7 @@ class MapModel extends BasicModel { $mapDataAll = $this->getData(); // get data of characters which have with map access - $activeUserCharactersData = $this->getCharactersData(); + $activeUserCharactersData = $this->getCharactersData(['hasLog' => true]); // sort characters by "active" status $sortByActiveLog = function($a, $b){ diff --git a/app/pathfinder.ini b/app/pathfinder.ini index 6e80088a..a8f444a5 100644 --- a/app/pathfinder.ini +++ b/app/pathfinder.ini @@ -128,9 +128,7 @@ EXECUTION_LIMIT = 50 ; CACHE =========================================================================================== [PATHFINDER.CACHE] -; mark characters inactive if nothing (ship/system/...) changed within X seconds (default: 5min) -CHARACTER_LOG_ACTIVE = 300 -; delete character log data if "inactive" for X seconds (seconds) (default: 10min) +; delete character log data if if nothing (ship/system/...) for X seconds (seconds) (default: 5min) CHARACTER_LOG_INACTIVE = 300 ; expire time for static system data (seconds) (default: 20d) CONSTELLATION_SYSTEMS = 1728000