fifa17-recon: club stats -- per-NATION buckets, and the sub-type sums
LIVE 2026-08-04: the ENGLAND tile read 0 while drilling into it showed Premier League
17. Same bug as before, one level up: the LEAGUE buckets were keyed and the NATION
buckets were not.
The eight-row MY CLUB panel is FUN_180094ce0 (not FUN_180043b90, which is a different
provider using a different string family), and it computes:
PLAYERS_EMPLOYED = +0x7f8(nationId, 4) + (nationId, 3) + (nationId, 2)
STAFF_EMPLOYED = +0x800 over 0xb, 0xc, 0xd, 0xe, 0xf
TROPHIES_WON = +0x800 over 0x33 .. 0x38
STADIA_OWNED = +0x800(0x14) BALLS_EARNED = +0x800(0x1e)
Two consequences:
1. The no-id modes (year / consumables / club / newcards), which is what the client
fires on entering MY CLUB, now carry PER-NATION buckets keyed by nation id. The
three screens are consistent at last:
no id -> nation buckets (the tab strip and the eight-row panel)
country/<id> -> league buckets (the leagues in that nation)
league/<id> -> team buckets (the teams in that league)
2. STAFF_EMPLOYED and TROPHIES_WON are SUMS OF SUB-TYPES. Sending staff(0xa) or
trophies(0x32) alone can never move those rows, whatever their value. The eleven
sub-type rows are now emitted: staffManager/HeadCoach/GKCoach/Physio/FitnessCoach
and trophiesOffline/Online/FeaturedOffline/FeaturedOnline/SeasonOffline. All zero
today because the club owns no staff and has won nothing, but the mapping is what
matters when it does.
All eleven new type strings verified against docs/fut_atoms.tsv, 0 mismatches.
Live: /club/stats/year now returns 117 rows across 16 nation buckets, England
(nation 14) summing to 11 players. 439 + 61 checks green, zero tracebacks.
This is the third correction to this one endpoint today. The pattern in all three is
identical and worth stating once more: the parser accepts anything, and only the
CONSUMER tells you which bucket and which type ids it reads. Every time I reasoned
about the body instead of reading the reader, I shipped a wrong one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUT92pz6RWKih9dSr8ZpxW
This commit is contained in:
@@ -1112,7 +1112,13 @@ def _club_stat_context(kind):
|
||||
whether contextId carries further meaning; nothing read so far gives it one.
|
||||
"""
|
||||
players = [i for i in STORE.items() if i.get("itemType") == "player"]
|
||||
field = {"country": "leagueId", "league": "teamid"}.get(kind)
|
||||
# kind -> which id the SCREEN's rows are keyed by.
|
||||
# "" the MY CLUB tab strip itself: its nation tiles and the eight-row
|
||||
# panel FUN_180094ce0, which computes PLAYERS_EMPLOYED as
|
||||
# +0x7f8(nationId, 4) + (nationId, 3) + (nationId, 2). Per NATION.
|
||||
# country the leagues inside a nation (case 3, keyed by LEAGUE_ID)
|
||||
# league the teams inside a league (case 4, keyed by TEAM_ID)
|
||||
field = {"": "nation", "country": "leagueId", "league": "teamid"}.get(kind)
|
||||
if not field:
|
||||
return [], 0
|
||||
ctxs = sorted({i.get(field) for i in players if i.get(field) is not None})
|
||||
@@ -1149,11 +1155,26 @@ def _club_stat_set():
|
||||
("rarePlayers", len(rare)),
|
||||
# Honest zeros: this club holds no non-player items of any kind.
|
||||
("staff", 0),
|
||||
("stadia", 0),
|
||||
("balls", 0),
|
||||
("stadia", 0), # 0x14, read directly by STADIA_OWNED
|
||||
("balls", 0), # 0x1e, read directly by BALLS_EARNED
|
||||
("kits", 0),
|
||||
("badges", 0),
|
||||
("trophies", 0),
|
||||
# The eight-row panel FUN_180094ce0 does NOT read staff(0xa) or trophies(0x32).
|
||||
# It SUMS the sub-types: STAFF_EMPLOYED = +0x800 over 0xb..0xf, and
|
||||
# TROPHIES_WON = +0x800 over 0x33..0x38. Sending the parent ids alone can
|
||||
# never move those two rows. All zero today because the club owns no staff and
|
||||
# has won nothing, but the mapping is what matters when it does.
|
||||
("staffManager", 0), # 0xb
|
||||
("staffHeadCoach", 0), # 0xc
|
||||
("staffGKCoach", 0), # 0xd
|
||||
("staffPhysio", 0), # 0xe
|
||||
("staffFitnessCoach", 0), # 0xf
|
||||
("trophiesOffline", 0), # 0x33
|
||||
("trophiesOnline", 0), # 0x34
|
||||
("trophiesFeaturedOffline", 0), # 0x35
|
||||
("trophiesFeaturedOnline", 0), # 0x36
|
||||
("trophiesSeasonOffline", 0), # 0x37
|
||||
]
|
||||
# All four keys in every element -- see note 3 above.
|
||||
return [{"contextId": 1, "contextValue": 0, "type": t, "typeValue": int(v)}
|
||||
@@ -1184,8 +1205,15 @@ def club_stats_route(h):
|
||||
% (mode, len(stats), stats[0]["typeValue"], n,
|
||||
{"country": "league", "league": "team"}.get(parts[0], parts[0])))
|
||||
return 200, {"stat": stats}
|
||||
log(" CLUBSTATS: %s -> %d stat rows (players=%d)"
|
||||
% (mode or "(none)", len(stats), stats[0]["typeValue"]))
|
||||
# No id in the URL: this is the MY CLUB tab strip (year / consumables / club /
|
||||
# newcards). Its nation tiles and its eight-row panel read PER-NATION buckets.
|
||||
# LIVE 2026-08-04: the ENGLAND tile read 0 while its own Premier League row read
|
||||
# 17, which is this bug exactly one level up -- the leagues were keyed and the
|
||||
# nations were not.
|
||||
ctx_rows, n = _club_stat_context("")
|
||||
stats = stats + ctx_rows
|
||||
log(" CLUBSTATS: %s -> %d rows (global players=%d, %d nation buckets)"
|
||||
% (mode or "(none)", len(stats), stats[0]["typeValue"], n))
|
||||
return 200, {"stat": stats}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user