From 750d5b3c211328cd72f8ae843c9946dda819e2e8 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Fri, 2 Oct 2026 17:30:48 +0200 Subject: [PATCH] [Client] Add option to use game time in game log timestamps (#7265) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Client] Add option to use game time in game log timestamps (#2202) Game log timestamps showed the local clock time, which is meaningless in a replay or in a log read after the fact — nobody can tell when a play happened relative to the game. Add a "Use game time instead of local time in game logs" setting. When enabled, the message log stamps entries with the elapsed game time (HH:MM:SS since the game started); regular chat views keep the local clock. * [Game] Move formatElapsedTime helper out of public slots (#2202) The message log needs the HH:MM:SS formatter used by the game timer, but it lives in GameState's public slots, tying a pure formatting helper to the signal-slot machinery. Move formatElapsedTime into a public section of GameState so the log can call it directly, and add the missing trailing newline to game_state.cpp. --------- Co-authored-by: Lukas Brübach --- cockatrice/src/game/game_state.cpp | 12 ++++++++---- cockatrice/src/game/game_state.h | 8 ++++++++ .../src/game_graphics/log/message_log_widget.cpp | 16 +++++++++++++--- .../src/game_graphics/log/message_log_widget.h | 3 +++ .../widgets/server/chat_view/chat_view.cpp | 9 +++++++-- .../widgets/server/chat_view/chat_view.h | 6 +++--- .../settings_page/messages_settings_page.cpp | 6 ++++++ .../settings_page/messages_settings_page.h | 1 + .../interface_chat_settings_provider.h | 1 + .../libcockatrice/settings/chat_settings.cpp | 10 ++++++++++ .../libcockatrice/settings/chat_settings.h | 2 ++ tests/settings/settings_defaults_test.cpp | 6 ++++++ 12 files changed, 68 insertions(+), 12 deletions(-) diff --git a/cockatrice/src/game/game_state.cpp b/cockatrice/src/game/game_state.cpp index 854878b92..dc9ba02a3 100644 --- a/cockatrice/src/game/game_state.cpp +++ b/cockatrice/src/game/game_state.cpp @@ -29,13 +29,17 @@ void GameState::incrementGameTime() void GameState::setGameTime(int _secondsElapsed) { secondsElapsed = _secondsElapsed; + emit updateTimeElapsedLabel(formatElapsedTime(_secondsElapsed)); +} + +QString GameState::formatElapsedTime(int _secondsElapsed) +{ int seconds = _secondsElapsed; int minutes = seconds / 60; seconds -= minutes * 60; int hours = minutes / 60; minutes -= hours * 60; - emit updateTimeElapsedLabel(QString::number(hours).rightJustified(2, '0') + ":" + - QString::number(minutes).rightJustified(2, '0') + ":" + - QString::number(seconds).rightJustified(2, '0')); -} \ No newline at end of file + return QString::number(hours).rightJustified(2, '0') + ":" + QString::number(minutes).rightJustified(2, '0') + ":" + + QString::number(seconds).rightJustified(2, '0'); +} diff --git a/cockatrice/src/game/game_state.h b/cockatrice/src/game/game_state.h index 5a57d4321..94bbb853c 100644 --- a/cockatrice/src/game/game_state.h +++ b/cockatrice/src/game/game_state.h @@ -110,6 +110,11 @@ public: return hostId; } + int getSecondsElapsed() const + { + return secondsElapsed; + } + signals: void updateTimeElapsedLabel(QString newTime); void gameStarted(bool resuming); @@ -121,6 +126,9 @@ public slots: void incrementGameTime(); void setGameTime(int _secondsElapsed); +public: + static QString formatElapsedTime(int _secondsElapsed); + private: QTimer *gameTimer; int secondsElapsed; diff --git a/cockatrice/src/game_graphics/log/message_log_widget.cpp b/cockatrice/src/game_graphics/log/message_log_widget.cpp index ccd903b04..06b0301bd 100644 --- a/cockatrice/src/game_graphics/log/message_log_widget.cpp +++ b/cockatrice/src/game_graphics/log/message_log_widget.cpp @@ -1,7 +1,9 @@ #include "message_log_widget.h" +#include "../../client/settings/cache_settings.h" #include "../../client/settings/card_counter_settings.h" #include "../../client/sound_engine.h" +#include "../../game/game_state.h" #include "../../game/phase.h" #include "../../game/player/player_logic.h" #include "../../interface/widgets/tabs/tab_game.h" @@ -10,6 +12,7 @@ #include #include +#include #include #include @@ -626,13 +629,12 @@ void MessageLogWidget::logSetActivePhase(int phaseNumber) soundEngine->playSound(phase.soundFileName); - appendHtml("" + QDateTime::currentDateTime().toString("[hh:mm:ss] ") + - phase.getName() + ""); + appendHtml("" + getCurrentTime() + phase.getName() + ""); } void MessageLogWidget::logSetActivePlayer(PlayerLogic *player) { - appendHtml("
" + QDateTime::currentDateTime().toString("[hh:mm:ss] ") + + appendHtml("
" + getCurrentTime() + QString(tr("%1's turn.")).arg(player->getPlayerInfo()->getName()) + "
"); } @@ -826,6 +828,14 @@ void MessageLogWidget::appendHtmlServerMessage(const QString &html, bool optiona ChatView::appendHtmlServerMessage(messagePrefix + html + messageSuffix, optionalIsBold, optionalFontColor); } +QString MessageLogWidget::getCurrentTime() const +{ + if (SettingsCache::instance().chat().getUseGameTime()) { + return "[" + GameState::formatElapsedTime(game->getGameState()->getSecondsElapsed()) + "] "; + } + return ChatView::getCurrentTime(); +} + void MessageLogWidget::connectToPlayerEventHandler(PlayerEventHandler *playerEventHandler) { connect(playerEventHandler, &PlayerEventHandler::logSay, this, &MessageLogWidget::logSay); diff --git a/cockatrice/src/game_graphics/log/message_log_widget.h b/cockatrice/src/game_graphics/log/message_log_widget.h index a145d358d..375c6df4d 100644 --- a/cockatrice/src/game_graphics/log/message_log_widget.h +++ b/cockatrice/src/game_graphics/log/message_log_widget.h @@ -104,6 +104,9 @@ public slots: void appendHtmlServerMessage(const QString &html, bool optionalIsBold = false, QString optionalFontColor = QString()) override; + +private: + [[nodiscard]] QString getCurrentTime() const override; }; #endif diff --git a/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp b/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp index 0a287c9ce..ae9b2deb4 100644 --- a/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp +++ b/cockatrice/src/interface/widgets/server/chat_view/chat_view.cpp @@ -167,7 +167,7 @@ void ChatView::appendHtmlServerMessage(const QString &html, bool optionalIsBold, QString htmlText = "" + - QDateTime::currentDateTime().toString("[hh:mm:ss] ") + html + ""; + getCurrentTime() + html + "
"; if (optionalIsBold) { htmlText = "" + htmlText + ""; @@ -179,6 +179,11 @@ void ChatView::appendHtmlServerMessage(const QString &html, bool optionalIsBold, } } +QString ChatView::getCurrentTime() const +{ + return QDateTime::currentDateTime().toString("[hh:mm:ss] "); +} + void ChatView::appendCardTag(QTextCursor &cursor, const QString &cardName) { QTextCharFormat oldFormat = cursor.charFormat(); @@ -290,7 +295,7 @@ void ChatView::appendMessage(QString message, timeFormat.setForeground(serverMessageColor); timeFormat.setFontWeight(QFont::Bold); cursor.setCharFormat(timeFormat); - cursor.insertText(QDateTime::currentDateTime().toString("[hh:mm:ss] ")); + cursor.insertText(getCurrentTime()); } // nickname diff --git a/cockatrice/src/interface/widgets/server/chat_view/chat_view.h b/cockatrice/src/interface/widgets/server/chat_view/chat_view.h index 671b35163..06e3a0ed8 100644 --- a/cockatrice/src/interface/widgets/server/chat_view/chat_view.h +++ b/cockatrice/src/interface/widgets/server/chat_view/chat_view.h @@ -107,9 +107,8 @@ public: ChatView(TabSupervisor *_tabSupervisor, AbstractGame *_game, bool _showTimestamps, QWidget *parent = nullptr); void retranslateUi(); void appendHtml(const QString &html); - void virtual appendHtmlServerMessage(const QString &html, - bool optionalIsBold = false, - QString optionalFontColor = QString()); + virtual void + appendHtmlServerMessage(const QString &html, bool optionalIsBold = false, QString optionalFontColor = QString()); void appendMessage(QString message, RoomMessageTypeFlags messageType = {}, const ServerInfo_User &userInfo = {}, @@ -119,6 +118,7 @@ public: QString getRecentChatLog(int maxMessages = 50) const; protected: + [[nodiscard]] virtual QString getCurrentTime() const; void enterEvent(QEnterEvent *event) override; void leaveEvent(QEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; diff --git a/cockatrice/src/interface/widgets/settings_page/messages_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/messages_settings_page.cpp index a3b89f8c4..68e3f2ac7 100644 --- a/cockatrice/src/interface/widgets/settings_page/messages_settings_page.cpp +++ b/cockatrice/src/interface/widgets/settings_page/messages_settings_page.cpp @@ -64,6 +64,10 @@ MessagesSettingsPage::MessagesSettingsPage() connect(&ignoreAllPrivateMessagesCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance().chat(), &ChatSettings::setIgnoreAllPrivateMessages); + useGameTimeCheckBox.setChecked(SettingsCache::instance().chat().getUseGameTime()); + connect(&useGameTimeCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance().chat(), + &ChatSettings::setUseGameTime); + customAlertString = new QLineEdit(); customAlertString->setText(SettingsCache::instance().chat().getHighlightWords()); connect(customAlertString, &QLineEdit::textChanged, &SettingsCache::instance().chat(), @@ -82,6 +86,7 @@ MessagesSettingsPage::MessagesSettingsPage() chatGrid->addWidget(&mentionPopups, 6, 0); chatGrid->addWidget(&roomHistory, 7, 0); chatGrid->addWidget(&ignoreAllPrivateMessagesCheckBox, 8, 0); + chatGrid->addWidget(&useGameTimeCheckBox, 9, 0); chatGroupBox = new QGroupBox; chatGroupBox->setLayout(chatGrid); @@ -263,6 +268,7 @@ void MessagesSettingsPage::retranslateUi() mentionPopups.setText(tr("Enable desktop notification for mentions")); roomHistory.setText(tr("Enable room message history on join")); ignoreAllPrivateMessagesCheckBox.setText(tr("Ignore all private messages")); + useGameTimeCheckBox.setText(tr("Use game time instead of local time in game logs")); hexLabel.setText(tr("(Color is hexadecimal)")); hexHighlightLabel.setText(tr("(Color is hexadecimal)")); customAlertStringLabel.setText(tr("Separate words with a space, alphanumeric characters only")); diff --git a/cockatrice/src/interface/widgets/settings_page/messages_settings_page.h b/cockatrice/src/interface/widgets/settings_page/messages_settings_page.h index 436ebbad9..4d491136f 100644 --- a/cockatrice/src/interface/widgets/settings_page/messages_settings_page.h +++ b/cockatrice/src/interface/widgets/settings_page/messages_settings_page.h @@ -41,6 +41,7 @@ private: QCheckBox mentionPopups; QCheckBox roomHistory; QCheckBox ignoreAllPrivateMessagesCheckBox; + QCheckBox useGameTimeCheckBox; QGroupBox *chatGroupBox; QGroupBox *highlightGroupBox; QGroupBox *messageGroupBox; diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/interface_chat_settings_provider.h b/libcockatrice_interfaces/libcockatrice/interfaces/interface_chat_settings_provider.h index cdf2da5eb..04dad9243 100644 --- a/libcockatrice_interfaces/libcockatrice/interfaces/interface_chat_settings_provider.h +++ b/libcockatrice_interfaces/libcockatrice/interfaces/interface_chat_settings_provider.h @@ -21,6 +21,7 @@ public: [[nodiscard]] virtual bool getShowMentionPopup() const = 0; [[nodiscard]] virtual bool getRoomHistory() const = 0; [[nodiscard]] virtual bool getIgnoreAllPrivateMessages() const = 0; + [[nodiscard]] virtual bool getUseGameTime() const = 0; [[nodiscard]] virtual QString getHighlightWords() const = 0; }; diff --git a/libcockatrice_settings/libcockatrice/settings/chat_settings.cpp b/libcockatrice_settings/libcockatrice/settings/chat_settings.cpp index 974173371..0ee29b4f5 100644 --- a/libcockatrice_settings/libcockatrice/settings/chat_settings.cpp +++ b/libcockatrice_settings/libcockatrice/settings/chat_settings.cpp @@ -70,6 +70,11 @@ bool ChatSettings::getIgnoreAllPrivateMessages() const return getValue("ignoreAllPrivateMessages", QString(), QString(), false).toBool(); } +bool ChatSettings::getUseGameTime() const +{ + return getValue("useGameTime", QString(), QString(), false).toBool(); +} + QString ChatSettings::getHighlightWords() const { return getValue("highlightWords").toString(); @@ -141,6 +146,11 @@ void ChatSettings::setIgnoreAllPrivateMessages(bool _ignoreAllPrivateMessages) setValue(_ignoreAllPrivateMessages, "ignoreAllPrivateMessages"); } +void ChatSettings::setUseGameTime(bool _useGameTime) +{ + setValue(_useGameTime, "useGameTime"); +} + void ChatSettings::setHighlightWords(const QString &_highlightWords) { setValue(_highlightWords, "highlightWords"); diff --git a/libcockatrice_settings/libcockatrice/settings/chat_settings.h b/libcockatrice_settings/libcockatrice/settings/chat_settings.h index 9566671e2..d928b23b7 100644 --- a/libcockatrice_settings/libcockatrice/settings/chat_settings.h +++ b/libcockatrice_settings/libcockatrice/settings/chat_settings.h @@ -24,6 +24,7 @@ public: [[nodiscard]] bool getShowMentionPopup() const override; [[nodiscard]] bool getRoomHistory() const override; [[nodiscard]] bool getIgnoreAllPrivateMessages() const override; + [[nodiscard]] bool getUseGameTime() const override; [[nodiscard]] QString getHighlightWords() const override; void setChatMention(bool _chatMention); @@ -39,6 +40,7 @@ public: void setShowMentionPopups(bool _showMentionPopups); void setRoomHistory(bool _roomHistory); void setIgnoreAllPrivateMessages(bool _ignoreAllPrivateMessages); + void setUseGameTime(bool _useGameTime); void setHighlightWords(const QString &_highlightWords); signals: diff --git a/tests/settings/settings_defaults_test.cpp b/tests/settings/settings_defaults_test.cpp index 56555eae6..379f6cd84 100644 --- a/tests/settings/settings_defaults_test.cpp +++ b/tests/settings/settings_defaults_test.cpp @@ -306,6 +306,12 @@ TEST_F(SettingsDefaultsTest, Chat_IgnoreAllPrivateMessages_Default) ASSERT_EQ(s.getIgnoreAllPrivateMessages(), false); } +TEST_F(SettingsDefaultsTest, Chat_UseGameTime_Default) +{ + ChatSettings s(settingsPath, nullptr); + ASSERT_EQ(s.getUseGameTime(), false); +} + // --- PersonalSettings --- TEST_F(SettingsDefaultsTest, Personal_Lang_Default)