From 122b7b94d2cec5fd6d17a88d7501bb4ca4066aa3 Mon Sep 17 00:00:00 2001 From: funman300 Date: Wed, 5 Aug 2026 11:34:01 -0700 Subject: [PATCH] fifa17-recon: consumables are STACK records, not bare items The route was right and the body was wrong. We served bare items, the client took the response and inserted NOTHING (the live card map held only the 11 squad players), and the screen stayed empty with no error logged anywhere. A 200 with a well-formed body that the consumer silently discards is the worst failure shape there is, and it is the third time this project has hit it. FutConsumablesSearchServerResponse resolved from the RS4 literal 0x1802222f8: factory 0x180130a10, vtable 0x180222200, deserializer +0x08 = 0x180130d10 (6873 chars). It takes itemData(0x16b) at the root like the club list, but its ELEMENT is not an item. It is a five-atom wrapper and exactly one of the five carries the item: 0xbc count int 0xd7 discardValue int 0x16a item -> FUN_18013fe00, the item parser itself 0x287 resourceId int 0x362 untradeableCount int Everything else goes to the value-skip handler, which is precisely why a bare item was accepted and did nothing. It also explains the UI: FUT draws consumables as one stack with a quantity, not as N cards, and the wrapper is that quantity. Identical consumables are now collapsed by resourceId and counted. 439 + 414 checks green. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01VUT92pz6RWKih9dSr8ZpxW --- fifa17-recon/tools/utas_server.py | 41 ++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/fifa17-recon/tools/utas_server.py b/fifa17-recon/tools/utas_server.py index ce95e79..ad081a1 100755 --- a/fifa17-recon/tools/utas_server.py +++ b/fifa17-recon/tools/utas_server.py @@ -1295,6 +1295,43 @@ CLUB_CONSUMABLE_CATS = { } +def _consumable_stacks(items): + """Wrap consumables as the STACK records this response class actually reads. + + RESOLVED LIVE 2026-08-05 the hard way: we served bare items here, the client + took the response and inserted NOTHING (the card map held only the 11 squad + players), and the screen stayed empty with no error anywhere. + + FutConsumablesSearchServerResponse (RS4 literal 0x1802222f8, factory + 0x180130a10, vtable 0x180222200, deserializer +0x08 = 0x180130d10, 6873 chars) + takes itemData(0x16b) at the root like the club list, but its ELEMENT is not an + item. It is a five-atom wrapper and only ONE of those five carries the item: + + 0xbc count int + 0xd7 discardValue int + 0x16a item -> FUN_18013fe00, the item parser itself + 0x287 resourceId int + 0x362 untradeableCount int + + Everything else falls to the value-skip handler, which is why a bare item was + accepted and silently did nothing. That is also why FUT draws consumables as one + stack with a quantity rather than N separate cards. + + Identical consumables are therefore COLLAPSED by resourceId and counted. + """ + stacks = {} + for it in items: + rid = it.get("resourceId") + s = stacks.get(rid) + if s is None: + s = stacks[rid] = {"count": 0, "discardValue": 0, "item": it, + "resourceId": rid, "untradeableCount": 0} + s["count"] += 1 + if it.get("untradeable"): + s["untradeableCount"] += 1 + return list(stacks.values()) + + def club_consumables_route(h): seg = h.path.split("/club/consumables", 1)[-1].split("?")[0].strip("/").lower() if not CONSUMABLES: @@ -1315,7 +1352,9 @@ def club_consumables_route(h): items = [i for i in shelf if fut_consumables.BY_SUBTYPE[i["cardsubtypeid"]]["category"] in cats] log(" CONSUMABLES: %s -> %d item(s)" % (seg or "(none)", len(items))) - return 200, {"itemData": items} + stacks = _consumable_stacks(items) + log(" CONSUMABLES: %d item(s) collapsed into %d stack(s)" % (len(items), len(stacks))) + return 200, {"itemData": stacks} def club_stats_route(h):