*/ protected Map $rankVarRead; /** @var \sammo\ActionLogger */ protected $logger; const TURNTIME_FULL_MS = -1; const TURNTIME_FULL = 0; const TURNTIME_HMS = 1; const TURNTIME_HM = 2; protected static $prohibitedDirectUpdateVars = [ //Reason: iAction 'leadership' => 1, 'power' => 1, 'intel' => 1, 'nation' => 2, 'officer_level' => 1, //NOTE: officerLevelObj로 인해 국가의 '레벨'이 바뀌는 것도 조심해야 하나, 국가 레벨의 변경은 월 초/말에만 일어남. 'special' => 1, 'special2' => 1, 'personal' => 1, 'horse' => 1, 'weapon' => 1, 'book' => 1, 'item' => 1 ]; /** * @param array $raw DB row값. * @param null|array $city DB city 테이블의 row값 * @param int|null $year 게임 연도 * @param int|null $month 게임 월 */ public function __construct(array $raw, ?Map $rawRank, ?array $city, ?array $nation, ?int $year, ?int $month) { if ($nation === null) { $nation = getNationStaticInfo($raw['nation']); } $this->raw = $raw; $this->rawCity = $city; if ($year !== null && $month !== null) { $this->initLogger($year, $month); } if ($rawRank) { $this->rankVarRead = $rawRank; } else { $this->rankVarRead = new Map(); } } function initLogger(int $year, int $month) { $this->logger = new ActionLogger( $this->getVar('no'), $this->getVar('nation'), $year, $month, false ); } function getTurnTime(int $short = self::TURNTIME_FULL_MS): ?string { if(!key_exists('turntime', $this->raw)){ return null; } return [ self::TURNTIME_FULL_MS => function ($turntime) { return $turntime; }, self::TURNTIME_FULL => function ($turntime) { return substr($turntime, 0, 19); }, self::TURNTIME_HMS => function ($turntime) { return substr($turntime, 11, 8); }, self::TURNTIME_HM => function ($turntime) { return substr($turntime, 11, 5); }, ][$short]($this->getVar('turntime')); } function getNPCType(): int { return $this->raw['npc']; } function getName(): string { return $this->raw['name']; } function getID(): int { return $this->raw['no']; } function getRawCity(): ?array { return $this->rawCity; } function setRawCity(?array $city) { $this->rawCity = $city; } function getCityID(): int { return $this->raw['city']; } function getNationID(): int { return $this->raw['nation']; } function getStaticNation(): array { return getNationStaticInfo($this->raw['nation']); } function getLogger(): ?ActionLogger { return $this->logger; } function getDex(GameUnitDetail $crewType) { $armType = $crewType->armType; if ($armType == GameUnitConst::T_CASTLE) { $armType = GameUnitConst::T_SIEGE; } return $this->getVar("dex{$armType}"); } function getRankVar(RankColumn $key, $defaultValue = null): int { if (!$this->rankVarRead->hasKey($key)) { if ($defaultValue === null) { throw new \RuntimeException('인자가 없음 : ' . $key->value); } return $defaultValue; } return $this->rankVarRead[$key]; } /** * @param \MeekroDB $db */ function applyDB($db): bool { $updateVals = $this->getUpdatedValues(); $generalID = $this->getID(); $result = false; if ($updateVals) { $db->update('general', $updateVals, 'no=%i', $generalID); $result = $result || $db->affectedRows() > 0; if (key_exists('nation', $updateVals)) { $db->update('rank_data', [ 'nation_id' => $updateVals['nation'] ], 'general_id = %i', $generalID); $result = true; } $this->flushUpdateValues(); } $this->getLogger()->flush(); return $result; } static public function mergeQueryColumn(?array $reqColumns = null, GeneralQueryMode $queryMode = GeneralQueryMode::Full): array { $minimumColumn = ['no', 'name', 'owner', 'npc', 'city', 'nation', 'officer_level', 'officer_city']; $defaultEventColumn = [ 'no', 'name', 'npc', 'owner', 'city', 'nation', 'officer_level', 'officer_city', 'special', 'special2', 'personal', 'horse', 'weapon', 'book', 'item', 'last_turn', 'aux', 'turntime', ]; $fullColumn = [ 'no', 'name', 'owner', 'owner_name', 'picture', 'imgsvr', 'nation', 'city', 'troop', 'injury', 'affinity', 'leadership', 'leadership_exp', 'strength', 'strength_exp', 'intel', 'intel_exp', 'weapon', 'book', 'horse', 'item', 'experience', 'dedication', 'officer_level', 'officer_city', 'gold', 'rice', 'crew', 'crewtype', 'train', 'atmos', 'turntime', 'makelimit', 'killturn', 'block', 'dedlevel', 'explevel', 'age', 'startage', 'belong', 'personal', 'special', 'special2', 'defence_train', 'tnmt', 'npc', 'npc_org', 'deadyear', 'npcmsg', 'dex1', 'dex2', 'dex3', 'dex4', 'dex5', 'betray', 'recent_war', 'last_turn', 'myset', 'specage', 'specage2', 'aux', 'permission', 'penalty', ]; $fullAcessLogColumn = [ GeneralAccessLogColumn::refreshScore, GeneralAccessLogColumn::refreshScoreTotal, ]; if ($reqColumns === null) { switch ($queryMode) { case GeneralQueryMode::Core: return [$minimumColumn, [], []]; case GeneralQueryMode::Lite: return [$defaultEventColumn, [], []]; case GeneralQueryMode::FullWithoutIAction: case GeneralQueryMode::Full: return [$fullColumn, RankColumn::cases(), []]; case GeneralQueryMode::FullWithAccessLog: return [$fullColumn, RankColumn::cases(), $fullAcessLogColumn]; } } /** @var RankColumn[] */ $rankColumn = []; $subColumn = []; $accessLogColumn = []; foreach ($reqColumns as $column) { if ($column instanceof RankColumn) { $rankColumn[] = $column; continue; } if ($column instanceof GeneralAccessLogColumn) { $accessLogColumn[] = $column; continue; } $enumKey = RankColumn::tryFrom($column); if ($enumKey !== null) { $rankColumn[] = $enumKey; continue; } $enumKey = GeneralAccessLogColumn::tryFrom($column); if ($enumKey !== null) { $accessLogColumn[] = $enumKey; continue; } $subColumn[] = $column; } switch ($queryMode) { case GeneralQueryMode::Core: return [array_unique(array_merge($minimumColumn, $subColumn)), $rankColumn, $accessLogColumn]; case GeneralQueryMode::Lite: return [array_unique(array_merge($defaultEventColumn, $subColumn)), $rankColumn, $accessLogColumn]; case GeneralQueryMode::FullWithoutIAction: case GeneralQueryMode::Full: return [array_unique(array_merge($fullColumn, $subColumn)), $rankColumn, $accessLogColumn]; case GeneralQueryMode::FullWithAccessLog: return [array_unique(array_merge($fullColumn, $subColumn)), $rankColumn, array_unique(array_merge($fullAcessLogColumn, $accessLogColumn))]; default: throw new \RuntimeException('invalid query mode'); } } /** * @param ?int[] $generalIDList * @param null|array $column * @param GeneralQueryMode $queryMode * @return \sammo\GeneralLite[] * @throws MustNotBeReachedException */ static public function createGeneralLiteObjListFromDB(?array $generalIDList, ?array $column = null, GeneralQueryMode $queryMode = GeneralQueryMode::FullWithoutIAction): array { if ($generalIDList === []) { return []; } if($queryMode > GeneralQueryMode::FullWithoutIAction){ throw new \InvalidArgumentException('지원하지 않는 queryMode:' . $queryMode->name); } $db = DB::db(); if ($queryMode->value > 0) { $gameStor = KVStorage::getStorage($db, 'game_env'); [$year, $month] = $gameStor->getValuesAsArray(['year', 'month']); } else { $year = null; $month = null; } /** * @var string[] $column */ [$column, $rankColumn,] = static::mergeQueryColumn($column, $queryMode); if ($generalIDList === null) { $rawGenerals = Util::convertArrayToDict( $db->query('SELECT %l FROM general WHERE 1', Util::formatListOfBackticks($column)), 'no' ); $generalIDList = array_keys($rawGenerals); } else { $rawGenerals = Util::convertArrayToDict( $db->query('SELECT %l FROM general WHERE no IN %li', Util::formatListOfBackticks($column), $generalIDList), 'no' ); } /** @var Map> */ $rawRanks = new Map(); if ($rankColumn) { $rawValue = $db->queryAllLists( 'SELECT `general_id`, `type`, `value` FROM rank_data WHERE general_id IN %li AND `type` IN %ls', $generalIDList, array_map(fn (\BackedEnum $e) => $e->value, $rankColumn) ); foreach ($rawValue as [$generalID, $rawRankType, $rankValue]) { if (!$rawRanks->hasKey($generalID)) { $rawRanks[$generalID] = new Map(); } $rankType = RankColumn::from($rawRankType); $rawRanks[$generalID][$rankType] = $rankValue; } } $result = []; foreach ($generalIDList as $generalID) { if (!key_exists($generalID, $rawGenerals)) { $result[$generalID] = new DummyGeneral($queryMode->value > 0); continue; } $result[$generalID] = new static($rawGenerals[$generalID], $rawRanks[$generalID] ?? null, null, null, $year, $month); } return $result; } static public function createGeneralLiteObjFromDB(int $generalID, ?array $column = null, GeneralQueryMode $queryMode = GeneralQueryMode::FullWithoutIAction): self { if($queryMode > GeneralQueryMode::FullWithoutIAction){ throw new \InvalidArgumentException('지원하지 않는 queryMode:' . $queryMode->name); } $db = DB::db(); if ($queryMode->value > 0) { $gameStor = KVStorage::getStorage($db, 'game_env'); [$year, $month] = $gameStor->getValuesAsArray(['year', 'month']); } else { $year = null; $month = null; } /** * @var string[] $column * @var RankColumn[] $rankColumn */ [$column, $rankColumn,] = static::mergeQueryColumn($column, $queryMode); $rawGeneral = $db->queryFirstRow('SELECT %l FROM general WHERE no = %i', Util::formatListOfBackticks($column), $generalID); if (!$rawGeneral) { return new DummyGeneral($queryMode->value > 0); } $rawRankValues = new Map(); if ($rankColumn) { $rawValue = $db->queryAllLists( 'SELECT `type`, `value` FROM rank_data WHERE general_id = %i AND `type` IN %ls', $generalID, array_map(fn (\BackedEnum $e) => $e->value, $rankColumn) ); foreach ($rawValue as [$rawRankType, $rankValue]) { $rankType = RankColumn::tryFrom($rawRankType); $rawRankValues->put($rankType, $rankValue); } } $general = new static($rawGeneral, $rawRankValues, null, null, $year, $month); return $general; } }