Files
OpenFUT/openfut-adapter-fifa17/src/lib.rs
T
funman300 c0a3f68ded feat(utas): FIFA17 UTAS migration host + /club adapter mappings
openfut-utas-host: the first live UTAS host. Serves GET /ut/game/<title>/club
from OpenFUT Core via the FIFA17 adapter and reverse-proxies every other UTAS
route verbatim to the Python oracle. Plaintext HTTP/1.1 keep-alive (no TLS);
route classification before execution; a Core error on /club degrades to an
empty page and never falls back to Python. CoreAccess is a host-owned boundary
(the adapter stays transport-agnostic).

openfut-adapter-fifa17::fut: owned_query (wire parse + FIFA id->name mapping,
unknown id = hard error), entities (id<->name from committed tables), and
club_response (FIFA _item shaping; drops items lacking a real FIFA asset id,
never fabricates one).

openfut-core submodule advanced to the reconciled trunk (6acae54 = 8c8a4116
multi-game + eab522a replace_squad/SquadRules + the /club semantic query).
11 host tests + adapter fut tests; 10/10 host mutations killed. rare=SP UNKNOWN.
Retail rendering of Core inventory still blocked on the Core-card->asset-id
identity decision (next phase).
2026-08-11 21:40:15 +00:00

80 lines
3.3 KiB
Rust

//! # openfut-adapter-fifa17
//!
//! The FIFA 17 game adapter: everything that is true of *FIFA 17 specifically*
//! and must therefore stay out of OpenFUT Core and out of the generic protocol
//! crates.
//!
//! ## Layering
//!
//! ```text
//! openfut-protocol-blaze generic Blaze: Fire2 framing, Heat2/TDF codec
//! ▲
//! openfut-adapter-fifa17 THIS: FIFA 17 command tables, response bodies,
//! ▲ dispatch ordering, session identity
//! OpenFUT Core game-independent FUT domain (not yet wired)
//! ```
//!
//! A second title gets its own adapter crate and reuses the protocol layer
//! underneath. Nothing here is written to be shared with one; if something in
//! this crate turns out to be title-independent, it belongs one layer down.
//!
//! ## Modules
//!
//! * [`blaze`] — the Blaze/Fire2 RPC surface. Implemented, runtime validated.
//! * [`redirector`] — the first hop's `<serverinstanceinfo>` response.
//! Implemented; runtime validated against the retail client.
//! * [`roster`] — the FUT roster-update response, the last gate before the hub.
//! Implemented; not yet runtime validated.
//! * [`fut`] — FUT/RS4 (UTAS) wire → Core semantic mappings; currently the
//! owned-player ("My Squad") search: query parse + FIFA-id→name resolution +
//! semantic filter/pagination. Pure mapping; no Rust UTAS host yet.
//!
//! Still served only by the Python backend: LSX/Origin (`:4216`), UTAS/RS4
//! (`:8099`) and POW/EASFC (`:8094`). Roster XML (`:8081`) has an adapter here
//! but no Rust host yet.
//!
//! **Nucleus (`:42131`) is deliberately not ported.** Instrumentation across
//! every live session showed the client never dials it: the listener is bound,
//! the handler logs unconditionally on connect, the client fetches the
//! `OSDK_NUCLEUS` config that carries the URL — and makes zero requests. It is
//! dead code on the observed path, so porting it would add an untested
//! component for no parity gain. See the vault's Protocol Findings.
//!
//! ## Provenance
//!
//! Ported from `fifa17-recon/tools/blaze_responder_v3b.py`, the implementation
//! that drove a retail FIFA 17 client from Origin login to an opened FUT pack.
//! Parity is tested, not asserted: `fixtures/blaze_transactions.jsonl` records
//! real request→response(s) transactions produced by the Python dispatcher, and
//! `tests/oracle_parity.rs` replays them byte-for-byte.
//!
//! ## Not a server
//!
//! This crate answers frames. It opens no socket, terminates no TLS and owns no
//! runtime. Hosting it is a separate, later decision — the Python backend
//! remains the live runtime and nothing here is wired into it.
//!
//! ```
//! use openfut_adapter_fifa17::blaze::{Adapter, AdapterConfig, Session};
//! use openfut_protocol_blaze::fire2::{Header, MsgType};
//! use openfut_protocol_blaze::heat2::Struct;
//!
//! let adapter = Adapter::new(AdapterConfig::loopback());
//! let mut session = Session::new("session-key", 0x656E5553);
//!
//! // Util::ping
//! let request = Header::new(0x0009, 0x0002, 1, MsgType::Message);
//! let out = adapter.dispatch(&request, &Struct::new(), &mut session, 1_754_870_400);
//!
//! assert_eq!(out.len(), 1);
//! assert_eq!(out[0].header.msg_type, MsgType::Reply);
//! ```
pub mod blaze;
pub mod fut;
pub mod redirector;
pub mod roster;
pub mod tls;
pub use blaze::{Adapter, AdapterConfig, Session};