fifa17-recon: answer the CONSUMABLES panel with consumable counts, not player counts
The tab was empty because we answered the wrong question. The client asks GET club/stats/consumables 41 times a session; we replied with the PLAYER stat set (players 205, playersGold 189 ...), which that panel does not read. Confirmed on screen: seven categories present and selectable, every one reading 0. Now appends 14 consumables* rows counted from the SHELF. The shelf, not STORE.items(): the consumables we serve are a synthetic overlay never granted into the save, so counting the store gives fourteen zeros, which on screen is byte-identical to failure and would have made the experiment unreadable. Counts match the independently derived expectation exactly: 126 total, 21 healing, 7 player contracts, 21 player training, 3 player fitness, 20 position, 21 GK training, 6 manager contracts, 19 playstyle. Safe by construction: the vocabulary is an ATOM switch (FUN_18012fd40, 40 arms, default return 0), so an unrecognised name is inert rather than fatal, and the rows are APPENDED -- the player, nation and league rows that drive the working screens are untouched. Zero rows unless FUT_CONSUMABLES is armed. Default ON because answering the consumables panel with player counts is wrong by inspection rather than a judgement call. FUT_CONSUM_STATS=0 reverts. 439 + 414 checks green. The open question this sets up: whether a non-zero count makes the client request an item list at all. If it does, the log names the route and /consumables/%s is settled for free. If the numbers move and no request follows, the panel renders from counts alone and the 126-item shelf was never needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VUT92pz6RWKih9dSr8ZpxW
This commit is contained in:
@@ -1163,6 +1163,48 @@ def _club_stat_context(kind):
|
||||
|
||||
|
||||
|
||||
# FUT_CONSUM_STATS: answer the CONSUMABLES panel with CONSUMABLE counts.
|
||||
#
|
||||
# LIVE 2026-08-05, and this is the whole reason the tab was empty. The client asks
|
||||
# GET club/stats/consumables 41 times a session and we answered it with the PLAYER
|
||||
# stat set (players 205, playersGold 189 ...). The panel reads a different set of
|
||||
# names entirely, so it was told about footballers when it asked about contracts.
|
||||
# The player screenshot is unambiguous: seven categories -- Training, Contracts,
|
||||
# Fitness, Healing, Chemistry Style, Manager League, Position Modifier -- every one
|
||||
# reading 0, on a tab that is present and selectable.
|
||||
#
|
||||
# The vocabulary is not a string table: FUN_18012fd40 looks the `type` string up in
|
||||
# the ATOM table and switches on 40 atom ids, default `return 0`. An unrecognised
|
||||
# name is therefore INERT, not fatal, which is what makes appending these rows safe.
|
||||
#
|
||||
# COUNT THE SHELF, NOT THE STORE. The consumables we serve are a synthetic overlay
|
||||
# and are never granted into the save, so STORE.items() holds none of them and
|
||||
# counting it yields fourteen zeros -- which looks exactly like failure on screen and
|
||||
# would make the experiment unreadable.
|
||||
#
|
||||
# Default ON: answering the consumables panel with player counts is wrong by
|
||||
# inspection, not a judgement call. Set FUT_CONSUM_STATS=0 to go back.
|
||||
CONSUM_STATS = os.environ.get("FUT_CONSUM_STATS", "1") == "1"
|
||||
|
||||
|
||||
def _consumable_stat_rows():
|
||||
"""[(stat name, count)] for the consumables panel, counted from the shelf."""
|
||||
if not (CONSUM_STATS and CONSUMABLES):
|
||||
return []
|
||||
try:
|
||||
import fut_consumables
|
||||
import fut_club_stats
|
||||
except Exception as e: # never break the panel over this
|
||||
log(" CLUBSTATS: consumable rows unavailable (%s)" % e)
|
||||
return []
|
||||
shelf = fut_consumables.starter_consumables(fut_consumables.CONSUMABLE_ID_BASE)
|
||||
g = fut_club_stats.global_counts(shelf)
|
||||
rows = [(fut_club_stats.VOCAB[sid], v) for sid, v in sorted(g.items())
|
||||
if sid >= 0x3C and sid in fut_club_stats.VOCAB]
|
||||
log(" CLUBSTATS: %d consumable row(s) from a shelf of %d" % (len(rows), len(shelf)))
|
||||
return rows
|
||||
|
||||
|
||||
def _club_stat_set():
|
||||
"""The complete global stat set, computed from what the club actually holds."""
|
||||
items = STORE.items()
|
||||
@@ -1211,6 +1253,7 @@ def _club_stat_set():
|
||||
("trophiesFeaturedOnline", 0), # 0x36
|
||||
("trophiesSeasonOffline", 0), # 0x37
|
||||
]
|
||||
counts += _consumable_stat_rows()
|
||||
# All four keys in every element -- see note 3 above.
|
||||
return [{"contextId": 1, "contextValue": 0, "type": t, "typeValue": int(v)}
|
||||
for t, v in counts]
|
||||
|
||||
Reference in New Issue
Block a user