mirror of
https://github.com/ppy/osu.git
synced 2026-05-17 01:43:01 +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.
158 lines
5.6 KiB
C#
158 lines
5.6 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.
|
|
|
|
using System;
|
|
using osu.Game.Beatmaps.Timing;
|
|
using osu.Game.Rulesets.Objects;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using osu.Game.Beatmaps.ControlPoints;
|
|
using Newtonsoft.Json;
|
|
using osu.Framework.Lists;
|
|
using osu.Game.Beatmaps.Formats;
|
|
using osu.Game.IO.Serialization.Converters;
|
|
|
|
namespace osu.Game.Beatmaps
|
|
{
|
|
public class Beatmap<T> : IBeatmap<T>
|
|
where T : HitObject
|
|
{
|
|
private BeatmapDifficulty difficulty = new BeatmapDifficulty();
|
|
|
|
public BeatmapDifficulty Difficulty
|
|
{
|
|
get => difficulty;
|
|
set
|
|
{
|
|
difficulty = value;
|
|
|
|
beatmapInfo.Difficulty = difficulty.Clone();
|
|
}
|
|
}
|
|
|
|
private BeatmapInfo beatmapInfo;
|
|
|
|
public BeatmapInfo BeatmapInfo
|
|
{
|
|
get => beatmapInfo;
|
|
set
|
|
{
|
|
beatmapInfo = value;
|
|
|
|
Difficulty = beatmapInfo.Difficulty.Clone();
|
|
}
|
|
}
|
|
|
|
public Beatmap()
|
|
{
|
|
beatmapInfo = new BeatmapInfo
|
|
{
|
|
Metadata = new BeatmapMetadata
|
|
{
|
|
Artist = @"Unknown",
|
|
Title = @"Unknown",
|
|
Author = { Username = @"Unknown Creator" },
|
|
},
|
|
DifficultyName = @"Normal",
|
|
Difficulty = Difficulty,
|
|
};
|
|
}
|
|
|
|
[JsonIgnore]
|
|
public BeatmapMetadata Metadata => BeatmapInfo.Metadata;
|
|
|
|
public ControlPointInfo ControlPointInfo { get; set; } = new ControlPointInfo();
|
|
|
|
public SortedList<BreakPeriod> Breaks { get; set; } = new SortedList<BreakPeriod>(Comparer<BreakPeriod>.Default);
|
|
|
|
public List<string> UnhandledEventLines { get; set; } = new List<string>();
|
|
|
|
[JsonIgnore]
|
|
public double TotalBreakTime => Breaks.Sum(b => b.Duration);
|
|
|
|
[JsonConverter(typeof(TypedListConverter<HitObject>))]
|
|
public List<T> HitObjects { get; set; } = new List<T>();
|
|
|
|
IReadOnlyList<T> IBeatmap<T>.HitObjects => HitObjects;
|
|
|
|
IReadOnlyList<HitObject> IBeatmap.HitObjects => HitObjects;
|
|
|
|
public virtual IEnumerable<BeatmapStatistic> GetStatistics() => Enumerable.Empty<BeatmapStatistic>();
|
|
|
|
public double GetMostCommonBeatLength()
|
|
{
|
|
double lastTime;
|
|
|
|
// The last playable time in the beatmap - the last timing point extends to this time.
|
|
// Note: This is more accurate and may present different results because osu-stable didn't have the ability to calculate slider durations in this context.
|
|
if (!HitObjects.Any())
|
|
lastTime = ControlPointInfo.TimingPoints.LastOrDefault()?.Time ?? 0;
|
|
else
|
|
lastTime = this.GetLastObjectTime();
|
|
|
|
var mostCommon =
|
|
// Construct a set of (beatLength, duration) tuples for each individual timing point.
|
|
ControlPointInfo.TimingPoints.Select((t, i) =>
|
|
{
|
|
if (t.Time > lastTime)
|
|
return (beatLength: t.BeatLength, 0);
|
|
|
|
// osu-stable forced the first control point to start at 0.
|
|
// This is reproduced here to maintain compatibility around osu!mania scroll speed and song select display.
|
|
double currentTime = i == 0 ? 0 : t.Time;
|
|
double nextTime = i == ControlPointInfo.TimingPoints.Count - 1 ? lastTime : ControlPointInfo.TimingPoints[i + 1].Time;
|
|
|
|
return (beatLength: t.BeatLength, duration: nextTime - currentTime);
|
|
})
|
|
// Aggregate durations into a set of (beatLength, duration) tuples for each beat length
|
|
.GroupBy(t => Math.Round(t.beatLength * 1000) / 1000)
|
|
.Select(g => (beatLength: g.Key, duration: g.Sum(t => t.duration)))
|
|
// Get the most common one, or 0 as a suitable default (see handling below)
|
|
.OrderByDescending(i => i.duration).FirstOrDefault();
|
|
|
|
if (mostCommon.beatLength == 0)
|
|
return TimingControlPoint.DEFAULT_BEAT_LENGTH;
|
|
|
|
return mostCommon.beatLength;
|
|
}
|
|
|
|
public double AudioLeadIn { get; set; }
|
|
|
|
public float StackLeniency { get; set; } = 0.7f;
|
|
|
|
public bool SpecialStyle { get; set; }
|
|
|
|
public bool LetterboxInBreaks { get; set; }
|
|
|
|
public bool WidescreenStoryboard { get; set; } = true;
|
|
|
|
public bool EpilepsyWarning { get; set; }
|
|
|
|
public bool SamplesMatchPlaybackRate { get; set; }
|
|
|
|
public double DistanceSpacing { get; set; } = 1.0;
|
|
|
|
public int GridSize { get; set; }
|
|
|
|
public double TimelineZoom { get; set; } = 1.0;
|
|
|
|
public CountdownType Countdown { get; set; } = CountdownType.None;
|
|
|
|
public int CountdownOffset { get; set; }
|
|
|
|
public int[] Bookmarks { get; set; } = Array.Empty<int>();
|
|
|
|
public int BeatmapVersion { get; set; } = LegacyBeatmapEncoder.FIRST_LAZER_VERSION;
|
|
|
|
IBeatmap IBeatmap.Clone() => Clone();
|
|
|
|
public Beatmap<T> Clone() => (Beatmap<T>)MemberwiseClone();
|
|
|
|
public override string ToString() => BeatmapInfo.ToString();
|
|
}
|
|
|
|
public class Beatmap : Beatmap<HitObject>
|
|
{
|
|
}
|
|
}
|