1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-13 08:32:57 +08:00

Merge branch 'legacy-skin-default-fallback' into fix-skin-sample-lookup

This commit is contained in:
Dean Herbert 2021-05-31 21:39:04 +09:00
commit 00b3eea840
58 changed files with 380 additions and 163 deletions

View File

@ -1,8 +1,16 @@
// 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 NUnit.Framework;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Screens.Play.HUD;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Catch.Tests
{
@ -10,5 +18,22 @@ namespace osu.Game.Rulesets.Catch.Tests
public class TestSceneCatchPlayerLegacySkin : LegacySkinPlayerTestScene
{
protected override Ruleset CreatePlayerRuleset() => new CatchRuleset();
[Test]
public void TestLegacyHUDComboCounterHidden([Values] bool withModifiedSkin)
{
if (withModifiedSkin)
{
AddStep("change component scale", () => Player.ChildrenOfType<LegacyScoreCounter>().First().Scale = new Vector2(2f));
AddStep("update target", () => Player.ChildrenOfType<SkinnableTargetContainer>().ForEach(LegacySkin.UpdateDrawableTarget));
AddStep("exit player", () => Player.Exit());
CreateTest(null);
}
AddAssert("legacy HUD combo counter hidden", () =>
{
return Player.ChildrenOfType<LegacyComboCounter>().All(c => c.ChildrenOfType<Container>().Single().Alpha == 0f);
});
}
}
}

View File

@ -32,28 +32,28 @@ namespace osu.Game.Rulesets.Catch.Tests
[TestCase(true, false)]
[TestCase(false, true)]
[TestCase(false, false)]
public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin)
public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin);
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true));
ConfigureTest(useBeatmapSkin, true, userHasCustomColours);
AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours));
}
[TestCase(true)]
[TestCase(false)]
public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin)
public void TestBeatmapComboColoursOverride(bool useBeatmapSkin)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
base.TestBeatmapComboColoursOverride(useBeatmapSkin);
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true));
ConfigureTest(useBeatmapSkin, false, true);
AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
}
[TestCase(true)]
[TestCase(false)]
public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin)
public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin);
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true));
ConfigureTest(useBeatmapSkin, false, false);
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
}
@ -61,10 +61,10 @@ namespace osu.Game.Rulesets.Catch.Tests
[TestCase(false, true)]
[TestCase(true, false)]
[TestCase(false, false)]
public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour)
public void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, false);
base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour);
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, false));
ConfigureTest(useBeatmapSkin, useBeatmapColour, false);
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
}
@ -72,10 +72,10 @@ namespace osu.Game.Rulesets.Catch.Tests
[TestCase(false, true)]
[TestCase(true, false)]
[TestCase(false, false)]
public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour)
public void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, false);
base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour);
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, false));
ConfigureTest(useBeatmapSkin, useBeatmapColour, true);
AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
}
@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Catch.Tests
[TestCase(false)]
public void TestBeatmapHyperDashColours(bool useBeatmapSkin)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true));
ConfigureTest(useBeatmapSkin, true, true);
AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestBeatmapSkin.HYPER_DASH_COLOUR);
AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestBeatmapSkin.HYPER_DASH_AFTER_IMAGE_COLOUR);
@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Catch.Tests
[TestCase(false)]
public void TestBeatmapHyperDashColoursOverride(bool useBeatmapSkin)
{
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true));
ConfigureTest(useBeatmapSkin, false, true);
AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestSkin.HYPER_DASH_COLOUR);
AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestSkin.HYPER_DASH_AFTER_IMAGE_COLOUR);

View File

@ -1,8 +1,10 @@
// 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 osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Screens.Play.HUD;
using osu.Game.Skinning;
using osuTK.Graphics;
@ -22,16 +24,22 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
public override Drawable GetDrawableComponent(ISkinComponent component)
{
if (component is HUDSkinComponent hudComponent)
if (component is SkinnableTargetComponent targetComponent)
{
switch (hudComponent.Component)
switch (targetComponent.Target)
{
case HUDSkinComponents.ComboCounter:
// catch may provide its own combo counter; hide the default.
if (providesComboCounter)
return Drawable.Empty();
case SkinnableTarget.MainHUDComponents:
var components = Source.GetDrawableComponent(component) as SkinnableTargetComponentsContainer;
break;
if (providesComboCounter && components != null)
{
// catch may provide its own combo counter; hide the default.
// todo: this should be done in an elegant way per ruleset, defining which HUD skin components should be displayed.
foreach (var legacyComboCounter in components.OfType<LegacyComboCounter>())
legacyComboCounter.HiddenByRulesetImplementation = false;
}
return components;
}
}

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.Tests
public class TestSceneManiaHitObjectSamples : HitObjectSampleTest
{
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
protected override IResourceStore<byte[]> Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneManiaHitObjectSamples)));
protected override IResourceStore<byte[]> RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneManiaHitObjectSamples)));
/// <summary>
/// Tests that when a normal sample bank is used, the normal hitsound will be looked up.

View File

@ -69,10 +69,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
private void sourceChanged()
{
isLegacySkin = new Lazy<bool>(() => Source.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version) != null);
hasKeyTexture = new Lazy<bool>(() => Source.GetAnimation(
isLegacySkin = new Lazy<bool>(() => FindProvider(s => s.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version) != null) != null);
hasKeyTexture = new Lazy<bool>(() => FindProvider(s => s.GetAnimation(
this.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value
?? "mania-key1", true, true) != null);
?? "mania-key1", true, true) != null) != null);
}
public override Drawable GetDrawableComponent(ISkinComponent component)

View File

