github.com/sol-vin/libgodot
latest / published Sep 16, 2026 / repository
High-performance Crystal bindings and 2-way host integration for Godot Engine 4.8+ using LibGodot and GDExtension.
LibGodot for Crystal
LibGodot for Crystal provides high-performance Crystal bindings and a bidirectional runtime integration for Godot Engine 4.8+. It empowers game developers to write Godot games with native machine speed, complete compile-time type safety, and Ruby-like elegance.
Architecture: Dual-Paradigm Integration
LibGodot supports two distinct execution paradigms designed for both rapid in-editor iteration and lean standalone production shipping:
graph TD
subgraph Mode A: GDExtension In-Editor Workflow
GE[Godot Editor 4.8] -->|Loads| GDX[addons/crystal_integration/crystal.gdextension]
GDX -->|Loads| CB[bin/crystal_bridge.dll]
CB -->|1. Initializes Boehm GC| CRT[Crystal Runtime]
CB -->|2. Shadow loads| GDL[bin/game_loaded_pid_ts.dll]
GDL -->|3. crystal_godot_init| REG[ClassDB & EditorHelp]
REG -->|4. Exposes Nodes & Inspector| GE
GE -->|F5 / Build Hook| EB[EditorPlugin._build]
EB -->|Recompiles| GDL
end
subgraph Mode B: Standalone LibGodot Host Paradigm
EXE[bin/game.exe] -->|1. Owns main Entry Point| CRTM[Crystal Boehm GC]
EXE -->|2. In-Memory Boot| LGD[bin/libgodot.dll]
LGD -->|3. libgodot_create_godot_instance| GINST[Godot Engine Instance]
GINST -->|4. Pass GDExtension C-API Table| EXE
EXE -->|5. Step Main Loop & Dispatch| WIN[Game Window & Audio]
end
- Mode A: GDExtension In-Editor Workflow (
game.dll+crystal_bridge.dll): Develop inside the Godot Editor (make editor). The native C++ bridge boots the Boehm GC and shadow-copiesgame.dllto prevent Windows file locking. Pressing F5 in the editor triggers live compilation and reload. - Mode B: Standalone LibGodot Host Paradigm (
game.exe+libgodot.dll): Crystal ownsmain(), initializes its runtime natively, boots Godot in-memory, and controls the main loop for standalone shipping builds (make game_exe).
Detailed architectural deep-dive is available in
Docs::A_ARCHITECTUREandDocs::B_COMPILATION_AND_BUILD.
Features
- Intuitive Node DSL: Define Godot nodes with concise
node ClassName < ParentNode do ... endsyntax. - Inspector Export System: Complete support for
@[Export], numeric ranges, enums, file pickers, bitmask flags, groups, categories, and tool buttons. - Automated Doc Comment Harvesting: Standard Crystal
# commentsabove classes, properties, signals, and methods are extracted at compile time and registered into Godot'sEditorHelpXML database for in-editor tooltips and offline F1 Help. - Type-Safe Signals: Declare signals via
signal health_changed(new_health : Int32)with generatedemit_<signal>helpers. - GDScript Interoperability: Automatic compile-time generation of typed Crystal wrappers for project GDScript nodes and scenes (
make project_bindings). - Engine Reflection & Global Singletons: First-class access to singletons like
Godot.input,Godot.engine,Godot.audio_server, and generated Godot classes. - Native In-Editor Debugging with LLDB: Breakpoints in Godot's Script Editor gutter seamlessly synchronize with LLDB. Includes an interactive in-editor console, call stack navigation, and Multiplayer Lockstep Break coordination to eliminate peer timeout disconnects.
- Strict Decoupling: Clean separation between reusable library (
src/), test suite (test/), and examples (examples/).
Prerequisites & Installation
Required Tooling
- Crystal Compiler: 1.14+ (or preview builds)
- Godot Engine: 4.3+ or 4.4+ (Standard build, 64-bit)
- C++ Compiler: GCC (
g++) or Clang (for compiling the GDExtension loader bridge) - Make: GNU Make
- Powershell: Used for support scripts and make process
Native In-Editor Debugging Prerequisite (LLDB)
For native in-editor debugging, breakpoint synchronization, and multiplayer lockstep inspection, install LLDB:
- Windows:
scoop install llvmorwinget install LLVM.LLVM - Ubuntu / Debian:
sudo apt install lldb - Arch Linux:
sudo pacman -S lldb - macOS:
brew install llvmorxcode-select --install
Documentation
- Official Online Documentation Site: https://sol-vin.github.io/libgodot/
- Architecture & Guides in
DocsModule: Complete guides covering architecture, build toolchains, memory management, and concurrency are contained inDocs(such asDocs::I_CONCURRENCY_FIBERS_AND_THREAD_SAFETY). - Offline HTML API Documentation: Generate complete API documentation locally with
make docs(output atdocs/index.html). - In-Editor Help: Class and method descriptions are harvested at compile time and accessible directly inside Godot via
F1or Inspector tooltips.
Quickstart
require "libgodot"
# Player character with physics movement, health tracking, and signals
node Player < CharacterBody3D do
# Movement speed in meters per second
@[Export(range: 1.0_f32..20.0_f32, step: 0.5_f32)]
property speed : Float32 = 7.0_f32
# Jump velocity impulse
@[Export(range: 1.0_f32..25.0_f32, step: 0.5_f32)]
property jump_velocity : Float32 = 8.0_f32
# Gravitational acceleration
@[Export(range: 1.0_f32..50.0_f32, step: 1.0_f32)]
property gravity : Float32 = 18.0_f32
# Maximum hit points
@[Export(range: 10..500, step: 10)]
property max_health : Int32 = 100
# Emitted when the player's health changes
signal health_changed(current : Int32, max_health : Int32)
# Emitted when the player dies
signal died
# Called when node enters the active scene tree
def _ready : Void
@current_health = @max_health
Godot.print("Player initialized at #{position}")
end
# Called every fixed physics step
def _physics_process(delta : Float64) : Void
vel = velocity
unless is_on_floor
vel.y -= @gravity * delta.to_f32
end
if Input.is_action_just_pressed("jump") && is_on_floor
vel.y = @jump_velocity
end
input_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
direction = (transform.basis * Vector3.new(input_dir.x, 0.0_f32, input_dir.y)).normalized
if direction.length > 0.001_f32
vel.x = direction.x * @speed
vel.z = direction.z * @speed
else
vel.x = Math.move_toward(vel.x, 0.0_f32, @speed * delta.to_f32)
vel.z = Math.move_toward(vel.z, 0.0_f32, @speed * delta.to_f32)
end
self.velocity = vel
move_and_slide
end
# Inflicts damage on the player
def take_damage(amount : Int32) : Void
return if @current_health <= 0
@current_health = Math.max(0, @current_health - amount)
emit_health_changed(@current_health, @max_health)
if @current_health <= 0
emit_died
queue_free
end
end
end
Comprehensive In-Code Documentation (Docs Module)
LibGodot features an extensive in-code documentation suite under the Docs module. Each submodule details internal mechanics, macro pipelines, export options, and engine caveats:
| Submodule | Topic |
|---|---|
Docs::A_ARCHITECTURE |
Dual-paradigm model, GDExtension Mode A vs Standalone LibGodot Mode B. |
Docs::B_COMPILATION_AND_BUILD |
Bridge compilation, Windows shadow DLL file-locking bypass, F5 editor hook, and Makefile orchestration. |
Docs::C_EXPORTS_AND_INSPECTOR |
All @[Export*] annotations, PropertyInfo mapping, ranges, enums, flags, categories, and buttons. |
Docs::D_NODE_DSL_AND_SIGNALS |
Node macro DSL, lifecycle callbacks (_ready, _physics_process), signal registration, and scene APIs. |
Docs::E_DOC_COMMENTS_AND_HELP |
Compile-time doc comment harvesting, DocData XML generation, and Godot offline F1 Help integration. |
Docs::F_GDSCRIPT_INTEROP |
Automated compile-time GDScript bindings and Variant marshaling. |
Docs::G_CAVEATS_AND_INTERNALS |
Boehm GC vs Godot memory lifecycles, threading rules, method bind caching, and Windows toolchains. |
Docs::H_LIFECYCLE_MEMORY_AND_DEAD_POINTER_SAFETY |
Dead-pointer prevention, monotonic 64-bit instance IDs, O(1) liveness checks, and zero-crash DisposedObjectError. |
Docs::I_CONCURRENCY_FIBERS_AND_THREAD_SAFETY |
Crystal fibers, background OS threads, actor channel message passing, mutexes, and main-thread SceneTree affinity. |
To generate and browse the complete HTML documentation locally:
make docs
Then open docs/index.html in your browser.
Build System & Commands
Build operations are orchestrated through the root Makefile.
| Command | Description |
|---|---|
make all |
Default Build: Compiles loader bridge, test project, examples, template, and synchronizes all DLLs. |
make run |
Launches the test suite project directly in the Godot engine. |
make editor |
Opens the test project in the Godot Editor (godot.exe --editor --path test). |
make test |
Runs the multi-tier automated test suite: Crystal specs, headless in-editor tool tests, runtime test project, standalone test runner (--autorun), and smoke tests. |
make test_standalone |
Packages and executes the standalone test runner executable (tests.exe --autorun) in debug or release mode. |
make docs |
Generates offline HTML documentation into docs/. |
make bridge |
Compiles src/bridge/crystal_bridge.cpp into bin/crystal_bridge.dll. |
make test_project |
Compiles the test suite project (test/bin/game.dll). |
make examples |
Compiles all showcase projects in examples/. |
make template |
Compiles the starter template (template/bin/game.dll). |
make game_exe |
Compiles standalone host executable bin/game.exe (Mode B). |
make sync |
Synchronizes binaries, runtime DLLs, and addons across all consumer directories. |
make clean |
Removes compiled binaries and intermediate build artifacts while preserving runtime DLLs. |
Build Options
RELEASE=1: Compiles Crystal code with release optimizations (--release -O3) and definesLIBGODOT_RELEASE=1/NDEBUG.ENTRY=<path>: Customizes the Crystal entry file (default:test/src/main.cr).CXX=<compiler>: Specifies C++ compiler for the bridge (default:g++).
Support & Toolchain Scripts (scripts/)
LibGodot provides specialized automation scripts under scripts/ to orchestrate builds, cross-compilation, asset synchronization, testing, code generation, and deployment:
Build & Compilation Scripts
| Script | Description & Role |
|---|---|
scripts/build_crystal.ps1 |
Primary Crystal compilation driver. Sets up CRYSTAL_PATH, configures platform-specific linker flags (/DLL on Windows, -shared on Linux, -dynamiclib on macOS), and automatically injects symbol localization wrappers on Linux to prevent symbol collisions. |
scripts/build_android.ps1 |
Cross-compiles LibGodot shared libraries (libcrystal_bridge.so and libgame.so) for Android architectures (arm64-v8a), resolving Android NDK paths, sysroots, and clang toolchains. |
scripts/build_examples.ps1 |
Iterates through all showcase demo projects in examples/ (e.g. basic_demo), invoking their local Makefiles to build game shared libraries or standalone game executables (-Exe). |
scripts/build_dummy_addons.ps1 |
Compiles lightweight mock GDExtension addons (dummy_audio, dummy_dialogue, dummy_inventory) in test/addons/ to verify multi-addon isolation, independent ClassDB registrations, and compiler hooks. |
scripts/cc_wrapper.sh |
POSIX compiler wrapper script. Filters out -rdynamic on Linux shared library builds and invokes ld -r and objcopy --keep-global-symbol=crystal_godot_init to localize internal Crystal runtime symbols, preventing GNU ld/LLD version node link errors. |
Packaging & Distribution Scripts
| Script | Description & Role |
|---|---|
scripts/package_game.ps1 |
Packages a project (such as test or an example) into a playable standalone executable (e.g. tests.exe, game.exe). Automatically discovers all .gdextension manifests, generates .godot/extension_list.cfg, synchronizes runtime dependencies, and sets up self-contained binaries for production shipping. |
scripts/package_android.ps1 |
Builds and packages complete installable Android APK bundles for Godot + Crystal games, configuring Gradle project templates, export manifests, and native .so packaging. |
scripts/manage_keystore.ps1 |
Manages Android signing keystores and certificates for debug and release builds, including generation, inspection, and base64 environment variable decoding for CI pipelines. |
scripts/ensure_export_templates.ps1 |
Verifies that the appropriate Godot 4 export templates are installed on the host system for desktop and mobile targets, reporting readiness for standalone release exports. |
Synchronization & Dependency Scripts
| Script | Description & Role |
|---|---|
scripts/sync_bins.ps1 |
Synchronizes compiled binaries (crystal_bridge.dll, game.dll, plugin.dll) and Crystal runtime libraries (gc.dll, iconv-2.dll, pcre2-8.dll, libgodot.dll) across bin/, test/bin/, template/bin/, examples/*/bin/, and all addon directories. |
scripts/sync_addons.ps1 |
Maintains strict file parity for addons/crystal_integration across root, test, template, and showcase projects, updating GDExtension manifests and editor build hooks. |
scripts/ensure_deps.ps1 |
Locates and copies required Crystal runtime dynamic libraries (Boehm GC, iconv, PCRE2, and LibGodot engine shared libraries) into target binary output folders. |
scripts/ensure_dirs.ps1 |
Ensures all required build, output, and staging directories exist across the repository workspace. |
Testing & Verification Scripts
| Script | Description & Role |
|---|---|
scripts/run_tests.ps1 |
The master multi-tier test suite runner. Executes Phase 1 Crystal specs, Phase 2 in-editor @tool tests, Phase 3 runtime project suites, Phase 3b standalone compiled runner (tests.exe --autorun in debug and release modes), and Phase 4 smoke tests. Generates test_report.md and JSON summaries for GitHub Actions CI. |
scripts/run_ci_local.ps1 |
Runs a complete local simulation of the GitHub Actions CI pipeline, executing builds, format checks, multi-tier tests, and packaging steps within a clean staging environment. |
scripts/report_android.ps1 |
Audits Android APK bundles (generated by package_android.ps1), verifying ELF architecture ABI compatibility, dynamic symbol tables, permissions, and manifest integrity. |
Binding Generation & Documentation Scripts
| Script | Description & Role |
|---|---|
scripts/generate_bindings.cr |
Automated code generator that parses Godot's extension_api.json and synthesizes strongly typed Crystal classes, global enums, singletons, and method bindings into src/libgodot/generated/. |
scripts/overrides.yml |
Type mapping and signature override definitions consumed by generate_bindings.cr to handle engine C-API quirks, method rename rules, and custom Variant marshalers. |
scripts/patch_docs.ps1 |
Post-processes offline HTML documentation produced by crystal docs, injecting modern CSS themes, navigation enhancements, and repository links into docs/. |
Memory Safety, Object Lifecycle & Dead-Pointer Protection
Developing with a garbage-collected language like Crystal embedded inside a native C++ engine like Godot introduces a dual memory model hazard:
- Crystal Boehm GC: Manages Crystal heap objects and node wrappers (
Godot::Object). - Godot ObjectDB & Reference Counting: Manages native C++ engine nodes and refcounted resources.
The Dangling Pointer Hazard in Cross-Language Bindings
When an object is freed on the Godot engine side or via GDScript (e.g. queue_free() or target.free()), standard C-API bindings retain a raw C++ pointer to dead memory. Subsequent operations (e.g. target.position = new_pos) dereference the unmapped address, triggering an immediate, fatal segmentation fault (ACCESS_VIOLATION / SIGSEGV) that crashes the game process with no traceback.
[ Crystal Runtime ] [ Godot Engine / GDScript ]
enemy = get_node("Enemy")
enemy.@pointer = 0x7FFE_1234 --------> Node instance at 0x7FFE_1234
|
| GDScript: enemy.queue_free()
v
ObjectDB destroys Node & frees memory!
0x7FFE_1234 is now DEAD / UNMAPPED!
enemy.position = Vector2.new(...)
|
v
[ LibGodot check_alive! ]
|
+---> Query ObjectDB for 64-bit instance ID: ID is INVALID!
|
+---> Marks wrapper dead (@pointer = null)
|
+---> Raises Godot::DisposedObjectError (Clean, catchable Crystal exception!)
[ ZERO NATIVE CRASHES! ]
How LibGodot Guarantees Dead-Pointer Safety
- Monotonic 64-bit Instance ID Tracking:
Every
Godot::Objectwrapper tracks its engine-assignedinstance_id. Because Godot'sObjectDBgenerates monotonic 64-bit IDs, newly allocated heap objects will never collide with previously freed IDs. - Pre-Dispatch Liveness Check (
#check_alive!): Before executing method dispatches or reflection calls, LibGodot queries Godot's ObjectDB in O(1) time (Bridge.is_instance_valid(instance_id)). - Graceful
DisposedObjectErrorException: If an object was destroyed by GDScript, the engine, or Crystal, LibGodot marks the pointer null and immediately raisesGodot::DisposedObjectError:begin enemy.position = Vector2.new(10.0, 20.0) rescue ex : Godot::DisposedObjectError Godot.print_warn "Attempted operation on dead node (ID: #{ex.instance_id})" end - Defensive Inspection with
#alive?and#destroyed?: Game logic can check entity liveness before issuing operations:if target.alive? target.apply_damage(50) else active_targets.delete(target) end
Quantitative Zero-Leak Verification
LibGodot's test suite integrates Godot's Performance singleton monitors (OBJECT_COUNT, OBJECT_NODE_COUNT, MEMORY_STATIC) and Crystal's GC.collect to mathematically verify that creating, reparenting, and destroying nodes across hundreds of iterations leaves zero memory leaks in both Godot's ObjectDB and Crystal's heap.
Native In-Editor Debugging with LLDB
LibGodot features first-class native debugging directly inside the Godot Editor using LLDB:
- Gutter Breakpoint Sync: Set red breakpoints directly in Godot's Script Editor gutter; breakpoints are translated and dispatched to LLDB instantly.
- Interactive In-Editor Panel: Dedicated Crystal LLDB tab docked in the Godot Debugger panel featuring Continue (
F5), Step Over (F10), Step Into (F11), Step Out (Shift+F11), call stack frame navigation, and live variable inspection. - Multiplayer Multi-Session Support: Distinct session tabs with automatic role identification (
[SERVER],[CLIENT 1], etc.) for multiple instances launched from the editor. - Multiplayer Lockstep Break Mode: When any peer hits a breakpoint, all other active instances are automatically paused via cooperative interrupt to prevent network heartbeat timeouts (ENet/WebSocket/WebRTC).
Debugging Tool Scripts (@[Tool])
| Execution Context | Will Breakpoint Trigger in Editor Tab? | Reason & Workflow |
|---|---|---|
| Running Game Instance (F5 / F6) | YES | The tool script runs in the child game process attached to the in-editor LLDB session. |
| Live In-Editor Viewport / Inspector | NO |
Code executes inside the parent Godot Editor process (godot.exe), not a child game process.Host Deadlock Paradox: An OS-level native breakpoint ( SIGTRAP) in the editor process freezes the editor GUI thread, making it impossible to click "Continue" or "Step" in the debugger tab.Solution: Attach an external debugger (e.g. VS Code CodeLLDB or terminal lldb -- godot.exe --editor --path <project>) to debug live tool scripts without freezing the debugger UI.
|
Detailed architectural breakdown and VS Code
.vscode/launch.jsontemplates are documented inDocs::M_LLDB_NATIVE_DEBUGGING_GUIDE.
Repository Structure
libgodot/
├── src/ # Reusable LibGodot library (STRICTLY decoupled)
│ ├── libgodot.cr # Library entry point
│ ├── bridge/crystal_bridge.cpp # C++ GDExtension loader bridge
│ └── libgodot/
│ ├── docs.cr # Comprehensive Docs module (A-G guides)
│ ├── macros.cr # Node DSL, signal, export, and doc harvesting macros
│ ├── bridge.cr # C-API bindings & GDExtension interface table
│ ├── core.cr # Object, ClassDB, PropertyInfo, SignalInfo
│ ├── doc_macro.cr # EditorDocRegistry & XML loader
│ ├── gdscript.cr # GDScript interop bindings
│ └── generated/ # Complete generated Godot engine class bindings
├── test/ # Dedicated verification and test project
├── examples/ # Independent consumer showcase examples
├── template/ # Clean starter template for new games
├── addons/crystal_integration/ # Godot editor extension manifest & build hook
├── bin/ # Output binaries, bridge DLL, and dependencies
├── spec/ # Automated unit specifications
└── AGENTS.md # Agent development guidelines
License
Distributed under the MIT License. Copyright (c) 2026 Ian and contributors.
API
- AABB
- Abstract
Marks an extension class as abstract, preventing direct instantiation in the editor.
- Array(T)
An
Arrayis an ordered, integer-indexed collection of objects of type T. - Basis
- Color
- CrystalHighlighter
- CrystalIntegrationPlugin
- CrystalLanguage
- CrystalPanel
- CrystalScript
- Doc
Annotation for explicit documentation on methods, properties, or classes
- Docs
- Export
Marks an instance variable as an exported property visible in the Godot inspector.
- ExportCategory
Starts a top-level category header in the Godot inspector.
- ExportColorNoAlpha
Exports a Color property while suppressing the alpha (transparency) channel selector.
- ExportCustom
Exports a property with custom PropertyHint and hint string parameters.
- ExportDir
Exports a string property as a directory picker in the Godot inspector.
- ExportEnum
Exports a property whose values are constrained to an enumeration or list of string choices.
- ExportExpEasing
Exports a float property with exponential easing curve visualization in the inspector.
- ExportFile
Exports a string property as a file picker in the Godot inspector.
- ExportFilePath
Exports a string property as a file path selector.
- ExportFlags
Exports an integer property as a bitmask flag field in the Godot inspector.
- ExportFlags2DNavigation
Exports an integer property as 2D navigation layers.
- ExportFlags2DPhysics
Exports an integer property as 2D physics collision layers and masks.
- ExportFlags2DRender
Exports an integer property as 2D render layer visibility bitmask flags.
- ExportFlags3DNavigation
Exports an integer property as 3D navigation layers.
- ExportFlags3DPhysics
Exports an integer property as 3D physics collision layers and masks.
- ExportFlags3DRender
Exports an integer property as 3D render layer visibility bitmask flags.
- ExportFlagsAvoidance
Exports an integer property as navigation avoidance obstacle layers.
- ExportGlobalDir
Exports a string property as a global (system-wide filesystem) directory picker.
- ExportGlobalFile
Exports a string property as a global (system-wide filesystem) file picker.
- ExportGroup
Groups subsequent exported properties under a collapsible heading in the inspector.
- ExportMultiline
Exports a string property with a multiline text editor in the Godot inspector.
- ExportNodePath
Exports a NodePath property restricted to specific node types in the scene hierarchy.
- ExportPlaceholder
Exports a string property with placeholder ghost text shown when empty.
- ExportRange
Exports a numeric property constrained to a specific range in the Godot inspector.
- ExportStorage
Exports a property stored within the scene file without displaying in the editor inspector.
- ExportSubgroup
Groups exported properties under a subgroup within an existing group.
- ExportToolButton
Exposes a method or property as an interactive button in the inspector.
- Godot
- GodotClass
Explicitly designates a Crystal class for GDExtension registration.
- Group
Assigns the node to one or more scene tree groups upon
_ready. - Hash(K, V)
Crystal Standard Library Extensions for Godot Collections
- Icon
Specifies a custom editor icon path for the node class.
- Int
Int is the base type of all integer types.
- LibGodot
- Math
- Nil
The
Niltype has only one possible value:nil. - OnReady
Automatically initializes a node property when
_readyis called by querying the scene tree. - Plane
- Quaternion
- RPC
Configures Remote Procedure Call (RPC) network replication for a method.
- Range(B, E)
Crystal Standard Library Extension: Type cohesion for Crystal Range <-> Godot
- Rect2
- Rect2i
- ResourceFormatLoaderCrystal
- ResourceFormatSaverCrystal
- ScriptPath
Explicitly sets the source script path for editor script linking.
- StaticUnload
Configures GDExtension library unloading behavior.
- Tool
Marks a script class to execute in the editor as a tool script.
- Transform2D
- Transform3D
- Vector2
Core math and transform value-type aliases
- Vector2i
- Vector3
- Vector3i
- Vector4
- Vector4i
- WarningIgnore
Suppresses specific compiler or engine warnings for a class or member.
- WarningIgnoreRestore
Restores normal warning behavior following a suppression block.
- WarningIgnoreStart
Starts a warning suppression block.