add dynamic world config
This commit is contained in:
115
Torch.Server/ViewModels/DynamicViewModel.cs
Normal file
115
Torch.Server/ViewModels/DynamicViewModel.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using HarmonyLib;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Torch.Server.ViewModels;
|
||||
|
||||
public class DynamicViewModel<T>(T obj) : ViewModel
|
||||
{
|
||||
private readonly T _obj = obj;
|
||||
|
||||
public ViewModel Wrapper { get; } = CreateProxy(obj);
|
||||
|
||||
public static implicit operator T(DynamicViewModel<T> obj) => obj._obj;
|
||||
|
||||
private static ViewModel CreateProxy(T obj)
|
||||
{
|
||||
if (DynamicViewModel.Proxies.TryGetValue(typeof(T), out var proxy))
|
||||
return (ViewModel)Activator.CreateInstance(proxy, obj);
|
||||
|
||||
var viewModelSetMethod = AccessTools.GetDeclaredMethods(typeof(ViewModel))
|
||||
.First(b => b.Name == "SetValue" && b.GetParameters()[0].ParameterType.IsByRef);
|
||||
|
||||
DynamicViewModel.ModuleBuilder ??=
|
||||
AssemblyBuilder.DefineDynamicAssembly(new("Torch.Server.ViewModels.Generated"), AssemblyBuilderAccess.Run)
|
||||
.DefineDynamicModule("Torch.Server.ViewModels.Generated");
|
||||
|
||||
var type = DynamicViewModel.ModuleBuilder.DefineType($"{typeof(T).FullName}ViewModel",
|
||||
TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class, typeof(ViewModel));
|
||||
|
||||
var instanceField = type.DefineField("_instance", typeof(T), FieldAttributes.Private | FieldAttributes.InitOnly);
|
||||
|
||||
var ctor = type.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, [typeof(T)]);
|
||||
{
|
||||
var il = ctor.GetILGenerator();
|
||||
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Stfld, instanceField);
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
foreach (var field in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public))
|
||||
{
|
||||
var prop = type.DefineProperty(field.Name, PropertyAttributes.None, field.FieldType, null);
|
||||
|
||||
foreach (var customAttribute in field.GetCustomAttributesData())
|
||||
{
|
||||
var hasCustomArgs = customAttribute.NamedArguments?.Any() ?? false;
|
||||
var customArgsField = hasCustomArgs && customAttribute.NamedArguments[0].IsField;
|
||||
|
||||
var constructorArgs = customAttribute.ConstructorArguments.Select(b => b.Value).ToArray();
|
||||
|
||||
CustomAttributeBuilder attributeBuilder;
|
||||
if (!hasCustomArgs)
|
||||
attributeBuilder = new CustomAttributeBuilder(customAttribute.Constructor, constructorArgs);
|
||||
else if (customArgsField)
|
||||
attributeBuilder = new CustomAttributeBuilder(customAttribute.Constructor, constructorArgs,
|
||||
customAttribute.NamedArguments.Select(b => (FieldInfo)b.MemberInfo).ToArray(),
|
||||
customAttribute.NamedArguments.Select(b => b.TypedValue.Value).ToArray());
|
||||
else
|
||||
attributeBuilder = new CustomAttributeBuilder(customAttribute.Constructor, constructorArgs,
|
||||
customAttribute.NamedArguments.Select(b => (PropertyInfo)b.MemberInfo).ToArray(),
|
||||
customAttribute.NamedArguments.Select(b => b.TypedValue.Value).ToArray());
|
||||
|
||||
prop.SetCustomAttribute(attributeBuilder);
|
||||
}
|
||||
|
||||
var getMethod = type.DefineMethod($"get_{field.Name}", MethodAttributes.Public | MethodAttributes.HideBySig, field.FieldType, Type.EmptyTypes);
|
||||
{
|
||||
var il = getMethod.GetILGenerator();
|
||||
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldfld, instanceField);
|
||||
il.Emit(OpCodes.Ldfld, field);
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
prop.SetGetMethod(getMethod);
|
||||
|
||||
if (field.IsInitOnly) continue;
|
||||
|
||||
var setMethod = type.DefineMethod($"set_{field.Name}", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(void),
|
||||
[field.FieldType]);
|
||||
{
|
||||
var il = setMethod.GetILGenerator();
|
||||
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Dup);
|
||||
il.Emit(OpCodes.Ldfld, instanceField);
|
||||
il.Emit(OpCodes.Ldflda, field);
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Ldstr, field.Name);
|
||||
il.Emit(OpCodes.Callvirt, viewModelSetMethod.MakeGenericMethod(field.FieldType));
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
prop.SetSetMethod(setMethod);
|
||||
}
|
||||
|
||||
proxy = type.CreateType();
|
||||
|
||||
DynamicViewModel.Proxies[typeof(T)] = proxy;
|
||||
|
||||
return (ViewModel)Activator.CreateInstance(proxy, obj);
|
||||
}
|
||||
}
|
||||
|
||||
file static class DynamicViewModel
|
||||
{
|
||||
public static readonly Dictionary<Type, Type> Proxies = [];
|
||||
[CanBeNull] public static ModuleBuilder ModuleBuilder;
|
||||
}
|
Reference in New Issue
Block a user