diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index 5db502804d..4111a67b24 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; using JetBrains.Annotations; @@ -170,6 +171,39 @@ namespace osu.Game.Configuration private static readonly ConcurrentDictionary property_info_cache = new ConcurrentDictionary(); + /// + /// Returns the underlying value of the given mod setting object. + /// Can be used for serialization and equality comparison purposes. + /// + /// A bindable. + public static object GetUnderlyingSettingValue(this object setting) + { + switch (setting) + { + case Bindable d: + return d.Value; + + case Bindable i: + return i.Value; + + case Bindable f: + return f.Value; + + case Bindable b: + return b.Value; + + case IBindable u: + // An unknown (e.g. enum) generic type. + var valueMethod = u.GetType().GetProperty(nameof(IBindable.Value)); + Debug.Assert(valueMethod != null); + return valueMethod.GetValue(u); + + default: + // fall back for non-bindable cases. + return setting; + } + } + public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourceProperties(this object obj) { var type = obj.GetType(); diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 44b1c460d5..524f7b7108 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -12,7 +12,6 @@ using osu.Framework.Logging; using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Utils; namespace osu.Game.Online.API { @@ -43,7 +42,7 @@ namespace osu.Game.Online.API var bindable = (IBindable)property.GetValue(mod); if (!bindable.IsDefault) - Settings.Add(property.Name.Underscore(), ModUtils.GetSettingUnderlyingValue(bindable)); + Settings.Add(property.Name.Underscore(), bindable.GetUnderlyingSettingValue()); } } @@ -93,13 +92,13 @@ namespace osu.Game.Online.API public bool Equals(KeyValuePair x, KeyValuePair y) { - object xValue = ModUtils.GetSettingUnderlyingValue(x.Value); - object yValue = ModUtils.GetSettingUnderlyingValue(y.Value); + object xValue = x.Value.GetUnderlyingSettingValue(); + object yValue = y.Value.GetUnderlyingSettingValue(); return x.Key == y.Key && EqualityComparer.Default.Equals(xValue, yValue); } - public int GetHashCode(KeyValuePair obj) => HashCode.Combine(obj.Key, ModUtils.GetSettingUnderlyingValue(obj.Value)); + public int GetHashCode(KeyValuePair obj) => HashCode.Combine(obj.Key, obj.Value.GetUnderlyingSettingValue()); } } } diff --git a/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs b/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs index 81ecc74ddb..a7c63c17f9 100644 --- a/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs +++ b/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Text; using MessagePack; using MessagePack.Formatters; -using osu.Game.Utils; +using osu.Game.Configuration; namespace osu.Game.Online.API { @@ -23,7 +23,7 @@ namespace osu.Game.Online.API var stringBytes = new ReadOnlySequence(Encoding.UTF8.GetBytes(kvp.Key)); writer.WriteString(in stringBytes); - primitiveFormatter.Serialize(ref writer, ModUtils.GetSettingUnderlyingValue(kvp.Value), options); + primitiveFormatter.Serialize(ref writer, kvp.Value.GetUnderlyingSettingValue(), options); } } diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 7136795461..b2d4be54ce 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Mods hashCode.Add(GetType()); foreach (var setting in Settings) - hashCode.Add(ModUtils.GetSettingUnderlyingValue(setting)); + hashCode.Add(setting.GetUnderlyingSettingValue()); return hashCode.ToHashCode(); } @@ -208,13 +208,13 @@ namespace osu.Game.Rulesets.Mods public bool Equals(IBindable x, IBindable y) { - object xValue = x == null ? null : ModUtils.GetSettingUnderlyingValue(x); - object yValue = y == null ? null : ModUtils.GetSettingUnderlyingValue(y); + object xValue = x?.GetUnderlyingSettingValue(); + object yValue = y?.GetUnderlyingSettingValue(); return EqualityComparer.Default.Equals(xValue, yValue); } - public int GetHashCode(IBindable obj) => ModUtils.GetSettingUnderlyingValue(obj).GetHashCode(); + public int GetHashCode(IBindable obj) => obj.GetUnderlyingSettingValue().GetHashCode(); } } } diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs index 5a51e7f455..95395f8181 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics.Containers; using osu.Game.Configuration; using osu.Game.Extensions; using osu.Game.Skinning; -using osu.Game.Utils; using osuTK; namespace osu.Game.Screens.Play.HUD @@ -69,7 +68,7 @@ namespace osu.Game.Screens.Play.HUD var bindable = (IBindable)property.GetValue(component); if (!bindable.IsDefault) - Settings.Add(property.Name.Underscore(), ModUtils.GetSettingUnderlyingValue(bindable)); + Settings.Add(property.Name.Underscore(), bindable.GetUnderlyingSettingValue()); } if (component is Container container) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index cdca277dd8..d5ea74c404 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -1,18 +1,16 @@ // 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.Diagnostics.CodeAnalysis; using System.Linq; -using osu.Framework.Bindables; using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -#nullable enable - namespace osu.Game.Utils { /// @@ -154,39 +152,6 @@ namespace osu.Game.Utils yield return mod; } - /// - /// Returns the underlying value of the given mod setting object. - /// Used in for serialization and equality comparison purposes. - /// - /// The mod setting. - public static object GetSettingUnderlyingValue(object setting) - { - switch (setting) - { - case Bindable d: - return d.Value; - - case Bindable i: - return i.Value; - - case Bindable f: - return f.Value; - - case Bindable b: - return b.Value; - - case IBindable u: - // A mod with unknown (e.g. enum) generic type. - var valueMethod = u.GetType().GetProperty(nameof(IBindable.Value)); - Debug.Assert(valueMethod != null); - return valueMethod.GetValue(u); - - default: - // fall back for non-bindable cases. - return setting; - } - } - /// /// Verifies all proposed mods are valid for a given ruleset and returns instantiated s for further processing. ///