fix(fifa17): keep empty My Packs group client-safe

When the account owns zero unopened packs, store_catalog() emits a synthetic `mypacks` group placeholder (id 65534, absent from PACK_CATALOG). Change its state from "inactive" to "active".

Root cause (bug 6c): FIFA 17's Store/Scaleform path resolves the `mypacks` category even with zero unopened packs (category chosen client-side via the movie's CATEGORY_ID -> screen+0x290; no server field gates it). CardsDLL FUN_1800147f0 then dereferences the resolved group with no null guard, so an absent group crashes the client (CardsDLL+0x14882, [NULL+0x48], minidump-confirmed). An inactive placeholder avoids the crash but makes the Store report the pack unavailable on entry and bounce to the Hub; an active placeholder lets the Store open normally.

65534 stays economy-safe: pack_by_id() returns None, so store_buy()/purchased_items() cannot open it or grant items/coins, and grant_unopened_pack() rejects it. Explicit selection is rejected client-side ("This pack is no longer available") and sends no backend request. This is a FIFA-17 client-compatibility shim (P2), not an EA-authentic representation, confined to the FIFA-17 backend (not OpenFUT Core). A clean zero-pack UX needs a client-side fix (docs/plans/FIFA17_EMPTY_MYPACKS_CLIENT_FIX.md).

Adds regression tests (test_empty_mypacks.py): empty -> one active 65534 placeholder (absent from PACK_CATALOG); non-empty [70] -> no placeholder, genuine pack shown; economy safety; normal packs 1/5/6/7 untouched.
This commit is contained in:
funman300
2026-08-13 01:08:37 +00:00
parent 54ad9e8f79
commit f42279f869
2 changed files with 162 additions and 7 deletions
+26 -7
View File
@@ -3426,12 +3426,31 @@ def store_catalog(h):
if owned:
packs.append(_pack_body(owned, idx, owned=True))
if not owned_ids:
# GOTO_STORE_MYPACK resolves the hard-coded `mypacks` group before it
# renders rows. If the group is absent FIFA falls back to Bronze and
# shows the empty-category dialog over the wrong tab. Retain an inactive
# zero-item sentinel so the destination resolves, while state != active
# keeps it out of the visible row list. Its id is deliberately absent
# from PACK_CATALOG, so both purchase/open handlers reject it as well.
# EMPTY MY PACKS -- FIFA 17 client-compatibility workaround (bug 6c, P2).
#
# The Store/Scaleform path RESOLVES the `mypacks` category even when the
# account owns zero unopened packs (the category is chosen client-side from
# the movie's CATEGORY_ID -> screen+0x290; no server field gates it).
# CardsDLL FUN_1800147f0 then dereferences the resolved group with NO null
# guard, so if no `mypacks` group exists the client CRASHES
# (CardsDLL_Win64_retail.dll+0x14882, read of [NULL+0x48] -- confirmed by
# minidump). We therefore MUST emit a `mypacks` group when empty.
#
# state="inactive" avoids the crash but makes the client report the pack
# unavailable immediately on Store entry and bounce to the Hub. state="active"
# keeps the group structurally valid AND lets the Store open normally; the
# empty tile renders as "0 items" and an explicit open is rejected
# CLIENT-SIDE ("This pack is no longer available") -- it sends NO backend
# request and mutates nothing.
#
# id 65534 is deliberately ABSENT from PACK_CATALOG, so pack_by_id() returns
# None and store_buy()/purchased_items() cannot open it, grant items/coins,
# or add it to unopenedPackIds. This is a compatibility shim for FIFA 17
# client behavior, NOT an EA-authentic empty-My-Packs representation, and it
# is FIFA17-specific (do not lift into game-independent Core). A fully clean
# zero-pack UX requires a client-side fix -- see
# docs/plans/FIFA17_EMPTY_MYPACKS_CLIENT_FIX.md and the evidence in
# docs/evidence/STORE_TILE_6C.md / FIFA17_EMPTY_MYPACKS_CLIENT_CONTRACT.md.
sentinel = {
"id": 65534,
"name": "",
@@ -3441,7 +3460,7 @@ def store_catalog(h):
"specialChance": 0.0,
}
empty = _pack_body(sentinel, 1, owned=True)
empty["state"] = "inactive"
empty["state"] = "active"
empty["unopened"] = False
packs.append(empty)
return 200, {"purchase": packs, "timestamp": 1596326400}