From cb628ad5d62cabef1f27b1cbc23d59fb0b1a4d3d Mon Sep 17 00:00:00 2001 From: Michal Pikulski Date: Thu, 16 Oct 2025 11:26:53 +0200 Subject: [PATCH] Bootstrap initialization order figured out and provided events + dispatchers --- .../Bootstrap/BootCompletionService.cs | 166 ++++++++++++++++++ .../Bootstrap/BootCompletionService.cs.meta | 3 + Assets/Scripts/Bootstrap/CustomBoot.cs | 26 +++ Assets/Scripts/Input/InputManager.cs | 2 +- 4 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 Assets/Scripts/Bootstrap/BootCompletionService.cs create mode 100644 Assets/Scripts/Bootstrap/BootCompletionService.cs.meta diff --git a/Assets/Scripts/Bootstrap/BootCompletionService.cs b/Assets/Scripts/Bootstrap/BootCompletionService.cs new file mode 100644 index 00000000..60015087 --- /dev/null +++ b/Assets/Scripts/Bootstrap/BootCompletionService.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using UnityEngine; + +namespace Bootstrap +{ + /// + /// A service that provides information about the boot completion status + /// and allows systems to register for callbacks when boot is complete. + /// + public static class BootCompletionService + { + /// + /// True if the boot process has fully completed + /// + public static bool IsBootComplete { get; private set; } + + /// + /// Timestamp when the boot completed (Time.realtimeSinceStartup) + /// + public static float BootCompletionTime { get; private set; } + + /// + /// Event triggered when boot process completes. + /// Will be triggered immediately for new subscribers if boot is already complete. + /// + public static event Action OnBootComplete; + + /// + /// Actions to be executed in a specific order once boot completes + /// + private static readonly List _orderedInitActions = new List(); + + /// + /// Task completion source for async boot completion waiting + /// + private static TaskCompletionSource _bootCompletionTask; + + /// + /// Initialize the service - called by CustomBoot + /// + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] + private static void Initialize() + { + // Reset state + IsBootComplete = false; + _bootCompletionTask = new TaskCompletionSource(); + + // Subscribe to CustomBoot completion event + CustomBoot.OnBootCompleted += HandleBootCompleted; + + Debug.Log("[BootCompletionService] Initialized and waiting for boot completion"); + } + + /// + /// Handler for when CustomBoot reports completion + /// + private static void HandleBootCompleted() + { + if (IsBootComplete) return; // Avoid duplicate triggers + + IsBootComplete = true; + BootCompletionTime = Time.realtimeSinceStartup; + + Debug.Log($"[BootCompletionService] Boot completed at {BootCompletionTime:F2}s since startup"); + + // Execute ordered initialization actions + ExecuteOrderedInitActions(); + + // Complete the task + _bootCompletionTask.TrySetResult(true); + + // Trigger the event + OnBootComplete?.Invoke(); + } + + /// + /// Execute the registered ordered initialization actions + /// + private static void ExecuteOrderedInitActions() + { + // Sort actions by priority + _orderedInitActions.Sort((a, b) => a.Priority.CompareTo(b.Priority)); + + foreach (var action in _orderedInitActions) + { + try + { + Debug.Log($"[BootCompletionService] Executing priority {action.Priority} initialization: {action.Name}"); + action.Action?.Invoke(); + } + catch (Exception e) + { + Debug.LogError($"[BootCompletionService] Error executing initialization action '{action.Name}': {e.Message}\n{e.StackTrace}"); + } + } + + // Clear the list after execution + _orderedInitActions.Clear(); + } + + /// + /// Register an action to be executed when boot is complete. + /// If boot is already complete, the action will execute immediately. + /// + /// The action to execute + /// Lower numbers execute first (default: 100) + /// Optional name for debugging + public static void RegisterInitAction(Action action, int priority = 100, string name = null) + { + if (action == null) return; + + name = name ?? $"Anonymous_{_orderedInitActions.Count}"; + + if (IsBootComplete) + { + // Boot already completed, execute immediately + Debug.Log($"[BootCompletionService] Executing late initialization: {name} (boot already complete)"); + try + { + action.Invoke(); + } + catch (Exception e) + { + Debug.LogError($"[BootCompletionService] Error executing late initialization action '{name}': {e.Message}\n{e.StackTrace}"); + } + } + else + { + // Add to ordered list + _orderedInitActions.Add(new OrderedInitAction + { + Action = action, + Priority = priority, + Name = name + }); + Debug.Log($"[BootCompletionService] Registered initialization action: {name} with priority {priority}"); + } + } + + /// + /// Returns a task that completes when the boot process is complete. + /// If boot is already complete, returns a completed task. + /// + public static Task WaitForBootCompletionAsync() + { + if (IsBootComplete) + { + return Task.CompletedTask; + } + + return _bootCompletionTask.Task; + } + + /// + /// Structure for ordered initialization actions + /// + private class OrderedInitAction + { + public Action Action; + public int Priority; + public string Name; + } + } +} diff --git a/Assets/Scripts/Bootstrap/BootCompletionService.cs.meta b/Assets/Scripts/Bootstrap/BootCompletionService.cs.meta new file mode 100644 index 00000000..2f02bd4f --- /dev/null +++ b/Assets/Scripts/Bootstrap/BootCompletionService.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: aa0228cf33a64515bc166b7a9bc8c0b9 +timeCreated: 1760606319 \ No newline at end of file diff --git a/Assets/Scripts/Bootstrap/CustomBoot.cs b/Assets/Scripts/Bootstrap/CustomBoot.cs index a4ea917b..342e1c8e 100644 --- a/Assets/Scripts/Bootstrap/CustomBoot.cs +++ b/Assets/Scripts/Bootstrap/CustomBoot.cs @@ -94,6 +94,19 @@ namespace Bootstrap CurrentProgress = 1f; OnBootProgressChanged?.Invoke(1f); OnBootCompleted?.Invoke(); + + // Notify the BootCompletionService that boot is complete + if (Application.isPlaying) + { + // We use reflection to avoid direct reference in case the service is disabled/removed + var type = System.Type.GetType("Bootstrap.BootCompletionService, Assembly-CSharp"); + if (type != null) + { + var method = type.GetMethod("HandleBootCompleted", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + method?.Invoke(null, null); + } + } } /// @@ -106,6 +119,19 @@ namespace Bootstrap CurrentProgress = 1f; OnBootProgressChanged?.Invoke(1f); OnBootCompleted?.Invoke(); + + // Notify the BootCompletionService that boot is complete + if (Application.isPlaying) + { + // We use reflection to avoid direct reference in case the service is disabled/removed + var type = System.Type.GetType("Bootstrap.BootCompletionService, Assembly-CSharp"); + if (type != null) + { + var method = type.GetMethod("HandleBootCompleted", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + method?.Invoke(null, null); + } + } } diff --git a/Assets/Scripts/Input/InputManager.cs b/Assets/Scripts/Input/InputManager.cs index ada4a561..a095e18f 100644 --- a/Assets/Scripts/Input/InputManager.cs +++ b/Assets/Scripts/Input/InputManager.cs @@ -149,7 +149,7 @@ namespace Input { _isQuitting = true; } - + /// /// Sets the default ITouchInputConsumer to receive input events. ///