@ -102,6 +102,8 @@ namespace osu.Game.Rulesets.Osu.Tests
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
public event Action SourceChanged
{
add { }

View File

@ -113,6 +113,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public Drawable GetDrawableComponent(ISkinComponent component) => null;
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
public ISample GetSample(ISampleInfo sampleInfo) => null;
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
{

View File

@ -30,28 +30,28 @@ namespace osu.Game.Rulesets.Osu.Tests
[TestCase(true, false)]
[TestCase(false, true)]
[TestCase(false, false)]
public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin)
public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin)
{
TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true);
base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin);
PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true));
ConfigureTest(useBeatmapSkin, true, userHasCustomColours);
AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours));
}
[TestCase(true)]
[TestCase(false)]
public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin)
public void TestBeatmapComboColoursOverride(bool useBeatmapSkin)
{
TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true);
base.TestBeatmapComboColoursOverride(useBeatmapSkin);
PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true));
ConfigureTest(useBeatmapSkin, false, true);
AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
}
[TestCase(true)]
[TestCase(false)]
public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin)
public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin)
{
TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true);
base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin);
PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true));
ConfigureTest(useBeatmapSkin, false, false);
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
}
@ -59,10 +59,10 @@ namespace osu.Game.Rulesets.Osu.Tests
[TestCase(false, true)]
[TestCase(true, false)]
[TestCase(false, false)]
public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour)
public void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour)
{
TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, false);
base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour);
PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, false));
ConfigureTest(useBeatmapSkin, useBeatmapColour, false);
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
}
@ -70,10 +70,10 @@ namespace osu.Game.Rulesets.Osu.Tests
[TestCase(false, true)]
[TestCase(true, false)]
[TestCase(false, false)]
public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour)
public void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour)
{
TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, false);
base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour);
PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, false));
ConfigureTest(useBeatmapSkin, useBeatmapColour, true);
AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
}

View File

@ -166,6 +166,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => default;
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
public event Action SourceChanged;

View File

@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
private void sourceChanged()
{
hasHitCircle = new Lazy<bool>(() => Source.GetTexture("hitcircle") != null);
hasHitCircle = new Lazy<bool>(() => FindProvider(s => s.GetTexture("hitcircle") != null) != null);
}
public override Drawable GetDrawableComponent(ISkinComponent component)

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
protected override Ruleset CreatePlayerRuleset() => new TaikoRuleset();
protected override IResourceStore<byte[]> Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples)));
protected override IResourceStore<byte[]> RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples)));
[TestCase("taiko-normal-hitnormal")]
[TestCase("normal-hitnormal")]

View File

@ -123,6 +123,8 @@ namespace osu.Game.Tests.Gameplay
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
{
switch (lookup)

View File

@ -14,7 +14,7 @@ namespace osu.Game.Tests.Gameplay
public class TestSceneHitObjectSamples : HitObjectSampleTest
{
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
protected override IResourceStore<byte[]> Resources => TestResources.GetStore();
protected override IResourceStore<byte[]> RulesetResources => TestResources.GetStore();
/// <summary>
/// Tests that a hitobject which provides no custom sample set retrieves samples from the user skin.

View File

@ -219,6 +219,7 @@ namespace osu.Game.Tests.Gameplay
public AudioManager AudioManager => Audio;
public IResourceStore<byte[]> Files => null;
public new IResourceStore<byte[]> Resources => base.Resources;
public IResourceStore<TextureUpload> CreateTextureLoaderStore(IResourceStore<byte[]> underlyingStore) => null;
#endregion

View File

@ -61,6 +61,7 @@ namespace osu.Game.Tests.NonVisual.Skinning
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotSupportedException();
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException();
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotSupportedException();
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
}
private class TestAnimationTimeReference : IAnimationTimeReference

View File

@ -12,6 +12,7 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
@ -44,7 +45,7 @@ namespace osu.Game.Tests.Online
private void load(AudioManager audio, GameHost host)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.CacheAs<BeatmapManager>(beatmaps = new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, API, audio, host, Beatmap.Default));
Dependencies.CacheAs<BeatmapManager>(beatmaps = new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, API, audio, Resources, host, Beatmap.Default));
}
[SetUp]
@ -160,8 +161,8 @@ namespace osu.Game.Tests.Online
protected override ArchiveDownloadRequest<BeatmapSetInfo> CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize)
=> new TestDownloadRequest(set);
public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false)
: base(storage, contextFactory, rulesets, api, audioManager, host, defaultBeatmap, performOnlineLookups)
public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore<byte[]> resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false)
: base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap, performOnlineLookups)
{
}

View File

@ -1,6 +1,7 @@
// 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;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -222,6 +223,8 @@ namespace osu.Game.Tests.Skins
public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo);
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => skin.GetConfig<TLookup, TValue>(lookup);
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => skin.FindProvider(lookupFunction);
}
}
}

View File

@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Background
private void load(GameHost host, AudioManager audio)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
Dependencies.Cache(new OsuConfigManager(LocalStorage));
manager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();

View File

@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Collections
private void load(GameHost host)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, host, Beatmap.Default));
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, Resources, host, Beatmap.Default));
beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();

View File

