Skip to content

erocs/gd2rust

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

51 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gd2rust

A source-to-source transpiler that converts GDScript (.gd) files into Rust GDExtension code, compiling to a native shared library (.dll / .so / .dylib) that Godot 4 loads directly.

Why

Goal Detail
IP protection Compiled Rust is not trivially decompiled; GDScript ships as plain text or lightly-bytecoded PCK files
Performance Hot paths (AI, physics helpers, procedural generation) benefit from Rust's zero-cost abstractions
Drop-in replacement Generated code registers the same class names, method names, signals, and properties as the original GDScript
Incremental adoption Transpile one file at a time; the rest of the project stays in GDScript

Requirements

  • Rust (2021 edition)
  • Godot 4.6.1 (bundled API; overridable via --godot-api)
  • rustfmt (optional, for --format)

Installation

cargo install --path .

Usage

gd2rust <files>... [OPTIONS]

Arguments:

Argument Default Description
<files>... required .gd files or directories to transpile (directories are scanned recursively)
-o, --output <PATH> output Output directory for the generated Rust project
-n, --name <NAME> my_game Name of the generated GDExtension project
--godot-api <PATH> bundled Replace the bundled Godot extension_api.json entirely (use when targeting a different engine version or a custom build)
--extension-api <PATH> Add a third-party plugin API on top of the built-in DB (repeatable; use for GDExtension plugins that expose their own classes)
--godot-version <VER> 4.6.1 Godot version string written into generated Cargo.toml
--format off Run rustfmt on generated files
-v, --verbose off Print per-file progress and warnings

Examples:

# Transpile a single file
gd2rust player.gd -o output -n my_game

# Transpile an entire scripts directory
gd2rust src/scripts/ -o rust_ext -n my_game --format

# Use a different Godot version's API dump (replaces the bundled 4.6.1 DB)
gd2rust player.gd --godot-api ~/godot-4.5/extension_api.json

# Add a third-party plugin's class DB (e.g. GodotSteam) on top of the built-in DB
gd2rust player.gd --extension-api ~/plugins/godotsteam_api.json

Output Structure

For a project with player.gd and enemy.gd:

my_game_ext/
├── Cargo.toml               # workspace root
├── my_game_ext.gdextension  # Godot extension manifest
├── player/
│   ├── Cargo.toml
│   └── src/lib.rs           # Player struct + impls
└── enemy/
    ├── Cargo.toml
    └── src/lib.rs           # Enemy struct + impls

Build the extension:

cd my_game_ext
cargo build --release

The .gdextension manifest in the output points Godot to the compiled library. Copy or symlink the output directory into your Godot project folder.

Quick Example

Input (player.gd):

extends CharacterBody2D

@export var speed: float = 200.0

func _physics_process(delta: float) -> void:
    var direction := Input.get_axis("move_left", "move_right")
    velocity.x = direction * speed
    move_and_slide()

Output (player/src/lib.rs):

use godot::prelude::*;

#[derive(GodotClass)]
#[class(base = CharacterBody2D)]
pub struct Player {
    #[export]
    speed: f64,
    base: Base<CharacterBody2D>,
}

#[godot_api]
impl ICharacterBody2D for Player {
    fn init(base: Base<CharacterBody2D>) -> Self {
        Self { speed: 200.0, base }
    }

    fn physics_process(&mut self, delta: f64) {
        let direction = Input::singleton()
            .get_axis("move_left".into(), "move_right".into());
        self.base_mut().set_velocity(Vector2::new(direction * self.speed as f32, 0.0));
        self.base_mut().move_and_slide();
    }
}

Supported GDScript Features

Class structure

  • class_name / extends (defaults to RefCounted when omitted)
  • Instance variables: var, const, static var
  • Annotations: @export, @export_range, @export_enum, @onready, @tool, @warning_ignore
  • Signals (declaration and emit_signal)
  • Inner classes

Functions

  • Public and private methods
  • Static methods
  • Virtual lifecycle methods: _ready, _process, _physics_process, _input, _unhandled_input, _draw, _enter_tree, _exit_tree, _notification, _init
  • Return types and typed parameters
  • super.method() calls

Control flow

  • if / elif / else
  • for loops (ranges and iterables)
  • while loops
  • match statements
  • break, continue, return, pass
  • await (coroutines via gdext_coroutines)

Expressions

  • Arithmetic, comparison, boolean, and bitwise operators
  • ** (power operator)
  • and, or, not
  • in / not in
  • is / is not (type checks)
  • as (casts)
  • Ternary expressions
  • Lambda / Callable (basic)
  • Dictionary literals

Built-in types

int, float, bool, String, Vector2, Vector3, Vector4, Vector2i, Vector3i, Color, Array, Dictionary, Rect2, Transform2D, Transform3D, Basis, Quaternion, Plane, AABB, and packed array types.

Built-in calls

print, push_error, len, range, abs, min, max, clamp, sqrt, sin, cos, floor, ceil, round, sign, and common Input, Vector, String, Array, and Dictionary methods.

Enums

Named enums and enums with explicit values.

Limitations

  • One-way transpilation. The Rust output becomes the source of truth; there is no sync back to GDScript.
  • Godot 4 only. Godot 3 / GDScript 1 is not supported.
  • Common subset, not every edge case. Highly dynamic GDScript patterns (e.g. runtime set()/get(), call(), deeply untyped code) may not transpile correctly and will require manual fixes.
  • Recursive coroutines emit a compiler warning and require manual rewriting.
  • Property setters/getters (set(value) / get():) have partial support; complex getter/setter bodies may need adjustment.
  • No GUI or IDE integration. Command-line only.
  • No WASM output. The tool targets native GDExtension builds. For web exports, ship the original GDScript unchanged alongside the project — the transpiler does not modify .gd source files or the scene tree, so both targets can coexist in the same project.
  • Basic Rust literacy required. The generated code is idiomatic gdext, but you will occasionally need to read and edit it.

Web Export Workflow

gd2rust is designed for native builds. The recommended split for projects targeting both platforms:

Native release  →  gd2rust  →  Rust GDExtension (.dll / .so)
Web export      →  GDScript unchanged  →  Godot web build (.wasm + .pck)

Because the transpiler only adds an extension alongside the project and never modifies .gd files, the same Godot project can produce both builds without any source changes.

Architecture

The pipeline has five stages:

.gd file(s)
    │
    ▼
 Lexer         tokenize; emit INDENT / DEDENT tokens
    │
    ▼
 Parser        recursive-descent → untyped AST
    │
    ▼
 Type Inferrer  resolve / infer Rust types for every AST node
    │
    ▼
 Code Gen       emit Rust source using gdext patterns
    │
    ▼
 Project Emitter  write Cargo.toml, lib.rs, .gdextension manifest

See docs/architecture.md for a full description of each stage and docs/gdscript-mapping.md for the authoritative GDScript-to-Rust reference.

License

MIT

About

Godot Gdscript to Rust Transpiler

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages