[Replay] Implement option to skip empty sections

This commit is contained in:
RickyRister 2026-07-27 01:41:59 -07:00
parent b55b106735
commit d7b5de9c73
4 changed files with 65 additions and 2 deletions

View file

@ -3,9 +3,11 @@
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include <QTimer> #include <QTimer>
#include <libcockatrice/protocol/get_pb_extension.h>
#include <libcockatrice/settings/interface_settings.h> #include <libcockatrice/settings/interface_settings.h>
static constexpr int TIMER_INTERVAL_MS = 200; static constexpr int TIMER_INTERVAL_MS = 200;
static constexpr int EMPTY_SECTION_MARGIN_MS = 500;
static QList<int> createReplayTimeline(const GameReplay *replay) static QList<int> createReplayTimeline(const GameReplay *replay)
{ {
@ -119,6 +121,10 @@ void ReplayManager::replayTimerTimeout()
processNewEvents(NORMAL_PLAYBACK); processNewEvents(NORMAL_PLAYBACK);
timeChanged(currentVisualTime); timeChanged(currentVisualTime);
if (skipEmptySections) {
handleSkipEmptySection();
}
} }
/** @brief Processes all unprocessed events up to the current time. */ /** @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<GameEvent::GameEventType>(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) void ReplayManager::setTimeScaleFactor(qreal _timeScaleFactor)
{ {
timeScaleFactor = _timeScaleFactor; timeScaleFactor = _timeScaleFactor;
@ -156,6 +207,11 @@ void ReplayManager::setTimeScaleFactor(qreal _timeScaleFactor)
replayTimer->setInterval(interval); replayTimer->setInterval(interval);
} }
void ReplayManager::setSkipEmptySections(bool value)
{
skipEmptySections = value;
}
void ReplayManager::startReplay() void ReplayManager::startReplay()
{ {
replayTimer->start(); replayTimer->start();

View file

@ -31,6 +31,7 @@ class ReplayManager : public QObject
QTimer *rewindBufferingTimer; QTimer *rewindBufferingTimer;
qreal timeScaleFactor = 1.0; qreal timeScaleFactor = 1.0;
bool skipEmptySections = false;
int currentVisualTime = 0; ///< time currently displayed by the timeline 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 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 handleBackwardsSkip(bool doRewindBuffering);
void processRewind(); void processRewind();
void processNewEvents(PlaybackMode playbackMode); void processNewEvents(PlaybackMode playbackMode);
void handleSkipEmptySection();
private slots: private slots:
void replayTimerTimeout(); void replayTimerTimeout();
@ -63,6 +65,7 @@ public:
} }
void setTimeScaleFactor(qreal _timeScaleFactor); void setTimeScaleFactor(qreal _timeScaleFactor);
void setSkipEmptySections(bool value);
public slots: public slots:
void startReplay(); void startReplay();

View file

@ -18,7 +18,7 @@ ReplayQuickSettingsWidget::ReplayQuickSettingsWidget(QWidget *parent) : Settings
connect(&fastForwardSpeedBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, connect(&fastForwardSpeedBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this,
&ReplayQuickSettingsWidget::actUpdateFastForwardSpeed); &ReplayQuickSettingsWidget::actUpdateFastForwardSpeed);
skipEmptyCheckBox.setChecked(SettingsCache::instance().interface().getSkipEmptySections()); skipEmptyCheckBox.setChecked(SettingsCache::instance().userInterface().getSkipEmptySections());
connect(&skipEmptyCheckBox, &QCheckBox::QT_STATE_CHANGED, this, connect(&skipEmptyCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
&ReplayQuickSettingsWidget::actUpdateSkipEmptySections); &ReplayQuickSettingsWidget::actUpdateSkipEmptySections);
@ -53,6 +53,6 @@ void ReplayQuickSettingsWidget::actUpdateFastForwardSpeed(qreal value)
void ReplayQuickSettingsWidget::actUpdateSkipEmptySections(QT_STATE_CHANGED_T value) void ReplayQuickSettingsWidget::actUpdateSkipEmptySections(QT_STATE_CHANGED_T value)
{ {
SettingsCache::instance().interface().setSkipEmptySections(value); SettingsCache::instance().userInterface().setSkipEmptySections(value);
emit skipEmptySectionsChanged(value); emit skipEmptySectionsChanged(value);
} }

View file

@ -66,6 +66,10 @@ ReplayWidget::ReplayWidget(QWidget *parent, GameReplay *replay)
settingsWidget->setFixedSize(QSize(32, 32)); settingsWidget->setFixedSize(QSize(32, 32));
connect(settingsWidget, &ReplayQuickSettingsWidget::fastForwardSpeedChanged, this, connect(settingsWidget, &ReplayQuickSettingsWidget::fastForwardSpeedChanged, this,
[this] { updateTimeScaleFactor(replayFastForwardButton->isChecked()); }); [this] { updateTimeScaleFactor(replayFastForwardButton->isChecked()); });
connect(settingsWidget, &ReplayQuickSettingsWidget::skipEmptySectionsChanged, replayManager,
&ReplayManager::setSkipEmptySections);
replayManager->setSkipEmptySections(SettingsCache::instance().userInterface().getSkipEmptySections());
// putting everything together // putting everything together
auto replayControlLayout = new QHBoxLayout; auto replayControlLayout = new QHBoxLayout;