mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 13:15:08 +08:00
Add proof of concept flow to ensure RealmBackedResourceStore
is invalidated on realm file changes
I'm not at all happy with this, but it does work so let's go with it for now.
This commit is contained in:
parent
66f5eae530
commit
9c3dad9fbf
@ -11,6 +11,7 @@ using osu.Framework.IO.Stores;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets.Objects.Legacy;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
@ -37,11 +38,11 @@ namespace osu.Game.Skinning
|
||||
|
||||
private static IResourceStore<byte[]> createRealmBackedStore(BeatmapInfo beatmapInfo, IStorageResourceProvider? resources)
|
||||
{
|
||||
if (resources == null)
|
||||
if (resources == null || beatmapInfo.BeatmapSet == null)
|
||||
// should only ever be used in tests.
|
||||
return new ResourceStore<byte[]>();
|
||||
|
||||
return new RealmBackedResourceStore(beatmapInfo.BeatmapSet, resources.Files, new[] { @"ogg" });
|
||||
return new RealmBackedResourceStore<BeatmapSetInfo>(beatmapInfo.BeatmapSet.ToLive(resources.RealmAccess), resources.Files, resources.RealmAccess);
|
||||
}
|
||||
|
||||
public override Drawable? GetDrawableComponent(ISkinComponent component)
|
||||
|
@ -1,51 +1,68 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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;
|
||||
using System.Threading;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Extensions;
|
||||
using Realms;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class RealmBackedResourceStore : ResourceStore<byte[]>
|
||||
public class RealmBackedResourceStore<T> : ResourceStore<byte[]>
|
||||
where T : RealmObject, IHasRealmFiles, IHasGuidPrimaryKey
|
||||
{
|
||||
private readonly Dictionary<string, string> fileToStoragePathMapping = new Dictionary<string, string>();
|
||||
private Lazy<Dictionary<string, string>> fileToStoragePathMapping;
|
||||
|
||||
public RealmBackedResourceStore(IHasRealmFiles source, IResourceStore<byte[]> underlyingStore, string[] extensions = null)
|
||||
private readonly Live<T> liveSource;
|
||||
|
||||
public RealmBackedResourceStore(Live<T> source, IResourceStore<byte[]> underlyingStore, RealmAccess? realm)
|
||||
: base(underlyingStore)
|
||||
{
|
||||
// Must be initialised before the file cache.
|
||||
if (extensions != null)
|
||||
{
|
||||
foreach (string extension in extensions)
|
||||
AddExtension(extension);
|
||||
}
|
||||
liveSource = source;
|
||||
|
||||
initialiseFileCache(source);
|
||||
invalidateCache();
|
||||
Debug.Assert(fileToStoragePathMapping != null);
|
||||
}
|
||||
|
||||
private void initialiseFileCache(IHasRealmFiles source)
|
||||
{
|
||||
fileToStoragePathMapping.Clear();
|
||||
foreach (var f in source.Files)
|
||||
fileToStoragePathMapping[f.Filename.ToLowerInvariant()] = f.File.GetStoragePath();
|
||||
}
|
||||
public void Invalidate() => invalidateCache();
|
||||
|
||||
protected override IEnumerable<string> GetFilenames(string name)
|
||||
{
|
||||
foreach (string filename in base.GetFilenames(name))
|
||||
{
|
||||
string path = getPathForFile(filename.ToStandardisedPath());
|
||||
string? path = getPathForFile(filename.ToStandardisedPath());
|
||||
if (path != null)
|
||||
yield return path;
|
||||
}
|
||||
}
|
||||
|
||||
private string getPathForFile(string filename) =>
|
||||
fileToStoragePathMapping.TryGetValue(filename.ToLower(), out string path) ? path : null;
|
||||
private string? getPathForFile(string filename)
|
||||
{
|
||||
if (fileToStoragePathMapping.Value.TryGetValue(filename.ToLowerInvariant(), out string path))
|
||||
return path;
|
||||
|
||||
public override IEnumerable<string> GetAvailableResources() => fileToStoragePathMapping.Keys;
|
||||
return null;
|
||||
}
|
||||
|
||||
private void invalidateCache() => fileToStoragePathMapping = new Lazy<Dictionary<string, string>>(initialiseFileCache, LazyThreadSafetyMode.ExecutionAndPublication);
|
||||
|
||||
private Dictionary<string, string> initialiseFileCache() => liveSource.PerformRead(source =>
|
||||
{
|
||||
var dictionary = new Dictionary<string, string>();
|
||||
dictionary.Clear();
|
||||
foreach (var f in source.Files)
|
||||
dictionary[f.Filename.ToLowerInvariant()] = f.File.GetStoragePath();
|
||||
|
||||
return dictionary;
|
||||
});
|
||||
|
||||
public override IEnumerable<string> GetAvailableResources() => fileToStoragePathMapping.Value.Keys;
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,10 @@ namespace osu.Game.Skinning
|
||||
where TLookup : notnull
|
||||
where TValue : notnull;
|
||||
|
||||
public void InvalidateCaches() => realmBackedStorage?.Invalidate();
|
||||
|
||||
private readonly RealmBackedResourceStore<SkinInfo> realmBackedStorage;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new skin.
|
||||
/// </summary>
|
||||
@ -67,7 +71,9 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
SkinInfo = skin.ToLive(resources.RealmAccess);
|
||||
|
||||
storage ??= new RealmBackedResourceStore(skin, resources.Files, new[] { @"ogg" });
|
||||
storage ??= realmBackedStorage = new RealmBackedResourceStore<SkinInfo>(SkinInfo, resources.Files, resources.RealmAccess);
|
||||
|
||||
(storage as ResourceStore<byte[]>)?.AddExtension("ogg");
|
||||
|
||||
var samples = resources.AudioManager?.GetSampleStore(storage);
|
||||
if (samples != null)
|
||||
|
@ -16,6 +16,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.OpenGL.Textures;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Threading;
|
||||
@ -26,6 +27,7 @@ using osu.Game.IO;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Models;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using Realms;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
@ -59,6 +61,8 @@ namespace osu.Game.Skinning
|
||||
|
||||
private readonly IResourceStore<byte[]> userFiles;
|
||||
|
||||
private IDisposable currentSkinSubscription;
|
||||
|
||||
/// <summary>
|
||||
/// The default skin.
|
||||
/// </summary>
|
||||
@ -97,7 +101,16 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
});
|
||||
|
||||
CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = skin.NewValue.PerformRead(GetSkin);
|
||||
CurrentSkinInfo.ValueChanged += skin =>
|
||||
{
|
||||
CurrentSkin.Value = skin.NewValue.PerformRead(GetSkin);
|
||||
|
||||
scheduler.Add(() =>
|
||||
{
|
||||
currentSkinSubscription?.Dispose();
|
||||
currentSkinSubscription = realm.RegisterForNotifications(r => r.All<SkinInfo>().Where(s => s.ID == skin.NewValue.ID), realmSkinChanged);
|
||||
});
|
||||
};
|
||||
|
||||
CurrentSkin.Value = DefaultSkin;
|
||||
CurrentSkin.ValueChanged += skin =>
|
||||
@ -109,6 +122,12 @@ namespace osu.Game.Skinning
|
||||
};
|
||||
}
|
||||
|
||||
private void realmSkinChanged<T>(IRealmCollection<T> sender, ChangeSet changes, Exception error) where T : RealmObjectBase
|
||||
{
|
||||
Logger.Log("Detected a skin change");
|
||||
CurrentSkin.Value.InvalidateCaches();
|
||||
}
|
||||
|
||||
public void SelectRandomSkin()
|
||||
{
|
||||
realm.Run(r =>
|
||||
|
Loading…
Reference in New Issue
Block a user