Skip to content

Commit

Permalink
Fix similar Bindable-related crashes
Browse files Browse the repository at this point in the history
  • Loading branch information
smoogipoo committed Sep 25, 2024
1 parent 3ab04d9 commit 59e0707
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 7 deletions.
7 changes: 2 additions & 5 deletions osu.Game/Configuration/SettingSourceAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
Expand All @@ -15,6 +14,7 @@
using osu.Framework.Localisation;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Settings;
using osu.Game.Utils;

namespace osu.Game.Configuration
{
Expand Down Expand Up @@ -228,10 +228,7 @@ public static object GetUnderlyingSettingValue(this object setting)
return b.Value;

case IBindable u:
// An unknown (e.g. enum) generic type.
var valueMethod = u.GetType().GetProperty(nameof(IBindable<int>.Value));
Debug.Assert(valueMethod != null);
return valueMethod.GetValue(u)!;
return BindableValueAccessor.GetValue(u);

default:
// fall back for non-bindable cases.
Expand Down
3 changes: 1 addition & 2 deletions osu.Game/Rulesets/Mods/Mod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,7 @@ public void CopyCommonSettingsFrom(Mod source)

// TODO: special case for handling number types

PropertyInfo property = targetSetting.GetType().GetProperty(nameof(Bindable<bool>.Value))!;
property.SetValue(targetSetting, property.GetValue(sourceSetting));
BindableValueAccessor.SetValue(targetSetting, BindableValueAccessor.GetValue(sourceSetting));
}
}

Expand Down
40 changes: 40 additions & 0 deletions osu.Game/Utils/BindableValueAccessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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.Linq;
using System.Reflection;
using osu.Framework.Bindables;
using osu.Framework.Extensions.TypeExtensions;

namespace osu.Game.Utils
{
internal static class BindableValueAccessor
{
public static object GetValue(IBindable bindable)
{
Type? bindableWithValueType = bindable.GetType().GetInterfaces().FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IBindable<>));
if (bindableWithValueType == null)
return bindable;

return typeof(BindableValueAccessor).GetMethod(nameof(getValue), BindingFlags.Static | BindingFlags.NonPublic)!
.MakeGenericMethod(bindableWithValueType.GenericTypeArguments[0])
.Invoke(null, [bindable])!;
}

public static void SetValue(IBindable bindable, object value)
{
Type? bindableWithValueType = bindable.GetType().EnumerateBaseTypes().FirstOrDefault(t => t == typeof(Bindable<>));
if (bindableWithValueType == null)
return;

typeof(BindableValueAccessor).GetMethod(nameof(setValue), BindingFlags.Static | BindingFlags.NonPublic)!
.MakeGenericMethod(bindableWithValueType.GenericTypeArguments[0])
.Invoke(null, [bindable, value]);
}

private static object getValue<T>(object bindable) => ((IBindable<T>)bindable).Value!;

private static object setValue<T>(object bindable, object value) => ((Bindable<T>)bindable).Value = (T)value;
}
}

0 comments on commit 59e0707

Please sign in to comment.