$fileModified) { continue; } $lastModified = $fileModified; } if ($lastModified === null) { return null; } return $lastModified; } public function getCacheKey(): string { if ($this->cacheKey !== null) { return $this->cacheKey; } if (is_subclass_of('\\sammo\\VersionGit', '\\sammo\VersionGitDynamic')) { $lastModified = $this->findLastModified(); if ($lastModified !== null) { $versionHash = "lt{$lastModified}"; } else { $versionHash = VersionGit::getVersion(); } } else { $versionHash = VersionGit::getVersion(); } $apiVersion = static::CONST_API_VERSION; $serverID = UniqueConst::$serverID; $cacheKey = "{$apiVersion}_{$serverID}_{$versionHash}"; $this->cacheKey = $cacheKey; return $cacheKey; } public function tryCache(): ?APICacheResult { if (is_subclass_of('\\sammo\\VersionGit', '\\sammo\VersionGitDynamic')) { return new APICacheResult(TimeUtil::secondsToDateTime($this->findLastModified()??\time(), true, true)); } return new APICacheResult(null, $this->getCacheKey(), 60, true); } public function readCache(Cache $cache): null|string|array|int|float|bool { $rawJSONCache = $cache->load(static::CACHE_KEY); if ($rawJSONCache === null) { return null; } $jsonCache = Json::decode($rawJSONCache); if (!key_exists('cacheKey', $jsonCache)) { return null; } $jsonCacheKey = $jsonCache['cacheKey']; if ($jsonCacheKey !== $this->getCacheKey()) { return null; } return $jsonCache['data'] ?? null; } public function extractObjClassInfo(string $objKey, callable $callerFunction): array { /** @var \sammo\iAction */ $target = $callerFunction($objKey); if (!($target instanceof \sammo\iAction)) { throw new \RuntimeException("{$objKey}의 대상이 iAction이 아님"); } return [ 'value' => $objKey, 'name' => $target->getName(), 'info' => $target->getInfo(), ]; } public function extractObjClassInfoFromArray(array $constArray, callable $callerFunction): array { $result = []; foreach ($constArray as $key => $target) { if (is_string($target)) { $actionInfo = $this->extractObjClassInfo($target, $callerFunction); if (is_string($key)) { $result[$key] = $actionInfo; } else { $result[$target] = $actionInfo; } continue; } if (is_array($target)) { $result[$key] = $this->extractObjClassInfoFromArray($target, $callerFunction); continue; } if(is_string($key) && is_int($target)){ //역전된 상황이다. $result[$key] = $this->extractObjClassInfo($key, $callerFunction); continue; } $result[$key] = $target; } return $result; } public function extractObjClassInfoFromGameConst(string $gameConstKey, callable $callerFunction): array { $gameConstKeyList = explode(".", $gameConstKey); $target = (GameConst::${$gameConstKeyList[0]}) ?? []; foreach(\array_slice($gameConstKeyList, 1) as $gameConstSubKey){ $target = $target[$gameConstSubKey] ?? []; } if (is_string($target)) { return [$target => $this->extractObjClassInfo($target, $callerFunction)]; } if (!is_array($target)) { throw new \RuntimeException("GameConst::{$gameConstKey}의 값이 {$target}임"); } return $this->extractObjClassInfoFromArray($target, $callerFunction); } public function genConstData() { /** @var array */ $gameConstKeys = [ 'nationType' => [ '\sammo\buildNationTypeClass', ['availableNationType', 'neutralNationType'] ], 'specialDomestic' => [ '\sammo\buildGeneralSpecialDomesticClass', ['defaultSpecialDomestic', 'availableSpecialDomestic', 'optionalSpecialDomestic'] ], 'specialWar' => [ '\sammo\buildGeneralSpecialWarClass', ['defaultSpecialWar', 'availableSpecialWar', 'optionalSpecialWar'] ], 'personality' => [ '\sammo\buildPersonalityClass', ['neutralPersonality', 'availablePersonality', 'optionalPersonality'] ], 'item' => [ '\sammo\buildItemClass', ['allItems'], 1 ], ]; //GameConst 중 BaseCommand는 시간 흐름, 장수에 따라 정보가 달라지므로 따로 처리해야함 $iActionInfo = []; $iActionKeyMap = []; foreach ($gameConstKeys as $mappedKey => $callerTarget) { $flatLevel = 0; if (is_array($callerTarget)) { $callerFunction = $callerTarget[0]; $gameConstSubKey = $callerTarget[1]; if(count($callerTarget) > 2){ /** @var int */ $flatLevel = $callerTarget[2]; } } else { $gameConstSubKey = [$mappedKey]; $callerFunction = $callerTarget; } if (!is_callable($callerFunction)) { throw new \RuntimeException("{$mappedKey} => {$callerFunction}이 callable이 아님"); } $actionInfo = []; foreach ($gameConstSubKey as $key) { $appendInfo = $this->extractObjClassInfoFromGameConst($key, $callerFunction); $actionInfo = array_merge($actionInfo, $appendInfo); $iActionKeyMap[$key] = $mappedKey; } if($flatLevel > 0){ foreach(range(0, $flatLevel - 1) as $tryFlatLevel){ $actionInfo = array_merge(...array_values($actionInfo)); } } $iActionInfo[$mappedKey] = $actionInfo; } $crewtypeMap = []; foreach(GameUnitConst::all() as $crewtypeObj){ $crewtypeMap[$crewtypeObj->id] = [ 'value'=>(string)$crewtypeObj->id, 'name'=>$crewtypeObj->name, 'info'=>$crewtypeObj->getInfo(), ]; } $iActionInfo['crewtype'] = $crewtypeMap; return [ 'gameConst' => get_class_vars('\sammo\GameConst'), 'gameUnitConst' => GameUnitConst::all(), 'cityConst' => CityConst::all(), 'cityConstMap' => [ 'region' => CityConst::$regionMap, 'level' => CityConst::$levelMap, ], 'iActionInfo' => $iActionInfo, 'iActionKeyMap' => $iActionKeyMap, 'version' => VersionGit::getVersion(), ]; } public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType { $cacheDir = $this->rootPath . '/data/file_cache'; if (!prepareDir($cacheDir)) { throw new RuntimeException('cache 폴더 없음'); } $absolutePath = realpath($cacheDir); if (!$absolutePath) { throw new RuntimeException('cache 폴더 없음'); } $storage = new \Nette\Caching\Storages\FileStorage($cacheDir); $cache = new Cache($storage); $currentCacheKey = $this->tryCache(); if($modifiedSince !== null && $currentCacheKey->lastModified == $modifiedSince){ return null; } if($reqEtag !== null && $currentCacheKey->etag == $reqEtag){ return null; } $constCache = $this->readCache($cache); if ($constCache !== null) { return [ 'result' => true, 'cacheKey' => $this->getCacheKey(), 'data' => $constCache, ]; } $constCache = $this->genConstData(); $cache->save(static::CACHE_KEY, Json::encode([ 'cacheKey' => $this->getCacheKey(), 'data' => $constCache ])); return [ 'result' => true, 'cacheKey' => $this->getCacheKey(), 'data' => $constCache, ]; } }