diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index 70b2c8c82a..14a4d02396 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Catch.Tests private Drawable setupSkinHierarchy(Drawable child, ISkin skin) { - var legacySkinProvider = new SkinProvidingContainer(skins.GetSkin(DefaultLegacySkin.Info)); + var legacySkinProvider = new SkinProvidingContainer(skins.GetSkin(DefaultLegacySkin.CreateInfo())); var testSkinProvider = new SkinProvidingContainer(skin); var legacySkinTransformer = new SkinProvidingContainer(new CatchLegacySkinTransformer(testSkinProvider)); diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs index a7e76e44b5..91f5f93905 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs @@ -56,13 +56,13 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor [Test] public void TestDefaultSkin() { - AddStep("set default skin", () => skins.CurrentSkinInfo.Value = SkinInfo.Default.ToLive()); + AddStep("set default skin", () => skins.CurrentSkinInfo.Value = DefaultSkin.CreateInfo().ToLive()); } [Test] public void TestLegacySkin() { - AddStep("set legacy skin", () => skins.CurrentSkinInfo.Value = DefaultLegacySkin.Info.ToLive()); + AddStep("set legacy skin", () => skins.CurrentSkinInfo.Value = DefaultLegacySkin.CreateInfo().ToLive()); } } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs index e698766aac..d673b7a6ac 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("create slider", () => { - var tintingSkin = skinManager.GetSkin(DefaultLegacySkin.Info); + var tintingSkin = skinManager.GetSkin(DefaultLegacySkin.CreateInfo()); tintingSkin.Configuration.ConfigDictionary["AllowSliderBallTint"] = "1"; Child = new SkinProvidingContainer(tintingSkin) diff --git a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs index 3bf6aaac7a..88f35976ad 100644 --- a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs @@ -167,7 +167,7 @@ namespace osu.Game.Tests.Gameplay private class TestSkin : LegacySkin { public TestSkin(string resourceName, IStorageResourceProvider resources) - : base(DefaultLegacySkin.Info, new TestResourceStore(resourceName), resources, "skin.ini") + : base(DefaultLegacySkin.CreateInfo(), new TestResourceStore(resourceName), resources, "skin.ini") { } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index c174a3edc2..cccc962a3f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestEmptyLegacyBeatmapSkinFallsBack() { - CreateSkinTest(SkinInfo.Default, () => new LegacyBeatmapSkin(new BeatmapInfo(), null, null)); + CreateSkinTest(DefaultSkin.CreateInfo(), () => new LegacyBeatmapSkin(new BeatmapInfo(), null, null)); AddUntilStep("wait for hud load", () => Player.ChildrenOfType().All(c => c.ComponentsLoaded)); AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value)); } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1ffbb4cb85..8af76c1289 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -256,10 +256,10 @@ namespace osu.Game if (skinInfo == null) { if (guid == SkinInfo.CLASSIC_SKIN) - skinInfo = DefaultLegacySkin.Info.ToLive(); + skinInfo = DefaultLegacySkin.CreateInfo().ToLive(); } - SkinManager.CurrentSkinInfo.Value = skinInfo ?? SkinInfo.Default.ToLive(); + SkinManager.CurrentSkinInfo.Value = skinInfo ?? DefaultSkin.CreateInfo().ToLive(); }; configSkin.TriggerChange(); @@ -660,7 +660,7 @@ namespace osu.Game // make config aware of how to lookup skins for on-screen display purposes. // if this becomes a more common thing, tracked settings should be reconsidered to allow local DI. - LocalConfig.LookupSkinName = id => SkinManager.GetAllUsableSkins().FirstOrDefault(s => s.ID == id)?.ToString() ?? "Unknown"; + LocalConfig.LookupSkinName = id => SkinManager.Query(s => s.ID == id)?.ToString() ?? "Unknown"; LocalConfig.LookupKeyBindings = l => { diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 88f60a6004..bf0f6bf142 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Settings.Sections Icon = FontAwesome.Solid.PaintBrush }; - private readonly Bindable> dropdownBindable = new Bindable> { Default = SkinInfo.Default.ToLive() }; + private readonly Bindable> dropdownBindable = new Bindable> { Default = DefaultSkin.CreateInfo().ToLive() }; private readonly Bindable configBindable = new Bindable(); private static readonly ILive random_skin_info = new SkinInfo @@ -81,7 +81,8 @@ namespace osu.Game.Overlays.Settings.Sections realmSkins = realmFactory.Context.All() .Where(s => !s.DeletePending) - .OrderBy(s => s.Name, StringComparer.OrdinalIgnoreCase); + .OrderBy(s => s.Protected) + .ThenBy(s => s.Name, StringComparer.OrdinalIgnoreCase); realmSubscription = realmSkins .SubscribeForNotifications((sender, changes, error) => @@ -141,11 +142,11 @@ namespace osu.Game.Overlays.Settings.Sections private void updateItems() { + int protectedCount = realmSkins.Count(s => s.Protected); + skinItems = realmSkins.ToLive(); - skinItems.Insert(0, SkinInfo.Default.ToLive()); - skinItems.Insert(1, DefaultLegacySkin.Info.ToLive()); - skinItems.Insert(2, random_skin_info); + skinItems.Insert(protectedCount, random_skin_info); skinDropdown.Items = skinItems; } diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 2332e6e160..c7033d37dc 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -12,8 +12,17 @@ namespace osu.Game.Skinning { public class DefaultLegacySkin : LegacySkin { + public static SkinInfo CreateInfo() => new SkinInfo + { + ID = Skinning.SkinInfo.CLASSIC_SKIN, // this is temporary until database storage is decided upon. + Name = "osu!classic", + Creator = "team osu!", + Protected = true, + InstantiationInfo = typeof(DefaultLegacySkin).GetInvariantInstantiationInfo() + }; + public DefaultLegacySkin(IStorageResourceProvider resources) - : this(Info, resources) + : this(CreateInfo(), resources) { } @@ -39,13 +48,5 @@ namespace osu.Game.Skinning Configuration.LegacyVersion = 2.7m; } - - public static SkinInfo Info { get; } = new SkinInfo - { - ID = osu.Game.Skinning.SkinInfo.CLASSIC_SKIN, // this is temporary until database storage is decided upon. - Name = "osu!classic", - Creator = "team osu!", - InstantiationInfo = typeof(DefaultLegacySkin).GetInvariantInstantiationInfo() - }; } } diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 8d3a83f589..951e3f9cc5 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -23,10 +23,19 @@ namespace osu.Game.Skinning { public class DefaultSkin : Skin { + public static SkinInfo CreateInfo() => new SkinInfo + { + ID = osu.Game.Skinning.SkinInfo.DEFAULT_SKIN, + Name = "osu! (triangles)", + Creator = "team osu!", + Protected = true, + InstantiationInfo = typeof(DefaultSkin).GetInvariantInstantiationInfo() + }; + private readonly IStorageResourceProvider resources; public DefaultSkin(IStorageResourceProvider resources) - : this(osu.Game.Skinning.SkinInfo.Default, resources) + : this(CreateInfo(), resources) { } diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index c09d45c227..9a82964933 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Testing; using osu.Game.Database; -using osu.Game.Extensions; using osu.Game.IO; using osu.Game.Models; using Realms; @@ -32,6 +31,8 @@ namespace osu.Game.Skinning public string Hash { get; set; } = string.Empty; + public bool Protected { get; set; } + public string InstantiationInfo { get; set; } = string.Empty; public virtual Skin CreateInstance(IStorageResourceProvider resources) @@ -48,14 +49,6 @@ namespace osu.Game.Skinning public bool DeletePending { get; set; } - public static SkinInfo Default { get; } = new SkinInfo - { - ID = DEFAULT_SKIN, - Name = "osu! (triangles)", - Creator = "team osu!", - InstantiationInfo = typeof(DefaultSkin).GetInvariantInstantiationInfo() - }; - public bool Equals(SkinInfo? other) { if (ReferenceEquals(this, other)) return true; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index bcad5277ab..5c07244e0f 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -45,7 +45,11 @@ namespace osu.Game.Skinning private readonly IResourceStore resources; public readonly Bindable CurrentSkin = new Bindable(); - public readonly Bindable> CurrentSkinInfo = new Bindable>(SkinInfo.Default.ToLive()) { Default = SkinInfo.Default.ToLive() }; + + public readonly Bindable> CurrentSkinInfo = new Bindable>(Skinning.DefaultSkin.CreateInfo().ToLive()) + { + Default = Skinning.DefaultSkin.CreateInfo().ToLive() + }; private readonly SkinModelManager skinModelManager; private readonly RealmContextFactory contextFactory; @@ -74,8 +78,24 @@ namespace osu.Game.Skinning skinModelManager = new SkinModelManager(storage, contextFactory, host, this); - DefaultLegacySkin = new DefaultLegacySkin(this); - DefaultSkin = new DefaultSkin(this); + var defaultSkins = new[] + { + DefaultLegacySkin = new DefaultLegacySkin(this), + DefaultSkin = new DefaultSkin(this), + }; + + // Ensure the default entries are present. + using (var context = contextFactory.CreateContext()) + using (var transaction = context.BeginWrite()) + { + foreach (var skin in defaultSkins) + { + if (context.Find(skin.SkinInfo.ID) == null) + context.Add(skin.SkinInfo.Value); + } + + transaction.Commit(); + } CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = skin.NewValue.PerformRead(GetSkin); @@ -89,21 +109,6 @@ namespace osu.Game.Skinning }; } - /// - /// Returns a list of all usable s. Includes the non-databased default skins. - /// - /// A newly allocated list of available . - public List> GetAllUsableSkins() - { - using (var context = contextFactory.CreateContext()) - { - var userSkins = context.All().Where(s => !s.DeletePending).ToLive(); - userSkins.Insert(0, DefaultSkin.SkinInfo); - userSkins.Insert(1, DefaultLegacySkin.SkinInfo); - return userSkins; - } - } - public void SelectRandomSkin() { using (var context = contextFactory.CreateContext()) @@ -113,7 +118,7 @@ namespace osu.Game.Skinning if (randomChoices.Length == 0) { - CurrentSkinInfo.Value = SkinInfo.Default.ToLive(); + CurrentSkinInfo.Value = Skinning.DefaultSkin.CreateInfo().ToLive(); return; }