From 3e020073c50d325f9c19ded1515d0653e19f5fb6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Mar 2022 00:08:01 +0900 Subject: [PATCH 1/5] Convert `Skin` to use `nullable` --- osu.Game/Skinning/ISkin.cs | 18 +++++++----------- osu.Game/Skinning/Skin.cs | 36 +++++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index 73f7cf6d39..4b14dcfd62 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -1,7 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using JetBrains.Annotations; +#nullable enable + using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -21,16 +22,14 @@ namespace osu.Game.Skinning /// /// The requested component. /// A drawable representation for the requested component, or null if unavailable. - [CanBeNull] - Drawable GetDrawableComponent(ISkinComponent component); + Drawable? GetDrawableComponent(ISkinComponent component); /// /// Retrieve a . /// /// The requested texture. /// A matching texture, or null if unavailable. - [CanBeNull] - Texture GetTexture(string componentName) => GetTexture(componentName, default, default); + Texture? GetTexture(string componentName) => GetTexture(componentName, default, default); /// /// Retrieve a . @@ -39,23 +38,20 @@ namespace osu.Game.Skinning /// The texture wrap mode in horizontal direction. /// The texture wrap mode in vertical direction. /// A matching texture, or null if unavailable. - [CanBeNull] - Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT); + Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT); /// /// Retrieve a . /// /// The requested sample. /// A matching sample channel, or null if unavailable. - [CanBeNull] - ISample GetSample(ISampleInfo sampleInfo); + ISample? GetSample(ISampleInfo sampleInfo); /// /// Retrieve a configuration value. /// /// The requested configuration value. /// A matching value boxed in an , or null if unavailable. - [CanBeNull] - IBindable GetConfig(TLookup lookup); + IBindable? GetConfig(TLookup lookup); } } diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index b6f46a0d81..643e0c38d4 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -1,12 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; -using JetBrains.Annotations; using Newtonsoft.Json; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; @@ -27,14 +29,12 @@ namespace osu.Game.Skinning /// /// A texture store which can be used to perform user file loops for this skin. /// - [CanBeNull] - protected TextureStore Textures { get; } + protected TextureStore? Textures { get; } /// /// A sample store which can be used to perform user file loops for this skin. /// - [CanBeNull] - protected ISampleStore Samples { get; } + protected ISampleStore? Samples { get; } public readonly Live SkinInfo; @@ -44,13 +44,13 @@ namespace osu.Game.Skinning private readonly Dictionary drawableComponentInfo = new Dictionary(); - public abstract ISample GetSample(ISampleInfo sampleInfo); + public abstract ISample? GetSample(ISampleInfo sampleInfo); - public Texture GetTexture(string componentName) => GetTexture(componentName, default, default); + public Texture? GetTexture(string componentName) => GetTexture(componentName, default, default); - public abstract Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT); + public abstract Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT); - public abstract IBindable GetConfig(TLookup lookup); + public abstract IBindable? GetConfig(TLookup lookup); /// /// Construct a new skin. @@ -59,13 +59,11 @@ namespace osu.Game.Skinning /// Access to game-wide resources. /// An optional store which will be used for looking up skin resources. If null, one will be created from realm pattern. /// An optional filename to read the skin configuration from. If not provided, the configuration will be retrieved from the storage using "skin.ini". - protected Skin(SkinInfo skin, [CanBeNull] IStorageResourceProvider resources, [CanBeNull] IResourceStore storage = null, [CanBeNull] string configurationFilename = @"skin.ini") + protected Skin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? storage = null, string configurationFilename = @"skin.ini") { if (resources != null) { - SkinInfo = resources.RealmAccess != null - ? skin.ToLive(resources.RealmAccess) - : skin.ToLiveUnmanaged(); + SkinInfo = skin.ToLive(resources.RealmAccess); storage ??= new RealmBackedResourceStore(skin, resources.Files, new[] { @"ogg" }); @@ -76,12 +74,20 @@ namespace osu.Game.Skinning Samples = samples; Textures = new TextureStore(resources.CreateTextureLoaderStore(storage)); } + else + { + // Generally only used for tests. + SkinInfo = skin.ToLiveUnmanaged(); + } var configurationStream = storage?.GetStream(configurationFilename); if (configurationStream != null) + { // stream will be closed after use by LineBufferedReader. ParseConfigurationStream(configurationStream); + Debug.Assert(Configuration != null); + } else Configuration = new SkinConfiguration(); @@ -90,7 +96,7 @@ namespace osu.Game.Skinning { string filename = $"{skinnableTarget}.json"; - byte[] bytes = storage?.Get(filename); + byte[]? bytes = storage?.Get(filename); if (bytes == null) continue; @@ -136,7 +142,7 @@ namespace osu.Game.Skinning DrawableComponentInfo[targetContainer.Target] = targetContainer.CreateSkinnableInfo().ToArray(); } - public virtual Drawable GetDrawableComponent(ISkinComponent component) + public virtual Drawable? GetDrawableComponent(ISkinComponent component) { switch (component) { From 194bf4fb057e60eff9781c2d85896e3b79075202 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Mar 2022 00:21:19 +0900 Subject: [PATCH 2/5] Convert `LegacySkin` to use `nullable` --- osu.Game/Skinning/LegacySkin.cs | 35 ++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 1c2ca797c6..20cac52d21 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; using System.Diagnostics; @@ -57,7 +59,7 @@ namespace osu.Game.Skinning /// Access to raw game resources. /// An optional store which will be used for looking up skin resources. If null, one will be created from realm pattern. /// The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file. - protected LegacySkin(SkinInfo skin, [CanBeNull] IStorageResourceProvider resources, [CanBeNull] IResourceStore storage, string configurationFilename = @"skin.ini") + protected LegacySkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? storage, string configurationFilename = @"skin.ini") : base(skin, resources, storage, configurationFilename) { // todo: this shouldn't really be duplicated here (from ManiaLegacySkinTransformer). we need to come up with a better solution. @@ -81,7 +83,7 @@ namespace osu.Game.Skinning } } - public override IBindable GetConfig(TLookup lookup) + public override IBindable? GetConfig(TLookup lookup) { switch (lookup) { @@ -127,7 +129,7 @@ namespace osu.Game.Skinning return null; } - private IBindable lookupForMania(LegacyManiaSkinConfigurationLookup maniaLookup) + private IBindable? lookupForMania(LegacyManiaSkinConfigurationLookup maniaLookup) { if (!maniaConfigurations.TryGetValue(maniaLookup.Keys, out var existing)) maniaConfigurations[maniaLookup.Keys] = existing = new LegacyManiaSkinConfiguration(maniaLookup.Keys); @@ -267,20 +269,19 @@ namespace osu.Game.Skinning /// The source to retrieve the combo colours from. /// The preferred index for retrieving the combo colour with. /// Information on the combo whose using the returned colour. - protected virtual IBindable GetComboColour(IHasComboColours source, int colourIndex, IHasComboInformation combo) + protected virtual IBindable? GetComboColour(IHasComboColours source, int colourIndex, IHasComboInformation combo) { var colour = source.ComboColours?[colourIndex % source.ComboColours.Count]; return colour.HasValue ? new Bindable(colour.Value) : null; } - private IBindable getCustomColour(IHasCustomColours source, string lookup) + private IBindable? getCustomColour(IHasCustomColours source, string lookup) => source.CustomColours.TryGetValue(lookup, out var col) ? new Bindable(col) : null; - private IBindable getManiaImage(LegacyManiaSkinConfiguration source, string lookup) + private IBindable? getManiaImage(LegacyManiaSkinConfiguration source, string lookup) => source.ImageLookups.TryGetValue(lookup, out string image) ? new Bindable(image) : null; - [CanBeNull] - private IBindable legacySettingLookup(SkinConfiguration.LegacySetting legacySetting) + private IBindable? legacySettingLookup(SkinConfiguration.LegacySetting legacySetting) { switch (legacySetting) { @@ -292,9 +293,11 @@ namespace osu.Game.Skinning } } - [CanBeNull] - private IBindable genericLookup(TLookup lookup) + private IBindable? genericLookup(TLookup lookup) { + if (lookup == null) + return null; + try { if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out string val)) @@ -316,7 +319,7 @@ namespace osu.Game.Skinning return null; } - public override Drawable GetDrawableComponent(ISkinComponent component) + public override Drawable? GetDrawableComponent(ISkinComponent component) { if (base.GetDrawableComponent(component) is Drawable c) return c; @@ -385,7 +388,7 @@ namespace osu.Game.Skinning case GameplaySkinComponent resultComponent: // TODO: this should be inside the judgement pieces. - Func createDrawable = () => getJudgementAnimation(resultComponent.Component); + Func createDrawable = () => getJudgementAnimation(resultComponent.Component); // kind of wasteful that we throw this away, but should do for now. if (createDrawable() != null) @@ -404,7 +407,7 @@ namespace osu.Game.Skinning return this.GetAnimation(component.LookupName, false, false); } - private Texture getParticleTexture(HitResult result) + private Texture? getParticleTexture(HitResult result) { switch (result) { @@ -421,7 +424,7 @@ namespace osu.Game.Skinning return null; } - private Drawable getJudgementAnimation(HitResult result) + private Drawable? getJudgementAnimation(HitResult result) { switch (result) { @@ -441,7 +444,7 @@ namespace osu.Game.Skinning return null; } - public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) + public override Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) { foreach (string name in getFallbackNames(componentName)) { @@ -469,7 +472,7 @@ namespace osu.Game.Skinning return null; } - public override ISample GetSample(ISampleInfo sampleInfo) + public override ISample? GetSample(ISampleInfo sampleInfo) { IEnumerable lookupNames; From 7296bad294c93d7e088f52a103a0f395dae1581b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Mar 2022 00:24:06 +0900 Subject: [PATCH 3/5] Convert `LegacyBeatmapSkin` to use `nullable` --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index e3685c986b..16a05f4197 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using JetBrains.Annotations; +#nullable enable + using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.IO.Stores; using osu.Game.Audio; @@ -26,14 +28,14 @@ namespace osu.Game.Skinning /// /// The model for this beatmap. /// Access to raw game resources. - public LegacyBeatmapSkin(BeatmapInfo beatmapInfo, [CanBeNull] IStorageResourceProvider resources) - : base(createSkinInfo(beatmapInfo), resources, createRealmBackedStore(beatmapInfo, resources), beatmapInfo.Path) + public LegacyBeatmapSkin(BeatmapInfo beatmapInfo, IStorageResourceProvider? resources) + : base(createSkinInfo(beatmapInfo), resources, createRealmBackedStore(beatmapInfo, resources), beatmapInfo.Path.AsNonNull()) { // Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer) Configuration.AllowDefaultComboColoursFallback = false; } - private static IResourceStore createRealmBackedStore(BeatmapInfo beatmapInfo, [CanBeNull] IStorageResourceProvider resources) + private static IResourceStore createRealmBackedStore(BeatmapInfo beatmapInfo, IStorageResourceProvider? resources) { if (resources == null) // should only ever be used in tests. @@ -42,7 +44,7 @@ namespace osu.Game.Skinning return new RealmBackedResourceStore(beatmapInfo.BeatmapSet, resources.Files, new[] { @"ogg" }); } - public override Drawable GetDrawableComponent(ISkinComponent component) + public override Drawable? GetDrawableComponent(ISkinComponent component) { if (component is SkinnableTargetComponent targetComponent) { @@ -61,7 +63,7 @@ namespace osu.Game.Skinning return base.GetDrawableComponent(component); } - public override IBindable GetConfig(TLookup lookup) + public override IBindable? GetConfig(TLookup lookup) { switch (lookup) { @@ -77,10 +79,10 @@ namespace osu.Game.Skinning return base.GetConfig(lookup); } - protected override IBindable GetComboColour(IHasComboColours source, int comboIndex, IHasComboInformation combo) + protected override IBindable? GetComboColour(IHasComboColours source, int comboIndex, IHasComboInformation combo) => base.GetComboColour(source, combo.ComboIndexWithOffsets, combo); - public override ISample GetSample(ISampleInfo sampleInfo) + public override ISample? GetSample(ISampleInfo sampleInfo) { if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy && legacy.CustomSampleBank == 0) { From 23c4f9910e5bba1c0b556bb3838d9eea87cdd6dd Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 25 Mar 2022 15:53:55 +0900 Subject: [PATCH 4/5] Apply notnull constraint --- osu.Game/Skinning/ISkin.cs | 4 +++- osu.Game/Skinning/LegacySkin.cs | 6 +++--- osu.Game/Skinning/ResourceStoreBackedSkin.cs | 5 ++++- osu.Game/Skinning/Skin.cs | 4 +++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index 4b14dcfd62..414a316dec 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -52,6 +52,8 @@ namespace osu.Game.Skinning /// /// The requested configuration value. /// A matching value boxed in an , or null if unavailable. - IBindable? GetConfig(TLookup lookup); + IBindable? GetConfig(TLookup lookup) + where TLookup : notnull + where TValue : notnull; } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index a3c08f4ba2..92713023f4 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -282,6 +282,7 @@ namespace osu.Game.Skinning => source.ImageLookups.TryGetValue(lookup, out string image) ? new Bindable(image) : null; private IBindable? legacySettingLookup(SkinConfiguration.LegacySetting legacySetting) + where TValue : notnull { switch (legacySetting) { @@ -294,10 +295,9 @@ namespace osu.Game.Skinning } private IBindable? genericLookup(TLookup lookup) + where TLookup : notnull + where TValue : notnull { - if (lookup == null) - return null; - try { if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out string val)) diff --git a/osu.Game/Skinning/ResourceStoreBackedSkin.cs b/osu.Game/Skinning/ResourceStoreBackedSkin.cs index 4787b5a4e9..48286bff59 100644 --- a/osu.Game/Skinning/ResourceStoreBackedSkin.cs +++ b/osu.Game/Skinning/ResourceStoreBackedSkin.cs @@ -46,7 +46,10 @@ namespace osu.Game.Skinning return null; } - public IBindable? GetConfig(TLookup lookup) => null; + public IBindable? GetConfig(TLookup lookup) + where TLookup : notnull + where TValue : notnull + => null; public void Dispose() { diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 2766e58c96..324ed90a67 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -50,7 +50,9 @@ namespace osu.Game.Skinning public abstract Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT); - public abstract IBindable? GetConfig(TLookup lookup); + public abstract IBindable? GetConfig(TLookup lookup) + where TLookup : notnull + where TValue : notnull; /// /// Construct a new skin. From 6b22e5774f79d7e7b19e0ff24aee61b77ddb8b9e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Mar 2022 16:42:35 +0900 Subject: [PATCH 5/5] Remove conditional access on known non-null --- osu.Game/Tests/Visual/SkinnableTestScene.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index b4da91a97a..2e1ca09fe4 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual }, new OsuSpriteText { - Text = skin?.SkinInfo?.Value.Name ?? "none", + Text = skin?.SkinInfo.Value.Name ?? "none", Scale = new Vector2(1.5f), Padding = new MarginPadding(5), },