From f0caa1006631c08c9f15e665e865b3108543a64c Mon Sep 17 00:00:00 2001 From: Tollii Date: Fri, 5 Nov 2021 23:53:48 +0100 Subject: [PATCH 01/90] Add support for a provided cancellation token for GetPlayableBeatmap() --- osu.Game/Beatmaps/IWorkingBeatmap.cs | 4 +++- osu.Game/Beatmaps/WorkingBeatmap.cs | 7 ++++--- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index a916b37b85..6f0f408060 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Rulesets; @@ -57,9 +58,10 @@ namespace osu.Game.Beatmaps /// The to create a playable for. /// The s to apply to the . /// The maximum length in milliseconds to wait for load to complete. Defaults to 10,000ms. + /// Externally provided cancellation token. /// The converted . /// If could not be converted to . - IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null); + IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default); /// /// Load a new audio track instance for this beatmap. This should be called once before accessing . diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index d2c0f7de0f..affc19c700 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -81,7 +81,7 @@ namespace osu.Game.Beatmaps /// The applicable . protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap); - public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null) + public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default) { using (var cancellationSource = createCancellationTokenSource(timeout)) { @@ -105,7 +105,7 @@ namespace osu.Game.Beatmaps } // Convert - IBeatmap converted = converter.Convert(cancellationSource.Token); + IBeatmap converted = converter.Convert(cancellationToken != CancellationToken.None ? cancellationToken : cancellationSource.Token); // Apply conversion mods to the result foreach (var mod in mods.OfType()) @@ -143,7 +143,7 @@ namespace osu.Game.Beatmaps if (cancellationSource.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); - obj.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty, cancellationSource.Token); + obj.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty, cancellationToken != CancellationToken.None ? cancellationToken : cancellationSource.Token); } } catch (OperationCanceledException) @@ -167,6 +167,7 @@ namespace osu.Game.Beatmaps foreach (var mod in mods.OfType()) { cancellationSource.Token.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); mod.ApplyToBeatmap(converted); } diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index ef289c2a20..922d1e6bbe 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -210,7 +210,7 @@ namespace osu.Game.Screens.Play.HUD this.gameplayBeatmap = gameplayBeatmap; } - public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null) + public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default) => gameplayBeatmap; protected override IBeatmap GetBeatmap() => gameplayBeatmap; From eb7d04bc77da85ea95461828e0988bd1668d2e58 Mon Sep 17 00:00:00 2001 From: Tollii Date: Sat, 6 Nov 2021 00:19:48 +0100 Subject: [PATCH 02/90] Add cancellation token support for beatmap difficulty calculation. --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 9 +++++---- osu.Game/Beatmaps/BeatmapModelManager.cs | 2 +- .../Difficulty/DifficultyCalculator.cs | 18 +++++++++++------- osu.Game/Stores/BeatmapImporter.cs | 2 +- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 035f438b89..bbe5ecb0df 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -147,13 +147,13 @@ namespace osu.Game.Beatmaps if (CheckExists(lookup, out var existing)) return existing; - return computeDifficulty(lookup); + return computeDifficulty(lookup, token); }, token, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); } public Task> GetTimedDifficultyAttributesAsync(WorkingBeatmap beatmap, Ruleset ruleset, Mod[] mods, CancellationToken token = default) { - return Task.Factory.StartNew(() => ruleset.CreateDifficultyCalculator(beatmap).CalculateTimed(mods), + return Task.Factory.StartNew(() => ruleset.CreateDifficultyCalculator(beatmap).CalculateTimed(mods, token), token, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); @@ -270,8 +270,9 @@ namespace osu.Game.Beatmaps /// Computes the difficulty defined by a key, and stores it to the timed cache. /// /// The that defines the computation parameters. + /// The cancellation token. /// The . - private StarDifficulty computeDifficulty(in DifficultyCacheLookup key) + private StarDifficulty computeDifficulty(in DifficultyCacheLookup key, CancellationToken cancellationToken = default) { // In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset. var beatmapInfo = key.BeatmapInfo; @@ -283,7 +284,7 @@ namespace osu.Game.Beatmaps Debug.Assert(ruleset != null); var calculator = ruleset.CreateDifficultyCalculator(beatmapManager.GetWorkingBeatmap(key.BeatmapInfo)); - var attributes = calculator.Calculate(key.OrderedMods); + var attributes = calculator.Calculate(key.OrderedMods, cancellationToken); return new StarDifficulty(attributes); } diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs index f148d05aca..2166ae1dfc 100644 --- a/osu.Game/Beatmaps/BeatmapModelManager.cs +++ b/osu.Game/Beatmaps/BeatmapModelManager.cs @@ -408,7 +408,7 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo.Ruleset = ruleset; // TODO: this should be done in a better place once we actually need to dynamically update it. - beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0; + beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate(null).StarRating ?? 0; beatmap.BeatmapInfo.Length = calculateLength(beatmap); beatmap.BeatmapInfo.BPM = 60000 / beatmap.GetMostCommonBeatLength(); diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 5b4284dc2f..4fc984b491 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using osu.Framework.Audio.Track; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Beatmaps; @@ -39,10 +40,11 @@ namespace osu.Game.Rulesets.Difficulty /// Calculates the difficulty of the beatmap using a specific mod combination. /// /// The mods that should be applied to the beatmap. + /// The cancellation token. /// A structure describing the difficulty of the beatmap. - public DifficultyAttributes Calculate(params Mod[] mods) + public DifficultyAttributes Calculate(IEnumerable mods, CancellationToken cancellationToken = default) { - preProcess(mods); + preProcess(mods, cancellationToken); var skills = CreateSkills(Beatmap, playableMods, clockRate); @@ -62,10 +64,11 @@ namespace osu.Game.Rulesets.Difficulty /// Calculates the difficulty of the beatmap and returns a set of representing the difficulty at every relevant time value in the beatmap. /// /// The mods that should be applied to the beatmap. + /// The cancellation token. /// The set of . - public List CalculateTimed(params Mod[] mods) + public List CalculateTimed(IEnumerable mods, CancellationToken cancellationToken = default) { - preProcess(mods); + preProcess(mods, cancellationToken); var attribs = new List(); @@ -99,7 +102,7 @@ namespace osu.Game.Rulesets.Difficulty if (combination is MultiMod multi) yield return Calculate(multi.Mods); else - yield return Calculate(combination); + yield return Calculate(new[] { combination }); } } @@ -112,11 +115,12 @@ namespace osu.Game.Rulesets.Difficulty /// Performs required tasks before every calculation. /// /// The original list of s. - private void preProcess(Mod[] mods) + /// The cancellation cancellationToken. + private void preProcess(IEnumerable mods, CancellationToken cancellationToken = default) { playableMods = mods.Select(m => m.DeepClone()).ToArray(); - Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, playableMods); + Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, playableMods, cancellationToken: cancellationToken); var track = new TrackVirtual(10000); playableMods.OfType().ForEach(m => m.ApplyToTrack(track)); diff --git a/osu.Game/Stores/BeatmapImporter.cs b/osu.Game/Stores/BeatmapImporter.cs index 787b1ddd60..7916583ce1 100644 --- a/osu.Game/Stores/BeatmapImporter.cs +++ b/osu.Game/Stores/BeatmapImporter.cs @@ -284,7 +284,7 @@ namespace osu.Game.Stores decoded.BeatmapInfo.Ruleset = rulesetInstance.RulesetInfo; // TODO: this should be done in a better place once we actually need to dynamically update it. - beatmap.StarRating = rulesetInstance.CreateDifficultyCalculator(new DummyConversionBeatmap(decoded)).Calculate().StarRating; + beatmap.StarRating = rulesetInstance.CreateDifficultyCalculator(new DummyConversionBeatmap(decoded)).Calculate(null).StarRating; beatmap.Length = calculateLength(decoded); beatmap.BPM = 60000 / decoded.GetMostCommonBeatLength(); } From cf0b757b16f8369efd82968b6357aafe120e8549 Mon Sep 17 00:00:00 2001 From: Tollii Date: Sat, 6 Nov 2021 16:03:53 +0100 Subject: [PATCH 03/90] Fix PR comments. Nitpick, more cancellation token checks. --- osu.Game/Beatmaps/BeatmapModelManager.cs | 2 +- osu.Game/Beatmaps/IWorkingBeatmap.cs | 4 ++-- osu.Game/Beatmaps/WorkingBeatmap.cs | 24 +++++++++---------- .../Difficulty/DifficultyCalculator.cs | 18 ++++++++++---- .../Play/HUD/PerformancePointsCounter.cs | 2 +- osu.Game/Stores/BeatmapImporter.cs | 2 +- 6 files changed, 30 insertions(+), 22 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs index 2166ae1dfc..f148d05aca 100644 --- a/osu.Game/Beatmaps/BeatmapModelManager.cs +++ b/osu.Game/Beatmaps/BeatmapModelManager.cs @@ -408,7 +408,7 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo.Ruleset = ruleset; // TODO: this should be done in a better place once we actually need to dynamically update it. - beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate(null).StarRating ?? 0; + beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0; beatmap.BeatmapInfo.Length = calculateLength(beatmap); beatmap.BeatmapInfo.BPM = 60000 / beatmap.GetMostCommonBeatLength(); diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index 6f0f408060..3bbfb2a889 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -58,10 +58,10 @@ namespace osu.Game.Beatmaps /// The to create a playable for. /// The s to apply to the . /// The maximum length in milliseconds to wait for load to complete. Defaults to 10,000ms. - /// Externally provided cancellation token. + /// Cancellation token that cancels the beatmap loading process. /// The converted . /// If could not be converted to . - IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default); + IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken timeoutToken = default); /// /// Load a new audio track instance for this beatmap. This should be called once before accessing . diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index affc19c700..e0c2ae7873 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -81,9 +81,10 @@ namespace osu.Game.Beatmaps /// The applicable . protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap); - public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default) + public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken timeoutToken = default) { - using (var cancellationSource = createCancellationTokenSource(timeout)) + using (var timeoutSource = createTimeoutTokenSource(timeout)) + using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutToken, timeoutSource.Token)) { mods ??= Array.Empty(); @@ -98,19 +99,19 @@ namespace osu.Game.Beatmaps // Apply conversion mods foreach (var mod in mods.OfType()) { - if (cancellationSource.IsCancellationRequested) + if (linkedTokenSource.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); mod.ApplyToBeatmapConverter(converter); } // Convert - IBeatmap converted = converter.Convert(cancellationToken != CancellationToken.None ? cancellationToken : cancellationSource.Token); + IBeatmap converted = converter.Convert(linkedTokenSource.Token); // Apply conversion mods to the result foreach (var mod in mods.OfType()) { - if (cancellationSource.IsCancellationRequested) + if (linkedTokenSource.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); mod.ApplyToBeatmap(converted); @@ -121,7 +122,7 @@ namespace osu.Game.Beatmaps { foreach (var mod in mods.OfType()) { - if (cancellationSource.IsCancellationRequested) + if (linkedTokenSource.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); mod.ApplyToDifficulty(converted.Difficulty); @@ -140,10 +141,10 @@ namespace osu.Game.Beatmaps { foreach (var obj in converted.HitObjects) { - if (cancellationSource.IsCancellationRequested) + if (linkedTokenSource.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); - obj.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty, cancellationToken != CancellationToken.None ? cancellationToken : cancellationSource.Token); + obj.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty, linkedTokenSource.Token); } } catch (OperationCanceledException) @@ -155,7 +156,7 @@ namespace osu.Game.Beatmaps { foreach (var obj in converted.HitObjects) { - if (cancellationSource.IsCancellationRequested) + if (linkedTokenSource.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); mod.ApplyToHitObject(obj); @@ -166,8 +167,7 @@ namespace osu.Game.Beatmaps foreach (var mod in mods.OfType()) { - cancellationSource.Token.ThrowIfCancellationRequested(); - cancellationToken.ThrowIfCancellationRequested(); + linkedTokenSource.Token.ThrowIfCancellationRequested(); mod.ApplyToBeatmap(converted); } @@ -200,7 +200,7 @@ namespace osu.Game.Beatmaps } } - private CancellationTokenSource createCancellationTokenSource(TimeSpan? timeout) + private CancellationTokenSource createTimeoutTokenSource(TimeSpan? timeout) { if (Debugger.IsAttached) // ignore timeout when debugger is attached (may be breakpointing / debugging). diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 4fc984b491..ffad6131be 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -42,8 +42,9 @@ namespace osu.Game.Rulesets.Difficulty /// The mods that should be applied to the beatmap. /// The cancellation token. /// A structure describing the difficulty of the beatmap. - public DifficultyAttributes Calculate(IEnumerable mods, CancellationToken cancellationToken = default) + public DifficultyAttributes Calculate(IEnumerable mods = null, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); preProcess(mods, cancellationToken); var skills = CreateSkills(Beatmap, playableMods, clockRate); @@ -54,7 +55,10 @@ namespace osu.Game.Rulesets.Difficulty foreach (var hitObject in getDifficultyHitObjects()) { foreach (var skill in skills) + { + cancellationToken.ThrowIfCancellationRequested(); skill.ProcessInternal(hitObject); + } } return CreateDifficultyAttributes(Beatmap, playableMods, skills, clockRate); @@ -68,6 +72,7 @@ namespace osu.Game.Rulesets.Difficulty /// The set of . public List CalculateTimed(IEnumerable mods, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); preProcess(mods, cancellationToken); var attribs = new List(); @@ -83,7 +88,10 @@ namespace osu.Game.Rulesets.Difficulty progressiveBeatmap.HitObjects.Add(hitObject.BaseObject); foreach (var skill in skills) + { + cancellationToken.ThrowIfCancellationRequested(); skill.ProcessInternal(hitObject); + } attribs.Add(new TimedDifficultyAttributes(hitObject.EndTime * clockRate, CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate))); } @@ -102,7 +110,7 @@ namespace osu.Game.Rulesets.Difficulty if (combination is MultiMod multi) yield return Calculate(multi.Mods); else - yield return Calculate(new[] { combination }); + yield return Calculate(combination.Yield()); } } @@ -115,12 +123,12 @@ namespace osu.Game.Rulesets.Difficulty /// Performs required tasks before every calculation. /// /// The original list of s. - /// The cancellation cancellationToken. - private void preProcess(IEnumerable mods, CancellationToken cancellationToken = default) + /// The cancellation timeoutToken. + private void preProcess(IEnumerable mods = null, CancellationToken cancellationToken = default) { playableMods = mods.Select(m => m.DeepClone()).ToArray(); - Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, playableMods, cancellationToken: cancellationToken); + Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, playableMods, timeoutToken: cancellationToken); var track = new TrackVirtual(10000); playableMods.OfType().ForEach(m => m.ApplyToTrack(track)); diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 922d1e6bbe..bd06e71304 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -210,7 +210,7 @@ namespace osu.Game.Screens.Play.HUD this.gameplayBeatmap = gameplayBeatmap; } - public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default) + public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken timeoutToken = default) => gameplayBeatmap; protected override IBeatmap GetBeatmap() => gameplayBeatmap; diff --git a/osu.Game/Stores/BeatmapImporter.cs b/osu.Game/Stores/BeatmapImporter.cs index 7916583ce1..787b1ddd60 100644 --- a/osu.Game/Stores/BeatmapImporter.cs +++ b/osu.Game/Stores/BeatmapImporter.cs @@ -284,7 +284,7 @@ namespace osu.Game.Stores decoded.BeatmapInfo.Ruleset = rulesetInstance.RulesetInfo; // TODO: this should be done in a better place once we actually need to dynamically update it. - beatmap.StarRating = rulesetInstance.CreateDifficultyCalculator(new DummyConversionBeatmap(decoded)).Calculate(null).StarRating; + beatmap.StarRating = rulesetInstance.CreateDifficultyCalculator(new DummyConversionBeatmap(decoded)).Calculate().StarRating; beatmap.Length = calculateLength(decoded); beatmap.BPM = 60000 / decoded.GetMostCommonBeatLength(); } From d5f5d74a89e880d3510867bab46698455504f7cd Mon Sep 17 00:00:00 2001 From: Tollii Date: Sun, 7 Nov 2021 13:38:00 +0100 Subject: [PATCH 04/90] Rename CancellationToken variable --- osu.Game/Beatmaps/IWorkingBeatmap.cs | 4 ++-- osu.Game/Beatmaps/WorkingBeatmap.cs | 4 ++-- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index 3bbfb2a889..1e46e265ac 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -58,10 +58,10 @@ namespace osu.Game.Beatmaps /// The to create a playable for. /// The s to apply to the . /// The maximum length in milliseconds to wait for load to complete. Defaults to 10,000ms. - /// Cancellation token that cancels the beatmap loading process. + /// Cancellation token that cancels the beatmap loading process. /// The converted . /// If could not be converted to . - IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken timeoutToken = default); + IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken token = default); /// /// Load a new audio track instance for this beatmap. This should be called once before accessing . diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index e0c2ae7873..a93c50ce74 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -81,10 +81,10 @@ namespace osu.Game.Beatmaps /// The applicable . protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap); - public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken timeoutToken = default) + public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken token = default) { using (var timeoutSource = createTimeoutTokenSource(timeout)) - using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutToken, timeoutSource.Token)) + using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutSource.Token)) { mods ??= Array.Empty(); diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index ffad6131be..6630530b65 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -123,12 +123,12 @@ namespace osu.Game.Rulesets.Difficulty /// Performs required tasks before every calculation. /// /// The original list of s. - /// The cancellation timeoutToken. + /// The cancellation token. private void preProcess(IEnumerable mods = null, CancellationToken cancellationToken = default) { playableMods = mods.Select(m => m.DeepClone()).ToArray(); - Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, playableMods, timeoutToken: cancellationToken); + Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, playableMods, token: cancellationToken); var track = new TrackVirtual(10000); playableMods.OfType().ForEach(m => m.ApplyToTrack(track)); From 5b5e3dc4a2ee03256aac220341ad419d20df9e87 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Nov 2021 14:33:15 +0900 Subject: [PATCH 05/90] Revert incorrect mod nullable parameter specification --- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 6630530b65..a6f3a75302 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Difficulty /// /// The original list of s. /// The cancellation token. - private void preProcess(IEnumerable mods = null, CancellationToken cancellationToken = default) + private void preProcess(IEnumerable mods, CancellationToken cancellationToken = default) { playableMods = mods.Select(m => m.DeepClone()).ToArray(); From 97345ac9e6123c8fc3e990bc5fdc244c7ba14206 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Nov 2021 14:33:32 +0900 Subject: [PATCH 06/90] Remove unnecessary `TimeSpan timeout` parameter (`CancellationToken` can now be used) --- osu.Game/Beatmaps/IWorkingBeatmap.cs | 4 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 170 +++++++++--------- .../Play/HUD/PerformancePointsCounter.cs | 2 +- 3 files changed, 85 insertions(+), 91 deletions(-) diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index 1e46e265ac..ed100d1876 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -1,7 +1,6 @@ // 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 System.Collections.Generic; using System.IO; using System.Threading; @@ -57,11 +56,10 @@ namespace osu.Game.Beatmaps /// /// The to create a playable for. /// The s to apply to the . - /// The maximum length in milliseconds to wait for load to complete. Defaults to 10,000ms. /// Cancellation token that cancels the beatmap loading process. /// The converted . /// If could not be converted to . - IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken token = default); + IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken token = default); /// /// Load a new audio track instance for this beatmap. This should be called once before accessing . diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index a93c50ce74..0527acca8f 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -81,98 +81,94 @@ namespace osu.Game.Beatmaps /// The applicable . protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap); - public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken token = default) + public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken token = default) { - using (var timeoutSource = createTimeoutTokenSource(timeout)) - using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutSource.Token)) + mods ??= Array.Empty(); + + var rulesetInstance = ruleset.CreateInstance(); + + IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance); + + // Check if the beatmap can be converted + if (Beatmap.HitObjects.Count > 0 && !converter.CanConvert()) + throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter})."); + + // Apply conversion mods + foreach (var mod in mods.OfType()) { - mods ??= Array.Empty(); - - var rulesetInstance = ruleset.CreateInstance(); - - IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance); - - // Check if the beatmap can be converted - if (Beatmap.HitObjects.Count > 0 && !converter.CanConvert()) - throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter})."); - - // Apply conversion mods - foreach (var mod in mods.OfType()) - { - if (linkedTokenSource.IsCancellationRequested) - throw new BeatmapLoadTimeoutException(BeatmapInfo); - - mod.ApplyToBeatmapConverter(converter); - } - - // Convert - IBeatmap converted = converter.Convert(linkedTokenSource.Token); - - // Apply conversion mods to the result - foreach (var mod in mods.OfType()) - { - if (linkedTokenSource.IsCancellationRequested) - throw new BeatmapLoadTimeoutException(BeatmapInfo); - - mod.ApplyToBeatmap(converted); - } - - // Apply difficulty mods - if (mods.Any(m => m is IApplicableToDifficulty)) - { - foreach (var mod in mods.OfType()) - { - if (linkedTokenSource.IsCancellationRequested) - throw new BeatmapLoadTimeoutException(BeatmapInfo); - - mod.ApplyToDifficulty(converted.Difficulty); - } - } - - IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted); - - foreach (var mod in mods.OfType()) - mod.ApplyToBeatmapProcessor(processor); - - processor?.PreProcess(); - - // Compute default values for hitobjects, including creating nested hitobjects in-case they're needed - try - { - foreach (var obj in converted.HitObjects) - { - if (linkedTokenSource.IsCancellationRequested) - throw new BeatmapLoadTimeoutException(BeatmapInfo); - - obj.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty, linkedTokenSource.Token); - } - } - catch (OperationCanceledException) - { + if (token.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); - } - foreach (var mod in mods.OfType()) - { - foreach (var obj in converted.HitObjects) - { - if (linkedTokenSource.IsCancellationRequested) - throw new BeatmapLoadTimeoutException(BeatmapInfo); - - mod.ApplyToHitObject(obj); - } - } - - processor?.PostProcess(); - - foreach (var mod in mods.OfType()) - { - linkedTokenSource.Token.ThrowIfCancellationRequested(); - mod.ApplyToBeatmap(converted); - } - - return converted; + mod.ApplyToBeatmapConverter(converter); } + + // Convert + IBeatmap converted = converter.Convert(token); + + // Apply conversion mods to the result + foreach (var mod in mods.OfType()) + { + if (token.IsCancellationRequested) + throw new BeatmapLoadTimeoutException(BeatmapInfo); + + mod.ApplyToBeatmap(converted); + } + + // Apply difficulty mods + if (mods.Any(m => m is IApplicableToDifficulty)) + { + foreach (var mod in mods.OfType()) + { + if (token.IsCancellationRequested) + throw new BeatmapLoadTimeoutException(BeatmapInfo); + + mod.ApplyToDifficulty(converted.Difficulty); + } + } + + IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted); + + foreach (var mod in mods.OfType()) + mod.ApplyToBeatmapProcessor(processor); + + processor?.PreProcess(); + + // Compute default values for hitobjects, including creating nested hitobjects in-case they're needed + try + { + foreach (var obj in converted.HitObjects) + { + if (token.IsCancellationRequested) + throw new BeatmapLoadTimeoutException(BeatmapInfo); + + obj.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty, token); + } + } + catch (OperationCanceledException) + { + throw new BeatmapLoadTimeoutException(BeatmapInfo); + } + + foreach (var mod in mods.OfType()) + { + foreach (var obj in converted.HitObjects) + { + if (token.IsCancellationRequested) + throw new BeatmapLoadTimeoutException(BeatmapInfo); + + mod.ApplyToHitObject(obj); + } + } + + processor?.PostProcess(); + + foreach (var mod in mods.OfType()) + { + token.ThrowIfCancellationRequested(); + mod.ApplyToBeatmap(converted); + } + + return converted; } private CancellationTokenSource loadCancellation = new CancellationTokenSource(); diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index bd06e71304..dea1964faa 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -210,7 +210,7 @@ namespace osu.Game.Screens.Play.HUD this.gameplayBeatmap = gameplayBeatmap; } - public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null, CancellationToken timeoutToken = default) + public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken timeoutToken = default) => gameplayBeatmap; protected override IBeatmap GetBeatmap() => gameplayBeatmap; From c58f21a11531860719268823ba02c2436668ccde Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Nov 2021 14:43:46 +0900 Subject: [PATCH 07/90] Handle mods with overloaded method signature instead --- .../Difficulty/DifficultyCalculator.cs | 25 ++++++++++++++++--- .../Difficulty/TimedDifficultyAttributes.cs | 2 +- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index a6f3a75302..6748158d8f 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; +using JetBrains.Annotations; using osu.Framework.Audio.Track; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Beatmaps; @@ -36,13 +37,21 @@ namespace osu.Game.Rulesets.Difficulty this.beatmap = beatmap; } + /// + /// Calculates the difficulty of the beatmap with no mods applied. + /// + /// The cancellation token. + /// A structure describing the difficulty of the beatmap. + public DifficultyAttributes Calculate(CancellationToken cancellationToken = default) + => Calculate(Array.Empty(), cancellationToken); + /// /// Calculates the difficulty of the beatmap using a specific mod combination. /// /// The mods that should be applied to the beatmap. /// The cancellation token. /// A structure describing the difficulty of the beatmap. - public DifficultyAttributes Calculate(IEnumerable mods = null, CancellationToken cancellationToken = default) + public DifficultyAttributes Calculate([NotNull] IEnumerable mods, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); preProcess(mods, cancellationToken); @@ -65,12 +74,20 @@ namespace osu.Game.Rulesets.Difficulty } /// - /// Calculates the difficulty of the beatmap and returns a set of representing the difficulty at every relevant time value in the beatmap. + /// Calculates the difficulty of the beatmap with no mods applied and returns a set of representing the difficulty at every relevant time value in the beatmap. + /// + /// The cancellation token. + /// The set of . + public List CalculateTimed(CancellationToken cancellationToken = default) + => CalculateTimed(Array.Empty(), cancellationToken); + + /// + /// Calculates the difficulty of the beatmap using a specific mod combination and returns a set of representing the difficulty at every relevant time value in the beatmap. /// /// The mods that should be applied to the beatmap. /// The cancellation token. /// The set of . - public List CalculateTimed(IEnumerable mods, CancellationToken cancellationToken = default) + public List CalculateTimed([NotNull] IEnumerable mods, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); preProcess(mods, cancellationToken); @@ -124,7 +141,7 @@ namespace osu.Game.Rulesets.Difficulty /// /// The original list of s. /// The cancellation token. - private void preProcess(IEnumerable mods, CancellationToken cancellationToken = default) + private void preProcess([NotNull] IEnumerable mods, CancellationToken cancellationToken = default) { playableMods = mods.Select(m => m.DeepClone()).ToArray(); diff --git a/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs index 2509971389..c07d1cd46e 100644 --- a/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs @@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Difficulty { /// /// Wraps a object and adds a time value for which the attribute is valid. - /// Output by . + /// Output by DifficultyCalculator.CalculateTimed methods. /// public class TimedDifficultyAttributes : IComparable { From a67e156883fb6f80aa10e3af6701ae2417181c48 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Nov 2021 18:09:45 +0900 Subject: [PATCH 08/90] Add `IBeatmapInfo` equality support --- osu.Game/Beatmaps/BeatmapInfo.cs | 11 +++++++++++ osu.Game/Beatmaps/IBeatmapInfo.cs | 3 ++- osu.Game/Models/RealmBeatmap.cs | 15 +++++++++++++++ .../Online/API/Requests/Responses/APIBeatmap.cs | 12 ++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 5bbd48f26d..ea2346f060 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -151,6 +151,17 @@ namespace osu.Game.Beatmaps public override string ToString() => this.GetDisplayTitle(); + public bool Equals(IBeatmapInfo other) + { + if (other is BeatmapInfo b) + return Equals(b); + + if (OnlineID > 0 && other?.OnlineID > 0) + return other.OnlineID == OnlineID; + + return false; + } + public bool Equals(BeatmapInfo other) { if (ID == 0 || other?.ID == 0) diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index 84ea6d3019..ab096b8897 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -1,6 +1,7 @@ // 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.Game.Database; using osu.Game.Rulesets; @@ -11,7 +12,7 @@ namespace osu.Game.Beatmaps /// /// A single beatmap difficulty. /// - public interface IBeatmapInfo : IHasOnlineID + public interface IBeatmapInfo : IHasOnlineID, IEquatable { /// /// The user-specified name given to this beatmap. diff --git a/osu.Game/Models/RealmBeatmap.cs b/osu.Game/Models/RealmBeatmap.cs index 9311425cb7..d1907d3ffd 100644 --- a/osu.Game/Models/RealmBeatmap.cs +++ b/osu.Game/Models/RealmBeatmap.cs @@ -98,6 +98,21 @@ namespace osu.Game.Models #endregion + #region Implementation of IEquatable + + public bool Equals(IBeatmapInfo? other) + { + if (other is RealmBeatmap b) + return b.ID == ID; + + if (OnlineID > 0 && other?.OnlineID > 0) + return other.OnlineID == OnlineID; + + return false; + } + + #endregion + public bool AudioEquals(RealmBeatmap? other) => other != null && BeatmapSet != null && other.BeatmapSet != null diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 2560502173..33098a2919 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -103,5 +103,17 @@ namespace osu.Game.Online.API.Requests.Responses public string Hash => throw new NotImplementedException(); #endregion + + #region Implementation of IEquatable + + public bool Equals(IBeatmapInfo? other) + { + if (OnlineID > 0 && other?.OnlineID > 0) + return other.OnlineID == OnlineID; + + return false; + } + + #endregion } } From 54cd1158a4b198e55d6747fd98124e50d29aa401 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Nov 2021 18:14:11 +0900 Subject: [PATCH 09/90] Add `IBeatmapSetInfo` equality support --- osu.Game/Beatmaps/BeatmapSetInfo.cs | 11 +++++++++++ osu.Game/Beatmaps/IBeatmapSetInfo.cs | 2 +- osu.Game/Models/RealmBeatmapSet.cs | 13 ++++++++++++- .../Online/API/Requests/Responses/APIBeatmapSet.cs | 12 ++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 0c93c4b9db..5bdaf26471 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -66,6 +66,17 @@ namespace osu.Game.Beatmaps public bool Protected { get; set; } + public bool Equals(IBeatmapSetInfo other) + { + if (other is BeatmapSetInfo b) + return Equals(b); + + if (OnlineID > 0 && other?.OnlineID > 0) + return other.OnlineID == OnlineID; + + return false; + } + public bool Equals(BeatmapSetInfo other) { if (other == null) diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs index 20c46d9063..67f38397d4 100644 --- a/osu.Game/Beatmaps/IBeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps /// /// A representation of a collection of beatmap difficulties, generally packaged as an ".osz" archive. /// - public interface IBeatmapSetInfo : IHasOnlineID + public interface IBeatmapSetInfo : IHasOnlineID, IEquatable { /// /// The date when this beatmap was imported. diff --git a/osu.Game/Models/RealmBeatmapSet.cs b/osu.Game/Models/RealmBeatmapSet.cs index 6735510422..ddf2cf48fa 100644 --- a/osu.Game/Models/RealmBeatmapSet.cs +++ b/osu.Game/Models/RealmBeatmapSet.cs @@ -53,7 +53,16 @@ namespace osu.Game.Models /// The name of the file to get the storage path of. public string? GetPathForFile(string filename) => Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.File.StoragePath; - public override string ToString() => Metadata?.ToString() ?? base.ToString(); + public bool Equals(IBeatmapSetInfo? other) + { + if (other is RealmBeatmap b) + return b.ID == ID; + + if (OnlineID > 0 && other?.OnlineID > 0) + return other.OnlineID == OnlineID; + + return false; + } public bool Equals(RealmBeatmapSet? other) { @@ -72,6 +81,8 @@ namespace osu.Game.Models return ReferenceEquals(this, other); } + public override string ToString() => Metadata?.ToString() ?? base.ToString(); + IEnumerable IBeatmapSetInfo.Beatmaps => Beatmaps; IEnumerable IBeatmapSetInfo.Files => Files; diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 168e9d5d51..f4515c4258 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -141,5 +141,17 @@ namespace osu.Game.Online.API.Requests.Responses double IBeatmapSetInfo.MaxBPM => BPM; #endregion + + #region Implementation of IEquatable + + public bool Equals(IBeatmapSetInfo? other) + { + if (OnlineID > 0 && other?.OnlineID > 0) + return other.OnlineID == OnlineID; + + return false; + } + + #endregion } } From 7349e5521a0dbbb73b50ad7bbbab2f8e7db19c26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Nov 2021 18:10:06 +0900 Subject: [PATCH 10/90] Update `BeatmapCollection` to use `IBeatmapInfo` --- osu.Game/Collections/BeatmapCollection.cs | 2 +- osu.Game/Collections/CollectionFilterDropdown.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Collections/BeatmapCollection.cs b/osu.Game/Collections/BeatmapCollection.cs index 7e4b15ecf9..1a739f824f 100644 --- a/osu.Game/Collections/BeatmapCollection.cs +++ b/osu.Game/Collections/BeatmapCollection.cs @@ -25,7 +25,7 @@ namespace osu.Game.Collections /// /// The beatmaps contained by the collection. /// - public readonly BindableList Beatmaps = new BindableList(); + public readonly BindableList Beatmaps = new BindableList(); /// /// The date when this collection was last modified. diff --git a/osu.Game/Collections/CollectionFilterDropdown.cs b/osu.Game/Collections/CollectionFilterDropdown.cs index 7067f82fd3..ad23874b2e 100644 --- a/osu.Game/Collections/CollectionFilterDropdown.cs +++ b/osu.Game/Collections/CollectionFilterDropdown.cs @@ -39,7 +39,7 @@ namespace osu.Game.Collections } private readonly IBindableList collections = new BindableList(); - private readonly IBindableList beatmaps = new BindableList(); + private readonly IBindableList beatmaps = new BindableList(); private readonly BindableList filters = new BindableList(); [Resolved(CanBeNull = true)] @@ -200,7 +200,7 @@ namespace osu.Game.Collections private IBindable beatmap { get; set; } [CanBeNull] - private readonly BindableList collectionBeatmaps; + private readonly BindableList collectionBeatmaps; [NotNull] private readonly Bindable collectionName; From 1c49c4a6029942c8fa8a07efe18a2a34534597a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Nov 2021 18:32:39 +0900 Subject: [PATCH 11/90] Fix incorrect type check --- osu.Game/Models/RealmBeatmapSet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Models/RealmBeatmapSet.cs b/osu.Game/Models/RealmBeatmapSet.cs index ddf2cf48fa..3217abb419 100644 --- a/osu.Game/Models/RealmBeatmapSet.cs +++ b/osu.Game/Models/RealmBeatmapSet.cs @@ -55,7 +55,7 @@ namespace osu.Game.Models public bool Equals(IBeatmapSetInfo? other) { - if (other is RealmBeatmap b) + if (other is RealmBeatmapSet b) return b.ID == ID; if (OnlineID > 0 && other?.OnlineID > 0) From 63bc41556548be75ceab8fd9d1d0f5fb347d78b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Nov 2021 18:35:42 +0900 Subject: [PATCH 12/90] Warn on suspicious types --- osu.sln.DotSettings | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index f35bdfce66..3fac94b243 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -209,6 +209,8 @@ WARNING SUGGESTION DO_NOT_SHOW + + True DO_NOT_SHOW WARNING WARNING From 1d962648c29bd3e06e2b7609698274f57256da80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Nov 2021 18:48:34 +0900 Subject: [PATCH 13/90] Standardise implementations --- osu.Game/Beatmaps/BeatmapInfo.cs | 26 +++++++++++++---------- osu.Game/Beatmaps/BeatmapSetInfo.cs | 33 +++++++++++++---------------- osu.Game/Models/RealmBeatmap.cs | 22 +++++++++++++++---- osu.Game/Models/RealmBeatmapSet.cs | 32 ++++++++++++---------------- 4 files changed, 61 insertions(+), 52 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index ea2346f060..211a5f4088 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -151,25 +151,29 @@ namespace osu.Game.Beatmaps public override string ToString() => this.GetDisplayTitle(); - public bool Equals(IBeatmapInfo other) + public bool Equals(BeatmapInfo other) { - if (other is BeatmapInfo b) - return Equals(b); + if (ReferenceEquals(this, other)) return true; + if (other == null) return false; - if (OnlineID > 0 && other?.OnlineID > 0) - return other.OnlineID == OnlineID; + if (ID != 0 && other.ID != 0) + return ID == other.ID; return false; } - public bool Equals(BeatmapInfo other) + public bool Equals(IBeatmapInfo other) { - if (ID == 0 || other?.ID == 0) - // one of the two BeatmapInfos we are comparing isn't sourced from a database. - // fall back to reference equality. - return ReferenceEquals(this, other); + if (ReferenceEquals(this, other)) return true; + if (other == null) return false; - return ID == other?.ID; + if (other is BeatmapInfo b && Equals(b)) + return true; + + if (OnlineID > 0 && other.OnlineID > 0) + return other.OnlineID == OnlineID; + + return false; } public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 5bdaf26471..51d049c8f9 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -66,32 +66,29 @@ namespace osu.Game.Beatmaps public bool Protected { get; set; } - public bool Equals(IBeatmapSetInfo other) - { - if (other is BeatmapSetInfo b) - return Equals(b); - - if (OnlineID > 0 && other?.OnlineID > 0) - return other.OnlineID == OnlineID; - - return false; - } - public bool Equals(BeatmapSetInfo other) { - if (other == null) - return false; + if (ReferenceEquals(this, other)) return true; + if (other == null) return false; if (ID != 0 && other.ID != 0) return ID == other.ID; - if (OnlineBeatmapSetID.HasValue && other.OnlineBeatmapSetID.HasValue) - return OnlineBeatmapSetID == other.OnlineBeatmapSetID; + return false; + } - if (!string.IsNullOrEmpty(Hash) && !string.IsNullOrEmpty(other.Hash)) - return Hash == other.Hash; + public bool Equals(IBeatmapSetInfo other) + { + if (ReferenceEquals(this, other)) return true; + if (other == null) return false; - return ReferenceEquals(this, other); + if (other is BeatmapSetInfo b && Equals(b)) + return true; + + if (OnlineID > 0 && other.OnlineID > 0) + return other.OnlineID == OnlineID; + + return false; } #region Implementation of IHasOnlineID diff --git a/osu.Game/Models/RealmBeatmap.cs b/osu.Game/Models/RealmBeatmap.cs index d1907d3ffd..f80d1f513a 100644 --- a/osu.Game/Models/RealmBeatmap.cs +++ b/osu.Game/Models/RealmBeatmap.cs @@ -20,7 +20,7 @@ namespace osu.Game.Models [ExcludeFromDynamicCompile] [Serializable] [MapTo("Beatmap")] - public class RealmBeatmap : RealmObject, IHasGuidPrimaryKey, IBeatmapInfo + public class RealmBeatmap : RealmObject, IHasGuidPrimaryKey, IBeatmapInfo, IEquatable { [PrimaryKey] public Guid ID { get; set; } = Guid.NewGuid(); @@ -98,14 +98,28 @@ namespace osu.Game.Models #endregion + public bool Equals(RealmBeatmap? other) + { + if (ReferenceEquals(this, other)) return true; + if (other == null) return false; + + return ID == other.ID; + } + #region Implementation of IEquatable public bool Equals(IBeatmapInfo? other) { - if (other is RealmBeatmap b) - return b.ID == ID; + if (ReferenceEquals(this, other)) return true; + if (other == null) return false; - if (OnlineID > 0 && other?.OnlineID > 0) + if (other is RealmBeatmap b && Equals(b)) + return true; + + if (!string.IsNullOrEmpty(Hash) && !string.IsNullOrEmpty(other.Hash)) + return Hash == other.Hash; + + if (OnlineID > 0 && other.OnlineID > 0) return other.OnlineID == OnlineID; return false; diff --git a/osu.Game/Models/RealmBeatmapSet.cs b/osu.Game/Models/RealmBeatmapSet.cs index 3217abb419..9cfa2de44d 100644 --- a/osu.Game/Models/RealmBeatmapSet.cs +++ b/osu.Game/Models/RealmBeatmapSet.cs @@ -53,32 +53,26 @@ namespace osu.Game.Models /// The name of the file to get the storage path of. public string? GetPathForFile(string filename) => Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.File.StoragePath; - public bool Equals(IBeatmapSetInfo? other) - { - if (other is RealmBeatmapSet b) - return b.ID == ID; - - if (OnlineID > 0 && other?.OnlineID > 0) - return other.OnlineID == OnlineID; - - return false; - } - public bool Equals(RealmBeatmapSet? other) { - if (other == null) - return false; + if (ReferenceEquals(this, other)) return true; + if (other == null) return false; - if (IsManaged && other.IsManaged) - return ID == other.ID; + return ID == other.ID; + } + + public bool Equals(IBeatmapSetInfo? other) + { + if (ReferenceEquals(this, other)) return true; + if (other == null) return false; + + if (other is RealmBeatmapSet b && Equals(b)) + return true; if (OnlineID > 0 && other.OnlineID > 0) return OnlineID == other.OnlineID; - if (!string.IsNullOrEmpty(Hash) && !string.IsNullOrEmpty(other.Hash)) - return Hash == other.Hash; - - return ReferenceEquals(this, other); + return false; } public override string ToString() => Metadata?.ToString() ?? base.ToString(); From 9800cd490384bf41bebb1fe0d105e3a7c7ae4f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 13 Nov 2021 16:58:24 +0100 Subject: [PATCH 14/90] Add test coverage for sample control point piece operation --- ...estSceneHitObjectSamplePointAdjustments.cs | 139 ++++++++++++++++++ .../Components/Timeline/SamplePointPiece.cs | 6 +- 2 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs new file mode 100644 index 0000000000..24048e6052 --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -0,0 +1,139 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using Humanizer; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Screens.Edit.Compose.Components.Timeline; +using osu.Game.Screens.Edit.Timing; +using osu.Game.Tests.Beatmaps; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Editing +{ + public class TestSceneHitObjectSamplePointAdjustments : EditorTestScene + { + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); + + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("add test objects", () => + { + EditorBeatmap.Add(new HitCircle + { + StartTime = 0, + Position = (OsuPlayfield.BASE_SIZE - new Vector2(100, 0)) / 2, + SampleControlPoint = new SampleControlPoint + { + SampleBank = "normal", + SampleVolume = 80 + } + }); + + EditorBeatmap.Add(new HitCircle() + { + StartTime = 500, + Position = (OsuPlayfield.BASE_SIZE + new Vector2(100, 0)) / 2, + SampleControlPoint = new SampleControlPoint + { + SampleBank = "soft", + SampleVolume = 60 + } + }); + }); + } + + [Test] + public void TestSingleSelection() + { + clickSamplePiece(0); + samplePopoverHasSingleBank("normal"); + samplePopoverHasSingleVolume(80); + + dismissPopover(); + + // select first object to ensure that sample pieces for unselected objects + // work independently from selection state. + AddStep("select first object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.First())); + + clickSamplePiece(1); + samplePopoverHasSingleBank("soft"); + samplePopoverHasSingleVolume(60); + + setVolumeViaPopover(90); + hitObjectHasSampleVolume(1, 90); + + setBankViaPopover("drum"); + hitObjectHasSampleBank(1, "drum"); + } + + private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} difficulty piece", () => + { + var difficultyPiece = this.ChildrenOfType().Single(piece => piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex)); + + InputManager.MoveMouseTo(difficultyPiece); + InputManager.Click(MouseButton.Left); + }); + + private void samplePopoverHasSingleVolume(int volume) => AddUntilStep($"sample popover has volume {volume}", () => + { + var popover = this.ChildrenOfType().SingleOrDefault(); + var slider = popover?.ChildrenOfType>().Single(); + + return slider?.Current.Value == volume; + }); + + private void samplePopoverHasSingleBank(string bank) => AddUntilStep($"sample popover has bank {bank}", () => + { + var popover = this.ChildrenOfType().SingleOrDefault(); + var textBox = popover?.ChildrenOfType().First(); + + return textBox?.Current.Value == bank; + }); + + private void dismissPopover() + { + AddStep("dismiss popover", () => InputManager.Key(Key.Escape)); + AddUntilStep("wait for dismiss", () => !this.ChildrenOfType().Any(popover => popover.IsPresent)); + } + + private void setVolumeViaPopover(int volume) => AddStep($"set volume {volume} via popover", () => + { + var popover = this.ChildrenOfType().Single(); + var slider = popover.ChildrenOfType>().Single(); + slider.Current.Value = volume; + }); + + private void hitObjectHasSampleVolume(int objectIndex, int volume) => AddAssert($"{objectIndex.ToOrdinalWords()} has volume {volume}", () => + { + var h = EditorBeatmap.HitObjects.ElementAt(objectIndex); + return h.SampleControlPoint.SampleVolume == volume; + }); + + private void setBankViaPopover(string bank) => AddStep($"set bank {bank} via popover", () => + { + var popover = this.ChildrenOfType().Single(); + var textBox = popover.ChildrenOfType().First(); + textBox.Current.Value = bank; + }); + + private void hitObjectHasSampleBank(int objectIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} has bank {bank}", () => + { + var h = EditorBeatmap.HitObjects.ElementAt(objectIndex); + return h.SampleControlPoint.SampleBank == bank; + }); + } +} diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs index 6a26f69e41..6250a9ccb8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public class SamplePointPiece : HitObjectPointPiece, IHasPopover { - private readonly HitObject hitObject; + public readonly HitObject HitObject; private readonly Bindable bank; private readonly BindableNumber volume; @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public SamplePointPiece(HitObject hitObject) : base(hitObject.SampleControlPoint) { - this.hitObject = hitObject; + HitObject = hitObject; volume = hitObject.SampleControlPoint.SampleVolumeBindable.GetBoundCopy(); bank = hitObject.SampleControlPoint.SampleBankBindable.GetBoundCopy(); } @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Label.Text = $"{bank.Value} {volume.Value}"; } - public Popover GetPopover() => new SampleEditPopover(hitObject); + public Popover GetPopover() => new SampleEditPopover(HitObject); public class SampleEditPopover : OsuPopover { From 6ba154129bdae1037e4a3ae305020b0193e949b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 13 Nov 2021 17:02:55 +0100 Subject: [PATCH 15/90] Add failing test coverage for setting sample volume on multiple objects --- ...estSceneHitObjectSamplePointAdjustments.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs index 24048e6052..0c4056e90c 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -80,6 +80,46 @@ namespace osu.Game.Tests.Visual.Editing hitObjectHasSampleBank(1, "drum"); } + [Test] + public void TestMultipleSelectionWithSameSampleVolume() + { + AddStep("unify sample volume", () => + { + foreach (var h in EditorBeatmap.HitObjects) + h.SampleControlPoint.SampleVolume = 50; + }); + + AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); + clickSamplePiece(0); + samplePopoverHasSingleVolume(50); + + dismissPopover(); + + clickSamplePiece(1); + samplePopoverHasSingleVolume(50); + + setVolumeViaPopover(75); + hitObjectHasSampleVolume(0, 75); + hitObjectHasSampleVolume(1, 75); + } + + [Test] + public void TestMultipleSelectionWithDifferentSampleVolume() + { + AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); + clickSamplePiece(0); + samplePopoverHasIndeterminateVolume(); + + dismissPopover(); + + clickSamplePiece(1); + samplePopoverHasIndeterminateVolume(); + + setVolumeViaPopover(30); + hitObjectHasSampleVolume(0, 30); + hitObjectHasSampleVolume(1, 30); + } + private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} difficulty piece", () => { var difficultyPiece = this.ChildrenOfType().Single(piece => piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex)); @@ -96,6 +136,14 @@ namespace osu.Game.Tests.Visual.Editing return slider?.Current.Value == volume; }); + private void samplePopoverHasIndeterminateVolume() => AddUntilStep($"sample popover has indeterminate volume", () => + { + var popover = this.ChildrenOfType().SingleOrDefault(); + var slider = popover?.ChildrenOfType>().Single(); + + return slider != null && slider.Current.Value == null; + }); + private void samplePopoverHasSingleBank(string bank) => AddUntilStep($"sample popover has bank {bank}", () => { var popover = this.ChildrenOfType().SingleOrDefault(); From 9cf45e418ab78da6e12dd40665777a285cd88269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 13 Nov 2021 17:07:19 +0100 Subject: [PATCH 16/90] Add failing test coverage for setting sample bank on multiple objects --- ...estSceneHitObjectSamplePointAdjustments.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs index 0c4056e90c..5b2b6fc780 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -120,6 +120,46 @@ namespace osu.Game.Tests.Visual.Editing hitObjectHasSampleVolume(1, 30); } + [Test] + public void TestMultipleSelectionWithSameSampleBank() + { + AddStep("unify sample bank", () => + { + foreach (var h in EditorBeatmap.HitObjects) + h.SampleControlPoint.SampleBank = "soft"; + }); + + AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); + clickSamplePiece(0); + samplePopoverHasSingleBank("soft"); + + dismissPopover(); + + clickSamplePiece(1); + samplePopoverHasSingleBank("soft"); + + setBankViaPopover("drum"); + hitObjectHasSampleBank(0, "drum"); + hitObjectHasSampleBank(1, "drum"); + } + + [Test] + public void TestMultipleSelectionWithDifferentSampleBank() + { + AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); + clickSamplePiece(0); + samplePopoverHasIndeterminateBank(); + + dismissPopover(); + + clickSamplePiece(1); + samplePopoverHasIndeterminateBank(); + + setBankViaPopover("normal"); + hitObjectHasSampleBank(0, "normal"); + hitObjectHasSampleBank(1, "normal"); + } + private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} difficulty piece", () => { var difficultyPiece = this.ChildrenOfType().Single(piece => piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex)); @@ -152,6 +192,14 @@ namespace osu.Game.Tests.Visual.Editing return textBox?.Current.Value == bank; }); + private void samplePopoverHasIndeterminateBank() => AddUntilStep($"sample popover has indeterminate bank", () => + { + var popover = this.ChildrenOfType().SingleOrDefault(); + var textBox = popover?.ChildrenOfType().First(); + + return textBox != null && textBox.Current.Value == null; + }); + private void dismissPopover() { AddStep("dismiss popover", () => InputManager.Key(Key.Escape)); From 3fee6b0938261e62654a1b1e8ab677d68653e524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 13 Nov 2021 17:21:48 +0100 Subject: [PATCH 17/90] Add support for setting sample bank & volume for multiple objects at once --- ...estSceneHitObjectSamplePointAdjustments.cs | 10 +-- .../Components/Timeline/SamplePointPiece.cs | 71 +++++++++++++++---- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs index 5b2b6fc780..f30c56d53c 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -171,15 +171,15 @@ namespace osu.Game.Tests.Visual.Editing private void samplePopoverHasSingleVolume(int volume) => AddUntilStep($"sample popover has volume {volume}", () => { var popover = this.ChildrenOfType().SingleOrDefault(); - var slider = popover?.ChildrenOfType>().Single(); + var slider = popover?.ChildrenOfType>().Single(); return slider?.Current.Value == volume; }); - private void samplePopoverHasIndeterminateVolume() => AddUntilStep($"sample popover has indeterminate volume", () => + private void samplePopoverHasIndeterminateVolume() => AddUntilStep("sample popover has indeterminate volume", () => { var popover = this.ChildrenOfType().SingleOrDefault(); - var slider = popover?.ChildrenOfType>().Single(); + var slider = popover?.ChildrenOfType>().Single(); return slider != null && slider.Current.Value == null; }); @@ -197,7 +197,7 @@ namespace osu.Game.Tests.Visual.Editing var popover = this.ChildrenOfType().SingleOrDefault(); var textBox = popover?.ChildrenOfType().First(); - return textBox != null && textBox.Current.Value == null; + return textBox != null && string.IsNullOrEmpty(textBox.Current.Value); }); private void dismissPopover() @@ -209,7 +209,7 @@ namespace osu.Game.Tests.Visual.Editing private void setVolumeViaPopover(int volume) => AddStep($"set volume {volume} via popover", () => { var popover = this.ChildrenOfType().Single(); - var slider = popover.ChildrenOfType>().Single(); + var slider = popover.ChildrenOfType>().Single(); slider.Current.Value = volume; }); diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs index 6250a9ccb8..f0b11ce96e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs @@ -1,9 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + +using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -55,18 +60,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public class SampleEditPopover : OsuPopover { private readonly HitObject hitObject; - private readonly SampleControlPoint point; - private LabelledTextBox bank; - private SliderWithTextBoxInput volume; + private LabelledTextBox bank = null!; + private IndeterminateSliderWithTextBoxInput volume = null!; [Resolved(canBeNull: true)] - private EditorBeatmap beatmap { get; set; } + private EditorBeatmap beatmap { get; set; } = null!; public SampleEditPopover(HitObject hitObject) { this.hitObject = hitObject; - point = hitObject.SampleControlPoint; } [BackgroundDependencyLoader] @@ -85,19 +88,61 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { Label = "Bank Name", }, - volume = new SliderWithTextBoxInput("Volume") - { - Current = new SampleControlPoint().SampleVolumeBindable, - } + volume = new IndeterminateSliderWithTextBoxInput("Volume", new SampleControlPoint().SampleVolumeBindable) } } }; - bank.Current = point.SampleBankBindable; - bank.Current.BindValueChanged(_ => beatmap.Update(hitObject)); + // if the piece belongs to a currently selected object, assume that the user wants to change all selected objects. + // if the piece belongs to an unselected object, operate on that object alone, independently of the selection. + var relevantObjects = (beatmap.SelectedHitObjects.Contains(hitObject) ? beatmap.SelectedHitObjects : hitObject.Yield()).ToArray(); + var relevantControlPoints = relevantObjects.Select(h => h.SampleControlPoint).ToArray(); - volume.Current = point.SampleVolumeBindable; - volume.Current.BindValueChanged(_ => beatmap.Update(hitObject)); + // even if there are multiple objects selected, we can still display sample volume or bank if they all have the same value. + string? commonBank = relevantControlPoints.Select(point => point.SampleBank).Distinct().Count() == 1 ? relevantControlPoints.First().SampleBank : null; + + if (!string.IsNullOrEmpty(commonBank)) + bank.Current.Value = commonBank; + + int? commonVolume = relevantControlPoints.Select(point => point.SampleVolume).Distinct().Count() == 1 ? (int?)relevantControlPoints.First().SampleVolume : null; + + if (commonVolume != null) + volume.Current.Value = commonVolume.Value; + + bank.Current.BindValueChanged(val => updateBankFor(relevantObjects, val.NewValue)); + volume.Current.BindValueChanged(val => updateVolumeFor(relevantObjects, val.NewValue)); + } + + private void updateBankFor(IEnumerable objects, string? newBank) + { + if (string.IsNullOrEmpty(newBank)) + return; + + beatmap.BeginChange(); + + foreach (var h in objects) + { + h.SampleControlPoint.SampleBank = newBank; + beatmap.Update(h); + } + + beatmap.EndChange(); + } + + private void updateVolumeFor(IEnumerable objects, int? newVolume) + { + if (newVolume == null) + return; + + beatmap.BeginChange(); + + foreach (var h in objects) + { + h.SampleControlPoint.SampleVolume = newVolume.Value; + beatmap.Update(h); + } + + beatmap.EndChange(); } } } From 76baf08140b4ae9971a44c9443f5b780a3d7ecef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 13 Nov 2021 17:43:33 +0100 Subject: [PATCH 18/90] Expand test coverage with desired bank text box placeholder behaviour --- ...estSceneHitObjectSamplePointAdjustments.cs | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs index f30c56d53c..460b608166 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; @@ -43,7 +44,7 @@ namespace osu.Game.Tests.Visual.Editing } }); - EditorBeatmap.Add(new HitCircle() + EditorBeatmap.Add(new HitCircle { StartTime = 500, Position = (OsuPlayfield.BASE_SIZE + new Vector2(100, 0)) / 2, @@ -138,9 +139,15 @@ namespace osu.Game.Tests.Visual.Editing clickSamplePiece(1); samplePopoverHasSingleBank("soft"); + setBankViaPopover(string.Empty); + hitObjectHasSampleBank(0, "soft"); + hitObjectHasSampleBank(1, "soft"); + samplePopoverHasSingleBank("soft"); + setBankViaPopover("drum"); hitObjectHasSampleBank(0, "drum"); hitObjectHasSampleBank(1, "drum"); + samplePopoverHasSingleBank("drum"); } [Test] @@ -155,9 +162,15 @@ namespace osu.Game.Tests.Visual.Editing clickSamplePiece(1); samplePopoverHasIndeterminateBank(); + setBankViaPopover(string.Empty); + hitObjectHasSampleBank(0, "normal"); + hitObjectHasSampleBank(1, "soft"); + samplePopoverHasIndeterminateBank(); + setBankViaPopover("normal"); hitObjectHasSampleBank(0, "normal"); hitObjectHasSampleBank(1, "normal"); + samplePopoverHasSingleBank("normal"); } private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} difficulty piece", () => @@ -187,17 +200,17 @@ namespace osu.Game.Tests.Visual.Editing private void samplePopoverHasSingleBank(string bank) => AddUntilStep($"sample popover has bank {bank}", () => { var popover = this.ChildrenOfType().SingleOrDefault(); - var textBox = popover?.ChildrenOfType().First(); + var textBox = popover?.ChildrenOfType().First(); - return textBox?.Current.Value == bank; + return textBox?.Current.Value == bank && string.IsNullOrEmpty(textBox?.PlaceholderText.ToString()); }); - private void samplePopoverHasIndeterminateBank() => AddUntilStep($"sample popover has indeterminate bank", () => + private void samplePopoverHasIndeterminateBank() => AddUntilStep("sample popover has indeterminate bank", () => { var popover = this.ChildrenOfType().SingleOrDefault(); - var textBox = popover?.ChildrenOfType().First(); + var textBox = popover?.ChildrenOfType().First(); - return textBox != null && string.IsNullOrEmpty(textBox.Current.Value); + return textBox != null && string.IsNullOrEmpty(textBox.Current.Value) && !string.IsNullOrEmpty(textBox.PlaceholderText.ToString()); }); private void dismissPopover() From 73ca1d39a2fbaeef88ad13ebc7904027eece8d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 13 Nov 2021 18:06:32 +0100 Subject: [PATCH 19/90] Improve sample bank text box UX in case of multiple selection --- ...estSceneHitObjectSamplePointAdjustments.cs | 4 +++ .../Components/Timeline/SamplePointPiece.cs | 26 +++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs index 460b608166..dca30a6fc0 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -237,6 +237,10 @@ namespace osu.Game.Tests.Visual.Editing var popover = this.ChildrenOfType().Single(); var textBox = popover.ChildrenOfType().First(); textBox.Current.Value = bank; + // force a commit via keyboard. + // this is needed when testing attempting to set empty bank - which should revert to the previous value, but only on commit. + InputManager.ChangeFocus(textBox); + InputManager.Key(Key.Enter); }); private void hitObjectHasSampleBank(int objectIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} has bank {bank}", () => diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs index f0b11ce96e..fbbbb153b9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs @@ -99,20 +99,30 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline var relevantControlPoints = relevantObjects.Select(h => h.SampleControlPoint).ToArray(); // even if there are multiple objects selected, we can still display sample volume or bank if they all have the same value. - string? commonBank = relevantControlPoints.Select(point => point.SampleBank).Distinct().Count() == 1 ? relevantControlPoints.First().SampleBank : null; - + string? commonBank = getCommonBank(relevantControlPoints); if (!string.IsNullOrEmpty(commonBank)) bank.Current.Value = commonBank; - int? commonVolume = relevantControlPoints.Select(point => point.SampleVolume).Distinct().Count() == 1 ? (int?)relevantControlPoints.First().SampleVolume : null; - + int? commonVolume = getCommonVolume(relevantControlPoints); if (commonVolume != null) volume.Current.Value = commonVolume.Value; - bank.Current.BindValueChanged(val => updateBankFor(relevantObjects, val.NewValue)); + updateBankPlaceholderText(relevantObjects); + bank.Current.BindValueChanged(val => + { + updateBankFor(relevantObjects, val.NewValue); + updateBankPlaceholderText(relevantObjects); + }); + // on commit, ensure that the value is correct by sourcing it from the objects' control points again. + // this ensures that committing empty text causes a revert to the previous value. + bank.OnCommit += (_, __) => bank.Current.Value = getCommonBank(relevantControlPoints); + volume.Current.BindValueChanged(val => updateVolumeFor(relevantObjects, val.NewValue)); } + private static string? getCommonBank(SampleControlPoint[] relevantControlPoints) => relevantControlPoints.Select(point => point.SampleBank).Distinct().Count() == 1 ? relevantControlPoints.First().SampleBank : null; + private static int? getCommonVolume(SampleControlPoint[] relevantControlPoints) => relevantControlPoints.Select(point => point.SampleVolume).Distinct().Count() == 1 ? (int?)relevantControlPoints.First().SampleVolume : null; + private void updateBankFor(IEnumerable objects, string? newBank) { if (string.IsNullOrEmpty(newBank)) @@ -129,6 +139,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline beatmap.EndChange(); } + private void updateBankPlaceholderText(IEnumerable objects) + { + string? commonBank = getCommonBank(objects.Select(h => h.SampleControlPoint).ToArray()); + bank.PlaceholderText = string.IsNullOrEmpty(commonBank) ? "(multiple)" : null; + } + private void updateVolumeFor(IEnumerable objects, int? newVolume) { if (newVolume == null) From 9a19a516f99cd62b4b2dc5d5baa76e982ca96bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 13 Nov 2021 18:09:40 +0100 Subject: [PATCH 20/90] Adjust spacings on sample point piece popover --- .../Edit/Compose/Components/Timeline/SamplePointPiece.cs | 2 ++ .../Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs index fbbbb153b9..2cbfe88519 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs @@ -19,6 +19,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit.Timing; +using osuTK; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { @@ -82,6 +83,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Width = 200, Direction = FillDirection.Vertical, AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0, 10), Children = new Drawable[] { bank = new LabelledTextBox diff --git a/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs b/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs index 17f82f4978..14b8c4c9de 100644 --- a/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs +++ b/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs @@ -11,6 +11,7 @@ using osu.Framework.Localisation; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays.Settings; using osu.Game.Utils; +using osuTK; namespace osu.Game.Screens.Edit.Timing { @@ -62,6 +63,7 @@ namespace osu.Game.Screens.Edit.Timing RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), Children = new Drawable[] { textbox = new LabelledTextBox From ca239ca40a2913103958552d911342bb2abef98b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 12 Nov 2021 12:41:16 +0100 Subject: [PATCH 21/90] Add failing test case for sharing clock state with gameplay test --- .../Editing/TestSceneEditorTestGameplay.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs index 1d572772eb..160af47a6d 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs @@ -14,6 +14,7 @@ using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components.Timelines.Summary; using osu.Game.Screens.Edit.GameplayTest; +using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps.IO; using osuTK.Graphics; using osuTK.Input; @@ -150,6 +151,35 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("track stopped", () => !Beatmap.Value.Track.IsRunning); } + [Test] + public void TestSharedClockState() + { + AddStep("seek to 00:01:00", () => EditorClock.Seek(60_000)); + AddStep("click test gameplay button", () => + { + var button = Editor.ChildrenOfType().Single(); + + InputManager.MoveMouseTo(button); + InputManager.Click(MouseButton.Left); + }); + + EditorPlayer editorPlayer = null; + AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null); + + GameplayClockContainer gameplayClockContainer = null; + AddStep("fetch gameplay clock", () => gameplayClockContainer = editorPlayer.ChildrenOfType().First()); + AddUntilStep("gameplay clock running", () => gameplayClockContainer.IsRunning); + AddAssert("gameplay time past 00:01:00", () => gameplayClockContainer.CurrentTime >= 60_000); + + double timeAtPlayerExit = 0; + AddWaitStep("wait some", 5); + AddStep("store time before exit", () => timeAtPlayerExit = gameplayClockContainer.CurrentTime); + + AddStep("exit player", () => editorPlayer.Exit()); + AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor); + AddAssert("time is past player exit", () => EditorClock.CurrentTime >= timeAtPlayerExit); + } + public override void TearDownSteps() { base.TearDownSteps(); From d2ddc25ab3a7457bf2496e5a5216f29ba31b4c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 12 Nov 2021 12:50:38 +0100 Subject: [PATCH 22/90] Propagate clock state from editor to gameplay test --- osu.Game/Screens/Edit/Editor.cs | 21 +++++++++++++------ .../Screens/Edit/GameplayTest/EditorPlayer.cs | 15 +++++++++---- .../Edit/GameplayTest/EditorPlayerLoader.cs | 4 ++-- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index ab24391bb4..ff9a19434b 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -325,6 +325,19 @@ namespace osu.Game.Screens.Edit /// public void UpdateClockSource() => clock.ChangeSource(Beatmap.Value.Track); + /// + /// Creates an instance representing the current state of the editor. + /// + /// + /// The next beatmap to be shown, in the case of difficulty switch. + /// indicates that the beatmap will not be changing. + /// + private EditorState getState([CanBeNull] BeatmapInfo nextBeatmap = null) => new EditorState + { + Time = clock.CurrentTimeAccurate, + ClipboardContent = nextBeatmap == null || editorBeatmap.BeatmapInfo.RulesetID == nextBeatmap.RulesetID ? Clipboard.Content.Value : string.Empty + }; + /// /// Restore the editor to a provided state. /// @@ -780,11 +793,7 @@ namespace osu.Game.Screens.Edit return new DifficultyMenuItem(beatmapInfo, isCurrentDifficulty, SwitchToDifficulty); } - protected void SwitchToDifficulty(BeatmapInfo nextBeatmap) => loader?.ScheduleDifficultySwitch(nextBeatmap, new EditorState - { - Time = clock.CurrentTimeAccurate, - ClipboardContent = editorBeatmap.BeatmapInfo.RulesetID == nextBeatmap.RulesetID ? Clipboard.Content.Value : string.Empty - }); + protected void SwitchToDifficulty(BeatmapInfo nextBeatmap) => loader?.ScheduleDifficultySwitch(nextBeatmap, getState(nextBeatmap)); private void cancelExit() { @@ -807,7 +816,7 @@ namespace osu.Game.Screens.Edit pushEditorPlayer(); } - void pushEditorPlayer() => this.Push(new EditorPlayerLoader()); + void pushEditorPlayer() => this.Push(new EditorPlayerLoader(getState())); } public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); diff --git a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs index 9856ad62bb..266c5e81d4 100644 --- a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs +++ b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Screens; +using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Screens.Play; @@ -10,14 +11,20 @@ namespace osu.Game.Screens.Edit.GameplayTest { public class EditorPlayer : Player { - public EditorPlayer() - : base(new PlayerConfiguration { ShowResults = false }) - { - } + private readonly EditorState editorState; [Resolved] private MusicController musicController { get; set; } + public EditorPlayer(EditorState editorState) + : base(new PlayerConfiguration { ShowResults = false }) + { + this.editorState = editorState; + } + + protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) + => new MasterGameplayClockContainer(beatmap, editorState.Time, true); + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs b/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs index 610fff70f2..5c2ab04fd7 100644 --- a/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs +++ b/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs @@ -14,8 +14,8 @@ namespace osu.Game.Screens.Edit.GameplayTest [Resolved] private OsuLogo osuLogo { get; set; } - public EditorPlayerLoader() - : base(() => new EditorPlayer()) + public EditorPlayerLoader(EditorState editorState) + : base(() => new EditorPlayer(editorState)) { } From 2562412125e974efc2c89a4b4b31daa2070aa589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 12 Nov 2021 13:12:25 +0100 Subject: [PATCH 23/90] Propagate clock state from gameplay test back to editor --- osu.Game/Screens/Edit/Editor.cs | 6 +++--- osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs | 9 +++++++-- osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index ff9a19434b..2a7e2c9cef 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -332,7 +332,7 @@ namespace osu.Game.Screens.Edit /// The next beatmap to be shown, in the case of difficulty switch. /// indicates that the beatmap will not be changing. /// - private EditorState getState([CanBeNull] BeatmapInfo nextBeatmap = null) => new EditorState + public EditorState GetState([CanBeNull] BeatmapInfo nextBeatmap = null) => new EditorState { Time = clock.CurrentTimeAccurate, ClipboardContent = nextBeatmap == null || editorBeatmap.BeatmapInfo.RulesetID == nextBeatmap.RulesetID ? Clipboard.Content.Value : string.Empty @@ -793,7 +793,7 @@ namespace osu.Game.Screens.Edit return new DifficultyMenuItem(beatmapInfo, isCurrentDifficulty, SwitchToDifficulty); } - protected void SwitchToDifficulty(BeatmapInfo nextBeatmap) => loader?.ScheduleDifficultySwitch(nextBeatmap, getState(nextBeatmap)); + protected void SwitchToDifficulty(BeatmapInfo nextBeatmap) => loader?.ScheduleDifficultySwitch(nextBeatmap, GetState(nextBeatmap)); private void cancelExit() { @@ -816,7 +816,7 @@ namespace osu.Game.Screens.Edit pushEditorPlayer(); } - void pushEditorPlayer() => this.Push(new EditorPlayerLoader(getState())); + void pushEditorPlayer() => this.Push(new EditorPlayerLoader(this)); } public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); diff --git a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs index 266c5e81d4..479dc2e6b3 100644 --- a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs +++ b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs @@ -11,15 +11,17 @@ namespace osu.Game.Screens.Edit.GameplayTest { public class EditorPlayer : Player { + private readonly Editor editor; private readonly EditorState editorState; [Resolved] private MusicController musicController { get; set; } - public EditorPlayer(EditorState editorState) + public EditorPlayer(Editor editor) : base(new PlayerConfiguration { ShowResults = false }) { - this.editorState = editorState; + this.editor = editor; + editorState = editor.GetState(); } protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) @@ -45,6 +47,9 @@ namespace osu.Game.Screens.Edit.GameplayTest public override bool OnExiting(IScreen next) { musicController.Stop(); + + editorState.Time = GameplayClockContainer.CurrentTime; + editor.RestoreState(editorState); return base.OnExiting(next); } } diff --git a/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs b/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs index 5c2ab04fd7..addc79ba61 100644 --- a/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs +++ b/osu.Game/Screens/Edit/GameplayTest/EditorPlayerLoader.cs @@ -14,8 +14,8 @@ namespace osu.Game.Screens.Edit.GameplayTest [Resolved] private OsuLogo osuLogo { get; set; } - public EditorPlayerLoader(EditorState editorState) - : base(() => new EditorPlayer(editorState)) + public EditorPlayerLoader(Editor editor) + : base(() => new EditorPlayer(editor)) { } From 6b4b6de5545f16762a8a63fdeb011ed6e95ee093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 12 Nov 2021 13:51:02 +0100 Subject: [PATCH 24/90] Fix test gameplay starting in a hidden state --- osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs index 479dc2e6b3..f49603c754 100644 --- a/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs +++ b/osu.Game/Screens/Edit/GameplayTest/EditorPlayer.cs @@ -44,6 +44,16 @@ namespace osu.Game.Screens.Edit.GameplayTest protected override bool CheckModsAllowFailure() => false; // never fail. + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + + // finish alpha transforms on entering to avoid gameplay starting in a half-hidden state. + // the finish calls are purposefully not propagated to children to avoid messing up their state. + FinishTransforms(); + GameplayClockContainer.FinishTransforms(false, nameof(Alpha)); + } + public override bool OnExiting(IScreen next) { musicController.Stop(); From a4c11e8813308b4bbd71489b711c9e556a7ca2f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 14:34:50 +0900 Subject: [PATCH 25/90] Use extension method to compare online IDs --- osu.Game/Database/IHasOnlineID.cs | 3 +++ osu.Game/Extensions/ModelExtensions.cs | 29 ++++++++++++++++++++++++++ osu.Game/Overlays/Music/Playlist.cs | 3 ++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/IHasOnlineID.cs b/osu.Game/Database/IHasOnlineID.cs index 4e83ed8876..7a720989cd 100644 --- a/osu.Game/Database/IHasOnlineID.cs +++ b/osu.Game/Database/IHasOnlineID.cs @@ -1,11 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; + #nullable enable namespace osu.Game.Database { public interface IHasOnlineID + where T : IEquatable { /// /// 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). diff --git a/osu.Game/Extensions/ModelExtensions.cs b/osu.Game/Extensions/ModelExtensions.cs index d8e0938d46..2545045d96 100644 --- a/osu.Game/Extensions/ModelExtensions.cs +++ b/osu.Game/Extensions/ModelExtensions.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Users; @@ -57,5 +58,33 @@ namespace osu.Game.Extensions result ??= model?.ToString() ?? @"null"; return result; } + + /// + /// Check whether the online ID of two instances match. + /// + /// The instance to compare. + /// The other instance to compare against. + /// Whether online IDs match. If either instance is missing an online ID, this will return false. + public static bool MatchesOnlineID(this IHasOnlineID instance, IHasOnlineID other) + { + if (instance.OnlineID < 0 || other.OnlineID < 0) + return false; + + return instance.OnlineID.Equals(other.OnlineID); + } + + /// + /// Check whether the online ID of two instances match. + /// + /// The instance to compare. + /// The other instance to compare against. + /// Whether online IDs match. If either instance is missing an online ID, this will return false. + public static bool MatchesOnlineID(this IHasOnlineID instance, IHasOnlineID other) + { + if (instance.OnlineID < 0 || other.OnlineID < 0) + return false; + + return instance.OnlineID.Equals(other.OnlineID); + } } } diff --git a/osu.Game/Overlays/Music/Playlist.cs b/osu.Game/Overlays/Music/Playlist.cs index 4fe338926f..5de62ebfb8 100644 --- a/osu.Game/Overlays/Music/Playlist.cs +++ b/osu.Game/Overlays/Music/Playlist.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; +using osu.Game.Extensions; using osu.Game.Graphics.Containers; using osuTK; @@ -29,7 +30,7 @@ namespace osu.Game.Overlays.Music var items = (SearchContainer>)ListContainer; foreach (var item in items.OfType()) - item.InSelectedCollection = criteria.Collection?.Beatmaps.Any(b => b.BeatmapSet.Equals(item.Model)) ?? true; + item.InSelectedCollection = criteria.Collection?.Beatmaps.Any(b => b.MatchesOnlineID(item.Model)) ?? true; items.SearchTerm = criteria.SearchText; } From 0b4822b55225bb995bff04411082cc36cf42a3a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 14:35:01 +0900 Subject: [PATCH 26/90] Remove newly added equality --- osu.Game/Beatmaps/BeatmapInfo.cs | 14 -------------- osu.Game/Beatmaps/BeatmapSetInfo.cs | 14 -------------- osu.Game/Beatmaps/IBeatmapInfo.cs | 3 +-- osu.Game/Beatmaps/IBeatmapSetInfo.cs | 2 +- osu.Game/Models/RealmBeatmap.cs | 21 --------------------- osu.Game/Models/RealmBeatmapSet.cs | 17 ++--------------- 6 files changed, 4 insertions(+), 67 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 0b234b3c2d..6f45f5ec71 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -163,20 +163,6 @@ namespace osu.Game.Beatmaps return false; } - public bool Equals(IBeatmapInfo other) - { - if (ReferenceEquals(this, other)) return true; - if (other == null) return false; - - if (other is BeatmapInfo b && Equals(b)) - return true; - - if (OnlineID > 0 && other.OnlineID > 0) - return other.OnlineID == OnlineID; - - return false; - } - public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && BeatmapSet.Hash == other.BeatmapSet.Hash && (Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile; diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index fa314fe400..6dd8cc5ade 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -78,20 +78,6 @@ namespace osu.Game.Beatmaps return false; } - public bool Equals(IBeatmapSetInfo other) - { - if (ReferenceEquals(this, other)) return true; - if (other == null) return false; - - if (other is BeatmapSetInfo b && Equals(b)) - return true; - - if (OnlineID > 0 && other.OnlineID > 0) - return other.OnlineID == OnlineID; - - return false; - } - #region Implementation of IHasOnlineID int IHasOnlineID.OnlineID => OnlineID ?? -1; diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index ab096b8897..84ea6d3019 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -1,7 +1,6 @@ // 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.Game.Database; using osu.Game.Rulesets; @@ -12,7 +11,7 @@ namespace osu.Game.Beatmaps /// /// A single beatmap difficulty. /// - public interface IBeatmapInfo : IHasOnlineID, IEquatable + public interface IBeatmapInfo : IHasOnlineID { /// /// The user-specified name given to this beatmap. diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs index 67f38397d4..20c46d9063 100644 --- a/osu.Game/Beatmaps/IBeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps /// /// A representation of a collection of beatmap difficulties, generally packaged as an ".osz" archive. /// - public interface IBeatmapSetInfo : IHasOnlineID, IEquatable + public interface IBeatmapSetInfo : IHasOnlineID { /// /// The date when this beatmap was imported. diff --git a/osu.Game/Models/RealmBeatmap.cs b/osu.Game/Models/RealmBeatmap.cs index f80d1f513a..2a197d296a 100644 --- a/osu.Game/Models/RealmBeatmap.cs +++ b/osu.Game/Models/RealmBeatmap.cs @@ -106,27 +106,6 @@ namespace osu.Game.Models return ID == other.ID; } - #region Implementation of IEquatable - - public bool Equals(IBeatmapInfo? other) - { - if (ReferenceEquals(this, other)) return true; - if (other == null) return false; - - if (other is RealmBeatmap b && Equals(b)) - return true; - - if (!string.IsNullOrEmpty(Hash) && !string.IsNullOrEmpty(other.Hash)) - return Hash == other.Hash; - - if (OnlineID > 0 && other.OnlineID > 0) - return other.OnlineID == OnlineID; - - return false; - } - - #endregion - public bool AudioEquals(RealmBeatmap? other) => other != null && BeatmapSet != null && other.BeatmapSet != null diff --git a/osu.Game/Models/RealmBeatmapSet.cs b/osu.Game/Models/RealmBeatmapSet.cs index 9cfa2de44d..1747cce67a 100644 --- a/osu.Game/Models/RealmBeatmapSet.cs +++ b/osu.Game/Models/RealmBeatmapSet.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Extensions; using Realms; #nullable enable @@ -61,21 +62,7 @@ namespace osu.Game.Models return ID == other.ID; } - public bool Equals(IBeatmapSetInfo? other) - { - if (ReferenceEquals(this, other)) return true; - if (other == null) return false; - - if (other is RealmBeatmapSet b && Equals(b)) - return true; - - if (OnlineID > 0 && other.OnlineID > 0) - return OnlineID == other.OnlineID; - - return false; - } - - public override string ToString() => Metadata?.ToString() ?? base.ToString(); + public override string ToString() => Metadata?.GetDisplayString() ?? base.ToString(); IEnumerable IBeatmapSetInfo.Beatmaps => Beatmaps; From 18f73b985b82de1d0eb07a9e827ba53065330008 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 14:38:01 +0900 Subject: [PATCH 27/90] Make `ModelExtensions` nullable enabled --- osu.Game/Extensions/ModelExtensions.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game/Extensions/ModelExtensions.cs b/osu.Game/Extensions/ModelExtensions.cs index 2545045d96..48a3bac112 100644 --- a/osu.Game/Extensions/ModelExtensions.cs +++ b/osu.Game/Extensions/ModelExtensions.cs @@ -7,6 +7,8 @@ using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Users; +#nullable enable + namespace osu.Game.Extensions { public static class ModelExtensions @@ -23,9 +25,9 @@ namespace osu.Game.Extensions /// extension method type inference rules cause this method to call itself and cause a stack overflow. /// /// - public static string GetDisplayString(this object model) + public static string GetDisplayString(this object? model) { - string result = null; + string? result = null; switch (model) { @@ -65,8 +67,11 @@ namespace osu.Game.Extensions /// The instance to compare. /// The other instance to compare against. /// Whether online IDs match. If either instance is missing an online ID, this will return false. - public static bool MatchesOnlineID(this IHasOnlineID instance, IHasOnlineID other) + public static bool MatchesOnlineID(this IHasOnlineID? instance, IHasOnlineID? other) { + if (instance == null || other == null) + return false; + if (instance.OnlineID < 0 || other.OnlineID < 0) return false; @@ -79,8 +84,11 @@ namespace osu.Game.Extensions /// The instance to compare. /// The other instance to compare against. /// Whether online IDs match. If either instance is missing an online ID, this will return false. - public static bool MatchesOnlineID(this IHasOnlineID instance, IHasOnlineID other) + public static bool MatchesOnlineID(this IHasOnlineID? instance, IHasOnlineID? other) { + if (instance == null || other == null) + return false; + if (instance.OnlineID < 0 || other.OnlineID < 0) return false; From 93fe57d39970ee9c9dcc7dc0a757c2537aa10e55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 14:43:31 +0900 Subject: [PATCH 28/90] Update tests to match new equality not including online ID checks --- osu.Game.Tests/NonVisual/BeatmapSetInfoEqualityTest.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/NonVisual/BeatmapSetInfoEqualityTest.cs b/osu.Game.Tests/NonVisual/BeatmapSetInfoEqualityTest.cs index 938edf07c6..534983f869 100644 --- a/osu.Game.Tests/NonVisual/BeatmapSetInfoEqualityTest.cs +++ b/osu.Game.Tests/NonVisual/BeatmapSetInfoEqualityTest.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using osu.Game.Beatmaps; +using osu.Game.Extensions; namespace osu.Game.Tests.NonVisual { @@ -15,7 +16,8 @@ namespace osu.Game.Tests.NonVisual var ourInfo = new BeatmapSetInfo { OnlineID = 123 }; var otherInfo = new BeatmapSetInfo { OnlineID = 123 }; - Assert.AreEqual(ourInfo, otherInfo); + Assert.AreNotEqual(ourInfo, otherInfo); + Assert.IsTrue(ourInfo.MatchesOnlineID(otherInfo)); } [Test] @@ -33,7 +35,8 @@ namespace osu.Game.Tests.NonVisual var ourInfo = new BeatmapSetInfo { ID = 123, OnlineID = 12 }; var otherInfo = new BeatmapSetInfo { OnlineID = 12 }; - Assert.AreEqual(ourInfo, otherInfo); + Assert.AreNotEqual(ourInfo, otherInfo); + Assert.IsTrue(ourInfo.MatchesOnlineID(otherInfo)); } [Test] From 285b161da73612b3ed0f4f9c189730b6483cf024 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 14:38:14 +0900 Subject: [PATCH 29/90] Update other usages of online ID comparisons to use new extension method --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 9 ++------- osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs | 9 ++------- osu.Game/Online/API/Requests/Responses/APIUser.cs | 3 ++- .../BeatmapListing/Panels/BeatmapPanelDownloadButton.cs | 3 ++- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 3 ++- osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs | 3 ++- osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs | 3 ++- 7 files changed, 14 insertions(+), 19 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 33098a2919..bbffaee87b 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -4,6 +4,7 @@ using System; using Newtonsoft.Json; using osu.Game.Beatmaps; +using osu.Game.Extensions; using osu.Game.Rulesets; #nullable enable @@ -106,13 +107,7 @@ namespace osu.Game.Online.API.Requests.Responses #region Implementation of IEquatable - public bool Equals(IBeatmapInfo? other) - { - if (OnlineID > 0 && other?.OnlineID > 0) - return other.OnlineID == OnlineID; - - return false; - } + public bool Equals(IBeatmapInfo? other) => this.MatchesOnlineID(other); #endregion } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index f4515c4258..ab8287871f 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Extensions; #nullable enable @@ -144,13 +145,7 @@ namespace osu.Game.Online.API.Requests.Responses #region Implementation of IEquatable - public bool Equals(IBeatmapSetInfo? other) - { - if (OnlineID > 0 && other?.OnlineID > 0) - return other.OnlineID == OnlineID; - - return false; - } + public bool Equals(IBeatmapInfo? other) => this.MatchesOnlineID(other); #endregion } diff --git a/osu.Game/Online/API/Requests/Responses/APIUser.cs b/osu.Game/Online/API/Requests/Responses/APIUser.cs index 49edfd036b..50f5d67796 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUser.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUser.cs @@ -7,6 +7,7 @@ using System.Linq; using JetBrains.Annotations; using Newtonsoft.Json; using osu.Framework.Bindables; +using osu.Game.Extensions; using osu.Game.Users; namespace osu.Game.Online.API.Requests.Responses @@ -240,6 +241,6 @@ namespace osu.Game.Online.API.Requests.Responses public int OnlineID => Id; - public bool Equals(APIUser other) => OnlineID == other?.OnlineID; + public bool Equals(APIUser other) => this.MatchesOnlineID(other); } } diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index 8c7846783d..1282a14c3d 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Extensions; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; @@ -80,7 +81,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels case DownloadState.LocallyAvailable: Predicate findPredicate = null; if (SelectedBeatmap.Value != null) - findPredicate = b => b.OnlineID == SelectedBeatmap.Value.OnlineID; + findPredicate = b => b.MatchesOnlineID(SelectedBeatmap.Value); game?.PresentBeatmap(beatmapSet, findPredicate); break; diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index b152375062..59e8e8db3c 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; +using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -166,7 +167,7 @@ namespace osu.Game.Overlays.BeatmapSet if (BeatmapSet != null) { Difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps - .Where(b => b.Ruleset.OnlineID == ruleset.Value?.OnlineID) + .Where(b => b.Ruleset.MatchesOnlineID(ruleset.Value)) .OrderBy(b => b.StarRating) .Select(b => new DifficultySelectorButton(b) { diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs index b3b3d1980b..e8cdc6913b 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; @@ -64,7 +65,7 @@ namespace osu.Game.Overlays.BeatmapSet BeatmapSet.BindValueChanged(setInfo => { - int beatmapsCount = setInfo.NewValue?.Beatmaps.Count(b => b.Ruleset.OnlineID == Value.OnlineID) ?? 0; + int beatmapsCount = setInfo.NewValue?.Beatmaps.Count(b => b.Ruleset.MatchesOnlineID(Value)) ?? 0; count.Text = beatmapsCount.ToString(); countContainer.FadeTo(beatmapsCount > 0 ? 1 : 0); diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index 22537c3ce0..56863cafad 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Screens; +using osu.Game.Extensions; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Scoring; @@ -32,7 +33,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void load(IBindable ruleset) { // Sanity checks to ensure that PlaylistsPlayer matches the settings for the current PlaylistItem - if (Beatmap.Value.BeatmapInfo.OnlineID != PlaylistItem.Beatmap.Value.OnlineID) + if (!Beatmap.Value.BeatmapInfo.MatchesOnlineID(PlaylistItem.Beatmap.Value)) throw new InvalidOperationException("Current Beatmap does not match PlaylistItem's Beatmap"); if (ruleset.Value.ID != PlaylistItem.Ruleset.Value.ID) From 611b9fe9422e5e33ae7845b911dceda44a7b6b24 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 14:50:09 +0900 Subject: [PATCH 30/90] Remove now unused implementations of interface equality --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 7 ------- osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs | 7 ------- 2 files changed, 14 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index bbffaee87b..2560502173 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -4,7 +4,6 @@ using System; using Newtonsoft.Json; using osu.Game.Beatmaps; -using osu.Game.Extensions; using osu.Game.Rulesets; #nullable enable @@ -104,11 +103,5 @@ namespace osu.Game.Online.API.Requests.Responses public string Hash => throw new NotImplementedException(); #endregion - - #region Implementation of IEquatable - - public bool Equals(IBeatmapInfo? other) => this.MatchesOnlineID(other); - - #endregion } } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index ab8287871f..168e9d5d51 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Database; -using osu.Game.Extensions; #nullable enable @@ -142,11 +141,5 @@ namespace osu.Game.Online.API.Requests.Responses double IBeatmapSetInfo.MaxBPM => BPM; #endregion - - #region Implementation of IEquatable - - public bool Equals(IBeatmapInfo? other) => this.MatchesOnlineID(other); - - #endregion } } From 73cb80d8a44a0c69717a47acdb2adb09da341b1a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 16:00:45 +0900 Subject: [PATCH 31/90] Update one more missed usage --- osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 85efdcef1a..32ae7cf859 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -82,7 +82,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { bool matchingFilter = true; - matchingFilter &= r.Room.Playlist.Count == 0 || criteria.Ruleset == null || r.Room.Playlist.Any(i => i.Ruleset.Value.Equals(criteria.Ruleset)); + matchingFilter &= r.Room.Playlist.Count == 0 || criteria.Ruleset == null || r.Room.Playlist.Any(i => i.Ruleset.Value.MatchesOnlineID(criteria.Ruleset)); if (!string.IsNullOrEmpty(criteria.SearchString)) matchingFilter &= r.FilterTerms.Any(term => term.Contains(criteria.SearchString, StringComparison.InvariantCultureIgnoreCase)); From 1988b263c60331f02fd973eee2da70f077ea78df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 16:02:08 +0900 Subject: [PATCH 32/90] One more missed usage.. --- osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index 56863cafad..35d417520e 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists if (!Beatmap.Value.BeatmapInfo.MatchesOnlineID(PlaylistItem.Beatmap.Value)) throw new InvalidOperationException("Current Beatmap does not match PlaylistItem's Beatmap"); - if (ruleset.Value.ID != PlaylistItem.Ruleset.Value.ID) + if (!ruleset.Value.MatchesOnlineID(PlaylistItem.Ruleset.Value)) throw new InvalidOperationException("Current Ruleset does not match PlaylistItem's Ruleset"); if (!PlaylistItem.RequiredMods.All(m => Mods.Value.Any(m.Equals))) From 907499f73afacb8cb419fbc2553d04cc26c0bd57 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Nov 2021 16:06:29 +0900 Subject: [PATCH 33/90] Add json properties to difficulty attributes --- .../Difficulty/CatchDifficultyAttributes.cs | 2 ++ .../Difficulty/ManiaDifficultyAttributes.cs | 4 ++++ .../Difficulty/OsuDifficultyAttributes.cs | 20 +++++++++++++++++++ .../Difficulty/TaikoDifficultyAttributes.cs | 10 ++++++++++ .../Difficulty/DifficultyAttributes.cs | 7 +++++++ 5 files changed, 43 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs index 4e05b1e3e0..57305ce62b 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs @@ -1,12 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using Newtonsoft.Json; using osu.Game.Rulesets.Difficulty; namespace osu.Game.Rulesets.Catch.Difficulty { public class CatchDifficultyAttributes : DifficultyAttributes { + [JsonProperty("approach_rate")] public double ApproachRate { get; set; } } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs index 628d77107f..dd966ca859 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs @@ -1,13 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using Newtonsoft.Json; using osu.Game.Rulesets.Difficulty; namespace osu.Game.Rulesets.Mania.Difficulty { public class ManiaDifficultyAttributes : DifficultyAttributes { + [JsonProperty("great_hit_window")] public double GreatHitWindow { get; set; } + + [JsonProperty("score_multiplier")] public double ScoreMultiplier { get; set; } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index e0a216c8e0..323cda76d4 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -1,21 +1,41 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using Newtonsoft.Json; using osu.Game.Rulesets.Difficulty; namespace osu.Game.Rulesets.Osu.Difficulty { public class OsuDifficultyAttributes : DifficultyAttributes { + [JsonProperty("aim_strain")] public double AimStrain { get; set; } + + [JsonProperty("speed_strain")] public double SpeedStrain { get; set; } + + [JsonProperty("flashlight_rating")] public double FlashlightRating { get; set; } + + [JsonProperty("slider_factor")] public double SliderFactor { get; set; } + + [JsonProperty("approach_rate")] public double ApproachRate { get; set; } + + [JsonProperty("overall_difficulty")] public double OverallDifficulty { get; set; } + + [JsonProperty("drain_rate")] public double DrainRate { get; set; } + + [JsonProperty("hit_circle_count")] public int HitCircleCount { get; set; } + + [JsonProperty("slider_count")] public int SliderCount { get; set; } + + [JsonProperty("spinner_count")] public int SpinnerCount { get; set; } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs index 36adbd5a5b..171ac89e6f 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs @@ -1,16 +1,26 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using Newtonsoft.Json; using osu.Game.Rulesets.Difficulty; namespace osu.Game.Rulesets.Taiko.Difficulty { public class TaikoDifficultyAttributes : DifficultyAttributes { + [JsonProperty("stamina_strain")] public double StaminaStrain { get; set; } + + [JsonProperty("rhythm_strain")] public double RhythmStrain { get; set; } + + [JsonProperty("colour_strain")] public double ColourStrain { get; set; } + + [JsonProperty("approach_rate")] public double ApproachRate { get; set; } + + [JsonProperty("great_hit_window")] public double GreatHitWindow { get; set; } } } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index 6bb780a68b..fb67396718 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using Newtonsoft.Json; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; @@ -8,10 +9,16 @@ namespace osu.Game.Rulesets.Difficulty { public class DifficultyAttributes { + [JsonIgnore] public Mod[] Mods { get; set; } + + [JsonIgnore] public Skill[] Skills { get; set; } + [JsonProperty("star_rating")] public double StarRating { get; set; } + + [JsonProperty("max_combo")] public int MaxCombo { get; set; } public DifficultyAttributes() From 0cfd6fdf0455224de5d8a98be1aaae954e0f82c0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Nov 2021 16:06:46 +0900 Subject: [PATCH 34/90] Add to/from database mapping functions to difficulty attributes --- .../Difficulty/CatchDifficultyAttributes.cs | 21 +++++++++++ .../Difficulty/ManiaDifficultyAttributes.cs | 21 +++++++++++ .../Difficulty/OsuDifficultyAttributes.cs | 37 +++++++++++++++++++ .../Difficulty/TaikoDifficultyAttributes.cs | 20 ++++++++++ .../Difficulty/DifficultyAttributes.cs | 8 ++++ 5 files changed, 107 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs index 57305ce62b..3f03fc102b 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using Newtonsoft.Json; using osu.Game.Rulesets.Difficulty; @@ -10,5 +11,25 @@ namespace osu.Game.Rulesets.Catch.Difficulty { [JsonProperty("approach_rate")] public double ApproachRate { get; set; } + + public override IEnumerable<(int attributeId, object value)> ToDatabase() + { + foreach (var v in base.ToDatabase()) + yield return v; + + // Todo: Catch should not output star rating in the 'aim' attribute. + yield return (1, StarRating); + yield return (7, ApproachRate); + yield return (9, MaxCombo); + } + + public override void FromDatabase(IReadOnlyDictionary values, int hitCircleCount, int spinnerCount) + { + base.FromDatabase(values, hitCircleCount, spinnerCount); + + StarRating = values[1]; + ApproachRate = values[7]; + MaxCombo = (int)values[9]; + } } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs index dd966ca859..d1cb48f728 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using Newtonsoft.Json; using osu.Game.Rulesets.Difficulty; @@ -13,5 +14,25 @@ namespace osu.Game.Rulesets.Mania.Difficulty [JsonProperty("score_multiplier")] public double ScoreMultiplier { get; set; } + + public override IEnumerable<(int attributeId, object value)> ToDatabase() + { + foreach (var v in base.ToDatabase()) + yield return v; + + // Todo: Mania doesn't output MaxCombo attribute for some reason. + yield return (11, StarRating); + yield return (13, GreatHitWindow); + yield return (15, ScoreMultiplier); + } + + public override void FromDatabase(IReadOnlyDictionary values, int hitCircleCount, int spinnerCount) + { + base.FromDatabase(values, hitCircleCount, spinnerCount); + + StarRating = values[11]; + GreatHitWindow = values[13]; + ScoreMultiplier = values[15]; + } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 323cda76d4..75d1eed1ab 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -1,8 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Osu.Difficulty { @@ -37,5 +40,39 @@ namespace osu.Game.Rulesets.Osu.Difficulty [JsonProperty("spinner_count")] public int SpinnerCount { get; set; } + + public override IEnumerable<(int attributeId, object value)> ToDatabase() + { + foreach (var v in base.ToDatabase()) + yield return v; + + yield return (1, AimStrain); + yield return (3, SpeedStrain); + yield return (5, OverallDifficulty); + yield return (7, ApproachRate); + yield return (9, MaxCombo); + yield return (11, StarRating); + + if (Mods.Any(m => m is ModFlashlight)) + yield return (17, FlashlightRating); + + yield return (19, SliderFactor); + } + + public override void FromDatabase(IReadOnlyDictionary values, int hitCircleCount, int spinnerCount) + { + base.FromDatabase(values, hitCircleCount, spinnerCount); + + AimStrain = values[1]; + SpeedStrain = values[3]; + OverallDifficulty = values[5]; + ApproachRate = values[7]; + MaxCombo = (int)values[9]; + StarRating = values[11]; + FlashlightRating = values.GetValueOrDefault(17); + SliderFactor = values[19]; + HitCircleCount = hitCircleCount; + SpinnerCount = spinnerCount; + } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs index 171ac89e6f..b2208ea617 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using Newtonsoft.Json; using osu.Game.Rulesets.Difficulty; @@ -22,5 +23,24 @@ namespace osu.Game.Rulesets.Taiko.Difficulty [JsonProperty("great_hit_window")] public double GreatHitWindow { get; set; } + + public override IEnumerable<(int attributeId, object value)> ToDatabase() + { + foreach (var v in base.ToDatabase()) + yield return v; + + yield return (9, MaxCombo); + yield return (11, StarRating); + yield return (13, GreatHitWindow); + } + + public override void FromDatabase(IReadOnlyDictionary values, int hitCircleCount, int spinnerCount) + { + base.FromDatabase(values, hitCircleCount, spinnerCount); + + MaxCombo = (int)values[9]; + StarRating = values[11]; + GreatHitWindow = values[13]; + } } } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index fb67396718..96d52833e0 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; @@ -31,5 +33,11 @@ namespace osu.Game.Rulesets.Difficulty Skills = skills; StarRating = starRating; } + + public virtual IEnumerable<(int attributeId, object value)> ToDatabase() => Enumerable.Empty<(int, object)>(); + + public virtual void FromDatabase(IReadOnlyDictionary values, int hitCircleCount, int spinnerCount) + { + } } } From 5489b19c57f1f6d547d9d15efbdc2cd001ca062e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 16:13:03 +0900 Subject: [PATCH 35/90] Update `PlaylistItem` to use `IRulesetInfo` --- osu.Game/Online/Rooms/MultiplayerScore.cs | 5 +++-- osu.Game/Online/Rooms/PlaylistItem.cs | 4 ++-- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 2 +- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 6 +++++- .../Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs | 6 +++++- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/Rooms/MultiplayerScore.cs b/osu.Game/Online/Rooms/MultiplayerScore.cs index cfb81f0bad..7bc3377ad9 100644 --- a/osu.Game/Online/Rooms/MultiplayerScore.cs +++ b/osu.Game/Online/Rooms/MultiplayerScore.cs @@ -10,6 +10,7 @@ using Newtonsoft.Json.Converters; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; @@ -62,7 +63,7 @@ namespace osu.Game.Online.Rooms [CanBeNull] public MultiplayerScoresAround ScoresAround { get; set; } - public ScoreInfo CreateScoreInfo(PlaylistItem playlistItem, [NotNull] BeatmapInfo beatmap) + public ScoreInfo CreateScoreInfo(RulesetStore rulesets, PlaylistItem playlistItem, [NotNull] BeatmapInfo beatmap) { var rulesetInstance = playlistItem.Ruleset.Value.CreateInstance(); @@ -73,7 +74,7 @@ namespace osu.Game.Online.Rooms MaxCombo = MaxCombo, BeatmapInfo = beatmap, BeatmapInfoID = playlistItem.BeatmapID, - Ruleset = playlistItem.Ruleset.Value, + Ruleset = rulesets.GetRuleset(playlistItem.RulesetID), RulesetID = playlistItem.RulesetID, Statistics = Statistics, User = User, diff --git a/osu.Game/Online/Rooms/PlaylistItem.cs b/osu.Game/Online/Rooms/PlaylistItem.cs index cb550281a9..6f3d13b224 100644 --- a/osu.Game/Online/Rooms/PlaylistItem.cs +++ b/osu.Game/Online/Rooms/PlaylistItem.cs @@ -34,7 +34,7 @@ namespace osu.Game.Online.Rooms public readonly Bindable Beatmap = new Bindable(); [JsonIgnore] - public readonly Bindable Ruleset = new Bindable(); + public readonly Bindable Ruleset = new Bindable(); [JsonIgnore] public readonly BindableList AllowedMods = new BindableList(); @@ -66,7 +66,7 @@ namespace osu.Game.Online.Rooms public PlaylistItem() { Beatmap.BindValueChanged(beatmap => BeatmapID = beatmap.NewValue?.OnlineID ?? -1); - Ruleset.BindValueChanged(ruleset => RulesetID = ruleset.NewValue?.ID ?? 0); + Ruleset.BindValueChanged(ruleset => RulesetID = ruleset.NewValue?.OnlineID ?? 0); } public void MapObjects(RulesetStore rulesets) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 85cee46a29..817fb07d4b 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.OnlinePlay private ModDisplay modDisplay; private readonly Bindable beatmap = new Bindable(); - private readonly Bindable ruleset = new Bindable(); + private readonly Bindable ruleset = new Bindable(); private readonly BindableList requiredMods = new BindableList(); public readonly PlaylistItem Item; diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index dc928d90e9..323d38c881 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -18,6 +18,7 @@ using osu.Game.Beatmaps; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Overlays.Mods; +using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.OnlinePlay.Match.Components; @@ -59,6 +60,9 @@ namespace osu.Game.Screens.OnlinePlay.Match [Resolved] private BeatmapManager beatmapManager { get; set; } + [Resolved] + private RulesetStore rulesets { get; set; } + [Resolved(canBeNull: true)] protected OnlinePlayScreen ParentScreen { get; private set; } @@ -344,7 +348,7 @@ namespace osu.Game.Screens.OnlinePlay.Match UpdateMods(); - Ruleset.Value = selected.Ruleset.Value; + Ruleset.Value = rulesets.GetRuleset(selected.RulesetID); if (!selected.AllowedMods.Any()) { diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs index 34698fccab..aed3635cbc 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsResultsScreen.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.Rooms; +using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Screens.Ranking; @@ -35,6 +36,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [Resolved] private ScoreManager scoreManager { get; set; } + [Resolved] + private RulesetStore rulesets { get; set; } + public PlaylistsResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, bool allowRetry, bool allowWatchingReplay = true) : base(score, allowRetry, allowWatchingReplay) { @@ -169,7 +173,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists /// An optional pivot around which the scores were retrieved. private void performSuccessCallback([NotNull] Action> callback, [NotNull] List scores, [CanBeNull] MultiplayerScores pivot = null) { - var scoreInfos = scores.Select(s => s.CreateScoreInfo(playlistItem, Beatmap.Value.BeatmapInfo)).ToArray(); + var scoreInfos = scores.Select(s => s.CreateScoreInfo(rulesets, playlistItem, Beatmap.Value.BeatmapInfo)).ToArray(); // Score panels calculate total score before displaying, which can take some time. In order to count that calculation as part of the loading spinner display duration, // calculate the total scores locally before invoking the success callback. From 45382a81277ae4f5663bded357cc8d9920eacef2 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Nov 2021 16:32:25 +0900 Subject: [PATCH 36/90] Ignore some properties --- .../Difficulty/OsuDifficultyAttributes.cs | 8 ++++---- osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 75d1eed1ab..0d76cb855b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -29,16 +29,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty [JsonProperty("overall_difficulty")] public double OverallDifficulty { get; set; } - [JsonProperty("drain_rate")] + [JsonIgnore] public double DrainRate { get; set; } - [JsonProperty("hit_circle_count")] + [JsonIgnore] public int HitCircleCount { get; set; } - [JsonProperty("slider_count")] + [JsonIgnore] public int SliderCount { get; set; } - [JsonProperty("spinner_count")] + [JsonIgnore] public int SpinnerCount { get; set; } public override IEnumerable<(int attributeId, object value)> ToDatabase() diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index 96d52833e0..6a3b97ab1d 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -17,10 +17,10 @@ namespace osu.Game.Rulesets.Difficulty [JsonIgnore] public Skill[] Skills { get; set; } - [JsonProperty("star_rating")] + [JsonProperty("star_rating", Order = -3)] public double StarRating { get; set; } - [JsonProperty("max_combo")] + [JsonProperty("max_combo", Order = -2)] public int MaxCombo { get; set; } public DifficultyAttributes() From 21c0882b74d3267361ff2c9b55cf00b7408435e6 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Nov 2021 17:24:53 +0900 Subject: [PATCH 37/90] Don't serialize FL rating without FL mod --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 0d76cb855b..089f163dfb 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using Newtonsoft.Json; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; @@ -74,5 +75,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty HitCircleCount = hitCircleCount; SpinnerCount = spinnerCount; } + + [UsedImplicitly] + public bool ShouldSerializeFlashlightRating() => Mods.OfType().Any(); } } From c8a01c35f76fd0a85d242aabb6f788f7d8bd8634 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Nov 2021 18:11:07 +0900 Subject: [PATCH 38/90] Remove extra members from FromDatabaseAttributes --- .../Difficulty/CatchDifficultyAttributes.cs | 8 ++++---- .../Difficulty/ManiaDifficultyAttributes.cs | 8 ++++---- .../Difficulty/OsuDifficultyAttributes.cs | 10 ++++------ .../Difficulty/TaikoDifficultyAttributes.cs | 8 ++++---- osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs | 4 ++-- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs index 3f03fc102b..ca63e87f8d 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs @@ -12,9 +12,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty [JsonProperty("approach_rate")] public double ApproachRate { get; set; } - public override IEnumerable<(int attributeId, object value)> ToDatabase() + public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() { - foreach (var v in base.ToDatabase()) + foreach (var v in base.ToDatabaseAttributes()) yield return v; // Todo: Catch should not output star rating in the 'aim' attribute. @@ -23,9 +23,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty yield return (9, MaxCombo); } - public override void FromDatabase(IReadOnlyDictionary values, int hitCircleCount, int spinnerCount) + public override void FromDatabaseAttributes(IReadOnlyDictionary values) { - base.FromDatabase(values, hitCircleCount, spinnerCount); + base.FromDatabaseAttributes(values); StarRating = values[1]; ApproachRate = values[7]; diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs index d1cb48f728..1e663fe103 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs @@ -15,9 +15,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty [JsonProperty("score_multiplier")] public double ScoreMultiplier { get; set; } - public override IEnumerable<(int attributeId, object value)> ToDatabase() + public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() { - foreach (var v in base.ToDatabase()) + foreach (var v in base.ToDatabaseAttributes()) yield return v; // Todo: Mania doesn't output MaxCombo attribute for some reason. @@ -26,9 +26,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty yield return (15, ScoreMultiplier); } - public override void FromDatabase(IReadOnlyDictionary values, int hitCircleCount, int spinnerCount) + public override void FromDatabaseAttributes(IReadOnlyDictionary values) { - base.FromDatabase(values, hitCircleCount, spinnerCount); + base.FromDatabaseAttributes(values); StarRating = values[11]; GreatHitWindow = values[13]; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 089f163dfb..88f26db93c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -42,9 +42,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty [JsonIgnore] public int SpinnerCount { get; set; } - public override IEnumerable<(int attributeId, object value)> ToDatabase() + public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() { - foreach (var v in base.ToDatabase()) + foreach (var v in base.ToDatabaseAttributes()) yield return v; yield return (1, AimStrain); @@ -60,9 +60,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty yield return (19, SliderFactor); } - public override void FromDatabase(IReadOnlyDictionary values, int hitCircleCount, int spinnerCount) + public override void FromDatabaseAttributes(IReadOnlyDictionary values) { - base.FromDatabase(values, hitCircleCount, spinnerCount); + base.FromDatabaseAttributes(values); AimStrain = values[1]; SpeedStrain = values[3]; @@ -72,8 +72,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty StarRating = values[11]; FlashlightRating = values.GetValueOrDefault(17); SliderFactor = values[19]; - HitCircleCount = hitCircleCount; - SpinnerCount = spinnerCount; } [UsedImplicitly] diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs index b2208ea617..a6057d7952 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs @@ -24,9 +24,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty [JsonProperty("great_hit_window")] public double GreatHitWindow { get; set; } - public override IEnumerable<(int attributeId, object value)> ToDatabase() + public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() { - foreach (var v in base.ToDatabase()) + foreach (var v in base.ToDatabaseAttributes()) yield return v; yield return (9, MaxCombo); @@ -34,9 +34,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty yield return (13, GreatHitWindow); } - public override void FromDatabase(IReadOnlyDictionary values, int hitCircleCount, int spinnerCount) + public override void FromDatabaseAttributes(IReadOnlyDictionary values) { - base.FromDatabase(values, hitCircleCount, spinnerCount); + base.FromDatabaseAttributes(values); MaxCombo = (int)values[9]; StarRating = values[11]; diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index 6a3b97ab1d..e25a8476f0 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -34,9 +34,9 @@ namespace osu.Game.Rulesets.Difficulty StarRating = starRating; } - public virtual IEnumerable<(int attributeId, object value)> ToDatabase() => Enumerable.Empty<(int, object)>(); + public virtual IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() => Enumerable.Empty<(int, object)>(); - public virtual void FromDatabase(IReadOnlyDictionary values, int hitCircleCount, int spinnerCount) + public virtual void FromDatabaseAttributes(IReadOnlyDictionary values) { } } From a0e25d18cd5f809bfade52754a652d318d47a805 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 18:24:00 +0900 Subject: [PATCH 39/90] Expose more of `WorkingBeatmap` via interface --- osu.Game/Beatmaps/IWorkingBeatmap.cs | 65 +++++++++++++++++++++++++--- osu.Game/Beatmaps/WorkingBeatmap.cs | 21 ++++----- 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index a916b37b85..22a922db59 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -17,33 +17,69 @@ namespace osu.Game.Beatmaps { public interface IWorkingBeatmap { + IBeatmapInfo BeatmapInfo { get; } + + IBeatmapSetInfo BeatmapSetInfo => BeatmapInfo.BeatmapSet; + + IBeatmapMetadataInfo Metadata => BeatmapInfo.Metadata; + /// - /// Retrieves the which this represents. + /// Whether the Beatmap has finished loading. + /// + public bool BeatmapLoaded { get; } + + /// + /// Whether the Background has finished loading. + /// + public bool BackgroundLoaded { get; } + + /// + /// Whether the Waveform has finished loading. + /// + public bool WaveformLoaded { get; } + + /// + /// Whether the Storyboard has finished loading. + /// + public bool StoryboardLoaded { get; } + + /// + /// Whether the Skin has finished loading. + /// + public bool SkinLoaded { get; } + + /// + /// Whether the Track has finished loading. + /// + public bool TrackLoaded { get; } + + /// + /// Retrieves the which this represents. /// IBeatmap Beatmap { get; } /// - /// Retrieves the background for this . + /// Retrieves the background for this . /// Texture Background { get; } /// - /// Retrieves the for the of this . + /// Retrieves the for the of this . /// Waveform Waveform { get; } /// - /// Retrieves the which this provides. + /// Retrieves the which this provides. /// Storyboard Storyboard { get; } /// - /// Retrieves the which this provides. + /// Retrieves the which this provides. /// ISkin Skin { get; } /// - /// Retrieves the which this has loaded. + /// Retrieves the which this has loaded. /// Track Track { get; } @@ -67,7 +103,7 @@ namespace osu.Game.Beatmaps /// /// /// In a standard game context, the loading of the track is managed solely by MusicController, which will - /// automatically load the track of the current global IBindable WorkingBeatmap. + /// automatically load the track of the current global IBindable IWorkingBeatmap. /// As such, this method should only be called in very special scenarios, such as external tests or apps which are /// outside of the game context. /// @@ -79,5 +115,20 @@ namespace osu.Game.Beatmaps /// /// The storage path to the file. Stream GetStream(string storagePath); + + /// + /// Beings loading the contents of this asynchronously. + /// + public void BeginAsyncLoad(); + + /// + /// Cancels the asynchronous loading of the contents of this . + /// + public void CancelAsyncLoad(); + + /// + /// Reads the correct track restart point from beatmap metadata and sets looping to enabled. + /// + void PrepareTrackForPreviewLooping(); } } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index d2c0f7de0f..51eea94d3a 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -28,8 +28,10 @@ namespace osu.Game.Beatmaps { public readonly BeatmapInfo BeatmapInfo; + // ReSharper disable once FieldHidesInterfacePropertyWithDefaultImplementation public readonly BeatmapSetInfo BeatmapSetInfo; + // ReSharper disable once FieldHidesInterfacePropertyWithDefaultImplementation public readonly BeatmapMetadata Metadata; protected AudioManager AudioManager { get; } @@ -89,6 +91,9 @@ namespace osu.Game.Beatmaps var rulesetInstance = ruleset.CreateInstance(); + if (rulesetInstance == null) + throw new RulesetLoadException("Creating ruleset instance failed when attempting to create playable beatmap."); + IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance); // Check if the beatmap can be converted @@ -176,17 +181,8 @@ namespace osu.Game.Beatmaps private CancellationTokenSource loadCancellation = new CancellationTokenSource(); - /// - /// Beings loading the contents of this asynchronously. - /// - public void BeginAsyncLoad() - { - loadBeatmapAsync(); - } + public void BeginAsyncLoad() => loadBeatmapAsync(); - /// - /// Cancels the asynchronous loading of the contents of this . - /// public void CancelAsyncLoad() { lock (beatmapFetchLock) @@ -234,6 +230,8 @@ namespace osu.Game.Beatmaps public virtual bool BeatmapLoaded => beatmapLoadTask?.IsCompleted ?? false; + IBeatmapInfo IWorkingBeatmap.BeatmapInfo => BeatmapInfo; + public IBeatmap Beatmap { get @@ -273,9 +271,6 @@ namespace osu.Game.Beatmaps [NotNull] public Track LoadTrack() => loadedTrack = GetBeatmapTrack() ?? GetVirtualTrack(1000); - /// - /// Reads the correct track restart point from beatmap metadata and sets looping to enabled. - /// public void PrepareTrackForPreviewLooping() { Track.Looping = true; From 62d670a3cac537459ebe3cfdcaf66a4df3cfc328 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 18:19:23 +0900 Subject: [PATCH 40/90] Update `DifficultyCalculator` to take an `IWorkingBeatmap` --- .../EmptyFreeformDifficultyCalculator.cs | 2 +- .../EmptyFreeformRuleset.cs | 2 +- .../PippidonDifficultyCalculator.cs | 2 +- .../osu.Game.Rulesets.Pippidon/PippidonRuleset.cs | 2 +- .../EmptyScrollingDifficultyCalculator.cs | 2 +- .../EmptyScrollingRuleset.cs | 2 +- .../PippidonDifficultyCalculator.cs | 2 +- .../osu.Game.Rulesets.Pippidon/PippidonRuleset.cs | 2 +- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- .../Difficulty/ManiaDifficultyCalculator.cs | 7 ++++--- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- .../Difficulty/OsuDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- .../Difficulty/TaikoDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs | 2 +- osu.Game.Tests/Online/TestAPIModJsonSerialization.cs | 2 +- .../Online/TestAPIModMessagePackSerialization.cs | 2 +- osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs | 2 +- .../Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs | 2 +- .../Visual/UserInterface/TestSceneModSettings.cs | 2 +- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 +- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 4 ++-- osu.Game/Rulesets/Ruleset.cs | 4 ++-- 27 files changed, 32 insertions(+), 31 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs index a80f1178b6..34124a022d 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.EmptyFreeform { public class EmptyFreeformDifficultyCalculator : DifficultyCalculator { - public EmptyFreeformDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + public EmptyFreeformDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformRuleset.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformRuleset.cs index 96675e3e99..bd928404d5 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformRuleset.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformRuleset.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.EmptyFreeform public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new EmptyFreeformBeatmapConverter(beatmap, this); - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new EmptyFreeformDifficultyCalculator(this, beatmap); public override IEnumerable GetModsFor(ModType type) diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index 290148d14b..ef484e27bc 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Pippidon { public class PippidonDifficultyCalculator : DifficultyCalculator { - public PippidonDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + public PippidonDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs index 89fed791cd..5f463e71f8 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Pippidon public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new PippidonBeatmapConverter(beatmap, this); - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new PippidonDifficultyCalculator(this, beatmap); public override IEnumerable GetModsFor(ModType type) diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs index f557a4c754..37c2b9f1eb 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.EmptyScrolling { public class EmptyScrollingDifficultyCalculator : DifficultyCalculator { - public EmptyScrollingDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + public EmptyScrollingDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingRuleset.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingRuleset.cs index c1d4de52b7..940eaa1a55 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingRuleset.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingRuleset.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.EmptyScrolling public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new EmptyScrollingBeatmapConverter(beatmap, this); - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new EmptyScrollingDifficultyCalculator(this, beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new EmptyScrollingDifficultyCalculator(this, beatmap); public override IEnumerable GetModsFor(ModType type) { diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index 290148d14b..ef484e27bc 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Pippidon { public class PippidonDifficultyCalculator : DifficultyCalculator { - public PippidonDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + public PippidonDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs index ede00f1510..4b1ef7c9a8 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Pippidon public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new PippidonBeatmapConverter(beatmap, this); - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new PippidonDifficultyCalculator(this, beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new PippidonDifficultyCalculator(this, beatmap); public override IEnumerable GetModsFor(ModType type) { diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 93240d312b..c879398d23 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -178,7 +178,7 @@ namespace osu.Game.Rulesets.Catch return base.GetDisplayNameForHitResult(result); } - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap); public override ISkin CreateLegacySkinProvider(ISkin skin, IBeatmap beatmap) => new CatchLegacySkinTransformer(skin); diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 03a76f10ef..50ddbaba71 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty private float halfCatcherWidth; - public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + public CatchDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index aee3268544..640fdac225 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Extensions; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; @@ -28,11 +29,11 @@ namespace osu.Game.Rulesets.Mania.Difficulty private readonly bool isForCurrentRuleset; private readonly double originalOverallDifficulty; - public ManiaDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + public ManiaDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { - isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo); - originalOverallDifficulty = beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty; + isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.MatchesOnlineID(ruleset); + originalOverallDifficulty = beatmap.BeatmapInfo.Difficulty.OverallDifficulty; } protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index a9b16c61d4..d7d0fed846 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -272,7 +272,7 @@ namespace osu.Game.Rulesets.Mania public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetMania }; - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap); public int LegacyID => 3; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 558ddc16ef..892b8da527 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty private const double difficulty_multiplier = 0.0675; private double hitWindowGreat; - public OsuDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + public OsuDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index ee4712c3b8..6d50dc0a06 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -208,7 +208,7 @@ namespace osu.Game.Rulesets.Osu public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetOsu }; - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(this, beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(this, beatmap); public override PerformanceCalculator CreatePerformanceCalculator(DifficultyAttributes attributes, ScoreInfo score) => new OsuPerformanceCalculator(this, attributes, score); diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 7dd47e804b..31d7304184 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty private const double colour_skill_multiplier = 0.01; private const double stamina_skill_multiplier = 0.02; - public TaikoDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + public TaikoDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index adc924ba38..e4dea77377 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -168,7 +168,7 @@ namespace osu.Game.Rulesets.Taiko public override HitObjectComposer CreateHitObjectComposer() => new TaikoHitObjectComposer(this); - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(this, beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TaikoDifficultyCalculator(this, beatmap); public override PerformanceCalculator CreatePerformanceCalculator(DifficultyAttributes attributes, ScoreInfo score) => new TaikoPerformanceCalculator(this, attributes, score); diff --git a/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs b/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs index fd620a0e95..85b4688eb9 100644 --- a/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs +++ b/osu.Game.Tests/Mods/ModDifficultyAdjustTest.cs @@ -156,7 +156,7 @@ namespace osu.Game.Tests.Mods throw new System.NotImplementedException(); } - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) { throw new System.NotImplementedException(); } diff --git a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs index 656e333073..8378b33b3d 100644 --- a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs @@ -128,7 +128,7 @@ namespace osu.Game.Tests.Online public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException(); - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException(); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException(); public override string Description { get; } = string.Empty; public override string ShortName { get; } = string.Empty; diff --git a/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs index 0462e9feb5..69e19826fd 100644 --- a/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs @@ -90,7 +90,7 @@ namespace osu.Game.Tests.Online public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new System.NotImplementedException(); - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new System.NotImplementedException(); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new System.NotImplementedException(); public override string Description { get; } = string.Empty; public override string ShortName { get; } = string.Empty; diff --git a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs index 8c6932e792..bb9b705c7e 100644 --- a/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs +++ b/osu.Game.Tests/Testing/TestSceneRulesetDependencies.cs @@ -79,7 +79,7 @@ namespace osu.Game.Tests.Testing public override IEnumerable GetModsFor(ModType type) => Array.Empty(); public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => null; public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => null; - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => null; + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => null; } private class TestRulesetConfigManager : IRulesetConfigManager diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 3f10d7892d..e1755b8fc3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -293,7 +293,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TestBeatmapConverter(beatmap, null); - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException(); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException(); public override string Description { get; } = string.Empty; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs index 0a39d94027..883b8a1ae0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new System.NotImplementedException(); - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new System.NotImplementedException(); public override IEnumerable GetDefaultKeyBindings(int variant = 0) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs index 17a009a2ce..911fffbe7f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs @@ -182,7 +182,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TestBeatmapConverter(beatmap, this); - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException(); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException(); public override string Description { get; } = string.Empty; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index da0fa5d76d..4bb5e29589 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -199,7 +199,7 @@ namespace osu.Game.Tests.Visual.UserInterface public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException(); - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException(); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException(); public override string Description { get; } = "test"; public override string ShortName { get; } = "tst"; diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index acfd01a3c8..fcb44c462d 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -69,7 +69,7 @@ namespace osu.Game.Beatmaps public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new DummyBeatmapConverter { Beatmap = beatmap }; - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => null; + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => null; public override string Description => "dummy"; diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 5b4284dc2f..f9855a819f 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -27,9 +27,9 @@ namespace osu.Game.Rulesets.Difficulty private double clockRate; private readonly Ruleset ruleset; - private readonly WorkingBeatmap beatmap; + private readonly IWorkingBeatmap beatmap; - protected DifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + protected DifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) { this.ruleset = ruleset; this.beatmap = beatmap; diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index b0c3836774..ade763eed8 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -222,7 +222,7 @@ namespace osu.Game.Rulesets /// The . public virtual IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => null; - public abstract DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap); + public abstract DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap); /// /// Optionally creates a to generate performance data from the provided score. @@ -240,7 +240,7 @@ namespace osu.Game.Rulesets /// The score to be processed. /// A performance calculator instance for the provided score. [CanBeNull] - public PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) + public PerformanceCalculator CreatePerformanceCalculator(IWorkingBeatmap beatmap, ScoreInfo score) { var difficultyCalculator = CreateDifficultyCalculator(beatmap); var difficultyAttributes = difficultyCalculator.Calculate(score.Mods); From 369b4ba789be41026c54979b24872284fb536e13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 18:23:03 +0900 Subject: [PATCH 41/90] Update `DifficultyCalculator` to take an `IRulesetInfo` --- .../EmptyFreeformDifficultyCalculator.cs | 2 +- .../osu.Game.Rulesets.EmptyFreeform/EmptyFreeformRuleset.cs | 2 +- .../PippidonDifficultyCalculator.cs | 2 +- .../osu.Game.Rulesets.Pippidon/PippidonRuleset.cs | 2 +- .../EmptyScrollingDifficultyCalculator.cs | 2 +- .../EmptyScrollingRuleset.cs | 2 +- .../PippidonDifficultyCalculator.cs | 2 +- .../osu.Game.Rulesets.Pippidon/PippidonRuleset.cs | 2 +- .../CatchDifficultyCalculatorTest.cs | 2 +- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- .../Difficulty/CatchDifficultyCalculator.cs | 2 +- .../ManiaDifficultyCalculatorTest.cs | 2 +- .../Difficulty/ManiaDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- .../TaikoDifficultyCalculatorTest.cs | 2 +- .../Difficulty/TaikoDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- osu.Game/Beatmaps/IWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 6 +++--- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 2 +- 24 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs index 34124a022d..79be2b27da 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformDifficultyCalculator.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.EmptyFreeform { public class EmptyFreeformDifficultyCalculator : DifficultyCalculator { - public EmptyFreeformDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) + public EmptyFreeformDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformRuleset.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformRuleset.cs index bd928404d5..baf503085d 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformRuleset.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/EmptyFreeformRuleset.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.EmptyFreeform new EmptyFreeformBeatmapConverter(beatmap, this); public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => - new EmptyFreeformDifficultyCalculator(this, beatmap); + new EmptyFreeformDifficultyCalculator(RulesetInfo, beatmap); public override IEnumerable GetModsFor(ModType type) { diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index ef484e27bc..c612512938 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Pippidon { public class PippidonDifficultyCalculator : DifficultyCalculator { - public PippidonDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) + public PippidonDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs index 5f463e71f8..15e988f466 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Pippidon new PippidonBeatmapConverter(beatmap, this); public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => - new PippidonDifficultyCalculator(this, beatmap); + new PippidonDifficultyCalculator(RulesetInfo, beatmap); public override IEnumerable GetModsFor(ModType type) { diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs index 37c2b9f1eb..4628e6696b 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingDifficultyCalculator.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.EmptyScrolling { public class EmptyScrollingDifficultyCalculator : DifficultyCalculator { - public EmptyScrollingDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) + public EmptyScrollingDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingRuleset.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingRuleset.cs index 940eaa1a55..b9bed74c88 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingRuleset.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling/EmptyScrollingRuleset.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.EmptyScrolling public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new EmptyScrollingBeatmapConverter(beatmap, this); - public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new EmptyScrollingDifficultyCalculator(this, beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new EmptyScrollingDifficultyCalculator(RulesetInfo, beatmap); public override IEnumerable GetModsFor(ModType type) { diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs index ef484e27bc..c612512938 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonDifficultyCalculator.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Pippidon { public class PippidonDifficultyCalculator : DifficultyCalculator { - public PippidonDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) + public PippidonDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs index 4b1ef7c9a8..ea94ceb4b5 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/PippidonRuleset.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Pippidon public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new PippidonBeatmapConverter(beatmap, this); - public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new PippidonDifficultyCalculator(this, beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new PippidonDifficultyCalculator(RulesetInfo, beatmap); public override IEnumerable GetModsFor(ModType type) { diff --git a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs index 2fab47f857..971c47a3b1 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Tests public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new CatchModDoubleTime()); - protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset(), beatmap); + protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset().RulesetInfo, beatmap); protected override Ruleset CreateRuleset() => new CatchRuleset(); } diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index c879398d23..70d11c42e5 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -178,7 +178,7 @@ namespace osu.Game.Rulesets.Catch return base.GetDisplayNameForHitResult(result); } - public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new CatchDifficultyCalculator(RulesetInfo, beatmap); public override ISkin CreateLegacySkinProvider(ISkin skin, IBeatmap beatmap) => new CatchLegacySkinTransformer(skin); diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 50ddbaba71..77a783a10d 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty private float halfCatcherWidth; - public CatchDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) + public CatchDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs index 6e6500a339..a1cae855cf 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Tests public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new ManiaModDoubleTime()); - protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset(), beatmap); + protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset().RulesetInfo, beatmap); protected override Ruleset CreateRuleset() => new ManiaRuleset(); } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 640fdac225..0040df72aa 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty private readonly bool isForCurrentRuleset; private readonly double originalOverallDifficulty; - public ManiaDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) + public ManiaDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.MatchesOnlineID(ruleset); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index d7d0fed846..b0e7545d3e 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -272,7 +272,7 @@ namespace osu.Game.Rulesets.Mania public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetMania }; - public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new ManiaDifficultyCalculator(RulesetInfo, beatmap); public int LegacyID => 3; diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 9148f0715c..6150927826 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Tests public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); - protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap); + protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset().RulesetInfo, beatmap); protected override Ruleset CreateRuleset() => new OsuRuleset(); } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 892b8da527..3b4e85d0d3 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty private const double difficulty_multiplier = 0.0675; private double hitWindowGreat; - public OsuDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) + public OsuDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 6d50dc0a06..18e4bb259c 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -208,7 +208,7 @@ namespace osu.Game.Rulesets.Osu public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetOsu }; - public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(this, beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(RulesetInfo, beatmap); public override PerformanceCalculator CreatePerformanceCalculator(DifficultyAttributes attributes, ScoreInfo score) => new OsuPerformanceCalculator(this, attributes, score); diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs index 4b0b74ad27..a44c6a1ded 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Tests public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new TaikoModDoubleTime()); - protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset(), beatmap); + protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset().RulesetInfo, beatmap); protected override Ruleset CreateRuleset() => new TaikoRuleset(); } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 31d7304184..606afdbabc 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty private const double colour_skill_multiplier = 0.01; private const double stamina_skill_multiplier = 0.02; - public TaikoDifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) + public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index e4dea77377..ca860f24c3 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -168,7 +168,7 @@ namespace osu.Game.Rulesets.Taiko public override HitObjectComposer CreateHitObjectComposer() => new TaikoHitObjectComposer(this); - public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TaikoDifficultyCalculator(this, beatmap); + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TaikoDifficultyCalculator(RulesetInfo, beatmap); public override PerformanceCalculator CreatePerformanceCalculator(DifficultyAttributes attributes, ScoreInfo score) => new TaikoPerformanceCalculator(this, attributes, score); diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index 22a922db59..27082916cb 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -95,7 +95,7 @@ namespace osu.Game.Beatmaps /// The maximum length in milliseconds to wait for load to complete. Defaults to 10,000ms. /// The converted . /// If could not be converted to . - IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null); + IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null); /// /// Load a new audio track instance for this beatmap. This should be called once before accessing . diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 51eea94d3a..2ce1de380b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -83,7 +83,7 @@ namespace osu.Game.Beatmaps /// The applicable . protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap); - public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null) + public virtual IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null) { using (var cancellationSource = createCancellationTokenSource(timeout)) { diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index f9855a819f..5e431d54de 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -26,10 +26,10 @@ namespace osu.Game.Rulesets.Difficulty private Mod[] playableMods; private double clockRate; - private readonly Ruleset ruleset; + private readonly IRulesetInfo ruleset; private readonly IWorkingBeatmap beatmap; - protected DifficultyCalculator(Ruleset ruleset, IWorkingBeatmap beatmap) + protected DifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) { this.ruleset = ruleset; this.beatmap = beatmap; @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Difficulty { playableMods = mods.Select(m => m.DeepClone()).ToArray(); - Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, playableMods); + Beatmap = beatmap.GetPlayableBeatmap(ruleset, playableMods); var track = new TrackVirtual(10000); playableMods.OfType().ForEach(m => m.ApplyToTrack(track)); diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index b4359fe518..e6c549c7a9 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -216,7 +216,7 @@ namespace osu.Game.Screens.Play.HUD this.gameplayBeatmap = gameplayBeatmap; } - public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null) + public override IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList mods = null, TimeSpan? timeout = null) => gameplayBeatmap; protected override IBeatmap GetBeatmap() => gameplayBeatmap; From fabf253381a3b4b7ebf5ea84789e257d08f20e7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 18:42:47 +0900 Subject: [PATCH 42/90] Update difficulty calculator tests to also use `IWorkingBeatmap` --- .../CatchDifficultyCalculatorTest.cs | 2 +- .../ManiaDifficultyCalculatorTest.cs | 2 +- osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs | 2 +- .../TaikoDifficultyCalculatorTest.cs | 2 +- osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs index 971c47a3b1..7e8d567fbe 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Tests public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new CatchModDoubleTime()); - protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset().RulesetInfo, beatmap); + protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset().RulesetInfo, beatmap); protected override Ruleset CreateRuleset() => new CatchRuleset(); } diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs index a1cae855cf..6ec49d7634 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Tests public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new ManiaModDoubleTime()); - protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset().RulesetInfo, beatmap); + protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset().RulesetInfo, beatmap); protected override Ruleset CreateRuleset() => new ManiaRuleset(); } diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 6150927826..b7984e6995 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Tests public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); - protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset().RulesetInfo, beatmap); + protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset().RulesetInfo, beatmap); protected override Ruleset CreateRuleset() => new OsuRuleset(); } diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs index a44c6a1ded..2b1cbc580e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Tests public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new TaikoModDoubleTime()); - protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset().RulesetInfo, beatmap); + protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset().RulesetInfo, beatmap); protected override Ruleset CreateRuleset() => new TaikoRuleset(); } diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs index e5b641b606..9f8811c7f9 100644 --- a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -28,7 +28,7 @@ namespace osu.Game.Tests.Beatmaps Assert.That(CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods).StarRating, Is.EqualTo(expected).Within(0.00001)); } - private WorkingBeatmap getBeatmap(string name) + private IWorkingBeatmap getBeatmap(string name) { using (var resStream = openResource($"{resource_namespace}.{name}.osu")) using (var stream = new LineBufferedReader(resStream)) @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Beatmaps return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}"); } - protected abstract DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap); + protected abstract DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap); protected abstract Ruleset CreateRuleset(); } From 86b8fd37207850d0e611d6622590b4fd22603d4b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 18:05:10 +0900 Subject: [PATCH 43/90] Fix `TestSceneBeatmapRecommendations` testing with an online ID of 0 --- .../TestSceneBeatmapRecommendations.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index a0742b862b..41e824a513 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -102,8 +102,8 @@ namespace osu.Game.Tests.Visual.SongSelect { BeatmapSetInfo catchSet = null, mixedSet = null; - AddStep("create catch beatmapset", () => catchSet = importBeatmapSet(0, new[] { new CatchRuleset().RulesetInfo })); - AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(1, + AddStep("create catch beatmapset", () => catchSet = importBeatmapSet(1, new[] { new CatchRuleset().RulesetInfo })); + AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(2, new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new ManiaRuleset().RulesetInfo })); AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { catchSet, mixedSet })); @@ -120,8 +120,8 @@ namespace osu.Game.Tests.Visual.SongSelect { BeatmapSetInfo osuSet = null, mixedSet = null; - AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(0, new[] { new OsuRuleset().RulesetInfo })); - AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(1, + AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(1, new[] { new OsuRuleset().RulesetInfo })); + AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(2, new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new ManiaRuleset().RulesetInfo })); AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, mixedSet })); @@ -138,8 +138,8 @@ namespace osu.Game.Tests.Visual.SongSelect { BeatmapSetInfo osuSet = null, mixedSet = null; - AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(0, new[] { new OsuRuleset().RulesetInfo })); - AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(1, + AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(1, new[] { new OsuRuleset().RulesetInfo })); + AddStep("create mixed beatmapset", () => mixedSet = importBeatmapSet(2, new[] { new TaikoRuleset().RulesetInfo, new CatchRuleset().RulesetInfo, new TaikoRuleset().RulesetInfo })); AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, mixedSet })); @@ -156,8 +156,8 @@ namespace osu.Game.Tests.Visual.SongSelect { BeatmapSetInfo osuSet = null, maniaSet = null; - AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(0, new[] { new OsuRuleset().RulesetInfo })); - AddStep("create mania beatmapset", () => maniaSet = importBeatmapSet(1, Enumerable.Repeat(new ManiaRuleset().RulesetInfo, 10))); + AddStep("create osu! beatmapset", () => osuSet = importBeatmapSet(1, new[] { new OsuRuleset().RulesetInfo })); + AddStep("create mania beatmapset", () => maniaSet = importBeatmapSet(2, Enumerable.Repeat(new ManiaRuleset().RulesetInfo, 10))); AddAssert("all sets imported", () => ensureAllBeatmapSetsImported(new[] { osuSet, maniaSet })); From 485618091249455a7a6aa2b7472c877d2df1fc63 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 18:06:56 +0900 Subject: [PATCH 44/90] Use new helper method --- .../Visual/SongSelect/TestSceneBeatmapRecommendations.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs index 41e824a513..c9ec53cfd5 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs @@ -7,6 +7,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Extensions; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -203,11 +204,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("present beatmap", () => Game.PresentBeatmap(getImport())); AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect); - AddUntilStep("recommended beatmap displayed", () => - { - int? expectedID = getImport().Beatmaps[expectedDiff - 1].OnlineID; - return Game.Beatmap.Value.BeatmapInfo.OnlineID == expectedID; - }); + AddUntilStep("recommended beatmap displayed", () => Game.Beatmap.Value.BeatmapInfo.MatchesOnlineID(getImport().Beatmaps[expectedDiff - 1])); } } } From a90cfb4a2f9b4cf839e3ad0daac1219c984b96e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 19:30:46 +0900 Subject: [PATCH 45/90] Implement remaining properties via implicit implementation rather than interface methods --- osu.Game/Beatmaps/IWorkingBeatmap.cs | 4 ++-- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index 22a922db59..ba887edf62 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -19,9 +19,9 @@ namespace osu.Game.Beatmaps { IBeatmapInfo BeatmapInfo { get; } - IBeatmapSetInfo BeatmapSetInfo => BeatmapInfo.BeatmapSet; + IBeatmapSetInfo BeatmapSetInfo { get; } - IBeatmapMetadataInfo Metadata => BeatmapInfo.Metadata; + IBeatmapMetadataInfo Metadata { get; } /// /// Whether the Beatmap has finished loading. diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 51eea94d3a..bba836484f 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -231,6 +231,8 @@ namespace osu.Game.Beatmaps public virtual bool BeatmapLoaded => beatmapLoadTask?.IsCompleted ?? false; IBeatmapInfo IWorkingBeatmap.BeatmapInfo => BeatmapInfo; + IBeatmapMetadataInfo IWorkingBeatmap.Metadata => Metadata; + IBeatmapSetInfo IWorkingBeatmap.BeatmapSetInfo => BeatmapSetInfo; public IBeatmap Beatmap { From 1d87b47fecb9ed281d2c641a7e15262de827e55d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Nov 2021 19:40:31 +0900 Subject: [PATCH 46/90] Remove now-unnecessary R# disables --- osu.Game/Beatmaps/WorkingBeatmap.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index bba836484f..f68f34f673 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -27,11 +27,7 @@ namespace osu.Game.Beatmaps public abstract class WorkingBeatmap : IWorkingBeatmap { public readonly BeatmapInfo BeatmapInfo; - - // ReSharper disable once FieldHidesInterfacePropertyWithDefaultImplementation public readonly BeatmapSetInfo BeatmapSetInfo; - - // ReSharper disable once FieldHidesInterfacePropertyWithDefaultImplementation public readonly BeatmapMetadata Metadata; protected AudioManager AudioManager { get; } From 2c45a327bb20713d74368f79a0500a07de860ee0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 15 Nov 2021 19:54:35 +0900 Subject: [PATCH 47/90] Remove JsonIgnores, require opt-in properties --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 4 ---- osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 88f26db93c..7673d08440 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -30,16 +30,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty [JsonProperty("overall_difficulty")] public double OverallDifficulty { get; set; } - [JsonIgnore] public double DrainRate { get; set; } - [JsonIgnore] public int HitCircleCount { get; set; } - [JsonIgnore] public int SliderCount { get; set; } - [JsonIgnore] public int SpinnerCount { get; set; } public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index e25a8476f0..cc9d111aa2 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -9,12 +9,11 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Difficulty { + [JsonObject(MemberSerialization.OptIn)] public class DifficultyAttributes { - [JsonIgnore] public Mod[] Mods { get; set; } - [JsonIgnore] public Skill[] Skills { get; set; } [JsonProperty("star_rating", Order = -3)] From 3fe89293affaec4a0aae564e767341bcff2d0bca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 12:11:11 +0900 Subject: [PATCH 48/90] Add update manager which performs no update action This is to be used in cases where updates are handled by an external means. See https://github.com/flathub/flathub/pull/2619#issuecomment-969731305 for initial usage. --- osu.Desktop/OsuGameDesktop.cs | 5 ++ osu.Game/Updater/GitHubAsset.cs | 16 ++++++ osu.Game/Updater/GitHubRelease.cs | 20 +++++++ osu.Game/Updater/NoActionUpdateManager.cs | 67 +++++++++++++++++++++++ osu.Game/Updater/SimpleUpdateManager.cs | 23 -------- 5 files changed, 108 insertions(+), 23 deletions(-) create mode 100644 osu.Game/Updater/GitHubAsset.cs create mode 100644 osu.Game/Updater/GitHubRelease.cs create mode 100644 osu.Game/Updater/NoActionUpdateManager.cs diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 19e1252a4d..645ea66654 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -93,6 +93,11 @@ namespace osu.Desktop protected override UpdateManager CreateUpdateManager() { + string packageManaged = Environment.GetEnvironmentVariable("OSU_EXTERNAL_UPDATE_PROVIDER"); + + if (!string.IsNullOrEmpty(packageManaged)) + return new NoActionUpdateManager(); + switch (RuntimeInfo.OS) { case RuntimeInfo.Platform.Windows: diff --git a/osu.Game/Updater/GitHubAsset.cs b/osu.Game/Updater/GitHubAsset.cs new file mode 100644 index 0000000000..4783161859 --- /dev/null +++ b/osu.Game/Updater/GitHubAsset.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Newtonsoft.Json; + +namespace osu.Game.Updater +{ + public class GitHubAsset + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("browser_download_url")] + public string BrowserDownloadUrl { get; set; } + } +} diff --git a/osu.Game/Updater/GitHubRelease.cs b/osu.Game/Updater/GitHubRelease.cs new file mode 100644 index 0000000000..363b2b628f --- /dev/null +++ b/osu.Game/Updater/GitHubRelease.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace osu.Game.Updater +{ + public class GitHubRelease + { + [JsonProperty("html_url")] + public string HtmlUrl { get; set; } + + [JsonProperty("tag_name")] + public string TagName { get; set; } + + [JsonProperty("assets")] + public List Assets { get; set; } + } +} diff --git a/osu.Game/Updater/NoActionUpdateManager.cs b/osu.Game/Updater/NoActionUpdateManager.cs new file mode 100644 index 0000000000..641263ed0f --- /dev/null +++ b/osu.Game/Updater/NoActionUpdateManager.cs @@ -0,0 +1,67 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Platform; +using osu.Game.Online.API; +using osu.Game.Overlays.Notifications; + +namespace osu.Game.Updater +{ + /// + /// An update manager that shows notifications if a newer release is detected. + /// This is a case where updates are handled externally by a package manager or other means, so no action is performed on clicking the notification. + /// + public class NoActionUpdateManager : UpdateManager + { + private string version; + + [Resolved] + private GameHost host { get; set; } + + [BackgroundDependencyLoader] + private void load(OsuGameBase game) + { + version = game.Version; + } + + protected override async Task PerformUpdateCheck() + { + try + { + var releases = new OsuJsonWebRequest("https://api.github.com/repos/ppy/osu/releases/latest"); + + await releases.PerformAsync().ConfigureAwait(false); + + var latest = releases.ResponseObject; + + // avoid any discrepancies due to build suffixes for now. + // eventually we will want to support release streams and consider these. + version = version.Split('-').First(); + string latestTagName = latest.TagName.Split('-').First(); + + if (latestTagName != version) + { + Notifications.Post(new SimpleNotification + { + Text = $"A newer release of osu! has been found ({version} → {latestTagName}).\n\n" + + "Check with your package manager / provider to bring osu! up-to-date!", + Icon = FontAwesome.Solid.Upload, + }); + + return true; + } + } + catch + { + // we shouldn't crash on a web failure. or any failure for the matter. + return true; + } + + return false; + } + } +} diff --git a/osu.Game/Updater/SimpleUpdateManager.cs b/osu.Game/Updater/SimpleUpdateManager.cs index 5e466cc57f..61ca68a1ab 100644 --- a/osu.Game/Updater/SimpleUpdateManager.cs +++ b/osu.Game/Updater/SimpleUpdateManager.cs @@ -2,10 +2,8 @@ // 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 Newtonsoft.Json; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; @@ -104,26 +102,5 @@ namespace osu.Game.Updater return bestAsset?.BrowserDownloadUrl ?? release.HtmlUrl; } - - public class GitHubRelease - { - [JsonProperty("html_url")] - public string HtmlUrl { get; set; } - - [JsonProperty("tag_name")] - public string TagName { get; set; } - - [JsonProperty("assets")] - public List Assets { get; set; } - } - - public class GitHubAsset - { - [JsonProperty("name")] - public string Name { get; set; } - - [JsonProperty("browser_download_url")] - public string BrowserDownloadUrl { get; set; } - } } } From 9207b87b7686ccef1ec9b17492b353ae51fb720d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 12:25:37 +0900 Subject: [PATCH 49/90] Add back interface equality but limit to only matching types --- osu.Game/Beatmaps/BeatmapInfo.cs | 2 ++ osu.Game/Beatmaps/BeatmapSetInfo.cs | 2 ++ osu.Game/Beatmaps/IBeatmapInfo.cs | 3 ++- osu.Game/Beatmaps/IBeatmapSetInfo.cs | 2 +- osu.Game/Models/RealmBeatmap.cs | 2 ++ osu.Game/Models/RealmBeatmapSet.cs | 2 ++ osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 2 ++ osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs | 2 ++ 8 files changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 6f45f5ec71..d2b322a843 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -163,6 +163,8 @@ namespace osu.Game.Beatmaps return false; } + public bool Equals(IBeatmapInfo other) => other is BeatmapInfo b && Equals(b); + public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && BeatmapSet.Hash == other.BeatmapSet.Hash && (Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile; diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 6dd8cc5ade..a0de50a311 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -78,6 +78,8 @@ namespace osu.Game.Beatmaps return false; } + public bool Equals(IBeatmapSetInfo other) => other is BeatmapSetInfo b && Equals(b); + #region Implementation of IHasOnlineID int IHasOnlineID.OnlineID => OnlineID ?? -1; diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index 84ea6d3019..ab096b8897 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -1,6 +1,7 @@ // 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.Game.Database; using osu.Game.Rulesets; @@ -11,7 +12,7 @@ namespace osu.Game.Beatmaps /// /// A single beatmap difficulty. /// - public interface IBeatmapInfo : IHasOnlineID + public interface IBeatmapInfo : IHasOnlineID, IEquatable { /// /// The user-specified name given to this beatmap. diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs index 20c46d9063..67f38397d4 100644 --- a/osu.Game/Beatmaps/IBeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps /// /// A representation of a collection of beatmap difficulties, generally packaged as an ".osz" archive. /// - public interface IBeatmapSetInfo : IHasOnlineID + public interface IBeatmapSetInfo : IHasOnlineID, IEquatable { /// /// The date when this beatmap was imported. diff --git a/osu.Game/Models/RealmBeatmap.cs b/osu.Game/Models/RealmBeatmap.cs index 2a197d296a..1a25d55d04 100644 --- a/osu.Game/Models/RealmBeatmap.cs +++ b/osu.Game/Models/RealmBeatmap.cs @@ -106,6 +106,8 @@ namespace osu.Game.Models return ID == other.ID; } + public bool Equals(IBeatmapInfo? other) => other is RealmBeatmap b && Equals(b); + public bool AudioEquals(RealmBeatmap? other) => other != null && BeatmapSet != null && other.BeatmapSet != null diff --git a/osu.Game/Models/RealmBeatmapSet.cs b/osu.Game/Models/RealmBeatmapSet.cs index 1747cce67a..b9b47b697c 100644 --- a/osu.Game/Models/RealmBeatmapSet.cs +++ b/osu.Game/Models/RealmBeatmapSet.cs @@ -64,6 +64,8 @@ namespace osu.Game.Models public override string ToString() => Metadata?.GetDisplayString() ?? base.ToString(); + public bool Equals(IBeatmapSetInfo? other) => other is RealmBeatmapSet b && Equals(b); + IEnumerable IBeatmapSetInfo.Beatmaps => Beatmaps; IEnumerable IBeatmapSetInfo.Files => Files; diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 2560502173..e90de5bdb7 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -103,5 +103,7 @@ namespace osu.Game.Online.API.Requests.Responses public string Hash => throw new NotImplementedException(); #endregion + + public bool Equals(IBeatmapInfo? other) => other is APIBeatmap b && Equals(b); } } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 168e9d5d51..2628880588 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -141,5 +141,7 @@ namespace osu.Game.Online.API.Requests.Responses double IBeatmapSetInfo.MaxBPM => BPM; #endregion + + public bool Equals(IBeatmapSetInfo? other) => other is APIBeatmapSet b && Equals(b); } } From 68e269904377205b62661aac719c886be9ba320d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 12:28:57 +0900 Subject: [PATCH 50/90] Fix oversight in playlist matching logic --- osu.Game/Overlays/Music/Playlist.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Music/Playlist.cs b/osu.Game/Overlays/Music/Playlist.cs index 5de62ebfb8..0b15a3a1bc 100644 --- a/osu.Game/Overlays/Music/Playlist.cs +++ b/osu.Game/Overlays/Music/Playlist.cs @@ -7,7 +7,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; -using osu.Game.Extensions; using osu.Game.Graphics.Containers; using osuTK; @@ -30,7 +29,7 @@ namespace osu.Game.Overlays.Music var items = (SearchContainer>)ListContainer; foreach (var item in items.OfType()) - item.InSelectedCollection = criteria.Collection?.Beatmaps.Any(b => b.MatchesOnlineID(item.Model)) ?? true; + item.InSelectedCollection = criteria.Collection?.Beatmaps.Any(b => item.Model.Equals(b.BeatmapSet)) ?? true; items.SearchTerm = criteria.SearchText; } From fbc46941fa1beab0c846176c113b9a360936dd8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 12:37:47 +0900 Subject: [PATCH 51/90] Add type safety to `MatchesOnlineID` extension methods --- osu.Game/Extensions/ModelExtensions.cs | 30 ++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/osu.Game/Extensions/ModelExtensions.cs b/osu.Game/Extensions/ModelExtensions.cs index 48a3bac112..c8561ecdb3 100644 --- a/osu.Game/Extensions/ModelExtensions.cs +++ b/osu.Game/Extensions/ModelExtensions.cs @@ -62,12 +62,30 @@ namespace osu.Game.Extensions } /// - /// Check whether the online ID of two instances match. + /// Check whether the online ID of two s match. /// /// The instance to compare. /// The other instance to compare against. /// Whether online IDs match. If either instance is missing an online ID, this will return false. - public static bool MatchesOnlineID(this IHasOnlineID? instance, IHasOnlineID? other) + public static bool MatchesOnlineID(this IBeatmapSetInfo? instance, IBeatmapSetInfo? other) => matchesOnlineID(instance, other); + + /// + /// Check whether the online ID of two s match. + /// + /// The instance to compare. + /// The other instance to compare against. + /// Whether online IDs match. If either instance is missing an online ID, this will return false. + public static bool MatchesOnlineID(this IBeatmapInfo? instance, IBeatmapInfo? other) => matchesOnlineID(instance, other); + + /// + /// Check whether the online ID of two s match. + /// + /// The instance to compare. + /// The other instance to compare against. + /// Whether online IDs match. If either instance is missing an online ID, this will return false. + public static bool MatchesOnlineID(this IRulesetInfo? instance, IRulesetInfo? other) => matchesOnlineID(instance, other); + + private static bool matchesOnlineID(this IHasOnlineID? instance, IHasOnlineID? other) { if (instance == null || other == null) return false; @@ -78,13 +96,7 @@ namespace osu.Game.Extensions return instance.OnlineID.Equals(other.OnlineID); } - /// - /// Check whether the online ID of two instances match. - /// - /// The instance to compare. - /// The other instance to compare against. - /// Whether online IDs match. If either instance is missing an online ID, this will return false. - public static bool MatchesOnlineID(this IHasOnlineID? instance, IHasOnlineID? other) + private static bool matchesOnlineID(this IHasOnlineID? instance, IHasOnlineID? other) { if (instance == null || other == null) return false; From 2cbdac91ad02b84e9ec7c04685d6d55f2c574d92 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 12:44:20 +0900 Subject: [PATCH 52/90] Add missing `APIUser` comparison method --- osu.Game/Extensions/ModelExtensions.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Extensions/ModelExtensions.cs b/osu.Game/Extensions/ModelExtensions.cs index c8561ecdb3..3426484c14 100644 --- a/osu.Game/Extensions/ModelExtensions.cs +++ b/osu.Game/Extensions/ModelExtensions.cs @@ -3,6 +3,7 @@ using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Users; @@ -85,6 +86,14 @@ namespace osu.Game.Extensions /// Whether online IDs match. If either instance is missing an online ID, this will return false. public static bool MatchesOnlineID(this IRulesetInfo? instance, IRulesetInfo? other) => matchesOnlineID(instance, other); + /// + /// Check whether the online ID of two s match. + /// + /// The instance to compare. + /// The other instance to compare against. + /// Whether online IDs match. If either instance is missing an online ID, this will return false. + public static bool MatchesOnlineID(this APIUser? instance, APIUser? other) => matchesOnlineID(instance, other); + private static bool matchesOnlineID(this IHasOnlineID? instance, IHasOnlineID? other) { if (instance == null || other == null) From 71fef241dff90f4d9e9979396596bd15022e9c1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 14:13:47 +0900 Subject: [PATCH 53/90] Fix recursive equality call on `APIBeatmap` and `APIBeatmapSet` --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 3 ++- osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index e90de5bdb7..3fb9335629 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -4,6 +4,7 @@ using System; using Newtonsoft.Json; using osu.Game.Beatmaps; +using osu.Game.Extensions; using osu.Game.Rulesets; #nullable enable @@ -104,6 +105,6 @@ namespace osu.Game.Online.API.Requests.Responses #endregion - public bool Equals(IBeatmapInfo? other) => other is APIBeatmap b && Equals(b); + public bool Equals(IBeatmapInfo? other) => other is APIBeatmap b && this.MatchesOnlineID(b); } } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 2628880588..8d50f3d7dd 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Extensions; #nullable enable @@ -142,6 +143,6 @@ namespace osu.Game.Online.API.Requests.Responses #endregion - public bool Equals(IBeatmapSetInfo? other) => other is APIBeatmapSet b && Equals(b); + public bool Equals(IBeatmapSetInfo? other) => other is APIBeatmapSet b && this.MatchesOnlineID(b); } } From 6cca657a2d48c6424759493d8fe264791ed211db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 14:45:51 +0900 Subject: [PATCH 54/90] Standardise naming of `CancellationToken` parameters --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 12 ++++++------ osu.Game/Beatmaps/IWorkingBeatmap.cs | 4 ++-- osu.Game/Beatmaps/WorkingBeatmap.cs | 18 +++++++++--------- .../Difficulty/DifficultyCalculator.cs | 2 +- .../Play/HUD/PerformancePointsCounter.cs | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index dabff0141e..d761ea3212 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -140,21 +140,21 @@ namespace osu.Game.Beatmaps return GetAsync(new DifficultyCacheLookup(localBeatmapInfo, localRulesetInfo, mods), cancellationToken); } - protected override Task ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken token = default) + protected override Task ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken cancellationToken = default) { return Task.Factory.StartNew(() => { if (CheckExists(lookup, out var existing)) return existing; - return computeDifficulty(lookup, token); - }, token, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); + return computeDifficulty(lookup, cancellationToken); + }, cancellationToken, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); } - public Task> GetTimedDifficultyAttributesAsync(WorkingBeatmap beatmap, Ruleset ruleset, Mod[] mods, CancellationToken token = default) + public Task> GetTimedDifficultyAttributesAsync(WorkingBeatmap beatmap, Ruleset ruleset, Mod[] mods, CancellationToken cancellationToken = default) { - return Task.Factory.StartNew(() => ruleset.CreateDifficultyCalculator(beatmap).CalculateTimed(mods, token), - token, + return Task.Factory.StartNew(() => ruleset.CreateDifficultyCalculator(beatmap).CalculateTimed(mods, cancellationToken), + cancellationToken, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); } diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index 39f6ad7b58..5e9b77392a 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -92,10 +92,10 @@ namespace osu.Game.Beatmaps /// /// The to create a playable for. /// The s to apply to the . - /// Cancellation token that cancels the beatmap loading process. + /// Cancellation token that cancels the beatmap loading process. /// The converted . /// If could not be converted to . - IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken token = default); + IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken cancellationToken = default); /// /// Load a new audio track instance for this beatmap. This should be called once before accessing . diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index b4db4b9f9f..4098a57bf2 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -79,7 +79,7 @@ namespace osu.Game.Beatmaps /// The applicable . protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap); - public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken token = default) + public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken cancellationToken = default) { mods ??= Array.Empty(); @@ -97,19 +97,19 @@ namespace osu.Game.Beatmaps // Apply conversion mods foreach (var mod in mods.OfType()) { - if (token.IsCancellationRequested) + if (cancellationToken.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); mod.ApplyToBeatmapConverter(converter); } // Convert - IBeatmap converted = converter.Convert(token); + IBeatmap converted = converter.Convert(cancellationToken); // Apply conversion mods to the result foreach (var mod in mods.OfType()) { - if (token.IsCancellationRequested) + if (cancellationToken.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); mod.ApplyToBeatmap(converted); @@ -120,7 +120,7 @@ namespace osu.Game.Beatmaps { foreach (var mod in mods.OfType()) { - if (token.IsCancellationRequested) + if (cancellationToken.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); mod.ApplyToDifficulty(converted.Difficulty); @@ -139,10 +139,10 @@ namespace osu.Game.Beatmaps { foreach (var obj in converted.HitObjects) { - if (token.IsCancellationRequested) + if (cancellationToken.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); - obj.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty, token); + obj.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty, cancellationToken); } } catch (OperationCanceledException) @@ -154,7 +154,7 @@ namespace osu.Game.Beatmaps { foreach (var obj in converted.HitObjects) { - if (token.IsCancellationRequested) + if (cancellationToken.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); mod.ApplyToHitObject(obj); @@ -165,7 +165,7 @@ namespace osu.Game.Beatmaps foreach (var mod in mods.OfType()) { - token.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); mod.ApplyToBeatmap(converted); } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 6748158d8f..f5b6883b27 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Difficulty { playableMods = mods.Select(m => m.DeepClone()).ToArray(); - Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, playableMods, token: cancellationToken); + Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, playableMods, cancellationToken: cancellationToken); var track = new TrackVirtual(10000); playableMods.OfType().ForEach(m => m.ApplyToTrack(track)); diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 24c6c87c7d..6e679279a4 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -216,7 +216,7 @@ namespace osu.Game.Screens.Play.HUD this.gameplayBeatmap = gameplayBeatmap; } - public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken timeoutToken = default) + public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken cancellationToken = default) => gameplayBeatmap; protected override IBeatmap GetBeatmap() => gameplayBeatmap; From d2a767049437b30e6bdf1eb09e301e2bd92474ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 14:48:02 +0900 Subject: [PATCH 55/90] Remove no longer used helper method --- osu.Game/Beatmaps/WorkingBeatmap.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 4098a57bf2..9be239e3b0 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; @@ -188,15 +187,6 @@ namespace osu.Game.Beatmaps } } - private CancellationTokenSource createTimeoutTokenSource(TimeSpan? timeout) - { - if (Debugger.IsAttached) - // ignore timeout when debugger is attached (may be breakpointing / debugging). - return new CancellationTokenSource(); - - return new CancellationTokenSource(timeout ?? TimeSpan.FromSeconds(10)); - } - private readonly object beatmapFetchLock = new object(); private Task loadBeatmapAsync() From 49cdcffa3008897850b0fe1072fa1fecf8c731b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 16:58:44 +0900 Subject: [PATCH 56/90] Simplify schedule logic in `DrawableRoomPlaylistItem` --- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 85cee46a29..a1e3bfdd8b 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; @@ -87,23 +86,15 @@ namespace osu.Game.Screens.OnlinePlay SelectedItem.BindValueChanged(selected => maskingContainer.BorderThickness = selected.NewValue == Model ? 5 : 0, true); - beatmap.BindValueChanged(_ => scheduleRefresh()); - ruleset.BindValueChanged(_ => scheduleRefresh()); - - requiredMods.CollectionChanged += (_, __) => scheduleRefresh(); + beatmap.BindValueChanged(_ => Scheduler.AddOnce(refresh)); + ruleset.BindValueChanged(_ => Scheduler.AddOnce(refresh)); + requiredMods.CollectionChanged += (_, __) => Scheduler.AddOnce(refresh); refresh(); } - private ScheduledDelegate scheduledRefresh; private PanelBackground panelBackground; - private void scheduleRefresh() - { - scheduledRefresh?.Cancel(); - scheduledRefresh = Schedule(refresh); - } - private void refresh() { difficultyIconContainer.Child = new DifficultyIcon(Item.Beatmap.Value, ruleset.Value, requiredMods, performBackgroundDifficultyLookup: false) { Size = new Vector2(32) }; From aa188d5a52fa5004d5219596af75d9387ad81995 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 17:01:24 +0900 Subject: [PATCH 57/90] Add ability for playlist items to be marked as invalid --- osu.Game/Online/Rooms/PlaylistItem.cs | 7 +++++++ .../Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 14 +++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/PlaylistItem.cs b/osu.Game/Online/Rooms/PlaylistItem.cs index cb550281a9..4a94191a34 100644 --- a/osu.Game/Online/Rooms/PlaylistItem.cs +++ b/osu.Game/Online/Rooms/PlaylistItem.cs @@ -30,6 +30,11 @@ namespace osu.Game.Online.Rooms [JsonProperty("expired")] public bool Expired { get; set; } + [JsonIgnore] + public IBindable Valid => valid; + + private readonly Bindable valid = new BindableBool(true); + [JsonIgnore] public readonly Bindable Beatmap = new Bindable(); @@ -69,6 +74,8 @@ namespace osu.Game.Online.Rooms Ruleset.BindValueChanged(ruleset => RulesetID = ruleset.NewValue?.ID ?? 0); } + public void MarkInvalid() => valid.Value = false; + public void MapObjects(RulesetStore rulesets) { Beatmap.Value ??= apiBeatmap; diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index a1e3bfdd8b..90cb362d5c 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -45,6 +45,7 @@ namespace osu.Game.Screens.OnlinePlay private ModDisplay modDisplay; private readonly Bindable beatmap = new Bindable(); + private readonly IBindable valid = new Bindable(); private readonly Bindable ruleset = new Bindable(); private readonly BindableList requiredMods = new BindableList(); @@ -65,14 +66,18 @@ namespace osu.Game.Screens.OnlinePlay this.allowSelection = allowSelection; beatmap.BindTo(item.Beatmap); + valid.BindTo(item.Valid); ruleset.BindTo(item.Ruleset); requiredMods.BindTo(item.RequiredMods); ShowDragHandle.Value = allowEdit; } + [Resolved] + private OsuColour colours { get; set; } + [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { if (!allowEdit) HandleColour = HandleColour.Opacity(0); @@ -88,6 +93,7 @@ namespace osu.Game.Screens.OnlinePlay beatmap.BindValueChanged(_ => Scheduler.AddOnce(refresh)); ruleset.BindValueChanged(_ => Scheduler.AddOnce(refresh)); + valid.BindValueChanged(_ => Scheduler.AddOnce(refresh)); requiredMods.CollectionChanged += (_, __) => Scheduler.AddOnce(refresh); refresh(); @@ -97,6 +103,12 @@ namespace osu.Game.Screens.OnlinePlay private void refresh() { + if (!valid.Value) + { + maskingContainer.BorderThickness = 5; + maskingContainer.BorderColour = colours.Red; + } + difficultyIconContainer.Child = new DifficultyIcon(Item.Beatmap.Value, ruleset.Value, requiredMods, performBackgroundDifficultyLookup: false) { Size = new Vector2(32) }; panelBackground.Beatmap.Value = Item.Beatmap.Value; From e78c5d0858479780a8bafaac5462533f5230738d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 17:13:50 +0900 Subject: [PATCH 58/90] Show `loadingLayer` before calling `CreateRoom` This fixes `TestScenePlaylistsMatchSettingsOverlay` incorrectly leaving the loading layer visible. --- .../OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index c2bd7730e9..3bcfb89aa7 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -339,9 +339,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Duration.Value = DurationField.Current.Value; - manager?.CreateRoom(room, onSuccess, onError); - loadingLayer.Show(); + manager?.CreateRoom(room, onSuccess, onError); } private void hideError() => ErrorText.FadeOut(50); From 6f82e6351fabbeb4adc71b36065a7a942664778a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 17:01:37 +0900 Subject: [PATCH 59/90] Parse osu-web error text to present invalid items to the user in a more approachable format --- .../Playlists/PlaylistsRoomSettingsOverlay.cs | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index 3bcfb89aa7..cb4a81f108 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Specialized; +using System.Linq; using Humanizer; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -349,9 +350,31 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void onError(string text) { - ErrorText.Text = text; - ErrorText.FadeIn(50); + // see https://github.com/ppy/osu-web/blob/2c97aaeb64fb4ed97c747d8383a35b30f57428c7/app/Models/Multiplayer/PlaylistItem.php#L48. + const string not_found_prefix = "beatmaps not found:"; + if (text.StartsWith(not_found_prefix, StringComparison.Ordinal)) + { + ErrorText.Text = "One or more beatmap was not available online. Please remove or replaced the highlighted items."; + + int[] invalidBeatmapIDs = text + .Substring(not_found_prefix.Length + 1) + .Split(", ") + .Select(int.Parse) + .ToArray(); + + foreach (var item in Playlist) + { + if (invalidBeatmapIDs.Contains(item.BeatmapID)) + item.MarkInvalid(); + } + } + else + { + ErrorText.Text = text; + } + + ErrorText.FadeIn(50); loadingLayer.Hide(); } } From 2d125893fce2f08702ec31e0fc6704cc25a38f45 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 17:08:21 +0900 Subject: [PATCH 60/90] Add tests and disable selection on invalid items --- .../TestSceneDrawableRoomPlaylist.cs | 13 +++++ .../TestScenePlaylistsMatchSettingsOverlay.cs | 52 +++++++++++++++---- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 2 +- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 8ac0a2cbe5..efc3b07d0a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -67,6 +67,19 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("no item selected", () => playlist.SelectedItem.Value == null); } + [Test] + public void TestMarkInvalid() + { + createPlaylist(true, true); + + AddStep("mark item 0 as invalid", () => playlist.Items[0].MarkInvalid()); + + moveToItem(0); + + AddStep("click", () => InputManager.Click(MouseButton.Left)); + AddAssert("no item selected", () => playlist.SelectedItem.Value == null); + } + [Test] public void TestSelectable() { diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index 48222e6b94..278379c692 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.Playlists RoomManager.CreateRequested = r => { createdRoom = r; - return true; + return string.Empty; }; }); @@ -82,28 +82,58 @@ namespace osu.Game.Tests.Visual.Playlists AddAssert("has correct duration", () => createdRoom.Duration.Value == expectedDuration); } + [Test] + public void TestInvalidBeatmapError() + { + const string not_found_prefix = "beatmaps not found:"; + + string errorMesage = null; + + AddStep("setup", () => + { + var beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo; + + SelectedRoom.Value.Name.Value = "Test Room"; + SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = beatmap } }); + + errorMesage = $"{not_found_prefix} {beatmap.OnlineID}"; + + RoomManager.CreateRequested = _ => errorMesage; + }); + + AddAssert("error not displayed", () => !settings.ErrorText.IsPresent); + AddAssert("playlist item valid", () => SelectedRoom.Value.Playlist[0].Valid.Value); + + AddStep("create room", () => settings.ApplyButton.Action.Invoke()); + + AddAssert("error displayed", () => settings.ErrorText.IsPresent); + AddAssert("error has custom text", () => settings.ErrorText.Text != errorMesage); + AddAssert("playlist item marked invalid", () => !SelectedRoom.Value.Playlist[0].Valid.Value); + } + [Test] public void TestCreationFailureDisplaysError() { - bool fail; + const string error_message = "failed"; + + string failText = error_message; AddStep("setup", () => { SelectedRoom.Value.Name.Value = "Test Room"; SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } }); - fail = true; - RoomManager.CreateRequested = _ => !fail; + RoomManager.CreateRequested = _ => failText; }); AddAssert("error not displayed", () => !settings.ErrorText.IsPresent); AddStep("create room", () => settings.ApplyButton.Action.Invoke()); AddAssert("error displayed", () => settings.ErrorText.IsPresent); - AddAssert("error has correct text", () => settings.ErrorText.Text == TestRoomManager.FAILED_TEXT); + AddAssert("error has correct text", () => settings.ErrorText.Text == error_message); AddStep("create room no fail", () => { - fail = false; + failText = string.Empty; settings.ApplyButton.Action.Invoke(); }); @@ -132,9 +162,7 @@ namespace osu.Game.Tests.Visual.Playlists protected class TestRoomManager : IRoomManager { - public const string FAILED_TEXT = "failed"; - - public Func CreateRequested; + public Func CreateRequested; public event Action RoomsUpdated { @@ -157,8 +185,10 @@ namespace osu.Game.Tests.Visual.Playlists if (CreateRequested == null) return; - if (!CreateRequested.Invoke(room)) - onError?.Invoke(FAILED_TEXT); + string error = CreateRequested.Invoke(room); + + if (!string.IsNullOrEmpty(error)) + onError?.Invoke(error); else onSuccess?.Invoke(room); } diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 90cb362d5c..7af858f1f8 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -281,7 +281,7 @@ namespace osu.Game.Screens.OnlinePlay protected override bool OnClick(ClickEvent e) { - if (allowSelection) + if (allowSelection && valid.Value) SelectedItem.Value = Model; return true; } From 2dd721f7609480d212051bc80f214b3fc5068b3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 17:41:16 +0900 Subject: [PATCH 61/90] Fix incorrect `CancellationToken` being used for inner check during leaderboard updates --- .../Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 9c8ccee99b..03da930c5c 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -116,6 +116,8 @@ namespace osu.Game.Screens.Select.Leaderboards loadCancellationSource?.Cancel(); loadCancellationSource = new CancellationTokenSource(); + var cancellationToken = loadCancellationSource.Token; + if (BeatmapInfo == null) { PlaceholderState = PlaceholderState.NoneSelected; @@ -140,7 +142,7 @@ namespace osu.Game.Screens.Select.Leaderboards scores = scores.Where(s => s.Mods.Any(m => selectedMods.Contains(m.Acronym))); } - scoreManager.OrderByTotalScoreAsync(scores.ToArray(), loadCancellationSource.Token) + scoreManager.OrderByTotalScoreAsync(scores.ToArray(), cancellationToken) .ContinueWith(ordered => scoresCallback?.Invoke(ordered.Result), TaskContinuationOptions.OnlyOnRanToCompletion); return null; @@ -176,10 +178,10 @@ namespace osu.Game.Screens.Select.Leaderboards req.Success += r => { - scoreManager.OrderByTotalScoreAsync(r.Scores.Select(s => s.CreateScoreInfo(rulesets, BeatmapInfo)).ToArray(), loadCancellationSource.Token) + scoreManager.OrderByTotalScoreAsync(r.Scores.Select(s => s.CreateScoreInfo(rulesets, BeatmapInfo)).ToArray(), cancellationToken) .ContinueWith(ordered => Schedule(() => { - if (loadCancellationSource.IsCancellationRequested) + if (cancellationToken.IsCancellationRequested) return; scoresCallback?.Invoke(ordered.Result); From f74afb48fdc424ecf1c08c1e60a33de0b75623ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Nov 2021 17:50:51 +0900 Subject: [PATCH 62/90] Fix `StandAloneChatDisplay`'s `TextBox` having different corner radius --- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index ede76235b1..1f8d05fcd1 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -41,8 +41,10 @@ namespace osu.Game.Online.Chat /// Whether a textbox for posting new messages should be displayed. public StandAloneChatDisplay(bool postingTextbox = false) { + const float corner_radius = 10; + this.postingTextbox = postingTextbox; - CornerRadius = 10; + CornerRadius = corner_radius; Masking = true; InternalChildren = new Drawable[] @@ -62,6 +64,7 @@ namespace osu.Game.Online.Chat RelativeSizeAxes = Axes.X, Height = textbox_height, PlaceholderText = "type your message", + CornerRadius = corner_radius, ReleaseFocusOnCommit = false, HoldFocus = true, Anchor = Anchor.BottomLeft, From 13f3e2eea9d3c590cd910e556feab0ee5e9742a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Nov 2021 10:48:33 +0900 Subject: [PATCH 63/90] Add back default timeout to `GetPlayableBeatmap` --- osu.Game/Beatmaps/IWorkingBeatmap.cs | 4 ++-- osu.Game/Beatmaps/WorkingBeatmap.cs | 19 ++++++++++--------- .../Play/HUD/PerformancePointsCounter.cs | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index 5e9b77392a..6bf338db59 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -92,10 +92,10 @@ namespace osu.Game.Beatmaps /// /// The to create a playable for. /// The s to apply to the . - /// Cancellation token that cancels the beatmap loading process. + /// Cancellation token that cancels the beatmap loading process. If not provided, a default timeout of 10,000ms will be applied to the load process. /// The converted . /// If could not be converted to . - IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken cancellationToken = default); + IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken? cancellationToken = null); /// /// Load a new audio track instance for this beatmap. This should be called once before accessing . diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 9be239e3b0..a04345ec5c 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -78,8 +78,9 @@ namespace osu.Game.Beatmaps /// The applicable . protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap); - public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken cancellationToken = default) + public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken? cancellationToken = null) { + var token = cancellationToken ?? new CancellationTokenSource(10000).Token; mods ??= Array.Empty(); var rulesetInstance = ruleset.CreateInstance(); @@ -96,19 +97,19 @@ namespace osu.Game.Beatmaps // Apply conversion mods foreach (var mod in mods.OfType()) { - if (cancellationToken.IsCancellationRequested) + if (token.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); mod.ApplyToBeatmapConverter(converter); } // Convert - IBeatmap converted = converter.Convert(cancellationToken); + IBeatmap converted = converter.Convert(token); // Apply conversion mods to the result foreach (var mod in mods.OfType()) { - if (cancellationToken.IsCancellationRequested) + if (token.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); mod.ApplyToBeatmap(converted); @@ -119,7 +120,7 @@ namespace osu.Game.Beatmaps { foreach (var mod in mods.OfType()) { - if (cancellationToken.IsCancellationRequested) + if (token.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); mod.ApplyToDifficulty(converted.Difficulty); @@ -138,10 +139,10 @@ namespace osu.Game.Beatmaps { foreach (var obj in converted.HitObjects) { - if (cancellationToken.IsCancellationRequested) + if (token.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); - obj.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty, cancellationToken); + obj.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty, token); } } catch (OperationCanceledException) @@ -153,7 +154,7 @@ namespace osu.Game.Beatmaps { foreach (var obj in converted.HitObjects) { - if (cancellationToken.IsCancellationRequested) + if (token.IsCancellationRequested) throw new BeatmapLoadTimeoutException(BeatmapInfo); mod.ApplyToHitObject(obj); @@ -164,7 +165,7 @@ namespace osu.Game.Beatmaps foreach (var mod in mods.OfType()) { - cancellationToken.ThrowIfCancellationRequested(); + token.ThrowIfCancellationRequested(); mod.ApplyToBeatmap(converted); } diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 6e679279a4..2f46682b1a 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -216,7 +216,7 @@ namespace osu.Game.Screens.Play.HUD this.gameplayBeatmap = gameplayBeatmap; } - public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken cancellationToken = default) + public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken? cancellationToken = null) => gameplayBeatmap; protected override IBeatmap GetBeatmap() => gameplayBeatmap; From 4d11794d31d0822e0738f49d5adb09492843a021 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Nov 2021 11:06:43 +0900 Subject: [PATCH 64/90] Add test coverage of `GetPlayableBeatmap` timeout and cancellation --- osu.Game.Tests/Beatmaps/WorkingBeatmapTest.cs | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 osu.Game.Tests/Beatmaps/WorkingBeatmapTest.cs diff --git a/osu.Game.Tests/Beatmaps/WorkingBeatmapTest.cs b/osu.Game.Tests/Beatmaps/WorkingBeatmapTest.cs new file mode 100644 index 0000000000..02e4a87a7a --- /dev/null +++ b/osu.Game.Tests/Beatmaps/WorkingBeatmapTest.cs @@ -0,0 +1,102 @@ +// 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 System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Beatmaps; + +namespace osu.Game.Tests.Beatmaps +{ + [TestFixture] + public class WorkingBeatmapTest + { + [Test] + public void TestGetPlayableSuccess() + { + var working = new TestNeverLoadsWorkingBeatmap(); + + working.ResetEvent.Set(); + + Assert.NotNull(working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo)); + } + + [Test] + public void TestGetPlayableCancellationToken() + { + var working = new TestNeverLoadsWorkingBeatmap(); + + var cts = new CancellationTokenSource(); + var loadStarted = new ManualResetEventSlim(); + var loadCompleted = new ManualResetEventSlim(); + + Task.Factory.StartNew(() => + { + loadStarted.Set(); + Assert.Throws(() => working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo, cancellationToken: cts.Token)); + loadCompleted.Set(); + }, TaskCreationOptions.LongRunning); + + Assert.IsTrue(loadStarted.Wait(10000)); + + cts.Cancel(); + + Assert.IsTrue(loadCompleted.Wait(10000)); + + working.ResetEvent.Set(); + } + + [Test] + public void TestGetPlayableDefaultTimeout() + { + var working = new TestNeverLoadsWorkingBeatmap(); + + Assert.Throws(() => working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo)); + + working.ResetEvent.Set(); + } + + public class TestNeverLoadsWorkingBeatmap : TestWorkingBeatmap + { + public ManualResetEventSlim ResetEvent = new ManualResetEventSlim(); + + public TestNeverLoadsWorkingBeatmap() + : base(new Beatmap()) + { + } + + protected override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => new TestConverter(beatmap, ResetEvent); + + public class TestConverter : IBeatmapConverter + { + private readonly ManualResetEventSlim resetEvent; + + public TestConverter(IBeatmap beatmap, ManualResetEventSlim resetEvent) + { + this.resetEvent = resetEvent; + Beatmap = beatmap; + } + + public event Action> ObjectConverted; + + protected virtual void OnObjectConverted(HitObject arg1, IEnumerable arg2) => ObjectConverted?.Invoke(arg1, arg2); + + public IBeatmap Beatmap { get; } + + public bool CanConvert() => true; + + public IBeatmap Convert(CancellationToken cancellationToken = default) + { + resetEvent.Wait(cancellationToken); + return new OsuBeatmap(); + } + } + } + } +} From 8e9b55ef6dbd061666e75b4b1228eeaa43d93bd8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Nov 2021 11:28:43 +0900 Subject: [PATCH 65/90] Disallow selection of invalid items --- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 2fa1a79ddd..96ff44fab5 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -90,7 +90,24 @@ namespace osu.Game.Screens.OnlinePlay { base.LoadComplete(); - SelectedItem.BindValueChanged(selected => maskingContainer.BorderThickness = selected.NewValue == Model ? 5 : 0, true); + SelectedItem.BindValueChanged(selected => + { + bool isCurrent = selected.NewValue == Model; + + if (!valid.Value) + { + // Don't allow selection when not valid. + if (isCurrent) + { + SelectedItem.Value = selected.OldValue; + } + + // Don't update border when not valid (the border is displaying this fact). + return; + } + + maskingContainer.BorderThickness = isCurrent ? 5 : 0; + }, true); beatmap.BindValueChanged(_ => Scheduler.AddOnce(refresh)); ruleset.BindValueChanged(_ => Scheduler.AddOnce(refresh)); From d600a7327704dea2bd70a7e2f181c3ecacc8fbf3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Nov 2021 11:38:41 +0900 Subject: [PATCH 66/90] Disable cancelling of update via notification --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index 910751a723..d48c9e9661 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -183,6 +183,19 @@ namespace osu.Desktop.Updater } }); } + + public override void Close() + { + // cancelling updates is not currently supported by the underlying updater. + // only allow dismissing for now. + + switch (State) + { + case ProgressNotificationState.Cancelled: + base.Close(); + break; + } + } } private class SquirrelLogger : Splat.ILogger, IDisposable From 96e983bbad1cdd70108eb9a37c4a9a205606c6a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Nov 2021 14:40:05 +0900 Subject: [PATCH 67/90] Disallow playlist item selection at playlist room creation screen Wasn't being used for anything, confusing that it's even possible. --- .../OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index cb4a81f108..7d96e5303c 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -205,7 +205,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { new Drawable[] { - playlist = new DrawableRoomPlaylist(true, true) { RelativeSizeAxes = Axes.Both } + playlist = new DrawableRoomPlaylist(true, false) { RelativeSizeAxes = Axes.Both } }, new Drawable[] { From 08f129b4c885c2e98402b7445e4b1b8094717e42 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 17 Nov 2021 20:09:30 +0900 Subject: [PATCH 68/90] Expose LegacyScoreDecoder.CalculateAccuracy() --- osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 7bfe0308f7..100ef121a8 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -124,7 +124,7 @@ namespace osu.Game.Scoring.Legacy return score; } - protected void CalculateAccuracy(ScoreInfo score) + public static void CalculateAccuracy(ScoreInfo score) { int countMiss = score.GetCountMiss() ?? 0; int count50 = score.GetCount50() ?? 0; From abb333299bcddd0ecb5f2b4fe1aed1073f118063 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 17 Nov 2021 20:20:54 +0900 Subject: [PATCH 69/90] Document base class --- .../Difficulty/DifficultyAttributes.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index cc9d111aa2..1073f689dd 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -9,23 +9,47 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Difficulty { + /// + /// Describes the difficulty of a beatmap, as output by a . + /// [JsonObject(MemberSerialization.OptIn)] public class DifficultyAttributes { + /// + /// The mods which were applied to the beatmap. + /// public Mod[] Mods { get; set; } + /// + /// The skills resulting from the difficulty calculation. + /// public Skill[] Skills { get; set; } + /// + /// The combined star rating of all skill. + /// [JsonProperty("star_rating", Order = -3)] public double StarRating { get; set; } + /// + /// The maximum achievable combo. + /// [JsonProperty("max_combo", Order = -2)] public int MaxCombo { get; set; } + /// + /// Creates new . + /// public DifficultyAttributes() { } + /// + /// Creates new . + /// + /// The mods which were applied to the beatmap. + /// The skills resulting from the difficulty calculation. + /// The combined star rating of all skills. public DifficultyAttributes(Mod[] mods, Skill[] skills, double starRating) { Mods = mods; @@ -33,8 +57,18 @@ namespace osu.Game.Rulesets.Difficulty StarRating = starRating; } + /// + /// Converts this to osu-web compatible database attribute mappings. + /// + /// + /// See: osu_difficulty_attribs table. + /// public virtual IEnumerable<(int attributeId, object value)> ToDatabaseAttributes() => Enumerable.Empty<(int, object)>(); + /// + /// Reads osu-web database attribute mappings into this object. + /// + /// The attribute mappings. public virtual void FromDatabaseAttributes(IReadOnlyDictionary values) { } From 2ae46f901e7785adeb064f1a73733787e679c2cf Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 17 Nov 2021 20:22:43 +0900 Subject: [PATCH 70/90] Add comment for implicitly-used method --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 7673d08440..14a5a3e745 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -70,6 +70,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty SliderFactor = values[19]; } + // Used implicitly by Newtonsoft.Json to not serialize flashlight property in some cases. [UsedImplicitly] public bool ShouldSerializeFlashlightRating() => Mods.OfType().Any(); } From 43cbb23b159ad9a3887cdcb63ae1f80a5e7c5695 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 17 Nov 2021 20:23:08 +0900 Subject: [PATCH 71/90] Use ShouldSerializeFlashlightRating() to serialise database attribute --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 14a5a3e745..a22b605176 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty yield return (9, MaxCombo); yield return (11, StarRating); - if (Mods.Any(m => m is ModFlashlight)) + if (ShouldSerializeFlashlightRating()) yield return (17, FlashlightRating); yield return (19, SliderFactor); @@ -72,6 +72,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Used implicitly by Newtonsoft.Json to not serialize flashlight property in some cases. [UsedImplicitly] - public bool ShouldSerializeFlashlightRating() => Mods.OfType().Any(); + public bool ShouldSerializeFlashlightRating() => Mods.Any(m => m is ModFlashlight); } } From 815179f713dd2d1f7b636ebdd0bf387d821fbd35 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 17 Nov 2021 20:31:18 +0900 Subject: [PATCH 72/90] Use consts for attribute IDs --- .../Difficulty/CatchDifficultyAttributes.cs | 14 ++++---- .../Difficulty/ManiaDifficultyAttributes.cs | 14 ++++---- .../Difficulty/OsuDifficultyAttributes.cs | 32 +++++++++---------- .../Difficulty/TaikoDifficultyAttributes.cs | 12 +++---- .../Difficulty/DifficultyAttributes.cs | 11 +++++++ 5 files changed, 47 insertions(+), 36 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs index ca63e87f8d..39a58d336d 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs @@ -17,19 +17,19 @@ namespace osu.Game.Rulesets.Catch.Difficulty foreach (var v in base.ToDatabaseAttributes()) yield return v; - // Todo: Catch should not output star rating in the 'aim' attribute. - yield return (1, StarRating); - yield return (7, ApproachRate); - yield return (9, MaxCombo); + // Todo: osu!catch should not output star rating in the 'aim' attribute. + yield return (ATTRIB_ID_AIM, StarRating); + yield return (ATTRIB_ID_APPROACH_RATE, ApproachRate); + yield return (ATTRIB_ID_MAX_COMBO, MaxCombo); } public override void FromDatabaseAttributes(IReadOnlyDictionary values) { base.FromDatabaseAttributes(values); - StarRating = values[1]; - ApproachRate = values[7]; - MaxCombo = (int)values[9]; + StarRating = values[ATTRIB_ID_AIM]; + ApproachRate = values[ATTRIB_ID_APPROACH_RATE]; + MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO]; } } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs index 1e663fe103..bfdef893e9 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs @@ -20,19 +20,19 @@ namespace osu.Game.Rulesets.Mania.Difficulty foreach (var v in base.ToDatabaseAttributes()) yield return v; - // Todo: Mania doesn't output MaxCombo attribute for some reason. - yield return (11, StarRating); - yield return (13, GreatHitWindow); - yield return (15, ScoreMultiplier); + // Todo: osu!mania doesn't output MaxCombo attribute for some reason. + yield return (ATTRIB_ID_STRAIN, StarRating); + yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow); + yield return (ATTRIB_ID_SCORE_MULTIPLIER, ScoreMultiplier); } public override void FromDatabaseAttributes(IReadOnlyDictionary values) { base.FromDatabaseAttributes(values); - StarRating = values[11]; - GreatHitWindow = values[13]; - ScoreMultiplier = values[15]; + StarRating = values[ATTRIB_ID_STRAIN]; + GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW]; + ScoreMultiplier = values[ATTRIB_ID_SCORE_MULTIPLIER]; } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index a22b605176..4b2e54da17 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -43,31 +43,31 @@ namespace osu.Game.Rulesets.Osu.Difficulty foreach (var v in base.ToDatabaseAttributes()) yield return v; - yield return (1, AimStrain); - yield return (3, SpeedStrain); - yield return (5, OverallDifficulty); - yield return (7, ApproachRate); - yield return (9, MaxCombo); - yield return (11, StarRating); + yield return (ATTRIB_ID_AIM, AimStrain); + yield return (ATTRIB_ID_SPEED, SpeedStrain); + yield return (ATTRIB_ID_OVERALL_DIFFICULTY, OverallDifficulty); + yield return (ATTRIB_ID_APPROACH_RATE, ApproachRate); + yield return (ATTRIB_ID_MAX_COMBO, MaxCombo); + yield return (ATTRIB_ID_STRAIN, StarRating); if (ShouldSerializeFlashlightRating()) - yield return (17, FlashlightRating); + yield return (ATTRIB_ID_FLASHLIGHT, FlashlightRating); - yield return (19, SliderFactor); + yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor); } public override void FromDatabaseAttributes(IReadOnlyDictionary values) { base.FromDatabaseAttributes(values); - AimStrain = values[1]; - SpeedStrain = values[3]; - OverallDifficulty = values[5]; - ApproachRate = values[7]; - MaxCombo = (int)values[9]; - StarRating = values[11]; - FlashlightRating = values.GetValueOrDefault(17); - SliderFactor = values[19]; + AimStrain = values[ATTRIB_ID_AIM]; + SpeedStrain = values[ATTRIB_ID_SPEED]; + OverallDifficulty = values[ATTRIB_ID_OVERALL_DIFFICULTY]; + ApproachRate = values[ATTRIB_ID_APPROACH_RATE]; + MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO]; + StarRating = values[ATTRIB_ID_STRAIN]; + FlashlightRating = values.GetValueOrDefault(ATTRIB_ID_FLASHLIGHT); + SliderFactor = values[ATTRIB_ID_SLIDER_FACTOR]; } // Used implicitly by Newtonsoft.Json to not serialize flashlight property in some cases. diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs index a6057d7952..b2b5d056c3 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs @@ -29,18 +29,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty foreach (var v in base.ToDatabaseAttributes()) yield return v; - yield return (9, MaxCombo); - yield return (11, StarRating); - yield return (13, GreatHitWindow); + yield return (ATTRIB_ID_MAX_COMBO, MaxCombo); + yield return (ATTRIB_ID_STRAIN, StarRating); + yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow); } public override void FromDatabaseAttributes(IReadOnlyDictionary values) { base.FromDatabaseAttributes(values); - MaxCombo = (int)values[9]; - StarRating = values[11]; - GreatHitWindow = values[13]; + MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO]; + StarRating = values[ATTRIB_ID_STRAIN]; + GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW]; } } } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index 1073f689dd..320321440a 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -72,5 +72,16 @@ namespace osu.Game.Rulesets.Difficulty public virtual void FromDatabaseAttributes(IReadOnlyDictionary values) { } + + protected const int ATTRIB_ID_AIM = 1; + protected const int ATTRIB_ID_SPEED = 3; + protected const int ATTRIB_ID_OVERALL_DIFFICULTY = 5; + protected const int ATTRIB_ID_APPROACH_RATE = 7; + protected const int ATTRIB_ID_MAX_COMBO = 9; + protected const int ATTRIB_ID_STRAIN = 11; + protected const int ATTRIB_ID_GREAT_HIT_WINDOW = 13; + protected const int ATTRIB_ID_SCORE_MULTIPLIER = 15; + protected const int ATTRIB_ID_FLASHLIGHT = 17; + protected const int ATTRIB_ID_SLIDER_FACTOR = 19; } } From 9182eab48651f4ec41eeb8dc214f4bb11858e782 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 17 Nov 2021 20:45:48 +0900 Subject: [PATCH 73/90] Rename method + xmldoc --- osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 100ef121a8..7ac4f90c07 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -115,7 +115,7 @@ namespace osu.Game.Scoring.Legacy } } - CalculateAccuracy(score.ScoreInfo); + PopulateAccuracy(score.ScoreInfo); // before returning for database import, we must restore the database-sourced BeatmapInfo. // if not, the clone operation in GetPlayableBeatmap will cause a dereference and subsequent database exception. @@ -124,7 +124,14 @@ namespace osu.Game.Scoring.Legacy return score; } - public static void CalculateAccuracy(ScoreInfo score) + /// + /// Populates the accuracy of a given from its contained statistics. + /// + /// + /// Legacy use only. + /// + /// The to populate. + public static void PopulateAccuracy(ScoreInfo score) { int countMiss = score.GetCountMiss() ?? 0; int count50 = score.GetCount50() ?? 0; From 0a7498014fa7125929e93cab6f1682771ec58a32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Nov 2021 20:47:52 +0900 Subject: [PATCH 74/90] Move `const`s to top of file --- .../Difficulty/DifficultyAttributes.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index 320321440a..e0d27799ce 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -15,6 +15,17 @@ namespace osu.Game.Rulesets.Difficulty [JsonObject(MemberSerialization.OptIn)] public class DifficultyAttributes { + protected const int ATTRIB_ID_AIM = 1; + protected const int ATTRIB_ID_SPEED = 3; + protected const int ATTRIB_ID_OVERALL_DIFFICULTY = 5; + protected const int ATTRIB_ID_APPROACH_RATE = 7; + protected const int ATTRIB_ID_MAX_COMBO = 9; + protected const int ATTRIB_ID_STRAIN = 11; + protected const int ATTRIB_ID_GREAT_HIT_WINDOW = 13; + protected const int ATTRIB_ID_SCORE_MULTIPLIER = 15; + protected const int ATTRIB_ID_FLASHLIGHT = 17; + protected const int ATTRIB_ID_SLIDER_FACTOR = 19; + /// /// The mods which were applied to the beatmap. /// @@ -72,16 +83,5 @@ namespace osu.Game.Rulesets.Difficulty public virtual void FromDatabaseAttributes(IReadOnlyDictionary values) { } - - protected const int ATTRIB_ID_AIM = 1; - protected const int ATTRIB_ID_SPEED = 3; - protected const int ATTRIB_ID_OVERALL_DIFFICULTY = 5; - protected const int ATTRIB_ID_APPROACH_RATE = 7; - protected const int ATTRIB_ID_MAX_COMBO = 9; - protected const int ATTRIB_ID_STRAIN = 11; - protected const int ATTRIB_ID_GREAT_HIT_WINDOW = 13; - protected const int ATTRIB_ID_SCORE_MULTIPLIER = 15; - protected const int ATTRIB_ID_FLASHLIGHT = 17; - protected const int ATTRIB_ID_SLIDER_FACTOR = 19; } } From 7c2e79f911b1d09d9c0a022cdf3c4b59cb770367 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Nov 2021 18:46:11 +0900 Subject: [PATCH 75/90] Update all simple cases of switching to `IWorkingBeatmap` --- .../TestSceneDrawableHitObjects.cs | 2 +- .../DrawableTaikoRulesetTestScene.cs | 2 +- .../Skins/TestSceneBeatmapSkinResources.cs | 2 +- .../Editing/TestSceneHitObjectComposer.cs | 4 ++-- .../Visual/Editing/TestSceneWaveform.cs | 2 +- .../Visual/Gameplay/TestScenePlayerLoader.cs | 5 ++-- .../TestScenePlayerReferenceLeaking.cs | 2 +- .../Visual/Gameplay/TestSceneStoryboard.cs | 2 +- .../Menus/TestSceneMusicActionHandling.cs | 4 ++-- .../Navigation/TestScenePresentBeatmap.cs | 3 ++- .../Navigation/TestSceneScreenNavigation.cs | 4 ++-- .../TestSceneBeatmapMetadataDisplay.cs | 2 +- .../SongSelect/TestScenePlaySongSelect.cs | 23 ++++++++++--------- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- .../Drawables/BeatmapBackgroundSprite.cs | 4 ++-- osu.Game/Online/Chat/NowPlayingCommand.cs | 4 ++-- .../Screens/Play/BeatmapMetadataDisplay.cs | 4 ++-- osu.Game/Screens/Play/Player.cs | 4 ++-- .../Select/BeatmapInfoWedgeBackground.cs | 4 ++-- .../Select/Carousel/SetPanelBackground.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- osu.Game/Storyboards/Storyboard.cs | 2 +- osu.Game/Users/UserActivity.cs | 14 +++++------ 24 files changed, 52 insertions(+), 49 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs index 3c61eb19e5..459b8e1f6f 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Tests var controlPointInfo = new ControlPointInfo(); controlPointInfo.Add(0, new TimingControlPoint()); - WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap + IWorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap { HitObjects = new List { new Fruit() }, BeatmapInfo = new BeatmapInfo diff --git a/osu.Game.Rulesets.Taiko.Tests/DrawableTaikoRulesetTestScene.cs b/osu.Game.Rulesets.Taiko.Tests/DrawableTaikoRulesetTestScene.cs index 783636a62d..4bdb85ba60 100644 --- a/osu.Game.Rulesets.Taiko.Tests/DrawableTaikoRulesetTestScene.cs +++ b/osu.Game.Rulesets.Taiko.Tests/DrawableTaikoRulesetTestScene.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Tests var controlPointInfo = new ControlPointInfo(); controlPointInfo.Add(0, new TimingControlPoint()); - WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap + IWorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap { HitObjects = new List { new Hit { Type = HitType.Centre } }, BeatmapInfo = new BeatmapInfo diff --git a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs index f03cda1489..1d8b754837 100644 --- a/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs +++ b/osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Skins [Resolved] private BeatmapManager beatmaps { get; set; } - private WorkingBeatmap beatmap; + private IWorkingBeatmap beatmap; [BackgroundDependencyLoader] private void load() diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index c758bd1707..eee0d6672c 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -81,11 +81,11 @@ namespace osu.Game.Tests.Visual.Editing public class EditorBeatmapContainer : Container { - private readonly WorkingBeatmap working; + private readonly IWorkingBeatmap working; public EditorBeatmap EditorBeatmap { get; private set; } - public EditorBeatmapContainer(WorkingBeatmap working) + public EditorBeatmapContainer(IWorkingBeatmap working) { this.working = working; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneWaveform.cs b/osu.Game.Tests/Visual/Editing/TestSceneWaveform.cs index c3a5a0e944..eb7e90f0a7 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneWaveform.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneWaveform.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Editing [TestFixture] public class TestSceneWaveform : OsuTestScene { - private WorkingBeatmap waveformBeatmap; + private IWorkingBeatmap waveformBeatmap; [BackgroundDependencyLoader] private void load(AudioManager audio) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index ba0ee5ac6e..06eaa726c9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -95,8 +95,9 @@ namespace osu.Game.Tests.Visual.Gameplay private void prepareBeatmap() { - Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - Beatmap.Value.BeatmapInfo.EpilepsyWarning = epilepsyWarning; + var workingBeatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + workingBeatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning; + Beatmap.Value = workingBeatmap; foreach (var mod in SelectedMods.Value.OfType()) mod.ApplyToTrack(Beatmap.Value.Track); diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs index 8f767659c6..7c2f1c5bb2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerReferenceLeaking.cs @@ -12,7 +12,7 @@ namespace osu.Game.Tests.Visual.Gameplay { public class TestScenePlayerReferenceLeaking : TestSceneAllRulesetPlayers { - private readonly WeakList workingWeakReferences = new WeakList(); + private readonly WeakList workingWeakReferences = new WeakList(); private readonly WeakList playerWeakReferences = new WeakList(); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs index 5a2b8d22fd..3b6d02c67c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs @@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual.Gameplay track.Start(); } - private void loadStoryboard(WorkingBeatmap working) + private void loadStoryboard(IWorkingBeatmap working) { if (storyboard != null) storyboardContainer.Remove(storyboard); diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs index 79dfe79299..6dda8df6f0 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Menus public void TestMusicNavigationActions() { int importId = 0; - Queue<(WorkingBeatmap working, TrackChangeDirection changeDirection)> trackChangeQueue = null; + Queue<(IWorkingBeatmap working, TrackChangeDirection changeDirection)> trackChangeQueue = null; // ensure we have at least two beatmaps available to identify the direction the music controller navigated to. AddRepeatStep("import beatmap", () => Game.BeatmapManager.Import(new BeatmapSetInfo @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Menus AddStep("bind to track change", () => { - trackChangeQueue = new Queue<(WorkingBeatmap, TrackChangeDirection)>(); + trackChangeQueue = new Queue<(IWorkingBeatmap, TrackChangeDirection)>(); Game.MusicController.TrackChanged += (working, changeDirection) => trackChangeQueue.Enqueue((working, changeDirection)); }); diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs index ff976c7bf6..d1ee984682 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentBeatmap.cs @@ -7,6 +7,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Screens; using osu.Game.Beatmaps; +using osu.Game.Extensions; using osu.Game.Rulesets; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; @@ -139,7 +140,7 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("present beatmap", () => Game.PresentBeatmap(getImport())); AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is Screens.Select.SongSelect); - AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapSetInfo.ID == getImport().ID); + AddUntilStep("correct beatmap displayed", () => Game.Beatmap.Value.BeatmapSetInfo.MatchesOnlineID(getImport())); AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Beatmaps.First().Ruleset.ID); } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 5d4594c415..ef71c54e03 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Navigation Player player = null; ResultsScreen results = null; - WorkingBeatmap beatmap() => Game.Beatmap.Value; + IWorkingBeatmap beatmap() => Game.Beatmap.Value; PushAndConfirm(() => new TestPlaySongSelect()); @@ -128,7 +128,7 @@ namespace osu.Game.Tests.Visual.Navigation { Player player = null; - WorkingBeatmap beatmap() => Game.Beatmap.Value; + IWorkingBeatmap beatmap() => Game.Beatmap.Value; PushAndConfirm(() => new TestPlaySongSelect()); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 9473b058cc..11d589f7bc 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual.SongSelect }); } - private void showMetadataForBeatmap(Func getBeatmap) + private void showMetadataForBeatmap(Func getBeatmap) { AddStep("setup display", () => { diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index ee5a61f21f..3646b52cfa 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -17,6 +17,7 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; @@ -360,7 +361,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo.Equals(target)); // this is an important check, to make sure updateComponentFromBeatmap() was actually run - AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo.Equals(target)); + AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo.MatchesOnlineID(target)); } [Test] @@ -392,7 +393,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("has correct ruleset", () => Ruleset.Value.ID == 0); // this is an important check, to make sure updateComponentFromBeatmap() was actually run - AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo.Equals(target)); + AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo.MatchesOnlineID(target)); } [Test] @@ -672,7 +673,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("wait for selection", () => !Beatmap.IsDefault); - AddStep("record set ID", () => previousSetID = Beatmap.Value.BeatmapSetInfo.ID); + AddStep("record set ID", () => previousSetID = ((IBeatmapSetInfo)Beatmap.Value.BeatmapSetInfo).OnlineID); AddAssert("selection changed once", () => changeCount == 1); AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0); @@ -683,8 +684,8 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("selection changed", () => changeCount > 1); - AddAssert("Selected beatmap still same set", () => Beatmap.Value.BeatmapSetInfo.ID == previousSetID); - AddAssert("Selected beatmap is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.ID == 3); + AddAssert("Selected beatmap still same set", () => Beatmap.Value.BeatmapSetInfo.OnlineID == previousSetID); + AddAssert("Selected beatmap is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.OnlineID == 3); AddAssert("selection changed only fired twice", () => changeCount == 2); @@ -727,7 +728,7 @@ namespace osu.Game.Tests.Visual.SongSelect int previousSetID = 0; - AddStep("record set ID", () => previousSetID = Beatmap.Value.BeatmapSetInfo.ID); + AddStep("record set ID", () => previousSetID = ((IBeatmapSetInfo)Beatmap.Value.BeatmapSetInfo).OnlineID); AddStep("Click on a difficulty", () => { @@ -739,7 +740,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3); AddAssert("Selected beatmap still same set", () => songSelect.Carousel.SelectedBeatmapInfo.BeatmapSet.ID == previousSetID); - AddAssert("Selected beatmap is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.ID == 3); + AddAssert("Selected beatmap is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.OnlineID == 3); } [Test] @@ -784,7 +785,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3); - AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo.Equals(groupIcon.Items.First().BeatmapInfo)); + AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(groupIcon.Items.First().BeatmapInfo)); } [Test] @@ -815,7 +816,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen()); - AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(getPresentBeatmap())); + AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(getPresentBeatmap())); AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0); } @@ -847,7 +848,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("wait for results screen presented", () => !songSelect.IsCurrentScreen()); - AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.Equals(getPresentBeatmap())); + AddAssert("check beatmap is correct for score", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(getPresentBeatmap())); AddAssert("check ruleset is correct for score", () => Ruleset.Value.ID == 0); } @@ -960,7 +961,7 @@ namespace osu.Game.Tests.Visual.SongSelect public new FilterControl FilterControl => base.FilterControl; public WorkingBeatmap CurrentBeatmap => Beatmap.Value; - public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; + public IWorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; public new BeatmapCarousel Carousel => base.Carousel; public new void PresentScore(ScoreInfo score) => base.PresentScore(score); diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 7231409dc5..0e9a92989c 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -151,7 +151,7 @@ namespace osu.Game.Beatmaps }, token, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); } - public Task> GetTimedDifficultyAttributesAsync(WorkingBeatmap beatmap, Ruleset ruleset, Mod[] mods, CancellationToken token = default) + public Task> GetTimedDifficultyAttributesAsync(IWorkingBeatmap beatmap, Ruleset ruleset, Mod[] mods, CancellationToken token = default) { return Task.Factory.StartNew(() => ruleset.CreateDifficultyCalculator(beatmap).CalculateTimed(mods), token, diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 48a6663adb..c2a2e93caf 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -179,7 +179,7 @@ namespace osu.Game.Beatmaps /// /// A default representation of a WorkingBeatmap to use when no beatmap is available. /// - public WorkingBeatmap DefaultBeatmap => workingBeatmapCache.DefaultBeatmap; + public IWorkingBeatmap DefaultBeatmap => workingBeatmapCache.DefaultBeatmap; /// /// Fired when a notification should be presented to the user. diff --git a/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs index 0c59eec1ef..916df429a4 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs @@ -9,9 +9,9 @@ namespace osu.Game.Beatmaps.Drawables { public class BeatmapBackgroundSprite : Sprite { - private readonly WorkingBeatmap working; + private readonly IWorkingBeatmap working; - public BeatmapBackgroundSprite(WorkingBeatmap working) + public BeatmapBackgroundSprite(IWorkingBeatmap working) { if (working == null) throw new ArgumentNullException(nameof(working)); diff --git a/osu.Game/Online/Chat/NowPlayingCommand.cs b/osu.Game/Online/Chat/NowPlayingCommand.cs index 34b12c23e6..2bf35d7973 100644 --- a/osu.Game/Online/Chat/NowPlayingCommand.cs +++ b/osu.Game/Online/Chat/NowPlayingCommand.cs @@ -37,7 +37,7 @@ namespace osu.Game.Online.Chat base.LoadComplete(); string verb; - BeatmapInfo beatmapInfo; + IBeatmapInfo beatmapInfo; switch (api.Activity.Value) { @@ -57,7 +57,7 @@ namespace osu.Game.Online.Chat break; } - string beatmapString = beatmapInfo.OnlineID.HasValue ? $"[{api.WebsiteRootUrl}/b/{beatmapInfo.OnlineID} {beatmapInfo}]" : beatmapInfo.ToString(); + string beatmapString = beatmapInfo.OnlineID > 0 ? $"[{api.WebsiteRootUrl}/b/{beatmapInfo.OnlineID} {beatmapInfo}]" : beatmapInfo.ToString(); channelManager.PostMessage($"is {verb} {beatmapString}", true, target); Expire(); diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 430571e1da..795dddfaf5 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Play /// public class BeatmapMetadataDisplay : Container { - private readonly WorkingBeatmap beatmap; + private readonly IWorkingBeatmap beatmap; private readonly Bindable> mods; private readonly Drawable logoFacade; private LoadingSpinner loading; @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable logoFacade) + public BeatmapMetadataDisplay(IWorkingBeatmap beatmap, Bindable> mods, Drawable logoFacade) { this.beatmap = beatmap; this.logoFacade = logoFacade; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 1381493fdf..4d574dea99 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -354,7 +354,7 @@ namespace osu.Game.Screens.Play private Drawable createUnderlayComponents() => DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both }; - private Drawable createGameplayComponents(WorkingBeatmap working, IBeatmap playableBeatmap) => new ScalingContainer(ScalingMode.Gameplay) + private Drawable createGameplayComponents(IWorkingBeatmap working, IBeatmap playableBeatmap) => new ScalingContainer(ScalingMode.Gameplay) { Children = new Drawable[] { @@ -372,7 +372,7 @@ namespace osu.Game.Screens.Play } }; - private Drawable createOverlayComponents(WorkingBeatmap working) + private Drawable createOverlayComponents(IWorkingBeatmap working) { var container = new Container { diff --git a/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs index 25056790cc..d9b8a20846 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedgeBackground.cs @@ -15,9 +15,9 @@ namespace osu.Game.Screens.Select { internal class BeatmapInfoWedgeBackground : CompositeDrawable { - private readonly WorkingBeatmap beatmap; + private readonly IWorkingBeatmap beatmap; - public BeatmapInfoWedgeBackground(WorkingBeatmap beatmap) + public BeatmapInfoWedgeBackground(IWorkingBeatmap beatmap) { this.beatmap = beatmap; } diff --git a/osu.Game/Screens/Select/Carousel/SetPanelBackground.cs b/osu.Game/Screens/Select/Carousel/SetPanelBackground.cs index b4279cce51..2a37969be1 100644 --- a/osu.Game/Screens/Select/Carousel/SetPanelBackground.cs +++ b/osu.Game/Screens/Select/Carousel/SetPanelBackground.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Select.Carousel { public class SetPanelBackground : BufferedContainer { - public SetPanelBackground(WorkingBeatmap working) + public SetPanelBackground(IWorkingBeatmap working) : base(cachedFrameBuffer: true) { RedrawOnScale = false; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 4da15ee53c..aef00a9b00 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -671,7 +671,7 @@ namespace osu.Game.Screens.Select music.TrackChanged -= ensureTrackLooping; } - private void ensureTrackLooping(WorkingBeatmap beatmap, TrackChangeDirection changeDirection) + private void ensureTrackLooping(IWorkingBeatmap beatmap, TrackChangeDirection changeDirection) => beatmap.PrepareTrackForPreviewLooping(); public override bool OnBackButton() diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 9e3d3df915..d3d6797eae 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -89,7 +89,7 @@ namespace osu.Game.Storyboards } } - public DrawableStoryboard CreateDrawable(WorkingBeatmap working = null) => + public DrawableStoryboard CreateDrawable(IWorkingBeatmap working = null) => new DrawableStoryboard(this); public Drawable CreateSpriteFromResourcePath(string path, TextureStore textureStore) diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index 91bcb37fcc..0874685f49 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -27,11 +27,11 @@ namespace osu.Game.Users public abstract class InGame : UserActivity { - public BeatmapInfo BeatmapInfo { get; } + public IBeatmapInfo BeatmapInfo { get; } public RulesetInfo Ruleset { get; } - protected InGame(BeatmapInfo beatmapInfo, RulesetInfo ruleset) + protected InGame(IBeatmapInfo beatmapInfo, RulesetInfo ruleset) { BeatmapInfo = beatmapInfo; Ruleset = ruleset; @@ -42,7 +42,7 @@ namespace osu.Game.Users public class InMultiplayerGame : InGame { - public InMultiplayerGame(BeatmapInfo beatmapInfo, RulesetInfo ruleset) + public InMultiplayerGame(IBeatmapInfo beatmapInfo, RulesetInfo ruleset) : base(beatmapInfo, ruleset) { } @@ -52,7 +52,7 @@ namespace osu.Game.Users public class InPlaylistGame : InGame { - public InPlaylistGame(BeatmapInfo beatmapInfo, RulesetInfo ruleset) + public InPlaylistGame(IBeatmapInfo beatmapInfo, RulesetInfo ruleset) : base(beatmapInfo, ruleset) { } @@ -60,7 +60,7 @@ namespace osu.Game.Users public class InSoloGame : InGame { - public InSoloGame(BeatmapInfo beatmapInfo, RulesetInfo ruleset) + public InSoloGame(IBeatmapInfo beatmapInfo, RulesetInfo ruleset) : base(beatmapInfo, ruleset) { } @@ -68,9 +68,9 @@ namespace osu.Game.Users public class Editing : UserActivity { - public BeatmapInfo BeatmapInfo { get; } + public IBeatmapInfo BeatmapInfo { get; } - public Editing(BeatmapInfo info) + public Editing(IBeatmapInfo info) { BeatmapInfo = info; } From a4953b5658a9f3dd71a4907fc032837ee6eed15a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Nov 2021 11:34:46 +0900 Subject: [PATCH 76/90] Update some remaining test usage of database ID --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 3646b52cfa..0437c1b25b 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -508,13 +508,13 @@ namespace osu.Game.Tests.Visual.SongSelect i.IsFiltered || i.Item.BeatmapInfo.Ruleset.ID == targetRuleset || i.Item.BeatmapInfo.Ruleset.ID == 0); }); - AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.OnlineID == target.OnlineID); - AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineID == target.OnlineID); + AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.MatchesOnlineID(target) == true); + AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(target)); AddStep("reset filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = string.Empty); - AddAssert("game still correct", () => Beatmap.Value?.BeatmapInfo.OnlineID == target.OnlineID); - AddAssert("carousel still correct", () => songSelect.Carousel.SelectedBeatmapInfo.OnlineID == target.OnlineID); + AddAssert("game still correct", () => Beatmap.Value?.BeatmapInfo.MatchesOnlineID(target) == true); + AddAssert("carousel still correct", () => songSelect.Carousel.SelectedBeatmapInfo.MatchesOnlineID(target)); } [Test] @@ -545,8 +545,8 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmapInfo != null); - AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.OnlineID == target.OnlineID); - AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineID == target.OnlineID); + AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmapInfo?.MatchesOnlineID(target) == true); + AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(target)); AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = "nononoo"); @@ -739,7 +739,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3); - AddAssert("Selected beatmap still same set", () => songSelect.Carousel.SelectedBeatmapInfo.BeatmapSet.ID == previousSetID); + AddAssert("Selected beatmap still same set", () => songSelect.Carousel.SelectedBeatmapInfo.BeatmapSet.OnlineID == previousSetID); AddAssert("Selected beatmap is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.OnlineID == 3); } From 5fb5baa4c9d5442f8cb14b6c06577e2c829504af Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 17 Nov 2021 21:06:33 +0900 Subject: [PATCH 77/90] Adjust error message --- .../OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index 7d96e5303c..dc773b7762 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -355,7 +355,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists if (text.StartsWith(not_found_prefix, StringComparison.Ordinal)) { - ErrorText.Text = "One or more beatmap was not available online. Please remove or replaced the highlighted items."; + ErrorText.Text = "One or more beatmaps were not available online. Please remove or replaced the highlighted items."; int[] invalidBeatmapIDs = text .Substring(not_found_prefix.Length + 1) From 214705f1e528cc53e820bbc01a33313e61d608da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Nov 2021 21:11:37 +0900 Subject: [PATCH 78/90] Adjust error message, take 2 --- .../OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index dc773b7762..27c8dc1120 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -355,7 +355,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists if (text.StartsWith(not_found_prefix, StringComparison.Ordinal)) { - ErrorText.Text = "One or more beatmaps were not available online. Please remove or replaced the highlighted items."; + ErrorText.Text = "One or more beatmaps were not available online. Please remove or replace the highlighted items."; int[] invalidBeatmapIDs = text .Substring(not_found_prefix.Length + 1) From 8528cab40b862c4b03c27c8543ebb9c95f6ea15d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Nov 2021 22:07:24 +0100 Subject: [PATCH 79/90] Add test coverage for ruleset load failure scenario --- osu.Game.Tests/Beatmaps/WorkingBeatmapTest.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/WorkingBeatmapTest.cs b/osu.Game.Tests/Beatmaps/WorkingBeatmapTest.cs index 02e4a87a7a..6ad63a5fbb 100644 --- a/osu.Game.Tests/Beatmaps/WorkingBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/WorkingBeatmapTest.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Moq; using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets; @@ -62,6 +63,17 @@ namespace osu.Game.Tests.Beatmaps working.ResetEvent.Set(); } + [Test] + public void TestGetPlayableRulesetLoadFailure() + { + var working = new TestWorkingBeatmap(new Beatmap()); + + // by default mocks return nulls if not set up, which is actually desired here to simulate a ruleset load failure scenario. + var ruleset = new Mock(); + + Assert.Throws(() => working.GetPlayableBeatmap(ruleset.Object)); + } + public class TestNeverLoadsWorkingBeatmap : TestWorkingBeatmap { public ManualResetEventSlim ResetEvent = new ManualResetEventSlim(); From 1c13b39104a14cf9dc9d7727a0460e8bfb63bab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Nov 2021 22:00:09 +0100 Subject: [PATCH 80/90] Revert incorrect ordering change --- osu.Game/Beatmaps/WorkingBeatmap.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index dc38a9af69..f46cd405b5 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -85,11 +85,11 @@ namespace osu.Game.Beatmaps var rulesetInstance = ruleset.CreateInstance(); - IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance); - if (rulesetInstance == null) throw new RulesetLoadException("Creating ruleset instance failed when attempting to create playable beatmap."); + IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance); + // Check if the beatmap can be converted if (Beatmap.HitObjects.Count > 0 && !converter.CanConvert()) throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter})."); From cd5e379c4828e8456bf2e6cd90d10ae3d8621e80 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 16 Nov 2021 23:32:37 -0800 Subject: [PATCH 81/90] Link osu!supporter icon to its web page --- .../Profile/Header/Components/SupporterIcon.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs index 77f0378762..bee3548008 100644 --- a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs @@ -5,22 +5,22 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Header.Components { - public class SupporterIcon : CompositeDrawable, IHasTooltip + public class SupporterIcon : OsuClickableContainer { private readonly Box background; private readonly FillFlowContainer iconContainer; private readonly CircularContainer content; - public LocalisableString TooltipText => UsersStrings.ShowIsSupporter; + public override LocalisableString TooltipText => UsersStrings.ShowIsSupporter; public int SupportLevel { @@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { AutoSizeAxes = Axes.X; - InternalChild = content = new CircularContainer + Child = content = new CircularContainer { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, @@ -78,10 +78,12 @@ namespace osu.Game.Overlays.Profile.Header.Components }; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + [BackgroundDependencyLoader(true)] + private void load(OsuColour colours, OsuGame game) { background.Colour = colours.Pink; + + Action = () => game?.OpenUrlExternally(@"/home/support"); } } } From 3de8125eac9e73127a183553587fadf04f391061 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Nov 2021 12:35:47 +0900 Subject: [PATCH 82/90] Update UI cases where repeat should not be handled --- osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs | 3 +++ osu.Game/Graphics/ScreenshotManager.cs | 3 +++ osu.Game/Graphics/UserInterface/BackButton.cs | 3 +++ osu.Game/Graphics/UserInterface/FocusedTextBox.cs | 3 +++ osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs | 3 +++ osu.Game/OsuGame.cs | 3 +++ osu.Game/Overlays/ChangelogOverlay.cs | 3 +++ osu.Game/Overlays/DialogOverlay.cs | 3 +++ osu.Game/Overlays/Music/MusicKeyBindingHandler.cs | 3 +++ osu.Game/Screens/Menu/ButtonSystem.cs | 3 +++ osu.Game/Screens/Menu/ExitConfirmOverlay.cs | 3 +++ osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 3 +++ .../Screens/OnlinePlay/Match/Components/CreateRoomButton.cs | 3 +++ .../Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs | 3 +++ osu.Game/Screens/Play/HUD/HoldForMenuButton.cs | 3 +++ osu.Game/Screens/Play/HUDOverlay.cs | 3 +++ osu.Game/Screens/Play/HotkeyExitOverlay.cs | 3 +++ osu.Game/Screens/Play/HotkeyRetryOverlay.cs | 3 +++ osu.Game/Screens/Play/SkipOverlay.cs | 3 +++ osu.Game/Screens/Ranking/ResultsScreen.cs | 3 +++ osu.Game/Screens/Select/SongSelect.cs | 3 +++ 21 files changed, 63 insertions(+) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 16ec7ab838..68351acd7e 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -90,6 +90,9 @@ namespace osu.Game.Graphics.Containers public virtual bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case GlobalAction.Back: diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 1f7f93b3c3..a39d7bfb47 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -60,6 +60,9 @@ namespace osu.Game.Graphics public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case GlobalAction.TakeScreenshot: diff --git a/osu.Game/Graphics/UserInterface/BackButton.cs b/osu.Game/Graphics/UserInterface/BackButton.cs index c965fbcf45..1b564ef1b4 100644 --- a/osu.Game/Graphics/UserInterface/BackButton.cs +++ b/osu.Game/Graphics/UserInterface/BackButton.cs @@ -64,6 +64,9 @@ namespace osu.Game.Graphics.UserInterface public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case GlobalAction.Back: diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index 88608bf43c..578ff3c618 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -75,6 +75,9 @@ namespace osu.Game.Graphics.UserInterface public virtual bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + if (!HasFocus) return false; if (e.Action == GlobalAction.Back) diff --git a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs index 226c39c030..d1857dd174 100644 --- a/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs +++ b/osu.Game/Graphics/UserInterfaceV2/OsuPopover.cs @@ -58,6 +58,9 @@ namespace osu.Game.Graphics.UserInterfaceV2 public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + if (State.Value == Visibility.Hidden) return false; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 095add399c..1274d8d867 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1007,6 +1007,9 @@ namespace osu.Game public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + if (introScreen == null) return false; switch (e.Action) diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs index ce12e9554d..fe611d0134 100644 --- a/osu.Game/Overlays/ChangelogOverlay.cs +++ b/osu.Game/Overlays/ChangelogOverlay.cs @@ -94,6 +94,9 @@ namespace osu.Game.Overlays public override bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case GlobalAction.Back: diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 9db0f34d1b..9dea1ca00a 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -102,6 +102,9 @@ namespace osu.Game.Overlays public override bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case GlobalAction.Select: diff --git a/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs index 18ec69e106..baee17fb00 100644 --- a/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs +++ b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs @@ -32,6 +32,9 @@ namespace osu.Game.Overlays.Music public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + if (beatmap.Disabled) return false; diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 5f76176aab..feb6f6c92a 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -220,6 +220,9 @@ namespace osu.Game.Screens.Menu public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case GlobalAction.Back: diff --git a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs index 364da2f887..a90b83c5fe 100644 --- a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs +++ b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs @@ -21,6 +21,9 @@ namespace osu.Game.Screens.Menu public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + if (e.Action == GlobalAction.Back) { BeginConfirm(); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 72574b729a..0d2b2249ef 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -134,6 +134,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + if (SelectedRoom.Value != Room) return false; diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs b/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs index 53131ab90e..81e1cb2406 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/CreateRoomButton.cs @@ -19,6 +19,9 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + if (!Enabled.Value) return false; diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs index 6d14b95aec..435c9aca02 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/RoomSettingsOverlay.cs @@ -65,6 +65,9 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case GlobalAction.Select: diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index 850543136c..8e0a38aa1f 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -208,6 +208,9 @@ namespace osu.Game.Screens.Play.HUD public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case GlobalAction.Back: diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 54c74a7177..b5c4433719 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -283,6 +283,9 @@ namespace osu.Game.Screens.Play public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case GlobalAction.HoldForHUD: diff --git a/osu.Game/Screens/Play/HotkeyExitOverlay.cs b/osu.Game/Screens/Play/HotkeyExitOverlay.cs index 13b72ffaf6..9fe7d17cc7 100644 --- a/osu.Game/Screens/Play/HotkeyExitOverlay.cs +++ b/osu.Game/Screens/Play/HotkeyExitOverlay.cs @@ -12,6 +12,9 @@ namespace osu.Game.Screens.Play { public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + if (e.Action != GlobalAction.QuickExit) return false; BeginConfirm(); diff --git a/osu.Game/Screens/Play/HotkeyRetryOverlay.cs b/osu.Game/Screens/Play/HotkeyRetryOverlay.cs index 308befe372..2812df8bbe 100644 --- a/osu.Game/Screens/Play/HotkeyRetryOverlay.cs +++ b/osu.Game/Screens/Play/HotkeyRetryOverlay.cs @@ -12,6 +12,9 @@ namespace osu.Game.Screens.Play { public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + if (e.Action != GlobalAction.QuickRetry) return false; BeginConfirm(); diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index c35548c6b4..84d5507dce 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -146,6 +146,9 @@ namespace osu.Game.Screens.Play public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case GlobalAction.SkipCutscene: diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index dacc4f5f9e..e43f40e203 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -330,6 +330,9 @@ namespace osu.Game.Screens.Ranking public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case GlobalAction.Select: diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index aef00a9b00..2c36bf5fc8 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -821,6 +821,9 @@ namespace osu.Game.Screens.Select public virtual bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + if (!this.IsCurrentScreen()) return false; switch (e.Action) From 7599efac3044cc813abe6ceed8c39f59841e4a76 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Nov 2021 12:36:15 +0900 Subject: [PATCH 83/90] Update editor cases where repeat should not be handled --- .../Screens/Edit/Compose/Components/BlueprintContainer.cs | 3 +++ osu.Game/Screens/Edit/Editor.cs | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 29e3f12d03..130d7a015f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -239,6 +239,9 @@ namespace osu.Game.Screens.Edit.Compose.Components public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case PlatformAction.SelectAll: diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 2a7e2c9cef..94b6e58b67 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -393,6 +393,9 @@ namespace osu.Game.Screens.Edit return true; case PlatformAction.Save: + if (e.Repeat) + return false; + Save(); return true; } @@ -457,6 +460,9 @@ namespace osu.Game.Screens.Edit public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case GlobalAction.Back: From d7b178ea37225d7618916a534a923dd7aa101588 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Nov 2021 12:36:52 +0900 Subject: [PATCH 84/90] Update test scenes which should not handle key repeat --- osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs | 3 +++ osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs | 3 +++ osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs | 3 +++ osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs | 3 +++ 4 files changed, 12 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs index 883b8a1ae0..70a43fafb5 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneKeyBindings.cs @@ -83,6 +83,9 @@ namespace osu.Game.Tests.Visual.Gameplay public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + ReceivedAction = e.Action == TestAction.Down; return true; } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs index c8040f42f0..159d583fc0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs @@ -231,6 +231,9 @@ namespace osu.Game.Tests.Visual.Gameplay public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + box.Colour = Color4.White; return true; } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs index 08578168d6..1d4245308d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecording.cs @@ -164,6 +164,9 @@ namespace osu.Game.Tests.Visual.Gameplay public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + box.Colour = Color4.White; return true; } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index b4de060578..ef870a32a9 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -283,6 +283,9 @@ namespace osu.Game.Tests.Visual.Gameplay public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + box.Colour = Color4.White; return true; } From 66c307e0eecc4842adbdf9c52f0b56ae8bd1768b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Nov 2021 12:38:20 +0900 Subject: [PATCH 85/90] Remove usage of key repeat helper method --- osu.Game/Extensions/DrawableExtensions.cs | 27 --------- .../Overlays/Volume/VolumeControlReceptor.cs | 9 --- .../UI/Scrolling/DrawableScrollingRuleset.cs | 11 +--- .../Lounge/Components/RoomsContainer.cs | 39 +------------ osu.Game/Screens/Play/ReplayPlayer.cs | 17 +----- osu.Game/Screens/Select/BeatmapCarousel.cs | 58 ++----------------- 6 files changed, 10 insertions(+), 151 deletions(-) diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs index 03cc345947..005804789e 100644 --- a/osu.Game/Extensions/DrawableExtensions.cs +++ b/osu.Game/Extensions/DrawableExtensions.cs @@ -1,11 +1,8 @@ // 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.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Bindings; -using osu.Framework.Threading; using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; using osuTK; @@ -14,30 +11,6 @@ namespace osu.Game.Extensions { public static class DrawableExtensions { - public const double REPEAT_INTERVAL = 70; - public const double INITIAL_DELAY = 250; - - /// - /// Helper method that is used while doesn't support repetitions of . - /// Simulates repetitions by continually invoking a delegate according to the default key repeat rate. - /// - /// - /// The returned delegate can be cancelled to stop repeat events from firing (usually in ). - /// - /// The which is handling the repeat. - /// The to schedule repetitions on. - /// The to be invoked once immediately and with every repetition. - /// The delay imposed on the first repeat. Defaults to . - /// A which can be cancelled to stop the repeat events from firing. - public static ScheduledDelegate BeginKeyRepeat(this IKeyBindingHandler handler, Scheduler scheduler, Action action, double initialRepeatDelay = INITIAL_DELAY) - { - action(); - - ScheduledDelegate repeatDelegate = new ScheduledDelegate(action, handler.Time.Current + initialRepeatDelay, REPEAT_INTERVAL); - scheduler.Add(repeatDelegate); - return repeatDelegate; - } - /// /// Shakes this drawable. /// diff --git a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs index 4129b46ce3..c601ce4130 100644 --- a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs +++ b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs @@ -6,8 +6,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Framework.Threading; -using osu.Game.Extensions; using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Volume @@ -17,18 +15,12 @@ namespace osu.Game.Overlays.Volume public Func ActionRequested; public Func ScrollActionRequested; - private ScheduledDelegate keyRepeat; - public bool OnPressed(KeyBindingPressEvent e) { switch (e.Action) { case GlobalAction.DecreaseVolume: case GlobalAction.IncreaseVolume: - keyRepeat?.Cancel(); - keyRepeat = this.BeginKeyRepeat(Scheduler, () => ActionRequested?.Invoke(e.Action), 150); - return true; - case GlobalAction.ToggleMute: case GlobalAction.NextVolumeMeter: case GlobalAction.PreviousVolumeMeter: @@ -41,7 +33,6 @@ namespace osu.Game.Overlays.Volume public void OnReleased(KeyBindingReleaseEvent e) { - keyRepeat?.Cancel(); } protected override bool OnScroll(ScrollEvent e) diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 2a9d3d1cf0..1f3a937311 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -14,7 +14,6 @@ using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; -using osu.Game.Extensions; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -204,11 +203,11 @@ namespace osu.Game.Rulesets.UI.Scrolling switch (e.Action) { case GlobalAction.IncreaseScrollSpeed: - scheduleScrollSpeedAdjustment(1); + AdjustScrollSpeed(1); return true; case GlobalAction.DecreaseScrollSpeed: - scheduleScrollSpeedAdjustment(-1); + AdjustScrollSpeed(-1); return true; } @@ -223,12 +222,6 @@ namespace osu.Game.Rulesets.UI.Scrolling scheduledScrollSpeedAdjustment = null; } - private void scheduleScrollSpeedAdjustment(int amount) - { - scheduledScrollSpeedAdjustment?.Cancel(); - scheduledScrollSpeedAdjustment = this.BeginKeyRepeat(Scheduler, () => AdjustScrollSpeed(amount)); - } - private class LocalScrollingInfo : IScrollingInfo { public IBindable Direction { get; } = new Bindable(); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs index 32ae7cf859..54c762b8ce 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Framework.Threading; using osu.Game.Extensions; using osu.Game.Graphics.Cursor; using osu.Game.Input.Bindings; @@ -146,11 +145,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components switch (e.Action) { case GlobalAction.SelectNext: - beginRepeatSelection(() => selectNext(1), e.Action); + selectNext(1); return true; case GlobalAction.SelectPrevious: - beginRepeatSelection(() => selectNext(-1), e.Action); + selectNext(-1); return true; } @@ -159,40 +158,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components public void OnReleased(KeyBindingReleaseEvent e) { - switch (e.Action) - { - case GlobalAction.SelectNext: - case GlobalAction.SelectPrevious: - endRepeatSelection(e.Action); - break; - } - } - - private ScheduledDelegate repeatDelegate; - private object lastRepeatSource; - - /// - /// Begin repeating the specified selection action. - /// - /// The action to perform. - /// The source of the action. Used in conjunction with to only cancel the correct action (most recently pressed key). - private void beginRepeatSelection(Action action, object source) - { - endRepeatSelection(); - - lastRepeatSource = source; - repeatDelegate = this.BeginKeyRepeat(Scheduler, action); - } - - private void endRepeatSelection(object source = null) - { - // only the most recent source should be able to cancel the current action. - if (source != null && !EqualityComparer.Default.Equals(lastRepeatSource, source)) - return; - - repeatDelegate?.Cancel(); - repeatDelegate = null; - lastRepeatSource = null; } private void selectNext(int direction) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 93054b7bb5..617374b4fd 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -7,9 +7,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Framework.Threading; using osu.Game.Beatmaps; -using osu.Game.Extensions; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -48,8 +46,6 @@ namespace osu.Game.Screens.Play protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false); - private ScheduledDelegate keyboardSeekDelegate; - public bool OnPressed(KeyBindingPressEvent e) { const double keyboard_seek_amount = 5000; @@ -57,13 +53,11 @@ namespace osu.Game.Screens.Play switch (e.Action) { case GlobalAction.SeekReplayBackward: - keyboardSeekDelegate?.Cancel(); - keyboardSeekDelegate = this.BeginKeyRepeat(Scheduler, () => keyboardSeek(-1)); + keyboardSeek(-1); return true; case GlobalAction.SeekReplayForward: - keyboardSeekDelegate?.Cancel(); - keyboardSeekDelegate = this.BeginKeyRepeat(Scheduler, () => keyboardSeek(1)); + keyboardSeek(1); return true; case GlobalAction.TogglePauseReplay: @@ -86,13 +80,6 @@ namespace osu.Game.Screens.Play public void OnReleased(KeyBindingReleaseEvent e) { - switch (e.Action) - { - case GlobalAction.SeekReplayBackward: - case GlobalAction.SeekReplayForward: - keyboardSeekDelegate?.Cancel(); - break; - } } } } diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 0c593ebea1..0fc7a99c96 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -18,7 +18,6 @@ using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; -using osu.Game.Extensions; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Input.Bindings; @@ -479,42 +478,27 @@ namespace osu.Game.Screens.Select switch (e.Key) { case Key.Left: - if (!e.Repeat) - beginRepeatSelection(() => SelectNext(-1), e.Key); + SelectNext(-1); return true; case Key.Right: - if (!e.Repeat) - beginRepeatSelection(() => SelectNext(), e.Key); + SelectNext(); return true; } return false; } - protected override void OnKeyUp(KeyUpEvent e) - { - switch (e.Key) - { - case Key.Left: - case Key.Right: - endRepeatSelection(e.Key); - break; - } - - base.OnKeyUp(e); - } - public bool OnPressed(KeyBindingPressEvent e) { switch (e.Action) { case GlobalAction.SelectNext: - beginRepeatSelection(() => SelectNext(1, false), e.Action); + SelectNext(1, false); return true; case GlobalAction.SelectPrevious: - beginRepeatSelection(() => SelectNext(-1, false), e.Action); + SelectNext(-1, false); return true; } @@ -523,40 +507,6 @@ namespace osu.Game.Screens.Select public void OnReleased(KeyBindingReleaseEvent e) { - switch (e.Action) - { - case GlobalAction.SelectNext: - case GlobalAction.SelectPrevious: - endRepeatSelection(e.Action); - break; - } - } - - private ScheduledDelegate repeatDelegate; - private object lastRepeatSource; - - /// - /// Begin repeating the specified selection action. - /// - /// The action to perform. - /// The source of the action. Used in conjunction with to only cancel the correct action (most recently pressed key). - private void beginRepeatSelection(Action action, object source) - { - endRepeatSelection(); - - lastRepeatSource = source; - repeatDelegate = this.BeginKeyRepeat(Scheduler, action); - } - - private void endRepeatSelection(object source = null) - { - // only the most recent source should be able to cancel the current action. - if (source != null && !EqualityComparer.Default.Equals(lastRepeatSource, source)) - return; - - repeatDelegate?.Cancel(); - repeatDelegate = null; - lastRepeatSource = null; } #endregion From cdb2a92f865c9a85a010e1010073b21c9392f120 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Nov 2021 12:57:51 +0900 Subject: [PATCH 86/90] Disable key repeat for all ruleset handling cases --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index a3f311c7a6..6564ff9e23 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -168,6 +168,8 @@ namespace osu.Game.Rulesets.UI public class RulesetKeyBindingContainer : DatabasedKeyBindingContainer { + protected override bool HandleRepeats => false; + public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique) { From db4e4c982c879e3f5f60b3b8d09fb82022a9a043 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Nov 2021 13:35:42 +0900 Subject: [PATCH 87/90] Add hover effect to supporter button --- .../Profile/Header/Components/SupporterIcon.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs index bee3548008..f450754c02 100644 --- a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs +++ b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -78,12 +79,27 @@ namespace osu.Game.Overlays.Profile.Header.Components }; } + [Resolved] + private OsuColour colours { get; set; } + [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, OsuGame game) + private void load(OsuGame game) { background.Colour = colours.Pink; Action = () => game?.OpenUrlExternally(@"/home/support"); } + + protected override bool OnHover(HoverEvent e) + { + background.FadeColour(colours.PinkLight, 500, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + background.FadeColour(colours.Pink, 500, Easing.OutQuint); + base.OnHoverLost(e); + } } } From 9ec6ecd3f6cf9fc147d910efaa6f670cd2bba98e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Nov 2021 13:39:25 +0900 Subject: [PATCH 88/90] 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 f7b7b6fb23..cbd1c92fbe 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 93fb729f46..6b722f00ea 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 3a2a3fd65e..00cbe2c849 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From a09589f8339f39e19edc3c5c62ee765cbabc430f Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 17 Nov 2021 23:15:51 -0800 Subject: [PATCH 89/90] Fix score panels sometimes jumping when toggling statistics fast --- osu.Game/Screens/Ranking/ResultsScreen.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index e43f40e203..cb842ce4a0 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -311,10 +311,10 @@ namespace osu.Game.Screens.Ranking ScorePanelList.Attach(detachedPanel); // Move into its original location in the attached container first, then to the final location. - var origLocation = detachedPanel.Parent.ToLocalSpace(screenSpacePos); - detachedPanel.MoveTo(origLocation) + float origLocation = detachedPanel.Parent.ToLocalSpace(screenSpacePos).X; + detachedPanel.MoveToX(origLocation) .Then() - .MoveTo(new Vector2(0, origLocation.Y), 150, Easing.OutQuint); + .MoveToX(0, 150, Easing.OutQuint); // Show contracted panels. foreach (var contracted in ScorePanelList.GetScorePanels().Where(p => p.State == PanelState.Contracted)) From bf8507c7b92dad894becf3d4eb41443cdff9ee5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Nov 2021 23:26:45 +0900 Subject: [PATCH 90/90] Only apply default timeout when debugger not attached --- osu.Game/Beatmaps/WorkingBeatmap.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index f46cd405b5..d2912229c6 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; @@ -80,7 +81,10 @@ namespace osu.Game.Beatmaps public virtual IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList mods = null, CancellationToken? cancellationToken = null) { - var token = cancellationToken ?? new CancellationTokenSource(10000).Token; + var token = cancellationToken ?? + // don't apply the default timeout when debugger is attached (may be breakpointing / debugging). + (Debugger.IsAttached ? new CancellationToken() : new CancellationTokenSource(10000).Token); + mods ??= Array.Empty(); var rulesetInstance = ruleset.CreateInstance();