Giter Site home page Giter Site logo

godot-rust / gdnative Goto Github PK

View Code? Open in Web Editor NEW
3.6K 46.0 208.0 7.77 MB

Rust bindings for Godot 3

Home Page: https://godot-rust.github.io

License: MIT License

Rust 79.45% C 19.48% GDScript 0.41% Shell 0.66%
gamedev rust godot game-development

gdnative's Introduction

GDNative bindings for Rust

crates.io stable docs master docs book website

godot-rust is a Rust library that implements native bindings for the Godot game engine. This allows you to develop games or other applications in Godot, while benefiting from Rust's strengths, such as its type system, scalability and performance.

Note: if you are looking for a Rust binding for GDExtension (Godot 4), checkout gdext.

Maintenance Policy

gdnative is considered mostly feature complete, and is maintained with a focus on API stability. We try to avoid unnecessary breaking changes, and try to limit their end-user impact to a minimum whenever we have to make them.

We adhere to Cargo's semantic versioning as the means to convey changes in the public API between versions. Future releases are planned publicly on GitHub, with the milestone feature. Note that we use the breaking-change label to indicate the existence of any technical breakage, regardless of the expected impact on end user programs.

If you are looking to contribute, but are not sure if what you want to do falls in the scope of the project and is permitted by our maintenance policy, feel free to get in touch with the project maintainers before you start.

Toolchain compatibility

gdnative currently has a minimum supported Rust version (MSRV) of 1.70. We use the Rust 2021 Edition.

Warning: Linux users: Be aware of the source of your Godot binary! Binary distributions of Godot using a container-based format may ship versions of dependencies that may not be compatible with GDNative libraries built directly from your base system. Examples of such formats include Flatpak, Snap, and AppImage.

As of 2023, some package managers might silently install one of these instead of a normal package when Godot is requested, which can then cause bizarre compatibility issues with your GDNative libraries. We recommend using the official binaries from godotengine.org for both the editor and the export templates.

Due to GDNative API not strictly following SemVer and some concepts not mapping 1:1 to Rust (default parameters), it is difficult for a godot-rust version to remain compatible with multiple Godot versions simultaneously.

However, we support the latest stable Godot 3 minor release out-of-the-box, and allow to easily use custom engine versions using the custom-godot feature flag (see below).

Compatibility list:

  • Godot 3.5.1 (works with gdnative 0.11)
  • Godot 3.4 (works with gdnative 0.10, custom build for 0.11)
  • Godot 3.3 (custom build)
  • Godot 3.2 (custom build)

The bindings do not support Godot 4. If you are looking for a Rust binding for GDExtension (Godot 4), checkout gdextension.

Getting started

Detailed setup is explained in the Getting Started section of the book. In case of problems, consider also reading the FAQ.

Latest released version

This is the recommended way of using godot-rust. After bindgen dependencies and a current Godot version are installed, add the gdnative crate as a dependency, and set the crate type to cdylib:

[dependencies]
gdnative = "0.11"

[lib]
crate-type = ["cdylib"]

Latest GitHub version

If you would like to benefit from cutting-edge features and bugfixes, you can use the GitHub version. We have a relatively sophisticated CI and test suite for basic stability, but the GitHub version is typically more experimental and less battle-tested than a crates.io release. We also do not guarantee any SemVer compatibility here.

[dependencies]
gdnative = { git = "https://github.com/godot-rust/godot-rust.git" }

[lib]
crate-type = ["cdylib"]

Custom builds

To use the bindings with a different Godot version or a custom build of the engine, see Custom Godot builds in the user guide.

Async/yield support

Async support is a work-in-progress, with a low-level API available in gdnative::tasks, if the async feature is enabled on gdnative. See this page in the book for an introduction to use the async feature with Tokio.

Example

A typical use case is to expose your own Native Class, a Rust API that can be invoked from the Godot engine. The resulting native script can be attached to the scene tree, just like GDScript (.gd files).

This happens via dynamic libraries and the GDNative interface, which will be loaded from Godot. The necessary wiring is done behind the scenes by godot-rust. A simple "Hello world" application could look like this:

use gdnative::prelude::*;

#[derive(NativeClass)]
#[inherit(Node)]
pub struct HelloWorld;

#[methods]
impl HelloWorld {
    fn new(_base: &Node) -> Self {
        HelloWorld
    }

    #[method]
    fn _ready(&self, #[base] _base: &Node) {
        godot_print!("Hello, world.");
    }
}

