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):