[Mapper] Map FIFA 23 Match Result, Squad Battles, and Startup Endpoints #4

Open
opened 2026-06-26 05:05:39 +00:00 by funman300 · 0 comments
Owner

Overview

Add mapper entries for FIFA 23 match submission, Squad Battles, and the usermassinfo startup bundle. These are needed for the game to record results, advance Squad Battles rank, and display the FUT hub.

EA → Core Endpoint Table

EA Endpoint Core Endpoint Notes
POST /ut/game/fut/game/r POST /matches/result Submit match result
POST /ut/game/fut/squadbattle/result POST /matches/result (mode=squad_battles) SB result
GET /ut/game/fut/squadbattle/opponents GET /squad-battles/opponents Weekly AI opponents
GET /ut/game/fut/usermassinfo GET /club Startup bundle (critical path)
GET /ut/game/fut/squads/user GET /squad Squad list (called on hub load)

Implementation Steps

1. Map match result in src/mapper.rs

("POST", "/ut/game/fut/game/r") => {
    Some(MappedRoute { core_path: "/matches/result".into(), method: Method::POST })
}

2. Map Squad Battles result with mode injection

Extend MappedRoute with an optional extra_body field if it does not already exist:

pub struct MappedRoute {
    pub core_path: String,
    pub method: Method,
    pub extra_body: Option<serde_json::Value>, // merged into the forwarded request body
}

Then:

("POST", "/ut/game/fut/squadbattle/result") => {
    Some(MappedRoute {
        core_path: "/matches/result".into(),
        method: Method::POST,
        extra_body: Some(json!({ "mode": "squad_battles" })),
    })
}

In src/proxy.rs, when forwarding, merge extra_body into the request JSON before sending to Core.

3. Map Squad Battles opponents

("GET", path) if path.starts_with("/ut/game/fut/squadbattle/opponents") => {
    Some(MappedRoute { core_path: "/squad-battles/opponents".into(), method: Method::GET })
}

4. Map usermassinfo (critical startup endpoint)

("GET", "/ut/game/fut/usermassinfo") => {
    Some(MappedRoute { core_path: "/club".into(), method: Method::GET })
}

5. Map squad list

("GET", "/ut/game/fut/squads/user") => {
    Some(MappedRoute { core_path: "/squad".into(), method: Method::GET })
}

6. Add match result shaper in src/shaper.rs

EA expects a specific ACK envelope for match submission:

pub fn shape_match_result(core_resp: serde_json::Value) -> serde_json::Value {
    json!({
        "tradeId": 0,
        "status": "active",
        "resultCode": 200,
        "userInfo": {
            "coins": core_resp["coins_after"].as_u64().unwrap_or(0)
        }
    })
}

7. Add usermassinfo shaper

usermassinfo is a large startup bundle. Build a minimal but valid version from the Core /club response:

pub fn shape_usermassinfo(club: serde_json::Value) -> serde_json::Value {
    json!({
        "userInfo": {
            "credits": club["coins"],
            "futCurrencyMap": { "1": { "finalCoins": club["coins"] } }
        },
        "pileSizeClientData": { "entries": [] },
        "userMassinfoRet": { "seasonTicketInfo": null }
    })
}

Expand this envelope as real capture data reveals more required fields.

8. Register all new shapers in src/proxy.rs

9. Add mapper unit tests

  • map_request("POST", "/ut/game/fut/game/r")POST /matches/result, no extra body
  • map_request("POST", "/ut/game/fut/squadbattle/result")POST /matches/result with extra_body.mode == "squad_battles"
  • map_request("GET", "/ut/game/fut/squadbattle/opponents")GET /squad-battles/opponents
  • map_request("GET", "/ut/game/fut/usermassinfo")GET /club

10. Add shaper unit tests

  • shape_match_result(core_resp) → has resultCode: 200 and userInfo.coins
  • shape_usermassinfo(club) → has userInfo.credits matching club.coins

Acceptance Criteria

  • Match result EA endpoint is mapped to Core
  • Squad Battles result is mapped with mode: squad_battles merged into body
  • usermassinfo is mapped and shaped into a valid EA envelope
  • Squad Battles opponents endpoint is mapped
  • Squad list endpoint is mapped
  • All unit tests pass
  • cargo clippy -- -D warnings passes
