using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEngine; namespace ShrinkModFramework { /// /// 把工程内程序集和外部 DLL 的当前 revision 投影为延迟组件源。 /// 同一路径的旧外部程序集无法从 Mono AppDomain 卸载,但不会再进入候选集合。 /// internal static class ShrinkModComponentDiscovery { public static IReadOnlyList Discover( ShrinkModFrameworkSettings settings, bool verboseLogging, IEnumerable authorizedDllPaths) { ShrinkExternalModAssemblyLoader.ScanExternalAssemblyRevisions( settings, verboseLogging, authorizedDllPaths); var results = new List(); 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(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(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 GetTypesSafely(Assembly assembly) { try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException exception) { return exception.Types.Where(type => type != null); } catch { return Array.Empty(); } } } }