Context & State


This page clarifies the context objects and state containers that flow through the Grid Building systems.

If you are looking for the previous (5.1 injector-era) architecture, treat these as canonical legacy references:

  • docs/content/grid-placement/v5-1/guides/runtime-chain.md
  • docs/content/grid-placement/v5-1/guides/user-scope-root-and-lifetime.md

Why Distinguish Context vs State?

  • Context = relatively stable service/registry style objects (DI container, manager singletons, configuration blocks) passed around for lookups.
  • State = ephemeral, frame or interaction scoped data (current selection, targeting position, last rule results) that mutates frequently.

Separating them helps keep rule evaluation & indicators pure (depend on inputs, not global singletons) and simplifies testing (inject mock contexts, build explicit state).

Core Context Objects

ObjectPurposeNotes
ServiceRegistryHost-owned composition root for registering ports, plugin modules, and workflows.Prefer resolving services/adapters instead of relying on globals or engine singletons.
IGridBuildingSessionHost-provided session/identity seam used by workflows to map GPUserIdIUserScope.Your game can register a real implementation; Godot demos may provide a minimal default.
IUserScopePer-user lifetime container (identity + scope + disposables).Enables multi-user/multi-session isolation.
Configuration objectsGame-owned config and plugin config registered into the registry.Prefer explicit config services instead of monolithic “container resource” ownership.

State Containers

StateFields (Representative)Lifecycle
BuildingStateplaced_parent: Node, preview: Node; owner from GBOwnerContext. Signals: success, failed, preview_changed, placed_parent_changed, system_changed.Build mode session
ManipulationStatedata: ManipulationData, active_manipulatable: Manipulatable, active_target_node: Node, parent: Node2D. Signals: started, confirmed, finished, canceled, failed, plus change signals.Active while previewing/manipulating
ModeStatecurrent: GBEnums.Mode (OFF/BUILD/DEMOLISH/INSPECT). Signal: mode_changed.Changes via input/mode toggles
GridTargetingStateready: bool, target: Node2D, positioner: Node2D, target_map: TileMapLayer, maps: Array[TileMapLayer]. Signals: ready_changed, target_changed, positioner_changed, target_map_changed, maps_changed.Revalidated on movement/changes

Rule evaluation inputs (what actually gets passed)

For placement rules, the only runtime input passed into rules is the GridTargetingState:

  • The validator calls PlacementRule.setup(p_gts: GridTargetingState) for each rule before evaluation.
  • Rules read from GridTargetingState (e.g., target, target_map, maps) and derive their validation results.

What rules do NOT receive directly:

Implicit dependencies managed by systems (not passed to rules):

Summary:

  • Keep rules pure over GridTargetingState. Configure the state via GBLevelContext and DI once, then let rules evaluate.
  • Use BuildingState for post-validation signals and instantiation via placed_parent.
  • For rules that need owner context (e.g., inventory spending), read the placer from GridTargetingState:
    • GridTargetingState.get_owner() or get_owner_root() provide access to the owner entity/root.
    • Example: SpendMaterialsRuleGeneric pulls its spender via the targeting state and locates the material container from there.

Identity vs Owner vs Engine Owner IDs

  • User identity (who is acting):
    • Represented conceptually as a UserId (and optionally a GameSessionId) at the game/backend level.
    • The host game is responsible for resolving the current user/session and passing that identity into GridBuilding-facing APIs (for example, placement or demolition commands).
    • GridBuilding does not reference PlayerSessions directly; it treats user/session identifiers as opaque values supplied by the host.
  • Owner / owner root (engine entity):
    • get_owner() / get_owner_root() and related Owner/OwnerContext concepts represent the engine entity (player character, NPC, controller node) that owns or drives a grid-building context.
    • Use this when rules or systems need access to the scene entity (for parenting, material containers, etc.), not when they just need a user ID.
  • Engine-specific owner IDs (Godot APIs):
    • Some Godot APIs expose their own “owner IDs” (for example, CollisionObject2D shape owner IDs used in collision utilities).
    • These IDs are engine-internal identifiers, not user or session IDs.
    • When you see fields like shapeOwnerId or collisionOwnerId in GridBuilding’s Godot utilities, treat them strictly as wrappers over Godot’s engine API, not as gameplay identity.

Summary:

  • Use user/session identity (supplied by the host game) for attribution, persistence, and cross-plugin coordination.
  • Use owner/owner root when you need the actual engine entity/node that is performing an action within GridBuilding.
  • Treat engine owner IDs from Godot as low-level technical details that should never be repurposed as user IDs.

Immutability Guidelines

  • Context objects: treat as effectively immutable once gameplay starts. If dynamic reconfiguration is required (e.g. hot‑reloading settings), expose a narrow API that emits change events—avoid direct field mutation relied on by many subsystems.
  • State objects: may be mutated, but prefer wrapping mutations in clearly named system methods (set_rotation_deg(), select_variant()) to centralize side effects (cache invalidation, events).

{/* Testing Strategy moved to internal/testing_strategy.md */}

Cross‑References


Support / Purchase Hub: Linktree – All Grid Builder Links

Glossary

Placement Context
Packaged snapshot object containing the minimal inputs required for placement rule evaluation at a given frame: targeting state, manipulation state, config, systems, level, and owner contexts. Built fresh (or partially reused) whenever cursor position, rotation, variant, or selection changes. Its purpose is to present rules with an immutable view for pure evaluation—rules should not mutate it. See assembly details above.