@ -24,13 +24,13 @@ namespace osu.Game.Tests.Visual.Editing
protected override bool EditorComponentsReady => Editor.ChildrenOfType<SetupScreen>().SingleOrDefault()?.IsLoaded == true;
protected override bool IsolateSavingFromDatabase => false;
[Resolved]
private BeatmapManager beatmapManager { get; set; }
public override void SetUpSteps()
{
AddStep("set dummy", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null));
base.SetUpSteps();
// if we save a beatmap with a hash collision, things fall over.
@ -38,6 +38,12 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("make new beatmap unique", () => EditorBeatmap.Metadata.Title = Guid.NewGuid().ToString());
}
protected override void LoadEditor()
{
Beatmap.Value = new DummyWorkingBeatmap(Audio, null);
base.LoadEditor();
}
[Test]
public void TestCreateNewBeatmap()
{

View File

@ -300,6 +300,8 @@ namespace osu.Game.Tests.Visual.Gameplay
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => throw new NotImplementedException();
}
private class SecondarySource : ISkin
@ -311,6 +313,8 @@ namespace osu.Game.Tests.Visual.Gameplay
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => throw new NotImplementedException();
}
[Cached(typeof(ISkinSource))]
@ -324,6 +328,8 @@ namespace osu.Game.Tests.Visual.Gameplay
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => throw new NotImplementedException();
public event Action SourceChanged
{
add { }

View File

@ -146,6 +146,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => source?.GetTexture(componentName, wrapModeS, wrapModeT);
public ISample GetSample(ISampleInfo sampleInfo) => source?.GetSample(sampleInfo);
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => source?.GetConfig<TLookup, TValue>(lookup);
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => source?.FindProvider(lookupFunction);
public void TriggerSourceChanged()
{

View File

@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void load(GameHost host, AudioManager audio)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait();
}

View File

@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void load(GameHost host, AudioManager audio)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
}
[SetUp]

View File

@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void load(GameHost host, AudioManager audio)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
beatmaps = new List<BeatmapInfo>();

View File

@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void load(GameHost host, AudioManager audio)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First();

View File

@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void load(GameHost host, AudioManager audio)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
Add(beatmapTracker = new OnlinePlayBeatmapAvailabilityTracker

View File

@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void load(GameHost host, AudioManager audio)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
var beatmapTracker = new OnlinePlayBeatmapAvailabilityTracker { SelectedItem = { BindTarget = selectedItem } };
base.Content.Add(beatmapTracker);

View File

@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void load(GameHost host, AudioManager audio)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
var beatmaps = new List<BeatmapInfo>();

View File

@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Playlists
private void load(GameHost host, AudioManager audio)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait();

View File

@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private void load(GameHost host)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, host, Beatmap.Default));
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, Resources, host, Beatmap.Default));
beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();

View File

@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private void load(GameHost host, AudioManager audio)
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, defaultBeatmap = Beatmap.Default));
Dependencies.Cache(music = new MusicController());

View File

@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.UserInterface
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory));
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get<AudioManager>(), dependencies.Get<GameHost>(), Beatmap.Default));
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default));
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory));
beatmap = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Beatmaps[0];

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
@ -73,6 +72,7 @@ namespace osu.Game.Beatmaps
private readonly RulesetStore rulesets;
private readonly BeatmapStore beatmaps;
private readonly AudioManager audioManager;
private readonly IResourceStore<byte[]> resources;
private readonly LargeTextureStore largeTextureStore;
private readonly ITrackStore trackStore;
@ -82,12 +82,13 @@ namespace osu.Game.Beatmaps
[CanBeNull]
private readonly BeatmapOnlineLookupQueue onlineLookupQueue;
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, GameHost host = null,
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore<byte[]> resources, GameHost host = null,
WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false)
: base(storage, contextFactory, api, new BeatmapStore(contextFactory), host)
{
this.rulesets = rulesets;
this.audioManager = audioManager;
this.resources = resources;
this.host = host;
DefaultBeatmap = defaultBeatmap;
@ -241,12 +242,10 @@ namespace osu.Game.Beatmaps
/// <param name="info">The <see cref="BeatmapInfo"/> to save the content against. The file referenced by <see cref="BeatmapInfo.Path"/> will be replaced.</param>
/// <param name="beatmapContent">The <see cref="IBeatmap"/> content to write.</param>
/// <param name="beatmapSkin">The beatmap <see cref="ISkin"/> content to write, null if to be omitted.</param>
public void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null)
public virtual void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null)
{
var setInfo = info.BeatmapSet;
Debug.Assert(setInfo.Files != null);
using (var stream = new MemoryStream())
{
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
@ -283,25 +282,17 @@ namespace osu.Game.Beatmaps
/// Retrieve a <see cref="WorkingBeatmap"/> instance for the provided <see cref="BeatmapInfo"/>
/// </summary>
/// <param name="beatmapInfo">The beatmap to lookup.</param>
/// <param name="previous">The currently loaded <see cref="WorkingBeatmap"/>. Allows for optimisation where elements are shared with the new beatmap. May be returned if beatmapInfo requested matches</param>
/// <returns>A <see cref="WorkingBeatmap"/> instance correlating to the provided <see cref="BeatmapInfo"/>.</returns>
public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null)
public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo)
{
if (beatmapInfo?.ID > 0 && previous != null && previous.BeatmapInfo?.ID == beatmapInfo.ID)
return previous;
if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo)
return DefaultBeatmap;
// force a re-query if files are not in a state which looks like the model has
// full database information present.
if (beatmapInfo.BeatmapSet.Files == null || beatmapInfo.BeatmapSet.Files.Count == 0)
// if there are no files, presume the full beatmap info has not yet been fetched from the database.
if (beatmapInfo?.BeatmapSet?.Files.Count == 0)
{
var info = beatmapInfo;
beatmapInfo = QueryBeatmap(b => b.ID == info.ID);
int lookupId = beatmapInfo.ID;
beatmapInfo = QueryBeatmap(b => b.ID == lookupId);
}
if (beatmapInfo == null)
if (beatmapInfo?.BeatmapSet == null)
return DefaultBeatmap;
lock (workingCache)
@ -511,6 +502,7 @@ namespace osu.Game.Beatmaps
ITrackStore IBeatmapResourceProvider.Tracks => trackStore;
AudioManager IStorageResourceProvider.AudioManager => audioManager;
IResourceStore<byte[]> IStorageResourceProvider.Files => Files.Store;
IResourceStore<byte[]> IStorageResourceProvider.Resources => resources;
IResourceStore<TextureUpload> IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore<byte[]> underlyingStore) => host?.CreateTextureLoaderStore(underlyingStore);
#endregion

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Testing;
using osu.Game.Database;
@ -31,6 +32,9 @@ namespace osu.Game.Beatmaps
public List<BeatmapInfo> Beatmaps { get; set; }
[NotNull]
public List<BeatmapSetFileInfo> Files { get; set; } = new List<BeatmapSetFileInfo>();
[NotMapped]
public BeatmapSetOnlineInfo OnlineInfo { get; set; }
@ -57,16 +61,14 @@ namespace osu.Game.Beatmaps
public string Hash { get; set; }
public string StoryboardFile => Files?.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename;
public string StoryboardFile => Files.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename;
/// <summary>
/// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null.
/// The path returned is relative to the user file storage.
/// </summary>
/// <param name="filename">The name of the file to get the storage path of.</param>
public string GetPathForFile(string filename) => Files?.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath;
public List<BeatmapSetFileInfo> Files { get; set; }
public string GetPathForFile(string filename) => Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath;
public override string ToString() => Metadata?.ToString() ?? base.ToString();

