#!/usr/bin/env bash # ============================================================================ # OpenFUT FIFA-17 FUT backend — in-CONTAINER orchestrator. # # Runs the 5 network responders that the game dials. Unlike the host-based # openfut-fut.sh, this does NO host arming (no pkexec / iptables / /etc/hosts / # ptrace) — those are client-side concerns handled on the game machine (105). # autopatch.py is NOT run here: it patches the FIFA17.exe process memory and must # run on the box the game runs on. # # Address behaviour is driven by three env vars (see each responder): # OPENFUT_BIND bind address for every listener (container: 0.0.0.0) # OPENFUT_ADVERTISE IP address handed out for Blaze, UTAS, telemetry, and QoS # OPENFUT_ROSTER_HOST certificate DNS host:port handed out for roster HTTPS # ============================================================================ set -uo pipefail cd "$(dirname "$(readlink -f "$0")")/tools" BIND="${OPENFUT_BIND:-0.0.0.0}" ADV="${OPENFUT_ADVERTISE:?OPENFUT_ADVERTISE must be set to the server LAN IP (e.g. 203.0.113.10)}" ROSTER_HOST="${OPENFUT_ROSTER_HOST:-winter15.gosredirector.ea.com:8081}" export OPENFUT_BIND="$BIND" export OPENFUT_ADVERTISE="$ADV" export OPENFUT_ROSTER_HOST="$ROSTER_HOST" # POW keys advertised by blaze must also point at the server, not loopback. export POW_HOST="${POW_HOST:-$ADV:8094}" export POW_CONTENT_HOST="${POW_CONTENT_HOST:-$ADV:8080}" export POW_ADDR="${POW_ADDR:-$BIND:8094}" export POW_CONTENT_ADDR="${POW_CONTENT_ADDR:-$BIND:8080}" echo "[openfut] bind=$BIND advertise=$ADV roster=$ROSTER_HOST" # FIFA17's roster verifier compares only dNSName SAN entries. It ignores a valid # iPAddress SAN when the advertised URL contains an IP literal, so certificate # regeneration cannot fix that URL. Keep the certificate stable and fail startup # if the configured roster hostname is not already one of its DNS identities. CERT=redir_cert.pem ROSTER_NAME="${ROSTER_HOST%%:*}" if ! openssl x509 -in "$CERT" -noout -checkhost "$ROSTER_NAME" >/dev/null 2>&1; then echo "[openfut] FATAL: TLS cert does not cover roster hostname $ROSTER_NAME" >&2 exit 1 fi echo "[openfut] roster certificate matches $ROSTER_NAME; fingerprint: $(openssl x509 -in "$CERT" -noout -fingerprint -sha256)" # name script extra-env declare -a SERVERS=( "lsx|lsx_responder_v2.py|OPENFUT_LSX_EVENT_COUNT=100000" "blaze|blaze_responder_v3b.py|-" "roster|roster_server.py|-" "utas|utas_server.py|FUT_TRADING=1 FUT_PILESIZES=1 FUT_TRADEABLE=1 FUT_DISCARD_TABLE=1 FUT_DISCARD_SEND=1" "pow|pow_server.py|-" ) # ── Component selection ────────────────────────────────────────────────────── # OPENFUT_SERVERS picks which Python responders run (space- or comma-separated). # This container is the SERVER side (.120). It serves ONLY components that have # NOT been migrated to a Rust host — as each moves to Rust (which runs OUTSIDE # Docker during migration), drop its name so the two never serve the same role. # blaze Blaze redirector + main + nucleus (bundled) :42127 :42130 :42131 # roster FUT roster-update XML :8081 # utas FUT/UTAS RS4 API :8099 # (the Rust utas-host currently reverse-proxies its non-/club routes # back here, so keep this enabled until UTAS is fully migrated) # pow POW / EASFC :8094 (+ content :8080) # lsx Origin LSX bootstrap :4216 # CLIENT-SIDE — LSX runs on the game machine (.105) with autopatch, # NOT on the server. Excluded by default; enable ONLY for an # all-on-one-box dev setup where client and server share a host. OPENFUT_SERVERS="${OPENFUT_SERVERS:-blaze roster utas pow}" want=" ${OPENFUT_SERVERS//,/ } " known=" lsx blaze roster utas pow " for w in $want; do case "$known" in *" $w "*) ;; *) echo "[openfut] unknown component '$w' in OPENFUT_SERVERS (valid: lsx blaze roster utas pow)" >&2; exit 2 ;; esac done echo "[openfut] servers=$OPENFUT_SERVERS" pids=() names=() for entry in "${SERVERS[@]}"; do IFS='|' read -r name script env <<<"$entry" case "$want" in *" $name "*) ;; *) echo "[openfut] skipping $name (not in OPENFUT_SERVERS)"; continue ;; esac envprefix=""; [ "$env" != "-" ] && envprefix="env $env" echo "[openfut] starting $name ($script)" # shellcheck disable=SC2086 $envprefix python3 -u "$script" & pids+=($!) names+=("$name") done if [ "${#pids[@]}" -eq 0 ]; then echo "[openfut] OPENFUT_SERVERS selected no components; nothing to run" >&2 exit 2 fi # Propagate SIGTERM/SIGINT to children so `docker stop` is clean. term() { echo "[openfut] shutting down…" for p in "${pids[@]}"; do kill "$p" 2>/dev/null || true; done wait exit 0 } trap term TERM INT # If ANY responder dies, take the whole container down so the failure is visible # (they all bind ports the game needs — a partial stack is a broken stack). while true; do for i in "${!pids[@]}"; do if ! kill -0 "${pids[$i]}" 2>/dev/null; then echo "[openfut] responder ${names[$i]} (pid ${pids[$i]}) exited - bringing container down" term fi done sleep 2 done