Upload from upload_mods.ps1
This commit is contained in:
262
Scripts/Utilities/Modular/ModuleManagers.cs
Normal file
262
Scripts/Utilities/Modular/ModuleManagers.cs
Normal file
@@ -0,0 +1,262 @@
|
||||
using HarmonyLib;
|
||||
using KFCommonUtilityLib.Scripts.Attributes;
|
||||
using Mono.Cecil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Security.Permissions;
|
||||
using UniLinq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace KFCommonUtilityLib
|
||||
{
|
||||
public static class ModuleManagers
|
||||
{
|
||||
private static class ModuleExtensions<T>
|
||||
{
|
||||
public readonly static List<Type> extensions = new List<Type>();
|
||||
}
|
||||
public static AssemblyDefinition WorkingAssembly { get; private set; } = null;
|
||||
public static event Action OnAssemblyCreated;
|
||||
public static event Action OnAssemblyLoaded;
|
||||
public static bool Inited { get; private set; }
|
||||
private static bool extensionScanned;
|
||||
private static readonly HashSet<string> list_registered_path = new HashSet<string>();
|
||||
private static readonly List<Assembly> list_created = new List<Assembly>();
|
||||
private static DefaultAssemblyResolver resolver;
|
||||
private static ModuleAttributes moduleAttributes;
|
||||
private static ModuleCharacteristics moduleCharacteristics;
|
||||
private static MethodInfo mtdinf = AccessTools.Method(typeof(ModuleManagers), nameof(ModuleManagers.AddModuleExtension));
|
||||
|
||||
public static void InitModuleExtensions()
|
||||
{
|
||||
if (extensionScanned)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var assemblies = ModManager.GetLoadedAssemblies();
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
var attr = type.GetCustomAttribute<TypeTargetExtensionAttribute>();
|
||||
if (attr != null)
|
||||
{
|
||||
if ((bool)mtdinf.MakeGenericMethod(attr.ModuleType).Invoke(null, new object[] { type }))
|
||||
{
|
||||
Log.Out($"Found Module Extension {type.FullName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
extensionScanned = true;
|
||||
}
|
||||
|
||||
public static bool AddModuleExtension<T>(Type extType)
|
||||
{
|
||||
if (typeof(T).GetCustomAttribute<TypeTargetAttribute>() == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ModuleExtensions<T>.extensions.Contains(extType))
|
||||
ModuleExtensions<T>.extensions.Add(extType);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Type[] GetModuleExtensions<T>()
|
||||
{
|
||||
if (typeof(T).GetCustomAttribute<TypeTargetAttribute>() == null)
|
||||
{
|
||||
return Array.Empty<Type>();
|
||||
}
|
||||
|
||||
return ModuleExtensions<T>.extensions.ToArray();
|
||||
}
|
||||
|
||||
public static void AddAssemblySearchPath(string path)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
list_registered_path.Add(path);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ClearOutputFolder()
|
||||
{
|
||||
Mod self = ModManager.GetMod("CommonUtilityLib");
|
||||
string path = Path.Combine(self.Path, "AssemblyOutput");
|
||||
if (Directory.Exists(path))
|
||||
Array.ForEach(Directory.GetFiles(path), File.Delete);
|
||||
else
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
internal static void InitNew()
|
||||
{
|
||||
if (Inited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
InitModuleExtensions();
|
||||
WorkingAssembly?.Dispose();
|
||||
if (resolver == null)
|
||||
{
|
||||
resolver = new DefaultAssemblyResolver();
|
||||
resolver.AddSearchDirectory(Path.Combine(Application.dataPath, "Managed"));
|
||||
|
||||
foreach (var mod in ModManager.GetLoadedMods())
|
||||
{
|
||||
resolver.AddSearchDirectory(mod.Path);
|
||||
}
|
||||
|
||||
foreach (var path in list_registered_path)
|
||||
{
|
||||
resolver.AddSearchDirectory(path);
|
||||
}
|
||||
|
||||
AssemblyDefinition assdef_main = AssemblyDefinition.ReadAssembly($"{Application.dataPath}/Managed/Assembly-CSharp.dll", new ReaderParameters() { AssemblyResolver = resolver });
|
||||
moduleAttributes = assdef_main.MainModule.Attributes;
|
||||
moduleCharacteristics = assdef_main.MainModule.Characteristics;
|
||||
Log.Out("Reading Attributes from assembly: " + assdef_main.FullName);
|
||||
}
|
||||
string assname = "RuntimeAssembled" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
WorkingAssembly = AssemblyDefinition.CreateAssembly(new AssemblyNameDefinition(assname,
|
||||
new Version(0, 0, 0, 0)),
|
||||
assname + ".dll",
|
||||
new ModuleParameters()
|
||||
{
|
||||
Kind = ModuleKind.Dll,
|
||||
AssemblyResolver = resolver,
|
||||
Architecture = TargetArchitecture.I386,
|
||||
Runtime = TargetRuntime.Net_4_0,
|
||||
});
|
||||
WorkingAssembly.MainModule.Attributes = moduleAttributes;
|
||||
WorkingAssembly.MainModule.Characteristics = moduleCharacteristics;
|
||||
//write security attributes so that calling non-public patch methods from this assembly is allowed
|
||||
Mono.Cecil.SecurityAttribute sattr_permission = new Mono.Cecil.SecurityAttribute(WorkingAssembly.MainModule.ImportReference(typeof(SecurityPermissionAttribute)));
|
||||
Mono.Cecil.CustomAttributeNamedArgument caarg_SkipVerification = new Mono.Cecil.CustomAttributeNamedArgument(nameof(SecurityPermissionAttribute.SkipVerification), new CustomAttributeArgument(WorkingAssembly.MainModule.TypeSystem.Boolean, true));
|
||||
sattr_permission.Properties.Add(caarg_SkipVerification);
|
||||
SecurityDeclaration sdec = new SecurityDeclaration(Mono.Cecil.SecurityAction.RequestMinimum);
|
||||
sdec.SecurityAttributes.Add(sattr_permission);
|
||||
WorkingAssembly.SecurityDeclarations.Add(sdec);
|
||||
OnAssemblyCreated?.Invoke();
|
||||
Inited = true;
|
||||
Log.Out("======Init New======");
|
||||
}
|
||||
|
||||
public static bool PatchType<T>(Type targetType, Type baseType, string moduleNames, out string typename) where T : IModuleProcessor, new()
|
||||
{
|
||||
Type[] moduleTypes = moduleNames.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(s => new T().GetModuleTypeByName(s.Trim()))
|
||||
.Where(t => t.GetCustomAttribute<TypeTargetAttribute>().BaseType.IsAssignableFrom(targetType)).ToArray();
|
||||
return PatchType(targetType, baseType, moduleTypes, new T(), out typename);
|
||||
}
|
||||
|
||||
public static bool PatchType<T>(Type targetType, Type baseType, Type[] moduleTypes, out string typename) where T : IModuleProcessor, new()
|
||||
{
|
||||
return PatchType(targetType, baseType, moduleTypes, new T(), out typename);
|
||||
}
|
||||
|
||||
public static bool PatchType<T>(Type targetType, Type baseType, Type[] moduleTypes, T processor, out string typename) where T : IModuleProcessor
|
||||
{
|
||||
if (moduleTypes.Length == 0)
|
||||
{
|
||||
typename = string.Empty;
|
||||
return false;
|
||||
}
|
||||
typename = ModuleUtils.CreateTypeName(targetType, moduleTypes);
|
||||
//Log.Out(typename);
|
||||
if (!ModuleManagers.TryFindType(typename, out _) && !ModuleManagers.TryFindInCur(typename, out _))
|
||||
_ = new ModuleManipulator(ModuleManagers.WorkingAssembly, processor, targetType, baseType, moduleTypes);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static void FinishAndLoad()
|
||||
{
|
||||
if (!Inited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//output assembly
|
||||
Mod self = ModManager.GetMod("CommonUtilityLib");
|
||||
if (self == null)
|
||||
{
|
||||
Log.Warning("Failed to get mod!");
|
||||
self = ModManager.GetModForAssembly(typeof(ItemActionModuleManager).Assembly);
|
||||
}
|
||||
if (self != null && WorkingAssembly != null)
|
||||
{
|
||||
if (WorkingAssembly.MainModule.Types.Count > 1)
|
||||
{
|
||||
Log.Out("Assembly is valid!");
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
try
|
||||
{
|
||||
WorkingAssembly.Write(ms);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
new ConsoleCmdShutdown().Execute(new List<string>(), new CommandSenderInfo());
|
||||
}
|
||||
DirectoryInfo dirInfo = Directory.CreateDirectory(Path.Combine(self.Path, "AssemblyOutput"));
|
||||
string filename = Path.Combine(dirInfo.FullName, WorkingAssembly.Name.Name + ".dll");
|
||||
Log.Out("Output Assembly: " + filename);
|
||||
using (FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write))
|
||||
{
|
||||
ms.WriteTo(fs);
|
||||
}
|
||||
Assembly newAssembly = Assembly.LoadFile(filename);
|
||||
list_created.Add(newAssembly);
|
||||
}
|
||||
}
|
||||
|
||||
Log.Out("======Finish and Load======");
|
||||
Inited = false;
|
||||
OnAssemblyLoaded?.Invoke();
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
//cleanup
|
||||
internal static void Cleanup()
|
||||
{
|
||||
Inited = false;
|
||||
WorkingAssembly?.Dispose();
|
||||
WorkingAssembly = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if type is already generated in previous assemblies.
|
||||
/// </summary>
|
||||
/// <param name="name">Full type name.</param>
|
||||
/// <param name="type">The retrieved type, null if not found.</param>
|
||||
/// <returns>true if found.</returns>
|
||||
public static bool TryFindType(string name, out Type type)
|
||||
{
|
||||
type = null;
|
||||
foreach (var assembly in list_created)
|
||||
{
|
||||
type = assembly.GetType(name, false);
|
||||
if (type != null)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if type is already generated in current working assembly definition.
|
||||
/// </summary>
|
||||
/// <param name="name">Full type name.</param>
|
||||
/// <param name="typedef">The retrieved type definition, null if not found.</param>
|
||||
/// <returns>true if found.</returns>
|
||||
public static bool TryFindInCur(string name, out TypeDefinition typedef)
|
||||
{
|
||||
typedef = WorkingAssembly?.MainModule.GetType(name);
|
||||
return typedef != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user