From c88c8ced15739b93c308dbe74f59d3b4bf863bcf Mon Sep 17 00:00:00 2001 From: dnviti Date: Mon, 22 Dec 2025 22:15:32 +0100 Subject: [PATCH] feat: Implement resume match button, enhance card image loading, and improve server-side logging and deck handling. --- src/client/dev-dist/sw.js | 2 +- src/client/src/components/CardVisual.tsx | 18 ++++++++++++----- .../modules/tournament/TournamentManager.tsx | 10 ++++++++++ src/server/game/RulesEngine.ts | 3 ++- src/server/managers/GameManager.ts | 20 +++++++++++++++++-- src/server/managers/TournamentManager.ts | 7 ++++++- 6 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/client/dev-dist/sw.js b/src/client/dev-dist/sw.js index b35b2f5..7276bc2 100644 --- a/src/client/dev-dist/sw.js +++ b/src/client/dev-dist/sw.js @@ -82,7 +82,7 @@ define(['./workbox-5a5d9309'], (function (workbox) { 'use strict'; "revision": "3ca0b8505b4bec776b69afdba2768812" }, { "url": "index.html", - "revision": "0.ca9afac9bpo" + "revision": "0.99c1h4jant" }], {}); workbox.cleanupOutdatedCaches(); workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { diff --git a/src/client/src/components/CardVisual.tsx b/src/client/src/components/CardVisual.tsx index 6e2a136..805ffa0 100644 --- a/src/client/src/components/CardVisual.tsx +++ b/src/client/src/components/CardVisual.tsx @@ -61,10 +61,14 @@ export const CardVisual: React.FC = ({ // Robustly resolve Image Source based on viewMode let src = card.imageUrl || card.image; + // Use top-level properties if available (common in DraftCard / Game Card objects) + const setCode = card.setCode || card.set || card.definition?.set; + const cardId = card.scryfallId || card.definition?.id; + if (viewMode === 'cutout') { // Priority 1: Local Cache (standard naming convention) - PREFERRED BY USER - if (card.definition?.set && card.definition?.id) { - src = `/cards/images/${card.definition.set}/crop/${card.definition.id}.jpg`; + if (setCode && cardId) { + src = `/cards/images/${setCode}/crop/${cardId}.jpg`; } // Priority 2: Direct Image URIs (if available) - Fallback else if (card.image_uris?.art_crop || card.image_uris?.crop) { @@ -77,15 +81,19 @@ export const CardVisual: React.FC = ({ else if (card.definition?.card_faces?.[0]?.image_uris?.art_crop) { src = card.definition.card_faces[0].image_uris.art_crop; } - // Priority 4: If card has a manually set image property that looks like a crop (less reliable) + // Priority 4: Server-provided explicit property + else if (card.imageArtCrop) { + src = card.imageArtCrop; + } // Fallback: If no crop found, src remains whatever it was (likely full) } else { // Normal / Full View + // Priority 1: Local Cache (standard naming convention) - PREFERRED - if (card.definition?.set && card.definition?.id) { + if (setCode && cardId) { // Check if we want standard full image path - src = `/cards/images/${card.definition.set}/full/${card.definition.id}.jpg`; + src = `/cards/images/${setCode}/full/${cardId}.jpg`; } // Priority 2: Direct Image URIs else if (card.image_uris?.normal) { diff --git a/src/client/src/modules/tournament/TournamentManager.tsx b/src/client/src/modules/tournament/TournamentManager.tsx index 1694394..3d1daa8 100644 --- a/src/client/src/modules/tournament/TournamentManager.tsx +++ b/src/client/src/modules/tournament/TournamentManager.tsx @@ -107,6 +107,16 @@ export const TournamentManager: React.FC = ({ tournament Play Match )} + + {/* Resume Button */} + {match.status === 'in_progress' && isMyMatch && ( + + )} ); })} diff --git a/src/server/game/RulesEngine.ts b/src/server/game/RulesEngine.ts index 6a8ae0e..6ea04c7 100644 --- a/src/server/game/RulesEngine.ts +++ b/src/server/game/RulesEngine.ts @@ -615,7 +615,8 @@ export class RulesEngine { console.log(`Player ${playerId} draws ${card.name}`); } else { // Empty library loss? - console.log(`Player ${playerId} attempts to draw from empty library.`); + // Empty library loss? + console.warn(`[RulesEngine] Player ${playerId} attempts to draw from empty library. (Deck size: 0)`); } } diff --git a/src/server/managers/GameManager.ts b/src/server/managers/GameManager.ts index 55e4fb3..ec38609 100644 --- a/src/server/managers/GameManager.ts +++ b/src/server/managers/GameManager.ts @@ -61,6 +61,8 @@ export class GameManager extends EventEmitter { // Track rooms where a bot is currently "thinking" to avoid double-queuing private thinkingRooms: Set = new Set(); + // Throttle logs + private lastBotLog: Record = {}; // Helper to trigger bot actions if game is stuck or just started public triggerBotCheck(roomId: string): StrictGameState | null { @@ -86,7 +88,11 @@ export class GameManager extends EventEmitter { // If it is a Bot's turn to have priority, and we aren't already processing if (priorityPlayer?.isBot && !this.thinkingRooms.has(roomId)) { - console.log(`[Bot Loop] Bot ${priorityPlayer.name} is thinking...`); + const now = Date.now(); + if (!this.lastBotLog[roomId] || now - this.lastBotLog[roomId] > 5000) { + console.log(`[Bot Loop] Bot ${priorityPlayer.name} is thinking...`); + this.lastBotLog[roomId] = now; + } this.thinkingRooms.add(roomId); setTimeout(() => { @@ -206,8 +212,18 @@ export class GameManager extends EventEmitter { if (!bot || !bot.isBot) return; - // 1. Mulligan: Always Keep + // 1. Mulligan: Always Keep (but check if we have cards?) if (game.step === 'mulligan') { + const hand = Object.values(game.cards).filter(c => c.ownerId === botId && c.zone === 'hand'); + if (hand.length === 0 && !bot.handKept) { + // We have NO cards to keep? Something is wrong (deck didn't load?). + // Don't loop infinitely trying to keep an empty hand if that's invalid, + // but technically "keeping" 0 cards is just accepting 0 cards. + // However, usually this means initialization failed. + // We'll log once and stop? Or just keep to unstuck the game? + // Let's try to keep. + // console.warn(`[Bot AI] ${bot.name} has 0 cards in hand during Mulligan. Initializing?`); + } if (!bot.handKept) { try { engine.resolveMulligan(botId, true, []); } catch (e) { } } diff --git a/src/server/managers/TournamentManager.ts b/src/server/managers/TournamentManager.ts index f3128a5..0d77078 100644 --- a/src/server/managers/TournamentManager.ts +++ b/src/server/managers/TournamentManager.ts @@ -245,7 +245,12 @@ export class TournamentManager extends EventEmitter { // Update Player Deck in Tournament Roster const player = t.players.find(p => p.id === playerId); if (player) { - player.deck = deck; + if (deck && deck.length > 0) { + player.deck = deck; + } else if (!player.deck || player.deck.length === 0) { + // Only warn if we are missing a deck entirely + console.warn(`[Tournament] received empty deck for ${playerId}, and no previous deck found.`); + } } // Add to Ready