Skip to content

Document JobGauge padding, private cached fields, and two previously-missing fields#1756

Closed
RoseOfficial wants to merge 22 commits intoaers:mainfrom
RoseOfficial:gauge-struct-findings
Closed

Document JobGauge padding, private cached fields, and two previously-missing fields#1756
RoseOfficial wants to merge 22 commits intoaers:mainfrom
RoseOfficial:gauge-struct-findings

Conversation

@RoseOfficial
Copy link

Summary

Exhaustive vtable analysis of all 21 JobGauge subclasses. For each gauge, every overridden vtable function was inspected to determine which bytes are read or written. Bytes that are confirmed to never be accessed by any override are documented as padding. Additionally, several previously-undocumented fields have been named and typed.


Methodology

For each gauge:

  1. The full vtable listing was pulled to identify all overrides.
  2. Init, Tick, and ReceivePacketValues were checked to establish which bytes are packet-sourced vs. client-computed vs. untouched.
  3. Every other job-specific override was checked for gauge field accesses.
  4. ProcessDeferredReplaceAction was confirmed as a safe skip — it does not read gauge fields in any gauge.
  5. JobGaugeManager::ChangeGauge was analyzed to confirm exact zeroing ranges per gauge, providing additional corroboration of struct data sizes.

Changes

New public fields

NinjaGauge.NinjutsuActivated (0x0C, byte)
Previously noted as an unknown with a commented-out stub. Confirmed by analysis: Init zeroes this byte; ReceivePacketValues never writes it; it is set to 1 locally when a mudra sequence is in progress (confirmed via ProcessDeferredReplaceAction). This is a client-local flag, not server-sourced.

AstrologianGauge.ActiveSpell (0x0E, AstrologianSpell)
A previously undocumented server-sourced byte at 0x0E. ReceivePacketValues copies a QWORD covering 0x08–0x0F from the packet; vf6 (the action upgrade resolver) reads this byte and maps values 1/2/3 to action rows for Fall Malefic (29246), Aspected Benefic (29247), and Gravity II (29248). A new AstrologianSpell : byte enum has been added to JobGaugeEnums.cs for the three values.

New private cached fields

These fields are written only by Init (using sub_14080B560 for action sheet row lookups or direct computation) and are never exposed to consumers. They are documented as private to make struct layout explicit and to prevent future confusion about "unknown" gaps.

BlackMageGauge._betweenTheLinesRow (0x20, nint)
Action sheet row pointer for Between the Lines (Action 7419), cached by Init. Used by vf10 and vf11 for action upgrade and cost resolution.

ReaperGauge._regressRangeSquared (0x20, float)
Precomputed Action.Range² for Regress (Action 24403), cached by Init. Allows external code to perform squared-distance comparisons without a square root.

AstrologianGauge._retrogradeRangeSquared (0x20, float)
Same pattern as Reaper — precomputed Action.Range² for Retrograde (Action 41507), cached by Init.

RedMageGauge._enchantedRiposteRow through _enchantedRipostePvPRow (0x10–0x48, 8× nint)
Eight action sheet row pointers cached by Init for PrimaryCostValue lookups on the eight enchanted actions (7527, 7528, 7529, 7530, 16528, 29685, 29688, 41488). These explain the large gap between ManaStacks at 0x0A and the end of the struct.

Padding documentation

// offset: padding comments added to every gauge where gap bytes are confirmed. Each comment reflects the specific reason the bytes are padding:

Gauge Ranges Confirmation
ScholarGauge 0x0D–0x0F Within WORD copy range; never read
AstrologianGauge 0x0B–0x0D, 0x0F, 0x10–0x1F, 0x24–0x2F QWORD copy range ends at 0x0F; ChangeGauge zeroing; no vtable reads
SageGauge 0x0D–0x0F Within WORD copy range; never read
BlackMageGauge 0x0E–0x0F, 0x10–0x1F, 0x28–0x2F Outside DWORD+WORD copy ranges; ChangeGauge zeroing; no vtable reads
RedMageGauge 0x0B–0x0F Outside DWORD copy range; never read by any override
PictomancerGauge 0x09, 0x0D–0x0F Within DWORD/WORD copy ranges; never read
BardGauge 0x0A–0x0B Within bulk QWORD copy; never read
DancerGauge 0x0F Not packet-sourced; never read
DragoonGauge 0x0D–0x0F Within WORD copy range; never read
NinjaGauge 0x09, 0x0B, 0x0D–0x0F Within DWORD copy range; never read
SamuraiGauge 0x08–0x09, 0x0E–0x0F Within DWORD copy range at 0x08; never read at 0x0E
ReaperGauge 0x0E–0x1F, 0x24–0x2F ChangeGauge zeroing; no vtable reads
DarkKnightGauge 0x0E–0x0F, 0x12–0x17 Within bulk QWORD copy; ChangeGauge zeroing; no vtable reads
PaladinGauge 0x09, 0x0D–0x0F Within DWORD/WORD copy ranges; never read
WarriorGauge 0x09–0x0F Not packet-sourced; never read
GunbreakerGauge 0x09, 0x0D–0x0F Within DWORD/WORD copy ranges; never read

