From bb50e8e3f858609b321b83eb8874ca06c7abcbfa Mon Sep 17 00:00:00 2001 From: WilliamQiufeng Date: Tue, 23 Dec 2025 18:52:42 +0800 Subject: [PATCH] Fix mines not handled properly in autoplay This was due to that when generating the autoplay replay, the last frame might not be after the mine, which would've been the case for human played replays (See ReplayCapturer.Capture, where it adds additional frames if judgement count doesn't match). So, an additional frame is added at the end + 10000ms to force handling all mines. --- Quaver.API/Replays/Replay.cs | 4 ++++ Quaver.API/Replays/Virtual/VirtualReplayPlayer.cs | 13 +++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Quaver.API/Replays/Replay.cs b/Quaver.API/Replays/Replay.cs index 75d183d17..707b15e84 100644 --- a/Quaver.API/Replays/Replay.cs +++ b/Quaver.API/Replays/Replay.cs @@ -378,6 +378,10 @@ public static Replay GeneratePerfectReplayKeys(Replay replay, Qua map) replay.Frames.Add(new ReplayFrame(item.Key, state)); } + // Add ending frame w/ no press state. (+10000 just to be on the safe side.) + // This ensures that mines are handled at the end of the map. + replay.Frames.Add(new ReplayFrame(map.Length + 10000, 0)); + return replay; } diff --git a/Quaver.API/Replays/Virtual/VirtualReplayPlayer.cs b/Quaver.API/Replays/Virtual/VirtualReplayPlayer.cs index bda071c0b..a1887866f 100644 --- a/Quaver.API/Replays/Virtual/VirtualReplayPlayer.cs +++ b/Quaver.API/Replays/Virtual/VirtualReplayPlayer.cs @@ -153,8 +153,15 @@ public void PlayNextFrame() { var obj = Map.GetHitObjectAtJudgementIndex(i); - var hitStat = new HitStat(HitStatType.Miss, KeyPressType.None, obj, obj.StartTime, - Judgement.Miss, int.MinValue, ScoreProcessor.Accuracy, ScoreProcessor.Health); + var hitStat = obj.Type switch + { + HitObjectType.Normal => new HitStat(HitStatType.Miss, KeyPressType.None, obj, obj.StartTime, + Judgement.Miss, int.MinValue, ScoreProcessor.Accuracy, ScoreProcessor.Health), + HitObjectType.Mine => new HitStat(HitStatType.Hit, KeyPressType.None, obj, obj.StartTime, + Judgement.Marv, 0, ScoreProcessor.Accuracy, ScoreProcessor.Health), + _ => throw new ArgumentOutOfRangeException(nameof(obj.Type), obj.Type, + "Unhandled note type") + }; ScoreProcessor.CalculateScore(hitStat); @@ -230,6 +237,8 @@ private void HandleKeyPressesInFrame() // Handle mines that were hit between frames. // The previous frame's pressed keys are held up until now, so [previousFrameTime..Time) // is the interval to check for mine hits. + // This covers the last frame too! see ReplayCapturer.Capture: it adds a frame if judgement count + // does not match expected, forcing another call into this. foreach (var lane in previousFramePressed) { foreach (var mine in ActiveMines)