fix(club): hide only ACTIVELY-LISTED cards, not the whole trade pile
Keying the club exclusion on the `trade` pile put cards in limbo: the pile can hold cards with no active listing (a bare "Place on Transfer Market" move, or a listing later cancelled/sold), and `/tradePile` renders ONLY active listings — so those cards were invisible in BOTH views. Live prod had 5 trade-pile rows but 1 active listing, so 4 owned cards had no reachable screen (clubPlayers 1966->1961). Key on the ACTIVE LISTING instead (market store `core_item_id` of `state=active`). This is self-healing: the moment a listing stops being active the card is back in the club, with no extra transition to maintain and no need to invent an "unlisted transfer-list" wire shape (`tradeState` has no verified spelling for that state, and guessing enum spellings is the documented client-freeze class). A bare pile move therefore no longer hides a card. That is deliberate: our `/tradePile` shows only active listings, so hiding on the move alone would reintroduce the limbo it is meant to prevent. Verified live: clubPlayers 1961 -> 1965 (exactly the one listed card hidden, the 4 stranded cards recovered); listed wire still absent from /club; counts and tradePile unchanged. 14 targets green + clippy clean.
This commit is contained in:
@@ -1118,10 +1118,9 @@ pub struct ClubDeps<'a> {
|
||||
pub core: &'a dyn CoreAccess,
|
||||
pub entities: &'a Fifa17Entities,
|
||||
pub assets: &'a (dyn ItemIdentityResolver + Send + Sync),
|
||||
/// Core owned-instance ids that are NOT in the club view — currently the
|
||||
/// transfer (`trade`) pile. In FIFA a card on the transfer list has LEFT the
|
||||
/// club, so it must not also appear here. Empty = show everything Core owns
|
||||
/// (an item with no recorded pile defaults to the club).
|
||||
/// Core owned-instance ids that are NOT in the club view — the cards with an
|
||||
/// ACTIVE transfer-market listing. In FIFA a listed card has LEFT the club, so
|
||||
/// it must not also appear here. Empty = show everything Core owns.
|
||||
pub hidden: &'a std::collections::HashSet<String>,
|
||||
}
|
||||
|
||||
@@ -1200,7 +1199,7 @@ pub fn handle_club(query: &str, deps: &ClubDeps<'_>) -> (WireResponse, ClubLog)
|
||||
|
||||
// Host-side filters Core cannot express:
|
||||
// * "Special" (rare=SP): rareflag lives in the FIFA catalog, not Core.
|
||||
// * pile exclusion: the transfer pile is host-owned state.
|
||||
// * listed-card exclusion: transfer-market listings are host-owned state.
|
||||
// Either way Core must NOT paginate — it would paginate the unfiltered set
|
||||
// and return short pages. So fetch everything matching the OTHER filters,
|
||||
// exclude/shape locally, then paginate the filtered set here. When neither
|
||||
@@ -2761,22 +2760,31 @@ impl Server {
|
||||
json_status(status, &body)
|
||||
}
|
||||
|
||||
/// Core owned-instance ids that are NOT part of the CLUB view: currently the
|
||||
/// transfer (`trade`) pile. In FIFA a listed card has LEFT the club, so it must
|
||||
/// not appear in `/club` or the hub's `clubPlayers` tally while it sits on the
|
||||
/// transfer list. Only an EXPLICIT non-club pile hides an item — an item with
|
||||
/// no recorded pile defaults to the club, so an imported profile is unaffected.
|
||||
/// Empty when no economy services are wired (bare test server), and a pile-store
|
||||
/// Core owned-instance ids that are NOT part of the CLUB view: the cards with
|
||||
/// an ACTIVE transfer-market listing. In FIFA a listed card has left the club,
|
||||
/// so it must not appear in `/club` or the hub's `clubPlayers` tally while it
|
||||
/// is on sale.
|
||||
///
|
||||
/// Keyed on the ACTIVE LISTING, deliberately NOT on the `trade` pile. The pile
|
||||
/// can hold cards with no listing (a bare "Place on Transfer Market" move, or a
|
||||
/// listing that was cancelled/sold), and `/tradePile` renders ONLY active
|
||||
/// listings — so hiding the whole pile would make those cards invisible in BOTH
|
||||
/// views. Keying on the listing makes visibility self-healing: the moment a
|
||||
/// listing stops being active the card is back in the club, with no extra
|
||||
/// transition to maintain and no need to invent an "unlisted transfer-list"
|
||||
/// wire shape (`tradeState` has no verified spelling for that state).
|
||||
///
|
||||
/// Empty when no economy services are wired (bare test server); a market-store
|
||||
/// read failure degrades to showing everything (never hides inventory silently).
|
||||
fn club_hidden_ids(&self) -> std::collections::HashSet<String> {
|
||||
let Some(svc) = self.economy.as_ref() else {
|
||||
return std::collections::HashSet::new();
|
||||
};
|
||||
let (bridge, piles) = (svc.bridge.clone(), svc.piles.clone());
|
||||
match bridge.block_on(async move { piles.list_by_pile("trade").await }) {
|
||||
Ok(ids) => ids.into_iter().collect(),
|
||||
let (bridge, market) = (svc.bridge.clone(), svc.market.clone());
|
||||
match bridge.block_on(async move { market.query_listings("active").await }) {
|
||||
Ok(listings) => listings.into_iter().filter_map(|l| l.core_item_id).collect(),
|
||||
Err(e) => {
|
||||
eprintln!("utas-host WARN pile read failed (club shows all): {e}");
|
||||
eprintln!("utas-host WARN market read failed (club shows all): {e}");
|
||||
std::collections::HashSet::new()
|
||||
}
|
||||
}
|
||||
@@ -2784,9 +2792,9 @@ impl Server {
|
||||
|
||||
/// `GET …/hub` — the FUT hub tile counts, owned in Rust (no Python). Derived
|
||||
/// from authoritative state: `clubPlayers` is the count of owned PLAYER cards
|
||||
/// in Core that are in the club (transfer-pile cards excluded — they have left
|
||||
/// the club), and the auction / tradePile counts are the user's active listings
|
||||
/// in the durable market store. `clubPlayers` may be lower than the Python
|
||||
/// in Core that are in the club (actively-listed cards excluded — they have
|
||||
/// left the club), and the auction / tradePile counts are the user's active
|
||||
/// listings in the durable market store. `clubPlayers` may be lower than the Python
|
||||
/// oracle's profile count by exactly the deferred (unnameable Legend)
|
||||
/// instances — DIFFERENT-BY-DESIGN, since deferred cards are not owned in
|
||||
/// Core. Fail-closed on Core error (503); a market-store read failure degrades
|
||||
|
||||
Reference in New Issue
Block a user