Core/Godot DLL Integration Guide
This guide covers the new 6.0.0 architecture where GridBuilding’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 the plugin and Hugo docs are branded GridPlacement 6.0, while the compiled C# assemblies and namespaces stay under GridBuilding.* (for example GridBuilding.Core, GridBuilding.Godot). When this guide talks about the “core DLL” and “Godot integration layer”, it is describing the GridPlacement 6.0 C# plugin.
🏗️ 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 (GridBuilding.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.
🚀 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/
│ └── GridBuilding.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
33
34
35
36
37
| // In your Godot C# script (6.0 service-based integration)
using Godot;
using GridBuilding.Core.Services.Building;
using GridBuilding.Bootstrap; // ServiceCompositionRoot
public partial class BuildingController : Node
{
private IBuildingService? _buildingService;
public override void _Ready()
{
// Resolve services from the shared ServiceRegistry
var registry = ServiceCompositionRoot.GetGlobalRegistry();
_buildingService = registry.GetService<IBuildingService>();
// Subscribe to domain events if needed
_buildingService.BuildingPlaced += OnBuildingPlaced;
_buildingService.BuildingRemoved += OnBuildingRemoved;
}
private void OnBuildingPlaced(string buildingType, Vector2I position, string instanceId)
{
GD.Print($"Building {buildingType} placed at {position}");
// Update UI, play sounds, etc.
}
public void PlaceBuilding(string type, Vector2I position)
{
if (_buildingService == null)
{
GD.PrintErr("IBuildingService not available");
return;
}
_buildingService.PlaceBuilding(type, position);
}
}
|
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);
|
🔧 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
6
7
8
9
| // In StateBridge
protected override void OnBuildingPlaced(object? sender, BuildingPlacedEvent e)
{
base.OnBuildingPlaced(sender, e);
// Add custom logic
LogBuildingPlacement(e.Building);
UpdateStatistics(e.Building);
}
|
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
10
11
12
13
14
15
16
| try
{
var building = _stateBridge.PlaceBuilding(buildingType, position);
// Success - update UI
}
catch (BuildingPlacementException ex)
{
// Show validation error to user
ShowError($"Cannot place building: {ex.Message}");
}
catch (Exception ex)
{
// Log unexpected errors
GD.PrintErr($"Unexpected error: {ex.Message}");
ShowError("An unexpected error occurred");
}
|
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
6
7
8
9
10
11
| // Check signal connections
if (!stateBridge.IsConnected("building_placed", Callable(OnBuildingPlaced)))
{
GD.PrintErr("Building placed signal not connected!");
}
// Verify Core DLL is loaded
if (!File.Exists("res://addons/grid_building/bin/GridBuilding.Core.dll"))
{
GD.PrintErr("Core DLL not found!");
}
|
✅ 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.