1daaebaefa
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
181 lines
6.1 KiB
Markdown
181 lines
6.1 KiB
Markdown
# 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`.
|