From 08701b5eab700ea2944cf358cd60616d8fe9c96a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 7 Jun 2021 23:23:10 +0900 Subject: [PATCH 1/9] Ensure all lookups in `LegacyHealthDisplay` use the found provider Not actually needed to fix the remaining issue but does feel better --- osu.Game/Skinning/LegacyHealthDisplay.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/osu.Game/Skinning/LegacyHealthDisplay.cs b/osu.Game/Skinning/LegacyHealthDisplay.cs index 5a5c7f11ea..9d3bafd0b1 100644 --- a/osu.Game/Skinning/LegacyHealthDisplay.cs +++ b/osu.Game/Skinning/LegacyHealthDisplay.cs @@ -20,9 +20,6 @@ namespace osu.Game.Skinning { private const double epic_cutoff = 0.5; - [Resolved] - private ISkinSource skin { get; set; } - private LegacyHealthPiece fill; private LegacyHealthPiece marker; @@ -31,14 +28,14 @@ namespace osu.Game.Skinning private bool isNewStyle; [BackgroundDependencyLoader] - private void load() + private void load(ISkinSource source) { AutoSizeAxes = Axes.Both; - var backgroundSource = skin.FindProvider(s => getTexture(s, "bg") != null); + var skin = source.FindProvider(s => getTexture(s, "bg") != null); // the marker lookup to decide which display style must be performed on the source of the bg, which is the most common element. - isNewStyle = getTexture(backgroundSource, "marker") != null; + isNewStyle = getTexture(skin, "marker") != null; // background implementation is the same for both versions. AddInternal(new Sprite { Texture = getTexture(skin, "bg") }); @@ -98,7 +95,7 @@ namespace osu.Game.Skinning private readonly Texture dangerTexture; private readonly Texture superDangerTexture; - public LegacyOldStyleMarker(ISkinSource skin) + public LegacyOldStyleMarker(ISkin skin) { normalTexture = getTexture(skin, "ki"); dangerTexture = getTexture(skin, "kidanger"); @@ -129,9 +126,9 @@ namespace osu.Game.Skinning public class LegacyNewStyleMarker : LegacyMarker { - private readonly ISkinSource skin; + private readonly ISkin skin; - public LegacyNewStyleMarker(ISkinSource skin) + public LegacyNewStyleMarker(ISkin skin) { this.skin = skin; } @@ -153,7 +150,7 @@ namespace osu.Game.Skinning internal class LegacyOldStyleFill : LegacyHealthPiece { - public LegacyOldStyleFill(ISkinSource skin) + public LegacyOldStyleFill(ISkin skin) { // required for sizing correctly.. var firstFrame = getTexture(skin, "colour-0"); @@ -176,7 +173,7 @@ namespace osu.Game.Skinning internal class LegacyNewStyleFill : LegacyHealthPiece { - public LegacyNewStyleFill(ISkinSource skin) + public LegacyNewStyleFill(ISkin skin) { InternalChild = new Sprite { From c0305343bc9312333957362d124c906f2febd82b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 7 Jun 2021 23:23:44 +0900 Subject: [PATCH 2/9] Fix `FindProvider` incorrectly returning `LegacySkinTransformer` itself --- osu.Game/Skinning/LegacySkinTransformer.cs | 8 +++++++- osu.Game/Skinning/SkinProvidingContainer.cs | 12 ++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacySkinTransformer.cs b/osu.Game/Skinning/LegacySkinTransformer.cs index cace4acf6c..651fdddb1b 100644 --- a/osu.Game/Skinning/LegacySkinTransformer.cs +++ b/osu.Game/Skinning/LegacySkinTransformer.cs @@ -16,7 +16,7 @@ namespace osu.Game.Skinning /// /// Transformer used to handle support of legacy features for individual rulesets. /// - public abstract class LegacySkinTransformer : ISkin + public abstract class LegacySkinTransformer : ISkinSource { /// /// Source of the which is being transformed. @@ -50,5 +50,11 @@ namespace osu.Game.Skinning public abstract IBindable GetConfig(TLookup lookup); public ISkin FindProvider(Func lookupFunction) => Source.FindProvider(lookupFunction); + + public event Action SourceChanged + { + add { throw new NotSupportedException(); } + remove { } + } } } diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index 863b5f5a24..0e16cf43ee 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -46,8 +46,16 @@ namespace osu.Game.Skinning public ISkin FindProvider(Func lookupFunction) { - if (skin != null && lookupFunction(skin)) - return skin; + if (skin is ISkinSource source) + { + if (source.FindProvider(lookupFunction) is ISkin found) + return found; + } + else if (skin != null) + { + if (lookupFunction(skin)) + return skin; + } return fallbackSource?.FindProvider(lookupFunction); } From 6d56e02ddbe825599c3aaf1c09310c08858c8158 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 00:02:57 +0900 Subject: [PATCH 3/9] Add back incorrectly reverted animation handling logic This reverts commit b904fa6615ad210afc94e874a3861572e9bb0499. --- osu.Game/Skinning/LegacySkinExtensions.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index d8fb1fa664..051bcf4a8e 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -54,9 +54,16 @@ namespace osu.Game.Skinning IEnumerable getTextures() { + ISkin lookupSource = null; + for (int i = 0; true; i++) { - if ((texture = source.GetTexture($"{componentName}{animationSeparator}{i}", wrapModeS, wrapModeT)) == null) + string frameName = $"{componentName}{animationSeparator}{i}"; + + // ensure all textures are retrieved from the same skin source. + lookupSource ??= (source as ISkinSource)?.FindProvider(s => s.GetTexture(frameName, wrapModeS, wrapModeT) != null) ?? source; + + if ((texture = lookupSource?.GetTexture(frameName, wrapModeS, wrapModeT)) == null) break; yield return texture; From 273d66a0e0a76ca45e5188601bd03a34e9804011 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 00:42:34 +0900 Subject: [PATCH 4/9] Fix `TaikoMascot` texture animation lookups --- osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index 9c76aea54c..3706acbe23 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -85,8 +85,12 @@ namespace osu.Game.Rulesets.Taiko.UI } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load(ISkinSource source) { + ISkin skin = source.FindProvider(s => getAnimationFrame(s, state, 0) != null); + + if (skin == null) return; + for (int frameIndex = 0; true; frameIndex++) { var texture = getAnimationFrame(skin, state, frameIndex); @@ -112,8 +116,12 @@ namespace osu.Game.Rulesets.Taiko.UI } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load(ISkinSource source) { + ISkin skin = source.FindProvider(s => getAnimationFrame(s, TaikoMascotAnimationState.Clear, 0) != null); + + if (skin == null) return; + foreach (var frameIndex in clear_animation_sequence) { var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex); From e7e9197f03d2247fb2dfbb599401dff62a8160ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 00:42:50 +0900 Subject: [PATCH 5/9] Fix `FindProvider` not correctly checking legacy default in `SkinManager` --- osu.Game/Skinning/SkinManager.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 9aa2d90064..ea4e1232c3 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -48,6 +48,8 @@ namespace osu.Game.Skinning protected override string ImportFromStablePath => "Skins"; + private readonly Skin defaultLegacySkin; + public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, IResourceStore resources, AudioManager audio) : base(storage, contextFactory, new SkinStore(contextFactory, storage), host) { @@ -55,6 +57,8 @@ namespace osu.Game.Skinning this.host = host; this.resources = resources; + defaultLegacySkin = new DefaultLegacySkin(this); + CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue); CurrentSkin.ValueChanged += skin => { @@ -212,9 +216,16 @@ namespace osu.Game.Skinning public IBindable GetConfig(TLookup lookup) => lookupWithFallback(s => s.GetConfig(lookup)); - public ISkin FindProvider(Func lookupFunction) => lookupFunction(CurrentSkin.Value) ? CurrentSkin.Value : null; + public ISkin FindProvider(Func lookupFunction) + { + if (lookupFunction(CurrentSkin.Value)) + return CurrentSkin.Value; - private Skin defaultLegacySkin; + if (CurrentSkin.Value is LegacySkin && lookupFunction(defaultLegacySkin)) + return defaultLegacySkin; + + return null; + } private T lookupWithFallback(Func func) where T : class @@ -224,8 +235,6 @@ namespace osu.Game.Skinning if (selectedSkin != null) return selectedSkin; - defaultLegacySkin ??= new DefaultLegacySkin(this); - if (CurrentSkin.Value is LegacySkin) return func(defaultLegacySkin); From 2c1f22d7ae914e84a6263d4ae7231d2f0f2a3bb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 01:17:20 +0900 Subject: [PATCH 6/9] Refactor animation lookup to properly handle skins providing non-animated resources --- osu.Game/Skinning/LegacySkinExtensions.cs | 27 ++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index 051bcf4a8e..ec25268be4 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -27,6 +27,18 @@ namespace osu.Game.Skinning { Texture texture; + // find the first source which provides either the animated or non-animated version. + ISkin skin = (source as ISkinSource)?.FindProvider(s => + { + if (animatable && s.GetTexture(getFrameName(0)) != null) + return true; + + return s.GetTexture(componentName, wrapModeS, wrapModeT) != null; + }) ?? source; + + if (skin == null) + return null; + if (animatable) { var textures = getTextures().ToArray(); @@ -35,7 +47,7 @@ namespace osu.Game.Skinning { var animation = new SkinnableTextureAnimation(startAtCurrentTime) { - DefaultFrameLength = frameLength ?? getFrameLength(source, applyConfigFrameRate, textures), + DefaultFrameLength = frameLength ?? getFrameLength(skin, applyConfigFrameRate, textures), Loop = looping, }; @@ -47,28 +59,23 @@ namespace osu.Game.Skinning } // if an animation was not allowed or not found, fall back to a sprite retrieval. - if ((texture = source.GetTexture(componentName, wrapModeS, wrapModeT)) != null) + if ((texture = skin.GetTexture(componentName, wrapModeS, wrapModeT)) != null) return new Sprite { Texture = texture }; return null; IEnumerable getTextures() { - ISkin lookupSource = null; - for (int i = 0; true; i++) { - string frameName = $"{componentName}{animationSeparator}{i}"; - - // ensure all textures are retrieved from the same skin source. - lookupSource ??= (source as ISkinSource)?.FindProvider(s => s.GetTexture(frameName, wrapModeS, wrapModeT) != null) ?? source; - - if ((texture = lookupSource?.GetTexture(frameName, wrapModeS, wrapModeT)) == null) + if ((texture = skin.GetTexture(getFrameName(i), wrapModeS, wrapModeT)) == null) break; yield return texture; } } + + string getFrameName(int frameIndex) => $"{componentName}{animationSeparator}{frameIndex}"; } public static bool HasFont(this ISkin source, LegacyFont font) From 06840d78cc4df29d7b5f8a58c0b7cdb44f83a9ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 12:04:29 +0900 Subject: [PATCH 7/9] Remove now unused method --- osu.Game/Skinning/DefaultLegacySkin.cs | 2 -- osu.Game/Skinning/LegacyBeatmapSkin.cs | 8 -------- osu.Game/Skinning/LegacySkin.cs | 3 --- 3 files changed, 13 deletions(-) diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 1d17b5ce20..30192182f3 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -31,8 +31,6 @@ namespace osu.Game.Skinning Configuration.LegacyVersion = 2.7m; } - protected override DefaultLegacySkin CreateFallbackSkin(IResourceStore storage, IStorageResourceProvider resources) => null; - public static SkinInfo Info { get; } = new SkinInfo { ID = SkinInfo.CLASSIC_SKIN, // this is temporary until database storage is decided upon. diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 2374cb976b..caf37e5bc9 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -70,14 +70,6 @@ namespace osu.Game.Skinning return base.GetSample(sampleInfo); } - protected override DefaultLegacySkin CreateFallbackSkin(IResourceStore storage, IStorageResourceProvider resources) - { - // for simplicity, beatmap skins don't do lookups on the default skin. - // this will mean that fallback always occurs to the user (then default) skin. - // this may not offer perfect behaviour, but helps keep things simple. - return null; - } - private static SkinInfo createSkinInfo(BeatmapInfo beatmap) => new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata?.AuthorString }; } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 337acee9e8..e255fbae81 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -109,9 +109,6 @@ namespace osu.Game.Skinning true) != null); } - [CanBeNull] - protected virtual DefaultLegacySkin CreateFallbackSkin(IResourceStore storage, IStorageResourceProvider resources) => new DefaultLegacySkin(resources); - public override IBindable GetConfig(TLookup lookup) { switch (lookup) From 88b87b98a850b89b4e6f52e47791ba5c60da9f00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 12:10:14 +0900 Subject: [PATCH 8/9] Fix slider ball layer sources --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs | 7 +++++-- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs index 1a8c5ada1b..e4e1483665 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderBall.cs @@ -14,18 +14,21 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { private readonly Drawable animationContent; + private readonly ISkin skin; + private Sprite layerNd; private Sprite layerSpec; - public LegacySliderBall(Drawable animationContent) + public LegacySliderBall(Drawable animationContent, ISkin skin) { this.animationContent = animationContent; + this.skin = skin; AutoSizeAxes = Axes.Both; } [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load() { var ballColour = skin.GetConfig(OsuSkinColour.SliderBall)?.Value ?? Color4.White; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index e3f32fb76f..3267b48ebf 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -49,13 +49,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return followCircle; case OsuSkinComponents.SliderBall: - var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: ""); + // specular and nd layers must come from the same source as the ball texure. + var ballProvider = Source.FindProvider(s => s.GetTexture("sliderb") != null || s.GetTexture("sliderb0") != null); + + var sliderBallContent = ballProvider.GetAnimation("sliderb", true, true, animationSeparator: ""); // todo: slider ball has a custom frame delay based on velocity // Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME); if (sliderBallContent != null) - return new LegacySliderBall(sliderBallContent); + return new LegacySliderBall(sliderBallContent, ballProvider); return null; From 27e3de3ea33a99abaf453a3eac542221be6c1e61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 8 Jun 2021 12:12:14 +0900 Subject: [PATCH 9/9] Add TODO about beatmap skin fallback support --- osu.Game/Skinning/SkinManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index ea4e1232c3..25cd909035 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -235,6 +235,9 @@ namespace osu.Game.Skinning if (selectedSkin != null) return selectedSkin; + // TODO: we also want to return a DefaultLegacySkin here if the current *beatmap* is providing any skinned elements. + // When attempting to address this, we may want to move the full DefaultLegacySkin fallback logic to within Player itself (to better allow + // for beatmap skin visibility). if (CurrentSkin.Value is LegacySkin) return func(defaultLegacySkin);