From 9015ac6ba8d9fd25ceb23321c9a0e9d3dc551221 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 19:52:41 +0900 Subject: [PATCH 01/48] Implement new version of download tracker --- osu.Game/Database/ModelDownloader.cs | 4 +- osu.Game/Online/BeatmapDownloadTracker.cs | 172 ++++++++++++++++++++++ osu.Game/Online/DownloadTracker.cs | 39 +++++ osu.Game/Online/ScoreDownloadTracker.cs | 172 ++++++++++++++++++++++ osu.Game/Scoring/ScoreInfo.cs | 2 +- 5 files changed, 386 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Online/BeatmapDownloadTracker.cs create mode 100644 osu.Game/Online/DownloadTracker.cs create mode 100644 osu.Game/Online/ScoreDownloadTracker.cs diff --git a/osu.Game/Database/ModelDownloader.cs b/osu.Game/Database/ModelDownloader.cs index e613b39b6b..b9677c1b2b 100644 --- a/osu.Game/Database/ModelDownloader.cs +++ b/osu.Game/Database/ModelDownloader.cs @@ -15,7 +15,7 @@ using osu.Game.Overlays.Notifications; namespace osu.Game.Database { public abstract class ModelDownloader : IModelDownloader - where TModel : class, IHasPrimaryKey, ISoftDelete, IEquatable + where TModel : class, IHasPrimaryKey, ISoftDelete, IEquatable, IHasOnlineID { public Action PostNotification { protected get; set; } @@ -107,7 +107,7 @@ namespace osu.Game.Database } } - public ArchiveDownloadRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Model.Equals(model)); + public ArchiveDownloadRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Model.OnlineID == model.OnlineID); private bool canDownload(TModel model) => GetExistingDownload(model) == null && api != null; diff --git a/osu.Game/Online/BeatmapDownloadTracker.cs b/osu.Game/Online/BeatmapDownloadTracker.cs new file mode 100644 index 0000000000..969cb9af36 --- /dev/null +++ b/osu.Game/Online/BeatmapDownloadTracker.cs @@ -0,0 +1,172 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Online.API; + +#nullable enable + +namespace osu.Game.Online +{ + public class BeatmapDownloadTracker : DownloadTracker + { + [Resolved(CanBeNull = true)] + protected BeatmapManager? Manager { get; private set; } + + private ArchiveDownloadRequest? attachedRequest; + + public BeatmapDownloadTracker(IBeatmapSetInfo trackedItem) + : base(trackedItem) + { + } + + private IBindable>? managerUpdated; + private IBindable>? managerRemoved; + private IBindable>>? managerDownloadBegan; + private IBindable>>? managerDownloadFailed; + + [BackgroundDependencyLoader(true)] + private void load() + { + // Used to interact with manager classes that don't support interface types. Will eventually be replaced. + var beatmapSetInfo = new BeatmapSetInfo { OnlineBeatmapSetID = TrackedItem.OnlineID }; + + if ((TrackedItem as BeatmapSetInfo)?.ID > 0 || Manager?.IsAvailableLocally(beatmapSetInfo) == true) + UpdateState(DownloadState.LocallyAvailable); + else if (Manager != null) + attachDownload(Manager.GetExistingDownload(beatmapSetInfo)); + + if (Manager != null) + { + managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); + managerDownloadBegan.BindValueChanged(downloadBegan); + managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); + managerDownloadFailed.BindValueChanged(downloadFailed); + managerUpdated = Manager.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(itemUpdated); + managerRemoved = Manager.ItemRemoved.GetBoundCopy(); + managerRemoved.BindValueChanged(itemRemoved); + } + } + + /// + /// Checks that a database model matches the one expected to be downloaded. + /// + /// + /// For online play, this could be used to check that the databased model matches the online beatmap. + /// + /// The model in database. + protected virtual bool VerifyDatabasedModel(BeatmapSetInfo databasedModel) => true; // TODO: do we still need this? + + private void downloadBegan(ValueChangedEvent>> weakRequest) + { + if (weakRequest.NewValue.TryGetTarget(out var request)) + { + Schedule(() => + { + if (checkEquality(request.Model, TrackedItem)) + attachDownload(request); + }); + } + } + + private void downloadFailed(ValueChangedEvent>> weakRequest) + { + if (weakRequest.NewValue.TryGetTarget(out var request)) + { + Schedule(() => + { + if (checkEquality(request.Model, TrackedItem)) + attachDownload(null); + }); + } + } + + private void attachDownload(ArchiveDownloadRequest? request) + { + if (attachedRequest != null) + { + attachedRequest.Failure -= onRequestFailure; + attachedRequest.DownloadProgressed -= onRequestProgress; + attachedRequest.Success -= onRequestSuccess; + } + + attachedRequest = request; + + if (attachedRequest != null) + { + if (attachedRequest.Progress == 1) + { + UpdateProgress(1); + UpdateState(DownloadState.Importing); + } + else + { + UpdateProgress(attachedRequest.Progress); + UpdateState(DownloadState.Downloading); + + attachedRequest.Failure += onRequestFailure; + attachedRequest.DownloadProgressed += onRequestProgress; + attachedRequest.Success += onRequestSuccess; + } + } + else + { + UpdateState(DownloadState.NotDownloaded); + } + } + + private void onRequestSuccess(string _) => Schedule(() => UpdateState(DownloadState.Importing)); + + private void onRequestProgress(float progress) => Schedule(() => UpdateProgress(progress)); + + private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null)); + + private void itemUpdated(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var item)) + { + Schedule(() => + { + if (!checkEquality(item, TrackedItem)) + return; + + if (!VerifyDatabasedModel(item)) + { + UpdateState(DownloadState.NotDownloaded); + return; + } + + UpdateState(DownloadState.LocallyAvailable); + }); + } + } + + private void itemRemoved(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var item)) + { + Schedule(() => + { + if (checkEquality(item, TrackedItem)) + UpdateState(DownloadState.NotDownloaded); + }); + } + } + + private bool checkEquality(IBeatmapSetInfo x, IBeatmapSetInfo y) => x.OnlineID == y.OnlineID; + + #region Disposal + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + attachDownload(null); + } + + #endregion + } +} diff --git a/osu.Game/Online/DownloadTracker.cs b/osu.Game/Online/DownloadTracker.cs new file mode 100644 index 0000000000..357c64b6a3 --- /dev/null +++ b/osu.Game/Online/DownloadTracker.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Framework.Graphics; + +#nullable enable + +namespace osu.Game.Online +{ + public abstract class DownloadTracker : Component + where T : class + { + public readonly T TrackedItem; + + /// + /// Holds the current download state of the download - whether is has already been downloaded, is in progress, or is not downloaded. + /// + public IBindable State => state; + + private readonly Bindable state = new Bindable(); + + /// + /// The progress of an active download. + /// + public IBindableNumber Progress => progress; + + private readonly BindableNumber progress = new BindableNumber { MinValue = 0, MaxValue = 1 }; + + protected DownloadTracker(T trackedItem) + { + TrackedItem = trackedItem; + } + + protected void UpdateState(DownloadState newState) => state.Value = newState; + + protected void UpdateProgress(double newProgress) => progress.Value = newProgress; + } +} diff --git a/osu.Game/Online/ScoreDownloadTracker.cs b/osu.Game/Online/ScoreDownloadTracker.cs new file mode 100644 index 0000000000..47cbabbee8 --- /dev/null +++ b/osu.Game/Online/ScoreDownloadTracker.cs @@ -0,0 +1,172 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Online.API; +using osu.Game.Scoring; + +#nullable enable + +namespace osu.Game.Online +{ + public class ScoreDownloadTracker : DownloadTracker + { + [Resolved(CanBeNull = true)] + protected ScoreManager? Manager { get; private set; } + + private ArchiveDownloadRequest? attachedRequest; + + public ScoreDownloadTracker(ScoreInfo trackedItem) + : base(trackedItem) + { + } + + private IBindable>? managerUpdated; + private IBindable>? managerRemoved; + private IBindable>>? managerDownloadBegan; + private IBindable>>? managerDownloadFailed; + + [BackgroundDependencyLoader(true)] + private void load() + { + // Used to interact with manager classes that don't support interface types. Will eventually be replaced. + var beatmapSetInfo = new ScoreInfo { OnlineScoreID = TrackedItem.OnlineScoreID }; + + if (TrackedItem.ID > 0 || Manager?.IsAvailableLocally(beatmapSetInfo) == true) + UpdateState(DownloadState.LocallyAvailable); + else if (Manager != null) + attachDownload(Manager.GetExistingDownload(beatmapSetInfo)); + + if (Manager != null) + { + managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); + managerDownloadBegan.BindValueChanged(downloadBegan); + managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); + managerDownloadFailed.BindValueChanged(downloadFailed); + managerUpdated = Manager.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(itemUpdated); + managerRemoved = Manager.ItemRemoved.GetBoundCopy(); + managerRemoved.BindValueChanged(itemRemoved); + } + } + + /// + /// Checks that a database model matches the one expected to be downloaded. + /// + /// + /// For online play, this could be used to check that the databased model matches the online beatmap. + /// + /// The model in database. + protected virtual bool VerifyDatabasedModel(ScoreInfo databasedModel) => true; // TODO: do we still need this? + + private void downloadBegan(ValueChangedEvent>> weakRequest) + { + if (weakRequest.NewValue.TryGetTarget(out var request)) + { + Schedule(() => + { + if (checkEquality(request.Model, TrackedItem)) + attachDownload(request); + }); + } + } + + private void downloadFailed(ValueChangedEvent>> weakRequest) + { + if (weakRequest.NewValue.TryGetTarget(out var request)) + { + Schedule(() => + { + if (checkEquality(request.Model, TrackedItem)) + attachDownload(null); + }); + } + } + + private void attachDownload(ArchiveDownloadRequest? request) + { + if (attachedRequest != null) + { + attachedRequest.Failure -= onRequestFailure; + attachedRequest.DownloadProgressed -= onRequestProgress; + attachedRequest.Success -= onRequestSuccess; + } + + attachedRequest = request; + + if (attachedRequest != null) + { + if (attachedRequest.Progress == 1) + { + UpdateProgress(1); + UpdateState(DownloadState.Importing); + } + else + { + UpdateProgress(attachedRequest.Progress); + UpdateState(DownloadState.Downloading); + + attachedRequest.Failure += onRequestFailure; + attachedRequest.DownloadProgressed += onRequestProgress; + attachedRequest.Success += onRequestSuccess; + } + } + else + { + UpdateState(DownloadState.NotDownloaded); + } + } + + private void onRequestSuccess(string _) => Schedule(() => UpdateState(DownloadState.Importing)); + + private void onRequestProgress(float progress) => Schedule(() => UpdateProgress(progress)); + + private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null)); + + private void itemUpdated(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var item)) + { + Schedule(() => + { + if (!checkEquality(item, TrackedItem)) + return; + + if (!VerifyDatabasedModel(item)) + { + UpdateState(DownloadState.NotDownloaded); + return; + } + + UpdateState(DownloadState.LocallyAvailable); + }); + } + } + + private void itemRemoved(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var item)) + { + Schedule(() => + { + if (checkEquality(item, TrackedItem)) + UpdateState(DownloadState.NotDownloaded); + }); + } + } + + private bool checkEquality(ScoreInfo x, ScoreInfo y) => x.OnlineScoreID == y.OnlineScoreID; + + #region Disposal + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + attachDownload(null); + } + + #endregion + } +} diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 5cf22f7945..1bc04cd21e 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -19,7 +19,7 @@ using osu.Game.Utils; namespace osu.Game.Scoring { - public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable + public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable, IHasOnlineID { public int ID { get; set; } From 617e6febb6bb81c61fbf126089a836edf7860817 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 20:02:51 +0900 Subject: [PATCH 02/48] Refactor `ModelDownloader` to allow for different `OnlineID matching` --- osu.Game/Beatmaps/BeatmapModelDownloader.cs | 3 +++ osu.Game/Database/ModelDownloader.cs | 12 ++++++------ osu.Game/Scoring/ScoreInfo.cs | 2 +- osu.Game/Scoring/ScoreModelDownloader.cs | 3 +++ 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapModelDownloader.cs b/osu.Game/Beatmaps/BeatmapModelDownloader.cs index 30dc95a966..001726e741 100644 --- a/osu.Game/Beatmaps/BeatmapModelDownloader.cs +++ b/osu.Game/Beatmaps/BeatmapModelDownloader.cs @@ -13,6 +13,9 @@ namespace osu.Game.Beatmaps protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => new DownloadBeatmapSetRequest(set, minimiseDownloadSize); + public override ArchiveDownloadRequest GetExistingDownload(BeatmapSetInfo model) + => CurrentDownloads.Find(r => r.Model.OnlineID == model.OnlineID); + public BeatmapModelDownloader(IBeatmapModelManager beatmapModelManager, IAPIProvider api, GameHost host = null) : base(beatmapModelManager, api, host) { diff --git a/osu.Game/Database/ModelDownloader.cs b/osu.Game/Database/ModelDownloader.cs index b9677c1b2b..12bf5e9ce7 100644 --- a/osu.Game/Database/ModelDownloader.cs +++ b/osu.Game/Database/ModelDownloader.cs @@ -15,7 +15,7 @@ using osu.Game.Overlays.Notifications; namespace osu.Game.Database { public abstract class ModelDownloader : IModelDownloader - where TModel : class, IHasPrimaryKey, ISoftDelete, IEquatable, IHasOnlineID + where TModel : class, IHasPrimaryKey, ISoftDelete, IEquatable { public Action PostNotification { protected get; set; } @@ -30,7 +30,7 @@ namespace osu.Game.Database private readonly IModelManager modelManager; private readonly IAPIProvider api; - private readonly List> currentDownloads = new List>(); + protected readonly List> CurrentDownloads = new List>(); protected ModelDownloader(IModelManager modelManager, IAPIProvider api, IIpcHost importHost = null) { @@ -74,7 +74,7 @@ namespace osu.Game.Database if (!imported.Any()) downloadFailed.Value = new WeakReference>(request); - currentDownloads.Remove(request); + CurrentDownloads.Remove(request); }, TaskCreationOptions.LongRunning); }; @@ -86,7 +86,7 @@ namespace osu.Game.Database return true; }; - currentDownloads.Add(request); + CurrentDownloads.Add(request); PostNotification?.Invoke(notification); api.PerformAsync(request); @@ -96,7 +96,7 @@ namespace osu.Game.Database void triggerFailure(Exception error) { - currentDownloads.Remove(request); + CurrentDownloads.Remove(request); downloadFailed.Value = new WeakReference>(request); @@ -107,7 +107,7 @@ namespace osu.Game.Database } } - public ArchiveDownloadRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Model.OnlineID == model.OnlineID); + public abstract ArchiveDownloadRequest GetExistingDownload(TModel model); private bool canDownload(TModel model) => GetExistingDownload(model) == null && api != null; diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 1bc04cd21e..5cf22f7945 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -19,7 +19,7 @@ using osu.Game.Utils; namespace osu.Game.Scoring { - public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable, IHasOnlineID + public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable { public int ID { get; set; } diff --git a/osu.Game/Scoring/ScoreModelDownloader.cs b/osu.Game/Scoring/ScoreModelDownloader.cs index b3c1e2928a..52355585a9 100644 --- a/osu.Game/Scoring/ScoreModelDownloader.cs +++ b/osu.Game/Scoring/ScoreModelDownloader.cs @@ -16,5 +16,8 @@ namespace osu.Game.Scoring } protected override ArchiveDownloadRequest CreateDownloadRequest(ScoreInfo score, bool minimiseDownload) => new DownloadReplayRequest(score); + + public override ArchiveDownloadRequest GetExistingDownload(ScoreInfo model) + => CurrentDownloads.Find(r => r.Model.OnlineScoreID == model.OnlineScoreID); } } From 692db9a9ec2f424153e0374923247edeee0eb98e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 20:21:00 +0900 Subject: [PATCH 03/48] Remove unnecessary verification flow from score download tracking --- osu.Game/Online/BeatmapDownloadTracker.cs | 15 --------------- osu.Game/Online/ScoreDownloadTracker.cs | 17 +---------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/osu.Game/Online/BeatmapDownloadTracker.cs b/osu.Game/Online/BeatmapDownloadTracker.cs index 969cb9af36..6df74efe9c 100644 --- a/osu.Game/Online/BeatmapDownloadTracker.cs +++ b/osu.Game/Online/BeatmapDownloadTracker.cs @@ -52,15 +52,6 @@ namespace osu.Game.Online } } - /// - /// Checks that a database model matches the one expected to be downloaded. - /// - /// - /// For online play, this could be used to check that the databased model matches the online beatmap. - /// - /// The model in database. - protected virtual bool VerifyDatabasedModel(BeatmapSetInfo databasedModel) => true; // TODO: do we still need this? - private void downloadBegan(ValueChangedEvent>> weakRequest) { if (weakRequest.NewValue.TryGetTarget(out var request)) @@ -134,12 +125,6 @@ namespace osu.Game.Online if (!checkEquality(item, TrackedItem)) return; - if (!VerifyDatabasedModel(item)) - { - UpdateState(DownloadState.NotDownloaded); - return; - } - UpdateState(DownloadState.LocallyAvailable); }); } diff --git a/osu.Game/Online/ScoreDownloadTracker.cs b/osu.Game/Online/ScoreDownloadTracker.cs index 47cbabbee8..7a714a7a0e 100644 --- a/osu.Game/Online/ScoreDownloadTracker.cs +++ b/osu.Game/Online/ScoreDownloadTracker.cs @@ -52,15 +52,6 @@ namespace osu.Game.Online } } - /// - /// Checks that a database model matches the one expected to be downloaded. - /// - /// - /// For online play, this could be used to check that the databased model matches the online beatmap. - /// - /// The model in database. - protected virtual bool VerifyDatabasedModel(ScoreInfo databasedModel) => true; // TODO: do we still need this? - private void downloadBegan(ValueChangedEvent>> weakRequest) { if (weakRequest.NewValue.TryGetTarget(out var request)) @@ -134,13 +125,7 @@ namespace osu.Game.Online if (!checkEquality(item, TrackedItem)) return; - if (!VerifyDatabasedModel(item)) - { - UpdateState(DownloadState.NotDownloaded); - return; - } - - UpdateState(DownloadState.LocallyAvailable); + UpdateState(DownloadState.NotDownloaded); }); } } From 6339064dbd152ac4ebf121e8911037b32c9756b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 20:07:00 +0900 Subject: [PATCH 04/48] Remove old versions of `DownloadTrackingComposite` --- osu.Game/Online/DownloadTrackingComposite.cs | 196 ------------------ .../BeatmapDownloadTrackingComposite.cs | 19 -- 2 files changed, 215 deletions(-) delete mode 100644 osu.Game/Online/DownloadTrackingComposite.cs delete mode 100644 osu.Game/Overlays/BeatmapDownloadTrackingComposite.cs diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs deleted file mode 100644 index 2a96051427..0000000000 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using JetBrains.Annotations; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics.Containers; -using osu.Game.Database; -using osu.Game.Online.API; - -namespace osu.Game.Online -{ - /// - /// A component which tracks a through potential download/import/deletion. - /// - public abstract class DownloadTrackingComposite : CompositeDrawable - where TModel : class, IEquatable - where TModelManager : class, IModelDownloader, IModelManager - { - protected readonly Bindable Model = new Bindable(); - - [Resolved(CanBeNull = true)] - protected TModelManager Manager { get; private set; } - - /// - /// Holds the current download state of the , whether is has already been downloaded, is in progress, or is not downloaded. - /// - protected readonly Bindable State = new Bindable(); - - protected readonly BindableNumber Progress = new BindableNumber { MinValue = 0, MaxValue = 1 }; - - protected DownloadTrackingComposite(TModel model = null) - { - Model.Value = model; - } - - private IBindable> managerUpdated; - private IBindable> managerRemoved; - private IBindable>> managerDownloadBegan; - private IBindable>> managerDownloadFailed; - - [BackgroundDependencyLoader(true)] - private void load() - { - Model.BindValueChanged(modelInfo => - { - if (modelInfo.NewValue == null) - attachDownload(null); - else if (IsModelAvailableLocally()) - State.Value = DownloadState.LocallyAvailable; - else - attachDownload(Manager?.GetExistingDownload(modelInfo.NewValue)); - }, true); - - if (Manager == null) - return; - - managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); - managerDownloadBegan.BindValueChanged(downloadBegan); - managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); - managerDownloadFailed.BindValueChanged(downloadFailed); - managerUpdated = Manager.ItemUpdated.GetBoundCopy(); - managerUpdated.BindValueChanged(itemUpdated); - managerRemoved = Manager.ItemRemoved.GetBoundCopy(); - managerRemoved.BindValueChanged(itemRemoved); - } - - /// - /// Checks that a database model matches the one expected to be downloaded. - /// - /// - /// For online play, this could be used to check that the databased model matches the online beatmap. - /// - /// The model in database. - protected virtual bool VerifyDatabasedModel([NotNull] TModel databasedModel) => true; - - /// - /// Whether the given model is available in the database. - /// By default, this calls , - /// but can be overriden to add additional checks for verifying the model in database. - /// - protected virtual bool IsModelAvailableLocally() => Manager?.IsAvailableLocally(Model.Value) == true; - - private void downloadBegan(ValueChangedEvent>> weakRequest) - { - if (weakRequest.NewValue.TryGetTarget(out var request)) - { - Schedule(() => - { - if (request.Model.Equals(Model.Value)) - attachDownload(request); - }); - } - } - - private void downloadFailed(ValueChangedEvent>> weakRequest) - { - if (weakRequest.NewValue.TryGetTarget(out var request)) - { - Schedule(() => - { - if (request.Model.Equals(Model.Value)) - attachDownload(null); - }); - } - } - - private ArchiveDownloadRequest attachedRequest; - - private void attachDownload(ArchiveDownloadRequest request) - { - if (attachedRequest != null) - { - attachedRequest.Failure -= onRequestFailure; - attachedRequest.DownloadProgressed -= onRequestProgress; - attachedRequest.Success -= onRequestSuccess; - } - - attachedRequest = request; - - if (attachedRequest != null) - { - if (attachedRequest.Progress == 1) - { - Progress.Value = 1; - State.Value = DownloadState.Importing; - } - else - { - Progress.Value = attachedRequest.Progress; - State.Value = DownloadState.Downloading; - - attachedRequest.Failure += onRequestFailure; - attachedRequest.DownloadProgressed += onRequestProgress; - attachedRequest.Success += onRequestSuccess; - } - } - else - { - State.Value = DownloadState.NotDownloaded; - } - } - - private void onRequestSuccess(string _) => Schedule(() => State.Value = DownloadState.Importing); - - private void onRequestProgress(float progress) => Schedule(() => Progress.Value = progress); - - private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null)); - - private void itemUpdated(ValueChangedEvent> weakItem) - { - if (weakItem.NewValue.TryGetTarget(out var item)) - { - Schedule(() => - { - if (!item.Equals(Model.Value)) - return; - - if (!VerifyDatabasedModel(item)) - { - State.Value = DownloadState.NotDownloaded; - return; - } - - State.Value = DownloadState.LocallyAvailable; - }); - } - } - - private void itemRemoved(ValueChangedEvent> weakItem) - { - if (weakItem.NewValue.TryGetTarget(out var item)) - { - Schedule(() => - { - if (item.Equals(Model.Value)) - State.Value = DownloadState.NotDownloaded; - }); - } - } - - #region Disposal - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - State.UnbindAll(); - - attachDownload(null); - } - - #endregion - } -} diff --git a/osu.Game/Overlays/BeatmapDownloadTrackingComposite.cs b/osu.Game/Overlays/BeatmapDownloadTrackingComposite.cs deleted file mode 100644 index f6b5b181c3..0000000000 --- a/osu.Game/Overlays/BeatmapDownloadTrackingComposite.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Bindables; -using osu.Game.Beatmaps; -using osu.Game.Online; - -namespace osu.Game.Overlays -{ - public abstract class BeatmapDownloadTrackingComposite : DownloadTrackingComposite - { - public Bindable BeatmapSet => Model; - - protected BeatmapDownloadTrackingComposite(BeatmapSetInfo set = null) - : base(set) - { - } - } -} From dd06c617b149f2459b209640808b6a88d9bee440 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 20:19:34 +0900 Subject: [PATCH 05/48] Update `OnlinePlayBeatmapAvailabilityTracker` in line with new download tracker The structure here has changed a bit - rather than deriving from the donwload tracker I moved the additional logic into the callbacks. I think this feels better? --- .../OnlinePlayBeatmapAvailabilityTracker.cs | 93 ++++++++++--------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 52aa115083..6dd1204555 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -2,8 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Framework.Threading; using osu.Game.Beatmaps; @@ -16,10 +17,13 @@ namespace osu.Game.Online.Rooms /// This differs from a regular download tracking composite as this accounts for the /// databased beatmap set's checksum, to disallow from playing with an altered version of the beatmap. /// - public class OnlinePlayBeatmapAvailabilityTracker : DownloadTrackingComposite + public class OnlinePlayBeatmapAvailabilityTracker : CompositeDrawable { public readonly IBindable SelectedItem = new Bindable(); + [Resolved] + private BeatmapManager beatmapManager { get; set; } + /// /// The availability state of the currently selected playlist item. /// @@ -29,6 +33,8 @@ namespace osu.Game.Online.Rooms private ScheduledDelegate progressUpdate; + private BeatmapDownloadTracker downloadTracker; + protected override void LoadComplete() { base.LoadComplete(); @@ -40,58 +46,39 @@ namespace osu.Game.Online.Rooms if (item.NewValue == null) return; - Model.Value = item.NewValue.Beatmap.Value.BeatmapSet; + downloadTracker?.Expire(); + downloadTracker = new BeatmapDownloadTracker(item.NewValue.Beatmap.Value.BeatmapSet); + + downloadTracker.Progress.BindValueChanged(_ => + { + if (downloadTracker.State.Value != DownloadState.Downloading) + return; + + // incoming progress changes are going to be at a very high rate. + // we don't want to flood the network with this, so rate limit how often we send progress updates. + if (progressUpdate?.Completed != false) + progressUpdate = Scheduler.AddDelayed(updateAvailability, progressUpdate == null ? 0 : 500); + }); + + downloadTracker.State.BindValueChanged(_ => updateAvailability(), true); + + AddInternal(downloadTracker); }, true); - - Progress.BindValueChanged(_ => - { - if (State.Value != DownloadState.Downloading) - return; - - // incoming progress changes are going to be at a very high rate. - // we don't want to flood the network with this, so rate limit how often we send progress updates. - if (progressUpdate?.Completed != false) - progressUpdate = Scheduler.AddDelayed(updateAvailability, progressUpdate == null ? 0 : 500); - }); - - State.BindValueChanged(_ => updateAvailability(), true); - } - - protected override bool VerifyDatabasedModel(BeatmapSetInfo databasedSet) - { - int beatmapId = SelectedItem.Value?.Beatmap.Value.OnlineID ?? -1; - string checksum = SelectedItem.Value?.Beatmap.Value.MD5Hash; - - var matchingBeatmap = databasedSet.Beatmaps.FirstOrDefault(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum); - - if (matchingBeatmap == null) - { - Logger.Log("The imported beatmap set does not match the online version.", LoggingTarget.Runtime, LogLevel.Important); - return false; - } - - return true; - } - - protected override bool IsModelAvailableLocally() - { - int onlineId = SelectedItem.Value.Beatmap.Value.OnlineID; - string checksum = SelectedItem.Value.Beatmap.Value.MD5Hash; - - var beatmap = Manager.QueryBeatmap(b => b.OnlineBeatmapID == onlineId && b.MD5Hash == checksum); - return beatmap?.BeatmapSet.DeletePending == false; } private void updateAvailability() { - switch (State.Value) + if (downloadTracker == null) + return; + + switch (downloadTracker.State.Value) { case DownloadState.NotDownloaded: availability.Value = BeatmapAvailability.NotDownloaded(); break; case DownloadState.Downloading: - availability.Value = BeatmapAvailability.Downloading((float)Progress.Value); + availability.Value = BeatmapAvailability.Downloading((float)downloadTracker.Progress.Value); break; case DownloadState.Importing: @@ -99,12 +86,28 @@ namespace osu.Game.Online.Rooms break; case DownloadState.LocallyAvailable: - availability.Value = BeatmapAvailability.LocallyAvailable(); + bool hashMatches = checkHashValidity(); + + availability.Value = hashMatches ? BeatmapAvailability.LocallyAvailable() : BeatmapAvailability.NotDownloaded(); + + // only display a message to the user if a download seems to have just completed. + if (!hashMatches && downloadTracker.Progress.Value == 1) + Logger.Log("The imported beatmap set does not match the online version.", LoggingTarget.Runtime, LogLevel.Important); + break; default: - throw new ArgumentOutOfRangeException(nameof(State)); + throw new ArgumentOutOfRangeException(); } } + + private bool checkHashValidity() + { + int onlineId = SelectedItem.Value.Beatmap.Value.OnlineID; + string checksum = SelectedItem.Value.Beatmap.Value.MD5Hash; + + var beatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == onlineId && b.MD5Hash == checksum); + return beatmap?.BeatmapSet.DeletePending == false; + } } } From f014ceaead3556b47c8e0ea510cb3382206455a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 20:49:15 +0900 Subject: [PATCH 06/48] Update remaining usages of download tracking --- .../Online/TestSceneDirectDownloadButton.cs | 3 +- .../Panels/BeatmapPanelDownloadButton.cs | 36 +++++++++++++------ .../Panels/DownloadProgressBar.cs | 18 +++++++--- .../BeatmapSet/BeatmapSetHeaderContent.cs | 19 +++++++--- .../Buttons/HeaderDownloadButton.cs | 33 ++++++++++------- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 2 +- .../Screens/Ranking/ReplayDownloadButton.cs | 35 ++++++++++++------ 7 files changed, 100 insertions(+), 46 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs index bb7fcc2fce..879e35057b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Online; using osu.Game.Online.API.Requests.Responses; @@ -144,7 +145,7 @@ namespace osu.Game.Tests.Visual.Online { public new bool DownloadEnabled => base.DownloadEnabled; - public DownloadState DownloadState => State.Value; + public DownloadState DownloadState => this.ChildrenOfType().First().State.Value; public TestDownloadButton(BeatmapSetInfo beatmapSet) : base(beatmapSet) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index a8c4334ffb..5c6472cd1b 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Containers; @@ -13,7 +14,7 @@ using osu.Game.Online; namespace osu.Game.Overlays.BeatmapListing.Panels { - public class BeatmapPanelDownloadButton : BeatmapDownloadTrackingComposite + public class BeatmapPanelDownloadButton : CompositeDrawable { protected bool DownloadEnabled => button.Enabled.Value; @@ -26,16 +27,29 @@ namespace osu.Game.Overlays.BeatmapListing.Panels private readonly DownloadButton button; private Bindable noVideoSetting; + protected readonly BeatmapDownloadTracker DownloadTracker; + + protected readonly Bindable State = new Bindable(); + + private readonly BeatmapSetInfo beatmapSet; + public BeatmapPanelDownloadButton(BeatmapSetInfo beatmapSet) - : base(beatmapSet) { - InternalChild = shakeContainer = new ShakeContainer + this.beatmapSet = beatmapSet; + InternalChildren = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Child = button = new DownloadButton + shakeContainer = new ShakeContainer { RelativeSizeAxes = Axes.Both, + Child = button = new DownloadButton + { + RelativeSizeAxes = Axes.Both, + }, }, + DownloadTracker = new BeatmapDownloadTracker(beatmapSet) + { + State = { BindTarget = State } + } }; button.Add(new DownloadProgressBar(beatmapSet) @@ -50,7 +64,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { base.LoadComplete(); - button.State.BindTo(State); + ((IBindable)button.State).BindTo(DownloadTracker.State); FinishTransforms(true); } @@ -61,7 +75,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels button.Action = () => { - switch (State.Value) + switch (DownloadTracker.State.Value) { case DownloadState.Downloading: case DownloadState.Importing: @@ -73,16 +87,16 @@ namespace osu.Game.Overlays.BeatmapListing.Panels if (SelectedBeatmap.Value != null) findPredicate = b => b.OnlineBeatmapID == SelectedBeatmap.Value.OnlineBeatmapID; - game?.PresentBeatmap(BeatmapSet.Value, findPredicate); + game?.PresentBeatmap(beatmapSet, findPredicate); break; default: - beatmaps.Download(BeatmapSet.Value, noVideoSetting.Value); + beatmaps.Download(beatmapSet, noVideoSetting.Value); break; } }; - State.BindValueChanged(state => + DownloadTracker.State.BindValueChanged(state => { switch (state.NewValue) { @@ -92,7 +106,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels break; default: - if (BeatmapSet.Value?.OnlineInfo?.Availability.DownloadDisabled ?? false) + if (beatmapSet.OnlineInfo?.Availability.DownloadDisabled ?? false) { button.Enabled.Value = false; button.TooltipText = "this beatmap is currently not available for download."; diff --git a/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs index ca94078401..24f929f55e 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; @@ -12,13 +13,22 @@ using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapListing.Panels { - public class DownloadProgressBar : BeatmapDownloadTrackingComposite + public class DownloadProgressBar : CompositeDrawable { private readonly ProgressBar progressBar; + private readonly BeatmapDownloadTracker downloadTracker; public DownloadProgressBar(BeatmapSetInfo beatmapSet) - : base(beatmapSet) { + InternalChildren = new Drawable[] + { + progressBar = new ProgressBar(false) + { + Height = 0, + Alpha = 0, + }, + downloadTracker = new BeatmapDownloadTracker(beatmapSet), + }; AddInternal(progressBar = new ProgressBar(false) { Height = 0, @@ -34,9 +44,9 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { progressBar.FillColour = colours.Blue; progressBar.BackgroundColour = Color4.Black.Opacity(0.7f); - progressBar.Current = Progress; + progressBar.Current.BindTarget = downloadTracker.Progress; - State.BindValueChanged(state => + downloadTracker.State.BindValueChanged(state => { switch (state.NewValue) { diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index 6f85846720..e0a09ed520 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -3,12 +3,14 @@ using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -21,8 +23,10 @@ using osuTK; namespace osu.Game.Overlays.BeatmapSet { - public class BeatmapSetHeaderContent : BeatmapDownloadTrackingComposite + public class BeatmapSetHeaderContent : CompositeDrawable { + public readonly Bindable BeatmapSet = new Bindable(); + private const float transition_duration = 200; private const float buttons_height = 45; private const float buttons_spacing = 5; @@ -45,6 +49,8 @@ namespace osu.Game.Overlays.BeatmapSet private readonly FillFlowContainer fadeContent; private readonly LoadingSpinner loading; + private BeatmapDownloadTracker downloadTracker; + [Resolved] private IAPIProvider api { get; set; } @@ -222,13 +228,13 @@ namespace osu.Game.Overlays.BeatmapSet { coverGradient.Colour = ColourInfo.GradientVertical(colourProvider.Background6.Opacity(0.3f), colourProvider.Background6.Opacity(0.8f)); - State.BindValueChanged(_ => updateDownloadButtons()); - BeatmapSet.BindValueChanged(setInfo => { Picker.BeatmapSet = rulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue; cover.BeatmapSet = setInfo.NewValue; + downloadTracker?.Expire(); + if (setInfo.NewValue == null) { onlineStatusPill.FadeTo(0.5f, 500, Easing.OutQuint); @@ -241,6 +247,9 @@ namespace osu.Game.Overlays.BeatmapSet } else { + downloadTracker = new BeatmapDownloadTracker(setInfo.NewValue); + downloadTracker.State.BindValueChanged(_ => updateDownloadButtons()); + fadeContent.FadeIn(500, Easing.OutQuint); loading.Hide(); @@ -266,13 +275,13 @@ namespace osu.Game.Overlays.BeatmapSet { if (BeatmapSet.Value == null) return; - if (BeatmapSet.Value.OnlineInfo.Availability.DownloadDisabled && State.Value != DownloadState.LocallyAvailable) + if (BeatmapSet.Value.OnlineInfo.Availability.DownloadDisabled && downloadTracker.State.Value != DownloadState.LocallyAvailable) { downloadButtonsContainer.Clear(); return; } - switch (State.Value) + switch (downloadTracker.State.Value) { case DownloadState.LocallyAvailable: // temporary for UX until new design is implemented. diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index e7a55079ec..88d0778ae4 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -22,7 +22,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet.Buttons { - public class HeaderDownloadButton : BeatmapDownloadTrackingComposite, IHasTooltip + public class HeaderDownloadButton : CompositeDrawable, IHasTooltip { private const int text_size = 12; @@ -35,9 +35,12 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private ShakeContainer shakeContainer; private HeaderButton button; + private BeatmapDownloadTracker downloadTracker; + private readonly BeatmapSetInfo beatmapSet; + public HeaderDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) - : base(beatmapSet) { + this.beatmapSet = beatmapSet; this.noVideo = noVideo; Width = 120; @@ -49,13 +52,17 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { FillFlowContainer textSprites; - AddInternal(shakeContainer = new ShakeContainer + InternalChildren = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 5, - Child = button = new HeaderButton { RelativeSizeAxes = Axes.Both }, - }); + shakeContainer = new ShakeContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 5, + Child = button = new HeaderButton { RelativeSizeAxes = Axes.Both }, + }, + downloadTracker = new BeatmapDownloadTracker(beatmapSet), + }; button.AddRange(new Drawable[] { @@ -83,7 +90,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons }, } }, - new DownloadProgressBar(BeatmapSet.Value) + new DownloadProgressBar(beatmapSet) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, @@ -92,20 +99,20 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons button.Action = () => { - if (State.Value != DownloadState.NotDownloaded) + if (downloadTracker.State.Value != DownloadState.NotDownloaded) { shakeContainer.Shake(); return; } - beatmaps.Download(BeatmapSet.Value, noVideo); + beatmaps.Download(beatmapSet, noVideo); }; localUser.BindTo(api.LocalUser); localUser.BindValueChanged(userChanged, true); button.Enabled.BindValueChanged(enabledChanged, true); - State.BindValueChanged(state => + downloadTracker.State.BindValueChanged(state => { switch (state.NewValue) { @@ -161,7 +168,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private LocalisableString getVideoSuffixText() { - if (!BeatmapSet.Value.OnlineInfo.HasVideo) + if (!beatmapSet.OnlineInfo.HasVideo) return string.Empty; return noVideo ? BeatmapsetsStrings.ShowDetailsDownloadNoVideo : BeatmapsetsStrings.ShowDetailsDownloadVideo; diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 585b024623..16dd7b498b 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -302,7 +302,7 @@ namespace osu.Game.Screens.OnlinePlay { base.LoadComplete(); - State.BindValueChanged(stateChanged, true); + DownloadTracker.State.BindValueChanged(stateChanged, true); FinishTransforms(true); } diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index e644eb671a..f7325d2eb4 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; @@ -12,13 +13,17 @@ using osuTK; namespace osu.Game.Screens.Ranking { - public class ReplayDownloadButton : DownloadTrackingComposite + public class ReplayDownloadButton : CompositeDrawable { - public Bindable Score => Model; + public readonly Bindable Score = new Bindable(); + + protected readonly Bindable State = new Bindable(); private DownloadButton button; private ShakeContainer shakeContainer; + private ScoreDownloadTracker downloadTracker; + private ReplayAvailability replayAvailability { get @@ -26,7 +31,7 @@ namespace osu.Game.Screens.Ranking if (State.Value == DownloadState.LocallyAvailable) return ReplayAvailability.Local; - if (!string.IsNullOrEmpty(Model.Value?.Hash)) + if (!string.IsNullOrEmpty(Score.Value?.Hash)) return ReplayAvailability.Online; return ReplayAvailability.NotAvailable; @@ -34,8 +39,8 @@ namespace osu.Game.Screens.Ranking } public ReplayDownloadButton(ScoreInfo score) - : base(score) { + Score.Value = score; Size = new Vector2(50, 30); } @@ -56,11 +61,11 @@ namespace osu.Game.Screens.Ranking switch (State.Value) { case DownloadState.LocallyAvailable: - game?.PresentScore(Model.Value, ScorePresentType.Gameplay); + game?.PresentScore(Score.Value, ScorePresentType.Gameplay); break; case DownloadState.NotDownloaded: - scores.Download(Model.Value, false); + scores.Download(Score.Value, false); break; case DownloadState.Importing: @@ -70,17 +75,25 @@ namespace osu.Game.Screens.Ranking } }; - State.BindValueChanged(state => + Score.BindValueChanged(score => { - button.State.Value = state.NewValue; + downloadTracker?.Expire(); + if (score.NewValue != null) + { + AddInternal(downloadTracker = new ScoreDownloadTracker(score.NewValue) + { + State = { BindTarget = State } + }); + } + + button.Enabled.Value = replayAvailability != ReplayAvailability.NotAvailable; updateTooltip(); }, true); - Model.BindValueChanged(_ => + State.BindValueChanged(state => { - button.Enabled.Value = replayAvailability != ReplayAvailability.NotAvailable; - + button.State.Value = state.NewValue; updateTooltip(); }, true); } From 746d6a4c166f274e456b36a7e7f40fcd25ae39f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 21:26:26 +0900 Subject: [PATCH 07/48] Fix some oversights and test failures --- .../Online/TestSceneDirectDownloadButton.cs | 3 +-- osu.Game/Online/BeatmapDownloadTracker.cs | 8 +++----- .../OnlinePlayBeatmapAvailabilityTracker.cs | 12 +++++++----- .../Panels/BeatmapPanelDownloadButton.cs | 18 +++++++++--------- .../BeatmapSet/BeatmapSetHeaderContent.cs | 2 +- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 7 +++---- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 -- .../Multiplayer/MultiplayerMatchSubScreen.cs | 4 ++-- .../Screens/Ranking/ReplayDownloadButton.cs | 2 +- 9 files changed, 27 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs index 879e35057b..bb7fcc2fce 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs @@ -5,7 +5,6 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Online; using osu.Game.Online.API.Requests.Responses; @@ -145,7 +144,7 @@ namespace osu.Game.Tests.Visual.Online { public new bool DownloadEnabled => base.DownloadEnabled; - public DownloadState DownloadState => this.ChildrenOfType().First().State.Value; + public DownloadState DownloadState => State.Value; public TestDownloadButton(BeatmapSetInfo beatmapSet) : base(beatmapSet) diff --git a/osu.Game/Online/BeatmapDownloadTracker.cs b/osu.Game/Online/BeatmapDownloadTracker.cs index 6df74efe9c..181968c02f 100644 --- a/osu.Game/Online/BeatmapDownloadTracker.cs +++ b/osu.Game/Online/BeatmapDownloadTracker.cs @@ -34,7 +34,7 @@ namespace osu.Game.Online // Used to interact with manager classes that don't support interface types. Will eventually be replaced. var beatmapSetInfo = new BeatmapSetInfo { OnlineBeatmapSetID = TrackedItem.OnlineID }; - if ((TrackedItem as BeatmapSetInfo)?.ID > 0 || Manager?.IsAvailableLocally(beatmapSetInfo) == true) + if (Manager?.IsAvailableLocally(beatmapSetInfo) == true) UpdateState(DownloadState.LocallyAvailable); else if (Manager != null) attachDownload(Manager.GetExistingDownload(beatmapSetInfo)); @@ -122,10 +122,8 @@ namespace osu.Game.Online { Schedule(() => { - if (!checkEquality(item, TrackedItem)) - return; - - UpdateState(DownloadState.LocallyAvailable); + if (checkEquality(item, TrackedItem)) + UpdateState(DownloadState.LocallyAvailable); }); } } diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 6dd1204555..5e09f42aa8 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Framework.Threading; @@ -17,10 +18,12 @@ namespace osu.Game.Online.Rooms /// This differs from a regular download tracking composite as this accounts for the /// databased beatmap set's checksum, to disallow from playing with an altered version of the beatmap. /// - public class OnlinePlayBeatmapAvailabilityTracker : CompositeDrawable + public sealed class OnlinePlayBeatmapAvailabilityTracker : CompositeDrawable { public readonly IBindable SelectedItem = new Bindable(); + protected override bool RequiresChildrenUpdate => true; + [Resolved] private BeatmapManager beatmapManager { get; set; } @@ -46,9 +49,10 @@ namespace osu.Game.Online.Rooms if (item.NewValue == null) return; - downloadTracker?.Expire(); - downloadTracker = new BeatmapDownloadTracker(item.NewValue.Beatmap.Value.BeatmapSet); + downloadTracker?.RemoveAndDisposeImmediately(); + downloadTracker = new BeatmapDownloadTracker(item.NewValue.Beatmap.Value.BeatmapSet); + downloadTracker.State.BindValueChanged(_ => updateAvailability()); downloadTracker.Progress.BindValueChanged(_ => { if (downloadTracker.State.Value != DownloadState.Downloading) @@ -60,8 +64,6 @@ namespace osu.Game.Online.Rooms progressUpdate = Scheduler.AddDelayed(updateAvailability, progressUpdate == null ? 0 : 500); }); - downloadTracker.State.BindValueChanged(_ => updateAvailability(), true); - AddInternal(downloadTracker); }, true); } diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index 5c6472cd1b..dd12e8e467 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -36,6 +36,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels public BeatmapPanelDownloadButton(BeatmapSetInfo beatmapSet) { this.beatmapSet = beatmapSet; + InternalChildren = new Drawable[] { shakeContainer = new ShakeContainer @@ -44,6 +45,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels Child = button = new DownloadButton { RelativeSizeAxes = Axes.Both, + State = { BindTarget = State } }, }, DownloadTracker = new BeatmapDownloadTracker(beatmapSet) @@ -60,14 +62,6 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }); } - protected override void LoadComplete() - { - base.LoadComplete(); - - ((IBindable)button.State).BindTo(DownloadTracker.State); - FinishTransforms(true); - } - [BackgroundDependencyLoader(true)] private void load(OsuGame game, BeatmapManager beatmaps, OsuConfigManager osuConfig) { @@ -96,7 +90,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels } }; - DownloadTracker.State.BindValueChanged(state => + State.BindValueChanged(state => { switch (state.NewValue) { @@ -116,5 +110,11 @@ namespace osu.Game.Overlays.BeatmapListing.Panels } }, true); } + + protected override void LoadComplete() + { + base.LoadComplete(); + FinishTransforms(true); + } } } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index e0a09ed520..624d07fb0f 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -233,7 +233,7 @@ namespace osu.Game.Overlays.BeatmapSet Picker.BeatmapSet = rulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue; cover.BeatmapSet = setInfo.NewValue; - downloadTracker?.Expire(); + downloadTracker?.RemoveAndDisposeImmediately(); if (setInfo.NewValue == null) { diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 16dd7b498b..5639265617 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -289,7 +289,8 @@ namespace osu.Game.Screens.OnlinePlay [Resolved] private BeatmapManager beatmapManager { get; set; } - public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; + // required for download tracking, as this button hides itself. can probably be removed with a bit of consideration. + public override bool IsPresent => true; public PlaylistDownloadButton(PlaylistItem playlistItem) : base(playlistItem.Beatmap.Value.BeatmapSet) @@ -300,10 +301,8 @@ namespace osu.Game.Screens.OnlinePlay protected override void LoadComplete() { + State.BindValueChanged(stateChanged, true); base.LoadComplete(); - - DownloadTracker.State.BindValueChanged(stateChanged, true); - FinishTransforms(true); } private void stateChanged(ValueChangedEvent state) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index bcb793062b..270d12ec97 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -67,8 +67,6 @@ namespace osu.Game.Screens.OnlinePlay.Match [Cached] protected OnlinePlayBeatmapAvailabilityTracker BeatmapAvailabilityTracker { get; private set; } - protected IBindable BeatmapAvailability => BeatmapAvailabilityTracker.Availability; - public readonly Room Room; private readonly bool allowEdit; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 544eac4127..0af41c977b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -66,7 +66,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer SelectedItem.BindTo(client.CurrentMatchPlayingItem); - BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true); + BeatmapAvailabilityTracker.Availability.BindValueChanged(updateBeatmapAvailability, true); UserMods.BindValueChanged(onUserModsChanged); client.LoadRequested += onLoadRequested; @@ -362,7 +362,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void onLoadRequested() { - if (BeatmapAvailability.Value.State != DownloadState.LocallyAvailable) + if (BeatmapAvailabilityTracker.Availability.Value.State != DownloadState.LocallyAvailable) return; // In the case of spectating, IMultiplayerClient.LoadRequested can be fired while the game is still spectating a previous session. diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index f7325d2eb4..66b3c973f5 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Ranking Score.BindValueChanged(score => { - downloadTracker?.Expire(); + downloadTracker?.RemoveAndDisposeImmediately(); if (score.NewValue != null) { From 347f41f367c67e2e7c2d79f76e1bdcc66ea907f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 23:54:02 +0900 Subject: [PATCH 08/48] Change test to allow for potentially single-frame delayed events firing --- .../Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 558b874234..5e7ce3abf5 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -128,7 +128,7 @@ namespace osu.Game.Tests.Online private void addAvailabilityCheckStep(string description, Func expected) { - AddAssert(description, () => availabilityTracker.Availability.Value.Equals(expected.Invoke())); + AddUntilStep(description, () => availabilityTracker.Availability.Value.Equals(expected.Invoke())); } private static BeatmapInfo getTestBeatmapInfo(string archiveFile) From 290f583ae0d19aa35549d00e101598d96de7500a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 00:59:48 +0900 Subject: [PATCH 09/48] Fix incorrect initial state --- osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 5e09f42aa8..87348fbd1c 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -32,7 +32,7 @@ namespace osu.Game.Online.Rooms /// public IBindable Availability => availability; - private readonly Bindable availability = new Bindable(BeatmapAvailability.LocallyAvailable()); + private readonly Bindable availability = new Bindable(BeatmapAvailability.NotDownloaded()); private ScheduledDelegate progressUpdate; From ce29d6fd81b9aef105bca0befc77a16ab3fab1e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:13:06 +0900 Subject: [PATCH 10/48] Change scroll speed adjust defaults to F3/F4 Not the most obvious keys, but does match stable expectations so let's go with it for now. Rationale for not using +/- is that local audio offset was bound to those, and people will expect it to be when we get to implementing eventually. Also, adjust the scroll speed is a pretty rare thing to do since it's been constant across beatmaps for years now. --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 9fd7caadd0..22446634c1 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -84,8 +84,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.ExtraMouseButton2, GlobalAction.SkipCutscene), new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry), new KeyBinding(new[] { InputKey.Control, InputKey.Tilde }, GlobalAction.QuickExit), - new KeyBinding(new[] { InputKey.Control, InputKey.Plus }, GlobalAction.IncreaseScrollSpeed), - new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed), + new KeyBinding(new[] { InputKey.F3 }, GlobalAction.DecreaseScrollSpeed), + new KeyBinding(new[] { InputKey.F4 }, GlobalAction.IncreaseScrollSpeed), new KeyBinding(new[] { InputKey.Shift, InputKey.Tab }, GlobalAction.ToggleInGameInterface), new KeyBinding(InputKey.MouseMiddle, GlobalAction.PauseGameplay), new KeyBinding(InputKey.Space, GlobalAction.TogglePauseReplay), From 8d21f0b04b430e9c0943213a64b59cdc71ea3e2e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:14:22 +0900 Subject: [PATCH 11/48] Add migration to reset conflicting scroll speed key bindings --- osu.Game/Database/RealmContextFactory.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index 013a2e9d64..40b67beed5 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -6,9 +6,11 @@ using System.Linq; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Development; +using osu.Framework.Input.Bindings; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Statistics; +using osu.Game.Input.Bindings; using osu.Game.Models; using Realms; @@ -32,8 +34,9 @@ namespace osu.Game.Database /// Version history: /// 6 First tracked version (~20211018) /// 7 Changed OnlineID fields to non-nullable to add indexing support (20211018) + /// 8 Rebind scroll adjust keys to not have control modifier (20211029) /// - private const int schema_version = 7; + private const int schema_version = 8; /// /// Lock object which is held during sections, blocking context creation during blocking periods. @@ -148,6 +151,21 @@ namespace osu.Game.Database private void onMigration(Migration migration, ulong lastSchemaVersion) { + if (lastSchemaVersion < 8) + { + // Ctrl -/+ now adjusts UI scale so let's clear any bindings which overlap these combinations. + // New defaults will be populated by the key store afterwards. + var keyBindings = migration.NewRealm.All(); + + var increaseSpeedBinding = keyBindings.FirstOrDefault(k => k.ActionInt == (int)GlobalAction.IncreaseScrollSpeed); + if (increaseSpeedBinding != null && increaseSpeedBinding.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Control, InputKey.Plus })) + migration.NewRealm.Remove(increaseSpeedBinding); + + var descreaseSpeedBinding = keyBindings.FirstOrDefault(k => k.ActionInt == (int)GlobalAction.DecreaseScrollSpeed); + if (descreaseSpeedBinding != null && descreaseSpeedBinding.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Control, InputKey.Minus })) + migration.NewRealm.Remove(descreaseSpeedBinding); + } + if (lastSchemaVersion < 7) { convertOnlineIDs(); From 3838fe5c6adc2d84964e9aebb934edbed40dd02b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:39:15 +0900 Subject: [PATCH 12/48] Fix typo in variable name --- osu.Game/Database/RealmContextFactory.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index 40b67beed5..85f7dd6b89 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -161,9 +161,9 @@ namespace osu.Game.Database if (increaseSpeedBinding != null && increaseSpeedBinding.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Control, InputKey.Plus })) migration.NewRealm.Remove(increaseSpeedBinding); - var descreaseSpeedBinding = keyBindings.FirstOrDefault(k => k.ActionInt == (int)GlobalAction.DecreaseScrollSpeed); - if (descreaseSpeedBinding != null && descreaseSpeedBinding.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Control, InputKey.Minus })) - migration.NewRealm.Remove(descreaseSpeedBinding); + var decreaseSpeedBinding = keyBindings.FirstOrDefault(k => k.ActionInt == (int)GlobalAction.DecreaseScrollSpeed); + if (decreaseSpeedBinding != null && decreaseSpeedBinding.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Control, InputKey.Minus })) + migration.NewRealm.Remove(decreaseSpeedBinding); } if (lastSchemaVersion < 7) From 748003b016ce84bf4d0f13f98a70ed494cbb8915 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:54:19 +0900 Subject: [PATCH 13/48] Simplify `Manager` null check to a single pre-check --- osu.Game/Online/BeatmapDownloadTracker.cs | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/BeatmapDownloadTracker.cs b/osu.Game/Online/BeatmapDownloadTracker.cs index 181968c02f..4a7d0b660a 100644 --- a/osu.Game/Online/BeatmapDownloadTracker.cs +++ b/osu.Game/Online/BeatmapDownloadTracker.cs @@ -31,25 +31,25 @@ namespace osu.Game.Online [BackgroundDependencyLoader(true)] private void load() { + if (Manager == null) + return; + // Used to interact with manager classes that don't support interface types. Will eventually be replaced. var beatmapSetInfo = new BeatmapSetInfo { OnlineBeatmapSetID = TrackedItem.OnlineID }; - if (Manager?.IsAvailableLocally(beatmapSetInfo) == true) + if (Manager.IsAvailableLocally(beatmapSetInfo)) UpdateState(DownloadState.LocallyAvailable); - else if (Manager != null) + else attachDownload(Manager.GetExistingDownload(beatmapSetInfo)); - if (Manager != null) - { - managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); - managerDownloadBegan.BindValueChanged(downloadBegan); - managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); - managerDownloadFailed.BindValueChanged(downloadFailed); - managerUpdated = Manager.ItemUpdated.GetBoundCopy(); - managerUpdated.BindValueChanged(itemUpdated); - managerRemoved = Manager.ItemRemoved.GetBoundCopy(); - managerRemoved.BindValueChanged(itemRemoved); - } + managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); + managerDownloadBegan.BindValueChanged(downloadBegan); + managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); + managerDownloadFailed.BindValueChanged(downloadFailed); + managerUpdated = Manager.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(itemUpdated); + managerRemoved = Manager.ItemRemoved.GetBoundCopy(); + managerRemoved.BindValueChanged(itemRemoved); } private void downloadBegan(ValueChangedEvent>> weakRequest) From 75a088d4f4f8082986a418c901e7def8b7eae859 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:54:42 +0900 Subject: [PATCH 14/48] Fix variable name mismatch --- osu.Game/Online/ScoreDownloadTracker.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/ScoreDownloadTracker.cs b/osu.Game/Online/ScoreDownloadTracker.cs index 7a714a7a0e..ac700fc587 100644 --- a/osu.Game/Online/ScoreDownloadTracker.cs +++ b/osu.Game/Online/ScoreDownloadTracker.cs @@ -32,12 +32,12 @@ namespace osu.Game.Online private void load() { // Used to interact with manager classes that don't support interface types. Will eventually be replaced. - var beatmapSetInfo = new ScoreInfo { OnlineScoreID = TrackedItem.OnlineScoreID }; + var scoreInfo = new ScoreInfo { OnlineScoreID = TrackedItem.OnlineScoreID }; - if (TrackedItem.ID > 0 || Manager?.IsAvailableLocally(beatmapSetInfo) == true) + if (TrackedItem.ID > 0 || Manager?.IsAvailableLocally(scoreInfo) == true) UpdateState(DownloadState.LocallyAvailable); else if (Manager != null) - attachDownload(Manager.GetExistingDownload(beatmapSetInfo)); + attachDownload(Manager.GetExistingDownload(scoreInfo)); if (Manager != null) { From dc44734db20e25c4a39b52faa4df0ff62740ee21 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:56:17 +0900 Subject: [PATCH 15/48] Simplify null check in `ScoreDownloadTracker` and remove unnecessary ID check --- osu.Game/Online/ScoreDownloadTracker.cs | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/ScoreDownloadTracker.cs b/osu.Game/Online/ScoreDownloadTracker.cs index ac700fc587..8222a5382c 100644 --- a/osu.Game/Online/ScoreDownloadTracker.cs +++ b/osu.Game/Online/ScoreDownloadTracker.cs @@ -31,25 +31,25 @@ namespace osu.Game.Online [BackgroundDependencyLoader(true)] private void load() { + if (Manager == null) + return; + // Used to interact with manager classes that don't support interface types. Will eventually be replaced. var scoreInfo = new ScoreInfo { OnlineScoreID = TrackedItem.OnlineScoreID }; - if (TrackedItem.ID > 0 || Manager?.IsAvailableLocally(scoreInfo) == true) + if (Manager.IsAvailableLocally(scoreInfo)) UpdateState(DownloadState.LocallyAvailable); - else if (Manager != null) + else attachDownload(Manager.GetExistingDownload(scoreInfo)); - if (Manager != null) - { - managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); - managerDownloadBegan.BindValueChanged(downloadBegan); - managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); - managerDownloadFailed.BindValueChanged(downloadFailed); - managerUpdated = Manager.ItemUpdated.GetBoundCopy(); - managerUpdated.BindValueChanged(itemUpdated); - managerRemoved = Manager.ItemRemoved.GetBoundCopy(); - managerRemoved.BindValueChanged(itemRemoved); - } + managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); + managerDownloadBegan.BindValueChanged(downloadBegan); + managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); + managerDownloadFailed.BindValueChanged(downloadFailed); + managerUpdated = Manager.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(itemUpdated); + managerRemoved = Manager.ItemRemoved.GetBoundCopy(); + managerRemoved.BindValueChanged(itemRemoved); } private void downloadBegan(ValueChangedEvent>> weakRequest) From f3811edb0b83377ceee067571f4d553bb8bb5b1a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:57:54 +0900 Subject: [PATCH 16/48] Add inline comment explaining usage of `RequiresChildrenUpdate` override --- osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 87348fbd1c..88793c7cfc 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -22,6 +22,7 @@ namespace osu.Game.Online.Rooms { public readonly IBindable SelectedItem = new Bindable(); + // Required to allow child components to update. Can potentially be replaced with a `CompositeComponent` class if or when we make one. protected override bool RequiresChildrenUpdate => true; [Resolved] From b252b176d45770bbf9e8e1fbd089fa37408b5a51 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:59:07 +0900 Subject: [PATCH 17/48] Seal implementation and add comment about `LoadComplete` execution order --- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 1a59f36c11..c946bf12fd 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -282,7 +282,7 @@ namespace osu.Game.Screens.OnlinePlay return true; } - private class PlaylistDownloadButton : BeatmapPanelDownloadButton + private sealed class PlaylistDownloadButton : BeatmapPanelDownloadButton { private readonly PlaylistItem playlistItem; @@ -302,6 +302,8 @@ namespace osu.Game.Screens.OnlinePlay protected override void LoadComplete() { State.BindValueChanged(stateChanged, true); + + // base implementation calls FinishTransforms, so should be run after the above state update. base.LoadComplete(); } From 414d920ca25caf0f06e619a0c810414212d8b5a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 12:02:10 +0900 Subject: [PATCH 18/48] Revert to previous exposure of `RoomSubScreen.BeatmapAvailability` --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 8 +++++--- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 270d12ec97..2015a050bb 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -65,7 +65,9 @@ namespace osu.Game.Screens.OnlinePlay.Match private IBindable> managerUpdated; [Cached] - protected OnlinePlayBeatmapAvailabilityTracker BeatmapAvailabilityTracker { get; private set; } + private OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker { get; set; } + + protected IBindable BeatmapAvailability => beatmapAvailabilityTracker.Availability; public readonly Room Room; private readonly bool allowEdit; @@ -86,7 +88,7 @@ namespace osu.Game.Screens.OnlinePlay.Match Padding = new MarginPadding { Top = Header.HEIGHT }; - BeatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker + beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker { SelectedItem = { BindTarget = SelectedItem } }; @@ -101,7 +103,7 @@ namespace osu.Game.Screens.OnlinePlay.Match InternalChildren = new Drawable[] { - BeatmapAvailabilityTracker, + beatmapAvailabilityTracker, new GridContainer { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 0af41c977b..544eac4127 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -66,7 +66,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer SelectedItem.BindTo(client.CurrentMatchPlayingItem); - BeatmapAvailabilityTracker.Availability.BindValueChanged(updateBeatmapAvailability, true); + BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true); UserMods.BindValueChanged(onUserModsChanged); client.LoadRequested += onLoadRequested; @@ -362,7 +362,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void onLoadRequested() { - if (BeatmapAvailabilityTracker.Availability.Value.State != DownloadState.LocallyAvailable) + if (BeatmapAvailability.Value.State != DownloadState.LocallyAvailable) return; // In the case of spectating, IMultiplayerClient.LoadRequested can be fired while the game is still spectating a previous session. From 6f863ca204cd9488a4e9a141ce8feadbbabef26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 1 Aug 2021 17:12:04 +0200 Subject: [PATCH 19/48] Adjust game-side text flow containers to part-based model --- .../Graphics/Containers/LinkFlowContainer.cs | 50 ++++++++++++++----- .../Containers/OsuTextFlowContainer.cs | 6 +-- osu.Game/Graphics/ErrorTextFlowContainer.cs | 9 ++-- osu.Game/Online/Chat/DrawableLinkCompiler.cs | 7 +++ .../Overlays/AccountCreation/ScreenEntry.cs | 5 +- .../Changelog/ChangelogSupporterPromo.cs | 7 ++- osu.Game/Overlays/Music/PlaylistItem.cs | 16 +++--- osu.Game/Screens/Menu/Disclaimer.cs | 10 ++-- 8 files changed, 73 insertions(+), 37 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 21c1d70d45..7569200c53 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Sprites; using System.Collections.Generic; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Game.Graphics.Sprites; @@ -71,26 +72,20 @@ namespace osu.Game.Graphics.Containers var spriteText = new OsuSpriteText { Text = text }; AddText(spriteText, creationParameters); - createLink(spriteText.Yield(), new LinkDetails(action, argument), tooltipText); + createLink(new TextPartManual(spriteText.Yield()), new LinkDetails(action, argument), tooltipText); } public void AddLink(IEnumerable text, LinkAction action, string linkArgument, string tooltipText = null) { - foreach (var t in text) - AddArbitraryDrawable(t); - - createLink(text, new LinkDetails(action, linkArgument), tooltipText); + createLink(new TextPartManual(text), new LinkDetails(action, linkArgument), tooltipText); } public void AddUserLink(User user, Action creationParameters = null) => createLink(AddText(user.Username, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "view profile"); - private void createLink(IEnumerable drawables, LinkDetails link, string tooltipText, Action action = null) + private void createLink(ITextPart textPart, LinkDetails link, LocalisableString tooltipText, Action action = null) { - var linkCompiler = CreateLinkCompiler(drawables.OfType()); - linkCompiler.RelativeSizeAxes = Axes.Both; - linkCompiler.TooltipText = tooltipText; - linkCompiler.Action = () => + Action onClickAction = () => { if (action != null) action(); @@ -101,10 +96,41 @@ namespace osu.Game.Graphics.Containers host.OpenUrlExternally(link.Argument); }; - AddInternal(linkCompiler); + AddPart(new TextLink(textPart, tooltipText, onClickAction)); } - protected virtual DrawableLinkCompiler CreateLinkCompiler(IEnumerable parts) => new DrawableLinkCompiler(parts); + private class TextLink : TextPart + { + private readonly ITextPart innerPart; + private readonly LocalisableString tooltipText; + private readonly Action action; + + public TextLink(ITextPart innerPart, LocalisableString tooltipText, Action action) + { + this.innerPart = innerPart; + this.tooltipText = tooltipText; + this.action = action; + } + + protected override IEnumerable CreateDrawablesFor(TextFlowContainer textFlowContainer) + { + var linkFlowContainer = (LinkFlowContainer)textFlowContainer; + + innerPart.RecreateDrawablesFor(linkFlowContainer); + var drawables = innerPart.Drawables.ToList(); + + drawables.Add(linkFlowContainer.CreateLinkCompiler(innerPart).With(c => + { + c.RelativeSizeAxes = Axes.Both; + c.TooltipText = tooltipText; + c.Action = action; + })); + + return drawables; + } + } + + protected virtual DrawableLinkCompiler CreateLinkCompiler(ITextPart textPart) => new DrawableLinkCompiler(textPart); // We want the compilers to always be visible no matter where they are, so RelativeSizeAxes is used. // However due to https://github.com/ppy/osu-framework/issues/2073, it's possible for the compilers to be relative size in the flow's auto-size axes - an unsupported operation. diff --git a/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs b/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs index 6a87a4b8b9..b8237832a3 100644 --- a/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -19,8 +19,8 @@ namespace osu.Game.Graphics.Containers protected override SpriteText CreateSpriteText() => new OsuSpriteText(); - public void AddArbitraryDrawable(Drawable drawable) => AddInternal(drawable); + public ITextPart AddArbitraryDrawable(Drawable drawable) => AddPart(new TextPartManual(drawable.Yield())); - public IEnumerable AddIcon(IconUsage icon, Action creationParameters = null) => AddText(icon.Icon.ToString(), creationParameters); + public ITextPart AddIcon(IconUsage icon, Action creationParameters = null) => AddText(icon.Icon.ToString(), creationParameters); } } diff --git a/osu.Game/Graphics/ErrorTextFlowContainer.cs b/osu.Game/Graphics/ErrorTextFlowContainer.cs index 486382bf33..dafc363973 100644 --- a/osu.Game/Graphics/ErrorTextFlowContainer.cs +++ b/osu.Game/Graphics/ErrorTextFlowContainer.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; using osuTK.Graphics; @@ -10,7 +10,7 @@ namespace osu.Game.Graphics { public class ErrorTextFlowContainer : OsuTextFlowContainer { - private readonly List errorDrawables = new List(); + private readonly List errorTextParts = new List(); public ErrorTextFlowContainer() : base(cp => cp.Font = cp.Font.With(size: 12)) @@ -19,7 +19,8 @@ namespace osu.Game.Graphics public void ClearErrors() { - errorDrawables.ForEach(d => d.Expire()); + foreach (var textPart in errorTextParts) + RemovePart(textPart); } public void AddErrors(string[] errors) @@ -29,7 +30,7 @@ namespace osu.Game.Graphics if (errors == null) return; foreach (string error in errors) - errorDrawables.AddRange(AddParagraph(error, cp => cp.Colour = Color4.Red)); + errorTextParts.Add(AddParagraph(error, cp => cp.Colour = Color4.Red)); } } } diff --git a/osu.Game/Online/Chat/DrawableLinkCompiler.cs b/osu.Game/Online/Chat/DrawableLinkCompiler.cs index 4df60eba69..8356b36667 100644 --- a/osu.Game/Online/Chat/DrawableLinkCompiler.cs +++ b/osu.Game/Online/Chat/DrawableLinkCompiler.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -30,6 +32,11 @@ namespace osu.Game.Online.Chat protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts); + public DrawableLinkCompiler(ITextPart part) + : this(part.Drawables.OfType()) + { + } + public DrawableLinkCompiler(IEnumerable parts) : base(HoverSampleSet.Submit) { diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index bcb3d4b635..8ee3b1cb2e 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; @@ -37,7 +36,7 @@ namespace osu.Game.Overlays.AccountCreation private IAPIProvider api { get; set; } private ShakeContainer registerShake; - private IEnumerable characterCheckText; + private ITextPart characterCheckText; private OsuTextBox[] textboxes; private LoadingLayer loadingLayer; @@ -136,7 +135,7 @@ namespace osu.Game.Overlays.AccountCreation characterCheckText = passwordDescription.AddText("8 characters long"); passwordDescription.AddText(". Choose something long but also something you will remember, like a line from your favourite song."); - passwordTextBox.Current.ValueChanged += password => { characterCheckText.ForEach(s => s.Colour = password.NewValue.Length == 0 ? Color4.White : Interpolation.ValueAt(password.NewValue.Length, Color4.OrangeRed, Color4.YellowGreen, 0, 8, Easing.In)); }; + passwordTextBox.Current.ValueChanged += password => { characterCheckText.Drawables.ForEach(s => s.Colour = password.NewValue.Length == 0 ? Color4.White : Interpolation.ValueAt(password.NewValue.Length, Color4.OrangeRed, Color4.YellowGreen, 0, 8, Easing.In)); }; } public override void OnEntering(IScreen last) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 1b0a62dc4a..5cc598ae70 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -166,12 +165,12 @@ namespace osu.Game.Overlays.Changelog { } - protected override DrawableLinkCompiler CreateLinkCompiler(IEnumerable parts) => new SupporterPromoLinkCompiler(parts); + protected override DrawableLinkCompiler CreateLinkCompiler(ITextPart textPart) => new SupporterPromoLinkCompiler(textPart); private class SupporterPromoLinkCompiler : DrawableLinkCompiler { - public SupporterPromoLinkCompiler(IEnumerable parts) - : base(parts) + public SupporterPromoLinkCompiler(ITextPart part) + : base(part) { } diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index ef25de77c6..eea2a9dc7e 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -3,12 +3,10 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Beatmaps; @@ -25,7 +23,7 @@ namespace osu.Game.Overlays.Music public Action RequestSelection; private TextFlowContainer text; - private IEnumerable titleSprites; + private ITextPart titlePart; private ILocalisedBindableString title; private ILocalisedBindableString artist; @@ -63,11 +61,16 @@ namespace osu.Game.Overlays.Music if (set.OldValue?.Equals(Model) != true && set.NewValue?.Equals(Model) != true) return; - foreach (Drawable s in titleSprites) - s.FadeColour(set.NewValue.Equals(Model) ? selectedColour : Color4.White, FADE_DURATION); + updateSelectionState(false); }, true); } + private void updateSelectionState(bool instant) + { + foreach (Drawable s in titlePart.Drawables) + s.FadeColour(SelectedSet.Value?.Equals(Model) == true ? selectedColour : Color4.White, instant ? 0 : FADE_DURATION); + } + protected override Drawable CreateContent() => text = new OsuTextFlowContainer { RelativeSizeAxes = Axes.X, @@ -79,7 +82,8 @@ namespace osu.Game.Overlays.Music text.Clear(); // space after the title to put a space between the title and artist - titleSprites = text.AddText(title.Value + @" ", sprite => sprite.Font = OsuFont.GetFont(weight: FontWeight.Regular)).OfType(); + titlePart = text.AddText(title.Value + @" ", sprite => sprite.Font = OsuFont.GetFont(weight: FontWeight.Regular)); + updateSelectionState(true); text.AddText(artist.Value, sprite => { diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 7f34e1e395..b8abc131fd 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Menu private readonly Bindable currentUser = new Bindable(); private FillFlowContainer fill; - private readonly List expendableText = new List(); + private readonly List expendableText = new List(); public Disclaimer(OsuScreen nextScreen = null) { @@ -97,7 +97,7 @@ namespace osu.Game.Screens.Menu textFlow.AddText("this is osu!", t => t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.Regular)); - expendableText.AddRange(textFlow.AddText("lazer", t => + expendableText.Add(textFlow.AddText("lazer", t => { t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.Regular); t.Colour = colours.PinkLight; @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Menu t.Font = t.Font.With(Typeface.Torus, 20, FontWeight.SemiBold); t.Colour = colours.Pink; }); - expendableText.AddRange(textFlow.AddText(" coming to osu!", formatRegular)); + expendableText.Add(textFlow.AddText(" coming to osu!", formatRegular)); textFlow.AddText(".", formatRegular); textFlow.NewParagraph(); @@ -152,7 +152,7 @@ namespace osu.Game.Screens.Menu t.Font = t.Font.With(size: 20); t.Origin = Anchor.Centre; t.Colour = colours.Pink; - }).First(); + }).Drawables.First(); if (IsLoaded) animateHeart(); @@ -193,7 +193,7 @@ namespace osu.Game.Screens.Menu using (BeginDelayedSequence(520 + 160)) { fill.MoveToOffset(new Vector2(0, 15), 160, Easing.OutQuart); - Schedule(() => expendableText.ForEach(t => + Schedule(() => expendableText.SelectMany(t => t.Drawables).ForEach(t => { t.FadeOut(100); t.ScaleTo(new Vector2(0, 1), 100, Easing.OutQuart); From 61f0339c1d43bac24a1b1addf9d66859fba62401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 Aug 2021 20:52:12 +0200 Subject: [PATCH 20/48] Avoid double-adding chunks in link flow container --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 7569200c53..0b43c16ebe 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -59,19 +59,20 @@ namespace osu.Game.Graphics.Containers } public void AddLink(string text, string url, Action creationParameters = null) => - createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.External, url), url); + createLink(CreateChunkFor(text, true, CreateSpriteText, creationParameters), new LinkDetails(LinkAction.External, url), url); public void AddLink(string text, Action action, string tooltipText = null, Action creationParameters = null) - => createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.Custom, string.Empty), tooltipText, action); + => createLink(CreateChunkFor(text, true, CreateSpriteText, creationParameters), new LinkDetails(LinkAction.Custom, string.Empty), tooltipText, action); public void AddLink(string text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) - => createLink(AddText(text, creationParameters), new LinkDetails(action, argument), tooltipText); + => createLink(CreateChunkFor(text, true, CreateSpriteText, creationParameters), new LinkDetails(action, argument), tooltipText); public void AddLink(LocalisableString text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) { var spriteText = new OsuSpriteText { Text = text }; AddText(spriteText, creationParameters); + RemoveInternal(spriteText); // TODO: temporary, will go away when TextParts support localisation properly. createLink(new TextPartManual(spriteText.Yield()), new LinkDetails(action, argument), tooltipText); } @@ -81,7 +82,7 @@ namespace osu.Game.Graphics.Containers } public void AddUserLink(User user, Action creationParameters = null) - => createLink(AddText(user.Username, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "view profile"); + => createLink(CreateChunkFor(user.Username, true, CreateSpriteText, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "view profile"); private void createLink(ITextPart textPart, LinkDetails link, LocalisableString tooltipText, Action action = null) { From 7583435901388816aaf9cf6000977b627a429e6e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 16:45:10 +0900 Subject: [PATCH 21/48] Refactor `BeatmapDifficultyCache` to work with `IBeatmapInfo` --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 47 +++++++++++---------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 3777365088..9a0cdb387d 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -88,7 +88,7 @@ namespace osu.Game.Beatmaps /// The to get the difficulty of. /// An optional which stops updating the star difficulty for the given . /// A bindable that is updated to contain the star difficulty when it becomes available. Will be null while in an initial calculating state (but not during updates to ruleset and mods if a stale value is already propagated). - public IBindable GetBindableDifficulty([NotNull] BeatmapInfo beatmapInfo, CancellationToken cancellationToken = default) + public IBindable GetBindableDifficulty([NotNull] IBeatmapInfo beatmapInfo, CancellationToken cancellationToken = default) { var bindable = createBindable(beatmapInfo, currentRuleset.Value, currentMods.Value, cancellationToken); @@ -99,42 +99,45 @@ namespace osu.Game.Beatmaps } /// - /// Retrieves a bindable containing the star difficulty of a with a given and combination. + /// Retrieves a bindable containing the star difficulty of a with a given and combination. /// /// /// The bindable will not update to follow the currently-selected ruleset and mods or its settings. /// - /// The to get the difficulty of. - /// The to get the difficulty with. If null, the 's ruleset is used. + /// The to get the difficulty of. + /// The to get the difficulty with. If null, the 's ruleset is used. /// The s to get the difficulty with. If null, no mods will be assumed. - /// An optional which stops updating the star difficulty for the given . + /// An optional which stops updating the star difficulty for the given . /// A bindable that is updated to contain the star difficulty when it becomes available. Will be null while in an initial calculating state. - public IBindable GetBindableDifficulty([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo, [CanBeNull] IEnumerable mods, + public IBindable GetBindableDifficulty([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo rulesetInfo, [CanBeNull] IEnumerable mods, CancellationToken cancellationToken = default) => createBindable(beatmapInfo, rulesetInfo, mods, cancellationToken); /// - /// Retrieves the difficulty of a . + /// Retrieves the difficulty of a . /// - /// The to get the difficulty of. - /// The to get the difficulty with. + /// The to get the difficulty of. + /// The to get the difficulty with. /// The s to get the difficulty with. /// An optional which stops computing the star difficulty. /// The . - public virtual Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, + public virtual Task GetDifficultyAsync([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo rulesetInfo = null, [CanBeNull] IEnumerable 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; + var localBeatmapInfo = beatmapInfo as BeatmapInfo; + var localRulesetInfo = rulesetInfo as RulesetInfo; + // Difficulty can only be computed if the beatmap and ruleset are locally available. - if (beatmapInfo.ID == 0 || rulesetInfo.ID == null) + if (localBeatmapInfo == null || localRulesetInfo == null) { // If not, fall back to the existing star difficulty (e.g. from an online source). - return Task.FromResult(new StarDifficulty(beatmapInfo.StarDifficulty, beatmapInfo.MaxCombo ?? 0)); + return Task.FromResult(new StarDifficulty(beatmapInfo.StarRating, (beatmapInfo as IBeatmapOnlineInfo)?.MaxCombo ?? 0)); } - return GetAsync(new DifficultyCacheLookup(beatmapInfo, rulesetInfo, mods), cancellationToken); + return GetAsync(new DifficultyCacheLookup(localBeatmapInfo, localRulesetInfo, mods), cancellationToken); } protected override Task ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken token = default) @@ -227,12 +230,12 @@ namespace osu.Game.Beatmaps /// /// Creates a new and triggers an initial value update. /// - /// The that star difficulty should correspond to. - /// The initial to get the difficulty with. + /// The that star difficulty should correspond to. + /// The initial to get the difficulty with. /// The initial s to get the difficulty with. - /// An optional which stops updating the star difficulty for the given . + /// An optional which stops updating the star difficulty for the given . /// The . - private BindableStarDifficulty createBindable([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo initialRulesetInfo, [CanBeNull] IEnumerable initialMods, + private BindableStarDifficulty createBindable([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo initialRulesetInfo, [CanBeNull] IEnumerable initialMods, CancellationToken cancellationToken) { var bindable = new BindableStarDifficulty(beatmapInfo, cancellationToken); @@ -244,12 +247,12 @@ namespace osu.Game.Beatmaps /// Updates the value of a with a given ruleset + mods. /// /// The to update. - /// The to update with. + /// The to update with. /// The s to update with. /// A token that may be used to cancel this update. - private void updateBindable([NotNull] BindableStarDifficulty bindable, [CanBeNull] RulesetInfo rulesetInfo, [CanBeNull] IEnumerable mods, CancellationToken cancellationToken = default) + private void updateBindable([NotNull] BindableStarDifficulty bindable, [CanBeNull] IRulesetInfo rulesetInfo, [CanBeNull] IEnumerable mods, CancellationToken cancellationToken = default) { - // GetDifficultyAsync will fall back to existing data from BeatmapInfo if not locally available + // GetDifficultyAsync will fall back to existing data from IBeatmapInfo if not locally available // (contrary to GetAsync) GetDifficultyAsync(bindable.BeatmapInfo, rulesetInfo, mods, cancellationToken) .ContinueWith(t => @@ -343,10 +346,10 @@ namespace osu.Game.Beatmaps private class BindableStarDifficulty : Bindable { - public readonly BeatmapInfo BeatmapInfo; + public readonly IBeatmapInfo BeatmapInfo; public readonly CancellationToken CancellationToken; - public BindableStarDifficulty(BeatmapInfo beatmapInfo, CancellationToken cancellationToken) + public BindableStarDifficulty(IBeatmapInfo beatmapInfo, CancellationToken cancellationToken) { BeatmapInfo = beatmapInfo; CancellationToken = cancellationToken; From aff216840fcf94c98a609c3f5cc9fbd4bbdc628c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 16:53:54 +0900 Subject: [PATCH 22/48] Add a note about `OnlineID` potentially being zero in non-autoincrement cases --- osu.Game/Database/IHasOnlineID.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/IHasOnlineID.cs b/osu.Game/Database/IHasOnlineID.cs index 6e2be7e1f9..bb05bf75cb 100644 --- a/osu.Game/Database/IHasOnlineID.cs +++ b/osu.Game/Database/IHasOnlineID.cs @@ -8,7 +8,7 @@ namespace osu.Game.Database public interface IHasOnlineID { /// - /// The server-side ID representing this instance, if one exists. Any value 0 or less denotes a missing ID. + /// The server-side ID representing this instance, if one exists. Any value 0 or less denotes a missing ID (except in special cases where autoincrement is not used, like rulesets). /// /// /// Generally we use -1 when specifying "missing" in code, but values of 0 are also considered missing as the online source From 3598adb3449e395bc15ce29d2e895a5421c84705 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 17:04:56 +0900 Subject: [PATCH 23/48] Fix test implementing old version of class --- .../Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index f91d3f595b..50ae673c06 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -140,7 +140,7 @@ namespace osu.Game.Tests.Visual.SongSelect } } - public override async Task GetDifficultyAsync(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo = null, IEnumerable mods = null, CancellationToken cancellationToken = default) + public override async Task GetDifficultyAsync(IBeatmapInfo beatmapInfo, IRulesetInfo rulesetInfo = null, IEnumerable mods = null, CancellationToken cancellationToken = default) { if (blockCalculation) await calculationBlocker.Task.ConfigureAwait(false); From be0564f7326960959970c398203bac1e8ed4280f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 17:03:50 +0900 Subject: [PATCH 24/48] Update `DifficultyIcon` classes to use `IBeatmapInfo` --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 23 +++++++++++-------- .../Drawables/DifficultyIconTooltip.cs | 6 ++--- .../Drawables/GroupedDifficultyIcon.cs | 6 ++--- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 880d70aec2..3a55b9261f 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -37,10 +37,10 @@ namespace osu.Game.Beatmaps.Drawables } [NotNull] - private readonly BeatmapInfo beatmapInfo; + private readonly IBeatmapInfo beatmapInfo; [CanBeNull] - private readonly RulesetInfo ruleset; + private readonly IRulesetInfo ruleset; [CanBeNull] private readonly IReadOnlyList mods; @@ -60,7 +60,7 @@ namespace osu.Game.Beatmaps.Drawables /// The ruleset to show the difficulty with. /// The mods to show the difficulty with. /// Whether to display a tooltip when hovered. - public DifficultyIcon([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo ruleset, [CanBeNull] IReadOnlyList mods, bool shouldShowTooltip = true) + public DifficultyIcon([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo ruleset, [CanBeNull] IReadOnlyList mods, bool shouldShowTooltip = true) : this(beatmapInfo, shouldShowTooltip) { this.ruleset = ruleset ?? beatmapInfo.Ruleset; @@ -73,7 +73,7 @@ namespace osu.Game.Beatmaps.Drawables /// The beatmap to show the difficulty of. /// Whether to display a tooltip when hovered. /// Whether to perform difficulty lookup (including calculation if necessary). - public DifficultyIcon([NotNull] BeatmapInfo beatmapInfo, bool shouldShowTooltip = true, bool performBackgroundDifficultyLookup = true) + public DifficultyIcon([NotNull] IBeatmapInfo beatmapInfo, bool shouldShowTooltip = true, bool performBackgroundDifficultyLookup = true) { this.beatmapInfo = beatmapInfo ?? throw new ArgumentNullException(nameof(beatmapInfo)); this.shouldShowTooltip = shouldShowTooltip; @@ -84,6 +84,9 @@ namespace osu.Game.Beatmaps.Drawables InternalChild = iconContainer = new Container { Size = new Vector2(20f) }; } + [Resolved] + private RulesetStore rulesets { get; set; } + [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -105,7 +108,7 @@ namespace osu.Game.Beatmaps.Drawables Child = background = new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.ForStarDifficulty(beatmapInfo.StarDifficulty) // Default value that will be re-populated once difficulty calculation completes + Colour = colours.ForStarDifficulty(beatmapInfo.StarRating) // Default value that will be re-populated once difficulty calculation completes }, }, new ConstrainedIconContainer @@ -114,14 +117,14 @@ namespace osu.Game.Beatmaps.Drawables Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) - Icon = (ruleset ?? beatmapInfo.Ruleset)?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } + Icon = rulesets.GetRuleset((ruleset ?? beatmapInfo.Ruleset).OnlineID)?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } }, }; if (performBackgroundDifficultyLookup) iconContainer.Add(new DelayedLoadUnloadWrapper(() => new DifficultyRetriever(beatmapInfo, ruleset, mods) { StarDifficulty = { BindTarget = difficultyBindable } }, 0)); else - difficultyBindable.Value = new StarDifficulty(beatmapInfo.StarDifficulty, 0); + difficultyBindable.Value = new StarDifficulty(beatmapInfo.StarRating, 0); difficultyBindable.BindValueChanged(difficulty => background.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars)); } @@ -134,8 +137,8 @@ namespace osu.Game.Beatmaps.Drawables { public readonly Bindable StarDifficulty = new Bindable(); - private readonly BeatmapInfo beatmapInfo; - private readonly RulesetInfo ruleset; + private readonly IBeatmapInfo beatmapInfo; + private readonly IRulesetInfo ruleset; private readonly IReadOnlyList mods; private CancellationTokenSource difficultyCancellation; @@ -143,7 +146,7 @@ namespace osu.Game.Beatmaps.Drawables [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } - public DifficultyRetriever(BeatmapInfo beatmapInfo, RulesetInfo ruleset, IReadOnlyList mods) + public DifficultyRetriever(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset, IReadOnlyList mods) { this.beatmapInfo = beatmapInfo; this.ruleset = ruleset; diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs index d4c9f83a0a..ec4bcbd65f 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs @@ -89,7 +89,7 @@ namespace osu.Game.Beatmaps.Drawables public void SetContent(DifficultyIconTooltipContent content) { - difficultyName.Text = content.BeatmapInfo.Version; + difficultyName.Text = content.BeatmapInfo.DifficultyName; starDifficulty.UnbindAll(); starDifficulty.BindTo(content.Difficulty); @@ -109,10 +109,10 @@ namespace osu.Game.Beatmaps.Drawables internal class DifficultyIconTooltipContent { - public readonly BeatmapInfo BeatmapInfo; + public readonly IBeatmapInfo BeatmapInfo; public readonly IBindable Difficulty; - public DifficultyIconTooltipContent(BeatmapInfo beatmapInfo, IBindable difficulty) + public DifficultyIconTooltipContent(IBeatmapInfo beatmapInfo, IBindable difficulty) { BeatmapInfo = beatmapInfo; Difficulty = difficulty; diff --git a/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs index fcee4c2f1a..799a02579e 100644 --- a/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs @@ -19,8 +19,8 @@ namespace osu.Game.Beatmaps.Drawables /// public class GroupedDifficultyIcon : DifficultyIcon { - public GroupedDifficultyIcon(List beatmaps, RulesetInfo ruleset, Color4 counterColour) - : base(beatmaps.OrderBy(b => b.StarDifficulty).Last(), ruleset, null, false) + public GroupedDifficultyIcon(IEnumerable beatmaps, IRulesetInfo ruleset, Color4 counterColour) + : base(beatmaps.OrderBy(b => b.StarRating).Last(), ruleset, null, false) { AddInternal(new OsuSpriteText { @@ -29,7 +29,7 @@ namespace osu.Game.Beatmaps.Drawables Padding = new MarginPadding { Left = Size.X }, Margin = new MarginPadding { Left = 2, Right = 5 }, Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold), - Text = beatmaps.Count.ToString(), + Text = beatmaps.Count().ToString(), Colour = counterColour, }); } From 7db8bdfb7c01db278088b8aa45c963eef6fad5ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 18:16:53 +0900 Subject: [PATCH 25/48] Fix fallback logic not considering case where ruleset is not available Occurs only in tests. --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 3a55b9261f..6d1984db5b 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -117,7 +117,7 @@ namespace osu.Game.Beatmaps.Drawables Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) - Icon = rulesets.GetRuleset((ruleset ?? beatmapInfo.Ruleset).OnlineID)?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } + Icon = getRulesetIcon() }, }; @@ -129,6 +129,16 @@ namespace osu.Game.Beatmaps.Drawables difficultyBindable.BindValueChanged(difficulty => background.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars)); } + private Drawable getRulesetIcon() + { + int? onlineID = (ruleset ?? beatmapInfo.Ruleset)?.OnlineID; + + if (onlineID >= 0 && rulesets.GetRuleset(onlineID.Value)?.CreateInstance() is Ruleset rulesetInstance) + return rulesetInstance.CreateIcon(); + + return new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }; + } + ITooltip IHasCustomTooltip.GetCustomTooltip() => new DifficultyIconTooltip(); DifficultyIconTooltipContent IHasCustomTooltip.TooltipContent => shouldShowTooltip ? new DifficultyIconTooltipContent(beatmapInfo, difficultyBindable) : null; From 07e3ced315fe457eb97e32879c0c061541e201f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 18:22:23 +0900 Subject: [PATCH 26/48] Fix test scene and remove "impossible" nullable coalesce --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs | 2 ++ osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 66f15670f5..1eda3be039 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -15,6 +15,7 @@ using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Carousel; using osu.Game.Screens.Select.Filter; @@ -868,6 +869,7 @@ namespace osu.Game.Tests.Visual.SongSelect OnlineBeatmapID = id++ * 10, Version = version, StarDifficulty = diff, + Ruleset = new OsuRuleset().RulesetInfo, BaseDifficulty = new BeatmapDifficulty { OverallDifficulty = diff, diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 6d1984db5b..64412675bb 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -131,7 +131,7 @@ namespace osu.Game.Beatmaps.Drawables private Drawable getRulesetIcon() { - int? onlineID = (ruleset ?? beatmapInfo.Ruleset)?.OnlineID; + int? onlineID = (ruleset ?? beatmapInfo.Ruleset).OnlineID; if (onlineID >= 0 && rulesets.GetRuleset(onlineID.Value)?.CreateInstance() is Ruleset rulesetInstance) return rulesetInstance.CreateIcon(); From 04acc7601c2a96906fb6e462391df4502ee61015 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 18:35:15 +0900 Subject: [PATCH 27/48] Fix one more missed case --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 1eda3be039..7a38d213d9 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -685,6 +685,7 @@ namespace osu.Game.Tests.Visual.SongSelect set.Beatmaps.Add(new BeatmapInfo { Version = $"Stars: {i}", + Ruleset = new OsuRuleset().RulesetInfo, StarDifficulty = i, }); } From 1fd05ab78aa7b09c63c8040e19fd92420ad02fc6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 18:56:13 +0900 Subject: [PATCH 28/48] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 552675d706..dec994bcb2 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8ba6e41d53..8052ab5254 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index e55dbb3bfe..d152cb7066 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 4d988340eb02121e4f4645b480d657bbafbbfe4b Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 30 Oct 2021 01:02:53 +0200 Subject: [PATCH 29/48] Add hardware acceleration toggle --- .../Localisation/GraphicsSettingsStrings.cs | 10 +++++ .../Sections/Graphics/VideoSettings.cs | 43 +++++++++++++++++++ .../Settings/Sections/GraphicsSection.cs | 1 + 3 files changed, 54 insertions(+) create mode 100644 osu.Game/Overlays/Settings/Sections/Graphics/VideoSettings.cs diff --git a/osu.Game/Localisation/GraphicsSettingsStrings.cs b/osu.Game/Localisation/GraphicsSettingsStrings.cs index f85cc0f2ae..996a1350eb 100644 --- a/osu.Game/Localisation/GraphicsSettingsStrings.cs +++ b/osu.Game/Localisation/GraphicsSettingsStrings.cs @@ -119,6 +119,16 @@ namespace osu.Game.Localisation /// public static LocalisableString ShowCursorInScreenshots => new TranslatableString(getKey(@"show_cursor_in_screenshots"), @"Show menu cursor in screenshots"); + /// + /// "Video" + /// + public static LocalisableString VideoHeader => new TranslatableString(getKey(@"video_header"), @"Video"); + + /// + /// "Use hardware acceleration" + /// + public static LocalisableString UseHardwareAcceleration => new TranslatableString(getKey(@"use_hardware_acceleration"), @"Use hardware acceleration"); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/VideoSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/VideoSettings.cs new file mode 100644 index 0000000000..921eab63ed --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Graphics/VideoSettings.cs @@ -0,0 +1,43 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Video; +using osu.Framework.Localisation; +using osu.Game.Localisation; + +namespace osu.Game.Overlays.Settings.Sections.Graphics +{ + public class VideoSettings : SettingsSubsection + { + protected override LocalisableString Header => GraphicsSettingsStrings.VideoHeader; + + private Bindable hardwareVideoDecoder; + private SettingsCheckbox hwAccelCheckbox; + + [BackgroundDependencyLoader] + private void load(FrameworkConfigManager config) + { + hardwareVideoDecoder = config.GetBindable(FrameworkSetting.HardwareVideoDecoder); + + Children = new Drawable[] + { + hwAccelCheckbox = new SettingsCheckbox + { + LabelText = GraphicsSettingsStrings.UseHardwareAcceleration, + }, + }; + + hwAccelCheckbox.Current.Default = hardwareVideoDecoder.Default != HardwareVideoDecoder.None; + hwAccelCheckbox.Current.Value = hardwareVideoDecoder.Value != HardwareVideoDecoder.None; + + hwAccelCheckbox.Current.BindValueChanged(val => + { + hardwareVideoDecoder.Value = val.NewValue ? HardwareVideoDecoder.Any : HardwareVideoDecoder.None; + }); + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs index 591848506a..c792098c6d 100644 --- a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs @@ -25,6 +25,7 @@ namespace osu.Game.Overlays.Settings.Sections new LayoutSettings(), new RendererSettings(), new ScreenshotSettings(), + new VideoSettings(), }; } } From e9473db77c61ce3e7b15f7f770634c5665b50d2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 30 Oct 2021 20:37:55 +0900 Subject: [PATCH 30/48] Reorder to have video settings next to renderer Co-authored-by: Salman Ahmed --- osu.Game/Overlays/Settings/Sections/GraphicsSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs index c792098c6d..8cd3b841c2 100644 --- a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs @@ -24,8 +24,8 @@ namespace osu.Game.Overlays.Settings.Sections { new LayoutSettings(), new RendererSettings(), - new ScreenshotSettings(), new VideoSettings(), + new ScreenshotSettings(), }; } } From 6e4f7af8d36dbf59d396fec4d1ccdcbed9a0ea45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 30 Oct 2021 14:15:20 +0200 Subject: [PATCH 31/48] Mark `IHasOnlineID` implementation with region --- osu.Game/Scoring/ScoreInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 8eaec0a477..ed3b27dd22 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -272,6 +272,10 @@ namespace osu.Game.Scoring return ReferenceEquals(this, other); } + #region Implementation of IHasOnlineID + public long OnlineID => OnlineScoreID ?? -1; + + #endregion } } From e9ba1ea1989b3dd03a4703a069df2ff9939b0714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 30 Oct 2021 15:08:45 +0200 Subject: [PATCH 32/48] Mark `IScoreInfo` implementation with region --- osu.Game/Scoring/ScoreInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 0efce4686c..36608e2a74 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -250,8 +250,12 @@ namespace osu.Game.Scoring #endregion + #region Implementation of IScoreInfo + IBeatmapInfo IScoreInfo.Beatmap => BeatmapInfo; IRulesetInfo IScoreInfo.Ruleset => Ruleset; bool IScoreInfo.HasReplay => Files.Any(); + + #endregion } } From b63a90966ba260c5122203c4b06299d2af40aa52 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 31 Oct 2021 23:49:26 +0900 Subject: [PATCH 33/48] Remove misplaced access modifier in interface specification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Scoring/IScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/IScoreInfo.cs b/osu.Game/Scoring/IScoreInfo.cs index 171964206d..77579f23d9 100644 --- a/osu.Game/Scoring/IScoreInfo.cs +++ b/osu.Game/Scoring/IScoreInfo.cs @@ -29,7 +29,7 @@ namespace osu.Game.Scoring IRulesetInfo Ruleset { get; } - public ScoreRank Rank { get; } + ScoreRank Rank { get; } // Mods is currently missing from this interface as the `IMod` class has properties which can't be fulfilled by `APIMod`, // but also doesn't expose `Settings`. We can consider how to implement this in the future if required. From 914e5c0f9d22485ace4e69a42d44e3d020f6d26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 31 Oct 2021 18:58:17 +0100 Subject: [PATCH 34/48] Fix star rating not updating for some mods on details tab in song select `AdvancedStats` was locally assuming that the only changes in mods that are relevant to it are if the mods are `IApplicableToDifficulty`. This is not true, as other mods (such as `IApplicableToRate` mods, or more recently, Flashlight) can also affect star difficulty, which is shown on one of the bars in `AdvancedStats`. --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 562ebad9fe..25ca6ee264 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -94,9 +94,6 @@ namespace osu.Game.Screens.Select.Details modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue); modSettingChangeTracker.SettingChanged += m => { - if (!(m is IApplicableToDifficulty)) - return; - debouncedStatisticsUpdate?.Cancel(); debouncedStatisticsUpdate = Scheduler.AddDelayed(updateStatistics, 100); }; From 02e69626e3eb8ffe01c0037421710dbbd2a6e37a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 13:06:26 +0900 Subject: [PATCH 35/48] Reduce fail animation tint slightly --- osu.Game/Screens/Play/FailAnimation.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/FailAnimation.cs b/osu.Game/Screens/Play/FailAnimation.cs index edfb8186bb..f3676baf80 100644 --- a/osu.Game/Screens/Play/FailAnimation.cs +++ b/osu.Game/Screens/Play/FailAnimation.cs @@ -10,6 +10,7 @@ using ManagedBass.Fx; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -83,7 +84,7 @@ namespace osu.Game.Screens.Play Content, redFlashLayer = new Box { - Colour = Color4.Red, + Colour = Color4.Red.Opacity(0.6f), RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, Depth = float.MinValue, From 269a8df0ecff144c4052c3640767aa98632e2e07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 13:20:37 +0900 Subject: [PATCH 36/48] Fix `HasReplay` not being corrrectly implemented by `APIScoreInfo` --- .../Visual/Gameplay/TestSceneReplayDownloadButton.cs | 2 +- osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index b84ac6c2f1..288da71ba3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -136,7 +136,7 @@ namespace osu.Game.Tests.Visual.Gameplay { OnlineID = 2553163309, OnlineRulesetID = 0, - Replay = replayAvailable, + HasReplay = replayAvailable, User = new User { Id = 39828, diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index 4abb227414..1057819c6d 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -26,13 +26,11 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"user")] public User User { get; set; } - public bool HasReplay { get; set; } - [JsonProperty(@"id")] public long OnlineID { get; set; } [JsonProperty(@"replay")] - public bool Replay { get; set; } + public bool HasReplay { get; set; } [JsonProperty(@"created_at")] public DateTimeOffset Date { get; set; } @@ -97,7 +95,7 @@ namespace osu.Game.Online.API.Requests.Responses Date = Date, PP = PP, RulesetID = OnlineRulesetID, - Hash = Replay ? "online" : string.Empty, // todo: temporary? + Hash = HasReplay ? "online" : string.Empty, // todo: temporary? Rank = Rank, Ruleset = ruleset, Mods = mods, From 708b57348dfb2c1ef369c8a260875a10c7e11cb8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 13:22:16 +0900 Subject: [PATCH 37/48] Change loose api ordering requirement to throw instead --- osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index 1057819c6d..3d82f91fb5 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -50,8 +50,11 @@ namespace osu.Game.Online.API.Requests.Responses set { // in the deserialisation case we need to ferry this data across. - if (Beatmap is APIBeatmap apiBeatmap) - apiBeatmap.BeatmapSet = value; + // the order of properties returned by the API guarantees that the beatmap is populated by this point. + if (!(Beatmap is APIBeatmap apiBeatmap)) + throw new InvalidOperationException("Beatmap set metadata arrived before beatmap metadata in response"); + + apiBeatmap.BeatmapSet = value; } } From 5bfb16efe25986088f7686b681391ae9faec09ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 13:55:34 +0900 Subject: [PATCH 38/48] Ignore case when searching for existing `skin.ini` --- osu.Game/Skinning/Skin.cs | 2 +- osu.Game/Skinning/SkinManager.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index a5639c3301..f8f9c1172d 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -93,7 +93,7 @@ namespace osu.Game.Skinning private Stream getConfigurationStream() { - string path = SkinInfo.Files.SingleOrDefault(f => f.Filename == "skin.ini")?.FileInfo.StoragePath; + string path = SkinInfo.Files.SingleOrDefault(f => f.Filename.Equals(@"skin.ini", StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; if (string.IsNullOrEmpty(path)) return null; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 2187d2d875..a537e8d5c3 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -194,7 +194,7 @@ namespace osu.Game.Skinning string nameLine = $"Name: {item.Name}"; string authorLine = $"Author: {item.Creator}"; - var existingFile = item.Files.SingleOrDefault(f => f.Filename == "skin.ini"); + var existingFile = item.Files.SingleOrDefault(f => f.Filename.Equals(@"skin.ini", StringComparison.OrdinalIgnoreCase)); if (existingFile != null) { From 32817a4c66dda765de5774222e730af71686cf2f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 13:55:45 +0900 Subject: [PATCH 39/48] Add test coverage of failure from incorrect case in `skin.ini` filename --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index b62fb8bd87..a5f588be45 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -29,6 +29,15 @@ namespace osu.Game.Tests.Skins.IO assertCorrectMetadata(import1, "test skin [skin]", "skinner", osu); }); + [Test] + public Task TestSingleImportWeirdIniFileCase() => runSkinTest(async osu => + { + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner", iniFilename: "Skin.InI"), "skin.osk")); + + // When the import filename doesn't match, it should be appended (and update the skin.ini). + assertCorrectMetadata(import1, "test skin [skin]", "skinner", osu); + }); + [Test] public Task TestSingleImportMatchingFilename() => runSkinTest(async osu => { @@ -190,11 +199,11 @@ namespace osu.Game.Tests.Skins.IO return zipStream; } - private MemoryStream createOskWithIni(string name, string author, bool makeUnique = false) + private MemoryStream createOskWithIni(string name, string author, bool makeUnique = false, string iniFilename = "skin.ini") { var zipStream = new MemoryStream(); using var zip = ZipArchive.Create(); - zip.AddEntry("skin.ini", generateSkinIni(name, author, makeUnique)); + zip.AddEntry(iniFilename, generateSkinIni(name, author, makeUnique)); zip.SaveTo(zipStream); return zipStream; } From 6b250955d7ad884d66403b8e956b3327f640fb59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 14:09:17 +0900 Subject: [PATCH 40/48] Add missing verbatim string markers --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 2 +- osu.Game/Skinning/SkinManager.cs | 30 +++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index a5f588be45..c75714032e 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -199,7 +199,7 @@ namespace osu.Game.Tests.Skins.IO return zipStream; } - private MemoryStream createOskWithIni(string name, string author, bool makeUnique = false, string iniFilename = "skin.ini") + private MemoryStream createOskWithIni(string name, string author, bool makeUnique = false, string iniFilename = @"skin.ini") { var zipStream = new MemoryStream(); using var zip = ZipArchive.Create(); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index a537e8d5c3..76d36ae7d9 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -108,7 +108,7 @@ namespace osu.Game.Skinning } } - protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osk"; + protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == @".osk"; /// /// Returns a list of all usable s. Includes the special default skin plus all skins from . @@ -149,9 +149,9 @@ namespace osu.Game.Skinning CurrentSkinInfo.Value = ModelStore.ConsumableItems.Single(i => i.ID == chosen.ID); } - protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name ?? "No name" }; + protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name ?? @"No name" }; - private const string unknown_creator_string = "Unknown"; + private const string unknown_creator_string = @"Unknown"; protected override bool HasCustomHashFunction => true; @@ -164,7 +164,7 @@ namespace osu.Game.Skinning // `Skin` will parse the skin.ini and populate `Skin.Configuration` during construction above. string skinIniSourcedName = instance.Configuration.SkinInfo.Name; string skinIniSourcedCreator = instance.Configuration.SkinInfo.Creator; - string archiveName = item.Name.Replace(".osk", "", StringComparison.OrdinalIgnoreCase); + string archiveName = item.Name.Replace(@".osk", string.Empty, StringComparison.OrdinalIgnoreCase); bool isImport = item.ID == 0; @@ -177,7 +177,7 @@ namespace osu.Game.Skinning // In an ideal world, skin.ini would be the only source of metadata, but a lot of skin creators and users don't update it when making modifications. // In both of these cases, the expectation from the user is that the filename or folder name is displayed somewhere to identify the skin. if (archiveName != item.Name) - item.Name = $"{item.Name} [{archiveName}]"; + item.Name = @$"{item.Name} [{archiveName}]"; } // By this point, the metadata in SkinInfo will be correct. @@ -191,8 +191,8 @@ namespace osu.Game.Skinning private void updateSkinIniMetadata(SkinInfo item) { - string nameLine = $"Name: {item.Name}"; - string authorLine = $"Author: {item.Creator}"; + string nameLine = @$"Name: {item.Name}"; + string authorLine = @$"Author: {item.Creator}"; var existingFile = item.Files.SingleOrDefault(f => f.Filename.Equals(@"skin.ini", StringComparison.OrdinalIgnoreCase)); @@ -210,12 +210,12 @@ namespace osu.Game.Skinning while ((line = sr.ReadLine()) != null) { - if (line.StartsWith("Name:", StringComparison.Ordinal)) + if (line.StartsWith(@"Name:", StringComparison.Ordinal)) { outputLines.Add(nameLine); addedName = true; } - else if (line.StartsWith("Author:", StringComparison.Ordinal)) + else if (line.StartsWith(@"Author:", StringComparison.Ordinal)) { outputLines.Add(authorLine); addedAuthor = true; @@ -229,7 +229,7 @@ namespace osu.Game.Skinning { outputLines.AddRange(new[] { - "[General]", + @"[General]", nameLine, authorLine, }); @@ -252,13 +252,13 @@ namespace osu.Game.Skinning { using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) { - sw.WriteLine("[General]"); + sw.WriteLine(@"[General]"); sw.WriteLine(nameLine); sw.WriteLine(authorLine); - sw.WriteLine("Version: latest"); + sw.WriteLine(@"Version: latest"); } - AddFile(item, stream, "skin.ini"); + AddFile(item, stream, @"skin.ini"); } } } @@ -295,7 +295,7 @@ namespace osu.Game.Skinning // if the user is attempting to save one of the default skin implementations, create a copy first. CurrentSkinInfo.Value = Import(new SkinInfo { - Name = skin.SkinInfo.Name + " (modified)", + Name = skin.SkinInfo.Name + @" (modified)", Creator = skin.SkinInfo.Creator, InstantiationInfo = skin.SkinInfo.InstantiationInfo, }).Result.Value; @@ -312,7 +312,7 @@ namespace osu.Game.Skinning using (var streamContent = new MemoryStream(Encoding.UTF8.GetBytes(json))) { - string filename = $"{drawableInfo.Key}.json"; + string filename = @$"{drawableInfo.Key}.json"; var oldFile = skin.SkinInfo.Files.FirstOrDefault(f => f.Filename == filename); From 10a9266956104120a6b17e36d26f142671a6fc09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 14:33:24 +0900 Subject: [PATCH 41/48] Fix beatmap creator showing twice in tournament map pool panels --- osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 2 +- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 4 ++-- osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index ed107c3a83..f3550f7465 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -76,7 +76,7 @@ namespace osu.Game.Tournament.Components { new TournamentSpriteText { - Text = Beatmap.GetDisplayTitleRomanisable(false), + Text = Beatmap.GetDisplayTitleRomanisable(false, false), Font = OsuFont.Torus.With(weight: FontWeight.Bold), }, new FillFlowContainer diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs index c35370d572..2d69015933 100644 --- a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs @@ -16,9 +16,9 @@ namespace osu.Game.Beatmaps /// /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields. /// - public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapInfo beatmapInfo, bool includeDifficultyName = true) + public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapInfo beatmapInfo, bool includeDifficultyName = true, bool includeCreator = true) { - var metadata = getClosestMetadata(beatmapInfo).GetDisplayTitleRomanisable(); + var metadata = getClosestMetadata(beatmapInfo).GetDisplayTitleRomanisable(includeCreator); if (includeDifficultyName) { diff --git a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs index 732b76e967..fcaad17059 100644 --- a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs @@ -34,9 +34,9 @@ namespace osu.Game.Beatmaps /// /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields. /// - public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapMetadataInfo metadataInfo) + public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapMetadataInfo metadataInfo, bool includeCreator = true) { - string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})"; + string author = !includeCreator || string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})"; string artistUnicode = string.IsNullOrEmpty(metadataInfo.ArtistUnicode) ? metadataInfo.Artist : metadataInfo.ArtistUnicode; string titleUnicode = string.IsNullOrEmpty(metadataInfo.TitleUnicode) ? metadataInfo.Title : metadataInfo.TitleUnicode; From 21e0074d3bf2f23476012ff1a83568cf1148a9fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 14:43:32 +0900 Subject: [PATCH 42/48] Add missing `AddInternal` call for `BeatmapSetHeaderContent`'s download tracker --- osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index 97cd17313e..1bfa7d1c47 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -249,6 +249,7 @@ namespace osu.Game.Overlays.BeatmapSet { downloadTracker = new BeatmapDownloadTracker(setInfo.NewValue); downloadTracker.State.BindValueChanged(_ => updateDownloadButtons()); + AddInternal(downloadTracker); fadeContent.FadeIn(500, Easing.OutQuint); From 123b10f647d379195d4a702c56209fa7eae9a3e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 15:10:32 +0900 Subject: [PATCH 43/48] Fix playlist item download button being clickable when it shouldn't --- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index c946bf12fd..69ab7225ac 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -248,10 +248,7 @@ namespace osu.Game.Screens.OnlinePlay protected virtual IEnumerable CreateButtons() => new Drawable[] { - new PlaylistDownloadButton(Item) - { - Size = new Vector2(50, 30) - }, + new PlaylistDownloadButton(Item), new PlaylistRemoveButton { Size = new Vector2(30, 30), @@ -292,10 +289,14 @@ namespace osu.Game.Screens.OnlinePlay // required for download tracking, as this button hides itself. can probably be removed with a bit of consideration. public override bool IsPresent => true; + private const float width = 50; + public PlaylistDownloadButton(PlaylistItem playlistItem) : base(playlistItem.Beatmap.Value.BeatmapSet) { this.playlistItem = playlistItem; + + Size = new Vector2(width, 30); Alpha = 0; } @@ -316,12 +317,16 @@ namespace osu.Game.Screens.OnlinePlay if (beatmapManager.QueryBeatmap(b => b.MD5Hash == playlistItem.Beatmap.Value.MD5Hash) == null) State.Value = DownloadState.NotDownloaded; else - this.FadeTo(0, 500); + { + this.FadeTo(0, 500) + .ResizeWidthTo(0, 500, Easing.OutQuint); + } break; default: - this.FadeTo(1, 500); + this.ResizeWidthTo(width, 500, Easing.OutQuint) + .FadeTo(1, 500); break; } } From 227963047a681aea23ccadb0307065ab6be0e6b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 15:15:41 +0900 Subject: [PATCH 44/48] Fix hash validity check function returning incorrect results after soft deleting a beatmap The soft-deleted one would potentially match before the newly imported copy, resulting in a false-positive hash mismatch. --- osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 88793c7cfc..6cd735af23 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -109,8 +109,7 @@ namespace osu.Game.Online.Rooms int onlineId = SelectedItem.Value.Beatmap.Value.OnlineID; string checksum = SelectedItem.Value.Beatmap.Value.MD5Hash; - var beatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == onlineId && b.MD5Hash == checksum); - return beatmap?.BeatmapSet.DeletePending == false; + return beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == onlineId && b.MD5Hash == checksum && !b.BeatmapSet.DeletePending) != null; } } } From 94dce3f92acf5ff156afd7cefe0b3ad609e8f2ed Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Nov 2021 15:43:19 +0900 Subject: [PATCH 45/48] Remove whitespace --- osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs b/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs index a0fc549d98..48b7134901 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs @@ -17,7 +17,6 @@ namespace osu.Game.Online.API.Requests.Responses public APIScoreInfo Score; public ScoreInfo CreateScoreInfo(RulesetStore rulesets, BeatmapInfo beatmap = null) - { var score = Score.CreateScoreInfo(rulesets, beatmap); score.Position = Position; From 722e0d50bb9c2602c82c81b182c9dca4e51373ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 15:54:39 +0900 Subject: [PATCH 46/48] Fix `BeatmapInfo` not being correctly populated in `CreateScoreInfo` call --- osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index 3d82f91fb5..1b66a1dcc3 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -92,6 +92,7 @@ namespace osu.Game.Online.API.Requests.Responses { TotalScore = TotalScore, MaxCombo = MaxCombo, + BeatmapInfo = Beatmap.ToBeatmapInfo(rulesets), User = User, Accuracy = Accuracy, OnlineScoreID = OnlineID, From 63c9512ab11a722ffe0849d4de279797ae011c94 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 16:02:00 +0900 Subject: [PATCH 47/48] Update dashboard components to use `APIBeatmapSet` --- .../TestSceneDashboardBeatmapListing.cs | 108 +++++++----------- .../Dashboard/Home/DashboardBeatmapListing.cs | 8 +- .../Dashboard/Home/DashboardBeatmapPanel.cs | 20 ++-- .../Home/DashboardNewBeatmapPanel.cs | 8 +- .../Home/DashboardPopularBeatmapPanel.cs | 8 +- .../Dashboard/Home/DrawableBeatmapList.cs | 12 +- .../Dashboard/Home/DrawableNewBeatmapList.cs | 8 +- .../Home/DrawablePopularBeatmapList.cs | 8 +- 8 files changed, 78 insertions(+), 102 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs index 6727c7560b..06c64a566e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs @@ -56,95 +56,71 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Set width to 300", () => content.ResizeWidthTo(300, 500)); } - private static readonly List new_beatmaps = new List + private static readonly List new_beatmaps = new List { - new BeatmapSetInfo + new APIBeatmapSet { - Metadata = new BeatmapMetadata + Title = "Very Long Title (TV size) [TATOE]", + Artist = "This artist has a really long name how is this possible", + Author = new User { - Title = "Very Long Title (TV size) [TATOE]", - Artist = "This artist has a really long name how is this possible", - Author = new User - { - Username = "author", - Id = 100 - } + Username = "author", + Id = 100 }, - OnlineInfo = new APIBeatmapSet + Covers = new BeatmapSetOnlineCovers { - Covers = new BeatmapSetOnlineCovers - { - Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", - }, - Ranked = DateTimeOffset.Now - } + Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", + }, + Ranked = DateTimeOffset.Now }, - new BeatmapSetInfo + new APIBeatmapSet { - Metadata = new BeatmapMetadata + Title = "Very Long Title (TV size) [TATOE]", + Artist = "This artist has a really long name how is this possible", + Author = new User { - Title = "Very Long Title (TV size) [TATOE]", - Artist = "This artist has a really long name how is this possible", - Author = new User - { - Username = "author", - Id = 100 - } + Username = "author", + Id = 100 }, - OnlineInfo = new APIBeatmapSet + Covers = new BeatmapSetOnlineCovers { - Covers = new BeatmapSetOnlineCovers - { - Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", - }, - Ranked = DateTimeOffset.MinValue - } + Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", + }, + Ranked = DateTimeOffset.Now } }; - private static readonly List popular_beatmaps = new List + private static readonly List popular_beatmaps = new List { - new BeatmapSetInfo + new APIBeatmapSet { - Metadata = new BeatmapMetadata + Title = "Very Long Title (TV size) [TATOE]", + Artist = "This artist has a really long name how is this possible", + Author = new User { - Title = "Title", - Artist = "Artist", - Author = new User - { - Username = "author", - Id = 100 - } + Username = "author", + Id = 100 }, - OnlineInfo = new APIBeatmapSet + Covers = new BeatmapSetOnlineCovers { - Covers = new BeatmapSetOnlineCovers - { - Cover = "https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg?1595295586", - }, - FavouriteCount = 100 - } + Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", + }, + Ranked = DateTimeOffset.Now }, - new BeatmapSetInfo + new APIBeatmapSet { - Metadata = new BeatmapMetadata + Title = "Very Long Title (TV size) [TATOE]", + Artist = "This artist has a really long name how is this possible", + Author = new User { - Title = "Title 2", - Artist = "Artist 2", - Author = new User - { - Username = "someone", - Id = 100 - } + Username = "author", + Id = 100 }, - OnlineInfo = new APIBeatmapSet + Covers = new BeatmapSetOnlineCovers { - Covers = new BeatmapSetOnlineCovers - { - Cover = "https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg?1595295586", - }, - FavouriteCount = 10 - } + Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", + }, + Ranked = DateTimeOffset.Now } }; } diff --git a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapListing.cs b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapListing.cs index 4d96825353..c781aa0cfb 100644 --- a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapListing.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapListing.cs @@ -5,17 +5,17 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; using osuTK; namespace osu.Game.Overlays.Dashboard.Home { public class DashboardBeatmapListing : CompositeDrawable { - private readonly List newBeatmaps; - private readonly List popularBeatmaps; + private readonly List newBeatmaps; + private readonly List popularBeatmaps; - public DashboardBeatmapListing(List newBeatmaps, List popularBeatmaps) + public DashboardBeatmapListing(List newBeatmaps, List popularBeatmaps) { this.newBeatmaps = newBeatmaps; this.popularBeatmaps = popularBeatmaps; diff --git a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs index 50186def37..9276e6ce80 100644 --- a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs @@ -7,11 +7,11 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osuTK; namespace osu.Game.Overlays.Dashboard.Home @@ -24,14 +24,14 @@ namespace osu.Game.Overlays.Dashboard.Home [Resolved(canBeNull: true)] private BeatmapSetOverlay beatmapOverlay { get; set; } - protected readonly BeatmapSetInfo SetInfo; + protected readonly APIBeatmapSet BeatmapSet; private Box hoverBackground; private SpriteIcon chevron; - protected DashboardBeatmapPanel(BeatmapSetInfo setInfo) + protected DashboardBeatmapPanel(APIBeatmapSet beatmapSet) { - SetInfo = setInfo; + BeatmapSet = beatmapSet; } [BackgroundDependencyLoader] @@ -82,7 +82,7 @@ namespace osu.Game.Overlays.Dashboard.Home RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - OnlineInfo = SetInfo.OnlineInfo + OnlineInfo = BeatmapSet } }, new Container @@ -103,14 +103,14 @@ namespace osu.Game.Overlays.Dashboard.Home RelativeSizeAxes = Axes.X, Truncate = true, Font = OsuFont.GetFont(weight: FontWeight.Regular), - Text = SetInfo.Metadata.Title + Text = BeatmapSet.Title }, new OsuSpriteText { RelativeSizeAxes = Axes.X, Truncate = true, Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), - Text = SetInfo.Metadata.Artist + Text = BeatmapSet.Artist }, new LinkFlowContainer(f => f.Font = OsuFont.GetFont(size: 10, weight: FontWeight.Regular)) { @@ -121,7 +121,7 @@ namespace osu.Game.Overlays.Dashboard.Home }.With(c => { c.AddText("by"); - c.AddUserLink(SetInfo.Metadata.Author); + c.AddUserLink(BeatmapSet.Author); c.AddArbitraryDrawable(CreateInfo()); }) } @@ -143,8 +143,8 @@ namespace osu.Game.Overlays.Dashboard.Home Action = () => { - if (SetInfo.OnlineBeatmapSetID.HasValue) - beatmapOverlay?.FetchAndShowBeatmapSet(SetInfo.OnlineBeatmapSetID.Value); + if (BeatmapSet.OnlineID > 0) + beatmapOverlay?.FetchAndShowBeatmapSet(BeatmapSet.OnlineID); }; } diff --git a/osu.Game/Overlays/Dashboard/Home/DashboardNewBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Home/DashboardNewBeatmapPanel.cs index b212eaf20a..249b355be3 100644 --- a/osu.Game/Overlays/Dashboard/Home/DashboardNewBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardNewBeatmapPanel.cs @@ -3,19 +3,19 @@ using System; using osu.Framework.Graphics; -using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Dashboard.Home { public class DashboardNewBeatmapPanel : DashboardBeatmapPanel { - public DashboardNewBeatmapPanel(BeatmapSetInfo setInfo) - : base(setInfo) + public DashboardNewBeatmapPanel(APIBeatmapSet beatmapSet) + : base(beatmapSet) { } - protected override Drawable CreateInfo() => new DrawableDate(SetInfo.OnlineInfo.Ranked ?? DateTimeOffset.Now, 10, false) + protected override Drawable CreateInfo() => new DrawableDate(BeatmapSet.Ranked ?? DateTimeOffset.Now, 10, false) { Colour = ColourProvider.Foreground1 }; diff --git a/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs index e9066c0657..4e50cce890 100644 --- a/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs @@ -4,17 +4,17 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osuTK; namespace osu.Game.Overlays.Dashboard.Home { public class DashboardPopularBeatmapPanel : DashboardBeatmapPanel { - public DashboardPopularBeatmapPanel(BeatmapSetInfo setInfo) - : base(setInfo) + public DashboardPopularBeatmapPanel(APIBeatmapSet beatmapSet) + : base(beatmapSet) { } @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Dashboard.Home new OsuSpriteText { Font = OsuFont.GetFont(size: 10, weight: FontWeight.Regular), - Text = SetInfo.OnlineInfo.FavouriteCount.ToString() + Text = BeatmapSet.FavouriteCount.ToString() } } }; diff --git a/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs index f6535b7db3..c73cc828e2 100644 --- a/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs @@ -6,20 +6,20 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osuTK; namespace osu.Game.Overlays.Dashboard.Home { public abstract class DrawableBeatmapList : CompositeDrawable { - private readonly List beatmaps; + private readonly List beatmapSets; - protected DrawableBeatmapList(List beatmaps) + protected DrawableBeatmapList(List beatmapSets) { - this.beatmaps = beatmaps; + this.beatmapSets = beatmapSets; } [BackgroundDependencyLoader] @@ -46,11 +46,11 @@ namespace osu.Game.Overlays.Dashboard.Home } }; - flow.AddRange(beatmaps.Select(CreateBeatmapPanel)); + flow.AddRange(beatmapSets.Select(CreateBeatmapPanel)); } protected abstract string Title { get; } - protected abstract DashboardBeatmapPanel CreateBeatmapPanel(BeatmapSetInfo setInfo); + protected abstract DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet); } } diff --git a/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs index 75e8ca336d..714e07a7ed 100644 --- a/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs @@ -2,18 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Dashboard.Home { public class DrawableNewBeatmapList : DrawableBeatmapList { - public DrawableNewBeatmapList(List beatmaps) - : base(beatmaps) + public DrawableNewBeatmapList(List beatmapSets) + : base(beatmapSets) { } - protected override DashboardBeatmapPanel CreateBeatmapPanel(BeatmapSetInfo setInfo) => new DashboardNewBeatmapPanel(setInfo); + protected override DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet) => new DashboardNewBeatmapPanel(beatmapSet); protected override string Title => "New Ranked Beatmaps"; } diff --git a/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs index 90bd00008c..48b100b04e 100644 --- a/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs @@ -2,18 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Dashboard.Home { public class DrawablePopularBeatmapList : DrawableBeatmapList { - public DrawablePopularBeatmapList(List beatmaps) - : base(beatmaps) + public DrawablePopularBeatmapList(List beatmapSets) + : base(beatmapSets) { } - protected override DashboardBeatmapPanel CreateBeatmapPanel(BeatmapSetInfo setInfo) => new DashboardPopularBeatmapPanel(setInfo); + protected override DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet) => new DashboardPopularBeatmapPanel(beatmapSet); protected override string Title => "Popular Beatmaps"; } From 6a44cf3ff1181311d6acffd769793838505a3f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 1 Nov 2021 09:14:02 +0100 Subject: [PATCH 48/48] Add missing beatmap spec in test scene --- osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index 288da71ba3..311c3ddc03 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -13,6 +13,7 @@ using osu.Framework.Bindables; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Ranking; using osuTK.Input; @@ -136,6 +137,7 @@ namespace osu.Game.Tests.Visual.Gameplay { OnlineID = 2553163309, OnlineRulesetID = 0, + Beatmap = CreateAPIBeatmapSet(new OsuRuleset().RulesetInfo).Beatmaps.First(), HasReplay = replayAvailable, User = new User {