fix(fifa17): key the kit home/away split on the carddbid, not assetid

Staging served `kits=2 kitsHome=0 kitsAway=0`: the split compared the catalog
`asset_id` against `fcc_kitcards.assetid` (14/15), but a kit's catalog
`asset_id` IS its carddbid (6300006), not that column, so neither family ever
matched.

The carddbid range is the same fact in the form we actually carry: across all
1482 kit rows, assetid 14 covers precisely the 828 `63xxxxx` ids and assetid 15
precisely the 654 `64xxxxx` ids, with no exceptions either way. Keying on the id
we already have avoids carrying `assetid` as a second source of truth for the
same split. Tests now use the real team-21 pair (6300006 home / 6400003 away).

Verified against the staging stack: `kits=2 kitsHome=1 kitsAway=1`, team-21
bucket 2, `?type=kit` still activeHomeKit/activeAwayKit, `?type=player` 12.
This commit is contained in:
funman300
2026-08-21 04:13:11 +00:00
parent 3442eac6f0
commit 054a912357
+33 -21
View File
@@ -66,12 +66,20 @@ const S_KITS_HOME: i64 = 0x29;
const S_KITS_AWAY: i64 = 0x2A; const S_KITS_AWAY: i64 = 0x2A;
const S_BADGES: i64 = 0x2D; const S_BADGES: i64 = 0x2D;
/// `fcc_kitcards.assetid` splits the kit table into the home and away families. /// First `carddbid` of the AWAY kit family. `fcc_kitcards` is split into a
/// Verified across all 1482 rows of the FIFA 17 kit table: assetid 14 covers /// `63xxxxx` home family and a `64xxxxx` away family, and the table's own
/// exactly the `63xxxxx` carddbids (828 rows) and assetid 15 exactly the /// `assetid` column agrees exactly: across all 1482 rows, assetid 14 covers
/// `64xxxxx` ones (654 rows), with no exceptions in either direction. /// precisely the 828 `63xxxxx` ids and assetid 15 precisely the 654 `64xxxxx`
const KIT_ASSET_HOME: i64 = 14; /// ids, with no exceptions either way. A kit's catalog `asset_id` IS its
const KIT_ASSET_AWAY: i64 = 15; /// carddbid, so the id itself is the family key -- the `assetid` column is not
/// carried on the wire and would be a second source of truth for the same fact.
const KIT_AWAY_FLOOR: i64 = 6_400_000;
/// Which kit family an owned kit belongs to. Only meaningful for
/// [`ContentKind::Kit`]; the caller filters first.
fn is_home_kit(kit: &ClubStatInput) -> bool {
kit.asset_id < KIT_AWAY_FLOOR
}
/// cardsubtypeid (staff family) -> stat id (STAFF_SUBTYPE_STAT). /// cardsubtypeid (staff family) -> stat id (STAFF_SUBTYPE_STAT).
fn staff_stat(subtype: i64) -> Option<i64> { fn staff_stat(subtype: i64) -> Option<i64> {
@@ -240,11 +248,10 @@ pub fn club_stats_body(items: &[ClubStatInput], ctx: ContextField) -> Value {
.iter() .iter()
.filter(|item| matches!(item.kind, ContentKind::Kit)) .filter(|item| matches!(item.kind, ContentKind::Kit))
.collect(); .collect();
let kits_with_asset = let home = kits.iter().filter(|kit| is_home_kit(kit)).count() as i64;
|asset: i64| kits.iter().filter(|item| item.asset_id == asset).count() as i64;
g.insert(S_KITS, kits.len() as i64); g.insert(S_KITS, kits.len() as i64);
g.insert(S_KITS_HOME, kits_with_asset(KIT_ASSET_HOME)); g.insert(S_KITS_HOME, home);
g.insert(S_KITS_AWAY, kits_with_asset(KIT_ASSET_AWAY)); g.insert(S_KITS_AWAY, kits.len() as i64 - home);
let mut stat: Vec<Value> = g.iter().map(|(sid, v)| row(1, 0, *sid, *v)).collect(); let mut stat: Vec<Value> = g.iter().map(|(sid, v)| row(1, 0, *sid, *v)).collect();
@@ -344,22 +351,27 @@ mod tests {
team_id: None, team_id: None,
} }
} }
/// A kit of the given family (`KIT_ASSET_HOME` / `KIT_ASSET_AWAY`) worn by /// A kit worn by `team`. `carddbid` is the real `fcc_kitcards` id, which is
/// `team`. /// also the catalog `asset_id` and therefore the home/away family key.
fn kit_of(asset_id: i64, team: i64) -> ClubStatInput { fn kit_of(carddbid: i64, team: i64) -> ClubStatInput {
ClubStatInput { ClubStatInput {
kind: ContentKind::Kit, kind: ContentKind::Kit,
subtype: 9, subtype: 9,
rating: 0, rating: 0,
rare: false, rare: false,
asset_id, asset_id: carddbid,
nation_id: None, nation_id: None,
league_id: None, league_id: None,
team_id: Some(team), team_id: Some(team),
} }
} }
/// Real team-21 kits from `fcc_kitcards`: 6300006 is its home kit and
/// 6400003 its away kit.
const HOME_KIT: i64 = 6_300_006;
const AWAY_KIT: i64 = 6_400_003;
fn kit() -> ClubStatInput { fn kit() -> ClubStatInput {
kit_of(KIT_ASSET_HOME, 21) kit_of(HOME_KIT, 21)
} }
fn global(body: &Value) -> std::collections::HashMap<String, i64> { fn global(body: &Value) -> std::collections::HashMap<String, i64> {
@@ -432,9 +444,9 @@ mod tests {
#[test] #[test]
fn kit_counts_split_by_home_and_away_family() { fn kit_counts_split_by_home_and_away_family() {
let items = vec![ let items = vec![
kit_of(KIT_ASSET_HOME, 21), kit_of(HOME_KIT, 21),
kit_of(KIT_ASSET_HOME, 38), kit_of(6_300_010, 38),
kit_of(KIT_ASSET_AWAY, 21), kit_of(AWAY_KIT, 21),
]; ];
let g = global(&club_stats_body(&items, ContextField::Nation)); let g = global(&club_stats_body(&items, ContextField::Nation));
assert_eq!(g["kits"], 3); assert_eq!(g["kits"], 3);
@@ -450,9 +462,9 @@ mod tests {
with_team.team_id = Some(21); with_team.team_id = Some(21);
let items = vec![ let items = vec![
with_team, with_team,
kit_of(KIT_ASSET_HOME, 21), kit_of(HOME_KIT, 21),
kit_of(KIT_ASSET_AWAY, 21), kit_of(AWAY_KIT, 21),
kit_of(KIT_ASSET_HOME, 38), kit_of(6_300_010, 38),
]; ];
let body = club_stats_body(&items, ContextField::Team); let body = club_stats_body(&items, ContextField::Team);
let kits_for = |team: i64| { let kits_for = |team: i64| {