Files
OpenFUT/CLAUDE.md
T
funman300 06b6cdd043 docs: add openfut-launcher to CLAUDE.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 08:58:34 -07:00

7.8 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Repository Layout

This is a monorepo containing three independent Rust crates as git submodules:

Submodule Role
openfut-core/ Game-independent offline FUT backend (Axum + SQLite)
openfut-bridge/ FIFA 23 integration layer and reverse-engineering proxy
openfut-launcher/ Native GUI launcher (egui/eframe) for starting/stopping services and managing setup

Each submodule has its own Cargo.toml, src/, tests/, and target/. Commands must be run from within the submodule directory or with --manifest-path.

Common Commands

All commands run from within the relevant submodule directory.

openfut-core

# Run (dev mode; creates openfut.db in current dir)
cargo run

# Run (release)
cargo build --release && ./target/release/openfut-core

# Test (full integration suite against in-memory SQLite)
cargo test

# Run a single test
cargo test test_health_endpoint

# Lint
cargo clippy -- -D warnings

# Format
cargo fmt

openfut-bridge

# Run (Core must be running first on port 8080)
cargo run

# Test
cargo test

# Run a single test
cargo test test_bridge_health_endpoint

# Lint / format same as core
cargo clippy -- -D warnings
cargo fmt

openfut-launcher

# Run (native GUI window)
cargo run

# Build release
cargo build --release

# Build the hook DLL (Windows cross-compile, required for FIFA 23 integration)
cd openfut-hook
cargo build --release --target x86_64-pc-windows-gnu

Full-stack via setup.sh (Linux, from repo root)

./setup.sh quickstart    # build + start + hosts + NAT + TLS cert (first-time)
./setup.sh start         # start both services in background
./setup.sh stop          # stop both + clean up hosts/NAT
./setup.sh status        # show running state
./setup.sh logs          # tail both logs
./setup.sh watch-domains # find unknown EA domains hitting the bridge

Environment variables

Core (openfut-core/):

Variable Default
LISTEN_ADDR 127.0.0.1:8080
DATABASE_URL sqlite://openfut.db
DATA_DIR data

Bridge (openfut-bridge/):

Variable Default
BRIDGE_LISTEN_ADDR 127.0.0.1:8443
CORE_URL http://127.0.0.1:8080
CAPTURES_DIR captures
PLACEHOLDER_MODE true
TLS_ENABLED true

Architecture

FIFA 23 client
      │
      ▼
openfut-bridge  (port 8443)
      │
      ├── Known route  → openfut-core (port 8080)
      └── Unknown route → placeholder JSON + saved to captures/
                                │
                           SQLite database
                                │
                           data/ (JSON)

Core has zero knowledge of FIFA 23. It exposes a clean REST API and stores everything in SQLite. Bridge has zero game logic — it only translates FIFA 23's wire protocol into Core API calls.

openfut-core internals

Module map:

Path Purpose
src/main.rs Entry point: tracing, config, pool, migrations, seed, serve
src/lib.rs build_app(pool, data_dir) — used by integration tests
src/app.rs Router construction, AppState definition
src/config.rs Config struct from env vars
src/db.rs Pool init + migration runner
src/error.rs AppError enum + IntoResponse impl
src/models/ Pure data types (Serde + SQLx FromRow)
src/services/ Business logic; all DB access lives here
src/routes/ Axum handler functions; one file per domain
src/seed/ First-run starter pack grant
src/modding/ Generic JSON directory loader
data/ Moddable JSON content: cards, packs, objectives, SBCs, events
migrations/ SQLx SQL migrations (numbered sequentially)

AppState is cloned into every handler via State<AppState>. It holds the SQLite pool and Arc-wrapped in-memory registries loaded at startup from data/ (CardDb, Vec<PackDefinition>, etc.).

Layer discipline: routes extract state and call services; services own all DB access and data-loading logic; models are pure data.

Single-profile design: Only one profile per database. All services fetch "the active profile" by selecting the first row — this is intentional.

openfut-bridge internals

Module map:

Path Purpose
src/main.rs Entry point: config, TLS setup, serve
src/lib.rs Library root — re-exports modules
src/config.rs Config struct
src/proxy.rs catch_all_handler — the central routing brain
src/mapper.rs map_to_core(method, path) — FIFA 23 → Core path translation
src/capture.rs CapturedRequest struct + disk serialization
src/shaper.rs Response body transformation (Core → FIFA 23 wire format)
src/tls.rs Self-signed cert generation + rustls acceptor
src/routes/ Bridge admin endpoints (/_bridge/*)
captures/ JSON files written for every intercepted request

Request flow: all traffic hits catch_all_handlermapper::map_to_core() → if known, proxy to Core; if unknown, return placeholder JSON and write to captures/.

Adding a new FIFA 23 endpoint:

  1. Capture it in captures/ or /_bridge/unknown
  2. Document it in docs/endpoint-map.md
  3. Add a mapping in src/mapper.rs (EXACT array or prefix_routes())
  4. Implement the handler in Core if needed

openfut-launcher internals

A native desktop GUI (egui/eframe) that wraps Core and Bridge as managed child processes. It is the recommended way to run OpenFUT on a desktop — no terminal or setup.sh required.

Module map:

Path Purpose
src/main.rs Entry point: eframe window setup
src/app.rs LauncherApp — main UI state + four tabs (Dashboard, Logs, Setup, Config)
src/config.rs LauncherConfig — persisted settings (binary paths, ports, game dir)
src/process.rs ServiceHandle — spawns/kills Core and Bridge child processes, tracks status
src/logs.rs LogBuffer — ring buffer collecting stdout/stderr from child processes
src/setup.rs Hook DLL deployment, TLS cert installation into Wine/Proton cert store
openfut-hook/ Windows DLL (version.dll) injected into FIFA 23 via Proton to redirect EA hostnames to the local bridge (cross-compiled with x86_64-pc-windows-gnu)

Setup flow the launcher automates:

  1. Deploy openfut_hook.dll → FIFA 23 game dir as version.dll (intercepts network calls inside Wine/Proton, no /etc/hosts changes needed)
  2. Configure redirect IP written into openfut.cfg alongside the DLL
  3. Install bridge's self-signed TLS cert into the Wine/Proton cert store
  4. Start Core then Bridge (Dashboard tab shows live status)

Testing approach

Core uses a single tests/integration_test.rs that spins up a full Axum app against an in-memory SQLite database (build_app(pool, "data")). Tests use raw HTTP via tower::ServiceExt::oneshot. The data/ directory is always required at test time (tests run from openfut-core/).

Bridge tests are in tests/proxy_test.rs. They build a bridge app in placeholder_mode: true pointing at a dummy Core URL — no live Core required.

Modding / data files

All game content lives in openfut-core/data/. Files are JSON arrays loaded at startup. To add content, drop a JSON file into the right subdirectory and restart Core:

data/cards/       ← CardDefinition[]
data/packs/       ← PackDefinition[]
data/objectives/  ← ObjectiveDefinition[]
data/sbcs/        ← SbcDefinition[]
data/events/      ← EventDefinition[]
data/achievements/← AchievementDefinition[]

Version control

All three submodules (openfut-core, openfut-bridge, openfut-launcher) have their own independent git history. Use tea (Gitea CLI) for remote operations, not gh.