416 lines
14 KiB
PHP
416 lines
14 KiB
PHP
<?php
|
|
|
|
namespace sammo;
|
|
|
|
use Ds\Map;
|
|
use sammo\Command\GeneralCommand;
|
|
use sammo\Enums\GeneralAccessLogColumn;
|
|
use sammo\Enums\GeneralQueryMode;
|
|
use sammo\Enums\InheritanceKey;
|
|
use sammo\Enums\RankColumn;
|
|
use sammo\WarUnitTrigger as WarUnitTrigger;
|
|
|
|
class GeneralLite
|
|
{
|
|
use LazyVarUpdater;
|
|
|
|
protected $raw = [];
|
|
protected $rawCity = null;
|
|
|
|
/** @var Map<RankColumn,int> */
|
|
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 updateVar(string $key, $value)
|
|
{
|
|
if (($this->raw[$key] ?? null) === $value) {
|
|
return;
|
|
}
|
|
if (!key_exists($key, $this->updatedVar)) {
|
|
$this->updatedVar[$key] = true;
|
|
}
|
|
$this->raw[$key] = $value;
|
|
}
|
|
|
|
|
|
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<string|RankColumn> $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<int,Map<RankColumn,int|float>> */
|
|
$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;
|
|
}
|
|
}
|