fix(fifa17): send a club item's real wire assetId, not its carddbid
A club item's `assetId` (record +0x20) is family specific and is NOT the carddbid: per the client's own tables a kit carries the art class from fcc_kitcards.assetid - 14 for the 63xxxxx home/third band, 15 for the 64xxxxx away band - a badge carries its team id, and a ball and stadium their own asset number. The catalog shipped `asset_id`, the carddbid, in that slot. Measured on the live client with both kits resident: record +0x20 held 6300006 (home) and 6400003 (away) where the table says 14 and 15, while every other field - resourceId, cardassetid 35, category 2/3, teamid 21, year 0, itemState 101/102 - already matched. Operator reports both pre-match kit tiles rendering identically. assetId is the only field that diverges from the client's own data, and an assetId that is not a valid kit art class cannot resolve to distinct art. `resource_id` is derived from `asset_id`, and every home kit shares art class 14, so the two cannot be the same field: catalogs now carry an optional `club_asset_id`, defaulting to `asset_id` so a catalog predating the field and every non-club kind are unchanged. resolve_kit emits it as the wire `assetId`. Fixed at the source too - scripts/sold-staging-up.py emitted asset_id as the wire assetId for all four club families, so a re-emit would have regressed it. Field-offset note: club_items.json's _record_map is authoritative and my earlier working note had these transposed - assetId is +0x20 and cardassetid is +0x1c, not the reverse. Adds tools/live/diff_kit_records.py, which byte-diffs the two resident kit records and names the fields the decoded clone query consumes. Staging wire now reads assetId 14/15 with cardassetid 35 on both squad.actives and /club?type=equippables. Workspace 1250 passed, 0 failed. Client re-parse still to be confirmed visually.
This commit is contained in:
Executable
+135
@@ -0,0 +1,135 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Byte-level diff of the two resident kit records in a live FIFA17 client.
|
||||||
|
|
||||||
|
The pre-match selector draws each kit from a clone query keyed on the record's
|
||||||
|
own fields, so if both tiles render identically the question is precisely: which
|
||||||
|
bytes of the home record differ from the away record? This prints every differing
|
||||||
|
offset with the known field names attached, and dumps the fields the decoded
|
||||||
|
clone query consumes.
|
||||||
|
|
||||||
|
Read-only. Never writes to the process.
|
||||||
|
|
||||||
|
Decoded query (FUN_1801c3480 -> FUN_1801c44b0):
|
||||||
|
teamtechid == record+0x94
|
||||||
|
teamkittypetechid == derived from itemState (101 -> 0 home, 102 -> 1 away)
|
||||||
|
year == record+0xba
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
|
||||||
|
PID = int(sys.argv[1])
|
||||||
|
WANT = [int(a) for a in sys.argv[2:]] or [100004874, 100004873]
|
||||||
|
|
||||||
|
mem = open(f"/proc/{PID}/mem", "rb", buffering=0)
|
||||||
|
|
||||||
|
|
||||||
|
def rd(a, n):
|
||||||
|
mem.seek(a)
|
||||||
|
return mem.read(n)
|
||||||
|
|
||||||
|
|
||||||
|
def q(a):
|
||||||
|
return struct.unpack("<Q", rd(a, 8))[0]
|
||||||
|
|
||||||
|
|
||||||
|
named = []
|
||||||
|
for ln in open(f"/proc/{PID}/maps"):
|
||||||
|
m = re.match(r"([0-9a-f]+)-([0-9a-f]+) \S{4} \S+ \S+ \S+\s+(.+)", ln)
|
||||||
|
if m:
|
||||||
|
named.append((int(m.group(1), 16), m.group(3).strip()))
|
||||||
|
named.sort()
|
||||||
|
base = next((s for s, p in named if p.endswith("CardsDLL_Win64_retail.dll")), None)
|
||||||
|
if base is None:
|
||||||
|
sys.exit("CardsDLL mapping not found")
|
||||||
|
|
||||||
|
|
||||||
|
def live(static):
|
||||||
|
return base + (static - 0x180000000)
|
||||||
|
|
||||||
|
|
||||||
|
if rd(live(0x180026FEA), 5) != bytes.fromhex("ba75750000"):
|
||||||
|
sys.exit("SANITY FAILED - wrong base")
|
||||||
|
print(f" CardsDLL base = {base:#x} (sanity ok)")
|
||||||
|
|
||||||
|
owner = q(live(0x1802E6398))
|
||||||
|
sentinel = owner + 0x160C8
|
||||||
|
root = q(owner + 0x160D8)
|
||||||
|
|
||||||
|
# Known record fields, offset -> (name, width)
|
||||||
|
FIELDS = {
|
||||||
|
0x08: ("id", 8),
|
||||||
|
0x18: ("resourceId/definitionId", 4),
|
||||||
|
0x1C: ("assetId", 4),
|
||||||
|
0x20: ("cardassetid", 4),
|
||||||
|
0x38: ("discardValue", 4),
|
||||||
|
0x4C: ("cardtype", 4),
|
||||||
|
0x50: ("cardsubtypeid", 4),
|
||||||
|
0x5C: ("itemState", 4),
|
||||||
|
0x60: ("category(club slot)", 4),
|
||||||
|
0x8C: ("contract", 4),
|
||||||
|
0x94: ("teamid", 4),
|
||||||
|
0xB4: ("rating", 4),
|
||||||
|
0xB8: ("wire category", 1),
|
||||||
|
0xBA: ("year", 2),
|
||||||
|
0x148: ("nation", 4),
|
||||||
|
0x154: ("leagueId", 4),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def walk(node, out):
|
||||||
|
if not node or node == sentinel:
|
||||||
|
return
|
||||||
|
walk(q(node + 0x00), out)
|
||||||
|
# The record is EMBEDDED at node+0x28 — NOT a pointer stored there.
|
||||||
|
out.append((struct.unpack("<q", rd(node + 0x20, 8))[0], node + 0x28))
|
||||||
|
walk(q(node + 0x08), out)
|
||||||
|
|
||||||
|
|
||||||
|
nodes = []
|
||||||
|
walk(root, nodes)
|
||||||
|
recs = {k: v for k, v in nodes}
|
||||||
|
|
||||||
|
found = [(w, recs[w]) for w in WANT if w in recs]
|
||||||
|
if len(found) < 2:
|
||||||
|
sys.exit(f" need two resident kit records, found {[w for w, _ in found]}")
|
||||||
|
|
||||||
|
(id_a, ptr_a), (id_b, ptr_b) = found[0], found[1]
|
||||||
|
a = rd(ptr_a, 0x180)
|
||||||
|
b = rd(ptr_b, 0x180)
|
||||||
|
print(f" A = {id_a} @ {ptr_a:#x}")
|
||||||
|
print(f" B = {id_b} @ {ptr_b:#x}")
|
||||||
|
|
||||||
|
print("\n --- fields the clone query consumes ---")
|
||||||
|
for off in (0x94, 0x5C, 0xBA):
|
||||||
|
name = FIELDS[off][0]
|
||||||
|
w = FIELDS[off][1]
|
||||||
|
va = int.from_bytes(a[off : off + w], "little")
|
||||||
|
vb = int.from_bytes(b[off : off + w], "little")
|
||||||
|
flag = "" if va != vb else " <== IDENTICAL"
|
||||||
|
print(f" +{off:#05x} {name:24} A={va:<12} B={vb:<12}{flag}")
|
||||||
|
|
||||||
|
print("\n --- every differing byte range ---")
|
||||||
|
diffs = [i for i in range(0x180) if a[i] != b[i]]
|
||||||
|
runs = []
|
||||||
|
for i in diffs:
|
||||||
|
if runs and i == runs[-1][1] + 1:
|
||||||
|
runs[-1][1] = i
|
||||||
|
else:
|
||||||
|
runs.append([i, i])
|
||||||
|
for s, e in runs:
|
||||||
|
named_field = next(
|
||||||
|
(n for o, (n, w) in FIELDS.items() if o <= s < o + w), "(unmapped)"
|
||||||
|
)
|
||||||
|
va = int.from_bytes(a[s : e + 1], "little")
|
||||||
|
vb = int.from_bytes(b[s : e + 1], "little")
|
||||||
|
print(f" +{s:#05x}..{e:#05x} {named_field:24} A={va:<12} B={vb}")
|
||||||
|
print(f"\n {len(diffs)} differing bytes in {len(runs)} runs")
|
||||||
|
|
||||||
|
print("\n --- known fields, side by side ---")
|
||||||
|
for off in sorted(FIELDS):
|
||||||
|
name, w = FIELDS[off]
|
||||||
|
va = int.from_bytes(a[off : off + w], "little")
|
||||||
|
vb = int.from_bytes(b[off : off + w], "little")
|
||||||
|
mark = " DIFFERS" if va != vb else ""
|
||||||
|
print(f" +{off:#05x} {name:24} A={va:<12} B={vb:<12}{mark}")
|
||||||
@@ -41,6 +41,20 @@ pub struct Fifa17CardIdentity {
|
|||||||
/// FIFA card-art class. Players default to `asset_id`; kit definitions carry
|
/// FIFA card-art class. Players default to `asset_id`; kit definitions carry
|
||||||
/// the verified `fcc_kitcards.cardassetid` value (`35`).
|
/// the verified `fcc_kitcards.cardassetid` value (`35`).
|
||||||
pub card_asset_id: u32,
|
pub card_asset_id: u32,
|
||||||
|
/// The wire `assetId` for a CLUB item (record `+0x20`), which is family
|
||||||
|
/// specific and is NOT the carddbid: a kit carries the art class from
|
||||||
|
/// `fcc_kitcards.assetid` (`14` home/third band, `15` away band), a badge
|
||||||
|
/// carries its team id, a stadium and a ball their own asset number.
|
||||||
|
///
|
||||||
|
/// Distinct from [`Self::asset_id`], which for these definitions is the
|
||||||
|
/// carddbid and is what `resource_id` is derived from — so the two cannot be
|
||||||
|
/// the same field. Shipping the carddbid here is what left the client
|
||||||
|
/// holding `assetId 6300006` at record `+0x20` where its own table says
|
||||||
|
/// `14`, with both pre-match kit tiles rendering identically.
|
||||||
|
///
|
||||||
|
/// Defaults to `asset_id` when a catalog does not specify it, which is the
|
||||||
|
/// pre-existing behaviour and is correct for every non-club kind.
|
||||||
|
pub club_asset_id: u32,
|
||||||
/// Source team id for a club kit, or a manager's real club. Zero for content
|
/// Source team id for a club kit, or a manager's real club. Zero for content
|
||||||
/// kinds that do not use it.
|
/// kinds that do not use it.
|
||||||
pub team_id: i64,
|
pub team_id: i64,
|
||||||
@@ -206,6 +220,9 @@ struct RawCard {
|
|||||||
/// Separate card-art id for non-player definitions; absent → `asset_id`.
|
/// Separate card-art id for non-player definitions; absent → `asset_id`.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
card_asset_id: Option<u32>,
|
card_asset_id: Option<u32>,
|
||||||
|
/// Wire `assetId` for a club item; defaults to `asset_id`. See
|
||||||
|
/// [`Fifa17CardIdentity::club_asset_id`].
|
||||||
|
club_asset_id: Option<u32>,
|
||||||
/// Source team id for a kit or manager definition; absent → `0`.
|
/// Source team id for a kit or manager definition; absent → `0`.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
team_id: Option<i64>,
|
team_id: Option<i64>,
|
||||||
@@ -287,6 +304,7 @@ impl Fifa17CardCatalog {
|
|||||||
kind: ContentKind::from_str(&rc.kind),
|
kind: ContentKind::from_str(&rc.kind),
|
||||||
subtype: rc.subtype,
|
subtype: rc.subtype,
|
||||||
card_asset_id: rc.card_asset_id.unwrap_or(rc.asset_id),
|
card_asset_id: rc.card_asset_id.unwrap_or(rc.asset_id),
|
||||||
|
club_asset_id: rc.club_asset_id.unwrap_or(rc.asset_id),
|
||||||
team_id: rc.team_id.unwrap_or(0),
|
team_id: rc.team_id.unwrap_or(0),
|
||||||
category: rc.category.unwrap_or(0),
|
category: rc.category.unwrap_or(0),
|
||||||
year: rc.year.unwrap_or(0),
|
year: rc.year.unwrap_or(0),
|
||||||
@@ -380,6 +398,49 @@ mod tests {
|
|||||||
assert_eq!(cat.lookup("card_missing"), None);
|
assert_eq!(cat.lookup("card_missing"), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A club item's wire `assetId` is family specific and is NOT the carddbid.
|
||||||
|
///
|
||||||
|
/// Regression: the catalog shipped `asset_id` (the carddbid) as the wire
|
||||||
|
/// `assetId`, so the client held `assetId 6300006` at record `+0x20` where
|
||||||
|
/// its own `fcc_kitcards` says `14`, and both pre-match kit tiles rendered
|
||||||
|
/// identically. `resource_id` is derived from `asset_id`, and every home kit
|
||||||
|
/// shares art class 14, so the two genuinely cannot be one field.
|
||||||
|
#[test]
|
||||||
|
fn club_items_carry_their_own_wire_asset_id_distinct_from_the_carddbid() {
|
||||||
|
let cat = Fifa17CardCatalog::from_json_str(
|
||||||
|
r#"{"schema_version":1,"game":"fifa17","cards":{
|
||||||
|
"fifa17_6300006":{"asset_id":6300006,"kind":"kit","subtype":9,
|
||||||
|
"card_asset_id":35,"club_asset_id":14,"team_id":21,"category":2,"year":0},
|
||||||
|
"fifa17_6400003":{"asset_id":6400003,"kind":"kit","subtype":9,
|
||||||
|
"card_asset_id":35,"club_asset_id":15,"team_id":21,"category":3,"year":0},
|
||||||
|
"fifa17_20801":{"asset_id":20801}
|
||||||
|
}}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let home = cat.lookup("fifa17_6300006").unwrap();
|
||||||
|
let away = cat.lookup("fifa17_6400003").unwrap();
|
||||||
|
|
||||||
|
// resourceId stays the carddbid — it is what the staff/kit merge keys on.
|
||||||
|
assert_eq!(home.resource_id, 6300006);
|
||||||
|
assert_eq!(away.resource_id, 6400003);
|
||||||
|
// The card frame art is shared by the whole kit family.
|
||||||
|
assert_eq!(home.card_asset_id, 35);
|
||||||
|
assert_eq!(away.card_asset_id, 35);
|
||||||
|
// The art class is what distinguishes home from away on the wire.
|
||||||
|
assert_eq!(home.club_asset_id, 14);
|
||||||
|
assert_eq!(away.club_asset_id, 15);
|
||||||
|
assert_ne!(
|
||||||
|
home.club_asset_id, away.club_asset_id,
|
||||||
|
"home and away must not present the same assetId"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Absent: defaults to asset_id, which is correct for every non-club kind
|
||||||
|
// and preserves the behaviour of a catalog that predates the field.
|
||||||
|
let player = cat.lookup("fifa17_20801").unwrap();
|
||||||
|
assert_eq!(player.club_asset_id, 20801);
|
||||||
|
}
|
||||||
|
|
||||||
/// The non-player definition fields a consumable needs, and the ABSENCE that
|
/// The non-player definition fields a consumable needs, and the ABSENCE that
|
||||||
/// must stay an absence: a defaulted `amount` would draw "-1" on the card and
|
/// must stay an absence: a defaulted `amount` would draw "-1" on the card and
|
||||||
/// a defaulted `contract` would invent the number of matches a card grants.
|
/// a defaulted `contract` would invent the number of matches a card grants.
|
||||||
|
|||||||
@@ -2128,7 +2128,9 @@ impl ItemIdentityResolver for Fifa17IdentityResolver {
|
|||||||
}
|
}
|
||||||
Some(Fifa17KitIdentity {
|
Some(Fifa17KitIdentity {
|
||||||
item_id: self.wire_for(item)?,
|
item_id: self.wire_for(item)?,
|
||||||
asset_id: ident.asset_id,
|
// The club-item wire `assetId` is family specific and is NOT the
|
||||||
|
// carddbid — see `Fifa17CardIdentity::club_asset_id`.
|
||||||
|
asset_id: ident.club_asset_id,
|
||||||
resource_id: ident.resource_id,
|
resource_id: ident.resource_id,
|
||||||
card_asset_id: ident.card_asset_id,
|
card_asset_id: ident.card_asset_id,
|
||||||
subtype: ident.subtype,
|
subtype: ident.subtype,
|
||||||
|
|||||||
+28
-16
@@ -182,9 +182,15 @@ DISPOSABLE_CARD = "fifa17_232273" # Nelson Atiagli LB 51, rareflag 1
|
|||||||
# Two authoritative modern kit-card definitions for one real source team. These
|
# Two authoritative modern kit-card definitions for one real source team. These
|
||||||
# are staging fixtures derived from fcc_kitcards, not synthetic FIFA identities.
|
# are staging fixtures derived from fcc_kitcards, not synthetic FIFA identities.
|
||||||
KIT_TEAM_ID = 21
|
KIT_TEAM_ID = 21
|
||||||
|
# The trailing value is the client's own `fcc_kitcards.assetid`, the wire
|
||||||
|
# `assetId` (record +0x20). It is the ART CLASS, not the carddbid: every
|
||||||
|
# 63xxxxx (home/third) kit carries 14 and every 64xxxxx (away) kit carries 15,
|
||||||
|
# per club_items.json and fifa17-kit-map.json's band_x_assetid evidence
|
||||||
|
# (6300000/14 x828, 6400000/15 x654). Shipping the carddbid here left both
|
||||||
|
# pre-match kit tiles rendering identically.
|
||||||
STAGING_KITS = [
|
STAGING_KITS = [
|
||||||
("home", "owned-a-kit-home", "fifa17_6300006", 6_300_006),
|
("home", "owned-a-kit-home", "fifa17_6300006", 6_300_006, 14),
|
||||||
("away", "owned-a-kit-away", "fifa17_6400003", 6_400_003),
|
("away", "owned-a-kit-away", "fifa17_6400003", 6_400_003, 15),
|
||||||
]
|
]
|
||||||
|
|
||||||
# The remaining club-item families, so the rig exercises EVERY ownable class
|
# The remaining club-item families, so the rig exercises EVERY ownable class
|
||||||
@@ -198,11 +204,15 @@ STAGING_KITS = [
|
|||||||
# badge 39, logo 40), which is what the importer gates on. A league logo has no
|
# badge 39, logo 40), which is what the importer gates on. A league logo has no
|
||||||
# equipped slot, so it is owned as generic `misc` content.
|
# equipped slot, so it is owned as generic `misc` content.
|
||||||
STAGING_CLUB_ITEMS = [
|
STAGING_CLUB_ITEMS = [
|
||||||
# (slot, owned_id, card_id, resource_id, kind, subtype, card_asset_id, team_id)
|
# (slot, owned_id, card_id, resource_id, kind, subtype, card_asset_id, team_id,
|
||||||
("badge", "owned-a-badge", "fifa17_6000005", 6_000_005, "badge", 11, 39, 21),
|
# club_asset_id)
|
||||||
("ball", "owned-a-ball", "fifa17_8120194", 8_120_194, "ball", 30, 37, None),
|
# club_asset_id is the wire `assetId` from club_items.json, family specific
|
||||||
("stadium", "owned-a-stadium", "fifa17_6200000", 6_200_000, "stadium", 10, 36, None),
|
# and never the carddbid: badge 6000005 -> its teamid 21, ball 8120194 -> 100,
|
||||||
(None, "owned-a-leaguelogo", "fifa17_8010015", 8_010_015, "misc", 31, 40, None),
|
# stadium 6200000 -> 1. A league logo has no equipped slot and keeps its own.
|
||||||
|
("badge", "owned-a-badge", "fifa17_6000005", 6_000_005, "badge", 11, 39, 21, 21),
|
||||||
|
("ball", "owned-a-ball", "fifa17_8120194", 8_120_194, "ball", 30, 37, None, 100),
|
||||||
|
("stadium", "owned-a-stadium", "fifa17_6200000", 6_200_000, "stadium", 10, 36, None, 1),
|
||||||
|
(None, "owned-a-leaguelogo", "fifa17_8010015", 8_010_015, "misc", 31, 40, None, None),
|
||||||
]
|
]
|
||||||
|
|
||||||
# The club manager. FIFA refuses to start a match without one ("your player or
|
# The club manager. FIFA refuses to start a match without one ("your player or
|
||||||
@@ -524,7 +534,7 @@ def materialise(lay: Layout) -> None:
|
|||||||
with open(safe_path(lay.catalog)) as fh:
|
with open(safe_path(lay.catalog)) as fh:
|
||||||
catalog = json.load(fh)
|
catalog = json.load(fh)
|
||||||
existing = {definition["id"] for definition in definitions}
|
existing = {definition["id"] for definition in definitions}
|
||||||
for _slot, _owned_id, card_id, resource_id in STAGING_KITS:
|
for _slot, _owned_id, card_id, resource_id, club_asset_id in STAGING_KITS:
|
||||||
if card_id not in existing:
|
if card_id not in existing:
|
||||||
definitions.append({
|
definitions.append({
|
||||||
"id": card_id,
|
"id": card_id,
|
||||||
@@ -550,10 +560,11 @@ def materialise(lay: Layout) -> None:
|
|||||||
"kind": "kit",
|
"kind": "kit",
|
||||||
"subtype": 9,
|
"subtype": 9,
|
||||||
"card_asset_id": 35,
|
"card_asset_id": 35,
|
||||||
|
"club_asset_id": club_asset_id,
|
||||||
"team_id": KIT_TEAM_ID,
|
"team_id": KIT_TEAM_ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _slot, _owned, card_id, resource_id, kind, subtype, art, team in STAGING_CLUB_ITEMS:
|
for _slot, _owned, card_id, resource_id, kind, subtype, art, team, club_asset in STAGING_CLUB_ITEMS:
|
||||||
if card_id not in existing:
|
if card_id not in existing:
|
||||||
definitions.append({
|
definitions.append({
|
||||||
"id": card_id,
|
"id": card_id,
|
||||||
@@ -580,6 +591,8 @@ def materialise(lay: Layout) -> None:
|
|||||||
"subtype": subtype,
|
"subtype": subtype,
|
||||||
"card_asset_id": art,
|
"card_asset_id": art,
|
||||||
}
|
}
|
||||||
|
if club_asset is not None:
|
||||||
|
entry["club_asset_id"] = club_asset
|
||||||
if team is not None:
|
if team is not None:
|
||||||
entry["team_id"] = team
|
entry["team_id"] = team
|
||||||
catalog["cards"][card_id] = entry
|
catalog["cards"][card_id] = entry
|
||||||
@@ -701,7 +714,7 @@ def assert_seed_cards_resolvable(lay: Layout, real_club: dict | None) -> None:
|
|||||||
wanted = set(
|
wanted = set(
|
||||||
[card for _, card in SELLER_SQUAD_CARDS]
|
[card for _, card in SELLER_SQUAD_CARDS]
|
||||||
+ [DISPOSABLE_CARD]
|
+ [DISPOSABLE_CARD]
|
||||||
+ [card for _, _, card, _ in STAGING_KITS]
|
+ [card for _, _, card, _, _ in STAGING_KITS]
|
||||||
+ [STAGING_MANAGER["card_id"]]
|
+ [STAGING_MANAGER["card_id"]]
|
||||||
)
|
)
|
||||||
what = f"all {len(wanted)} fixture seed card ids"
|
what = f"all {len(wanted)} fixture seed card ids"
|
||||||
@@ -712,7 +725,7 @@ def assert_seed_cards_resolvable(lay: Layout, real_club: dict | None) -> None:
|
|||||||
conn.execute("SELECT DISTINCT card_id FROM owned_cards")}
|
conn.execute("SELECT DISTINCT card_id FROM owned_cards")}
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
wanted |= {card for _, _, card, _ in STAGING_KITS}
|
wanted |= {card for _, _, card, _, _ in STAGING_KITS}
|
||||||
wanted.add(STAGING_MANAGER["card_id"])
|
wanted.add(STAGING_MANAGER["card_id"])
|
||||||
what = (f"all {len(wanted)} distinct card ids owned by the real club "
|
what = (f"all {len(wanted)} distinct card ids owned by the real club "
|
||||||
"(plus the kit and manager fixtures)")
|
"(plus the kit and manager fixtures)")
|
||||||
@@ -983,7 +996,7 @@ def seed_core_db(lay: Layout, real_club: dict | None) -> None:
|
|||||||
# calling a kit a player, and Core is the ownership authority.
|
# calling a kit a player, and Core is the ownership authority.
|
||||||
owned = [
|
owned = [
|
||||||
(owned_id, seller_club, card_id, "kit", TS)
|
(owned_id, seller_club, card_id, "kit", TS)
|
||||||
for _slot, owned_id, card_id, _resource_id in STAGING_KITS
|
for _slot, owned_id, card_id, _resource_id, _ca in STAGING_KITS
|
||||||
]
|
]
|
||||||
owned.append(
|
owned.append(
|
||||||
(
|
(
|
||||||
@@ -996,7 +1009,7 @@ def seed_core_db(lay: Layout, real_club: dict | None) -> None:
|
|||||||
)
|
)
|
||||||
owned.extend(
|
owned.extend(
|
||||||
(owned_id, seller_club, card_id, kind, TS)
|
(owned_id, seller_club, card_id, kind, TS)
|
||||||
for _slot, owned_id, card_id, _rid, kind, _st, _art, _team
|
for _slot, owned_id, card_id, _rid, kind, _st, _art, _team, _ca
|
||||||
in STAGING_CLUB_ITEMS
|
in STAGING_CLUB_ITEMS
|
||||||
)
|
)
|
||||||
if real_club is None:
|
if real_club is None:
|
||||||
@@ -1010,7 +1023,6 @@ def seed_core_db(lay: Layout, real_club: dict | None) -> None:
|
|||||||
"content_kind, acquired_at) VALUES (?, ?, ?, 0, ?, ?)",
|
"content_kind, acquired_at) VALUES (?, ?, ?, 0, ?, ?)",
|
||||||
owned,
|
owned,
|
||||||
)
|
)
|
||||||
|
|
||||||
if real_club is None:
|
if real_club is None:
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT INTO squads (id, club_id, name, formation, created_at, "
|
"INSERT INTO squads (id, club_id, name, formation, created_at, "
|
||||||
@@ -1034,14 +1046,14 @@ def seed_core_db(lay: Layout, real_club: dict | None) -> None:
|
|||||||
"VALUES (?, ?, ?, ?)",
|
"VALUES (?, ?, ?, ?)",
|
||||||
[
|
[
|
||||||
(seller_club, f"{slot}_kit", owned_id, TS)
|
(seller_club, f"{slot}_kit", owned_id, TS)
|
||||||
for slot, owned_id, _card_id, _resource_id in STAGING_KITS
|
for slot, owned_id, _card_id, _resource_id, _ca in STAGING_KITS
|
||||||
]
|
]
|
||||||
# badge / ball / stadium are slot-keyed exactly like the kits.
|
# badge / ball / stadium are slot-keyed exactly like the kits.
|
||||||
# A league logo has no slot, so it stays owned-but-unequipped —
|
# A league logo has no slot, so it stays owned-but-unequipped —
|
||||||
# which is itself worth exercising.
|
# which is itself worth exercising.
|
||||||
+ [
|
+ [
|
||||||
(seller_club, slot, owned_id, TS)
|
(seller_club, slot, owned_id, TS)
|
||||||
for slot, owned_id, _c, _r, _k, _s, _a, _t in STAGING_CLUB_ITEMS
|
for slot, owned_id, _c, _r, _k, _s, _a, _t, _ca in STAGING_CLUB_ITEMS
|
||||||
if slot is not None
|
if slot is not None
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user