Grid Placement 6.0.0 separates what the game supports, what each player is doing, and how input is routed.
Version note: This guide is for Grid Placement 6.0.0. For first-time setup, start with Getting Started.
The Three Scopes
| Scope | Main type | Owns |
|---|---|---|
| Game-scoped | GridPlacementBundle / GridPlacementSettings |
Shared configuration: settings, actions, templates, rules, dimensions. |
| Player-scoped | PlacementSession |
Mutable state for one player/controller: targeting, placement, manipulation, mode, selected terrain/placeable, logger, contexts. |
| Runtime/input-scoped | GridPlacementHost |
Input dispatch, session registry, service group, and host-owned infrastructure. |
Settings determine which domains exist. Sessions own player/controller state. The host resolves the active session and dispatches work.
Ownership Split
Controller / Player
└── owns PlacementSession
├── PlacementStates
│ ├── ModeState
│ ├── GridTargetingState
│ ├── PlacementState
│ └── ManipulationState
├── PlacementContexts
└── PlacementLogger
GridPlacementHost
├── registers controller sessions
├── owns PlacementServiceGroup
│ ├── terrain service
│ ├── manipulation service
│ ├── targeting service
│ ├── brush coordinator
│ ├── refund service
│ └── drag service
└── owns input/mode dispatch infrastructure
Scene adapters such as GridPositioner2D, TargetingShapeCast2D, TerrainPreview, and UI nodes live in the scene tree and resolve into the active session. The host does not need hard-coded scene paths to those nodes.
PlacementSession
A PlacementSession is a resource that holds the mutable runtime state for one controller.
var session := PlacementSession.new()
session.grid_placement_bundle = preload("res://config/grid_placement_bundle.tres")
session.catalog = preload("res://catalog.tres")
Each session should be treated as isolated. Do not share one session between two active players unless you intentionally want them to share selection, targeting, mode, and action history.
PlacementStates structure
| Field | Type | What it tracks |
|---|---|---|
states.mode |
ModeState |
Current placement/manipulation mode. |
states.targeting |
GridTargetingState |
Cursor cell, target map, maps array, positioner, target object. |
states.building |
PlacementState |
Preview, placed parent, placement signals, terrain signals. |
states.manipulation |
ManipulationState |
Active manipulatable, manipulation parent, refunder registry. |
How Services Work
Services are RefCounted logic objects held by PlacementServiceGroup. They operate on the active session's state objects.
When the host dispatches input for another controller, it activates that controller's session and swaps service state pointers to that session's PlacementStates.
The important rule:
Services do logic. Sessions own state. Host routes input.
Single-Player Convenience
For a single-player project, the setup can be simple:
@onready var host: GridPlacementHost = $GridPlacementHost
var session := PlacementSession.new()
func _ready() -> void:
session.grid_placement_bundle = preload("res://config/grid_placement_bundle.tres")
host.configure(session)
Editor-composed template scenes can use PlacementInjectorSystem to configure and register a session automatically.
Scoped / Controller-Owned Sessions
For split-screen or per-controller setup, each controller creates and registers its own session.
@export var host: GridPlacementHost
@export var device_id: int = -1
var session := PlacementSession.new()
func _ready() -> void:
session.grid_placement_bundle = preload("res://config/grid_placement_bundle.tres")
host.register_session(self, session, device_id)
func _exit_tree() -> void:
if host != null:
host.unregister_session(self)
Use PlacementInjectorSystem.set_session_scope(controller_root, session) when you want injection limited to one controller subtree.
Pre-6.0 vs 6.0 Mental Model
| Concern | Older mental model | 6.0 mental model |
|---|---|---|
| State ownership | Individual system nodes | PlacementSession per controller |
| Input entry point | Multiple system nodes | GridPlacementHost dispatch |
| Logic organization | Node lifecycle mixed with logic | Host/session/services split |
| Multi-controller support | Global-ish state | Session registry + device routing |
| Scene wiring | Scene paths and system references | Injection/context/session resolution |
Key Rule
Settings determine which placement domains exist. Sessions own who is doing what. The host owns routing and dispatch. Scene adapters and UI should resolve dependencies through the session/context path rather than finding private nodes by hard-coded paths.
Related Guides
Source
docs/v6-0/guides/session-ownership.md
Plugin docs root:gdscript/plugins/grid_placement_dev/docs