- ShrinkModComponentDiscovery.Discover 公开:只发现、不执行生命周期 - ShrinkModLoader.LoadComposed / ShrinkModCordisRuntime.ApplyComposedAsync:宿主提供元数据组合加载,支持纯数据包与 IShrinkModWrapper 公开真实实例 - 公开 TryAttachEventBusInstance,随生命周期逆操作释放 - 依赖新增 VersionRange(Maven/Forge 区间语法)与 Ordering(Before/After) - AssemblyResolvePolicy 可限制跨模组程序集解析
109 lines
5.0 KiB
C#
109 lines
5.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Cysharp.Threading.Tasks;
|
|
|
|
namespace ShrinkModFramework
|
|
{
|
|
/// <summary>默认模组组件根;目录变更只提交完整的新组合,失败时由 Host 恢复旧组合。</summary>
|
|
public static class ShrinkModCordisRuntime
|
|
{
|
|
private static ShrinkModContextHost _host;
|
|
|
|
public static bool IsInitialized => _host != null;
|
|
public static ShrinkModContextHost Host => _host ?? throw new InvalidOperationException(
|
|
"ShrinkModCordisRuntime has not been initialized.");
|
|
public static IReadOnlyDictionary<string, ShrinkModHandle> Mods =>
|
|
_host?.Mods ?? EmptyMods;
|
|
|
|
private static readonly IReadOnlyDictionary<string, ShrinkModHandle> EmptyMods =
|
|
new Dictionary<string, ShrinkModHandle>();
|
|
|
|
public static async UniTask<IReadOnlyDictionary<string, ShrinkModHandle>> ApplyDiscoveredAsync(
|
|
ShrinkModFrameworkSettings settings = null,
|
|
IEnumerable<string> authorizedDllPaths = null)
|
|
{
|
|
settings ??= ShrinkModFrameworkSettings.Instance;
|
|
var verboseLogging = settings == null || settings.verboseLogging;
|
|
if (_host == null)
|
|
{
|
|
_host = new ShrinkModContextHost(
|
|
settings == null || settings.enableHarmonyPatching,
|
|
verboseLogging);
|
|
}
|
|
|
|
var revisionState = ShrinkExternalModAssemblyLoader.CaptureState();
|
|
ShrinkModNetworkManager.Configure(settings == null || settings.enableNetworkSync, verboseLogging);
|
|
try
|
|
{
|
|
var authorizedPaths = authorizedDllPaths?.ToArray() ?? Array.Empty<string>();
|
|
var sources = ShrinkModComponentDiscovery.DiscoverCurrent(
|
|
settings, verboseLogging, authorizedPaths);
|
|
await _host.ApplyAsync(sources, () => ShrinkExternalModAssemblyLoader.RestoreState(revisionState));
|
|
}
|
|
catch
|
|
{
|
|
// DLL revision discovery is part of the same composition transaction:
|
|
// a failing replacement must not make the failed assembly current.
|
|
ShrinkExternalModAssemblyLoader.RestoreState(revisionState);
|
|
throw;
|
|
}
|
|
|
|
if (verboseLogging)
|
|
UnityEngine.Debug.Log($"[ShrinkModFramework] Cordis 模组组合已提交,共 {_host.Mods.Count} 个。");
|
|
return _host.Mods;
|
|
}
|
|
|
|
/// <summary>提交宿主组合;生命周期或发现失败时恢复之前的程序集 revision 和组件。</summary>
|
|
public static async UniTask<IReadOnlyDictionary<string, ShrinkModHandle>> ApplyComposedAsync(
|
|
ShrinkModFrameworkSettings settings, IReadOnlyList<ShrinkModComponentSource> sources)
|
|
{
|
|
if (sources == null) throw new ArgumentNullException(nameof(sources));
|
|
settings ??= ShrinkModFrameworkSettings.Instance;
|
|
var verbose = settings != null && settings.verboseLogging;
|
|
if (_host == null) _host = new ShrinkModContextHost(settings == null || settings.enableHarmonyPatching, verbose);
|
|
var previous = ShrinkExternalModAssemblyLoader.CaptureState();
|
|
try
|
|
{
|
|
var states = sources.Where(source => source != null && source.DiscoveryState != null)
|
|
.Select(source => source.DiscoveryState).Distinct().ToArray();
|
|
var composedState = new ShrinkExternalModAssemblyLoader.RevisionState();
|
|
foreach (var state in states)
|
|
{
|
|
foreach (var pair in state.Current)
|
|
{
|
|
if (composedState.Current.TryGetValue(pair.Key, out var other) && other.Revision != pair.Value.Revision)
|
|
throw new InvalidOperationException($"Conflicting discovered revisions for {pair.Key}.");
|
|
composedState.Current[pair.Key] = pair.Value;
|
|
}
|
|
foreach (var pair in state.Known) composedState.Known[pair.Key] = pair.Value;
|
|
}
|
|
ShrinkExternalModAssemblyLoader.RestoreState(composedState);
|
|
ShrinkModNetworkManager.Configure(settings == null || settings.enableNetworkSync, verbose);
|
|
await _host.ApplyAsync(sources, () => ShrinkExternalModAssemblyLoader.RestoreState(previous));
|
|
}
|
|
catch
|
|
{
|
|
ShrinkExternalModAssemblyLoader.RestoreState(previous);
|
|
throw;
|
|
}
|
|
return _host.Mods;
|
|
}
|
|
|
|
public static IReadOnlyShrinkModRegistry<T> GetOrCreateRegistry<T>(string name) =>
|
|
Host.GetOrCreateRegistry<T>(name);
|
|
|
|
internal static void ResetForDomainReload()
|
|
{
|
|
_host = null;
|
|
}
|
|
|
|
internal static void ResetForTesting()
|
|
{
|
|
if (_host != null)
|
|
_host.ShutdownAsync().GetAwaiter().GetResult();
|
|
_host = null;
|
|
}
|
|
}
|
|
}
|