## Overview Add mapper entries for FIFA 23 match submission, Squad Battles, and the `usermassinfo` startup bundle. These are needed for the game to record results, advance Squad Battles rank, and display the FUT hub. ## EA → Core Endpoint Table | EA Endpoint | Core Endpoint | Notes | |-------------|--------------|-------| | `POST /ut/game/fut/game/r` | `POST /matches/result` | Submit match result | | `POST /ut/game/fut/squadbattle/result` | `POST /matches/result` (mode=squad_battles) | SB result | | `GET /ut/game/fut/squadbattle/opponents` | `GET /squad-battles/opponents` | Weekly AI opponents | | `GET /ut/game/fut/usermassinfo` | `GET /club` | Startup bundle (critical path) | | `GET /ut/game/fut/squads/user` | `GET /squad` | Squad list (called on hub load) | ## Implementation Steps ### 1. Map match result in `src/mapper.rs` ```rust ("POST", "/ut/game/fut/game/r") => { Some(MappedRoute { core_path: "/matches/result".into(), method: Method::POST }) } ``` ### 2. Map Squad Battles result with mode injection Extend `MappedRoute` with an optional `extra_body` field if it does not already exist: ```rust pub struct MappedRoute { pub core_path: String, pub method: Method, pub extra_body: Option<serde_json::Value>, // merged into the forwarded request body } ``` Then: ```rust ("POST", "/ut/game/fut/squadbattle/result") => { Some(MappedRoute { core_path: "/matches/result".into(), method: Method::POST, extra_body: Some(json!({ "mode": "squad_battles" })), }) } ``` In `src/proxy.rs`, when forwarding, merge `extra_body` into the request JSON before sending to Core. ### 3. Map Squad Battles opponents ```rust ("GET", path) if path.starts_with("/ut/game/fut/squadbattle/opponents") => { Some(MappedRoute { core_path: "/squad-battles/opponents".into(), method: Method::GET }) } ``` ### 4. Map `usermassinfo` (critical startup endpoint) ```rust ("GET", "/ut/game/fut/usermassinfo") => { Some(MappedRoute { core_path: "/club".into(), method: Method::GET }) } ``` ### 5. Map squad list ```rust ("GET", "/ut/game/fut/squads/user") => { Some(MappedRoute { core_path: "/squad".into(), method: Method::GET }) } ``` ### 6. Add match result shaper in `src/shaper.rs` EA expects a specific ACK envelope for match submission: ```rust pub fn shape_match_result(core_resp: serde_json::Value) -> serde_json::Value { json!({ "tradeId": 0, "status": "active", "resultCode": 200, "userInfo": { "coins": core_resp["coins_after"].as_u64().unwrap_or(0) } }) } ``` ### 7. Add `usermassinfo` shaper `usermassinfo` is a large startup bundle. Build a minimal but valid version from the Core `/club` response: ```rust pub fn shape_usermassinfo(club: serde_json::Value) -> serde_json::Value { json!({ "userInfo": { "credits": club["coins"], "futCurrencyMap": { "1": { "finalCoins": club["coins"] } } }, "pileSizeClientData": { "entries": [] }, "userMassinfoRet": { "seasonTicketInfo": null } }) } ``` Expand this envelope as real capture data reveals more required fields. ### 8. Register all new shapers in `src/proxy.rs` ### 9. Add mapper unit tests - `map_request("POST", "/ut/game/fut/game/r")` → `POST /matches/result`, no extra body - `map_request("POST", "/ut/game/fut/squadbattle/result")` → `POST /matches/result` with `extra_body.mode == "squad_battles"` - `map_request("GET", "/ut/game/fut/squadbattle/opponents")` → `GET /squad-battles/opponents` - `map_request("GET", "/ut/game/fut/usermassinfo")` → `GET /club` ### 10. Add shaper unit tests - `shape_match_result(core_resp)` → has `resultCode: 200` and `userInfo.coins` - `shape_usermassinfo(club)` → has `userInfo.credits` matching `club.coins` ## Acceptance Criteria - [ ] Match result EA endpoint is mapped to Core - [ ] Squad Battles result is mapped with `mode: squad_battles` merged into body - [ ] `usermassinfo` is mapped and shaped into a valid EA envelope - [ ] Squad Battles opponents endpoint is mapped - [ ] Squad list endpoint is mapped - [ ] All unit tests pass - [ ] `cargo clippy -- -D warnings` passes
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: funman300/OpenFUT-Bridge#4