fifa17-python: commit working FUT backend deployment (client/server split)
Freeze the running offline FUT backend into version control as fifa17-recon/docker/fifa17-python/ - declarative and rebuildable from a fresh checkout: * OPENFUT_BIND / OPENFUT_ADVERTISE client/server split in the responders (lsx, blaze, roster, utas, pow) + entrypoint.sh; OPENFUT_ADVERTISE is required for remote mode (compose and entrypoint fail without it) * docker-compose.yml reproducing the frozen baseline container exactly (env, ports incl. the 8085->8080 POW-content remap, /state bind, restart) * .env.example / .env for site config - the LAN IP is never hardcoded in source * tools/ + data/ staged from openfut-fut-backend:python-baseline-2026-08-10, verified byte-identical to the running container at freeze time * client_arm.sh (the 105 client-side arming counterpart) * Dockerfile bakes /app/SHA256SUMS.txt so any image is self-identifying * docs/BASELINE-python-2026-08-10.md: frozen image/container/hash record, restore instructions and rebuild-equivalence procedure Secrets (redir key/cert, .env) and runtime state (docker/state) stay gitignored. The live container is untouched pending the .105 launcher audit.
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Did clubPlayers actually land in the client, or was it eaten as the envelope key?
|
||||
|
||||
Read-only. The argument cannot settle this; the client's own memory can.
|
||||
|
||||
Chain, from the comment at utas_server.py:1076 (established earlier by two independent
|
||||
agents and two reviewers, so this probe TESTS that chain rather than assuming it):
|
||||
R = <model> + 0x1fd70 (FUN_18011a810 is `lea rax,[rcx+0x1fd70]; ret`)
|
||||
clubPlayers -> R + 0x3c
|
||||
auctionCount -> R + 0x38
|
||||
The server logged "HUB: clubPlayers=205 auctionCount=0" for this session.
|
||||
|
||||
PREDICTIONS, stated before reading so this cannot be rationalised after the fact:
|
||||
* if R+0x3c reads 205, clubPlayers reached its arm. The flat two-key hub body is
|
||||
fine and the envelope worry does not apply to this root.
|
||||
* if R+0x3c reads 0 while R+0x38 reads 0 too, the result is ambiguous, because
|
||||
auctionCount is legitimately 0 this session. Say so rather than claiming a result.
|
||||
* if R+0x3c reads 0 and some other plausible field is populated, clubPlayers was
|
||||
eaten as the first key/value pair and the MY CLUB tile is showing a wrong number.
|
||||
"""
|
||||
import os
|
||||
import struct
|
||||
|
||||
pid = None
|
||||
for d in os.listdir('/proc'):
|
||||
if d.isdigit():
|
||||
try:
|
||||
if open('/proc/%s/comm' % d).read().strip() == 'FIFA17.exe':
|
||||
pid = int(d)
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
if not pid:
|
||||
raise SystemExit("FIFA17.exe not running")
|
||||
|
||||
base = None
|
||||
for ln in open('/proc/%d/maps' % pid):
|
||||
if 'CardsDLL' in ln:
|
||||
base = int(ln.split('-')[0], 16)
|
||||
if not base:
|
||||
raise SystemExit("CardsDLL not mapped: the client has not reached Ultimate Team")
|
||||
slide = base - 0x180000000
|
||||
print("pid %d cardsdll %#x slide %#x" % (pid, base, slide))
|
||||
|
||||
fd = os.open('/proc/%d/mem' % pid, os.O_RDONLY)
|
||||
|
||||
|
||||
def rd(va, n):
|
||||
return os.pread(fd, n, va)
|
||||
|
||||
|
||||
# Prove the slide before trusting any address derived from it.
|
||||
pe = open('/mnt/games/FIFA 17/CardsDLL_Win64_retail.dll', 'rb').read()
|
||||
off = 0x180180D00 - 0x180000000 - 0x1000 + 0x400
|
||||
ok = pe[off:off + 32] == rd(0x180180D00 + slide, 32)
|
||||
print("slide control (FNV prologue): %s" % ("MATCH" if ok else "MISMATCH -- STOP"))
|
||||
if not ok:
|
||||
raise SystemExit(1)
|
||||
|
||||
model = struct.unpack('<Q', rd(0x1802E6398 + slide, 8))[0]
|
||||
print("model singleton %#x" % model)
|
||||
R = model + 0x1FD70
|
||||
club, auction = struct.unpack('<i', rd(R + 0x3C, 4))[0], struct.unpack('<i', rd(R + 0x38, 4))[0]
|
||||
print("\n R = model+0x1fd70 = %#x" % R)
|
||||
print(" R+0x3c clubPlayers = %d (server sent 205)" % club)
|
||||
print(" R+0x38 auctionCount = %d (server sent 0)" % auction)
|
||||
|
||||
print("\nVERDICT:")
|
||||
if club == 205:
|
||||
print(" clubPlayers REACHED its arm. The flat hub body parses correctly and the")
|
||||
print(" envelope concern does not apply to FutGetHubData.")
|
||||
elif club == 0:
|
||||
print(" clubPlayers is 0. Either it was eaten as the first key/value pair, or the")
|
||||
print(" hub has not been loaded this session. Check the tile in game before")
|
||||
print(" concluding: auctionCount is legitimately 0, so it cannot break the tie.")
|
||||
else:
|
||||
print(" clubPlayers = %d, which is neither 205 nor 0. The chain above is wrong"
|
||||
" somewhere." % club)
|
||||
os.close(fd)
|
||||
Reference in New Issue
Block a user