70a64e3709
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.
71 lines
2.7 KiB
Python
71 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Launcher-to-server active-account selection for the single-player stack."""
|
|
import json
|
|
import os
|
|
|
|
from fut_account import ACCOUNT
|
|
from fut_store import STORE, profile_path_for
|
|
|
|
|
|
def _existing_identity(persona_id):
|
|
path = profile_path_for(persona_id)
|
|
try:
|
|
with open(path) as f:
|
|
profile = json.load(f)
|
|
except (OSError, ValueError):
|
|
return {}
|
|
if not isinstance(profile, dict):
|
|
return {}
|
|
return {
|
|
"club_name": profile.get("clubName"),
|
|
"club_abbr": profile.get("clubAbbr"),
|
|
"established": profile.get("established"),
|
|
"pow_level": profile.get("powLevel"),
|
|
"pow_exp": profile.get("powExp"),
|
|
"pow_exp_max": profile.get("powExpMax"),
|
|
"pow_funds": profile.get("powFunds"),
|
|
"pow_funds_cap": profile.get("powFundsCap"),
|
|
}
|
|
|
|
|
|
def activate(payload):
|
|
"""Select/create one persistent profile and publish it to all responders."""
|
|
if not isinstance(payload, dict):
|
|
raise ValueError("account payload must be an object")
|
|
try:
|
|
persona_id = int(payload.get("personaId"))
|
|
except (TypeError, ValueError):
|
|
raise ValueError("personaId must be a positive integer") from None
|
|
persona_name = payload.get("personaName")
|
|
if persona_id <= 0 or not isinstance(persona_name, str) or not persona_name.strip():
|
|
raise ValueError("personaId must be positive and personaName must not be empty")
|
|
|
|
values = _existing_identity(persona_id)
|
|
values.update(persona_id=persona_id, persona_name=persona_name.strip())
|
|
for wire, field in (("clubName", "club_name"), ("clubAbbr", "club_abbr"),
|
|
("established", "established"), ("squadName", "squad_name"),
|
|
("level", "pow_level"), ("experience", "pow_exp"),
|
|
("experienceMax", "pow_exp_max"), ("accountFunds", "pow_funds"),
|
|
("accountFundsCap", "pow_funds_cap")):
|
|
if payload.get(wire) not in (None, ""):
|
|
values[field] = payload[wire]
|
|
|
|
ACCOUNT.replace(values)
|
|
ACCOUNT.set_online_profile()
|
|
ACCOUNT.save()
|
|
profile = STORE.select_account(persona_id)
|
|
STORE.ensure_security_question()
|
|
return {
|
|
"personaId": ACCOUNT.persona_id,
|
|
"personaName": ACCOUNT.persona_name,
|
|
"clubName": ACCOUNT.club_name,
|
|
"clubAbbr": ACCOUNT.club_abbr,
|
|
"level": ACCOUNT.pow_level,
|
|
"experience": ACCOUNT.pow_exp,
|
|
"experienceMax": ACCOUNT.pow_exp_max,
|
|
"accountFunds": ACCOUNT.pow_funds,
|
|
"accountFundsCap": ACCOUNT.pow_funds_cap,
|
|
"profilePath": os.path.relpath(STORE.path, os.path.dirname(ACCOUNT.path)),
|
|
"coins": profile.get("coins", 0),
|
|
"unopenedPacks": len(profile.get("unopenedPackIds", [])),
|
|
} |