From 10b9a65f1748c5399eb473e71947bef66fb8ae59 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Wed, 20 May 2026 02:23:02 -0700 Subject: [PATCH] [Server][Game] Make undo draw failure visible in chat (#6889) * [Server][Game] Make undo draw failure visible in chat * genericize the proto --- .../src/game/log/message_log_widget.cpp | 7 +++++++ cockatrice/src/game/log/message_log_widget.h | 1 + .../src/game/player/player_event_handler.cpp | 16 +++++++++++++++ .../src/game/player/player_event_handler.h | 4 ++++ .../server/remote/game/server_player.cpp | 4 ++++ .../libcockatrice/protocol/pb/CMakeLists.txt | 1 + .../protocol/pb/event_game_log_notice.proto | 20 +++++++++++++++++++ .../protocol/pb/game_event.proto | 1 + 8 files changed, 54 insertions(+) create mode 100644 libcockatrice_protocol/libcockatrice/protocol/pb/event_game_log_notice.proto diff --git a/cockatrice/src/game/log/message_log_widget.cpp b/cockatrice/src/game/log/message_log_widget.cpp index 53f671256..fe564b531 100644 --- a/cockatrice/src/game/log/message_log_widget.cpp +++ b/cockatrice/src/game/log/message_log_widget.cpp @@ -806,6 +806,12 @@ void MessageLogWidget::logUndoDraw(PlayerLogic *player, QString cardName) } } +void MessageLogWidget::logUndoDrawFailed(PlayerLogic *player) +{ + appendHtmlServerMessage( + tr("%1 failed to undo their last draw.").arg(sanitizeHtml(player->getPlayerInfo()->getName()))); +} + void MessageLogWidget::setContextJudgeName(QString name) { messagePrefix = QString(""); @@ -839,6 +845,7 @@ void MessageLogWidget::connectToPlayerEventHandler(PlayerEventHandler *playerEve connect(playerEventHandler, &PlayerEventHandler::logDumpZone, this, &MessageLogWidget::logDumpZone); connect(playerEventHandler, &PlayerEventHandler::logDrawCards, this, &MessageLogWidget::logDrawCards); connect(playerEventHandler, &PlayerEventHandler::logUndoDraw, this, &MessageLogWidget::logUndoDraw); + connect(playerEventHandler, &PlayerEventHandler::logUndoDrawFailed, this, &MessageLogWidget::logUndoDrawFailed); connect(playerEventHandler, &PlayerEventHandler::logRevealCards, this, &MessageLogWidget::logRevealCards); connect(playerEventHandler, &PlayerEventHandler::logAlwaysRevealTopCard, this, &MessageLogWidget::logAlwaysRevealTopCard); diff --git a/cockatrice/src/game/log/message_log_widget.h b/cockatrice/src/game/log/message_log_widget.h index 25db21864..f8d70a52e 100644 --- a/cockatrice/src/game/log/message_log_widget.h +++ b/cockatrice/src/game/log/message_log_widget.h @@ -99,6 +99,7 @@ public slots: void logSpectatorSay(const ServerInfo_User &spectator, QString message); void logUnattachCard(PlayerLogic *player, QString cardName); void logUndoDraw(PlayerLogic *player, QString cardName); + void logUndoDrawFailed(PlayerLogic *player); void setContextJudgeName(QString player); void appendHtmlServerMessage(const QString &html, bool optionalIsBold = false, diff --git a/cockatrice/src/game/player/player_event_handler.cpp b/cockatrice/src/game/player/player_event_handler.cpp index 244929338..15c40c638 100644 --- a/cockatrice/src/game/player/player_event_handler.cpp +++ b/cockatrice/src/game/player/player_event_handler.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -581,6 +582,18 @@ void PlayerEventHandler::eventChangeZoneProperties(const Event_ChangeZonePropert } } +void PlayerEventHandler::eventGameLogNotice(const Event_GameLogNotice &event) +{ + Event_GameLogNotice::NoticeType type = event.notice_type(); + switch (type) { + case Event_GameLogNotice::UNDO_DRAW_FAILED: + emit logUndoDrawFailed(player); + break; + default: + qWarning() << "Received Event_GameLogNotice with unknown noticeType: " << type; + } +} + void PlayerEventHandler::processGameEvent(GameEvent::GameEventType type, const GameEvent &event, const GameEventContext &context, @@ -644,6 +657,9 @@ void PlayerEventHandler::processGameEvent(GameEvent::GameEventType type, case GameEvent::CHANGE_ZONE_PROPERTIES: eventChangeZoneProperties(event.GetExtension(Event_ChangeZoneProperties::ext)); break; + case GameEvent::GAME_LOG_NOTICE: + eventGameLogNotice(event.GetExtension(Event_GameLogNotice::ext)); + break; default: { qWarning() << "unhandled game event" << type; } diff --git a/cockatrice/src/game/player/player_event_handler.h b/cockatrice/src/game/player/player_event_handler.h index ae3d9aaae..c9bdb98ae 100644 --- a/cockatrice/src/game/player/player_event_handler.h +++ b/cockatrice/src/game/player/player_event_handler.h @@ -35,6 +35,8 @@ class Event_SetCardAttr; class Event_SetCardCounter; class Event_SetCounter; class Event_Shuffle; +class Event_GameLogNotice; + class PlayerEventHandler : public QObject { @@ -52,6 +54,7 @@ signals: void logCreateToken(PlayerLogic *player, QString cardName, QString pt, bool faceDown); void logDrawCards(PlayerLogic *player, int number, bool deckIsEmpty); void logUndoDraw(PlayerLogic *player, QString cardName); + void logUndoDrawFailed(PlayerLogic *player); void logMoveCard(PlayerLogic *player, CardItem *card, CardZoneLogic *startZone, @@ -108,6 +111,7 @@ public: void eventDrawCards(const Event_DrawCards &event); void eventRevealCards(const Event_RevealCards &event, EventProcessingOptions options); void eventChangeZoneProperties(const Event_ChangeZoneProperties &event); + void eventGameLogNotice(const Event_GameLogNotice &event); private: PlayerLogic *player; diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp index 1175e4b57..a1a0a3b3a 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -409,6 +410,9 @@ Server_Player::cmdUndoDraw(const Command_UndoDraw & /*cmd*/, ResponseContainer & } if (lastDrawList.isEmpty()) { + Event_GameLogNotice event; + event.set_notice_type(Event_GameLogNotice::UNDO_DRAW_FAILED); + ges.enqueueGameEvent(event, playerId); return Response::RespContextError; } diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt b/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt index 212ab69dd..b4c7b6ac8 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt @@ -76,6 +76,7 @@ set(PROTO_FILES event_game_closed.proto event_game_host_changed.proto event_game_joined.proto + event_game_log_notice.proto event_game_say.proto event_game_state_changed.proto event_game_state_changed.proto diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/event_game_log_notice.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/event_game_log_notice.proto new file mode 100644 index 000000000..ef0dcc102 --- /dev/null +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/event_game_log_notice.proto @@ -0,0 +1,20 @@ +syntax = "proto2"; +import "game_event.proto"; + +// Notifies clients of an event that happened, and which could safely be dropped without affect the game state. +// This mostly just means events that should cause a message to be logged to chat. +message Event_GameLogNotice { + + // The type of the notice. + // Clients who do not recognize the type should drop the event. + enum NoticeType { + // Player's "undo draw" command failed due to losing track of recent draw + UNDO_DRAW_FAILED = 1; + } + + extend GameEvent { + optional Event_GameLogNotice ext = 2022; + } + + optional NoticeType notice_type = 1; +} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/game_event.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/game_event.proto index 8682128af..7d3147701 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/game_event.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/game_event.proto @@ -33,6 +33,7 @@ message GameEvent { // STOP_DUMP_ZONE = 2019; // obsolete CHANGE_ZONE_PROPERTIES = 2020; REVERSE_TURN = 2021; + GAME_LOG_NOTICE = 2022; } optional sint32 player_id = 1 [default = -1]; extensions 100 to max;