test: export canonical legacy turn snapshots
This commit is contained in:
@@ -0,0 +1,330 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace sammo;
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
http_response_code(404);
|
||||
exit;
|
||||
}
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
require_once 'lib.php';
|
||||
require_once 'func.php';
|
||||
|
||||
/** @return list<int> */
|
||||
function comparisonIntegerList(mixed $value, string $label): array
|
||||
{
|
||||
if ($value === null) {
|
||||
return [];
|
||||
}
|
||||
if (!is_array($value)) {
|
||||
throw new \InvalidArgumentException("{$label} must be an array");
|
||||
}
|
||||
$result = [];
|
||||
foreach ($value as $entry) {
|
||||
if (!is_int($entry) || $entry < 0) {
|
||||
throw new \InvalidArgumentException("{$label} entries must be non-negative integers");
|
||||
}
|
||||
$result[$entry] = $entry;
|
||||
}
|
||||
ksort($result, SORT_NUMERIC);
|
||||
return array_values($result);
|
||||
}
|
||||
|
||||
function comparisonJsonValue(mixed $value): mixed
|
||||
{
|
||||
if (!is_string($value) || $value === '') {
|
||||
return $value;
|
||||
}
|
||||
try {
|
||||
return json_decode($value, true, flags: JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $row */
|
||||
function comparisonPickRow(array $row, array $mapping, array $jsonKeys = []): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($mapping as $canonical => $legacy) {
|
||||
if (!array_key_exists($legacy, $row)) {
|
||||
continue;
|
||||
}
|
||||
$value = $row[$legacy];
|
||||
if (in_array($legacy, $jsonKeys, true)) {
|
||||
$value = comparisonJsonValue($value);
|
||||
}
|
||||
$result[$canonical] = $value;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @param list<int> $ids */
|
||||
function comparisonRowsById(string $table, string $idColumn, array $ids): array
|
||||
{
|
||||
$db = DB::db();
|
||||
$rows = [];
|
||||
foreach ($ids as $id) {
|
||||
$row = $db->queryFirstRow(
|
||||
"SELECT * FROM `{$table}` WHERE `{$idColumn}` = %i",
|
||||
$id,
|
||||
);
|
||||
if ($row !== null) {
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
function comparisonTurnStateSnapshot(array $request): array
|
||||
{
|
||||
$observe = $request['observe'] ?? [];
|
||||
if (!is_array($observe)) {
|
||||
throw new \InvalidArgumentException('observe must be an object');
|
||||
}
|
||||
$generalIds = comparisonIntegerList($observe['generalIds'] ?? [], 'generalIds');
|
||||
$cityIds = comparisonIntegerList($observe['cityIds'] ?? [], 'cityIds');
|
||||
$nationIds = comparisonIntegerList($observe['nationIds'] ?? [], 'nationIds');
|
||||
$logAfterId = $observe['logAfterId'] ?? 0;
|
||||
$messageAfterId = $observe['messageAfterId'] ?? 0;
|
||||
if (!is_int($logAfterId) || $logAfterId < 0 || !is_int($messageAfterId) || $messageAfterId < 0) {
|
||||
throw new \InvalidArgumentException('logAfterId and messageAfterId must be non-negative integers');
|
||||
}
|
||||
|
||||
$db = DB::db();
|
||||
$game = KVStorage::getStorage($db, 'game_env');
|
||||
$game->resetCache();
|
||||
$worldValues = $game->getValues(['year', 'month', 'turnterm', 'turntime', 'isunited']);
|
||||
|
||||
$generals = array_map(
|
||||
static fn(array $row): array => comparisonPickRow(
|
||||
$row,
|
||||
[
|
||||
'id' => 'no',
|
||||
'name' => 'name',
|
||||
'nationId' => 'nation',
|
||||
'cityId' => 'city',
|
||||
'troopId' => 'troop',
|
||||
'leadership' => 'leadership',
|
||||
'strength' => 'strength',
|
||||
'intelligence' => 'intel',
|
||||
'experience' => 'experience',
|
||||
'dedication' => 'dedication',
|
||||
'officerLevel' => 'officer_level',
|
||||
'injury' => 'injury',
|
||||
'gold' => 'gold',
|
||||
'rice' => 'rice',
|
||||
'crew' => 'crew',
|
||||
'crewTypeId' => 'crewtype',
|
||||
'train' => 'train',
|
||||
'atmos' => 'atmos',
|
||||
'age' => 'age',
|
||||
'npcState' => 'npc',
|
||||
'turnTime' => 'turntime',
|
||||
'recentWarTime' => 'recent_war',
|
||||
'lastTurn' => 'last_turn',
|
||||
'meta' => 'aux',
|
||||
'leadershipExp' => 'leadership_exp',
|
||||
'strengthExp' => 'strength_exp',
|
||||
'intelExp' => 'intel_exp',
|
||||
'killTurn' => 'killturn',
|
||||
'mySet' => 'myset',
|
||||
],
|
||||
['last_turn', 'aux'],
|
||||
),
|
||||
comparisonRowsById('general', 'no', $generalIds),
|
||||
);
|
||||
|
||||
$cities = array_map(
|
||||
static fn(array $row): array => comparisonPickRow(
|
||||
$row,
|
||||
[
|
||||
'id' => 'city',
|
||||
'name' => 'name',
|
||||
'nationId' => 'nation',
|
||||
'level' => 'level',
|
||||
'population' => 'pop',
|
||||
'populationMax' => 'pop_max',
|
||||
'agriculture' => 'agri',
|
||||
'agricultureMax' => 'agri_max',
|
||||
'commerce' => 'comm',
|
||||
'commerceMax' => 'comm_max',
|
||||
'security' => 'secu',
|
||||
'securityMax' => 'secu_max',
|
||||
'supplyState' => 'supply',
|
||||
'frontState' => 'front',
|
||||
'defence' => 'def',
|
||||
'defenceMax' => 'def_max',
|
||||
'wall' => 'wall',
|
||||
'wallMax' => 'wall_max',
|
||||
'state' => 'state',
|
||||
'term' => 'term',
|
||||
'trust' => 'trust',
|
||||
'trade' => 'trade',
|
||||
],
|
||||
),
|
||||
comparisonRowsById('city', 'city', $cityIds),
|
||||
);
|
||||
|
||||
$nations = array_map(
|
||||
static fn(array $row): array => comparisonPickRow(
|
||||
$row,
|
||||
[
|
||||
'id' => 'nation',
|
||||
'name' => 'name',
|
||||
'color' => 'color',
|
||||
'capitalCityId' => 'capital',
|
||||
'gold' => 'gold',
|
||||
'rice' => 'rice',
|
||||
'tech' => 'tech',
|
||||
'level' => 'level',
|
||||
'typeCode' => 'type',
|
||||
'generalCount' => 'gennum',
|
||||
'power' => 'power',
|
||||
'war' => 'war',
|
||||
'meta' => 'aux',
|
||||
],
|
||||
['aux'],
|
||||
),
|
||||
comparisonRowsById('nation', 'nation', $nationIds),
|
||||
);
|
||||
|
||||
$diplomacy = [];
|
||||
foreach ($nationIds as $fromNationId) {
|
||||
foreach ($nationIds as $toNationId) {
|
||||
if ($fromNationId === $toNationId) {
|
||||
continue;
|
||||
}
|
||||
$row = $db->queryFirstRow(
|
||||
'SELECT me, you, state, term, dead FROM diplomacy WHERE me = %i AND you = %i',
|
||||
$fromNationId,
|
||||
$toNationId,
|
||||
);
|
||||
if ($row !== null) {
|
||||
$diplomacy[] = comparisonPickRow($row, [
|
||||
'fromNationId' => 'me',
|
||||
'toNationId' => 'you',
|
||||
'state' => 'state',
|
||||
'term' => 'term',
|
||||
'dead' => 'dead',
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$generalTurns = [];
|
||||
foreach ($generalIds as $generalId) {
|
||||
foreach ($db->query(
|
||||
'SELECT general_id, turn_idx, action, arg FROM general_turn WHERE general_id = %i ORDER BY turn_idx',
|
||||
$generalId,
|
||||
) as $row) {
|
||||
$generalTurns[] = comparisonPickRow(
|
||||
$row,
|
||||
['generalId' => 'general_id', 'turnIndex' => 'turn_idx', 'action' => 'action', 'args' => 'arg'],
|
||||
['arg'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$nationTurns = [];
|
||||
foreach ($nationIds as $nationId) {
|
||||
foreach ($db->query(
|
||||
'SELECT nation_id, officer_level, turn_idx, action, arg FROM nation_turn WHERE nation_id = %i ORDER BY officer_level, turn_idx',
|
||||
$nationId,
|
||||
) as $row) {
|
||||
$nationTurns[] = comparisonPickRow(
|
||||
$row,
|
||||
[
|
||||
'nationId' => 'nation_id',
|
||||
'officerLevel' => 'officer_level',
|
||||
'turnIndex' => 'turn_idx',
|
||||
'action' => 'action',
|
||||
'args' => 'arg',
|
||||
],
|
||||
['arg'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$logs = [];
|
||||
if ($generalIds !== []) {
|
||||
foreach ($db->query(
|
||||
'SELECT id, general_id, log_type, year, month, text FROM general_record WHERE id > %i ORDER BY id',
|
||||
$logAfterId,
|
||||
) as $row) {
|
||||
if ((int)$row['general_id'] !== 0 && !in_array((int)$row['general_id'], $generalIds, true)) {
|
||||
continue;
|
||||
}
|
||||
$logs[] = comparisonPickRow($row, [
|
||||
'id' => 'id',
|
||||
'generalId' => 'general_id',
|
||||
'category' => 'log_type',
|
||||
'year' => 'year',
|
||||
'month' => 'month',
|
||||
'text' => 'text',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$messages = array_map(
|
||||
static fn(array $row): array => comparisonPickRow(
|
||||
$row,
|
||||
[
|
||||
'id' => 'id',
|
||||
'mailbox' => 'mailbox',
|
||||
'type' => 'type',
|
||||
'sourceId' => 'src',
|
||||
'destinationId' => 'dest',
|
||||
'createdAt' => 'time',
|
||||
'payload' => 'message',
|
||||
],
|
||||
['message'],
|
||||
),
|
||||
$db->query(
|
||||
'SELECT id, mailbox, type, src, dest, time, message FROM message WHERE id > %i ORDER BY id',
|
||||
$messageAfterId,
|
||||
),
|
||||
);
|
||||
|
||||
return [
|
||||
'schemaVersion' => 1,
|
||||
'engine' => 'ref',
|
||||
'world' => [
|
||||
'year' => (int)$worldValues['year'],
|
||||
'month' => (int)$worldValues['month'],
|
||||
'tickMinutes' => (int)$worldValues['turnterm'],
|
||||
'turnTime' => (string)$worldValues['turntime'],
|
||||
'isUnited' => (int)$worldValues['isunited'],
|
||||
],
|
||||
'generals' => $generals,
|
||||
'cities' => $cities,
|
||||
'nations' => $nations,
|
||||
'diplomacy' => $diplomacy,
|
||||
'generalTurns' => $generalTurns,
|
||||
'nationTurns' => $nationTurns,
|
||||
'logs' => $logs,
|
||||
'messages' => $messages,
|
||||
'watermarks' => [
|
||||
'logId' => (int)($db->queryFirstField('SELECT COALESCE(MAX(id), 0) FROM general_record') ?? 0),
|
||||
'messageId' => (int)($db->queryFirstField('SELECT COALESCE(MAX(id), 0) FROM message') ?? 0),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$input = stream_get_contents(STDIN);
|
||||
$request = json_decode($input === '' ? '{}' : $input, true, flags: JSON_THROW_ON_ERROR);
|
||||
if (!is_array($request)) {
|
||||
throw new \InvalidArgumentException('request must be an object');
|
||||
}
|
||||
echo json_encode(
|
||||
comparisonTurnStateSnapshot($request),
|
||||
JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION,
|
||||
), PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
fwrite(STDERR, $throwable::class . ': ' . $throwable->getMessage() . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user