1e2b073e04
LIVE 2026-08-04: the draft-state array fix WORKED. The screen rendered instead of
hanging and the client advanced to the entry-fee screen, then crashed on the next
call, which we had never implemented:
GET /squad/mode/draft/state?mode=ONLINE -> our array body screen RENDERED
GET /user/credits -> 7200
GET /store/purchasegroup/all -> the entry-fee screen
POST /purchase/mode/0/draft {"currency":"COINS","usePreOrder":0}
-> {} UNMAPPED, then the crash
Advancing the failure to the next unimplemented call is what a correct fix looks like.
ENVELOPE AMBIGUITY RESOLVED, and ENDPOINT_MAP's note about it is wrong. Two structures
reference RS4:FutPurchaseDraftModeServerResponse:
0x18014c260 vtable 0x180224ef8, factory 0x18014c090. 3188 chars, OBJECT root
(prologue tests != 10 = END_OBJECT), 1 skip handler, exactly the seven
scalar ints. THIS IS THE RESPONSE PARSER.
0x180150310 vtable 0x1802262f0, factory 0x180150260. 1836 chars, ARRAY root
(loops until 0xd), ZERO skip handlers -- and NOT a response root at
all. It parses ENTRANCE CRITERIA: each element's name is strcmp'd
against the literals "COINS", "POINTS", "DRAFT_TOKEN" and stored at
+0x28/+0x2c/+0x30. It shares the class-name string because it is the
fee sub-object, not an "alternate/summary envelope" as documented.
THE CRASH ITSELF DISCRIMINATED, which is worth keeping as a technique. An object-root
parser handed {} parses benignly and leaves defaults; an array-root parser handed {}
desyncs and HANGS, which is exactly what draft/state did before the fix. We observed a
CRASH, not a hang, so the object-root parser is what ran and the failure is downstream
of an empty-but-valid parse. Consistent with 0x18014c260, inconsistent with the other.
Coins are NOT deducted. The client posts the price in the URL and it sent 0, because we
omit entranceCriteria from draft/state so there is no fee to charge. Charging a guessed
amount would be inventing an economy rule.
ALSO FIXED, before it reached the game: the route table hands handlers the compiled
PATTERN, not a match object (the dispatcher calls fn(rx, self)), so calling .group() on
the first argument raised AttributeError and killed the connection outright. That is
strictly worse than the {} it was replacing. Caught by verifying the response actually
changed after the restart rather than assuming the route worked.
Default ON: the behaviour it replaces is a confirmed crash, so no working state is at
risk. FUT_DRAFT_PURCHASE=0 reverts.
392 + 61 checks green, zero tracebacks on a clean boot.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUT92pz6RWKih9dSr8ZpxW