mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 07:33:55 +09:00
Merge branch 'master' into playlist-unicode
This commit is contained in:
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user