Secondary vtable moved from 0x1420F68D8 to 0x1420F6008 in current binary,
confirmed via constructor xrefs in FishingEventHandler_ctor and FishingEventHandler_Create.
Init() caches an action sheet row pointer for Between the Lines (Action 7419)
at gauge+0x20, following the same pattern as RedMageGauge's action row pointers.
All vtable overrides exhaustively checked; neither range is read by
any gauge function.
Only BeastGauge at 0x08 is packet-sourced; remaining bytes are
never touched by any vtable function.
0x09 falls within a DWORD packet copy, 0x0D within a WORD copy;
neither is read by any vtable function. 0x0E-0x0F not packet-sourced.
0x09 within DWORD packet copy, 0x0D within WORD copy; neither read
by any vtable function. 0x0E-0x0F not packet-sourced.
0x0D falls within a WORD packet copy but is never read by any vtable
function. 0x0E-0x0F not packet-sourced.
Packet copy only reaches 0x0E (StepIndex); 0x0F is never written or
read by any vtable function.
Within bulk QWORD copy range; never read by any vtable function.
0x08-0x09 within DWORD packet copy, never read. 0x0E-0x0F not
packet-sourced, never read by any vtable function.
0x0D within WORD packet copy, never read. 0x0E-0x0F not
packet-sourced, never read by any vtable function.
0x0D within WORD packet copy, never read. 0x0E-0x0F not
packet-sourced, never read by any vtable function.
0x09 within DWORD packet copy, 0x0D within WORD copy; neither read
by any vtable function. 0x0E-0x0F not packet-sourced.
0x09 and 0x0B within DWORD packet copy, never read. 0x0D-0x0F not
packet-sourced, never read by any vtable function.
Both ranges are zeroed by ChangeGauge and never written or read by
any vtable function. Confirmed via vtable analysis and ChangeGauge
zeroing pattern.
Add AstrologianSpell enum for the ActiveSpell field at 0x0E (values 1/2/3
correspond to Fall Malefic, Aspected Benefic, Gravity II; read by the action
upgrade resolver). Document all confirmed padding bytes. Mark struct complete.
All vtable overrides confirmed: Init writes DWORD to 0x08-0x0B and WORD to
0x0C-0x0D then caches one action row at 0x20. Tick only touches 0x08-0x09.
vf10 reads only 0x0A and 0x0D. vf11 reads no gauge fields. Nothing writes to
0x0E-0x0F, 0x10-0x1F, or 0x28-0x2F. Struct is fully mapped.
@RoseOfficial RoseOfficial deleted the gauge-struct-findings branch March 17, 2026 21:04
@RoseOfficial RoseOfficial restored the gauge-struct-findings branch March 18, 2026 04:14
@RoseOfficial RoseOfficial reopened this Mar 18, 2026
@wolfcomp
Copy link
Collaborator

So a few points:

  1. You didn't understand what I posted in Fix FishingEventHandler AtkEventInterface secondary vtable address #1755 when it comes how addresses are located.
  2. Introducing padding documentation just for the sake of completeness is wastefull time better spent elsewhere.
  3. Clearly you don't understand how to document things properly with the project as [FieldOffset(0x<offset>)] private nint _<name>; isn't something that is done in the project unless the field is of:
  • Padding in sequential structs
  • Enumerator structs
  1. If you went through the other functions you would try to document them with virutal functions but clearly not if you are using AI to do this.
  2. The summary reaks of AI more specifically Claudes LLM

With all that in mind come back to this when you stop using AI for most of your job.

@wolfcomp wolfcomp closed this Mar 18, 2026
@RoseOfficial
Copy link
Author

The two public fields (NinjutsuActivated and ActiveSpell) are real findings regardless of your other objections. Happy to resubmit those cleanly if you're open to it.

@RoseOfficial RoseOfficial deleted the gauge-struct-findings branch March 18, 2026 21:20
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.

2 participants