diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs index 53079a3b16..8a38bf2b08 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneObjectOrderedHitPolicy.cs @@ -3,12 +3,17 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; using NUnit.Framework; +using osu.Framework.Extensions; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Formats; using osu.Game.Replays; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; @@ -20,6 +25,7 @@ using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Scoring.Legacy; using osu.Game.Screens.Play; using osu.Game.Tests.Visual; using osuTK; @@ -30,6 +36,13 @@ namespace osu.Game.Rulesets.Osu.Tests { private readonly OsuHitWindows referenceHitWindows; + /// + /// This is provided as a convenience for testing note lock behaviour against osu!stable. + /// Setting this field to a non-null path will cause beatmap files and replays used in all test cases + /// to be exported to disk so that they can be cross-checked against stable. + /// + private readonly string? exportLocation = null; + public TestSceneObjectOrderedHitPolicy() { referenceHitWindows = new OsuHitWindows(); @@ -171,14 +184,14 @@ namespace osu.Game.Rulesets.Osu.Tests performTest(hitObjects, new List { - new OsuReplayFrame { Time = time_first_circle - 200, Position = positionFirstCircle, Actions = { OsuAction.LeftButton } }, - new OsuReplayFrame { Time = time_first_circle - 100, Position = positionSecondCircle, Actions = { OsuAction.RightButton } } + new OsuReplayFrame { Time = time_first_circle - 190, Position = positionFirstCircle, Actions = { OsuAction.LeftButton } }, + new OsuReplayFrame { Time = time_first_circle - 90, Position = positionSecondCircle, Actions = { OsuAction.RightButton } } }); addJudgementAssert(hitObjects[0], HitResult.Meh); addJudgementAssert(hitObjects[1], HitResult.Meh); - addJudgementOffsetAssert(hitObjects[0], -200); // time_first_circle - 200 - addJudgementOffsetAssert(hitObjects[0], -200); // time_second_circle - first_circle_time - 100 + addJudgementOffsetAssert(hitObjects[0], -190); // time_first_circle - 190 + addJudgementOffsetAssert(hitObjects[0], -90); // time_second_circle - first_circle_time - 90 } /// @@ -208,13 +221,13 @@ namespace osu.Game.Rulesets.Osu.Tests performTest(hitObjects, new List { - new OsuReplayFrame { Time = time_first_circle - 200, Position = positionFirstCircle, Actions = { OsuAction.LeftButton } }, + new OsuReplayFrame { Time = time_first_circle - 190, Position = positionFirstCircle, Actions = { OsuAction.LeftButton } }, new OsuReplayFrame { Time = time_first_circle, Position = positionSecondCircle, Actions = { OsuAction.RightButton } } }); addJudgementAssert(hitObjects[0], HitResult.Meh); addJudgementAssert(hitObjects[1], HitResult.Ok); - addJudgementOffsetAssert(hitObjects[0], -200); // time_first_circle - 200 + addJudgementOffsetAssert(hitObjects[0], -190); // time_first_circle - 190 addJudgementOffsetAssert(hitObjects[1], -100); // time_second_circle - first_circle_time } @@ -330,7 +343,7 @@ namespace osu.Game.Rulesets.Osu.Tests performTest(hitObjects, new List { - new OsuReplayFrame { Time = time_spinner - 100, Position = positionCircle, Actions = { OsuAction.LeftButton } }, + new OsuReplayFrame { Time = time_spinner - 90, Position = positionCircle, Actions = { OsuAction.LeftButton } }, new OsuReplayFrame { Time = time_spinner + 10, Position = new Vector2(236, 192), Actions = { OsuAction.RightButton } }, new OsuReplayFrame { Time = time_spinner + 20, Position = new Vector2(256, 172), Actions = { OsuAction.RightButton } }, new OsuReplayFrame { Time = time_spinner + 30, Position = new Vector2(276, 192), Actions = { OsuAction.RightButton } }, @@ -401,12 +414,21 @@ namespace osu.Game.Rulesets.Osu.Tests private ScoreAccessibleReplayPlayer currentPlayer = null!; private List judgementResults = null!; - private void performTest(List hitObjects, List frames) + private void performTest(List hitObjects, List frames, [CallerMemberName] string testCaseName = "") { - AddStep("load player", () => + IBeatmap playableBeatmap = null!; + Score score = null!; + + AddStep("create beatmap", () => { + var cpi = new ControlPointInfo(); + cpi.Add(0, new TimingControlPoint { BeatLength = 1000 }); Beatmap.Value = CreateWorkingBeatmap(new Beatmap { + Metadata = + { + Title = testCaseName + }, HitObjects = hitObjects, Difficulty = new BeatmapDifficulty { @@ -415,13 +437,67 @@ namespace osu.Game.Rulesets.Osu.Tests }, BeatmapInfo = { - Ruleset = new OsuRuleset().RulesetInfo + Ruleset = new OsuRuleset().RulesetInfo, + BeatmapVersion = LegacyBeatmapEncoder.FIRST_LAZER_VERSION // for correct offset treatment by score encoder }, + ControlPointInfo = cpi + }); + playableBeatmap = Beatmap.Value.GetPlayableBeatmap(new OsuRuleset().RulesetInfo); + }); + + AddStep("create score", () => + { + score = new Score + { + Replay = new Replay + { + Frames = new List + { + // required for correct playback in stable + new OsuReplayFrame(0, new Vector2(256, -500)), + new OsuReplayFrame(0, new Vector2(256, -500)) + }.Concat(frames).ToList() + }, + ScoreInfo = + { + Ruleset = new OsuRuleset().RulesetInfo, + BeatmapInfo = playableBeatmap.BeatmapInfo + } + }; + }); + + if (exportLocation != null) + { + AddStep("export beatmap", () => + { + var beatmapEncoder = new LegacyBeatmapEncoder(playableBeatmap, null); + + using (var stream = File.Open(Path.Combine(exportLocation, $"{testCaseName}.osu"), FileMode.Create)) + { + var memoryStream = new MemoryStream(); + using (var writer = new StreamWriter(memoryStream, Encoding.UTF8, leaveOpen: true)) + beatmapEncoder.Encode(writer); + + memoryStream.Seek(0, SeekOrigin.Begin); + memoryStream.CopyTo(stream); + memoryStream.Seek(0, SeekOrigin.Begin); + playableBeatmap.BeatmapInfo.MD5Hash = memoryStream.ComputeMD5Hash(); + } }); + AddStep("export score", () => + { + using var stream = File.Open(Path.Combine(exportLocation, $"{testCaseName}.osr"), FileMode.Create); + var encoder = new LegacyScoreEncoder(score, playableBeatmap); + encoder.Encode(stream); + }); + } + + AddStep("load player", () => + { SelectedMods.Value = new[] { new OsuModClassic() }; - var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } }); + var p = new ScoreAccessibleReplayPlayer(score); p.OnLoadComplete += _ => {