From 178d6f042b91b419c87efe81fcd4e7687dcb26dc Mon Sep 17 00:00:00 2001 From: hided62 Date: Sat, 25 Jul 2026 21:15:35 +0000 Subject: [PATCH] test: trace monthly unique and inheritance actions --- .../fixtures/monthly_lost_unique_item.json | 42 ++++++++++ .../monthly_merge_inherit_point_rank.json | 74 ++++++++++++++++ hwe/compare/monthly_event_trace.php | 84 ++++++++++++++++--- 3 files changed, 190 insertions(+), 10 deletions(-) create mode 100644 hwe/compare/fixtures/monthly_lost_unique_item.json create mode 100644 hwe/compare/fixtures/monthly_merge_inherit_point_rank.json diff --git a/hwe/compare/fixtures/monthly_lost_unique_item.json b/hwe/compare/fixtures/monthly_lost_unique_item.json new file mode 100644 index 00000000..262a84bb --- /dev/null +++ b/hwe/compare/fixtures/monthly_lost_unique_item.json @@ -0,0 +1,42 @@ +{ + "action": "LostUniqueItem", + "args": [0.2], + "environment": { + "year": 210, + "month": 1, + "startyear": 180 + }, + "setup": { + "syncEnvironment": true, + "resetGenerals": true, + "deleteOtherGenerals": true, + "keepGeneralIds": [1, 2], + "general": [ + { + "id": 1, + "values": { + "name": "갑", + "npc": 0, + "horse": "che_명마_12_옥란백용구", + "weapon": "che_무기_15_의천검", + "book": "che_서적_01_효경전", + "item": "che_저지_삼황내문" + } + }, + { + "id": 2, + "values": { + "name": "을", + "npc": 1, + "horse": "che_명마_12_사륜거", + "weapon": "che_무기_01_단도", + "book": "None", + "item": "che_부적_태현청생부" + } + } + ] + }, + "observe": { + "generalIds": [1, 2] + } +} diff --git a/hwe/compare/fixtures/monthly_merge_inherit_point_rank.json b/hwe/compare/fixtures/monthly_merge_inherit_point_rank.json new file mode 100644 index 00000000..394ac083 --- /dev/null +++ b/hwe/compare/fixtures/monthly_merge_inherit_point_rank.json @@ -0,0 +1,74 @@ +{ + "action": "MergeInheritPointRank", + "environment": { + "year": 210, + "month": 1, + "startyear": 180 + }, + "setup": { + "resetGenerals": true, + "deleteOtherGenerals": true, + "keepGeneralIds": [1], + "clearInheritanceOwnerIds": [100], + "gameEnvironment": { + "isunited": 0 + }, + "general": [ + { + "id": 1, + "values": { + "name": "갑", + "nation": 1, + "owner": 100, + "npc": 0, + "belong": 3, + "dex1": 1000, + "dex2": 0, + "dex3": 0, + "dex4": 0, + "dex5": 0 + } + } + ], + "inheritancePoints": [ + {"ownerId": 100, "key": "previous", "value": 999}, + {"ownerId": 100, "key": "lived_month", "value": 2}, + {"ownerId": 100, "key": "max_domestic_critical", "value": 4}, + {"ownerId": 100, "key": "active_action", "value": 15}, + {"ownerId": 100, "key": "unifier", "value": 6}, + {"ownerId": 100, "key": "tournament", "value": 7} + ], + "rankData": [ + {"generalId": 1, "type": "warnum", "value": 2}, + {"generalId": 1, "type": "firenum", "value": 1}, + {"generalId": 1, "type": "betwin", "value": 2}, + {"generalId": 1, "type": "betgold", "value": 4000}, + {"generalId": 1, "type": "betwingold", "value": 2000}, + {"generalId": 1, "type": "inherit_earned_dyn", "value": 999}, + {"generalId": 1, "type": "inherit_earned_act", "value": 11}, + {"generalId": 1, "type": "inherit_earned", "value": 999}, + {"generalId": 1, "type": "inherit_spent_dyn", "value": 9}, + {"generalId": 1, "type": "inherit_spent", "value": 999} + ] + }, + "observe": { + "generalIds": [1], + "ownerIds": [100], + "inheritanceKeys": [ + "previous", + "lived_month", + "max_domestic_critical", + "active_action", + "unifier", + "tournament" + ], + "rankGeneralIds": [1], + "rankTypes": [ + "inherit_earned", + "inherit_earned_act", + "inherit_earned_dyn", + "inherit_spent", + "inherit_spent_dyn" + ] + } +} diff --git a/hwe/compare/monthly_event_trace.php b/hwe/compare/monthly_event_trace.php index c666e0ee..46346b5f 100644 --- a/hwe/compare/monthly_event_trace.php +++ b/hwe/compare/monthly_event_trace.php @@ -108,6 +108,10 @@ function comparisonMonthlyDetails(array $observe, int $worldHistoryAfterId): arr 'specialWar' => 'special2', 'specAge2' => 'specage2', 'betray' => 'betray', + 'horse' => 'horse', + 'weapon' => 'weapon', + 'book' => 'book', + 'item' => 'item', 'dex1' => 'dex1', 'dex2' => 'dex2', 'dex3' => 'dex3', @@ -180,24 +184,42 @@ function comparisonMonthlyDetails(array $observe, int $worldHistoryAfterId): arr $worldHistoryAfterId, ), ); + $inheritanceKeys = $observe['inheritanceKeys'] ?? ['unifier']; + if (!is_array($inheritanceKeys) || array_filter($inheritanceKeys, static fn($key): bool => !is_string($key))) { + throw new \InvalidArgumentException('inheritanceKeys must be an array of strings'); + } $inheritancePoints = []; foreach (comparisonIntegerList($observe['ownerIds'] ?? [], 'ownerIds') as $ownerId) { - $value = $db->queryFirstField( - 'SELECT value FROM storage WHERE namespace = %s AND `key` = %s', - "inheritance_{$ownerId}", - 'unifier', - ); - $inheritancePoints[] = [ - 'ownerId' => $ownerId, - 'unifier' => $value === null ? 0 : (comparisonJsonValue($value)[0] ?? 0), - ]; + $entry = ['ownerId' => $ownerId]; + foreach ($inheritanceKeys as $key) { + $value = $db->queryFirstField( + 'SELECT value FROM storage WHERE namespace = %s AND `key` = %s', + "inheritance_{$ownerId}", + $key, + ); + $entry[$key] = $value === null ? 0 : (comparisonJsonValue($value)[0] ?? 0); + } + $inheritancePoints[] = $entry; } + $rankGeneralIds = comparisonIntegerList($observe['rankGeneralIds'] ?? [], 'rankGeneralIds'); + $rankTypes = $observe['rankTypes'] ?? []; + if (!is_array($rankTypes) || array_filter($rankTypes, static fn($type): bool => !is_string($type))) { + throw new \InvalidArgumentException('rankTypes must be an array of strings'); + } + $rankData = (!$rankGeneralIds || !$rankTypes) + ? [] + : iterator_to_array($db->query( + 'SELECT general_id AS generalId, nation_id AS nationId, `type`, `value` FROM rank_data WHERE general_id IN %li AND `type` IN %ls ORDER BY general_id, `type`', + $rankGeneralIds, + $rankTypes, + )); return [ 'generals' => $generals, 'cities' => $cities, 'nations' => $nations, 'worldHistory' => $worldHistory, 'inheritancePoints' => $inheritancePoints, + 'rankData' => $rankData, ]; } @@ -231,6 +253,8 @@ function comparisonMonthlyEventTraceMain(): void 'UnblockScoutAction', 'AssignGeneralSpeciality', 'AddGlobalBetray', + 'LostUniqueItem', + 'MergeInheritPointRank', ]; if (!in_array($actionName, $supportedActions, true)) { throw new \InvalidArgumentException('unsupported monthly action'); @@ -346,6 +370,43 @@ function comparisonMonthlyEventTraceMain(): void } KVStorage::getStorage(DB::db(), "inheritance_{$ownerId}")->setValue('previous', [$value, null]); } + foreach ($setup['inheritancePoints'] ?? [] as $row) { + if ( + !is_array($row) + || !is_int($row['ownerId'] ?? null) + || !is_string($row['key'] ?? null) + || !is_numeric($row['value'] ?? null) + ) { + throw new \InvalidArgumentException('invalid setup.inheritancePoints row'); + } + KVStorage::getStorage(DB::db(), "inheritance_{$row['ownerId']}")->setValue( + $row['key'], + [$row['value'], null], + ); + } + foreach ($setup['rankData'] ?? [] as $row) { + if ( + !is_array($row) + || !is_int($row['generalId'] ?? null) + || !is_string($row['type'] ?? null) + || !is_int($row['value'] ?? null) + ) { + throw new \InvalidArgumentException('invalid setup.rankData row'); + } + $nationId = (int)(DB::db()->queryFirstField( + 'SELECT nation FROM general WHERE no = %i', + $row['generalId'], + ) ?? 0); + DB::db()->insertUpdate('rank_data', [ + 'general_id' => $row['generalId'], + 'nation_id' => $nationId, + 'type' => $row['type'], + 'value' => $row['value'], + ], [ + 'nation_id' => $nationId, + 'value' => $row['value'], + ]); + } if (($setup['resetDiplomacy'] ?? false) === true) { DB::db()->query('DELETE FROM diplomacy'); } @@ -442,7 +503,7 @@ function comparisonMonthlyEventTraceMain(): void throw new \InvalidArgumentException('ProcessSemiAnnual requires resource'); } $action = new $actionClass($resource); - } elseif (in_array($actionName, ['CreateManyNPC', 'RegNPC', 'RegNeutralNPC', 'RaiseInvader', 'AutoDeleteInvader', 'ChangeCity', 'OpenNationBetting', 'FinishNationBetting', 'BlockScoutAction', 'UnblockScoutAction', 'AddGlobalBetray'], true)) { + } elseif (in_array($actionName, ['CreateManyNPC', 'RegNPC', 'RegNeutralNPC', 'RaiseInvader', 'AutoDeleteInvader', 'ChangeCity', 'OpenNationBetting', 'FinishNationBetting', 'BlockScoutAction', 'UnblockScoutAction', 'AddGlobalBetray', 'LostUniqueItem'], true)) { $args = $request['args'] ?? []; if (!is_array($args)) { throw new \InvalidArgumentException("{$actionName} args must be an array"); @@ -468,6 +529,9 @@ function comparisonMonthlyEventTraceMain(): void if ($actionName === 'AddGlobalBetray' && count($args) > 2) { throw new \InvalidArgumentException('AddGlobalBetray args must have at most two entries'); } + if ($actionName === 'LostUniqueItem' && count($args) > 1) { + throw new \InvalidArgumentException('LostUniqueItem args must have at most one entry'); + } $action = new $actionClass(...$args); } else { $action = new $actionClass();