From 855debd5f66c2c1a4cd25d63bfd61d0dc477fbaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BA=E4=BB=80=E9=BA=BC?= Date: Sun, 10 Jul 2022 09:29:17 +0800 Subject: [PATCH 001/142] Remove nullable disable annotation and mark some return value as nullable. --- osu.Game/Rulesets/Ruleset.cs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index c1ec6c30ef..7130f4bc35 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -81,7 +79,7 @@ namespace osu.Game.Rulesets /// Returns a fresh instance of the mod matching the specified acronym. /// /// The acronym to query for . - public Mod CreateModFromAcronym(string acronym) + public Mod? CreateModFromAcronym(string acronym) { return AllMods.FirstOrDefault(m => m.Acronym == acronym)?.CreateInstance(); } @@ -89,7 +87,7 @@ namespace osu.Game.Rulesets /// /// Returns a fresh instance of the mod matching the specified type. /// - public T CreateMod() + public T? CreateMod() where T : Mod { return AllMods.FirstOrDefault(m => m is T)?.CreateInstance() as T; @@ -183,10 +181,9 @@ namespace osu.Game.Rulesets return value; } - [CanBeNull] - public ModAutoplay GetAutoplayMod() => CreateMod(); + public ModAutoplay? GetAutoplayMod() => CreateMod(); - public virtual ISkin CreateLegacySkinProvider([NotNull] ISkin skin, IBeatmap beatmap) => null; + public virtual ISkin? CreateLegacySkinProvider([NotNull] ISkin skin, IBeatmap beatmap) => null; protected Ruleset() { @@ -206,7 +203,7 @@ namespace osu.Game.Rulesets /// The beatmap to create the hit renderer for. /// The s to apply. /// Unable to successfully load the beatmap to be usable with this ruleset. - public abstract DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null); + public abstract DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList? mods = null); /// /// Creates a for this . @@ -240,8 +237,7 @@ namespace osu.Game.Rulesets /// Optionally creates a to generate performance data from the provided score. /// /// A performance calculator instance for the provided score. - [CanBeNull] - public virtual PerformanceCalculator CreatePerformanceCalculator() => null; + public virtual PerformanceCalculator? CreatePerformanceCalculator() => null; public virtual HitObjectComposer CreateHitObjectComposer() => null; @@ -253,7 +249,7 @@ namespace osu.Game.Rulesets public abstract string Description { get; } - public virtual RulesetSettingsSubsection CreateSettings() => null; + public virtual RulesetSettingsSubsection? CreateSettings() => null; /// /// Creates the for this . @@ -303,7 +299,6 @@ namespace osu.Game.Rulesets /// The to create the statistics for. The score is guaranteed to have populated. /// The , converted for this with all relevant s applied. /// The s to display. Each may contain 0 or more . - [NotNull] public virtual StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => Array.Empty(); /// @@ -356,13 +351,11 @@ namespace osu.Game.Rulesets /// /// Creates ruleset-specific beatmap filter criteria to be used on the song select screen. /// - [CanBeNull] - public virtual IRulesetFilterCriteria CreateRulesetFilterCriteria() => null; + public virtual IRulesetFilterCriteria? CreateRulesetFilterCriteria() => null; /// /// Can be overridden to add a ruleset-specific section to the editor beatmap setup screen. /// - [CanBeNull] - public virtual RulesetSetupSection CreateEditorSetupSection() => null; + public virtual RulesetSetupSection? CreateEditorSetupSection() => null; } } From 4a503bab0a3d520965efeb54594053baaacc348c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BA=E4=BB=80=E9=BA=BC?= Date: Sun, 10 Jul 2022 09:29:59 +0800 Subject: [PATCH 002/142] Remove unnecessary attribute. --- osu.Game/Rulesets/Ruleset.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 7130f4bc35..af8ff10fa1 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -23,7 +23,6 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Skinning; using osu.Game.Users; -using JetBrains.Annotations; using osu.Framework.Extensions; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Testing; @@ -101,7 +100,6 @@ namespace osu.Game.Rulesets /// then the proper behaviour is to return an empty enumerable. /// mods should not be present in the returned enumerable. /// - [ItemNotNull] public abstract IEnumerable GetModsFor(ModType type); /// @@ -183,7 +181,7 @@ namespace osu.Game.Rulesets public ModAutoplay? GetAutoplayMod() => CreateMod(); - public virtual ISkin? CreateLegacySkinProvider([NotNull] ISkin skin, IBeatmap beatmap) => null; + public virtual ISkin? CreateLegacySkinProvider(ISkin skin, IBeatmap beatmap) => null; protected Ruleset() { From e67cb4c90520e4c328c653372bd11b20a67a9935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BA=E4=BB=80=E9=BA=BC?= Date: Sun, 10 Jul 2022 09:35:46 +0800 Subject: [PATCH 003/142] Mark create beatmap verifier as nullable because seems it's not requirement to be implemented. --- osu.Game/Rulesets/Ruleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index af8ff10fa1..14513b06b8 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -239,7 +239,7 @@ namespace osu.Game.Rulesets public virtual HitObjectComposer CreateHitObjectComposer() => null; - public virtual IBeatmapVerifier CreateBeatmapVerifier() => null; + public virtual IBeatmapVerifier? CreateBeatmapVerifier() => null; public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle }; From 857377e1451402cb8848da21c9cd0d00c01dbb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BA=E4=BB=80=E9=BA=BC?= Date: Sun, 10 Jul 2022 09:53:05 +0800 Subject: [PATCH 004/142] Move CreateConvertibleReplayFrame() into legacy ruleset interface because technically only legacy ruleset use it to convert the legacy frame. But seems some of the customized ruleset use it for save the replay frame. --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- osu.Game/Rulesets/ILegacyRuleset.cs | 9 ++++++++- osu.Game/Rulesets/Ruleset.cs | 8 -------- osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 2 +- osu.Game/Screens/Play/SpectatorPlayer.cs | 6 +++++- 8 files changed, 18 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index f832d99807..766b5f6f58 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -187,7 +187,7 @@ namespace osu.Game.Rulesets.Catch public int LegacyID => 2; - public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame(); + public IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame(); public override HitObjectComposer CreateHitObjectComposer() => new CatchHitObjectComposer(this); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 4723416c30..f45a2517df 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -280,7 +280,7 @@ namespace osu.Game.Rulesets.Mania public int LegacyID => 3; - public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); + public IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaRulesetConfigManager(settings, RulesetInfo); diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 120ce32612..392574ef03 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -234,7 +234,7 @@ namespace osu.Game.Rulesets.Osu public int LegacyID => 0; - public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); + public IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo); diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 223e268d7f..66fc64c018 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -177,7 +177,7 @@ namespace osu.Game.Rulesets.Taiko public int LegacyID => 1; - public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame(); + public IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame(); protected override IEnumerable GetValidHitResults() { diff --git a/osu.Game/Rulesets/ILegacyRuleset.cs b/osu.Game/Rulesets/ILegacyRuleset.cs index a8cfed4866..c2617a065c 100644 --- a/osu.Game/Rulesets/ILegacyRuleset.cs +++ b/osu.Game/Rulesets/ILegacyRuleset.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable +using osu.Game.Rulesets.Replays.Types; namespace osu.Game.Rulesets { @@ -13,5 +13,12 @@ namespace osu.Game.Rulesets /// Identifies the server-side ID of a legacy ruleset. /// int LegacyID { get; } + + /// + /// For rulesets which support legacy (osu-stable) replay conversion, this method will create an empty replay frame + /// for conversion use. + /// + /// An empty frame for the current ruleset, or null if unsupported. + IConvertibleReplayFrame CreateConvertibleReplayFrame(); } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 14513b06b8..89ef6d2d54 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -13,7 +13,6 @@ using osu.Game.Beatmaps; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.UI; using osu.Game.Beatmaps.Legacy; using osu.Game.Configuration; @@ -284,13 +283,6 @@ namespace osu.Game.Rulesets /// A descriptive name of the variant. public virtual string GetVariantName(int variant) => string.Empty; - /// - /// For rulesets which support legacy (osu-stable) replay conversion, this method will create an empty replay frame - /// for conversion use. - /// - /// An empty frame for the current ruleset, or null if unsupported. - public virtual IConvertibleReplayFrame CreateConvertibleReplayFrame() => null; - /// /// Creates the statistics for a to be displayed in the results screen. /// diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index f64e730c06..2b22b4abc7 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -282,7 +282,7 @@ namespace osu.Game.Scoring.Legacy private ReplayFrame convertFrame(LegacyReplayFrame currentFrame, ReplayFrame lastFrame) { - var convertible = currentRuleset.CreateConvertibleReplayFrame(); + var convertible = (currentRuleset as ILegacyRuleset)?.CreateConvertibleReplayFrame(); if (convertible == null) throw new InvalidOperationException($"Legacy replay cannot be converted for the ruleset: {currentRuleset.Description}"); diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index 797787b194..f59fd9559c 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -10,6 +10,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Spectator; +using osu.Game.Rulesets; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Scoring; @@ -79,11 +80,14 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; + if (GameplayState.Ruleset is not ILegacyRuleset legacyRuleset) + return; + bool isFirstBundle = score.Replay.Frames.Count == 0; foreach (var frame in bundle.Frames) { - IConvertibleReplayFrame convertibleFrame = GameplayState.Ruleset.CreateConvertibleReplayFrame(); + IConvertibleReplayFrame convertibleFrame = legacyRuleset.CreateConvertibleReplayFrame(); convertibleFrame.FromLegacy(frame, GameplayState.Beatmap); var convertedFrame = (ReplayFrame)convertibleFrame; From d39f53f1f0e2ab307c593eb928078f0c55a13684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BA=E4=BB=80=E9=BA=BC?= Date: Sun, 10 Jul 2022 10:01:56 +0800 Subject: [PATCH 005/142] Mark `CreateConfig()` return type as nullable because it's not required all ruleset to implement. Also, remove nullable disable annotation for all using classes. Setting store can be nullable because `RulesetConfigManager()` can accept null setting store. --- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Rulesets/RulesetConfigCache.cs | 6 ++---- osu.Game/Tests/Rulesets/TestRulesetConfigCache.cs | 6 ++---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 89ef6d2d54..cd3ab4f726 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -252,7 +252,7 @@ namespace osu.Game.Rulesets /// Creates the for this . /// /// The to store the settings. - public virtual IRulesetConfigManager CreateConfig(SettingsStore settings) => null; + public virtual IRulesetConfigManager? CreateConfig(SettingsStore? settings) => null; /// /// A unique short name to reference this ruleset in online requests. diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs index 017214df61..ab44e86048 100644 --- a/osu.Game/Rulesets/RulesetConfigCache.cs +++ b/osu.Game/Rulesets/RulesetConfigCache.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using osu.Framework.Graphics; @@ -18,7 +16,7 @@ namespace osu.Game.Rulesets private readonly RealmAccess realm; private readonly RulesetStore rulesets; - private readonly Dictionary configCache = new Dictionary(); + private readonly Dictionary configCache = new Dictionary(); public RulesetConfigCache(RealmAccess realm, RulesetStore rulesets) { @@ -42,7 +40,7 @@ namespace osu.Game.Rulesets } } - public IRulesetConfigManager GetConfigFor(Ruleset ruleset) + public IRulesetConfigManager? GetConfigFor(Ruleset ruleset) { if (!IsLoaded) throw new InvalidOperationException($@"Cannot retrieve {nameof(IRulesetConfigManager)} before {nameof(RulesetConfigCache)} has loaded"); diff --git a/osu.Game/Tests/Rulesets/TestRulesetConfigCache.cs b/osu.Game/Tests/Rulesets/TestRulesetConfigCache.cs index a80154c38e..cf637983d9 100644 --- a/osu.Game/Tests/Rulesets/TestRulesetConfigCache.cs +++ b/osu.Game/Tests/Rulesets/TestRulesetConfigCache.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Concurrent; using osu.Game.Rulesets; using osu.Game.Rulesets.Configuration; @@ -14,8 +12,8 @@ namespace osu.Game.Tests.Rulesets /// public class TestRulesetConfigCache : IRulesetConfigCache { - private readonly ConcurrentDictionary configCache = new ConcurrentDictionary(); + private readonly ConcurrentDictionary configCache = new ConcurrentDictionary(); - public IRulesetConfigManager GetConfigFor(Ruleset ruleset) => configCache.GetOrAdd(ruleset.ShortName, _ => ruleset.CreateConfig(null)); + public IRulesetConfigManager? GetConfigFor(Ruleset ruleset) => configCache.GetOrAdd(ruleset.ShortName, _ => ruleset.CreateConfig(null)); } } From 57c6763556920d09399fe679c1dfc8d386686759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BA=E4=BB=80=E9=BA=BC?= Date: Sun, 10 Jul 2022 10:07:09 +0800 Subject: [PATCH 006/142] Mark the `CreateBeatmapProcessor()` as nullable. Also, should add the null check in the working beatmap. --- osu.Game/Beatmaps/WorkingBeatmap.cs | 11 +++++++---- osu.Game/Rulesets/Ruleset.cs | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 16464932e0..22c3ad43e6 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -279,12 +279,15 @@ namespace osu.Game.Beatmaps } } - IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted); + var processor = rulesetInstance.CreateBeatmapProcessor(converted); - foreach (var mod in mods.OfType()) - mod.ApplyToBeatmapProcessor(processor); + if (processor != null) + { + foreach (var mod in mods.OfType()) + mod.ApplyToBeatmapProcessor(processor); - processor?.PreProcess(); + processor.PreProcess(); + } // Compute default values for hitobjects, including creating nested hitobjects in-case they're needed foreach (var obj in converted.HitObjects) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index cd3ab4f726..bd2d147a08 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -226,7 +226,7 @@ namespace osu.Game.Rulesets /// /// The to be processed. /// The . - public virtual IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => null; + public virtual IBeatmapProcessor? CreateBeatmapProcessor(IBeatmap beatmap) => null; public abstract DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap); From 8e1ed1c621d12c4b1a1f97812968043b8eb7017c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BA=E4=BB=80=E9=BA=BC?= Date: Sun, 10 Jul 2022 10:09:32 +0800 Subject: [PATCH 007/142] Mark CreateHitObjectComposer() accept null. And add the null check in the test case. --- osu.Game.Tests/Visual/Editing/TimelineTestScene.cs | 8 +++++++- osu.Game/Rulesets/Ruleset.cs | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs index 437f06c47f..7c11b4aa56 100644 --- a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs +++ b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs @@ -39,7 +39,13 @@ namespace osu.Game.Tests.Visual.Editing Dependencies.Cache(EditorBeatmap); Dependencies.CacheAs(EditorBeatmap); - Composer = playable.BeatmapInfo.Ruleset.CreateInstance().CreateHitObjectComposer().With(d => d.Alpha = 0); + Composer = playable.BeatmapInfo.Ruleset.CreateInstance().CreateHitObjectComposer().With(d => + { + if (d == null) + return; + + d.Alpha = 0; + }); Add(new OsuContextMenuContainer { diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index bd2d147a08..2cab529a4b 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -236,7 +236,7 @@ namespace osu.Game.Rulesets /// A performance calculator instance for the provided score. public virtual PerformanceCalculator? CreatePerformanceCalculator() => null; - public virtual HitObjectComposer CreateHitObjectComposer() => null; + public virtual HitObjectComposer? CreateHitObjectComposer() => null; public virtual IBeatmapVerifier? CreateBeatmapVerifier() => null; From 1725a76fa0183cd9e9c9b7a2139ee498f525a11a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BA=E4=BB=80=E9=BA=BC?= Date: Sun, 10 Jul 2022 10:15:02 +0800 Subject: [PATCH 008/142] Remove the nullable disable annotation for all rulesets. --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 4 +--- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 6 ++---- osu.Game.Rulesets.Osu/OsuRuleset.cs | 6 ++---- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 4 +--- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 766b5f6f58..964598dcaf 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets.Catch.Mods; @@ -32,7 +30,7 @@ namespace osu.Game.Rulesets.Catch { public class CatchRuleset : Ruleset, ILegacyRuleset { - public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableCatchRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList? mods = null) => new DrawableCatchRuleset(this, beatmap, mods); public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index f45a2517df..b391c64572 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Mods; @@ -47,7 +45,7 @@ namespace osu.Game.Rulesets.Mania /// public const int MAX_STAGE_KEYS = 10; - public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableManiaRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList? mods = null) => new DrawableManiaRuleset(this, beatmap, mods); public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(); @@ -282,7 +280,7 @@ namespace osu.Game.Rulesets.Mania public IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); - public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaRulesetConfigManager(settings, RulesetInfo); + public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new ManiaRulesetConfigManager(settings, RulesetInfo); public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this); diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 392574ef03..458a0a12f4 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets.Mods; @@ -43,7 +41,7 @@ namespace osu.Game.Rulesets.Osu { public class OsuRuleset : Ruleset, ILegacyRuleset { - public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableOsuRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList? mods = null) => new DrawableOsuRuleset(this, beatmap, mods); public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(); @@ -236,7 +234,7 @@ namespace osu.Game.Rulesets.Osu public IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); - public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo); + public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new OsuRulesetConfigManager(settings, RulesetInfo); protected override IEnumerable GetValidHitResults() { diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 66fc64c018..19aa3e1016 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets.Mods; @@ -36,7 +34,7 @@ namespace osu.Game.Rulesets.Taiko { public class TaikoRuleset : Ruleset, ILegacyRuleset { - public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableTaikoRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList? mods = null) => new DrawableTaikoRuleset(this, beatmap, mods); public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(); From 3dbd0c9055dac6c32e9b8bd31baad2d6d42ac7cb Mon Sep 17 00:00:00 2001 From: andy Date: Tue, 12 Jul 2022 09:37:16 +0800 Subject: [PATCH 009/142] Update osu.Game.Tests/Visual/Editing/TimelineTestScene.cs Co-authored-by: Salman Ahmed --- osu.Game.Tests/Visual/Editing/TimelineTestScene.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs index 7c11b4aa56..40d91dcbaa 100644 --- a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs +++ b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs @@ -39,13 +39,10 @@ namespace osu.Game.Tests.Visual.Editing Dependencies.Cache(EditorBeatmap); Dependencies.CacheAs(EditorBeatmap); - Composer = playable.BeatmapInfo.Ruleset.CreateInstance().CreateHitObjectComposer().With(d => - { - if (d == null) - return; + Composer = playable.BeatmapInfo.Ruleset.CreateInstance().CreateHitObjectComposer(); + Debug.Assert(Composer != null); - d.Alpha = 0; - }); + Composer.Alpha = 0; Add(new OsuContextMenuContainer { From 079fcf13a6245a987e12fa7db7a06401452be51f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BA=E4=BB=80=E9=BA=BC?= Date: Tue, 12 Jul 2022 09:41:03 +0800 Subject: [PATCH 010/142] Add missing import. --- osu.Game.Tests/Visual/Editing/TimelineTestScene.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs index 40d91dcbaa..d4c96a837b 100644 --- a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs +++ b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs @@ -3,6 +3,7 @@ #nullable disable +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; From 6a643cb6eae329fe7e434c07392f195f283890ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=82=BA=E4=BB=80=E9=BA=BC?= Date: Tue, 12 Jul 2022 09:41:19 +0800 Subject: [PATCH 011/142] Use Debug.Assert instead. --- osu.Game/Screens/Play/SpectatorPlayer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index f59fd9559c..659d7d5d6c 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -3,6 +3,7 @@ #nullable disable +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Screens; @@ -80,8 +81,8 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; - if (GameplayState.Ruleset is not ILegacyRuleset legacyRuleset) - return; + var legacyRuleset = GameplayState.Ruleset as ILegacyRuleset; + Debug.Assert(legacyRuleset != null); bool isFirstBundle = score.Replay.Frames.Count == 0; From a5a8fac66f6e58afd9b4984c94be0dedef7a2e4a Mon Sep 17 00:00:00 2001 From: solstice23 Date: Thu, 21 Jul 2022 18:24:31 +0800 Subject: [PATCH 012/142] Add multiple units support in search length criteria --- osu.Game/Screens/Select/FilterQueryParser.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index 03b72bf5e9..436beae30d 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -312,11 +312,18 @@ namespace osu.Game.Screens.Select private static bool tryUpdateLengthRange(FilterCriteria criteria, Operator op, string val) { - if (!tryParseDoubleWithPoint(val.TrimEnd('m', 's', 'h'), out double length)) - return false; - - int scale = getLengthScale(val); - return tryUpdateCriteriaRange(ref criteria.Length, op, length * scale, scale / 2.0); + string[] parts = Regex.Split(val, @"(?<=[msh])").Where(x => x.Length > 0).ToArray(); + double totalLength = 0; + int minScale = 1000; + foreach (string part in parts) + { + if (!tryParseDoubleWithPoint(part.TrimEnd('m', 's', 'h'), out double length)) + return false; + int scale = getLengthScale(part); + totalLength += length * scale; + minScale = Math.Min(minScale, scale); + } + return tryUpdateCriteriaRange(ref criteria.Length, op, totalLength, minScale / 2.0); } } } From 6baaef432f086209c163b52d5393d3d32ce62e7c Mon Sep 17 00:00:00 2001 From: solstice23 Date: Thu, 21 Jul 2022 18:49:13 +0800 Subject: [PATCH 013/142] Add colon parsing support in search length criteria --- osu.Game/Screens/Select/FilterQueryParser.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index 436beae30d..ee7a1617cd 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -312,9 +312,25 @@ namespace osu.Game.Screens.Select private static bool tryUpdateLengthRange(FilterCriteria criteria, Operator op, string val) { + if (val.Contains(':')) + { + if (val.Contains('h') || val.Contains('m') || val.Contains('s')) + return false; + + int count = val.Count(c => c == ':'); + if (count > 3) + return false; + + if (count > 2) + val = val.Replace(':', 'h'); + + val = val.Replace(':', 'm'); + } + string[] parts = Regex.Split(val, @"(?<=[msh])").Where(x => x.Length > 0).ToArray(); double totalLength = 0; int minScale = 1000; + foreach (string part in parts) { if (!tryParseDoubleWithPoint(part.TrimEnd('m', 's', 'h'), out double length)) @@ -323,6 +339,7 @@ namespace osu.Game.Screens.Select totalLength += length * scale; minScale = Math.Min(minScale, scale); } + return tryUpdateCriteriaRange(ref criteria.Length, op, totalLength, minScale / 2.0); } } From 52fad1e14d4292f0c867cb099eb1fcee1b6b960f Mon Sep 17 00:00:00 2001 From: solstice23 Date: Thu, 21 Jul 2022 18:59:48 +0800 Subject: [PATCH 014/142] Add test cases for search length criteria --- osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index bd0617515b..9f97c39462 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -125,6 +125,10 @@ namespace osu.Game.Tests.NonVisual.Filtering new object[] { "9m", TimeSpan.FromMinutes(9), TimeSpan.FromMinutes(1) }, new object[] { "0.25h", TimeSpan.FromHours(0.25), TimeSpan.FromHours(1) }, new object[] { "70", TimeSpan.FromSeconds(70), TimeSpan.FromSeconds(1) }, + new object[] { "7m27s", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1) }, + new object[] { "6h5m", TimeSpan.FromMinutes(365), TimeSpan.FromMinutes(1) }, + new object[] { "7:27", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1) }, + new object[] { "0:2:35", TimeSpan.FromSeconds(155), TimeSpan.FromSeconds(1) }, }; [Test] From ae0902ca86e30d690459c1dedb28a9d01f0d6ff7 Mon Sep 17 00:00:00 2001 From: solstice23 Date: Fri, 22 Jul 2022 02:55:11 +0800 Subject: [PATCH 015/142] Fix lax in search criteria parsing --- osu.Game/Screens/Select/FilterQueryParser.cs | 49 +++++++++++++------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index ee7a1617cd..25dd342353 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; @@ -312,34 +313,50 @@ namespace osu.Game.Screens.Select private static bool tryUpdateLengthRange(FilterCriteria criteria, Operator op, string val) { - if (val.Contains(':')) + List parts = new List(); + + if (Regex.IsMatch(val, @"^\d+(:\d+(:\d+)?)?$")) // formats like 12:34 { - if (val.Contains('h') || val.Contains('m') || val.Contains('s')) + string[] splited = val.Split(':'); + for (int i = splited.Length - 1; i >= 0; i--) + parts.Add(splited[i] + "smh"[splited.Length - i - 1]); + } + else if (Regex.IsMatch(val, @"^(\d+(\.\d+)?[hms]){1,3}$")) // formats like 1h2m3s + { + if (!"hms".Contains(Regex.Replace(val, @"[\d\.]", ""))) return false; - int count = val.Count(c => c == ':'); - if (count > 3) - return false; - - if (count > 2) - val = val.Replace(':', 'h'); - - val = val.Replace(':', 'm'); + string[] splited = Regex.Split(val, @"(?<=[hms])").Where(x => x.Length > 0).ToArray(); + for (int i = splited.Length - 1; i >= 0; i--) + parts.Add(splited[i]); + } + else if (Regex.IsMatch(val, @"^\d+(\.\d+)?$")) // only one number + { + parts.Add(val + 's'); } - string[] parts = Regex.Split(val, @"(?<=[msh])").Where(x => x.Length > 0).ToArray(); - double totalLength = 0; - int minScale = 1000; + if (parts.Count == 0) + return false; - foreach (string part in parts) + double totalLength = 0; + int minScale = 3600000; + + for (int i = 0; i < parts.Count; i++) { - if (!tryParseDoubleWithPoint(part.TrimEnd('m', 's', 'h'), out double length)) + string part = parts[i]; + string partNoUnit = part.TrimEnd('m', 's', 'h'); + if (!tryParseDoubleWithPoint(partNoUnit, out double length)) return false; + + if (i != parts.Count - 1 && length >= 60) + return false; + if (i != 0 && partNoUnit.Contains('.')) + return false; + int scale = getLengthScale(part); totalLength += length * scale; minScale = Math.Min(minScale, scale); } - return tryUpdateCriteriaRange(ref criteria.Length, op, totalLength, minScale / 2.0); } } From de25830b2ba1b0a696331d79da54f2ea7605a8e2 Mon Sep 17 00:00:00 2001 From: solstice23 Date: Fri, 22 Jul 2022 03:27:08 +0800 Subject: [PATCH 016/142] Add more test cases --- .../NonVisual/Filtering/FilterQueryParserTest.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index 9f97c39462..8f17410cad 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -126,9 +126,16 @@ namespace osu.Game.Tests.NonVisual.Filtering new object[] { "0.25h", TimeSpan.FromHours(0.25), TimeSpan.FromHours(1) }, new object[] { "70", TimeSpan.FromSeconds(70), TimeSpan.FromSeconds(1) }, new object[] { "7m27s", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1) }, - new object[] { "6h5m", TimeSpan.FromMinutes(365), TimeSpan.FromMinutes(1) }, new object[] { "7:27", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1) }, - new object[] { "0:2:35", TimeSpan.FromSeconds(155), TimeSpan.FromSeconds(1) }, + new object[] { "1h2m3s", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1) }, + new object[] { "1h2m3.5s", TimeSpan.FromSeconds(3723.5), TimeSpan.FromSeconds(1) }, + new object[] { "1:2:3", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1) }, + new object[] { "1:02:03", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1) }, + new object[] { "6", TimeSpan.FromSeconds(6), TimeSpan.FromSeconds(1) }, + new object[] { "6.5", TimeSpan.FromSeconds(6.5), TimeSpan.FromSeconds(1) }, + new object[] { "6.5s", TimeSpan.FromSeconds(6.5), TimeSpan.FromSeconds(1) }, + new object[] { "6.5m", TimeSpan.FromMinutes(6.5), TimeSpan.FromMinutes(1) }, + new object[] { "6h5m", TimeSpan.FromMinutes(365), TimeSpan.FromMinutes(1) }, }; [Test] From b36e23c0da7c02e61d87862bbe30eb5f8b3ccc1e Mon Sep 17 00:00:00 2001 From: solstice23 Date: Fri, 22 Jul 2022 03:30:31 +0800 Subject: [PATCH 017/142] Simplify the regex expression --- osu.Game/Screens/Select/FilterQueryParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index 25dd342353..9245c13243 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -315,7 +315,7 @@ namespace osu.Game.Screens.Select { List parts = new List(); - if (Regex.IsMatch(val, @"^\d+(:\d+(:\d+)?)?$")) // formats like 12:34 + if (Regex.IsMatch(val, @"^\d+(:\d+){1,2}$")) // formats like 12:34 { string[] splited = val.Split(':'); for (int i = splited.Length - 1; i >= 0; i--) From 80e82763e35304022e9c42a35a4f4c18fba7ef2e Mon Sep 17 00:00:00 2001 From: solstice23 Date: Fri, 22 Jul 2022 12:09:47 +0800 Subject: [PATCH 018/142] Add negative test cases --- .../Filtering/FilterQueryParserTest.cs | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index 8f17410cad..4b20fc42c9 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -120,35 +120,54 @@ namespace osu.Game.Tests.NonVisual.Filtering private static readonly object[] length_query_examples = { - new object[] { "6ms", TimeSpan.FromMilliseconds(6), TimeSpan.FromMilliseconds(1) }, - new object[] { "23s", TimeSpan.FromSeconds(23), TimeSpan.FromSeconds(1) }, - new object[] { "9m", TimeSpan.FromMinutes(9), TimeSpan.FromMinutes(1) }, - new object[] { "0.25h", TimeSpan.FromHours(0.25), TimeSpan.FromHours(1) }, - new object[] { "70", TimeSpan.FromSeconds(70), TimeSpan.FromSeconds(1) }, - new object[] { "7m27s", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1) }, - new object[] { "7:27", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1) }, - new object[] { "1h2m3s", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1) }, - new object[] { "1h2m3.5s", TimeSpan.FromSeconds(3723.5), TimeSpan.FromSeconds(1) }, - new object[] { "1:2:3", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1) }, - new object[] { "1:02:03", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1) }, - new object[] { "6", TimeSpan.FromSeconds(6), TimeSpan.FromSeconds(1) }, - new object[] { "6.5", TimeSpan.FromSeconds(6.5), TimeSpan.FromSeconds(1) }, - new object[] { "6.5s", TimeSpan.FromSeconds(6.5), TimeSpan.FromSeconds(1) }, - new object[] { "6.5m", TimeSpan.FromMinutes(6.5), TimeSpan.FromMinutes(1) }, - new object[] { "6h5m", TimeSpan.FromMinutes(365), TimeSpan.FromMinutes(1) }, + new object[] { "23s", TimeSpan.FromSeconds(23), TimeSpan.FromSeconds(1), true }, + new object[] { "9m", TimeSpan.FromMinutes(9), TimeSpan.FromMinutes(1), true }, + new object[] { "0.25h", TimeSpan.FromHours(0.25), TimeSpan.FromHours(1), true }, + new object[] { "70", TimeSpan.FromSeconds(70), TimeSpan.FromSeconds(1), true }, + new object[] { "7m27s", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1), true }, + new object[] { "7:27", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1), true }, + new object[] { "1h2m3s", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1), true }, + new object[] { "1h2m3.5s", TimeSpan.FromSeconds(3723.5), TimeSpan.FromSeconds(1), true }, + new object[] { "1:2:3", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1), true }, + new object[] { "1:02:03", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1), true }, + new object[] { "6", TimeSpan.FromSeconds(6), TimeSpan.FromSeconds(1), true }, + new object[] { "6.5", TimeSpan.FromSeconds(6.5), TimeSpan.FromSeconds(1), true }, + new object[] { "6.5s", TimeSpan.FromSeconds(6.5), TimeSpan.FromSeconds(1), true }, + new object[] { "6.5m", TimeSpan.FromMinutes(6.5), TimeSpan.FromMinutes(1), true }, + new object[] { "6h5m", TimeSpan.FromMinutes(365), TimeSpan.FromMinutes(1), true }, + new object[] { "65m", TimeSpan.FromMinutes(65), TimeSpan.FromMinutes(1), true }, + new object[] { "90s", TimeSpan.FromSeconds(90), TimeSpan.FromSeconds(1), true }, + new object[] { "80m20s", TimeSpan.FromSeconds(4820), TimeSpan.FromSeconds(1), true }, + new object[] { "7.5m27s", new TimeSpan(), new TimeSpan(), false }, + new object[] { "7m27", new TimeSpan(), new TimeSpan(), false }, + new object[] { "7m7m7m", new TimeSpan(), new TimeSpan(), false }, + new object[] { "7m70s", new TimeSpan(), new TimeSpan(), false }, + new object[] { "5s6m", new TimeSpan(), new TimeSpan(), false }, + new object[] { "0:", new TimeSpan(), new TimeSpan(), false }, + new object[] { ":0", new TimeSpan(), new TimeSpan(), false }, + new object[] { "0:3:", new TimeSpan(), new TimeSpan(), false }, + new object[] { "3:15.5", new TimeSpan(), new TimeSpan(), false }, }; [Test] [TestCaseSource(nameof(length_query_examples))] - public void TestApplyLengthQueries(string lengthQuery, TimeSpan expectedLength, TimeSpan scale) + public void TestApplyLengthQueries(string lengthQuery, TimeSpan expectedLength, TimeSpan scale, bool isValid) { string query = $"length={lengthQuery} time"; var filterCriteria = new FilterCriteria(); FilterQueryParser.ApplyQueries(filterCriteria, query); - Assert.AreEqual("time", filterCriteria.SearchText.Trim()); - Assert.AreEqual(1, filterCriteria.SearchTerms.Length); - Assert.AreEqual(expectedLength.TotalMilliseconds - scale.TotalMilliseconds / 2.0, filterCriteria.Length.Min); - Assert.AreEqual(expectedLength.TotalMilliseconds + scale.TotalMilliseconds / 2.0, filterCriteria.Length.Max); + if (isValid) + { + Assert.AreEqual("time", filterCriteria.SearchText.Trim()); + Assert.AreEqual(1, filterCriteria.SearchTerms.Length); + Assert.AreEqual(expectedLength.TotalMilliseconds - scale.TotalMilliseconds / 2.0, filterCriteria.Length.Min); + Assert.AreEqual(expectedLength.TotalMilliseconds + scale.TotalMilliseconds / 2.0, filterCriteria.Length.Max); + } + else + { + Assert.AreEqual(false, filterCriteria.Length.HasFilter); + } + } [Test] From 7c222505e941f0afd2f4925cacb3922c6d10938a Mon Sep 17 00:00:00 2001 From: solstice23 Date: Fri, 22 Jul 2022 14:24:17 +0800 Subject: [PATCH 019/142] Simplify length parsing --- .../Filtering/FilterQueryParserTest.cs | 1 - osu.Game/Screens/Select/FilterQueryParser.cs | 22 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index 4b20fc42c9..f08bd09cf5 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -167,7 +167,6 @@ namespace osu.Game.Tests.NonVisual.Filtering { Assert.AreEqual(false, filterCriteria.Length.HasFilter); } - } [Test] diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index 9245c13243..b8c85c5ce0 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -317,18 +317,18 @@ namespace osu.Game.Screens.Select if (Regex.IsMatch(val, @"^\d+(:\d+){1,2}$")) // formats like 12:34 { - string[] splited = val.Split(':'); - for (int i = splited.Length - 1; i >= 0; i--) - parts.Add(splited[i] + "smh"[splited.Length - i - 1]); - } - else if (Regex.IsMatch(val, @"^(\d+(\.\d+)?[hms]){1,3}$")) // formats like 1h2m3s - { - if (!"hms".Contains(Regex.Replace(val, @"[\d\.]", ""))) - return false; + List splitted = val.Split(':').ToList(); + while (splitted.Count < 3) + splitted.Insert(0, "0"); - string[] splited = Regex.Split(val, @"(?<=[hms])").Where(x => x.Length > 0).ToArray(); - for (int i = splited.Length - 1; i >= 0; i--) - parts.Add(splited[i]); + parts.Add(splitted[2] + 's'); + parts.Add(splitted[1] + 'm'); + parts.Add(splitted[0] + 'h'); + } + else if (Regex.IsMatch(val, @"^(\d+(\.\d+)?[hms]){1,3}$") && "hms".Contains(Regex.Replace(val, @"[\d\.]", ""))) // formats like 1h2m3s + { + string[] splitted = Regex.Split(val, @"(?<=[hms])").Where(x => x.Length > 0).Reverse().ToArray(); + parts.AddRange(splitted); } else if (Regex.IsMatch(val, @"^\d+(\.\d+)?$")) // only one number { From 1220250bb677567498a10e4f9604ce8291bc8b3d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Jul 2022 07:26:34 +0300 Subject: [PATCH 020/142] Improve test scene and add failing test case --- osu.Game.Tests/Resources/TestResources.cs | 2 +- .../SongSelect/TestSceneTopLocalRank.cs | 79 ++++++++++++++++--- 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index ee29cc8644..37bcef0414 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -190,7 +190,7 @@ namespace osu.Game.Tests.Resources [HitResult.SmallTickMiss] = 25, [HitResult.LargeTickHit] = 100, [HitResult.LargeTickMiss] = 50, - [HitResult.SmallBonus] = 10, + [HitResult.LargeBonus] = 10, [HitResult.SmallBonus] = 50 }, }; diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs index 05b5c5c0cd..d998c4e161 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs @@ -3,6 +3,8 @@ #nullable disable +using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -13,6 +15,7 @@ using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Select.Carousel; using osu.Game.Tests.Resources; @@ -54,6 +57,8 @@ namespace osu.Game.Tests.Visual.SongSelect Scale = new Vector2(10), }); }); + + AddAssert("No rank displayed initially", () => topLocalRank.Rank == null); } [Test] @@ -61,8 +66,6 @@ namespace osu.Game.Tests.Visual.SongSelect { ScoreInfo testScoreInfo = null; - AddAssert("Initially not present", () => !topLocalRank.IsPresent); - AddStep("Add score for current user", () => { testScoreInfo = TestResources.CreateTestScoreInfo(importedBeatmap); @@ -73,15 +76,14 @@ namespace osu.Game.Tests.Visual.SongSelect scoreManager.Import(testScoreInfo); }); - AddUntilStep("Became present", () => topLocalRank.IsPresent); - AddAssert("Correct rank", () => topLocalRank.Rank == ScoreRank.B); + AddUntilStep("B rank displayed", () => topLocalRank.Rank == ScoreRank.B); AddStep("Delete score", () => { scoreManager.Delete(testScoreInfo); }); - AddUntilStep("Became not present", () => !topLocalRank.IsPresent); + AddUntilStep("No rank displayed", () => topLocalRank.Rank == null); } [Test] @@ -99,13 +101,13 @@ namespace osu.Game.Tests.Visual.SongSelect scoreManager.Import(testScoreInfo); }); - AddUntilStep("Wait for initial presence", () => topLocalRank.IsPresent); + AddUntilStep("Wait for initial display", () => topLocalRank.Rank == ScoreRank.B); AddStep("Change ruleset", () => Ruleset.Value = rulesets.GetRuleset("fruits")); - AddUntilStep("Became not present", () => !topLocalRank.IsPresent); + AddUntilStep("No rank displayed", () => topLocalRank.Rank == null); AddStep("Change ruleset back", () => Ruleset.Value = rulesets.GetRuleset("osu")); - AddUntilStep("Became present", () => topLocalRank.IsPresent); + AddUntilStep("B rank displayed", () => topLocalRank.Rank == ScoreRank.B); } [Test] @@ -113,8 +115,6 @@ namespace osu.Game.Tests.Visual.SongSelect { ScoreInfo testScoreInfo = null; - AddAssert("Initially not present", () => !topLocalRank.IsPresent); - AddStep("Add score for current user", () => { testScoreInfo = TestResources.CreateTestScoreInfo(importedBeatmap); @@ -125,8 +125,7 @@ namespace osu.Game.Tests.Visual.SongSelect scoreManager.Import(testScoreInfo); }); - AddUntilStep("Became present", () => topLocalRank.IsPresent); - AddUntilStep("Correct rank", () => topLocalRank.Rank == ScoreRank.B); + AddUntilStep("B rank displayed", () => topLocalRank.Rank == ScoreRank.B); AddStep("Add higher score for current user", () => { @@ -135,11 +134,65 @@ namespace osu.Game.Tests.Visual.SongSelect testScoreInfo2.User = API.LocalUser.Value; testScoreInfo2.Rank = ScoreRank.S; testScoreInfo2.TotalScore = testScoreInfo.TotalScore + 1; + testScoreInfo2.Statistics = new Dictionary + { + [HitResult.Miss] = 0, + [HitResult.Perfect] = 970, + [HitResult.SmallTickHit] = 75, + [HitResult.LargeTickHit] = 150, + [HitResult.LargeBonus] = 10, + [HitResult.SmallBonus] = 50 + }; scoreManager.Import(testScoreInfo2); }); - AddUntilStep("Correct rank", () => topLocalRank.Rank == ScoreRank.S); + AddUntilStep("S rank displayed", () => topLocalRank.Rank == ScoreRank.S); + } + + [Test] + public void TestLegacyScore() + { + ScoreInfo testScoreInfo = null; + + AddStep("Add legacy score for current user", () => + { + testScoreInfo = TestResources.CreateTestScoreInfo(importedBeatmap); + + testScoreInfo.User = API.LocalUser.Value; + testScoreInfo.Rank = ScoreRank.B; + testScoreInfo.TotalScore = scoreManager.GetTotalScoreAsync(testScoreInfo, ScoringMode.Classic).GetResultSafely(); + + scoreManager.Import(testScoreInfo); + }); + + AddUntilStep("B rank displayed", () => topLocalRank.Rank == ScoreRank.B); + + AddStep("Add higher score for current user", () => + { + var testScoreInfo2 = TestResources.CreateTestScoreInfo(importedBeatmap); + + testScoreInfo2.User = API.LocalUser.Value; + testScoreInfo2.Rank = ScoreRank.S; + testScoreInfo2.Statistics = new Dictionary + { + [HitResult.Miss] = 0, + [HitResult.Perfect] = 970, + [HitResult.SmallTickHit] = 75, + [HitResult.LargeTickHit] = 150, + [HitResult.LargeBonus] = 10, + [HitResult.SmallBonus] = 50 + }; + + testScoreInfo2.TotalScore = scoreManager.GetTotalScoreAsync(testScoreInfo).GetResultSafely(); + + // ensure standardised total score is less than classic, otherwise this test is pointless. + Debug.Assert(testScoreInfo2.TotalScore < testScoreInfo.TotalScore); + + scoreManager.Import(testScoreInfo2); + }); + + AddUntilStep("S rank displayed", () => topLocalRank.Rank == ScoreRank.S); } } } From 727fe76b6090e2071507e1e1622e1f79fd2d22ca Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Jul 2022 08:09:00 +0300 Subject: [PATCH 021/142] Fix `TopLocalRank` hacking around presence to hide on null rank Fixed this here because that blocks `Schedule` from running, and I don't want to add another override to the `IsPresent` flag. --- .../SongSelect/TestSceneTopLocalRank.cs | 20 ++++++------- .../Online/Leaderboards/UpdateableRank.cs | 2 +- .../Screens/Select/Carousel/TopLocalRank.cs | 29 ++++++++++++++----- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs index d998c4e161..90d7aaacc3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.SongSelect }); }); - AddAssert("No rank displayed initially", () => topLocalRank.Rank == null); + AddAssert("No rank displayed initially", () => topLocalRank.DisplayedRank == null); } [Test] @@ -76,14 +76,14 @@ namespace osu.Game.Tests.Visual.SongSelect scoreManager.Import(testScoreInfo); }); - AddUntilStep("B rank displayed", () => topLocalRank.Rank == ScoreRank.B); + AddUntilStep("B rank displayed", () => topLocalRank.DisplayedRank == ScoreRank.B); AddStep("Delete score", () => { scoreManager.Delete(testScoreInfo); }); - AddUntilStep("No rank displayed", () => topLocalRank.Rank == null); + AddUntilStep("No rank displayed", () => topLocalRank.DisplayedRank == null); } [Test] @@ -101,13 +101,13 @@ namespace osu.Game.Tests.Visual.SongSelect scoreManager.Import(testScoreInfo); }); - AddUntilStep("Wait for initial display", () => topLocalRank.Rank == ScoreRank.B); + AddUntilStep("Wait for initial display", () => topLocalRank.DisplayedRank == ScoreRank.B); AddStep("Change ruleset", () => Ruleset.Value = rulesets.GetRuleset("fruits")); - AddUntilStep("No rank displayed", () => topLocalRank.Rank == null); + AddUntilStep("No rank displayed", () => topLocalRank.DisplayedRank == null); AddStep("Change ruleset back", () => Ruleset.Value = rulesets.GetRuleset("osu")); - AddUntilStep("B rank displayed", () => topLocalRank.Rank == ScoreRank.B); + AddUntilStep("B rank displayed", () => topLocalRank.DisplayedRank == ScoreRank.B); } [Test] @@ -125,7 +125,7 @@ namespace osu.Game.Tests.Visual.SongSelect scoreManager.Import(testScoreInfo); }); - AddUntilStep("B rank displayed", () => topLocalRank.Rank == ScoreRank.B); + AddUntilStep("B rank displayed", () => topLocalRank.DisplayedRank == ScoreRank.B); AddStep("Add higher score for current user", () => { @@ -147,7 +147,7 @@ namespace osu.Game.Tests.Visual.SongSelect scoreManager.Import(testScoreInfo2); }); - AddUntilStep("S rank displayed", () => topLocalRank.Rank == ScoreRank.S); + AddUntilStep("S rank displayed", () => topLocalRank.DisplayedRank == ScoreRank.S); } [Test] @@ -166,7 +166,7 @@ namespace osu.Game.Tests.Visual.SongSelect scoreManager.Import(testScoreInfo); }); - AddUntilStep("B rank displayed", () => topLocalRank.Rank == ScoreRank.B); + AddUntilStep("B rank displayed", () => topLocalRank.DisplayedRank == ScoreRank.B); AddStep("Add higher score for current user", () => { @@ -192,7 +192,7 @@ namespace osu.Game.Tests.Visual.SongSelect scoreManager.Import(testScoreInfo2); }); - AddUntilStep("S rank displayed", () => topLocalRank.Rank == ScoreRank.S); + AddUntilStep("S rank displayed", () => topLocalRank.DisplayedRank == ScoreRank.S); } } } diff --git a/osu.Game/Online/Leaderboards/UpdateableRank.cs b/osu.Game/Online/Leaderboards/UpdateableRank.cs index e4f5f72886..e640fe8494 100644 --- a/osu.Game/Online/Leaderboards/UpdateableRank.cs +++ b/osu.Game/Online/Leaderboards/UpdateableRank.cs @@ -17,7 +17,7 @@ namespace osu.Game.Online.Leaderboards set => Model = value; } - public UpdateableRank(ScoreRank? rank) + public UpdateableRank(ScoreRank? rank = null) { Rank = rank; } diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs index cc01f61c57..033da61461 100644 --- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs +++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs @@ -5,9 +5,12 @@ using System; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Models; @@ -20,7 +23,7 @@ using Realms; namespace osu.Game.Screens.Select.Carousel { - public class TopLocalRank : UpdateableRank + public class TopLocalRank : CompositeDrawable { private readonly BeatmapInfo beatmapInfo; @@ -34,13 +37,25 @@ namespace osu.Game.Screens.Select.Carousel private IAPIProvider api { get; set; } private IDisposable scoreSubscription; + private CancellationTokenSource scoreOrderCancellationSource; + + private readonly UpdateableRank updateable; + + public ScoreRank? DisplayedRank => updateable.Rank; public TopLocalRank(BeatmapInfo beatmapInfo) - : base(null) { this.beatmapInfo = beatmapInfo; - Size = new Vector2(40, 20); + AutoSizeAxes = Axes.Both; + + InternalChild = updateable = new UpdateableRank + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(40, 20), + Alpha = 0, + }; } protected override void LoadComplete() @@ -59,19 +74,17 @@ namespace osu.Game.Screens.Select.Carousel .OrderByDescending(s => s.TotalScore), (items, _, _) => { - Rank = items.FirstOrDefault()?.Rank; - // Required since presence is changed via IsPresent override - Invalidate(Invalidation.Presence); + updateable.Rank = items.FirstOrDefault()?.Rank; + updateable.Alpha = updateable.Rank != null ? 1 : 0; }); }, true); } - public override bool IsPresent => base.IsPresent && Rank != null; - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); + scoreOrderCancellationSource?.Cancel(); scoreSubscription?.Dispose(); } } From 1d0306810ab63e471a459c5488bef69cfe96610c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Jul 2022 08:13:05 +0300 Subject: [PATCH 022/142] Fix `TopLocalRank` not handling legacy scores properly --- .../Screens/Select/Carousel/TopLocalRank.cs | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs index 033da61461..d372cb8dbc 100644 --- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs +++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; @@ -33,6 +34,9 @@ namespace osu.Game.Screens.Select.Carousel [Resolved] private RealmAccess realm { get; set; } + [Resolved] + private ScoreManager scoreManager { get; set; } + [Resolved] private IAPIProvider api { get; set; } @@ -70,14 +74,29 @@ namespace osu.Game.Screens.Select.Carousel .Filter($"{nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} == $0" + $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $1" + $" && {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $2" - + $" && {nameof(ScoreInfo.DeletePending)} == false", api.LocalUser.Value.Id, beatmapInfo.ID, ruleset.Value.ShortName) - .OrderByDescending(s => s.TotalScore), - (items, _, _) => - { - updateable.Rank = items.FirstOrDefault()?.Rank; - updateable.Alpha = updateable.Rank != null ? 1 : 0; - }); + + $" && {nameof(ScoreInfo.DeletePending)} == false", api.LocalUser.Value.Id, beatmapInfo.ID, ruleset.Value.ShortName), + localScoresChanged); }, true); + + void localScoresChanged(IRealmCollection sender, ChangeSet changes, Exception error) + { + // This subscription may fire from changes to linked beatmaps, which we don't care about. + // It's currently not possible for a score to be modified after insertion, so we can safely ignore callbacks with only modifications. + if (changes?.HasCollectionChanges() == false) + return; + + scoreOrderCancellationSource?.Cancel(); + + scoreManager.OrderByTotalScoreAsync(sender.Detach().ToArray(), (scoreOrderCancellationSource = new CancellationTokenSource()).Token) + .ContinueWith(ordered => Schedule(() => + { + if (scoreOrderCancellationSource.IsCancellationRequested) + return; + + updateable.Rank = ordered.GetResultSafely().FirstOrDefault()?.Rank; + updateable.Alpha = updateable.Rank != null ? 1 : 0; + }), TaskContinuationOptions.OnlyOnRanToCompletion); + } } protected override void Dispose(bool isDisposing) From 3aecd288e22d985bb42b934bfc749fbe2f73b97d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 23 Jul 2022 09:25:06 +0300 Subject: [PATCH 023/142] Enable NRT on `TopLocalRank` --- .../SongSelect/TestSceneTopLocalRank.cs | 25 +++++++++---------- .../Screens/Select/Carousel/TopLocalRank.cs | 16 ++++++------ 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs index 90d7aaacc3..b122898346 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -10,6 +8,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Framework.Testing; @@ -25,10 +24,10 @@ namespace osu.Game.Tests.Visual.SongSelect { public class TestSceneTopLocalRank : OsuTestScene { - private RulesetStore rulesets; - private BeatmapManager beatmapManager; - private ScoreManager scoreManager; - private TopLocalRank topLocalRank; + private RulesetStore rulesets = null!; + private BeatmapManager beatmapManager = null!; + private ScoreManager scoreManager = null!; + private TopLocalRank topLocalRank = null!; [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) @@ -64,7 +63,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestBasicImportDelete() { - ScoreInfo testScoreInfo = null; + ScoreInfo? testScoreInfo = null; AddStep("Add score for current user", () => { @@ -80,7 +79,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("Delete score", () => { - scoreManager.Delete(testScoreInfo); + scoreManager.Delete(testScoreInfo.AsNonNull()); }); AddUntilStep("No rank displayed", () => topLocalRank.DisplayedRank == null); @@ -113,7 +112,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestHigherScoreSet() { - ScoreInfo testScoreInfo = null; + ScoreInfo? testScoreInfo = null; AddStep("Add score for current user", () => { @@ -133,7 +132,7 @@ namespace osu.Game.Tests.Visual.SongSelect testScoreInfo2.User = API.LocalUser.Value; testScoreInfo2.Rank = ScoreRank.S; - testScoreInfo2.TotalScore = testScoreInfo.TotalScore + 1; + testScoreInfo2.TotalScore = testScoreInfo.AsNonNull().TotalScore + 1; testScoreInfo2.Statistics = new Dictionary { [HitResult.Miss] = 0, @@ -153,7 +152,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestLegacyScore() { - ScoreInfo testScoreInfo = null; + ScoreInfo? testScoreInfo = null; AddStep("Add legacy score for current user", () => { @@ -184,10 +183,10 @@ namespace osu.Game.Tests.Visual.SongSelect [HitResult.SmallBonus] = 50 }; - testScoreInfo2.TotalScore = scoreManager.GetTotalScoreAsync(testScoreInfo).GetResultSafely(); + testScoreInfo2.TotalScore = scoreManager.GetTotalScoreAsync(testScoreInfo.AsNonNull()).GetResultSafely(); // ensure standardised total score is less than classic, otherwise this test is pointless. - Debug.Assert(testScoreInfo2.TotalScore < testScoreInfo.TotalScore); + Debug.Assert(testScoreInfo2.TotalScore < testScoreInfo.AsNonNull().TotalScore); scoreManager.Import(testScoreInfo2); }); diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs index d372cb8dbc..2a9dae6712 100644 --- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs +++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Linq; using System.Threading; @@ -29,19 +27,19 @@ namespace osu.Game.Screens.Select.Carousel private readonly BeatmapInfo beatmapInfo; [Resolved] - private IBindable ruleset { get; set; } + private IBindable ruleset { get; set; } = null!; [Resolved] - private RealmAccess realm { get; set; } + private RealmAccess realm { get; set; } = null!; [Resolved] - private ScoreManager scoreManager { get; set; } + private ScoreManager scoreManager { get; set; } = null!; [Resolved] - private IAPIProvider api { get; set; } + private IAPIProvider api { get; set; } = null!; - private IDisposable scoreSubscription; - private CancellationTokenSource scoreOrderCancellationSource; + private IDisposable? scoreSubscription; + private CancellationTokenSource? scoreOrderCancellationSource; private readonly UpdateableRank updateable; @@ -78,7 +76,7 @@ namespace osu.Game.Screens.Select.Carousel localScoresChanged); }, true); - void localScoresChanged(IRealmCollection sender, ChangeSet changes, Exception error) + void localScoresChanged(IRealmCollection sender, ChangeSet? changes, Exception _) { // This subscription may fire from changes to linked beatmaps, which we don't care about. // It's currently not possible for a score to be modified after insertion, so we can safely ignore callbacks with only modifications. From 3f2c3413691d1745b7688b01b0fbe159cc633ec2 Mon Sep 17 00:00:00 2001 From: solstice23 Date: Sat, 23 Jul 2022 18:13:19 +0800 Subject: [PATCH 024/142] Simplify length parsing --- .../Filtering/FilterQueryParserTest.cs | 1 + osu.Game/Screens/Select/FilterQueryParser.cs | 42 ++++++++++--------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index f08bd09cf5..181b740729 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -138,6 +138,7 @@ namespace osu.Game.Tests.NonVisual.Filtering new object[] { "65m", TimeSpan.FromMinutes(65), TimeSpan.FromMinutes(1), true }, new object[] { "90s", TimeSpan.FromSeconds(90), TimeSpan.FromSeconds(1), true }, new object[] { "80m20s", TimeSpan.FromSeconds(4820), TimeSpan.FromSeconds(1), true }, + new object[] { "1h20s", TimeSpan.FromSeconds(3620), TimeSpan.FromSeconds(1), true }, new object[] { "7.5m27s", new TimeSpan(), new TimeSpan(), false }, new object[] { "7m27", new TimeSpan(), new TimeSpan(), false }, new object[] { "7m7m7m", new TimeSpan(), new TimeSpan(), false }, diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index b8c85c5ce0..19fb8665d2 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -129,6 +129,16 @@ namespace osu.Game.Screens.Select value = Enum.GetNames(typeof(TEnum)).FirstOrDefault(name => name.StartsWith(value, true, CultureInfo.InvariantCulture)); return Enum.TryParse(value, true, out result); } + private static bool tryMatchRegex(string value, string regex, ref GroupCollection result) + { + Match matchs = Regex.Match(value, regex); + if (matchs.Success) + { + result = matchs.Groups; + return true; + } + return false; + } /// /// Attempts to parse a keyword filter with the specified and textual . @@ -314,28 +324,22 @@ namespace osu.Game.Screens.Select private static bool tryUpdateLengthRange(FilterCriteria criteria, Operator op, string val) { List parts = new List(); + GroupCollection groups = null; - if (Regex.IsMatch(val, @"^\d+(:\d+){1,2}$")) // formats like 12:34 + if ( + tryMatchRegex(val, @"^((?\d+):)?(?\d+):(?\d+)$", ref groups) || + tryMatchRegex(val, @"^((?\d+(\.\d+)?)h)?((?\d+(\.\d+)?)m)?((?\d+(\.\d+)?)s)?$", ref groups) || + tryMatchRegex(val, @"^(?\d+(\.\d+)?)$", ref groups) + ) { - List splitted = val.Split(':').ToList(); - while (splitted.Count < 3) - splitted.Insert(0, "0"); - - parts.Add(splitted[2] + 's'); - parts.Add(splitted[1] + 'm'); - parts.Add(splitted[0] + 'h'); + if (groups["seconds"].Success) + parts.Add(groups["seconds"].Value + "s"); + if (groups["minutes"].Success) + parts.Add(groups["minutes"].Value + "m"); + if (groups["hours"].Success) + parts.Add(groups["hours"].Value + "h"); } - else if (Regex.IsMatch(val, @"^(\d+(\.\d+)?[hms]){1,3}$") && "hms".Contains(Regex.Replace(val, @"[\d\.]", ""))) // formats like 1h2m3s - { - string[] splitted = Regex.Split(val, @"(?<=[hms])").Where(x => x.Length > 0).Reverse().ToArray(); - parts.AddRange(splitted); - } - else if (Regex.IsMatch(val, @"^\d+(\.\d+)?$")) // only one number - { - parts.Add(val + 's'); - } - - if (parts.Count == 0) + else return false; double totalLength = 0; From aaad2e474ce271166de0aa9655352cc70cd4db07 Mon Sep 17 00:00:00 2001 From: solstice23 Date: Sat, 23 Jul 2022 21:43:27 +0800 Subject: [PATCH 025/142] Refactor the multiple regex checks in criteria parsing --- osu.Game/Screens/Select/FilterQueryParser.cs | 35 +++++++++----------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index 19fb8665d2..75574c061d 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -129,15 +129,14 @@ namespace osu.Game.Screens.Select value = Enum.GetNames(typeof(TEnum)).FirstOrDefault(name => name.StartsWith(value, true, CultureInfo.InvariantCulture)); return Enum.TryParse(value, true, out result); } - private static bool tryMatchRegex(string value, string regex, ref GroupCollection result) + private static GroupCollection tryMatchRegex(string value, string regex) { Match matchs = Regex.Match(value, regex); if (matchs.Success) { - result = matchs.Groups; - return true; + return matchs.Groups; } - return false; + return null; } /// @@ -324,24 +323,22 @@ namespace osu.Game.Screens.Select private static bool tryUpdateLengthRange(FilterCriteria criteria, Operator op, string val) { List parts = new List(); - GroupCollection groups = null; - if ( - tryMatchRegex(val, @"^((?\d+):)?(?\d+):(?\d+)$", ref groups) || - tryMatchRegex(val, @"^((?\d+(\.\d+)?)h)?((?\d+(\.\d+)?)m)?((?\d+(\.\d+)?)s)?$", ref groups) || - tryMatchRegex(val, @"^(?\d+(\.\d+)?)$", ref groups) - ) - { - if (groups["seconds"].Success) - parts.Add(groups["seconds"].Value + "s"); - if (groups["minutes"].Success) - parts.Add(groups["minutes"].Value + "m"); - if (groups["hours"].Success) - parts.Add(groups["hours"].Value + "h"); - } - else + GroupCollection match = null; + match ??= tryMatchRegex(val, @"^((?\d+):)?(?\d+):(?\d+)$"); + match ??= tryMatchRegex(val, @"^((?\d+(\.\d+)?)h)?((?\d+(\.\d+)?)m)?((?\d+(\.\d+)?)s)?$"); + match ??= tryMatchRegex(val, @"^(?\d+(\.\d+)?)$"); + + if (match == null) return false; + if (match["seconds"].Success) + parts.Add(match["seconds"].Value + "s"); + if (match["minutes"].Success) + parts.Add(match["minutes"].Value + "m"); + if (match["hours"].Success) + parts.Add(match["hours"].Value + "h"); + double totalLength = 0; int minScale = 3600000; From ea97122425706078cd91944eb099dff9f4d8311d Mon Sep 17 00:00:00 2001 From: solstice23 Date: Sat, 23 Jul 2022 21:47:51 +0800 Subject: [PATCH 026/142] Separate positive and negative test cases --- .../Filtering/FilterQueryParserTest.cs | 91 ++++++++++--------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index 181b740729..ac16c59e5b 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -118,56 +118,63 @@ namespace osu.Game.Tests.NonVisual.Filtering Assert.IsNull(filterCriteria.BPM.Max); } - private static readonly object[] length_query_examples = + private static readonly object[] correct_length_query_examples = { - new object[] { "23s", TimeSpan.FromSeconds(23), TimeSpan.FromSeconds(1), true }, - new object[] { "9m", TimeSpan.FromMinutes(9), TimeSpan.FromMinutes(1), true }, - new object[] { "0.25h", TimeSpan.FromHours(0.25), TimeSpan.FromHours(1), true }, - new object[] { "70", TimeSpan.FromSeconds(70), TimeSpan.FromSeconds(1), true }, - new object[] { "7m27s", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1), true }, - new object[] { "7:27", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1), true }, - new object[] { "1h2m3s", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1), true }, - new object[] { "1h2m3.5s", TimeSpan.FromSeconds(3723.5), TimeSpan.FromSeconds(1), true }, - new object[] { "1:2:3", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1), true }, - new object[] { "1:02:03", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1), true }, - new object[] { "6", TimeSpan.FromSeconds(6), TimeSpan.FromSeconds(1), true }, - new object[] { "6.5", TimeSpan.FromSeconds(6.5), TimeSpan.FromSeconds(1), true }, - new object[] { "6.5s", TimeSpan.FromSeconds(6.5), TimeSpan.FromSeconds(1), true }, - new object[] { "6.5m", TimeSpan.FromMinutes(6.5), TimeSpan.FromMinutes(1), true }, - new object[] { "6h5m", TimeSpan.FromMinutes(365), TimeSpan.FromMinutes(1), true }, - new object[] { "65m", TimeSpan.FromMinutes(65), TimeSpan.FromMinutes(1), true }, - new object[] { "90s", TimeSpan.FromSeconds(90), TimeSpan.FromSeconds(1), true }, - new object[] { "80m20s", TimeSpan.FromSeconds(4820), TimeSpan.FromSeconds(1), true }, - new object[] { "1h20s", TimeSpan.FromSeconds(3620), TimeSpan.FromSeconds(1), true }, - new object[] { "7.5m27s", new TimeSpan(), new TimeSpan(), false }, - new object[] { "7m27", new TimeSpan(), new TimeSpan(), false }, - new object[] { "7m7m7m", new TimeSpan(), new TimeSpan(), false }, - new object[] { "7m70s", new TimeSpan(), new TimeSpan(), false }, - new object[] { "5s6m", new TimeSpan(), new TimeSpan(), false }, - new object[] { "0:", new TimeSpan(), new TimeSpan(), false }, - new object[] { ":0", new TimeSpan(), new TimeSpan(), false }, - new object[] { "0:3:", new TimeSpan(), new TimeSpan(), false }, - new object[] { "3:15.5", new TimeSpan(), new TimeSpan(), false }, + new object[] { "23s", TimeSpan.FromSeconds(23), TimeSpan.FromSeconds(1) }, + new object[] { "9m", TimeSpan.FromMinutes(9), TimeSpan.FromMinutes(1) }, + new object[] { "0.25h", TimeSpan.FromHours(0.25), TimeSpan.FromHours(1) }, + new object[] { "70", TimeSpan.FromSeconds(70), TimeSpan.FromSeconds(1) }, + new object[] { "7m27s", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1) }, + new object[] { "7:27", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1) }, + new object[] { "1h2m3s", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1) }, + new object[] { "1h2m3.5s", TimeSpan.FromSeconds(3723.5), TimeSpan.FromSeconds(1) }, + new object[] { "1:2:3", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1) }, + new object[] { "1:02:03", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1) }, + new object[] { "6", TimeSpan.FromSeconds(6), TimeSpan.FromSeconds(1) }, + new object[] { "6.5", TimeSpan.FromSeconds(6.5), TimeSpan.FromSeconds(1) }, + new object[] { "6.5s", TimeSpan.FromSeconds(6.5), TimeSpan.FromSeconds(1) }, + new object[] { "6.5m", TimeSpan.FromMinutes(6.5), TimeSpan.FromMinutes(1) }, + new object[] { "6h5m", TimeSpan.FromMinutes(365), TimeSpan.FromMinutes(1) }, + new object[] { "65m", TimeSpan.FromMinutes(65), TimeSpan.FromMinutes(1) }, + new object[] { "90s", TimeSpan.FromSeconds(90), TimeSpan.FromSeconds(1) }, + new object[] { "80m20s", TimeSpan.FromSeconds(4820), TimeSpan.FromSeconds(1) }, + new object[] { "1h20s", TimeSpan.FromSeconds(3620), TimeSpan.FromSeconds(1) }, }; [Test] - [TestCaseSource(nameof(length_query_examples))] - public void TestApplyLengthQueries(string lengthQuery, TimeSpan expectedLength, TimeSpan scale, bool isValid) + [TestCaseSource(nameof(correct_length_query_examples))] + public void TestApplyLengthQueries(string lengthQuery, TimeSpan expectedLength, TimeSpan scale) { string query = $"length={lengthQuery} time"; var filterCriteria = new FilterCriteria(); FilterQueryParser.ApplyQueries(filterCriteria, query); - if (isValid) - { - Assert.AreEqual("time", filterCriteria.SearchText.Trim()); - Assert.AreEqual(1, filterCriteria.SearchTerms.Length); - Assert.AreEqual(expectedLength.TotalMilliseconds - scale.TotalMilliseconds / 2.0, filterCriteria.Length.Min); - Assert.AreEqual(expectedLength.TotalMilliseconds + scale.TotalMilliseconds / 2.0, filterCriteria.Length.Max); - } - else - { - Assert.AreEqual(false, filterCriteria.Length.HasFilter); - } + Assert.AreEqual("time", filterCriteria.SearchText.Trim()); + Assert.AreEqual(1, filterCriteria.SearchTerms.Length); + Assert.AreEqual(expectedLength.TotalMilliseconds - scale.TotalMilliseconds / 2.0, filterCriteria.Length.Min); + Assert.AreEqual(expectedLength.TotalMilliseconds + scale.TotalMilliseconds / 2.0, filterCriteria.Length.Max); + } + + private static readonly object[] incorrect_length_query_examples = + { + new object[] { "7.5m27s" }, + new object[] { "7m27" }, + new object[] { "7m7m7m" }, + new object[] { "7m70s" }, + new object[] { "5s6m" }, + new object[] { "0:" }, + new object[] { ":0" }, + new object[] { "0:3:" }, + new object[] { "3:15.5" }, + }; + + [Test] + [TestCaseSource(nameof(incorrect_length_query_examples))] + public void TestInvalidLengthQueries(string lengthQuery) + { + string query = $"length={lengthQuery} time"; + var filterCriteria = new FilterCriteria(); + FilterQueryParser.ApplyQueries(filterCriteria, query); + Assert.AreEqual(false, filterCriteria.Length.HasFilter); } [Test] From 3e828c3416bd6d4f4701a5916be185eff71ab0f5 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Sat, 27 Aug 2022 20:11:38 +0300 Subject: [PATCH 027/142] Move base layout from DifficultyMultiplierDisplay to another class --- osu.Game/Overlays/Mods/ModsEffectDisplay.cs | 144 ++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 osu.Game/Overlays/Mods/ModsEffectDisplay.cs diff --git a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs new file mode 100644 index 0000000000..a7c5ec9ef9 --- /dev/null +++ b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs @@ -0,0 +1,144 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Mods; +using osuTK; + +namespace osu.Game.Overlays.Mods +{ + /// + /// + /// Base class for displays of mods effects. + /// + public abstract class ModsEffectDisplay : Container + { + public const float HEIGHT = 42; + private const float value_area_width = 56; + private const float transition_duration = 200; + + private readonly Box contentBackground; + private readonly Box labelBackground; + private readonly Container content; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + /// + /// Text to display in the left area of the display. + /// + protected abstract LocalisableString Label { get; } + + protected override Container Content => content; + + protected ModsEffectDisplay() + { + Height = HEIGHT; + AutoSizeAxes = Axes.X; + + InternalChild = new InputBlockingContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Masking = true, + CornerRadius = ModSelectPanel.CORNER_RADIUS, + Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), + Children = new Drawable[] + { + contentBackground = new Box + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Y, + Width = value_area_width + ModSelectPanel.CORNER_RADIUS + }, + new GridContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, value_area_width) + }, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Masking = true, + CornerRadius = ModSelectPanel.CORNER_RADIUS, + Children = new Drawable[] + { + labelBackground = new Box + { + RelativeSizeAxes = Axes.Both + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Margin = new MarginPadding { Horizontal = 18 }, + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Text = Label, + Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) + } + } + }, + content = new Container + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) + } + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load() + { + labelBackground.Colour = colourProvider.Background4; + } + + /// + /// Fades colours of text and its background according to displayed value. + /// + /// Difference between actual value and default value. + /// Negative leads to green color, positive to red. Can be obtained via CompareTo(defaultValue). + protected void SetColours(int difference) + { + if (difference == 0) + { + contentBackground.FadeColour(colourProvider.Background3, transition_duration, Easing.OutQuint); + content.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); + } + else if (difference < 0) + { + contentBackground.FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); + content.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); + } + else + { + contentBackground.FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); + content.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); + } + } + } +} From 90334a2b91888698e449797f8c29d6f71c526885 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Sat, 27 Aug 2022 20:12:45 +0300 Subject: [PATCH 028/142] Add test scene --- .../TestSceneModsEffectDisplay.cs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs new file mode 100644 index 0000000000..36267b0e2a --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs @@ -0,0 +1,60 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osu.Game.Overlays.Mods; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneModsEffectDisplay : OsuTestScene + { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + + [Test] + public void TestEffectDisplay() + { + TestDisplay dsp; + Add(dsp = new TestDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }); + AddSliderStep("value", 40, 60, 50, i => dsp.Value = i); + } + + private class TestDisplay : ModsEffectDisplay + { + private readonly OsuSpriteText text; + + protected override LocalisableString Label => "Test display"; + + public int Value + { + set + { + text.Text = value.ToString(); + SetColours(value.CompareTo(50)); + } + } + + public TestDisplay() + { + Add(text = new OsuSpriteText + { + Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold), + Text = "50" + }); + } + + [BackgroundDependencyLoader] + private void load() => SetColours(0); + } + } +} From 039f00956225ef2459da64469206a6a168c18687 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Sat, 27 Aug 2022 20:26:05 +0300 Subject: [PATCH 029/142] Inherit difficulty multiplier display from `ModsEffectDiplay` --- .../Mods/DifficultyMultiplierDisplay.cs | 140 +++--------------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 +- 2 files changed, 21 insertions(+), 123 deletions(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 835883fb93..e5daa2de63 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -3,27 +3,24 @@ #nullable disable -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets.Mods; using osuTK; using osu.Game.Localisation; namespace osu.Game.Overlays.Mods { - public class DifficultyMultiplierDisplay : CompositeDrawable, IHasCurrentValue + public sealed class DifficultyMultiplierDisplay : ModsEffectDisplay, IHasCurrentValue { - public const float HEIGHT = 42; + protected override LocalisableString Label => DifficultyMultiplierDisplayStrings.DifficultyMultiplier; public Bindable Current { @@ -33,143 +30,44 @@ namespace osu.Game.Overlays.Mods private readonly BindableNumberWithCurrent current = new BindableNumberWithCurrent(1); - private readonly Box underlayBackground; - private readonly Box contentBackground; - private readonly FillFlowContainer multiplierFlow; private readonly MultiplierCounter multiplierCounter; - [Resolved] - private OsuColour colours { get; set; } - - [Resolved] - private OverlayColourProvider colourProvider { get; set; } - - private const float multiplier_value_area_width = 56; - private const float transition_duration = 200; - public DifficultyMultiplierDisplay() { - Height = HEIGHT; - AutoSizeAxes = Axes.X; - - InternalChild = new InputBlockingContainer + Add(new FillFlowContainer { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Masking = true, - CornerRadius = ModSelectPanel.CORNER_RADIUS, - Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(2, 0), Children = new Drawable[] { - underlayBackground = new Box + multiplierCounter = new MultiplierCounter { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Y, - Width = multiplier_value_area_width + ModSelectPanel.CORNER_RADIUS + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Current = { BindTarget = Current } }, - new GridContainer + new SpriteIcon { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Absolute, multiplier_value_area_width) - }, - Content = new[] - { - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Masking = true, - CornerRadius = ModSelectPanel.CORNER_RADIUS, - Children = new Drawable[] - { - contentBackground = new Box - { - RelativeSizeAxes = Axes.Both - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Horizontal = 18 }, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - Text = DifficultyMultiplierDisplayStrings.DifficultyMultiplier, - Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) - } - } - }, - multiplierFlow = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - Direction = FillDirection.Horizontal, - Spacing = new Vector2(2, 0), - Children = new Drawable[] - { - multiplierCounter = new MultiplierCounter - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Current = { BindTarget = Current } - }, - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.Solid.Times, - Size = new Vector2(7), - Margin = new MarginPadding { Top = 1 } - } - } - } - } - } + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Solid.Times, + Size = new Vector2(7), + Margin = new MarginPadding { Top = 1 } } } - }; - } - - [BackgroundDependencyLoader] - private void load() - { - contentBackground.Colour = colourProvider.Background4; + }); } protected override void LoadComplete() { base.LoadComplete(); - current.BindValueChanged(_ => updateState(), true); + current.BindValueChanged(e => SetColours(e.NewValue.CompareTo(current.Default)), true); // required to prevent the counter initially rolling up from 0 to 1 // due to `Current.Value` having a nonstandard default value of 1. - multiplierCounter.SetCountWithoutRolling(Current.Value); - } - - private void updateState() - { - if (Current.IsDefault) - { - underlayBackground.FadeColour(colourProvider.Background3, transition_duration, Easing.OutQuint); - multiplierFlow.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); - } - else - { - var backgroundColour = Current.Value < 1 - ? colours.ForModType(ModType.DifficultyReduction) - : colours.ForModType(ModType.DifficultyIncrease); - - underlayBackground.FadeColour(backgroundColour, transition_duration, Easing.OutQuint); - multiplierFlow.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); - } + multiplierCounter.SetCountWithoutRolling(current.Value); } private class MultiplierCounter : RollingCounter diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index b993aca0ca..ccc075b190 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -153,7 +153,7 @@ namespace osu.Game.Overlays.Mods { Padding = new MarginPadding { - Top = (ShowTotalMultiplier ? DifficultyMultiplierDisplay.HEIGHT : 0) + PADDING, + Top = (ShowTotalMultiplier ? ModsEffectDisplay.HEIGHT : 0) + PADDING, Bottom = PADDING }, RelativeSizeAxes = Axes.Both, @@ -191,7 +191,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.TopRight, Origin = Anchor.TopRight, AutoSizeAxes = Axes.X, - Height = DifficultyMultiplierDisplay.HEIGHT, + Height = ModsEffectDisplay.HEIGHT, Margin = new MarginPadding { Horizontal = 100 }, Child = multiplierDisplay = new DifficultyMultiplierDisplay { From 2a12194ff9243e44dc8b29ff6e25dfd456ff4767 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Sun, 28 Aug 2022 02:59:38 +0300 Subject: [PATCH 030/142] Test colours --- .../TestSceneModsEffectDisplay.cs | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs index 36267b0e2a..dd663ad59d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs @@ -1,38 +1,65 @@ // 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 NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; +using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Mods; +using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface { + [TestFixture] public class TestSceneModsEffectDisplay : OsuTestScene { [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + [Resolved] + private OsuColour colours { get; set; } = null!; + [Test] - public void TestEffectDisplay() + public void TestModsEffectDisplay() { - TestDisplay dsp; - Add(dsp = new TestDisplay + TestDisplay testDisplay = null!; + Box background = null!; + + AddStep("add display", () => { - Anchor = Anchor.Centre, - Origin = Anchor.Centre + Add(testDisplay = new TestDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }); + var boxes = testDisplay.ChildrenOfType(); + background = boxes.First(); }); - AddSliderStep("value", 40, 60, 50, i => dsp.Value = i); + + AddStep("set value to default", () => testDisplay.Value = 50); + AddUntilStep("colours are correct", () => testDisplay.Container.Colour == Color4.White && background.Colour == colourProvider.Background3); + + AddStep("set value to less", () => testDisplay.Value = 40); + AddUntilStep("colours are correct", () => testDisplay.Container.Colour == colourProvider.Background5 && background.Colour == colours.ForModType(ModType.DifficultyReduction)); + + AddStep("set value to bigger", () => testDisplay.Value = 60); + AddUntilStep("colours are correct", () => testDisplay.Container.Colour == colourProvider.Background5 && background.Colour == colours.ForModType(ModType.DifficultyIncrease)); } private class TestDisplay : ModsEffectDisplay { private readonly OsuSpriteText text; + public Container Container => Content; + protected override LocalisableString Label => "Test display"; public int Value From 7faeed88b053e6b06f3de90fdb74b89ca1793215 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Mon, 29 Aug 2022 22:08:43 +0300 Subject: [PATCH 031/142] Add ability to override width of value area --- osu.Game/Overlays/Mods/ModsEffectDisplay.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs index a7c5ec9ef9..4b97614ade 100644 --- a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs +++ b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs @@ -20,7 +20,6 @@ namespace osu.Game.Overlays.Mods public abstract class ModsEffectDisplay : Container { public const float HEIGHT = 42; - private const float value_area_width = 56; private const float transition_duration = 200; private readonly Box contentBackground; @@ -38,6 +37,8 @@ namespace osu.Game.Overlays.Mods /// protected abstract LocalisableString Label { get; } + protected virtual float ValueAreaWidth => 56; + protected override Container Content => content; protected ModsEffectDisplay() @@ -59,7 +60,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.Y, - Width = value_area_width + ModSelectPanel.CORNER_RADIUS + Width = ValueAreaWidth + ModSelectPanel.CORNER_RADIUS }, new GridContainer { @@ -68,7 +69,7 @@ namespace osu.Game.Overlays.Mods ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Absolute, value_area_width) + new Dimension(GridSizeMode.Absolute, ValueAreaWidth) }, Content = new[] { From 5343c2645253407a218ee7916dfe11cd347ed4e5 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Mon, 29 Aug 2022 22:48:27 +0300 Subject: [PATCH 032/142] Control colour via `Current` bindable --- osu.Game/Overlays/Mods/ModsEffectDisplay.cs | 92 +++++++++++++++++---- 1 file changed, 74 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs index 4b97614ade..2ff6bf043b 100644 --- a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs +++ b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs @@ -1,10 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -13,11 +16,10 @@ using osuTK; namespace osu.Game.Overlays.Mods { - /// /// /// Base class for displays of mods effects. /// - public abstract class ModsEffectDisplay : Container + public abstract class ModsEffectDisplay : Container, IHasCurrentValue { public const float HEIGHT = 42; private const float transition_duration = 200; @@ -26,6 +28,14 @@ namespace osu.Game.Overlays.Mods private readonly Box labelBackground; private readonly Container content; + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } + + private readonly BindableWithCurrent current = new BindableWithCurrent(); + [Resolved] private OsuColour colours { get; set; } = null!; @@ -37,6 +47,13 @@ namespace osu.Game.Overlays.Mods /// protected abstract LocalisableString Label { get; } + /// + /// Calculates, does current value increase difficulty or decrease it. + /// + /// Value to calculate for. Will arrive from bindable once it changes. + /// Effect of the value. + protected abstract ModEffect CalculateEffect(TValue value); + protected virtual float ValueAreaWidth => 56; protected override Container Content => content; @@ -118,28 +135,67 @@ namespace osu.Game.Overlays.Mods labelBackground.Colour = colourProvider.Background4; } + protected override void LoadComplete() + { + Current.BindValueChanged(e => + { + var effect = CalculateEffect(e.NewValue); + setColours(effect); + }, true); + } + /// /// Fades colours of text and its background according to displayed value. /// - /// Difference between actual value and default value. - /// Negative leads to green color, positive to red. Can be obtained via CompareTo(defaultValue). - protected void SetColours(int difference) + /// Effect of the value. + private void setColours(ModEffect effect) { - if (difference == 0) + switch (effect) { - contentBackground.FadeColour(colourProvider.Background3, transition_duration, Easing.OutQuint); - content.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); - } - else if (difference < 0) - { - contentBackground.FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); - content.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); - } - else - { - contentBackground.FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); - content.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); + case ModEffect.NotChanged: + contentBackground.FadeColour(colourProvider.Background3, transition_duration, Easing.OutQuint); + content.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); + break; + + case ModEffect.DifficultyReduction: + contentBackground.FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); + content.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); + break; + + case ModEffect.DifficultyIncrease: + contentBackground.FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); + content.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); + break; + + default: + throw new ArgumentOutOfRangeException(nameof(effect)); } } + + #region Quick implementations for CalculateEffect + + /// + /// Converts signed integer into . Negative values are counted as difficulty reduction, positive as increase. + /// + /// Value to convert. Can be obtained from CompareTo. + /// Effect. + protected ModEffect CalculateForSign(int comparison) + { + if (comparison == 0) + return ModEffect.NotChanged; + if (comparison < 0) + return ModEffect.DifficultyReduction; + + return ModEffect.DifficultyIncrease; + } + + #endregion + + protected enum ModEffect + { + NotChanged, + DifficultyReduction, + DifficultyIncrease + } } } From 545e0bbcef580ab282235ba69585996a16bd6020 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Mon, 29 Aug 2022 22:48:45 +0300 Subject: [PATCH 033/142] Adjust inheritors and test --- .../TestSceneModsEffectDisplay.cs | 25 ++++++++----------- .../Mods/DifficultyMultiplierDisplay.cs | 19 ++++---------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 +-- 3 files changed, 17 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs index dd663ad59d..ec50a07998 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs @@ -44,32 +44,24 @@ namespace osu.Game.Tests.Visual.UserInterface background = boxes.First(); }); - AddStep("set value to default", () => testDisplay.Value = 50); + AddStep("set value to default", () => testDisplay.Current.Value = 50); AddUntilStep("colours are correct", () => testDisplay.Container.Colour == Color4.White && background.Colour == colourProvider.Background3); - AddStep("set value to less", () => testDisplay.Value = 40); + AddStep("set value to less", () => testDisplay.Current.Value = 40); AddUntilStep("colours are correct", () => testDisplay.Container.Colour == colourProvider.Background5 && background.Colour == colours.ForModType(ModType.DifficultyReduction)); - AddStep("set value to bigger", () => testDisplay.Value = 60); + AddStep("set value to bigger", () => testDisplay.Current.Value = 60); AddUntilStep("colours are correct", () => testDisplay.Container.Colour == colourProvider.Background5 && background.Colour == colours.ForModType(ModType.DifficultyIncrease)); } - private class TestDisplay : ModsEffectDisplay + private class TestDisplay : ModsEffectDisplay { private readonly OsuSpriteText text; public Container Container => Content; protected override LocalisableString Label => "Test display"; - - public int Value - { - set - { - text.Text = value.ToString(); - SetColours(value.CompareTo(50)); - } - } + protected override ModEffect CalculateEffect(int value) => CalculateForSign(value.CompareTo(50)); public TestDisplay() { @@ -80,8 +72,11 @@ namespace osu.Game.Tests.Visual.UserInterface }); } - [BackgroundDependencyLoader] - private void load() => SetColours(0); + protected override void LoadComplete() + { + base.LoadComplete(); + Current.BindValueChanged(e => text.Text = e.NewValue.ToString(), true); + } } } } diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index e5daa2de63..843310249e 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -3,12 +3,10 @@ #nullable disable -using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -18,22 +16,17 @@ using osu.Game.Localisation; namespace osu.Game.Overlays.Mods { - public sealed class DifficultyMultiplierDisplay : ModsEffectDisplay, IHasCurrentValue + public sealed class DifficultyMultiplierDisplay : ModsEffectDisplay { protected override LocalisableString Label => DifficultyMultiplierDisplayStrings.DifficultyMultiplier; - - public Bindable Current - { - get => current.Current; - set => current.Current = value; - } - - private readonly BindableNumberWithCurrent current = new BindableNumberWithCurrent(1); + protected override ModEffect CalculateEffect(double value) => CalculateForSign(value.CompareTo(1d)); private readonly MultiplierCounter multiplierCounter; public DifficultyMultiplierDisplay() { + Current.Default = 1d; + Current.Value = 1d; Add(new FillFlowContainer { AutoSizeAxes = Axes.Both, @@ -63,11 +56,9 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); - current.BindValueChanged(e => SetColours(e.NewValue.CompareTo(current.Default)), true); - // required to prevent the counter initially rolling up from 0 to 1 // due to `Current.Value` having a nonstandard default value of 1. - multiplierCounter.SetCountWithoutRolling(current.Value); + multiplierCounter.SetCountWithoutRolling(Current.Value); } private class MultiplierCounter : RollingCounter diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index ccc075b190..80f18eafeb 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -153,7 +153,7 @@ namespace osu.Game.Overlays.Mods { Padding = new MarginPadding { - Top = (ShowTotalMultiplier ? ModsEffectDisplay.HEIGHT : 0) + PADDING, + Top = (ShowTotalMultiplier ? ModsEffectDisplay.HEIGHT : 0) + PADDING, Bottom = PADDING }, RelativeSizeAxes = Axes.Both, @@ -191,7 +191,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.TopRight, Origin = Anchor.TopRight, AutoSizeAxes = Axes.X, - Height = ModsEffectDisplay.HEIGHT, + Height = ModsEffectDisplay.HEIGHT, Margin = new MarginPadding { Horizontal = 100 }, Child = multiplierDisplay = new DifficultyMultiplierDisplay { From b5779508d06bc3455d61a0ef617a35b8f5d8c826 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Tue, 6 Sep 2022 17:10:32 +0100 Subject: [PATCH 034/142] Retrieve great hit window from the hit object --- .../Difficulty/Evaluators/RhythmEvaluator.cs | 4 ++-- .../Difficulty/Evaluators/SpeedEvaluator.cs | 7 +++---- .../Difficulty/OsuDifficultyCalculator.cs | 13 ++++++------- .../Preprocessing/OsuDifficultyHitObject.cs | 15 +++++++++++++++ osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 8 +++----- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs index 3ffd45b588..ca57c87af9 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators /// /// Calculates a rhythm multiplier for the difficulty of the tap associated with historic data of the current . /// - public static double EvaluateDifficultyOf(DifficultyHitObject current, double greatWindow) + public static double EvaluateDifficultyOf(DifficultyHitObject current) { if (current.BaseObject is Spinner) return 0; @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators double lastDelta = lastObj.StrainTime; double currRatio = 1.0 + 6.0 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses. - double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - greatWindow * 0.6) / (greatWindow * 0.6)); + double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - currObj.HitWindowGreat * 0.6) / (currObj.HitWindowGreat * 0.6)); windowPenalty = Math.Min(1, windowPenalty); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs index 5187717639..c98f875eb5 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators /// and how easily they can be cheesed. /// /// - public static double EvaluateDifficultyOf(DifficultyHitObject current, double greatWindow) + public static double EvaluateDifficultyOf(DifficultyHitObject current) { if (current.BaseObject is Spinner) return 0; @@ -35,7 +35,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators var osuNextObj = (OsuDifficultyHitObject)current.Next(0); double strainTime = osuCurrObj.StrainTime; - double greatWindowFull = greatWindow * 2; double doubletapness = 1; // Nerf doubletappable doubles. @@ -45,13 +44,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators double nextDeltaTime = Math.Max(1, osuNextObj.DeltaTime); double deltaDifference = Math.Abs(nextDeltaTime - currDeltaTime); double speedRatio = currDeltaTime / Math.Max(currDeltaTime, deltaDifference); - double windowRatio = Math.Pow(Math.Min(1, currDeltaTime / greatWindowFull), 2); + double windowRatio = Math.Pow(Math.Min(1, currDeltaTime / osuCurrObj.HitWindowGreat), 2); doubletapness = Math.Pow(speedRatio, 1 - windowRatio); } // Cap deltatime to the OD 300 hitwindow. // 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly, whilst 0.92 limits the effect of the cap. - strainTime /= Math.Clamp((strainTime / greatWindowFull) / 0.93, 0.92, 1); + strainTime /= Math.Clamp((strainTime / osuCurrObj.HitWindowGreat) / 0.93, 0.92, 1); // derive speedBonus for calculation double speedBonus = 1.0; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 6ef17d47c0..33787da8f6 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -23,7 +23,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty public class OsuDifficultyCalculator : DifficultyCalculator { private const double difficulty_multiplier = 0.0675; - private double hitWindowGreat; public override int Version => 20220902; @@ -76,6 +75,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty int sliderCount = beatmap.HitObjects.Count(h => h is Slider); int spinnerCount = beatmap.HitObjects.Count(h => h is Spinner); + HitWindows hitWindows = new OsuHitWindows(); + hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); + + double hitWindowGreat = hitWindows.WindowFor(HitResult.Great) / clockRate; + return new OsuDifficultyAttributes { StarRating = starRating, @@ -112,16 +116,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) { - HitWindows hitWindows = new OsuHitWindows(); - hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty); - - hitWindowGreat = hitWindows.WindowFor(HitResult.Great) / clockRate; - return new Skill[] { new Aim(mods, true), new Aim(mods, false), - new Speed(mods, hitWindowGreat), + new Speed(mods), new Flashlight(mods) }; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index c7c5650184..6aea00fd35 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; using osuTK; namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing @@ -78,6 +79,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing /// public double? Angle { get; private set; } + /// + /// Retrieves the full hit window for a Great . + /// + public double HitWindowGreat { get; private set; } + private readonly OsuHitObject lastLastObject; private readonly OsuHitObject lastObject; @@ -90,6 +96,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing // Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects. StrainTime = Math.Max(DeltaTime, min_delta_time); + if (BaseObject is Slider sliderObject) + { + HitWindowGreat = 2 * sliderObject.HeadCircle.HitWindows.WindowFor(HitResult.Great) / clockRate; + } + else + { + HitWindowGreat = 2 * BaseObject.HitWindows.WindowFor(HitResult.Great) / clockRate; + } + setDistances(clockRate); } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index c39d61020c..efe0e136bf 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -26,14 +26,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override int ReducedSectionCount => 5; protected override double DifficultyMultiplier => 1.04; - private readonly double greatWindow; private readonly List objectStrains = new List(); - public Speed(Mod[] mods, double hitWindowGreat) + public Speed(Mod[] mods) : base(mods) { - greatWindow = hitWindowGreat; } private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000); @@ -43,9 +41,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double StrainValueAt(DifficultyHitObject current) { currentStrain *= strainDecay(((OsuDifficultyHitObject)current).StrainTime); - currentStrain += SpeedEvaluator.EvaluateDifficultyOf(current, greatWindow) * skillMultiplier; + currentStrain += SpeedEvaluator.EvaluateDifficultyOf(current) * skillMultiplier; - currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current, greatWindow); + currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current); double totalStrain = currentStrain * currentRhythm; From 1997519364810b178ddda6b7a21b6110737c4c39 Mon Sep 17 00:00:00 2001 From: apollo-dw <83023433+apollo-dw@users.noreply.github.com> Date: Wed, 7 Sep 2022 13:24:54 +0100 Subject: [PATCH 035/142] Don't use full hit window in rhythm --- osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs index ca57c87af9..3bec2346ce 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/RhythmEvaluator.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators double lastDelta = lastObj.StrainTime; double currRatio = 1.0 + 6.0 * Math.Min(0.5, Math.Pow(Math.Sin(Math.PI / (Math.Min(prevDelta, currDelta) / Math.Max(prevDelta, currDelta))), 2)); // fancy function to calculate rhythmbonuses. - double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - currObj.HitWindowGreat * 0.6) / (currObj.HitWindowGreat * 0.6)); + double windowPenalty = Math.Min(1, Math.Max(0, Math.Abs(prevDelta - currDelta) - currObj.HitWindowGreat * 0.3) / (currObj.HitWindowGreat * 0.3)); windowPenalty = Math.Min(1, windowPenalty); From 602ffebd54d79e76bb19a4b95572b4023f07562f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Sep 2022 23:29:04 +0900 Subject: [PATCH 036/142] Apply NRT and fix code style --- osu.Game/Screens/Select/FilterQueryParser.cs | 25 ++++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index 75574c061d..a7c1aec361 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Globalization; @@ -126,16 +124,21 @@ namespace osu.Game.Screens.Select { if (Enum.TryParse(value, true, out result)) return true; - value = Enum.GetNames(typeof(TEnum)).FirstOrDefault(name => name.StartsWith(value, true, CultureInfo.InvariantCulture)); + string? prefixMatch = Enum.GetNames(typeof(TEnum)).FirstOrDefault(name => name.StartsWith(value, true, CultureInfo.InvariantCulture)); + + if (prefixMatch == null) + return false; + return Enum.TryParse(value, true, out result); } - private static GroupCollection tryMatchRegex(string value, string regex) + + private static GroupCollection? tryMatchRegex(string value, string regex) { - Match matchs = Regex.Match(value, regex); - if (matchs.Success) - { - return matchs.Groups; - } + Match matches = Regex.Match(value, regex); + + if (matches.Success) + return matches.Groups; + return null; } @@ -324,7 +327,8 @@ namespace osu.Game.Screens.Select { List parts = new List(); - GroupCollection match = null; + GroupCollection? match = null; + match ??= tryMatchRegex(val, @"^((?\d+):)?(?\d+):(?\d+)$"); match ??= tryMatchRegex(val, @"^((?\d+(\.\d+)?)h)?((?\d+(\.\d+)?)m)?((?\d+(\.\d+)?)s)?$"); match ??= tryMatchRegex(val, @"^(?\d+(\.\d+)?)$"); @@ -358,6 +362,7 @@ namespace osu.Game.Screens.Select totalLength += length * scale; minScale = Math.Min(minScale, scale); } + return tryUpdateCriteriaRange(ref criteria.Length, op, totalLength, minScale / 2.0); } } From ae53e27e6cad2157dba515c71946012dcbca1c5d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 9 Sep 2022 19:39:01 +0300 Subject: [PATCH 037/142] Adjust test coverage to read better --- .../SongSelect/TestSceneTopLocalRank.cs | 63 ++++++------------- 1 file changed, 19 insertions(+), 44 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs index 1a72dc2a72..cced9b8b89 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneTopLocalRank.cs @@ -1,14 +1,12 @@ // 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.Diagnostics; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Extensions; -using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Framework.Testing; @@ -49,12 +47,12 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("Create local rank", () => { - Add(topLocalRank = new TopLocalRank(importedBeatmap) + Child = topLocalRank = new TopLocalRank(importedBeatmap) { Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(10), - }); + }; }); AddAssert("No rank displayed initially", () => topLocalRank.DisplayedRank == null); @@ -63,7 +61,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestBasicImportDelete() { - ScoreInfo? testScoreInfo = null; + ScoreInfo testScoreInfo = null!; AddStep("Add score for current user", () => { @@ -77,10 +75,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("B rank displayed", () => topLocalRank.DisplayedRank == ScoreRank.B); - AddStep("Delete score", () => - { - scoreManager.Delete(testScoreInfo.AsNonNull()); - }); + AddStep("Delete score", () => scoreManager.Delete(testScoreInfo)); AddUntilStep("No rank displayed", () => topLocalRank.DisplayedRank == null); } @@ -88,11 +83,9 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestRulesetChange() { - ScoreInfo testScoreInfo; - AddStep("Add score for current user", () => { - testScoreInfo = TestResources.CreateTestScoreInfo(importedBeatmap); + var testScoreInfo = TestResources.CreateTestScoreInfo(importedBeatmap); testScoreInfo.User = API.LocalUser.Value; testScoreInfo.Rank = ScoreRank.B; @@ -112,11 +105,9 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestHigherScoreSet() { - ScoreInfo? testScoreInfo = null; - AddStep("Add score for current user", () => { - testScoreInfo = TestResources.CreateTestScoreInfo(importedBeatmap); + var testScoreInfo = TestResources.CreateTestScoreInfo(importedBeatmap); testScoreInfo.User = API.LocalUser.Value; testScoreInfo.Rank = ScoreRank.B; @@ -131,28 +122,20 @@ namespace osu.Game.Tests.Visual.SongSelect var testScoreInfo2 = TestResources.CreateTestScoreInfo(importedBeatmap); testScoreInfo2.User = API.LocalUser.Value; - testScoreInfo2.Rank = ScoreRank.S; - testScoreInfo2.TotalScore = testScoreInfo.AsNonNull().TotalScore + 1; - testScoreInfo2.Statistics = new Dictionary - { - [HitResult.Miss] = 0, - [HitResult.Perfect] = 970, - [HitResult.SmallTickHit] = 75, - [HitResult.LargeTickHit] = 150, - [HitResult.LargeBonus] = 10, - [HitResult.SmallBonus] = 50 - }; + testScoreInfo2.Rank = ScoreRank.X; + testScoreInfo2.TotalScore = 1000000; + testScoreInfo2.Statistics = testScoreInfo2.MaximumStatistics; scoreManager.Import(testScoreInfo2); }); - AddUntilStep("S rank displayed", () => topLocalRank.DisplayedRank == ScoreRank.S); + AddUntilStep("SS rank displayed", () => topLocalRank.DisplayedRank == ScoreRank.X); } [Test] public void TestLegacyScore() { - ScoreInfo? testScoreInfo = null; + ScoreInfo testScoreInfo = null!; AddStep("Add legacy score for current user", () => { @@ -160,7 +143,7 @@ namespace osu.Game.Tests.Visual.SongSelect testScoreInfo.User = API.LocalUser.Value; testScoreInfo.Rank = ScoreRank.B; - testScoreInfo.TotalScore = scoreManager.GetTotalScoreAsync(testScoreInfo, ScoringMode.Classic).GetResultSafely(); + testScoreInfo.TotalScore = scoreManager.GetTotalScore(testScoreInfo, ScoringMode.Classic); scoreManager.Import(testScoreInfo); }); @@ -172,26 +155,18 @@ namespace osu.Game.Tests.Visual.SongSelect var testScoreInfo2 = TestResources.CreateTestScoreInfo(importedBeatmap); testScoreInfo2.User = API.LocalUser.Value; - testScoreInfo2.Rank = ScoreRank.S; - testScoreInfo2.Statistics = new Dictionary - { - [HitResult.Miss] = 0, - [HitResult.Perfect] = 970, - [HitResult.SmallTickHit] = 75, - [HitResult.LargeTickHit] = 150, - [HitResult.LargeBonus] = 10, - [HitResult.SmallBonus] = 50 - }; + testScoreInfo2.Rank = ScoreRank.X; + testScoreInfo2.Statistics = testScoreInfo2.MaximumStatistics; + testScoreInfo2.TotalScore = scoreManager.GetTotalScore(testScoreInfo2); - testScoreInfo2.TotalScore = scoreManager.GetTotalScoreAsync(testScoreInfo.AsNonNull()).GetResultSafely(); - - // ensure standardised total score is less than classic, otherwise this test is pointless. - Debug.Assert(testScoreInfo2.TotalScore < testScoreInfo.AsNonNull().TotalScore); + // ensure second score has a total score (standardised) less than first one (classic) + // despite having better statistics, otherwise this test is pointless. + Debug.Assert(testScoreInfo2.TotalScore < testScoreInfo.TotalScore); scoreManager.Import(testScoreInfo2); }); - AddUntilStep("S rank displayed", () => topLocalRank.DisplayedRank == ScoreRank.S); + AddUntilStep("SS rank displayed", () => topLocalRank.DisplayedRank == ScoreRank.X); } } } From 95062c2cc8fc804eb8e4a7d52d28e1f8b190d6c7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 9 Sep 2022 19:42:58 +0300 Subject: [PATCH 038/142] Remove center origin specification to avoid single-frame layout issues --- osu.Game/Screens/Select/Carousel/TopLocalRank.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs index cb2ff86794..0f000555d5 100644 --- a/osu.Game/Screens/Select/Carousel/TopLocalRank.cs +++ b/osu.Game/Screens/Select/Carousel/TopLocalRank.cs @@ -49,8 +49,6 @@ namespace osu.Game.Screens.Select.Carousel InternalChild = updateable = new UpdateableRank { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, Size = new Vector2(40, 20), Alpha = 0, }; From 5e4e3dfc2cb8ea50f83d4edbaab4f8ab19a77e8a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 10 Sep 2022 02:55:20 +0300 Subject: [PATCH 039/142] Fix markdown container not rendering certain text correctly --- .../Markdown/OsuMarkdownContainer.cs | 36 ++++++++++++++++--- .../Comments/CommentMarkdownContainer.cs | 2 ++ .../Wiki/Markdown/WikiMarkdownContainer.cs | 2 ++ 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs index a2370a76cb..62fefe201d 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownContainer.cs @@ -4,7 +4,9 @@ #nullable disable using Markdig; -using Markdig.Extensions.AutoIdentifiers; +using Markdig.Extensions.AutoLinks; +using Markdig.Extensions.EmphasisExtras; +using Markdig.Extensions.Footnotes; using Markdig.Extensions.Tables; using Markdig.Extensions.Yaml; using Markdig.Syntax; @@ -18,6 +20,18 @@ namespace osu.Game.Graphics.Containers.Markdown { public class OsuMarkdownContainer : MarkdownContainer { + /// + /// Allows this markdown container to parse and link footnotes. + /// + /// + protected virtual bool Footnotes => false; + + /// + /// Allows this markdown container to make URL text clickable. + /// + /// + protected virtual bool Autolinks => false; + public OsuMarkdownContainer() { LineSpacing = 21; @@ -78,10 +92,22 @@ namespace osu.Game.Graphics.Containers.Markdown return new OsuMarkdownUnorderedListItem(level); } + // reference: https://github.com/ppy/osu-web/blob/05488a96b25b5a09f2d97c54c06dd2bae59d1dc8/app/Libraries/Markdown/OsuMarkdown.php#L301 protected override MarkdownPipeline CreateBuilder() - => new MarkdownPipelineBuilder().UseAutoIdentifiers(AutoIdentifierOptions.GitHub) - .UseEmojiAndSmiley() - .UseYamlFrontMatter() - .UseAdvancedExtensions().Build(); + { + var pipeline = new MarkdownPipelineBuilder() + .UseAutoIdentifiers() + .UsePipeTables() + .UseEmphasisExtras(EmphasisExtraOptions.Strikethrough) + .UseYamlFrontMatter(); + + if (Footnotes) + pipeline = pipeline.UseFootnotes(); + + if (Autolinks) + pipeline = pipeline.UseAutoLinks(); + + return pipeline.Build(); + } } } diff --git a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs index 8fc011b2bf..b32b1c74c4 100644 --- a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs +++ b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs @@ -11,6 +11,8 @@ namespace osu.Game.Overlays.Comments { public class CommentMarkdownContainer : OsuMarkdownContainer { + protected override bool Autolinks => true; + protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new CommentMarkdownHeading(headingBlock); private class CommentMarkdownHeading : OsuMarkdownHeading diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs index 7754c1d450..4f1083a75c 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs @@ -15,6 +15,8 @@ namespace osu.Game.Overlays.Wiki.Markdown { public class WikiMarkdownContainer : OsuMarkdownContainer { + protected override bool Footnotes => true; + public string CurrentPath { set => DocumentUrl = value; From ec86700f2903bca40fd9cbc7f5a0d062e0373466 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sat, 10 Sep 2022 10:45:51 +0800 Subject: [PATCH 040/142] Add the nullable disable annotation back becuse it will cause the api broken if remove the nullable disable annotation in the mania ruleset. --- .../Configuration/ManiaRulesetConfigManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs index 64e1f75e2c..b30d72f2e2 100644 --- a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.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. +#nullable disable + using System; using osu.Framework.Configuration.Tracking; using osu.Game.Configuration; From 2f91b5c846b4055422574620717416c60480f60d Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sat, 10 Sep 2022 10:46:10 +0800 Subject: [PATCH 041/142] Add missing import. --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 24a4f16e9f..90733e0206 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.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 System.Collections.Generic; using System.Linq; using osu.Framework.Extensions.EnumExtensions; diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index b53dc67584..90d9ffe527 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.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 System.Collections.Generic; using System.Linq; using osu.Framework.Extensions.EnumExtensions; From 1398a7e11ebffc6815bf26a33a615ae63e30f54c Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sat, 10 Sep 2022 10:46:40 +0800 Subject: [PATCH 042/142] Property should accept the nullable state. --- osu.Game/Online/API/APIMod.cs | 2 +- osu.Game/Overlays/Settings/Sections/RulesetSection.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index dcbaaea012..176f10975d 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -48,7 +48,7 @@ namespace osu.Game.Online.API public Mod ToMod(Ruleset ruleset) { - Mod resultMod = ruleset.CreateModFromAcronym(Acronym); + Mod? resultMod = ruleset.CreateModFromAcronym(Acronym); if (resultMod == null) { diff --git a/osu.Game/Overlays/Settings/Sections/RulesetSection.cs b/osu.Game/Overlays/Settings/Sections/RulesetSection.cs index a5f5810214..6f0b3c27a0 100644 --- a/osu.Game/Overlays/Settings/Sections/RulesetSection.cs +++ b/osu.Game/Overlays/Settings/Sections/RulesetSection.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections { try { - SettingsSubsection section = ruleset.CreateSettings(); + SettingsSubsection? section = ruleset.CreateSettings(); if (section != null) Add(section); From 3d7367a8420ca60b029d4b557b7458dff7e7d487 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sat, 10 Sep 2022 11:07:23 +0800 Subject: [PATCH 043/142] Move the CreateConvertibleReplayFrame() into the base ruleset class for avoid api breaking change in the customized ruleset. --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- osu.Game/Rulesets/ILegacyRuleset.cs | 9 --------- osu.Game/Rulesets/Ruleset.cs | 8 ++++++++ osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 2 +- osu.Game/Screens/Play/SpectatorPlayer.cs | 7 ++----- 8 files changed, 15 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index d2e05fbeb9..999b9f6e2c 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -188,7 +188,7 @@ namespace osu.Game.Rulesets.Catch public int LegacyID => 2; - public IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame(); + public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame(); public override HitObjectComposer CreateHitObjectComposer() => new CatchHitObjectComposer(this); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 72be77ddab..c6a5e8bdaa 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -281,7 +281,7 @@ namespace osu.Game.Rulesets.Mania public int LegacyID => 3; - public IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); + public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new ManiaRulesetConfigManager(settings, RulesetInfo); diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 90733e0206..6af765361d 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Osu public int LegacyID => 0; - public IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); + public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new OsuRulesetConfigManager(settings, RulesetInfo); diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 90d9ffe527..6beb83575d 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -178,7 +178,7 @@ namespace osu.Game.Rulesets.Taiko public int LegacyID => 1; - public IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame(); + public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame(); protected override IEnumerable GetValidHitResults() { diff --git a/osu.Game/Rulesets/ILegacyRuleset.cs b/osu.Game/Rulesets/ILegacyRuleset.cs index c2617a065c..f4b03baccd 100644 --- a/osu.Game/Rulesets/ILegacyRuleset.cs +++ b/osu.Game/Rulesets/ILegacyRuleset.cs @@ -1,8 +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 osu.Game.Rulesets.Replays.Types; - namespace osu.Game.Rulesets { public interface ILegacyRuleset @@ -13,12 +11,5 @@ namespace osu.Game.Rulesets /// Identifies the server-side ID of a legacy ruleset. /// int LegacyID { get; } - - /// - /// For rulesets which support legacy (osu-stable) replay conversion, this method will create an empty replay frame - /// for conversion use. - /// - /// An empty frame for the current ruleset, or null if unsupported. - IConvertibleReplayFrame CreateConvertibleReplayFrame(); } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index a10575ef73..4bc82a5f7e 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -23,6 +23,7 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Filter; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; @@ -302,6 +303,13 @@ namespace osu.Game.Rulesets /// A descriptive name of the variant. public virtual LocalisableString GetVariantName(int variant) => string.Empty; + /// + /// For rulesets which support legacy (osu-stable) replay conversion, this method will create an empty replay frame + /// for conversion use. + /// + /// An empty frame for the current ruleset, or null if unsupported. + public virtual IConvertibleReplayFrame? CreateConvertibleReplayFrame() => null; + /// /// Creates the statistics for a to be displayed in the results screen. /// diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index 2b22b4abc7..f64e730c06 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -282,7 +282,7 @@ namespace osu.Game.Scoring.Legacy private ReplayFrame convertFrame(LegacyReplayFrame currentFrame, ReplayFrame lastFrame) { - var convertible = (currentRuleset as ILegacyRuleset)?.CreateConvertibleReplayFrame(); + var convertible = currentRuleset.CreateConvertibleReplayFrame(); if (convertible == null) throw new InvalidOperationException($"Legacy replay cannot be converted for the ruleset: {currentRuleset.Description}"); diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index 659d7d5d6c..68cc21fc1c 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -11,7 +11,6 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Spectator; -using osu.Game.Rulesets; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Scoring; @@ -81,14 +80,12 @@ namespace osu.Game.Screens.Play if (!this.IsCurrentScreen()) return; - var legacyRuleset = GameplayState.Ruleset as ILegacyRuleset; - Debug.Assert(legacyRuleset != null); - bool isFirstBundle = score.Replay.Frames.Count == 0; foreach (var frame in bundle.Frames) { - IConvertibleReplayFrame convertibleFrame = legacyRuleset.CreateConvertibleReplayFrame(); + IConvertibleReplayFrame convertibleFrame = GameplayState.Ruleset.CreateConvertibleReplayFrame(); + Debug.Assert(convertibleFrame != null); convertibleFrame.FromLegacy(frame, GameplayState.Beatmap); var convertedFrame = (ReplayFrame)convertibleFrame; From b056cac10a1259044e65e67ab059f108b66c23bb Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Sat, 10 Sep 2022 08:21:38 +0300 Subject: [PATCH 044/142] Remove generic and add default implementation for `CalculateEffect` --- .../Overlays/Mods/DifficultyMultiplierDisplay.cs | 3 +-- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 ++-- osu.Game/Overlays/Mods/ModsEffectDisplay.cs | 14 ++++++++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 843310249e..5530947d3d 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -16,10 +16,9 @@ using osu.Game.Localisation; namespace osu.Game.Overlays.Mods { - public sealed class DifficultyMultiplierDisplay : ModsEffectDisplay + public sealed class DifficultyMultiplierDisplay : ModsEffectDisplay { protected override LocalisableString Label => DifficultyMultiplierDisplayStrings.DifficultyMultiplier; - protected override ModEffect CalculateEffect(double value) => CalculateForSign(value.CompareTo(1d)); private readonly MultiplierCounter multiplierCounter; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 80f18eafeb..ccc075b190 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -153,7 +153,7 @@ namespace osu.Game.Overlays.Mods { Padding = new MarginPadding { - Top = (ShowTotalMultiplier ? ModsEffectDisplay.HEIGHT : 0) + PADDING, + Top = (ShowTotalMultiplier ? ModsEffectDisplay.HEIGHT : 0) + PADDING, Bottom = PADDING }, RelativeSizeAxes = Axes.Both, @@ -191,7 +191,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.TopRight, Origin = Anchor.TopRight, AutoSizeAxes = Axes.X, - Height = ModsEffectDisplay.HEIGHT, + Height = ModsEffectDisplay.HEIGHT, Margin = new MarginPadding { Horizontal = 100 }, Child = multiplierDisplay = new DifficultyMultiplierDisplay { diff --git a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs index 2ff6bf043b..4e630439ee 100644 --- a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs +++ b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Mods /// /// Base class for displays of mods effects. /// - public abstract class ModsEffectDisplay : Container, IHasCurrentValue + public abstract class ModsEffectDisplay : Container, IHasCurrentValue { public const float HEIGHT = 42; private const float transition_duration = 200; @@ -28,13 +28,13 @@ namespace osu.Game.Overlays.Mods private readonly Box labelBackground; private readonly Container content; - public Bindable Current + public Bindable Current { get => current.Current; set => current.Current = value; } - private readonly BindableWithCurrent current = new BindableWithCurrent(); + private readonly BindableWithCurrent current = new BindableWithCurrent(); [Resolved] private OsuColour colours { get; set; } = null!; @@ -52,7 +52,13 @@ namespace osu.Game.Overlays.Mods /// /// Value to calculate for. Will arrive from bindable once it changes. /// Effect of the value. - protected abstract ModEffect CalculateEffect(TValue value); + /// + /// Default implementation compares the value with ., bigger value counts as difficulty increase. + /// + protected virtual ModEffect CalculateEffect(double value) + { + return CalculateForSign(value.CompareTo(Current.Default)); + } protected virtual float ValueAreaWidth => 56; From efe3b1aa7bd047847510fe50f75f1f87d0c2f78b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Sep 2022 14:10:49 +0900 Subject: [PATCH 045/142] Allow dismissing notifications without performing action using middle / right click --- .../TestSceneNotificationOverlay.cs | 60 ++++++++++++++++++- .../Overlays/Notifications/Notification.cs | 19 +++++- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index 699b8f7d89..c0b29dde9a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -12,11 +12,13 @@ using osu.Framework.Utils; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; +using osuTK; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneNotificationOverlay : OsuTestScene + public class TestSceneNotificationOverlay : OsuManualInputManagerTestScene { private NotificationOverlay notificationOverlay = null!; @@ -32,7 +34,7 @@ namespace osu.Game.Tests.Visual.UserInterface TimeToCompleteProgress = 2000; progressingNotifications.Clear(); - Content.Children = new Drawable[] + Children = new Drawable[] { notificationOverlay = new NotificationOverlay { @@ -45,6 +47,60 @@ namespace osu.Game.Tests.Visual.UserInterface notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; }; }); + [Test] + public void TestDismissWithoutActivationRightClick() + { + bool activated = false; + SimpleNotification notification = null!; + + AddStep("post", () => + { + activated = false; + notificationOverlay.Post(notification = new SimpleNotification + { + Text = @"Welcome to osu!. Enjoy your stay!", + Activated = () => activated = true, + }); + }); + + AddStep("click to activate", () => + { + InputManager.MoveMouseTo(notificationOverlay.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Right); + }); + + AddUntilStep("wait for closed", () => notification.WasClosed); + AddAssert("was not activated", () => !activated); + AddStep("reset mouse position", () => InputManager.MoveMouseTo(Vector2.Zero)); + } + + [Test] + public void TestActivate() + { + bool activated = false; + SimpleNotification notification = null!; + + AddStep("post", () => + { + activated = false; + notificationOverlay.Post(notification = new SimpleNotification + { + Text = @"Welcome to osu!. Enjoy your stay!", + Activated = () => activated = true, + }); + }); + + AddStep("click to activate", () => + { + InputManager.MoveMouseTo(notificationOverlay.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("wait for closed", () => notification.WasClosed); + AddAssert("was activated", () => activated); + AddStep("reset mouse position", () => InputManager.MoveMouseTo(Vector2.Zero)); + } + [Test] public void TestPresence() { diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index e5f739bb08..09abdb865d 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -16,6 +16,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osuTK; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Overlays.Notifications { @@ -170,11 +171,25 @@ namespace osu.Game.Overlays.Notifications base.OnHoverLost(e); } + protected override bool OnMouseDown(MouseDownEvent e) + { + // right click doesn't trigger OnClick so we need to handle here until that changes. + if (e.Button != MouseButton.Left) + { + Close(); + return true; + } + + return base.OnMouseDown(e); + } + protected override bool OnClick(ClickEvent e) { - if (Activated?.Invoke() ?? true) - Close(); + // Clicking with anything but left button should dismiss but not perform the activation action. + if (e.Button == MouseButton.Left) + Activated?.Invoke(); + Close(); return true; } From a9094c6b03bf302382cf171c72a5f217e2668189 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 10 Sep 2022 15:18:32 +0900 Subject: [PATCH 046/142] Add test coverage of clicking close button to dismiss --- .../TestSceneNotificationOverlay.cs | 29 +++++++++++++++++++ .../Overlays/Notifications/Notification.cs | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index c0b29dde9a..5f82a0024b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -47,6 +47,35 @@ namespace osu.Game.Tests.Visual.UserInterface notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; }; }); + [Test] + public void TestDismissWithoutActivationCloseButton() + { + bool activated = false; + SimpleNotification notification = null!; + + AddStep("post", () => + { + activated = false; + notificationOverlay.Post(notification = new SimpleNotification + { + Text = @"Welcome to osu!. Enjoy your stay!", + Activated = () => activated = true, + }); + }); + + AddStep("click to activate", () => + { + InputManager.MoveMouseTo(notificationOverlay + .ChildrenOfType().Single() + .ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("wait for closed", () => notification.WasClosed); + AddAssert("was not activated", () => !activated); + AddStep("reset mouse position", () => InputManager.MoveMouseTo(Vector2.Zero)); + } + [Test] public void TestDismissWithoutActivationRightClick() { diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 09abdb865d..67739c2089 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -218,7 +218,7 @@ namespace osu.Game.Overlays.Notifications Expire(); } - private class CloseButton : OsuClickableContainer + internal class CloseButton : OsuClickableContainer { private SpriteIcon icon = null!; private Box background = null!; From 667854b03445f7eb73292fa40fcc84ac2970fd4a Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Sat, 10 Sep 2022 23:18:48 +0300 Subject: [PATCH 047/142] Make effect display have a built-in counter --- osu.Game/Overlays/Mods/ModsEffectDisplay.cs | 37 +++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs index 4e630439ee..98651ef5f0 100644 --- a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs +++ b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -11,6 +12,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; using osuTK; @@ -26,7 +28,7 @@ namespace osu.Game.Overlays.Mods private readonly Box contentBackground; private readonly Box labelBackground; - private readonly Container content; + private readonly FillFlowContainer content; public Bindable Current { @@ -62,6 +64,8 @@ namespace osu.Game.Overlays.Mods protected virtual float ValueAreaWidth => 56; + protected virtual string CounterFormat => "N0"; + protected override Container Content => content; protected ModsEffectDisplay() @@ -121,12 +125,20 @@ namespace osu.Game.Overlays.Mods } } }, - content = new Container + content = new FillFlowContainer { AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) + Direction = FillDirection.Horizontal, + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Spacing = new Vector2(2, 0), + Child = new EffectCounter(CounterFormat) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Current = { BindTarget = Current } + } } } } @@ -203,5 +215,24 @@ namespace osu.Game.Overlays.Mods DifficultyReduction, DifficultyIncrease } + + private class EffectCounter : RollingCounter + { + private readonly string? format; + + public EffectCounter(string? format) + { + this.format = format; + } + + protected override double RollingDuration => 500; + + protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(format); + + protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText + { + Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) + }; + } } } From 064fe832c78196b05cd0a807951747940e81d39b Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Sat, 10 Sep 2022 23:18:59 +0300 Subject: [PATCH 048/142] Fix test --- .../TestSceneModsEffectDisplay.cs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs index ec50a07998..42eceb3242 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; @@ -54,28 +53,15 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("colours are correct", () => testDisplay.Container.Colour == colourProvider.Background5 && background.Colour == colours.ForModType(ModType.DifficultyIncrease)); } - private class TestDisplay : ModsEffectDisplay + private class TestDisplay : ModsEffectDisplay { - private readonly OsuSpriteText text; - public Container Container => Content; protected override LocalisableString Label => "Test display"; - protected override ModEffect CalculateEffect(int value) => CalculateForSign(value.CompareTo(50)); public TestDisplay() { - Add(text = new OsuSpriteText - { - Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold), - Text = "50" - }); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - Current.BindValueChanged(e => text.Text = e.NewValue.ToString(), true); + Current.Default = 50; } } } From 3f93ec8538d3082bd0255d46ec0995372c18ed90 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Sat, 10 Sep 2022 23:20:28 +0300 Subject: [PATCH 049/142] Expose counter to inheritors --- osu.Game/Overlays/Mods/ModsEffectDisplay.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs index 98651ef5f0..3b112fdbd3 100644 --- a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs +++ b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs @@ -68,6 +68,8 @@ namespace osu.Game.Overlays.Mods protected override Container Content => content; + protected readonly RollingCounter Counter; + protected ModsEffectDisplay() { Height = HEIGHT; @@ -133,7 +135,7 @@ namespace osu.Game.Overlays.Mods Direction = FillDirection.Horizontal, Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Spacing = new Vector2(2, 0), - Child = new EffectCounter(CounterFormat) + Child = Counter = new EffectCounter(CounterFormat) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, From 324a3723a5dbba4b85f6d39617028acef7af005a Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Sat, 10 Sep 2022 23:23:04 +0300 Subject: [PATCH 050/142] Rewrite DMD --- .../Mods/DifficultyMultiplierDisplay.cs | 48 ++++--------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 5530947d3d..f582a010c6 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -3,14 +3,9 @@ #nullable disable -using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osuTK; using osu.Game.Localisation; @@ -20,34 +15,19 @@ namespace osu.Game.Overlays.Mods { protected override LocalisableString Label => DifficultyMultiplierDisplayStrings.DifficultyMultiplier; - private readonly MultiplierCounter multiplierCounter; + protected override string CounterFormat => @"N2"; public DifficultyMultiplierDisplay() { Current.Default = 1d; Current.Value = 1d; - Add(new FillFlowContainer + Add(new SpriteIcon { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(2, 0), - Children = new Drawable[] - { - multiplierCounter = new MultiplierCounter - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Current = { BindTarget = Current } - }, - new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.Solid.Times, - Size = new Vector2(7), - Margin = new MarginPadding { Top = 1 } - } - } + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Solid.Times, + Size = new Vector2(7), + Margin = new MarginPadding { Top = 1 } }); } @@ -57,19 +37,7 @@ namespace osu.Game.Overlays.Mods // required to prevent the counter initially rolling up from 0 to 1 // due to `Current.Value` having a nonstandard default value of 1. - multiplierCounter.SetCountWithoutRolling(Current.Value); - } - - private class MultiplierCounter : RollingCounter - { - protected override double RollingDuration => 500; - - protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(@"N2"); - - protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText - { - Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) - }; + Counter.SetCountWithoutRolling(Current.Value); } } } From 1eb2c74e57c3d18dfcac974bf7c52101a18806ec Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 11 Sep 2022 18:50:51 +0900 Subject: [PATCH 051/142] Fix countdown serialisation --- osu.Game/Online/Multiplayer/MultiplayerCountdown.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs b/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs index 61637ae970..fd22420b99 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerCountdown.cs @@ -33,6 +33,7 @@ namespace osu.Game.Online.Multiplayer /// /// Whether only a single instance of this type may be active at any one time. /// + [IgnoreMember] public virtual bool IsExclusive => true; } } From d6f90e3b9fd23496db6c57b8a3cb5339cf691cca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Sep 2022 21:32:22 +0900 Subject: [PATCH 052/142] Add basic fling-to-dismiss support --- .../Overlays/Notifications/Notification.cs | 132 +++++++++++++++++- 1 file changed, 128 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 67739c2089..3e7bd0ab2f 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -2,16 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; +using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osuTK; @@ -56,6 +59,8 @@ namespace osu.Game.Overlays.Notifications protected Container MainContent; + private readonly DragContainer dragContainer; + public virtual bool Read { get; set; } protected virtual IconUsage CloseButtonIcon => FontAwesome.Solid.Check; @@ -80,7 +85,11 @@ namespace osu.Game.Overlays.Notifications Anchor = Anchor.CentreLeft, Origin = Anchor.CentreRight, }, - MainContent = new Container + dragContainer = new DragContainer(this) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }.WithChild(MainContent = new Container { CornerRadius = 6, Masking = true, @@ -144,7 +153,7 @@ namespace osu.Game.Overlays.Notifications Blending = BlendingParameters.Additive, }, } - } + }) }; } @@ -213,11 +222,126 @@ namespace osu.Game.Overlays.Notifications WasClosed = true; - Closed?.Invoke(); - this.FadeOut(100); + if (dragContainer.FlingLeft()) + { + Light.FadeOut(100); + this.FadeOut(600, Easing.In); + } + else + { + Closed?.Invoke(); + this.FadeOut(100); + } + Expire(); } + private class DragContainer : Container + { + private Vector2 velocity; + private Vector2 lastPosition; + + private readonly Notification notification; + + public DragContainer(Notification notification) + { + this.notification = notification; + notification.Closed += () => FlingLeft(); + } + + public override RectangleF BoundingBox + { + get + { + var childBounding = Children.First().BoundingBox; + + if (X < 0) childBounding *= new Vector2(1, Math.Max(0, 1 + (X / 800))); + if (Y > 0) childBounding *= new Vector2(1, Math.Max(0, 1 - (Y / 800))); + + return childBounding; + } + } + + protected override bool OnDragStart(DragStartEvent e) => true; + + protected override void OnDrag(DragEvent e) + { + Vector2 change = e.MousePosition - e.MouseDownPosition; + + // Diminish the drag distance as we go further to simulate "rubber band" feeling. + change *= change.Length <= 0 ? 0 : MathF.Pow(change.Length, 0.9f) / change.Length; + + // Only apply Y change if dragging to the left. + if (change.X > 0) + change.Y = 0; + + this.MoveTo(change); + } + + protected override void OnDragEnd(DragEndEvent e) + { + if (Rotation < -10 || velocity.X < -0.3f) + { + FlingLeft(); + } + else + { + this.MoveTo(Vector2.Zero, 800, Easing.OutElastic); + this.RotateTo(0, 800, Easing.OutElastic); + } + + base.OnDragEnd(e); + } + + private bool flinging; + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + Rotation = Math.Min(0, X * 0.1f); + + if (flinging) + { + velocity.Y += (float)Clock.ElapsedFrameTime * 0.005f; + Position += (float)Clock.ElapsedFrameTime * velocity; + } + else + { + Vector2 change = (Position - lastPosition) / (float)Clock.ElapsedFrameTime; + + if (velocity.X == 0) + velocity = change; + else + { + velocity = new Vector2( + (float)Interpolation.DampContinuously(velocity.X, change.X, 40, Clock.ElapsedFrameTime), + (float)Interpolation.DampContinuously(velocity.Y, change.Y, 40, Clock.ElapsedFrameTime) + ); + } + + lastPosition = Position; + } + } + + public bool FlingLeft() + { + if (this.FindClosestParent() == null) + return false; + + if (flinging) + return true; + + if (velocity.X > -0.3f) + velocity.X = -0.3f - 0.5f * RNG.NextSingle(); + + flinging = true; + ClearTransforms(); + notification.Close(); + return true; + } + } + internal class CloseButton : OsuClickableContainer { private SpriteIcon icon = null!; From b5a2f7003e0d0770c53c775a36a2a24a893be05a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Sep 2022 21:34:35 +0900 Subject: [PATCH 053/142] Disallow flinging when not in toast state --- osu.Game/Overlays/NotificationOverlay.cs | 5 +++++ osu.Game/Overlays/Notifications/Notification.cs | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index b170ea5dfa..708bd4874d 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -158,7 +158,10 @@ namespace osu.Game.Overlays playDebouncedSample(notification.PopInSampleName); if (State.Value == Visibility.Hidden) + { + notification.IsInTray = true; toastTray.Post(notification); + } else addPermanently(notification); @@ -167,6 +170,8 @@ namespace osu.Game.Overlays private void addPermanently(Notification notification) { + notification.IsInTray = false; + var ourType = notification.GetType(); int depth = notification.DisplayOnTop ? -runningDepth : runningDepth; diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 3e7bd0ab2f..595cff4171 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -68,6 +68,11 @@ namespace osu.Game.Overlays.Notifications [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; + /// + /// Whether this notification is in the . + /// + public bool IsInTray { get; set; } + private readonly Box initialFlash; private Box background = null!; @@ -262,7 +267,7 @@ namespace osu.Game.Overlays.Notifications } } - protected override bool OnDragStart(DragStartEvent e) => true; + protected override bool OnDragStart(DragStartEvent e) => notification.IsInTray; protected override void OnDrag(DragEvent e) { @@ -326,7 +331,7 @@ namespace osu.Game.Overlays.Notifications public bool FlingLeft() { - if (this.FindClosestParent() == null) + if (!notification.IsInTray) return false; if (flinging) From a56cadcf9057278f2aa3735a79385e0c2c097239 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Sep 2022 21:52:19 +0900 Subject: [PATCH 054/142] Ensure drag position is reset when transferred to tray --- osu.Game/Overlays/NotificationOverlay.cs | 4 +-- .../Overlays/Notifications/Notification.cs | 33 ++++++++++++++----- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 708bd4874d..15573f99af 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -159,7 +159,7 @@ namespace osu.Game.Overlays if (State.Value == Visibility.Hidden) { - notification.IsInTray = true; + notification.IsInToastTray = true; toastTray.Post(notification); } else @@ -170,7 +170,7 @@ namespace osu.Game.Overlays private void addPermanently(Notification notification) { - notification.IsInTray = false; + notification.IsInToastTray = false; var ourType = notification.GetType(); int depth = notification.DisplayOnTop ? -runningDepth : runningDepth; diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 595cff4171..ded3c1606a 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -68,10 +68,21 @@ namespace osu.Game.Overlays.Notifications [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; + private bool isInToastTray; + /// /// Whether this notification is in the . /// - public bool IsInTray { get; set; } + public bool IsInToastTray + { + private get => isInToastTray; + set + { + isInToastTray = value; + if (!isInToastTray) + dragContainer.ResetPosition(); + } + } private readonly Box initialFlash; @@ -267,10 +278,13 @@ namespace osu.Game.Overlays.Notifications } } - protected override bool OnDragStart(DragStartEvent e) => notification.IsInTray; + protected override bool OnDragStart(DragStartEvent e) => notification.IsInToastTray; protected override void OnDrag(DragEvent e) { + if (!notification.IsInToastTray) + return; + Vector2 change = e.MousePosition - e.MouseDownPosition; // Diminish the drag distance as we go further to simulate "rubber band" feeling. @@ -286,14 +300,9 @@ namespace osu.Game.Overlays.Notifications protected override void OnDragEnd(DragEndEvent e) { if (Rotation < -10 || velocity.X < -0.3f) - { FlingLeft(); - } else - { - this.MoveTo(Vector2.Zero, 800, Easing.OutElastic); - this.RotateTo(0, 800, Easing.OutElastic); - } + ResetPosition(); base.OnDragEnd(e); } @@ -331,7 +340,7 @@ namespace osu.Game.Overlays.Notifications public bool FlingLeft() { - if (!notification.IsInTray) + if (!notification.IsInToastTray) return false; if (flinging) @@ -345,6 +354,12 @@ namespace osu.Game.Overlays.Notifications notification.Close(); return true; } + + public void ResetPosition() + { + this.MoveTo(Vector2.Zero, 800, Easing.OutElastic); + this.RotateTo(0, 800, Easing.OutElastic); + } } internal class CloseButton : OsuClickableContainer From 9ef23c79ced4e8ca705bec6ba20505a216791312 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 11 Sep 2022 22:05:44 +0900 Subject: [PATCH 055/142] Disallow forwarding during a drag operation --- osu.Game/Overlays/NotificationOverlayToastTray.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/NotificationOverlayToastTray.cs b/osu.Game/Overlays/NotificationOverlayToastTray.cs index 40324963fc..e4d3864beb 100644 --- a/osu.Game/Overlays/NotificationOverlayToastTray.cs +++ b/osu.Game/Overlays/NotificationOverlayToastTray.cs @@ -118,7 +118,7 @@ namespace osu.Game.Overlays return; // Notification hovered; delay dismissal. - if (notification.IsHovered) + if (notification.IsHovered || notification.IsDragged) { scheduleDismissal(); return; From bb3d5bc9f4d8b5c47ec2c833fd59a985c90264e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?= Date: Mon, 12 Sep 2022 04:43:15 +0900 Subject: [PATCH 056/142] test(osu.Game): add slider steps to change the size in timing distribution graph tests --- .../TestSceneHitEventTimingDistributionGraph.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs b/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs index 198be4035b..e014d79402 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; @@ -19,14 +20,24 @@ namespace osu.Game.Tests.Visual.Ranking public class TestSceneHitEventTimingDistributionGraph : OsuTestScene { private HitEventTimingDistributionGraph graph = null!; + private readonly BindableFloat width = new BindableFloat(600); + private readonly BindableFloat height = new BindableFloat(130); private static readonly HitObject placeholder_object = new HitCircle(); + public TestSceneHitEventTimingDistributionGraph() + { + width.BindValueChanged(e => graph.Width = e.NewValue); + height.BindValueChanged(e => graph.Height = e.NewValue); + } + [Test] public void TestManyDistributedEvents() { createTest(CreateDistributedHitEvents()); AddStep("add adjustment", () => graph.UpdateOffset(10)); + AddSliderStep("width", 0.0f, 1000.0f, width.Value, width.Set); + AddSliderStep("height", 0.0f, 1000.0f, height.Value, height.Set); } [Test] @@ -137,7 +148,7 @@ namespace osu.Game.Tests.Visual.Ranking { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(600, 130) + Size = new Vector2(width.Value, height.Value) } }; }); From ac324c63e2d0e3f0e9293519b6728aa7d415f90b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?= Date: Mon, 12 Sep 2022 04:46:32 +0900 Subject: [PATCH 057/142] fix(osu.Game): handle size changes in timing distribution graph --- .../HitEventTimingDistributionGraph.cs | 83 ++++++++++++++----- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs index 5335d77243..72603fd46b 100644 --- a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs +++ b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -209,13 +210,12 @@ namespace osu.Game.Screens.Ranking.Statistics private class Bar : CompositeDrawable { - private float totalValue => values.Sum(v => v.Value); - private float basalHeight => BoundingBox.Width / BoundingBox.Height; - private float availableHeight => 1 - basalHeight; - private readonly IReadOnlyList> values; private readonly float maxValue; private readonly bool isCentre; + private readonly float totalValue; + private readonly BindableFloat basalHeight; + private readonly BindableFloat offsetAdjustment; private Circle[] boxOriginals; private Circle boxAdjustment; @@ -228,6 +228,9 @@ namespace osu.Game.Screens.Ranking.Statistics this.values = values.OrderBy(v => v.Key.GetIndexForOrderedDisplay()).ToList(); this.maxValue = maxValue; this.isCentre = isCentre; + totalValue = values.Sum(v => v.Value); + basalHeight = new BindableFloat(); + offsetAdjustment = new BindableFloat(totalValue); RelativeSizeAxes = Axes.Both; Masking = true; @@ -254,38 +257,70 @@ namespace osu.Game.Screens.Ranking.Statistics else { // A bin with no value draws a grey dot instead. - InternalChildren = boxOriginals = new[] + Circle dot = new Circle { - new Circle - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Colour = isCentre ? Color4.White : Color4.Gray, - Height = 0, - }, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Colour = isCentre ? Color4.White : Color4.Gray, + Height = 0, }; + InternalChildren = boxOriginals = new[] { dot }; + basalHeight.BindValueChanged(e => dot.Height = e.NewValue); } + + offsetAdjustment.BindValueChanged(_ => drawAdjustmentBar()); } private const double duration = 300; - private float offsetForValue(float value) + private float calculateBasalHeight() => DrawHeight == 0 ? 0 : DrawWidth / DrawHeight; + + private float offsetForValue(float value) => (1 - basalHeight.Value) * value / maxValue; + + private float heightForValue(float value) => MathF.Max(basalHeight.Value + offsetForValue(value), 0); + + private void draw() { - return availableHeight * value / maxValue; + resizeBars(); + + if (boxAdjustment != null) + drawAdjustmentBar(); } - private float heightForValue(float value) + private void resizeBars() { - return basalHeight + offsetForValue(value); + float offsetValue = 0; + + for (int i = 0; i < values.Count; i++) + { + boxOriginals[i].Y = offsetForValue(offsetValue) * BoundingBox.Height; + boxOriginals[i].Height = heightForValue(values[i].Value); + offsetValue -= values[i].Value; + } + } + + private void drawAdjustmentBar() + { + bool hasAdjustment = offsetAdjustment.Value != totalValue; + + boxAdjustment.ResizeHeightTo(heightForValue(offsetAdjustment.Value), duration, Easing.OutQuint); + boxAdjustment.FadeTo(!hasAdjustment ? 0 : 1, duration, Easing.OutQuint); } protected override void LoadComplete() { base.LoadComplete(); + float height = calculateBasalHeight(); + + basalHeight.Set(height); + + if (!values.Any()) + return; + foreach (var boxOriginal in boxOriginals) - boxOriginal.Height = basalHeight; + boxOriginal.Height = height; float offsetValue = 0; @@ -295,6 +330,15 @@ namespace osu.Game.Screens.Ranking.Statistics boxOriginals[i].ResizeHeightTo(heightForValue(values[i].Value), duration, Easing.OutQuint); offsetValue -= values[i].Value; } + + basalHeight.BindValueChanged(_ => draw()); + } + + protected override void Update() + { + base.Update(); + + basalHeight.Set(calculateBasalHeight()); } public void UpdateOffset(float adjustment) @@ -318,8 +362,7 @@ namespace osu.Game.Screens.Ranking.Statistics }); } - boxAdjustment.ResizeHeightTo(heightForValue(adjustment), duration, Easing.OutQuint); - boxAdjustment.FadeTo(!hasAdjustment ? 0 : 1, duration, Easing.OutQuint); + offsetAdjustment.Set(adjustment); } } } From 24138b65a74c1d57a2267dce3510f01442a3fabb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 14:05:16 +0900 Subject: [PATCH 058/142] Fix storyboard animations not starting their animation playback from the correct point in time --- .../Drawables/DrawableStoryboardAnimation.cs | 16 ++++++++++++++++ osu.Game/Storyboards/StoryboardSprite.cs | 10 +++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index f3187d77b7..369a3ee7ba 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Textures; using osu.Framework.Utils; +using osu.Game.Screens.Play; using osu.Game.Skinning; using osuTK; @@ -115,6 +116,21 @@ namespace osu.Game.Storyboards.Drawables Animation.ApplyTransforms(this); } + [Resolved] + private IGameplayClock gameplayClock { get; set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // Framework animation class tries its best to synchronise the animation at LoadComplete, + // but in some cases (such as fast forward) this results in an incorrect start offset. + // + // In the case of storyboard animations, we want to synchronise with game time perfectly + // so let's get a correct time based on gameplay clock and earliest transform. + PlaybackPosition = gameplayClock.CurrentTime - Animation.EarliestTransformTime; + } + private void skinSourceChanged() { ClearFrames(); diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 1eeaa0f084..589bcb60be 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -26,7 +26,7 @@ namespace osu.Game.Storyboards public readonly CommandTimelineGroup TimelineGroup = new CommandTimelineGroup(); - public double StartTime + public virtual double StartTime { get { @@ -54,6 +54,14 @@ namespace osu.Game.Storyboards return firstAlpha.startTime; } + return EarliestTransformTime; + } + } + + public double EarliestTransformTime + { + get + { // If we got to this point, either no alpha commands were present, or the earliest had a non-zero start value. // The sprite's StartTime will be determined by the earliest command, regardless of type. double earliestStartTime = TimelineGroup.StartTime; From cf25ee8e8482b5c60fb6fd8b8f0d7e23fcf903d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 14:17:15 +0900 Subject: [PATCH 059/142] Add test coverage of storyboard animation start time --- .../Formats/LegacyStoryboardDecoderTest.cs | 19 +++++++++++++++++++ .../animation-starts-before-alpha.osb | 5 +++++ 2 files changed, 24 insertions(+) create mode 100644 osu.Game.Tests/Resources/animation-starts-before-alpha.osb diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index 6e41043b0b..d9e80fa111 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -97,6 +97,25 @@ namespace osu.Game.Tests.Beatmaps.Formats } } + [Test] + public void TestCorrectAnimationStartTime() + { + var decoder = new LegacyStoryboardDecoder(); + + using (var resStream = TestResources.OpenResource("animation-starts-before-alpha.osb")) + using (var stream = new LineBufferedReader(resStream)) + { + var storyboard = decoder.Decode(stream); + + StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3); + Assert.AreEqual(1, background.Elements.Count); + + Assert.AreEqual(2000, background.Elements[0].StartTime); + // This property should be used in DrawableStoryboardAnimation as a starting point for animation playback. + Assert.AreEqual(1000, (background.Elements[0] as StoryboardAnimation)?.EarliestTransformTime); + } + } + [Test] public void TestOutOfOrderStartTimes() { diff --git a/osu.Game.Tests/Resources/animation-starts-before-alpha.osb b/osu.Game.Tests/Resources/animation-starts-before-alpha.osb new file mode 100644 index 0000000000..ceef204f3f --- /dev/null +++ b/osu.Game.Tests/Resources/animation-starts-before-alpha.osb @@ -0,0 +1,5 @@ +[Events] +//Storyboard Layer 0 (Background) +Animation,Background,Centre,"img.jpg",320,240,2,150,LoopForever + S,0,1000,1500,0.08 // animation should start playing from this point in time.. + F,0,2000,,0,1 // .. not this point in time From bbf906ee064507157512ba692962707a4c682b1b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 14:20:48 +0900 Subject: [PATCH 060/142] Remove unnecessary `virtual` spec --- osu.Game/Storyboards/StoryboardSprite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs index 589bcb60be..cd7788bb08 100644 --- a/osu.Game/Storyboards/StoryboardSprite.cs +++ b/osu.Game/Storyboards/StoryboardSprite.cs @@ -26,7 +26,7 @@ namespace osu.Game.Storyboards public readonly CommandTimelineGroup TimelineGroup = new CommandTimelineGroup(); - public virtual double StartTime + public double StartTime { get { From eca241e9a71943f45b0226b02ea645c1790751e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 14:52:11 +0900 Subject: [PATCH 061/142] Move `UpdateProgressNotification` to base `UpdateManager` class --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 112 ++++++------------- osu.Game/Updater/UpdateManager.cs | 72 +++++++++++- 2 files changed, 99 insertions(+), 85 deletions(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index 6f45237522..84bac9da7c 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -5,26 +5,24 @@ using System; using System.Runtime.Versioning; using System.Threading.Tasks; using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Framework.Logging; using osu.Game; -using osu.Game.Graphics; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; -using osuTK; using Squirrel; using Squirrel.SimpleSplat; +using LogLevel = Squirrel.SimpleSplat.LogLevel; +using UpdateManager = osu.Game.Updater.UpdateManager; namespace osu.Desktop.Updater { [SupportedOSPlatform("windows")] - public class SquirrelUpdateManager : osu.Game.Updater.UpdateManager + public class SquirrelUpdateManager : UpdateManager { - private UpdateManager? updateManager; + private Squirrel.UpdateManager? updateManager; private INotificationOverlay notificationOverlay = null!; - public Task PrepareUpdateAsync() => UpdateManager.RestartAppWhenExited(); + public Task PrepareUpdateAsync() => Squirrel.UpdateManager.RestartAppWhenExited(); private static readonly Logger logger = Logger.GetLogger("updater"); @@ -35,6 +33,9 @@ namespace osu.Desktop.Updater private readonly SquirrelLogger squirrelLogger = new SquirrelLogger(); + [Resolved] + private OsuGameBase game { get; set; } = null!; + [BackgroundDependencyLoader] private void load(INotificationOverlay notifications) { @@ -63,7 +64,14 @@ namespace osu.Desktop.Updater if (updatePending) { // the user may have dismissed the completion notice, so show it again. - notificationOverlay.Post(new UpdateCompleteNotification(this)); + notificationOverlay.Post(new UpdateApplicationCompleteNotification + { + Activated = () => + { + restartToApplyUpdate(); + return true; + }, + }); return true; } @@ -75,19 +83,21 @@ namespace osu.Desktop.Updater if (notification == null) { - notification = new UpdateProgressNotification(this) { State = ProgressNotificationState.Active }; + notification = new UpdateProgressNotification + { + CompletionClickAction = restartToApplyUpdate, + }; + Schedule(() => notificationOverlay.Post(notification)); } - notification.Progress = 0; - notification.Text = @"Downloading update..."; + notification.StartDownload(); try { await updateManager.DownloadReleases(info.ReleasesToApply, p => notification.Progress = p / 100f).ConfigureAwait(false); - notification.Progress = 0; - notification.Text = @"Installing update..."; + notification.StartInstall(); await updateManager.ApplyReleases(info, p => notification.Progress = p / 100f).ConfigureAwait(false); @@ -107,9 +117,7 @@ namespace osu.Desktop.Updater else { // In the case of an error, a separate notification will be displayed. - notification.State = ProgressNotificationState.Cancelled; - notification.Close(); - + notification.FailDownload(); Logger.Error(e, @"update failed!"); } } @@ -131,78 +139,24 @@ namespace osu.Desktop.Updater return true; } + private bool restartToApplyUpdate() + { + PrepareUpdateAsync() + .ContinueWith(_ => Schedule(() => game.AttemptExit())); + return true; + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); updateManager?.Dispose(); } - private class UpdateCompleteNotification : ProgressCompletionNotification - { - [Resolved] - private OsuGame game { get; set; } = null!; - - public UpdateCompleteNotification(SquirrelUpdateManager updateManager) - { - Text = @"Update ready to install. Click to restart!"; - - Activated = () => - { - updateManager.PrepareUpdateAsync() - .ContinueWith(_ => updateManager.Schedule(() => game.AttemptExit())); - return true; - }; - } - } - - private class UpdateProgressNotification : ProgressNotification - { - private readonly SquirrelUpdateManager updateManager; - - public UpdateProgressNotification(SquirrelUpdateManager updateManager) - { - this.updateManager = updateManager; - } - - protected override Notification CreateCompletionNotification() - { - return new UpdateCompleteNotification(updateManager); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - IconContent.AddRange(new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = FontAwesome.Solid.Upload, - Size = new Vector2(20), - } - }); - } - - 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 : ILogger, IDisposable { - public Squirrel.SimpleSplat.LogLevel Level { get; set; } = Squirrel.SimpleSplat.LogLevel.Info; + public LogLevel Level { get; set; } = LogLevel.Info; - public void Write(string message, Squirrel.SimpleSplat.LogLevel logLevel) + public void Write(string message, LogLevel logLevel) { if (logLevel < Level) return; diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 4790055cd1..801f2daac5 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -1,16 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Threading.Tasks; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; +using osuTK; namespace osu.Game.Updater { @@ -27,13 +27,13 @@ namespace osu.Game.Updater GetType() != typeof(UpdateManager); [Resolved] - private OsuConfigManager config { get; set; } + private OsuConfigManager config { get; set; } = null!; [Resolved] - private OsuGameBase game { get; set; } + private OsuGameBase game { get; set; } = null!; [Resolved] - protected INotificationOverlay Notifications { get; private set; } + protected INotificationOverlay Notifications { get; private set; } = null!; protected override void LoadComplete() { @@ -59,7 +59,7 @@ namespace osu.Game.Updater private readonly object updateTaskLock = new object(); - private Task updateCheckTask; + private Task? updateCheckTask; public async Task CheckForUpdateAsync() { @@ -109,5 +109,65 @@ namespace osu.Game.Updater }; } } + + protected class UpdateApplicationCompleteNotification : ProgressCompletionNotification + { + public UpdateApplicationCompleteNotification() + { + Text = @"Update ready to install. Click to restart!"; + } + } + + public class UpdateProgressNotification : ProgressNotification + { + protected override Notification CreateCompletionNotification() => new UpdateApplicationCompleteNotification(); + + [BackgroundDependencyLoader] + private void load() + { + IconContent.AddRange(new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.Upload, + Size = new Vector2(14), + } + }); + } + + 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; + } + } + + public void StartDownload() + { + State = ProgressNotificationState.Active; + Progress = 0; + Text = @"Downloading update..."; + } + + public void StartInstall() + { + Progress = 0; + Text = @"Installing update..."; + } + + public void FailDownload() + { + State = ProgressNotificationState.Cancelled; + Close(); + } + } } } From 2b79e6b2de20e64cd984c4bb8c5878bc28bdb10e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 14:52:16 +0900 Subject: [PATCH 062/142] Add test coverage of update notification --- .../TestSceneNotificationOverlay.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index 699b8f7d89..1afe8327b3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -12,6 +12,7 @@ using osu.Framework.Utils; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; +using osu.Game.Updater; namespace osu.Game.Tests.Visual.UserInterface { @@ -134,6 +135,31 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("cancel notification", () => notification.State = ProgressNotificationState.Cancelled); } + [Test] + public void TestUpdateNotificationFlow() + { + bool applyUpdate = false; + + AddStep(@"post update", () => + { + applyUpdate = false; + + var updateNotification = new UpdateManager.UpdateProgressNotification + { + CompletionClickAction = () => applyUpdate = true + }; + + notificationOverlay.Post(updateNotification); + progressingNotifications.Add(updateNotification); + }); + + checkProgressingCount(1); + waitForCompletion(); + AddStep("click notification", () => notificationOverlay.ChildrenOfType().Single().TriggerClick()); + + AddUntilStep("wait for update applied", () => applyUpdate); + } + [Test] public void TestBasicFlow() { From 60b0b909a51d1986956cddbf56a6927f1025b649 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 14:58:46 +0900 Subject: [PATCH 063/142] Move update icon to background to avoid colour collission with progress spinner --- .../Overlays/Notifications/ProgressNotification.cs | 1 + osu.Game/Updater/UpdateManager.cs | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 64ad69adf3..bdf6f704e5 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -226,6 +226,7 @@ namespace osu.Game.Overlays.Notifications { RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background5, + Depth = float.MaxValue, }, loadingSpinner = new LoadingSpinner { diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 801f2daac5..8dcb1c4562 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -132,11 +132,19 @@ namespace osu.Game.Updater Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.Solid.Upload, - Size = new Vector2(14), + Size = new Vector2(34), + Colour = OsuColour.Gray(0.2f), + Depth = float.MaxValue, } }); } + protected override void LoadComplete() + { + base.LoadComplete(); + StartDownload(); + } + public override void Close() { // cancelling updates is not currently supported by the underlying updater. From 9b31aa6d7ace1a41888315f0bdcfc06ebf86c01a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 15:04:30 +0900 Subject: [PATCH 064/142] Fix activation not firing with refactors --- .../Visual/UserInterface/TestSceneNotificationOverlay.cs | 6 +++++- osu.Game/Updater/UpdateManager.cs | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index 1afe8327b3..f497a5bd6b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -155,7 +155,11 @@ namespace osu.Game.Tests.Visual.UserInterface checkProgressingCount(1); waitForCompletion(); - AddStep("click notification", () => notificationOverlay.ChildrenOfType().Single().TriggerClick()); + + UpdateManager.UpdateApplicationCompleteNotification? completionNotification = null; + AddUntilStep("wait for completion notification", + () => (completionNotification = notificationOverlay.ChildrenOfType().SingleOrDefault()) != null); + AddStep("click notification", () => completionNotification?.TriggerClick()); AddUntilStep("wait for update applied", () => applyUpdate); } diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 8dcb1c4562..49009e9124 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -110,7 +110,7 @@ namespace osu.Game.Updater } } - protected class UpdateApplicationCompleteNotification : ProgressCompletionNotification + public class UpdateApplicationCompleteNotification : ProgressCompletionNotification { public UpdateApplicationCompleteNotification() { @@ -120,7 +120,10 @@ namespace osu.Game.Updater public class UpdateProgressNotification : ProgressNotification { - protected override Notification CreateCompletionNotification() => new UpdateApplicationCompleteNotification(); + protected override Notification CreateCompletionNotification() => new UpdateApplicationCompleteNotification + { + Activated = CompletionClickAction + }; [BackgroundDependencyLoader] private void load() From 92beb6cbe740336ab84e920818bb4fe5b372563f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 15:16:45 +0900 Subject: [PATCH 065/142] Hide notification read light when in a toast state Also adds test coverage of read state and light. --- .../TestSceneNotificationOverlay.cs | 20 +++++++++++++++++++ .../Overlays/Notifications/Notification.cs | 11 ++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index 5f82a0024b..6b737b7f5f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -219,6 +219,26 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("cancel notification", () => notification.State = ProgressNotificationState.Cancelled); } + [Test] + public void TestReadState() + { + SimpleNotification notification = null!; + AddStep(@"post", () => notificationOverlay.Post(notification = new BackgroundNotification { Text = @"Welcome to osu!. Enjoy your stay!" })); + AddUntilStep("check is toast", () => !notification.IsInToastTray); + AddAssert("light is not visible", () => notification.ChildrenOfType().Single().Alpha == 0); + + AddUntilStep("wait for forward to overlay", () => !notification.IsInToastTray); + + setState(Visibility.Visible); + AddAssert("state is not read", () => !notification.Read); + AddUntilStep("light is visible", () => notification.ChildrenOfType().Single().Alpha == 1); + + setState(Visibility.Hidden); + setState(Visibility.Visible); + AddAssert("state is read", () => notification.Read); + AddUntilStep("light is not visible", () => notification.ChildrenOfType().Single().Alpha == 0); + } + [Test] public void TestBasicFlow() { diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index ded3c1606a..91f5f3b19b 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -75,12 +75,17 @@ namespace osu.Game.Overlays.Notifications /// public bool IsInToastTray { - private get => isInToastTray; + get => isInToastTray; set { isInToastTray = value; + if (!isInToastTray) + { dragContainer.ResetPosition(); + if (!Read) + Light.FadeIn(100); + } } } @@ -97,6 +102,7 @@ namespace osu.Game.Overlays.Notifications { Light = new NotificationLight { + Alpha = 0, Margin = new MarginPadding { Right = 5 }, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreRight, @@ -239,10 +245,7 @@ namespace osu.Game.Overlays.Notifications WasClosed = true; if (dragContainer.FlingLeft()) - { - Light.FadeOut(100); this.FadeOut(600, Easing.In); - } else { Closed?.Invoke(); From e06a0f7300fc9cb71b7a9914a1f8143481a57790 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 15:21:01 +0900 Subject: [PATCH 066/142] Fix dragged state not being exposed correctly --- osu.Game/Overlays/Notifications/Notification.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 91f5f3b19b..248d66ac16 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -63,6 +63,8 @@ namespace osu.Game.Overlays.Notifications public virtual bool Read { get; set; } + public new bool IsDragged => dragContainer.IsDragged; + protected virtual IconUsage CloseButtonIcon => FontAwesome.Solid.Check; [Resolved] From 2476cf8fb36e4e964b397d86ae082a0ab1de6149 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 15:37:48 +0900 Subject: [PATCH 067/142] Adjust movement to look less sudden when snapping back to Y=0 --- osu.Game/Overlays/Notifications/Notification.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 248d66ac16..2e4b0c430a 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -293,11 +293,13 @@ namespace osu.Game.Overlays.Notifications Vector2 change = e.MousePosition - e.MouseDownPosition; // Diminish the drag distance as we go further to simulate "rubber band" feeling. - change *= change.Length <= 0 ? 0 : MathF.Pow(change.Length, 0.9f) / change.Length; + change *= change.Length <= 0 ? 0 : MathF.Pow(change.Length, 0.8f) / change.Length; // Only apply Y change if dragging to the left. - if (change.X > 0) + if (change.X >= 0) change.Y = 0; + else + change.Y *= (float)Interpolation.ApplyEasing(Easing.InOutQuart, Math.Min(1, -change.X / 200)); this.MoveTo(change); } From 0755289ec3cfce14b47250baffabbbcacb76203e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 15:48:50 +0900 Subject: [PATCH 068/142] Apply NRT to `HitEventTimingDistributionGraph` --- .../HitEventTimingDistributionGraph.cs | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs index 72603fd46b..930a702958 100644 --- a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs +++ b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; @@ -48,6 +46,12 @@ namespace osu.Game.Screens.Ranking.Statistics /// private readonly IReadOnlyList hitEvents; + private readonly IDictionary[] bins; + private double binSize; + private double hitOffset; + + private Bar[]? barDrawables; + /// /// Creates a new . /// @@ -55,22 +59,15 @@ namespace osu.Game.Screens.Ranking.Statistics public HitEventTimingDistributionGraph(IReadOnlyList hitEvents) { this.hitEvents = hitEvents.Where(e => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result.IsHit()).ToList(); + bins = Enumerable.Range(0, total_timing_distribution_bins).Select(_ => new Dictionary()).ToArray>(); } - private IDictionary[] bins; - private double binSize; - private double hitOffset; - - private Bar[] barDrawables; - [BackgroundDependencyLoader] private void load() { - if (hitEvents == null || hitEvents.Count == 0) + if (hitEvents.Count == 0) return; - bins = Enumerable.Range(0, total_timing_distribution_bins).Select(_ => new Dictionary()).ToArray>(); - binSize = Math.Ceiling(hitEvents.Max(e => Math.Abs(e.TimeOffset)) / timing_distribution_bins); // Prevent div-by-0 by enforcing a minimum bin size @@ -217,11 +214,12 @@ namespace osu.Game.Screens.Ranking.Statistics private readonly BindableFloat basalHeight; private readonly BindableFloat offsetAdjustment; - private Circle[] boxOriginals; - private Circle boxAdjustment; + private Circle[] boxOriginals = null!; + + private Circle? boxAdjustment; [Resolved] - private OsuColour colours { get; set; } + private OsuColour colours { get; set; } = null!; public Bar(IDictionary values, float maxValue, bool isCentre) { From bcd2445d9b7a91d9e870cde3d3db5b505ca94a21 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 15:50:10 +0900 Subject: [PATCH 069/142] Move helper functions down in file --- .../HitEventTimingDistributionGraph.cs | 115 +++++++++--------- 1 file changed, 57 insertions(+), 58 deletions(-) diff --git a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs index 930a702958..8ef85d24c4 100644 --- a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs +++ b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs @@ -221,6 +221,8 @@ namespace osu.Game.Screens.Ranking.Statistics [Resolved] private OsuColour colours { get; set; } = null!; + private const double duration = 300; + public Bar(IDictionary values, float maxValue, bool isCentre) { this.values = values.OrderBy(v => v.Key.GetIndexForOrderedDisplay()).ToList(); @@ -270,7 +272,61 @@ namespace osu.Game.Screens.Ranking.Statistics offsetAdjustment.BindValueChanged(_ => drawAdjustmentBar()); } - private const double duration = 300; + protected override void LoadComplete() + { + base.LoadComplete(); + + float height = calculateBasalHeight(); + + basalHeight.Value = height; + + if (!values.Any()) + return; + + foreach (var boxOriginal in boxOriginals) + boxOriginal.Height = height; + + float offsetValue = 0; + + for (int i = 0; i < values.Count; i++) + { + boxOriginals[i].MoveToY(offsetForValue(offsetValue) * BoundingBox.Height, duration, Easing.OutQuint); + boxOriginals[i].ResizeHeightTo(heightForValue(values[i].Value), duration, Easing.OutQuint); + offsetValue -= values[i].Value; + } + + basalHeight.BindValueChanged(_ => draw()); + } + + protected override void Update() + { + base.Update(); + basalHeight.Value = calculateBasalHeight(); + } + + public void UpdateOffset(float adjustment) + { + bool hasAdjustment = adjustment != totalValue; + + if (boxAdjustment == null) + { + if (!hasAdjustment) + return; + + AddInternal(boxAdjustment = new Circle + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Colour = Color4.Yellow, + Blending = BlendingParameters.Additive, + Alpha = 0.6f, + Height = 0, + }); + } + + offsetAdjustment.Set(adjustment); + } private float calculateBasalHeight() => DrawHeight == 0 ? 0 : DrawWidth / DrawHeight; @@ -305,63 +361,6 @@ namespace osu.Game.Screens.Ranking.Statistics boxAdjustment.ResizeHeightTo(heightForValue(offsetAdjustment.Value), duration, Easing.OutQuint); boxAdjustment.FadeTo(!hasAdjustment ? 0 : 1, duration, Easing.OutQuint); } - - protected override void LoadComplete() - { - base.LoadComplete(); - - float height = calculateBasalHeight(); - - basalHeight.Set(height); - - if (!values.Any()) - return; - - foreach (var boxOriginal in boxOriginals) - boxOriginal.Height = height; - - float offsetValue = 0; - - for (int i = 0; i < values.Count; i++) - { - boxOriginals[i].MoveToY(offsetForValue(offsetValue) * BoundingBox.Height, duration, Easing.OutQuint); - boxOriginals[i].ResizeHeightTo(heightForValue(values[i].Value), duration, Easing.OutQuint); - offsetValue -= values[i].Value; - } - - basalHeight.BindValueChanged(_ => draw()); - } - - protected override void Update() - { - base.Update(); - - basalHeight.Set(calculateBasalHeight()); - } - - public void UpdateOffset(float adjustment) - { - bool hasAdjustment = adjustment != totalValue; - - if (boxAdjustment == null) - { - if (!hasAdjustment) - return; - - AddInternal(boxAdjustment = new Circle - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Colour = Color4.Yellow, - Blending = BlendingParameters.Additive, - Alpha = 0.6f, - Height = 0, - }); - } - - offsetAdjustment.Set(adjustment); - } } } } From 96951057df199df099e61af0b4136cd1909ebe1f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 15:56:06 +0900 Subject: [PATCH 070/142] Avoid using bindables --- .../HitEventTimingDistributionGraph.cs | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs index 8ef85d24c4..ebf521ba21 100644 --- a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs +++ b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -211,8 +210,9 @@ namespace osu.Game.Screens.Ranking.Statistics private readonly float maxValue; private readonly bool isCentre; private readonly float totalValue; - private readonly BindableFloat basalHeight; - private readonly BindableFloat offsetAdjustment; + + private float basalHeight; + private float offsetAdjustment; private Circle[] boxOriginals = null!; @@ -229,8 +229,7 @@ namespace osu.Game.Screens.Ranking.Statistics this.maxValue = maxValue; this.isCentre = isCentre; totalValue = values.Sum(v => v.Value); - basalHeight = new BindableFloat(); - offsetAdjustment = new BindableFloat(totalValue); + offsetAdjustment = totalValue; RelativeSizeAxes = Axes.Both; Masking = true; @@ -266,25 +265,20 @@ namespace osu.Game.Screens.Ranking.Statistics Height = 0, }; InternalChildren = boxOriginals = new[] { dot }; - basalHeight.BindValueChanged(e => dot.Height = e.NewValue); } - - offsetAdjustment.BindValueChanged(_ => drawAdjustmentBar()); } protected override void LoadComplete() { base.LoadComplete(); - float height = calculateBasalHeight(); - - basalHeight.Value = height; - if (!values.Any()) return; + updateBasalHeight(); + foreach (var boxOriginal in boxOriginals) - boxOriginal.Height = height; + boxOriginal.Height = basalHeight; float offsetValue = 0; @@ -294,14 +288,12 @@ namespace osu.Game.Screens.Ranking.Statistics boxOriginals[i].ResizeHeightTo(heightForValue(values[i].Value), duration, Easing.OutQuint); offsetValue -= values[i].Value; } - - basalHeight.BindValueChanged(_ => draw()); } protected override void Update() { base.Update(); - basalHeight.Value = calculateBasalHeight(); + updateBasalHeight(); } public void UpdateOffset(float adjustment) @@ -325,14 +317,27 @@ namespace osu.Game.Screens.Ranking.Statistics }); } - offsetAdjustment.Set(adjustment); + offsetAdjustment = adjustment; + drawAdjustmentBar(); } - private float calculateBasalHeight() => DrawHeight == 0 ? 0 : DrawWidth / DrawHeight; + private void updateBasalHeight() + { + float newBasalHeight = DrawHeight == 0 ? 0 : DrawWidth / DrawHeight; - private float offsetForValue(float value) => (1 - basalHeight.Value) * value / maxValue; + if (newBasalHeight == basalHeight) + return; - private float heightForValue(float value) => MathF.Max(basalHeight.Value + offsetForValue(value), 0); + basalHeight = newBasalHeight; + foreach (var dot in boxOriginals) + dot.Height = basalHeight; + + draw(); + } + + private float offsetForValue(float value) => (1 - basalHeight) * value / maxValue; + + private float heightForValue(float value) => MathF.Max(basalHeight + offsetForValue(value), 0); private void draw() { @@ -356,9 +361,9 @@ namespace osu.Game.Screens.Ranking.Statistics private void drawAdjustmentBar() { - bool hasAdjustment = offsetAdjustment.Value != totalValue; + bool hasAdjustment = offsetAdjustment != totalValue; - boxAdjustment.ResizeHeightTo(heightForValue(offsetAdjustment.Value), duration, Easing.OutQuint); + boxAdjustment.ResizeHeightTo(heightForValue(offsetAdjustment), duration, Easing.OutQuint); boxAdjustment.FadeTo(!hasAdjustment ? 0 : 1, duration, Easing.OutQuint); } } From 94f8197e22f647d61ee82c42bee7d9dbd386d65c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 16:15:14 +0900 Subject: [PATCH 071/142] Show login overlay when attempting to update a beatmap while logged out --- .../Select/Carousel/UpdateBeatmapSetButton.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/UpdateBeatmapSetButton.cs b/osu.Game/Screens/Select/Carousel/UpdateBeatmapSetButton.cs index 73e7d23df0..3c4ed4734b 100644 --- a/osu.Game/Screens/Select/Carousel/UpdateBeatmapSetButton.cs +++ b/osu.Game/Screens/Select/Carousel/UpdateBeatmapSetButton.cs @@ -11,6 +11,8 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Overlays; using osuTK; using osuTK.Graphics; @@ -22,6 +24,15 @@ namespace osu.Game.Screens.Select.Carousel private SpriteIcon icon = null!; private Box progressFill = null!; + [Resolved] + private BeatmapModelDownloader beatmapDownloader { get; set; } = null!; + + [Resolved] + private IAPIProvider api { get; set; } = null!; + + [Resolved(canBeNull: true)] + private LoginOverlay? loginOverlay { get; set; } + public UpdateBeatmapSetButton(BeatmapSetInfo beatmapSetInfo) { this.beatmapSetInfo = beatmapSetInfo; @@ -32,9 +43,6 @@ namespace osu.Game.Screens.Select.Carousel Origin = Anchor.CentreLeft; } - [Resolved] - private BeatmapModelDownloader beatmapDownloader { get; set; } = null!; - [BackgroundDependencyLoader] private void load() { @@ -90,6 +98,12 @@ namespace osu.Game.Screens.Select.Carousel Action = () => { + if (!api.IsLoggedIn) + { + loginOverlay?.Show(); + return; + } + beatmapDownloader.DownloadAsUpdate(beatmapSetInfo); attachExistingDownload(); }; From 0d0e2e7bcce9445e9513cacb8281b03b8adf91be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?= Date: Mon, 12 Sep 2022 16:20:33 +0900 Subject: [PATCH 072/142] fix(osu.Game): bars in hit distribution graph stretching in the opposite direction when `DrawHeight` is less than `DrawWidth` --- .../Ranking/Statistics/HitEventTimingDistributionGraph.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs index ebf521ba21..5f3b2773ea 100644 --- a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs +++ b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs @@ -323,7 +323,7 @@ namespace osu.Game.Screens.Ranking.Statistics private void updateBasalHeight() { - float newBasalHeight = DrawHeight == 0 ? 0 : DrawWidth / DrawHeight; + float newBasalHeight = DrawHeight > DrawWidth ? DrawWidth / DrawHeight : 1; if (newBasalHeight == basalHeight) return; @@ -353,7 +353,7 @@ namespace osu.Game.Screens.Ranking.Statistics for (int i = 0; i < values.Count; i++) { - boxOriginals[i].Y = offsetForValue(offsetValue) * BoundingBox.Height; + boxOriginals[i].Y = offsetForValue(offsetValue) * DrawHeight; boxOriginals[i].Height = heightForValue(values[i].Value); offsetValue -= values[i].Value; } From 4f298db066bba408f1aaaa8c5cfa76b29738cec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?= Date: Mon, 12 Sep 2022 16:25:29 +0900 Subject: [PATCH 073/142] fix(osu.Game): reset Y axis of the bars in hit distribution graph at the first drawing --- .../Ranking/Statistics/HitEventTimingDistributionGraph.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs index 5f3b2773ea..764237ef96 100644 --- a/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs +++ b/osu.Game/Screens/Ranking/Statistics/HitEventTimingDistributionGraph.cs @@ -278,7 +278,10 @@ namespace osu.Game.Screens.Ranking.Statistics updateBasalHeight(); foreach (var boxOriginal in boxOriginals) + { + boxOriginal.Y = 0; boxOriginal.Height = basalHeight; + } float offsetValue = 0; From bd3673baa9fe9fa5c5b77659e270c1e784938191 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 16:25:32 +0900 Subject: [PATCH 074/142] Fix being able to drag after already closing a notification --- osu.Game/Overlays/Notifications/Notification.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 2e4b0c430a..facf43128d 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -70,6 +70,8 @@ namespace osu.Game.Overlays.Notifications [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; + public override bool PropagatePositionalInputSubTree => base.PropagatePositionalInputSubTree && !WasClosed; + private bool isInToastTray; /// @@ -327,7 +329,7 @@ namespace osu.Game.Overlays.Notifications velocity.Y += (float)Clock.ElapsedFrameTime * 0.005f; Position += (float)Clock.ElapsedFrameTime * velocity; } - else + else if (Clock.ElapsedFrameTime > 0) { Vector2 change = (Position - lastPosition) / (float)Clock.ElapsedFrameTime; From a50617857176acbefb571e777429d9411cfdbff0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 16:39:46 +0900 Subject: [PATCH 075/142] Make bounding box shrink faster to allow for rapid flinging --- osu.Game/Overlays/Notifications/Notification.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index facf43128d..f83be0c446 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -278,8 +278,8 @@ namespace osu.Game.Overlays.Notifications { var childBounding = Children.First().BoundingBox; - if (X < 0) childBounding *= new Vector2(1, Math.Max(0, 1 + (X / 800))); - if (Y > 0) childBounding *= new Vector2(1, Math.Max(0, 1 - (Y / 800))); + if (X < 0) childBounding *= new Vector2(1, Math.Max(0, 1 + (X / 300))); + if (Y > 0) childBounding *= new Vector2(1, Math.Max(0, 1 - (Y / 200))); return childBounding; } From 5a02e1e7133e2be8f6eba967049b058a720957f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 16:46:45 +0900 Subject: [PATCH 076/142] Use padding instead of `FillFlow.Spacing` to avoid artifact during animation --- osu.Game/Overlays/NotificationOverlayToastTray.cs | 1 - osu.Game/Overlays/Notifications/Notification.cs | 3 +++ osu.Game/Overlays/Notifications/NotificationSection.cs | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlayToastTray.cs b/osu.Game/Overlays/NotificationOverlayToastTray.cs index e4d3864beb..1da05dab75 100644 --- a/osu.Game/Overlays/NotificationOverlayToastTray.cs +++ b/osu.Game/Overlays/NotificationOverlayToastTray.cs @@ -78,7 +78,6 @@ namespace osu.Game.Overlays { LayoutDuration = 150, LayoutEasing = Easing.OutQuart, - Spacing = new Vector2(3), RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, }, diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index f83be0c446..fe2f67973a 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -113,6 +113,9 @@ namespace osu.Game.Overlays.Notifications }, dragContainer = new DragContainer(this) { + // Use margin instead of FillFlow spacing to fix extra padding appearing when notification shrinks + // in height. + Padding = new MarginPadding { Vertical = 3f }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, }.WithChild(MainContent = new Container diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index d2e18a0cee..14bacca534 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -106,7 +106,6 @@ namespace osu.Game.Overlays.Notifications RelativeSizeAxes = Axes.X, LayoutDuration = 150, LayoutEasing = Easing.OutQuart, - Spacing = new Vector2(3), } }); } From d561fcb126689dade097acce3f4f425504680fff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 16:54:25 +0900 Subject: [PATCH 077/142] Don't trigger fling animation when `Close` is triggered by non-user action --- .../Online/TestSceneBeatmapDownloading.cs | 2 +- osu.Game/Database/ModelDownloader.cs | 2 +- osu.Game/Overlays/Notifications/Notification.cs | 13 ++++++------- .../Overlays/Notifications/NotificationSection.cs | 2 +- .../Overlays/Notifications/ProgressNotification.cs | 6 +++--- osu.Game/Updater/UpdateManager.cs | 6 +++--- 6 files changed, 15 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Online/TestSceneBeatmapDownloading.cs b/osu.Game.Tests/Online/TestSceneBeatmapDownloading.cs index e7a6e9a543..641c1ad523 100644 --- a/osu.Game.Tests/Online/TestSceneBeatmapDownloading.cs +++ b/osu.Game.Tests/Online/TestSceneBeatmapDownloading.cs @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Online { AddStep("download beatmap", () => beatmaps.Download(test_db_model)); - AddStep("cancel download from notification", () => recentNotification.Close()); + AddStep("cancel download from notification", () => recentNotification.Close(true)); AddUntilStep("is removed from download list", () => beatmaps.GetExistingDownload(test_db_model) == null); AddAssert("is notification cancelled", () => recentNotification.State == ProgressNotificationState.Cancelled); diff --git a/osu.Game/Database/ModelDownloader.cs b/osu.Game/Database/ModelDownloader.cs index a3678602d1..877c90a534 100644 --- a/osu.Game/Database/ModelDownloader.cs +++ b/osu.Game/Database/ModelDownloader.cs @@ -111,7 +111,7 @@ namespace osu.Game.Database { if (error is WebException webException && webException.Message == @"TooManyRequests") { - notification.Close(); + notification.Close(false); PostNotification?.Invoke(new TooManyDownloadsNotification()); } else diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index fe2f67973a..acb277d4b4 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -168,7 +168,7 @@ namespace osu.Game.Overlays.Notifications }, new CloseButton(CloseButtonIcon) { - Action = Close, + Action = () => Close(true), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, } @@ -214,7 +214,7 @@ namespace osu.Game.Overlays.Notifications // right click doesn't trigger OnClick so we need to handle here until that changes. if (e.Button != MouseButton.Left) { - Close(); + Close(true); return true; } @@ -227,7 +227,7 @@ namespace osu.Game.Overlays.Notifications if (e.Button == MouseButton.Left) Activated?.Invoke(); - Close(); + Close(true); return true; } @@ -245,13 +245,13 @@ namespace osu.Game.Overlays.Notifications public bool WasClosed; - public virtual void Close() + public virtual void Close(bool userTriggered) { if (WasClosed) return; WasClosed = true; - if (dragContainer.FlingLeft()) + if (userTriggered && dragContainer.FlingLeft()) this.FadeOut(600, Easing.In); else { @@ -272,7 +272,6 @@ namespace osu.Game.Overlays.Notifications public DragContainer(Notification notification) { this.notification = notification; - notification.Closed += () => FlingLeft(); } public override RectangleF BoundingBox @@ -363,7 +362,7 @@ namespace osu.Game.Overlays.Notifications flinging = true; ClearTransforms(); - notification.Close(); + notification.Close(true); return true; } diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index 14bacca534..16105f913f 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -112,7 +112,7 @@ namespace osu.Game.Overlays.Notifications private void clearAll() { - notifications.Children.ForEach(c => c.Close()); + notifications.Children.ForEach(c => c.Close(true)); } protected override void Update() diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index bdf6f704e5..55d6d5057a 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -142,7 +142,7 @@ namespace osu.Game.Overlays.Notifications case ProgressNotificationState.Completed: loadingSpinner.Hide(); attemptPostCompletion(); - base.Close(); + base.Close(false); break; } } @@ -235,12 +235,12 @@ namespace osu.Game.Overlays.Notifications }); } - public override void Close() + public override void Close(bool userTriggered) { switch (State) { case ProgressNotificationState.Cancelled: - base.Close(); + base.Close(userTriggered); break; case ProgressNotificationState.Active: diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 49009e9124..2ef6741ffd 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -148,7 +148,7 @@ namespace osu.Game.Updater StartDownload(); } - public override void Close() + public override void Close(bool userTriggered) { // cancelling updates is not currently supported by the underlying updater. // only allow dismissing for now. @@ -156,7 +156,7 @@ namespace osu.Game.Updater switch (State) { case ProgressNotificationState.Cancelled: - base.Close(); + base.Close(userTriggered); break; } } @@ -177,7 +177,7 @@ namespace osu.Game.Updater public void FailDownload() { State = ProgressNotificationState.Cancelled; - Close(); + Close(false); } } } From 91c415f29b693460123762b6e7023c15d32b856c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 16:58:20 +0900 Subject: [PATCH 078/142] Fix nullability oversight in `ManiaRulesetConfigManager` --- .../Configuration/ManiaRulesetConfigManager.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs index b30d72f2e2..d367c82ed8 100644 --- a/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs +++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Framework.Configuration.Tracking; using osu.Game.Configuration; @@ -13,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.Configuration { public class ManiaRulesetConfigManager : RulesetConfigManager { - public ManiaRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null) + public ManiaRulesetConfigManager(SettingsStore? settings, RulesetInfo ruleset, int? variant = null) : base(settings, ruleset, variant) { } From 89e128c0f3469e31d80ef2e165b324d4cbe80385 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 17:35:56 +0900 Subject: [PATCH 079/142] Fix bindable event binds to `HitObject` directly --- .../Blueprints/Sliders/Components/PathControlPointPiece.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 92d83d900e..ab4b492767 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -53,6 +54,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private IBindable sliderPosition; private IBindable sliderScale; + [UsedImplicitly] + private readonly IBindable sliderVersion; + public PathControlPointPiece(Slider slider, PathControlPoint controlPoint) { this.slider = slider; @@ -61,7 +65,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components // we don't want to run the path type update on construction as it may inadvertently change the slider. cachePoints(slider); - slider.Path.Version.BindValueChanged(_ => + sliderVersion = slider.Path.Version.GetBoundCopy(); + sliderVersion.BindValueChanged(_ => { cachePoints(slider); updatePathType(); From 58c1ea03215bc894bb770e2864d3b33c869d19db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 17:51:15 +0900 Subject: [PATCH 080/142] Fix potential crash when opening changelog overlay if entry has no URL --- osu.Game/Overlays/Changelog/ChangelogEntry.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogEntry.cs b/osu.Game/Overlays/Changelog/ChangelogEntry.cs index fdeba3f304..4d034007b1 100644 --- a/osu.Game/Overlays/Changelog/ChangelogEntry.cs +++ b/osu.Game/Overlays/Changelog/ChangelogEntry.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using System.Diagnostics; using System.Net; using System.Text.RegularExpressions; using osu.Framework.Allocation; @@ -93,7 +94,7 @@ namespace osu.Game.Overlays.Changelog t.Colour = entryColour; }); - if (!string.IsNullOrEmpty(entry.Repository)) + if (!string.IsNullOrEmpty(entry.Repository) && !string.IsNullOrEmpty(entry.GithubUrl)) addRepositoryReference(title, entryColour); if (entry.GithubUser != null) @@ -104,17 +105,22 @@ namespace osu.Game.Overlays.Changelog private void addRepositoryReference(LinkFlowContainer title, Color4 entryColour) { + Debug.Assert(!string.IsNullOrEmpty(entry.Repository)); + Debug.Assert(!string.IsNullOrEmpty(entry.GithubUrl)); + title.AddText(" (", t => { t.Font = fontLarge; t.Colour = entryColour; }); + title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", entry.GithubUrl, t => { t.Font = fontLarge; t.Colour = entryColour; }); + title.AddText(")", t => { t.Font = fontLarge; From 27d4016ccb360991c37750bf5fe67e193b8fa038 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 12 Sep 2022 18:32:14 +0900 Subject: [PATCH 081/142] Add `ScoreProcessor.ComputeAccuracy()` --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 64d5639133..7456ce06bd 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -272,7 +272,24 @@ namespace osu.Game.Rulesets.Scoring } /// - /// Computes the total score of a given finalised . This should be used when a score is known to be complete. + /// Computes the accuracy of a given . + /// + /// The to compute the total score of. + /// The score's accuracy. + [Pure] + public double ComputeAccuracy(ScoreInfo scoreInfo) + { + if (!ruleset.RulesetInfo.Equals(scoreInfo.Ruleset)) + throw new ArgumentException($"Unexpected score ruleset. Expected \"{ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\"."); + + // We only extract scoring values from the score's statistics. This is because accuracy is always relative to the point of pass or fail rather than relative to the whole beatmap. + extractScoringValues(scoreInfo.Statistics, out var current, out var maximum); + + return maximum.BaseScore > 0 ? current.BaseScore / maximum.BaseScore : 1; + } + + /// + /// Computes the total score of a given . /// /// /// Does not require to have been called before use. From e29f5cb4568019d031c85394aaca8b648e471a78 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 18:53:37 +0900 Subject: [PATCH 082/142] Fix new-style legacy skins with animated judgements not adding correct transforms Closes #16173. --- osu.Game/Skinning/LegacyJudgementPieceNew.cs | 2 +- osu.Game/Skinning/LegacyJudgementPieceOld.cs | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/LegacyJudgementPieceNew.cs b/osu.Game/Skinning/LegacyJudgementPieceNew.cs index b87fc9acb5..2cb055d8ba 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceNew.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceNew.cs @@ -56,7 +56,7 @@ namespace osu.Game.Skinning if (result != HitResult.Miss) { //new judgement shows old as a temporary effect - AddInternal(temporaryOldStyle = new LegacyJudgementPieceOld(result, createMainDrawable, 1.05f) + AddInternal(temporaryOldStyle = new LegacyJudgementPieceOld(result, createMainDrawable, 1.05f, true) { Blending = BlendingParameters.Additive, Anchor = Anchor.Centre, diff --git a/osu.Game/Skinning/LegacyJudgementPieceOld.cs b/osu.Game/Skinning/LegacyJudgementPieceOld.cs index c47f46ed2b..7e7f0f027d 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceOld.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceOld.cs @@ -19,10 +19,13 @@ namespace osu.Game.Skinning private readonly float finalScale; - public LegacyJudgementPieceOld(HitResult result, Func createMainDrawable, float finalScale = 1f) + private readonly bool allowTransforms; + + public LegacyJudgementPieceOld(HitResult result, Func createMainDrawable, float finalScale = 1f, bool allowTransforms = false) { this.result = result; this.finalScale = finalScale; + this.allowTransforms = allowTransforms; AutoSizeAxes = Axes.Both; Origin = Anchor.Centre; @@ -43,8 +46,8 @@ namespace osu.Game.Skinning this.FadeInFromZero(fade_in_length); this.Delay(fade_out_delay).FadeOut(fade_out_length); - // legacy judgements don't play any transforms if they are an animation. - if (animation?.FrameCount > 1) + // legacy judgements don't play any transforms if they are an animation.... UNLESS they are the temporary displayed judgement from new piece. + if (!allowTransforms && animation?.FrameCount > 1) return; switch (result) From 4ee3e8f087e14631622ffca7f446485f50d4982c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 18:57:18 +0900 Subject: [PATCH 083/142] Don't play fling animation when activating a notification --- osu.Game/Overlays/Notifications/Notification.cs | 6 +++--- osu.Game/Overlays/Notifications/ProgressNotification.cs | 4 ++-- osu.Game/Updater/UpdateManager.cs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index acb277d4b4..887d729de7 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -227,7 +227,7 @@ namespace osu.Game.Overlays.Notifications if (e.Button == MouseButton.Left) Activated?.Invoke(); - Close(true); + Close(false); return true; } @@ -245,13 +245,13 @@ namespace osu.Game.Overlays.Notifications public bool WasClosed; - public virtual void Close(bool userTriggered) + public virtual void Close(bool runFlingAnimation) { if (WasClosed) return; WasClosed = true; - if (userTriggered && dragContainer.FlingLeft()) + if (runFlingAnimation && dragContainer.FlingLeft()) this.FadeOut(600, Easing.In); else { diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 55d6d5057a..c4d402e5b9 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -235,12 +235,12 @@ namespace osu.Game.Overlays.Notifications }); } - public override void Close(bool userTriggered) + public override void Close(bool runFlingAnimation) { switch (State) { case ProgressNotificationState.Cancelled: - base.Close(userTriggered); + base.Close(runFlingAnimation); break; case ProgressNotificationState.Active: diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 2ef6741ffd..100464029b 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -148,7 +148,7 @@ namespace osu.Game.Updater StartDownload(); } - public override void Close(bool userTriggered) + public override void Close(bool runFlingAnimation) { // cancelling updates is not currently supported by the underlying updater. // only allow dismissing for now. @@ -156,7 +156,7 @@ namespace osu.Game.Updater switch (State) { case ProgressNotificationState.Cancelled: - base.Close(userTriggered); + base.Close(runFlingAnimation); break; } } From d92e000fe6db82e258ec9a68e6d0fe9885c99d75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 19:00:03 +0900 Subject: [PATCH 084/142] Fix flinging a notification not correctly running `Close` --- osu.Game/Overlays/Notifications/Notification.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 887d729de7..6c1b43c3b5 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -254,11 +254,9 @@ namespace osu.Game.Overlays.Notifications if (runFlingAnimation && dragContainer.FlingLeft()) this.FadeOut(600, Easing.In); else - { - Closed?.Invoke(); this.FadeOut(100); - } + Closed?.Invoke(); Expire(); } @@ -311,7 +309,7 @@ namespace osu.Game.Overlays.Notifications protected override void OnDragEnd(DragEndEvent e) { if (Rotation < -10 || velocity.X < -0.3f) - FlingLeft(); + notification.Close(true); else ResetPosition(); @@ -362,7 +360,6 @@ namespace osu.Game.Overlays.Notifications flinging = true; ClearTransforms(); - notification.Close(true); return true; } From f56f6545c04cd3f4f56a7398a49998ab8fe13da7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 19:06:09 +0900 Subject: [PATCH 085/142] Add test coverage of flinging --- .../TestSceneNotificationOverlay.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index 8504da69a7..3d13e98865 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -48,6 +48,40 @@ namespace osu.Game.Tests.Visual.UserInterface notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; }; }); + [Test] + public void TestDismissWithoutActivationFling() + { + bool activated = false; + SimpleNotification notification = null!; + + AddStep("post", () => + { + activated = false; + notificationOverlay.Post(notification = new SimpleNotification + { + Text = @"Welcome to osu!. Enjoy your stay!", + Activated = () => activated = true, + }); + }); + + AddStep("start drag", () => + { + InputManager.MoveMouseTo(notification.ChildrenOfType().Single()); + InputManager.PressButton(MouseButton.Left); + InputManager.MoveMouseTo(notification.ChildrenOfType().Single().ScreenSpaceDrawQuad.Centre + new Vector2(-500, 0)); + }); + + AddStep("fling away", () => + { + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddUntilStep("wait for closed", () => notification.WasClosed); + AddAssert("was not activated", () => !activated); + AddStep("reset mouse position", () => InputManager.MoveMouseTo(Vector2.Zero)); + AddAssert("unread count zero", () => notificationOverlay.UnreadCount.Value == 0); + } + [Test] public void TestDismissWithoutActivationCloseButton() { @@ -75,6 +109,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("wait for closed", () => notification.WasClosed); AddAssert("was not activated", () => !activated); AddStep("reset mouse position", () => InputManager.MoveMouseTo(Vector2.Zero)); + AddAssert("unread count zero", () => notificationOverlay.UnreadCount.Value == 0); } [Test] From 431e84f99207fb0a429b85ad0a61ab7f7f55a206 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 19:09:24 +0900 Subject: [PATCH 086/142] Cancel in progress CI runs when new commits are pushed Same as https://github.com/ppy/osu-web/pull/9268. Should hopefully help with organisation concurrent limits. --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef729a779f..082e0d247c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,8 @@ on: [push, pull_request] name: Continuous Integration +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: inspect-code: From 837cc760195cd5c0c4aa5c445657a42151b8b7ca Mon Sep 17 00:00:00 2001 From: OliBomby Date: Mon, 12 Sep 2022 12:50:16 +0200 Subject: [PATCH 087/142] Create TestSceneSelectionBlueprintDeselection.cs --- .../TestSceneSelectionBlueprintDeselection.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSelectionBlueprintDeselection.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSelectionBlueprintDeselection.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSelectionBlueprintDeselection.cs new file mode 100644 index 0000000000..fdc05e9456 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSelectionBlueprintDeselection.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Osu.Tests.Editor +{ + public class TestSceneSelectionBlueprintDeselection : TestSceneOsuEditor + { + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); + + [Test] + public void TestSingleDeleteAtSameTime() + { + HitCircle? circle1 = null; + HitCircle? circle2 = null; + + AddStep("add two circles at the same time", () => + { + circle1 = new HitCircle(); + circle2 = new HitCircle(); + EditorClock.Seek(0); + EditorBeatmap.Add(circle1); + EditorBeatmap.Add(circle2); + EditorBeatmap.SelectedHitObjects.Add(circle1); + EditorBeatmap.SelectedHitObjects.Add(circle2); + }); + + AddStep("delete the first circle", () => EditorBeatmap.Remove(circle1)); + } + } +} From f31deaef7ca8dd0f7defd794c77f8a8b6f0f718c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 19:51:49 +0900 Subject: [PATCH 088/142] Simplify skin bindable flow Now, nothing touches the configuration apart from `OsuGame`, making everything else flow better and avoid weird cyclic set bugs. Closes https://github.com/ppy/osu/issues/20234. --- osu.Game/OsuGame.cs | 24 ++----- .../Overlays/Settings/Sections/SkinSection.cs | 62 ++++--------------- osu.Game/Skinning/SkinManager.cs | 20 +++++- 3 files changed, 36 insertions(+), 70 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 9e2384322a..0d115f62fe 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -56,14 +56,12 @@ using osu.Game.Screens.Menu; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Screens.Select; -using osu.Game.Skinning; using osu.Game.Skinning.Editor; using osu.Game.Updater; using osu.Game.Users; using osu.Game.Utils; using osuTK.Graphics; using Sentry; -using Logger = osu.Framework.Logging.Logger; namespace osu.Game { @@ -294,25 +292,13 @@ namespace osu.Game Ruleset.ValueChanged += r => configRuleset.Value = r.NewValue.ShortName; - // bind config int to database SkinInfo configSkin = LocalConfig.GetBindable(OsuSetting.Skin); + + // Transfer skin from config to realm instance once on startup. + SkinManager.SetSkinFromConfiguration(configSkin.Value); + + // Transfer any runtime changes back to configuration file. SkinManager.CurrentSkinInfo.ValueChanged += skin => configSkin.Value = skin.NewValue.ID.ToString(); - configSkin.ValueChanged += skinId => - { - Live skinInfo = null; - - if (Guid.TryParse(skinId.NewValue, out var guid)) - skinInfo = SkinManager.Query(s => s.ID == guid); - - if (skinInfo == null) - { - if (guid == SkinInfo.CLASSIC_SKIN) - skinInfo = DefaultLegacySkin.CreateInfo().ToLiveUnmanaged(); - } - - SkinManager.CurrentSkinInfo.Value = skinInfo ?? DefaultSkin.CreateInfo().ToLiveUnmanaged(); - }; - configSkin.TriggerChange(); IsActive.BindValueChanged(active => updateActiveState(active.NewValue), true); diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index d23ef7e3e7..13249e37ca 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -14,7 +14,6 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Framework.Platform; -using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; @@ -36,9 +35,6 @@ namespace osu.Game.Overlays.Settings.Sections Icon = FontAwesome.Solid.PaintBrush }; - private readonly Bindable> dropdownBindable = new Bindable> { Default = DefaultSkin.CreateInfo().ToLiveUnmanaged() }; - private readonly Bindable configBindable = new Bindable(); - private static readonly Live random_skin_info = new SkinInfo { ID = SkinInfo.RANDOM_SKIN, @@ -56,13 +52,14 @@ namespace osu.Game.Overlays.Settings.Sections private IDisposable realmSubscription; [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuConfigManager config, [CanBeNull] SkinEditorOverlay skinEditor) + private void load([CanBeNull] SkinEditorOverlay skinEditor) { Children = new Drawable[] { skinDropdown = new SkinSettingsDropdown { LabelText = SkinSettingsStrings.CurrentSkin, + Current = skins.CurrentSkinInfo, Keywords = new[] { @"skins" } }, new SettingsButton @@ -73,47 +70,30 @@ namespace osu.Game.Overlays.Settings.Sections new ExportSkinButton(), new DeleteSkinButton(), }; - - config.BindWith(OsuSetting.Skin, configBindable); } protected override void LoadComplete() { base.LoadComplete(); - skinDropdown.Current = dropdownBindable; + skinDropdown.Current = skins.CurrentSkinInfo; realmSubscription = realm.RegisterForNotifications(_ => realm.Realm.All() .Where(s => !s.DeletePending) .OrderByDescending(s => s.Protected) // protected skins should be at the top. .ThenBy(s => s.Name, StringComparer.OrdinalIgnoreCase), skinsChanged); - configBindable.BindValueChanged(_ => Scheduler.AddOnce(updateSelectedSkinFromConfig)); - - dropdownBindable.BindValueChanged(dropdownSelectionChanged); - } - - private void dropdownSelectionChanged(ValueChangedEvent> skin) - { - // Only handle cases where it's clear the user has intent to change skins. - if (skin.OldValue == null) return; - - if (skin.NewValue.Equals(random_skin_info)) + skinDropdown.Current.BindValueChanged(skin => { - var skinBefore = skins.CurrentSkinInfo.Value; - - skins.SelectRandomSkin(); - - if (skinBefore == skins.CurrentSkinInfo.Value) + if (skin.NewValue == random_skin_info) { - // the random selection didn't change the skin, so we should manually update the dropdown to match. - dropdownBindable.Value = skins.CurrentSkinInfo.Value; + // before selecting random, set the skin back to the previous selection. + // this is done because at this point it will be random_skin_info, and would + // cause SelectRandomSkin to be unable to skip the previous selection. + skins.CurrentSkinInfo.Value = skin.OldValue; + skins.SelectRandomSkin(); } - - return; - } - - configBindable.Value = skin.NewValue.ID.ToString(); + }); } private void skinsChanged(IRealmCollection sender, ChangeSet changes, Exception error) @@ -132,25 +112,7 @@ namespace osu.Game.Overlays.Settings.Sections dropdownItems.Add(skin.ToLive(realm)); dropdownItems.Insert(protectedCount, random_skin_info); - Schedule(() => - { - skinDropdown.Items = dropdownItems; - - updateSelectedSkinFromConfig(); - }); - } - - private void updateSelectedSkinFromConfig() - { - if (!skinDropdown.Items.Any()) - return; - - Live skin = null; - - if (Guid.TryParse(configBindable.Value, out var configId)) - skin = skinDropdown.Items.FirstOrDefault(s => s.ID == configId); - - dropdownBindable.Value = skin ?? skinDropdown.Items.First(); + Schedule(() => skinDropdown.Items = dropdownItems); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index f677cebe51..7ffea3b54f 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -119,7 +119,9 @@ namespace osu.Game.Skinning Realm.Run(r => { // choose from only user skins, removing the current selection to ensure a new one is chosen. - var randomChoices = r.All().Where(s => !s.DeletePending && s.ID != CurrentSkinInfo.Value.ID).ToArray(); + var randomChoices = r.All() + .Where(s => !s.DeletePending && s.ID != CurrentSkinInfo.Value.ID) + .ToArray(); if (randomChoices.Length == 0) { @@ -297,5 +299,21 @@ namespace osu.Game.Skinning Delete(items.ToList(), silent); }); } + + public void SetSkinFromConfiguration(string guidString) + { + Live skinInfo = null; + + if (Guid.TryParse(guidString, out var guid)) + skinInfo = Query(s => s.ID == guid); + + if (skinInfo == null) + { + if (guid == SkinInfo.CLASSIC_SKIN) + skinInfo = DefaultLegacySkin.SkinInfo; + } + + CurrentSkinInfo.Value = skinInfo ?? DefaultSkin.SkinInfo; + } } } From 2b4b14ca9990a4626fefcbecf624ca2d97bf4244 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 20:06:52 +0900 Subject: [PATCH 089/142] Fix `SongProgress` invalidating too often This regressed with https://github.com/ppy/osu/pull/19556. Rather than try and figure whether that new container needs to handle size differently, this is a simple solution. Height was taken from a runtime check (maxes out at about 14.5). Closes #20235. --- osu.Game/Screens/Play/HUD/SongProgressInfo.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/SongProgressInfo.cs b/osu.Game/Screens/Play/HUD/SongProgressInfo.cs index d0eb8f8ca1..b3d5066a9e 100644 --- a/osu.Game/Screens/Play/HUD/SongProgressInfo.cs +++ b/osu.Game/Screens/Play/HUD/SongProgressInfo.cs @@ -47,7 +47,10 @@ namespace osu.Game.Screens.Play.HUD if (clock != null) gameplayClock = clock; - AutoSizeAxes = Axes.Y; + // Lock height so changes in text autosize (if character height changes) + // don't cause parent invalidation. + Height = 14; + Children = new Drawable[] { new Container From 88107108eea0c440babac6e69c1367274c509e18 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Sep 2022 20:18:40 +0900 Subject: [PATCH 090/142] Add ability to flick notifications to the right to store for later --- .../TestSceneNotificationOverlay.cs | 37 +++++++++++++++++++ .../Overlays/NotificationOverlayToastTray.cs | 5 +++ .../Overlays/Notifications/Notification.cs | 4 ++ 3 files changed, 46 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index 3d13e98865..e978b57ba4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -48,6 +48,43 @@ namespace osu.Game.Tests.Visual.UserInterface notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; }; }); + [Test] + public void TestForwardWithFlingRight() + { + bool activated = false; + SimpleNotification notification = null!; + + AddStep("post", () => + { + activated = false; + notificationOverlay.Post(notification = new SimpleNotification + { + Text = @"Welcome to osu!. Enjoy your stay!", + Activated = () => activated = true, + }); + }); + + AddStep("start drag", () => + { + InputManager.MoveMouseTo(notification.ChildrenOfType().Single()); + InputManager.PressButton(MouseButton.Left); + InputManager.MoveMouseTo(notification.ChildrenOfType().Single().ScreenSpaceDrawQuad.Centre + new Vector2(500, 0)); + }); + + AddStep("fling away", () => + { + InputManager.ReleaseButton(MouseButton.Left); + }); + + AddAssert("was not closed", () => !notification.WasClosed); + AddAssert("was not activated", () => !activated); + AddAssert("is not read", () => !notification.Read); + AddAssert("is not toast", () => !notification.IsInToastTray); + + AddStep("reset mouse position", () => InputManager.MoveMouseTo(Vector2.Zero)); + AddAssert("unread count one", () => notificationOverlay.UnreadCount.Value == 1); + } + [Test] public void TestDismissWithoutActivationFling() { diff --git a/osu.Game/Overlays/NotificationOverlayToastTray.cs b/osu.Game/Overlays/NotificationOverlayToastTray.cs index 1da05dab75..e3f8734581 100644 --- a/osu.Game/Overlays/NotificationOverlayToastTray.cs +++ b/osu.Game/Overlays/NotificationOverlayToastTray.cs @@ -100,6 +100,8 @@ namespace osu.Game.Overlays { ++runningDepth; + notification.ForwardToOverlay = () => forwardNotification(notification); + int depth = notification.DisplayOnTop ? -runningDepth : runningDepth; toastFlow.Insert(depth, notification); @@ -130,6 +132,9 @@ namespace osu.Game.Overlays private void forwardNotification(Notification notification) { + if (!notification.IsInToastTray) + return; + Debug.Assert(notification.Parent == toastFlow); // Temporarily remove from flow so we can animate the position off to the right. diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 6c1b43c3b5..885bb2fc6c 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -42,6 +42,8 @@ namespace osu.Game.Overlays.Notifications /// public Func? Activated; + public Action? ForwardToOverlay { get; set; } + /// /// Should we show at the top of our section on display? /// @@ -310,6 +312,8 @@ namespace osu.Game.Overlays.Notifications { if (Rotation < -10 || velocity.X < -0.3f) notification.Close(true); + else if (X > 30 || velocity.X > 0.3f) + notification.ForwardToOverlay?.Invoke(); else ResetPosition(); From 8cbc0502aea885ce2468ccfda0b457297382ead9 Mon Sep 17 00:00:00 2001 From: ansel <79257300125@ya.ru> Date: Mon, 12 Sep 2022 15:51:18 +0300 Subject: [PATCH 091/142] Inline `CalculateEffect` --- osu.Game/Overlays/Mods/ModsEffectDisplay.cs | 27 ++++----------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs index 3b112fdbd3..eed3181e49 100644 --- a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs +++ b/osu.Game/Overlays/Mods/ModsEffectDisplay.cs @@ -49,22 +49,9 @@ namespace osu.Game.Overlays.Mods /// protected abstract LocalisableString Label { get; } - /// - /// Calculates, does current value increase difficulty or decrease it. - /// - /// Value to calculate for. Will arrive from bindable once it changes. - /// Effect of the value. - /// - /// Default implementation compares the value with ., bigger value counts as difficulty increase. - /// - protected virtual ModEffect CalculateEffect(double value) - { - return CalculateForSign(value.CompareTo(Current.Default)); - } - protected virtual float ValueAreaWidth => 56; - protected virtual string CounterFormat => "N0"; + protected virtual string CounterFormat => @"N0"; protected override Container Content => content; @@ -159,7 +146,7 @@ namespace osu.Game.Overlays.Mods { Current.BindValueChanged(e => { - var effect = CalculateEffect(e.NewValue); + var effect = CalculateEffectForComparison(e.NewValue.CompareTo(Current.Default)); setColours(effect); }, true); } @@ -192,14 +179,12 @@ namespace osu.Game.Overlays.Mods } } - #region Quick implementations for CalculateEffect - /// /// Converts signed integer into . Negative values are counted as difficulty reduction, positive as increase. /// - /// Value to convert. Can be obtained from CompareTo. - /// Effect. - protected ModEffect CalculateForSign(int comparison) + /// Value to convert. Will arrive from comparison between bindable once it changes and it's . + /// Effect of the value. + protected virtual ModEffect CalculateEffectForComparison(int comparison) { if (comparison == 0) return ModEffect.NotChanged; @@ -209,8 +194,6 @@ namespace osu.Game.Overlays.Mods return ModEffect.DifficultyIncrease; } - #endregion - protected enum ModEffect { NotChanged, From 8400de4b2ea1922138fc0d581f0ac8c3a531bd12 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Mon, 12 Sep 2022 17:50:11 +0200 Subject: [PATCH 092/142] invoking hitobject updated before invoking removed --- .../TestSceneSelectionBlueprintDeselection.cs | 35 +++++++++++++-- osu.Game/Screens/Edit/EditorBeatmap.cs | 45 +++++++++++++------ 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSelectionBlueprintDeselection.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSelectionBlueprintDeselection.cs index fdc05e9456..b00582d6f3 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSelectionBlueprintDeselection.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSelectionBlueprintDeselection.cs @@ -1,10 +1,12 @@ // 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 NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Tests.Beatmaps; +using osuTK.Input; namespace osu.Game.Rulesets.Osu.Tests.Editor { @@ -16,13 +18,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor public void TestSingleDeleteAtSameTime() { HitCircle? circle1 = null; - HitCircle? circle2 = null; AddStep("add two circles at the same time", () => { - circle1 = new HitCircle(); - circle2 = new HitCircle(); EditorClock.Seek(0); + circle1 = new HitCircle(); + var circle2 = new HitCircle(); EditorBeatmap.Add(circle1); EditorBeatmap.Add(circle2); EditorBeatmap.SelectedHitObjects.Add(circle1); @@ -31,5 +32,33 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("delete the first circle", () => EditorBeatmap.Remove(circle1)); } + + [Test] + public void TestBigStackDeleteAtSameTime() + { + AddStep("add 20 circles at the same time", () => + { + EditorClock.Seek(0); + + for (int i = 0; i < 20; i++) + { + EditorBeatmap.Add(new HitCircle()); + } + }); + + AddStep("select half of the circles", () => + { + foreach (var hitObject in EditorBeatmap.HitObjects.SkipLast(10).Reverse()) + { + EditorBeatmap.SelectedHitObjects.Add(hitObject); + } + }); + + AddStep("delete all selected circles", () => + { + InputManager.PressKey(Key.Delete); + InputManager.ReleaseKey(Key.Delete); + }); + } } } diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 8aa754b305..95b848fec0 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -16,6 +16,7 @@ using osu.Game.Beatmaps.Legacy; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Skinning; namespace osu.Game.Screens.Edit @@ -79,7 +80,7 @@ namespace osu.Game.Screens.Edit private readonly IBeatmapProcessor beatmapProcessor; - private readonly Dictionary> startTimeBindables = new Dictionary>(); + private readonly Dictionary> hitObjectBindables = new Dictionary>(); public EditorBeatmap(IBeatmap playableBeatmap, ISkin beatmapSkin = null, BeatmapInfo beatmapInfo = null) { @@ -97,7 +98,7 @@ namespace osu.Game.Screens.Edit beatmapProcessor = playableBeatmap.BeatmapInfo.Ruleset.CreateInstance().CreateBeatmapProcessor(PlayableBeatmap); foreach (var obj in HitObjects) - trackStartTime(obj); + trackBindables(obj); } /// @@ -222,7 +223,7 @@ namespace osu.Game.Screens.Edit /// The to insert. public void Insert(int index, HitObject hitObject) { - trackStartTime(hitObject); + trackBindables(hitObject); mutableHitObjects.Insert(index, hitObject); @@ -299,9 +300,9 @@ namespace osu.Game.Screens.Edit mutableHitObjects.RemoveAt(index); - var bindable = startTimeBindables[hitObject]; - bindable.UnbindAll(); - startTimeBindables.Remove(hitObject); + var bindables = hitObjectBindables[hitObject]; + bindables.ForEach(b => b.UnbindAll()); + hitObjectBindables.Remove(hitObject); BeginChange(); batchPendingDeletes.Add(hitObject); @@ -325,25 +326,25 @@ namespace osu.Game.Screens.Edit beatmapProcessor?.PreProcess(); + foreach (var h in batchPendingUpdates) processHitObject(h); foreach (var h in batchPendingDeletes) processHitObject(h); foreach (var h in batchPendingInserts) processHitObject(h); - foreach (var h in batchPendingUpdates) processHitObject(h); beatmapProcessor?.PostProcess(); // callbacks may modify the lists so let's be safe about it + var updates = batchPendingUpdates.ToArray(); + batchPendingUpdates.Clear(); + var deletes = batchPendingDeletes.ToArray(); batchPendingDeletes.Clear(); var inserts = batchPendingInserts.ToArray(); batchPendingInserts.Clear(); - var updates = batchPendingUpdates.ToArray(); - batchPendingUpdates.Clear(); - + foreach (var h in updates) HitObjectUpdated?.Invoke(h); foreach (var h in deletes) HitObjectRemoved?.Invoke(h); foreach (var h in inserts) HitObjectAdded?.Invoke(h); - foreach (var h in updates) HitObjectUpdated?.Invoke(h); updateInProgress.Value = false; } @@ -355,10 +356,12 @@ namespace osu.Game.Screens.Edit private void processHitObject(HitObject hitObject) => hitObject.ApplyDefaults(ControlPointInfo, PlayableBeatmap.Difficulty); - private void trackStartTime(HitObject hitObject) + private void trackBindables(HitObject hitObject) { - startTimeBindables[hitObject] = hitObject.StartTimeBindable.GetBoundCopy(); - startTimeBindables[hitObject].ValueChanged += _ => + var bindables = new List(3); + + var startTimeBindable = hitObject.StartTimeBindable.GetBoundCopy(); + startTimeBindable.ValueChanged += _ => { // For now we'll remove and re-add the hitobject. This is not optimal and can be improved if required. mutableHitObjects.Remove(hitObject); @@ -368,6 +371,20 @@ namespace osu.Game.Screens.Edit Update(hitObject); }; + bindables.Add(startTimeBindable); + + if (hitObject is IHasComboInformation hasCombo) + { + var comboIndexBindable = hasCombo.ComboIndexBindable.GetBoundCopy(); + comboIndexBindable.ValueChanged += _ => Update(hitObject); + bindables.Add(comboIndexBindable); + + var indexInCurrentComboBindable = hasCombo.IndexInCurrentComboBindable.GetBoundCopy(); + indexInCurrentComboBindable.ValueChanged += _ => Update(hitObject); + bindables.Add(indexInCurrentComboBindable); + } + + hitObjectBindables[hitObject] = bindables; } private int findInsertionIndex(IReadOnlyList list, double startTime) From a5b962d9a3bc55beac3b4f89aa1544879adf6c20 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Sep 2022 01:37:23 +0300 Subject: [PATCH 093/142] Add failing test case --- .../Online/TestSceneBeatmapListingOverlay.cs | 55 ++++++++++++++++--- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 8ef120d252..2c9b34e4a7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -35,6 +35,8 @@ namespace osu.Game.Tests.Visual.Online private OsuConfigManager localConfig; + private bool returnCursorOnResponse; + [BackgroundDependencyLoader] private void load() { @@ -61,6 +63,7 @@ namespace osu.Game.Tests.Visual.Online searchBeatmapSetsRequest.TriggerSuccess(new SearchBeatmapSetsResponse { BeatmapSets = setsForResponse, + Cursor = returnCursorOnResponse ? new Cursor() : null, }); return true; @@ -106,7 +109,7 @@ namespace osu.Game.Tests.Visual.Online { AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); - AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateAPIBeatmapSet(Ruleset.Value), 100).ToArray())); + AddStep("show many results", () => fetchFor(getManyBeatmaps(100).ToArray())); AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any(d => d.IsPresent)); @@ -127,10 +130,10 @@ namespace osu.Game.Tests.Visual.Online { AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); - AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateAPIBeatmapSet(Ruleset.Value), 100).ToArray())); + AddStep("show many results", () => fetchFor(getManyBeatmaps(100).ToArray())); assertAllCardsOfType(100); - AddStep("show more results", () => fetchFor(Enumerable.Repeat(CreateAPIBeatmapSet(Ruleset.Value), 30).ToArray())); + AddStep("show more results", () => fetchFor(getManyBeatmaps(30).ToArray())); assertAllCardsOfType(30); } @@ -139,7 +142,7 @@ namespace osu.Game.Tests.Visual.Online { AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); - AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateAPIBeatmapSet(Ruleset.Value), 100).ToArray())); + AddStep("show many results", () => fetchFor(getManyBeatmaps(100).ToArray())); assertAllCardsOfType(100); setCardSize(BeatmapCardSize.Extra, viaConfig); @@ -161,7 +164,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("fetch for 0 beatmaps", () => fetchFor()); placeholderShown(); - AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateAPIBeatmapSet(Ruleset.Value), 100).ToArray())); + AddStep("show many results", () => fetchFor(getManyBeatmaps(100).ToArray())); AddUntilStep("wait for loaded", () => this.ChildrenOfType().Count() == 100); AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any(d => d.IsPresent)); @@ -180,6 +183,32 @@ namespace osu.Game.Tests.Visual.Online }); } + /// + /// During pagination, the first beatmap of the second page may be a duplicate of the last beatmap from the previous page. + /// This is currently the case with osu!web API due to ES relevance score's presence in the response cursor. + /// See: https://github.com/ppy/osu-web/issues/9270 + /// + [Test] + public void TestDuplicatedBeatmapOnlyShowsOnce() + { + APIBeatmapSet beatmapSet = null; + + AddStep("show many results", () => + { + beatmapSet = CreateAPIBeatmapSet(Ruleset.Value); + beatmapSet.Title = "last beatmap of first page"; + + fetchFor(getManyBeatmaps(49).Append(beatmapSet).ToArray(), true); + }); + AddUntilStep("wait for loaded", () => this.ChildrenOfType().Count() == 50); + + AddStep("set next page", () => setSearchResponse(getManyBeatmaps(49).Prepend(beatmapSet).ToArray(), false)); + AddStep("scroll to end", () => overlay.ChildrenOfType().Single().ScrollToEnd()); + AddUntilStep("wait for loaded", () => this.ChildrenOfType().Count() == 99); + + AddAssert("beatmap not duplicated", () => overlay.ChildrenOfType().Count(c => c.BeatmapSet.Equals(beatmapSet)) == 1); + } + [Test] public void TestUserWithoutSupporterUsesSupporterOnlyFiltersWithoutResults() { @@ -336,15 +365,25 @@ namespace osu.Game.Tests.Visual.Online private static int searchCount; - private void fetchFor(params APIBeatmapSet[] beatmaps) + private APIBeatmapSet[] getManyBeatmaps(int count) => Enumerable.Range(0, count).Select(_ => CreateAPIBeatmapSet(Ruleset.Value)).ToArray(); + + private void fetchFor(params APIBeatmapSet[] beatmaps) => fetchFor(beatmaps, false); + + private void fetchFor(APIBeatmapSet[] beatmaps, bool hasNextPage) { - setsForResponse.Clear(); - setsForResponse.AddRange(beatmaps); + setSearchResponse(beatmaps, hasNextPage); // trigger arbitrary change for fetching. searchControl.Query.Value = $"search {searchCount++}"; } + private void setSearchResponse(APIBeatmapSet[] beatmaps, bool hasNextPage) + { + setsForResponse.Clear(); + setsForResponse.AddRange(beatmaps); + returnCursorOnResponse = hasNextPage; + } + private void setRankAchievedFilter(ScoreRank[] ranks) { AddStep($"set Rank Achieved filter to [{string.Join(',', ranks)}]", () => From 07f577a0c67314035588ba33856e135643632a02 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Sep 2022 01:38:52 +0300 Subject: [PATCH 094/142] Fix beatmap listing potentially showing duplicate beatmap cards --- osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs | 13 ++++++++++++- osu.Game/Overlays/BeatmapListingOverlay.cs | 4 +++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index b9e0a4e6cb..70312a1535 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -14,7 +14,7 @@ using osu.Game.Overlays; namespace osu.Game.Beatmaps.Drawables.Cards { - public abstract class BeatmapCard : OsuClickableContainer + public abstract class BeatmapCard : OsuClickableContainer, IEquatable { public const float TRANSITION_DURATION = 400; public const float CORNER_RADIUS = 10; @@ -96,5 +96,16 @@ namespace osu.Game.Beatmaps.Drawables.Cards throw new ArgumentOutOfRangeException(nameof(size), size, @"Unsupported card size"); } } + + public bool Equals(BeatmapCard? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return BeatmapSet.Equals(other.BeatmapSet); + } + + public override bool Equals(object obj) => obj is BeatmapCard other && Equals(other); + public override int GetHashCode() => BeatmapSet.GetHashCode(); } } diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 3136492af0..70c6c60ecd 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -176,6 +176,8 @@ namespace osu.Game.Overlays } else { + newCards = newCards.Except(foundContent); + panelLoadTask = LoadComponentsAsync(newCards, loaded => { lastFetchDisplayedTime = Time.Current; @@ -185,7 +187,7 @@ namespace osu.Game.Overlays } } - private BeatmapCard[] createCardsFor(IEnumerable beatmapSets) => beatmapSets.Select(set => BeatmapCard.Create(set, filterControl.CardSize.Value).With(c => + private IEnumerable createCardsFor(IEnumerable beatmapSets) => beatmapSets.Select(set => BeatmapCard.Create(set, filterControl.CardSize.Value).With(c => { c.Anchor = Anchor.TopCentre; c.Origin = Anchor.TopCentre; From 718f8c4ee206bfeaa52177730fc74cb0c7d404b6 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Tue, 13 Sep 2022 01:09:42 +0200 Subject: [PATCH 095/142] revert the fix --- osu.Game/Screens/Edit/EditorBeatmap.cs | 45 ++++++++------------------ 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 95b848fec0..8aa754b305 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -16,7 +16,6 @@ using osu.Game.Beatmaps.Legacy; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Skinning; namespace osu.Game.Screens.Edit @@ -80,7 +79,7 @@ namespace osu.Game.Screens.Edit private readonly IBeatmapProcessor beatmapProcessor; - private readonly Dictionary> hitObjectBindables = new Dictionary>(); + private readonly Dictionary> startTimeBindables = new Dictionary>(); public EditorBeatmap(IBeatmap playableBeatmap, ISkin beatmapSkin = null, BeatmapInfo beatmapInfo = null) { @@ -98,7 +97,7 @@ namespace osu.Game.Screens.Edit beatmapProcessor = playableBeatmap.BeatmapInfo.Ruleset.CreateInstance().CreateBeatmapProcessor(PlayableBeatmap); foreach (var obj in HitObjects) - trackBindables(obj); + trackStartTime(obj); } /// @@ -223,7 +222,7 @@ namespace osu.Game.Screens.Edit /// The to insert. public void Insert(int index, HitObject hitObject) { - trackBindables(hitObject); + trackStartTime(hitObject); mutableHitObjects.Insert(index, hitObject); @@ -300,9 +299,9 @@ namespace osu.Game.Screens.Edit mutableHitObjects.RemoveAt(index); - var bindables = hitObjectBindables[hitObject]; - bindables.ForEach(b => b.UnbindAll()); - hitObjectBindables.Remove(hitObject); + var bindable = startTimeBindables[hitObject]; + bindable.UnbindAll(); + startTimeBindables.Remove(hitObject); BeginChange(); batchPendingDeletes.Add(hitObject); @@ -326,25 +325,25 @@ namespace osu.Game.Screens.Edit beatmapProcessor?.PreProcess(); - foreach (var h in batchPendingUpdates) processHitObject(h); foreach (var h in batchPendingDeletes) processHitObject(h); foreach (var h in batchPendingInserts) processHitObject(h); + foreach (var h in batchPendingUpdates) processHitObject(h); beatmapProcessor?.PostProcess(); // callbacks may modify the lists so let's be safe about it - var updates = batchPendingUpdates.ToArray(); - batchPendingUpdates.Clear(); - var deletes = batchPendingDeletes.ToArray(); batchPendingDeletes.Clear(); var inserts = batchPendingInserts.ToArray(); batchPendingInserts.Clear(); - foreach (var h in updates) HitObjectUpdated?.Invoke(h); + var updates = batchPendingUpdates.ToArray(); + batchPendingUpdates.Clear(); + foreach (var h in deletes) HitObjectRemoved?.Invoke(h); foreach (var h in inserts) HitObjectAdded?.Invoke(h); + foreach (var h in updates) HitObjectUpdated?.Invoke(h); updateInProgress.Value = false; } @@ -356,12 +355,10 @@ namespace osu.Game.Screens.Edit private void processHitObject(HitObject hitObject) => hitObject.ApplyDefaults(ControlPointInfo, PlayableBeatmap.Difficulty); - private void trackBindables(HitObject hitObject) + private void trackStartTime(HitObject hitObject) { - var bindables = new List(3); - - var startTimeBindable = hitObject.StartTimeBindable.GetBoundCopy(); - startTimeBindable.ValueChanged += _ => + startTimeBindables[hitObject] = hitObject.StartTimeBindable.GetBoundCopy(); + startTimeBindables[hitObject].ValueChanged += _ => { // For now we'll remove and re-add the hitobject. This is not optimal and can be improved if required. mutableHitObjects.Remove(hitObject); @@ -371,20 +368,6 @@ namespace osu.Game.Screens.Edit Update(hitObject); }; - bindables.Add(startTimeBindable); - - if (hitObject is IHasComboInformation hasCombo) - { - var comboIndexBindable = hasCombo.ComboIndexBindable.GetBoundCopy(); - comboIndexBindable.ValueChanged += _ => Update(hitObject); - bindables.Add(comboIndexBindable); - - var indexInCurrentComboBindable = hasCombo.IndexInCurrentComboBindable.GetBoundCopy(); - indexInCurrentComboBindable.ValueChanged += _ => Update(hitObject); - bindables.Add(indexInCurrentComboBindable); - } - - hitObjectBindables[hitObject] = bindables; } private int findInsertionIndex(IReadOnlyList list, double startTime) From 5fba21d658335f88010cc1748891a9be65f6ea01 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Sep 2022 02:21:01 +0300 Subject: [PATCH 096/142] Rename parameter to clarify purpose --- osu.Game/Skinning/LegacyJudgementPieceOld.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/LegacyJudgementPieceOld.cs b/osu.Game/Skinning/LegacyJudgementPieceOld.cs index 7e7f0f027d..3f4d13c082 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceOld.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceOld.cs @@ -18,14 +18,13 @@ namespace osu.Game.Skinning private readonly HitResult result; private readonly float finalScale; + private readonly bool forceTransforms; - private readonly bool allowTransforms; - - public LegacyJudgementPieceOld(HitResult result, Func createMainDrawable, float finalScale = 1f, bool allowTransforms = false) + public LegacyJudgementPieceOld(HitResult result, Func createMainDrawable, float finalScale = 1f, bool forceTransforms = false) { this.result = result; this.finalScale = finalScale; - this.allowTransforms = allowTransforms; + this.forceTransforms = forceTransforms; AutoSizeAxes = Axes.Both; Origin = Anchor.Centre; @@ -47,7 +46,7 @@ namespace osu.Game.Skinning this.Delay(fade_out_delay).FadeOut(fade_out_length); // legacy judgements don't play any transforms if they are an animation.... UNLESS they are the temporary displayed judgement from new piece. - if (!allowTransforms && animation?.FrameCount > 1) + if (animation?.FrameCount > 1 && !forceTransforms) return; switch (result) From a1f4724685c356272ca11342b0eff4eff3470b46 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Tue, 13 Sep 2022 01:38:29 +0200 Subject: [PATCH 097/142] moved the location of the tests --- .../Editing}/TestSceneSelectionBlueprintDeselection.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) rename {osu.Game.Rulesets.Osu.Tests/Editor => osu.Game.Tests/Visual/Editing}/TestSceneSelectionBlueprintDeselection.cs (88%) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSelectionBlueprintDeselection.cs b/osu.Game.Tests/Visual/Editing/TestSceneSelectionBlueprintDeselection.cs similarity index 88% rename from osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSelectionBlueprintDeselection.cs rename to osu.Game.Tests/Visual/Editing/TestSceneSelectionBlueprintDeselection.cs index b00582d6f3..6141e5f31c 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSelectionBlueprintDeselection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneSelectionBlueprintDeselection.cs @@ -4,14 +4,18 @@ using System.Linq; using NUnit.Framework; using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Tests.Beatmaps; using osuTK.Input; -namespace osu.Game.Rulesets.Osu.Tests.Editor +namespace osu.Game.Tests.Visual.Editing { - public class TestSceneSelectionBlueprintDeselection : TestSceneOsuEditor + public class TestSceneSelectionBlueprintDeselection : EditorTestScene { + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); [Test] From a6f68e4a908dc8a6593445f6ee639b1912577b66 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Sep 2022 03:08:13 +0300 Subject: [PATCH 098/142] Fix NRT inspections --- osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs index 4510fda11d..1042341337 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs @@ -126,7 +126,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select unchanged Difficulty Adjust mod", () => { var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull(); - var difficultyAdjustMod = ruleset.CreateMod(); + var difficultyAdjustMod = ruleset.CreateMod().AsNonNull(); difficultyAdjustMod.ReadFromDifficulty(advancedStats.BeatmapInfo.Difficulty); SelectedMods.Value = new[] { difficultyAdjustMod }; }); @@ -145,7 +145,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select changed Difficulty Adjust mod", () => { var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull(); - var difficultyAdjustMod = ruleset.CreateMod(); + var difficultyAdjustMod = ruleset.CreateMod().AsNonNull(); var originalDifficulty = advancedStats.BeatmapInfo.Difficulty; difficultyAdjustMod.ReadFromDifficulty(originalDifficulty); From fd48249eef42c8af74fb50373744d58bc80eb3f7 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Tue, 13 Sep 2022 02:20:52 +0200 Subject: [PATCH 099/142] fix with new event --- .../Components/HitObjectOrderedSelectionContainer.cs | 6 ++---- osu.Game/Screens/Edit/EditorBeatmap.cs | 8 ++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs b/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs index d6fd07c998..2d8dca9c2d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs @@ -24,11 +24,9 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.LoadComplete(); - editorBeatmap.HitObjectUpdated += hitObjectUpdated; + editorBeatmap.SelectionBlueprintsShouldBeSorted += SortInternal; } - private void hitObjectUpdated(HitObject _) => SortInternal(); - public override void Add(SelectionBlueprint drawable) { SortInternal(); @@ -72,7 +70,7 @@ namespace osu.Game.Screens.Edit.Compose.Components base.Dispose(isDisposing); if (editorBeatmap != null) - editorBeatmap.HitObjectUpdated -= hitObjectUpdated; + editorBeatmap.SelectionBlueprintsShouldBeSorted -= SortInternal; } } } diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 8aa754b305..82f2187901 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -48,6 +48,11 @@ namespace osu.Game.Screens.Edit /// public event Action HitObjectUpdated; + /// + /// Invoked after is updated during and blueprints need to be sorted immediately to prevent a crash. + /// + public event Action SelectionBlueprintsShouldBeSorted; + /// /// All currently selected s. /// @@ -331,6 +336,9 @@ namespace osu.Game.Screens.Edit beatmapProcessor?.PostProcess(); + // Signal selection blueprint sorting because it is possible that the beatmap processor changed the order of the selection blueprints + SelectionBlueprintsShouldBeSorted?.Invoke(); + // callbacks may modify the lists so let's be safe about it var deletes = batchPendingDeletes.ToArray(); batchPendingDeletes.Clear(); From 0ca4be5e5bf43db46950b56c900891e1fd1097d4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Sep 2022 03:45:31 +0300 Subject: [PATCH 100/142] Fix one more inspection --- .../UI/DrawableRulesetDependencies.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index cffbf2c9a1..52387a5740 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -53,20 +53,17 @@ namespace osu.Game.Rulesets.UI { var resources = ruleset.CreateResourceStore(); - if (resources != null) - { - var host = parent.Get(); + var host = parent.Get(); - TextureStore = new TextureStore(host.Renderer, parent.Get().CreateTextureLoaderStore(new NamespacedResourceStore(resources, @"Textures"))); - CacheAs(TextureStore = new FallbackTextureStore(host.Renderer, TextureStore, parent.Get())); + TextureStore = new TextureStore(host.Renderer, parent.Get().CreateTextureLoaderStore(new NamespacedResourceStore(resources, @"Textures"))); + CacheAs(TextureStore = new FallbackTextureStore(host.Renderer, TextureStore, parent.Get())); - SampleStore = parent.Get().GetSampleStore(new NamespacedResourceStore(resources, @"Samples")); - SampleStore.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY; - CacheAs(SampleStore = new FallbackSampleStore(SampleStore, parent.Get())); + SampleStore = parent.Get().GetSampleStore(new NamespacedResourceStore(resources, @"Samples")); + SampleStore.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY; + CacheAs(SampleStore = new FallbackSampleStore(SampleStore, parent.Get())); - ShaderManager = new ShaderManager(host.Renderer, new NamespacedResourceStore(resources, @"Shaders")); - CacheAs(ShaderManager = new FallbackShaderManager(host.Renderer, ShaderManager, parent.Get())); - } + ShaderManager = new ShaderManager(host.Renderer, new NamespacedResourceStore(resources, @"Shaders")); + CacheAs(ShaderManager = new FallbackShaderManager(host.Renderer, ShaderManager, parent.Get())); RulesetConfigManager = parent.Get().GetConfigFor(ruleset); if (RulesetConfigManager != null) From 6bf6b7e125639aa3a20d972bb87cf2d712975b41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Sep 2022 13:07:23 +0900 Subject: [PATCH 101/142] Fix null considerations in `DrawableRulesetDependencies` --- .../UI/DrawableRulesetDependencies.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index 52387a5740..ad52b4affc 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.IO; @@ -12,6 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Textures; @@ -44,9 +43,9 @@ namespace osu.Game.Rulesets.UI public ShaderManager ShaderManager { get; } /// - /// The ruleset config manager. + /// The ruleset config manager. May be null if ruleset does not expose a configuration manager. /// - public IRulesetConfigManager RulesetConfigManager { get; private set; } + public IRulesetConfigManager? RulesetConfigManager { get; } public DrawableRulesetDependencies(Ruleset ruleset, IReadOnlyDependencyContainer parent) : base(parent) @@ -93,10 +92,9 @@ namespace osu.Game.Rulesets.UI isDisposed = true; - SampleStore?.Dispose(); - TextureStore?.Dispose(); - ShaderManager?.Dispose(); - RulesetConfigManager = null; + if (ShaderManager.IsNotNull()) SampleStore.Dispose(); + if (TextureStore.IsNotNull()) TextureStore.Dispose(); + if (ShaderManager.IsNotNull()) ShaderManager.Dispose(); } #endregion @@ -157,7 +155,7 @@ namespace osu.Game.Rulesets.UI public void Dispose() { - primary?.Dispose(); + if (primary.IsNotNull()) primary.Dispose(); } } @@ -182,7 +180,7 @@ namespace osu.Game.Rulesets.UI protected override void Dispose(bool disposing) { base.Dispose(disposing); - primary?.Dispose(); + if (primary.IsNotNull()) primary.Dispose(); } } @@ -198,12 +196,12 @@ namespace osu.Game.Rulesets.UI this.fallback = fallback; } - public override byte[] LoadRaw(string name) => primary.LoadRaw(name) ?? fallback.LoadRaw(name); + public override byte[]? LoadRaw(string name) => primary.LoadRaw(name) ?? fallback.LoadRaw(name); protected override void Dispose(bool disposing) { base.Dispose(disposing); - primary?.Dispose(); + if (primary.IsNotNull()) primary.Dispose(); } } } From d368f37c3ab8c360cd1a31138543118f90fd1625 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Sep 2022 13:50:59 +0900 Subject: [PATCH 102/142] Remove redundant second `Current` set operation --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 13249e37ca..4787b07af8 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -76,8 +76,6 @@ namespace osu.Game.Overlays.Settings.Sections { base.LoadComplete(); - skinDropdown.Current = skins.CurrentSkinInfo; - realmSubscription = realm.RegisterForNotifications(_ => realm.Realm.All() .Where(s => !s.DeletePending) .OrderByDescending(s => s.Protected) // protected skins should be at the top. From 0fcd9e02f632230d3d1b884e88a5d4c0891f2c9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Sep 2022 14:01:40 +0900 Subject: [PATCH 103/142] Fix skin dropdown not updating correctly when skin is changed externally --- osu.Game/Database/Live.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Database/Live.cs b/osu.Game/Database/Live.cs index 52e1d420f7..a21b7f606b 100644 --- a/osu.Game/Database/Live.cs +++ b/osu.Game/Database/Live.cs @@ -53,6 +53,8 @@ namespace osu.Game.Database public bool Equals(Live? other) => ID == other?.ID; + public override int GetHashCode() => HashCode.Combine(ID); + public override string ToString() => PerformRead(i => i.ToString()); } } From ac034bffebe3597716bdaf14395452925881549d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Sep 2022 14:21:54 +0900 Subject: [PATCH 104/142] Fix potential crash if multiplayer spectator load is aborted early --- .../Multiplayer/Spectate/MultiSpectatorPlayer.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs index 8e79c89685..6e939c3916 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.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.Threading; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Game.Beatmaps; @@ -34,8 +35,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate } [BackgroundDependencyLoader] - private void load() + private void load(CancellationToken cancellationToken) { + // HUD overlay may not be loaded if load has been cancelled early. + if (cancellationToken.IsCancellationRequested) + return; + HUDOverlay.PlayerSettingsOverlay.Expire(); HUDOverlay.HoldToQuit.Expire(); } From f53507828c0bfdb7909c895a0d921c6de31ef9ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Sep 2022 14:59:30 +0900 Subject: [PATCH 105/142] Rename event to be more generic (and add comprehensive xmldoc) --- .../Components/HitObjectOrderedSelectionContainer.cs | 4 ++-- osu.Game/Screens/Edit/EditorBeatmap.cs | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs b/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs index 2d8dca9c2d..18bb6284b8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/HitObjectOrderedSelectionContainer.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.LoadComplete(); - editorBeatmap.SelectionBlueprintsShouldBeSorted += SortInternal; + editorBeatmap.BeatmapReprocessed += SortInternal; } public override void Add(SelectionBlueprint drawable) @@ -70,7 +70,7 @@ namespace osu.Game.Screens.Edit.Compose.Components base.Dispose(isDisposing); if (editorBeatmap != null) - editorBeatmap.SelectionBlueprintsShouldBeSorted -= SortInternal; + editorBeatmap.BeatmapReprocessed -= SortInternal; } } } diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 82f2187901..16c0064e80 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -49,9 +49,13 @@ namespace osu.Game.Screens.Edit public event Action HitObjectUpdated; /// - /// Invoked after is updated during and blueprints need to be sorted immediately to prevent a crash. + /// Invoked after any state changes occurred which triggered a beatmap reprocess via an . /// - public event Action SelectionBlueprintsShouldBeSorted; + /// + /// Beatmap processing may change the order of hitobjects. This event gives external components a chance to handle any changes + /// not covered by the / / events. + /// + public event Action BeatmapReprocessed; /// /// All currently selected s. @@ -336,8 +340,7 @@ namespace osu.Game.Screens.Edit beatmapProcessor?.PostProcess(); - // Signal selection blueprint sorting because it is possible that the beatmap processor changed the order of the selection blueprints - SelectionBlueprintsShouldBeSorted?.Invoke(); + BeatmapReprocessed?.Invoke(); // callbacks may modify the lists so let's be safe about it var deletes = batchPendingDeletes.ToArray(); From 608c893b23f2007e6793de9cff1e72aec92fe70b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Sep 2022 15:03:13 +0900 Subject: [PATCH 106/142] Add basic test guarantees --- .../Editing/TestSceneSelectionBlueprintDeselection.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneSelectionBlueprintDeselection.cs b/osu.Game.Tests/Visual/Editing/TestSceneSelectionBlueprintDeselection.cs index 6141e5f31c..5c933468be 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneSelectionBlueprintDeselection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneSelectionBlueprintDeselection.cs @@ -28,13 +28,17 @@ namespace osu.Game.Tests.Visual.Editing EditorClock.Seek(0); circle1 = new HitCircle(); var circle2 = new HitCircle(); + EditorBeatmap.Add(circle1); EditorBeatmap.Add(circle2); + EditorBeatmap.SelectedHitObjects.Add(circle1); EditorBeatmap.SelectedHitObjects.Add(circle2); }); AddStep("delete the first circle", () => EditorBeatmap.Remove(circle1)); + AddAssert("one hitobject remains", () => EditorBeatmap.HitObjects.Count == 1); + AddAssert("one hitobject selected", () => EditorBeatmap.SelectedHitObjects.Count == 1); } [Test] @@ -63,6 +67,9 @@ namespace osu.Game.Tests.Visual.Editing InputManager.PressKey(Key.Delete); InputManager.ReleaseKey(Key.Delete); }); + + AddAssert("10 hitobjects remain", () => EditorBeatmap.HitObjects.Count == 10); + AddAssert("no hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 0); } } } From 9226f0abbca34bd14e6c3571ea7a4426cf9abf57 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Sep 2022 19:16:25 +0900 Subject: [PATCH 107/142] Implement equality correctly in `Live` --- osu.Game/Database/Live.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/Live.cs b/osu.Game/Database/Live.cs index a21b7f606b..3bb11c3a50 100644 --- a/osu.Game/Database/Live.cs +++ b/osu.Game/Database/Live.cs @@ -51,7 +51,13 @@ namespace osu.Game.Database ID = id; } - public bool Equals(Live? other) => ID == other?.ID; + public bool Equals(Live? other) + { + if (ReferenceEquals(this, other)) return true; + if (other == null) return false; + + return ID == other.ID; + } public override int GetHashCode() => HashCode.Combine(ID); From 04dbb6fc1b61e8e3be64484b079d7c20d0b26127 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Sep 2022 00:49:02 +0300 Subject: [PATCH 108/142] Add inline comment --- osu.Game/Overlays/BeatmapListingOverlay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 70c6c60ecd..2be328427b 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -176,6 +176,9 @@ namespace osu.Game.Overlays } else { + // new results may contain beatmaps from a previous page, + // this is dodgy but matches web behaviour for now. + // see: https://github.com/ppy/osu-web/issues/9270 newCards = newCards.Except(foundContent); panelLoadTask = LoadComponentsAsync(newCards, loaded => From b4e6a20846d10ddd8a2590d0fd5b8701ef9e5294 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Sep 2022 12:59:04 +0900 Subject: [PATCH 109/142] Fix song select status prefix matching no longer working Regressed in #19275 due to weird logic. Closes #20289. --- .../NonVisual/Filtering/FilterQueryParserTest.cs | 10 ++++++++++ osu.Game/Screens/Select/FilterQueryParser.cs | 7 +++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs index ac16c59e5b..da32edb8fb 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterQueryParserTest.cs @@ -191,6 +191,16 @@ namespace osu.Game.Tests.NonVisual.Filtering Assert.IsTrue(filterCriteria.BeatDivisor.IsUpperInclusive); } + [Test] + public void TestPartialStatusMatch() + { + const string query = "status=r"; + var filterCriteria = new FilterCriteria(); + FilterQueryParser.ApplyQueries(filterCriteria, query); + Assert.AreEqual(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Min); + Assert.AreEqual(BeatmapOnlineStatus.Ranked, filterCriteria.OnlineStatus.Max); + } + [Test] public void TestApplyStatusQueries() { diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index a7c1aec361..c86554ddbc 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -122,14 +122,17 @@ namespace osu.Game.Screens.Select private static bool tryParseEnum(string value, out TEnum result) where TEnum : struct { - if (Enum.TryParse(value, true, out result)) return true; + // First try an exact match. + if (Enum.TryParse(value, true, out result)) + return true; + // Then try a prefix match. string? prefixMatch = Enum.GetNames(typeof(TEnum)).FirstOrDefault(name => name.StartsWith(value, true, CultureInfo.InvariantCulture)); if (prefixMatch == null) return false; - return Enum.TryParse(value, true, out result); + return Enum.TryParse(prefixMatch, true, out result); } private static GroupCollection? tryMatchRegex(string value, string regex) From 85e3a681b12e79b8cb12b6b8551bb3eea7e28566 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 13 Sep 2022 21:07:30 -0700 Subject: [PATCH 110/142] Fix notification overlay toast tray background absorbing input from behind --- osu.Game/Overlays/NotificationOverlayToastTray.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/NotificationOverlayToastTray.cs b/osu.Game/Overlays/NotificationOverlayToastTray.cs index e3f8734581..329379de4a 100644 --- a/osu.Game/Overlays/NotificationOverlayToastTray.cs +++ b/osu.Game/Overlays/NotificationOverlayToastTray.cs @@ -26,6 +26,8 @@ namespace osu.Game.Overlays { public override bool IsPresent => toastContentBackground.Height > 0 || toastFlow.Count > 0; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => toastFlow.ReceivePositionalInputAt(screenSpacePos); + public bool IsDisplayingToasts => toastFlow.Count > 0; private FillFlowContainer toastFlow = null!; From bc07513c3c19f4440676aff013b111eec321ad50 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Sep 2022 14:19:53 +0900 Subject: [PATCH 111/142] Fix local scores potentially not being stable-sorted for leaderboard display --- osu.Game/Scoring/ScoreManager.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 6bb31eb4db..8342d3bcc1 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -58,7 +58,10 @@ namespace osu.Game.Scoring /// The array of s to reorder. /// The given ordered by decreasing total score. public IEnumerable OrderByTotalScore(IEnumerable scores) - => scores.OrderByDescending(s => GetTotalScore(s)).ThenBy(s => s.OnlineID); + => scores.OrderByDescending(s => GetTotalScore(s)) + .ThenBy(s => s.OnlineID) + // Local scores may not have an online ID. Fall back to date in these cases. + .ThenBy(s => s.Date); /// /// Retrieves a bindable that represents the total score of a . From 30cf14e824bc0eeb9f01f0eeb63eb91462c7954e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 14 Sep 2022 14:40:45 +0900 Subject: [PATCH 112/142] Update ReSharper CLI to 2022.2.3 --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 57694d7f57..1f937e1837 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2022.1.1", + "version": "2022.2.3", "commands": [ "jb" ] From a9eba2712991229a99e1f1b80c85d92e720a8264 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 15 Jun 2021 19:41:37 +0900 Subject: [PATCH 113/142] Factor out pooling logic from `Playfield` --- .../Objects/Pooling/HitObjectEntryManager.cs | 69 +++++++++++++++++++ osu.Game/Rulesets/UI/Playfield.cs | 49 +++++++------ 2 files changed, 98 insertions(+), 20 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs diff --git a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs new file mode 100644 index 0000000000..129c4cab44 --- /dev/null +++ b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs @@ -0,0 +1,69 @@ +// 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; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace osu.Game.Rulesets.Objects.Pooling +{ + /// + /// Manages a mapping between and + /// + internal class HitObjectEntryManager + { + /// + /// All entries, including entries of the nested hit objects. + /// + public IEnumerable AllEntries => entryMap.Values; + + public event Action? OnEntryAdded; + public event Action? OnEntryRemoved; + + private readonly Func createLifetimeEntry; + + private readonly Dictionary entryMap = new Dictionary(); + private readonly Dictionary parentMap = new Dictionary(); + + public HitObjectEntryManager(Func createLifetimeEntry) + { + this.createLifetimeEntry = createLifetimeEntry; + } + + public HitObjectLifetimeEntry Add(HitObject hitObject, HitObject? parentHitObject) + { + if (parentHitObject != null && !entryMap.TryGetValue(parentHitObject, out var parentEntry)) + throw new InvalidOperationException($@"The parent {nameof(HitObject)} must be added to this {nameof(HitObjectEntryManager)} before nested {nameof(HitObject)} is added."); + + if (entryMap.ContainsKey(hitObject)) + throw new InvalidOperationException($@"The {nameof(HitObject)} is already added to this {nameof(HitObjectEntryManager)}."); + + if (parentHitObject != null) + parentMap[hitObject] = parentHitObject; + + var entry = createLifetimeEntry(hitObject); + entryMap[hitObject] = entry; + + OnEntryAdded?.Invoke(entry, parentHitObject); + return entry; + } + + public bool Remove(HitObject hitObject) + { + if (!entryMap.TryGetValue(hitObject, out var entry)) + return false; + + parentMap.Remove(hitObject, out var parentHitObject); + + OnEntryRemoved?.Invoke(entry, parentHitObject); + return true; + } + + public bool TryGet(HitObject hitObject, [MaybeNullWhen(false)] out HitObjectLifetimeEntry entry) + { + return entryMap.TryGetValue(hitObject, out entry); + } + } +} diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 60d1555052..5aaa7682db 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -21,6 +21,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Skinning; using osuTK; +using osu.Game.Rulesets.Objects.Pooling; namespace osu.Game.Rulesets.UI { @@ -94,6 +95,8 @@ namespace osu.Game.Rulesets.UI [Resolved(CanBeNull = true)] private IReadOnlyList mods { get; set; } + private readonly HitObjectEntryManager entryManager; + /// /// Creates a new . /// @@ -108,6 +111,10 @@ namespace osu.Game.Rulesets.UI h.HitObjectUsageBegan += o => HitObjectUsageBegan?.Invoke(o); h.HitObjectUsageFinished += o => HitObjectUsageFinished?.Invoke(o); })); + + entryManager = new HitObjectEntryManager(CreateLifetimeEntry); + entryManager.OnEntryAdded += onEntryAdded; + entryManager.OnEntryRemoved += onEntryRemoved; } [BackgroundDependencyLoader] @@ -171,6 +178,7 @@ namespace osu.Game.Rulesets.UI /// The added . protected virtual void OnHitObjectAdded(HitObject hitObject) { + preloadSamples(hitObject); } /// @@ -263,13 +271,7 @@ namespace osu.Game.Rulesets.UI /// public virtual void Add(HitObject hitObject) { - var entry = CreateLifetimeEntry(hitObject); - lifetimeEntryMap[entry.HitObject] = entry; - - preloadSamples(hitObject); - - HitObjectContainer.Add(entry); - OnHitObjectAdded(entry.HitObject); + entryManager.Add(hitObject, null); } private void preloadSamples(HitObject hitObject) @@ -292,14 +294,23 @@ namespace osu.Game.Rulesets.UI /// Whether the was successfully removed. public virtual bool Remove(HitObject hitObject) { - if (lifetimeEntryMap.Remove(hitObject, out var entry)) - { - HitObjectContainer.Remove(entry); - OnHitObjectRemoved(hitObject); - return true; - } + return entryManager.Remove(hitObject) || nestedPlayfields.Any(p => p.Remove(hitObject)); + } - return nestedPlayfields.Any(p => p.Remove(hitObject)); + private void onEntryAdded(HitObjectLifetimeEntry entry, [CanBeNull] HitObject parentHitObject) + { + if (parentHitObject != null) return; + + HitObjectContainer.Add(entry); + OnHitObjectAdded(entry.HitObject); + } + + private void onEntryRemoved(HitObjectLifetimeEntry entry, [CanBeNull] HitObject parentHitObject) + { + if (parentHitObject != null) return; + + HitObjectContainer.Remove(entry); + OnHitObjectRemoved(entry.HitObject); } /// @@ -366,8 +377,8 @@ namespace osu.Game.Rulesets.UI } } - if (!lifetimeEntryMap.TryGetValue(hitObject, out var entry)) - lifetimeEntryMap[hitObject] = entry = CreateLifetimeEntry(hitObject); + if (!entryManager.TryGet(hitObject, out var entry)) + entry = entryManager.Add(hitObject, parent?.HitObject); dho.ParentHitObject = parent; dho.Apply(entry); @@ -442,8 +453,6 @@ namespace osu.Game.Rulesets.UI /// internal event Action HitObjectUsageFinished; - private readonly Dictionary lifetimeEntryMap = new Dictionary(); - /// /// Sets whether to keep a given always alive within this or any nested . /// @@ -451,7 +460,7 @@ namespace osu.Game.Rulesets.UI /// Whether to keep always alive. internal void SetKeepAlive(HitObject hitObject, bool keepAlive) { - if (lifetimeEntryMap.TryGetValue(hitObject, out var entry)) + if (entryManager.TryGet(hitObject, out var entry)) { entry.KeepAlive = keepAlive; return; @@ -466,7 +475,7 @@ namespace osu.Game.Rulesets.UI /// internal void KeepAllAlive() { - foreach (var (_, entry) in lifetimeEntryMap) + foreach (var entry in entryManager.AllEntries) entry.KeepAlive = true; foreach (var p in nestedPlayfields) From bde390828b017f3d62698f263ec14a962d05ad19 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 15 Jun 2021 20:48:00 +0900 Subject: [PATCH 114/142] Manage entries of nested hit objects in `HitObjectEntryManager` - Fix nested hit objects are leaked when parent hit object is edited or deleted --- .../Objects/Pooling/HitObjectEntryManager.cs | 54 ++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs index 129c4cab44..f14f8b6f61 100644 --- a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs +++ b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs @@ -25,39 +25,54 @@ namespace osu.Game.Rulesets.Objects.Pooling private readonly Func createLifetimeEntry; private readonly Dictionary entryMap = new Dictionary(); - private readonly Dictionary parentMap = new Dictionary(); + private readonly Dictionary parentMap = new Dictionary(); + private readonly Dictionary> childrenMap = new Dictionary>(); public HitObjectEntryManager(Func createLifetimeEntry) { this.createLifetimeEntry = createLifetimeEntry; } - public HitObjectLifetimeEntry Add(HitObject hitObject, HitObject? parentHitObject) + public HitObjectLifetimeEntry Add(HitObject hitObject, HitObject? parent) { - if (parentHitObject != null && !entryMap.TryGetValue(parentHitObject, out var parentEntry)) - throw new InvalidOperationException($@"The parent {nameof(HitObject)} must be added to this {nameof(HitObjectEntryManager)} before nested {nameof(HitObject)} is added."); - if (entryMap.ContainsKey(hitObject)) throw new InvalidOperationException($@"The {nameof(HitObject)} is already added to this {nameof(HitObjectEntryManager)}."); - if (parentHitObject != null) - parentMap[hitObject] = parentHitObject; - var entry = createLifetimeEntry(hitObject); entryMap[hitObject] = entry; + parentMap[entry] = parent; - OnEntryAdded?.Invoke(entry, parentHitObject); + if (parent != null && childrenMap.TryGetValue(parent, out var parentChildEntries)) + parentChildEntries.Add(entry); + + hitObject.DefaultsApplied += onDefaultsApplied; + + childrenMap[entry.HitObject] = new List(); + + OnEntryAdded?.Invoke(entry, parent); return entry; } public bool Remove(HitObject hitObject) { - if (!entryMap.TryGetValue(hitObject, out var entry)) + if (!entryMap.Remove(hitObject, out var entry)) return false; - parentMap.Remove(hitObject, out var parentHitObject); + parentMap.Remove(entry, out var parent); - OnEntryRemoved?.Invoke(entry, parentHitObject); + if (parent != null && childrenMap.TryGetValue(parent, out var parentChildEntries)) + parentChildEntries.Remove(entry); + + hitObject.DefaultsApplied -= onDefaultsApplied; + + // Remove all entries of the nested hit objects + if (childrenMap.Remove(entry.HitObject, out var childEntries)) + { + foreach (var childEntry in childEntries) + Remove(childEntry.HitObject); + } + + OnEntryRemoved?.Invoke(entry, parent); return true; } @@ -65,5 +80,20 @@ namespace osu.Game.Rulesets.Objects.Pooling { return entryMap.TryGetValue(hitObject, out entry); } + + /// + /// As nested hit objects are recreated, remove entries of the old nested hit objects. + /// + private void onDefaultsApplied(HitObject hitObject) + { + if (!childrenMap.Remove(hitObject, out var childEntries)) + return; + + foreach (var entry in childEntries) + Remove(entry.HitObject); + + childEntries.Clear(); + childrenMap[hitObject] = childEntries; + } } } From 47539e2129d85e50866e659e850d8bc769927057 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 16 Jun 2021 15:09:01 +0900 Subject: [PATCH 115/142] Add doc comments to `HitObjectEntryManager` --- .../Objects/Pooling/HitObjectEntryManager.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs index f14f8b6f61..71c0a88616 100644 --- a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs +++ b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs @@ -19,13 +19,38 @@ namespace osu.Game.Rulesets.Objects.Pooling /// public IEnumerable AllEntries => entryMap.Values; + /// + /// Invoked when a new is added to this .. + /// The second parameter of the event is the parent hit object. + /// public event Action? OnEntryAdded; + + /// + /// Invoked when a is removed from this . + /// The second parameter of the event is the parent hit object. + /// public event Action? OnEntryRemoved; private readonly Func createLifetimeEntry; + /// + /// Provides the reverse mapping of for each entry. + /// private readonly Dictionary entryMap = new Dictionary(); + + /// + /// Stores the parent hit object for entries of the nested hit objects. + /// A null is stored for entries of the top-level hit objects. + /// + /// + /// The parent hit object of a pooled hit object may be non-pooled. + /// In that case, no corresponding is stored in this . + /// private readonly Dictionary parentMap = new Dictionary(); + + /// + /// Stores the list of entries managed by this for each hit object managed by this . + /// private readonly Dictionary> childrenMap = new Dictionary>(); public HitObjectEntryManager(Func createLifetimeEntry) From c59aa574509d153883fc6dd6f4627058a7089e27 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 16 Jun 2021 15:15:33 +0900 Subject: [PATCH 116/142] Remove `createLifetimeEntry` from `HitObjectEntryManager` --- .../Objects/Pooling/HitObjectEntryManager.cs | 33 ++++++++----------- osu.Game/Rulesets/UI/Playfield.cs | 19 ++++++++--- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs index 71c0a88616..d8dd6fab7e 100644 --- a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs +++ b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs @@ -31,8 +31,6 @@ namespace osu.Game.Rulesets.Objects.Pooling /// public event Action? OnEntryRemoved; - private readonly Func createLifetimeEntry; - /// /// Provides the reverse mapping of for each entry. /// @@ -53,37 +51,33 @@ namespace osu.Game.Rulesets.Objects.Pooling /// private readonly Dictionary> childrenMap = new Dictionary>(); - public HitObjectEntryManager(Func createLifetimeEntry) + public void Add(HitObjectLifetimeEntry entry, HitObject? parent) { - this.createLifetimeEntry = createLifetimeEntry; - } + if (parentMap.ContainsKey(entry)) + throw new InvalidOperationException($@"The {nameof(HitObjectLifetimeEntry)} is already added to this {nameof(HitObjectEntryManager)}."); - public HitObjectLifetimeEntry Add(HitObject hitObject, HitObject? parent) - { - if (entryMap.ContainsKey(hitObject)) - throw new InvalidOperationException($@"The {nameof(HitObject)} is already added to this {nameof(HitObjectEntryManager)}."); - - var entry = createLifetimeEntry(hitObject); - entryMap[hitObject] = entry; + var hitObject = entry.HitObject; parentMap[entry] = parent; + entryMap[hitObject] = entry; if (parent != null && childrenMap.TryGetValue(parent, out var parentChildEntries)) parentChildEntries.Add(entry); hitObject.DefaultsApplied += onDefaultsApplied; - childrenMap[entry.HitObject] = new List(); + childrenMap[hitObject] = new List(); OnEntryAdded?.Invoke(entry, parent); - return entry; } - public bool Remove(HitObject hitObject) + public void Remove(HitObjectLifetimeEntry entry) { - if (!entryMap.Remove(hitObject, out var entry)) - return false; + if (!parentMap.ContainsKey(entry)) + throw new InvalidOperationException($@"The {nameof(HitObjectLifetimeEntry)} is not contained in this {nameof(HitObjectLifetimeEntry)}."); + var hitObject = entry.HitObject; parentMap.Remove(entry, out var parent); + entryMap.Remove(hitObject); if (parent != null && childrenMap.TryGetValue(parent, out var parentChildEntries)) parentChildEntries.Remove(entry); @@ -94,11 +88,10 @@ namespace osu.Game.Rulesets.Objects.Pooling if (childrenMap.Remove(entry.HitObject, out var childEntries)) { foreach (var childEntry in childEntries) - Remove(childEntry.HitObject); + Remove(childEntry); } OnEntryRemoved?.Invoke(entry, parent); - return true; } public bool TryGet(HitObject hitObject, [MaybeNullWhen(false)] out HitObjectLifetimeEntry entry) @@ -115,7 +108,7 @@ namespace osu.Game.Rulesets.Objects.Pooling return; foreach (var entry in childEntries) - Remove(entry.HitObject); + Remove(entry); childEntries.Clear(); childrenMap[hitObject] = childEntries; diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 5aaa7682db..2ec72d8fe3 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.UI [Resolved(CanBeNull = true)] private IReadOnlyList mods { get; set; } - private readonly HitObjectEntryManager entryManager; + private readonly HitObjectEntryManager entryManager = new HitObjectEntryManager(); /// /// Creates a new . @@ -112,7 +112,6 @@ namespace osu.Game.Rulesets.UI h.HitObjectUsageFinished += o => HitObjectUsageFinished?.Invoke(o); })); - entryManager = new HitObjectEntryManager(CreateLifetimeEntry); entryManager.OnEntryAdded += onEntryAdded; entryManager.OnEntryRemoved += onEntryRemoved; } @@ -271,7 +270,8 @@ namespace osu.Game.Rulesets.UI /// public virtual void Add(HitObject hitObject) { - entryManager.Add(hitObject, null); + var entry = CreateLifetimeEntry(hitObject); + entryManager.Add(entry, null); } private void preloadSamples(HitObject hitObject) @@ -294,7 +294,13 @@ namespace osu.Game.Rulesets.UI /// Whether the was successfully removed. public virtual bool Remove(HitObject hitObject) { - return entryManager.Remove(hitObject) || nestedPlayfields.Any(p => p.Remove(hitObject)); + if (entryManager.TryGet(hitObject, out var entry)) + { + entryManager.Remove(entry); + return true; + } + + return nestedPlayfields.Any(p => p.Remove(hitObject)); } private void onEntryAdded(HitObjectLifetimeEntry entry, [CanBeNull] HitObject parentHitObject) @@ -378,7 +384,10 @@ namespace osu.Game.Rulesets.UI } if (!entryManager.TryGet(hitObject, out var entry)) - entry = entryManager.Add(hitObject, parent?.HitObject); + { + entry = CreateLifetimeEntry(hitObject); + entryManager.Add(entry, parent?.HitObject); + } dho.ParentHitObject = parent; dho.Apply(entry); From 5dc0d32e4207199f93c0d2f054f3d145952ab066 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Sep 2022 15:24:20 +0900 Subject: [PATCH 117/142] Apply NRT to hitobject lifetime related classes --- osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs | 4 +--- osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs index 9af4cf3544..fedf419973 100644 --- a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs +++ b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Bindables; using osu.Framework.Graphics.Performance; using osu.Game.Rulesets.Judgements; @@ -24,7 +22,7 @@ namespace osu.Game.Rulesets.Objects /// The result that was judged with. /// This is set by the accompanying , and reused when required for rewinding. /// - internal JudgementResult Result; + internal JudgementResult? Result; private readonly IBindable startTimeBindable = new BindableDouble(); diff --git a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs index d8dd6fab7e..30a8e8f549 100644 --- a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs +++ b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs @@ -1,8 +1,6 @@ // 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; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; From 87384db8729401b7b6824880cd5cc95eeefa9126 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Sep 2022 00:51:02 +0900 Subject: [PATCH 118/142] Fix slider rotation causing thousands of new drawables to be created --- .../Sliders/Components/PathControlPointPiece.cs | 7 +++++-- osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs | 9 ++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index ab4b492767..59edbe8dc6 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -66,11 +66,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components cachePoints(slider); sliderVersion = slider.Path.Version.GetBoundCopy(); - sliderVersion.BindValueChanged(_ => + + // schedule ensure that updates are only applied after all operations from a single frame are applied. + // this avoids inadvertently changing the slider path type for bach operations. + sliderVersion.BindValueChanged(_ => Scheduler.AddOnce(() => { cachePoints(slider); updatePathType(); - }); + })); controlPoint.Changed += updateMarkerDisplay; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 57d67acad5..280a35500a 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -186,13 +186,8 @@ namespace osu.Game.Rulesets.Osu.Edit if (h is IHasPath path) { - var controlPoints = path.Path.ControlPoints.Select(p => - new PathControlPoint(RotatePointAroundOrigin(p.Position, Vector2.Zero, delta), p.Type)).ToArray(); - - // Importantly, update as a single operation so automatic adjustment of control points to different - // curve types does not unexpectedly trigger and change the slider's shape. - path.Path.ControlPoints.Clear(); - path.Path.ControlPoints.AddRange(controlPoints); + foreach (PathControlPoint t in path.Path.ControlPoints) + t.Position = RotatePointAroundOrigin(t.Position, Vector2.Zero, delta); } } From d6db82283a806ab62114ee3c830cd15152a5a1ef Mon Sep 17 00:00:00 2001 From: Mk-56spn Date: Wed, 14 Sep 2022 18:01:29 +0200 Subject: [PATCH 119/142] Removes unused class --- osu.Game/Screens/Play/KeyCounterState.cs | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 osu.Game/Screens/Play/KeyCounterState.cs diff --git a/osu.Game/Screens/Play/KeyCounterState.cs b/osu.Game/Screens/Play/KeyCounterState.cs deleted file mode 100644 index 45d027e5ec..0000000000 --- a/osu.Game/Screens/Play/KeyCounterState.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -namespace osu.Game.Screens.Play -{ - public class KeyCounterState - { - public KeyCounterState(double time, int count) - { - Time = time; - Count = count; - } - - public readonly double Time; - public readonly int Count; - } -} From c203a030c1bdec9b40168b1ca8844c802436b4d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Sep 2022 04:28:07 +0900 Subject: [PATCH 120/142] Update osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs Co-authored-by: apollo <83023433+apollo-dw@users.noreply.github.com> --- .../Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 59edbe8dc6..5dec5d1cb0 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components sliderVersion = slider.Path.Version.GetBoundCopy(); // schedule ensure that updates are only applied after all operations from a single frame are applied. - // this avoids inadvertently changing the slider path type for bach operations. + // this avoids inadvertently changing the slider path type for batch operations. sliderVersion.BindValueChanged(_ => Scheduler.AddOnce(() => { cachePoints(slider); From de7dd29d791304fe588ae1f297ecff8e128168c4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 15 Sep 2022 03:57:05 +0300 Subject: [PATCH 121/142] Add "Nominations" and "Updated" sorting criteria in beatmap listing --- osu.Game/Overlays/BeatmapListing/SortCriteria.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapListing/SortCriteria.cs b/osu.Game/Overlays/BeatmapListing/SortCriteria.cs index ff6e62b274..6c010c7504 100644 --- a/osu.Game/Overlays/BeatmapListing/SortCriteria.cs +++ b/osu.Game/Overlays/BeatmapListing/SortCriteria.cs @@ -19,6 +19,9 @@ namespace osu.Game.Overlays.BeatmapListing [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingDifficulty))] Difficulty, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingUpdated))] + Updated, + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingRanked))] Ranked, @@ -32,6 +35,9 @@ namespace osu.Game.Overlays.BeatmapListing Favourites, [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingRelevance))] - Relevance + Relevance, + + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingNominations))] + Nominations, } } From efebe55d228f9a669bc500a822bf211ca34e6ba3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 15 Sep 2022 03:58:32 +0300 Subject: [PATCH 122/142] Display certain sort criterias based on selected category and query --- .../BeatmapListingFilterControl.cs | 21 ++++---- .../BeatmapListingSortTabControl.cs | 53 +++++++++++++++++-- osu.Game/Overlays/OverlaySortTabControl.cs | 4 +- 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 2ca369d459..ff9a46ed4f 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -151,19 +151,20 @@ namespace osu.Game.Overlays.BeatmapListing config.BindWith(OsuSetting.BeatmapListingCardSize, cardSize); - var sortCriteria = sortControl.Current; - var sortDirection = sortControl.SortDirection; - - searchControl.Query.BindValueChanged(query => + searchControl.Query.BindValueChanged(_ => { - sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? SortCriteria.Ranked : SortCriteria.Relevance; - sortDirection.Value = SortDirection.Descending; + resetSortControl(); queueUpdateSearch(true); }); + searchControl.Category.BindValueChanged(_ => + { + resetSortControl(); + queueUpdateSearch(); + }); + searchControl.General.CollectionChanged += (_, _) => queueUpdateSearch(); searchControl.Ruleset.BindValueChanged(_ => queueUpdateSearch()); - searchControl.Category.BindValueChanged(_ => queueUpdateSearch()); searchControl.Genre.BindValueChanged(_ => queueUpdateSearch()); searchControl.Language.BindValueChanged(_ => queueUpdateSearch()); searchControl.Extra.CollectionChanged += (_, _) => queueUpdateSearch(); @@ -171,8 +172,8 @@ namespace osu.Game.Overlays.BeatmapListing searchControl.Played.BindValueChanged(_ => queueUpdateSearch()); searchControl.ExplicitContent.BindValueChanged(_ => queueUpdateSearch()); - sortCriteria.BindValueChanged(_ => queueUpdateSearch()); - sortDirection.BindValueChanged(_ => queueUpdateSearch()); + sortControl.Current.BindValueChanged(_ => queueUpdateSearch()); + sortControl.SortDirection.BindValueChanged(_ => queueUpdateSearch()); apiUser = api.LocalUser.GetBoundCopy(); apiUser.BindValueChanged(_ => queueUpdateSearch()); @@ -199,6 +200,8 @@ namespace osu.Game.Overlays.BeatmapListing performRequest(); } + private void resetSortControl() => sortControl.Reset(searchControl.Category.Value, !string.IsNullOrEmpty(searchControl.Query.Value)); + private void queueUpdateSearch(bool queryTextChanged = false) { SearchStarted?.Invoke(); diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs index 454f381c60..bc1f30dcaf 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs @@ -17,18 +17,65 @@ namespace osu.Game.Overlays.BeatmapListing { public readonly Bindable SortDirection = new Bindable(Overlays.SortDirection.Descending); - public BeatmapListingSortTabControl() + private SearchCategory? lastCategory; + private bool? lastHasQuery; + + protected override void LoadComplete() { - Current.Value = SortCriteria.Ranked; + base.LoadComplete(); + Reset(SearchCategory.Leaderboard, false); + } + + public void Reset(SearchCategory category, bool hasQuery) + { + if (category != lastCategory || hasQuery != lastHasQuery) + { + TabControl.Clear(); + + TabControl.AddItem(SortCriteria.Title); + TabControl.AddItem(SortCriteria.Artist); + TabControl.AddItem(SortCriteria.Difficulty); + + if (category == SearchCategory.Any || category > SearchCategory.Loved) + TabControl.AddItem(SortCriteria.Updated); + + if (category < SearchCategory.Pending || category == SearchCategory.Mine) + TabControl.AddItem(SortCriteria.Ranked); + + TabControl.AddItem(SortCriteria.Rating); + TabControl.AddItem(SortCriteria.Plays); + TabControl.AddItem(SortCriteria.Favourites); + + if (hasQuery) + TabControl.AddItem(SortCriteria.Relevance); + + if (category == SearchCategory.Pending) + TabControl.AddItem(SortCriteria.Nominations); + } + + var nonQueryCriteria = category >= SearchCategory.Pending ? SortCriteria.Updated : SortCriteria.Ranked; + + Current.Value = hasQuery ? SortCriteria.Relevance : nonQueryCriteria; + SortDirection.Value = Overlays.SortDirection.Descending; + + // if the new criteria isn't different from the previous one, + // then re-adding tab items will not mark the current tab as selected. + // see: https://github.com/ppy/osu-framework/issues/5412 + TabControl.Current.TriggerChange(); + + lastCategory = category; + lastHasQuery = hasQuery; } protected override SortTabControl CreateControl() => new BeatmapSortTabControl { - SortDirection = { BindTarget = SortDirection } + SortDirection = { BindTarget = SortDirection }, }; private class BeatmapSortTabControl : SortTabControl { + protected override bool AddEnumEntriesAutomatically => false; + public readonly Bindable SortDirection = new Bindable(); protected override TabItem CreateTabItem(SortCriteria value) => new BeatmapSortTabItem(value) diff --git a/osu.Game/Overlays/OverlaySortTabControl.cs b/osu.Game/Overlays/OverlaySortTabControl.cs index f02d2b388f..befb011353 100644 --- a/osu.Game/Overlays/OverlaySortTabControl.cs +++ b/osu.Game/Overlays/OverlaySortTabControl.cs @@ -26,6 +26,8 @@ namespace osu.Game.Overlays { public class OverlaySortTabControl : CompositeDrawable, IHasCurrentValue { + public TabControl TabControl { get; } + private readonly BindableWithCurrent current = new BindableWithCurrent(); public Bindable Current @@ -59,7 +61,7 @@ namespace osu.Game.Overlays Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), Text = SortStrings.Default }, - CreateControl().With(c => + TabControl = CreateControl().With(c => { c.Anchor = Anchor.CentreLeft; c.Origin = Anchor.CentreLeft; From 9824d09d490341424a52cfa54cb594036710de82 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 15 Sep 2022 03:58:42 +0300 Subject: [PATCH 123/142] Add test coverage --- .../TestSceneBeatmapListingSortTabControl.cs | 85 ++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs index 3247461fba..0ef13385ec 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSortTabControl.cs @@ -3,9 +3,13 @@ #nullable disable +using System.Linq; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Testing; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; @@ -15,12 +19,13 @@ namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneBeatmapListingSortTabControl : OsuTestScene { + private readonly BeatmapListingSortTabControl control; + [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); public TestSceneBeatmapListingSortTabControl() { - BeatmapListingSortTabControl control; OsuSpriteText current; OsuSpriteText direction; @@ -45,5 +50,83 @@ namespace osu.Game.Tests.Visual.UserInterface control.SortDirection.BindValueChanged(sortDirection => direction.Text = $"Sort direction: {sortDirection.NewValue}", true); control.Current.BindValueChanged(criteria => current.Text = $"Criteria: {criteria.NewValue}", true); } + + [Test] + public void TestRankedSort() + { + criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Any); + criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Leaderboard); + criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Ranked); + criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Qualified); + criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Loved); + criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Favourites); + criteriaShowsOnCategory(false, SortCriteria.Ranked, SearchCategory.Pending); + criteriaShowsOnCategory(false, SortCriteria.Ranked, SearchCategory.Wip); + criteriaShowsOnCategory(false, SortCriteria.Ranked, SearchCategory.Graveyard); + criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Mine); + } + + [Test] + public void TestUpdatedSort() + { + criteriaShowsOnCategory(true, SortCriteria.Updated, SearchCategory.Any); + criteriaShowsOnCategory(false, SortCriteria.Updated, SearchCategory.Leaderboard); + criteriaShowsOnCategory(false, SortCriteria.Updated, SearchCategory.Ranked); + criteriaShowsOnCategory(false, SortCriteria.Updated, SearchCategory.Qualified); + criteriaShowsOnCategory(false, SortCriteria.Updated, SearchCategory.Loved); + criteriaShowsOnCategory(true, SortCriteria.Updated, SearchCategory.Favourites); + criteriaShowsOnCategory(true, SortCriteria.Updated, SearchCategory.Pending); + criteriaShowsOnCategory(true, SortCriteria.Updated, SearchCategory.Wip); + criteriaShowsOnCategory(true, SortCriteria.Updated, SearchCategory.Graveyard); + criteriaShowsOnCategory(true, SortCriteria.Updated, SearchCategory.Mine); + } + + [Test] + public void TestNominationsSort() + { + criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Any); + criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Leaderboard); + criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Ranked); + criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Qualified); + criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Loved); + criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Favourites); + criteriaShowsOnCategory(true, SortCriteria.Nominations, SearchCategory.Pending); + criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Wip); + criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Graveyard); + criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Mine); + } + + [Test] + public void TestResetNoQuery() + { + resetUsesCriteriaOnCategory(SortCriteria.Ranked, SearchCategory.Any); + resetUsesCriteriaOnCategory(SortCriteria.Ranked, SearchCategory.Leaderboard); + resetUsesCriteriaOnCategory(SortCriteria.Ranked, SearchCategory.Ranked); + resetUsesCriteriaOnCategory(SortCriteria.Ranked, SearchCategory.Qualified); + resetUsesCriteriaOnCategory(SortCriteria.Ranked, SearchCategory.Loved); + resetUsesCriteriaOnCategory(SortCriteria.Ranked, SearchCategory.Favourites); + resetUsesCriteriaOnCategory(SortCriteria.Updated, SearchCategory.Pending); + resetUsesCriteriaOnCategory(SortCriteria.Updated, SearchCategory.Wip); + resetUsesCriteriaOnCategory(SortCriteria.Updated, SearchCategory.Graveyard); + resetUsesCriteriaOnCategory(SortCriteria.Updated, SearchCategory.Mine); + } + + private void criteriaShowsOnCategory(bool expected, SortCriteria criteria, SearchCategory category) + { + AddAssert($"{criteria.ToString().ToLowerInvariant()} {(expected ? "shown" : "not shown")} on {category.ToString().ToLowerInvariant()}", () => + { + control.Reset(category, false); + return control.ChildrenOfType>().Single().Items.Contains(criteria) == expected; + }); + } + + private void resetUsesCriteriaOnCategory(SortCriteria criteria, SearchCategory category) + { + AddAssert($"reset uses {criteria.ToString().ToLowerInvariant()} on {category.ToString().ToLowerInvariant()}", () => + { + control.Reset(category, false); + return control.Current.Value == criteria; + }); + } } } From 31bc067dd1f9a194729580875ef0a82f191988fb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 15 Sep 2022 15:38:44 +0900 Subject: [PATCH 124/142] Don't store nulls to parentMap --- .../Objects/Pooling/HitObjectEntryManager.cs | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs index 30a8e8f549..66aa2cfaf7 100644 --- a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs +++ b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs @@ -36,13 +36,12 @@ namespace osu.Game.Rulesets.Objects.Pooling /// /// Stores the parent hit object for entries of the nested hit objects. - /// A null is stored for entries of the top-level hit objects. /// /// /// The parent hit object of a pooled hit object may be non-pooled. /// In that case, no corresponding is stored in this . /// - private readonly Dictionary parentMap = new Dictionary(); + private readonly Dictionary parentMap = new Dictionary(); /// /// Stores the list of entries managed by this for each hit object managed by this . @@ -55,16 +54,17 @@ namespace osu.Game.Rulesets.Objects.Pooling throw new InvalidOperationException($@"The {nameof(HitObjectLifetimeEntry)} is already added to this {nameof(HitObjectEntryManager)}."); var hitObject = entry.HitObject; - parentMap[entry] = parent; entryMap[hitObject] = entry; - - if (parent != null && childrenMap.TryGetValue(parent, out var parentChildEntries)) - parentChildEntries.Add(entry); - - hitObject.DefaultsApplied += onDefaultsApplied; - childrenMap[hitObject] = new List(); + if (parent != null) + { + parentMap[entry] = parent; + if (childrenMap.TryGetValue(parent, out var parentChildEntries)) + parentChildEntries.Add(entry); + } + + hitObject.DefaultsApplied += onDefaultsApplied; OnEntryAdded?.Invoke(entry, parent); } @@ -74,14 +74,11 @@ namespace osu.Game.Rulesets.Objects.Pooling throw new InvalidOperationException($@"The {nameof(HitObjectLifetimeEntry)} is not contained in this {nameof(HitObjectLifetimeEntry)}."); var hitObject = entry.HitObject; - parentMap.Remove(entry, out var parent); entryMap.Remove(hitObject); - if (parent != null && childrenMap.TryGetValue(parent, out var parentChildEntries)) + if (parentMap.Remove(entry, out var parent) && childrenMap.TryGetValue(parent, out var parentChildEntries)) parentChildEntries.Remove(entry); - hitObject.DefaultsApplied -= onDefaultsApplied; - // Remove all entries of the nested hit objects if (childrenMap.Remove(entry.HitObject, out var childEntries)) { @@ -89,6 +86,7 @@ namespace osu.Game.Rulesets.Objects.Pooling Remove(childEntry); } + hitObject.DefaultsApplied -= onDefaultsApplied; OnEntryRemoved?.Invoke(entry, parent); } From 68e4d2289814dedf98fbe48dfe7116c3a30c9df6 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 15 Sep 2022 15:40:03 +0900 Subject: [PATCH 125/142] Precondition against entryMap intead of parentMap --- .../Objects/Pooling/HitObjectEntryManager.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs index 66aa2cfaf7..11caf79d8b 100644 --- a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs +++ b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs @@ -50,10 +50,11 @@ namespace osu.Game.Rulesets.Objects.Pooling public void Add(HitObjectLifetimeEntry entry, HitObject? parent) { - if (parentMap.ContainsKey(entry)) + HitObject hitObject = entry.HitObject; + + if (entryMap.ContainsKey(hitObject)) throw new InvalidOperationException($@"The {nameof(HitObjectLifetimeEntry)} is already added to this {nameof(HitObjectEntryManager)}."); - var hitObject = entry.HitObject; entryMap[hitObject] = entry; childrenMap[hitObject] = new List(); @@ -70,17 +71,18 @@ namespace osu.Game.Rulesets.Objects.Pooling public void Remove(HitObjectLifetimeEntry entry) { - if (!parentMap.ContainsKey(entry)) + HitObject hitObject = entry.HitObject; + + if (!entryMap.ContainsKey(hitObject)) throw new InvalidOperationException($@"The {nameof(HitObjectLifetimeEntry)} is not contained in this {nameof(HitObjectLifetimeEntry)}."); - var hitObject = entry.HitObject; entryMap.Remove(hitObject); if (parentMap.Remove(entry, out var parent) && childrenMap.TryGetValue(parent, out var parentChildEntries)) parentChildEntries.Remove(entry); // Remove all entries of the nested hit objects - if (childrenMap.Remove(entry.HitObject, out var childEntries)) + if (childrenMap.Remove(hitObject, out var childEntries)) { foreach (var childEntry in childEntries) Remove(childEntry); From c847cc521e34022b9146aa45b96aa069cd2715a1 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 15 Sep 2022 16:00:35 +0900 Subject: [PATCH 126/142] Add some inline comments --- .../Rulesets/Objects/Pooling/HitObjectEntryManager.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs index 11caf79d8b..0b30ec09f9 100644 --- a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs +++ b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs @@ -55,9 +55,11 @@ namespace osu.Game.Rulesets.Objects.Pooling if (entryMap.ContainsKey(hitObject)) throw new InvalidOperationException($@"The {nameof(HitObjectLifetimeEntry)} is already added to this {nameof(HitObjectEntryManager)}."); + // Add the entry. entryMap[hitObject] = entry; childrenMap[hitObject] = new List(); + // If the entry has a parent, set it and add the entry to the parent's children. if (parent != null) { parentMap[entry] = parent; @@ -74,14 +76,15 @@ namespace osu.Game.Rulesets.Objects.Pooling HitObject hitObject = entry.HitObject; if (!entryMap.ContainsKey(hitObject)) - throw new InvalidOperationException($@"The {nameof(HitObjectLifetimeEntry)} is not contained in this {nameof(HitObjectLifetimeEntry)}."); + throw new InvalidOperationException($@"The {nameof(HitObjectLifetimeEntry)} is not contained in this {nameof(HitObjectEntryManager)}."); entryMap.Remove(hitObject); + // If the entry has a parent, unset it and remove the entry from the parents' children. if (parentMap.Remove(entry, out var parent) && childrenMap.TryGetValue(parent, out var parentChildEntries)) parentChildEntries.Remove(entry); - // Remove all entries of the nested hit objects + // Remove all the entries' children. if (childrenMap.Remove(hitObject, out var childEntries)) { foreach (var childEntry in childEntries) @@ -105,9 +108,11 @@ namespace osu.Game.Rulesets.Objects.Pooling if (!childrenMap.Remove(hitObject, out var childEntries)) return; + // Remove all the entries' children. At this point the parents' (this entries') children list has been removed from the map, so this does not cause upwards traversal. foreach (var entry in childEntries) Remove(entry); + // The removed children list needs to be added back to the map for the entry to potentially receive children. childEntries.Clear(); childrenMap[hitObject] = childEntries; } From e56ea58e71ca78b6f284e37e0c68def4a5889eb5 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 15 Sep 2022 16:06:21 +0900 Subject: [PATCH 127/142] Fix up xmldoc --- osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs index 0b30ec09f9..6c39ea44da 100644 --- a/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs +++ b/osu.Game/Rulesets/Objects/Pooling/HitObjectEntryManager.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Objects.Pooling private readonly Dictionary parentMap = new Dictionary(); /// - /// Stores the list of entries managed by this for each hit object managed by this . + /// Stores the list of child entries for each hit object managed by this . /// private readonly Dictionary> childrenMap = new Dictionary>(); From 0329601e84c1a26929f0b89c3b86eb1e1ebf350e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 15 Sep 2022 16:09:27 +0900 Subject: [PATCH 128/142] Fix inspection --- .../Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs index 79e3cf000e..b6da562bd0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs @@ -103,7 +103,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("hit first hitobject", () => { InputManager.Click(MouseButton.Left); - return nextObjectEntry.Result.HasResult; + return nextObjectEntry.Result?.HasResult == true; }); AddAssert("check correct object after hit", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[1]); From 40a60f7145312992a29f0496fc9bb8c08827e1bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Sep 2022 16:31:00 +0900 Subject: [PATCH 129/142] Remove all entity framework code and migrations --- CodeAnalysis/BannedSymbols.txt | 2 - osu.Desktop/osu.Desktop.csproj | 4 - .../Navigation/TestEFToRealmMigration.cs | 63 -- .../Beatmaps/BeatmapUpdaterMetadataLookup.cs | 2 +- osu.Game/Database/DatabaseContextFactory.cs | 218 ------- osu.Game/Database/DatabaseWriteUsage.cs | 60 -- osu.Game/Database/EFToRealmMigrator.cs | 595 ------------------ osu.Game/Database/IDatabaseContextFactory.cs | 23 - osu.Game/Database/OsuDbContext.cs | 214 ------- .../20171019041408_InitialCreate.Designer.cs | 293 --------- .../20171019041408_InitialCreate.cs | 314 --------- ...025071459_AddMissingIndexRules.Designer.cs | 299 --------- .../20171025071459_AddMissingIndexRules.cs | 83 --- ...eatmapOnlineIDUniqueConstraint.Designer.cs | 302 --------- ...5731_AddBeatmapOnlineIDUniqueConstraint.cs | 26 - ...034410_AddRulesetInfoShortName.Designer.cs | 307 --------- .../20171209034410_AddRulesetInfoShortName.cs | 36 -- .../20180125143340_Settings.Designer.cs | 329 ---------- .../Migrations/20180125143340_Settings.cs | 58 -- .../20180131154205_AddMuteBinding.cs | 26 - .../20180219060912_AddSkins.Designer.cs | 379 ----------- .../Migrations/20180219060912_AddSkins.cs | 74 --- ...54_RemoveUniqueHashConstraints.Designer.cs | 377 ----------- ...80529055154_RemoveUniqueHashConstraints.cs | 54 -- ...111_UpdateTaikoDefaultBindings.Designer.cs | 376 ----------- ...180621044111_UpdateTaikoDefaultBindings.cs | 20 - ...628011956_RemoveNegativeSetIDs.Designer.cs | 376 ----------- .../20180628011956_RemoveNegativeSetIDs.cs | 21 - .../20180913080842_AddRankStatus.Designer.cs | 380 ----------- .../20180913080842_AddRankStatus.cs | 36 -- ...0181007180454_StandardizePaths.Designer.cs | 380 ----------- .../20181007180454_StandardizePaths.cs | 27 - ...20181128100659_AddSkinInfoHash.Designer.cs | 387 ------------ .../20181128100659_AddSkinInfoHash.cs | 44 -- ...81130113755_AddScoreInfoTables.Designer.cs | 484 -------------- .../20181130113755_AddScoreInfoTables.cs | 115 ---- ...20190225062029_AddUserIDColumn.Designer.cs | 487 -------------- .../20190225062029_AddUserIDColumn.cs | 25 - .../20190525060824_SkinSettings.Designer.cs | 498 --------------- .../Migrations/20190525060824_SkinSettings.cs | 57 -- ...AddDateAddedColumnToBeatmapSet.Designer.cs | 489 -------------- ...05091246_AddDateAddedColumnToBeatmapSet.cs | 27 - ...8070844_AddBPMAndLengthColumns.Designer.cs | 504 --------------- .../20190708070844_AddBPMAndLengthColumns.cs | 36 -- ...20190913104727_AddBeatmapVideo.Designer.cs | 506 --------------- .../20190913104727_AddBeatmapVideo.cs | 25 - ...02094919_RefreshVolumeBindings.Designer.cs | 506 --------------- .../20200302094919_RefreshVolumeBindings.cs | 19 - ...01019224408_AddEpilepsyWarning.Designer.cs | 508 --------------- .../20201019224408_AddEpilepsyWarning.cs | 26 - ...700_RefreshVolumeBindingsAgain.Designer.cs | 506 --------------- ...210412045700_RefreshVolumeBindingsAgain.cs | 19 - ...60743_AddSkinInstantiationInfo.Designer.cs | 508 --------------- ...20210511060743_AddSkinInstantiationInfo.cs | 25 - ...9_AddAuthorIdToBeatmapMetadata.Designer.cs | 511 --------------- ...0514062639_AddAuthorIdToBeatmapMetadata.cs | 26 - ...824185035_AddCountdownSettings.Designer.cs | 513 --------------- .../20210824185035_AddCountdownSettings.cs | 26 - ...11_AddSamplesMatchPlaybackRate.Designer.cs | 515 --------------- ...10912144011_AddSamplesMatchPlaybackRate.cs | 26 - .../20211020081609_ResetSkinHashes.cs | 23 - .../Migrations/OsuDbContextModelSnapshot.cs | 513 --------------- osu.Game/OsuGameBase.cs | 10 +- osu.Game/Screens/Loader.cs | 22 +- osu.sln.DotSettings | 1 - 65 files changed, 3 insertions(+), 13738 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Navigation/TestEFToRealmMigration.cs delete mode 100644 osu.Game/Database/DatabaseContextFactory.cs delete mode 100644 osu.Game/Database/DatabaseWriteUsage.cs delete mode 100644 osu.Game/Database/EFToRealmMigrator.cs delete mode 100644 osu.Game/Database/IDatabaseContextFactory.cs delete mode 100644 osu.Game/Database/OsuDbContext.cs delete mode 100644 osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs delete mode 100644 osu.Game/Migrations/20171019041408_InitialCreate.cs delete mode 100644 osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs delete mode 100644 osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs delete mode 100644 osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs delete mode 100644 osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs delete mode 100644 osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs delete mode 100644 osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs delete mode 100644 osu.Game/Migrations/20180125143340_Settings.Designer.cs delete mode 100644 osu.Game/Migrations/20180125143340_Settings.cs delete mode 100644 osu.Game/Migrations/20180131154205_AddMuteBinding.cs delete mode 100644 osu.Game/Migrations/20180219060912_AddSkins.Designer.cs delete mode 100644 osu.Game/Migrations/20180219060912_AddSkins.cs delete mode 100644 osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs delete mode 100644 osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs delete mode 100644 osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs delete mode 100644 osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs delete mode 100644 osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs delete mode 100644 osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs delete mode 100644 osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs delete mode 100644 osu.Game/Migrations/20180913080842_AddRankStatus.cs delete mode 100644 osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs delete mode 100644 osu.Game/Migrations/20181007180454_StandardizePaths.cs delete mode 100644 osu.Game/Migrations/20181128100659_AddSkinInfoHash.Designer.cs delete mode 100644 osu.Game/Migrations/20181128100659_AddSkinInfoHash.cs delete mode 100644 osu.Game/Migrations/20181130113755_AddScoreInfoTables.Designer.cs delete mode 100644 osu.Game/Migrations/20181130113755_AddScoreInfoTables.cs delete mode 100644 osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs delete mode 100644 osu.Game/Migrations/20190225062029_AddUserIDColumn.cs delete mode 100644 osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs delete mode 100644 osu.Game/Migrations/20190525060824_SkinSettings.cs delete mode 100644 osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs delete mode 100644 osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.cs delete mode 100644 osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs delete mode 100644 osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.cs delete mode 100644 osu.Game/Migrations/20190913104727_AddBeatmapVideo.Designer.cs delete mode 100644 osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs delete mode 100644 osu.Game/Migrations/20200302094919_RefreshVolumeBindings.Designer.cs delete mode 100644 osu.Game/Migrations/20200302094919_RefreshVolumeBindings.cs delete mode 100644 osu.Game/Migrations/20201019224408_AddEpilepsyWarning.Designer.cs delete mode 100644 osu.Game/Migrations/20201019224408_AddEpilepsyWarning.cs delete mode 100644 osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.Designer.cs delete mode 100644 osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs delete mode 100644 osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.Designer.cs delete mode 100644 osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs delete mode 100644 osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.Designer.cs delete mode 100644 osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.cs delete mode 100644 osu.Game/Migrations/20210824185035_AddCountdownSettings.Designer.cs delete mode 100644 osu.Game/Migrations/20210824185035_AddCountdownSettings.cs delete mode 100644 osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.Designer.cs delete mode 100644 osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.cs delete mode 100644 osu.Game/Migrations/20211020081609_ResetSkinHashes.cs delete mode 100644 osu.Game/Migrations/OsuDbContextModelSnapshot.cs diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index e779ee6658..022da0a2ea 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -6,8 +6,6 @@ T:System.IComparable;Don't use non-generic IComparable. Use generic version inst T:SixLabors.ImageSharp.IDeepCloneable`1;Use osu.Game.Utils.IDeepCloneable instead. M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText. M:osu.Framework.Bindables.IBindableList`1.GetBoundCopy();Fails on iOS. Use manual ctor + BindTo instead. (see https://github.com/mono/mono/issues/19900) -T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods. -T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods. T:NuGet.Packaging.CollectionExtensions;Don't use internal extension methods. M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.HasFlagFast() instead. M:Realms.IRealmCollection`1.SubscribeForNotifications`1(Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IRealmCollection,NotificationCallbackDelegate) instead. diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index c67017f175..8ef480d4e3 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -28,10 +28,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/osu.Game.Tests/Visual/Navigation/TestEFToRealmMigration.cs b/osu.Game.Tests/Visual/Navigation/TestEFToRealmMigration.cs deleted file mode 100644 index c3559589ed..0000000000 --- a/osu.Game.Tests/Visual/Navigation/TestEFToRealmMigration.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System.Linq; -using System.Runtime.InteropServices; -using NUnit.Framework; -using osu.Framework; -using osu.Framework.Allocation; -using osu.Game.Beatmaps; -using osu.Game.Database; -using osu.Game.Models; -using osu.Game.Scoring; -using osu.Game.Skinning; -using osu.Game.Tests.Resources; - -namespace osu.Game.Tests.Visual.Navigation -{ - public class TestEFToRealmMigration : OsuGameTestScene - { - public override void RecycleLocalStorage(bool isDisposing) - { - base.RecycleLocalStorage(isDisposing); - - if (isDisposing) - return; - - using (var outStream = LocalStorage.CreateFileSafely(DatabaseContextFactory.DATABASE_NAME)) - using (var stream = TestResources.OpenResource(DatabaseContextFactory.DATABASE_NAME)) - stream.CopyTo(outStream); - } - - [SetUp] - public void SetUp() - { - if (RuntimeInfo.OS == RuntimeInfo.Platform.macOS && RuntimeInformation.OSArchitecture == Architecture.Arm64) - Assert.Ignore("EF-to-realm migrations are not supported on M1 ARM architectures."); - } - - public override void SetUpSteps() - { - // base SetUpSteps are executed before the above SetUp, therefore early-return to allow ignoring test properly. - // attempting to ignore here would yield a TargetInvocationException instead. - if (RuntimeInfo.OS == RuntimeInfo.Platform.macOS && RuntimeInformation.OSArchitecture == Architecture.Arm64) - return; - - base.SetUpSteps(); - } - - [Test] - public void TestMigration() - { - // Numbers are taken from the test database (see commit f03de16ee5a46deac3b5f2ca1edfba5c4c5dca7d). - AddAssert("Check beatmaps", () => Game.Dependencies.Get().Run(r => r.All().Count(s => !s.Protected) == 1)); - AddAssert("Check skins", () => Game.Dependencies.Get().Run(r => r.All().Count(s => !s.Protected) == 1)); - AddAssert("Check scores", () => Game.Dependencies.Get().Run(r => r.All().Count() == 1)); - - // One extra file is created during realm migration / startup due to the circles intro import. - AddAssert("Check files", () => Game.Dependencies.Get().Run(r => r.All().Count() == 271)); - } - } -} diff --git a/osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs b/osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs index f96fcc2630..aa773da6d3 100644 --- a/osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs +++ b/osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs @@ -192,7 +192,7 @@ namespace osu.Game.Beatmaps try { - using (var db = new SqliteConnection(DatabaseContextFactory.CreateDatabaseConnectionString("online.db", storage))) + using (var db = new SqliteConnection(string.Concat("Data Source=", storage.GetFullPath($@"{"online.db"}", true)))) { db.Open(); diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs deleted file mode 100644 index af91fb4971..0000000000 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System; -using System.IO; -using System.Linq; -using System.Threading; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Framework.Logging; -using osu.Framework.Platform; -using osu.Framework.Statistics; - -namespace osu.Game.Database -{ - public class DatabaseContextFactory : IDatabaseContextFactory - { - private readonly Storage storage; - - public const string DATABASE_NAME = @"client.db"; - - private ThreadLocal threadContexts; - - private readonly object writeLock = new object(); - - private bool currentWriteDidWrite; - private bool currentWriteDidError; - - private int currentWriteUsages; - - private IDbContextTransaction currentWriteTransaction; - - public DatabaseContextFactory(Storage storage) - { - this.storage = storage; - recycleThreadContexts(); - } - - private static readonly GlobalStatistic reads = GlobalStatistics.Get("Database", "Get (Read)"); - private static readonly GlobalStatistic writes = GlobalStatistics.Get("Database", "Get (Write)"); - private static readonly GlobalStatistic commits = GlobalStatistics.Get("Database", "Commits"); - private static readonly GlobalStatistic rollbacks = GlobalStatistics.Get("Database", "Rollbacks"); - - /// - /// Get a context for the current thread for read-only usage. - /// If a is in progress, the existing write-safe context will be returned. - /// - public OsuDbContext Get() - { - reads.Value++; - return threadContexts.Value; - } - - /// - /// Request a context for write usage. Can be consumed in a nested fashion (and will return the same underlying context). - /// This method may block if a write is already active on a different thread. - /// - /// Whether to start a transaction for this write. - /// A usage containing a usable context. - public DatabaseWriteUsage GetForWrite(bool withTransaction = true) - { - writes.Value++; - Monitor.Enter(writeLock); - OsuDbContext context; - - try - { - if (currentWriteTransaction == null && withTransaction) - { - // this mitigates the fact that changes on tracked entities will not be rolled back with the transaction by ensuring write operations are always executed in isolated contexts. - // if this results in sub-optimal efficiency, we may need to look into removing Database-level transactions in favour of running SaveChanges where we currently commit the transaction. - if (threadContexts.IsValueCreated) - recycleThreadContexts(); - - context = threadContexts.Value; - currentWriteTransaction = context.Database.BeginTransaction(); - } - else - { - // we want to try-catch the retrieval of the context because it could throw an error (in CreateContext). - context = threadContexts.Value; - } - } - catch - { - // retrieval of a context could trigger a fatal error. - Monitor.Exit(writeLock); - throw; - } - - Interlocked.Increment(ref currentWriteUsages); - - return new DatabaseWriteUsage(context, usageCompleted) { IsTransactionLeader = currentWriteTransaction != null && currentWriteUsages == 1 }; - } - - private void usageCompleted(DatabaseWriteUsage usage) - { - int usages = Interlocked.Decrement(ref currentWriteUsages); - - try - { - currentWriteDidWrite |= usage.PerformedWrite; - currentWriteDidError |= usage.Errors.Any(); - - if (usages == 0) - { - if (currentWriteDidError) - { - rollbacks.Value++; - currentWriteTransaction?.Rollback(); - } - else - { - commits.Value++; - currentWriteTransaction?.Commit(); - } - - if (currentWriteDidWrite || currentWriteDidError) - { - // explicitly dispose to ensure any outstanding flushes happen as soon as possible (and underlying resources are purged). - usage.Context.Dispose(); - - // once all writes are complete, we want to refresh thread-specific contexts to make sure they don't have stale local caches. - recycleThreadContexts(); - } - - currentWriteTransaction = null; - currentWriteDidWrite = false; - currentWriteDidError = false; - } - } - finally - { - Monitor.Exit(writeLock); - } - } - - private void recycleThreadContexts() - { - // Contexts for other threads are not disposed as they may be in use elsewhere. Instead, fresh contexts are exposed - // for other threads to use, and we rely on the finalizer inside OsuDbContext to handle their previous contexts - threadContexts?.Value.Dispose(); - threadContexts = new ThreadLocal(CreateContext, true); - } - - protected virtual OsuDbContext CreateContext() => new OsuDbContext(CreateDatabaseConnectionString(DATABASE_NAME, storage)) - { - Database = { AutoTransactionsEnabled = false } - }; - - public void CreateBackup(string backupFilename) - { - Logger.Log($"Creating full EF database backup at {backupFilename}", LoggingTarget.Database); - - using (var source = storage.GetStream(DATABASE_NAME, mode: FileMode.Open)) - using (var destination = storage.GetStream(backupFilename, FileAccess.Write, FileMode.CreateNew)) - source.CopyTo(destination); - } - - public void ResetDatabase() - { - lock (writeLock) - { - recycleThreadContexts(); - - try - { - int attempts = 10; - - // Retry logic taken from MigratableStorage.AttemptOperation. - while (true) - { - try - { - storage.Delete(DATABASE_NAME); - return; - } - catch (Exception) - { - if (attempts-- == 0) - throw; - } - - Thread.Sleep(250); - } - } - catch - { - // for now we are not sure why file handles are kept open by EF, but this is generally only used in testing - } - } - } - - public void FlushConnections() - { - if (threadContexts != null) - { - foreach (var context in threadContexts.Values) - context.Dispose(); - } - - recycleThreadContexts(); - } - - public static string CreateDatabaseConnectionString(string filename, Storage storage) => string.Concat("Data Source=", storage.GetFullPath($@"{filename}", true)); - - private readonly ManualResetEventSlim migrationComplete = new ManualResetEventSlim(); - - public void SetMigrationCompletion() => migrationComplete.Set(); - - public void WaitForMigrationCompletion() - { - if (!migrationComplete.Wait(300000)) - throw new TimeoutException("Migration took too long (likely stuck)."); - } - } -} diff --git a/osu.Game/Database/DatabaseWriteUsage.cs b/osu.Game/Database/DatabaseWriteUsage.cs deleted file mode 100644 index b5f25eae20..0000000000 --- a/osu.Game/Database/DatabaseWriteUsage.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System; -using System.Collections.Generic; - -namespace osu.Game.Database -{ - public class DatabaseWriteUsage : IDisposable - { - public readonly OsuDbContext Context; - private readonly Action usageCompleted; - - public DatabaseWriteUsage(OsuDbContext context, Action onCompleted) - { - Context = context; - usageCompleted = onCompleted; - } - - public bool PerformedWrite { get; private set; } - - private bool isDisposed; - public List Errors = new List(); - - /// - /// Whether this write usage will commit a transaction on completion. - /// If false, there is a parent usage responsible for transaction commit. - /// - public bool IsTransactionLeader; - - protected void Dispose(bool disposing) - { - if (isDisposed) return; - - isDisposed = true; - - try - { - PerformedWrite |= Context.SaveChanges() > 0; - } - catch (Exception e) - { - Errors.Add(e); - throw; - } - finally - { - usageCompleted?.Invoke(this); - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - } -} diff --git a/osu.Game/Database/EFToRealmMigrator.cs b/osu.Game/Database/EFToRealmMigrator.cs deleted file mode 100644 index 294a8cd3ed..0000000000 --- a/osu.Game/Database/EFToRealmMigrator.cs +++ /dev/null @@ -1,595 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using osu.Framework; -using osu.Framework.Allocation; -using osu.Framework.Development; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Logging; -using osu.Framework.Platform; -using osu.Game.Beatmaps; -using osu.Game.Configuration; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Models; -using osu.Game.Overlays; -using osu.Game.Overlays.Notifications; -using osu.Game.Rulesets; -using osu.Game.Scoring; -using osu.Game.Skinning; -using osuTK; -using Realms; -using SharpCompress.Archives; -using SharpCompress.Archives.Zip; -using SharpCompress.Common; -using SharpCompress.Writers.Zip; - -namespace osu.Game.Database -{ - internal class EFToRealmMigrator : CompositeDrawable - { - public Task MigrationCompleted => migrationCompleted.Task; - - private readonly TaskCompletionSource migrationCompleted = new TaskCompletionSource(); - - [Resolved] - private DatabaseContextFactory efContextFactory { get; set; } = null!; - - [Resolved] - private RealmAccess realm { get; set; } = null!; - - [Resolved] - private OsuConfigManager config { get; set; } = null!; - - [Resolved] - private INotificationOverlay notificationOverlay { get; set; } = null!; - - [Resolved] - private OsuGame game { get; set; } = null!; - - [Resolved] - private Storage storage { get; set; } = null!; - - private readonly OsuTextFlowContainer currentOperationText; - - public EFToRealmMigrator() - { - RelativeSizeAxes = Axes.Both; - - InternalChildren = new Drawable[] - { - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Spacing = new Vector2(10), - Children = new Drawable[] - { - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = "Database migration in progress", - Font = OsuFont.Default.With(size: 40) - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = "This could take a few minutes depending on the speed of your disk(s).", - Font = OsuFont.Default.With(size: 30) - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = "Please keep the window open until this completes!", - Font = OsuFont.Default.With(size: 30) - }, - new LoadingSpinner(true) - { - State = { Value = Visibility.Visible } - }, - currentOperationText = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 30)) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - TextAnchor = Anchor.TopCentre, - }, - } - }, - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - beginMigration(); - } - - private void beginMigration() - { - const string backup_folder = "backups"; - - string backupSuffix = $"before_final_migration_{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}"; - - // required for initial backup. - var realmBlockOperations = realm.BlockAllOperations("EF migration"); - - Task.Factory.StartNew(() => - { - try - { - realm.CreateBackup(Path.Combine(backup_folder, $"client.{backupSuffix}.realm")); - } - finally - { - // Once the backup is created, we need to stop blocking operations so the migration can complete. - realmBlockOperations.Dispose(); - // Clean up here so we don't accidentally dispose twice. - realmBlockOperations = null; - } - - efContextFactory.CreateBackup(Path.Combine(backup_folder, $"client.{backupSuffix}.db")); - - using (var ef = efContextFactory.Get()) - { - realm.Write(r => - { - // Before beginning, ensure realm is in an empty state. - // Migrations which are half-completed could lead to issues if the user tries a second time. - // Note that we only do this for beatmaps and scores since the other migrations are yonks old. - r.RemoveAll(); - r.RemoveAll(); - r.RemoveAll(); - r.RemoveAll(); - }); - - ef.Migrate(); - - migrateSettings(ef); - migrateSkins(ef); - migrateBeatmaps(ef); - migrateScores(ef); - } - }, TaskCreationOptions.LongRunning).ContinueWith(t => - { - if (t.Exception == null) - { - log("Migration successful!"); - - if (DebugUtils.IsDebugBuild) - { - Logger.Log( - "Your development database has been fully migrated to realm. If you switch back to a pre-realm branch and need your previous database, rename the backup file back to \"client.db\".\n\nNote that doing this can potentially leave your file store in a bad state.", - level: LogLevel.Important); - } - } - else - { - log("Migration failed!"); - Logger.Log(t.Exception.ToString(), LoggingTarget.Database); - - if (RuntimeInfo.OS == RuntimeInfo.Platform.macOS && t.Exception.Flatten().InnerException is TypeInitializationException) - { - // Not guaranteed to be the only cause of exception, but let's roll with it for now. - log("Please download and run the intel version of osu! once\nto allow data migration to complete!"); - efContextFactory.SetMigrationCompletion(); - return; - } - - notificationOverlay.Post(new SimpleErrorNotification - { - Text = - "IMPORTANT: During data migration, some of your data could not be successfully migrated. The previous version has been backed up.\n\nFor further assistance, please open a discussion on github and attach your backup files (click to get started).", - Activated = () => - { - game.OpenUrlExternally( - $@"https://github.com/ppy/osu/discussions/new?title=Realm%20migration%20issue ({t.Exception.Message})&body=Please%20drag%20the%20""attach_me.zip""%20file%20here!&category=q-a", - true); - - const string attachment_filename = "attach_me.zip"; - - var backupStorage = storage.GetStorageForDirectory(backup_folder); - - backupStorage.Delete(attachment_filename); - - try - { - using (var zip = ZipArchive.Create()) - { - zip.AddAllFromDirectory(backupStorage.GetFullPath(string.Empty)); - zip.SaveTo(Path.Combine(backupStorage.GetFullPath(string.Empty), attachment_filename), new ZipWriterOptions(CompressionType.Deflate)); - } - } - catch { } - - backupStorage.PresentFileExternally(attachment_filename); - - return true; - } - }); - } - - // Regardless of success, since the game is going to continue with startup let's move the ef database out of the way. - // If we were to not do this, the migration would run another time the next time the user starts the game. - deletePreRealmData(); - - // If something went wrong and the disposal token wasn't invoked above, ensure it is here. - realmBlockOperations?.Dispose(); - - migrationCompleted.SetResult(true); - efContextFactory.SetMigrationCompletion(); - }); - } - - private void deletePreRealmData() - { - // Delete the database permanently. - // Will cause future startups to not attempt migration. - efContextFactory.ResetDatabase(); - } - - private void log(string message) - { - Logger.Log(message, LoggingTarget.Database); - Scheduler.AddOnce(m => currentOperationText.Text = m, message); - } - - private void migrateBeatmaps(OsuDbContext ef) - { - // can be removed 20220730. - var existingBeatmapSets = ef.EFBeatmapSetInfo - .Include(s => s.Beatmaps).ThenInclude(b => b.RulesetInfo) - .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) - .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) - .Include(s => s.Files).ThenInclude(f => f.FileInfo) - .Include(s => s.Metadata) - .AsSplitQuery(); - - log("Beginning beatmaps migration to realm"); - - // previous entries in EF are removed post migration. - if (!existingBeatmapSets.Any()) - { - log("No beatmaps found to migrate"); - return; - } - - int count = existingBeatmapSets.Count(); - - realm.Run(r => - { - log($"Found {count} beatmaps in EF"); - - var transaction = r.BeginWrite(); - int written = 0; - int missing = 0; - - try - { - foreach (var beatmapSet in existingBeatmapSets) - { - if (++written % 1000 == 0) - { - transaction.Commit(); - transaction = r.BeginWrite(); - log($"Migrated {written}/{count} beatmaps..."); - } - - var realmBeatmapSet = new BeatmapSetInfo - { - OnlineID = beatmapSet.OnlineID ?? -1, - DateAdded = beatmapSet.DateAdded, - Status = beatmapSet.Status, - DeletePending = beatmapSet.DeletePending, - Hash = beatmapSet.Hash, - Protected = beatmapSet.Protected, - }; - - migrateFiles(beatmapSet, r, realmBeatmapSet); - - foreach (var beatmap in beatmapSet.Beatmaps) - { - var ruleset = r.Find(beatmap.RulesetInfo.ShortName); - var metadata = getBestMetadata(beatmap.Metadata, beatmapSet.Metadata); - - if (ruleset == null) - { - log($"Skipping {++missing} beatmaps with missing ruleset"); - continue; - } - - var realmBeatmap = new BeatmapInfo(ruleset, new BeatmapDifficulty(beatmap.BaseDifficulty), metadata) - { - DifficultyName = beatmap.DifficultyName, - Status = beatmap.Status, - OnlineID = beatmap.OnlineID ?? -1, - Length = beatmap.Length, - BPM = beatmap.BPM, - Hash = beatmap.Hash, - StarRating = beatmap.StarRating, - MD5Hash = beatmap.MD5Hash, - Hidden = beatmap.Hidden, - AudioLeadIn = beatmap.AudioLeadIn, - StackLeniency = beatmap.StackLeniency, - SpecialStyle = beatmap.SpecialStyle, - LetterboxInBreaks = beatmap.LetterboxInBreaks, - WidescreenStoryboard = beatmap.WidescreenStoryboard, - EpilepsyWarning = beatmap.EpilepsyWarning, - SamplesMatchPlaybackRate = beatmap.SamplesMatchPlaybackRate, - DistanceSpacing = beatmap.DistanceSpacing, - BeatDivisor = beatmap.BeatDivisor, - GridSize = beatmap.GridSize, - TimelineZoom = beatmap.TimelineZoom, - Countdown = beatmap.Countdown, - CountdownOffset = beatmap.CountdownOffset, - Bookmarks = beatmap.Bookmarks, - BeatmapSet = realmBeatmapSet, - }; - - realmBeatmapSet.Beatmaps.Add(realmBeatmap); - } - - r.Add(realmBeatmapSet); - } - } - finally - { - transaction.Commit(); - } - - log($"Successfully migrated {count} beatmaps to realm"); - }); - } - - private BeatmapMetadata getBestMetadata(EFBeatmapMetadata? beatmapMetadata, EFBeatmapMetadata? beatmapSetMetadata) - { - var metadata = beatmapMetadata ?? beatmapSetMetadata ?? new EFBeatmapMetadata(); - - return new BeatmapMetadata - { - Title = metadata.Title, - TitleUnicode = metadata.TitleUnicode, - Artist = metadata.Artist, - ArtistUnicode = metadata.ArtistUnicode, - Author = - { - OnlineID = metadata.Author.Id, - Username = metadata.Author.Username, - }, - Source = metadata.Source, - Tags = metadata.Tags, - PreviewTime = metadata.PreviewTime, - AudioFile = metadata.AudioFile, - BackgroundFile = metadata.BackgroundFile, - }; - } - - private void migrateScores(OsuDbContext db) - { - // can be removed 20220730. - var existingScores = db.ScoreInfo - .Include(s => s.Ruleset) - .Include(s => s.BeatmapInfo) - .Include(s => s.Files) - .ThenInclude(f => f.FileInfo) - .AsSplitQuery(); - - log("Beginning scores migration to realm"); - - // previous entries in EF are removed post migration. - if (!existingScores.Any()) - { - log("No scores found to migrate"); - return; - } - - int count = existingScores.Count(); - - realm.Run(r => - { - log($"Found {count} scores in EF"); - - var transaction = r.BeginWrite(); - int written = 0; - int missing = 0; - - try - { - foreach (var score in existingScores) - { - if (++written % 1000 == 0) - { - transaction.Commit(); - transaction = r.BeginWrite(); - log($"Migrated {written}/{count} scores..."); - } - - var beatmap = r.All().FirstOrDefault(b => b.Hash == score.BeatmapInfo.Hash); - var ruleset = r.Find(score.Ruleset.ShortName); - - if (beatmap == null || ruleset == null) - { - log($"Skipping {++missing} scores with missing ruleset or beatmap"); - continue; - } - - var user = new RealmUser - { - OnlineID = score.User.OnlineID, - Username = score.User.Username - }; - - var realmScore = new ScoreInfo(beatmap, ruleset, user) - { - Hash = score.Hash, - DeletePending = score.DeletePending, - OnlineID = score.OnlineID ?? -1, - ModsJson = score.ModsJson, - StatisticsJson = score.StatisticsJson, - TotalScore = score.TotalScore, - MaxCombo = score.MaxCombo, - Accuracy = score.Accuracy, - Date = score.Date, - PP = score.PP, - Rank = score.Rank, - HitEvents = score.HitEvents, - Passed = score.Passed, - Combo = score.Combo, - Position = score.Position, - Statistics = score.Statistics, - Mods = score.Mods, - APIMods = score.APIMods, - }; - - migrateFiles(score, r, realmScore); - - r.Add(realmScore); - } - } - finally - { - transaction.Commit(); - } - - log($"Successfully migrated {count} scores to realm"); - }); - } - - private void migrateSkins(OsuDbContext db) - { - // can be removed 20220530. - var existingSkins = db.SkinInfo - .Include(s => s.Files) - .ThenInclude(f => f.FileInfo) - .AsSplitQuery() - .ToList(); - - // previous entries in EF are removed post migration. - if (!existingSkins.Any()) - return; - - var userSkinChoice = config.GetBindable(OsuSetting.Skin); - int.TryParse(userSkinChoice.Value, out int userSkinInt); - - switch (userSkinInt) - { - case EFSkinInfo.DEFAULT_SKIN: - userSkinChoice.Value = SkinInfo.DEFAULT_SKIN.ToString(); - break; - - case EFSkinInfo.CLASSIC_SKIN: - userSkinChoice.Value = SkinInfo.CLASSIC_SKIN.ToString(); - break; - } - - realm.Run(r => - { - using (var transaction = r.BeginWrite()) - { - // only migrate data if the realm database is empty. - // note that this cannot be written as: `r.All().All(s => s.Protected)`, because realm does not support `.All()`. - if (!r.All().Any(s => !s.Protected)) - { - log($"Migrating {existingSkins.Count} skins"); - - foreach (var skin in existingSkins) - { - var realmSkin = new SkinInfo - { - Name = skin.Name, - Creator = skin.Creator, - Hash = skin.Hash, - Protected = false, - InstantiationInfo = skin.InstantiationInfo, - }; - - migrateFiles(skin, r, realmSkin); - - r.Add(realmSkin); - - if (skin.ID == userSkinInt) - userSkinChoice.Value = realmSkin.ID.ToString(); - } - } - - transaction.Commit(); - } - }); - } - - private static void migrateFiles(IHasFiles fileSource, Realm realm, IHasRealmFiles realmObject) where T : INamedFileInfo - { - foreach (var file in fileSource.Files) - { - var realmFile = realm.Find(file.FileInfo.Hash); - - if (realmFile == null) - realm.Add(realmFile = new RealmFile { Hash = file.FileInfo.Hash }); - - realmObject.Files.Add(new RealmNamedFileUsage(realmFile, file.Filename)); - } - } - - private void migrateSettings(OsuDbContext db) - { - // migrate ruleset settings. can be removed 20220315. - var existingSettings = db.DatabasedSetting.ToList(); - - // previous entries in EF are removed post migration. - if (!existingSettings.Any()) - return; - - log("Beginning settings migration to realm"); - - realm.Run(r => - { - using (var transaction = r.BeginWrite()) - { - // only migrate data if the realm database is empty. - if (!r.All().Any()) - { - log($"Migrating {existingSettings.Count} settings"); - - foreach (var dkb in existingSettings) - { - if (dkb.RulesetID == null) - continue; - - string? shortName = getRulesetShortNameFromLegacyID(dkb.RulesetID.Value); - - if (string.IsNullOrEmpty(shortName)) - continue; - - r.Add(new RealmRulesetSetting - { - Key = dkb.Key, - Value = dkb.StringValue, - RulesetName = shortName, - Variant = dkb.Variant ?? 0, - }); - } - } - - transaction.Commit(); - } - }); - } - - private string? getRulesetShortNameFromLegacyID(long rulesetId) => - efContextFactory.Get().RulesetInfo.FirstOrDefault(r => r.ID == rulesetId)?.ShortName; - } -} diff --git a/osu.Game/Database/IDatabaseContextFactory.cs b/osu.Game/Database/IDatabaseContextFactory.cs deleted file mode 100644 index bddabad586..0000000000 --- a/osu.Game/Database/IDatabaseContextFactory.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -namespace osu.Game.Database -{ - public interface IDatabaseContextFactory - { - /// - /// Get a context for read-only usage. - /// - OsuDbContext Get(); - - /// - /// Request a context for write usage. Can be consumed in a nested fashion (and will return the same underlying context). - /// This method may block if a write is already active on a different thread. - /// - /// Whether to start a transaction for this write. - /// A usage containing a usable context. - DatabaseWriteUsage GetForWrite(bool withTransaction = true); - } -} diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs deleted file mode 100644 index 118a8ee469..0000000000 --- a/osu.Game/Database/OsuDbContext.cs +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using osu.Framework.Logging; -using osu.Framework.Statistics; -using osu.Game.Beatmaps; -using osu.Game.Configuration; -using osu.Game.IO; -using osu.Game.Rulesets; -using osu.Game.Scoring; -using osu.Game.Skinning; -using SQLitePCL; -using LogLevel = Microsoft.Extensions.Logging.LogLevel; - -namespace osu.Game.Database -{ - public class OsuDbContext : DbContext - { - public DbSet EFBeatmapInfo { get; set; } - public DbSet BeatmapDifficulty { get; set; } - public DbSet BeatmapMetadata { get; set; } - public DbSet EFBeatmapSetInfo { get; set; } - public DbSet FileInfo { get; set; } - public DbSet RulesetInfo { get; set; } - public DbSet SkinInfo { get; set; } - public DbSet ScoreInfo { get; set; } - - // migrated to realm - public DbSet DatabasedSetting { get; set; } - - private readonly string connectionString; - - private static readonly Lazy logger = new Lazy(() => new OsuDbLoggerFactory()); - - private static readonly GlobalStatistic contexts = GlobalStatistics.Get("Database", "Contexts"); - - static OsuDbContext() - { - // required to initialise native SQLite libraries on some platforms. - Batteries_V2.Init(); - - // https://github.com/aspnet/EntityFrameworkCore/issues/9994#issuecomment-508588678 - raw.sqlite3_config(2 /*SQLITE_CONFIG_MULTITHREAD*/); - } - - /// - /// Create a new in-memory OsuDbContext instance. - /// - public OsuDbContext() - : this("DataSource=:memory:") - { - // required for tooling (see https://wildermuth.com/2017/07/06/Program-cs-in-ASP-NET-Core-2-0). - - Migrate(); - } - - /// - /// Create a new OsuDbContext instance. - /// - /// A valid SQLite connection string. - public OsuDbContext(string connectionString) - { - this.connectionString = connectionString; - - var connection = Database.GetDbConnection(); - - try - { - connection.Open(); - - using (var cmd = connection.CreateCommand()) - { - cmd.CommandText = "PRAGMA journal_mode=WAL;"; - cmd.ExecuteNonQuery(); - - cmd.CommandText = "PRAGMA foreign_keys=OFF;"; - cmd.ExecuteNonQuery(); - } - } - catch - { - connection.Close(); - throw; - } - - contexts.Value++; - } - - ~OsuDbContext() - { - // DbContext does not contain a finalizer (https://github.com/aspnet/EntityFrameworkCore/issues/8872) - // This is used to clean up previous contexts when fresh contexts are exposed via DatabaseContextFactory - Dispose(); - } - - private bool isDisposed; - - public override void Dispose() - { - if (isDisposed) return; - - isDisposed = true; - - base.Dispose(); - - contexts.Value--; - GC.SuppressFinalize(this); - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - base.OnConfiguring(optionsBuilder); - optionsBuilder - // this is required for the time being due to the way we are querying in places like BeatmapStore. - // if we ever move to having consumers file their own .Includes, or get eager loading support, this could be re-enabled. - .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10)) - .UseLoggerFactory(logger.Value); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - modelBuilder.Entity().HasIndex(b => b.OnlineID).IsUnique(); - modelBuilder.Entity().HasIndex(b => b.MD5Hash); - modelBuilder.Entity().HasIndex(b => b.Hash); - - modelBuilder.Entity().HasIndex(b => b.OnlineID).IsUnique(); - modelBuilder.Entity().HasIndex(b => b.DeletePending); - modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); - - modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); - modelBuilder.Entity().HasIndex(b => b.DeletePending); - modelBuilder.Entity().HasMany(s => s.Files).WithOne(f => f.SkinInfo); - - modelBuilder.Entity().HasIndex(b => new { b.RulesetID, b.Variant }); - - modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique(); - modelBuilder.Entity().HasIndex(b => b.ReferenceCount); - - modelBuilder.Entity().HasIndex(b => b.Available); - modelBuilder.Entity().HasIndex(b => b.ShortName).IsUnique(); - - modelBuilder.Entity().HasOne(b => b.BaseDifficulty); - - modelBuilder.Entity().HasIndex(b => b.OnlineID).IsUnique(); - } - - private class OsuDbLoggerFactory : ILoggerFactory - { - #region Disposal - - public void Dispose() - { - } - - #endregion - - public ILogger CreateLogger(string categoryName) => new OsuDbLogger(); - - public void AddProvider(ILoggerProvider provider) - { - // no-op. called by tooling. - } - - private class OsuDbLogger : ILogger - { - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - if (logLevel < LogLevel.Information) - return; - - Framework.Logging.LogLevel frameworkLogLevel; - - switch (logLevel) - { - default: - frameworkLogLevel = Framework.Logging.LogLevel.Debug; - break; - - case LogLevel.Warning: - frameworkLogLevel = Framework.Logging.LogLevel.Important; - break; - - case LogLevel.Error: - case LogLevel.Critical: - frameworkLogLevel = Framework.Logging.LogLevel.Error; - break; - } - - Logger.Log(formatter(state, exception), LoggingTarget.Database, frameworkLogLevel); - } - - public bool IsEnabled(LogLevel logLevel) - { -#if DEBUG_DATABASE - return logLevel > LogLevel.Debug; -#else - return logLevel > LogLevel.Information; -#endif - } - - public IDisposable BeginScope(TState state) => null; - } - } - - public void Migrate() => Database.Migrate(); - } -} diff --git a/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs b/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs deleted file mode 100644 index c751530bf4..0000000000 --- a/osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs +++ /dev/null @@ -1,293 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Game.Database; -using System; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20171019041408_InitialCreate")] - partial class InitialCreate - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash"); - - b.HasIndex("MetadataID"); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20171019041408_InitialCreate.cs b/osu.Game/Migrations/20171019041408_InitialCreate.cs deleted file mode 100644 index 08ab64fd08..0000000000 --- a/osu.Game/Migrations/20171019041408_InitialCreate.cs +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class InitialCreate : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "BeatmapDifficulty", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - ApproachRate = table.Column(type: "REAL", nullable: false), - CircleSize = table.Column(type: "REAL", nullable: false), - DrainRate = table.Column(type: "REAL", nullable: false), - OverallDifficulty = table.Column(type: "REAL", nullable: false), - SliderMultiplier = table.Column(type: "REAL", nullable: false), - SliderTickRate = table.Column(type: "REAL", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapDifficulty", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "BeatmapMetadata", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Artist = table.Column(type: "TEXT", nullable: true), - ArtistUnicode = table.Column(type: "TEXT", nullable: true), - AudioFile = table.Column(type: "TEXT", nullable: true), - Author = table.Column(type: "TEXT", nullable: true), - BackgroundFile = table.Column(type: "TEXT", nullable: true), - PreviewTime = table.Column(type: "INTEGER", nullable: false), - Source = table.Column(type: "TEXT", nullable: true), - Tags = table.Column(type: "TEXT", nullable: true), - Title = table.Column(type: "TEXT", nullable: true), - TitleUnicode = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapMetadata", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "FileInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Hash = table.Column(type: "TEXT", nullable: true), - ReferenceCount = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_FileInfo", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "KeyBinding", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Action = table.Column(type: "INTEGER", nullable: false), - Keys = table.Column(type: "TEXT", nullable: true), - RulesetID = table.Column(type: "INTEGER", nullable: true), - Variant = table.Column(type: "INTEGER", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_KeyBinding", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "RulesetInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Available = table.Column(type: "INTEGER", nullable: false), - InstantiationInfo = table.Column(type: "TEXT", nullable: true), - Name = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_RulesetInfo", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "BeatmapSetInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - DeletePending = table.Column(type: "INTEGER", nullable: false), - Hash = table.Column(type: "TEXT", nullable: true), - MetadataID = table.Column(type: "INTEGER", nullable: true), - OnlineBeatmapSetID = table.Column(type: "INTEGER", nullable: true), - Protected = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapSetInfo", x => x.ID); - table.ForeignKey( - name: "FK_BeatmapSetInfo_BeatmapMetadata_MetadataID", - column: x => x.MetadataID, - principalTable: "BeatmapMetadata", - principalColumn: "ID", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "BeatmapInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - AudioLeadIn = table.Column(type: "INTEGER", nullable: false), - BaseDifficultyID = table.Column(type: "INTEGER", nullable: false), - BeatDivisor = table.Column(type: "INTEGER", nullable: false), - BeatmapSetInfoID = table.Column(type: "INTEGER", nullable: false), - Countdown = table.Column(type: "INTEGER", nullable: false), - DistanceSpacing = table.Column(type: "REAL", nullable: false), - GridSize = table.Column(type: "INTEGER", nullable: false), - Hash = table.Column(type: "TEXT", nullable: true), - Hidden = table.Column(type: "INTEGER", nullable: false), - LetterboxInBreaks = table.Column(type: "INTEGER", nullable: false), - MD5Hash = table.Column(type: "TEXT", nullable: true), - MetadataID = table.Column(type: "INTEGER", nullable: true), - OnlineBeatmapID = table.Column(type: "INTEGER", nullable: true), - Path = table.Column(type: "TEXT", nullable: true), - RulesetID = table.Column(type: "INTEGER", nullable: false), - SpecialStyle = table.Column(type: "INTEGER", nullable: false), - StackLeniency = table.Column(type: "REAL", nullable: false), - StarDifficulty = table.Column(type: "REAL", nullable: false), - StoredBookmarks = table.Column(type: "TEXT", nullable: true), - TimelineZoom = table.Column(type: "REAL", nullable: false), - Version = table.Column(type: "TEXT", nullable: true), - WidescreenStoryboard = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapInfo", x => x.ID); - table.ForeignKey( - name: "FK_BeatmapInfo_BeatmapDifficulty_BaseDifficultyID", - column: x => x.BaseDifficultyID, - principalTable: "BeatmapDifficulty", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_BeatmapInfo_BeatmapSetInfo_BeatmapSetInfoID", - column: x => x.BeatmapSetInfoID, - principalTable: "BeatmapSetInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_BeatmapInfo_BeatmapMetadata_MetadataID", - column: x => x.MetadataID, - principalTable: "BeatmapMetadata", - principalColumn: "ID", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_BeatmapInfo_RulesetInfo_RulesetID", - column: x => x.RulesetID, - principalTable: "RulesetInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "BeatmapSetFileInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - BeatmapSetInfoID = table.Column(type: "INTEGER", nullable: false), - FileInfoID = table.Column(type: "INTEGER", nullable: false), - Filename = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BeatmapSetFileInfo", x => x.ID); - table.ForeignKey( - name: "FK_BeatmapSetFileInfo_BeatmapSetInfo_BeatmapSetInfoID", - column: x => x.BeatmapSetInfoID, - principalTable: "BeatmapSetInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_BeatmapSetFileInfo_FileInfo_FileInfoID", - column: x => x.FileInfoID, - principalTable: "FileInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_BaseDifficultyID", - table: "BeatmapInfo", - column: "BaseDifficultyID"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_BeatmapSetInfoID", - table: "BeatmapInfo", - column: "BeatmapSetInfoID"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo", - column: "Hash"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo", - column: "MD5Hash"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_MetadataID", - table: "BeatmapInfo", - column: "MetadataID"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_RulesetID", - table: "BeatmapInfo", - column: "RulesetID"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetFileInfo_BeatmapSetInfoID", - table: "BeatmapSetFileInfo", - column: "BeatmapSetInfoID"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetFileInfo_FileInfoID", - table: "BeatmapSetFileInfo", - column: "FileInfoID"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetInfo_DeletePending", - table: "BeatmapSetInfo", - column: "DeletePending"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetInfo_Hash", - table: "BeatmapSetInfo", - column: "Hash"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetInfo_MetadataID", - table: "BeatmapSetInfo", - column: "MetadataID"); - - migrationBuilder.CreateIndex( - name: "IX_FileInfo_Hash", - table: "FileInfo", - column: "Hash", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_FileInfo_ReferenceCount", - table: "FileInfo", - column: "ReferenceCount"); - - migrationBuilder.CreateIndex( - name: "IX_KeyBinding_Action", - table: "KeyBinding", - column: "Action"); - - migrationBuilder.CreateIndex( - name: "IX_KeyBinding_Variant", - table: "KeyBinding", - column: "Variant"); - - migrationBuilder.CreateIndex( - name: "IX_RulesetInfo_Available", - table: "RulesetInfo", - column: "Available"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "BeatmapInfo"); - - migrationBuilder.DropTable( - name: "BeatmapSetFileInfo"); - - migrationBuilder.DropTable( - name: "KeyBinding"); - - migrationBuilder.DropTable( - name: "BeatmapDifficulty"); - - migrationBuilder.DropTable( - name: "RulesetInfo"); - - migrationBuilder.DropTable( - name: "BeatmapSetInfo"); - - migrationBuilder.DropTable( - name: "FileInfo"); - - migrationBuilder.DropTable( - name: "BeatmapMetadata"); - } - } -} diff --git a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs deleted file mode 100644 index 4cd234f2ef..0000000000 --- a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.Designer.cs +++ /dev/null @@ -1,299 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Game.Database; -using System; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20171025071459_AddMissingIndexRules")] - partial class AddMissingIndexRules - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MD5Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs b/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs deleted file mode 100644 index 4ec3952941..0000000000 --- a/osu.Game/Migrations/20171025071459_AddMissingIndexRules.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddMissingIndexRules : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_BeatmapSetInfo_Hash", - table: "BeatmapSetInfo"); - - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo"); - - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetInfo_Hash", - table: "BeatmapSetInfo", - column: "Hash", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetInfo_OnlineBeatmapSetID", - table: "BeatmapSetInfo", - column: "OnlineBeatmapSetID", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo", - column: "Hash", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo", - column: "MD5Hash", - unique: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_BeatmapSetInfo_Hash", - table: "BeatmapSetInfo"); - - migrationBuilder.DropIndex( - name: "IX_BeatmapSetInfo_OnlineBeatmapSetID", - table: "BeatmapSetInfo"); - - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo"); - - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapSetInfo_Hash", - table: "BeatmapSetInfo", - column: "Hash"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo", - column: "Hash"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo", - column: "MD5Hash"); - } - } -} diff --git a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs deleted file mode 100644 index 006acf12cd..0000000000 --- a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.Designer.cs +++ /dev/null @@ -1,302 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Game.Database; -using System; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20171119065731_AddBeatmapOnlineIDUniqueConstraint")] - partial class AddBeatmapOnlineIDUniqueConstraint - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MD5Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs b/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs deleted file mode 100644 index 6aba12f86f..0000000000 --- a/osu.Game/Migrations/20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddBeatmapOnlineIDUniqueConstraint : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_OnlineBeatmapID", - table: "BeatmapInfo", - column: "OnlineBeatmapID", - unique: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_OnlineBeatmapID", - table: "BeatmapInfo"); - } - } -} diff --git a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs deleted file mode 100644 index fc2496bc24..0000000000 --- a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.Designer.cs +++ /dev/null @@ -1,307 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Game.Database; -using System; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20171209034410_AddRulesetInfoShortName")] - partial class AddRulesetInfoShortName - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MD5Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs b/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs deleted file mode 100644 index 5688455f79..0000000000 --- a/osu.Game/Migrations/20171209034410_AddRulesetInfoShortName.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddRulesetInfoShortName : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "ShortName", - table: "RulesetInfo", - type: "TEXT", - nullable: true); - - migrationBuilder.CreateIndex( - name: "IX_RulesetInfo_ShortName", - table: "RulesetInfo", - column: "ShortName", - unique: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_RulesetInfo_ShortName", - table: "RulesetInfo"); - - migrationBuilder.DropColumn( - name: "ShortName", - table: "RulesetInfo"); - } - } -} diff --git a/osu.Game/Migrations/20180125143340_Settings.Designer.cs b/osu.Game/Migrations/20180125143340_Settings.Designer.cs deleted file mode 100644 index 4bb599eec1..0000000000 --- a/osu.Game/Migrations/20180125143340_Settings.Designer.cs +++ /dev/null @@ -1,329 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Game.Database; -using System; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20180125143340_Settings")] - partial class Settings - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MD5Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntKey") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20180125143340_Settings.cs b/osu.Game/Migrations/20180125143340_Settings.cs deleted file mode 100644 index 1feb37531f..0000000000 --- a/osu.Game/Migrations/20180125143340_Settings.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class Settings : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_KeyBinding_Variant", - table: "KeyBinding"); - - migrationBuilder.CreateTable( - name: "Settings", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Key = table.Column(type: "TEXT", nullable: false), - RulesetID = table.Column(type: "INTEGER", nullable: true), - Value = table.Column(type: "TEXT", nullable: true), - Variant = table.Column(type: "INTEGER", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Settings", x => x.ID); - }); - - migrationBuilder.CreateIndex( - name: "IX_KeyBinding_RulesetID_Variant", - table: "KeyBinding", - columns: new[] { "RulesetID", "Variant" }); - - migrationBuilder.CreateIndex( - name: "IX_Settings_RulesetID_Variant", - table: "Settings", - columns: new[] { "RulesetID", "Variant" }); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Settings"); - - migrationBuilder.DropIndex( - name: "IX_KeyBinding_RulesetID_Variant", - table: "KeyBinding"); - - migrationBuilder.CreateIndex( - name: "IX_KeyBinding_Variant", - table: "KeyBinding", - column: "Variant"); - } - } -} diff --git a/osu.Game/Migrations/20180131154205_AddMuteBinding.cs b/osu.Game/Migrations/20180131154205_AddMuteBinding.cs deleted file mode 100644 index 8646d1d76b..0000000000 --- a/osu.Game/Migrations/20180131154205_AddMuteBinding.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Infrastructure; -using osu.Game.Database; -using osu.Game.Input.Bindings; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20180131154205_AddMuteBinding")] - public partial class AddMuteBinding : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql($"UPDATE KeyBinding SET Action = Action + 1 WHERE RulesetID IS NULL AND Variant IS NULL AND Action >= {(int)GlobalAction.ToggleMute}"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql($"DELETE FROM KeyBinding WHERE RulesetID IS NULL AND Variant IS NULL AND Action = {(int)GlobalAction.ToggleMute}"); - migrationBuilder.Sql($"UPDATE KeyBinding SET Action = Action - 1 WHERE RulesetID IS NULL AND Variant IS NULL AND Action > {(int)GlobalAction.ToggleMute}"); - } - } -} diff --git a/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs b/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs deleted file mode 100644 index cdc4ef2e66..0000000000 --- a/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs +++ /dev/null @@ -1,379 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Game.Database; -using System; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20180219060912_AddSkins")] - partial class AddSkins - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MD5Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntKey") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20180219060912_AddSkins.cs b/osu.Game/Migrations/20180219060912_AddSkins.cs deleted file mode 100644 index 319748bed6..0000000000 --- a/osu.Game/Migrations/20180219060912_AddSkins.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddSkins : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "SkinInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Creator = table.Column(type: "TEXT", nullable: true), - DeletePending = table.Column(type: "INTEGER", nullable: false), - Name = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_SkinInfo", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "SkinFileInfo", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - FileInfoID = table.Column(type: "INTEGER", nullable: false), - Filename = table.Column(type: "TEXT", nullable: false), - SkinInfoID = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_SkinFileInfo", x => x.ID); - table.ForeignKey( - name: "FK_SkinFileInfo_FileInfo_FileInfoID", - column: x => x.FileInfoID, - principalTable: "FileInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_SkinFileInfo_SkinInfo_SkinInfoID", - column: x => x.SkinInfoID, - principalTable: "SkinInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_SkinFileInfo_FileInfoID", - table: "SkinFileInfo", - column: "FileInfoID"); - - migrationBuilder.CreateIndex( - name: "IX_SkinFileInfo_SkinInfoID", - table: "SkinFileInfo", - column: "SkinInfoID"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "SkinFileInfo"); - - migrationBuilder.DropTable( - name: "SkinInfo"); - } - } -} diff --git a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs deleted file mode 100644 index f28408bfb3..0000000000 --- a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.Designer.cs +++ /dev/null @@ -1,377 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using osu.Game.Database; -using System; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20180529055154_RemoveUniqueHashConstraints")] - partial class RemoveUniqueHashConstraints - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.3-rtm-10026"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntKey") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs b/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs deleted file mode 100644 index 91eabe8868..0000000000 --- a/osu.Game/Migrations/20180529055154_RemoveUniqueHashConstraints.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class RemoveUniqueHashConstraints : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo"); - - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo", - column: "Hash"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo", - column: "MD5Hash"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo"); - - migrationBuilder.DropIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo"); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_Hash", - table: "BeatmapInfo", - column: "Hash", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_BeatmapInfo_MD5Hash", - table: "BeatmapInfo", - column: "MD5Hash", - unique: true); - } - } -} diff --git a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs deleted file mode 100644 index aaa11e88b6..0000000000 --- a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs +++ /dev/null @@ -1,376 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20180621044111_UpdateTaikoDefaultBindings")] - partial class UpdateTaikoDefaultBindings - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.1.1-rtm-30846"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntKey") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs deleted file mode 100644 index d888ccd5a2..0000000000 --- a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class UpdateTaikoDefaultBindings : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql("DELETE FROM KeyBinding WHERE RulesetID = 1"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - // we can't really tell if these should be restored or not, so let's just not do so. - } - } -} diff --git a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs deleted file mode 100644 index 7eeacd56d7..0000000000 --- a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs +++ /dev/null @@ -1,376 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20180628011956_RemoveNegativeSetIDs")] - partial class RemoveNegativeSetIDs - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.1.1-rtm-30846"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntKey") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs deleted file mode 100644 index fdea636ac6..0000000000 --- a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class RemoveNegativeSetIDs : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - // There was a change that beatmaps were being loaded with "-1" online IDs, which is completely incorrect. - // This ensures there will not be unique key conflicts as a result of these incorrectly imported beatmaps. - migrationBuilder.Sql("UPDATE BeatmapSetInfo SET OnlineBeatmapSetID = null WHERE OnlineBeatmapSetID <= 0"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - } - } -} diff --git a/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs b/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs deleted file mode 100644 index 5ab43da046..0000000000 --- a/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs +++ /dev/null @@ -1,380 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20180913080842_AddRankStatus")] - partial class AddRankStatus - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.1.2-rtm-30932"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntKey") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20180913080842_AddRankStatus.cs b/osu.Game/Migrations/20180913080842_AddRankStatus.cs deleted file mode 100644 index bb147dff84..0000000000 --- a/osu.Game/Migrations/20180913080842_AddRankStatus.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddRankStatus : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "Status", - table: "BeatmapSetInfo", - nullable: false, - defaultValue: -3); // NONE - - migrationBuilder.AddColumn( - name: "Status", - table: "BeatmapInfo", - nullable: false, - defaultValue: -3); // NONE - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Status", - table: "BeatmapSetInfo"); - - migrationBuilder.DropColumn( - name: "Status", - table: "BeatmapInfo"); - } - } -} diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs deleted file mode 100644 index b387a45ecf..0000000000 --- a/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs +++ /dev/null @@ -1,380 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20181007180454_StandardizePaths")] - partial class StandardizePaths - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntKey") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.cs deleted file mode 100644 index 30f27043a0..0000000000 --- a/osu.Game/Migrations/20181007180454_StandardizePaths.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class StandardizePaths : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - string windowsStyle = @"\"; - string standardized = "/"; - - // Escaping \ does not seem to be needed. - migrationBuilder.Sql($"UPDATE `BeatmapInfo` SET `Path` = REPLACE(`Path`, '{windowsStyle}', '{standardized}')"); - migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `AudioFile` = REPLACE(`AudioFile`, '{windowsStyle}', '{standardized}')"); - migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `BackgroundFile` = REPLACE(`BackgroundFile`, '{windowsStyle}', '{standardized}')"); - migrationBuilder.Sql($"UPDATE `BeatmapSetFileInfo` SET `Filename` = REPLACE(`Filename`, '{windowsStyle}', '{standardized}')"); - migrationBuilder.Sql($"UPDATE `SkinFileInfo` SET `Filename` = REPLACE(`Filename`, '{windowsStyle}', '{standardized}')"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - } - } -} diff --git a/osu.Game/Migrations/20181128100659_AddSkinInfoHash.Designer.cs b/osu.Game/Migrations/20181128100659_AddSkinInfoHash.Designer.cs deleted file mode 100644 index 120674671a..0000000000 --- a/osu.Game/Migrations/20181128100659_AddSkinInfoHash.Designer.cs +++ /dev/null @@ -1,387 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20181128100659_AddSkinInfoHash")] - partial class AddSkinInfoHash - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.1.4-rtm-31024"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntKey") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20181128100659_AddSkinInfoHash.cs b/osu.Game/Migrations/20181128100659_AddSkinInfoHash.cs deleted file mode 100644 index ee825a1e9c..0000000000 --- a/osu.Game/Migrations/20181128100659_AddSkinInfoHash.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddSkinInfoHash : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "Hash", - table: "SkinInfo", - nullable: true); - - migrationBuilder.CreateIndex( - name: "IX_SkinInfo_DeletePending", - table: "SkinInfo", - column: "DeletePending"); - - migrationBuilder.CreateIndex( - name: "IX_SkinInfo_Hash", - table: "SkinInfo", - column: "Hash", - unique: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_SkinInfo_DeletePending", - table: "SkinInfo"); - - migrationBuilder.DropIndex( - name: "IX_SkinInfo_Hash", - table: "SkinInfo"); - - migrationBuilder.DropColumn( - name: "Hash", - table: "SkinInfo"); - } - } -} diff --git a/osu.Game/Migrations/20181130113755_AddScoreInfoTables.Designer.cs b/osu.Game/Migrations/20181130113755_AddScoreInfoTables.Designer.cs deleted file mode 100644 index eee53182ce..0000000000 --- a/osu.Game/Migrations/20181130113755_AddScoreInfoTables.Designer.cs +++ /dev/null @@ -1,484 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20181130113755_AddScoreInfoTables")] - partial class AddScoreInfoTables - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.1.4-rtm-31024"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntKey") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany() - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20181130113755_AddScoreInfoTables.cs b/osu.Game/Migrations/20181130113755_AddScoreInfoTables.cs deleted file mode 100644 index 58980132f3..0000000000 --- a/osu.Game/Migrations/20181130113755_AddScoreInfoTables.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddScoreInfoTables : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "ScoreInfo", - columns: table => new - { - ID = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Rank = table.Column(nullable: false), - TotalScore = table.Column(nullable: false), - Accuracy = table.Column(type: "DECIMAL(1,4)", nullable: false), - PP = table.Column(nullable: true), - MaxCombo = table.Column(nullable: false), - Combo = table.Column(nullable: false), - RulesetID = table.Column(nullable: false), - Mods = table.Column(nullable: true), - User = table.Column(nullable: true), - BeatmapInfoID = table.Column(nullable: false), - OnlineScoreID = table.Column(nullable: true), - Date = table.Column(nullable: false), - Statistics = table.Column(nullable: true), - Hash = table.Column(nullable: true), - DeletePending = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ScoreInfo", x => x.ID); - table.ForeignKey( - name: "FK_ScoreInfo_BeatmapInfo_BeatmapInfoID", - column: x => x.BeatmapInfoID, - principalTable: "BeatmapInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_ScoreInfo_RulesetInfo_RulesetID", - column: x => x.RulesetID, - principalTable: "RulesetInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ScoreFileInfo", - columns: table => new - { - ID = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - FileInfoID = table.Column(nullable: false), - Filename = table.Column(nullable: false), - ScoreInfoID = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_ScoreFileInfo", x => x.ID); - table.ForeignKey( - name: "FK_ScoreFileInfo_FileInfo_FileInfoID", - column: x => x.FileInfoID, - principalTable: "FileInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_ScoreFileInfo_ScoreInfo_ScoreInfoID", - column: x => x.ScoreInfoID, - principalTable: "ScoreInfo", - principalColumn: "ID", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateIndex( - name: "IX_ScoreFileInfo_FileInfoID", - table: "ScoreFileInfo", - column: "FileInfoID"); - - migrationBuilder.CreateIndex( - name: "IX_ScoreFileInfo_ScoreInfoID", - table: "ScoreFileInfo", - column: "ScoreInfoID"); - - migrationBuilder.CreateIndex( - name: "IX_ScoreInfo_BeatmapInfoID", - table: "ScoreInfo", - column: "BeatmapInfoID"); - - migrationBuilder.CreateIndex( - name: "IX_ScoreInfo_OnlineScoreID", - table: "ScoreInfo", - column: "OnlineScoreID", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_ScoreInfo_RulesetID", - table: "ScoreInfo", - column: "RulesetID"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "ScoreFileInfo"); - - migrationBuilder.DropTable( - name: "ScoreInfo"); - } - } -} diff --git a/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs b/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs deleted file mode 100644 index 8e1e3a59f3..0000000000 --- a/osu.Game/Migrations/20190225062029_AddUserIDColumn.Designer.cs +++ /dev/null @@ -1,487 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20190225062029_AddUserIDColumn")] - partial class AddUserIDColumn - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.1-servicing-10028"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntKey") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20190225062029_AddUserIDColumn.cs b/osu.Game/Migrations/20190225062029_AddUserIDColumn.cs deleted file mode 100644 index f2eef600dc..0000000000 --- a/osu.Game/Migrations/20190225062029_AddUserIDColumn.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddUserIDColumn : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "UserID", - table: "ScoreInfo", - nullable: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "UserID", - table: "ScoreInfo"); - } - } -} diff --git a/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs b/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs deleted file mode 100644 index 348c42adb9..0000000000 --- a/osu.Game/Migrations/20190525060824_SkinSettings.Designer.cs +++ /dev/null @@ -1,498 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20190525060824_SkinSettings")] - partial class SkinSettings - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Key") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("SkinInfoID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("SkinInfoID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Settings") - .HasForeignKey("SkinInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20190525060824_SkinSettings.cs b/osu.Game/Migrations/20190525060824_SkinSettings.cs deleted file mode 100644 index 7779b55bb7..0000000000 --- a/osu.Game/Migrations/20190525060824_SkinSettings.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class SkinSettings : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql(@"create table Settings_dg_tmp - ( - ID INTEGER not null - constraint PK_Settings - primary key autoincrement, - Key TEXT not null, - RulesetID INTEGER, - Value TEXT, - Variant INTEGER, - SkinInfoID int - constraint Settings_SkinInfo_ID_fk - references SkinInfo - on delete restrict - ); - - insert into Settings_dg_tmp(ID, Key, RulesetID, Value, Variant) select ID, Key, RulesetID, Value, Variant from Settings; - - drop table Settings; - - alter table Settings_dg_tmp rename to Settings; - - create index IX_Settings_RulesetID_Variant - on Settings (RulesetID, Variant); - - create index Settings_SkinInfoID_index - on Settings (SkinInfoID); - - "); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Settings_SkinInfo_SkinInfoID", - table: "Settings"); - - migrationBuilder.DropIndex( - name: "IX_Settings_SkinInfoID", - table: "Settings"); - - migrationBuilder.DropColumn( - name: "SkinInfoID", - table: "Settings"); - } - } -} diff --git a/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs b/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs deleted file mode 100644 index 9477369aa0..0000000000 --- a/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.Designer.cs +++ /dev/null @@ -1,489 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20190605091246_AddDateAddedColumnToBeatmapSet")] - partial class AddDateAddedColumnToBeatmapSet - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DateAdded"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Key") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.cs b/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.cs deleted file mode 100644 index 0620a0624f..0000000000 --- a/osu.Game/Migrations/20190605091246_AddDateAddedColumnToBeatmapSet.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddDateAddedColumnToBeatmapSet : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "DateAdded", - table: "BeatmapSetInfo", - nullable: false, - defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0))); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "DateAdded", - table: "BeatmapSetInfo"); - } - } -} diff --git a/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs b/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs deleted file mode 100644 index c5fcc16f84..0000000000 --- a/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.Designer.cs +++ /dev/null @@ -1,504 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20190708070844_AddBPMAndLengthColumns")] - partial class AddBPMAndLengthColumns - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.4-servicing-10062"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BPM"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("Length"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DateAdded"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Key") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("SkinInfoID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("SkinInfoID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Settings") - .HasForeignKey("SkinInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.cs b/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.cs deleted file mode 100644 index f8ce354aa1..0000000000 --- a/osu.Game/Migrations/20190708070844_AddBPMAndLengthColumns.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddBPMAndLengthColumns : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "BPM", - table: "BeatmapInfo", - nullable: false, - defaultValue: 0.0); - - migrationBuilder.AddColumn( - name: "Length", - table: "BeatmapInfo", - nullable: false, - defaultValue: 0.0); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "BPM", - table: "BeatmapInfo"); - - migrationBuilder.DropColumn( - name: "Length", - table: "BeatmapInfo"); - } - } -} diff --git a/osu.Game/Migrations/20190913104727_AddBeatmapVideo.Designer.cs b/osu.Game/Migrations/20190913104727_AddBeatmapVideo.Designer.cs deleted file mode 100644 index 826233a2b0..0000000000 --- a/osu.Game/Migrations/20190913104727_AddBeatmapVideo.Designer.cs +++ /dev/null @@ -1,506 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20190913104727_AddBeatmapVideo")] - partial class AddBeatmapVideo - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BPM"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("Length"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.Property("VideoFile"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DateAdded"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Key") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("SkinInfoID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("SkinInfoID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Settings") - .HasForeignKey("SkinInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs b/osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs deleted file mode 100644 index af82b4db20..0000000000 --- a/osu.Game/Migrations/20190913104727_AddBeatmapVideo.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddBeatmapVideo : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "VideoFile", - table: "BeatmapMetadata", - nullable: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "VideoFile", - table: "BeatmapMetadata"); - } - } -} diff --git a/osu.Game/Migrations/20200302094919_RefreshVolumeBindings.Designer.cs b/osu.Game/Migrations/20200302094919_RefreshVolumeBindings.Designer.cs deleted file mode 100644 index 22316b0380..0000000000 --- a/osu.Game/Migrations/20200302094919_RefreshVolumeBindings.Designer.cs +++ /dev/null @@ -1,506 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20200302094919_RefreshVolumeBindings")] - partial class RefreshVolumeBindings - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BPM"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("Length"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.Property("VideoFile"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DateAdded"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Key") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("SkinInfoID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("SkinInfoID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Settings") - .HasForeignKey("SkinInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20200302094919_RefreshVolumeBindings.cs b/osu.Game/Migrations/20200302094919_RefreshVolumeBindings.cs deleted file mode 100644 index 3d2ddbf6fc..0000000000 --- a/osu.Game/Migrations/20200302094919_RefreshVolumeBindings.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class RefreshVolumeBindings : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql("DELETE FROM KeyBinding WHERE action in (6,7)"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - } - } -} diff --git a/osu.Game/Migrations/20201019224408_AddEpilepsyWarning.Designer.cs b/osu.Game/Migrations/20201019224408_AddEpilepsyWarning.Designer.cs deleted file mode 100644 index 1c05de832e..0000000000 --- a/osu.Game/Migrations/20201019224408_AddEpilepsyWarning.Designer.cs +++ /dev/null @@ -1,508 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20201019224408_AddEpilepsyWarning")] - partial class AddEpilepsyWarning - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BPM"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("EpilepsyWarning"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("Length"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.Property("VideoFile"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DateAdded"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Key") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("SkinInfoID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("SkinInfoID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Settings") - .HasForeignKey("SkinInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20201019224408_AddEpilepsyWarning.cs b/osu.Game/Migrations/20201019224408_AddEpilepsyWarning.cs deleted file mode 100644 index 58a35a7bf3..0000000000 --- a/osu.Game/Migrations/20201019224408_AddEpilepsyWarning.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddEpilepsyWarning : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "EpilepsyWarning", - table: "BeatmapInfo", - nullable: false, - defaultValue: false); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "EpilepsyWarning", - table: "BeatmapInfo"); - } - } -} diff --git a/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.Designer.cs b/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.Designer.cs deleted file mode 100644 index 2c100d39b9..0000000000 --- a/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.Designer.cs +++ /dev/null @@ -1,506 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20210412045700_RefreshVolumeBindingsAgain")] - partial class RefreshVolumeBindingsAgain - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BPM"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("Length"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.Property("VideoFile"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DateAdded"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Key") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("SkinInfoID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("SkinInfoID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Settings") - .HasForeignKey("SkinInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs b/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs deleted file mode 100644 index 4d3941dd20..0000000000 --- a/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class RefreshVolumeBindingsAgain : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql("DELETE FROM KeyBinding WHERE action in (6,7)"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - } - } -} diff --git a/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.Designer.cs b/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.Designer.cs deleted file mode 100644 index b808c648da..0000000000 --- a/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.Designer.cs +++ /dev/null @@ -1,508 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20210511060743_AddSkinInstantiationInfo")] - partial class AddSkinInstantiationInfo - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BPM"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("EpilepsyWarning"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("Length"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DateAdded"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Key") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("SkinInfoID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("SkinInfoID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Settings") - .HasForeignKey("SkinInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs b/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs deleted file mode 100644 index 887635fa85..0000000000 --- a/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddSkinInstantiationInfo : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "InstantiationInfo", - table: "SkinInfo", - nullable: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "InstantiationInfo", - table: "SkinInfo"); - } - } -} diff --git a/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.Designer.cs b/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.Designer.cs deleted file mode 100644 index 89bab3a0fa..0000000000 --- a/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.Designer.cs +++ /dev/null @@ -1,511 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20210514062639_AddAuthorIdToBeatmapMetadata")] - partial class AddAuthorIdToBeatmapMetadata - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BPM"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("DistanceSpacing"); - - b.Property("EpilepsyWarning"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("Length"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorID") - .HasColumnName("AuthorID"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DateAdded"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Key") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("SkinInfoID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("SkinInfoID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Settings") - .HasForeignKey("SkinInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.cs b/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.cs deleted file mode 100644 index 7b579e27b9..0000000000 --- a/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddAuthorIdToBeatmapMetadata : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "AuthorID", - table: "BeatmapMetadata", - nullable: false, - defaultValue: 0); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "AuthorID", - table: "BeatmapMetadata"); - } - } -} diff --git a/osu.Game/Migrations/20210824185035_AddCountdownSettings.Designer.cs b/osu.Game/Migrations/20210824185035_AddCountdownSettings.Designer.cs deleted file mode 100644 index afeb42130d..0000000000 --- a/osu.Game/Migrations/20210824185035_AddCountdownSettings.Designer.cs +++ /dev/null @@ -1,513 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20210824185035_AddCountdownSettings")] - partial class AddCountdownSettings - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BPM"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("CountdownOffset"); - - b.Property("DistanceSpacing"); - - b.Property("EpilepsyWarning"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("Length"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorID") - .HasColumnName("AuthorID"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DateAdded"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Key") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("SkinInfoID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("SkinInfoID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Settings") - .HasForeignKey("SkinInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20210824185035_AddCountdownSettings.cs b/osu.Game/Migrations/20210824185035_AddCountdownSettings.cs deleted file mode 100644 index d1b09e2c1d..0000000000 --- a/osu.Game/Migrations/20210824185035_AddCountdownSettings.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddCountdownSettings : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "CountdownOffset", - table: "BeatmapInfo", - nullable: false, - defaultValue: 0); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "CountdownOffset", - table: "BeatmapInfo"); - } - } -} diff --git a/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.Designer.cs b/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.Designer.cs deleted file mode 100644 index 6e53d7fae0..0000000000 --- a/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.Designer.cs +++ /dev/null @@ -1,515 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20210912144011_AddSamplesMatchPlaybackRate")] - partial class AddSamplesMatchPlaybackRate - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BPM"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("CountdownOffset"); - - b.Property("DistanceSpacing"); - - b.Property("EpilepsyWarning"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("Length"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SamplesMatchPlaybackRate"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorID") - .HasColumnName("AuthorID"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DateAdded"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Key") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("SkinInfoID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("SkinInfoID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Settings") - .HasForeignKey("SkinInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.cs b/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.cs deleted file mode 100644 index f6fc1f4420..0000000000 --- a/osu.Game/Migrations/20210912144011_AddSamplesMatchPlaybackRate.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Migrations; - -namespace osu.Game.Migrations -{ - public partial class AddSamplesMatchPlaybackRate : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "SamplesMatchPlaybackRate", - table: "BeatmapInfo", - nullable: false, - defaultValue: false); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "SamplesMatchPlaybackRate", - table: "BeatmapInfo"); - } - } -} diff --git a/osu.Game/Migrations/20211020081609_ResetSkinHashes.cs b/osu.Game/Migrations/20211020081609_ResetSkinHashes.cs deleted file mode 100644 index 6d53c019ec..0000000000 --- a/osu.Game/Migrations/20211020081609_ResetSkinHashes.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - [Migration("20211020081609_ResetSkinHashes")] - public partial class ResetSkinHashes : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql($"UPDATE SkinInfo SET Hash = null"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - } - } -} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs deleted file mode 100644 index 036c26cb0a..0000000000 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ /dev/null @@ -1,513 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using osu.Game.Database; - -namespace osu.Game.Migrations -{ - [DbContext(typeof(OsuDbContext))] - partial class OsuDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("ApproachRate"); - - b.Property("CircleSize"); - - b.Property("DrainRate"); - - b.Property("OverallDifficulty"); - - b.Property("SliderMultiplier"); - - b.Property("SliderTickRate"); - - b.HasKey("ID"); - - b.ToTable("BeatmapDifficulty"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("AudioLeadIn"); - - b.Property("BPM"); - - b.Property("BaseDifficultyID"); - - b.Property("BeatDivisor"); - - b.Property("BeatmapSetInfoID"); - - b.Property("Countdown"); - - b.Property("CountdownOffset"); - - b.Property("DistanceSpacing"); - - b.Property("EpilepsyWarning"); - - b.Property("GridSize"); - - b.Property("Hash"); - - b.Property("Hidden"); - - b.Property("Length"); - - b.Property("LetterboxInBreaks"); - - b.Property("MD5Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapID"); - - b.Property("Path"); - - b.Property("RulesetID"); - - b.Property("SamplesMatchPlaybackRate"); - - b.Property("SpecialStyle"); - - b.Property("StackLeniency"); - - b.Property("StarDifficulty"); - - b.Property("Status"); - - b.Property("StoredBookmarks"); - - b.Property("TimelineZoom"); - - b.Property("Version"); - - b.Property("WidescreenStoryboard"); - - b.HasKey("ID"); - - b.HasIndex("BaseDifficultyID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("Hash"); - - b.HasIndex("MD5Hash"); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("BeatmapInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Artist"); - - b.Property("ArtistUnicode"); - - b.Property("AudioFile"); - - b.Property("AuthorID") - .HasColumnName("AuthorID"); - - b.Property("AuthorString") - .HasColumnName("Author"); - - b.Property("BackgroundFile"); - - b.Property("PreviewTime"); - - b.Property("Source"); - - b.Property("Tags"); - - b.Property("Title"); - - b.Property("TitleUnicode"); - - b.HasKey("ID"); - - b.ToTable("BeatmapMetadata"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("BeatmapSetInfoID"); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.HasKey("ID"); - - b.HasIndex("BeatmapSetInfoID"); - - b.HasIndex("FileInfoID"); - - b.ToTable("BeatmapSetFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("DateAdded"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MetadataID"); - - b.Property("OnlineBeatmapSetID"); - - b.Property("Protected"); - - b.Property("Status"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("MetadataID"); - - b.HasIndex("OnlineBeatmapSetID") - .IsUnique(); - - b.ToTable("BeatmapSetInfo"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Key") - .HasColumnName("Key"); - - b.Property("RulesetID"); - - b.Property("SkinInfoID"); - - b.Property("StringValue") - .HasColumnName("Value"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("SkinInfoID"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("osu.Game.IO.FileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Hash"); - - b.Property("ReferenceCount"); - - b.HasKey("ID"); - - b.HasIndex("Hash") - .IsUnique(); - - b.HasIndex("ReferenceCount"); - - b.ToTable("FileInfo"); - }); - - modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("IntAction") - .HasColumnName("Action"); - - b.Property("KeysString") - .HasColumnName("Keys"); - - b.Property("RulesetID"); - - b.Property("Variant"); - - b.HasKey("ID"); - - b.HasIndex("IntAction"); - - b.HasIndex("RulesetID", "Variant"); - - b.ToTable("KeyBinding"); - }); - - modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Available"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.Property("ShortName"); - - b.HasKey("ID"); - - b.HasIndex("Available"); - - b.HasIndex("ShortName") - .IsUnique(); - - b.ToTable("RulesetInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("ScoreInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("ScoreInfoID"); - - b.ToTable("ScoreFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Accuracy") - .HasColumnType("DECIMAL(1,4)"); - - b.Property("BeatmapInfoID"); - - b.Property("Combo"); - - b.Property("Date"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("MaxCombo"); - - b.Property("ModsJson") - .HasColumnName("Mods"); - - b.Property("OnlineScoreID"); - - b.Property("PP"); - - b.Property("Rank"); - - b.Property("RulesetID"); - - b.Property("StatisticsJson") - .HasColumnName("Statistics"); - - b.Property("TotalScore"); - - b.Property("UserID") - .HasColumnName("UserID"); - - b.Property("UserString") - .HasColumnName("User"); - - b.HasKey("ID"); - - b.HasIndex("BeatmapInfoID"); - - b.HasIndex("OnlineScoreID") - .IsUnique(); - - b.HasIndex("RulesetID"); - - b.ToTable("ScoreInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("FileInfoID"); - - b.Property("Filename") - .IsRequired(); - - b.Property("SkinInfoID"); - - b.HasKey("ID"); - - b.HasIndex("FileInfoID"); - - b.HasIndex("SkinInfoID"); - - b.ToTable("SkinFileInfo"); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => - { - b.Property("ID") - .ValueGeneratedOnAdd(); - - b.Property("Creator"); - - b.Property("DeletePending"); - - b.Property("Hash"); - - b.Property("InstantiationInfo"); - - b.Property("Name"); - - b.HasKey("ID"); - - b.HasIndex("DeletePending"); - - b.HasIndex("Hash") - .IsUnique(); - - b.ToTable("SkinInfo"); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") - .WithMany() - .HasForeignKey("BaseDifficultyID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") - .WithMany("Beatmaps") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("Beatmaps") - .HasForeignKey("MetadataID"); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") - .WithMany("Files") - .HasForeignKey("BeatmapSetInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") - .WithMany("BeatmapSets") - .HasForeignKey("MetadataID"); - }); - - modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => - { - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Settings") - .HasForeignKey("SkinInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Scoring.ScoreInfo") - .WithMany("Files") - .HasForeignKey("ScoreInfoID"); - }); - - modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => - { - b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") - .WithMany("Scores") - .HasForeignKey("BeatmapInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") - .WithMany() - .HasForeignKey("RulesetID") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => - { - b.HasOne("osu.Game.IO.FileInfo", "FileInfo") - .WithMany() - .HasForeignKey("FileInfoID") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("osu.Game.Skinning.SkinInfo") - .WithMany("Files") - .HasForeignKey("SkinInfoID") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 97142d5472..b30a065371 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -199,11 +199,6 @@ namespace osu.Game private readonly BindableNumber globalTrackVolumeAdjust = new BindableNumber(global_track_volume_adjust); - /// - /// A legacy EF context factory if migration has not been performed to realm yet. - /// - protected DatabaseContextFactory EFContextFactory { get; private set; } - /// /// Number of unhandled exceptions to allow before aborting execution. /// @@ -242,10 +237,7 @@ namespace osu.Game Resources.AddStore(new DllResourceStore(OsuResources.ResourceAssembly)); - if (Storage.Exists(DatabaseContextFactory.DATABASE_NAME)) - dependencies.Cache(EFContextFactory = new DatabaseContextFactory(Storage)); - - dependencies.Cache(realm = new RealmAccess(Storage, CLIENT_DATABASE_FILENAME, Host.UpdateThread, EFContextFactory)); + dependencies.Cache(realm = new RealmAccess(Storage, CLIENT_DATABASE_FILENAME, Host.UpdateThread)); dependencies.CacheAs(RulesetStore = new RealmRulesetStore(realm, Storage)); dependencies.CacheAs(RulesetStore); diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index dbb1278d76..afba00274c 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -14,7 +14,6 @@ using osu.Game.Screens.Menu; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Configuration; -using osu.Game.Database; using osu.Game.Graphics.UserInterface; using IntroSequence = osu.Game.Configuration.IntroSequence; @@ -66,32 +65,13 @@ namespace osu.Game.Screens protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler(); - [Resolved(canBeNull: true)] - private DatabaseContextFactory efContextFactory { get; set; } - - private EFToRealmMigrator realmMigrator; - public override void OnEntering(ScreenTransitionEvent e) { base.OnEntering(e); LoadComponentAsync(precompiler = CreateShaderPrecompiler(), AddInternal); - // A non-null context factory means there's still content to migrate. - if (efContextFactory != null) - { - LoadComponentAsync(realmMigrator = new EFToRealmMigrator(), AddInternal); - realmMigrator.MigrationCompleted.ContinueWith(_ => Schedule(() => - { - // Delay initial screen loading to ensure that the migration is in a complete and sane state - // before the intro screen may import the game intro beatmap. - LoadComponentAsync(loadableScreen = CreateLoadableScreen()); - })); - } - else - { - LoadComponentAsync(loadableScreen = CreateLoadableScreen()); - } + LoadComponentAsync(loadableScreen = CreateLoadableScreen()); LoadComponentAsync(spinner = new LoadingSpinner(true, true) { diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 3ad29ea6db..44df495929 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -807,7 +807,6 @@ See the LICENCE file in the repository root for full licence text. True True - True True True True From 0aa92c78ec0ae000f8f241db3b5a65a6ea28b7bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Sep 2022 16:34:35 +0900 Subject: [PATCH 130/142] Add local sqlite initialisation to `BeatmapUpdaterMetadataLookup` for now --- osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs b/osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs index aa773da6d3..007bec46bb 100644 --- a/osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs +++ b/osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs @@ -19,6 +19,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using SharpCompress.Compressors; using SharpCompress.Compressors.BZip2; +using SQLitePCL; namespace osu.Game.Beatmaps { @@ -41,6 +42,10 @@ namespace osu.Game.Beatmaps public BeatmapUpdaterMetadataLookup(IAPIProvider api, Storage storage) { + // required to initialise native SQLite libraries on some platforms. + Batteries_V2.Init(); + raw.sqlite3_config(2 /*SQLITE_CONFIG_MULTITHREAD*/); + this.api = api; this.storage = storage; From 581a4d2d6d033069af55d972e345f0d63b29a2fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Sep 2022 16:39:59 +0900 Subject: [PATCH 131/142] Use `APIBeatmap` for realm migration requiring `ShortName` mapping --- osu.Game/Database/RealmAccess.cs | 20 ++++++++++++------- .../API/Requests/Responses/APIBeatmap.cs | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index e23fc912df..cefc7da503 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -24,6 +24,7 @@ using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Input.Bindings; using osu.Game.Models; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -45,8 +46,6 @@ namespace osu.Game.Database /// public readonly string Filename; - private readonly IDatabaseContextFactory? efContextFactory; - private readonly SynchronizationContext? updateThreadSyncContext; /// @@ -162,11 +161,9 @@ namespace osu.Game.Database /// The game storage which will be used to create the realm backing file. /// The filename to use for the realm backing file. A ".realm" extension will be added automatically if not specified. /// The game update thread, used to post realm operations into a thread-safe context. - /// An EF factory used only for migration purposes. - public RealmAccess(Storage storage, string filename, GameThread? updateThread = null, IDatabaseContextFactory? efContextFactory = null) + public RealmAccess(Storage storage, string filename, GameThread? updateThread = null) { this.storage = storage; - this.efContextFactory = efContextFactory; updateThreadSyncContext = updateThread?.SynchronizationContext ?? SynchronizationContext.Current; @@ -876,8 +873,17 @@ namespace osu.Game.Database } } - private string? getRulesetShortNameFromLegacyID(long rulesetId) => - efContextFactory?.Get().RulesetInfo.FirstOrDefault(r => r.ID == rulesetId)?.ShortName; + private string? getRulesetShortNameFromLegacyID(long rulesetId) + { + try + { + return new APIBeatmap.APIRuleset { OnlineID = (int)rulesetId }.ShortName; + } + catch + { + return null; + } + } /// /// Create a full realm backup. diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 3fee81cf33..8a77801c3a 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -111,7 +111,7 @@ namespace osu.Game.Online.API.Requests.Responses public bool Equals(IBeatmapInfo? other) => other is APIBeatmap b && this.MatchesOnlineID(b); - private class APIRuleset : IRulesetInfo + public class APIRuleset : IRulesetInfo { public int OnlineID { get; set; } = -1; From 65d1c40dd54ec556ca28e30f01bfb16a56a5e048 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Sep 2022 16:47:26 +0900 Subject: [PATCH 132/142] Remove remaining package references and replace with `SQLitePCLRaw` direct reference This may require further consideration for test and iOS projects. --- .../osu.Game.Rulesets.EmptyFreeform.Tests.csproj | 1 - .../osu.Game.Rulesets.Pippidon.Tests.csproj | 1 - .../osu.Game.Rulesets.EmptyScrolling.Tests.csproj | 1 - .../osu.Game.Rulesets.Pippidon.Tests.csproj | 1 - osu.Desktop/osu.Desktop.csproj | 1 - .../osu.Game.Rulesets.Catch.Tests.csproj | 1 - .../osu.Game.Rulesets.Mania.Tests.csproj | 1 - .../osu.Game.Rulesets.Osu.Tests.csproj | 1 - .../osu.Game.Rulesets.Taiko.Tests.csproj | 1 - osu.Game.Tests/osu.Game.Tests.csproj | 1 - osu.Game/osu.Game.csproj | 4 ++-- osu.iOS.props | 2 -- 12 files changed, 2 insertions(+), 14 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj index 011a37cbdc..b8604169aa 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj @@ -12,7 +12,6 @@ - diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index c04f6132f3..4117452579 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -12,7 +12,6 @@ - diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj index 529054fd4f..0b119c8680 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj @@ -12,7 +12,6 @@ - diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index c04f6132f3..4117452579 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -12,7 +12,6 @@ - diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 8ef480d4e3..3f926ed45a 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -27,7 +27,6 @@ - diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 3ac1491946..d45f8a9692 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -4,7 +4,6 @@ - WinExe diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index d07df75864..2951076591 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -4,7 +4,6 @@ - WinExe diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 36c40c0fe2..c2973644cf 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -5,7 +5,6 @@ - WinExe diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index 51d4bbc630..e8eaff4368 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -4,7 +4,6 @@ - WinExe diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 65679deb01..2572864e09 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -6,7 +6,6 @@ - diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index fabef87c28..953087f9c8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,8 +26,7 @@ - - + @@ -41,6 +40,7 @@ + diff --git a/osu.iOS.props b/osu.iOS.props index 89166f924c..496bfbb85c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -81,8 +81,6 @@ - - From 28b15e232df3d8b6b9cc38b14d22819510e7b2f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Sep 2022 16:59:03 +0900 Subject: [PATCH 133/142] Remove all EF models --- osu.Game/Beatmaps/BeatmapSetFileInfo.cs | 31 --- osu.Game/Beatmaps/EFBeatmapDifficulty.cs | 80 ------- osu.Game/Beatmaps/EFBeatmapInfo.cs | 183 --------------- osu.Game/Beatmaps/EFBeatmapMetadata.cs | 89 -------- osu.Game/Beatmaps/EFBeatmapSetInfo.cs | 108 --------- osu.Game/Rulesets/EFRulesetInfo.cs | 87 -------- osu.Game/Scoring/EFScoreInfo.cs | 272 ----------------------- osu.Game/Scoring/ScoreFileInfo.cs | 31 --- osu.Game/Skinning/EFSkinInfo.cs | 65 ------ osu.Game/Skinning/SkinFileInfo.cs | 31 --- 10 files changed, 977 deletions(-) delete mode 100644 osu.Game/Beatmaps/BeatmapSetFileInfo.cs delete mode 100644 osu.Game/Beatmaps/EFBeatmapDifficulty.cs delete mode 100644 osu.Game/Beatmaps/EFBeatmapInfo.cs delete mode 100644 osu.Game/Beatmaps/EFBeatmapMetadata.cs delete mode 100644 osu.Game/Beatmaps/EFBeatmapSetInfo.cs delete mode 100644 osu.Game/Rulesets/EFRulesetInfo.cs delete mode 100644 osu.Game/Scoring/EFScoreInfo.cs delete mode 100644 osu.Game/Scoring/ScoreFileInfo.cs delete mode 100644 osu.Game/Skinning/EFSkinInfo.cs delete mode 100644 osu.Game/Skinning/SkinFileInfo.cs diff --git a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs deleted file mode 100644 index 051366fb99..0000000000 --- a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System.ComponentModel.DataAnnotations; -using osu.Game.Database; -using osu.Game.IO; - -namespace osu.Game.Beatmaps -{ - public class BeatmapSetFileInfo : INamedFileInfo, IHasPrimaryKey, INamedFileUsage - { - public int ID { get; set; } - - public bool IsManaged => ID > 0; - - public int BeatmapSetInfoID { get; set; } - - public EFBeatmapSetInfo BeatmapSetInfo { get; set; } - - public int FileInfoID { get; set; } - - public FileInfo FileInfo { get; set; } - - [Required] - public string Filename { get; set; } - - IFileInfo INamedFileUsage.File => FileInfo; - } -} diff --git a/osu.Game/Beatmaps/EFBeatmapDifficulty.cs b/osu.Game/Beatmaps/EFBeatmapDifficulty.cs deleted file mode 100644 index 8bcac24ca1..0000000000 --- a/osu.Game/Beatmaps/EFBeatmapDifficulty.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System; -using System.ComponentModel.DataAnnotations.Schema; -using osu.Game.Database; - -namespace osu.Game.Beatmaps -{ - [Table(@"BeatmapDifficulty")] - public class EFBeatmapDifficulty : IHasPrimaryKey, IBeatmapDifficultyInfo - { - /// - /// The default value used for all difficulty settings except and . - /// - public const float DEFAULT_DIFFICULTY = 5; - - public int ID { get; set; } - - public bool IsManaged => ID > 0; - - public float DrainRate { get; set; } = DEFAULT_DIFFICULTY; - public float CircleSize { get; set; } = DEFAULT_DIFFICULTY; - public float OverallDifficulty { get; set; } = DEFAULT_DIFFICULTY; - - private float? approachRate; - - public EFBeatmapDifficulty() - { - } - - public EFBeatmapDifficulty(IBeatmapDifficultyInfo source) - { - CopyFrom(source); - } - - public float ApproachRate - { - get => approachRate ?? OverallDifficulty; - set => approachRate = value; - } - - public double SliderMultiplier { get; set; } = 1; - public double SliderTickRate { get; set; } = 1; - - /// - /// Returns a shallow-clone of this . - /// - public EFBeatmapDifficulty Clone() - { - var diff = (EFBeatmapDifficulty)Activator.CreateInstance(GetType()); - CopyTo(diff); - return diff; - } - - public virtual void CopyFrom(IBeatmapDifficultyInfo other) - { - ApproachRate = other.ApproachRate; - DrainRate = other.DrainRate; - CircleSize = other.CircleSize; - OverallDifficulty = other.OverallDifficulty; - - SliderMultiplier = other.SliderMultiplier; - SliderTickRate = other.SliderTickRate; - } - - public virtual void CopyTo(EFBeatmapDifficulty other) - { - other.ApproachRate = ApproachRate; - other.DrainRate = DrainRate; - other.CircleSize = CircleSize; - other.OverallDifficulty = OverallDifficulty; - - other.SliderMultiplier = SliderMultiplier; - other.SliderTickRate = SliderTickRate; - } - } -} diff --git a/osu.Game/Beatmaps/EFBeatmapInfo.cs b/osu.Game/Beatmaps/EFBeatmapInfo.cs deleted file mode 100644 index 20abdc686a..0000000000 --- a/osu.Game/Beatmaps/EFBeatmapInfo.cs +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using Newtonsoft.Json; -using osu.Framework.Testing; -using osu.Game.Database; -using osu.Game.Online.API.Requests.Responses; -using osu.Game.Rulesets; -using osu.Game.Scoring; - -namespace osu.Game.Beatmaps -{ - [ExcludeFromDynamicCompile] - [Serializable] - [Table(@"BeatmapInfo")] - public class EFBeatmapInfo : IEquatable, IHasPrimaryKey, IBeatmapInfo - { - public int ID { get; set; } - - public bool IsManaged => ID > 0; - - public int BeatmapVersion; - - private int? onlineID; - - [JsonProperty("id")] - [Column("OnlineBeatmapID")] - public int? OnlineID - { - get => onlineID; - set => onlineID = value > 0 ? value : null; - } - - [JsonIgnore] - public int BeatmapSetInfoID { get; set; } - - public BeatmapOnlineStatus Status { get; set; } = BeatmapOnlineStatus.None; - - [Required] - public EFBeatmapSetInfo BeatmapSetInfo { get; set; } - - public EFBeatmapMetadata Metadata { get; set; } - - [JsonIgnore] - public int BaseDifficultyID { get; set; } - - public EFBeatmapDifficulty BaseDifficulty { get; set; } - - [NotMapped] - public APIBeatmap OnlineInfo { get; set; } - - /// - /// The playable length in milliseconds of this beatmap. - /// - public double Length { get; set; } - - /// - /// The most common BPM of this beatmap. - /// - public double BPM { get; set; } - - public string Path { get; set; } - - [JsonProperty("file_sha2")] - public string Hash { get; set; } - - [JsonIgnore] - public bool Hidden { get; set; } - - /// - /// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.). - /// - [JsonProperty("file_md5")] - public string MD5Hash { get; set; } - - // General - public double AudioLeadIn { get; set; } - public float StackLeniency { get; set; } = 0.7f; - public bool SpecialStyle { get; set; } - - [Column("RulesetID")] - public int RulesetInfoID { get; set; } - - public EFRulesetInfo RulesetInfo { get; set; } - - public bool LetterboxInBreaks { get; set; } - public bool WidescreenStoryboard { get; set; } - public bool EpilepsyWarning { get; set; } - - /// - /// Whether or not sound samples should change rate when playing with speed-changing mods. - /// TODO: only read/write supported for now, requires implementation in gameplay. - /// - public bool SamplesMatchPlaybackRate { get; set; } - - public CountdownType Countdown { get; set; } = CountdownType.Normal; - - /// - /// The number of beats to move the countdown backwards (compared to its default location). - /// - public int CountdownOffset { get; set; } - - [NotMapped] - public int[] Bookmarks { get; set; } = Array.Empty(); - - public double DistanceSpacing { get; set; } - public int BeatDivisor { get; set; } - public int GridSize { get; set; } - public double TimelineZoom { get; set; } - - // Metadata - [Column("Version")] - public string DifficultyName { get; set; } - - [JsonProperty("difficulty_rating")] - [Column("StarDifficulty")] - public double StarRating { get; set; } - - /// - /// Currently only populated for beatmap deletion. Use to query scores. - /// - public List Scores { get; set; } - - [JsonIgnore] - public DifficultyRating DifficultyRating => StarDifficulty.GetDifficultyRating(StarRating); - - public override string ToString() => this.GetDisplayTitle(); - - public bool Equals(EFBeatmapInfo other) - { - if (ReferenceEquals(this, other)) return true; - if (other == null) return false; - - if (ID != 0 && other.ID != 0) - return ID == other.ID; - - return false; - } - - public bool Equals(IBeatmapInfo other) => other is EFBeatmapInfo b && Equals(b); - - public bool AudioEquals(EFBeatmapInfo other) => other != null && BeatmapSetInfo != null && other.BeatmapSetInfo != null && - BeatmapSetInfo.Hash == other.BeatmapSetInfo.Hash && - (Metadata ?? BeatmapSetInfo.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSetInfo.Metadata).AudioFile; - - public bool BackgroundEquals(EFBeatmapInfo other) => other != null && BeatmapSetInfo != null && other.BeatmapSetInfo != null && - BeatmapSetInfo.Hash == other.BeatmapSetInfo.Hash && - (Metadata ?? BeatmapSetInfo.Metadata).BackgroundFile == (other.Metadata ?? other.BeatmapSetInfo.Metadata).BackgroundFile; - - /// - /// Returns a shallow-clone of this . - /// - public EFBeatmapInfo Clone() => (EFBeatmapInfo)MemberwiseClone(); - - #region Implementation of IHasOnlineID - - int IHasOnlineID.OnlineID => OnlineID ?? -1; - - #endregion - - #region Implementation of IBeatmapInfo - - [JsonIgnore] - IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata ?? BeatmapSetInfo?.Metadata ?? new EFBeatmapMetadata(); - - [JsonIgnore] - IBeatmapDifficultyInfo IBeatmapInfo.Difficulty => BaseDifficulty; - - [JsonIgnore] - IBeatmapSetInfo IBeatmapInfo.BeatmapSet => BeatmapSetInfo; - - [JsonIgnore] - IRulesetInfo IBeatmapInfo.Ruleset => RulesetInfo; - - #endregion - } -} diff --git a/osu.Game/Beatmaps/EFBeatmapMetadata.cs b/osu.Game/Beatmaps/EFBeatmapMetadata.cs deleted file mode 100644 index c0588f128c..0000000000 --- a/osu.Game/Beatmaps/EFBeatmapMetadata.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using Newtonsoft.Json; -using osu.Framework.Testing; -using osu.Game.Database; -using osu.Game.Online.API.Requests.Responses; -using osu.Game.Users; - -namespace osu.Game.Beatmaps -{ - [ExcludeFromDynamicCompile] - [Serializable] - [Table(@"BeatmapMetadata")] - public class EFBeatmapMetadata : IEquatable, IHasPrimaryKey, IBeatmapMetadataInfo - { - public int ID { get; set; } - - public bool IsManaged => ID > 0; - - public string Title { get; set; } = string.Empty; - - [JsonProperty("title_unicode")] - public string TitleUnicode { get; set; } = string.Empty; - - public string Artist { get; set; } = string.Empty; - - [JsonProperty("artist_unicode")] - public string ArtistUnicode { get; set; } = string.Empty; - - [JsonIgnore] - public List Beatmaps { get; set; } = new List(); - - [JsonIgnore] - public List BeatmapSets { get; set; } = new List(); - - /// - /// The author of the beatmaps in this set. - /// - [JsonIgnore] - public APIUser Author = new APIUser(); - - /// - /// Helper property to deserialize a username to . - /// - [JsonProperty(@"user_id")] - [Column("AuthorID")] - public int AuthorID - { - get => Author.Id; // This should not be used, but is required to make EF work correctly. - set => Author.Id = value; - } - - /// - /// Helper property to deserialize a username to . - /// - [JsonProperty(@"creator")] - [Column("Author")] - public string AuthorString - { - get => Author.Username; // This should not be used, but is required to make EF work correctly. - set => Author.Username = value; - } - - public string Source { get; set; } = string.Empty; - - [JsonProperty(@"tags")] - public string Tags { get; set; } = string.Empty; - - /// - /// The time in milliseconds to begin playing the track for preview purposes. - /// If -1, the track should begin playing at 40% of its length. - /// - public int PreviewTime { get; set; } = -1; - - public string AudioFile { get; set; } = string.Empty; - - public string BackgroundFile { get; set; } = string.Empty; - - public bool Equals(EFBeatmapMetadata other) => ((IBeatmapMetadataInfo)this).Equals(other); - - public override string ToString() => this.GetDisplayTitle(); - - IUser IBeatmapMetadataInfo.Author => Author; - } -} diff --git a/osu.Game/Beatmaps/EFBeatmapSetInfo.cs b/osu.Game/Beatmaps/EFBeatmapSetInfo.cs deleted file mode 100644 index 1f41d3727c..0000000000 --- a/osu.Game/Beatmaps/EFBeatmapSetInfo.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using JetBrains.Annotations; -using Newtonsoft.Json; -using osu.Framework.Testing; -using osu.Game.Database; -using osu.Game.Extensions; - -namespace osu.Game.Beatmaps -{ - [ExcludeFromDynamicCompile] - [Serializable] - [Table(@"BeatmapSetInfo")] - public class EFBeatmapSetInfo : IHasPrimaryKey, IHasFiles, ISoftDelete, IEquatable, IBeatmapSetInfo - { - public int ID { get; set; } - - public bool IsManaged => ID > 0; - - private int? onlineID; - - [Column("OnlineBeatmapSetID")] - public int? OnlineID - { - get => onlineID; - set => onlineID = value > 0 ? value : null; - } - - public DateTimeOffset DateAdded { get; set; } - - public EFBeatmapMetadata Metadata { get; set; } - - [NotNull] - public List Beatmaps { get; } = new List(); - - public BeatmapOnlineStatus Status { get; set; } = BeatmapOnlineStatus.None; - - public List Files { get; } = new List(); - - /// - /// The maximum star difficulty of all beatmaps in this set. - /// - [JsonIgnore] - public double MaxStarDifficulty => Beatmaps.Count == 0 ? 0 : Beatmaps.Max(b => b.StarRating); - - /// - /// The maximum playable length in milliseconds of all beatmaps in this set. - /// - [JsonIgnore] - public double MaxLength => Beatmaps.Count == 0 ? 0 : Beatmaps.Max(b => b.Length); - - /// - /// The maximum BPM of all beatmaps in this set. - /// - [JsonIgnore] - public double MaxBPM => Beatmaps.Count == 0 ? 0 : Beatmaps.Max(b => b.BPM); - - [NotMapped] - public bool DeletePending { get; set; } - - public string Hash { get; set; } - - /// - /// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null. - /// The path returned is relative to the user file storage. - /// - /// 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))?.FileInfo.GetStoragePath(); - - public override string ToString() => Metadata?.ToString() ?? base.ToString(); - - public bool Protected { get; set; } - - public bool Equals(EFBeatmapSetInfo other) - { - if (ReferenceEquals(this, other)) return true; - if (other == null) return false; - - if (ID != 0 && other.ID != 0) - return ID == other.ID; - - return false; - } - - public bool Equals(IBeatmapSetInfo other) => other is EFBeatmapSetInfo b && Equals(b); - - #region Implementation of IHasOnlineID - - int IHasOnlineID.OnlineID => OnlineID ?? -1; - - #endregion - - #region Implementation of IBeatmapSetInfo - - IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => Metadata ?? Beatmaps.FirstOrDefault()?.Metadata ?? new EFBeatmapMetadata(); - IEnumerable IBeatmapSetInfo.Beatmaps => Beatmaps; - IEnumerable IHasNamedFiles.Files => Files; - - #endregion - } -} diff --git a/osu.Game/Rulesets/EFRulesetInfo.cs b/osu.Game/Rulesets/EFRulesetInfo.cs deleted file mode 100644 index bd67bdb93c..0000000000 --- a/osu.Game/Rulesets/EFRulesetInfo.cs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System; -using System.ComponentModel.DataAnnotations.Schema; -using System.Diagnostics.CodeAnalysis; -using Newtonsoft.Json; -using osu.Framework.Testing; - -namespace osu.Game.Rulesets -{ - [ExcludeFromDynamicCompile] - [Table(@"RulesetInfo")] - public sealed class EFRulesetInfo : IEquatable, IComparable, IRulesetInfo - { - public int? ID { get; set; } - - public string Name { get; set; } - - public string ShortName { get; set; } - - public string InstantiationInfo { get; set; } - - [JsonIgnore] - public bool Available { get; set; } - - // TODO: this should probably be moved to RulesetStore. - public Ruleset CreateInstance() - { - if (!Available) - return null; - - var type = Type.GetType(InstantiationInfo); - - if (type == null) - return null; - - var ruleset = Activator.CreateInstance(type) as Ruleset; - - return ruleset; - } - - public bool Equals(EFRulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo; - - public int CompareTo(EFRulesetInfo other) => OnlineID.CompareTo(other.OnlineID); - - public int CompareTo(IRulesetInfo other) - { - if (!(other is EFRulesetInfo ruleset)) - throw new ArgumentException($@"Object is not of type {nameof(EFRulesetInfo)}.", nameof(other)); - - return CompareTo(ruleset); - } - - public override bool Equals(object obj) => obj is EFRulesetInfo rulesetInfo && Equals(rulesetInfo); - - public bool Equals(IRulesetInfo other) => other is RulesetInfo b && Equals(b); - - [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] - public override int GetHashCode() - { - unchecked - { - int hashCode = ID.HasValue ? ID.GetHashCode() : 0; - hashCode = (hashCode * 397) ^ (InstantiationInfo != null ? InstantiationInfo.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Name != null ? Name.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ Available.GetHashCode(); - return hashCode; - } - } - - public override string ToString() => Name ?? $"{Name} ({ShortName}) ID: {ID}"; - - #region Implementation of IHasOnlineID - - [NotMapped] - public int OnlineID - { - get => ID ?? -1; - set => ID = value >= 0 ? value : null; - } - - #endregion - } -} diff --git a/osu.Game/Scoring/EFScoreInfo.cs b/osu.Game/Scoring/EFScoreInfo.cs deleted file mode 100644 index 8a5bbbf0da..0000000000 --- a/osu.Game/Scoring/EFScoreInfo.cs +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using Newtonsoft.Json; -using osu.Framework.Localisation; -using osu.Game.Beatmaps; -using osu.Game.Database; -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.Users; -using osu.Game.Utils; - -namespace osu.Game.Scoring -{ - [Table(@"ScoreInfo")] - public class EFScoreInfo : IScoreInfo, IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable - { - public int ID { get; set; } - - public bool IsManaged => ID > 0; - - public ScoreRank Rank { get; set; } - - public long TotalScore { get; set; } - - [Column(TypeName = "DECIMAL(1,4)")] // TODO: This data type is wrong (should contain more precision). But at the same time, we probably don't need to be storing this in the database. - public double Accuracy { get; set; } - - public LocalisableString DisplayAccuracy => Accuracy.FormatAccuracy(); - - public double? PP { get; set; } - - public int MaxCombo { get; set; } - - public int Combo { get; set; } // Todo: Shouldn't exist in here - - public int RulesetID { get; set; } - - [NotMapped] - public bool Passed { get; set; } = true; - - public EFRulesetInfo Ruleset { get; set; } - - private APIMod[] localAPIMods; - - private Mod[] mods; - - [NotMapped] - public Mod[] Mods - { - get - { - var rulesetInstance = Ruleset?.CreateInstance(); - if (rulesetInstance == null) - return mods ?? Array.Empty(); - - Mod[] scoreMods = Array.Empty(); - - if (mods != null) - scoreMods = mods; - else if (localAPIMods != null) - scoreMods = APIMods.Select(m => m.ToMod(rulesetInstance)).ToArray(); - - return scoreMods; - } - set - { - localAPIMods = null; - mods = value; - } - } - - // Used for API serialisation/deserialisation. - [NotMapped] - public APIMod[] APIMods - { - get - { - if (localAPIMods != null) - return localAPIMods; - - if (mods == null) - return Array.Empty(); - - return localAPIMods = mods.Select(m => new APIMod(m)).ToArray(); - } - set - { - localAPIMods = value; - - // We potentially can't update this yet due to Ruleset being late-bound, so instead update on read as necessary. - mods = null; - } - } - - // Used for database serialisation/deserialisation. - [Column("Mods")] - public string ModsJson - { - get => JsonConvert.SerializeObject(APIMods); - set => APIMods = !string.IsNullOrEmpty(value) ? JsonConvert.DeserializeObject(value) : Array.Empty(); - } - - [NotMapped] - public APIUser User { get; set; } - - [Column("User")] - public string UserString - { - get => User?.Username; - set - { - User ??= new APIUser(); - User.Username = value; - } - } - - [Column("UserID")] - public int? UserID - { - get => User?.Id ?? 1; - set - { - User ??= new APIUser(); - User.Id = value ?? 1; - } - } - - public int BeatmapInfoID { get; set; } - - [Column("Beatmap")] - public EFBeatmapInfo BeatmapInfo { get; set; } - - private long? onlineID; - - [JsonProperty("id")] - [Column("OnlineScoreID")] - public long? OnlineID - { - get => onlineID; - set => onlineID = value > 0 ? value : null; - } - - public DateTimeOffset Date { get; set; } - - [NotMapped] - public Dictionary Statistics { get; set; } = new Dictionary(); - - [Column("Statistics")] - public string StatisticsJson - { - get => JsonConvert.SerializeObject(Statistics); - set - { - if (value == null) - { - Statistics.Clear(); - return; - } - - Statistics = JsonConvert.DeserializeObject>(value); - } - } - - [NotMapped] - public List HitEvents { get; set; } - - public List Files { get; } = new List(); - - public string Hash { get; set; } - - public bool DeletePending { get; set; } - - /// - /// The position of this score, starting at 1. - /// - [NotMapped] - public int? Position { get; set; } // TODO: remove after all calls to `CreateScoreInfo` are gone. - - /// - /// Whether this represents a legacy (osu!stable) score. - /// - [NotMapped] - public bool IsLegacyScore => Mods.OfType().Any(); - - public IEnumerable GetStatisticsForDisplay() - { - foreach (var r in Ruleset.CreateInstance().GetHitResults()) - { - int value = Statistics.GetValueOrDefault(r.result); - - switch (r.result) - { - case HitResult.SmallTickHit: - { - int total = value + Statistics.GetValueOrDefault(HitResult.SmallTickMiss); - if (total > 0) - yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName); - - break; - } - - case HitResult.LargeTickHit: - { - int total = value + Statistics.GetValueOrDefault(HitResult.LargeTickMiss); - if (total > 0) - yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName); - - break; - } - - case HitResult.SmallTickMiss: - case HitResult.LargeTickMiss: - break; - - default: - yield return new HitResultDisplayStatistic(r.result, value, null, r.displayName); - - break; - } - } - } - - public EFScoreInfo DeepClone() - { - var clone = (EFScoreInfo)MemberwiseClone(); - - clone.Statistics = new Dictionary(clone.Statistics); - - return clone; - } - - public override string ToString() => this.GetDisplayTitle(); - - public bool Equals(EFScoreInfo other) - { - if (ReferenceEquals(this, other)) return true; - if (other == null) return false; - - if (ID != 0 && other.ID != 0) - return ID == other.ID; - - return false; - } - - #region Implementation of IHasOnlineID - - long IHasOnlineID.OnlineID => OnlineID ?? -1; - - #endregion - - #region Implementation of IScoreInfo - - IBeatmapInfo IScoreInfo.Beatmap => BeatmapInfo; - IRulesetInfo IScoreInfo.Ruleset => Ruleset; - IUser IScoreInfo.User => User; - bool IScoreInfo.HasReplay => Files.Any(); - - #endregion - - IEnumerable IHasNamedFiles.Files => Files; - } -} diff --git a/osu.Game/Scoring/ScoreFileInfo.cs b/osu.Game/Scoring/ScoreFileInfo.cs deleted file mode 100644 index 6c3509bccd..0000000000 --- a/osu.Game/Scoring/ScoreFileInfo.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System.ComponentModel.DataAnnotations; -using osu.Game.Database; -using osu.Game.IO; - -namespace osu.Game.Scoring -{ - public class ScoreFileInfo : INamedFileInfo, IHasPrimaryKey, INamedFileUsage - { - public int ID { get; set; } - - public bool IsManaged => ID > 0; - - public int ScoreInfoID { get; set; } - - public EFScoreInfo ScoreInfo { get; set; } - - public int FileInfoID { get; set; } - - public FileInfo FileInfo { get; set; } - - [Required] - public string Filename { get; set; } - - IFileInfo INamedFileUsage.File => FileInfo; - } -} diff --git a/osu.Game/Skinning/EFSkinInfo.cs b/osu.Game/Skinning/EFSkinInfo.cs deleted file mode 100644 index 4204364c50..0000000000 --- a/osu.Game/Skinning/EFSkinInfo.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using osu.Framework.Extensions.ObjectExtensions; -using osu.Game.Database; -using osu.Game.Extensions; -using osu.Game.IO; - -namespace osu.Game.Skinning -{ - [Table(@"SkinInfo")] - public class EFSkinInfo : IHasFiles, IEquatable, IHasPrimaryKey, ISoftDelete - { - internal const int DEFAULT_SKIN = 0; - internal const int CLASSIC_SKIN = -1; - internal const int RANDOM_SKIN = -2; - - public int ID { get; set; } - - public string Name { get; set; } = string.Empty; - - public string Creator { get; set; } = string.Empty; - - public string Hash { get; set; } - - public string InstantiationInfo { get; set; } - - public virtual Skin CreateInstance(IStorageResourceProvider resources) - { - var type = string.IsNullOrEmpty(InstantiationInfo) - // handle the case of skins imported before InstantiationInfo was added. - ? typeof(LegacySkin) - : Type.GetType(InstantiationInfo).AsNonNull(); - - return (Skin)Activator.CreateInstance(type, this, resources); - } - - public List Files { get; set; } = new List(); - - public bool DeletePending { get; set; } - - public static EFSkinInfo Default { get; } = new EFSkinInfo - { - ID = DEFAULT_SKIN, - Name = "osu! (triangles)", - Creator = "team osu!", - InstantiationInfo = typeof(DefaultSkin).GetInvariantInstantiationInfo() - }; - - public bool Equals(EFSkinInfo other) => other != null && ID == other.ID; - - public override string ToString() - { - string author = Creator == null ? string.Empty : $"({Creator})"; - return $"{Name} {author}".Trim(); - } - - public bool IsManaged => ID > 0; - } -} diff --git a/osu.Game/Skinning/SkinFileInfo.cs b/osu.Game/Skinning/SkinFileInfo.cs deleted file mode 100644 index f3243d3f24..0000000000 --- a/osu.Game/Skinning/SkinFileInfo.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System.ComponentModel.DataAnnotations; -using osu.Game.Database; -using osu.Game.IO; - -namespace osu.Game.Skinning -{ - public class SkinFileInfo : INamedFileInfo, IHasPrimaryKey, INamedFileUsage - { - public int ID { get; set; } - - public bool IsManaged => ID > 0; - - public int SkinInfoID { get; set; } - - public EFSkinInfo SkinInfo { get; set; } - - public int FileInfoID { get; set; } - - public FileInfo FileInfo { get; set; } - - [Required] - public string Filename { get; set; } - - IFileInfo INamedFileUsage.File => FileInfo; - } -} From 4af76b9f48fd1d4602319af4552bee120b89f2ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Sep 2022 19:55:18 +0900 Subject: [PATCH 134/142] Apply the same change to `HandleFlip` --- .../Edit/OsuSelectionHandler.cs | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 280a35500a..eddc1390f0 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -127,16 +127,13 @@ namespace osu.Game.Rulesets.Osu.Edit { didFlip = true; - var controlPoints = slider.Path.ControlPoints.Select(p => - new PathControlPoint(new Vector2( - (direction == Direction.Horizontal ? -1 : 1) * p.Position.X, - (direction == Direction.Vertical ? -1 : 1) * p.Position.Y - ), p.Type)).ToArray(); - - // Importantly, update as a single operation so automatic adjustment of control points to different - // curve types does not unexpectedly trigger and change the slider's shape. - slider.Path.ControlPoints.Clear(); - slider.Path.ControlPoints.AddRange(controlPoints); + foreach (var cp in slider.Path.ControlPoints) + { + cp.Position = new Vector2( + (direction == Direction.Horizontal ? -1 : 1) * cp.Position.X, + (direction == Direction.Vertical ? -1 : 1) * cp.Position.Y + ); + } } } @@ -186,8 +183,8 @@ namespace osu.Game.Rulesets.Osu.Edit if (h is IHasPath path) { - foreach (PathControlPoint t in path.Path.ControlPoints) - t.Position = RotatePointAroundOrigin(t.Position, Vector2.Zero, delta); + foreach (PathControlPoint cp in path.Path.ControlPoints) + cp.Position = RotatePointAroundOrigin(cp.Position, Vector2.Zero, delta); } } From c7f8f948b928c9a240e6fc5f791ad07c7dd26746 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Sep 2022 17:36:14 +0900 Subject: [PATCH 135/142] Rename `CreateLegacySkinProvider` to `CreateSkinTransformer` and apply to all skins --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 11 +++++++++- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 11 +++++++++- .../TestSceneSliderApplication.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 11 +++++++++- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 11 +++++++++- .../Gameplay/TestSceneBeatmapSkinFallbacks.cs | 2 +- osu.Game/Rulesets/Ruleset.cs | 8 +++++++- .../Skinning/RulesetSkinProvidingContainer.cs | 20 +++++++++---------- osu.Game/Tests/Visual/SkinnableTestScene.cs | 2 +- 9 files changed, 60 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 999b9f6e2c..5c9c95827a 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -182,7 +182,16 @@ namespace osu.Game.Rulesets.Catch public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new CatchDifficultyCalculator(RulesetInfo, beatmap); - public override ISkin CreateLegacySkinProvider(ISkin skin, IBeatmap beatmap) => new CatchLegacySkinTransformer(skin); + public override ISkin? CreateSkinTransformer(ISkin skin, IBeatmap beatmap) + { + switch (skin) + { + case LegacySkin: + return new CatchLegacySkinTransformer(skin); + } + + return null; + } public override PerformanceCalculator CreatePerformanceCalculator() => new CatchPerformanceCalculator(); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index c6a5e8bdaa..061dedb07a 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -62,7 +62,16 @@ namespace osu.Game.Rulesets.Mania public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this); - public override ISkin CreateLegacySkinProvider(ISkin skin, IBeatmap beatmap) => new ManiaLegacySkinTransformer(skin, beatmap); + public override ISkin? CreateSkinTransformer(ISkin skin, IBeatmap beatmap) + { + switch (skin) + { + case LegacySkin: + return new ManiaLegacySkinTransformer(skin, beatmap); + } + + return null; + } public override IEnumerable ConvertFromLegacyMods(LegacyMods mods) { diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs index 08a62fe3ae..0169627867 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Osu.Tests var tintingSkin = skinManager.GetSkin(DefaultLegacySkin.CreateInfo()); tintingSkin.Configuration.ConfigDictionary["AllowSliderBallTint"] = "1"; - var provider = Ruleset.Value.CreateInstance().CreateLegacySkinProvider(tintingSkin, Beatmap.Value.Beatmap); + var provider = Ruleset.Value.CreateInstance().CreateSkinTransformer(tintingSkin, Beatmap.Value.Beatmap); Child = new SkinProvidingContainer(provider) { diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 6af765361d..0a5b3139fc 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -231,7 +231,16 @@ namespace osu.Game.Rulesets.Osu public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this); - public override ISkin CreateLegacySkinProvider(ISkin skin, IBeatmap beatmap) => new OsuLegacySkinTransformer(skin); + public override ISkin? CreateSkinTransformer(ISkin skin, IBeatmap beatmap) + { + switch (skin) + { + case LegacySkin: + return new OsuLegacySkinTransformer(skin); + } + + return null; + } public int LegacyID => 0; diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 6beb83575d..dc36bc0320 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -43,7 +43,16 @@ namespace osu.Game.Rulesets.Taiko public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap, this); - public override ISkin CreateLegacySkinProvider(ISkin skin, IBeatmap beatmap) => new TaikoLegacySkinTransformer(skin); + public override ISkin? CreateSkinTransformer(ISkin skin, IBeatmap beatmap) + { + switch (skin) + { + case LegacySkin: + return new TaikoLegacySkinTransformer(skin); + } + + return null; + } public const string SHORT_NAME = "taiko"; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 5c7321fb24..e12be6d3b4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -122,7 +122,7 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestOsuRuleset : OsuRuleset { - public override ISkin CreateLegacySkinProvider(ISkin skin, IBeatmap beatmap) => new TestOsuLegacySkinTransformer(skin); + public override ISkin CreateSkinTransformer(ISkin skin, IBeatmap beatmap) => new TestOsuLegacySkinTransformer(skin); private class TestOsuLegacySkinTransformer : OsuLegacySkinTransformer { diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 4bc82a5f7e..a446210c8a 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -200,7 +200,13 @@ namespace osu.Game.Rulesets public ModAutoplay? GetAutoplayMod() => CreateMod(); - public virtual ISkin? CreateLegacySkinProvider(ISkin skin, IBeatmap beatmap) => null; + /// + /// Create a transformer which adds lookups specific to a ruleset to skin sources. + /// + /// The source skin. + /// The current beatmap. + /// A skin with a transformer applied, or null if no transformation is provided by this ruleset. + public virtual ISkin? CreateSkinTransformer(ISkin skin, IBeatmap beatmap) => null; protected Ruleset() { diff --git a/osu.Game/Skinning/RulesetSkinProvidingContainer.cs b/osu.Game/Skinning/RulesetSkinProvidingContainer.cs index 937cf20bc6..d5690710bb 100644 --- a/osu.Game/Skinning/RulesetSkinProvidingContainer.cs +++ b/osu.Game/Skinning/RulesetSkinProvidingContainer.cs @@ -41,7 +41,7 @@ namespace osu.Game.Skinning Ruleset = ruleset; Beatmap = beatmap; - InternalChild = new BeatmapSkinProvidingContainer(beatmapSkin is LegacySkin ? GetLegacyRulesetTransformedSkin(beatmapSkin) : beatmapSkin) + InternalChild = new BeatmapSkinProvidingContainer(beatmapSkin is LegacySkin ? GetRulesetTransformedSkin(beatmapSkin) : beatmapSkin) { Child = Content = new Container { @@ -67,16 +67,16 @@ namespace osu.Game.Skinning Debug.Assert(ParentSource != null); - foreach (var skin in ParentSource.AllSources) + foreach (var source in ParentSource.AllSources) { - switch (skin) + switch (source) { - case LegacySkin legacySkin: - sources.Add(GetLegacyRulesetTransformedSkin(legacySkin)); + case Skin skin: + sources.Add(GetRulesetTransformedSkin(skin)); break; default: - sources.Add(skin); + sources.Add(source); break; } } @@ -94,16 +94,16 @@ namespace osu.Game.Skinning SetSources(sources); } - protected ISkin GetLegacyRulesetTransformedSkin(ISkin legacySkin) + protected ISkin GetRulesetTransformedSkin(ISkin skin) { - if (legacySkin == null) + if (skin == null) return null; - var rulesetTransformed = Ruleset.CreateLegacySkinProvider(legacySkin, Beatmap); + var rulesetTransformed = Ruleset.CreateSkinTransformer(skin, Beatmap); if (rulesetTransformed != null) return rulesetTransformed; - return legacySkin; + return skin; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index ffdde782a5..7278e1e93f 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual ISkin provider = skin; if (provider is LegacySkin legacyProvider) - provider = Ruleset.Value.CreateInstance().CreateLegacySkinProvider(legacyProvider, beatmap); + provider = Ruleset.Value.CreateInstance().CreateSkinTransformer(legacyProvider, beatmap); var children = new Container { From 5ce67345ae129f4d0fd80ca15bbb7130af15f8b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Sep 2022 01:02:38 +0900 Subject: [PATCH 136/142] Catch against sqlite initialisation failures --- osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs b/osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs index 007bec46bb..2fd1d06b7b 100644 --- a/osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs +++ b/osu.Game/Beatmaps/BeatmapUpdaterMetadataLookup.cs @@ -42,9 +42,16 @@ namespace osu.Game.Beatmaps public BeatmapUpdaterMetadataLookup(IAPIProvider api, Storage storage) { - // required to initialise native SQLite libraries on some platforms. - Batteries_V2.Init(); - raw.sqlite3_config(2 /*SQLITE_CONFIG_MULTITHREAD*/); + try + { + // required to initialise native SQLite libraries on some platforms. + Batteries_V2.Init(); + raw.sqlite3_config(2 /*SQLITE_CONFIG_MULTITHREAD*/); + } + catch + { + // may fail if platform not supported. + } this.api = api; this.storage = storage; From db822696eda362206817b1881c17e7613f79f4d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Sep 2022 01:02:46 +0900 Subject: [PATCH 137/142] Use latest version of sqlite3 bundle --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 953087f9c8..fed7c27f07 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -40,7 +40,7 @@ - + From 4f208416bedfd8cb3cd11474d7de2dca5aa40c8d Mon Sep 17 00:00:00 2001 From: Drison64 Date: Fri, 16 Sep 2022 13:50:18 +1400 Subject: [PATCH 138/142] Fixed irresponsiveness of score panel timestamp to time format --- .../Expanded/ExpandedPanelMiddleContent.cs | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index b496f4242d..e212dad65f 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -7,12 +7,14 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -34,6 +36,7 @@ namespace osu.Game.Screens.Ranking.Expanded private const float padding = 10; private readonly ScoreInfo score; + private Bindable bindableUse24HourDisplay; private readonly bool withFlair; private readonly List statisticDisplays = new List(); @@ -61,8 +64,9 @@ namespace osu.Game.Screens.Ranking.Expanded } [BackgroundDependencyLoader] - private void load(BeatmapDifficultyCache beatmapDifficultyCache) + private void load(BeatmapDifficultyCache beatmapDifficultyCache, OsuConfigManager config) { + bindableUse24HourDisplay = config.GetBindable(OsuSetting.Prefer24HourTime); var beatmap = score.BeatmapInfo; var metadata = beatmap.BeatmapSet?.Metadata ?? beatmap.Metadata; string creator = metadata.Author.Username; @@ -224,7 +228,7 @@ namespace osu.Game.Screens.Ranking.Expanded }); if (score.Date != default) - AddInternal(new PlayedOnText(score.Date)); + AddInternal(new PlayedOnText(score.Date, bindableUse24HourDisplay)); var starDifficulty = beatmapDifficultyCache.GetDifficultyAsync(beatmap, score.Ruleset, score.Mods).GetResultSafely(); @@ -280,12 +284,25 @@ namespace osu.Game.Screens.Ranking.Expanded public class PlayedOnText : OsuSpriteText { - public PlayedOnText(DateTimeOffset time) + private bool use24HourDisplay; + + public PlayedOnText(DateTimeOffset time, Bindable bindableUse24HourDisplay) { + use24HourDisplay = bindableUse24HourDisplay.Value; + bindableUse24HourDisplay.BindValueChanged(prefer24H => + { + use24HourDisplay = prefer24H.NewValue; + UpdateHourDisplay(time); + }, true); Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold); - Text = $"Played on {time.ToLocalTime():d MMMM yyyy HH:mm}"; + UpdateHourDisplay(time); + } + + public void UpdateHourDisplay(DateTimeOffset time) + { + Text = use24HourDisplay ? $"Played on {time.ToLocalTime():d MMMM yyyy HH:mm}" : $"Played on {time.ToLocalTime():d MMMM yyyy h:mm tt}"; } } } From d9499abc3387367b4922abda985c2ee90ff76c07 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 16 Sep 2022 13:14:25 +0900 Subject: [PATCH 139/142] Refactor to avoid passing around bindables --- .../Expanded/ExpandedPanelMiddleContent.cs | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index e212dad65f..829ba83696 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -36,7 +36,6 @@ namespace osu.Game.Screens.Ranking.Expanded private const float padding = 10; private readonly ScoreInfo score; - private Bindable bindableUse24HourDisplay; private readonly bool withFlair; private readonly List statisticDisplays = new List(); @@ -64,9 +63,8 @@ namespace osu.Game.Screens.Ranking.Expanded } [BackgroundDependencyLoader] - private void load(BeatmapDifficultyCache beatmapDifficultyCache, OsuConfigManager config) + private void load(BeatmapDifficultyCache beatmapDifficultyCache) { - bindableUse24HourDisplay = config.GetBindable(OsuSetting.Prefer24HourTime); var beatmap = score.BeatmapInfo; var metadata = beatmap.BeatmapSet?.Metadata ?? beatmap.Metadata; string creator = metadata.Author.Username; @@ -228,7 +226,7 @@ namespace osu.Game.Screens.Ranking.Expanded }); if (score.Date != default) - AddInternal(new PlayedOnText(score.Date, bindableUse24HourDisplay)); + AddInternal(new PlayedOnText(score.Date)); var starDifficulty = beatmapDifficultyCache.GetDifficultyAsync(beatmap, score.Ruleset, score.Mods).GetResultSafely(); @@ -284,25 +282,34 @@ namespace osu.Game.Screens.Ranking.Expanded public class PlayedOnText : OsuSpriteText { - private bool use24HourDisplay; + private readonly DateTimeOffset time; + private readonly Bindable prefer24HourTime = new Bindable(); - public PlayedOnText(DateTimeOffset time, Bindable bindableUse24HourDisplay) + public PlayedOnText(DateTimeOffset time) { - use24HourDisplay = bindableUse24HourDisplay.Value; - bindableUse24HourDisplay.BindValueChanged(prefer24H => - { - use24HourDisplay = prefer24H.NewValue; - UpdateHourDisplay(time); - }, true); + this.time = time; + Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold); - UpdateHourDisplay(time); } - public void UpdateHourDisplay(DateTimeOffset time) + [BackgroundDependencyLoader] + private void load(OsuConfigManager configManager) { - Text = use24HourDisplay ? $"Played on {time.ToLocalTime():d MMMM yyyy HH:mm}" : $"Played on {time.ToLocalTime():d MMMM yyyy h:mm tt}"; + configManager.BindWith(OsuSetting.Prefer24HourTime, prefer24HourTime); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + prefer24HourTime.BindValueChanged(_ => updateDisplay(), true); + } + + private void updateDisplay() + { + Text = prefer24HourTime.Value ? $"Played on {time.ToLocalTime():d MMMM yyyy HH:mm}" : $"Played on {time.ToLocalTime():d MMMM yyyy h:mm tt}"; } } } From 21ac3fd88be99c7b057e420b2d3d75dd5b592dff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Sep 2022 14:00:05 +0900 Subject: [PATCH 140/142] Catch any unobserved exceptions during beatmap metadata harvesting The process will retry when a connection is successful, so this doesn't need to be as loud as it was. Addresses https://github.com/ppy/osu/discussions/20331. --- osu.Game/Online/Metadata/OnlineMetadataClient.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Online/Metadata/OnlineMetadataClient.cs b/osu.Game/Online/Metadata/OnlineMetadataClient.cs index 95228c380f..06d24a82f3 100644 --- a/osu.Game/Online/Metadata/OnlineMetadataClient.cs +++ b/osu.Game/Online/Metadata/OnlineMetadataClient.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 System.Diagnostics; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR.Client; @@ -80,6 +81,10 @@ namespace osu.Game.Online.Metadata await ProcessChanges(catchUpChanges.BeatmapSetIDs); } } + catch (Exception e) + { + Logger.Log($"Error while processing catch-up of metadata ({e.Message})"); + } finally { catchingUp = false; From 289e6ad977f6e073ca61e20aafe961b3c0e479f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Sep 2022 15:12:05 +0900 Subject: [PATCH 141/142] Fix follow point animations in legacy skins not always starting at correct point in time --- .../Connections/FollowPointConnection.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index 0306c99fd5..1a9d12e860 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -1,9 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; +using System.Diagnostics; using osu.Framework.Graphics; using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Objects; @@ -21,32 +20,36 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections public const int SPACING = 32; public const double PREEMPT = 800; - public DrawablePool Pool; + public DrawablePool? Pool { private get; set; } protected override void OnApply(FollowPointLifetimeEntry entry) { base.OnApply(entry); - entry.Invalidated += onEntryInvalidated; - refreshPoints(); + entry.Invalidated += scheduleRefresh; + + // Our clock may not be correct at this point if `LoadComplete` has not run yet. + // Without a schedule, animations referencing FollowPoint's clock (see `IAnimationTimeReference`) would be incorrect on first pool usage. + scheduleRefresh(); } protected override void OnFree(FollowPointLifetimeEntry entry) { base.OnFree(entry); - entry.Invalidated -= onEntryInvalidated; + entry.Invalidated -= scheduleRefresh; // Return points to the pool. ClearInternal(false); } - private void onEntryInvalidated() => Scheduler.AddOnce(refreshPoints); - - private void refreshPoints() + private void scheduleRefresh() => Scheduler.AddOnce(() => { + Debug.Assert(Pool != null); + ClearInternal(false); var entry = Entry; + if (entry?.End == null) return; OsuHitObject start = entry.Start; @@ -95,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections } entry.LifetimeEnd = finalTransformEndTime; - } + }); /// /// Computes the fade time of follow point positioned between two hitobjects. From 85bb6f0beb859cf9f57d6ef6e68f28f557cb9c64 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 16 Sep 2022 00:54:16 +0900 Subject: [PATCH 142/142] Update framework Update framework (again) 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 2c186a52dd..77c29a5d6e 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 fed7c27f07..29e690a024 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 496bfbb85c..83410b08f6 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -82,7 +82,7 @@ - +