Merge branch 'master' into playlist-unicode

This commit is contained in:
Dan Balasescu
2021-05-17 11:47:10 +09:00
committed by GitHub
407 changed files with 12345 additions and 3149 deletions

View File

@ -103,8 +103,8 @@ namespace osu.Game.Beatmaps
/// <param name="mods">The <see cref="Mod"/>s to get the difficulty with.</param>
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/> which stops computing the star difficulty.</param>
/// <returns>The <see cref="StarDifficulty"/>.</returns>
public Task<StarDifficulty> GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, [CanBeNull] IEnumerable<Mod> mods = null,
CancellationToken cancellationToken = default)
public virtual Task<StarDifficulty> GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null,
[CanBeNull] IEnumerable<Mod> mods = null, CancellationToken cancellationToken = default)
{
// In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset.
rulesetInfo ??= beatmapInfo.Ruleset;

View File

@ -526,6 +526,7 @@ namespace osu.Game.Beatmaps
protected override IBeatmap GetBeatmap() => beatmap;
protected override Texture GetBackground() => null;
protected override Track GetBeatmapTrack() => null;
public override Stream GetStream(string storagePath) => null;
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading;
@ -83,6 +82,12 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
if (beatmap.Metadata != null)
beatmap.Metadata.AuthorID = res.AuthorID;
if (beatmap.BeatmapSet.Metadata != null)
beatmap.BeatmapSet.Metadata.AuthorID = res.AuthorID;
LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
}
}
@ -157,7 +162,7 @@ namespace osu.Game.Beatmaps
using (var cmd = db.CreateCommand())
{
cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path";
cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved, user_id FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path";
cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash));
cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID ?? (object)DBNull.Value));
@ -174,6 +179,12 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0);
beatmap.OnlineBeatmapID = reader.GetInt32(1);
if (beatmap.Metadata != null)
beatmap.Metadata.AuthorID = reader.GetInt32(3);
if (beatmap.BeatmapSet.Metadata != null)
beatmap.BeatmapSet.Metadata.AuthorID = reader.GetInt32(3);
LogForModel(set, $"Cached local retrieval for {beatmap}.");
return true;
}
@ -194,17 +205,6 @@ namespace osu.Game.Beatmaps
cacheDownloadRequest?.Dispose();
updateScheduler?.Dispose();
}
[Serializable]
[SuppressMessage("ReSharper", "InconsistentNaming")]
private class CachedOnlineBeatmapLookup
{
public int approved { get; set; }
public int? beatmapset_id { get; set; }
public int? beatmap_id { get; set; }
}
}
}
}

View File

@ -3,7 +3,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.IO;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Framework.Logging;
@ -36,7 +36,7 @@ namespace osu.Game.Beatmaps
try
{
using (var stream = new LineBufferedReader(resources.Files.GetStream(getPathForFile(BeatmapInfo.Path))))
using (var stream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path))))
return Decoder.GetDecoder<Beatmap>(stream).Decode(stream);
}
catch (Exception e)
@ -46,8 +46,6 @@ namespace osu.Game.Beatmaps
}
}
private string getPathForFile(string filename) => BeatmapSetInfo.Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath;
protected override bool BackgroundStillValid(Texture b) => false; // bypass lazy logic. we want to return a new background each time for refcounting purposes.
protected override Texture GetBackground()
@ -57,7 +55,7 @@ namespace osu.Game.Beatmaps
try
{
return resources.LargeTextureStore.Get(getPathForFile(Metadata.BackgroundFile));
return resources.LargeTextureStore.Get(BeatmapSetInfo.GetPathForFile(Metadata.BackgroundFile));
}
catch (Exception e)
{
@ -73,7 +71,7 @@ namespace osu.Game.Beatmaps
try
{
return resources.Tracks.Get(getPathForFile(Metadata.AudioFile));
return resources.Tracks.Get(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile));
}
catch (Exception e)
{
@ -89,7 +87,7 @@ namespace osu.Game.Beatmaps
try
{
var trackData = resources.Files.GetStream(getPathForFile(Metadata.AudioFile));
var trackData = GetStream(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile));
return trackData == null ? null : new Waveform(trackData);
}
catch (Exception e)
@ -105,7 +103,7 @@ namespace osu.Game.Beatmaps
try
{
using (var stream = new LineBufferedReader(resources.Files.GetStream(getPathForFile(BeatmapInfo.Path))))
using (var stream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path))))
{
var decoder = Decoder.GetDecoder<Storyboard>(stream);
@ -114,7 +112,7 @@ namespace osu.Game.Beatmaps
storyboard = decoder.Decode(stream);
else
{
using (var secondaryStream = new LineBufferedReader(resources.Files.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile))))
using (var secondaryStream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapSetInfo.StoryboardFile))))
storyboard = decoder.Decode(stream, secondaryStream);
}
}
@ -142,6 +140,8 @@ namespace osu.Game.Beatmaps
return null;
}
}
public override Stream GetStream(string storagePath) => resources.Files.GetStream(storagePath);
}
}
}

View File

@ -35,6 +35,21 @@ namespace osu.Game.Beatmaps
[JsonIgnore]
public List<BeatmapSetInfo> BeatmapSets { get; set; }
/// <summary>
/// Helper property to deserialize a username to <see cref="User"/>.
/// </summary>
[JsonProperty(@"user_id")]
[Column("AuthorID")]
public int AuthorID
{
get => Author?.Id ?? 1;
set
{
Author ??= new User();
Author.Id = value;
}
}
/// <summary>
/// Helper property to deserialize a username to <see cref="User"/>.
/// </summary>
@ -43,7 +58,11 @@ namespace osu.Game.Beatmaps
public string AuthorString
{
get => Author?.Username;
set => Author = new User { Username = value };
set
{
Author ??= new User();
Author.Username = value;
}
}
/// <summary>

