using System;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using ColdMint.scripts.debug;
using ColdMint.scripts.utils;
using Godot;
namespace ColdMint.scripts.mod;
///
/// Mod Loader
/// 模组加载器
///
public class ModLoader
{
///
/// AssemblyLoadContext
/// 装配加载上下文
///
private static AssemblyLoadContext? _assemblyLoadContext;
private static readonly string[] RequiredDllList = [Config.SolutionName];
///
/// Initializes the mod loader
/// 初始化模组加载器
///
///
///This exception is thrown if the built-in dll file cannot be found when it is loaded.
///如果加载内置dll文件时,找不到文件,则抛出此异常。
///
public static void Init()
{
//Initialize the context.
//初始化上下文环境。
LogCat.Log("initialize_the_context", LogCat.LogLabel.ModLoader);
_assemblyLoadContext = AssemblyLoadContext.GetLoadContext(typeof(Godot.Bridge.ScriptManagerBridge).Assembly);
if (_assemblyLoadContext == null)
{
LogCat.LogError("initialize_the_context_failed", LogCat.LogLabel.ModLoader);
return;
}
var dllFolder = ResUtils.GetSelfDllFolder();
if (dllFolder == null)
{
LogCat.LogError("get_dll_folder_failed", LogCat.LogLabel.ModLoader);
return;
}
foreach (var requiredDll in RequiredDllList)
{
var dllPath = Path.Join(dllFolder, requiredDll + ".dll");
//Load the necessary dll files.
//加载必须的dll文件。
if (!File.Exists(dllPath))
{
//When the dll that must be loaded does not exist, an error is reported immediately.
//当必须加载的dll不存在时,立即报错。
LogCat.LogErrorWithFormat("dll_not_exist", LogCat.LogLabel.ModLoader, true, dllPath);
throw new FileNotFoundException("dll not exist:" + dllPath);
}
LoadDllFile(dllPath);
}
}
///
/// Load Dll file
/// 加载Dll文件
///
///
///dll file path
///dll的文件路径
///
///
///Throw this error if the assemblyLoadContext has not been initialized.
///如果assemblyLoadContext尚未初始化,那么抛出此错误。
///
private static void LoadDllFile(string dllPath)
{
if (_assemblyLoadContext == null)
{
throw new NullReferenceException("assemblyLoadContext is null.");
}
//Load the dll.
//加载dll。
LogCat.LogWithFormat("load_dll", LogCat.LogLabel.ModLoader, true, dllPath);
try
{
var assembly = _assemblyLoadContext.LoadFromAssemblyPath(dllPath);
var assemblyName = assembly.GetName().Name;
if (assemblyName == null)
{
return;
}
LogCat.LogWithFormat("dll_name", LogCat.LogLabel.ModLoader, true, assemblyName);
//If the load is not its own Dll file.
//如果加载的不是自身的Dll文件.
if (assemblyName == Config.SolutionName)
{
return;
}
//Call the method of the entry class.
//调用入口类的方法
var exportedTypes = assembly.GetExportedTypes();
LogCat.LogWarningWithFormat("dll_type_length", LogCat.LogLabel.ModLoader, LogCat.UploadFormat, dllPath,
exportedTypes.Length);
var modLifecycleHandlerType =
FindTypeInTypeArray(exportedTypes, Config.ModLifecycleHandlerName);
if (modLifecycleHandlerType == null)
{
//The module does not register a lifecycle processor.
//模组没有注册生命周期处理器。
LogCat.LogWarningWithFormat("dll_does_not_register_lifecycle_processor", LogCat.LogLabel.ModLoader,
LogCat.UploadFormat,
dllPath, Config.ModLifecycleHandlerName);
return;
}
var constructor = modLifecycleHandlerType.GetConstructor(Type.EmptyTypes);
if (constructor == null)
{
//No parameterless constructor found.
//未找到无参构造方法。
LogCat.LogWarningWithFormat("dll_no_parameterless_constructor", LogCat.LogLabel.ModLoader,
LogCat.UploadFormat,
dllPath);
return;
}
var modLifecycleHandler = constructor.Invoke(null);
var methodInfo =
modLifecycleHandlerType.GetMethod(nameof(IModLifecycleHandler.OnModLoaded));
if (methodInfo == null)
{
LogCat.LogWarningWithFormat("mod_lifecycle_handler_not_implement_interface",
LogCat.LogLabel.ModLoader,
LogCat.UploadFormat, dllPath);
return;
}
methodInfo.Invoke(modLifecycleHandler, null);
}
catch (ArgumentNullException argumentNullException)
{
//The assemblyPath parameter is null.
//assemblyPath参数为空。
LogCat.LogErrorWithFormat("load_dll_argument_null_exception", LogCat.LogLabel.ModLoader, true, dllPath);
LogCat.WhenCaughtException(argumentNullException, LogCat.LogLabel.ModLoader);
return;
}
catch (ArgumentException argumentException)
{
//Not an absolute path.
//不是绝对路径
LogCat.LogErrorWithFormat("load_dll_argument_exception", LogCat.LogLabel.ModLoader, true, dllPath);
LogCat.WhenCaughtException(argumentException, LogCat.LogLabel.ModLoader);
return;
}
catch (FileLoadException fileLoadException)
{
//A file that was found could not be loaded.
//无法加载找到的文件。
LogCat.LogErrorWithFormat("load_dll_file_load_exception", LogCat.LogLabel.ModLoader, true, dllPath);
LogCat.WhenCaughtException(fileLoadException, LogCat.LogLabel.ModLoader);
return;
}
catch (BadImageFormatException badImageFormatException)
{
//assemblyPath is not a valid assembly.
//assemblyPath不是有效的程序集。
LogCat.LogErrorWithFormat("load_dll_bad_image_format_exception", LogCat.LogLabel.ModLoader, true,
dllPath);
LogCat.WhenCaughtException(badImageFormatException, LogCat.LogLabel.ModLoader);
return;
}
//Loading the dll succeeded.
//加载dll成功。
LogCat.LogWithFormat("load_dll_success", LogCat.LogLabel.ModLoader, true, dllPath);
}
///
/// Find a specific type by class name
/// 通过类名查找特定的类型
///
///
///TypeArray
///类型数组
///
///
///ClassName
///类名
///
///
public static Type? FindTypeInTypeArray(Type[] types, string className)
{
return types.FirstOrDefault(type => type.Name == className);
}
///
/// Load all mods
/// 加载全部模组
///
///
///This method scans the incoming subfolders and loads them as module folders.
///此方法会将扫描传入的子文件夹,并将其子文件夹看作模组文件夹加载。
///
///
///Mod folder
///模组文件夹
///
///
///If the given folder does not exist, throw this exception.
///如果给定的文件夹不存在,则抛出此异常。
///
public static void LoadAllMods(string modFolder)
{
if (!Directory.Exists(modFolder))
{
//The mod directory does not exist.
//模组目录不存在。
throw new DirectoryNotFoundException("mod folder not exist:" + modFolder);
}
var directoryInfo = new DirectoryInfo(modFolder);
foreach (var directory in directoryInfo.GetDirectories())
{
LoadSingleMod(directory.FullName);
}
}
///
/// Load a single mod
/// 加载单个模组
///
///
///Mod path
///模组路径
///
///
/// If the given directory does not exist, throw this exception.
///如果给定的目录不存在,那么抛出此异常。
///
///
///Throw this exception if the manifest file creation deserialization fails.
///如果清单文件创建反序列化失败,则抛出此异常。
///
private static void LoadSingleMod(string modFolderPath)
{
if (!Directory.Exists(modFolderPath))
{
//The module folder does not exist.
//模组文件夹不存在。
throw new DirectoryNotFoundException("Mod folder does not exist:" + modFolderPath);
}
var modManifestPath = Path.Join(modFolderPath, Config.ModManifestFileName);
var modManifest =
ModManifest.CreateModManifestFromPath(modManifestPath);
if (modManifest == null)
{
throw new NullReferenceException("mod manifest is null:" + modManifestPath);
}
var pckList = modManifest.PckList;
if (pckList == null || pckList.Length == 0)
{
//The module does not contain a pck file.
//模组不包含pck文件。
LogCat.LogWarningWithFormat("mod_not_contain_pck", LogCat.LogLabel.ModLoader, LogCat.UploadFormat,
modFolderPath);
}
else
{
//The module contains pck files, load the pck files.
//包含pck文件,加载pck文件。
foreach (var pck in pckList)
{
var pckPath = Path.GetFullPath(pck, modFolderPath);
LoadPckFile(pckPath);
}
}
var dllList = modManifest.DllList;
if (dllList == null || dllList.Length == 0)
{
//The module does not contain a dll file.
//模组不包含dll文件。
LogCat.LogWarningWithFormat("mod_not_contain_dll", LogCat.LogLabel.ModLoader, LogCat.UploadFormat,
modFolderPath);
}
else
{
//The module contains dll files, load the dll files.
//包含dll文件,加载dll文件。
foreach (var dll in dllList)
{
var dllPath = Path.GetFullPath(dll, modFolderPath);
LoadDllFile(dllPath);
}
}
}
///
/// Load the Pck file
/// 加载Pck文件
///
///
///Pck path
///Pck路径
///
///
///If the given path does not exist, throw this exception.
///如果给定的路径不存在,那么抛出此异常。
///
///
///Throw this exception if the pck package fails to load.
///如果pck包加载失败了,抛出此异常。
///
private static void LoadPckFile(string pckPath)
{
if (!File.Exists(pckPath))
{
throw new FileNotFoundException("pck file not exist:" + pckPath);
}
var success = ProjectSettings.LoadResourcePack(pckPath);
if (success)
{
LogCat.LogWithFormat("load_pck_success", LogCat.LogLabel.ModLoader, true, pckPath);
}
else
{
LogCat.LogErrorWithFormat("load_pck_failed", LogCat.LogLabel.ModLoader, true, pckPath);
//Throw a suitable exception here for handling at the caller.
//为这里抛出合适的异常,以便在调用方处理。
throw new DataException("load pck failed:" + pckPath);
}
}
}