From ca587ebb18fbdd5fb25c1d8877d3c04ffa0de506 Mon Sep 17 00:00:00 2001 From: Az <56699741+AlchlcDvl@users.noreply.github.com> Date: Thu, 15 Oct 2026 23:21:09 -0400 Subject: [PATCH] optimise code + add new example option --- MiraAPI.Example/Options/ExampleOptions.cs | 3 ++ MiraAPI.Example/Resources/Locale/en_US.xml | 1 + .../Attributes/ModdedPlayerOptionAttribute.cs | 2 +- .../ModdedPlayerOptionListAttribute.cs | 2 +- MiraAPI/GameOptions/ModdedOptionsManager.cs | 39 +++++++++++++------ MiraAPI/PluginLoading/MiraPluginManager.cs | 5 ++- 6 files changed, 37 insertions(+), 15 deletions(-) diff --git a/MiraAPI.Example/Options/ExampleOptions.cs b/MiraAPI.Example/Options/ExampleOptions.cs index c60038c..6f33856 100644 --- a/MiraAPI.Example/Options/ExampleOptions.cs +++ b/MiraAPI.Example/Options/ExampleOptions.cs @@ -45,6 +45,9 @@ public class ExampleOptions : AbstractOptionGroup public List ValueStates { get; set; } = [ValueState.Primary, ValueState.Secondary, ValueState.None, ValueState.None, ValueState.None]; + [ModdedPlayerOption("options.exampleOptions1.players")] + public NetworkedPlayerInfo PlayerSelected { get; set; } + public bool ToggleOpt1Visibility() { return true; diff --git a/MiraAPI.Example/Resources/Locale/en_US.xml b/MiraAPI.Example/Resources/Locale/en_US.xml index e446b69..b37cad6 100644 --- a/MiraAPI.Example/Resources/Locale/en_US.xml +++ b/MiraAPI.Example/Resources/Locale/en_US.xml @@ -48,6 +48,7 @@ Eliminate every other faction. Number Opt Best API Worst API + Choose A Player Example Options 2 YOU MUST CHOSE A PLAYER Eh whatever diff --git a/MiraAPI/GameOptions/Attributes/ModdedPlayerOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedPlayerOptionAttribute.cs index 086df17..610d0c8 100644 --- a/MiraAPI/GameOptions/Attributes/ModdedPlayerOptionAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/ModdedPlayerOptionAttribute.cs @@ -41,7 +41,7 @@ public class ModdedPlayerOptionAttribute( var final = value switch { - NetworkedPlayerInfo info => opt.Values.IndexOf(info), + NetworkedPlayerInfo info => info ? opt.Values.IndexOf(info) : (allowNone ? -1 : 0), _ => (int)value, }; opt?.SetValue(final); diff --git a/MiraAPI/GameOptions/Attributes/OptionList/ModdedPlayerOptionListAttribute.cs b/MiraAPI/GameOptions/Attributes/OptionList/ModdedPlayerOptionListAttribute.cs index c8e802f..533492b 100644 --- a/MiraAPI/GameOptions/Attributes/OptionList/ModdedPlayerOptionListAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/OptionList/ModdedPlayerOptionListAttribute.cs @@ -32,7 +32,7 @@ public class ModdedPlayerOptionListAttribute( var final = value switch { - NetworkedPlayerInfo info => opt.Values.IndexOf(info), + NetworkedPlayerInfo info => info ? opt.Values.IndexOf(info) : (allowNone ? -1 : 0), _ => (int)value, }; opt?.SetValue(final); diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs index 4cc8b03..21c9f1a 100644 --- a/MiraAPI/GameOptions/ModdedOptionsManager.cs +++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using BepInEx.Configuration; using HarmonyLib; using MiraAPI.GameModes; @@ -24,7 +25,10 @@ namespace MiraAPI.GameOptions; /// public static class ModdedOptionsManager { + private static readonly ConditionalWeakTable ListInstanceCache = []; private static readonly Dictionary OptionAttributes = []; + private static readonly Dictionary GetterLookups = []; + private static readonly Dictionary SetterLookups = []; private static readonly Dictionary TypeToGroup = []; private static readonly HashSet PatchedListTypes = []; @@ -168,12 +172,16 @@ public static class ModdedOptionsManager var getterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertyGetterPatch)); PluginSingleton.Instance.Harmony.Patch(getterOriginal, prefix: new HarmonyMethod(getterPatch)); + GetterLookups[getterOriginal!] = attribute; + var setterOriginal = property.GetSetMethod(true); if (setterOriginal != null) { var setterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertySetterPatch)); PluginSingleton.Instance.Harmony.Patch(setterOriginal, postfix: new HarmonyMethod(setterPatch)); + + SetterLookups[setterOriginal] = attribute; } OptionAttributes.Add(property, attribute); @@ -278,12 +286,16 @@ public static class ModdedOptionsManager var getterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertyGetterPatch)); PluginSingleton.Instance.Harmony.Patch(getterOriginal, prefix: new HarmonyMethod(getterPatch)); + GetterLookups[getterOriginal!] = attribute; + var setterOriginal = property.GetSetMethod(true); if (setterOriginal != null) { var setterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertySetterPatch)); PluginSingleton.Instance.Harmony.Patch(setterOriginal, postfix: new HarmonyMethod(setterPatch)); + + SetterLookups[setterOriginal] = attribute; } var listType = propertyList.GetType(); @@ -307,9 +319,11 @@ public static class ModdedOptionsManager OptionAttributes.Add(property, attribute); attribute.HolderOptionList = optionList; - attribute.Value = (IList)propertyVal; + attribute.Value = propertyList; attribute.ValueType = listType.GetGenericArguments()[0]; + ListInstanceCache.Add(propertyVal, attribute); + var valueType = property.PropertyType.GetGenericArguments()[0]; var visibilityAttr = property.GetCustomAttribute(); @@ -422,8 +436,13 @@ public static class ModdedOptionsManager [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Read above.")] public static void PropertySetterPatch(MethodBase __originalMethod, object value) { - var attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod() == __originalMethod).Value; + if (!SetterLookups.TryGetValue(__originalMethod, out var attribute)) + return; + attribute.SetValue(value); + + if (attribute is ModdedOptionListAttribute listAttribute && value != null) + ListInstanceCache.AddOrUpdate(value, listAttribute); } /// @@ -436,7 +455,9 @@ public static class ModdedOptionsManager [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Read above.")] public static bool PropertyGetterPatch(MethodBase __originalMethod, ref object __result) { - var attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod() == __originalMethod).Value; + if (!GetterLookups.TryGetValue(__originalMethod, out var attribute)) + return true; + __result = attribute.GetValue(); return false; } @@ -451,10 +472,8 @@ public static class ModdedOptionsManager [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Read above.")] public static void PropertyListSetterPatch(object __instance, int index, object value) { - var match = OptionAttributes.Values - .OfType() - .FirstOrDefault(list => ReferenceEquals(list.Value, __instance)); - match?.SetValue(index, value); + if (ListInstanceCache.TryGetValue(__instance, out var match)) + match.SetValue(index, value); } /// @@ -468,11 +487,7 @@ public static class ModdedOptionsManager [SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Read above.")] public static bool PropertyListGetterPatch(object __instance, int index, ref object __result) { - var match = OptionAttributes.Values - .OfType() - .FirstOrDefault(list => ReferenceEquals(list.Value, __instance)); - - if (match is null) + if (!ListInstanceCache.TryGetValue(__instance, out var match)) return true; __result = match.GetValue(index); diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index 5ce5636..f021c3f 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -313,7 +313,10 @@ public sealed class MiraPluginManager foreach (var property in type.GetProperties()) { - if (property.GetMethod?.IsStatic == true && property.GetCustomAttribute() == null) + if (property.GetCustomAttribute() != null) + continue; + + if (property.GetMethod?.IsStatic == true) { Error($"Non-ignored option property {property.Name} in {type.Name} must not be static."); continue;