fifa17-recon: decouple contract suite from the Python implementation
test_fut_contract.py no longer imports ACCOUNT from fut_account. The expected persona comes from FUT_TEST_PERSONA_ID and the target from FUT_TEST_BASE, so the suite now imports nothing but stdlib and talks to a server at a URL. That is what lets these 380 checks certify ANY implementation of the reversed spec, a future Rust openfut-core included, without replaying the reverse engineering. The original reason for reading ACCOUNT still holds and is preserved in the comment: suite and server must not each hold a private copy of the constant, or the identity-consistency checks would only prove two copies matched. Also adds pileSizeClientData(0x227) behind FUT_PILESIZES (default off). A probe run with 16 uniquely-valued entries did NOT move the MY CLUB counter, so that member is eliminated as its source; the code is kept for the record and flagged off. Docs: OPENFUT_PROJECT_REPORT.md and OPENFUT_HANDOFF.md. The report now separates "built but untested" from "never requested by the client" -- the server log records User-Agent, and splitting real client traffic (ProtoHttp) from this project's own probes shows /season, /tournament, /champion, /match, /clubUser and /user/list are at ZERO client requests. /clubUser (0 client, 93 probe) and /user/list (0 client, 180 probe) are the starkest: work was done on both assuming the client wanted them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VUT92pz6RWKih9dSr8ZpxW
This commit is contained in:
@@ -499,6 +499,45 @@ SETTINGS = {"configs": []}
|
||||
_MI = os.environ.get("FUT_MASSINFO", "full")
|
||||
|
||||
|
||||
# ---- pileSizeClientData: the MY CLUB counter --------------------------------
|
||||
# LIVE EVIDENCE (2026-08-04): the user opened MY CLUB, the client fetched GET /club
|
||||
# and DISPLAYED all 99 players -- and the MY CLUB counter still read 0. So that
|
||||
# counter is NOT derived from the item list; it is a PILE SIZE, delivered
|
||||
# separately. massinfo's pileSizeClientData(0x227) is that member and we have never
|
||||
# sent it. Parser 0x18013adb0: {"entries":[{"key":<int>,"value":<int>}]} -- key and
|
||||
# value BOTH read with the int getter 0x1801c79d0, and the parser IS skip-safe.
|
||||
#
|
||||
# The pile-id enum is not recoverable from the strings (the "club"/"tradepile"
|
||||
# literals are just atom names in the alphabetical key table). So rather than guess:
|
||||
#
|
||||
# FUT_PILESIZES=probe -> emit one entry per candidate key 0..15 with a UNIQUE
|
||||
# recognisable value (100+key). Whatever number MY CLUB then displays names the
|
||||
# club pile's key: 103 means key 3. One launch identifies the enum.
|
||||
# FUT_PILESIZES=1 -> emit the REAL counts once PILE_KEY_CLUB below is known.
|
||||
#
|
||||
# Default OFF: this adds a member to boot-critical massinfo. It is a documented
|
||||
# member of that parser and carries only ints, so the risk is low -- but "low" is
|
||||
# what I said about displayGroup before it froze the store, so it ships behind a flag.
|
||||
_PILESIZES = os.environ.get("FUT_PILESIZES", "")
|
||||
PILE_KEY_CLUB = int(os.environ.get("FUT_PILE_KEY_CLUB", "-1")) # set once probed
|
||||
|
||||
|
||||
def pile_size_body():
|
||||
"""massinfo.pileSizeClientData -- see the note above."""
|
||||
if _PILESIZES == "probe":
|
||||
return {"entries": [{"key": k, "value": 100 + k} for k in range(16)]}
|
||||
counts = {
|
||||
"club": len(STORE.items()),
|
||||
"purchased": len(STORE.purchased()),
|
||||
"tradepile": len(STORE.listings()),
|
||||
}
|
||||
if PILE_KEY_CLUB >= 0:
|
||||
return {"entries": [{"key": PILE_KEY_CLUB, "value": counts["club"]}]}
|
||||
# No verified key yet -> announce the club count on every candidate key. Crude,
|
||||
# but every value is truthful, so no pile can be told a wrong number.
|
||||
return {"entries": [{"key": k, "value": counts["club"]} for k in range(16)]}
|
||||
|
||||
|
||||
def massinfo():
|
||||
if _MI == "empty":
|
||||
return {} # old known-hub-reaching body
|
||||
@@ -518,6 +557,10 @@ def massinfo():
|
||||
# body and adding a member to it is the change class behind the last two
|
||||
# live regressions. Instant fallback: FUT_MASSINFO=squad.
|
||||
body["clubUser"] = club_user_body()
|
||||
if _PILESIZES:
|
||||
# pileSizeClientData(0x227) -> parser 0x18013adb0, int-only, skip-safe.
|
||||
# This is what the MY CLUB counter reads (see pile_size_body above).
|
||||
body["pileSizeClientData"] = pile_size_body()
|
||||
return body
|
||||
|
||||
# ---- FUT item-definition serving (wf_e41070d8) -------------------------------
|
||||
|
||||
Reference in New Issue
Block a user