using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
namespace ShrinkModFramework
{
/// 默认模组组件根;目录变更只提交完整的新组合,失败时由 Host 恢复旧组合。
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 Mods =>
_host?.Mods ?? EmptyMods;
private static readonly IReadOnlyDictionary EmptyMods =
new Dictionary();
public static async UniTask> ApplyDiscoveredAsync(
ShrinkModFrameworkSettings settings = null,
IEnumerable 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();
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;
}
/// 提交宿主组合;生命周期或发现失败时恢复之前的程序集 revision 和组件。
public static async UniTask> ApplyComposedAsync(
ShrinkModFrameworkSettings settings, IReadOnlyList 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 GetOrCreateRegistry(string name) =>
Host.GetOrCreateRegistry(name);
internal static void ResetForDomainReload()
{
_host = null;
}
internal static void ResetForTesting()
{
if (_host != null)
_host.ShutdownAsync().GetAwaiter().GetResult();
_host = null;
}
}
}