Core/Godot DLL Integration Guide
This guide covers the new 6.0.0 architecture where GridPlacement’s core logic is separated into an engine-agnostic C# DLL with a dedicated Godot integration layer.
Naming Note โ GridBuilding โ GridPlacement 6.0
In 5.x, the Godot plugin shipped as GridBuilding. For the C#โbased 6.0 line, GridPlacement (GP) is canonical across the board (plugin name, docs, assemblies, and C# namespaces). When this guide talks about the “core DLL” and “Godot integration layer”, it is describing the GridPlacement 6.0 C# plugin.
Runtime chains
When debugging, prefer tracing the runtime chain before diving into implementation details:
๐๏ธ Architecture Overview
Two-Layer Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Godot Layer โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Godot Systems & UI Nodes โ โ โ Call services + listen to events
โ โ Adapter Nodes (optional) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Core DLL โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Services & Domain Events โ โ โ Engine-agnostic logic
โ โ BuildingState & other state โ โ
โ โ Validation & Error Handling โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Core DLL (GridPlacement.Core.dll)
- Engine Agnostic: No Godot dependencies
- Business Logic: Building placement, validation, state management
- Event System: C# events for all building operations
- Thread Safe: ReaderWriterLockSlim for concurrent access
- Testable: Can be unit tested independently
Godot Integration Layer
- Adapter / system nodes: Thin Godot nodes that resolve Core services via DI
and forward domain events to Godot signals or local state.
- Godot Systems: Engine-specific implementations that call into services.
- UI Integration: GDScript-friendly wrappers around service APIs and events.
- Resource Management: Godot-specific asset handling.
First-class wiring (GameComposition + GameUserSessions)
GridPlacement 6.0 is designed so identity and lifetime are explicit integration concerns.
The shipped Godot addon stays self-contained: GPUserScopeRoot provides a minimal default session model and input routing without requiring GameComposition/GameUserSessions.
If your host game uses GameComposition/GameUserSessions, implement identity/session wiring in an integration layer and validate it with the GridPlacement.Integrations.* test projects.
๐ Quick Start
1. Project Setup
1
2
3
4
5
6
7
8
9
10
11
12
| # Your project structure
your_project/
โโโ addons/
โ โโโ grid_building/
โ โโโ plugin.cfg
โ โโโ GridBuilding.csproj
โ โโโ Systems/
โ โ โโโ [Godot systems]
โ โโโ addons/
โ โโโ grid_building/
โ โโโ bin/
โ โโโ GridPlacement.Core.dll โ Compiled Core DLL
|
2. Basic Usage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| // In your Godot C# script (6.0 service-based integration)
using Godot;
using GridPlacement.Godot.Bootstrap;
using GridPlacement.Godot.Bootstrap.Modules;
public partial class PlacementController : Node
{
private GridPlacementGodotWorkflowModule.PlacementWorkflowBridge? _workflow;
public override void _Ready()
{
// Recommended for real games: resolve services via a game-owned composition route.
// GPUserScopeRoot is a convenience wrapper when you choose to include it in your scene.
var scopeRoot = GetNodeOrNull<GPUserScopeRoot>("../GPUserScopeRoot");
if (scopeRoot is null)
{
GD.PrintErr("Composition root not found; cannot resolve GridPlacement workflows.");
return;
}
_workflow = scopeRoot.Registry.GetService<GridPlacementGodotWorkflowModule.PlacementWorkflowBridge>();
// Optional: subscribe to workflow events
_workflow.PlacementValidated += report => GD.Print($"Validated: {report}");
_workflow.PlacementExecuted += report => GD.Print($"Executed: {report}");
}
public void SelectPlaceable(Placeable placeable)
{
_workflow?.SelectPlaceable(placeable);
}
}
|
3. GDScript Integration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # In your GDScript (via a thin C# adapter node)
extends Node
@onready var building_system = $BuildingSystemNode
func _ready():
# Connect to signals raised by your C# system node
building_system.building_placed.connect(_on_building_placed)
building_system.building_removed.connect(_on_building_removed)
func _on_building_placed(building_type, position, instance_id):
print("Building placed: ", building_type, " at ", position)
func place_tower():
# Adapter node forwards to the underlying IBuildingService
building_system.place_building("tower", Vector2i(5, 5))
|
๐ Event System Integration
C# Events โ Godot Signals
The Godot integration layer converts Core C# domain events to Godot signals via thin adapter/system nodes. In a typical setup, a dedicated C# node subscribes to Core events and emits Godot signals that your UI or GDScript code can listen to.
| C# Event (Core DLL) | Godot Signal (adapter node) | Parameters |
|---|
BuildingPlacedEvent | building_placed | building_type: String, position: Vector2i, instance_id: String |
BuildingRemovedEvent | building_removed | building_type: String, position: Vector2i, instance_id: String |
BuildingDamagedEvent | building_damaged | instance_id: String, damage: float, current_health: float |
BuildingDestroyedEvent | building_destroyed | instance_id: String |
BuildingRepairedEvent | building_repaired | instance_id: String, repair_amount: float, new_health: float |
BuildingHealthChangedEvent | building_health_changed | instance_id: String, previous_health: float, current_health: float |
Event Flow Example
User Action โ Godot gameplay code calls a placement method (C# or GDScript)
โ
Core DLL IBuildingService.PlaceBuilding() handles the request
โ
Core DLL publishes BuildingPlacedEvent
โ
Adapter/system node receives the domain event
โ
Adapter/system node emits a building_placed Godot signal
โ
Godot signal system notifies connected GDScript/C# handlers
๐ ๏ธ Core DLL API Reference
BuildingService
Place Building
1
| public BuildingState PlaceBuilding(string buildingType, Vector2I gridPosition, string ownerId = "")
|
Validation Rules:
- Position coordinates must be non-negative
- Position must not be occupied
- Building type must be valid
- Special rules (towers: Y โฅ 5, walls: even coordinates)
Returns: BuildingState with generated InstanceId
Remove Building
1
| public bool RemoveBuilding(string buildingId)
|
Returns: true if building was found and removed
Apply Damage
1
| public bool ApplyDamage(string buildingId, float damageAmount)
|
Effects:
- Reduces building health
- Changes status to Damaged if health < max
- Triggers BuildingDamagedEvent
- Triggers BuildingDestroyedEvent if health โค 0
Repair Building
1
| public bool RepairBuilding(string buildingId, float repairAmount)
|
Effects:
- Increases building health (up to max)
- Changes status from Damaged to Operational
- Triggers BuildingRepairedEvent
BuildingState Properties
| Property | Type | Description |
|---|
BuildingType | string | Type of building (basic, tower, wall, etc.) |
GridPosition | Vector2I | Position on the grid |
InstanceId | string | Unique identifier (GUID) |
OwnerId | string | Owner of the building |
Status | BuildingStatus | Current state (Placed, Damaged, Destroyed, etc.) |
Health | float | Current health points |
MaxHealth | float | Maximum health points |
BuildingStatus Enum
1
2
3
4
5
6
7
8
9
| public enum BuildingStatus
{
Placed,
UnderConstruction,
Operational,
Damaged,
Destroyed,
Repairing
}
|
๐งช Testing Integration
Unit Testing Core DLL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| [Test]
public void BuildingService_CanPlaceAndRemoveBuilding()
{
var service = new BuildingService();
var building = service.PlaceBuilding("basic", new Vector2I(5, 10));
Assert.NotNull(building);
Assert.Equal("basic", building.BuildingType);
Assert.Equal(1, service.GetAllBuildings().Count());
var removed = service.RemoveBuilding(building.InstanceId);
Assert.True(removed);
Assert.Equal(0, service.GetAllBuildings().Count());
}
|
Integration Testing with Godot
1
2
3
4
5
6
7
8
9
10
11
12
| // Pseudo-code: adapter node converts Core events to Godot signals
// (replace `BuildingEventsAdapter` with your own adapter implementation)
var adapter = new BuildingEventsAdapter(service);
var signalReceived = false;
// Adapter exposes a Godot-style signal/event
adapter.BuildingPlaced += () => signalReceived = true;
// Trigger Core DLL event
var building = service.PlaceBuilding("tower", new Vector2I(3, 7));
Assert.True(signalReceived);
|
Integration testing (reuse-first, no engine)
When validating wiring for session support, identity propagation, or adapter readiness,
prefer extending the existing Core integration tests before adding new engine tests:
plugins/gameplay/GridPlacement/cs/Core/Tests/Core/GridBuilding.Core.Tests/PlacementWorkflowOrchestratorIntegrationTests.cs
This keeps the tests deterministic and avoids depending on Godot runtime behavior.
๐ง Advanced Configuration
Custom Validation Rules
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // Extend BuildingService validation
public class CustomBuildingService : BuildingService
{
protected override List<string> ValidateBuildingPlacement(Vector2I position, string buildingType)
{
var errors = base.ValidateBuildingPlacement(position, buildingType);
// Add custom rules
if (buildingType == "special" && position.X < 10)
{
errors.Add("Special buildings must be placed at X โฅ 10");
}
return errors;
}
}
|
Custom Event Handlers
1
2
3
4
5
| // In your adapter/system node
protected void OnBuildingPlaced(object? sender, BuildingPlacedEvent e)
{
// Forward to Godot-facing signals, UI hooks, etc.
}
|
Thread Safety Considerations
The Core DLL uses ReaderWriterLockSlim for thread safety:
1
2
3
4
5
| // Thread-safe operations are automatic
Parallel.ForEach(positions, pos => {
var building = service.PlaceBuilding("basic", pos);
// No additional synchronization needed
});
|
๐จ Error Handling
Exception Types
| Exception | When Thrown | How to Handle |
|---|
BuildingPlacementException | Validation fails | Show error message to user |
ArgumentException | Invalid parameters | Validate user input |
InvalidOperationException | Duplicate placement | Check position first |
Error Handling Pattern
1
2
3
4
5
6
7
8
9
| // Example: your adapter node delegates to a core service and reports errors.
try
{
_buildingService.PlaceBuilding(buildingType, position);
}
catch (Exception ex)
{
GD.PrintErr(ex);
}
|
Memory Management
- Core DLL uses efficient Dictionary-based lookup
- Event subscriptions are automatically cleaned up
- Building states are lightweight structs
- Read operations use read locks (concurrent access)
- Write operations use write locks (exclusive access)
- Event publishing creates handler copies to avoid deadlocks
Optimization Tips
1
2
3
4
5
6
7
8
9
10
11
12
| // Good: Batch operations
var buildings = new List<BuildingState>();
foreach (var pos in positions)
{
buildings.Add(service.PlaceBuilding("basic", pos));
}
// Avoid: Excessive individual calls
// foreach (var pos in positions)
// {
// service.PlaceBuilding("basic", pos); // Creates many events
// }
|
๐ Debugging
Core DLL Debugging
1
2
3
4
5
6
| // Enable console logging
Console.WriteLine($"[BuildingService] Placed {buildingType} at {position}");
// Check event subscriptions
var subscriberCount = EventDispatcher.Instance.GetSubscriberCount<BuildingPlacedEvent>();
GD.Print($"BuildingPlacedEvent subscribers: {subscriberCount}");
|
Godot Integration Debugging
1
2
3
4
5
| // Check signal connections
if (!IsConnected("building_placed", Callable.From<string, Vector2I, string>(OnBuildingPlaced)))
{
GD.PrintErr("building_placed signal not connected!");
}
|
โ
Integration Checklist
Setup
Testing
Documentation
๐ Success!
Your GridBuilding plugin is now using the new Core/Godot DLL architecture! You have:
- โ
Engine-agnostic core logic that can be tested independently
- โ
Robust event system with automatic signal conversion
- โ
Thread-safe operations for concurrent access
- โ
Comprehensive error handling with detailed validation
- โ
Better performance with optimized C# implementation
Next Steps
- Explore the Core DLL - Try unit testing your building logic
- Custom validation - Add game-specific building rules
- Performance optimization - Profile and optimize for your use case
- Contribute back - Share improvements with the community
Need help? Check the troubleshooting section or create an issue on GitHub.