Phase 7 (Bridge): response shaper, diff tool, expanded mapper
- shaper.rs: shape_response() dispatches on core_path to wrap Core JSON in FUT envelope format; shapes auth (/ut/auth → sid/pid/ phishingToken/persona envelope), profile, club, squad, market, packs; unknown paths pass through unchanged - proxy.rs: calls shape_response() on every successful Core response before returning to the FIFA 23 client - mapper.rs: expanded from 6 to 18 speculative FUT endpoint mappings covering auth, profile/settings, club/usermassinfo, item/collection, squad (GET+PUT), packs+purchase, transfer market+watchlist+bid, objectives, events, squad battles, and match result submission - admin.rs: GET /_bridge/captures/diff?a=<id>&b=<id> compares two captures; reports method/path/status changes, header diffs, and structured JSON body diffs (key-level for objects, length for others) - main.rs: registers /_bridge/captures/diff route - Unit tests: 4 shaper tests, 6 mapper tests (10 total, all passing) - All 13 integration tests still passing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+169
-12
@@ -11,43 +11,81 @@ pub struct CoreMapping {
|
||||
|
||||
/// Attempt to map an incoming FIFA 23 request to an OpenFUT Core call.
|
||||
/// Returns Some(mapping) if the route is known, None otherwise.
|
||||
///
|
||||
/// Mappings are speculative — refined as real traffic captures come in.
|
||||
pub fn map_to_core(method: &str, path: &str) -> Option<CoreMapping> {
|
||||
let m = method.to_uppercase();
|
||||
let p = path.trim_end_matches('/');
|
||||
|
||||
// These are speculative mappings based on common FUT API patterns.
|
||||
// They will be refined as reverse engineering progresses.
|
||||
let known: &[(&str, &str, CoreMapping)] = &[
|
||||
// Auth
|
||||
// ── Auth ─────────────────────────────────────────────────────────────────
|
||||
(
|
||||
"POST",
|
||||
"/ut/auth",
|
||||
CoreMapping {
|
||||
method: "POST",
|
||||
core_path: "/auth/local",
|
||||
notes: "FUT auth → Core local auth",
|
||||
notes: "FUT login → Core local auth (shaped into EA envelope format)",
|
||||
},
|
||||
),
|
||||
// ── Profile / Settings ───────────────────────────────────────────────────
|
||||
(
|
||||
"GET",
|
||||
"/ut/game/fut/user/settings",
|
||||
CoreMapping {
|
||||
method: "GET",
|
||||
core_path: "/profile",
|
||||
notes: "FUT settings → Core profile",
|
||||
notes: "FUT user settings → Core profile",
|
||||
},
|
||||
),
|
||||
// Club
|
||||
(
|
||||
"GET",
|
||||
"/ut/game/fut/user/accountinfo",
|
||||
CoreMapping {
|
||||
method: "GET",
|
||||
core_path: "/profile",
|
||||
notes: "FUT account info → Core profile",
|
||||
},
|
||||
),
|
||||
// ── Club / Mass info ──────────────────────────────────────────────────────
|
||||
(
|
||||
"GET",
|
||||
"/ut/game/fut/usermassinfo",
|
||||
CoreMapping {
|
||||
method: "GET",
|
||||
core_path: "/club",
|
||||
notes: "FUT mass info → Core club",
|
||||
notes: "FUT mass info (club + coins) → Core club",
|
||||
},
|
||||
),
|
||||
// Squad
|
||||
(
|
||||
"GET",
|
||||
"/ut/game/fut/club",
|
||||
CoreMapping {
|
||||
method: "GET",
|
||||
core_path: "/club",
|
||||
notes: "FUT club info → Core club",
|
||||
},
|
||||
),
|
||||
// ── Cards / Collection ────────────────────────────────────────────────────
|
||||
(
|
||||
"GET",
|
||||
"/ut/game/fut/item",
|
||||
CoreMapping {
|
||||
method: "GET",
|
||||
core_path: "/cards/collection",
|
||||
notes: "FUT collection → Core player card collection",
|
||||
},
|
||||
),
|
||||
(
|
||||
"GET",
|
||||
"/ut/game/fut/club/stats",
|
||||
CoreMapping {
|
||||
method: "GET",
|
||||
core_path: "/stats",
|
||||
notes: "FUT club stats → Core statistics summary",
|
||||
},
|
||||
),
|
||||
// ── Squad ─────────────────────────────────────────────────────────────────
|
||||
(
|
||||
"GET",
|
||||
"/ut/game/fut/squad/active",
|
||||
@@ -57,7 +95,16 @@ pub fn map_to_core(method: &str, path: &str) -> Option<CoreMapping> {
|
||||
notes: "FUT active squad → Core squad",
|
||||
},
|
||||
),
|
||||
// Packs
|
||||
(
|
||||
"PUT",
|
||||
"/ut/game/fut/squad/active",
|
||||
CoreMapping {
|
||||
method: "POST",
|
||||
core_path: "/squad",
|
||||
notes: "FUT save squad → Core save squad",
|
||||
},
|
||||
),
|
||||
// ── Packs ─────────────────────────────────────────────────────────────────
|
||||
(
|
||||
"GET",
|
||||
"/ut/game/fut/store/packdetails",
|
||||
@@ -67,14 +114,80 @@ pub fn map_to_core(method: &str, path: &str) -> Option<CoreMapping> {
|
||||
notes: "FUT pack store → Core pack list",
|
||||
},
|
||||
),
|
||||
// Transfer market (guessed)
|
||||
(
|
||||
"POST",
|
||||
"/ut/game/fut/store/purchase",
|
||||
CoreMapping {
|
||||
method: "POST",
|
||||
core_path: "/packs/buy",
|
||||
notes: "FUT pack purchase → Core pack buy (guess)",
|
||||
},
|
||||
),
|
||||
// ── Transfer Market ───────────────────────────────────────────────────────
|
||||
(
|
||||
"GET",
|
||||
"/ut/game/fut/transfermarket",
|
||||
CoreMapping {
|
||||
method: "GET",
|
||||
core_path: "/market",
|
||||
notes: "FUT transfer market → Core NPC market",
|
||||
notes: "FUT transfer market search → Core NPC market",
|
||||
},
|
||||
),
|
||||
(
|
||||
"POST",
|
||||
"/ut/game/fut/trade/bid",
|
||||
CoreMapping {
|
||||
method: "POST",
|
||||
core_path: "/market/buy",
|
||||
notes: "FUT bid/buy now → Core market buy",
|
||||
},
|
||||
),
|
||||
(
|
||||
"GET",
|
||||
"/ut/game/fut/trade/watchlist",
|
||||
CoreMapping {
|
||||
method: "GET",
|
||||
core_path: "/market",
|
||||
notes: "FUT watchlist → Core market (approximation)",
|
||||
},
|
||||
),
|
||||
// ── Objectives ────────────────────────────────────────────────────────────
|
||||
(
|
||||
"GET",
|
||||
"/ut/game/fut/objectives",
|
||||
CoreMapping {
|
||||
method: "GET",
|
||||
core_path: "/objectives",
|
||||
notes: "FUT objectives → Core objectives list",
|
||||
},
|
||||
),
|
||||
// ── Events ────────────────────────────────────────────────────────────────
|
||||
(
|
||||
"GET",
|
||||
"/ut/game/fut/events",
|
||||
CoreMapping {
|
||||
method: "GET",
|
||||
core_path: "/events",
|
||||
notes: "FUT events → Core events list",
|
||||
},
|
||||
),
|
||||
// ── Matches / Squad Battles ───────────────────────────────────────────────
|
||||
(
|
||||
"GET",
|
||||
"/ut/game/fut/squadbattle/opponent",
|
||||
CoreMapping {
|
||||
method: "GET",
|
||||
core_path: "/matches/opponent",
|
||||
notes: "FUT squad battles opponent → Core match opponent generator",
|
||||
},
|
||||
),
|
||||
(
|
||||
"POST",
|
||||
"/ut/game/fut/result",
|
||||
CoreMapping {
|
||||
method: "POST",
|
||||
core_path: "/matches",
|
||||
notes: "FUT match result submit → Core match submission",
|
||||
},
|
||||
),
|
||||
];
|
||||
@@ -89,7 +202,7 @@ pub fn map_to_core(method: &str, path: &str) -> Option<CoreMapping> {
|
||||
}
|
||||
|
||||
/// Return a safe placeholder response for unknown endpoints.
|
||||
/// This prevents the FIFA 23 client from crashing while we log traffic.
|
||||
/// Prevents the FIFA 23 client from crashing while we log traffic.
|
||||
pub fn placeholder_response(method: &str, path: &str) -> serde_json::Value {
|
||||
tracing::warn!(
|
||||
"UNKNOWN ENDPOINT: {} {} — returning placeholder",
|
||||
@@ -103,3 +216,47 @@ pub fn placeholder_response(method: &str, path: &str) -> serde_json::Value {
|
||||
"path": path,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_known_auth_maps() {
|
||||
let m = map_to_core("POST", "/ut/auth");
|
||||
assert!(m.is_some());
|
||||
assert_eq!(m.unwrap().core_path, "/auth/local");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_known_market_maps() {
|
||||
let m = map_to_core("GET", "/ut/game/fut/transfermarket");
|
||||
assert!(m.is_some());
|
||||
assert_eq!(m.unwrap().core_path, "/market");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_events_maps() {
|
||||
let m = map_to_core("GET", "/ut/game/fut/events");
|
||||
assert!(m.is_some());
|
||||
assert_eq!(m.unwrap().core_path, "/events");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_squad_put_maps() {
|
||||
let m = map_to_core("PUT", "/ut/game/fut/squad/active");
|
||||
assert!(m.is_some());
|
||||
assert_eq!(m.unwrap().method, "POST");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unknown_returns_none() {
|
||||
assert!(map_to_core("GET", "/ut/game/fut/some/unknown/path").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailing_slash_trimmed() {
|
||||
let m = map_to_core("POST", "/ut/auth/");
|
||||
assert!(m.is_some());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user