Input Routing (Option B) — 6.0 C# Canonical, 5.1 Legacy Glue
Decision
- 6.0: C# Core is canonical for input routing + domain intent emission.
- 5.1: stays GDScript (legacy), but we keep the same conceptual contract at the engine boundary.
Goals
- Single Godot-side input capture point per scene/root (one
_unhandled_inputowner). - Core routing policy lives in one place (priority, gating), not scattered across nodes.
- Domain adapters remain isolated (Positioning vs Placement vs Manipulation).
- Engine ↔ Core boundary is explicit and small.
Non-goals
- No requirement to make 5.1 nodes match 6.0 nodes structurally.
- No multi-node per-domain input capture.
Architecture
Layer 1 — Engine glue (Godot)
Responsibility: capture engine InputEvent and forward to a single handler.
- 5.1: existing injector/root node calls into
GBInputService.handle_unhandled_input(event)(GDScript). - 6.0: new/updated root node calls into C# bridge (or C#-exposed service) that ultimately delegates to the C# router.
Layer 2 — Input bridge (6.0)
Responsibility: convert engine event to Core InputEventData.
InputEvent(Godot) ->InputEventData(Core)- Contains minimal translation logic (mouse move, action press/release, etc.).
Layer 3 — Core router service (6.0)
Responsibility: routing policy + “handled” semantics.
- Holds interpreters in deterministic order.
- Applies gating (mode, settings) and stops on first handled.
Layer 4 — Core domain adapters (6.0)
Responsibility: translate InputEventData into domain intents (events/callbacks).
PositioningInputInterpreterPlacementInputInterpreterManipulationInputInterpreter
Contracts
Core adapter contract
ICoreInputInterpreter.Handle(InputEventData inputEvent) -> bool- Returns
trueiff the adapter consumed the event.
6.0 engine boundary contract
- Single call site from Godot node:
handled = inputService.HandleUnhandledInput(event)(or equivalent)
Routing policy (initial)
Priority order:
- Positioning (mouse move, keyboard movement)
- Placement (build/off/confirm)
- Manipulation (move/demolish/info/off/confirm + rotate/flip)
Gating:
- Placement only active when current mode allows placement.
- Manipulation only active when current mode allows manipulation.
- Positioning can be always-on or settings-gated.
Work plan (tracking)
Phase 1 — Core contracts
- Make
PositioningInputInterpreterimplementICoreInputInterpreter. - Make
PlacementInputInterpreterimplementICoreInputInterpreter. - Make
ManipulationInputInterpreterimplementICoreInputInterpreter. - Ensure adapters remain POCO and unit-testable.
Phase 2 — Core router
- Add
CoreInputServicein Core. - Router accepts interpreters + optional gating dependencies.
- Router enforces deterministic ordering.
Phase 3 — 6.0 Godot bridge
- Implement
InputEvent -> InputEventDatatranslator. - 6.0 root node forwards
_unhandled_inputinto the bridge/router.
Phase 4 — 5.1 compatibility stance
- Keep existing GDScript adapters/services as legacy.
- Ensure documentation clarifies 5.1 is GDScript canonical; 6.0 is C# canonical.
Phase 5 — Verification
- Add/extend Core xUnit tests to prove:
- routing order
- gating behavior
- first-handler-wins semantics
- adapter event emission on input
Demo export (canonical Core -> Godot demo addon)
The Godot C# demo addon consumes Core via a prebuilt DLL reference:
demos/grid_building_dev/godot_cs/addons/GridPlacement/src/Godot/GridBuilding.Godot.csproj<HintPath>..\..\lib\GridPlacement.Core.dll</HintPath>
To keep the demo aligned with canonical Core, build and export the DLL from the plugin workspace:
- Script:
plugins/gameplay/GridPlacement/scripts/export_core_to_demo.sh
- Output copied to:
demos/grid_building_dev/godot_cs/addons/GridPlacement/lib/GridPlacement.Core.dll
This export step is required whenever Core changes that must be reflected in the demo.
Open questions
- Which 6.0 node is the single input-capture owner (composition root)?
- Answer:
GBUserScopeRoot(single required per-user/context node).
- Answer:
- Where does mode live in 6.0 (Core service vs engine state) for gating?
- Do we need mouse button state aggregation (e.g., drag) at the bridge layer or Core layer?