View File

@ -19,6 +19,11 @@ namespace osu.Game.IO
/// </summary>
IResourceStore<byte[]> Files { get; }
/// <summary>
/// Access game-wide resources.
/// </summary>
IResourceStore<byte[]> Resources { get; }
/// <summary>
/// Create a texture loader store based on an underlying data store.
/// </summary>

View File

@ -209,7 +209,7 @@ namespace osu.Game
runMigrations();
dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Audio, new NamespacedResourceStore<byte[]>(Resources, "Skins/Legacy")));
dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Resources, Audio));
dependencies.CacheAs<ISkinSource>(SkinManager);
// needs to be done here rather than inside SkinManager to ensure thread safety of CurrentSkinInfo.
@ -242,7 +242,7 @@ namespace osu.Game
// ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup()
dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => difficultyCache, LocalConfig));
dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Host, defaultBeatmap, true));
dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, true));
// this should likely be moved to ArchiveModelManager when another case appers where it is necessary
// to have inter-dependent model managers. this could be obtained with an IHasForeign<T> interface to

View File

@ -252,7 +252,7 @@ namespace osu.Game.Overlays
if (playable != null)
{
changeBeatmap(beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value));
changeBeatmap(beatmaps.GetWorkingBeatmap(playable.Beatmaps.First()));
restartTrack();
return PreviousTrackResult.Previous;
}
@ -283,7 +283,7 @@ namespace osu.Game.Overlays
if (playable != null)
{
changeBeatmap(beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value));
changeBeatmap(beatmaps.GetWorkingBeatmap(playable.Beatmaps.First()));
restartTrack();
return true;
}

View File

@ -17,7 +17,7 @@ namespace osu.Game.Screens.Play.HUD
/// </summary>
public class LegacyComboCounter : CompositeDrawable, ISkinnableDrawable
{
public Bindable<int> Current { get; } = new BindableInt { MinValue = 0, };
public Bindable<int> Current { get; } = new BindableInt { MinValue = 0 };
private uint scheduledPopOutCurrentId;
@ -32,9 +32,9 @@ namespace osu.Game.Screens.Play.HUD
/// </summary>
private const double rolling_duration = 20;
private Drawable popOutCount;
private readonly Drawable popOutCount;
private Drawable displayedCountSpriteText;
private readonly Drawable displayedCountSpriteText;
private int previousValue;
@ -45,6 +45,20 @@ namespace osu.Game.Screens.Play.HUD
[Resolved]
private ISkinSource skin { get; set; }
private readonly Container counterContainer;
/// <summary>
/// Hides the combo counter internally without affecting its <see cref="SkinnableInfo"/>.
/// </summary>
/// <remarks>
/// This is used for rulesets that provide their own combo counter and don't want this HUD one to be visible,
/// without potentially affecting the user's selected skin.
/// </remarks>
public bool HiddenByRulesetImplementation
{
set => counterContainer.Alpha = value ? 1 : 0;
}
public LegacyComboCounter()
{
AutoSizeAxes = Axes.Both;
@ -55,6 +69,25 @@ namespace osu.Game.Screens.Play.HUD
Margin = new MarginPadding(10);
Scale = new Vector2(1.2f);
InternalChild = counterContainer = new Container
{
AutoSizeAxes = Axes.Both,
AlwaysPresent = true,
Children = new[]
{
popOutCount = new LegacySpriteText(LegacyFont.Combo)
{
Alpha = 0,
Margin = new MarginPadding(0.05f),
Blending = BlendingParameters.Additive,
},
displayedCountSpriteText = new LegacySpriteText(LegacyFont.Combo)
{
Alpha = 0,
},
}
};
}
/// <summary>
@ -82,20 +115,6 @@ namespace osu.Game.Screens.Play.HUD
[BackgroundDependencyLoader]
private void load(ScoreProcessor scoreProcessor)
{
InternalChildren = new[]
{
popOutCount = new LegacySpriteText(LegacyFont.Combo)
{
Alpha = 0,
Margin = new MarginPadding(0.05f),
Blending = BlendingParameters.Additive,
},
displayedCountSpriteText = new LegacySpriteText(LegacyFont.Combo)
{
Alpha = 0,
},
};
Current.BindTo(scoreProcessor.Combo);
}
@ -105,10 +124,12 @@ namespace osu.Game.Screens.Play.HUD
((IHasText)displayedCountSpriteText).Text = formatCount(Current.Value);
counterContainer.Anchor = Anchor;
counterContainer.Origin = Origin;
displayedCountSpriteText.Anchor = Anchor;
displayedCountSpriteText.Origin = Origin;
popOutCount.Origin = Origin;
popOutCount.Anchor = Anchor;
popOutCount.Origin = Origin;
Current.BindValueChanged(combo => updateCount(combo.NewValue == 0), true);
}

