From c5424158b95ccd0c9e7b5ad4ac6582846eb7e010 Mon Sep 17 00:00:00 2001 From: funman300 Date: Tue, 18 Aug 2026 02:57:29 +0000 Subject: [PATCH] fix: launcher-spawned FIFA never loaded the hook, so its Blaze ports were inert The hook ships as a `version.dll` proxy in the game directory, and Proton prefers a local DLL over its builtin ONLY when WINEDLLOVERRIDES names it -- exactly what setup::STEAM_LAUNCH_OPTIONS documents ("version=n,b"). Steam users get it from their launch options. When the launcher spawns the runner itself it applied profile.env and WINEPREFIX but never the override, so the hook silently did not load. The failure mode is worse than "hook missing", which is why it went unnoticed: with no hook there is no port rewrite, so the openfut.cfg the launcher writes two seconds earlier is inert and the game falls back to EA's real Blaze ports, where /etc/hosts (10.10.0.120 easw.easports.com) quietly routes it to whatever answers there. The launch looks completely healthy -- correct log lines, game boots, FUT loads -- while talking to a different server than the one configured. Production only works here by accident of the hosts file. Observed end-to-end: openfut.cfg written 19:50:00 with blaze 42327/42330, FIFA started 19:50:03, and the process was ESTAB to 10.10.0.120:42130 -- production. /proc//maps showed the mapped version.dll was Proton's own (compatibilitytools.d/UMU-Proton-10.0-4/files/lib/wine/x86_64-windows/version.dll), not the game-dir hook, and no hook log existed for the Proton prefix at all. launch() now always sets WINEDLLOVERRIDES, appending to any profile value and deferring to a profile that pins `version=` itself, so an operator disabling the hijack on purpose is not silently overruled. Four tests cover absent, unrelated, explicit and blank-string cases. Also worth noting for future debugging: ~/.wine/drive_c/openfut_hook.log is stale and belongs to a non-Proton prefix. It is not evidence about a umu/Proton launch, and reading it as current is how this was nearly misdiagnosed. --- src/game_launch.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/game_launch.rs b/src/game_launch.rs index 7a6d752..bb53eb7 100644 --- a/src/game_launch.rs +++ b/src/game_launch.rs @@ -24,6 +24,7 @@ //! falls back to it, so an existing working setup cannot be broken by upgrading. use parking_lot::Mutex; +use std::collections::BTreeMap; use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; use std::process::{Child, Command, Stdio}; @@ -67,6 +68,7 @@ pub fn launch( for (k, v) in &profile.env { cmd.env(k, v); } + cmd.env("WINEDLLOVERRIDES", hook_dll_overrides(&profile.env)); if !profile.wine_prefix.trim().is_empty() { cmd.env("WINEPREFIX", &profile.wine_prefix); } @@ -93,6 +95,30 @@ pub fn launch( Ok(()) } +/// The `WINEDLLOVERRIDES` value the game must be started with. +/// +/// The hook ships as a `version.dll` proxy inside the game directory, and Proton +/// prefers a local DLL over its own builtin ONLY when `WINEDLLOVERRIDES` names it +/// (see `setup::STEAM_LAUNCH_OPTIONS`). Steam users get that from their launch +/// options; when the launcher spawns the runner itself, nothing else supplies it. +/// +/// Without it the failure is silent and badly misleading: the hook never loads, so +/// the `openfut.cfg` the launcher just wrote is inert, the game ignores the +/// configured Blaze ports, and `/etc/hosts` quietly routes it to whatever answers +/// on EA's real ports. It looks like a working launch against the configured +/// server while actually talking to a different one. +/// +/// A profile that already pins `version=` wins: an operator overriding the hijack +/// deliberately must not be silently overruled. +fn hook_dll_overrides(env: &BTreeMap) -> String { + const HOOK: &str = "version=n,b"; + match env.get("WINEDLLOVERRIDES").map(|v| v.trim()) { + Some(existing) if existing.contains("version=") => existing.to_string(), + Some(existing) if !existing.is_empty() => format!("{existing};{HOOK}"), + _ => HOOK.to_string(), + } +} + /// Create the Wine prefix's `dosdevices` entries the profile asks for. /// /// Equivalent to `mkdir -p $WINEPREFIX/dosdevices && ln -sfn `: @@ -463,4 +489,32 @@ mod tests { let err = launch(&profile, &log(), || {}).unwrap_err().to_string(); assert!(err.contains("game_dir does not exist"), "{err}"); } + + #[test] + fn a_profile_without_overrides_still_gets_the_hook_hijack() { + // The regression this guards: FIFA launched from the launcher ignored the + // configured Blaze ports entirely, because Proton loaded its own builtin + // version.dll and the hook proxy never ran. The launch looked healthy. + assert_eq!(hook_dll_overrides(&BTreeMap::new()), "version=n,b"); + } + + #[test] + fn unrelated_overrides_are_preserved_and_appended_to() { + let env = BTreeMap::from([("WINEDLLOVERRIDES".to_string(), "d3d11=n".to_string())]); + assert_eq!(hook_dll_overrides(&env), "d3d11=n;version=n,b"); + } + + #[test] + fn an_explicit_version_override_is_never_overruled() { + // An operator disabling the hijack on purpose must win, otherwise the + // setting is a lie. + let env = BTreeMap::from([("WINEDLLOVERRIDES".to_string(), "version=b".to_string())]); + assert_eq!(hook_dll_overrides(&env), "version=b"); + } + + #[test] + fn a_blank_override_is_treated_as_absent_rather_than_appended_to() { + let env = BTreeMap::from([("WINEDLLOVERRIDES".to_string(), " ".to_string())]); + assert_eq!(hook_dll_overrides(&env), "version=n,b"); + } }