fn init(handle: InitHandle) {
    handle.add_class::<HelloWorld>();
}

godot_init!(init);

Further examples

Important note:

To run or edit an example, you need to build the native library for it first. Otherwise, the project will be broken. You can do so manually with cargo build, or use the example.sh shell script for convenience: ./example.sh run hello-world or ./example.sh edit hello-world for the editor.

The /examples directory contains several ready to use examples, complete with Godot projects and setup for easy compilation from Cargo:

At startup, the Godot editor tries to load all resources used by the project, including the native library. If the latter isn't present, the editor will skip properties or signals associated with the missing native scripts in the scene. This causes the scene tree to be non-functional for any sample that relies on properties or signals configured in the editor.

Third-party projects

To see a list of games and integrations developed on top of godot-rust, have a look at our list of third-party projects in the book.

Contributing

See the contribution guidelines.

License

Any contribution submitted for inclusion in the work by you shall be licensed under the MIT license, without any additional terms or conditions.

gdnative's People

Contributors

b-head avatar bakamomi avatar bluenote10 avatar bogay avatar bors[bot] avatar bromeon avatar canadahonk avatar chitoyuu avatar damszew avatar demindiro avatar dependabot[bot] avatar devjac avatar glfmn avatar halzy avatar haxeil avatar jacobsky avatar jonerikdsuero avatar karroffel avatar memoryruins avatar mivort avatar nical avatar parasyte avatar redwanfox avatar robert7301201 avatar setzer22 avatar tom-leys avatar tylermartinez avatar vurpo avatar waridley avatar wmorgue avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gdnative's Issues

Automate pre-commit testing with hook

Hey, it seems that tests are intended to run manually on every commit, so I decided I should make a pull request adding a pre-commit hook on bash platforms with a hook, adapted from one I wrote for amethyst. This would mean:

  • tests run automatically on every commit, requiring explicit --no-verify when tests fail
  • no need to change directory when running the test project
  • the script will automatically stash all unstaged changes just in case they affect the build (something I would previously often forget to do)

You can find the gist here, and feel free to suggest any improvements!

However, I'm running into a couple of troubling issues that I haven't been able to find help with elsewhere:

1. Godot can't find libgdnative_test.so

OpenGL ES 3.0 Renderer: Mesa DRI Intel(R) HD Graphics 5500 (Broadwell GT2) 
 -- Rust gdnative test suite:
ERROR: open_dynamic_library: Can't open dynamic library: /home/me/Documents/godot-rust/test/project/lib/libgdnative_test.so. Error: /opt/Godot/../lib/libgdnative_test.so: cannot open shared object file: No such file or directory
   At: drivers/unix/os_unix.cpp:373.
 -- Could not load the gdnative library.
 -- exiting.

It seems to look for libgdnative_test.so from a relative path for Godot, which I have installed at /opt/Godot/Godot_v3.0.2-stable_x11.64, symlinked as /opt/godot and added to my path so I can open it from cli with godot.

I don't see libgdnative_test.so mentioned anywhere, so I'm not sure what it is or where to get it, or where I should install it.

Are these the headers at godot-headers?

2. Godot exits status 0 on failure to find libgdnative_test.so

Quite puzzlingly, Godot exists with a normal status 0 exit code when it fails to find the file, so my pre-commit script still passes a potentially failed build.

When the Godot integration tests run, do they exit status != 0 on failure?

If not, and if we can't change that behaviour, I'll need to figure out another way to detect failure from the script. A hack I can think of is to capture the output and grep it for ERROR, but that's super brittle, and super jank.

Crash in godot when running the example.

The binding appears to be passing some invalid values to the engine which chokes on it. I get different behaviors on a local build and on the one I got from flatpak on fedora. On the flatpak all I could get from the stacktrace was that it happens when trying to set a property, so my guess is that there's something wrong going on in property.rs.
It's a bit intermittent. After a few modifications of the code and it stops reproducing, and then comes back for no apparent reason.

Make accessing a rust script's owner more convenient

@karroffel had some good feedback and ideas about this.
I don't remember the details, but in a nutshell, scripts are attached to things (their owner), and they tend to access the owner a lot, so we should make this interaction as smooth as possible.

One of the ideas was related to having the callback methods like _ready take a special owner argument, or maybe was it in all methods. It would be good to precise the plan and try it out!

Generate nicer enum variants

Here is an example of a generated enum:

