diff --git a/osu.Game/Screens/Edit/Components/FormSampleSet.cs b/osu.Game/Screens/Edit/Components/FormSampleSet.cs index a96b255b5c..2d3404a513 100644 --- a/osu.Game/Screens/Edit/Components/FormSampleSet.cs +++ b/osu.Game/Screens/Edit/Components/FormSampleSet.cs @@ -289,18 +289,7 @@ namespace osu.Game.Screens.Edit.Components AddInternal(hoverSounds = (ActualFilename.Value == null ? new HoverClickSounds(HoverSampleSet.Button) : new HoverSounds(HoverSampleSet.Button))); - if (ActualFilename.Value != null) - { - // to cover all bases, invalidate the extensionless filename (which gameplay is most likely to use) - // as well as the filename with extension (which we are using here). - editorBeatmap?.BeatmapSkin?.Skin.Samples?.Invalidate(ExpectedFilename.Value); - editorBeatmap?.BeatmapSkin?.Skin.Samples?.Invalidate(ActualFilename.Value); - sample = editorBeatmap?.BeatmapSkin?.Skin.Samples?.Get(ActualFilename.Value); - } - else - { - sample = null; - } + sample = ActualFilename.Value != null ? editorBeatmap?.BeatmapSkin?.Skin.Samples?.Get(ActualFilename.Value) : null; }); protected override bool OnHover(HoverEvent e) diff --git a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs index f47a6b020e..a88330f772 100644 --- a/osu.Game/Screens/Edit/EditorBeatmapSkin.cs +++ b/osu.Game/Screens/Edit/EditorBeatmapSkin.cs @@ -61,7 +61,13 @@ namespace osu.Game.Screens.Edit ComboColours.BindCollectionChanged((_, _) => updateColours()); if (skin.BeatmapSetResources != null) - skin.BeatmapSetResources.CacheInvalidated += InvokeSkinChanged; + skin.BeatmapSetResources.CacheInvalidated += beatmapResourcesInvalidated; + } + + private void beatmapResourcesInvalidated() + { + Skin.RecycleSamples(); + InvokeSkinChanged(); } public void InvokeSkinChanged() => BeatmapSkinChanged?.Invoke(); @@ -149,7 +155,7 @@ namespace osu.Game.Screens.Edit public void Dispose() { if (Skin.BeatmapSetResources != null) - Skin.BeatmapSetResources.CacheInvalidated -= InvokeSkinChanged; + Skin.BeatmapSetResources.CacheInvalidated -= beatmapResourcesInvalidated; Skin.Dispose(); } diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 3affd5afa6..d50fc2e3d0 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -38,7 +38,7 @@ namespace osu.Game.Skinning /// /// A sample store which can be used to perform user file lookups for this skin. /// - protected internal ISampleStore? Samples { get; } + protected internal ISampleStore? Samples { get; private set; } public readonly Live SkinInfo; @@ -84,18 +84,7 @@ namespace osu.Game.Skinning store.AddStore(new RealmBackedResourceStore(SkinInfo, resources.Files, resources.RealmAccess)); - var samples = resources.AudioManager?.GetSampleStore(store); - - if (samples != null) - { - samples.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY; - - // osu-stable performs audio lookups in order of wav -> mp3 -> ogg. - // The GetSampleStore() call above internally adds wav and mp3, so ogg is added at the end to ensure expected ordering. - samples.AddExtension(@"ogg"); - } - - Samples = samples; + RecycleSamples(); Textures = new TextureStore(resources.Renderer, CreateTextureLoaderStore(resources, store)); } else @@ -153,6 +142,30 @@ namespace osu.Game.Skinning } } + /// + /// Recreates . + /// All users of samples from the skin are expected to manually re-retrieve their samples from this skin after this is called. + /// Exposed as public for the purpose of e.g. editing flows where the skin's set of available samples changes. + /// In such a scenario a full recycle of the store is required to avoid accidentally retrieving stale samples that don't exist in the skin anymore. + /// + public void RecycleSamples() + { + Samples?.Dispose(); + + var samples = resources?.AudioManager?.GetSampleStore(store); + + if (samples != null) + { + samples.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY; + + // osu-stable performs audio lookups in order of wav -> mp3 -> ogg. + // The GetSampleStore() call above internally adds wav and mp3, so ogg is added at the end to ensure expected ordering. + samples.AddExtension(@"ogg"); + } + + Samples = samples; + } + protected virtual IResourceStore CreateTextureLoaderStore(IStorageResourceProvider resources, IResourceStore storage) => new MaxDimensionLimitedTextureLoaderStore(resources.CreateTextureLoaderStore(storage));