Repository navigation
feat: Beastmaster gauge + FCS 7.57 + Lumina 7.7.1 (9.2.5) - #137
Conversation
…(9.2.4) FFXIVClientStructs f0741b760 -> d6397a76b (333 commits, game patch 7.56 hotfix2 then 7.57, build 2026.10.06). Lumina 7.6.1 -> 7.7.1; Lumina.Excel stays at 7.5.0, already the newest release. Supersedes dependabot #135. 7.57 deprecated three members Sharlayan read, each left as an [Obsolete] alias at the identical offset, so the reads were still correct but the aliases will eventually be deleted upstream: - ActionBarSlotNumberArray.ActionType -> Type (offset 0). Pure rename; the mapper only needs the offset. - BlackMageGauge.EnochianFlags -> [BitField] EnochianActive / ParadoxActive / AstralSoulStacks over a private BitFieldD byte at 0x0D. - SummonerGauge.Attunement -> [BitField] AttunementType / AttunementCount over a private BitFieldE byte at 0x0E. For the two gauges the replacements are bit accessors over a private backing field, so the offsets now come from FieldOffsetReader string-name lookups - the same mechanism already used for _materia, _status and _attributes. JobResourceResolver keeps masking the bits itself and Sharlayan still surfaces the same values, so this is behaviour-neutral. Per the DEPENDENCY.md contract both new lookups get integrity tests, plus a test pinning BitFieldD=0x0D / BitFieldE=0x0E and ActionBarSlotNumberArray.Type=0 so an upstream layout move can't silently relocate these reads. Checked the things compile and tests cannot: no [StaticAddress] signature or declared size changed for any of the 21 scanner-key types across the whole range. Of the structs we depend on, PlayerState changed but none of our fields moved, Utf8String only altered a member-function signature we never call, and AstrologianGauge.Cards is still a short at 0x08 (FCS added bitfield accessors beside it and deprecated a CurrentCards helper we don't use). Useful side effect of reading the new [BitField] declarations: they are an authoritative statement of the bit layout, and our hand-rolled masks match exactly - BLM Enochian bit 0 / Paradox bit 1 / AstralSoul bits 2-4, and AST Card1/2/3 at bits 0-3/4-7/8-11 with CurrentArcana at 12-15. Those two gauges are now cross-validated against upstream rather than just assumed. Build is warning-free again (the obsolete aliases were the only CS warnings) and 228 tests pass, built via build.ps1 so this is compiled against real 7.57 structs rather than a cached DLL. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7.57 added BeastmasterGauge to FCS and wired it into JobGaugeManager as a union member like every other gauge. BST shipped as a limited job in 7.56 with no gauge struct at all, which is why 9.2.2 delivered its identity and level/EXP but no resources - that gap is now closable. Mapped all nine fields (0x08-0x10): TPGauge, PetTPGauge, PetTPAtLastUse, ActiveBattlehorn, InstinctualComboState, CurrentAffinity, ChainCount, KinshipState and InstinctState. The last two are single bytes that FCS splits with [BitField] accessors, so the resolver surfaces both the raw byte and the decoded halves - Kinship into Battlehorn (bits 0-3) and Classification (bits 4-7), Instinct into Natural (bits 0-1) and Mastered (bits 2-3). Keeping the raw byte means a consumer can re-mask it if the game turns out to use spare bits. BeastmasterAffinity is mirrored into Sharlayan's enum namespace with a test asserting the values and member count still track FCS, so an upstream addition can't silently mislabel a pet's affinity. GetJobResources now resolves BST, and the comment in Reader.JobResource is corrected: BLU is the only job left with no gauge. Also addresses a gap the review of 074f42e raised. Nothing connected the resolver's hand-rolled bit masks to FCS' [BitField] declarations, so an upstream bit reshuffle would have changed what Sharlayan reports while every test stayed green - a field moving is invisible here by design, a field changing meaning is not. GaugeBitFieldContractTests reads those attributes by reflection and pins (name, start bit, width) for all five packed bytes across BLM, SMN, AST and BST. That is also what let me confirm by hand that the existing BLM and AST masks were already correct. Rolls in the three remaining dependabot PRs per request: NLog and NLog.Schema 6.2.0 -> 6.2.1 (the schema regenerates substantially) and ILRepack.Lib.MSBuild.Task 2.0.46 -> 2.0.48. Supersedes #133, #134 and #136. Built via build.ps1 against real 7.57 structs. 237 tests pass, no compiler warnings. Not verified in-game: the BST values derive from FCS offsets and the bit splits are pinned against upstream's declarations, but nothing has been read off a live Beastmaster. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review follow-up on c12e6d7. Three real gaps: Version was left at 9.2.4, the same as the previous commit, despite this one adding public API. A stable publish from main would have collided with the version already released. Now 9.2.5. The contract tests pinned FCS' [BitField] attributes but nothing tied them to the masks the resolver actually ran, so the comments claimed more than the tests delivered: changing (kinship >> 4) & 0xF to >> 3 would have left FCS untouched and every test green. The positions now live once in GaugeBitField and both the resolver and the tests read them, so the loop is closed in both directions - the positions are asserted against FCS, and GaugeBitField.Extract is asserted to perform the arithmetic those positions describe. Verified by renumbering KinshipClassification to bit 3: the FCS contract test and the arithmetic test both failed. That refactor also replaces the hand-written shifts for BLM Enochian, SMN Attunement and the AST cards, which were correct but unverifiable. Behaviour is unchanged. Beastmaster was missing from Build_AllJobs_HaveAtLeastOneNonZeroMappedField, the per-gauge completeness test every other job appears in - a mapper returning all zeros would have passed it. Also adds a BST eye-check line to the harness, printing the raw Kinship and Instinct bytes in hex and binary next to the decoded halves, so a mismatch against the in-game gauge points at the mask rather than the offset. This is the only way to verify the new gauge, which has still never been read off a live Beastmaster. Corrects the now-stale "all 21 gauges" comment to 22. 242 tests pass, no compiler warnings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Scope grew after the dependency bump landed, per your go-ahead on both counts. Beastmaster now has a real gauge. 7.57 added The three remaining dependabot PRs are folded in: NLog and NLog.Schema 6.2.1 (the schema regenerates substantially) and ILRepack 2.0.48. Supersedes #133, #134, #136. One gap worth calling out, because it was a case of my comments claiming more than my tests delivered. I'd added tests pinning FCS's Also: the harness now prints a BST eye-check line with the Kinship and Instinct bytes in hex and binary next to the decoded values, so a mismatch against the in-game gauge points at the mask rather than the offset. 242 tests, zero compiler warnings, CI green, built through Still unverified in-game — nothing here has been read off a live Beastmaster. The harness line is the check. |
Pulls FFXIVClientStructs forward to the 7.57 snapshot (
f0741b760→d6397a76b, 333 commits spanning 7.56 hotfix2 and 7.57, game build 2026.10.06) and Lumina to 7.7.1. Lumina.Excel stays at 7.5.0 — that's already the newest release. Supersedes dependabot #135.7.57 deprecated three members we read. In every case FCS left an
[Obsolete]alias at the identical offset, so the reads were still returning correct bytes — but the aliases will be deleted upstream eventually, and they were the only CS warnings in the build.ActionBarSlotNumberArray.ActionType→Typeat offset 0. Pure rename; the mapper only ever wanted the offset.BlackMageGauge.EnochianFlags→[BitField]EnochianActive/ParadoxActive/AstralSoulStacksover a privateBitFieldDbyte at0x0D.SummonerGauge.Attunement→[BitField]AttunementType/AttunementCountover a privateBitFieldEbyte at0x0E.The two gauge replacements are bit accessors over a private backing field, so those offsets now come from
FieldOffsetReaderstring-name lookups — the same mechanism already carrying_materia,_statusand_attributes.JobResourceResolverkeeps doing its own masking and the surfaced values are unchanged, so this is behaviour-neutral. Both new lookups get integrity tests per the DEPENDENCY.md contract, plus one pinningBitFieldD=0x0D,BitFieldE=0x0EandType=0so an upstream layout move can't quietly relocate a live memory read instead of failing a test.Reading those new
[BitField]declarations turned out to be worth more than the migration itself. They're an authoritative statement of the bit layout, and our hand-rolled masks match exactly: BLM Enochian at bit 0, Paradox at bit 1, AstralSoul at bits 2-4; AST Card1/2/3 at bits 0-3 / 4-7 / 8-11 with CurrentArcana at 12-15. Those two gauges are now cross-validated against upstream rather than taken on faith, which is a small dent in the open "verify the gauges in-game" pile.On the checks that compile and tests can't do for a range this size: no
[StaticAddress]signature and no declared size changed for any of the 21 scanner-key types, and every private field we resolve by string name survived intact. Of the structs we depend on,PlayerStatechanged but none of our fields moved,Utf8Stringonly altered a member-function signature we never call, andAstrologianGauge.Cardsis still ashortat0x08— FCS added bitfield accessors beside it and deprecated aCurrentCardshelper we don't use.Build is warning-free again and 228 tests pass, built through
build.ps1so it's compiled against real 7.57 structs rather than a cached DLL.Not covered here: no live in-game run. The gauge bit layouts are now verified against upstream's own declarations, which is stronger than before, but reading them off a running client is still the real check.
Three unrelated dependabot PRs are also open and untouched by this — #133/#134 (NLog 6.2.1) and #136 (ILRepack 2.0.48). Say the word and I'll fold those in too.
🤖 Generated with Claude Code