Skip to content

feat: Beastmaster gauge + FCS 7.57 + Lumina 7.7.1 (9.2.5) - #137

Merged
logicallysynced merged 3 commits into
mainfrom
chore/fcs-7.57-lumina-7.7.1
Oct 19, 2026
Merged

logicallysynced merged 3 commits into
mainfrom
chore/fcs-7.57-lumina-7.7.1

Conversation

@logicallysynced

Copy link
Copy Markdown
Contributor

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 → Type at offset 0. Pure rename; the mapper only ever wanted 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.

The two gauge replacements are bit accessors over a private backing field, so those offsets now come from FieldOffsetReader string-name lookups — the same mechanism already carrying _materia, _status and _attributes. JobResourceResolver keeps 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 pinning BitFieldD=0x0D, BitFieldE=0x0E and Type=0 so 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, 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.

Build is warning-free again and 228 tests pass, built through build.ps1 so 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

…(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>
logicallysynced and others added 2 commits October 19, 2026 15:24
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>
@logicallysynced logicallysynced changed the title chore: FCS 7.57 + Lumina 7.7.1, migrate off deprecated gauge aliases (9.2.4) feat: Beastmaster gauge + FCS 7.57 + Lumina 7.7.1 (9.2.5) Oct 19, 2026
@logicallysynced

Copy link
Copy Markdown
Contributor Author

Scope grew after the dependency bump landed, per your go-ahead on both counts.

Beastmaster now has a real gauge. 7.57 added BeastmasterGauge to FCS and wired it into JobGaugeManager as a union member like every other gauge — the thing that was missing when BST support went in at 9.2.2. All nine fields are mapped (0x08–0x10): TP, pet TP, pet TP at last use, active battlehorn, instinctual combo state, affinity, chain count, plus the Kinship and Instinct bytes. Those last two are single bytes FCS splits with [BitField], so the resolver surfaces the raw byte alongside the decoded halves — Kinship into Battlehorn (bits 0-3) and Classification (4-7), Instinct into Natural (0-1) and Mastered (2-3). Keeping the raw byte means you can re-mask if the game turns out to use spare bits. BeastmasterAffinity is mirrored with a test asserting the values and member count still track FCS, so an upstream addition can't silently mislabel a pet.

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 [BitField] declarations and named them "MatchResolverMasks" — but nothing connected them to the masks the resolver actually ran. Changing (kinship >> 4) & 0xF to >> 3 would have left FCS untouched and every test green. The bit positions now live once in GaugeBitField, read by both the resolver and the tests, so the loop closes in both directions: positions asserted against FCS, and the extraction asserted to do what those positions describe. I verified it by renumbering a field to bit 3 and confirming both layers fail. That refactor also absorbed the hand-written shifts for BLM Enochian, SMN Attunement and the AST cards — previously correct but unverifiable.

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 build.ps1 against real 7.57 structs. Version 9.2.5.

Still unverified in-game — nothing here has been read off a live Beastmaster. The harness line is the check.

@logicallysynced logicallysynced self-assigned this Oct 19, 2026
@logicallysynced
logicallysynced merged commit 80617fb into main Oct 19, 2026
3 checks passed
@logicallysynced
logicallysynced deleted the chore/fcs-7.57-lumina-7.7.1 branch October 19, 2026 04:38

This branch was successfully deployed

1 active deployment
production — 9db91b25 Deployed Oct 19, 2026 by logicallysynced via build-and-publish #85
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant