Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Screw you I WIN..... #510

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions Example mod/ConfigExamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public class Config : ConfigFile
/// <para>Here, we are specifying the name of a method which can handle any OnChange event, for the purposes of demonstrating
/// its usage. See <see cref="MyGenericValueChangedEvent(ModOptionEventArgs)"/> for an example usage.</para>
/// </summary>
[Choice("My index-based choice", "One", "Two", "Three"), OnChange(nameof(MyChoiceValueChangedEvent))]
[Choice("My index-based choice", "1", "2", "3"), OnChange(nameof(MyChoiceValueChangedEvent))]
public int ChoiceIndex;

/// <summary>
Expand All @@ -182,7 +182,7 @@ public class Config : ConfigFile
/// <para>An option of <see cref="CustomChoice.One"/> will be represented by the <see cref="string"/> "1",
/// <see cref="CustomChoice.Two"/> will be represented by the <see cref="string"/> "2", and so on.</para>
/// </summary>
[Choice("My customised enum-based choice", "1", "2", "3"), OnChange(nameof(MyChoiceValueChangedEvent))]
[Choice("My customised enum-based choice", "One", "Three"), OnChange(nameof(MyChoiceValueChangedEvent))]
public CustomChoice ChoiceCustomEnum;

/// <summary>
Expand Down Expand Up @@ -273,10 +273,15 @@ private void MyToggleValueChangedEvent(ToggleChangedEventArgs e)

// On change events for different types of options:

private void MyChoiceValueChangedEvent<T>(ChoiceChangedEventArgs<T> e)
private void MyChoiceValueChangedEvent<T>(object sender, ChoiceChangedEventArgs<T> e)
{
T choice = e.Value;
ConfigExamples.LogSource.LogInfo($"Choice value {e.Id} was changed: {choice}");
if (e != null)
{
ConfigExamples.LogSource.LogInfo($"Choice value {e.Value} was changed by {sender}");
return;
}

ConfigExamples.LogSource.LogInfo($"Choice value was changed by {sender} but event was null!");
}

private void MyKeybindValueChangedEvent(KeybindChangedEventArgs e)
Expand Down
154 changes: 106 additions & 48 deletions Nautilus/Options/Attributes/ConfigFileMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using HarmonyLib;
using Nautilus.Json;
using Nautilus.Utility;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TMPro;
using UnityEngine;

Expand Down Expand Up @@ -222,7 +224,7 @@ private void addModOptionMetadata<TAttribute>(MemberInfo memberInfo, MemberType
Name = memberInfo.Name,
ValueType = underlyingType
},
OnGameObjectCreatedMetadata = GetEventMetadata<OnGameObjectCreatedAttribute>(memberInfo)
OnGameObjectCreatedMetadata = GetEventMetadata<OnGameObjectCreatedAttribute>(memberInfo, underlyingType)
};

