User Scope Root & Lifetime (5.1 → 6.0)

This guide explains why a user scope root exists and how to reason about service lifetime.

In 5.1 (GDScript), many projects end up with an implicit global singleton or a scene-root node that “owns everything”.

In 6.0 (C#), the architecture makes this explicit:

  • A composition root owns a ServiceRegistry.
  • A user scope root integrates identity and lifecycle.
  • Workflow adapters orchestrate calls into Core services and state.

Boundary

  • Core: engine-agnostic operations, state, and contracts.
  • Godot: wiring + event translation + UI/visual glue.

Lifetimes

Use these three lifetimes to keep wiring understandable.

1) Per-project

Definition: values that exist once for the whole running Godot application (or once per main scene tree / composition root).

  • Example: the shared ServiceRegistry.
  • Example: shared systems that are not identity-specific.

2) Per-session

Definition: values that exist for the duration of a play session (often aligned with save/load boundaries).

  • Example: a “grid building session” object that can resolve stable per-user scopes.

3) Per-user

Definition: values that are identity-specific (ownership, permissions, input ownership, per-user inventory, etc.).

  • Example: a per-user scope keyed by GPUserId.

The 6.0 reference implementation: GPUserScopeRoot

In the C# demo, the entry point for this wiring is:

  • demos/grid_building_dev/godot_cs/addons/GridPlacement/Godot/Bootstrap/GPUserScopeRoot.cs

Note: the 5.x GDScript addon folder remains res://addons/grid_building for backward compatibility, even when the docs are branded GridPlacement.

At a high level, it does the following:

1
2
3
4
5
6
7
GPUserScopeRoot._Ready()
EnsureRegistry()
RegisterMinimalServices(registry)
EnsureCompositionUserScope(registry)
ResolveInputSettings(registry)
ConfigureCoreInput(registry)

What it registers (examples)

The demo RegisterMinimalServices registers a minimal end-to-end chain:

  • State
    • GridState, GridState2D, TargetingState
  • Services
    • GridService2D
    • ICursorService -> GridCursorService
    • IPlacementService -> PlacementService2D
    • ITargetingLogic -> TargetingService2D
  • Catalog / configuration
    • IPlaceableCatalog (PlaceableCatalog seeded by PlaceableCatalogBootstrap)
  • Workflow adapters
    • IPlacementWorkflowAdapter -> PlacementWorkflowAdapter
  • Godot-facing bridge
    • PlacementWorkflowBridge (translates Godot types -> Core value types)

Identity: profile provider → user scope

The 6.0 pattern expects (optionally) a Godot-side provider:

  • IUserScopeProfileProvider

If present, it supplies identity, and GPUserScopeRoot creates a Composition IUserScope using:

  • GameUserSessions.Core.UserId as the source-of-truth

If absent, a demo-friendly fallback identity is used.

Mapping from 5.1 to 6.0

In 5.1 you often see

  • a global autoload or singleton (implicit registry)
  • controllers that directly touch many nodes/state (implicit scopes)

In 6.0 the target shape is

1
2
3
4
5
6
7
8
9
Godot node (caller)
Workflow bridge (Godot-friendly)
Workflow adapter (Core port)
Core services
Core state

What this page does NOT assert

  • It does not require multi-user support for all projects.
  • It does not require GameComposition/GameUserSessions for 5.1 projects.
  • It does define the preferred 6.0 integration seam so new docs and new code stay consistent.