View File

@ -505,12 +505,13 @@ namespace osu.Game.Screens.Select
{
Logger.Log($"beatmap changed from \"{Beatmap.Value.BeatmapInfo}\" to \"{beatmap}\"");
WorkingBeatmap previous = Beatmap.Value;
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, previous);
int? lastSetID = Beatmap.Value?.BeatmapInfo.BeatmapSetInfoID;
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap);
if (beatmap != null)
{
if (beatmap.BeatmapSetInfoID == previous?.BeatmapInfo.BeatmapSetInfoID)
if (beatmap.BeatmapSetInfoID == lastSetID)
sampleChangeDifficulty.Play();
else
sampleChangeBeatmap.Play();

View File

@ -11,14 +11,14 @@ namespace osu.Game.Skinning
{
public class DefaultLegacySkin : LegacySkin
{
public DefaultLegacySkin(IResourceStore<byte[]> storage, IStorageResourceProvider resources)
: this(Info, storage, resources)
public DefaultLegacySkin(IStorageResourceProvider resources)
: this(Info, resources)
{
}
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)]
public DefaultLegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, IStorageResourceProvider resources)
: base(skin, storage, resources, string.Empty)
public DefaultLegacySkin(SkinInfo skin, IStorageResourceProvider resources)
: base(skin, new NamespacedResourceStore<byte[]>(resources.Resources, "Skins/Legacy"), resources, string.Empty)
{
Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255);
Configuration.AddComboColours(
@ -31,6 +31,8 @@ namespace osu.Game.Skinning
Configuration.LegacyVersion = 2.7m;
}
protected override DefaultLegacySkin CreateFallbackSkin(IResourceStore<byte[]> storage, IStorageResourceProvider resources) => null;
public static SkinInfo Info { get; } = new SkinInfo
{
ID = SkinInfo.CLASSIC_SKIN, // this is temporary until database storage is decided upon.

View File

@ -1,6 +1,7 @@
// 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;
using JetBrains.Annotations;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
@ -57,5 +58,13 @@ namespace osu.Game.Skinning
/// <returns>A matching value boxed in an <see cref="IBindable{TValue}"/>, or null if unavailable.</returns>
[CanBeNull]
IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
/// <summary>
/// For the specified texture, find any potential skin that can fulfill the lookup.
/// This should be used for cases where subsequent lookups (for related components) need to occur on the same skin.
/// </summary>
/// <returns>The skin to be used for subsequent lookups, or <c>null</c> if none is available.</returns>
[CanBeNull]
ISkin FindProvider(Func<ISkin, bool> lookupFunction);
}
}

View File

@ -70,6 +70,14 @@ namespace osu.Game.Skinning
return base.GetSample(sampleInfo);
}
protected override DefaultLegacySkin CreateFallbackSkin(IResourceStore<byte[]> 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 };
}

View File

@ -54,16 +54,29 @@ namespace osu.Game.Skinning
private readonly Dictionary<int, LegacyManiaSkinConfiguration> maniaConfigurations = new Dictionary<int, LegacyManiaSkinConfiguration>();
[CanBeNull]
private readonly DefaultLegacySkin legacyDefaultFallback;
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)]
public LegacySkin(SkinInfo skin, IStorageResourceProvider resources)
: this(skin, new LegacySkinResourceStore<SkinFileInfo>(skin, resources.Files), resources, "skin.ini")
{
}
protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore<byte[]> storage, [CanBeNull] IStorageResourceProvider resources, string filename)
/// <summary>
/// Construct a new legacy skin instance.
/// </summary>
/// <param name="skin">The model for this skin.</param>
/// <param name="storage">A storage for looking up files within this skin using user-facing filenames.</param>
/// <param name="resources">Access to raw game resources.</param>
/// <param name="configurationFilename">The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file.</param>
protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore<byte[]> storage, [CanBeNull] IStorageResourceProvider resources, string configurationFilename)
: base(skin, resources)
{
using (var stream = storage?.GetStream(filename))
if (resources != null)
legacyDefaultFallback = CreateFallbackSkin(storage, resources);
using (var stream = storage?.GetStream(configurationFilename))
{
if (stream != null)
{
@ -102,6 +115,9 @@ namespace osu.Game.Skinning
true) != null);
}
[CanBeNull]
protected virtual DefaultLegacySkin CreateFallbackSkin(IResourceStore<byte[]> storage, IStorageResourceProvider resources) => new DefaultLegacySkin(resources);
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
{
switch (lookup)
@ -127,7 +143,7 @@ namespace osu.Game.Skinning
case LegacyManiaSkinConfigurationLookup maniaLookup:
if (!AllowManiaSkin)
return null;
break;
var result = lookupForMania<TValue>(maniaLookup);
if (result != null)
@ -142,7 +158,7 @@ namespace osu.Game.Skinning
return genericLookup<TLookup, TValue>(lookup);
}
return null;
return legacyDefaultFallback?.GetConfig<TLookup, TValue>(lookup);
}
private IBindable<TValue> lookupForMania<TValue>(LegacyManiaSkinConfigurationLookup maniaLookup)
@ -319,7 +335,7 @@ namespace osu.Game.Skinning
{
}
return null;
return legacyDefaultFallback?.GetConfig<TLookup, TValue>(lookup);
}
public override Drawable GetDrawableComponent(ISkinComponent component)
@ -333,7 +349,6 @@ namespace osu.Game.Skinning
switch (target.Target)
{
case SkinnableTarget.MainHUDComponents:
var skinnableTargetWrapper = new SkinnableTargetComponentsContainer(container =>
{
var score = container.OfType<LegacyScoreCounter>().FirstOrDefault();
@ -420,7 +435,12 @@ namespace osu.Game.Skinning
break;
}
return this.GetAnimation(component.LookupName, false, false);
var animation = this.GetAnimation(component.LookupName, false, false);
if (animation != null)
return animation;
return legacyDefaultFallback?.GetDrawableComponent(component);
}
private Texture getParticleTexture(HitResult result)
@ -480,7 +500,7 @@ namespace osu.Game.Skinning
return texture;
}
return null;
return legacyDefaultFallback?.GetTexture(componentName, wrapModeS, wrapModeT);
}
public override ISample GetSample(ISampleInfo sampleInfo)
@ -502,7 +522,17 @@ namespace osu.Game.Skinning
return sample;
}
return null;
return legacyDefaultFallback?.GetSample(sampleInfo);
}
public override ISkin FindProvider(Func<ISkin, bool> lookupFunction)
{
var source = base.FindProvider(lookupFunction);
if (source != null)
return source;
return legacyDefaultFallback?.FindProvider(lookupFunction);
}
private IEnumerable<string> getLegacyLookupNames(HitSampleInfo hitSample)
@ -533,7 +563,11 @@ namespace osu.Game.Skinning
// Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle").
string lastPiece = componentName.Split('/').Last();
yield return componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal) ? "taiko-" + lastPiece : lastPiece;
if (componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal))
yield return "taiko-" + lastPiece;
yield return lastPiece;
}
protected override void Dispose(bool isDisposing)

