#!/usr/bin/env python3 """Ensure the deployed launcher starts FIFA with the hook's WINEDLLOVERRIDES. The hook ships as a `version.dll` proxy in the game directory, and Proton prefers a local DLL over its own builtin ONLY when WINEDLLOVERRIDES names it. Launcher builds before openfut-launcher c542415 apply `game_profile.env` but never that override, so the hook silently never loads -- and with no hook there is no port rewrite, the openfut.cfg the launcher just wrote is inert, and the client reaches EA's real Blaze ports (production, via /etc/hosts) while looking like a healthy configured launch. This writes the override into `game_profile.env`, which the deployed launcher DOES apply, so it works without rebuilding. It is forward-compatible with the fixed launcher: hook_dll_overrides() defers to a profile that already pins `version=`, so the value set here simply wins and nothing conflicts. python3 scripts/client-hook-override.py show python3 scripts/client-hook-override.py apply python3 scripts/client-hook-override.py revert Safety: the prior value (including its absence) is recorded to a sidecar before the first mutation, so `revert` restores the real previous state rather than guessing; every operation re-reads and prints the result; and it refuses to edit while the launcher is running, because the launcher holds its config in memory and would write the stale value straight back over ours. """ import json import subprocess import sys CLIENT = "alex@10.10.0.105" CFG = "/home/alex/.config/openfut-launcher/config.json" SIDECAR = "/home/alex/.config/openfut-launcher/config.json.openfut-prev-dlloverrides" KEY = "WINEDLLOVERRIDES" HOOK = "version=n,b" ABSENT = "" def ssh(script): r = subprocess.run(["ssh", "-o", "BatchMode=yes", CLIENT, script], capture_output=True, text=True, timeout=60) if r.returncode != 0: raise SystemExit(f"ssh failed ({r.returncode}): {r.stderr.strip()}") return r.stdout def launcher_running(): """comm is truncated by the kernel to 15 chars ("openfut-launche").""" out = ssh("for d in /proc/[0-9]*; do c=$(cat $d/comm 2>/dev/null); " "case \"$c\" in openfut-launche*) echo ${d#/proc/};; esac; done || true") return bool(out.strip()) def fifa_running(): out = ssh("for d in /proc/[0-9]*; do " "[ \"$(cat $d/comm 2>/dev/null)\" = FIFA17.exe ] && echo ${d#/proc/}; " "done || true") return bool(out.strip()) def load(): return json.loads(ssh(f'cat "{CFG}"')) def env_of(d): return d.setdefault("game_profile", {}).setdefault("env", {}) def save(d): body = json.dumps(d, indent=2, sort_keys=True) ssh(f'cat > "{CFG}" <<\'EOF\'\n{body}\nEOF') def show(): d = load() cur = env_of(d).get(KEY, ABSENT) print(f"--- {CFG}") print(f" game_profile.env[{KEY}] = {cur}") print(f" hook active on launch = {'YES' if 'version=' in cur else 'NO'}") side = ssh(f'cat "{SIDECAR}" 2>/dev/null || true').strip() print(f" recorded previous value = {side or '(none recorded yet)'}") print(f" launcher running = {'YES' if launcher_running() else 'no'}") return cur def guard(): if fifa_running(): raise SystemExit("REFUSING: a FIFA client is running; exit it first.") if launcher_running(): raise SystemExit( "REFUSING: openfut-launcher is running. It holds its config in memory and\n" "would write the old value back over ours. Quit the launcher first.") def apply_override(): guard() d = load() env = env_of(d) prior = env.get(KEY, ABSENT) if not ssh(f'cat "{SIDECAR}" 2>/dev/null || true').strip(): ssh(f'cat > "{SIDECAR}" <<\'EOF\'\n{prior}\nEOF') print(f"recorded previous value to {SIDECAR}: {prior}") # Preserve an unrelated override rather than clobbering it, and never double up. if prior != ABSENT and "version=" in prior: print(f"already pins version= ({prior}); leaving it alone") return show() env[KEY] = HOOK if prior == ABSENT else f"{prior};{HOOK}" save(d) after = env_of(load()).get(KEY, ABSENT) if "version=" not in after: raise SystemExit(f"VERIFY FAILED: {KEY} is {after!r}") return show() def revert(): guard() side = ssh(f'cat "{SIDECAR}" 2>/dev/null || true').strip() if not side: raise SystemExit(f"no recorded previous value at {SIDECAR}; refusing to guess") d = load() env = env_of(d) if side == ABSENT: env.pop(KEY, None) else: env[KEY] = side save(d) return show() def main(): if len(sys.argv) != 2 or sys.argv[1] not in ("show", "apply", "revert"): print(__doc__) return 2 {"show": show, "apply": apply_override, "revert": revert}[sys.argv[1]]() return 0 if __name__ == "__main__": sys.exit(main())