pub enum CanvasItemBlendMode {
    BlendModeMix = 0,
    BlendModeAdd = 1,
    BlendModeSub = 2,
    BlendModeMul = 3,
    BlendModePremultAlpha = 4,
    BlendModeDisabled = 5,
}

It would be nice if we could detect that part of the enum name is in all variant names to generate this instead:

pub enum CanvasItemBlendMode {
    Mix = 0,
    Add = 1,
    Sub = 2,
    Mul = 3,
    Alpha = 4,
    Disabled = 5,
}

It'd be a tiny improvement but the API would be nicer overall since in Rust you end up see the enum name in front of the variant name most of the time (for example CanvasItemBlendMode::BlendModeAdd).

How good does this work already?

Could you maybe make a quick statement or readme about this projects state? Is it ready if you want to write your next game with rust/godot? What is missing?

Impement missing core types.

Filing this placeholder issue for the remaining work with core types.

  • Plane
  • Array
  • Dictionary
  • PoolStringArray
  • PoolByteArray
  • PoolVector2Array
  • PoolVector3Array
  • PoolIntArray
  • PoolRealArray
  • PoolColorArray
  • enums
  • read access arrays
  • write access arrays
  • String

Ownership model.

Currently we have GodotRef<T> which can refer to either reference counted objects or non-reference counted objects. GodotRef doesn't automatically deallocate non-reference counted objects.

The plan

  • GodotRef<T> only usable with reference counted types (Will probably rename it into RefCounted<T> in the process).
  • Owned<T> (or maybe Unique<T>) will work with non reference counted types and automatically deallocates the object in its Drop implementation.
  • Temporary<T> or maybe Ref<T> will work with the same types as Owned<T> but will be what programmers see for temporary references provided by the engine that should not be deallocated by the rust side.
  • Owned<T> can be turned into a Ref<T> for specific cases for example if the engine needs to take ownership of the value, although doing the conversion without giving ownership to the engine will result in a leak.

It could be that I don't need a Ref<T> wrapper and can just use &T in most cases.

Add docs showing basic setup intructions

