fix(networking): 0x2E group-box class caps are Warrior, Wizard, Rogue, Priest, Monk - #22
Merged
Merged
Conversation
…, Priest, Monk
GroupRequestPacket stage 4 read and wrote the five class-cap bytes as
Warrior, Wizard, Monk, Priest, Rogue. The retail client's order is
Warrior, Wizard, Rogue, Priest, Monk — positions 3 and 5 transposed, in
both directions.
Because reader and writer were swapped consistently, DALib round-tripped
with itself perfectly and every test passed. The bug only appears against
a real client or a correct server: DALib-as-server misreads a real
client's recruit box, and DALib-as-client (Brigid) publishes one a real
server misreads. The caps gate who may join an advertised group, so this
is behavioural, not cosmetic.
The file already contradicted itself — the layout comment and the
property declarations both had the correct order; only the prose
sentence, WriteBody and Parse were wrong. DALib's own S->C
GroupRecruitInfo has used the correct order all along.
Grounding (rung 1, Darkages.exe md5 3244dc0e68cd26f4fb1626da3673fda8 plus
that install's shipped UI assets):
- net_send_group_recruit_start @ 0x00513B30 is a bare five-iteration
loop over contiguous model bytes — it encodes no class ordering.
- ui_group_ad_dialog_read_model @ 0x005120A0 fills wire byte k from
dialog control 2k+7, a monotonic stride-2 walk over the per-row
control pairs. So wire byte k is screen row k, top to bottom.
- Class identity is in the artwork, not the code: setoa/_ngcdlg1.txt
places N_CLASS0 at y=206 and N_CLASS1 at y=233 (dy=+27); overlaying
those rects on _ngcdlg1.spf reads, top to bottom, Warrior, Wizard,
Rogue, Priest, Monk.
Corroborated at rung 2 by ewrogers/darkages-741-re @ f12a147
(docs/network/client/046-0x2e-group.md) and by Hybrasyl's GroupRecruit,
both of which already had it right.
Tests: the two byte-pinning cases carried the wrong expectation and its
false proof in a comment ("proving the order W, Wiz, Monk, Priest,
Rogue"). Corrected first and confirmed red against the unfixed code —
exactly two failures, differing at index 22, the third cap byte. Both
round-trip tests stayed green throughout and always would have; they are
symmetric in the defect and cannot pin this. Do not add a round-trip for
it. 947/947.
Provenance: the error entered from a distinct-value capture — fill the
recruit form 1/2/3/4/5, observe 01 02 03 04 05 on the wire. That
experiment cannot answer the question it was used to answer. Filling five
rows top-to-bottom with 1..5 yields that wire whichever class each row
belongs to; it establishes that the wire follows UI row order and is
silent on class identity. The names were assumed, and the assumption was
published as a correction to Chaos.Networking, which had it right.
Refs: HTOO-64
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes HTOO-64.
GroupRequestPacket(C→S 0x2E, stage 4 — the recruiting group box) read and wrote the five class-cap bytes as Warrior, Wizard, Monk, Priest, Rogue. The retail client's order is Warrior, Wizard, Rogue, Priest, Monk. Positions 3 and 5 were transposed, in both directions.Why no test caught it
Reader and writer were swapped consistently, so DALib round-tripped with itself perfectly. The two byte-pinning tests did assert wire order — but they asserted the wrong order, and their comments carried the false proof inline:
That experiment cannot answer the question it was used to answer. Filling the dialog's five rows top-to-bottom with 1,2,3,4,5 produces
01 02 03 04 05whichever class each row belongs to. It establishes that the wire follows UI row order and is silent on class identity. The names were assumed — and published as a correction toChaos.Networking, which had it right all along.Please don't pin this with a round-trip test. A round-trip agrees with itself under either ordering and passed throughout. The assertion has to be against absolute byte positions.
Impact
The caps gate who may join an advertised group, so this is behavioural, not cosmetic:
Grounding
Rung 1, from
Darkages.exe(md53244dc0e68cd26f4fb1626da3673fda8) plus that install's shipped UI assets. The executable alone cannot answer this — there are no class-name strings anywhere in the group module.net_send_group_recruit_start@0x00513B30— a bare five-iteration loop copying contiguous model bytes to the wire. Encodes no class ordering at all.ui_group_ad_dialog_read_model@0x005120A0— fills wire byte k from dialog control2k+7(verified against raw disassembly:0x0051211b: LEA EDX,[ECX + ECX*0x1 + 0x7]). A monotonic stride-2 walk over the per-row control pairs, so wire byte k is screen row k, top to bottom.Class identity comes from the artwork:
setoa/_ngcdlg1.txtplacesN_CLASS0at y=206 andN_CLASS1at y=233 (Δy=+27). Overlaying those rects on_ngcdlg1.spfreads, top to bottom:N_CLASS0_O/WN_CLASS1_ONote the client's own control naming independently binds
CLASS0→Warrior andCLASS1→Wizard, without reference to the disassembly.Corroborated at rung 2 by
ewrogers/darkages-741-re@f12a147(docs/network/client/046-0x2e-group.md), and by Hybrasyl'sGroupRecruit, both already correct.The file already contradicted itself
Only three places were wrong. The layout comment and the property declarations were correct and are unchanged:
[u8 MaxWarrior][u8 MaxWizard][u8 MaxRogue][u8 MaxPriest][u8 MaxMonk]— was already rightMaxWarrior…MaxMonk) — was already rightWriteBody,Parse— fixed hereDALib's S→C
GroupRecruitInfohas used Warrior, Wizard, Rogue, Priest, Monk all along, so the library was internally inconsistent and the C→S packet was the outlier.Verification
Corrected the two byte-pinning tests first and confirmed them red against the unfixed code — exactly 2 failed / 14 passed, differing at index 22 (the third cap byte). Both round-trip tests stayed green throughout, as expected.
After the fix: 947/947.
Downstream