From 11b7991d81bc41d9bc8d8edd77e10c990d482623 Mon Sep 17 00:00:00 2001 From: funman300 Date: Sun, 23 Aug 2026 17:38:32 +0000 Subject: [PATCH] feat(fifa17): gated kits-only club?type=equippables, to test the locked-kit cause A live kit_trace run on the retail client showed the kit clone driver only ever sees PLAYERS - about 19 records, all cardtype 1 / itemState 1 / +0x60 = 1 - and never a kit, with no KIT_DBCLONE line at all. So the locked-kit failure is UPSTREAM of the item+0x60 == 4 gate that this arm's comment blamed. In the same session the client asked for ?type=kit six times, which we answer and which feeds the items browser, and ?type=equippables twice, which we answer empty. If the equippable view is what populates the collection FUN_1800d73d0 scans to build the active-kit triple, an empty answer explains precisely why the triple stays zero and the engine falls back to its own catalogue kit. The arm now selects ContentKind::Kit behind OPENFUT_FIFA17_EQUIPPABLES=1, so it answers with TWO items rather than the thirty across five families that crashed the client on 2026-08-05. Default OFF: that crash is real and reproducible, and the narrow body is a hypothesis under test rather than an established safe response. Reverting is an env change plus a restart, no rebuild. --- openfut-utas-host/src/lib.rs | 42 ++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/openfut-utas-host/src/lib.rs b/openfut-utas-host/src/lib.rs index 532591f..c72278b 100644 --- a/openfut-utas-host/src/lib.rs +++ b/openfut-utas-host/src/lib.rs @@ -2316,12 +2316,30 @@ fn club_type_filter(token: Option<&str>) -> Option { label: "equippables", // The combined customisation view, and the one response that has ever // crashed this client: 30 items across five families at once - // (2026-08-05). A single-family answer here is not available either — - // the view is by definition multi-family — and the kit swap it feeds - // needs `item+0x60 == 4`, which a server can never produce (it can - // only ever produce 1 = club and 6 = purchased). So this stays empty - // until that is a client-side change, not a wire one. - selector: ClubSelector::Withheld("multi_family_crash_2026_08_05"), + // (2026-08-05). + // + // 2026-08-24: this is also the prime suspect for the LOCKED kit. A + // live `kit_trace` run showed the kit clone driver only ever sees + // PLAYERS (~19 records, cardtype 1 / itemState 1 / `+0x60` 1) and + // never a kit, with no `KIT_DBCLONE` at all — so the failure is + // upstream of the `item+0x60 == 4` gate. In the same session the + // client asked for `?type=kit` (6x, which we answer and which feeds + // the items BROWSER) and `?type=equippables` (2x, which we answer + // empty). If the equippable view is what populates the collection + // `FUN_1800d73d0` scans for the active-kit triple, an empty answer + // is exactly why the triple stays zero and the engine falls back to + // its own catalogue kit. + // + // So the arm is now selectable behind `OPENFUT_FIFA17_EQUIPPABLES=1` + // and answers with KITS ONLY — two items, not the thirty across five + // families that crashed the client. Default OFF: the crash is real + // and reproducible, and this narrower body is a hypothesis under + // test, not an established safe response. + selector: if equippables_enabled() { + ClubSelector::Kind(ContentKind::Kit) + } else { + ClubSelector::Withheld("multi_family_crash_2026_08_05") + }, }, Some("leaguelogos") => ClubTypeFilter { label: "leaguelogos", @@ -5571,6 +5589,18 @@ fn discard_table_enabled() -> bool { *ENABLED.get_or_init(|| std::env::var("OPENFUT_FIFA17_DISCARD_TABLE").as_deref() == Ok("1")) } +/// Answer `club?type=equippables` with a KITS-ONLY body. +/// +/// Default OFF. The multi-family form of this response crashed the client on +/// 2026-08-05, so serving it at all is an experiment: the narrow two-kit body is +/// a hypothesis about why the pre-match kit selector reports every kit locked, +/// not a response we have established as safe. Flip the env var off to revert +/// with a restart and no rebuild. +fn equippables_enabled() -> bool { + static ENABLED: std::sync::OnceLock = std::sync::OnceLock::new(); + *ENABLED.get_or_init(|| std::env::var("OPENFUT_FIFA17_EQUIPPABLES").as_deref() == Ok("1")) +} + /// A JSON response with an explicit status. fn json_status(status: u16, v: &Value) -> WireResponse { let body = serde_json::to_vec(v).unwrap_or_default();