$values */ function comparisonMonthlyPatch(string $table, int $id, array $values): void { $definitions = [ 'city' => [ 'idColumn' => 'city', 'allowed' => [ 'name', 'level', 'nation', 'supply', 'front', 'pop', 'pop_max', 'agri', 'agri_max', 'comm', 'comm_max', 'secu', 'secu_max', 'trust', 'trade', 'def', 'def_max', 'wall', 'wall_max', 'officer_set', 'state', 'region', 'term', 'conflict', ], ], 'nation' => [ 'idColumn' => 'nation', 'allowed' => ['name', 'color', 'capital', 'gold', 'rice', 'level', 'type'], ], 'general' => [ 'idColumn' => 'no', 'allowed' => [ 'name', 'nation', 'city', 'officer_level', 'officer_city', 'crew', 'train', 'atmos', ], ], ]; $definition = $definitions[$table] ?? null; if ($definition === null) { throw new \InvalidArgumentException("unsupported setup table: {$table}"); } $patch = []; foreach ($values as $key => $value) { if (!is_string($key) || !in_array($key, $definition['allowed'], true)) { throw new \InvalidArgumentException("unsupported {$table} setup field"); } if (in_array($key, ['conflict'], true) && is_array($value)) { $value = json_encode($value, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); } $patch[$key] = $value; } if ($table === 'nation') { $patch += [ 'nation' => $id, 'name' => "fixture-nation-{$id}", 'color' => '#777777', 'capital' => 0, 'gold' => 0, 'rice' => 0, 'level' => 1, 'type' => 'che_중립', 'aux' => '{}', ]; DB::db()->insertUpdate('nation', $patch, $patch); } elseif ($patch !== []) { DB::db()->update($table, $patch, "`{$definition['idColumn']}` = %i", $id); } } /** @param array $observe */ function comparisonMonthlyDetails(array $observe, int $worldHistoryAfterId): array { $db = DB::db(); $generals = array_map( static fn(array $row): array => comparisonPickRow( $row, [ 'id' => 'no', 'nationId' => 'nation', 'cityId' => 'city', 'officerLevel' => 'officer_level', 'officerCityId' => 'officer_city', 'crew' => 'crew', 'train' => 'train', 'atmos' => 'atmos', ], ), comparisonRowsById('general', 'no', comparisonIntegerList($observe['generalIds'] ?? [], 'generalIds')), ); $cities = array_map( static fn(array $row): array => comparisonPickRow( $row, [ 'id' => 'city', 'nationId' => 'nation', 'supplyState' => 'supply', 'frontState' => 'front', 'officerSet' => 'officer_set', 'term' => 'term', 'conflict' => 'conflict', ], ['conflict'], ), comparisonRowsById('city', 'city', comparisonIntegerList($observe['cityIds'] ?? [], 'cityIds')), ); $worldHistory = array_map( static fn(array $row): array => comparisonPickRow( $row, [ 'id' => 'id', 'nationId' => 'nation_id', 'year' => 'year', 'month' => 'month', 'text' => 'text', ], ), $db->query( 'SELECT id, nation_id, year, month, text FROM world_history WHERE id > %i ORDER BY id', $worldHistoryAfterId, ), ); return [ 'generals' => $generals, 'cities' => $cities, 'worldHistory' => $worldHistory, ]; } function comparisonMonthlyEventTraceMain(): void { 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'); } if (($request['action'] ?? null) !== 'UpdateCitySupply') { throw new \InvalidArgumentException('unsupported monthly action'); } $setup = $request['setup'] ?? []; if (!is_array($setup)) { throw new \InvalidArgumentException('setup must be an object'); } foreach (['city', 'nation', 'general'] as $table) { $rows = $setup[$table] ?? []; if (!is_array($rows)) { throw new \InvalidArgumentException("setup.{$table} must be an array"); } foreach ($rows as $row) { if (!is_array($row) || !is_int($row['id'] ?? null) || !is_array($row['values'] ?? null)) { throw new \InvalidArgumentException("invalid setup.{$table} row"); } comparisonMonthlyPatch($table, $row['id'], $row['values']); } } $db = DB::db(); $snapshotRequest = ['observe' => $request['observe'] ?? []]; $snapshotRequest['observe']['logAfterId'] = (int)($db->queryFirstField('SELECT COALESCE(MAX(id), 0) FROM general_record') ?? 0); $worldHistoryAfterId = (int)($db->queryFirstField('SELECT COALESCE(MAX(id), 0) FROM world_history') ?? 0); $before = comparisonTurnStateSnapshot($snapshotRequest); $beforeDetails = comparisonMonthlyDetails($snapshotRequest['observe'], $worldHistoryAfterId); $environment = $request['environment'] ?? []; if (!is_array($environment)) { throw new \InvalidArgumentException('environment must be an object'); } $year = $environment['year'] ?? null; $month = $environment['month'] ?? null; $startYear = $environment['startyear'] ?? null; if (!is_int($year) || !is_int($month) || !is_int($startYear)) { throw new \InvalidArgumentException('environment year, month, and startyear must be integers'); } $action = new Event\Action\UpdateCitySupply(); $action->run([ 'year' => $year, 'month' => $month, 'startyear' => $startYear, ]); unset($action); gc_collect_cycles(); $after = comparisonTurnStateSnapshot($snapshotRequest); $afterDetails = comparisonMonthlyDetails($snapshotRequest['observe'], $worldHistoryAfterId); echo json_encode( [ 'schemaVersion' => 1, 'engine' => 'ref', 'action' => 'UpdateCitySupply', 'before' => $before, 'beforeDetails' => $beforeDetails, 'after' => $after, 'afterDetails' => $afterDetails, ], 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); } } comparisonMonthlyEventTraceMain();