godot-rust/gdext

repository·master·Indexed 26 days ago

https://github.com/godot-rust/gdext

Rust bindings for Godot 4 (version 0.5.4) using the GDExtension API. It provides a type-safe, high-performance alternative to GDScript for games, editor plugins, and Godot-based applications. Key features include the #[derive(GodotClass)] attribute for defining Godot classes in Rust, support for virtual methods via traits, and thread-safe object access using GdCellBlocking.

Tokens
12.3K
Snippets
25
Records
68
Agent score
87%

What's inside godot-rust/gdext

  1. Overview of godot-rust/gdext

    master
    godot-rust is a library designed to integrate the Rust language with Godot 4 using the GDExtension API. It serves as a high-performance, type-safe alternative to GDScript. You can mix Rust and GDScript in the same project, allowing custom Rust APIs to be called type-safely from GDScript. The library is suitable for games, editor plugins, and other Godot-based applications.
  2. Getting started with godot-rust

    master

    To begin using godot-rust, refer to the following resources:

    • The godot-rust Book: The primary guide for learning the library.
    • API Docs: Detailed technical documentation for all exposed APIs.
    • Demo Projects: Practical examples and small games to study implementation patterns.

    If you encounter issues, you can join the community Discord and ask questions in the #help channel.

  3. Define a Godot class in Rust

    master

    To create a Godot class, use the #[derive(GodotClass)] attribute on a struct. You can specify inheritance using the #[class(base=BaseClassName)] attribute and enable automatic initialization with #[class(init)].

    Key features include:

    • Inheritance: Access base class methods via a base: Base<BaseClassName> field.
    • Field Initialization: Use #[init(val = value)] to set default values for fields.
    • Node Access: Use #[init(node = "path/to/node")] with an OnReady<Gd<T>> type to automatically fetch a child node when _ready() is called.
    • Virtual Methods: Implement Godot's virtual methods by implementing the corresponding trait (e.g., ISprite2D) and marking the implementation with #[godot_api].
    • Exposing Methods to GDScript: Use the #[func] attribute on methods within an #[godot_api] implementation block to make them callable from GDScript.
    use godot::classes::{ISprite2D, ProgressBar, Sprite2D};
    use godot::prelude::*;
    
    // Declare the Player class inheriting Sprite2D.
    #[derive(GodotClass)]
    #[class(init, base=Sprite2D)]
    struct Player {
        base: Base<Sprite2D>,
    
        #[init(val = 100)]
        hitpoints: i32,
    
        #[init(node = "Ui/HealthBar")]
        health_bar: OnReady<Gd<ProgressBar>>,
    }
    
    #[godot_api]
    impl ISprite2D for Player {
        fn ready(&mut self) {
            godot_print!("Player ready!");
            self.health_bar.set_max(self.hitpoints as f64);
            self.health_bar.set_value(self.hitpoints as f64);
    
            self.health_bar.signals().value_changed().connect(|hp| {
                godot_print!("Health changed to: {hp}");
            });
        }
    }
    
    #[godot_api]
    impl Player {
        #[func]
        fn take_damage(&mut self, damage: i32) {
            self.hitpoints -= damage;
            godot_print!("Player hit! HP left: {}", self.hitpoints);
    
            self.health_bar.set_value(self.hitpoints as f64);
    
            if self.hitpoints <= 0 {
                self.base_mut().queue_free();
            }
        }
    }
  4. Use GDExtension libraries as dependencies

    master

    If your crate depends on other GDExtension libraries, you must specify the main extension implementor using the GDRUST_MAIN_EXTENSION environment variable during build. This ensures all classes are loaded and callbacks are managed correctly.

    Example build command:

    GDRUST_MAIN_EXTENSION="MyExtension" cargo build

    Requirements for dependencies:

    • Dependencies must be compilable as rlib. In their Cargo.toml, they should have crate-type = ["cdylib", "rlib"] or no [lib] section at all.
    • The name of the ExtensionLibrary implementor must be unique across your workspace and its dependencies.
  5. Use the `godot` crate instead of `godot-core`

    master
    The godot-core crate is an internal crate used by the gdext library. It does not provide SemVer or other stability guarantees. End-users should depend on the godot crate for all development to ensure API stability and access to the intended public surface.
  6. Define Godot class properties with `#[var]` and `#[export]`

    master

    When defining a Godot class using #[derive(GodotClass)], you can expose struct fields to the Godot engine using the #[var] and #[export] attributes.

    • #[var]: Registers a field as a Godot property. You can specify custom getters, setters, renaming, and property hints.
    • #[export]: A shorthand that automatically registers a field as an exported property in the Godot Inspector. If used without #[var], it infers the necessary property registration.

    Properties can be organized into groups and subgroups using the group/subgroup metadata provided by the attributes.

  7. Implement the ExtensionLibrary trait

    master

    Every GDExtension Rust library must implement the ExtensionLibrary trait. This trait serves as the entry point for your extension, handling initialization, deinitialization, and lifecycle hooks. You must use the #[gdextension] proc-macro attribute on your implementation.

    Basic Usage

    By default, gdext automatically registers all classes marked with #[derive(GodotClass)] and handles necessary setup.

    use godot::init::*;
    
    struct MyExtension;
    
    #[gdextension]
    unsafe impl ExtensionLibrary for MyExtension {}

    Custom Entry Symbol

    If your .gdextension file specifies a custom entry_symbol in the [configuration] section, you must specify it in the attribute:

    #[gdextension(entry_symbol = custom_name)]
    unsafe impl ExtensionLibrary for MyExtension {}
  8. Configure RPC methods with the `#[rpc]` attribute

    master

    You can define Godot Remote Procedure Call (RPC) methods in Rust using the #[rpc] attribute. This attribute allows you to specify how the method is called across the network.

    There are two ways to configure an RPC:

    1. Using Separated Arguments

    You can pass individual configuration parameters directly into the attribute: #[rpc(rpc_mode, transfer_mode, call_local, channel = N)]

    • rpc_mode: Use RpcMode::AnyPeer or RpcMode::Authority.
    • transfer_mode: Use TransferMode::Reliable, TransferMode::Unreliable, or TransferMode::UnreliableOrdered.
    • call_local: A boolean indicating if the method should be called on the local instance.
    • channel: A u32 specifying the network channel.

    2. Using a Configuration Expression

    You can pass a pre-defined RpcConfig object using the config key: #[rpc(config = RPC_CFG)]

  9. Configure custom Godot API via Cargo features

    master

    You can control how the extension API and GDExtension interface are loaded by enabling specific Cargo features in your Cargo.toml:

    • api-custom: Regenerates all files by locating the Godot executable and reading its version and JSON files.
    • api-custom-json: Generates files based on user-provided JSON files.

    When using these features, the following functions become available to load the necessary metadata:

    • load_extension_api_json(watch: &mut StopWatch) -> Cow<'static, str>
    • load_gdextension_interface_json(watch: &mut StopWatch) -> Cow<'static, str>
  10. Enable WebAssembly (Wasm) support

    master

    To target WebAssembly, you must explicitly opt-in using the experimental-wasm feature.

    • With Threads (Default): Requires the wasm32-unknown-unknown target to be compiled with "-C", "link-args=-pthread". This must match Godot's Web export threading settings.
    • Without Threads: Enable the experimental-wasm-nothreads feature (which requires experimental-wasm). This must be kept in sync with Godot's Web export setting (threading disabled) and should not use the -pthread link argument.