From d56dbae7d5125bfc567f16a54bf249ce2dd7c1bc Mon Sep 17 00:00:00 2001 From: RickyRister Date: Mon, 27 Jul 2026 01:41:59 -0700 Subject: [PATCH] [Replay] Implement option to skip empty sections --- .../widgets/replay/replay_manager.cpp | 56 +++++++++++++++++++ .../interface/widgets/replay/replay_manager.h | 3 + .../widgets/replay/replay_widget.cpp | 4 ++ 3 files changed, 63 insertions(+) diff --git a/cockatrice/src/interface/widgets/replay/replay_manager.cpp b/cockatrice/src/interface/widgets/replay/replay_manager.cpp index 1037d36a8..ad00afa35 100644 --- a/cockatrice/src/interface/widgets/replay/replay_manager.cpp +++ b/cockatrice/src/interface/widgets/replay/replay_manager.cpp @@ -3,9 +3,11 @@ #include "../../../client/settings/cache_settings.h" #include +#include #include static constexpr int TIMER_INTERVAL_MS = 200; +static constexpr int EMPTY_SECTION_MARGIN_MS = 500; static QList createReplayTimeline(const GameReplay *replay) { @@ -119,6 +121,10 @@ void ReplayManager::replayTimerTimeout() processNewEvents(NORMAL_PLAYBACK); timeChanged(currentVisualTime); + + if (skipEmptySections) { + handleSkipEmptySection(); + } } /** @brief Processes all unprocessed events up to the current time. */ @@ -149,6 +155,51 @@ void ReplayManager::processNewEvents(PlaybackMode playbackMode) } } +static bool hasMeaningfulEvent(const GameEventContainer &cont) +{ + const int eventListSize = cont.event_list_size(); + for (int i = 0; i < eventListSize; ++i) { + const GameEvent &event = cont.event_list(i); + const auto eventType = static_cast(getPbExtension(event)); + + if (eventType != GameEvent::PLAYER_PROPERTIES_CHANGED) { + return true; + } + } + + return false; +} + +void ReplayManager::handleSkipEmptySection() +{ + if (currentEvent == replayTimeline.size()) { + return; + } + + // find most recent meaningful event + int prevEvent = currentEvent; + for (; prevEvent > 0 && !hasMeaningfulEvent(replay->event_list(prevEvent)); --prevEvent) { + } + + int prevEventTime = replayTimeline.value(prevEvent); + if (currentVisualTime - prevEventTime <= EMPTY_SECTION_MARGIN_MS) { + return; + } + + // find next earliest meaningful event + int nextEvent = currentEvent + 1; + for (; nextEvent < replayTimeline.size() - 1 && !hasMeaningfulEvent(replay->event_list(nextEvent)); ++nextEvent) { + } + + int nextEventTime = replayTimeline.value(nextEvent); + if (nextEventTime - currentVisualTime <= EMPTY_SECTION_MARGIN_MS) { + return; + } + + // skip forward if we're not within margin of either event + skipToTime(nextEventTime - EMPTY_SECTION_MARGIN_MS, false); +} + void ReplayManager::setTimeScaleFactor(qreal _timeScaleFactor) { timeScaleFactor = _timeScaleFactor; @@ -156,6 +207,11 @@ void ReplayManager::setTimeScaleFactor(qreal _timeScaleFactor) replayTimer->setInterval(interval); } +void ReplayManager::setSkipEmptySections(bool value) +{ + skipEmptySections = value; +} + void ReplayManager::startReplay() { replayTimer->start(); diff --git a/cockatrice/src/interface/widgets/replay/replay_manager.h b/cockatrice/src/interface/widgets/replay/replay_manager.h index 16d3591ba..81e66824d 100644 --- a/cockatrice/src/interface/widgets/replay/replay_manager.h +++ b/cockatrice/src/interface/widgets/replay/replay_manager.h @@ -31,6 +31,7 @@ class ReplayManager : public QObject QTimer *rewindBufferingTimer; qreal timeScaleFactor = 1.0; + bool skipEmptySections = false; int currentVisualTime = 0; ///< time currently displayed by the timeline int currentProcessedTime = 0; ///< time that events are currently processed up to. Could differ from visual time due @@ -41,6 +42,7 @@ class ReplayManager : public QObject void handleBackwardsSkip(bool doRewindBuffering); void processRewind(); void processNewEvents(PlaybackMode playbackMode); + void handleSkipEmptySection(); private slots: void replayTimerTimeout(); @@ -63,6 +65,7 @@ public: } void setTimeScaleFactor(qreal _timeScaleFactor); + void setSkipEmptySections(bool value); public slots: void startReplay(); diff --git a/cockatrice/src/interface/widgets/replay/replay_widget.cpp b/cockatrice/src/interface/widgets/replay/replay_widget.cpp index f5768a7aa..2fcc47b52 100644 --- a/cockatrice/src/interface/widgets/replay/replay_widget.cpp +++ b/cockatrice/src/interface/widgets/replay/replay_widget.cpp @@ -66,6 +66,10 @@ ReplayWidget::ReplayWidget(QWidget *parent, GameReplay *replay) settingsWidget->setFixedSize(QSize(32, 32)); connect(settingsWidget, &ReplayQuickSettingsWidget::fastForwardSpeedChanged, this, [this] { updateTimeScaleFactor(replayFastForwardButton->isChecked()); }); + connect(settingsWidget, &ReplayQuickSettingsWidget::skipEmptySectionsChanged, replayManager, + &ReplayManager::setSkipEmptySections); + + replayManager->setSkipEmptySections(SettingsCache::instance().interface().getSkipEmptySections()); // putting everything together auto replayControlLayout = new QHBoxLayout;