fix(fifa17): open Seasons/Draft via /settings config gate

RE-confirmed root cause: the FUT client's settings applier FUN_18011dc50 is
the sole writer of the IS_* UI gate bytes (byte = field==1). The Rust host
served GET /ut/game/fifa17/settings as {"configs":[]}, so the applier never
ran and Single-Player Seasons/Draft refused to open while issuing ZERO server
requests (friendlySeasonsEnabled -> settings slot 0x16 -> model byte 0x1fd3a
= IS_FRIENDLY_SEASON_ENABLED).

settings_body() is now env-gated: default (OPENFUT_FUT_SETTINGS unset/off)
keeps the empty baseline; OPENFUT_FUT_SETTINGS=gates returns the enable list.
The applier writes ALL gate bytes, so the working store flags (storeEnabled,
coinEnabled, cardPackStoreEnabled, pointsPackStoreEnabled, tradingEnabled,
+ _JP variants) are re-asserted alongside the Seasons/Draft enables to avoid
clearing the live Store. Instantly revertible by restarting without the env.
This commit is contained in:
funman300
2026-08-19 20:12:06 +00:00
parent e8d1c1ddac
commit 3bb4814760
+41 -2
View File
@@ -24,9 +24,48 @@ pub fn accountinfo_body() -> Value {
json!({})
}
/// `GET …/settings` — production oracle returns an empty config list.
/// `GET …/settings` — the FUT client applies this `configs` array via the applier
/// `FUN_18011dc50`, the ONLY writer of the `IS_*` UI gate bytes (each
/// `byte = (field == 1)`). A flag never sent is a gate never opened — which is why
/// Single-Player Seasons/Draft refuse to open while issuing ZERO server requests
/// (`friendlySeasonsEnabled` → slot `[0x16]` → model byte `0x1fd3a`
/// `IS_FRIENDLY_SEASON_ENABLED`; RE-confirmed via pyghidra on CardsDLL).
///
/// Default (`OPENFUT_FUT_SETTINGS` unset/`off`) is the historical empty baseline.
/// `OPENFUT_FUT_SETTINGS=gates` populates the array. SAFETY: populating it is what
/// makes the applier RUN, and it then writes EVERY gate byte, so the already-live
/// store flags MUST be re-asserted here or enabling Seasons would clear the working
/// Store (those reach the client today via the Blaze FUT_RS4_CONFIG store, not
/// here). Mirrors the audited list in `utas_server.py` `_SETTINGS_KEEP`/`_GATES`.
pub fn settings_body() -> Value {
json!({ "configs": [] })
if std::env::var("OPENFUT_FUT_SETTINGS").as_deref() != Ok("gates") {
return json!({ "configs": [] });
}
// Already-live store flags re-asserted (pinned to their working state).
const KEEP: &[&str] = &[
"storeEnabled",
"storeEnabled_JP",
"coinEnabled",
"coinEnabled_JP",
"cardPackStoreEnabled",
"cardPackStoreEnabled_JP",
"pointsPackStoreEnabled",
"tradingEnabled",
];
// Mode gates that nothing has ever populated (the point of the change).
const GATES: &[&str] = &[
"friendlySeasonsEnabled",
"enableDraftMode",
"enableSinglePlayerDraftMode",
"enableOfflineDraftMode",
"tournamentQuitEnabled",
];
let configs: Vec<Value> = KEEP
.iter()
.chain(GATES.iter())
.map(|k| json!({ "type": k, "value": 1 }))
.collect();
json!({ "configs": configs })
}
/// `GET …/leaderboards/options` — production oracle (FUT_MODES off) returns an