Apply NRT to WorkingBeatmap

This commit is contained in:
Dean Herbert 2021-12-22 18:40:37 +09:00
parent 53c0a6708f
commit 9e17d7d4e3
4 changed files with 37 additions and 31 deletions

View File

@ -37,12 +37,12 @@ namespace osu.Game.Beatmaps
/// <summary> /// <summary>
/// Retrieves the <see cref="IBeatmap"/> which this <see cref="IWorkingBeatmap"/> represents. /// Retrieves the <see cref="IBeatmap"/> which this <see cref="IWorkingBeatmap"/> represents.
/// </summary> /// </summary>
IBeatmap Beatmap { get; } IBeatmap? Beatmap { get; }
/// <summary> /// <summary>
/// Retrieves the background for this <see cref="IWorkingBeatmap"/>. /// Retrieves the background for this <see cref="IWorkingBeatmap"/>.
/// </summary> /// </summary>
Texture Background { get; } Texture? Background { get; }
/// <summary> /// <summary>
/// Retrieves the <see cref="Waveform"/> for the <see cref="Track"/> of this <see cref="IWorkingBeatmap"/>. /// Retrieves the <see cref="Waveform"/> for the <see cref="Track"/> of this <see cref="IWorkingBeatmap"/>.
@ -57,12 +57,12 @@ namespace osu.Game.Beatmaps
/// <summary> /// <summary>
/// Retrieves the <see cref="Skin"/> which this <see cref="IWorkingBeatmap"/> provides. /// Retrieves the <see cref="Skin"/> which this <see cref="IWorkingBeatmap"/> provides.
/// </summary> /// </summary>
ISkin Skin { get; } ISkin? Skin { get; }
/// <summary> /// <summary>
/// Retrieves the <see cref="Track"/> which this <see cref="IWorkingBeatmap"/> has loaded. /// Retrieves the <see cref="Track"/> which this <see cref="IWorkingBeatmap"/> has loaded.
/// </summary> /// </summary>
Track Track { get; } Track? Track { get; }
/// <summary> /// <summary>
/// Constructs a playable <see cref="IBeatmap"/> from <see cref="Beatmap"/> using the applicable converters for a specific <see cref="RulesetInfo"/>. /// Constructs a playable <see cref="IBeatmap"/> from <see cref="Beatmap"/> using the applicable converters for a specific <see cref="RulesetInfo"/>.
@ -114,7 +114,7 @@ namespace osu.Game.Beatmaps
/// Returns the stream of the file from the given storage path. /// Returns the stream of the file from the given storage path.
/// </summary> /// </summary>
/// <param name="storagePath">The storage path to the file.</param> /// <param name="storagePath">The storage path to the file.</param>
Stream GetStream(string storagePath); Stream? GetStream(string storagePath);
/// <summary> /// <summary>
/// Beings loading the contents of this <see cref="IWorkingBeatmap"/> asynchronously. /// Beings loading the contents of this <see cref="IWorkingBeatmap"/> asynchronously.

View File

