Add a playbook for using the boostrap system

This commit is contained in:
Michal Adam Pikulski
2025-10-21 14:19:58 +02:00
parent 8e90878a97
commit 9b6dc0f616

267
docs/bootstrap_readme.md Normal file
View File

@@ -0,0 +1,267 @@
# Apple Hills Bootstrap System
A concise, code-first overview of the projects 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 oneoffs)
```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<InteractionSettings>();
Debug.Log($"DefaultPromptRange: {gameplay.DefaultPuzzlePromptRange}");
}, name: "Load Interaction Settings");
```
## Events, APIs, and Keys
- `CustomBoot` (static)
- `bool Initialised`
- `float CurrentProgress`
- `event Action<float> 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.