This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ShrinkModFramework
|
||||
{
|
||||
/// <summary>
|
||||
/// 把工程内程序集和外部 DLL 的当前 revision 投影为延迟组件源。
|
||||
/// 同一路径的旧外部程序集无法从 Mono AppDomain 卸载,但不会再进入候选集合。
|
||||
/// </summary>
|
||||
internal static class ShrinkModComponentDiscovery
|
||||
{
|
||||
public static IReadOnlyList<ShrinkModComponentSource> Discover(ShrinkModFrameworkSettings settings,
|
||||
bool verboseLogging)
|
||||
{
|
||||
ShrinkExternalModAssemblyLoader.ScanExternalAssemblyRevisions(settings, verboseLogging);
|
||||
var results = new List<ShrinkModComponentSource>();
|
||||
var prefixes = settings?.assemblyNamePrefixes;
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (ShrinkExternalModAssemblyLoader.IsExternalAssembly(assembly) &&
|
||||
!ShrinkExternalModAssemblyLoader.TryGetCurrentRevision(assembly, out _))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!ShouldScanAssembly(assembly, prefixes))
|
||||
continue;
|
||||
|
||||
var assemblyRevision = GetAssemblyRevision(assembly);
|
||||
foreach (var type in GetTypesSafely(assembly))
|
||||
{
|
||||
if (type == null || type.IsAbstract || type.IsInterface)
|
||||
continue;
|
||||
|
||||
var modAttribute = type.GetCustomAttribute<ShrinkModAttribute>(false);
|
||||
if (modAttribute == null)
|
||||
continue;
|
||||
if (!typeof(IShrinkMod).IsAssignableFrom(type))
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"[ShrinkModFramework] 类型 {type.FullName} 标记了 ShrinkMod,但没有实现 IShrinkMod,已跳过。");
|
||||
continue;
|
||||
}
|
||||
if (type.GetConstructor(Type.EmptyTypes) == null)
|
||||
throw new InvalidOperationException($"模组 {type.FullName} 缺少无参构造函数,无法实例化。");
|
||||
|
||||
var dependencies = type
|
||||
.GetCustomAttributes<ShrinkModDependencyAttribute>(false)
|
||||
.Select(attribute => new ShrinkModDependency(
|
||||
attribute.ModId, attribute.MinimumVersion, attribute.Optional))
|
||||
.ToArray();
|
||||
var info = new ShrinkModInfo(
|
||||
modAttribute.ModId,
|
||||
modAttribute.DisplayName,
|
||||
modAttribute.Version,
|
||||
modAttribute.LoadOrder,
|
||||
type,
|
||||
modAttribute.AutoApplyHarmonyPatches,
|
||||
dependencies);
|
||||
var revision = $"{assemblyRevision}:{type.FullName}";
|
||||
results.Add(new ShrinkModComponentSource(info, revision,
|
||||
() => (IShrinkMod)Activator.CreateInstance(type)));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static string GetAssemblyRevision(Assembly assembly)
|
||||
{
|
||||
if (ShrinkExternalModAssemblyLoader.TryGetCurrentRevision(assembly, out var externalRevision))
|
||||
return externalRevision;
|
||||
|
||||
try
|
||||
{
|
||||
return assembly.ManifestModule.ModuleVersionId.ToString("N");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return assembly.FullName ?? assembly.GetName().Name ?? "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ShouldScanAssembly(Assembly assembly, string[] prefixes)
|
||||
{
|
||||
var name = assembly.GetName().Name;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return false;
|
||||
if (name.StartsWith("Unity", StringComparison.Ordinal) ||
|
||||
name.StartsWith("System", StringComparison.Ordinal) ||
|
||||
name.StartsWith("mscorlib", StringComparison.Ordinal) ||
|
||||
name.StartsWith("netstandard", StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (prefixes == null || prefixes.Length == 0)
|
||||
return true;
|
||||
|
||||
return prefixes.Any(prefix => !string.IsNullOrWhiteSpace(prefix) &&
|
||||
name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static IEnumerable<Type> GetTypesSafely(Assembly assembly)
|
||||
{
|
||||
try
|
||||
{
|
||||
return assembly.GetTypes();
|
||||
}
|
||||
catch (ReflectionTypeLoadException exception)
|
||||
{
|
||||
return exception.Types.Where(type => type != null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Array.Empty<Type>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user