I'm a rustacean and an indie game developer. The main reason i'm looking to switch to godot is for these rust bindings. Any chance someone could update the docs to show the basic path to integrating with godot and perhaps a list of missing features (missing api's, code completion, hot reloading, etc) to make it easier to determine the state of affairs.

Thanks for putting in the work guys!

Memory safety and non-reference objects

Followup from #78

Memory safety and non-reference objects

The non-reference counted stuff is exposed as a safe API but it really isn't safe to call a method on them since there is no way to prove that an object we call a function on is still alive, for example if we pass it to the engine and it deallocates it.

  • One drastic solution to this is to just mark all methods for these objects as unsafe. It's not great because it means reference counted objects inheriting methods from non-reference counted objects would inherit them as unsafe even though refcounting makes them safe to call.
  • Another solution is to pretend this is not a problem, and just mark things that can deallocate these objects as unsafe for good measure (the method free, and passing ownership to the engine). Not super rusty but might be the best compromise.

Casting a reference counted into non-reference counted

Right now if a reference counted object is casted into a non-reference counted pointer of a parent type, the resulting pointer does not handle reference counting. This kinda ties into the first todo, and might not be a real issue but I suspect it might be a bit surprising, for example:

{
    let mesh_library: MeshLibray = ... // MeshLibrary is ref counted.
    obj = mesh_library.cast<Object>().unwrap(); // Object is not ref counted.
    // Maybe this was the last reference to the mesh lib and it dies here.
}
let id = obj.get_instance_id(); // Oh noes! obj points to deallocated memory.

In principle it's the same issue as the first one but we could work around it by having all non-reference counted object pointers store a boolean to check at runtime whether the underlying object is actually reference counted. Figuring the value of that boolean when receiving an Object from the engine would come with quite a bit of overhead, though.

Rust's rule of "No memory safety hazards with safe interfaces unless the user wrote an unsafe block that interacts with it." could kinda be respected by making the cast functionality unsafe (or all accesses to non-reference counted objects unsafe).

Relevant comment from @karroffel in #78

Talking about Godot deallocating things under our butts, with NativeScript 1.1 there's a way to set "instance binding data", so per-(godot-)object storage that language bindings can use. The C++ bindings use that to implement small wrapper objects so that regular inheritance can be used etc.
The interface for this instance data requires setting a callback for when an object is about to die, then the language binding data should be freed properly, maybe that could be used to throw panics if there are still users of that object?

This binding data system is interesting. There is different ways we could use it to assist memory management.

Audit/document/fix the conventions about ownership in the bindings.

Some ffi functions take objects by value and some by pointer, so I naively thought the former meant the ownership was passed to C++ and the destructor should not be run on the rust side, it turns out to not be the case.

Objects passed as parameter in FFI functions implemented in C++ called by rust

Whether or not the object is passed by value, the destructor should be called on the rust side.

Objects returned by FFI functions implemented in C++ called by rust

The destructor should be called by the rust side (I think, let's double check).

Objects returned by FFI functions implemented in rust called by C++

Ownership is actually transferred to the C++ side this time. Make sure to not call the destructor on the rust side, for example using methods like Variant::forget.

Objects passed as parameter in FFI functions implemented in Rust called by C++

??

Clearing non-trivial godot types clears clones. Implement clone.

Calling clear() on a Dictionary clears the contents of its clones.

dict.clear();
assert!(dict.is_empty());
assert!(!dict_clone.is_empty());

Appending the above to the end of the current Dictionary tests, after let dict_clone = dict.clone();, will cause the test to fail with panicked at 'assertion failed: !dict_clone.is_empty()'.

I'd expect this to be the behavior of passing the dictionary object around in gdscript, but am I assuming correctly that this would be unexpected behavior for clones in Rust's api?

Missing methods on the wrapper types.

Most wrapper types are missing methods provided by the C API, it would be good to add them.

The best way to find missing methods is to generate the documentation for the sys and gdnative crates and compare them to see what's missing in the gdnative wrappers.

Issues exposing a simple class

Apologies if I shouldn't be asking these sorts of questions here. I'm building a fairly simple GDNative module which I can't seem to expose to GDScript, and I'd really like to use Rust. You can see my work here, I'm trying to expose text-to-speech modules to Godot. I'm testing this by checking out this addon, then running:

$ ln -sf ../godot-tts/target
$ ln -sf ../godot-tts/godot_tts.gdns
$ ln -sf ../godot-tts/godot_tts.gdnlib

Questions:

  1. My hope is to expose a TTS class with TTS.new(), and TTS.speak(). I symlink the included gdns/gdnlib files into a project, symlink the target, then try loading it like so:
onready var TTS = preload("res://godot_tts.gdns")

func _enter_tree():
    var tts = TTS.new()

This gives:

SCRIPT ERROR: _enter_tree: Invalid call. Nonexistent function 'new' in base 'Nil'.

Am I setting up my class incorrectly? Do I need to manually register rather than just doing:

fn init(handle: godot::init::InitHandle) {
    TTS::register_class(handle);
}

godot_gdnative_init!();
godot_nativescript_init!(init);
godot_gdnative_terminate!();

Another question. What data types should I use in exported functions for GDScript compatibility? If I want to pass GDScript strings to Rust, for instance, do I use &str or can I use String?

Finally, are there any examples of registering a GDScript function with an optional argument? Looks like I can wrap native Rust functions, but what I'd like to do is use something like Option<bool> then .unwrap_or(true) on the parameter to simulate optional parameters in GDScript, even if Rust needs a parameter. Is this possible?

Thanks for any assistance.

Question: Mobile Support

Hi,
Thanks for the effort to bring rust to godot. Im currently playing arround with the wrapper to evaluate the usage for creating a new game. In general I'm aware of rust's toolchain and platform support but just wanted to know if the wrapper is also platform independent...

Thanks for any feedback!
Kind regards
Georg

Implement signal arguments.

See property.rs. The Signal struct should take a &'l [SignalArgument] as parameter and fill the sys::godot_signal struct accordingly.

Staticlib Build fails for some Android platforms

Environments

macOS High Sierra
rustc 1.26.1
rustup 1.11.0
gdnative v0.3.0

Log

error[E0308]: mismatched types
  --> /Users/je/Downloads/godot-rust-master/target/armv7-linux-androideabi/debug/build/gdnative-53a4272163a872bf/out/types.rs:49:73
   |
49 |             table.class_constructor = (api.godot_get_class_constructor)(class_name);
   |                                                                         ^^^^^^^^^^ expected u8, found i8
   |
   = note: expected type `*const u8`
              found type `*const i8`

error[E0308]: mismatched types
   --> /Users/je/Downloads/godot-rust-master/target/armv7-linux-androideabi/debug/build/gdnative-53a4272163a872bf/out/types.rs:235:73
    |
235 |             table.class_constructor = (api.godot_get_class_constructor)(class_name);
    |                                                                         ^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`

error[E0308]: mismatched types
   --> /Users/je/Downloads/godot-rust-master/target/armv7-linux-androideabi/debug/build/gdnative-53a4272163a872bf/out/types.rs:236:70
    |
236 |             table._notification = (api.godot_method_bind_get_method)(class_name, "_notification\0".as_ptr() as *const i8 );
    |                                                                      ^^^^^^^^^^ expected u8, found i8
    |
    = note: expected type `*const u8`
               found type `*const i8`


...repeat...

Fail with error

armv7-linux-androideabi
aarch64-linux-android

Success without error

x86_64-apple-darwin
aarch64-apple-ios
armv7-apple-ios
i686-linux-android

Hello. I have encountered errors for every *const i8 casting in generated types.rs when build for armv7-linux-androideabi and aarch64-linux-android

BTW it works perfectly on OSX and iOS(only tested 64bits). Thank you for nice library ๐Ÿ‘ ๐Ÿ‘

Standard code style

What do you think about reformatting your code via cargo fmt ? I use only nightly toolchain and have
rustfmt 0.3.6-nightly, but I suggest using a stable version for formatting the code.

Make the API easy to use without macros.

I received the feedback that abusing custom syntax can be intimidating, and make it hard to understand what is happening under the hood. As a general rule of thumb I think that It's best for an API to be easily usable without macros and then on top of that optionally provide some macros to provide extra ergonomics for those who want it.
Let's follow this rule for defining and registering scripts and classes.

Error in "Manually Registered" Example

Hi!

I'm getting the following error when trying the "Manually Registered" example:

0:00:00:0289 - Can't resolve symbol godot_rust_gdnative_init. Error: dlsym(0x7fccc0696cf0, godot_rust_gdnative_init): symbol not found
0:00:00:0289 - Failed to obtain godot_gdnative_init symbol
0:00:00:0289 - No valid library handle, can't get symbol from GDNative object
0:00:00:0289 - No nativescript_init in "../../target/debug/libmanually_registered.dylib" found

The error started happening in commit: 8f9f7d7.

Integrate Piston (game engine) with Godot

Piston http://www.piston.rs is a game engine that specifically focuses on Rust programming. I was wondering if Godot can use Piston libraries for users who want to use Rust for their game development? Rust is far more powerful and faster compared to C# and it has got high level abstractions which makes it look and feel like a high level programming language.

Unit Testing Godot Classes

With the hello world example, I added a public method:

export fn foo(&mut self) -> String {
    String::from("foo works!")
}

I'm struggling with writing a unit test for this. Here's what I have so far.

#[test]
fn it_makes_foo_work() {
    let hello_world = HelloWorld { header: godot::NativeInstanceHeader { this: godot::init::InitHandle { handle: ... } } };
    assert_eq!(hello_world.foo(), "foo works!");
}

I cannot figure out what to put for the handle for godot::init::InitHandle.

Is there an easier way to test public methods for Godot classes?

Thanks!

example project

I'm interested in using Rust as a scripting language for Godot, but there's no example project here.

Generate methods that deal with enums.

This json API generator in build.rs currently skips methods that take or return enums. We just need to write bit of glue that converts between the godot enum values to the rust equivalents.

Is the project dead?

@vurpo
I would like to know if this is going to be completed soon.
If not I would like to know how to complete it or at least try to.

I am really interested in using Rust in Godot. Thank you.

Getting started

How about a document explaining how to get started with godot-rust?

API documentation.

Most of the wrapper types don't have documentation at the moment, it'd be great to improve in this area.

Improve the `godot_wrap_method` macro

  • Right now you need to explicitly use the -> type syntax, it would be nice if we could ommit the arrow if the result is ().
  • It currently only accept &mut self as the receiver, should support &self too.

wasm

rust has great support for compiling to WASM, but i was wondering if there are examples to compile a godot app to wasm that uses rust ??

Fix the generated doc

Because of the crate split, some doc links are broken (inter-crate ones), and it would be great if all generated classes could be documented in the same place.

Simplify registering a `NativeClass`

let class = handle.add_class::<Player>(
    ClassDescriptor {
        name: "Player",
        base_class: "Area2D",
        constructor: Some(constructor),
        destructor: Some(destructor),
    }
);

We could put most or all of this information in the trait, including the constructor and destructor.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.