pre cw with probe and a bunch script to read game behavior
This commit is contained in:
1 parent
5120219ae2
commit
da8a32f60d
310 files changed
+219693
-872
No files matched your search
@@ -0,0 +1,316 @@
|
||||
# Currency Wars implementation status
|
||||
|
||||
## Objective and completion requirements
|
||||
|
||||
CW must support the complete native gameplay loop, not just entering a battle.
|
||||
Completion remains unproven until the current Client demonstrates all of:
|
||||
|
||||
- Entry through Cosmic Strife with a valid season, activity state and reward track.
|
||||
- Tutorial, normal and Overcharge selection supported by the current Client tables.
|
||||
- New runs, reconnect/resume, abandon and restart without resetting unrelated saves.
|
||||
- Gold, interest, player EXP/levels, HP, shops, buying/selling and role upgrades.
|
||||
- Board/front/back/bench placement, equipment, crafting, tracking and trait effects.
|
||||
- Native routes, enemies, battle parameters and results for the selected node.
|
||||
- Supplies, selections, augments, portals, bonuses, bonfires and other node actions.
|
||||
- Chapter progression through the final boss, defeat and completion settlement.
|
||||
- Persistent records, reward claims, weekly progression and talents.
|
||||
- Client confirmation of the above and preservation of the established ordinary Battle paths.
|
||||
|
||||
## Entry update (2026-10-20)
|
||||
|
||||
- Current StartGamePlay request: division tag 2, season `LJNGPCKPJLF` tag 4, Overclock tag 9. Tag 3 remains semantically unresolved; it was observed as 1 on the tutorial path. Do not interpret it as season or remap division 0 to a regular division.
|
||||
- Start now saves a complete initial run before replying, then GetData restores it. Resume validates run identity; QuitLeave preserves it. Pending portal actions, shop commands and battles remain incomplete.
|
||||
- `resources/CurrencyWarsInitialRuns.json` contains captured initial runs for division 20101 in both modes, bound to the current Client assembly. Other divisions are rejected. Captured choices/modifiers are fixed samples; procedural generation remains unfinished.
|
||||
- Reference source: https://github.com/Mar7thLover/March7thHoney-OpenSource/tree/fe00bdbda3c28ca33d01f171566aa35b5ab95083 . Native reference requests were replayed against an isolated loopback copy and disposable account.
|
||||
- Error 6908 occurred with division 0 and tag 3 = 1. Named current-Client predicates confirmed `ShouldStartCurrentSeasonTutorial=true`, `IsFinishedTutorialThisSeason=false`, and `LastTutorialFinishedSeasonID=0`. Fresh bootstrap omitted tutorial state and selected the tutorial path.
|
||||
- The entry resource declares `freeplay_unlocked` explicitly using reference GetData tutorial metadata (seasonal completion tag 15, permanent latest tutorial season tag 11, and permanent per-season history map tag 6). Sending only the two scalar values did not resolve 6908: the Client continued requesting season 1 with division 0. The history map has now also been restored from the reference capture. This bypasses scripted tutorials for freeplay entry; it does not implement their missions or grant rewards. Client confirmation of corrected entry is pending.
|
||||
- Only builds were run for this change; a successful build does not prove Client entry or battle completion.
|
||||
|
||||
### Initial pending choices and recovery
|
||||
|
||||
The user confirmed entry to Investment Environment after the tutorial-history change. The first Confirm sent request 8485 and previously had no handler. Controlled native replay establishes the current table's `GridFightHandleBonfireHealHpCsReq` as the pending-action request, with `MFFCPLBNHCF.JCAPCNLJGFC` carrying the portal and a `GridFightHandlePendingActionScRsp` response (8466). The reference sends ordered `GridFightSyncUpdateResultScNotify` (8683) before that response. All handlers resolve names through the current table; no numeric aliases were added.
|
||||
|
||||
`CurrencyWarsEntryTransitions.json` now contains six initial outcomes for the already captured Standard/Overclock offers. The handler checks offer membership, pending queue, revision, resource identity/digests and next-phase structure before saving and syncing the result. This extends the fixed entry prototype only: starter outcomes are captured, not procedurally generated; later pending actions, shops, battle and settlement remain incomplete. Client confirmation of Confirm is pending.
|
||||
|
||||
`tools/export_cw_reference_tables.py` reads the native reference MemoryPack cache without loading its unrelated mismatched schemas. It extracted 493 node templates, 116 portals, 966 basic bonuses and 372 combination bonuses. All current captured route template IDs are covered. These reference tables remain diagnostic data. Current Client portal capture yielded 73 handbook rows and found 26 field/coverage differences, including decimal parameters on portal 121 that the reference represents as zero. Do not replace current effect formulas with these older integer parameters.
|
||||
|
||||
Unstuck is a separate explicit UI recovery operation. A queued nonce reaches the connected game through the existing heartbeat Lua transport and calls the observed `UIManager.ClearWaitNetPacketFullScreenBlock()`. Client acknowledgement is required before the web UI reports completion. It preserves saved runs and does not fabricate gameplay success or reset phase/loading flags. UI recovery still requires Client confirmation.
|
||||
|
||||
## Authoritative inputs captured on 2026-10-18
|
||||
|
||||
- Command table: PearlSR revision `005d2c12db4781071f4b5b68e8f364587886a710`, recorded in `protocol/proto/upstream_cmdids.go`.
|
||||
- Current GameAssembly SHA-256: `c5e02e6e984f7e5577c78b995a8efeee2d06ee22e67d484ad5e859c8bf21e144`.
|
||||
- `tools/inventory_cw.py` found 78 `GridFight*` commands and 491 reachable local protobuf declarations. This is a dependency inventory, not Client compatibility verification. Pending actions also use commands with obfuscated/non-GridFight names.
|
||||
- Initially no CW handler was registered. `dangerFamilies` suppressed empty replies because the native module dereferences missing state. Keep that guard for unsupported requests.
|
||||
- The current PearlSR gameserver checkout contains no CW gameplay implementation to port.
|
||||
- Native common/client constant tables provided 197 CW constants. Active season is 2, subseason 1. First season is 1; it is not the active-season default.
|
||||
- Captured division catalogs: 97 rows for season 1, 71 for season 2, each with a completed native enumeration marker. `resources/CurrencyWarsCatalog.json` records capture and assembly hashes.
|
||||
- Active `GridFightSeasonModuleConfigRow`: OfferingID 16, ActivityQuestConfigID 71101, ActivityModuleID 7110101, MaxRewardExp 48000. ActivityConfig maps that module to parent activity 71101. Runtime schedule dates are operator policy, not claimed official dates.
|
||||
|
||||
## Client entry evidence
|
||||
|
||||
The user's Cosmic Strife screenshot is the intended CW entry. Player.log contained:
|
||||
|
||||
- `GridFightModule.get_ExpLineRewardData` NullReferenceException through `HandbookRogueGridFightTabItem._setup_level`.
|
||||
- `HandbookRogueGotoBtnBehaviour.on_btn_go`: missing `StateData`.
|
||||
|
||||
CW failed before the gameplay loop. These errors do not establish that ordinary battle conversion is the cause.
|
||||
|
||||
## Protocol mapping evidence
|
||||
|
||||
Probes inspect named CW types, never all assembly types/methods. They remain outside `scripts/` when not being captured.
|
||||
|
||||
`tools/cw_scalar_wire_probe.lua` creates isolated protobuf objects, assigns one UInt32 backing field through boxed reflection and serializes it. No object is synced into a game module or sent as a response. This maps backing fields to wire tags and property getter tokens.
|
||||
|
||||
The native token-check correlation was anchored by the stack RVA for `get_ExpLineRewardData`, adjacent `GetSeason`/`_SyncSeason` implementations and named runtime MetadataToken values. `tools/cw_native_token_sites.py` reports candidate check sites only. Proto method tokens can also match unrelated metadata-initialization constants; a candidate does not prove a function address.
|
||||
|
||||
Native `_SyncSeason` at RVA `0xdb6efa0` reads offsets `0x40`, `0x30`, `0x34` into module SeasonID, SubSeasonID and keep-win count. Matching protobuf field order/encoding gives:
|
||||
|
||||
| Meaning | AMEEHIIPMFP Go field | Wire tag |
|
||||
| --- | --- | --- |
|
||||
| SeasonID | ADFEKPNIFOJ | 4 |
|
||||
| SubSeasonID | GEGMHOMNBDG | 11 |
|
||||
| DivisionID | DivisionId | 2 |
|
||||
| Keep-win count | GDAKCOICIDA | 15 |
|
||||
|
||||
The bootstrap populates the nested metadata, reward EXP and handbook containers dereferenced by `_OnGridFightGetDataScRsp`. The optional active gameplay session remains absent because no CW run has started.
|
||||
|
||||
Limitations: `Marshal.OffsetOf` is stripped, native field offset reflection returns -1, and the existing `.pdata` helper produced unusable boundaries. These are not successful layout verification. The season mapping still requires checking the bootstrap response in the Client. No method pointer is dereferenced or memory layout patched by retained probes.
|
||||
|
||||
## Current boundary and next work
|
||||
|
||||
- Version-checked catalog loader and source-based activity mapping; missing gameplay rules return errors, without guessed defaults.
|
||||
- `GridFightGetData` bootstrap for a fresh profile; CW progression/resume are not yet implemented.
|
||||
- Active CW activity in the schedule response. Timestamps moved from the handler to shared runtime config, retaining the previous interval as explicit configuration.
|
||||
- Other CW commands remain guarded; no successful empty gameplay replies added.
|
||||
- Initial code compiled. Client bootstrap verification and full gameplay remain pending.
|
||||
|
||||
Next: verify bootstrap/activity state, capture initialized CW role/route/level tables, implement persistent run state and the complete node/action/battle loop, then verify gameplay and settlement in the Client.
|
||||
|
||||
## First bootstrap result
|
||||
|
||||
The current Client confirmed Season=2, Subseason=1, Division=20101, no active game, and a non-null ExpLineRewardData object. This verifies the initial season field mapping; it does not verify the full guide or gameplay.
|
||||
|
||||
The user then reported an empty Cosmic Strife tab. The initial schedule entry used a parent ActivityID where the protocol expects an ActivityModuleID. PearlSR `services/events.zig` explicitly assigns module IDs to `ActivityScheduleData.activity_id` and parent ActivityID to `panel_id`. The active-season module alone also omits the whole-season CW module from `GridFight_WholeSeason_ActivityModuleID` in the native constants. The corrected CW schedule therefore derives both whole-season and current-season module/parent pairs from the captured constants, season metadata and ActivityConfig; it does not generate IDs arithmetically or expose unsupported unrelated activities. Ordinary Endgame schedule identities remain as before.
|
||||
|
||||
The timestamp config checks covered normal JSON, local override, environment override, an invalid interval and a missing interval in both Go and launcher. They do not prove Client entry, combat or settlement.
|
||||
|
||||
## Corrected schedule result and lobby investigation
|
||||
|
||||
After the schedule correction, the user confirmed that Go returns to a world scene, but does not open the CW event UI. The server received EnterScene for the native `GridFight_EntranceID`, then an InteractProp request. There was no subsequent CW gameplay-start request. The interacted entity resolves to a SandBoardGate, not evidence of a CW entrance interaction. The native module still has `_NeedShowEntrancePage=false` and LastTutorialFinishedSeasonID=0; neither value was changed by a probe.
|
||||
|
||||
The ordinary `loadScene` no-teleport path currently selects the first checkpoint instead of the MapEntrance start anchor. This can place a mission entrance in the outer hall. The matching MapEntrance row specifies a start group absent from the old res scene group list. Both placement and current RuntimeGroup data are being checked before updating the CW entrance. This is not yet a verified UI-entry fix.
|
||||
|
||||
The catalog now also retains 71 active-season stage wrapper rows and 60 default handbook role rows, with a completed capture and its SHA-256. Stage wrapper values were observed on a fresh profile without talents. Handbook defaults do not establish the full hidden/variant shop pool. `tools/export_cw_catalog.py --gameplay-log` rejects incomplete final batches and checks stage identities against the active division catalog and role season/identity/star values.
|
||||
|
||||
The native start anchor was captured and matches the existing Anchor table. The CW no-checkpoint path now uses that captured anchor; the user confirmed arrival inside the CW room. This fixes placement only. They still report that UI closes, and the console/hologram is absent and cannot be used.
|
||||
|
||||
Targeted RuntimeGroup captures identify the CW Console and Emblem in an activity-tagged Client-managed group. Both props have `IsClientOnly=false`, `LoadOnInitial=true`, state 1, and exact captured world transforms. The loaded MapPropDef enumeration contained neither prop (0 matches). The group's saved-value condition is `FSV_370GridFight >= 1`; native `RPG.GameCore.CompareType` enum capture confirms operation 2 means GreaterEqual.
|
||||
|
||||
The first prop-binding attempt produced no `CW_ROOM_BIND` log and the user still saw an empty table. Further inspection corrected an earlier mistaken inference: the supplementary table has MaxValue=2, but inline `res.json` has `FSV_370GridFight=0`, which wins during merging. The room condition was never satisfied; absence of the props did not prove that Client-managed entities needed server binding. The supplied video confirms the guide transition returns to a room with an empty table.
|
||||
|
||||
The replacement implementation removes forced prop binding. On the native CW entrance only, it clones saved values and applies a resource-defined freeplay profile for the condition's referenced name, using the current native RuntimeDimension MaxValue (the existing PearlSR freeplay policy). Native MinValue/MaxValue/AllowedValues and activation threshold are validated, and unrelated keys are rejected. It does not mutate shared scene data, other entrances, or Client graphs. Room visibility, UI entry and gameplay remain pending Client verification.
|
||||
|
||||
The user subsequently confirmed the hologram/props appeared. The actual packet path logged `CW_ROOM_ACTIVATION ... previous=0 selected=2`. Both the table interaction and Guide Go still opened the entrance briefly and closed it.
|
||||
|
||||
Temporary observers on seven named CW Lua lifecycle methods, automatically restored after two minutes, identified `MonoRemainTimer._UpdateTime -> UITransitionManager.SwitchBack` as the close path. A second bounded observation captured two CW timers: `LeftPanel/TitlePanel/RemainTime` targeted 2031, while `LeftPanel/PtRewardPanel/RemainTime` targeted 1970-01-01. This distinguishes the missing points-reward period from the correctly populated season schedule. No timer value or callback was suppressed.
|
||||
|
||||
The server receives `CycleScoreRewardGetDataCsReq` on login but previously left it unanswered under the CycleScore safety guard. The next typed handler supplies the non-null period object and its `end_time` using the explicit shared activity window; other CycleScore commands remain guarded. This is a fresh-profile/freeplay bootstrap, not implemented weekly rollover or reward-claim persistence, and needs Client confirmation. The separate CW EXP counters are not dates and remain zero.
|
||||
|
||||
Client verification after this build: the user confirmed the CW UI stays open. The server logged `CYCLE_SCORE_REWARD_DATA end_time=1924992000 score=0 bootstrap=true policy=activity_window` and sent the typed `CycleScoreRewardGetDataScRsp`. Room placement, hologram visibility and stable entrance UI are now user-confirmed; starting a run, shops, battles, settlement and persistence remain unfinished. All temporary lifecycle observers were restored and their active scripts removed.
|
||||
|
||||
## Native route catalog and battle configuration boundary
|
||||
|
||||
The complete `GridFightStageRouteConfigExcelTable.dataDict` capture contains 493 rows. Its owner is `RPG.GameCore.Config`, reached from the known `GridFightRoleConfigRow` assembly, rather than the Client module assembly. The producer now requires `--route-log`, rejects incomplete batches and duplicate `(ID, ChapterID, SectionID)` coordinates, and preserves the capture hash. The Go catalog indexes sorted copies of each route and checks that the native `GridFight_FirstPlay_StageRouteID` is present. NodeTemplateID is retained as a configuration identity, never substituted for battle StageID.
|
||||
|
||||
Targeted loader/type metadata identifies `GridFightStageTemplateConfig.GridFightWaveList` as `(WaveIndex, StageId)` entries, and a separate BattleTemplateList carrying monster waves, formation, level/hard level, elite group, monster stars and affixes. These schemas are captured in `cw-template-schema.log`; no graph or battle-template loader was invoked. The named NodeDisplay Excel table was absent in the inspected assemblies. Tutorial wrappers also do not expose the raw row through the field/getter paths used by the first bounded probe; those failed attempts are not captured gameplay data.
|
||||
|
||||
The route reader compiled into `.runtime/cw-route-catalog-gameserver.exe`. This binary has not replaced the running server, and compilation does not verify CW run entry. Starting a run still requires a complete native node, player and shop state mapping. The original full CW scope remains open.
|
||||
|
||||
The tutorial wrapper is flattened rather than retaining a raw Row. After inspecting that one named type, a bounded scalar exporter captured both initialized `TutorialStageConfigs` entries completely. The producer now also requires `--tutorial-log`; it preserves trial-avatar, portal/reveal/supply flags, sale restrictions, tutorial division/reward identities and native shop restriction objects. Obfuscated shop coordinate keys remain opaque; their numeric order has not been guessed. The Go loader validates identities, provenance and restriction shapes. These configuration captures do not create a run or bypass tutorial progression. All CW probes installed during this capture were removed from `scripts/` after completion; the unrelated scene metadata probe was left untouched.
|
||||
|
||||
## Run player scalar mapping
|
||||
|
||||
The next bounded capture encodes isolated `NIHNGPKHEPC`, `BEEFLEJAOCN`, `BCCIJHPLJOJ` and `OHMDHKGAEMM` UInt32 backing fields individually, without syncing objects or sending packets. `cw-gameplay-scalar-wire.log` completed all four types. Getter-token matches against metadata initialization calls are **not** treated as getter function addresses.
|
||||
|
||||
Native `GridFightPlayer.SetData` starts at RVA `0xdb9d670`. It reads player protobuf offsets and updates named setters identified by their MethodDef checks. `cw-player-proto-fields.log` supplies the declaration order: nine reference fields precede thirteen UInt32 fields. The offset-to-wire association below uses the sequential IL2CPP field-layout inference; it is corroborated by the already named HP/EXP/gold wire fields, but is not yet a Client gameplay verification. Native getter offsets were not independently obtained.
|
||||
|
||||
| Native offset | Wire tag / Go field | Player meaning |
|
||||
| --- | --- | --- |
|
||||
| 0x58 | 1 / PPKKHHNEOHB | MaxHP |
|
||||
| 0x5c | 10 / GridFightLineupHp | HP |
|
||||
| 0x60 | 3 / NDAINFLOLPJ | PlayerLevelLimit |
|
||||
| 0x64 | 11 / AFFCFLFIFPH | RoleMaxNum |
|
||||
| 0x68 | 2 / KNFOCCPOJEA | MaxInterestNum |
|
||||
| 0x6c | 9 / GridFightCurLevelExp | Exp |
|
||||
| 0x70 | 4 / JLOCMDIDPCI | MaxBackRoleNum |
|
||||
| 0x74 | 15 / CPKLHPJMMDO | BuyExpHpCost |
|
||||
| 0x78 | 7 / EHNJDIDABHD | Level |
|
||||
| 0x7c | 12 / BEPMODICONA | BuyExpCost |
|
||||
| 0x80 | 5 / GDAKCOICIDA | KeepWinCnt |
|
||||
| 0x84 | 8 / Gold | Gold, passed to UpdateGold |
|
||||
| 0x88 | 6 / MICEOJBAKGN | MaxBenchRoleNum |
|
||||
|
||||
`currency_wars_player.go` implements this isolated scalar conversion and basic health/level validation, preserving destination restriction/tracker containers. It supplies no invented initial stats and is deliberately not wired to StartGamePlay before the full context and initial configuration are available. Native SetData also dereferences the active session, so it was not invoked on an isolated player merely to test these mappings. Ordinary Battle converters are untouched.
|
||||
|
||||
Native `GridFightStage.Sync` (0xdc05680) copies the BEEFLEJAOCN scalars at 0x60/0x64/0x68. `_SyncRouteInfo` (0xdc05740) passes 0x68 and its route-node list to `SyncRouteNodes` (0xdc07090), which stores the scalar in `GridFightStageRoute._StageRouteID`. Thus the first parameter of SyncRouteNodes is route ID, not chapter ID. Together with scalar encoding this maps tag 7 / EMOHLJHPOPC to route ID, tag 5 / DCAHJELNJNP to chapter and tag 8 / SectionId to section. `currency_wars_route.go` builds that component from captured route coordinates and caller-provided display configurations, rejects missing/extra nodes and an invalid current coordinate, clones display objects, and commits only after validation. It does not invent display values from NodeTemplateID or replace battle-node state.
|
||||
|
||||
A metadata-only search on the known GameCoreConfigLoader found `LoadJsonConfig(string)`. Reading only the native FirstPlay path with this plain JSON reader returned nil; no tutorial JSON was captured and no graph loader was invoked. Row metadata attributes also supplied no table accessor. These attempts do not establish that the native binary configuration is missing. The completed captures are retained outside scripts and all probes from this turn are removed after completion.
|
||||
|
||||
## Standard Gambit and Overclock start request
|
||||
|
||||
Both modes use the same `GridFightStartGamePlayCsReq` command (8579); the current protocol schema distinguishes Overclock with `is_overlock` (field 9) and carries `division_id` (field 2). A command-ID-only packet trace cannot reveal that flag, so the user's identification of the 23:05 request as Overclock is retained as the context for that capture.
|
||||
|
||||
That trace has no `GridFightStartGamePlayScRsp` (8511). It only shows `GridFightGetDataCsReq` (8541) answered by `GridFightGetDataScRsp` (8503). A source-level diagnostic now logs the start mode, but deliberately sends no start response: the active-run object and its initialized state are not yet verified. This explains why this trace does not demonstrate a successful Overclock entry; it also must not be used as evidence about Standard Gambit. Do not return an empty start success or fabricate a run object to hide the missing state.
|
||||
|
||||
## CW snapshot persistence foundation
|
||||
|
||||
`currency_wars_store.go` adds a separate `currency_wars` object inside the existing persistent file, keyed by UID. Each envelope records its own save format, configured Client version, captured assembly SHA-256, a monotonic revision and serialized GridFightGetDataScRsp bytes. Reads do not create files, rewrite defaults or migrate data. Writes use the existing process mutex, shared OS file lock and atomic file replacement; unrelated JSON values are retained as RawMessage to avoid float conversion. A stale expected revision is rejected rather than overwriting a later run. Account/Client/schema mismatches preserve the existing save and return an error.
|
||||
|
||||
GridFightGetData now reads a matching snapshot when one exists, decodes it, checks the known permanent/season/handbook prerequisites and restores its response. With no saved snapshot, it retains the existing fresh-profile bootstrap and does not write during GetData. A session helper prepares future commits after validated gameplay transitions. No gameplay handler writes a run yet, and full active-run/action validation must accompany the upcoming state machine; envelope persistence alone does not implement run resume or playable CW. The save build compiles into `.runtime/cw-save-gameserver.exe`, has not replaced the live server, and was not tested with a completed CW run.
|
||||
|
||||
The known `_SessionPreloadProvider` field declares an obfuscated interface describing stage preparation. Its live value is nil before CW starts, so no concrete provider metadata or template paths were captured. Native `GridFightPlayer.set_Level` is a local scalar setter at 0xdba2110; the 0xdba2280 call in SetData is the Gold update path, not the level setter. No player/session instances were created or synced during this research.
|
||||
|
||||
## Captured roles and economy
|
||||
|
||||
The Go catalog now reads and indexes the 60 captured default roles and 71 fresh-profile stage wrappers, verifies source hash and active-season identities, rejects duplicate/missing division wrappers, and returns copies of mutable role/stage lists. This retains the original capture scope: handbook defaults are not the complete shop pool, and fresh-profile wrapper rewards are not the final totals after run modifiers.
|
||||
|
||||
Native sale arrays exist for star ranks 1–3. Their rarity-indexed default-star values match all 60 captured `DefaultSellPrice` getters. The catalog checks this relation at load time. `BaseSalePrice` reads those arrays and rejects unsupported stars/rarities rather than fabricating a price. It is a base quote, not implemented sale processing, special-role handling or stolen-card refunds.
|
||||
|
||||
Native `GridFightPlayer.GetWillGainInterest` (0xdba1aa0) divides the provided gold by a constant-table value and caps it with the player's resolved MaxInterestNum (0x50). The missing-native-rule fallback is not copied. `BaseInterest` uses captured `GridFight_DepositPerInterest` and an explicit run cap; zero caps remain zero. These helpers do not apply node rewards or mutate a run. The native string argument of the constant lookup was not independently decoded, so identifying the captured rule by its semantic name remains an inference pending Client confirmation.
|
||||
|
||||
The upstream repository was rechecked at `https://git.xeondev.com/HonkaiSlopRail/pearl-sr`; its master page still identifies revision 005d2c12db from 2026-10-16, matching the recorded reference. No newer master implementation was established. During the user's AA round, no CW probe is installed and the live server is not replaced.
|
||||
|
||||
`tools/cw_handbook_variants_export.lua` is prepared outside scripts for the next idle capture. It enumerates initialized DefaultRole and OtherRoles wrappers in bounded batches, recording role/star sale getters and native role rows without creating roles or changing stars. This will distinguish actual variants/higher-star wrappers from an assumed full pool. It has not run. No result from this planned capture is claimed in the current catalog.
|
||||
|
||||
## Completed handbook variant capture (2026-10-20)
|
||||
|
||||
The bounded capture now completed all 64 initialized wrappers: 60 default entries plus four OtherRoles entries. These are captured at star 1, not all stars or the complete hidden shop pool. The catalog producer retains native role/star/optional servant-star rows and the observed price/sale getters, with the completed capture SHA-256. The Go reader validates season, role/star identity, duplicate keys, and agreement with the earlier 60 default-role capture. Returned rows and slices are copied. Raw native star JSON preserves FixPoint strings and 64-bit text hashes without float conversion.
|
||||
|
||||
The OtherRoles entries are 151112, 151122, 151132 and 141402. In particular role 151132 has rarity 5 but native buy/sale getters of 7; it must not be priced by blindly indexing the ordinary rarity array. `CapturedSalePrice` returns the actually captured star quote and rejects stars that were not captured. Run effects, stolen refunds and transactions are still separate unfinished work. The catalog and reader compile into `.runtime/cw-variants-gameserver.exe`; this staged build has not replaced the live server.
|
||||
|
||||
Exact-name table lookup completed all 143 loaded assemblies without finding either `RPG.GameCore.GridFightPlayerLevelConfigExcelTable` or `RPG.GameCore.GridFightRoleConfigExcelTable`. This proves absence of those names, not absence of the native tables. A separate named-type metadata capture confirms `GridFightPlayer._ConfigRow` is a `GridFightPlayerLevelConfigRow`, and that the guessed PlayerLevelConfig/StageNodeTemplateConfig wrapper names are absent in the two relevant assemblies. All completed Lua probes from these captures were removed from `scripts/`.
|
||||
|
||||
The user's 00:49:57 Standard Gambit and 00:50:18 Overclock traces both show StartGamePlay (8579), then GetData (8541/8503), without a StartGamePlayScRsp. This corroborates the unfinished start path; neither run mode is playable yet. Role/price capture and a successful build are not evidence of run entry.
|
||||
|
||||
## Native player-level scalar capture
|
||||
|
||||
The AMD64 PE exception table identifies the handbook getter entry at RVA 0xdb5f860 and the player-level table getter at 0x1e3a7880. `disassemble_pe_range.py --containing-function` records exact exception-table bounds and assembly digest. Native `GetLevelUpNeedExp` at 0xdba1b90 calls the latter getter and reads row offset 0x20 when no run modifier is present; named Row field metadata corroborates the other scalar offsets.
|
||||
|
||||
A temporary Frida hook ran two table queries per read-only handbook call on managed Client thread 37460. The default level limit came from the captured constant rather than a probe default. An initial read-only check found that the table was not loaded. After inspecting its native Excel loader, the bounded capture used the normal getter's lazy initialization of that one Excel table; it did not invoke a level graph. Ten identity-checked rows completed, retaining EXP costs, front capacity and five shop rarity weights. The capture and REA Evidence are in `battle_diagnostics/cw-native-player-levels*`; the catalog producer hashes the scalar log and rejects missing/duplicate/out-of-range levels. `GeneralPropertyList` is explicitly outside this scalar capture. No run state, player object or saved progression was modified. The hook was detached and its Lua pulse removed.
|
||||
|
||||
The catalog now retains these scalar columns and `CurrencyWarsCatalog.PlayerLevel` returns a copied value. Run EXP modifiers, actual purchases, level transitions and shop draws are not implemented by this lookup. The stage build `.runtime/cw-levels-gameserver.exe` compiles; it has not replaced the live server or demonstrated CW entry.
|
||||
|
||||
A subsequent capture completed the full ten player-level rows, including `GeneralPropertyList`. The named Newtonsoft `SerializeObject(Object)` method exposes `RuntimeMethodHandle.Value` as Lua light userdata; reading that specific handle shows its executable method pointer at +8 (RVA 0x1d3efb60). PE function inspection confirms a static serialization overload forwarding path. This replaces an incorrect *candidate* produced by matching that method's MetadataToken to an unrelated native metadata-initialization check; that candidate was never invoked. Metadata-token pattern matches must not be generalized across assemblies as function addresses.
|
||||
|
||||
The existing bounded handbook-thread hook queried each native level row and serialized it through that verified method. Every serialized scalar was cross-checked against the earlier raw layout. The raw JSON preserves FixPoint property values as strings; levels 4–10 include nonzero property modifiers, so omitting these columns would lose gameplay data. Complete capture/Evidence are retained in `cw-native-player-levels-full*`. The resource now uses `captured_player_levels` and validates/copies the property rows. Both temporary hooks were detached, and all Lua probes from this work were removed from scripts. This capture still does not define initial run gold/level/context, shop pool depletion, stage generation or StartGamePlay behavior.
|
||||
|
||||
An additional metadata inspection of initialized DivisionStageConfigs (one selected stage in this Client session) and DivisionLevelConfigs (nine entries) found flattened wrapper fields rather than retained raw Row references. Its complete log is `cw-division-run-rows.log`. No raw division/server run configuration is claimed from that inspection.
|
||||
|
||||
## Route-node fields and chapter rules (2026-10-20)
|
||||
|
||||
The prior goal turn made concrete progress by capturing full player-level rows and role variants; CW start remains unfinished. This continuation resolves exact runtime handles for the named node/player protobuf getters and native node properties. `RuntimeMethodHandle.Value + 8` supplies their executable pointers. `cw-property-handle-rvas.json` and `cw-property-native-layout.json` retain the explicit targets and direct baseline return loads. The mapper accepts exact exception-table entries or short, branch-free leaf getters; computed getters remain unmapped. It tracks overwritten `this` aliases so a load through `_NodeParam` is not mistaken for an offset on the node itself. No all-type/assembly scan or getter invocation is used for that map.
|
||||
|
||||
The native node's `_RefreshCachedFields` (runtime-resolved RVA 0xdc09fa0) establishes these associations with previously captured wire tags:
|
||||
|
||||
| OHMDHKGAEMM offset | Wire tag / Go field | Native meaning |
|
||||
| --- | --- | --- |
|
||||
| 0x30 | 2 / StageId | CW node StageID |
|
||||
| 0x3c | 14 / CJCCMAACJDB | RawNodeType |
|
||||
| 0x50 | 12 / PICMDONCKCO | BasicReward |
|
||||
| 0x40 | 8 / PFEMDGNFHNH | compared with 1 for IsAugmentNode |
|
||||
| 0x4c | 11 / FCNNBODJNIK | base HasExpert flag, later affected by run modifiers |
|
||||
| 0x4d | 4 / HPNNLMBHOPF | base HasSpotLight flag |
|
||||
|
||||
The route-node protobuf's native offsets 0x20 and 0x24 are SectionId (tag 1) and ChapterId (tag 5), respectively. The player protobuf getter offsets independently corroborate the earlier thirteen scalar layout associations. `currency_wars_node.go` applies the mapped node subset while retaining other parameters; it rejects node types absent from the captured enum. The native enum capture contains GridFightNodeNone, Monster, CampMonster, EliteBranch, Boss and Supply. NodeTemplateID remains distinct from node StageID and must not be passed as battle StageID.
|
||||
|
||||
The same native node refresh calls a table getter at 0x1de40ce0 with ChapterID, then reads HealNum into RestHealNum. A bounded managed-thread capture serialized the three chapter rows referenced by the complete route catalog, checking each identity; all completed with HealNum=20. These values are captured resource data, not a code fallback. The catalog validates complete chapter coverage and source digests and returns copied values. Applying actual HP recovery still belongs to the node action; this lookup does not decide whether a later modifier changes the amount.
|
||||
|
||||
## Captured monster camps
|
||||
|
||||
`GetAllMonsterCampData` yielded 21 initialized active-season handbook configurations. A bounded exporter captured each native GridFightCampConfigRow and GridFightCampSeasonConfigRow, including season-specific monster IDs, normal battle areas and boss battle area. The producer verifies the common/season/wrapper identities, positive monster IDs, duplicate keys and complete capture markers. Text hashes retain integer precision in the resource and raw JSON in the Go reader. The catalog reader returns copied arrays and text metadata.
|
||||
|
||||
This is the observed handbook camp set, not proof of the full hidden pool. Camp MonsterList is not a complete node wave recipe: monster stars, levels, waves, battle templates and runtime enemy modifiers are still required for battle construction. No arbitrary camp list is substituted for an actual battle stage. The staged `.runtime/cw-node-state-gameserver.exe` includes the node/chapter/camp readers and compiles; the live server was not replaced during this capture, and neither Standard Gambit nor Overclock has been verified entering a run. All temporary probes/hooks from this continuation were removed or detached after their captures completed.
|
||||
|
||||
## Shop pool and transaction plans (2026-10-20)
|
||||
|
||||
The previous continuation progressed through the captured pool algorithm and bounded shop metadata capture. This continuation resolves 84 explicit method handles while the same Client process is live, then compares the native consumers and getters from the source assembly. Abstract GoodType has no executable pointer and is excluded from static getter analysis. Evidence is retained in `cw-shop-handle-rvas.json`, `cw-shop-getter-layout.json`, and the native shop disassemblies. The process subsequently closed; no handles from it may be reused after restart.
|
||||
|
||||
Important correction to the provisional shop interpretation: `RoleShop.Sync` RVA 0xdbc9fd0 reads POC +0x28 as MONJNCPINKN, then enumerates its pair list into **RarityWeights** at this+0x28. It is not the goods list or a card-pool count snapshot. The POC goods list is +0x18 / tag 3, passed to 0xdbca1a0, which fills shop slots. DLDJEOPKNMB at +0x10 / tag 15 has not been assigned a semantic meaning. Pair-column meaning still needs its exact selector resolved. POC +0x30 / tag 13 is the refresh price; +0x34 / tag 6 is free refresh count; +0x38 / tag 5 is lock state. Do not encode rarity weights into tag 15 based on its repeated-uint shape.
|
||||
|
||||
`GridFightShopGood.Cost` RVA 0xdbf2860 reads HDE +0x28 / tag 13. `Sold` RVA 0xdbf26f0 reads +0x24 / tag 7. The factory at 0xdbf2340 receives the slot index separately and selects the role constructor for oneof 1403, whose payload is NJBGFHDPFNO. The factory also recognizes native case 291, absent from the current generated Go oneof; that special goods variant is unsupported, not silently substituted. The source enum's normal/special meaning remains unassigned.
|
||||
|
||||
`currency_wars_pool.go` reserves offers against the captured eligible pool, reads native per-rarity capacities/exclusions, and returns unsold copies before a new draw. Within-rarity weighting is explicit server policy (`remaining_copies` or `uniform_role_ids`), not asserted to reproduce the original server. Depleted rarity buckets are excluded and errors discard the proposed draw. This is still the captured handbook pool, not every hidden role.
|
||||
|
||||
`currency_wars_shop.go` now produces detached transaction plans for manual refresh, node-entry refresh and purchase. Free refreshes, debit checks, repeated-purchase rejection and reserved-copy bookkeeping are included. Resolved slot count, refresh cost, rarity weights, quote and RNG are supplied explicitly; base rules come from the catalog. Upgraded offers require a separate copy-consumption rule. Board placement/merges and the run revision must be committed together with these plans by the upcoming action processor. No shop handler writes a run yet. The role-offer wire converter covers the supported role oneof and observed price/sold fields only.
|
||||
|
||||
`go build -o .runtime/cw-shop-transactions-gameserver.exe ./cmd/gameserver` succeeded. No tests were added or run. This staged binary has not replaced the root executable or running server, and it does not implement StartGamePlay. Both Standard Gambit and Overclock remain unverified at run entry. All temporary shop Lua probes were removed from `scripts/`; unrelated scripts were preserved. Remaining work includes complete initial run state, route/node generation rules, board actions, modifiers, battle construction, progression, rewards and Client verification.
|
||||
|
||||
## Offline continuation: references and EXP transitions
|
||||
|
||||
The preceding turn made progress through transaction plans and explicit shop field mapping. The Client process is now absent, so no runtime handles were reused and no new probe was deployed. Offline source reads and compilation remain available.
|
||||
|
||||
An adjacent older server installation contains GridFightRuntime, GridFightRewardRules and GridFightBasicOrbRewards JSON files. Their section inventory and source hashes are retained in `cw-offline-reference-inventory.json`. Reward rules contain nine combat profiles, three boss profiles, five node reward profiles and four talent triggers. These profile keys are not established as the same namespace as native NodeTemplateID, and the files carry no Client revision/provenance. They have not been imported or treated as current native node recipes. The SQLite file in that installation is player persistence, not a substitute for the missing native CW tables. The adjacent gameserver executable has no CLI directory, so managed reflection/CIL inspection cannot recover its implementation. No adjacent files or databases were modified or executed.
|
||||
|
||||
`CurrencyWarsCatalog.PlanBaseExperience` now resolves multiple crossed levels from captured thresholds, validates the current counter and run limit, preserves each crossed level's copied property/capacity/rarity configuration, and rejects a protocol counter overflow. It is a detached calculation, not a BuyExp handler. Initial run values, run modifiers, gold/HP payment and the native treatment of residual EXP at the cap still need to be established and applied together by the action processor. No cap-reset behavior is invented here. The staged `.runtime/cw-experience-gameserver.exe` compiles; no tests were added/run and the root executable was not replaced. This does not prove CW entry or progression in the Client.
|
||||
|
||||
## Preparation and position blockers (2026-10-20, continuation)
|
||||
|
||||
Client logs now confirm RoundBegin queue 2 -> ReturnPreparation queue 10 -> no pending, followed by successful UpdatePos saves. The previous 6900 was an unsupported RoundBegin; 8795 had no handler. Current implementation uses revision-bound pending recipe `CurrencyWarsPreparation.json`, generates a current catalog shop and saves before notifying. UpdatePos validates the entire final board, ownership, duplicate request IDs, collisions, capacity and duplicate active avatar IDs. GDAKCOICIDA is keep-win count, not bench overflow; overflow bounds now read the captured constant.
|
||||
|
||||
A controlled native commerce replay confirms `GridFightBuyGoodsCsReq` -> `BuyGoodRsp`, zero-based indices and add-role/shop sync. Buy, refresh and lock handlers are implemented. Purchases require shared entry property scales, use a fresh entity ID across the run and save the gold debit, sold offers and new bench role in one revision. This is the entry prototype: merges, role switches, trait recalculation and full modifier-dependent quotes remain incomplete.
|
||||
|
||||
The SDK `/cw` command path queues run-bound absolute gold/HP commands in a separate persistent field, without writing adventure data or changing the CW snapshot revision. The game applies them under the session lock only while CW is open, preparing and outside Battle. It saves before notifying and acknowledges afterwards; retrying an absolute assignment is idempotent.
|
||||
|
||||
Native replay also captures a first-node battle response. `tools/export_cw_battle_template.go` retains its exact enemy recipe. Runtime validates its route, node, division, mode, stage and penalty, replaces party/NPC equipment/portal/player identities, and rejects unsupported nodes. The captured party's traits/buffs/events are removed, not inherited. This describes the entry prototype at that point; subsequent Client evidence and settlement work are recorded below.
|
||||
|
||||
## Client-confirmed first settlement and preparation continuation (2026-10-21)
|
||||
|
||||
The user confirms first-node combat works. The actual game server trace records `CW_SETTLEMENT` for battle 21, followed by RoundBegin queue 12, Investment queue 13 and ReturnPreparation queue 14. The run reaches chapter 1, section 2 with its existing team, purchases and GM gold retained. This supersedes earlier statements in this investigation that run entry or all settlement is unimplemented. It does not establish a complete run or Overclock gameplay.
|
||||
|
||||
First-node settlement now checks uploaded battle identity, percentage bounds and the saved revision. Gold, HP, EXP, next-node enemy layer and pending action are saved together before notification. Interest and victory ranges use captured data. Three captured Investment outcomes update only owned augment/gold state; these are not a general augment effect engine. Loss/terminal settlement, all node transitions, orb collection, merging and full traits/equipment effects remain unfinished.
|
||||
|
||||
Native sale replay establishes request `GridFightRecycleRoleCsReq`, response `GridFightRecycleRoleScRsp`, the `HFCBOIOAECK` remove-role arm and observed sale source enum. The handler saves the role removal and captured sale quote atomically. Equipped roles are rejected until the equipment-return ownership notification is implemented; sale must not orphan gear.
|
||||
|
||||
Native EXP replay establishes `GridFightBuyExpCsReq` / `GridFightBuyExpScRsp`, source `JJDFGMMMCMI_IHEKGIGCGEC`, gold, capacity (`BPCFDPNHIEL`), shop weights and player-level sync. Repeated purchases reach level 10 with residual EXP 2; further purchases return failure without spending gold. Before/after native team property maps remain identical. The implemented base purchase uses the run's gold quote and current captured EXP rule, saves before notifying, and updates capacity and rarity weights together. Modifier-dependent EXP gains and additional capacity bonuses still need their own resolution; this is not a claim of complete modifier support.
|
||||
|
||||
The user's current Client exposes node 1-2 penalty row 92013, including TotalTurn 15, base HP penalty 5 and progress coefficient 10. This exact row and its capture digest are retained in `CurrencyWarsSettlementRules.json`. Named stage/monster metadata reads are bounded and do not load scene graphs. The isolated reference returns retcode 1 with no BattleInfo for node 1-2, even after re-entering that node and refreshing positions. The cause inside that binary is not established. Its first-node BattleInfo must not be copied onto another node to hide this failure. The current server still rejects an uncaptured enemy recipe explicitly; second-node combat and subsequent settlement remain work in progress.
|
||||
|
||||
### Second-node recipe recovered from the reference save
|
||||
|
||||
The isolated reference's `GridFight.RunInstance` holds a serialized native BattlePlan even though its second EnterBattle response fails. A unique bounded MemoryPack record at snapshot offsets 158:309 decodes as camp, elite group, penalty, difficulty and two waves; wave/monster record field counts match the public native schemas. `tools/export_cw_enemy_plan.py` rejects ambiguous matches, oversized arrays and nonempty drop payloads. It exports only the wave parameters and provenance hashes, not the account or party. The current target's flat `StageExcelTable` row independently supplies Level 90 and HardLevelGroup 1; no level graph loader was invoked. Its active stage reports zero additional difficulty.
|
||||
|
||||
`CurrencyWarsEnemyPlans.json` binds this recipe to the captured route/division/node/mode/stage/penalty. Runtime builds enemies from the saved layer's monster identities and stars, checks them against the current target season's camp, validates wave IDs/counts and rejects unresolved difficulty modifiers. The proven first-node path is retained. The second-node parameter reconstruction and resulting combat are still awaiting Client verification.
|
||||
|
||||
An isolated native administrative move to the next route node followed by RoundBegin captures the Supply layer/action. This is explicitly not a native second-battle settlement capture. Only next-node layer/pending definitions are imported; runtime settlement computes the player's own rewards and preserves their team. The queued action identifiers are opaque server-issued cursors following the preceding consumed action. The user confirms sale and EXP purchases work, including reaching the level cap. A staged and deployed second-node build now links its settlement to the Supply screen; Supply selection and later nodes remain unfinished.
|
||||
|
||||
### Current Client correction: node 1-2 is Plot Armor
|
||||
|
||||
The user reports that this node presents the Protagonist button, not ordinary Battle. Requests include `GridFightSpotLightEnterCsReq` and `GridFightUpdateSpotLightTrackCsReq`, both previously unhandled. The old reference lacks this current Spotlight implementation. The reconstructed normal second-battle path above is not verified as the appropriate current route. Moreover, the imported old layer's camp must not be treated as a generated member of the current run's active camps. Selection, actual effects and the appropriate enemy layer remain unfinished.
|
||||
|
||||
Bounded current Client captures now contain 84 initialized Spotlight rows, exported by `tools/export_cw_spotlights.py` to `resources/CurrencyWarsSpotlights.json`. These are real configurations and suitable-role lists, not evidence of selection weights, offer count, costs or grant policy. Do not substitute arbitrary offers or a success-only response for those missing rules.
|
||||
|
||||
Named MethodHandle +8 reads and bounded PE disassembly establish `GridFightSpotLightSelectAction._SyncFromProto` at RVA `0xdc00cd0`. Isolated protobuf setters encoded to bytes establish the field tags independently. Assembly SHA256 is `c5e02e6e984f7e5577c78b995a8efeee2d06ee22e67d484ad5e859c8bf21e144`. Evidence is retained in `battle_diagnostics/cw-spotlight-selection.asm`, `cw-getter-native.json`, `cw-spotlight-wire.log` and `cw-spotlight-getters.log` (ignored diagnostics).
|
||||
|
||||
| Pending BDLBHJGNCFM field | Current Client meaning |
|
||||
| --- | --- |
|
||||
| GFGJOKJGGCO (2) | TargetSpotLightUniqueId |
|
||||
| BKOGIFHEKHH (4) | IsGrant |
|
||||
| HPKFKBKOADP (5) | Candidate Spotlight IDs |
|
||||
| IAJCPLMAACH (7) | IsUnlimitedTier |
|
||||
| GFJDGNKAOFK (8) | NextRefreshCost |
|
||||
| DLIANLGFMBG (9) | TargetRoleId |
|
||||
| DOAHJPPPMGM (12) | RemainingRefreshCnt |
|
||||
| ACKOKJBBPKH (14) | IsAbandonForbidden |
|
||||
|
||||
ActionID comes from the enclosing pending QueuePosition. The protobuf arm is EEKMIAMGHFL (1817), not the ordinary Battle arm. `SelectCandidate` dispatches role and selected Spotlight ID through `Select`; this still needs a server-owned offered-choice/role validation path. FMBACDOFCAL mappings independently encoded: IHHNLLAJAGB (1) SpotlightID, UniqueId (2), MDNAGANGNKK (9), ELBPNDIENDL (11), RoleId (13). Meanings of fields 9/11 and special effects are not inferred merely from their types.
|
||||
|
||||
### Basic merges and equipment tracking
|
||||
|
||||
`CurrencyWarsCatalog.json` now contains 219 actual native star rows, identity checked against the requested role/star (no clamped rows accepted). `tools/import_cw_role_stars.py` preserves earlier configurations and imports only observed rows. Ordinary sale quotes use the current rarity table only when the captured default quote agrees with that table; unestablished special quotes are rejected.
|
||||
|
||||
`tools/export_cw_merge_rule.go` derives the required copy count from an isolated native merge sync. The captured merge group removes three UIDs, adds one new UID at star 2, and uses source `JJDFGMMMCMI_NJBMBFFLNGM`. Runtime basic merges cascade for identical role IDs up to the resource-defined ordinary limit, prefer active/equipped roles for the retained position, preserve the retained role's equipment attachments and leave other equipment in the bag. It rejects unresolved divergent role modifiers. Physical pool accounting uses the merged card's underlying copy count; ordinary full-star cards are excluded from new offers. A full bench can accept a third copy only when merging consumes it. Resume reconciles old triplets in a saved transition; its following GetData returns the changed roster, avoiding notifications before the Client session exists. Switch groups, Spotlight star locks, special higher-star upgrades and merge-triggered trait effects remain incomplete.
|
||||
|
||||
Current tracking handlers bind names from the current command table: GridFightUpdateEquipTrack and GridFightUpdateEquipTrackPriority. Tracks persist in the player component MIIDIMOCPEB; KFNCAIHIHCM.BMGFDIKDOBE carries the complete updated list. Native `CreateServerTrackUpdateInfo` at RVA `0xda5afa0`, `RemoveEquipTrack` at `0xda63a30` and `_AddTrackToLastAndSend` at `0xda628d0` prove BJDOINAHDLN is **enabled**: add passes true, removal false. It is not a deletion flag. Priority is NALMBOOCCIN; the source protocol comparison also names it priority, but old source tags/opcodes are not used. Equipment IDs are checked against the 142 current initialized handbook equipment rows. This implements tracking persistence/display, not equipping, crafting or combat item effects.
|
||||
|
||||
The merge/tracking build compiles; no automated tests were added or run. Client confirmation of these new changes remains pending. Finished research scripts have been removed from scripts/; their source remains under tools/.
|
||||
@@ -0,0 +1,104 @@
|
||||
# Damage Analyst: native live statistics
|
||||
|
||||
The Client's final PVEBattleResult packet can contain a correct monster/team
|
||||
total while all AvatarBattleInfo detail counters are zero. Its cached native
|
||||
PVEGameStatistics still contains the complete character details. The old live
|
||||
sender also read this last completed report during the next battle.
|
||||
|
||||
The live sender now reads the active BattleGamePhase.BattleInstanceRef's logic
|
||||
GameWorld, then the native level entity's StatisticsComponent. It reads only
|
||||
known fields and caches them; component discovery is bounded to 16 entities
|
||||
and 32 component slots per sample and uses the named StatisticsComponent type.
|
||||
No assembly-wide reflection is performed. Do not call BuildPVEStatistics while
|
||||
playing: native code caches the snapshot it builds, so calling it for a UI
|
||||
poll could freeze the game's eventual report at an early point.
|
||||
|
||||
The source `_LightTeamStt` contains the current AvatarBattleInfo. Native
|
||||
PVECharacterStt dictionaries supply attack-type totals (`FNFFCPLHPGI`), break
|
||||
totals (`HNJNDLEFMBE`) and maximum hits (`KEOOJBHFLFD`) because the corresponding
|
||||
protobuf lists are assembled only at settlement. The aggregate total remains
|
||||
the named TotalDamage; some attribution counters do not appear in the category
|
||||
dictionary, so that aggregate is not replaced by a category sum.
|
||||
|
||||
Additional fixes:
|
||||
|
||||
- XLua returns protobuf byte arrays as Lua strings on this Client. Their byte
|
||||
length is `#bytes`; `.Length` was nil and stopped all uploads.
|
||||
- UploadHandlerRaw accepts the ASCII JSON body directly as bytes. Calling the
|
||||
unavailable Encoding.GetBytes overload stopped uploads after the first fix.
|
||||
- Sampling runs every 750 ms through render callbacks, with a heartbeat
|
||||
fallback for background windows/render paths that suppress a callback.
|
||||
Callbacks read the current sample function after a script refresh; earlier
|
||||
broken callback states are disabled without changing Client battle state.
|
||||
- Native NOGHNMCDDAI is a damage-taken breakdown, verified against supports with
|
||||
zero dealt damage. It is no longer used as a damage-dealt fallback.
|
||||
- BattleInfo accompanies live reports, providing StageID, BattleID, level,
|
||||
rank and each avatar's actual slot. The companion UI orders by these slots,
|
||||
independently of the overworld team. Historical reports without slots keep
|
||||
their recorded order.
|
||||
- Final native details are attached only to a completed result whose duration,
|
||||
turn count and avatar IDs match. Server outcome and monster analysis remain
|
||||
intact; mismatching snapshots are rejected and cannot change the report.
|
||||
- The UI renders additional native attack types, including ElationDamage and
|
||||
TrueDamage, with escaped labels instead of omitting those categories.
|
||||
|
||||
## Evidence and verification
|
||||
|
||||
The ignored `battle_diagnostics/damage-live-20261019` directory preserves native
|
||||
protobuf snapshots, diagnostic logs and `live-api-verified.json`. The Client
|
||||
assembly SHA-256 is
|
||||
`c5e02e6e984f7e5577c78b995a8efeee2d06ee22e67d484ad5e859c8bf21e144`.
|
||||
|
||||
During the user's active stage 1052106, API `is_live=true` changed from
|
||||
5,906,923.87 to 6,825,838.95 damage before settlement, with four current
|
||||
characters in slots 0–3, level/rank metadata, native category breakdowns and
|
||||
shares summing to 100%. This verifies the Client-to-SDK live path; it is not a
|
||||
synthetic test report. Frontend slot/category changes require the corresponding
|
||||
GaC-srtool build to be published before they appear on hosted GitHub Pages.
|
||||
|
||||
Focused Go report/API tests cover damage-taken exclusion, native category data,
|
||||
slot identity, final matching/rejection and expiry. The frontend Node tests
|
||||
cover actual battle order versus a different overworld team and historical
|
||||
report behavior. Diagnostic probes are kept outside scripts after capture.
|
||||
|
||||
Published GaC-srtool commit e03e5b1 to main with user approval. The GitHub Pages workflow completed successfully, and fetching the public app.js confirmed the battle-slot and additional-category code. The final real Client report retained WIN, Stage1052106, four slots 0-3 and detailed damage after leaving battle. Browser UI automation was unavailable in local Work settings; frontend rendering was checked with the actual captured live report in a DOM fixture, plus a public asset check.
|
||||
|
||||
## Memosprite attribution and Other (2026-10-20)
|
||||
|
||||
The earlier reader omitted the separate servant statistics. The native named
|
||||
StatisticsComponent field `_ServantStt` contains `LLPEHDFMLNJ` values; bounded
|
||||
reflection of this specific type found `LJLDPJFOKCE` (HFALLFOEHKN servant
|
||||
protobuf) and the same three attack-type dictionaries as character statistics.
|
||||
Completed PVEGameStatistics exposes `servantBattleInfoList`. Both are now sent
|
||||
through the SDK as separate servant protobufs; active category dictionaries are
|
||||
hydrated without invoking the native final snapshot builder.
|
||||
|
||||
The checked-in protocol's BattleAvatarServantInfo explicitly names `avatar_id`,
|
||||
`entity_id`, aggregate damage and attack-type lists. Ownership uses `avatar_id`,
|
||||
with no character ID or lineup-position special cases. The report combines the
|
||||
separate servant damage/healing/shield with that owner and exposes the included
|
||||
damage as `memosprite_damage`. Damage taken and owner turns remain the owner's
|
||||
counters. Maximum hit uses the maximum across owner and servant, rather than
|
||||
adding maxima. Repeated nonzero entity IDs are ignored. Team totals/shares are
|
||||
rebuilt from attributed records so a monster damage-taken fallback is not added
|
||||
again. Inputs are not modified, and alternate servant sources cannot be mixed
|
||||
in the same request.
|
||||
|
||||
The fifth frontend category is Other: the nonnegative remainder after Basic,
|
||||
Skill, Ultimate and Break. Existing named extra categories remain visible as
|
||||
details. A Memosprite line explicitly marks its damage as included in the
|
||||
character total. Frontend commit `ca5831c` contains only these damage changes;
|
||||
unrelated item-catalog work in the shared checkout was left uncommitted.
|
||||
|
||||
Verification: focused Go report/API tests pass, including an HTTP roundtrip
|
||||
with owner-only and servant records, correct percentage recalculation,
|
||||
duplicate/unknown owner handling, and prevention of monster-fallback double
|
||||
counting. Four frontend tests pass, including Other arithmetic and existing
|
||||
slot order. The local SDK executable has the new receiver. Temporary servant
|
||||
probes have been removed from scripts/ and retained only under tools/.
|
||||
|
||||
Runtime limitation: the current session did not provide a nonempty servant
|
||||
sample. The previous real Client verification above used a team without
|
||||
memosprites. Evernight's live/final ownership and aggregate behavior still need
|
||||
a real battle capture; passing synthetic tests does not establish that the
|
||||
Client integration works. Native obfuscated field names remain revision-bound.
|
||||
@@ -0,0 +1,61 @@
|
||||
# AA cycle / star settlement investigation
|
||||
|
||||
The reported King in Check win displayed zero consumed cycles and no completed
|
||||
star conditions. This is not yet a verified complete fix.
|
||||
|
||||
## Confirmed evidence
|
||||
|
||||
- The original win report has 36 battle turns, `RoundCnt=0` and stage 30501021.
|
||||
The cached native PVEGameStatistics had `totalBattleRounds=0`,
|
||||
`challengeTurnCount=0`, `challengeTurnLeft=7`. A battle turn is not a cycle;
|
||||
the meaning and wire mapping of the additional counters remain unverified.
|
||||
- `finishChallengeBattle` copies `RoundCnt` into Peak `CyclesUsed` and never
|
||||
supplies `FinishedTargetList`. This accounts for the displayed values, but
|
||||
does not prove that the battle used zero cycles or earned no stars.
|
||||
- The original start packet had `RoundsLimit=40`: Peak 104 was configured
|
||||
through the separate ChallengeMaze table with the same numeric ID.
|
||||
- Read-only captures from the running Client's ConstValueClientExcelTable
|
||||
contain `ChallengePeak_Mob_Turn_Limit=6`,
|
||||
`ChallengePeak_Boss_Turn_Limit=6`, and
|
||||
`ChallengePeak_HardBoss_Turn_Limit=2`.
|
||||
- Native BattleTargetConfigExcelTable rows referenced by the installed Peak
|
||||
tables are preserved in `resources/PeakBattleConfig.json`, together with
|
||||
Client version, GameAssembly SHA-256 and capture SHA-256. Boss targets
|
||||
3003/3004/3005 have thresholds 6/4/2; 3000 is a death-count condition.
|
||||
These are BattleTargetConfig rows, not ChallengeTargetConfig rows.
|
||||
|
||||
The ignored evidence directory is `battle_diagnostics/peak-cycle-20261019`.
|
||||
`Player.native-tables.log` is the source for the new resource. Temporary
|
||||
probes inspect named types/tables with bounded work per heartbeat. A missing
|
||||
BattleTargetExcelTable was corrected to the observed
|
||||
BattleTargetConfigExcelTable; no assembly-wide reflection is used.
|
||||
|
||||
## Changes made
|
||||
|
||||
- Peak start and restart use an explicit AA battle type and the native
|
||||
normal/boss/hard limit, independent of the ChallengeMaze ID namespace.
|
||||
- Rules reject a mismatching Client version, missing provenance, unknown
|
||||
event or missing native limit. No guessed fallback is installed.
|
||||
- Full PVEBattleResult requests are archived under `battle_diagnostics`,
|
||||
including obfuscated and unknown protobuf fields that the aggregate damage
|
||||
report does not retain. JSON is for inspection; binary preserves unknowns.
|
||||
- `tools/peak_result_snapshot.lua` reads the six named native counters and
|
||||
target lists for comparison with the next server packet. Its temporary
|
||||
copy in scripts must be removed after reproduction.
|
||||
|
||||
Focused Peak configuration and battle capture tests pass; a game-server build
|
||||
passes. This does not verify Client battle loading, cycle display, stars,
|
||||
medals or persistence. The existing unconditional unlock/medal response in
|
||||
GetChallengePeakData has not been rewritten in this patch.
|
||||
|
||||
## Pending Client reproduction
|
||||
|
||||
The user agreed to complete another AA battle and leave the result screen
|
||||
open. Compare its full result packet with native counters before assigning
|
||||
semantic names to obfuscated fields or calculating FinishedTargetList. Do not
|
||||
derive cycles from TotalBattleTurns, subtract from a guessed initial counter,
|
||||
or grant every target merely because EndStatus is WIN.
|
||||
|
||||
The Main chat is concurrently repairing login in the same workspace and
|
||||
restarting the shared servers. Stop additional restarts from this chat while
|
||||
that deployment is in progress; both builds included the Peak source changes.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Endgame team recommendations
|
||||
|
||||
`GetFriendRecommendLineup` (CmdID 2985 from the checked-in 4.6.53 command table) now returns three recommended teams for the current MoC floor-12 snapshot. The response uses the `OJJJDPNOKHG.lineup_list` shape because its repeated `ChallengeLineupList` entries match the three stage rows in the referenced Client page. Each external character slug is resolved through the current `resources/AvatarConfig.json`; the server does not embed AvatarIDs in its handler.
|
||||
|
||||
The bundled snapshot is dataset revision 4.6.2, collected 2026-10-14, for MoC cycle 4.6.1 “Crossing the Afterlife”. The files identify the first-ranked Prydwen team for stages 12-1, 12-2, and 12-3. The dataset repository declares MIT and asks users to credit LvlUrArti; its README says it contains processed data shown on Prydwen. Attribution and source metadata are kept in `resources/PrydwenEndgameTeams.json`.
|
||||
|
||||
To refresh for another floor and the newest published snapshot, run `python tools/update_prydwen_endgame_teams.py --floor 12 --stage-count 3`. The updater validates every slug against this Client's AvatarConfig before replacing the snapshot; add a source-slug-to-AvatarVOTag alias in the resource if a name differs.
|
||||
|
||||
Protocol limitation: the current proto retains an obfuscated `JFNJKAPPNOF` enum and obfuscated response oneof names. The handler echoes the request `type` and `key`, and uses the lineup-list variant based on the visible three-row MoC UI and the message's repeated lineup schema. The exact numeric type-to-mode relationship and the meaning of response field `ALDBIIIGLOM` are not established. This first implementation does not discriminate modes using `type` or `key`, so it is limited to the illustrated MoC page and needs in-Client verification before claiming support for other Endgame modes or the friend-cleared tab.
|
||||
@@ -1,5 +1,40 @@
|
||||
# ตรวจ teleport และ Endgame — 17 ต.ค. 2026
|
||||
|
||||
## ห้องรถไฟค้างโหลด — 19 ต.ค. 2026
|
||||
|
||||
- ผู้ใช้รายงาน AA และห้องรถไฟเข้าไม่ได้ โดย log ที่ส่งระบุ Entry `100000352`, Floor `10000003` และยังมี heartbeat หลังส่ง EnterScene response/notify แล้ว จึงไม่ถือว่าเข้า Client สำเร็จ
|
||||
- Client log รอบ 00:03 พบ AsyncLoad MapProp ล้มเหลวใน `_EnterStep2` ขณะโหลด `DesignerBackup/Test_Prop_Teleport.bytes`, `Test_Prop_PTBathSalt.bytes`, `Test_Prop_PTWeaponRack.bytes` และ `Test_Prop_PTLightOff.bytes` ที่ไม่มีใน bundle index หลักฐานก่อนแก้เก็บใน ignored `battle_diagnostics/train-loading-20261019/`
|
||||
- พบว่า no-teleport adventure entrance เลือก checkpoint แรกของ Floor แม้ MapEntrance/Anchor ระบุจุดเริ่มเฉพาะห้อง แก้ให้ใช้ StartAnchorID ของ Entry บน Floor ที่ตรงกันก่อน checkpoint โดยไม่เปลี่ยน challenge loader หรือ checkpoint teleport ที่ระบุมาโดยตรง การไม่มี Anchor ยังใช้เส้นทางเดิม
|
||||
- Capture ก่อนหน้าไม่จบแม้แต่ Floor แรก จึงยังไม่ใช้ข้อมูลบางส่วนทับ metadata ปัจจุบัน เปลี่ยน capture เป็นเฉพาะ Floor ที่ล้มเหลวก่อน อ่านเฉพาะ RuntimeFloor/RuntimeGroup สูงสุด 4 กลุ่มต่อ heartbeat และเก็บ SavedValues ของ dimension เพื่อเทียบสถานะ debug กับ Client โดยไม่โหลดหรือรัน level graph
|
||||
- Unit test ของ entrance anchor ผ่านและ build game server ได้ การทดสอบ common ผ่าน แต่ integration `TestGatewayHandshakeAndLogin` ติด fixture monster event `30124011` ที่ไม่มี Client stage mapping ใน resource ปัจจุบัน จึงยังไม่ถือว่า suite ทั้งหมดผ่าน
|
||||
- AA ยังต้องเก็บ request/Client log ของการกดเข้าจริงแยกต่างหาก ไม่สรุปว่ามีสาเหตุเดียวกับห้องรถไฟ และยังรอยืนยันโหลดฉากหลังแก้จาก Client
|
||||
|
||||
Capture รอบใหม่ครบ Floor `10000003`: กลุ่มไม่ซ้ำ 202 กลุ่ม, Server ที่ยังไม่ลบ 19 กลุ่ม และ SavedValues 34 ชื่อ พบ Client-managed groups 2/25/28/79 อ้างถึง DesignerBackup graph ทั้งสี่ชื่อที่ล้มเหลว โดยมีเงื่อนไข `Utility_IsDebug == 1` (CompareType Equal=4 จาก enum ที่ capture ไว้) ขณะที่ native DefaultValue ของ flag นี้เป็น 0 ชุด MaxValue เดิมจึงเปิดกลุ่ม debug ที่ asset ไม่ได้ถูกแพ็กมา ใช้ native defaults ที่ capture สำหรับ Floor นี้และเก็บ min/max/default เป็น rules ไม่แก้แบบลบ PropID 100 ทั้งเกม
|
||||
|
||||
ไม่ได้ส่ง floor-wide start anchor ของรถไฟทับทุก Entry เพราะแต่ละห้องมี StartGroup/StartAnchor ของตัวเอง ใช้ `--omit-start-anchor` ใน exporter แล้วให้ no-teleport adventure path resolve Anchor ตาม Entry ข้อมูล Floor เดิมอีก 36 Floor เปรียบเทียบแล้วไม่เปลี่ยน และเก็บ source policy ของข้อมูลเดิมไว้ การ capture condition เป็นการอ่าน RuntimeGroup และ path ไม่ได้รัน graph ของ debug props หลัง capture นำ probe ของงานนี้ออกจาก `scripts/` แล้ว
|
||||
|
||||
คำสั่งตรวจ exporter ครอบคลุม incomplete/missing/invalid SavedValues โดยยืนยันว่าไม่เขียนทับ output เดิม, การเก็บ provenance เมื่อ merge และการคง defaults แทน MaxValue ผ่านแล้ว ส่วน Client หลังแก้และ AA ยังรอทดสอบจริง
|
||||
|
||||
### ผลทดสอบรอบแรกและการกรอง Dimension
|
||||
|
||||
ผู้ใช้ยืนยันว่ากดวาร์ปแล้วไม่มีอะไรเกิดขึ้นทั้งห้องและ AA หลังเปลี่ยน defaults รอบแรก Client ไม่แจ้ง DesignerBackup graph error เดิม แต่เกิด `MapDef.InitServerGroups` NRE เพราะ Group 251 ไม่มีใน MapDef นี่เป็นผลจากการเพิ่ม Server group ตาม RuntimeFloor ทั้งชุดโดยยังไม่กรอง Dimension ไม่ใช่หลักฐานว่าแค่ default ของ Floor แก้ครบแล้ว
|
||||
|
||||
การอ่าน RuntimeDimension ระบุว่า Dimension 0 มี GroupIndexList ของตัวเอง (199 GroupID ไม่ซ้ำจาก Floor รวม 202 กลุ่ม) และไม่มี 251 ขณะที่ Dimension อื่นมีข้อมูลกลุ่มนี้ เพิ่ม `dimension_groups` จาก GroupIndexList/GroupInstanceList ของ Client แล้วกรองทั้งกลุ่มเดิมและเส้นทางเพิ่ม prop จาก metadata ให้ตรง Dimension จริง ไม่ลบหมายเลขกลุ่มแบบเฉพาะ ID Unit test ตรวจว่ากลุ่มจาก Dimension อื่นไม่ถูกใส่กลับผ่าน props พร้อมรักษากลุ่มที่ถูกต้อง ผ่านแล้ว ผลวาร์ปหลังตัวกรองรอบนี้ยังรอ Client ยืนยัน
|
||||
|
||||
การตรวจต่อเพิ่ม `TestTrainRoomScenePacketMatchesCapturedDimension`: สร้าง SceneInfo ของ Entry ที่ผู้ใช้รายงานผ่าน `loadScene` ด้วย resource ปัจจุบัน แล้วตรวจ membership ของ EntityGroupList, DimensionID, SavedValues, XYZ/rotation ของ entrance และการคงจุดลงเมื่อระบุ checkpoint โดยตรง ผ่านแล้ว การทดสอบนี้สร้างข้อมูลใน session จำลองและไม่ส่งเข้า Client จึงไม่ใช่หลักฐานว่าภาพฉากโหลดได้ ส่วนการเฝ้า Game server และ Client process ที่ยังทำงานอยู่หลัง deploy ไม่พบคำขอ EnterScene ใหม่ในช่วงที่เฝ้า ยังต้องลองวาร์ปจาก Client หลังชุดกรอง Dimension ล่าสุด
|
||||
|
||||
### ผลยืนยัน AA และจุดลง Party Car
|
||||
|
||||
ผู้ใช้ยืนยันหลังชุดกรอง Dimension ว่าวาร์ปเข้าหน้า AA และกดเข้าได้แล้ว แต่ยังไม่ได้ลองเล่น จึงยืนยันเฉพาะเส้นทางเข้า ไม่รวมการต่อสู้หรือ settlement ส่วนการวาร์ปห้องอีกจุดลงห้องนอน MC: log แยกได้ว่า Entry 1000003 (Party Car) ถูกกดก่อน 100000352 (ChallengePeakBattleEntry/AA) โดย 1000003 ไม่มี StartAnchorID และไม่มี Anchor เฉพาะ Entry จึงยังตกไปใช้ checkpoint แรก
|
||||
|
||||
เก็บ `floor_default_anchor` แยกจาก `start_anchor` ที่ทับทางเข้าทั้ง Floor และใช้เฉพาะ MapEntrance ที่ไม่ระบุ StartAnchorID เท่านั้น Native default ของ Floor นี้ที่ capture ไว้คือ XYZ (-5449,-15289,-12), RotY 180000 ส่วนทางเข้า AA เก็บ anchor เฉพาะเดิม เพิ่ม regression test ทั้ง default Town entrance และ explicit checkpoint ผ่านแล้ว ยังรอผู้ใช้ยืนยันจุดลง Party Car หลังแก้รอบนี้ และรอชื่อห้องเป้าหมายเพื่อยืนยันว่าตรงรายการที่ผู้ใช้ตั้งใจ
|
||||
|
||||
### ยืนยันผลและปิดงาน — 19 ต.ค. 2026
|
||||
|
||||
ผู้ใช้ระบุชัดว่าข้อ 1 คือ Party Car และยืนยันว่า “ตอนนี้ถูกแล้ว” หลังใช้ floor default anchor ส่วน AA ผู้ใช้ยืนยันก่อนหน้านี้ว่าวาร์ปไปหน้า AA และกดเข้าได้แล้ว Log รอบล่าสุดของ Party Car เป็น Entry 1000003, Floor 10000003, XYZ (-5449,-15289,-12) และการเข้า Entry 100000304 สลับกลับ Party Car ใช้ anchor คนละจุดตาม resource โดยไม่ถูกทับเป็นจุดเดียวกัน จึงยืนยันการแก้อาการค้างโหลดและจุดลงผิดสำหรับ Party Car/ทางเข้า AA ที่รายงานได้ครบตามขอบเขตงานนี้
|
||||
|
||||
หลักฐานแยกกัน: Client ยืนยันการวาร์ปและเข้าฉากจากผู้ใช้; regression tests ตรวจ Dimension membership, SavedValues, anchor เฉพาะทางเข้า, floor default และ explicit checkpoint พร้อม build ผ่าน การเล่นต่อสู้และ settlement ของ AA ยังไม่ได้ทดสอบ และ integration suite ทั้งหมดยังไม่ถือว่าผ่านเพราะ fixture stage mapping ของ smoke test ที่บันทึกไว้ข้างต้น งานนี้ไม่ได้เปลี่ยนเส้นทาง Battle เพื่ออ้างผลจากการเข้าฉาก
|
||||
|
||||
## หลักฐานจาก Client
|
||||
|
||||
- `Player.log` แจ้ง `[HoyoGroupManager] 尝试激活的Group不存在, FloorID=20571001 FloorName=Starfroster GroupID=216` ก่อน `MapDef.InitServerGroups` เกิด NullReferenceException
|
||||
|
||||
Reference in new issue
Block a user