Files
OpenFUT/fifa17-recon/docker/fifa17-python/tools/strip_dead_cards.py
T
root 70a64e3709 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.
2026-08-10 23:54:04 +00:00

89 lines
2.9 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Remove club cards whose playerid does not exist in FIFA 17's roster.
These are leftovers from the invented-id pool that data/roster.json replaced. The
client resolves them through its own players table, misses, and stamps the generic
card: rating 50, teamid 1933, nation 14, position 2, all attributes 1, blank name.
They are the blanks on screen.
SAFETY
* backs the profile up first, and prints the backup path;
* refuses to touch any card referenced by a saved squad, so a squad slot can
never end up pointing at a deleted item;
* removes from the club pile only, and never from `purchased` in the same pass:
a card present in BOTH piles is the known fatal desync, and the way to avoid
it is to keep every card in exactly one place, which deleting from one pile
preserves.
* dry run unless --fire is passed.
"""
import argparse
import json
import shutil
import sys
import time
sys.path.insert(0, "/home/alex/Documents/OpenFUT/fifa17-recon/tools")
from fut_store import STORE # noqa: E402
import fut_cards # noqa: E402
ap = argparse.ArgumentParser()
ap.add_argument("--fire", action="store_true", help="actually write")
a = ap.parse_args()
REAL = {p[0] for p in fut_cards.POOL}
p = STORE.load()
items = p.get("items", [])
# Every item id any saved squad refers to. Squad shapes have varied, so walk the
# structure generically rather than assuming one layout.
squad_ids = set()
def walk(o):
if isinstance(o, dict):
for k, v in o.items():
if k in ("itemId", "id") and isinstance(v, int):
squad_ids.add(v)
walk(v)
elif isinstance(o, list):
for v in o:
walk(v)
for key in ("squad", "squads", "squadList"):
walk(p.get(key))
dead = [c for c in items
if c.get("assetId") not in REAL and c.get("id") not in squad_ids]
protected = [c for c in items
if c.get("assetId") not in REAL and c.get("id") in squad_ids]
print("club=%d roster=%d" % (len(items), len(REAL)))
print("dead cards to remove: %d" % len(dead))
for c in dead:
print(" id=%-11s asset=%-8s rating=%s" % (c.get("id"), c.get("assetId"), c.get("rating")))
if protected:
print("KEPT (referenced by a squad, removing them would break a slot): %d" % len(protected))
for c in protected:
print(" id=%-11s asset=%s" % (c.get("id"), c.get("assetId")))
if not a.fire:
print("\ndry run. Re-run with --fire to write.")
sys.exit(0)
if not dead:
print("\nnothing to do.")
sys.exit(0)
bak = STORE.path + ".bak-stripdead-%d" % int(time.time())
shutil.copy(STORE.path, bak)
print("\nbackup: %s" % bak)
drop = {c.get("id") for c in dead}
p["items"] = [c for c in items if c.get("id") not in drop]
STORE._save()
q = STORE.load()
print("club: %d -> %d purchased=%d coins=%s"
% (len(items), len(q.get("items", [])), len(q.get("purchased", [])), q.get("coins")))