Merge pull request #20354 from peppy/default-skin-refactor

Rename default skin to make way for new version
This commit is contained in:
Dan Balasescu 2022-09-21 11:48:38 +09:00 committed by GitHub
commit 8bf196d561
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 77 additions and 57 deletions

View File

@ -87,7 +87,7 @@ private void catchFruit(Fruit fruit, float x)
});
}
private class TestSkin : DefaultSkin
private class TestSkin : TrianglesSkin
{
public bool FlipCatcherPlate { get; set; }

View File

@ -56,7 +56,7 @@ public LegacyHitExplosion()
[BackgroundDependencyLoader]
private void load(SkinManager skins)
{
var defaultLegacySkin = skins.DefaultLegacySkin;
var defaultLegacySkin = skins.DefaultClassicSkin;
// sprite names intentionally swapped to match stable member naming / ease of cross-referencing
explosion1.Texture = defaultLegacySkin.GetTexture("scoreboard-explosion-2");

View File

@ -69,7 +69,7 @@ public void SetUpSteps()
[Test]
public void TestDefaultSkin()
{
AddStep("set default skin", () => skins.CurrentSkinInfo.Value = DefaultSkin.CreateInfo().ToLiveUnmanaged());
AddStep("set default skin", () => skins.CurrentSkinInfo.Value = TrianglesSkin.CreateInfo().ToLiveUnmanaged());
}
[Test]

View File

@ -170,7 +170,7 @@ private void createLegacyTest(bool autoplay, Func<IBeatmap> beatmap) => CreateTe
});
AddStep("setup default legacy skin", () =>
{
skinManager.CurrentSkinInfo.Value = skinManager.DefaultLegacySkin.SkinInfo;
skinManager.CurrentSkinInfo.Value = skinManager.DefaultClassicSkin.SkinInfo;
});
});
}

View File

