Lapis for Crystal
Lapis for Crystal provides high-performance Crystal bindings and a bidirectional runtime integration for Godot Engine 4.8+ using LibGodot and GDExtension. It empowers game developers to write Godot games with native machine speed, complete compile-time type safety, and Ruby-like elegance. https://youtu.be/EKMw_zQjovc
Architecture: Dual-Paradigm Integration
Lapis 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 & Decompilation with radare2: Breakpoints in Godot's Script Editor gutter seamlessly synchronize with radare2. Includes live pseudo-C decompilation (pdc), assembly disassembly (pdf), register inspection, call stack navigation, and Multiplayer Lockstep Break coordination to eliminate peer timeout disconnects.
- Unified Testing Infrastructure: Comprehensive Crystal specifications in
spec/alongside live in-editor and runtime test verification powered byLapis::Test::EditorDriver.
Prerequisites & Installation
Required Dependencies
- Crystal Compiler: 1.20+ (LLVM-backed compiled language)
- radare2: 5.8+ (Required native multi-threaded debugger, disassembler, decompiler
pdc, and breakpoint synchronization engine) - Godot Engine: 4.8-dev7+ (Standard build, 64-bit)
- C++ Compiler: GCC (
g++) or Clang (for compiling the GDExtension loader bridge) - GNU Make: Workspace build & synchronization automation
- Git: Shards dependency resolution & version control
- Lapis Toolchain: Bundled native CLI tool (
bin/lapis/bin/lapis.exe)
Quick Install by Platform
| Platform | One-Liner Command to Install Dependencies |
|---|---|
| Windows (Scoop) | scoop install crystal radare2 make git |
| Windows (Winget) | winget install CrystalLang.Crystal radare2.radare2 ezwinports.make Git.Git |
| Ubuntu / Debian | sudo apt update && sudo apt install -y crystal radare2 make git g++ |
| Arch Linux | sudo pacman -S crystal radare2 make git gcc |
| macOS (Homebrew) | brew install crystal radare2 make git |
Official Installers
- Windows Installer (
lapis-setup-windows-x86_64.exe): Installs the Lapis CLI, bundlescrystalline.exe(LSP) andradare2.exe(native debugger), configures userPATH, and sets up PowerShell completions. - Debian Package (
lapis_amd64.deb): Installs Lapis globally with automatic package dependency resolution (Depends: crystal, radare2).
Documentation
- Official Online Documentation Site: https://sol-vin.github.io/lapis/
- Interactive Performance Benchmarks Report: https://sol-vin.github.io/lapis/benchmarks.html (12 matching benchmarks comparing native Crystal against GDScript, featuring interactive SVG charts, in-editor overhead analysis, and raw metrics).
- Architecture & Guides in
DocsModule: Complete guides covering architecture, build toolchains, memory management, and concurrency are contained inDocs(such asDocs::I_CONCURRENCY_FIBERS_AND_THREAD_SAFETYandDocs::V_PERFORMANCE_AND_BENCHMARKS). - 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.
Installation & Shard Configuration
Add Lapis to your game's shard.yml:
dependencies:
lapis:
github: sol-vin/lapis
Run shards install to fetch the dependency.
Quickstart
require "lapis"
# 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
Performance & Benchmarks
Lapis includes a comprehensive, 1-to-1 cross-language benchmark suite comparing native compiled Crystal (GDExtension) against GDScript (Godot 4 Bytecode) across algorithmic compute and engine-core operations:
📊 Explore the Live Dashboard: https://sol-vin.github.io/lapis/benchmarks.html
| Category | Benchmarks Included | Speedup Range | Key Advantage |
|---|---|---|---|
| Algorithmic & Compute | Matmul, Primes, Brainfuck, Base64, JSON, NBody, BinaryTrees, Mandelbrot, TransformMath |
3.1x – 227.6x faster | LLVM -O3 auto-vectorization, native unboxed arithmetic, zero variant boxing. |
| Godot Engine Core | NodeLifecycle, MaterialResources, Signals |
1.1x – 307.7x faster | Direct GDExtension C-API dispatch, low GC pause times, fast resource allocation. |
Running Benchmarks Locally
# Compile and run the complete benchmark suite and open the report
make benchmarks-run [ITERATIONS=3]
# Run specific category or filter using the Lapis CLI runner
crystal run benchmarks/runner.cr -- -c compute
crystal run benchmarks/runner.cr -- -c engine
crystal run benchmarks/runner.cr -- --filter=signals
Reports are automatically generated into benchmarks/results/:
benchmark_report.html: Full dark-mode interactive dashboard with SVG charts and KPI cardsbenchmark_chart.svg: High-resolution comparative bar chartbenchmark_report.md: Markdown report for GitHub comments and summariesbenchmark_report.json/benchmark_report.csv: Machine-readable metrics for automated CI ingestion
Comprehensive In-Code Documentation (Docs Module)
Lapis 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. |
Docs::J_FIRST_CLASS_CRYSTAL_SCRIPTS |
Direct .cr editing in Godot Script Editor, pure-Crystal syntax highlighting, AST reflection, and Crystalline LSP integration. |
Docs::K_CONCURRENCY_CHANNELS_AND_ERGONOMICS |
GodotChannel interop with GDScript, reactive main-thread signals, async engine helpers (delay, next_frame), and collection wrappers. |
Docs::L_MACRO_DSL_REFERENCE |
Complete DSL reference for node, resource, gdclass, @[Export*], signal, @[Tool], @[RPC], and onready. |
Docs::G_DEBUGGING_AND_DIAGNOSTICS::A_RADARE2_NATIVE_DEBUGGING |
Native radare2 in-editor debugger plugin, PDB/DWARF symbol parsing, gutter breakpoint sync, native pseudo-C decompilation (pdc), lockstep multiplayer debugging, and tool script debugging. |
Docs::N_GODOT_UPGRADE_GUIDE |
Streamlined engine upgrades via lapis setup, API dumping, and workspace-wide synchronization. |
Docs::O_LOW_LATENCY_INPUT_GUIDE |
Virtual input callbacks (_unhandled_input), sub-frame streaming (use_accumulated_input), zero-allocation vector queries, and mouse look latency optimization. |
Docs::P_LAPIS_TOOLCHAIN_AND_PACKAGING |
Lapis CLI architecture, environment diagnostics (doctor), project adoption (init), clean packaging invariants, and storage reclamation. |
Docs::Q_TESTING_FRAMEWORK_AND_EDITOR_SUITES |
Reusable Lapis::Test apparatus, assertion matchers, cooperative frame-stepping, signal timeouts, and live in-editor suites. |
Docs::R_RELEASE_OPTIMIZATION_AND_EDITOR_STRIPPING |
Compile-time feature exclusion, DocData XML stripping, C++ ClassDB runtime filtering, and minimal distribution packaging. |
Docs::S_IDE_INTEGRATION_AND_DEVELOPER_EXPERIENCE |
Automated workspace configuration for VS Code, Cursor, Zed, and Neovim with Crystalline LSP and radare2 debugging. |
Docs::T_BINDINGS_ARCHITECTURE_AND_GENERATOR |
Automated extension_api.json ingestion, topological DAG sorting, zero-alloc stack ptrcalls, and type marshaling. |
Docs::U_CODE_CLEANUP_AND_DRY_PATTERNS |
Consolidated ptrcall dispatch macros, declarative singleton delegation, C++ RAII scoped memory, and actionable errors. |
Docs::V_PERFORMANCE_AND_BENCHMARKS |
Crystal vs GDScript benchmark architecture, reporter patterns, in-editor overhead measurement, and performance dashboard. |
Docs::W_CPP_BRIDGE_ARCHITECTURE |
Native GDExtension C++ loader bridge internals, memory layout, proc address caching, shadow DLL loading, and crash guards. |
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++).
Unified Lapis CLI Toolchain (bin/lapis)
Lapis includes a high-performance, cross-platform compiled CLI tool written in Crystal (bin/lapis or bin/lapis.exe on Windows). The toolchain replaces platform-dependent scripting with instant sub-20ms execution across Windows, Linux, and macOS:
| Command | Description & Role |
|---|---|
lapis doctor |
Diagnoses toolchain and environment health (Crystal compiler, Godot binary, C++ compiler, radare2 debugger, packaging utilities, and project health). |
lapis init [path] |
Initializes Crystal/Lapis integration into an existing Godot project (generates shard.yml, src/main.cr, installs GDExtension addon, and syncs runtime libraries). |
lapis build [target] |
Compiles Crystal targets (game, plugin, tests, bench) with automatic CRYSTAL_PATH resolution and platform-specific linker flags. |
lapis sync |
Synchronizes compiled binaries, runtime DLLs (GC, iconv, PCRE2, LibGodot), and addon manifests across all workspace consumers. |
lapis test [options] |
Unified multi-tier test runner with interactive Terminal User Interface (TUI) dashboard (or streaming logs via --no-tui): executes specs, headless in-editor @tool tests, runtime test suites, and standalone compiled tests. |
lapis editor |
Detects Godot installation, synchronizes assets, and launches the Godot Editor with automatic build hooks. |
lapis new <game|addon|example> [name]lapis scaffold <game|addon|example> [name] |
Scaffolds a new game from template (in target directory or CWD), a redistributable GDExtension addon, or a showcase example. |
lapis bind <engine|project> |
Generates typed Crystal API bindings for Godot engine classes and singletons (from extension_api.json) or custom GDScript project nodes. |
lapis package [target] |
Packages playable standalone game executables or distribution zip archives (template, addon, examples, tests, perf, release). |
lapis clean [options] |
Prunes compiled binaries, shadow DLLs, and logs while safely preserving runtime DLLs. Supports -d, --dry-run for space preview and reports reclaimed bytes. |
lapis completion <shell> |
Generates shell autocompletion scripts for powershell, bash, or zsh. |
lapis setup |
Downloads and sets up the targeted Godot engine binary for development and dumps the GDExtension API. |
lapis docs |
Generates offline HTML API documentation via crystal docs with responsive styling and sidebar navigation. |
lapis deps |
Validates and synchronizes required runtime dynamic libraries and export templates across output folders. |
lapis dirs |
Verifies and creates all required build, output, and staging directories across the workspace. |
lapis install [options]make install |
Installs the Lapis CLI toolchain globally into system/user PATH (%LOCALAPPDATA%\Microsoft\WindowsApps on Windows, /usr/local/bin or ~/.local/bin on Unix). |
Run lapis --help or lapis <command> --help for full parameter options and flags.
Platform & Support Scripts (scripts/)
Low-level environment setup and cross-compilation wrapper scripts:
| Script | Purpose & Description |
|---|---|
scripts/cc_wrapper.sh |
POSIX compiler wrapper script. Filters out -rdynamic on Linux shared library builds and localizes internal Crystal runtime symbols to prevent GNU ld/LLD version node link errors. |
scripts/windows/install_deps.ps1 |
PowerShell automated dependency installer for Windows. Downloads and verifies runtime DLLs, radare2 tooling, Crystalline LSP, and build dependencies. |
scripts/windows/install.ps1 |
PowerShell installation script for global Lapis toolchain staging and user PATH configuration on Windows. |
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
[ Lapis 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 Lapis 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, Lapis 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, Lapis 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
Lapis'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 & Decompilation with radare2
Lapis features first-class native debugging, disassembly, and pseudo-C decompilation directly inside the Godot Editor powered by radare2 via sol-vin/cradare2:
- Gutter Breakpoint Sync: Set red breakpoints directly in Godot's Script Editor gutter; breakpoints are translated and dispatched to radare2 via source line matching (
dbl) instantly. - Interactive In-Editor Panel: Dedicated Crystal Debugger 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 CPU register inspection. - Native Pseudo-C Decompiler & Disassembly: Built-in side-by-side view with native pseudo-C (
pdc) decompilation and annotated assembly (pdf/pdca) with zero external decompiler dependencies. - 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 (
DebugBreakProcess/SIGINT) 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 radare2 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. terminal radare2 -d godot.exe or lapis editor --debug) to debug live tool scripts without freezing the debugger UI.
|
Detailed architectural breakdown and workflows are documented in
Docs::G_DEBUGGING_AND_DIAGNOSTICS::A_RADARE2_NATIVE_DEBUGGING.
Repository Structure
lapis/
├── src/ # Reusable Lapis library & root host application
│ ├── libgodot.cr # Library root entry point (require "libgodot")
│ ├── lapis.cr # Lapis prelude & engine extensions
│ ├── main.cr # Root host game entry point & test runner panel
│ ├── bridge/crystal_bridge.cpp # C++ GDExtension loader bridge
│ └── libgodot/ # Core engine C-API, macros, and generated bindings
├── tools/ # Built-in CLI toolchain
│ └── lapis/ # Compiled native CLI (bin/lapis)
├── spec/ # Unified Crystal specifications & test suites
│ ├── spec_helper.cr # Common spec helper and test nodes
│ ├── editor_driver_spec.cr # In-editor @tool and runtime tests via EditorDriver
│ ├── suites/ # 40+ modular engine test suites
│ └── fixtures/ # Spec test targets & fixtures
├── scenes/ # Root Godot host project scenes (main_test_runner.tscn, etc.)
├── scripts/ # GDScript test fixtures and interop nodes
├── project.godot # Root Godot host project configuration
├── addons/ # GDExtensions & Editor Plugins
│ ├── crystal_integration/ # Official GDExtension manifest & editor build hook
│ └── dummy_*/ # Isolated test addons for multi-addon stress tests
├── examples/ # Independent consumer showcase examples
├── template/ # Clean starter template for new games
├── template-addon/ # Starter template for redistributable addons
├── bin/ # Output binaries, bridge DLL, and dependencies
└── AGENTS.md # Agent development guidelines
License
Distributed under the MIT License. Copyright (c) 2026 Ian and contributors.