mirror of
https://github.com/SK-la/Ez2Lazer.git
synced 2026-03-13 11:20:28 +00:00
Fix replays being misrecorded if an action is pressed and released in one update frame
Closes https://github.com/ppy/osu/issues/33465 probably. This reverts the replay frame de-duplication logic to what it was before https://github.com/ppy/osu/pull/33148#discussion_r2091549388. I don't have good reproduction steps. I tried to write a test case for this that isn't just "press and release a key in the same frame", thinking that maybe there was some loophole in the osu! touch input mapper that may produce this situation artificially, but I could not in many configurations. So I have to assume that this just *can happen* organically.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osuTK;
|
||||
|
||||
@@ -17,5 +18,8 @@ namespace osu.Game.Rulesets.EmptyFreeform.Replays
|
||||
if (button.HasValue)
|
||||
Actions.Add(button.Value);
|
||||
}
|
||||
|
||||
public override bool IsEquivalentTo(ReplayFrame other)
|
||||
=> other is EmptyFreeformReplayFrame freeformFrame && Time == freeformFrame.Time && Position == freeformFrame.Position && Actions.SequenceEqual(freeformFrame.Actions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,5 +9,8 @@ namespace osu.Game.Rulesets.Pippidon.Replays
|
||||
public class PippidonReplayFrame : ReplayFrame
|
||||
{
|
||||
public Vector2 Position;
|
||||
|
||||
public override bool IsEquivalentTo(ReplayFrame other)
|
||||
=> other is PippidonReplayFrame pippidonFrame && Time == pippidonFrame.Time && Position == pippidonFrame.Position;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.EmptyScrolling.Replays
|
||||
@@ -15,5 +16,8 @@ namespace osu.Game.Rulesets.EmptyScrolling.Replays
|
||||
if (button.HasValue)
|
||||
Actions.Add(button.Value);
|
||||
}
|
||||
|
||||
public override bool IsEquivalentTo(ReplayFrame other)
|
||||
=> other is EmptyScrollingReplayFrame scrollingFrame && Time == scrollingFrame.Time && Actions.SequenceEqual(scrollingFrame.Actions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Pippidon.Replays
|
||||
@@ -15,5 +16,8 @@ namespace osu.Game.Rulesets.Pippidon.Replays
|
||||
if (button.HasValue)
|
||||
Actions.Add(button.Value);
|
||||
}
|
||||
|
||||
public override bool IsEquivalentTo(ReplayFrame other)
|
||||
=> other is PippidonReplayFrame pippidonFrame && Time == pippidonFrame.Time && Actions.SequenceEqual(pippidonFrame.Actions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
@@ -64,5 +65,12 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
|
||||
return new LegacyReplayFrame(Time, Position, null, state);
|
||||
}
|
||||
|
||||
public override bool IsEquivalentTo(ReplayFrame other)
|
||||
=> other is CatchReplayFrame catchFrame
|
||||
&& Time == catchFrame.Time
|
||||
&& Position == catchFrame.Position
|
||||
&& Dashing == catchFrame.Dashing
|
||||
&& Actions.SequenceEqual(catchFrame.Actions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
@@ -47,5 +48,8 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
|
||||
return new LegacyReplayFrame(Time, keys, null, ReplayButtonState.None);
|
||||
}
|
||||
|
||||
public override bool IsEquivalentTo(ReplayFrame other)
|
||||
=> other is ManiaReplayFrame maniaFrame && Time == maniaFrame.Time && Actions.SequenceEqual(maniaFrame.Actions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
@@ -47,5 +48,8 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
|
||||
return new LegacyReplayFrame(Time, Position.X, Position.Y, state);
|
||||
}
|
||||
|
||||
public override bool IsEquivalentTo(ReplayFrame other)
|
||||
=> other is OsuReplayFrame osuFrame && Time == osuFrame.Time && Position == osuFrame.Position && Actions.SequenceEqual(osuFrame.Actions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
@@ -42,5 +43,8 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
||||
|
||||
return new LegacyReplayFrame(Time, null, null, state);
|
||||
}
|
||||
|
||||
public override bool IsEquivalentTo(ReplayFrame other)
|
||||
=> other is TaikoReplayFrame taikoFrame && Time == taikoFrame.Time && Actions.SequenceEqual(taikoFrame.Actions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,6 +383,9 @@ namespace osu.Game.Tests.NonVisual
|
||||
IsImportant = isImportant;
|
||||
FrameIndex = frameIndex;
|
||||
}
|
||||
|
||||
public override bool IsEquivalentTo(ReplayFrame other)
|
||||
=> other is TestReplayFrame testFrame && Time == testFrame.Time && IsImportant == testFrame.IsImportant && FrameIndex == testFrame.FrameIndex;
|
||||
}
|
||||
|
||||
private class TestInputHandler : FramedReplayInputHandler<TestReplayFrame>
|
||||
|
||||
@@ -317,6 +317,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Position = position;
|
||||
Actions.AddRange(actions);
|
||||
}
|
||||
|
||||
public override bool IsEquivalentTo(ReplayFrame other)
|
||||
=> other is TestReplayFrame testFrame && Time == testFrame.Time && Position == testFrame.Position && Actions.SequenceEqual(testFrame.Actions);
|
||||
}
|
||||
|
||||
public enum TestAction
|
||||
|
||||
@@ -353,6 +353,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
return new LegacyReplayFrame(Time, Position.X, Position.Y, state);
|
||||
}
|
||||
|
||||
public override bool IsEquivalentTo(ReplayFrame other)
|
||||
=> other is TestReplayFrame testFrame && Time == testFrame.Time && Position == testFrame.Position && Actions.SequenceEqual(testFrame.Actions);
|
||||
}
|
||||
|
||||
public enum TestAction
|
||||
|
||||
@@ -247,12 +247,10 @@ namespace osu.Game.Online.Spectator
|
||||
|
||||
var convertedFrame = convertible.ToLegacy(currentBeatmap);
|
||||
|
||||
// only keep the last recorded frame for a given timestamp.
|
||||
// this reduces redundancy of frames in the resulting replay.
|
||||
//
|
||||
// this is also done at `ReplayRecorded`, but needs to be done here as well
|
||||
// it is also done at `ReplayRecorder`, but needs to be done here as well
|
||||
// due to the flow being handled differently.
|
||||
if (pendingFrames.LastOrDefault()?.Time == convertedFrame.Time)
|
||||
if (pendingFrames.LastOrDefault()?.IsEquivalentTo(convertedFrame) == true)
|
||||
pendingFrames[^1] = convertedFrame;
|
||||
else
|
||||
pendingFrames.Add(convertedFrame);
|
||||
|
||||
@@ -64,5 +64,12 @@ namespace osu.Game.Replays.Legacy
|
||||
{
|
||||
return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}";
|
||||
}
|
||||
|
||||
public override bool IsEquivalentTo(ReplayFrame other)
|
||||
=> other is LegacyReplayFrame legacyFrame
|
||||
&& Time == legacyFrame.Time
|
||||
&& MouseX == legacyFrame.MouseX
|
||||
&& MouseY == legacyFrame.MouseY
|
||||
&& ButtonState == legacyFrame.ButtonState;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,5 +30,10 @@ namespace osu.Game.Rulesets.Replays
|
||||
{
|
||||
Time = time;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this frame is equivalent to <paramref name="other"/> with respect to replay recording.
|
||||
/// </summary>
|
||||
public virtual bool IsEquivalentTo(ReplayFrame other) => Time == other.Time;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,9 +86,8 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
if (frame != null)
|
||||
{
|
||||
// only keep the last recorded frame for a given timestamp.
|
||||
// this reduces redundancy of frames in the resulting replay.
|
||||
if (last?.Time == frame.Time)
|
||||
if (last?.IsEquivalentTo(frame) == true)
|
||||
target.Replay.Frames[^1] = frame;
|
||||
else
|
||||
target.Replay.Frames.Add(frame);
|
||||
|
||||
Reference in New Issue
Block a user