@ -35,7 +35,7 @@ public void SetUp() => Schedule(() =>
hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
Child = new SkinProvidingContainer(new DefaultSkin(null))
Child = new SkinProvidingContainer(new TrianglesSkin(null))
{
RelativeSizeAxes = Axes.Both,
Child = drawableHitCircle = new DrawableHitCircle(hitCircle)

View File

@ -202,7 +202,7 @@ public Task TestExportThenImportDefaultSkin() => runSkinTest(osu =>
skinManager.CurrentSkinInfo.Value.PerformRead(s =>
{
Assert.IsFalse(s.Protected);
Assert.AreEqual(typeof(DefaultSkin), s.CreateInstance(skinManager).GetType());
Assert.AreEqual(typeof(TrianglesSkin), s.CreateInstance(skinManager).GetType());
new LegacySkinExporter(osu.Dependencies.Get<Storage>()).ExportModelTo(s, exportStream);
@ -215,7 +215,7 @@ public Task TestExportThenImportDefaultSkin() => runSkinTest(osu =>
{
Assert.IsFalse(s.Protected);
Assert.AreNotEqual(originalSkinId, s.ID);
Assert.AreEqual(typeof(DefaultSkin), s.CreateInstance(skinManager).GetType());
Assert.AreEqual(typeof(TrianglesSkin), s.CreateInstance(skinManager).GetType());
});
return Task.CompletedTask;
@ -226,7 +226,7 @@ public Task TestExportThenImportClassicSkin() => runSkinTest(osu =>
{
var skinManager = osu.Dependencies.Get<SkinManager>();
skinManager.CurrentSkinInfo.Value = skinManager.DefaultLegacySkin.SkinInfo;
skinManager.CurrentSkinInfo.Value = skinManager.DefaultClassicSkin.SkinInfo;
skinManager.EnsureMutableSkin();

View File

@ -1,12 +1,11 @@
// 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 disable
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Skinning;
namespace osu.Game.Tests.Visual.Gameplay
{
@ -19,7 +18,7 @@ public void SetUp() => Schedule(() =>
{
SetContents(skin =>
{
var implementation = skin != null
var implementation = skin is LegacySkin
? CreateLegacyImplementation()
: CreateDefaultImplementation();

View File

@ -38,7 +38,7 @@ public class TestSceneBeatmapSkinFallbacks : OsuPlayerTestScene
[Test]
public void TestEmptyLegacyBeatmapSkinFallsBack()
{
CreateSkinTest(DefaultSkin.CreateInfo(), () => new LegacyBeatmapSkin(new BeatmapInfo(), null));
CreateSkinTest(TrianglesSkin.CreateInfo(), () => new LegacyBeatmapSkin(new BeatmapInfo(), null));
AddUntilStep("wait for hud load", () => Player.ChildrenOfType<SkinnableTargetContainer>().All(c => c.ComponentsLoaded));
AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value));
}

View File

@ -79,7 +79,7 @@ public void TestTimeJumps()
}
private TestParticleSpewer createSpewer() =>
new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2"))
new TestParticleSpewer(skinManager.DefaultClassicSkin.GetTexture("star2"))
{
Origin = Anchor.Centre,
RelativePositionAxes = Axes.Both,

View File

@ -21,7 +21,7 @@ public class TestSceneEditDefaultSkin : OsuGameTestScene
[Test]
public void TestEditDefaultSkin()
{
AddAssert("is default skin", () => skinManager.CurrentSkinInfo.Value.ID == SkinInfo.DEFAULT_SKIN);
AddAssert("is default skin", () => skinManager.CurrentSkinInfo.Value.ID == SkinInfo.TRIANGLES_SKIN);
AddStep("open settings", () => { Game.Settings.Show(); });
@ -32,7 +32,7 @@ public void TestEditDefaultSkin()
AddStep("open skin editor", () => skinEditor.Show());
// Until step required as the skin editor may take time to load (and an extra scheduled frame for the mutable part).
AddUntilStep("is modified default skin", () => skinManager.CurrentSkinInfo.Value.ID != SkinInfo.DEFAULT_SKIN);
AddUntilStep("is modified default skin", () => skinManager.CurrentSkinInfo.Value.ID != SkinInfo.TRIANGLES_SKIN);
AddAssert("is not protected", () => skinManager.CurrentSkinInfo.Value.PerformRead(s => !s.Protected));
AddUntilStep("export button enabled", () => Game.Settings.ChildrenOfType<SkinSection.ExportSkinButton>().SingleOrDefault()?.Enabled.Value == true);

View File

@ -39,7 +39,7 @@ protected override void InitialiseDefaults()
{
// UI/selection defaults
SetDefault(OsuSetting.Ruleset, string.Empty);
SetDefault(OsuSetting.Skin, SkinInfo.DEFAULT_SKIN.ToString());
SetDefault(OsuSetting.Skin, SkinInfo.TRIANGLES_SKIN.ToString());
SetDefault(OsuSetting.BeatmapDetailTab, PlayBeatmapDetailArea.TabType.Details);
SetDefault(OsuSetting.BeatmapDetailModsFilter, false);

View File

@ -69,8 +69,9 @@ public class RealmAccess : IDisposable
/// 22 2022-07-31 Added ModPreset.
/// 23 2022-08-01 Added LastLocalUpdate to BeatmapInfo.
/// 24 2022-08-22 Added MaximumStatistics to ScoreInfo.
/// 25 2022-09-18 Remove skins to add with new naming.
/// </summary>
private const int schema_version = 24;
private const int schema_version = 25;
/// <summary>
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
@ -870,6 +871,11 @@ void convertOnlineIDs<T>() where T : RealmObject
}
break;
case 25:
// Remove the default skins so they can be added back by SkinManager with updated naming.
migration.NewRealm.RemoveRange(migration.NewRealm.All<SkinInfo>().Where(s => s.Protected));
break;
}
}

View File

@ -78,8 +78,7 @@ protected override void LoadComplete()
realmSubscription = realm.RegisterForNotifications(_ => realm.Realm.All<SkinInfo>()
.Where(s => !s.DeletePending)
.OrderByDescending(s => s.Protected) // protected skins should be at the top.
.ThenBy(s => s.Name, StringComparer.OrdinalIgnoreCase), skinsChanged);
.OrderBy(s => s.Name, StringComparer.OrdinalIgnoreCase), skinsChanged);
skinDropdown.Current.BindValueChanged(skin =>
{
@ -101,14 +100,17 @@ private void skinsChanged(IRealmCollection<SkinInfo> sender, ChangeSet changes,
if (!sender.Any())
return;
int protectedCount = sender.Count(s => s.Protected);
// For simplicity repopulate the full list.
// In the future we should change this to properly handle ChangeSet events.
dropdownItems.Clear();
foreach (var skin in sender)
dropdownItems.Add(sender.Single(s => s.ID == SkinInfo.TRIANGLES_SKIN).ToLive(realm));
dropdownItems.Add(sender.Single(s => s.ID == SkinInfo.CLASSIC_SKIN).ToLive(realm));
dropdownItems.Add(random_skin_info);
foreach (var skin in sender.Where(s => !s.Protected))
dropdownItems.Add(skin.ToLive(realm));
dropdownItems.Insert(protectedCount, random_skin_info);
Schedule(() => skinDropdown.Items = dropdownItems);
}

View File

@ -130,7 +130,7 @@ private Background createBackground()
case BackgroundSource.Skin:
// default skins should use the default background rotation, which won't be the case if a SkinBackground is created for them.
if (skin.Value is DefaultSkin || skin.Value is DefaultLegacySkin)
if (skin.Value is TrianglesSkin || skin.Value is DefaultLegacySkin)
break;
newBackground = new SkinBackground(skin.Value, getBackgroundTextureName());

View File

@ -17,7 +17,7 @@ public class DefaultLegacySkin : LegacySkin
public static SkinInfo CreateInfo() => new SkinInfo
{
ID = Skinning.SkinInfo.CLASSIC_SKIN, // this is temporary until database storage is decided upon.
Name = "osu!classic",
Name = "osu! \"classic\" (2013)",
Creator = "team osu!",
Protected = true,
InstantiationInfo = typeof(DefaultLegacySkin).GetInvariantInstantiationInfo()

View File

@ -81,7 +81,7 @@ protected override void RefreshSources()
}
}
int lastDefaultSkinIndex = sources.IndexOf(sources.OfType<DefaultSkin>().LastOrDefault());
int lastDefaultSkinIndex = sources.IndexOf(sources.OfType<TrianglesSkin>().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.

View File

@ -232,6 +232,9 @@ public void Save(Skin skin)
{
skin.SkinInfo.PerformWrite(s =>
{
// Update for safety
s.InstantiationInfo = skin.GetType().GetInvariantInstantiationInfo();
// Serialise out the SkinInfo itself.
string skinInfoJson = JsonConvert.SerializeObject(s, new JsonSerializerSettings { Formatting = Formatting.Indented });

View File

@ -5,7 +5,6 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Newtonsoft.Json;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.IO;
@ -19,7 +18,7 @@ namespace osu.Game.Skinning
[JsonObject(MemberSerialization.OptIn)]
public class SkinInfo : RealmObject, IHasRealmFiles, IEquatable<SkinInfo>, IHasGuidPrimaryKey, ISoftDelete, IHasNamedFiles
{
internal static readonly Guid DEFAULT_SKIN = new Guid("2991CFD8-2140-469A-BCB9-2EC23FBCE4AD");
internal static readonly Guid TRIANGLES_SKIN = new Guid("2991CFD8-2140-469A-BCB9-2EC23FBCE4AD");
internal static readonly Guid CLASSIC_SKIN = new Guid("81F02CD3-EEC6-4865-AC23-FAE26A386187");
internal static readonly Guid RANDOM_SKIN = new Guid("D39DFEFB-477C-4372-B1EA-2BCEA5FB8908");
@ -45,7 +44,16 @@ 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();
: Type.GetType(InstantiationInfo);
if (type == null)
{
// Since the class was renamed from "DefaultSkin" to "TrianglesSkin", the type retrieval would fail
// for user modified skins. This aims to amicably handle that.
// If we ever add more default skins in the future this will need some kind of proper migration rather than
// a single fallback.
return new TrianglesSkin(this, resources);
}
return (Skin)Activator.CreateInstance(type, this, resources);
}

View File

@ -49,9 +49,9 @@ public class SkinManager : ModelManager<SkinInfo>, ISkinSource, IStorageResource
public readonly Bindable<Skin> CurrentSkin = new Bindable<Skin>();
public readonly Bindable<Live<SkinInfo>> CurrentSkinInfo = new Bindable<Live<SkinInfo>>(Skinning.DefaultSkin.CreateInfo().ToLiveUnmanaged())
public readonly Bindable<Live<SkinInfo>> CurrentSkinInfo = new Bindable<Live<SkinInfo>>(TrianglesSkin.CreateInfo().ToLiveUnmanaged())
{
Default = Skinning.DefaultSkin.CreateInfo().ToLiveUnmanaged()
Default = TrianglesSkin.CreateInfo().ToLiveUnmanaged()
};
private readonly SkinImporter skinImporter;
@ -59,14 +59,14 @@ public class SkinManager : ModelManager<SkinInfo>, ISkinSource, IStorageResource
private readonly IResourceStore<byte[]> userFiles;
/// <summary>
/// The default skin.
/// The default "triangles" skin.
/// </summary>
public Skin DefaultSkin { get; }
public Skin DefaultSkinTriangles { get; }
/// <summary>
/// The default legacy skin.
/// The default "classic" skin.
/// </summary>
public Skin DefaultLegacySkin { get; }
public Skin DefaultClassicSkin { get; }
public SkinManager(Storage storage, RealmAccess realm, GameHost host, IResourceStore<byte[]> resources, AudioManager audio, Scheduler scheduler)
: base(storage, realm)
@ -85,8 +85,8 @@ public SkinManager(Storage storage, RealmAccess realm, GameHost host, IResourceS
var defaultSkins = new[]
{
DefaultLegacySkin = new DefaultLegacySkin(this),
DefaultSkin = new DefaultSkin(this),
DefaultClassicSkin = new DefaultLegacySkin(this),
DefaultSkinTriangles = new TrianglesSkin(this),
};
// Ensure the default entries are present.
@ -104,7 +104,7 @@ public SkinManager(Storage storage, RealmAccess realm, GameHost host, IResourceS
CurrentSkin.Value = skin.NewValue.PerformRead(GetSkin);
};
CurrentSkin.Value = DefaultSkin;
CurrentSkin.Value = DefaultSkinTriangles;
CurrentSkin.ValueChanged += skin =>
{
if (!skin.NewValue.SkinInfo.Equals(CurrentSkinInfo.Value))
@ -125,7 +125,7 @@ public void SelectRandomSkin()
if (randomChoices.Length == 0)
{
CurrentSkinInfo.Value = Skinning.DefaultSkin.CreateInfo().ToLiveUnmanaged();
CurrentSkinInfo.Value = TrianglesSkin.CreateInfo().ToLiveUnmanaged();
return;
}
@ -229,11 +229,11 @@ public IEnumerable<ISkin> AllSources
{
yield return CurrentSkin.Value;
if (CurrentSkin.Value is LegacySkin && CurrentSkin.Value != DefaultLegacySkin)
yield return DefaultLegacySkin;
if (CurrentSkin.Value is LegacySkin && CurrentSkin.Value != DefaultClassicSkin)
yield return DefaultClassicSkin;
if (CurrentSkin.Value != DefaultSkin)
yield return DefaultSkin;
if (CurrentSkin.Value != DefaultSkinTriangles)
yield return DefaultSkinTriangles;
}
}
@ -294,7 +294,7 @@ public void Delete([CanBeNull] Expression<Func<SkinInfo, bool>> filter = null, b
Guid currentUserSkin = CurrentSkinInfo.Value.ID;
if (items.Any(s => s.ID == currentUserSkin))
scheduler.Add(() => CurrentSkinInfo.Value = Skinning.DefaultSkin.CreateInfo().ToLiveUnmanaged());
scheduler.Add(() => CurrentSkinInfo.Value = TrianglesSkin.CreateInfo().ToLiveUnmanaged());
Delete(items.ToList(), silent);
});
@ -310,10 +310,10 @@ public void SetSkinFromConfiguration(string guidString)
if (skinInfo == null)
{
if (guid == SkinInfo.CLASSIC_SKIN)
skinInfo = DefaultLegacySkin.SkinInfo;
skinInfo = DefaultClassicSkin.SkinInfo;
}
CurrentSkinInfo.Value = skinInfo ?? DefaultSkin.SkinInfo;
CurrentSkinInfo.Value = skinInfo ?? DefaultSkinTriangles.SkinInfo;
}
}
}

View File

@ -112,7 +112,7 @@ static ISkin getHighestPriorityUserSkin(IEnumerable<ISkin> skins)
// Temporarily used to exclude undesirable ISkin implementations
static bool isUserSkin(ISkin skin)
=> skin.GetType() == typeof(DefaultSkin)
=> skin.GetType() == typeof(TrianglesSkin)
|| skin.GetType() == typeof(DefaultLegacySkin)
|| skin.GetType() == typeof(LegacySkin);
}

View File

@ -22,26 +22,26 @@
namespace osu.Game.Skinning
{
public class DefaultSkin : Skin
public class TrianglesSkin : Skin
{
public static SkinInfo CreateInfo() => new SkinInfo
{
ID = osu.Game.Skinning.SkinInfo.DEFAULT_SKIN,
Name = "osu! (triangles)",
ID = osu.Game.Skinning.SkinInfo.TRIANGLES_SKIN,
Name = "osu! \"triangles\" (2017)",
Creator = "team osu!",
Protected = true,
InstantiationInfo = typeof(DefaultSkin).GetInvariantInstantiationInfo()
InstantiationInfo = typeof(TrianglesSkin).GetInvariantInstantiationInfo()
};
private readonly IStorageResourceProvider resources;
public DefaultSkin(IStorageResourceProvider resources)
public TrianglesSkin(IStorageResourceProvider resources)
: this(CreateInfo(), resources)
{
}
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)]
public DefaultSkin(SkinInfo skin, IStorageResourceProvider resources)
public TrianglesSkin(SkinInfo skin, IStorageResourceProvider resources)
: base(skin, resources)
{
this.resources = resources;

View File

@ -29,8 +29,9 @@ namespace osu.Game.Tests.Visual
{
public abstract class SkinnableTestScene : OsuGridTestScene, IStorageResourceProvider
{
private TrianglesSkin trianglesSkin;
private Skin metricsSkin;
private Skin defaultSkin;
private Skin legacySkin;
private Skin specialSkin;
private Skin oldSkin;
@ -47,8 +48,9 @@ private void load()
{
var dllStore = new DllResourceStore(GetType().Assembly);
trianglesSkin = new TrianglesSkin(this);
metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), this, true);
defaultSkin = new DefaultLegacySkin(this);
legacySkin = 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);
}
@ -61,9 +63,9 @@ protected void SetContents(Func<ISkin, Drawable> creationFunction)
var beatmap = CreateBeatmapForSkinProvider();
Cell(0).Child = createProvider(null, creationFunction, beatmap);
Cell(0).Child = createProvider(trianglesSkin, creationFunction, beatmap);
Cell(1).Child = createProvider(metricsSkin, creationFunction, beatmap);
Cell(2).Child = createProvider(defaultSkin, creationFunction, beatmap);
Cell(2).Child = createProvider(legacySkin, creationFunction, beatmap);
Cell(3).Child = createProvider(specialSkin, creationFunction, beatmap);
Cell(4).Child = createProvider(oldSkin, creationFunction, beatmap);
}