@ -8,7 +8,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
@ -21,6 +20,8 @@ using osu.Game.Rulesets.UI;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Storyboards; using osu.Game.Storyboards;
#nullable enable
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
[ExcludeFromDynamicCompile] [ExcludeFromDynamicCompile]
@ -30,28 +31,28 @@ namespace osu.Game.Beatmaps
public readonly BeatmapSetInfo BeatmapSetInfo; public readonly BeatmapSetInfo BeatmapSetInfo;
// TODO: remove once the fallback lookup is not required (and access via `working.BeatmapInfo.Metadata` directly). // TODO: remove once the fallback lookup is not required (and access via `working.BeatmapInfo.Metadata` directly).
public BeatmapMetadata Metadata => BeatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); public BeatmapMetadata Metadata => BeatmapInfo.Metadata ?? BeatmapSetInfo.Metadata;
public Waveform Waveform => waveform.Value; public Waveform Waveform => waveform.Value;
public Storyboard Storyboard => storyboard.Value; public Storyboard Storyboard => storyboard.Value;
public Texture Background => GetBackground(); // Texture uses ref counting, so we want to return a new instance every usage. public Texture? Background => GetBackground(); // Texture uses ref counting, so we want to return a new instance every usage.
public ISkin Skin => skin.Value; public ISkin? Skin => skin.Value;
private AudioManager audioManager { get; } private AudioManager? audioManager { get; }
private CancellationTokenSource loadCancellationSource = new CancellationTokenSource(); private CancellationTokenSource? loadCancellationSource;
private readonly object beatmapFetchLock = new object(); private readonly object beatmapFetchLock = new object();
private readonly Lazy<Waveform> waveform; private readonly Lazy<Waveform> waveform;
private readonly Lazy<Storyboard> storyboard; private readonly Lazy<Storyboard> storyboard;
private readonly Lazy<ISkin> skin; private readonly Lazy<ISkin?> skin;
private Track track; // track is not Lazy as we allow transferring and loading multiple times. private Track? track; // track is not Lazy as we allow transferring and loading multiple times.
protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager) protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager? audioManager)
{ {
this.audioManager = audioManager; this.audioManager = audioManager;
@ -60,7 +61,7 @@ namespace osu.Game.Beatmaps
waveform = new Lazy<Waveform>(GetWaveform); waveform = new Lazy<Waveform>(GetWaveform);
storyboard = new Lazy<Storyboard>(GetStoryboard); storyboard = new Lazy<Storyboard>(GetStoryboard);
skin = new Lazy<ISkin>(GetSkin); skin = new Lazy<ISkin?>(GetSkin);
} }
#region Resource getters #region Resource getters
@ -68,9 +69,9 @@ namespace osu.Game.Beatmaps
protected virtual Waveform GetWaveform() => new Waveform(null); protected virtual Waveform GetWaveform() => new Waveform(null);
protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo }; protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo };
protected abstract IBeatmap GetBeatmap(); protected abstract IBeatmap? GetBeatmap();
protected abstract Texture GetBackground(); protected abstract Texture? GetBackground();
protected abstract Track GetBeatmapTrack(); protected abstract Track? GetBeatmapTrack();
/// <summary> /// <summary>
/// Creates a new skin instance for this beatmap. /// Creates a new skin instance for this beatmap.
@ -80,7 +81,7 @@ namespace osu.Game.Beatmaps
/// (e.g. for editing purposes, to avoid state pollution). /// (e.g. for editing purposes, to avoid state pollution).
/// For standard reading purposes, <see cref="Skin"/> should always be used directly. /// For standard reading purposes, <see cref="Skin"/> should always be used directly.
/// </remarks> /// </remarks>
protected internal abstract ISkin GetSkin(); protected internal abstract ISkin? GetSkin();
#endregion #endregion
@ -93,7 +94,6 @@ namespace osu.Game.Beatmaps
lock (beatmapFetchLock) lock (beatmapFetchLock)
{ {
loadCancellationSource?.Cancel(); loadCancellationSource?.Cancel();
loadCancellationSource = new CancellationTokenSource();
if (beatmapLoadTask?.IsCompleted != true) if (beatmapLoadTask?.IsCompleted != true)
beatmapLoadTask = null; beatmapLoadTask = null;
@ -130,7 +130,7 @@ namespace osu.Game.Beatmaps
/// across difficulties in the same beatmap set. /// across difficulties in the same beatmap set.
/// </summary> /// </summary>
/// <param name="track">The track to transfer.</param> /// <param name="track">The track to transfer.</param>
public void TransferTrack([NotNull] Track track) => this.track = track ?? throw new ArgumentNullException(nameof(track)); public void TransferTrack(Track track) => this.track = track ?? throw new ArgumentNullException(nameof(track));
/// <summary> /// <summary>
/// Get the loaded audio track instance. <see cref="LoadTrack"/> must have first been called. /// Get the loaded audio track instance. <see cref="LoadTrack"/> must have first been called.
@ -143,6 +143,9 @@ namespace osu.Game.Beatmaps
if (!TrackLoaded) if (!TrackLoaded)
throw new InvalidOperationException($"Cannot access {nameof(Track)} without first calling {nameof(LoadTrack)}."); throw new InvalidOperationException($"Cannot access {nameof(Track)} without first calling {nameof(LoadTrack)}.");
// Covered by TrackLoaded call above.
Debug.Assert(track != null);
return track; return track;
} }
} }
@ -170,7 +173,7 @@ namespace osu.Game.Beatmaps
break; break;
} }
return audioManager.Tracks.GetVirtual(length); return audioManager?.Tracks.GetVirtual(length) ?? throw new InvalidOperationException($"Attempted to get virtual track without providing an {nameof(AudioManager)}");
} }
#endregion #endregion
@ -179,7 +182,7 @@ namespace osu.Game.Beatmaps
public virtual bool BeatmapLoaded => beatmapLoadTask?.IsCompleted ?? false; public virtual bool BeatmapLoaded => beatmapLoadTask?.IsCompleted ?? false;
public IBeatmap Beatmap public IBeatmap? Beatmap
{ {
get get
{ {
@ -204,7 +207,7 @@ namespace osu.Game.Beatmaps
} }
} }
private Task<IBeatmap> beatmapLoadTask; private Task<IBeatmap>? beatmapLoadTask;
private Task<IBeatmap> loadBeatmapAsync() private Task<IBeatmap> loadBeatmapAsync()
{ {
@ -222,7 +225,7 @@ namespace osu.Game.Beatmaps
b.BeatmapInfo = BeatmapInfo; b.BeatmapInfo = BeatmapInfo;
return b; return b;
}, loadCancellationSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); }, (loadCancellationSource = new CancellationTokenSource()).Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
} }
} }
@ -230,7 +233,7 @@ namespace osu.Game.Beatmaps
#region Playable beatmap #region Playable beatmap
public IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList<Mod> mods = null) public IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList<Mod>? mods = null)
{ {
try try
{ {
@ -253,6 +256,9 @@ namespace osu.Game.Beatmaps
if (rulesetInstance == null) if (rulesetInstance == null)
throw new RulesetLoadException("Creating ruleset instance failed when attempting to create playable beatmap."); throw new RulesetLoadException("Creating ruleset instance failed when attempting to create playable beatmap.");
if (Beatmap == null)
throw new InvalidOperationException("Beatmap could not be loaded.");
IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance); IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance);
// Check if the beatmap can be converted // Check if the beatmap can be converted
@ -332,7 +338,7 @@ namespace osu.Game.Beatmaps
public override string ToString() => BeatmapInfo.ToString(); public override string ToString() => BeatmapInfo.ToString();
public abstract Stream GetStream(string storagePath); public abstract Stream? GetStream(string storagePath);
IBeatmapInfo IWorkingBeatmap.BeatmapInfo => BeatmapInfo; IBeatmapInfo IWorkingBeatmap.BeatmapInfo => BeatmapInfo;

View File

@ -180,17 +180,17 @@ namespace osu.Game.Beatmaps
protected override Waveform GetWaveform() protected override Waveform GetWaveform()
{ {
if (string.IsNullOrEmpty(Metadata?.AudioFile)) if (string.IsNullOrEmpty(Metadata?.AudioFile))
return null; return base.GetWaveform();
try try
{ {
var trackData = GetStream(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile)); var trackData = GetStream(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile));
return trackData == null ? null : new Waveform(trackData); return trackData == null ? base.GetWaveform() : new Waveform(trackData);
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error(e, "Waveform failed to load"); Logger.Error(e, "Waveform failed to load");
return null; return base.GetWaveform();
} }
} }

View File

@ -324,7 +324,7 @@ namespace osu.Game.Stores
protected override IBeatmap GetBeatmap() => beatmap; protected override IBeatmap GetBeatmap() => beatmap;
protected override Texture? GetBackground() => null; protected override Texture? GetBackground() => null;
protected override Track? GetBeatmapTrack() => null; protected override Track GetBeatmapTrack() => null!;
protected internal override ISkin? GetSkin() => null; protected internal override ISkin? GetSkin() => null;
public override Stream? GetStream(string storagePath) => null; public override Stream? GetStream(string storagePath) => null;
} }