Project Architecture
The plugin architecture is layered to keep systems modular and testable while supporting flexible game integration.
Layer Overview
| Layer | Contents | Notes |
|---|---|---|
| Core (C#) | GridPlacement.Core / GridBuilding.Core services, state, validation logic | Engine-agnostic. Prefer constructor injection inside Core services. |
| Composition (host-owned) | ServiceRegistry + module registration | Your game registers ports (sessions, saving, etc.) then registers GridPlacement modules. |
| User Scopes | IUserScope / GPUserId-keyed lifetimes | Enables per-user/per-session isolation without global singletons. |
| Godot Integration | GPUserScopeRoot (optional composition root), adapter nodes/bridges | Godot nodes should resolve services/adapters from the registry rather than owning composition rules. |
| UI & Gameplay | Game-owned UI and input layers calling workflow adapters | Prefer depending on workflow adapters (stable contracts) rather than concrete internals. |
| Testing | Core unit tests + targeted Godot integration tests | Push logic into Core. Keep Godot tests focused on adapter wiring and engine glue. |
Dependency Injection Model
In 6.0, composition is registry-based, not injector-based.
Benefits:
- Multitenancy (multiple users/sessions)
- Clear test seams (swap in a fresh registry / fake ports)
- Explicit host ownership (your game controls what gets registered and when)
If you are looking for the previous (5.1 injector-era) dependency injection model, treat these as canonical legacy references:
docs/content/grid-placement/v5-1/guides/runtime-chain.mddocs/content/grid-placement/v5-1/guides/user-scope-root-and-lifetime.md
🎯 GridTargetingState: Authoritative targeting state
6.0.0 Architectural Principle: GridTargetingState is the authoritative, centralized source for what is actively targeted in the entire Grid Building System.
What it manages:
- BUILD mode: The placement preview being positioned
- MOVE mode: The manipulation copy during object manipulation
- Normal gameplay: The node detected by TargetingShapeCast2D
Why this matters:
- Consistency: All systems query the same targeting source
- Sync issues eliminated: No more state divergence between different components
- Simpler integrations: Third-party systems can rely on a single API for targeting info
- Clear responsibilities: Other systems (ManipulationState, BuildingState) focus on their domains; targeting is GridTargetingState’s job
6.0.0 API Improvement: ManipulationData was refactored to support this principle:
- Renamed
target→move_copyto clearly distinguish the manipulation preview from GridTargetingState’s target - Removed
ManipulationState.active_target_node(redundant with GridTargetingState) - Added safe helper methods:
get_move_copy_root()andget_active_root()
This eliminates semantic confusion and reinforces GridTargetingState as the authoritative source for targeting.
See GridTargetingState API for detailed targeting methods, Grid Targeting Guide for architectural details, and Breaking Changes - 6.0.0 for migration guidance.
How the Systems Work Together
Instead of a complex diagram, let’s walk through how the Grid Building system works in simple steps:
The Basic Flow:
- You pick something to build: Click a placeable in the UI
- BuildingSystem wakes up:
enter_build_mode()gets called with your choice - GridPositioner2D handles targeting: Processes mouse/keyboard input to track where you’re pointing on the grid
- TargetingShapeCast2D detects collisions: Updates collision state based on GridPositioner2D position
- IndicatorManager provides live feedback: Sets up indicators that follow your cursor and show placement validity
- PlacementValidator checks rules: Runs tile-based rules as you move the cursor around
- IndicatorManager shows visual feedback: Colors/animations appear to show valid/invalid spots in real-time
- ManipulationSystem (optional): If enabled, lets you move/rotate the preview before placing
- You confirm placement:
BuildingSystem.try_build()runs full validation (including non-tile rules) - Object gets created: If everything checks out, your building appears in the world as a PlaceableInstance
- ManipulationSystem (post-placement): Now you can select, move, rotate, or delete the placed object
v5.0.0 Architectural Improvements:
Clean System Boundaries:
GridPositioner2D ← Pure input handling & positioning
├── TargetingShapeCast2D ← Dedicated collision detection
└── ManipulationParent ← Transform handling container
├── IndicatorManager ← Visual feedback & validation manager
│ ├── RuleCheckIndicator ← Individual rule indicators
│ └── RuleCheckIndicator ← Individual rule indicators
└── PreviewObject ← Object being manipulated
Benefits:
- Unit testable components - Each system tested in isolation
- Composable collision strategies - Swap targeting implementations
- Eliminated race conditions - No hidden coupling between systems
- Reliable test execution - Consistent behavior in all environments
Key Points to Remember:
- BuildingSystem orchestrates the entire build workflow - from mode entry to final placement
- GridPositioner2D handles all input processing and grid coordinate conversion
- IndicatorManager manages the visual feedback system (creates and parents rule indicators)
- ManipulationParent serves as transform container for preview objects and indicators
- ManipulationSystem has two jobs: preview adjustment (if enabled) and post-placement editing
- Validation happens twice: quick tile rules during preview, full rule validation on confirmation
- Each component has a single responsibility but they work together seamlessly
Key Data Flows
- UI Selection → User selects placeable from UI →
BuildingSystem.enter_build_mode(placeable)is called - Build Mode Initialization: BuildingSystem prepares preview with selected placeable
- Input Processing: GridPositioner2D processes mouse/keyboard input and converts to grid coordinates
- Collision Detection: TargetingShapeCast2D updates collision state based on positioner position
- Live Validation: PlacementValidator runs tile rules, IndicatorManager shows live feedback via rule indicators
- Preview Manipulation: If enabled in placeable settings, ManipulationSystem can move/rotate preview instances during placement
- Confirmation: User confirms placement,
BuildingSystem.try_build()runs full rule validation (including non-tile rules) - Instantiation: If valid, BuildingSystem spawns PlaceableInstance with metadata for manipulation & save/load
- Post-Placement: ManipulationSystem handles move/rotate/delete operations on placed instances
Save / Load Considerations
PlaceableInstance holds references back to originating Placeable enabling reconstruction. Systems should avoid storing raw scene node references across sessions; rely on IDs or resource paths.
Logging & Debug Controls
GBDebugSettings gates verbosity. High verbosity surfaces:
- Injection operations
- Indicator setup summaries
- Rule validation traces (future extension)
{/* Testing Strategy moved to internal/testing_strategy.md */}
Extensibility Guidelines
- Favor composition (create a new rule resource) over conditionals inside existing rules.
- Keep resource scripts lean; complex logic belongs in systems.
- Add new UI affordances by observing states rather than polling systems directly.
Planned Evolutions
- Cross-context synchronization primitives (multiplayer scenarios).
- Performance profiling hooks (duration_ms in reports).
- Rule documentation tooling.
Core Architecture References
docs/content/grid-placement/v5-1/guides/runtime-chain.mddocs/content/grid-placement/v5-1/guides/user-scope-root-and-lifetime.md
High-Level System Architecture
6.0 components (conceptual):
- Core services: engine-agnostic placement, targeting, validation, and state logic.
- Service registry: host-owned composition boundary (
ServiceRegistry) where game ports and plugin modules are registered. - User scope: per-user lifetime (identity + disposable scope) used to isolate state and dependencies.
- Adapters/bridges: small Godot-facing nodes that translate engine types and forward to Core/workflows.
- Workflow adapters: stable game-facing orchestration contracts (select → validate → execute).
For the service/adapters boundary, see:
core-godot-integration.mdservice_architecture_and_adapters.md
Last updated: 2025-08-20