# 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 ```bash # 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 ```bash # 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 ```bash # 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) ```bash ./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`. It holds the SQLite pool and Arc-wrapped in-memory registries loaded at startup from `data/` (`CardDb`, `Vec`, 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_handler` → `mapper::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`.