diff --git a/osu.Android.props b/osu.Android.props index 1131203a95..563836d29b 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index a9e3575a49..7ec7d53a7e 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -90,7 +90,6 @@ namespace osu.Desktop Logger.Log("Starting legacy IPC provider..."); legacyIpc = new LegacyTcpIpcProvider(); legacyIpc.Bind(); - legacyIpc.StartAsync(); } catch (Exception ex) { diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs index 91f5f93905..a30e09cd29 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 = DefaultSkin.CreateInfo().ToLive()); + AddStep("set default skin", () => skins.CurrentSkinInfo.Value = DefaultSkin.CreateInfo().ToLiveUnmanaged()); } [Test] public void TestLegacySkin() { - AddStep("set legacy skin", () => skins.CurrentSkinInfo.Value = DefaultLegacySkin.CreateInfo().ToLive()); + AddStep("set legacy skin", () => skins.CurrentSkinInfo.Value = DefaultLegacySkin.CreateInfo().ToLiveUnmanaged()); } } } diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index 7314021a14..5c6b907e42 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -3,8 +3,10 @@ using System.Collections.Generic; using System.ComponentModel; +using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Input.StateChanges.Events; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu @@ -39,6 +41,19 @@ namespace osu.Game.Rulesets.Osu return base.Handle(e); } + protected override bool HandleMouseTouchStateChange(TouchStateChangeEvent e) + { + if (!AllowUserCursorMovement) + { + // Still allow for forwarding of the "touch" part, but replace the positional data with that of the mouse. + // Primarily relied upon by the "autopilot" osu! mod. + var touch = new Touch(e.Touch.Source, CurrentState.Mouse.Position); + e = new TouchStateChangeEvent(e.State, e.Input, touch, e.IsActive, null); + } + + return base.HandleMouseTouchStateChange(e); + } + private class OsuKeyBindingContainer : RulesetKeyBindingContainer { public bool AllowUserPresses = true; diff --git a/osu.Game.Tests/Database/RealmLiveTests.cs b/osu.Game.Tests/Database/RealmLiveTests.cs index 9b6769b788..06cb5a3607 100644 --- a/osu.Game.Tests/Database/RealmLiveTests.cs +++ b/osu.Game.Tests/Database/RealmLiveTests.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using NUnit.Framework; +using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Models; using Realms; @@ -21,14 +22,41 @@ namespace osu.Game.Tests.Database { RunTestWithRealm((realmFactory, _) => { - ILive beatmap = realmFactory.CreateContext().Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))).ToLive(); + ILive beatmap = realmFactory.CreateContext().Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))).ToLive(realmFactory); - ILive beatmap2 = realmFactory.CreateContext().All().First().ToLive(); + ILive beatmap2 = realmFactory.CreateContext().All().First().ToLive(realmFactory); Assert.AreEqual(beatmap, beatmap2); }); } + [Test] + public void TestAccessAfterStorageMigrate() + { + RunTestWithRealm((realmFactory, storage) => + { + var beatmap = new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()); + + ILive liveBeatmap; + + using (var context = realmFactory.CreateContext()) + { + context.Write(r => r.Add(beatmap)); + + liveBeatmap = beatmap.ToLive(realmFactory); + } + + using (var migratedStorage = new TemporaryNativeStorage("realm-test-migration-target")) + { + migratedStorage.DeleteDirectory(string.Empty); + + storage.Migrate(migratedStorage); + + Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden)); + } + }); + } + [Test] public void TestAccessAfterAttach() { @@ -36,7 +64,7 @@ namespace osu.Game.Tests.Database { var beatmap = new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()); - var liveBeatmap = beatmap.ToLive(); + var liveBeatmap = beatmap.ToLive(realmFactory); using (var context = realmFactory.CreateContext()) context.Write(r => r.Add(beatmap)); @@ -49,7 +77,7 @@ namespace osu.Game.Tests.Database public void TestAccessNonManaged() { var beatmap = new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()); - var liveBeatmap = beatmap.ToLive(); + var liveBeatmap = beatmap.ToLiveUnmanaged(); Assert.IsFalse(beatmap.Hidden); Assert.IsFalse(liveBeatmap.Value.Hidden); @@ -74,7 +102,7 @@ namespace osu.Game.Tests.Database { var beatmap = threadContext.Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))); - liveBeatmap = beatmap.ToLive(); + liveBeatmap = beatmap.ToLive(realmFactory); } }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); @@ -103,7 +131,7 @@ namespace osu.Game.Tests.Database { var beatmap = threadContext.Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))); - liveBeatmap = beatmap.ToLive(); + liveBeatmap = beatmap.ToLive(realmFactory); } }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); @@ -123,7 +151,7 @@ namespace osu.Game.Tests.Database RunTestWithRealm((realmFactory, _) => { var beatmap = new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()); - var liveBeatmap = beatmap.ToLive(); + var liveBeatmap = beatmap.ToLive(realmFactory); Assert.DoesNotThrow(() => { @@ -145,7 +173,7 @@ namespace osu.Game.Tests.Database { var beatmap = threadContext.Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))); - liveBeatmap = beatmap.ToLive(); + liveBeatmap = beatmap.ToLive(realmFactory); } }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); @@ -183,7 +211,7 @@ namespace osu.Game.Tests.Database { var beatmap = threadContext.Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))); - liveBeatmap = beatmap.ToLive(); + liveBeatmap = beatmap.ToLive(realmFactory); } }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); @@ -222,7 +250,7 @@ namespace osu.Game.Tests.Database // not just a refresh from the resolved Live. threadContext.Write(r => r.Add(new RealmBeatmap(ruleset, new RealmBeatmapDifficulty(), new RealmBeatmapMetadata()))); - liveBeatmap = beatmap.ToLive(); + liveBeatmap = beatmap.ToLive(realmFactory); } }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait(); diff --git a/osu.Game.Tests/Database/RealmTest.cs b/osu.Game.Tests/Database/RealmTest.cs index 04c9f2577a..6904464485 100644 --- a/osu.Game.Tests/Database/RealmTest.cs +++ b/osu.Game.Tests/Database/RealmTest.cs @@ -10,6 +10,7 @@ using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Database; +using osu.Game.IO; using osu.Game.Models; #nullable enable @@ -27,15 +28,16 @@ namespace osu.Game.Tests.Database storage.DeleteDirectory(string.Empty); } - protected void RunTestWithRealm(Action testAction, [CallerMemberName] string caller = "") + protected void RunTestWithRealm(Action testAction, [CallerMemberName] string caller = "") { using (HeadlessGameHost host = new CleanRunHeadlessGameHost(caller)) { host.Run(new RealmTestGame(() => { - var testStorage = storage.GetStorageForDirectory(caller); + // ReSharper disable once AccessToDisposedClosure + var testStorage = new OsuStorage(host, storage.GetStorageForDirectory(caller)); - using (var realmFactory = new RealmContextFactory(testStorage, caller)) + using (var realmFactory = new RealmContextFactory(testStorage, "client")) { Logger.Log($"Running test using realm file {testStorage.GetFullPath(realmFactory.Filename)}"); testAction(realmFactory, testStorage); @@ -58,7 +60,7 @@ namespace osu.Game.Tests.Database { var testStorage = storage.GetStorageForDirectory(caller); - using (var realmFactory = new RealmContextFactory(testStorage, caller)) + using (var realmFactory = new RealmContextFactory(testStorage, "client")) { Logger.Log($"Running test using realm file {testStorage.GetFullPath(realmFactory.Filename)}"); await testAction(realmFactory, testStorage); diff --git a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs index 88f35976ad..3aab28886e 100644 --- a/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneStoryboardSamples.cs @@ -15,6 +15,7 @@ using osu.Framework.IO.Stores; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Audio; +using osu.Game.Database; using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -220,6 +221,7 @@ namespace osu.Game.Tests.Gameplay public AudioManager AudioManager => Audio; public IResourceStore Files => null; public new IResourceStore Resources => base.Resources; + public RealmContextFactory RealmContextFactory => null; public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => null; #endregion diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs index 1d3dd35e1d..3211405670 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs @@ -235,7 +235,7 @@ namespace osu.Game.Tests.Visual.Background private void setCustomSkin() { // feign a skin switch. this doesn't do anything except force CurrentSkin to become a LegacySkin. - AddStep("set custom skin", () => skins.CurrentSkinInfo.Value = new SkinInfo().ToLive()); + AddStep("set custom skin", () => skins.CurrentSkinInfo.Value = new SkinInfo().ToLiveUnmanaged()); } private void setDefaultSkin() => AddStep("set default skin", () => skins.CurrentSkinInfo.SetDefault()); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index cccc962a3f..c5f56cae9e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("setup skins", () => { - skinManager.CurrentSkinInfo.Value = gameCurrentSkin.ToLive(); + skinManager.CurrentSkinInfo.Value = gameCurrentSkin.ToLiveUnmanaged(); currentBeatmapSkin = getBeatmapSkin(); }); }); diff --git a/osu.Game/Beatmaps/WorkingBeatmapCache.cs b/osu.Game/Beatmaps/WorkingBeatmapCache.cs index 559685d3c4..449406eadf 100644 --- a/osu.Game/Beatmaps/WorkingBeatmapCache.cs +++ b/osu.Game/Beatmaps/WorkingBeatmapCache.cs @@ -15,6 +15,7 @@ using osu.Framework.Platform; using osu.Framework.Statistics; using osu.Framework.Testing; using osu.Game.Beatmaps.Formats; +using osu.Game.Database; using osu.Game.IO; using osu.Game.Skinning; using osu.Game.Storyboards; @@ -108,6 +109,7 @@ namespace osu.Game.Beatmaps TextureStore IBeatmapResourceProvider.LargeTextureStore => largeTextureStore; ITrackStore IBeatmapResourceProvider.Tracks => trackStore; AudioManager IStorageResourceProvider.AudioManager => audioManager; + RealmContextFactory IStorageResourceProvider.RealmContextFactory => null; IResourceStore IStorageResourceProvider.Files => files; IResourceStore IStorageResourceProvider.Resources => resources; IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host?.CreateTextureLoaderStore(underlyingStore); diff --git a/osu.Game/Database/RealmLive.cs b/osu.Game/Database/RealmLive.cs index c376d5d503..90b8814c24 100644 --- a/osu.Game/Database/RealmLive.cs +++ b/osu.Game/Database/RealmLive.cs @@ -24,13 +24,17 @@ namespace osu.Game.Database /// private readonly T data; + private readonly RealmContextFactory realmFactory; + /// /// Construct a new instance of live realm data. /// /// The realm data. - public RealmLive(T data) + /// The realm factory the data was sourced from. May be null for an unmanaged object. + public RealmLive(T data, RealmContextFactory realmFactory) { this.data = data; + this.realmFactory = realmFactory; ID = data.ID; } @@ -47,7 +51,7 @@ namespace osu.Game.Database return; } - using (var realm = Realm.GetInstance(data.Realm.Config)) + using (var realm = realmFactory.CreateContext()) perform(realm.Find(ID)); } @@ -58,12 +62,12 @@ namespace osu.Game.Database public TReturn PerformRead(Func perform) { if (typeof(RealmObjectBase).IsAssignableFrom(typeof(TReturn))) - throw new InvalidOperationException($"Realm live objects should not exit the scope of {nameof(PerformRead)}."); + throw new InvalidOperationException(@$"Realm live objects should not exit the scope of {nameof(PerformRead)}."); if (!IsManaged) return perform(data); - using (var realm = Realm.GetInstance(data.Realm.Config)) + using (var realm = realmFactory.CreateContext()) return perform(realm.Find(ID)); } @@ -74,7 +78,7 @@ namespace osu.Game.Database public void PerformWrite(Action perform) { if (!IsManaged) - throw new InvalidOperationException("Can't perform writes on a non-managed underlying value"); + throw new InvalidOperationException(@"Can't perform writes on a non-managed underlying value"); PerformRead(t => { @@ -94,11 +98,7 @@ namespace osu.Game.Database if (!ThreadSafety.IsUpdateThread) throw new InvalidOperationException($"Can't use {nameof(Value)} on managed objects from non-update threads"); - // When using Value, we rely on garbage collection for the realm instance used to retrieve the instance. - // As we are sure that this is on the update thread, there should always be an open and constantly refreshing realm instance to ensure file size growth is a non-issue. - var realm = Realm.GetInstance(data.Realm.Config); - - return realm.Find(ID); + return realmFactory.Context.Find(ID); } } diff --git a/osu.Game/Database/RealmLiveUnmanaged.cs b/osu.Game/Database/RealmLiveUnmanaged.cs new file mode 100644 index 0000000000..5a69898206 --- /dev/null +++ b/osu.Game/Database/RealmLiveUnmanaged.cs @@ -0,0 +1,44 @@ +// 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 Realms; + +#nullable enable + +namespace osu.Game.Database +{ + /// + /// Provides a method of working with unmanaged realm objects. + /// Usually used for testing purposes where the instance is never required to be managed. + /// + /// The underlying object type. + public class RealmLiveUnmanaged : ILive where T : RealmObjectBase, IHasGuidPrimaryKey + { + /// + /// Construct a new instance of live realm data. + /// + /// The realm data. + public RealmLiveUnmanaged(T data) + { + Value = data; + } + + public bool Equals(ILive? other) => ID == other?.ID; + + public Guid ID => Value.ID; + + public void PerformRead(Action perform) => perform(Value); + + public TReturn PerformRead(Func perform) => perform(Value); + + public void PerformWrite(Action perform) => throw new InvalidOperationException(@"Can't perform writes on a non-managed underlying value"); + + public bool IsManaged => false; + + /// + /// The original live data used to create this instance. + /// + public T Value { get; } + } +} diff --git a/osu.Game/Database/RealmObjectExtensions.cs b/osu.Game/Database/RealmObjectExtensions.cs index b38e21453c..e5177823ba 100644 --- a/osu.Game/Database/RealmObjectExtensions.cs +++ b/osu.Game/Database/RealmObjectExtensions.cs @@ -53,16 +53,28 @@ namespace osu.Game.Database return mapper.Map(item); } - public static List> ToLive(this IEnumerable realmList) + public static List> ToLiveUnmanaged(this IEnumerable realmList) where T : RealmObject, IHasGuidPrimaryKey { - return realmList.Select(l => new RealmLive(l)).Cast>().ToList(); + return realmList.Select(l => new RealmLiveUnmanaged(l)).Cast>().ToList(); } - public static ILive ToLive(this T realmObject) + public static ILive ToLiveUnmanaged(this T realmObject) where T : RealmObject, IHasGuidPrimaryKey { - return new RealmLive(realmObject); + return new RealmLiveUnmanaged(realmObject); + } + + public static List> ToLive(this IEnumerable realmList, RealmContextFactory realmContextFactory) + where T : RealmObject, IHasGuidPrimaryKey + { + return realmList.Select(l => new RealmLive(l, realmContextFactory)).Cast>().ToList(); + } + + public static ILive ToLive(this T realmObject, RealmContextFactory realmContextFactory) + where T : RealmObject, IHasGuidPrimaryKey + { + return new RealmLive(realmObject, realmContextFactory); } /// diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index 3fa90e2330..8e272f637f 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -72,18 +72,21 @@ namespace osu.Game.Graphics.Cursor protected override bool OnMouseDown(MouseDownEvent e) { - // only trigger animation for main mouse buttons - activeCursor.Scale = new Vector2(1); - activeCursor.ScaleTo(0.90f, 800, Easing.OutQuint); - - activeCursor.AdditiveLayer.Alpha = 0; - activeCursor.AdditiveLayer.FadeInFromZero(800, Easing.OutQuint); - - if (cursorRotate.Value && dragRotationState != DragRotationState.Rotating) + if (State.Value == Visibility.Visible) { - // if cursor is already rotating don't reset its rotate origin - dragRotationState = DragRotationState.DragStarted; - positionMouseDown = e.MousePosition; + // only trigger animation for main mouse buttons + activeCursor.Scale = new Vector2(1); + activeCursor.ScaleTo(0.90f, 800, Easing.OutQuint); + + activeCursor.AdditiveLayer.Alpha = 0; + activeCursor.AdditiveLayer.FadeInFromZero(800, Easing.OutQuint); + + if (cursorRotate.Value && dragRotationState != DragRotationState.Rotating) + { + // if cursor is already rotating don't reset its rotate origin + dragRotationState = DragRotationState.DragStarted; + positionMouseDown = e.MousePosition; + } } return base.OnMouseDown(e); diff --git a/osu.Game/Graphics/DateTooltip.cs b/osu.Game/Graphics/DateTooltip.cs index 3094f9cc2b..d5768b259a 100644 --- a/osu.Game/Graphics/DateTooltip.cs +++ b/osu.Game/Graphics/DateTooltip.cs @@ -65,8 +65,10 @@ namespace osu.Game.Graphics public void SetContent(DateTimeOffset date) { - dateText.Text = $"{date:d MMMM yyyy} "; - timeText.Text = $"{date:HH:mm:ss \"UTC\"z}"; + DateTimeOffset localDate = date.ToLocalTime(); + + dateText.Text = $"{localDate:d MMMM yyyy} "; + timeText.Text = $"{localDate:HH:mm:ss \"UTC\"z}"; } public void Move(Vector2 pos) => Position = pos; diff --git a/osu.Game/IO/IStorageResourceProvider.cs b/osu.Game/IO/IStorageResourceProvider.cs index e4c97e18fa..950b5aae09 100644 --- a/osu.Game/IO/IStorageResourceProvider.cs +++ b/osu.Game/IO/IStorageResourceProvider.cs @@ -4,6 +4,7 @@ using osu.Framework.Audio; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Game.Database; namespace osu.Game.IO { @@ -24,6 +25,11 @@ namespace osu.Game.IO /// IResourceStore Resources { get; } + /// + /// Access realm. + /// + RealmContextFactory RealmContextFactory { get; } + /// /// Create a texture loader store based on an underlying data store. /// diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index a35191613c..a4471b56b9 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -255,10 +255,10 @@ namespace osu.Game if (skinInfo == null) { if (guid == SkinInfo.CLASSIC_SKIN) - skinInfo = DefaultLegacySkin.CreateInfo().ToLive(); + skinInfo = DefaultLegacySkin.CreateInfo().ToLiveUnmanaged(); } - SkinManager.CurrentSkinInfo.Value = skinInfo ?? DefaultSkin.CreateInfo().ToLive(); + SkinManager.CurrentSkinInfo.Value = skinInfo ?? DefaultSkin.CreateInfo().ToLiveUnmanaged(); }; configSkin.TriggerChange(); @@ -1149,16 +1149,11 @@ namespace osu.Game } } - private void screenPushed(IScreen lastScreen, IScreen newScreen) - { - ScreenChanged(lastScreen, newScreen); - Logger.Log($"Screen changed → {newScreen}"); - } + private void screenPushed(IScreen lastScreen, IScreen newScreen) => ScreenChanged(lastScreen, newScreen); private void screenExited(IScreen lastScreen, IScreen newScreen) { ScreenChanged(lastScreen, newScreen); - Logger.Log($"Screen changed ← {newScreen}"); if (newScreen == null) Exit(); diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index b1582d7bee..0fa6d78d4b 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -32,14 +32,14 @@ namespace osu.Game.Overlays.Settings.Sections Icon = FontAwesome.Solid.PaintBrush }; - private readonly Bindable> dropdownBindable = new Bindable> { Default = DefaultSkin.CreateInfo().ToLive() }; + private readonly Bindable> dropdownBindable = new Bindable> { Default = DefaultSkin.CreateInfo().ToLiveUnmanaged() }; private readonly Bindable configBindable = new Bindable(); private static readonly ILive random_skin_info = new SkinInfo { ID = SkinInfo.RANDOM_SKIN, Name = "", - }.ToLive(); + }.ToLiveUnmanaged(); private List> skinItems; @@ -133,7 +133,7 @@ namespace osu.Game.Overlays.Settings.Sections { int protectedCount = realmSkins.Count(s => s.Protected); - skinItems = realmSkins.ToLive(); + skinItems = realmSkins.ToLive(realmFactory); skinItems.Insert(protectedCount, random_skin_info); diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 54fc2340f1..d606d94b97 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -43,7 +43,11 @@ namespace osu.Game.Skinning protected Skin(SkinInfo skin, IStorageResourceProvider resources, [CanBeNull] Stream configurationStream = null) { - SkinInfo = skin.ToLive(); + SkinInfo = resources?.RealmContextFactory != null + ? skin.ToLive(resources.RealmContextFactory) + // This path should only be used in some tests. + : skin.ToLiveUnmanaged(); + this.resources = resources; configurationStream ??= getConfigurationStream(); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 5134632fb1..bb2f0a37b4 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -47,9 +47,9 @@ namespace osu.Game.Skinning public readonly Bindable CurrentSkin = new Bindable(); - public readonly Bindable> CurrentSkinInfo = new Bindable>(Skinning.DefaultSkin.CreateInfo().ToLive()) + public readonly Bindable> CurrentSkinInfo = new Bindable>(Skinning.DefaultSkin.CreateInfo().ToLiveUnmanaged()) { - Default = Skinning.DefaultSkin.CreateInfo().ToLive() + Default = Skinning.DefaultSkin.CreateInfo().ToLiveUnmanaged() }; private readonly SkinModelManager skinModelManager; @@ -119,13 +119,13 @@ namespace osu.Game.Skinning if (randomChoices.Length == 0) { - CurrentSkinInfo.Value = Skinning.DefaultSkin.CreateInfo().ToLive(); + CurrentSkinInfo.Value = Skinning.DefaultSkin.CreateInfo().ToLiveUnmanaged(); return; } var chosen = randomChoices.ElementAt(RNG.Next(0, randomChoices.Length)); - CurrentSkinInfo.Value = chosen.ToLive(); + CurrentSkinInfo.Value = chosen.ToLive(contextFactory); } } @@ -182,7 +182,7 @@ namespace osu.Game.Skinning public ILive Query(Expression> query) { using (var context = contextFactory.CreateContext()) - return context.All().FirstOrDefault(query)?.ToLive(); + return context.All().FirstOrDefault(query)?.ToLive(contextFactory); } public event Action SourceChanged; @@ -237,6 +237,7 @@ namespace osu.Game.Skinning AudioManager IStorageResourceProvider.AudioManager => audio; IResourceStore IStorageResourceProvider.Resources => resources; IResourceStore IStorageResourceProvider.Files => userFiles; + RealmContextFactory IStorageResourceProvider.RealmContextFactory => contextFactory; IResourceStore IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); #endregion @@ -302,7 +303,7 @@ namespace osu.Game.Skinning Guid currentUserSkin = CurrentSkinInfo.Value.ID; if (items.Any(s => s.ID == currentUserSkin)) - scheduler.Add(() => CurrentSkinInfo.Value = Skinning.DefaultSkin.CreateInfo().ToLive()); + scheduler.Add(() => CurrentSkinInfo.Value = Skinning.DefaultSkin.CreateInfo().ToLiveUnmanaged()); skinModelManager.Delete(items.ToList(), silent); } diff --git a/osu.Game/Stores/RealmArchiveModelImporter.cs b/osu.Game/Stores/RealmArchiveModelImporter.cs index 1681dad750..4aca079e2e 100644 --- a/osu.Game/Stores/RealmArchiveModelImporter.cs +++ b/osu.Game/Stores/RealmArchiveModelImporter.cs @@ -352,7 +352,7 @@ namespace osu.Game.Stores transaction.Commit(); } - return existing.ToLive(); + return existing.ToLive(ContextFactory); } LogForModel(item, @"Found existing (optimised) but failed pre-check."); @@ -387,7 +387,7 @@ namespace osu.Game.Stores existing.DeletePending = false; transaction.Commit(); - return existing.ToLive(); + return existing.ToLive(ContextFactory); } LogForModel(item, @"Found existing but failed re-use check."); @@ -416,7 +416,7 @@ namespace osu.Game.Stores throw; } - return item.ToLive(); + return item.ToLive(ContextFactory); } } diff --git a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs index 31a2071249..f919edecf7 100644 --- a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs +++ b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs @@ -14,6 +14,7 @@ using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; +using osu.Game.Database; using osu.Game.IO; using osu.Game.Models; using osu.Game.Rulesets; @@ -118,6 +119,7 @@ namespace osu.Game.Tests.Beatmaps public IResourceStore Files => userSkinResourceStore; public new IResourceStore Resources => base.Resources; public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => null; + RealmContextFactory IStorageResourceProvider.RealmContextFactory => null; #endregion diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index cdd3e47930..22aac79056 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.Graphics.Sprites; using osu.Game.IO; using osu.Game.Rulesets; @@ -158,6 +159,7 @@ namespace osu.Game.Tests.Visual public IResourceStore Files => null; public new IResourceStore Resources => base.Resources; public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); + RealmContextFactory IStorageResourceProvider.RealmContextFactory => null; #endregion diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index feae990df7..0e8486cabc 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 27ac1bf647..42d8962c14 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -60,7 +60,7 @@ - + @@ -83,7 +83,7 @@ - +