-
Notifications
You must be signed in to change notification settings - Fork 0
Bootstrap Handlers
Bootstrap handlers are how you hook into the two edges of the Bootstrap Flow: once right before any service is created, and once right after every service has finished initializing.
public interface IPreBootstrapHandler
{
Awaitable OnPreBootstrapAsync(in BootstrapContext context);
}
public interface IPostBootstrapHandler
{
Awaitable OnPostBootstrapAsync(BootstrapContext context);
}OnPreBootstrapAsync runs at the PreBootstrap step, before service discovery. OnPostBootstrapAsync runs at the PostBootstrap step, after service init and the async task flush have both completed, so every service is fully live by the time it runs.
An Environment has a PreBootstrapHandler and a PostBootstrapHandler field, each a polymorphic [SerializeReference] you assign the same way you add a service to a Service List, by picking a concrete type implementing the interface from a dropdown.
Setting one of these fields replaces the platform default for that slot entirely, it doesn't run alongside it. If you assign a custom PostBootstrapHandler and still want the default behavior (like restoring your edit mode scenes), your handler has to call into that behavior itself. See the example below.
If an environment doesn't supply its own handler, one of these runs instead, depending on context:
| Context | Handler | Behavior |
|---|---|---|
| Editor Play mode | PlayModeBootstrapHandler |
Pre-bootstrap stashes the saved edit mode scene/selection snapshot onto the context. Post-bootstrap reloads those scenes and restores selection, as described in Getting Back to Your Scenes. |
| Build | BuildBootstrapHandler |
Pre-bootstrap does nothing. Post-bootstrap loads the scene at build index 1 (skipped during test runs). |
Both are singletons (PlayModeBootstrapHandler.Instance, BuildBootstrapHandler.Instance) used for both the pre and post slots. You can also use AppInstance.GetDefaultBootstrapHandlers to get access to instances of the default implementations for your given execution environment (editor vs player build).
If the Addressables package is present, Bootstrap also ships AddressablesBootstrapHandler, a post-bootstrap-only handler you assign per environment (it's not an automatic default the way the two above are). Instead of loading a scene from the build list, it loads a scene by Addressables key:
[field: SerializeField]
private string Key { get; set; }
[field: SerializeField]
private LoadSceneParameters LoadSceneParameters { get; set; }Assign it to an environment's PostBootstrapHandler, set its Key to the Addressables address of your scene, and it replaces BuildBootstrapHandler's "load scene 1" behavior with an Addressables-sourced load instead.
Because setting a custom handler replaces the default rather than layering on top of it, a handler that wants to do extra work and keep the default behavior has to call the default itself. You can use AppInstance.GetDefaultBootstrapHandlers to get access to instances of the default implementations.
Here's a post-bootstrap handler that checks whether one of the scenes you had open in edit mode is a specific gameplay scene, and if so, starts a game session before those scenes get loaded back in:
[Serializable]
public class CustomBootstrapHandler : IPostBootstrapHandler
{
public async Awaitable OnPostBootstrapAsync(BootstrapContext context)
{
// context.EditModeState can be null
List<string> loadedScenes = context.EditModeState?.LoadedScenes;
bool enteringGameplayScene = loadedScenes != null
&& loadedScenes.Any(path => path.Contains("Gameplay"));
if (enteringGameplayScene)
{
var gameSessionService = App.Locate<GameSessionService>();
gameSessionService.CreateSession();
}
// Still hand off to the default behavior, otherwise the edit mode
// scenes this handler just checked for never actually get restored.
App.Instance.GetDefaultBootstrapHandlers(
out _,
out IPostBootstrapHandler defaultPostBootstrapHandler)
await defaultPostBootstrapHandler.OnPostBootstrapAsync(context);
}
}