*/ function comparisonInstantDiplomacyMessageRow(int $messageId): array { $row = DB::db()->queryFirstRow( 'SELECT id, mailbox, type, src, dest, time, valid_until, message FROM message WHERE id = %i', $messageId, ); if ($row === null) { throw new \RuntimeException("message {$messageId} was not persisted"); } return [ 'id' => (int)$row['id'], 'mailbox' => (int)$row['mailbox'], 'type' => (string)$row['type'], 'sourceId' => (int)$row['src'], 'destinationId' => (int)$row['dest'], 'timeTick' => (int)$row['time'], 'validUntilTick' => (int)$row['valid_until'], 'payload' => comparisonJsonValue($row['message']), ]; } /** @return array */ function comparisonRunInstantDiplomacyResponse(array $request): array { $action = $request['action'] ?? null; if (!in_array($action, [ DiplomaticMessage::TYPE_NO_AGGRESSION, DiplomaticMessage::TYPE_CANCEL_NA, DiplomaticMessage::TYPE_STOP_WAR, ], true)) { throw new \InvalidArgumentException('action must be noAggression, cancelNA, or stopWar'); } $actorGeneralId = $request['actorGeneralId'] ?? null; $proposerGeneralId = $request['proposerGeneralId'] ?? null; if (!is_int($actorGeneralId) || $actorGeneralId < 1) { throw new \InvalidArgumentException('actorGeneralId must be a positive integer'); } if (!is_int($proposerGeneralId) || $proposerGeneralId < 1) { throw new \InvalidArgumentException('proposerGeneralId must be a positive integer'); } if (($request['response'] ?? true) !== true) { throw new \InvalidArgumentException('this comparison runner currently covers accepted responses only'); } comparisonApplyTurnFixtureSetup($request['setup'] ?? null); $db = DB::db(); $actor = $db->queryFirstRow( 'SELECT no, name, nation FROM general WHERE no = %i', $actorGeneralId, ); $proposer = $db->queryFirstRow( 'SELECT no, name, nation FROM general WHERE no = %i', $proposerGeneralId, ); if ($actor === null || $proposer === null) { throw new \RuntimeException('fixture generals are missing'); } $actorNation = $db->queryFirstRow( 'SELECT nation, name, color FROM nation WHERE nation = %i', (int)$actor['nation'], ); $proposerNation = $db->queryFirstRow( 'SELECT nation, name, color FROM nation WHERE nation = %i', (int)$proposer['nation'], ); if ($actorNation === null || $proposerNation === null) { throw new \RuntimeException('fixture nations are missing'); } $messageBaseline = (int)($db->queryFirstField('SELECT COALESCE(MAX(id), 0) FROM message') ?? 0); $logBaseline = (int)($db->queryFirstField('SELECT COALESCE(MAX(id), 0) FROM general_record') ?? 0); $src = new MessageTarget( $proposerGeneralId, (string)$proposer['name'], (int)$proposerNation['nation'], (string)$proposerNation['name'], (string)$proposerNation['color'], ); $dest = new MessageTarget( $actorGeneralId, (string)$actor['name'], (int)$actorNation['nation'], (string)$actorNation['name'], (string)$actorNation['color'], ); $option = ['action' => $action]; if ($action === DiplomaticMessage::TYPE_NO_AGGRESSION) { $year = $request['year'] ?? null; $month = $request['month'] ?? null; if (!is_int($year) || !is_int($month)) { throw new \InvalidArgumentException('noAggression requires integer year and month'); } $option['year'] = $year; $option['month'] = $month; } $proposal = new DiplomaticMessage( MessageType::diplomacy, $src, $dest, '외교 제안', Message::gameNow(), new \DateTime('9999-12-31'), $option, ); $proposalMessageId = $proposal->send(); $proposalBefore = comparisonInstantDiplomacyMessageRow($proposalMessageId); $observe = $request['observe'] ?? []; if (!is_array($observe)) { throw new \InvalidArgumentException('observe must be an object'); } $observe['logAfterId'] = $logBaseline; $observe['messageAfterId'] = $messageBaseline; $snapshotRequest = ['observe' => $observe]; $before = comparisonTurnStateSnapshot($snapshotRequest); $session = Session::getInstance(); $_SESSION[UniqueConst::$serverID . Session::GAME_KEY_GENERAL_ID] = $actorGeneralId; $_SESSION[UniqueConst::$serverID . Session::GAME_KEY_GENERAL_NAME] = (string)$actor['name']; $api = new DecideMessageResponse(dirname(__DIR__), [ 'msgID' => $proposalMessageId, 'response' => true, ]); $validationError = $api->validateArgs(); if ($validationError !== null) { throw new \RuntimeException("DecideMessageResponse validation failed: {$validationError}"); } $apiResult = $api->launch($session, null, null); if (!is_array($apiResult)) { throw new \RuntimeException('DecideMessageResponse did not return an array result'); } return [ 'schemaVersion' => 1, 'engine' => 'ref', 'execution' => [ 'kind' => 'instantDiplomacyMessageResponse', 'entryPoint' => DecideMessageResponse::class, 'actorGeneralId' => $actorGeneralId, 'proposerGeneralId' => $proposerGeneralId, 'action' => $action, 'response' => true, 'outcome' => $apiResult, 'proposalMessageId' => $proposalMessageId, 'proposalBefore' => $proposalBefore, 'proposalAfter' => comparisonInstantDiplomacyMessageRow($proposalMessageId), ], 'before' => $before, 'after' => comparisonTurnStateSnapshot($snapshotRequest), 'rng' => [], ]; } 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( comparisonRunInstantDiplomacyResponse($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); }