#nullable enable using System; using System.Threading; using Cysharp.Threading.Tasks; using ShrinkApp; using ShrinkEventBus; using UnityEngine; namespace ShrinkContext.AppAdapter { /// /// ShrinkApp 加载器宿主的自动引导: /// hostingMode == ContextLoader 时在 BeforeSceneLoad 创建常驻 GameObject 并启动 ShrinkAppLoaderHost, /// 同时把服务容器接回 ShrinkApp 静态入口(InitializeForExternalHost),保证场景脚本照常访问 ShrinkApp.Services。 /// ClassicHost 模式下完全不动——经典路径零行为变化。 /// public sealed class ShrinkAppLoaderBootstrapper : MonoBehaviour { private static ShrinkAppLoaderBootstrapper? _instance; private static UniTaskCompletionSource _startupCompletion = new(); /// /// Starter/组合包在 SubsystemRegistration 阶段设置的默认装配表。 /// AppAdapter 保持不依赖具体业务模块,组合根负责注册原生组件;若兼容 installer 已被发现则覆盖同 id。 /// public static System.Action? DefaultComposition { get; set; } public static ShrinkAppLoaderBootstrapper? Instance => _instance; public static Exception? StartError { get; private set; } public ShrinkAppLoaderHost Host { get; private set; } = null!; /// /// 等待 ContextLoader 宿主完成启动。支持多个并发等待者;宿主失败时以原始异常结束。 /// public static async UniTask WaitUntilStartedAsync(CancellationToken cancellationToken = default) { if (_instance?.Host?.IsRunning == true) return; if (StartError != null) throw StartError; if (ShrinkAppSettings.Instance.hostingMode != ShrinkAppHostingMode.ContextLoader) { throw new InvalidOperationException( "ShrinkAppLoaderBootstrapper requires ShrinkAppHostingMode.ContextLoader."); } var task = _startupCompletion.Task; if (cancellationToken.CanBeCanceled) await task.AttachExternalCancellation(cancellationToken); else await task; if (StartError != null) throw StartError; } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetStaticState() { _instance = null; StartError = null; _startupCompletion = new UniTaskCompletionSource(); } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void AutoBootstrap() { if (ShrinkAppSettings.Instance.hostingMode != ShrinkAppHostingMode.ContextLoader) return; var gameObject = new GameObject("ShrinkAppLoaderHost"); DontDestroyOnLoad(gameObject); gameObject.AddComponent(); } private void Awake() { if (_instance != null && _instance != this) { Destroy(gameObject); return; } _instance = this; StartHostAsync().Forget(); } private async UniTask StartHostAsync() { try { Host = new ShrinkAppLoaderHost(); DefaultComposition?.Invoke(Host); var profile = ShrinkAppCompositionProfile.LoadDefault(); if (profile != null) Host.ApplyComposition(profile.ResolveDocument()); global::ShrinkApp.ShrinkApp.InitializeForExternalHost(Host.Services); await Host.StartAsync(); CompleteStartup(null); } catch (Exception ex) { CompleteStartup(ex); EventBus.Post(new ShrinkAppStartFailedEvent { ErrorMessage = ex.Message, FailedModuleId = string.Empty }); Debug.LogException(ex); Debug.LogError("[ShrinkApp.LoaderHost] 启动失败: " + ex.Message); } } private static void CompleteStartup(Exception? error) { StartError = error; _startupCompletion.TrySetResult(); } private void OnDestroy() { if (_instance == this) _instance = null; } } }