mirror of
https://github.com/ppy/osu
synced 2025-01-11 08:39:31 +00:00
Store layout version in SkinLayoutVersion
instead and refactor migration code
This commit is contained in:
parent
dc1fb4fdca
commit
0c34e7bebb
BIN
osu.Game.Tests/Resources/Archives/argon-layout-version-0.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/argon-layout-version-0.osk
Normal file
Binary file not shown.
BIN
osu.Game.Tests/Resources/Archives/classic-layout-version-0.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/classic-layout-version-0.osk
Normal file
Binary file not shown.
BIN
osu.Game.Tests/Resources/Archives/triangles-layout-version-0.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/triangles-layout-version-0.osk
Normal file
Binary file not shown.
@ -7,6 +7,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
@ -24,6 +25,7 @@ using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Skinning.Components;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -384,73 +386,82 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestMigrationArgon()
|
||||
{
|
||||
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded);
|
||||
AddStep("add combo to global hud target", () =>
|
||||
{
|
||||
globalHUDTarget.Add(new ArgonComboCounter
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
});
|
||||
Live<SkinInfo> importedSkin = null!;
|
||||
|
||||
Live<SkinInfo> modifiedSkin = null!;
|
||||
AddStep("import old argon skin", () => skins.CurrentSkinInfo.Value = importedSkin = importSkinFromArchives(@"argon-layout-version-0.osk").SkinInfo);
|
||||
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded && rulesetHUDTarget.ComponentsLoaded);
|
||||
AddAssert("no combo in global target", () => !globalHUDTarget.Components.OfType<ArgonComboCounter>().Any());
|
||||
AddAssert("combo placed in ruleset target", () => rulesetHUDTarget.Components.OfType<ArgonComboCounter>().Count() == 1);
|
||||
|
||||
AddStep("select another skin", () =>
|
||||
AddStep("add combo to global target", () => globalHUDTarget.Add(new ArgonComboCounter
|
||||
{
|
||||
modifiedSkin = skins.CurrentSkinInfo.Value;
|
||||
skins.CurrentSkinInfo.SetDefault();
|
||||
});
|
||||
AddStep("modify version", () => modifiedSkin.PerformWrite(s => s.LayoutVersion = 0));
|
||||
AddStep("select skin again", () => skins.CurrentSkinInfo.Value = modifiedSkin);
|
||||
AddAssert("global hud target does not contain combo", () => !globalHUDTarget.Components.Any(c => c is ArgonComboCounter));
|
||||
AddAssert("ruleset hud target contains both combos", () =>
|
||||
{
|
||||
var target = rulesetHUDTarget;
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(2f),
|
||||
}));
|
||||
AddStep("save skin", () => skins.Save(skins.CurrentSkin.Value));
|
||||
|
||||
return target.Components.Count == 2 &&
|
||||
target.Components[0] is ArgonComboCounter one && one.Anchor == Anchor.BottomLeft && one.Origin == Anchor.BottomLeft &&
|
||||
target.Components[1] is ArgonComboCounter two && two.Anchor == Anchor.Centre && two.Origin == Anchor.Centre;
|
||||
});
|
||||
AddStep("save skin", () => skinEditor.Save());
|
||||
AddAssert("version updated", () => modifiedSkin.PerformRead(s => s.LayoutVersion) == SkinInfo.LATEST_LAYOUT_VERSION);
|
||||
AddStep("select another skin", () => skins.CurrentSkinInfo.SetDefault());
|
||||
AddStep("select skin again", () => skins.CurrentSkinInfo.Value = importedSkin);
|
||||
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded && rulesetHUDTarget.ComponentsLoaded);
|
||||
AddAssert("combo placed in global target", () => globalHUDTarget.Components.OfType<ArgonComboCounter>().Count() == 1);
|
||||
AddAssert("combo placed in ruleset target", () => rulesetHUDTarget.Components.OfType<ArgonComboCounter>().Count() == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMigrationTriangles()
|
||||
{
|
||||
Live<SkinInfo> importedSkin = null!;
|
||||
|
||||
AddStep("import old triangles skin", () => skins.CurrentSkinInfo.Value = importedSkin = importSkinFromArchives(@"triangles-layout-version-0.osk").SkinInfo);
|
||||
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded && rulesetHUDTarget.ComponentsLoaded);
|
||||
AddAssert("no combo in global target", () => !globalHUDTarget.Components.OfType<DefaultComboCounter>().Any());
|
||||
AddAssert("combo placed in ruleset target", () => rulesetHUDTarget.Components.OfType<DefaultComboCounter>().Count() == 1);
|
||||
|
||||
AddStep("add combo to global target", () => globalHUDTarget.Add(new DefaultComboCounter
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(2f),
|
||||
}));
|
||||
AddStep("save skin", () => skins.Save(skins.CurrentSkin.Value));
|
||||
|
||||
AddStep("select another skin", () => skins.CurrentSkinInfo.SetDefault());
|
||||
AddStep("select skin again", () => skins.CurrentSkinInfo.Value = importedSkin);
|
||||
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded && rulesetHUDTarget.ComponentsLoaded);
|
||||
AddAssert("combo placed in global target", () => globalHUDTarget.Components.OfType<DefaultComboCounter>().Count() == 1);
|
||||
AddAssert("combo placed in ruleset target", () => rulesetHUDTarget.Components.OfType<DefaultComboCounter>().Count() == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMigrationLegacy()
|
||||
{
|
||||
AddStep("select legacy skin", () => skins.CurrentSkinInfo.Value = skins.DefaultClassicSkin.SkinInfo);
|
||||
Live<SkinInfo> importedSkin = null!;
|
||||
|
||||
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded);
|
||||
AddStep("add combo to global hud target", () =>
|
||||
AddStep("import old classic skin", () => skins.CurrentSkinInfo.Value = importedSkin = importSkinFromArchives(@"classic-layout-version-0.osk").SkinInfo);
|
||||
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded && rulesetHUDTarget.ComponentsLoaded);
|
||||
AddAssert("no combo in global target", () => !globalHUDTarget.Components.OfType<LegacyComboCounter>().Any());
|
||||
AddAssert("combo placed in ruleset target", () => rulesetHUDTarget.Components.OfType<LegacyComboCounter>().Count() == 1);
|
||||
|
||||
AddStep("add combo to global target", () => globalHUDTarget.Add(new LegacyComboCounter
|
||||
{
|
||||
globalHUDTarget.Add(new LegacyComboCounter
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
});
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(2f),
|
||||
}));
|
||||
AddStep("save skin", () => skins.Save(skins.CurrentSkin.Value));
|
||||
|
||||
Live<SkinInfo> modifiedSkin = null!;
|
||||
AddStep("select another skin", () => skins.CurrentSkinInfo.SetDefault());
|
||||
AddStep("select skin again", () => skins.CurrentSkinInfo.Value = importedSkin);
|
||||
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded && rulesetHUDTarget.ComponentsLoaded);
|
||||
AddAssert("combo placed in global target", () => globalHUDTarget.Components.OfType<LegacyComboCounter>().Count() == 1);
|
||||
AddAssert("combo placed in ruleset target", () => rulesetHUDTarget.Components.OfType<LegacyComboCounter>().Count() == 1);
|
||||
}
|
||||
|
||||
AddStep("select another skin", () =>
|
||||
{
|
||||
modifiedSkin = skins.CurrentSkinInfo.Value;
|
||||
skins.CurrentSkinInfo.SetDefault();
|
||||
});
|
||||
AddStep("modify version", () => modifiedSkin.PerformWrite(s => s.LayoutVersion = 0));
|
||||
AddStep("select skin again", () => skins.CurrentSkinInfo.Value = modifiedSkin);
|
||||
AddAssert("global hud target does not contain combo", () => !globalHUDTarget.Components.Any(c => c is LegacyComboCounter));
|
||||
AddAssert("ruleset hud target contains both combos", () =>
|
||||
{
|
||||
var target = rulesetHUDTarget;
|
||||
|
||||
return target.Components.Count == 2 &&
|
||||
target.Components[0] is LegacyComboCounter one && one.Anchor == Anchor.BottomLeft && one.Origin == Anchor.BottomLeft &&
|
||||
target.Components[1] is LegacyComboCounter two && two.Anchor == Anchor.Centre && two.Origin == Anchor.Centre;
|
||||
});
|
||||
AddStep("save skin", () => skinEditor.Save());
|
||||
AddAssert("version updated", () => modifiedSkin.PerformRead(s => s.LayoutVersion) == SkinInfo.LATEST_LAYOUT_VERSION);
|
||||
private Skin importSkinFromArchives(string filename)
|
||||
{
|
||||
var imported = skins.Import(new ImportTask(TestResources.OpenResource($@"Archives/{filename}"), filename)).GetResultSafely();
|
||||
return imported.PerformRead(skinInfo => skins.GetSkin(skinInfo));
|
||||
}
|
||||
|
||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||
|
@ -93,9 +93,8 @@ namespace osu.Game.Database
|
||||
/// 39 2023-12-19 Migrate any EndTimeObjectCount and TotalObjectCount values of 0 to -1 to better identify non-calculated values.
|
||||
/// 40 2023-12-21 Add ScoreInfo.Version to keep track of which build scores were set on.
|
||||
/// 41 2024-04-17 Add ScoreInfo.TotalScoreWithoutMods for future mod multiplier rebalances.
|
||||
/// 42 2024-06-25 Add SkinInfo.LayoutVersion to allow performing migrations of components on structural changes.
|
||||
/// </summary>
|
||||
private const int schema_version = 42;
|
||||
private const int schema_version = 41;
|
||||
|
||||
/// <summary>
|
||||
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
|
||||
|
@ -12,7 +12,6 @@ using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||
@ -70,28 +69,6 @@ namespace osu.Game.Skinning
|
||||
// Purple
|
||||
new Color4(92, 0, 241, 255),
|
||||
};
|
||||
|
||||
if (skin.LayoutVersion < 20240625
|
||||
&& LayoutInfos.TryGetValue(SkinComponentsContainerLookup.TargetArea.MainHUDComponents, out var hudLayout)
|
||||
&& hudLayout.TryGetDrawableInfo(null, out var hudComponents))
|
||||
{
|
||||
var comboCounters = hudComponents.Where(h => h.Type.Name == nameof(ArgonComboCounter)).ToArray();
|
||||
|
||||
if (comboCounters.Any())
|
||||
{
|
||||
hudLayout.Update(null, hudComponents.Except(comboCounters).ToArray());
|
||||
|
||||
resources.RealmAccess.Run(r =>
|
||||
{
|
||||
foreach (var ruleset in r.All<RulesetInfo>())
|
||||
{
|
||||
hudLayout.Update(ruleset, hudLayout.TryGetDrawableInfo(ruleset, out var rulesetComponents)
|
||||
? rulesetComponents.Concat(comboCounters).ToArray()
|
||||
: comboCounters);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => Textures?.Get(componentName, wrapModeS, wrapModeT);
|
||||
|
@ -19,7 +19,6 @@ using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
@ -57,28 +56,6 @@ namespace osu.Game.Skinning
|
||||
protected LegacySkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore<byte[]>? fallbackStore, string configurationFilename = @"skin.ini")
|
||||
: base(skin, resources, fallbackStore, configurationFilename)
|
||||
{
|
||||
if (resources != null
|
||||
&& skin.LayoutVersion < 20240625
|
||||
&& LayoutInfos.TryGetValue(SkinComponentsContainerLookup.TargetArea.MainHUDComponents, out var hudLayout)
|
||||
&& hudLayout.TryGetDrawableInfo(null, out var hudComponents))
|
||||
{
|
||||
var comboCounters = hudComponents.Where(h => h.Type.Name == nameof(LegacyComboCounter)).ToArray();
|
||||
|
||||
if (comboCounters.Any())
|
||||
{
|
||||
hudLayout.Update(null, hudComponents.Except(comboCounters).ToArray());
|
||||
|
||||
resources.RealmAccess.Run(r =>
|
||||
{
|
||||
foreach (var ruleset in r.All<RulesetInfo>())
|
||||
{
|
||||
hudLayout.Update(ruleset, hudLayout.TryGetDrawableInfo(ruleset, out var rulesetComponents)
|
||||
? rulesetComponents.Concat(comboCounters).ToArray()
|
||||
: comboCounters);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override IResourceStore<TextureUpload> CreateTextureLoaderStore(IStorageResourceProvider resources, IResourceStore<byte[]> storage)
|
||||
|
@ -21,11 +21,15 @@ using osu.Framework.Logging;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public abstract class Skin : IDisposable, ISkin
|
||||
{
|
||||
private readonly IStorageResourceProvider? resources;
|
||||
|
||||
/// <summary>
|
||||
/// A texture store which can be used to perform user file lookups for this skin.
|
||||
/// </summary>
|
||||
@ -68,6 +72,8 @@ namespace osu.Game.Skinning
|
||||
/// <param name="configurationFilename">An optional filename to read the skin configuration from. If not provided, the configuration will be retrieved from the storage using "skin.ini".</param>
|
||||
protected Skin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore<byte[]>? fallbackStore = null, string configurationFilename = @"skin.ini")
|
||||
{
|
||||
this.resources = resources;
|
||||
|
||||
Name = skin.Name;
|
||||
|
||||
if (resources != null)
|
||||
@ -131,40 +137,9 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
string jsonContent = Encoding.UTF8.GetString(bytes);
|
||||
|
||||
SkinLayoutInfo? layoutInfo = null;
|
||||
|
||||
// handle namespace changes...
|
||||
jsonContent = jsonContent.Replace(@"osu.Game.Screens.Play.SongProgress", @"osu.Game.Screens.Play.HUD.DefaultSongProgress");
|
||||
jsonContent = jsonContent.Replace(@"osu.Game.Screens.Play.HUD.LegacyComboCounter", @"osu.Game.Skinning.LegacyComboCounter");
|
||||
jsonContent = jsonContent.Replace(@"osu.Game.Screens.Play.HUD.PerformancePointsCounter", @"osu.Game.Skinning.Triangles.TrianglesPerformancePointsCounter");
|
||||
|
||||
try
|
||||
{
|
||||
// First attempt to deserialise using the new SkinLayoutInfo format
|
||||
layoutInfo = JsonConvert.DeserializeObject<SkinLayoutInfo>(jsonContent);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
// Of note, the migration code below runs on read of skins, but there's nothing to
|
||||
// force a rewrite after migration. Let's not remove these migration rules until we
|
||||
// have something in place to ensure we don't end up breaking skins of users that haven't
|
||||
// manually saved their skin since a change was implemented.
|
||||
|
||||
// If deserialisation using SkinLayoutInfo fails, attempt to deserialise using the old naked list.
|
||||
var layoutInfo = parseLayoutInfo(jsonContent, skinnableTarget);
|
||||
if (layoutInfo == null)
|
||||
{
|
||||
var deserializedContent = JsonConvert.DeserializeObject<IEnumerable<SerialisedDrawableInfo>>(jsonContent);
|
||||
|
||||
if (deserializedContent == null)
|
||||
continue;
|
||||
|
||||
layoutInfo = new SkinLayoutInfo();
|
||||
layoutInfo.Update(null, deserializedContent.ToArray());
|
||||
|
||||
Logger.Log($"Ferrying {deserializedContent.Count()} components in {skinnableTarget} to global section of new {nameof(SkinLayoutInfo)} format");
|
||||
}
|
||||
continue;
|
||||
|
||||
LayoutInfos[skinnableTarget] = layoutInfo;
|
||||
}
|
||||
@ -230,6 +205,81 @@ namespace osu.Game.Skinning
|
||||
return null;
|
||||
}
|
||||
|
||||
#region Deserialisation & Migration
|
||||
|
||||
private SkinLayoutInfo? parseLayoutInfo(string jsonContent, SkinComponentsContainerLookup.TargetArea target)
|
||||
{
|
||||
SkinLayoutInfo? layout = null;
|
||||
|
||||
// handle namespace changes...
|
||||
jsonContent = jsonContent.Replace(@"osu.Game.Screens.Play.SongProgress", @"osu.Game.Screens.Play.HUD.DefaultSongProgress");
|
||||
jsonContent = jsonContent.Replace(@"osu.Game.Screens.Play.HUD.LegacyComboCounter", @"osu.Game.Skinning.LegacyComboCounter");
|
||||
jsonContent = jsonContent.Replace(@"osu.Game.Screens.Play.HUD.PerformancePointsCounter", @"osu.Game.Skinning.Triangles.TrianglesPerformancePointsCounter");
|
||||
|
||||
try
|
||||
{
|
||||
// First attempt to deserialise using the new SkinLayoutInfo format
|
||||
layout = JsonConvert.DeserializeObject<SkinLayoutInfo>(jsonContent);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
// If deserialisation using SkinLayoutInfo fails, attempt to deserialise using the old naked list.
|
||||
if (layout == null)
|
||||
{
|
||||
var deserializedContent = JsonConvert.DeserializeObject<IEnumerable<SerialisedDrawableInfo>>(jsonContent);
|
||||
if (deserializedContent == null)
|
||||
return null;
|
||||
|
||||
layout = new SkinLayoutInfo { Version = 0 };
|
||||
layout.Update(null, deserializedContent.ToArray());
|
||||
|
||||
Logger.Log($"Ferrying {deserializedContent.Count()} components in {target} to global section of new {nameof(SkinLayoutInfo)} format");
|
||||
}
|
||||
|
||||
for (int i = layout.Version + 1; i <= SkinLayoutInfo.LATEST_VERSION; i++)
|
||||
applyMigration(layout, target, i);
|
||||
|
||||
layout.Version = SkinLayoutInfo.LATEST_VERSION;
|
||||
return layout;
|
||||
}
|
||||
|
||||
private void applyMigration(SkinLayoutInfo layout, SkinComponentsContainerLookup.TargetArea target, int version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
if (target != SkinComponentsContainerLookup.TargetArea.MainHUDComponents ||
|
||||
!layout.TryGetDrawableInfo(null, out var globalHUDComponents) ||
|
||||
resources == null)
|
||||
break;
|
||||
|
||||
var comboCounters = globalHUDComponents.Where(c =>
|
||||
c.Type.Name == nameof(LegacyComboCounter) ||
|
||||
c.Type.Name == nameof(DefaultComboCounter) ||
|
||||
c.Type.Name == nameof(ArgonComboCounter)).ToArray();
|
||||
|
||||
layout.Update(null, globalHUDComponents.Except(comboCounters).ToArray());
|
||||
|
||||
resources.RealmAccess.Run(r =>
|
||||
{
|
||||
foreach (var ruleset in r.All<RulesetInfo>())
|
||||
{
|
||||
layout.Update(ruleset, layout.TryGetDrawableInfo(ruleset, out var rulesetHUDComponents)
|
||||
? rulesetHUDComponents.Concat(comboCounters).ToArray()
|
||||
: comboCounters);
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Disposal
|
||||
|
||||
~Skin()
|
||||
|
@ -223,8 +223,6 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
}
|
||||
|
||||
s.LayoutVersion = SkinInfo.LATEST_LAYOUT_VERSION;
|
||||
|
||||
string newHash = ComputeHash(s);
|
||||
|
||||
hadChanges = newHash != s.Hash;
|
||||
|
@ -39,21 +39,6 @@ namespace osu.Game.Skinning
|
||||
|
||||
public bool Protected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The latest version in YYYYMMDD format for skin layout migrations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>20240625: Moves combo counters from ruleset-agnostic to ruleset-specific HUD targets.</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public const int LATEST_LAYOUT_VERSION = 20240625;
|
||||
|
||||
/// <summary>
|
||||
/// A version in YYYYMMDD format for applying skin layout migrations.
|
||||
/// </summary>
|
||||
public int LayoutVersion { get; set; }
|
||||
|
||||
public virtual Skin CreateInstance(IStorageResourceProvider resources)
|
||||
{
|
||||
var type = string.IsNullOrEmpty(InstantiationInfo)
|
||||
|
@ -19,12 +19,26 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
private const string global_identifier = @"global";
|
||||
|
||||
[JsonIgnore]
|
||||
public IEnumerable<SerialisedDrawableInfo> AllDrawables => DrawableInfo.Values.SelectMany(v => v);
|
||||
/// <summary>
|
||||
/// Latest version representing the schema of the skin layout.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>0: Initial version of all skin layouts.</description></item>
|
||||
/// <item><description>1: Moves existing combo counters from global to per-ruleset HUD targets.</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public const int LATEST_VERSION = 1;
|
||||
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||
public int Version = LATEST_VERSION;
|
||||
|
||||
[JsonProperty]
|
||||
public Dictionary<string, SerialisedDrawableInfo[]> DrawableInfo { get; set; } = new Dictionary<string, SerialisedDrawableInfo[]>();
|
||||
|
||||
[JsonIgnore]
|
||||
public IEnumerable<SerialisedDrawableInfo> AllDrawables => DrawableInfo.Values.SelectMany(v => v);
|
||||
|
||||
public bool TryGetDrawableInfo(RulesetInfo? ruleset, [NotNullWhen(true)] out SerialisedDrawableInfo[]? components) =>
|
||||
DrawableInfo.TryGetValue(ruleset?.ShortName ?? global_identifier, out components);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user