chore: add openfut-launcher submodule, CLAUDE.md, and setup.sh
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
# 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 two 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 |
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
### 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<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_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
|
||||
|
||||
## 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
|
||||
|
||||
Both `openfut-core` and `openfut-bridge` are git submodules with their own independent history. Use `tea` (Gitea CLI) for remote operations, not `gh`.
|
||||
Reference in New Issue
Block a user