TH1/Unity/Assets/Scripts/TH1_Logic/Hotfix/HotfixBootstrap.cs
2026-06-09 20:49:52 +08:00

118 lines
4.0 KiB
C#

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace TH1_Logic.Hotfix
{
public static class HotfixBootstrap
{
private static bool _initialized;
public static bool IsLoaded { get; private set; }
public static string LoadedAssemblyFullName { get; private set; }
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void AutoInitialize()
{
InitializeFromStreamingAssets(false);
}
public static bool InitializeFromStreamingAssets(bool requireHotfix)
{
if (_initialized) return IsLoaded;
try
{
var root = Path.Combine(Application.streamingAssetsPath, HotfixManifest.RootFolderName);
LoadAotMetadata(root);
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));
LoadedAssemblyFullName = assembly.FullName;
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;
}
}
private static void LoadAotMetadata(string root)
{
var runtimeApiType = FindType("HybridCLR.RuntimeApi");
var modeType = FindType("HybridCLR.HomologousImageMode");
if (runtimeApiType == null || modeType == null)
{
return;
}
var method = runtimeApiType.GetMethod(
"LoadMetadataForAOTAssembly",
BindingFlags.Public | BindingFlags.Static,
null,
new[] { typeof(byte[]), modeType },
null);
if (method == null)
{
Debug.LogWarning("[TH1.Hotfix] HybridCLR RuntimeApi.LoadMetadataForAOTAssembly not found.");
return;
}
var mode = Enum.Parse(modeType, "SuperSet");
var aotDir = Path.Combine(root, HotfixManifest.AotMetadataFolderName);
foreach (var fileName in HotfixManifest.AotMetadataAssemblyFileNames)
{
var path = Path.Combine(aotDir, fileName);
if (!File.Exists(path)) continue;
var result = method.Invoke(null, new object[] { File.ReadAllBytes(path), mode });
Debug.Log($"[TH1.Hotfix] Load AOT metadata {fileName}: {result}");
}
}
private static void InvokeHotfixEntry(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);
}
}
}