From 4feb31bd1a4c829294339b74dc6a5ef57631da2a Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Sun, 11 Oct 2026 16:53:26 +0200 Subject: [PATCH] [Game] Show loaded deck messages to players joining a game (#7268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Show loaded deck messages to players joining a game (#552) The "Player X has loaded a deck" message is only broadcast when a deck is selected, so players and spectators who join a game afterwards never see it. On game join, replay a deck-select event context privately to the joining participant for every player that already has a deck loaded, reusing the existing Event_PlayerPropertiesChanged + Context_DeckSelect mechanism so no protocol version bump is needed. - Server_AbstractPlayer: expose the sideboard-locked state - Server_Game: synthesize deck-select contexts in createGameJoinedEvent * [Server] Skip deck replay on rejoin and after start --------- Co-authored-by: Lukas Brübach --- .../remote/game/server_abstract_player.h | 4 ++ .../server/remote/game/server_game.cpp | 44 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.h index eff5034b4..ae20955bf 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.h @@ -56,6 +56,10 @@ public: { return deck; } + bool getSideboardLocked() const + { + return sideboardLocked; + } bool getReadyStart() const { return readyStart; diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp index 330d473b2..b1863c91e 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp @@ -61,6 +61,8 @@ #include #include #include +#include +#include #include #include #include @@ -825,6 +827,48 @@ void Server_Game::createGameJoinedEvent(Server_AbstractParticipant *joiningParti } rc.enqueuePostResponseItem(ServerMessage::GAME_EVENT_CONTAINER, prepareGameEvent(event2, -1)); + + // A newly joined player never saw the "Player X has loaded a deck" messages + // (they are only broadcast when a deck is selected), so replay a deck-select + // context for every player that already has a deck loaded. It is delivered + // privately to the joining participant, whose client renders it as a log + // entry in the same way as a fresh deck selection. Skipped on reconnect + // (resuming) — the player already saw these — and once the game has started, + // when the message is no longer useful and would only spam the log. + if (!resuming && !gameStarted) { + for (auto *participant : participants.values()) { + if (participant == joiningParticipant) { + continue; + } + auto *deckOwner = qobject_cast(participant); + if (deckOwner == nullptr) { + continue; + } + const DeckList *deckList = deckOwner->getDeckList(); + if (deckList == nullptr) { + continue; + } + + Event_PlayerPropertiesChanged event; + event.mutable_player_properties()->set_sideboard_locked(deckOwner->getSideboardLocked()); + event.mutable_player_properties()->set_deck_hash(deckList->getDeckHash().toStdString()); + + Context_DeckSelect deckSelect; + deckSelect.set_deck_hash(deckList->getDeckHash().toStdString()); + deckSelect.set_sideboard_size(deckList->getSideboardSize()); + if (getShareDecklistsOnLoad()) { + deckSelect.set_deck_list(deckList->writeToString_Native().toStdString()); + } + + GameEventContext context; + context.GetReflection() + ->MutableMessage(&context, deckSelect.GetDescriptor()->FindExtensionByName("ext")) + ->CopyFrom(deckSelect); + + rc.enqueuePostResponseItem(ServerMessage::GAME_EVENT_CONTAINER, + prepareGameEvent(event, deckOwner->getPlayerId(), &context)); + } + } } void Server_Game::sendGameEventContainer(GameEventContainer *cont,