View File

@ -1,6 +1,7 @@
// 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;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -47,5 +48,7 @@ namespace osu.Game.Skinning
}
public abstract IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => Source.FindProvider(lookupFunction);
}
}

View File

@ -35,6 +35,8 @@ namespace osu.Game.Skinning
public abstract IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
public virtual ISkin FindProvider(Func<ISkin, bool> lookupFunction) => lookupFunction(this) ? this : null;
protected Skin(SkinInfo skin, IStorageResourceProvider resources)
{
SkinInfo = skin;

View File

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.IO.Stores;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Extensions;
@ -28,16 +27,13 @@ namespace osu.Game.Skinning
public string InstantiationInfo { get; set; }
public virtual Skin CreateInstance(IResourceStore<byte[]> legacyDefaultResources, IStorageResourceProvider resources)
public virtual Skin CreateInstance(IStorageResourceProvider resources)
{
var type = string.IsNullOrEmpty(InstantiationInfo)
// handle the case of skins imported before InstantiationInfo was added.
? typeof(LegacySkin)
: Type.GetType(InstantiationInfo).AsNonNull();
if (type == typeof(DefaultLegacySkin))
return (Skin)Activator.CreateInstance(type, this, legacyDefaultResources, resources);
return (Skin)Activator.CreateInstance(type, this, resources);
}

View File

@ -37,7 +37,7 @@ namespace osu.Game.Skinning
private readonly GameHost host;
private readonly IResourceStore<byte[]> legacyDefaultResources;
private readonly IResourceStore<byte[]> resources;
public readonly Bindable<Skin> CurrentSkin = new Bindable<Skin>();
public readonly Bindable<SkinInfo> CurrentSkinInfo = new Bindable<SkinInfo>(SkinInfo.Default) { Default = SkinInfo.Default };
@ -48,13 +48,12 @@ namespace osu.Game.Skinning
protected override string ImportFromStablePath => "Skins";
public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, AudioManager audio, IResourceStore<byte[]> legacyDefaultResources)
public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, IResourceStore<byte[]> resources, AudioManager audio)
: base(storage, contextFactory, new SkinStore(contextFactory, storage), host)
{
this.audio = audio;
this.host = host;
this.legacyDefaultResources = legacyDefaultResources;
this.resources = resources;
CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue);
@ -154,7 +153,7 @@ namespace osu.Game.Skinning
/// </summary>
/// <param name="skinInfo">The skin to lookup.</param>
/// <returns>A <see cref="Skin"/> instance correlating to the provided <see cref="SkinInfo"/>.</returns>
public Skin GetSkin(SkinInfo skinInfo) => skinInfo.CreateInstance(legacyDefaultResources, this);
public Skin GetSkin(SkinInfo skinInfo) => skinInfo.CreateInstance(this);
/// <summary>
/// Ensure that the current skin is in a state it can accept user modifications.
@ -215,9 +214,12 @@ namespace osu.Game.Skinning
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => CurrentSkin.Value.GetConfig<TLookup, TValue>(lookup);
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => CurrentSkin.Value.FindProvider(lookupFunction);
#region IResourceStorageProvider
AudioManager IStorageResourceProvider.AudioManager => audio;
IResourceStore<byte[]> IStorageResourceProvider.Resources => resources;
IResourceStore<byte[]> IStorageResourceProvider.Files => Files.Store;
IResourceStore<TextureUpload> IStorageResourceProvider.CreateTextureLoaderStore(IResourceStore<byte[]> underlyingStore) => host.CreateTextureLoaderStore(underlyingStore);

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
@ -20,8 +21,10 @@ namespace osu.Game.Skinning
{
public event Action SourceChanged;
[CanBeNull]
private readonly ISkin skin;
[CanBeNull]
private ISkinSource fallbackSource;
protected virtual bool AllowDrawableLookup(ISkinComponent component) => true;
@ -41,6 +44,14 @@ namespace osu.Game.Skinning
RelativeSizeAxes = Axes.Both;
}
public ISkin FindProvider(Func<ISkin, bool> lookupFunction)
{
if (skin != null && lookupFunction(skin))
return skin;
return fallbackSource?.FindProvider(lookupFunction);
}
public Drawable GetDrawableComponent(ISkinComponent component)
{
Drawable sourceDrawable;
@ -85,7 +96,7 @@ namespace osu.Game.Skinning
{
if (canUseSkinLookup)
{
var bindable = skin.GetConfig<TLookup, TValue>(lookup);
var bindable = skin?.GetConfig<TLookup, TValue>(lookup);
if (bindable != null)
return bindable;
}

View File

@ -29,7 +29,7 @@ namespace osu.Game.Storyboards.Drawables
[BackgroundDependencyLoader(true)]
private void load(IBindable<WorkingBeatmap> beatmap, TextureStore textureStore)
{
var path = beatmap.Value.BeatmapSetInfo?.Files?.Find(f => f.Filename.Equals(Video.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath;
var path = beatmap.Value.BeatmapSetInfo?.Files.Find(f => f.Filename.Equals(Video.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath;
if (path == null)
return;

View File

@ -91,7 +91,7 @@ namespace osu.Game.Storyboards
public Drawable CreateSpriteFromResourcePath(string path, TextureStore textureStore)
{
Drawable drawable = null;
var storyboardPath = BeatmapInfo.BeatmapSet?.Files?.Find(f => f.Filename.Equals(path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath;
var storyboardPath = BeatmapInfo.BeatmapSet?.Files.Find(f => f.Filename.Equals(path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath;
if (storyboardPath != null)
drawable = new Sprite { Texture = textureStore.Get(storyboardPath) };

View File

@ -28,7 +28,7 @@ namespace osu.Game.Tests.Beatmaps
[HeadlessTest]
public abstract class HitObjectSampleTest : PlayerTestScene, IStorageResourceProvider
{
protected abstract IResourceStore<byte[]> Resources { get; }
protected abstract IResourceStore<byte[]> RulesetResources { get; }
protected LegacySkin Skin { get; private set; }
[Resolved]
@ -75,7 +75,7 @@ namespace osu.Game.Tests.Beatmaps
AddStep($"load {filename}", () =>
{
using (var reader = new LineBufferedReader(Resources.GetStream($"Resources/SampleLookups/{filename}")))
using (var reader = new LineBufferedReader(RulesetResources.GetStream($"Resources/SampleLookups/{filename}")))
currentTestBeatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
// populate ruleset for beatmap converters that require it to be present.
@ -127,6 +127,7 @@ namespace osu.Game.Tests.Beatmaps
public AudioManager AudioManager => Audio;
public IResourceStore<byte[]> Files => userSkinResourceStore;
public new IResourceStore<byte[]> Resources => base.Resources;
public IResourceStore<TextureUpload> CreateTextureLoaderStore(IResourceStore<byte[]> underlyingStore) => null;
#endregion

View File

@ -21,23 +21,17 @@ namespace osu.Game.Tests.Beatmaps
{
protected readonly Bindable<bool> BeatmapSkins = new Bindable<bool>();
protected readonly Bindable<bool> BeatmapColours = new Bindable<bool>();
protected ExposedPlayer TestPlayer;
protected WorkingBeatmap TestBeatmap;
public virtual void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, true, userHasCustomColours);
private WorkingBeatmap testBeatmap;
public virtual void TestBeatmapComboColoursOverride(bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, false, true);
protected void PrepareBeatmap(Func<WorkingBeatmap> createBeatmap) => AddStep("prepare beatmap", () => testBeatmap = createBeatmap());
public virtual void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin) => ConfigureTest(useBeatmapSkin, false, false);
public virtual void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour) => ConfigureTest(useBeatmapSkin, useBeatmapColour, false);
public virtual void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour) => ConfigureTest(useBeatmapSkin, useBeatmapColour, true);
protected virtual void ConfigureTest(bool useBeatmapSkin, bool useBeatmapColours, bool userHasCustomColours)
protected void ConfigureTest(bool useBeatmapSkin, bool useBeatmapColours, bool userHasCustomColours)
{
configureSettings(useBeatmapSkin, useBeatmapColours);
AddStep($"load {(((CustomSkinWorkingBeatmap)TestBeatmap).HasColours ? "coloured " : "")} beatmap", () => TestPlayer = LoadBeatmap(userHasCustomColours));
AddStep("load beatmap", () => TestPlayer = LoadBeatmap(userHasCustomColours));
AddUntilStep("wait for player load", () => TestPlayer.IsLoaded);
}
@ -57,7 +51,7 @@ namespace osu.Game.Tests.Beatmaps
{
ExposedPlayer player;
Beatmap.Value = TestBeatmap;
Beatmap.Value = testBeatmap;
LoadScreen(player = CreateTestPlayer(userHasCustomColours));

View File

@ -29,7 +29,6 @@ namespace osu.Game.Tests.Beatmaps
BeatmapInfo.Ruleset = ruleset;
BeatmapInfo.RulesetID = ruleset.ID ?? 0;
BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata;
BeatmapInfo.BeatmapSet.Files = new List<BeatmapSetFileInfo>();
BeatmapInfo.BeatmapSet.Beatmaps = new List<BeatmapInfo> { BeatmapInfo };
BeatmapInfo.Length = 75000;
BeatmapInfo.OnlineInfo = new BeatmapOnlineInfo();

View File

@ -4,11 +4,19 @@
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.IO.Archives;
using osu.Game.Online.API;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Edit;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osu.Game.Skinning;
namespace osu.Game.Tests.Visual
{
@ -20,10 +28,20 @@ namespace osu.Game.Tests.Visual
protected EditorClock EditorClock { get; private set; }
/// <summary>
/// Whether any saves performed by the editor should be isolate (and not persist) to the underlying <see cref="BeatmapManager"/>.
/// </summary>
protected virtual bool IsolateSavingFromDatabase => true;
[BackgroundDependencyLoader]
private void load()
private void load(GameHost host, AudioManager audio, RulesetStore rulesets)
{
Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value);
var working = CreateWorkingBeatmap(Ruleset.Value);
Beatmap.Value = working;
if (IsolateSavingFromDatabase)
Dependencies.CacheAs<BeatmapManager>(new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default, working));
}
protected virtual bool EditorComponentsReady => Editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true
@ -33,12 +51,17 @@ namespace osu.Game.Tests.Visual
{
base.SetUpSteps();
AddStep("load editor", () => LoadScreen(Editor = CreateEditor()));
AddStep("load editor", LoadEditor);
AddUntilStep("wait for editor to load", () => EditorComponentsReady);
AddStep("get beatmap", () => EditorBeatmap = Editor.ChildrenOfType<EditorBeatmap>().Single());
AddStep("get clock", () => EditorClock = Editor.ChildrenOfType<EditorClock>().Single());
}
protected virtual void LoadEditor()
{
LoadScreen(Editor = CreateEditor());
}
/// <summary>
/// Creates the ruleset for providing a corresponding beatmap to load the editor on.
/// </summary>
@ -65,5 +88,27 @@ namespace osu.Game.Tests.Visual
public new bool HasUnsavedChanges => base.HasUnsavedChanges;
}
private class TestBeatmapManager : BeatmapManager
{
private readonly WorkingBeatmap testBeatmap;
public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore<byte[]> resources, GameHost host, WorkingBeatmap defaultBeatmap, WorkingBeatmap testBeatmap)
: base(storage, contextFactory, rulesets, api, audioManager, resources, host, defaultBeatmap, false)
{
this.testBeatmap = testBeatmap;
}
protected override string ComputeHash(BeatmapSetInfo item, ArchiveReader reader = null)
=> string.Empty;
public override WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo)
=> testBeatmap;
public override void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null)
{
// don't actually care about saving for this context.
}
}
}
}

View File

@ -3,7 +3,8 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.IO.Stores;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Skinning;
@ -12,15 +13,40 @@ namespace osu.Game.Tests.Visual
[TestFixture]
public abstract class LegacySkinPlayerTestScene : PlayerTestScene
{
protected LegacySkin LegacySkin { get; private set; }
private ISkinSource legacySkinSource;
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(legacySkinSource);
[BackgroundDependencyLoader]
private void load(OsuGameBase game, SkinManager skins)
private void load(SkinManager skins)
{
var legacySkin = new DefaultLegacySkin(new NamespacedResourceStore<byte[]>(game.Resources, "Skins/Legacy"), skins);
legacySkinSource = new SkinProvidingContainer(legacySkin);
LegacySkin = new DefaultLegacySkin(skins);
legacySkinSource = new SkinProvidingContainer(LegacySkin);
}
[SetUpSteps]
public override void SetUpSteps()
{
base.SetUpSteps();
addResetTargetsStep();
}
[TearDownSteps]
public override void TearDownSteps()
{
addResetTargetsStep();
base.TearDownSteps();
}
private void addResetTargetsStep()
{
AddStep("reset targets", () => this.ChildrenOfType<SkinnableTargetContainer>().ForEach(t =>
{
LegacySkin.ResetDrawableTarget(t);
t.Reload();
}));
}
public class SkinProvidingPlayer : TestPlayer

View File

@ -14,6 +14,7 @@ using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Framework.Timing;
@ -49,6 +50,8 @@ namespace osu.Game.Tests.Visual
private Lazy<DatabaseContextFactory> contextFactory;
protected IResourceStore<byte[]> Resources;
protected IAPIProvider API
{
get
@ -81,6 +84,8 @@ namespace osu.Game.Tests.Visual
if (!UseFreshStoragePerRun)
isolatedHostStorage = (parent.Get<GameHost>() as HeadlessGameHost)?.Storage;
Resources = parent.Get<OsuGameBase>().Resources;
contextFactory = new Lazy<DatabaseContextFactory>(() =>
{
var factory = new DatabaseContextFactory(LocalStorage);

View File

@ -40,12 +40,12 @@ namespace osu.Game.Tests.Visual
}
[BackgroundDependencyLoader]
private void load(AudioManager audio, SkinManager skinManager, OsuGameBase game)
private void load(AudioManager audio, SkinManager skinManager)
{
var dllStore = new DllResourceStore(DynamicCompilationOriginal.GetType().Assembly);
metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), this, true);
defaultSkin = new DefaultLegacySkin(new NamespacedResourceStore<byte[]>(game.Resources, "Skins/Legacy"), this);
defaultSkin = new DefaultLegacySkin(this);
specialSkin = new TestLegacySkin(new SkinInfo { Name = "special-skin" }, new NamespacedResourceStore<byte[]>(dllStore, "Resources/special_skin"), this, true);
oldSkin = new TestLegacySkin(new SkinInfo { Name = "old-skin" }, new NamespacedResourceStore<byte[]>(dllStore, "Resources/old_skin"), this, true);
}
@ -156,6 +156,7 @@ namespace osu.Game.Tests.Visual
public AudioManager AudioManager => Audio;
public IResourceStore<byte[]> Files => null;
public new IResourceStore<byte[]> Resources => base.Resources;
public IResourceStore<TextureUpload> CreateTextureLoaderStore(IResourceStore<byte[]> underlyingStore) => host.CreateTextureLoaderStore(underlyingStore);
#endregion