149 lines
5.1 KiB
C#
149 lines
5.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using HybridCLR;
|
|
using UnityEngine;
|
|
|
|
namespace TH1_Logic.Hotfix
|
|
{
|
|
public static class HotfixBootstrap
|
|
{
|
|
private static bool _initialized;
|
|
private static bool _aotMetadataLoaded;
|
|
private static readonly HashSet<string> InvokedEntryAssemblies = new HashSet<string>();
|
|
|
|
public static bool IsLoaded { get; private set; }
|
|
public static Assembly LoadedAssembly { get; private set; }
|
|
public static string LoadedAssemblyFullName { get; private set; }
|
|
|
|
public static bool InitializeFromStreamingAssets(bool requireHotfix, bool invokeEntry = true)
|
|
{
|
|
try
|
|
{
|
|
var root = Path.Combine(Application.streamingAssetsPath, HotfixManifest.RootFolderName);
|
|
LoadAotMetadata(root);
|
|
|
|
if (_initialized) return IsLoaded;
|
|
|
|
var hotfixPath = Path.Combine(root, HotfixManifest.HotfixDllFolderName, HotfixManifest.HotfixAssemblyFileName);
|
|
if (!File.Exists(hotfixPath))
|
|
{
|
|
if (requireHotfix)
|
|
{
|
|
Debug.LogWarning($"[TH1.Hotfix] Hotfix dll missing: {hotfixPath}");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
var assembly = Assembly.Load(File.ReadAllBytes(hotfixPath));
|
|
LoadedAssembly = assembly;
|
|
LoadedAssemblyFullName = assembly.FullName;
|
|
RefreshRuntimeTypeCaches();
|
|
if (invokeEntry)
|
|
{
|
|
InvokeHotfixEntry(assembly);
|
|
}
|
|
|
|
_initialized = true;
|
|
IsLoaded = true;
|
|
Debug.Log($"[TH1.Hotfix] Loaded {LoadedAssemblyFullName}");
|
|
return true;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"[TH1.Hotfix] Initialize failed: {e}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static void EnsureAotMetadataLoaded()
|
|
{
|
|
try
|
|
{
|
|
var root = Path.Combine(Application.streamingAssetsPath, HotfixManifest.RootFolderName);
|
|
LoadAotMetadata(root);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogWarning($"[TH1.Hotfix] Ensure AOT metadata failed: {e.Message}");
|
|
}
|
|
}
|
|
|
|
public static void InvokeHotfixEntry(Assembly assembly)
|
|
{
|
|
if (assembly == null) return;
|
|
|
|
var assemblyKey = assembly.FullName ?? assembly.GetName().Name;
|
|
if (!InvokedEntryAssemblies.Add(assemblyKey)) return;
|
|
|
|
InvokeHotfixEntryInternal(assembly);
|
|
}
|
|
|
|
private static void LoadAotMetadata(string root)
|
|
{
|
|
if (_aotMetadataLoaded) return;
|
|
|
|
var aotDir = Path.Combine(root, HotfixManifest.AotMetadataFolderName);
|
|
var allLoaded = true;
|
|
|
|
foreach (var fileName in HotfixManifest.AotMetadataAssemblyFileNames)
|
|
{
|
|
var path = Path.Combine(aotDir, fileName);
|
|
if (!File.Exists(path))
|
|
{
|
|
allLoaded = false;
|
|
Debug.LogWarning($"[TH1.Hotfix] AOT metadata missing: {path}");
|
|
continue;
|
|
}
|
|
|
|
var result = RuntimeApi.LoadMetadataForAOTAssembly(File.ReadAllBytes(path), HomologousImageMode.SuperSet);
|
|
Debug.Log($"[TH1.Hotfix] Load AOT metadata {fileName}: {result}");
|
|
}
|
|
|
|
_aotMetadataLoaded = allLoaded;
|
|
}
|
|
|
|
private static void InvokeHotfixEntryInternal(Assembly assembly)
|
|
{
|
|
var entryType = assembly.GetType(HotfixManifest.EntryTypeName);
|
|
if (entryType == null)
|
|
{
|
|
Debug.LogWarning($"[TH1.Hotfix] Entry type not found: {HotfixManifest.EntryTypeName}");
|
|
return;
|
|
}
|
|
|
|
var method = entryType.GetMethod(HotfixManifest.EntryMethodName, BindingFlags.Public | BindingFlags.Static);
|
|
if (method == null)
|
|
{
|
|
Debug.LogWarning($"[TH1.Hotfix] Entry method not found: {HotfixManifest.EntryMethodName}");
|
|
return;
|
|
}
|
|
|
|
method.Invoke(null, null);
|
|
}
|
|
|
|
private static Type FindType(string fullName)
|
|
{
|
|
return AppDomain.CurrentDomain.GetAssemblies()
|
|
.Select(assembly => assembly.GetType(fullName, false))
|
|
.FirstOrDefault(type => type != null);
|
|
}
|
|
|
|
public static void RefreshRuntimeTypeCaches()
|
|
{
|
|
InvokeStaticNoArg("ParadoxNotion.ReflectionTools", "FlushMem");
|
|
InvokeStaticNoArg("ParadoxNotion.Serialization.JSONSerializer", "FlushMem");
|
|
}
|
|
|
|
private static void InvokeStaticNoArg(string typeName, string methodName)
|
|
{
|
|
var type = FindType(typeName);
|
|
var method = type?.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
|
|
method?.Invoke(null, null);
|
|
}
|
|
}
|
|
}
|