diff --git a/src/Etterna/Actor/Gameplay/PlayerReplay.cpp b/src/Etterna/Actor/Gameplay/PlayerReplay.cpp index 2f6db511dd..46772128c1 100644 --- a/src/Etterna/Actor/Gameplay/PlayerReplay.cpp +++ b/src/Etterna/Actor/Gameplay/PlayerReplay.cpp @@ -302,6 +302,61 @@ PlayerReplay::Update(float fDeltaTime) UpdateTapNotesMissedOlderThan(GetMaxStepDistanceSeconds()); } +void +PlayerReplay::SetPlaybackEvents( + const std::map>& v) +{ + playbackEvents.clear(); + + std::vector ghostTaps{}; + auto gapError = 0.F; + + auto musicRate = REPLAYS->GetActiveReplay()->GetMusicRate(); + + for (auto& p : v) { + auto noterow = p.first; + auto& evts = p.second; + + auto rowpos = m_Timing->GetElapsedTimeFromBeat(NoteRowToBeat(noterow)); + for (auto& evt : evts) { + auto supposedTime = evt.songPositionSeconds; + + if (evt.noterowJudged == -1) { + ghostTaps.push_back(evt); + continue; + } + + // discrepancy in notedata/timing from actual replay + // must update row and time to match current chart + if (fabsf(rowpos - supposedTime) > 0.01F) { + // haha oh my god + noterow = BeatToNoteRow(m_Timing->GetBeatFromElapsedTime( + m_Timing->GetElapsedTimeFromBeat( + NoteRowToBeat(evt.noterowJudged)) + + (evt.offset * musicRate))); + gapError = rowpos - supposedTime; + } + + if (playbackEvents.count(noterow) == 0u) { + playbackEvents.emplace(noterow, std::vector()); + } + playbackEvents.at(noterow).push_back(evt); + } + } + + // handle ghost taps last because we didnt have enough data to fix gaps + for (auto& evt : ghostTaps) { + auto time = + m_Timing->GetElapsedTimeFromBeat(NoteRowToBeat(evt.noterow)); + auto newrow = + BeatToNoteRow(m_Timing->GetBeatFromElapsedTime(time - gapError)); + if (playbackEvents.count(newrow) == 0u) { + playbackEvents.emplace(newrow, std::vector()); + } + playbackEvents.at(newrow).push_back(evt); + } +} + void PlayerReplay::CheckForSteps(const std::chrono::steady_clock::time_point& tm) { @@ -332,10 +387,36 @@ PlayerReplay::CheckForSteps(const std::chrono::steady_clock::time_point& tm) } // execute all the events - for (PlaybackEvent evt : evts) { + for (const auto& evt : evts) { if (evt.isPress) { + if (holdingColumns.contains(evt.track)) { + // it wont break the game, but we should track dupe presses + Locator::getLogger()->warn( + "Please report an issue with this replay: {} - press {}, row " + "{}, judgerow {}, time {}, col {}", + REPLAYS->GetActiveReplay()->GetScoreKey(), + evt.isPress, + evt.noterow, + evt.noterowJudged, + evt.songPositionSeconds, + evt.track); + } + holdingColumns.insert(evt.track); } else { + if (!holdingColumns.contains(evt.track)) { + // it wont break the game, but we should track dupe releases + Locator::getLogger()->warn( + "Please report an issue with this replay: {} - press {}, row " + "{}, judgerow {}, time {}, col {}", + REPLAYS->GetActiveReplay()->GetScoreKey(), + evt.isPress, + evt.noterow, + evt.noterowJudged, + evt.songPositionSeconds, + evt.track); + } + holdingColumns.erase(evt.track); } Step(evt.track, diff --git a/src/Etterna/Actor/Gameplay/PlayerReplay.h b/src/Etterna/Actor/Gameplay/PlayerReplay.h index d1fadffacb..0903cf2869 100644 --- a/src/Etterna/Actor/Gameplay/PlayerReplay.h +++ b/src/Etterna/Actor/Gameplay/PlayerReplay.h @@ -37,9 +37,8 @@ class PlayerReplay : public Player std::map>& GetPlaybackEvents() { return playbackEvents; } - void SetPlaybackEvents(const std::map>& v) { - playbackEvents = v; - } + void SetPlaybackEvents(const std::map>& v); + std::map>& GetDroppedHolds() { return droppedHolds; } diff --git a/src/Etterna/Models/HighScore/Replay.cpp b/src/Etterna/Models/HighScore/Replay.cpp index 0e1e3e05a6..3014a033fe 100644 --- a/src/Etterna/Models/HighScore/Replay.cpp +++ b/src/Etterna/Models/HighScore/Replay.cpp @@ -2876,10 +2876,9 @@ Replay::GeneratePlaybackEvents(int startRow) -> std::mapGetBeatFromElapsedTime(positionSeconds)); + BeatToNoteRow(td->GetBeatFromElapsedTime(evtPositionSeconds)); if (evt.nearestTapNoterow < startRow) { if (evt.nearestTapNoterow == -1) { // for ghost taps, only remove them if they are truly too early @@ -2892,8 +2891,9 @@ Replay::GeneratePlaybackEvents(int startRow) -> std::map()); } diff --git a/src/Etterna/Models/HighScore/ReplayConstantsAndTypes.h b/src/Etterna/Models/HighScore/ReplayConstantsAndTypes.h index 8164a979bd..c8dfc0fbe5 100644 --- a/src/Etterna/Models/HighScore/ReplayConstantsAndTypes.h +++ b/src/Etterna/Models/HighScore/ReplayConstantsAndTypes.h @@ -183,6 +183,7 @@ struct PlaybackEvent // only applies if the event judges a note // to prevent events triggering wrong judgments int noterowJudged = -1; + float offset = 0.F; PlaybackEvent() {