if (memberType == MemberType.Method)
Expand All @@ -232,7 +234,7 @@ private void addModOptionMetadata<TAttribute>(MemberInfo memberInfo, MemberType

if (typeof(TAttribute) != typeof(ButtonAttribute))
{
modOptionMetadata.OnChangeMetadata = GetEventMetadata<OnChangeAttribute>(memberInfo);
modOptionMetadata.OnChangeMetadata = GetEventMetadata<OnChangeAttribute>(memberInfo, underlyingType);
}

ModOptionAttributesMetadata.Add(modOptionAttribute.Id, modOptionMetadata);
Expand All @@ -250,8 +252,9 @@ private void addModOptionMetadata<TAttribute>(MemberInfo memberInfo, MemberType
/// The type of <see cref="ModOptionEventAttribute"/> attribute defined on the member to gather metadata for.
/// </typeparam>
/// <param name="memberInfo">The member to gather attribute metadata for.</param>
/// <param name="underlyingType">The type of the option value.</param>
/// <returns></returns>
private IEnumerable<MemberInfoMetadata<T>> GetEventMetadata<TAttribute>(MemberInfo memberInfo)
private IEnumerable<MemberInfoMetadata<T>> GetEventMetadata<TAttribute>(MemberInfo memberInfo, Type underlyingType)
where TAttribute : ModOptionEventAttribute
{
List<MemberInfoMetadata<T>> metadatas = new();
Expand All @@ -260,7 +263,8 @@ private IEnumerable<MemberInfoMetadata<T>> GetEventMetadata<TAttribute>(MemberIn
MemberInfoMetadata<T> methodMetadata = new()
{
MemberType = MemberType.Method,
Name = attribute.MethodName
Name = attribute.MethodName,
ValueType = underlyingType
};
methodMetadata.ParseMethodParameterTypes();
metadatas.Add(methodMetadata);
Expand Down Expand Up @@ -568,66 +572,102 @@ private void InvokeOnChangeEvents(ModOptionAttributeMetadata<T> modOptionMetadat
case ChoiceAttribute choiceAttribute when memberInfoMetadata.ValueType.IsEnum &&
(choiceAttribute.Options == null || !choiceAttribute.Options.Any()):
// Enum-based choice where the values are parsed from the enum type
{
string[] options = Enum.GetNames(memberInfoMetadata.ValueType);
var value = (T)memberInfoMetadata.GetValue(Config);
ChoiceChangedEventArgs<T> eventArgs = new(id, Array.IndexOf(options, value), value);
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
{
try
{
string[] options = Enum.GetNames(memberInfoMetadata.ValueType);
var value = (T) memberInfoMetadata.GetValue(Config);
var eventArgsConstructor = typeof(ChoiceChangedEventArgs<>).MakeGenericType(memberInfoMetadata.ValueType);
var eventArgs = Activator.CreateInstance(eventArgsConstructor, id, Array.IndexOf(options, value), value);
AccessTools.Method(this.GetType(), nameof(InvokeOnChangeEvents))
.MakeGenericMethod(eventArgsConstructor)
.Invoke(this, new object[] { modOptionMetadata, sender, eventArgs });
}
catch (Exception ex)
{
InternalLogger.Error($"[OptionsMenuBuilder] {ex.Message}");
}
}
break;
case ChoiceAttribute _ when memberInfoMetadata.ValueType.IsEnum:
case ChoiceAttribute choiceAttribute when memberInfoMetadata.ValueType.IsEnum:
// Enum-based choice where the values are defined as custom strings
{
string value = memberInfoMetadata.GetValue(Config).ToString();
int index = Math.Max(Array.IndexOf(Enum.GetValues(memberInfoMetadata.ValueType), value), 0);
ChoiceChangedEventArgs<string> eventArgs = new(id, index, value);
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
{
try
{
string value = memberInfoMetadata.GetValue(Config).ToString();
int index = Math.Max(Array.IndexOf(Enum.GetValues(memberInfoMetadata.ValueType), value), 0);
var eventArgsConstructor = typeof(ChoiceChangedEventArgs<>).MakeGenericType(memberInfoMetadata.ValueType);
var eventArgs = Activator.CreateInstance(eventArgsConstructor, id, Array.IndexOf(choiceAttribute.Options, value), value);
AccessTools.Method(this.GetType(), nameof(InvokeOnChangeEvents))
.MakeGenericMethod(eventArgsConstructor)
.Invoke(this, new object[] { modOptionMetadata, sender, eventArgs });
}
catch(Exception ex)
{
InternalLogger.Error($"[OptionsMenuBuilder] {ex.Message}");
}
}
break;
case ChoiceAttribute choiceAttribute when memberInfoMetadata.ValueType == typeof(string):
// string-based choice value
{
string[] options = choiceAttribute.Options;
string value = memberInfoMetadata.GetValue<string>(Config);
ChoiceChangedEventArgs<string> eventArgs = new(id, Array.IndexOf(options, value), value);
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
{
string[] options = choiceAttribute.Options;
string value = memberInfoMetadata.GetValue<string>(Config);
ChoiceChangedEventArgs<string> eventArgs = new(id, Array.IndexOf(options, value), value);
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
break;
case ChoiceAttribute choiceAttribute when memberInfoMetadata.ValueType == typeof(int):
// index-based choice value
{
string[] options = choiceAttribute.Options;
int index = memberInfoMetadata.GetValue<int>(Config);
ChoiceChangedEventArgs<string> eventArgs = new(id, index, options[index]);
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
{
int[] options = new int[choiceAttribute.Options.Length];
for (int i = 0; i < choiceAttribute.Options.Length; i++)
{
options[i] = int.Parse(choiceAttribute.Options[i]);
}
int index = memberInfoMetadata.GetValue<int>(Config);
ChoiceChangedEventArgs<int> eventArgs = new(id, index, options[index]);
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
break;
case ChoiceAttribute choiceAttribute:
{
string[] options = choiceAttribute.Options;
var value = (T) memberInfoMetadata.GetValue(Config);
int index = memberInfoMetadata.GetValue<int>(Config);
var eventArgsConstructor = typeof(ChoiceChangedEventArgs<>).MakeGenericType(memberInfoMetadata.ValueType);
var eventArgs = Activator.CreateInstance(eventArgsConstructor, id, Array.IndexOf(options, value), value);
AccessTools.Method(this.GetType(), nameof(InvokeOnChangeEvents))
.MakeGenericMethod(eventArgsConstructor)
.Invoke(this, new object[] { modOptionMetadata, sender, eventArgs });
}
break;
case ColorPickerAttribute _:
{
ColorChangedEventArgs eventArgs = new(id, memberInfoMetadata.GetValue<Color>(Config));
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
{
ColorChangedEventArgs eventArgs = new(id, memberInfoMetadata.GetValue<Color>(Config));
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
break;

case KeybindAttribute _:
{
KeybindChangedEventArgs eventArgs = new(id, memberInfoMetadata.GetValue<KeyCode>(Config));
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
{
KeybindChangedEventArgs eventArgs = new(id, memberInfoMetadata.GetValue<KeyCode>(Config));
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
break;

case SliderAttribute _:
{
SliderChangedEventArgs eventArgs = new(id, Convert.ToSingle(memberInfoMetadata.GetValue(Config)));
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
{
SliderChangedEventArgs eventArgs = new(id, Convert.ToSingle(memberInfoMetadata.GetValue(Config)));
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
break;

case ToggleAttribute _:
{
ToggleChangedEventArgs eventArgs = new(id, memberInfoMetadata.GetValue<bool>(Config));
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
{
ToggleChangedEventArgs eventArgs = new(id, memberInfoMetadata.GetValue<bool>(Config));
InvokeOnChangeEvents(modOptionMetadata, sender, eventArgs);
}
break;
}
}
Expand Down Expand Up @@ -681,21 +721,39 @@ private void InvokeEvent<TSource>(MemberInfoMetadata<T> memberInfoMetadata, obje

for (int i = 0; i < parameterTypes.Length; i++)
{
if (!senderFound && parameterTypes[i] == typeof(object))
var type = parameterTypes[i];

if (!senderFound && type == typeof(object))
{
senderFound = true;
parameters[i] = sender;
}
else if (!eventArgsFound && parameterTypes[i] == typeof(TSource))
else if (!eventArgsFound && type == typeof(TSource))
{
eventArgsFound = true;
parameters[i] = e;
}
else if (!modOptionEventFound && parameterTypes[i] == typeof(EventArgs))
else if (!modOptionEventFound && type == typeof(EventArgs))
{
modOptionEventFound = true;
parameters[i] = e;
}
else if (!modOptionEventFound && type.IsGenericType &&
type.GetGenericTypeDefinition() == typeof(ChoiceChangedEventArgs<>))
{
InternalLogger.Debug($"[OptionsMenuBuilder] Found a {type.Name}<{memberInfoMetadata.ValueType}> parameter for {typeof(T)}.{memberInfoMetadata.Name}");

var eventArgsConstructor = typeof(ChoiceChangedEventArgs<>).MakeGenericType(memberInfoMetadata.ValueType);

var id = (string)eventArgsConstructor.GetProperty("Id").GetValue(e);
var index = (int) eventArgsConstructor.GetProperty("Index").GetValue(e);
var value = eventArgsConstructor.GetProperty("Value").GetValue(e);

var eventArgs = Activator.CreateInstance(eventArgsConstructor, id, index, value);

modOptionEventFound = true;
parameters[i] = eventArgs;
}

if (senderFound && eventArgsFound && modOptionEventFound)
{
Expand Down
9 changes: 8 additions & 1 deletion Nautilus/Options/Attributes/MemberInfoMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,14 @@ public void InvokeMethod(T config, params object[] arguments)
return;
}

Traverse.Create(config).Method(Name, MethodParameterTypes).GetValue(arguments);
MethodInfo methodInfo = AccessTools.Method(typeof(T), Name, MethodParameterTypes);
if (methodInfo.ContainsGenericParameters)
{
methodInfo.MakeGenericMethod(ValueType).Invoke(config, arguments);
return;
}

methodInfo.Invoke(config, arguments);
}

public Action<V> GetMethodAsAction<V>(T config)
Expand Down
Loading
Loading