diff --git a/openfut-adapter-fifa17/src/fut/sbc.rs b/openfut-adapter-fifa17/src/fut/sbc.rs index 907ddc7..f5bc9b5 100644 --- a/openfut-adapter-fifa17/src/fut/sbc.rs +++ b/openfut-adapter-fifa17/src/fut/sbc.rs @@ -178,9 +178,15 @@ impl std::error::Error for SbcWireError {} /// Extract FIFA wire item ids from a saved/submitted challenge squad. /// -/// Retail request captures for this body are unavailable. The parser therefore accepts -/// only the two already-proven FIFA 17 squad containers: a normal `players` array or the -/// SBC `squad` array. Within either, only `itemData.id` is interpreted. +/// The exact retail challenge-squad body is now captured (2026-08-18 Gate C, +/// `PUT /ut/game/fifa17/sbs/challenge/101/squad`): a fixed 23-entry `players` +/// array of `{index, itemData:{id, dream}}` (empty slots carry `id == 0`), +/// alongside sibling `chemistry`, `rating`, `formation`, and a `manager` array +/// of `{id, dream}`. Only `players[].itemData.id` selects the consumed cards; +/// the manager, chemistry, rating, formation, dream, and index fields are +/// presentation/validation hints and are never consumed. The `squad` array +/// container remains accepted for the alternate proven shape. Within either, +/// only non-zero `itemData.id` values are interpreted. pub fn parse_wire_item_ids(body: &[u8]) -> Result, SbcWireError> { let root: Value = serde_json::from_slice(body).map_err(|error| SbcWireError::Json(error.to_string()))?; @@ -259,4 +265,49 @@ mod tests { Err(SbcWireError::Json(_)) )); } + + #[test] + fn parser_matches_captured_retail_challenge_squad_body() { + // Verbatim shape from the 2026-08-18 Gate C retail capture of + // PUT /ut/game/fifa17/sbs/challenge/101/squad (11 filled + 12 empty + // slots, plus manager/chemistry/rating/formation siblings). Only the 11 + // non-zero player itemData.id values are consumed, in wire order; the + // manager and all presentation fields are ignored. + let retail = br#"{"chemistry":21,"rating":86,"formation":"f433", + "manager":[{"id":100000427,"dream":false}], + "players":[ + {"index":0,"itemData":{"id":100004227,"dream":false}}, + {"index":1,"itemData":{"id":100004233,"dream":false}}, + {"index":2,"itemData":{"id":100001317,"dream":false}}, + {"index":3,"itemData":{"id":100001531,"dream":false}}, + {"index":4,"itemData":{"id":100000966,"dream":false}}, + {"index":5,"itemData":{"id":100001947,"dream":false}}, + {"index":6,"itemData":{"id":100000169,"dream":false}}, + {"index":7,"itemData":{"id":100002017,"dream":false}}, + {"index":8,"itemData":{"id":100002765,"dream":false}}, + {"index":9,"itemData":{"id":100000147,"dream":false}}, + {"index":10,"itemData":{"id":100000311,"dream":false}}, + {"index":11,"itemData":{"id":0,"dream":false}}, + {"index":12,"itemData":{"id":0,"dream":false}}, + {"index":13,"itemData":{"id":0,"dream":false}}, + {"index":14,"itemData":{"id":0,"dream":false}}, + {"index":15,"itemData":{"id":0,"dream":false}}, + {"index":16,"itemData":{"id":0,"dream":false}}, + {"index":17,"itemData":{"id":0,"dream":false}}, + {"index":18,"itemData":{"id":0,"dream":false}}, + {"index":19,"itemData":{"id":0,"dream":false}}, + {"index":20,"itemData":{"id":0,"dream":false}}, + {"index":21,"itemData":{"id":0,"dream":false}}, + {"index":22,"itemData":{"id":0,"dream":false}} + ]}"#; + assert_eq!( + parse_wire_item_ids(retail).unwrap(), + [ + 100_004_227, 100_004_233, 100_001_317, 100_001_531, 100_000_966, + 100_001_947, 100_000_169, 100_002_017, 100_002_765, 100_000_147, + 100_000_311 + ], + "exactly the 11 non-zero players in wire order; manager and empty slots ignored" + ); + } }