feat(fifa17): project owned kits with active home/away designation

Closes the server side of the FUT kit selector. Ownership stays generic in
Core (submodule bump: club_kit_assignments + GET/PUT /club/kits); this
commit adds the FIFA17 representation, the host projection and importer
support.

adapter:
* ContentKind::Kit ("kit") so kits are classified alongside player/staff/
  consumable instead of being mistaken for 0-rated players.
* Fifa17CardIdentity carries card_asset_id and team_id; RawCard keeps both
  optional because the emitted catalog writes null for non-kit definitions.
* shape_kit_item emits only the fields the client's kit path reads
  (id/resourceId/assetId/cardassetid/cardsubtypeid/itemState/owners/
  untradeable/teamid) — no attributeList, no itemType.
* itemState on the wire is the STRING token activeHomeKit/activeAwayKit;
  the 101/102 integers are the client's post-deserialisation runtime enum
  (item+0x5c) and are never emitted.
* club_stats S_KITS (0x28) now counts owned kits instead of a hard zero.

host:
* CoreKitAssignments + CoreAccess::get_active_kits (GET /club/kits),
  defaulting to no active kits so a Core without the endpoint degrades
  instead of fabricating a designation.
* handle_club classifies type=player|kit, rejects any other type with an
  empty page and outcome=unsupported_type, and now always fetches
  unpaginated from Core: kind and transfer-pile membership are host-side
  concepts Core cannot express, so filtering and pagination must both
  happen after shaping or pages come back short.

importer:
* ItemClass::Kit (cardsubtypeid == 9 and resourceId in 6_300_000..=6_400_654),
  kit counts/balances, and card_asset_id/team_id carried into the emitted
  catalog and manifest.
* a kit group missing cardassetid == 35 or teamid is DEFERRED
  (missing_kit_render_metadata) rather than defaulted; conflicting render
  metadata across instances defers as render_metadata_conflict.

staging: sold-staging-up.py seeds two owned kits (6300006 home / 6400003
away, team 21) plus both active designations so the projection can be
verified over HTTP before involving the client.
This commit is contained in:
funman300
2026-08-21 03:17:57 +00:00
parent ab62440dbf
commit db743ffd1f
13 changed files with 616 additions and 169 deletions
+21 -1
View File
@@ -38,6 +38,11 @@ pub struct Fifa17CardIdentity {
/// FIFA `cardsubtypeid` for a non-player definition (consumable family /
/// staff role), `0` for a player or when absent.
pub subtype: i64,
/// FIFA card-art class. Players default to `asset_id`; kit definitions carry
/// the verified `fcc_kitcards.cardassetid` value (`35`).
pub card_asset_id: u32,
/// Source team id for a club kit. Zero for content kinds that do not use it.
pub team_id: i64,
}
/// The FIFA 17 numeric namespace policy for owned-item wire ids.
@@ -131,6 +136,12 @@ struct RawCard {
/// FIFA `cardsubtypeid` for a non-player entry; absent → `0`.
#[serde(default)]
subtype: i64,
/// Separate card-art id for non-player definitions; absent → `asset_id`.
#[serde(default)]
card_asset_id: Option<u32>,
/// Source team id for a kit definition; absent → `0`.
#[serde(default)]
team_id: Option<i64>,
}
fn default_rareflag() -> i64 {
@@ -187,6 +198,8 @@ impl Fifa17CardCatalog {
rareflag: rc.rareflag,
kind: ContentKind::from_str(&rc.kind),
subtype: rc.subtype,
card_asset_id: rc.card_asset_id.unwrap_or(rc.asset_id),
team_id: rc.team_id.unwrap_or(0),
},
);
}
@@ -397,7 +410,9 @@ mod tests {
r#"{"schema_version":1,"game":"fifa17","cards":{
"fifa17_20801":{"asset_id":20801,"kind":"player","subtype":0},
"fifa17_5003012":{"asset_id":5003012,"kind":"consumable","subtype":54,"rareflag":0},
"fifa17_3000083":{"asset_id":3000083,"kind":"staff","subtype":8,"rareflag":0}
"fifa17_3000083":{"asset_id":3000083,"kind":"staff","subtype":8,"rareflag":0},
"fifa17_6300006":{"asset_id":6300006,"kind":"kit","subtype":9,
"card_asset_id":35,"team_id":21,"rareflag":0}
}}"#,
)
.unwrap();
@@ -407,5 +422,10 @@ mod tests {
assert_eq!(cat.kind_of("fifa17_3000083"), ContentKind::Staff);
assert_eq!(cat.subtype_of("fifa17_3000083"), 8);
assert_eq!(cat.lookup("fifa17_5003012").unwrap().rareflag, 0);
let kit = cat.lookup("fifa17_6300006").unwrap();
assert_eq!(kit.kind, ContentKind::Kit);
assert_eq!(kit.subtype, 9);
assert_eq!(kit.card_asset_id, 35);
assert_eq!(kit.team_id, 21);
}
}