Merge pull request #13441 from frenzibyte/ruleset-resources-skin

Provide access to ruleset resources during `ISkinSource` lookups
This commit is contained in:
Dean Herbert 2021-06-30 15:26:37 +09:00 committed by GitHub
commit 094cd31361
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 99 additions and 27 deletions

View File

@ -2,14 +2,15 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.OpenGL.Textures;
using osu.Framework.Graphics.Textures;
using osu.Framework.Testing;
using osu.Game.Audio;
using osu.Game.Rulesets;
using osu.Game.Skinning;
@ -18,14 +19,21 @@
namespace osu.Game.Tests.Rulesets
{
[HeadlessTest]
public class TestSceneRulesetSkinProvidingContainer : OsuTestScene
{
private SkinRequester requester;
protected override Ruleset CreateRuleset() => new TestSceneRulesetDependencies.TestRuleset();
[Cached(typeof(ISkinSource))]
private readonly ISkinSource testSource = new TestSkinProvider();
[Test]
public void TestRulesetResources()
{
setupProviderStep();
AddAssert("ruleset texture retrieved via skin", () => requester.GetTexture("test-image") != null);
AddAssert("ruleset sample retrieved via skin", () => requester.GetSample(new SampleInfo("test-sample")) != null);
}
[Test]
public void TestEarlyAddedSkinRequester()
@ -38,7 +46,7 @@ public void TestEarlyAddedSkinRequester()
rulesetSkinProvider.Add(requester = new SkinRequester());
requester.OnLoadAsync += () => textureOnLoad = requester.GetTexture(TestSkinProvider.TEXTURE_NAME);
requester.OnLoadAsync += () => textureOnLoad = requester.GetTexture("test-image");
Child = rulesetSkinProvider;
});
@ -46,6 +54,15 @@ public void TestEarlyAddedSkinRequester()
AddAssert("requester got correct initial texture", () => textureOnLoad != null);
}
private void setupProviderStep()
{
AddStep("setup provider", () =>
{
Child = new RulesetSkinProvidingContainer(Ruleset.Value.CreateInstance(), Beatmap.Value.Beatmap, Beatmap.Value.Skin)
.WithChild(requester = new SkinRequester());
});
}
private class SkinRequester : Drawable, ISkin
{
private ISkinSource skin;
@ -68,28 +85,5 @@ private void load(ISkinSource skin)
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => skin.GetConfig<TLookup, TValue>(lookup);
}
private class TestSkinProvider : ISkinSource
{
public const string TEXTURE_NAME = "some-texture";
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotImplementedException();
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => componentName == TEXTURE_NAME ? Texture.WhitePixel : null;
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
public event Action SourceChanged
{
add { }
remove { }
}
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => lookupFunction(this) ? this : null;
public IEnumerable<ISkin> AllSources => new[] { this };
}
}
}

View File

@ -0,0 +1,57 @@
// 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 osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.OpenGL.Textures;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Game.Audio;
namespace osu.Game.Skinning
{
/// <summary>
/// An <see cref="ISkin"/> that uses an underlying <see cref="IResourceStore{T}"/> with namespaces for resources retrieval.
/// </summary>
public class ResourceStoreBackedSkin : ISkin, IDisposable
{
private readonly TextureStore textures;
private readonly ISampleStore samples;
public ResourceStoreBackedSkin(IResourceStore<byte[]> resources, GameHost host, AudioManager audio)
{
textures = new TextureStore(host.CreateTextureLoaderStore(new NamespacedResourceStore<byte[]>(resources, @"Textures")));
samples = audio.GetSampleStore(new NamespacedResourceStore<byte[]>(resources, @"Samples"));
}
public Drawable? GetDrawableComponent(ISkinComponent component) => null;
public Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => textures.Get(componentName, wrapModeS, wrapModeT);
public ISample? GetSample(ISampleInfo sampleInfo)
{
foreach (var lookup in sampleInfo.LookupNames)
{
ISample? sample = samples.Get(lookup);
if (sample != null)
return sample;
}
return null;
}
public IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup) => null;
public void Dispose()
{
textures.Dispose();
samples.Dispose();
}
}
}

View File

@ -1,10 +1,14 @@
// 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.
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.UI;
@ -44,11 +48,16 @@ public RulesetSkinProvidingContainer(Ruleset ruleset, IBeatmap beatmap, [CanBeNu
private ISkinSource parentSource;
private ResourceStoreBackedSkin rulesetResourcesSkin;
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
parentSource = parent.Get<ISkinSource>();
parentSource.SourceChanged += OnSourceChanged;
if (Ruleset.CreateResourceStore() is IResourceStore<byte[]> resources)
rulesetResourcesSkin = new ResourceStoreBackedSkin(resources, parent.Get<GameHost>(), parent.Get<AudioManager>());
// ensure sources are populated and ready for use before childrens' asynchronous load flow.
UpdateSkinSources();
@ -78,6 +87,16 @@ protected virtual void UpdateSkinSources()
break;
}
}
int lastDefaultSkinIndex = SkinSources.IndexOf(SkinSources.OfType<DefaultSkin>().LastOrDefault());
// Ruleset resources should be given the ability to override game-wide defaults
// This is achieved by placing them before the last instance of DefaultSkin.
// Note that DefaultSkin may not be present in some test scenes.
if (lastDefaultSkinIndex >= 0)
SkinSources.Insert(lastDefaultSkinIndex, rulesetResourcesSkin);
else
SkinSources.Add(rulesetResourcesSkin);
}
protected ISkin GetLegacyRulesetTransformedSkin(ISkin legacySkin)
@ -98,6 +117,8 @@ protected override void Dispose(bool isDisposing)
if (parentSource != null)
parentSource.SourceChanged -= OnSourceChanged;
rulesetResourcesSkin?.Dispose();
}
}
}