View File

@ -59,6 +59,13 @@ namespace osu.Game.Beatmaps
public string StoryboardFile => Files?.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename;
/// <summary>
/// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null.
/// The path returned is relative to the user file storage.
/// </summary>
/// <param name="filename">The name of the file to get the storage path of.</param>
public string GetPathForFile(string filename) => Files?.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath;
public List<BeatmapSetFileInfo> Files { get; set; }
public override string ToString() => Metadata?.ToString() ?? base.ToString();

View File

@ -14,4 +14,10 @@ namespace osu.Game.Beatmaps
Qualified = 3,
Loved = 4,
}
public static class BeatmapSetOnlineStatusExtensions
{
public static bool GrantsPerformancePoints(this BeatmapSetOnlineStatus status)
=> status == BeatmapSetOnlineStatus.Ranked || status == BeatmapSetOnlineStatus.Approved;
}
}

View File

@ -7,6 +7,8 @@ using System.Linq;
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Framework.Lists;
using osu.Framework.Utils;
using osu.Game.Screens.Edit;
namespace osu.Game.Beatmaps.ControlPoints
{
@ -160,6 +162,58 @@ namespace osu.Game.Beatmaps.ControlPoints
groups.Remove(group);
}
/// <summary>
/// Returns the time on the given beat divisor closest to the given time.
/// </summary>
/// <param name="time">The time to find the closest snapped time to.</param>
/// <param name="beatDivisor">The beat divisor to snap to.</param>
/// <param name="referenceTime">An optional reference point to use for timing point lookup.</param>
public double GetClosestSnappedTime(double time, int beatDivisor, double? referenceTime = null)
{
var timingPoint = TimingPointAt(referenceTime ?? time);
return getClosestSnappedTime(timingPoint, time, beatDivisor);
}
/// <summary>
/// Returns the time on *ANY* valid beat divisor, favouring the divisor closest to the given time.
/// </summary>
/// <param name="time">The time to find the closest snapped time to.</param>
public double GetClosestSnappedTime(double time) => GetClosestSnappedTime(time, GetClosestBeatDivisor(time));
/// <summary>
/// Returns the beat snap divisor closest to the given time. If two are equally close, the smallest divisor is returned.
/// </summary>
/// <param name="time">The time to find the closest beat snap divisor to.</param>
/// <param name="referenceTime">An optional reference point to use for timing point lookup.</param>
public int GetClosestBeatDivisor(double time, double? referenceTime = null)
{
TimingControlPoint timingPoint = TimingPointAt(referenceTime ?? time);
int closestDivisor = 0;
double closestTime = double.MaxValue;
foreach (int divisor in BindableBeatDivisor.VALID_DIVISORS)
{
double distanceFromSnap = Math.Abs(time - getClosestSnappedTime(timingPoint, time, divisor));
if (Precision.DefinitelyBigger(closestTime, distanceFromSnap))
{
closestDivisor = divisor;
closestTime = distanceFromSnap;
}
}
return closestDivisor;
}
private static double getClosestSnappedTime(TimingControlPoint timingPoint, double time, int beatDivisor)
{
var beatLength = timingPoint.BeatLength / beatDivisor;
var beatLengths = (int)Math.Round((time - timingPoint.Time) / beatLength, MidpointRounding.AwayFromZero);
return timingPoint.Time + beatLengths * beatLength;
}
/// <summary>
/// Binary searches one of the control point lists to find the active control point at <paramref name="time"/>.
/// Includes logic for returning a specific point when no matching point is found.

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using JetBrains.Annotations;
using osu.Framework.Audio;
@ -48,6 +49,8 @@ namespace osu.Game.Beatmaps
protected override Track GetBeatmapTrack() => GetVirtualTrack();
public override Stream GetStream(string storagePath) => null;
private class DummyRulesetInfo : RulesetInfo
{
public override Ruleset CreateInstance() => new DummyRuleset();

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets;
@ -41,6 +42,11 @@ namespace osu.Game.Beatmaps
/// </summary>
ISkin Skin { get; }
/// <summary>
/// Retrieves the <see cref="Track"/> which this <see cref="WorkingBeatmap"/> has loaded.
/// </summary>
Track Track { get; }
/// <summary>
/// Constructs a playable <see cref="IBeatmap"/> from <see cref="Beatmap"/> using the applicable converters for a specific <see cref="RulesetInfo"/>.
/// <para>
@ -67,5 +73,11 @@ namespace osu.Game.Beatmaps
/// </remarks>
/// <returns>A fresh track instance, which will also be available via <see cref="Track"/>.</returns>
Track LoadTrack();
/// <summary>
/// Returns the stream of the file from the given storage path.
/// </summary>
/// <param name="storagePath">The storage path to the file.</param>
Stream GetStream(string storagePath);
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -323,9 +324,11 @@ namespace osu.Game.Beatmaps
public bool SkinLoaded => skin.IsResultAvailable;
public ISkin Skin => skin.Value;
protected virtual ISkin GetSkin() => new DefaultSkin();
protected virtual ISkin GetSkin() => new DefaultSkin(null);
private readonly RecyclableLazy<ISkin> skin;
public abstract Stream GetStream(string storagePath);
public class RecyclableLazy<T>
{
private Lazy<T> lazy;