# Apple Hills Bootstrap System A concise, code-first overview of the project’s bootstrap system. It covers how boot runs (runtime and editor), how to wait for boot to complete, how to register post-boot initialization, how to retrieve bootstrapped systems safely, and practical case studies. ## Table of Contents - [What This Solves](#what-this-solves) - [Architecture at a Glance](#architecture-at-a-glance) - [Quick Start (Code-First)](#quick-start-code-first) - [Check/Wire Boot Completion](#checkwire-boot-completion) - [Register Post-Boot Initialization](#register-post-boot-initialization) - [Await Boot Asynchronously](#await-boot-asynchronously) - [Retrieve Bootstrapped Systems Safely](#retrieve-bootstrapped-systems-safely) - [Editor Workflow (Project Settings + Edit-Mode Boot)](#editor-workflow-project-settings--edit-mode-boot) - [Enable/Disable Edit-Mode Boot](#enabledisable-edit-mode-boot) - [What Happens in Edit Mode (Caveats)](#what-happens-in-edit-mode-caveats) - [Case Studies](#case-studies) - [UI Page Controller post-boot wiring](#ui-page-controller-post-boot-wiring) - [Card System post-boot registration](#card-system-post-boot-registration) - [Settings access after boot](#settings-access-after-boot) - [Events, APIs, and Keys](#events-apis-and-keys) - [Troubleshooting / FAQ](#troubleshooting--faq) - [Paths & Namespaces](#paths--namespaces) - [Change Log](#change-log) ## What This Solves - Centralizes startup of global systems via a data-driven list of prefabs. - Guarantees an explicit moment when all bootstrapped systems are ready. - Provides a robust way to run your own initialization code after boot, including priority ordering. - Supports editor preview of the booted world without having to enter Play Mode. ## Architecture at a Glance - Runtime orchestrator: `CustomBoot` (static) - Starts automatically via `RuntimeInitializeOnLoadMethod(BeforeSplashScreen)`. - Loads `CustomBootSettings` with Addressables and instantiates `BootPrefabs` under a runtime container (`DontDestroyOnLoad`). - Emits progress via `CustomBoot.OnBootProgressChanged` and completion via `CustomBoot.OnBootCompleted`. - Calls `BootCompletionService.HandleBootCompleted()` when done to unlock post-boot actions. - Boot data: `CustomBootSettings` (`ScriptableObject`) - Field: `GameObject[] BootPrefabs` — prefabs to instantiate at boot (usually contain your singletons/services). - Methods: `Initialise()`/`InitialiseSync()` instantiate prefabs and report progress; `Cleanup()` destroys them. - Post-boot coordination: `BootCompletionService` (static) - `bool IsBootComplete` — runtime flag. - `event Action OnBootComplete` — notify late subscribers. - `void RegisterInitAction(Action action, int priority = 100, string name = null)` — queue work to run after boot (priority: lower runs earlier). If boot has already completed, runs immediately. - `Task WaitForBootCompletionAsync()` — await boot in async flows. - Editor integration: `CustomBootEditorUtils`, `CustomBootSettingsProvider`, `CustomBootProjectSettings` - Project settings UI to assign Addressable references for runtime/editor boot settings. - Edit-mode boot toggle to preview booted state in the Scene view. ## Quick Start (Code-First) ### Check/Wire Boot Completion ```csharp using Bootstrap; using UnityEngine; public class BootGateExample : MonoBehaviour { private void Awake() { if (BootCompletionService.IsBootComplete) { SafeStart(); } else { BootCompletionService.OnBootComplete += SafeStart; } } private void OnDestroy() { BootCompletionService.OnBootComplete -= SafeStart; } private void SafeStart() { // Your code here will only run once all BootPrefabs have been instantiated. Debug.Log("All systems ready – starting feature!"); } } ``` ### Register Post-Boot Initialization Register work to run once all bootstrapped systems are ready. Two equivalent approaches are supported: 1) Inline lambda (great for small one‑offs) ```csharp using Bootstrap; // Lower priority runs earlier; name is used in logs. BootCompletionService.RegisterInitAction( action: () => AnalyticsService.Instance.StartSession(), priority: 50, name: "Start Analytics Session" ); // Can also skip priority and name: BootCompletionService.RegisterInitAction( action: () => AnalyticsService.Instance.StartSession() ); ``` 2) Full function callback (method group) ```csharp using Bootstrap; using UnityEngine; public class AnalyticsBootstrap : MonoBehaviour { private void Awake() { // Pass a method group (no lambda needed). Helpful for reuse and testing. BootCompletionService.RegisterInitAction( action: InitAnalytics, priority: 50, name: "Init Analytics" ); // Can also skip priority and name: BootCompletionService.RegisterInitAction( action: InitAnalytics ); } private void InitAnalytics() { AnalyticsService.Instance.StartSession(); // You can call other setup methods here as needed } } ``` Notes: - Only the callback parameter is required. You can omit the priority and name parameters. If you don't have use for them, you probably should. - If boot has already completed when you call `RegisterInitAction`, your callback runs immediately. - Use `priority` to control ordering (lower numbers run earlier); `name` helps with log readability. ### Await Boot Asynchronously ```csharp using System.Threading.Tasks; using Bootstrap; public class AsyncBootConsumer { public async Task StartAfterBootAsync() { await BootCompletionService.WaitForBootCompletionAsync(); // Now safe to query singletons installed by boot prefabs var inv = CardSystemManager.Instance; // example } } ``` ### Retrieve Bootstrapped Systems Safely Boot usually instantiates service prefabs that expose global accessors (singletons or service locators). Always guard access behind boot completion. ```csharp using Bootstrap; using UnityEngine; public class UseServicesAfterBoot : MonoBehaviour { private void Start() { BootCompletionService.RegisterInitAction(() => { // Example: get your services here var pages = UI.Core.UIPageController.Instance; // created in a boot prefab or scene var settings = AppleHills.Core.Settings.SettingsProvider.Instance; Debug.Log("Services acquired and ready."); }, priority: 100, name: "Acquire Services"); } } ``` ## Editor Workflow (Project Settings + Edit-Mode Boot) - Project Settings UI: `Project/Custom Boot` (`CustomBootSettingsProvider`) - Assign Addressables references for `RuntimeSettings` and `EditorSettings` in `CustomBootProjectSettings`. - Backed by assets `CustomBootSettings_Runtime` and `CustomBootSettings_Editor` that list `BootPrefabs` to spawn. - Asset management helper: `CustomBootSettingsUtil` - Creates missing assets and Addressables groups/entries automatically: - Runtime asset path: `Assets/Data/Bootstrap/Runtime/CustomBootSettings_Runtime.asset` - Editor asset path: `Assets/Data/Bootstrap/Editor/CustomBootSettings_Editor.asset` - Project settings asset path: `ProjectSettings/CustomBoot.asset` - Addressables keys: `CustomBootSettings_Runtime`, `CustomBootSettings_Editor`. ### Enable/Disable Edit-Mode Boot - Menu toggle: `Bootstrap/Editor Initialise` (`CustomBootEditorUtils`). - When enabled in Edit Mode (not Play), the system instantiates the `BootPrefabs` into the currently open scene so you can preview booted systems. ### What Happens in Edit Mode (Caveats) - `DontDestroyOnLoad` does not apply in edit mode; booted objects are added to the current scene. - Entering Play Mode de-initializes any edit-mode boot and hands off to the runtime boot. - Scene save is guarded: edit-mode boot de-initializes just before save to avoid polluting the scene, then re-initializes after. - Changing the active scene in Edit Mode triggers de-init/re-init of edit-mode boot. ## Case Studies ### UI Page Controller post-boot wiring `UIPageController` registers a post-boot hook: ```csharp // Inside UIPageController.Awake() Bootstrap.BootCompletionService.RegisterInitAction(InitializePostBoot); ``` This guarantees input/transition wiring happens once systems from `BootPrefabs` are in place. ### Card System post-boot registration To ensure the card data/UI only touches `CardSystemManager` after boot: ```csharp using Bootstrap; using AppleHills.Data.CardSystem; BootCompletionService.RegisterInitAction(() => { var mgr = CardSystemManager.Instance; // subscribe to events, warm up caches, etc. }, priority: 90, name: "CardSystem Init"); ``` ### Settings access after boot If your settings provider lives in a boot prefab, wait for boot before first access: ```csharp using Bootstrap; using AppleHills.Core.Settings; BootCompletionService.RegisterInitAction(() => { var gameplay = SettingsProvider.Instance.GetSettings(); Debug.Log($"DefaultPromptRange: {gameplay.DefaultPuzzlePromptRange}"); }, name: "Load Interaction Settings"); ``` ## Events, APIs, and Keys - `CustomBoot` (static) - `bool Initialised` - `float CurrentProgress` - `event Action OnBootProgressChanged` - `event Action OnBootCompleted` - `BootCompletionService` (static) - `bool IsBootComplete` - `event Action OnBootComplete` - `void RegisterInitAction(Action action, int priority = 100, string name = null)` - `Task WaitForBootCompletionAsync()` - Addressables keys - `CustomBootSettings_Runtime` - `CustomBootSettings_Editor` ## Troubleshooting / FAQ - My code runs before services exist: - Wrap it in `BootCompletionService.RegisterInitAction(...)` or check `IsBootComplete` and subscribe to `OnBootComplete`. - Boot never seems to complete in Edit Mode: - Edit-mode boot is a preview; verify the menu toggle `Bootstrap/Editor Initialise` is enabled and that your `CustomBootSettings_Editor.asset` lists the necessary `BootPrefabs`. - I get duplicate singletons after entering Play Mode: - Edit-mode boot de-inits on entering Play. If you manually created objects, ensure you destroy them; prefer boot prefabs. - Addressables key not found: - Open `Project/Custom Boot` and ensure both runtime and editor settings assets exist and are assigned; let the utility create missing assets. ## Paths & Namespaces - Runtime scripts: `Assets/Scripts/Bootstrap/` - `CustomBoot.cs`, `CustomBootSettings.cs`, `BootCompletionService.cs` - Editor scripts: `Assets/Editor/Bootstrap/` - `CustomBootSettingsProvider.cs`, `CustomBootEditorUtils.cs`, `CustomBootProjectSettings.cs`, `CustomBootSettingsUtil.cs` - Addressables keys: `CustomBootSettings_Runtime`, `CustomBootSettings_Editor` - Namespace: `Bootstrap` (runtime), `Editor.Bootstrap` (editor) ## Change Log - v1.0: Initial overview document with TOC, code-first playbooks, editor workflow, case studies, and API summary.