mirror of
https://github.com/ppy/osu.git
synced 2026-05-16 15:43:04 +08:00
91f3be5fea
Closes https://github.com/ppy/osu/issues/32420. The failure cause here is that in editor the beatmap version for the beatmap affected (or... any beatmap, really), is 0 (ZERO). That is probably a regression from https://github.com/ppy/osu/pull/32315, but like... can we universally agree that calling that change "a regression" in any capacity is dumb? Like what was that code *doing* playing dumb reference games and copying stuff into an arbitrary instance that could get or not get used later on? And now you have a 50/50 chance of accessing the *correct* model's field, depending on whether you go via `BeatmapInfo` or `Beatmap.BeatmapInfo`? Moving the field to `IBeatmap`, i.e. what is by now - by consensus, since https://github.com/ppy/osu/pull/28473 - supposed to be the "decoded and materialised" beatmap, fixes this issue. I probably should have done this as part of https://github.com/ppy/osu/pull/28473 but it slipped my mind. Probably for the better too because this change has rather large chances of breaking stuff so maybe better to examine it in isolation (via diffcalc runs or whatever). For added humour points, you'd say that the field on `BeatmapInfo` was not `[Ignore]`d, so this is a realm schema change, right? No. As far as I can tell, it's not. I opened realm studio and `BeatmapVersion` *is not a listed column` on `Beatmap` models. I'm also not gonna get into the fact that I think `EditorBeatmap` doing dumb games with juggling two `BeatmapInfo` references since https://github.com/ppy/osu/pull/15075 is bad, because I don't think I have the mental capacity to hotfix this by going down that train of thought.
140 lines
5.5 KiB
C#
140 lines
5.5 KiB
C#
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
#nullable disable
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using osu.Framework.Lists;
|
|
using osu.Game.Beatmaps.Timing;
|
|
using osu.Game.Rulesets;
|
|
using osu.Game.Rulesets.Objects;
|
|
|
|
namespace osu.Game.Beatmaps
|
|
{
|
|
/// <summary>
|
|
/// Converts a Beatmap for another mode.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of HitObject stored in the Beatmap.</typeparam>
|
|
public abstract class BeatmapConverter<T> : IBeatmapConverter
|
|
where T : HitObject
|
|
{
|
|
private event Action<HitObject, IEnumerable<HitObject>> objectConverted;
|
|
|
|
event Action<HitObject, IEnumerable<HitObject>> IBeatmapConverter.ObjectConverted
|
|
{
|
|
add => objectConverted += value;
|
|
remove => objectConverted -= value;
|
|
}
|
|
|
|
public IBeatmap Beatmap { get; }
|
|
|
|
protected BeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
|
|
{
|
|
Beatmap = beatmap;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether <see cref="Beatmap"/> can be converted by this <see cref="BeatmapConverter{T}"/>.
|
|
/// </summary>
|
|
public abstract bool CanConvert();
|
|
|
|
public IBeatmap Convert(CancellationToken cancellationToken = default)
|
|
{
|
|
// We always operate on a clone of the original beatmap, to not modify it game-wide
|
|
var original = Beatmap.Clone();
|
|
|
|
// Shallow clone isn't enough to ensure we don't mutate beatmap info unexpectedly.
|
|
// Can potentially be removed after `Beatmap.Difficulty` doesn't save back to `Beatmap.BeatmapInfo`.
|
|
original.BeatmapInfo = original.BeatmapInfo.Clone();
|
|
original.ControlPointInfo = original.ControlPointInfo.DeepClone();
|
|
|
|
// Used in osu!mania conversion.
|
|
original.Breaks = new SortedList<BreakPeriod>(Comparer<BreakPeriod>.Default);
|
|
original.Breaks.AddRange(Beatmap.Breaks);
|
|
|
|
return ConvertBeatmap(original, cancellationToken);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Performs the conversion of a Beatmap using this Beatmap Converter.
|
|
/// </summary>
|
|
/// <param name="original">The un-converted Beatmap.</param>
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
|
/// <returns>The converted Beatmap.</returns>
|
|
protected virtual Beatmap<T> ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken)
|
|
{
|
|
var beatmap = CreateBeatmap();
|
|
|
|
beatmap.BeatmapInfo = original.BeatmapInfo;
|
|
beatmap.ControlPointInfo = original.ControlPointInfo;
|
|
beatmap.HitObjects = convertHitObjects(original.HitObjects, original, cancellationToken).OrderBy(s => s.StartTime).ToList();
|
|
beatmap.Breaks = original.Breaks;
|
|
beatmap.UnhandledEventLines = original.UnhandledEventLines;
|
|
beatmap.AudioLeadIn = original.AudioLeadIn;
|
|
beatmap.StackLeniency = original.StackLeniency;
|
|
beatmap.SpecialStyle = original.SpecialStyle;
|
|
beatmap.LetterboxInBreaks = original.LetterboxInBreaks;
|
|
beatmap.WidescreenStoryboard = original.WidescreenStoryboard;
|
|
beatmap.EpilepsyWarning = original.EpilepsyWarning;
|
|
beatmap.SamplesMatchPlaybackRate = original.SamplesMatchPlaybackRate;
|
|
beatmap.DistanceSpacing = original.DistanceSpacing;
|
|
beatmap.GridSize = original.GridSize;
|
|
beatmap.TimelineZoom = original.TimelineZoom;
|
|
beatmap.Countdown = original.Countdown;
|
|
beatmap.CountdownOffset = original.CountdownOffset;
|
|
beatmap.Bookmarks = original.Bookmarks;
|
|
beatmap.BeatmapVersion = original.BeatmapVersion;
|
|
|
|
return beatmap;
|
|
}
|
|
|
|
private List<T> convertHitObjects(IReadOnlyList<HitObject> hitObjects, IBeatmap beatmap, CancellationToken cancellationToken)
|
|
{
|
|
var result = new List<T>(hitObjects.Count);
|
|
|
|
foreach (var obj in hitObjects)
|
|
{
|
|
if (obj is T tObj)
|
|
{
|
|
result.Add(tObj);
|
|
continue;
|
|
}
|
|
|
|
var converted = ConvertHitObject(obj, beatmap, cancellationToken);
|
|
|
|
if (objectConverted != null)
|
|
{
|
|
converted = converted.ToList();
|
|
objectConverted.Invoke(obj, converted);
|
|
}
|
|
|
|
foreach (var c in converted)
|
|
{
|
|
if (c != null)
|
|
result.Add(c);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates the <see cref="Beatmap{T}"/> that will be returned by this <see cref="BeatmapProcessor"/>.
|
|
/// </summary>
|
|
protected virtual Beatmap<T> CreateBeatmap() => new Beatmap<T>();
|
|
|
|
/// <summary>
|
|
/// Performs the conversion of a hit object.
|
|
/// This method is generally executed sequentially for all objects in a beatmap.
|
|
/// </summary>
|
|
/// <param name="original">The hit object to convert.</param>
|
|
/// <param name="beatmap">The un-converted Beatmap.</param>
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
|
/// <returns>The converted hit object.</returns>
|
|
protected virtual IEnumerable<T> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken) => Enumerable.Empty<T>();
|
|
}
|
|
}
|