From f15b6b1d71a0b9d45b66a0861fc5d3878755a4fa Mon Sep 17 00:00:00 2001 From: OliBomby Date: Wed, 13 Nov 2024 14:09:19 +0100 Subject: [PATCH 1/3] Create LegacyBeatmapExporterTest --- .../Beatmaps/IO/LegacyBeatmapExporterTest.cs | 64 ++++++++++++++++++ .../Archives/decimal-timing-beatmap.olz | Bin 0 -> 990 bytes 2 files changed, 64 insertions(+) create mode 100644 osu.Game.Tests/Beatmaps/IO/LegacyBeatmapExporterTest.cs create mode 100644 osu.Game.Tests/Resources/Archives/decimal-timing-beatmap.olz diff --git a/osu.Game.Tests/Beatmaps/IO/LegacyBeatmapExporterTest.cs b/osu.Game.Tests/Beatmaps/IO/LegacyBeatmapExporterTest.cs new file mode 100644 index 0000000000..7973867114 --- /dev/null +++ b/osu.Game.Tests/Beatmaps/IO/LegacyBeatmapExporterTest.cs @@ -0,0 +1,64 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.IO; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Tests.Resources; +using osu.Game.Tests.Visual; +using MemoryStream = System.IO.MemoryStream; + +namespace osu.Game.Tests.Beatmaps.IO +{ + [HeadlessTest] + public partial class LegacyBeatmapExporterTest : OsuTestScene + { + [Resolved] + private BeatmapManager beatmaps { get; set; } = null!; + + [Test] + public void TestObjectsSnappedAfterTruncatingExport() + { + IWorkingBeatmap beatmap = null!; + MemoryStream outStream = null!; + + // Ensure importer encoding is correct + AddStep("import beatmap", () => beatmap = importBeatmapFromArchives(@"decimal-timing-beatmap.olz")); + AddAssert("timing point has decimal offset", () => Math.Abs(beatmap.Beatmap.ControlPointInfo.TimingPoints[0].Time - 284.725) < 0.001); + AddAssert("kiai has decimal offset", () => Math.Abs(beatmap.Beatmap.ControlPointInfo.EffectPoints[0].Time - 28520.019) < 0.001); + AddAssert("hit object has decimal offset", () => Math.Abs(beatmap.Beatmap.HitObjects[0].StartTime - 28520.019) < 0.001); + + // Ensure exporter legacy conversion is correct + AddStep("export", () => + { + outStream = new MemoryStream(); + + new LegacyBeatmapExporter(LocalStorage) + .ExportToStream((BeatmapSetInfo)beatmap.BeatmapInfo.BeatmapSet!, outStream, null); + }); + + AddStep("import beatmap again", () => beatmap = importBeatmapFromStream(outStream)); + AddAssert("timing point has truncated offset", () => Math.Abs(beatmap.Beatmap.ControlPointInfo.TimingPoints[0].Time - 284) < 0.001); + AddAssert("kiai is snapped", () => Math.Abs(beatmap.Beatmap.ControlPointInfo.EffectPoints[0].Time - 28519) < 0.001); + AddAssert("hit object is snapped", () => Math.Abs(beatmap.Beatmap.HitObjects[0].StartTime - 28519) < 0.001); + } + + private IWorkingBeatmap importBeatmapFromStream(Stream stream) + { + var imported = beatmaps.Import(new ImportTask(stream, "filename.osz")).GetResultSafely(); + return imported.AsNonNull().PerformRead(s => beatmaps.GetWorkingBeatmap(s.Beatmaps[0])); + } + + private IWorkingBeatmap importBeatmapFromArchives(string filename) + { + var imported = beatmaps.Import(new ImportTask(TestResources.OpenResource($@"Archives/{filename}"), filename)).GetResultSafely(); + return imported.AsNonNull().PerformRead(s => beatmaps.GetWorkingBeatmap(s.Beatmaps[0])); + } + } +} diff --git a/osu.Game.Tests/Resources/Archives/decimal-timing-beatmap.olz b/osu.Game.Tests/Resources/Archives/decimal-timing-beatmap.olz new file mode 100644 index 0000000000000000000000000000000000000000..38dedc35d15f1a11baa49f00917acf93ee2b23eb GIT binary patch literal 990 zcmWIWW@Zs#U|`^2m|l<@`RKWA-YF&q1~FC!26v!nXiFO#b zw&N&j>KL znO3-0PWb%6t>+K57#VpNeE9Hmt9*dd^{Hx?cku7*jW;-d@=k8K>={M>7u^ek+@IYv zoj>3CgTO4~DH^7hy}CR}=M~dEPXG8CSux{4?Ys5|*7u$6t27v#H=L`x*!%6$qWV`w zn`f53%jw*-K8Ec~qH=#!{|i>jkF{kB8-JcNwOeBtS@cOD#_l8g{o^JJC)s%JoBKQ8 zU{1T6mHY$ep4)*hMK)!}mT{kX%IA3eUq;NuJ&xgD&i&r8yq8@A-FR8}pm; zlYYnlVAouHmh*1ASkV0=EPq|!I6cYAnl-^HB0l#sTh;x^+h=?>{4?)k_V!~p9V5#3 z-MmM?2AGz!%` zar*FwypriXi3$8=yG3F*OL}&lvF`WtH+)+@x81~N&!*$6&1PkWetyODN*{pU6T+^F&&vvb6;ZXM071Pt! z{XC79`erV<5EK^S7rR{LeA<#Vqq literal 0 HcmV?d00001 From b6eff38520b632d1fe311458ce761e27e0be1ce5 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Wed, 13 Nov 2024 14:23:28 +0100 Subject: [PATCH 2/3] Fix timing point truncation in legacy beatmap export --- osu.Game/Database/LegacyBeatmapExporter.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index 17c2c8c88d..eb48425588 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -59,7 +59,25 @@ namespace osu.Game.Database }; // Convert beatmap elements to be compatible with legacy format - // So we truncate time and position values to integers, and convert paths with multiple segments to bezier curves + // So we truncate time and position values to integers, and convert paths with multiple segments to Bézier curves + + // We must first truncate all timing points and move all objects in the timing section with it to ensure everything stays snapped + for (int i = 0; i < playableBeatmap.ControlPointInfo.TimingPoints.Count; i++) + { + var timingPoint = playableBeatmap.ControlPointInfo.TimingPoints[i]; + double offset = Math.Floor(timingPoint.Time) - timingPoint.Time; + double nextTimingPointTime = i + 1 < playableBeatmap.ControlPointInfo.TimingPoints.Count + ? playableBeatmap.ControlPointInfo.TimingPoints[i + 1].Time + : double.PositiveInfinity; + + // Offset all control points in the timing section (including the current one) + foreach (var controlPoint in playableBeatmap.ControlPointInfo.AllControlPoints.Where(o => o.Time >= timingPoint.Time && o.Time < nextTimingPointTime)) + controlPoint.Time += offset; + + // Offset all hit objects in the timing section + foreach (var hitObject in playableBeatmap.HitObjects.Where(o => o.StartTime >= timingPoint.Time && o.StartTime < nextTimingPointTime)) + hitObject.StartTime += offset; + } foreach (var controlPoint in playableBeatmap.ControlPointInfo.AllControlPoints) controlPoint.Time = Math.Floor(controlPoint.Time); From 8df7a6f2f31226dab8e1a1d300968b527e24c00b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Nov 2024 14:24:11 +0100 Subject: [PATCH 3/3] Adjust test to have better assertions --- .../Beatmaps/IO/LegacyBeatmapExporterTest.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/LegacyBeatmapExporterTest.cs b/osu.Game.Tests/Beatmaps/IO/LegacyBeatmapExporterTest.cs index 7973867114..9947def06d 100644 --- a/osu.Game.Tests/Beatmaps/IO/LegacyBeatmapExporterTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/LegacyBeatmapExporterTest.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.IO; using NUnit.Framework; using osu.Framework.Allocation; @@ -30,9 +29,9 @@ namespace osu.Game.Tests.Beatmaps.IO // Ensure importer encoding is correct AddStep("import beatmap", () => beatmap = importBeatmapFromArchives(@"decimal-timing-beatmap.olz")); - AddAssert("timing point has decimal offset", () => Math.Abs(beatmap.Beatmap.ControlPointInfo.TimingPoints[0].Time - 284.725) < 0.001); - AddAssert("kiai has decimal offset", () => Math.Abs(beatmap.Beatmap.ControlPointInfo.EffectPoints[0].Time - 28520.019) < 0.001); - AddAssert("hit object has decimal offset", () => Math.Abs(beatmap.Beatmap.HitObjects[0].StartTime - 28520.019) < 0.001); + AddAssert("timing point has decimal offset", () => beatmap.Beatmap.ControlPointInfo.TimingPoints[0].Time, () => Is.EqualTo(284.725).Within(0.001)); + AddAssert("kiai has decimal offset", () => beatmap.Beatmap.ControlPointInfo.EffectPoints[0].Time, () => Is.EqualTo(28520.019).Within(0.001)); + AddAssert("hit object has decimal offset", () => beatmap.Beatmap.HitObjects[0].StartTime, () => Is.EqualTo(28520.019).Within(0.001)); // Ensure exporter legacy conversion is correct AddStep("export", () => @@ -44,9 +43,9 @@ namespace osu.Game.Tests.Beatmaps.IO }); AddStep("import beatmap again", () => beatmap = importBeatmapFromStream(outStream)); - AddAssert("timing point has truncated offset", () => Math.Abs(beatmap.Beatmap.ControlPointInfo.TimingPoints[0].Time - 284) < 0.001); - AddAssert("kiai is snapped", () => Math.Abs(beatmap.Beatmap.ControlPointInfo.EffectPoints[0].Time - 28519) < 0.001); - AddAssert("hit object is snapped", () => Math.Abs(beatmap.Beatmap.HitObjects[0].StartTime - 28519) < 0.001); + AddAssert("timing point has truncated offset", () => beatmap.Beatmap.ControlPointInfo.TimingPoints[0].Time, () => Is.EqualTo(284).Within(0.001)); + AddAssert("kiai is snapped", () => beatmap.Beatmap.ControlPointInfo.EffectPoints[0].Time, () => Is.EqualTo(28519).Within(0.001)); + AddAssert("hit object is snapped", () => beatmap.Beatmap.HitObjects[0].StartTime, () => Is.EqualTo(28519).Within(0.001)); } private IWorkingBeatmap importBeatmapFromStream(Stream stream)