Runtime Chain: Nodes → Workflows → Services → State (5.1 → 6.0)

The most useful way to understand or debug GridPlacement is to follow a chain from the Godot-facing entrypoint down to the backend logic.

This page documents that chain for 5.1 (GDScript) and shows how the same mental model maps to 6.0 (C#).

Boundary

  • Godot layer (engine/UI glue)
    • Nodes, scenes, visuals, input, signals.
    • Allowed to reference Godot types.
  • Backend logic (workflow/services/state)
    • The place where placement/manipulation rules live.
    • In 6.0, this is the C# Core DLL (engine-agnostic).

The 5.1 chain (GDScript)

In 5.1, you will typically see a GDScript node call into a workflow/controller script that then calls deeper logic.

1
2
3
4
5
6
7
8
9
Godot Node (scene)
  ↓ (signals/calls)
GDScript workflow adapter / orchestrator
  ↓ (calls)
Backend workflow logic
Services (operations + events)
State (data)

5.1 input routing (no binding layer)

In the 5.1 GDScript track, the input boundary is intentionally direct:

1
2
3
4
5
6
7
8
9
Godot InputEvent
GBInputRouter (routes events in order)
Input adapters (Positioning / Placement / Manipulation)
Workflow orchestrators (PlacementWorkflowOrchestrator / ManipulationWorkflowOrchestrator)
GBOrchestratorOutput (emitted via signals)

Rules:

  • Adapters call orchestrators directly. There is no separate “binding” layer in 5.1.
  • TargetingService2D is injected into adapters as the authoritative resolver for:
    • mouse position → targeting snapshot
    • current target → Node2D
  • Effects are applied by workflow/services; adapters only translate input into workflow calls.

5.1 output effects (GBEffectApplier)

5.1 uses an explicit output/effects model to keep the workflow layer testable and to keep scene mutation in one place.

1
2
3
4
5
6
7
Input adapter
  ↓ emits
GBOrchestratorOutput (models + effects)
  ↓ consumed by
GBEffectApplier (engine-glue interpreter)
  ↓ emits typed signals
UI / scene nodes (spawn/update/clear preview, show indicators, play feedback)

Notes:

  • GBEffectApplier is not a C#-style “binding layer” for intent → backend services. In 5.1, adapters already call workflow orchestrators directly.
  • GBEffectApplier exists to interpret GBOrchestratorEffect entries and broadcast them as signals; scene nodes decide how to render/apply them.

How to use this chain in practice

  • Start at the node or autoload your gameplay code calls.
  • Identify the workflow adapter it delegates to.
  • Trace the call into the backend workflow.
  • Identify the service(s) that actually execute the operation.
  • Finally, identify which state object(s) are mutated/read.

Concrete examples from the 5.1 demo (evidence-based)

These examples are intended to help you follow the chain in real files.

Example A: world boot → container → registry → mode

File:

  • demos/grid_building_dev/godot/demos/shared/world/world.gd

Chain:

1
2
3
4
5
6
7
World._ready()
World.resolve_gb_dependencies(p_container: GBCompositionContainer)
container.get_mode_service()  (ModeService)
mode_service.set_mode(GBEnums.Mode.BUILD)

This is a canonical example of the 5.1 composition-container / registry flow: even when you start from a normal scene node, you quickly land in the container/registry to reach backend services.

Example B: placement lifecycle test → BuildingSystem → PlacementService / TargetingService2D

File:

  • demos/grid_building_dev/godot/test/grid_building/systems/placement/integration/placement_lifecycle_tests.gd

Chain (high-level):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
BuildingSystem.enter_build_mode(placeable)
env.get_container() (GBCompositionContainer)
container.get_placement_service()  (PlacementService)
container.get_targeting_service()  (TargetingService2D)
placement_service.exit_place_mode()
targeting_service.get_collision_exclusions()

This is a concrete example of why the “chain” model is useful: even when a high-level system node drives the flow, the authoritative lifecycle invariants (preview cleanup, collision exclusions) are enforced by backend services.

Example C: container → workflow factory → placement workflow adapter → placement workflow orchestrator

File:

  • demos/grid_building_dev/godot/addons/grid_building/resources/gb_composition_container.gd

Chain (high-level):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
GBCompositionContainer.get_build_orchestrator()
GBWorkflowFactory.create_build_orchestrator()
PlacementWorkflowAdapter.new(
  registry.get_placement_service(),
  registry.get_indicator_service()
)
PlacementWorkflowOrchestrator.new(adapter)

Key point:

  • The orchestrator now depends on a workflow adapter API, not on raw services.
  • This keeps service wiring inside the factory/composition layer, and keeps the orchestrator focused on workflow state + decision logic.

The 6.0 chain (C#) you should be targeting

The 6.0 design makes the chain more explicit and consistent.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Godot scene tree
Adapter / system nodes (C# Godot Nodes)
Core workflow port (interface)
Core workflow adapter (concrete)
Core services
Core state

For the canonical explanation, see:

  • ../../v6-0/guides/service_architecture_and_adapters/
  • ../../v6-0/guides/core-godot-integration/

Ports / adapters (what to look for)

In 6.0, the “workflow boundary” is intentionally represented as a port interface, for example:

  • IPlacementWorkflowAdapter

…and a Core implementation:

  • PlacementWorkflowAdapter

Godot nodes should be thin adapters: they convert Godot types (like Vector2I) to Core value types and call the workflow port.

Identity & lifetime (why user scope root exists)

6.0 treats identity and lifetime as first-class integration concerns.

1
2
3
4
5
6
7
8
Project lifetime (scene tree)
GPUserScopeRoot
  - owns/receives ServiceRegistry
  - resolves user identity (provider)
  - provides per-user scope/session integration
Workflow adapters + services

Lifetime rules of thumb

  • Per-project: composition root / service registry.
  • Per-session: “current play session” scope (save/load boundary).
  • Per-user: identity-specific scope for inputs, ownership, and persistence.

Testing alignment

  • Prefer Core unit tests for deterministic logic.
  • Prefer Core integration tests for wiring and workflow orchestration.
  • Use Godot tests only for:
    • resource loading
    • input translation
    • signal hookups / adapter behavior

If you are working on 6.0 wiring, the canonical Core test suite to extend is:

  • plugins/gameplay/GridPlacement/cs/Core/Tests/Core/GridBuilding.Core.Tests/PlacementWorkflowOrchestratorIntegrationTests.cs

What this page does NOT assert

  • It does not guarantee the exact class names in 5.1 (GDScript) are stable.
  • It does not promise a compatibility bridge between 5.1 and 6.0 at runtime.
  • It does define the recommended debugging mental model and the forward architecture target.

Evolution: 5.0 → 5.1 → 6.0 (clean architecture)

This section explains how the architecture evolved so you can interpret older docs and older code correctly.

5.0: service-based principles (direction established)

The shipped 5.0.0 line is best understood as a composition-container / injector era.

  • Composition was typically handled through GBCompositionContainer and GBInjectorSystem patterns.

In other words: 5.0 was not the finalized “ports + adapters” service architecture yet.

Reference (legacy integration pattern):

  • ../../v5-0/csharp-state-integration/

Note on docs:

  • ../../v5-0/service-based-architecture/ should be treated as a design direction / post-hoc architecture doc, not a guarantee that 5.0.0 shipped that fully in its runtime wiring.

What 5.0.0 did not standardize yet:

  • A stable, explicit “workflow port” boundary (a single place to call that orchestrates services)
  • Explicit identity/lifetime seams (project/session/user)
  • A strict rule that Godot nodes only speak to narrow workflow ports (instead of a broad bridge/container surface)

5.1: stabilization + a traceable runtime chain

The 5.1 line keeps the plugin stable but makes it easier to reason about the runtime by encouraging a traceable chain:

1
2
3
4
5
6
7
Node / UI glue
Workflow adapter / orchestrator
Services
State

What got better in 5.1:

  • A more repeatable “start from the node and trace downward” debugging workflow
  • Clearer separation between “public Node API” and internal helpers
  • A documented “do not break public API casually” stability contract

What is still not fully finalized in 5.1:

  • The boundary is still primarily Node-shaped (public APIs are nodes, dictionaries, signals)
  • Identity/lifetime is often still implicit (autoloads / singleton roots)

Naming + folder layout (5.1)

  • Brand (docs): GridPlacement (5.1+)
  • Godot addon folder: res://addons/grid_building (kept for backward compatibility)

6.0: clean architecture final form (ports + adapters + explicit lifetimes)

6.0 finalizes the architecture by making the boundaries explicit and enforceable:

  • Ports (Core interfaces) define the integration surface (example: IPlacementWorkflowAdapter).
  • Adapters (engine layer) are thin nodes/bridges that convert Godot types to Core value types.
  • Composition root + registry makes service lifetime explicit.
  • User/session seams are first-class so multi-user or per-owner behaviors are testable.

Canonical docs:

  • ../../v6-0/guides/service_architecture_and_adapters/
  • ../../v6-0/guides/core-godot-integration/

Rule of thumb:

  • In 5.1 you can get work done by calling nodes.
  • In 6.0 you keep things maintainable by tracing everything through:
    • engine adapters → workflow ports → services → state.

Testing: where each behavior should live

This is the recommended split when you are trying to keep 5.1 stable but move architecture toward 6.0.

Core unit tests (fast, deterministic)

Use when you are proving:

  • validation rules
  • coordinate conversions (Core value types)
  • state transition logic

Core integration tests (preferred for workflow wiring)

Use when you are proving:

  • workflow orchestration calls the right internal components
  • per-user session support behaves correctly
  • readiness checks gate placement/manipulation correctly

Reference suite to extend:

  • plugins/gameplay/GridPlacement/cs/Core/Tests/Core/GridBuilding.Core.Tests/PlacementWorkflowOrchestratorIntegrationTests.cs

Godot tests (glue only)

Use only when you are proving:

  • input translation (InputEvent -> Core input events)
  • signal hookups / adapter nodes emitting the right Godot signals
  • resource loading (catalogs, settings resources)

Avoid using Godot tests for core placement logic when a Core test is possible.