# OpenFUT — Direction Document
*The pivot: FUT lives in the app; FIFA 23 is the match renderer.*
*Supersedes the Blaze-backend approach as the primary plan. Last updated 2026-06-30.*

---

## 1. Goal (revised)

Deliver an **intuitive way to play a FUT-style experience with FIFA 23**, where:

- The entire **FUT experience** — cards, squads, packs, SBCs, coins, chemistry,
  progression — lives in a **custom app** (web UI or desktop) built on the
  already-complete OpenFUT Core economy backend.
- **FIFA 23 is demoted to a match renderer.** Its only job is to play a
  single-player match using the squad the app built. No FUT mode, no online, no
  Blaze, no EA servers.

This deliberately drops in-game FUT cards/UI (they live in the app) in exchange
for a project that **converges** instead of being gated behind months of
backend reverse-engineering.

### Why this replaces the backend plan

The status review confirmed the backend route (faking EA's online stack) is
blocked at an upstream in-process EbisuSDK gate, with Blaze/Fire2 unconfirmed
beyond it — realistically 3–6 months of expert RE that may not converge. The
app-centric route sidesteps **every** wall in that review by never making FIFA's
own FUT mode run.

---

## 2. Base mode: Career, not Kick-Off

**Career mode is the base.** Reasons:

- FLE's live-editing API (`EditDBTableField`, Freeze Lineup) is **confirmed to
  work in career mode** and explicitly does NOT work in FUT/online modes.
- Career already provides the FUT-shaped scaffolding we'd otherwise fake:
  persistent club, a fixture schedule, recorded results, progression across a
  season.
- **Match results are written into the career DB**, making result capture a DB
  read rather than a fragile live-memory grab.

**Kick-Off is the prototype sandbox.** Use it first to prove squad injection
works with nothing to corrupt (no save to break), then move the real loop onto
career. Run the foundational injection test in BOTH.

---

## 3. Core architecture: the bidirectional FLE bridge

The backbone is a **bidirectional channel between the app and a resident FLE Lua
script running inside the game.** Everything else is messages over this channel.

```
  Custom App (FUT experience)
      │  squad push  ──────────────►   ┌─────────────────────────────┐
      │                                │  Resident FLE Lua script     │
      │  ◄──────────  game state       │  (inside FIFA 23, career)    │
      │  ◄──────────  match result     │  - reads game state          │
      └────────────────────────────►   │  - applies squad live        │
         (file-watch or local socket)  │  - reads results from DB     │
                                       └─────────────────────────────┘
                                                  │
                                       FIFA 23 plays the match
```

Three message types over the bridge:

1. **App → Game: squad push.** The app's chosen XI + stats applied LIVE via
   `EditDBTableField`, replicating whatever DB write FLE's "Freeze Lineup"
   feature performs (see `docs/foundational-xi-injection-test.md` — the exact
   field(s) are found by diffing, not assumed). No restart, no
   file-copy-reload. (File-load remains a fallback.)

2. **Game → App: game state.** The resident script polls the game's current
   screen/menu state and reports "safe to apply" vs "not safe", driving a smart
   Apply button in the app (see §5).

3. **Game → App: match result.** After full-time, the script reads the result
   from the career DB and pushes score/scorers to the app, which awards
   coins/progression. (Manual entry is the baseline fallback.)

The bridge transport can be a watched file the in-game Lua polls, or a local
socket — decided in build (see §7). Either way the *game keeps running*; a file,
if used, is just the message channel, not a reload.

---

## 4. Tiered mod scope

Build in tiers matched to risk. The core tier is all the SAME kind of DB write,
so it lands together once squad injection works.

### Tier 1 — Core writes (ride the same live DB-edit mechanism)
- **Squad / custom XI** — the load-bearing primitive (Freeze Lineup's
  underlying write, replicated via script — see §6).
- **Player stats as "cards"** — card tiers, in-form versions, SBC upgrades all
  expressed as written attribute values.
- **Chemistry as stat adjustment** — app computes FUT chemistry, applies it as
  small stat bumps when writing players in (no in-game chem UI; that's in the app).
- **Appearance / identity** — kits, names, team assignment, so the club looks
  like your club on the pitch.
- **Formation / tactics** — squad structure carries the app's build onto the pitch.

### Tier 2 — Confirm-then-add
- **Match difficulty per game** — to drive a Squad-Battles-style "this opponent is
  World Class". Settable in-game trivially; programmatic drive needs confirming.
- **Match rules / modifiers** (half length, etc.) — for app-defined challenges.

### Tier 3 — Result capture (manual baseline + automated stretch)
- **Manual:** user enters the score in the app after the match. Zero RE, ships
  first.
- **Automated:** resident script reads the career-DB result (or, for Kick-Off,
  reads the in-match score from memory at full-time — precedent exists: the
  CM cheat table's `export_season_stats.lua` already reads goals/cards from
  memory via known offsets). Push to app → auto-award progression.

### Out of scope (stays in the app, by design)
- In-game FUT cards, FUT menus, pack-opening animation, chemistry board, FUT
  presentation. The app is where it looks/feels like FUT.

---

## 5. The smart Apply button (state-aware)

Live DB edits only "stick" in safe menu states (the in-game "Edit Player" screen,
for example, overwrites edits). So the bridge reads game state and gates applying:

- Resident Lua script polls the game's current-screen value (a few Hz),
  classifies **safe / not safe**, reports to the app.
- App's **Apply button is enabled only when the script confirms a safe state**
  (squad hub, main menu); greyed otherwise.
- **Safe-by-default-OFF:** unknown state → button greyed → never a risky write.
  Expand the known-safe list incrementally as states are confirmed.
- **v2 (more seamless):** instead of greying, the app always lets you click and
  the script **queues** the apply, executing the moment a safe state is entered,
  then confirms back. Greying is v1; queue-and-apply is v2.

`IsInCM()` is a confirmed state-read; the specific screen-state address + the
value→screen mapping is one-time reconnaissance (same technique as result reading).

---

## 6. What's confirmed vs what needs validating

**Confirmed (from FLE's own Lua API docs/wiki, checked 2026-06-30):**
- FLE live-edits the running career DB without restart, via `EditDBTableField`
  (real signature: `EditDBTableField(cell)` where `cell = row["fieldname"]`
  with `.value` mutated first — not the table/index/field/value form an
  earlier draft of this doc assumed).
- FLE reads game state via `IsInCM()`.
- A `MEMORY` Lua class exists (`ReadInt`/`WriteInt`/`ReadMultilevelPointer`/
  etc.) for arbitrary process memory — confirms the result-reading fallback
  in §4 Tier 3 is a real, documented capability, not just cheat-table analogy.
- `GetPlayersStats()` is a documented function returning per-player
  goals/assists/cards/etc. — a better confirmed path for match-result capture
  than raw memory offsets.
- **Freeze Lineup** (Formation Editor → arrange XI → tick "Freeze Lineup" →
  `Data → Save`) is FLE's actual documented mechanism for forcing a starting
  XI in career mode. This **replaces** "selection bias" below.
- OpenFUT Core (economy) is complete and tested.

**Walked back — not actually confirmed:**
- "Selection bias forces specific players into the starting XI" — no such
  field appears anywhere in FLE's documented Lua API or its own example
  scripts. This was an unverified assumption carried over from general FIFA
  modding precedent (other titles), not anything checked against FLE/FIFA 23.
  See `docs/foundational-xi-injection-test.md` for the corrected plan, which
  uses Freeze Lineup instead.

**Needs validating (the foundational tests — see §7):**
- Whether Freeze Lineup actually holds into a played match (FLE's wiki
  documents the feature but not a live-match test of it).
- What DB table/field Freeze Lineup's `Data → Save` actually writes — it's
  GUI-only and undocumented at that level; finding it is part of the
  foundational test.
- Whether that write can be replicated by a script (`EditDBTableField`) well
  enough to drive it from an EXTERNAL trigger, not just the Formation Editor
  UI — required for the app↔game bridge.
- The app↔game bridge transport (file-watch vs socket) works cleanly under the
  run setup.
- The screen-state address + safe/not-safe classification (FLE's `Events`
  API page exists in the wiki index but its content is currently empty/
  undocumented — this is more open than previously assumed).
- Result read-back from the career DB after a match.

**Standing caveat:** the whole stack rides on **EAAC staying neutralized**
(FLE's fake-launcher bypass). If a game update re-enables it, hooks fail. Keep
game updates off; confirm neutralized state each session.

---

## 7. Build order / next steps

Each is a bounded, verifiable step. Do them in order; later ones depend on
earlier answers.

1. **FOUNDATIONAL TEST — live custom XI in career.** Confirm Freeze Lineup
   holds into a played match, reverse-engineer the DB write it makes, then
   replicate that write from a script so it can be triggered externally
   instead of through the Formation Editor UI. See
   `docs/foundational-xi-injection-test.md` for the full procedure. *Done =
   a script-driven write produces a match that fields the squad you
   specified.* Everything rests on this.

2. **Pick the bridge transport.** Decide file-watch vs local socket for app↔game
   messaging; implement the minimal app→game squad push. *Done = app sends a
   squad, the resident script receives and applies it.*

3. **Game-state reader + smart Apply.** Find the screen-state address, classify
   safe/not-safe, expose to the app, gate the Apply button. *Done = button greys
   when you enter a match/edit screen, enables in the squad hub.*

4. **Result read-back.** Read the career-DB match result post-game, push to app,
   award progression. Manual entry ships alongside as the fallback. *Done = app
   updates coins from a played match.*

5. **Tier 1 breadth.** Extend the squad push to carry stats, appearance,
   formation (same write mechanism). *Done = the club looks and plays like the
   app's build.*

6. **Tier 2 + economy loop polish.** Difficulty drive, challenges, and the full
   pack → SBC → squad → match → reward loop closed end-to-end.

### Decision still open
- **App form factor:** web UI vs desktop app. This affects the bridge transport
  (a desktop app can hold a local socket more naturally; a web UI leans toward a
  small local helper/file-watch). Decide before step 2.

---

## 8. Provenance

Clean-room throughout. This route relies on FLE's documented public API and the
game's own supported career mode — no EA backend, no Blaze, and nothing derived
from leaked EA source. The earlier backend RE remains clean-room and is preserved
as a spec artifact; it is simply no longer the primary path.

---

## 9. One-paragraph summary

OpenFUT becomes a **FUT companion app that uses FIFA 23 as a match engine.** The
app owns the entire FUT experience; a resident FLE Lua script in career mode
applies the app's squad live (no restart), reports game state to drive a safe
Apply button, and reads match results back to feed progression. This sidesteps
every backend wall, runs on confirmed FLE capabilities, builds on the finished
economy core, and delivers the intuitive, offline, FUT-flavored loop that is the
actual goal.
