Skip to content

Commit 4394873

Browse files
committed
Restructure into GDNative overview and Rust binding chapters
1 parent d062194 commit 4394873

File tree

11 files changed

+633
-417
lines changed

11 files changed

+633
-417
lines changed

src/SUMMARY.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@
44
- [Getting Started](./getting-started.md)
55
- [Setup](./getting-started/setup.md)
66
- [Hello, world!](./getting-started/hello-world.md)
7+
- [An Overview of GDNative](./gdnative-overview.md)
8+
- [Data representations](gdnative-overview/data-representations.md)
9+
- [Ref, TRef and Instance -- wrappers for custom types](gdnative-overview/wrappers.md)
10+
- [Binding to Rust code](./rust-binding.md)
11+
- [Class registration](./rust-binding/classes.md)
12+
- [Exported methods](./rust-binding/methods.md)
13+
- [Exported properties](./rust-binding/properties.md)
14+
- [Calling into GDScript from Rust](./rust-binding/calling-gdscript.md)
715
- [FAQ](./faq.md)
8-
- [(TODO) An Overview of GDNative](./gdnative-overview.md)
916
- [(TODO) Testing](./testing.md)
1017
- [Structuring Code for Testing](./testing/structure.md)
1118
- [Testing with the Engine](./testing/engine.md)
@@ -15,6 +22,4 @@
1522
- [Mac OS X](./exporting/macosx.md)
1623
- [Advanced Guides](./advanced-guides.md)
1724
- [Using custom builds of Godot](./advanced-guides/custom-bindings.md)
18-
- [Data representations](advanced-guides/data-representations.md)
19-
- [Exchanging data between GDScript and Rust](./advanced-guides/data-exchange.md)
2025
- [Migrating from godot-rust 0.8.x](./migrating-0-8.md)

src/advanced-guides/data-exchange.md

Lines changed: 0 additions & 386 deletions
This file was deleted.

src/faq.md

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ It's relatively easy to work around this problem, though: Because of how the use
1616

1717
**Question**
1818

19-
A native script type needs to implement `fn new(owner: &Node) -> Self`.
19+
A native script type needs to implement `fn new(owner: &Node) -> Self`.
2020
Is it possible to pass additional arguments to `new`?
2121

2222
**Answer**
@@ -26,26 +26,66 @@ Unfortunately this is currently a general limitation of GDNative (see [related i
2626
As a result, a common pattern to work-around the limitation is to use explicit initialization methods. For instance:
2727

2828
```rust
29+
struct EnemyData {
30+
name: String,
31+
health: f32,
32+
}
33+
2934
#[derive(NativeClass)]
3035
#[inherit(Object)]
31-
struct DataWrapper {
32-
data: Option<Data>,
36+
struct Enemy {
37+
data: Option<EnemyData>,
3338
}
3439

35-
#[godot::methods]
36-
impl DataWrapper {
40+
#[methods]
41+
impl Enemy {
3742
fn new(_owner: &Object) -> Self {
38-
DataWrapper {
43+
Enemy {
3944
data: None,
4045
}
4146
}
4247

4348
#[export]
44-
fn set_data(&mut self, _owner: &Object, additional_arg1: i32, additional_arg2: i32) {
45-
self.data = Some(Data::new(additional_arg1, additional_arg2));
49+
fn set_data(&mut self, _owner: &Object, name: String, health: f32) {
50+
self.data = Some(EnemyData { name, health });
51+
}
52+
}
53+
```
54+
55+
This however has two disadvantages:
56+
1. You need to use an `Option` with the sole purpose of late initialization, and subsequent `unwrap()` calls or checks -- weaker invariants in short.
57+
1. An additional type `EnemyData` for each native class like `Enemy` is required (unless you have very few properties, or decide to add `Option` for each of them, which has its own disadvantages).
58+
59+
An alternative is to register a separate factory class, which returns fully-constructed instances:
60+
```rust
61+
#[derive(NativeClass)]
62+
#[no_constructor] // disallow default constructor
63+
#[inherit(Object)]
64+
struct Enemy {
65+
name: String,
66+
health: f32,
67+
}
68+
69+
#[methods]
70+
impl Enemy {
71+
// nothing here
72+
}
73+
74+
#[derive(NativeClass)]
75+
#[inherit(Reference)]
76+
struct EntityFactory {}
77+
78+
#[methods]
79+
impl EntityFactory {
80+
#[export]
81+
fn enemy(&self, _owner: &Object, name: String, health: f32)
82+
-> Instance<Enemy, Unique> {
83+
Enemy { name, health }.emplace()
4684
}
4785
}
4886
```
87+
So instead of `Enemy.new()` you can write `EntityFactory.enemy(args)` in GDScript.
88+
This still needs an extra type `EntityFactory`, however you could reuse that for multiple classes.
4989

5090

5191
## Static methods

src/gdnative-overview.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
# An Overview of GDNative
22

3-
TODO
3+
GDNative is the interface between the Godot engine and bindings in native languages, such as C, C++ or Rust.
4+
5+
This chapter gives a broad overview of basic GDNative concepts and godot-rust's approach to implement them in Rust. It is not a usage guide for exposing your Rust code to Godot; see chapter [Binding to Rust code](rust-binding.md) for concrete examples.
6+
7+
Subchapters:
8+
9+
1. [Data representations](gdnative-overview/data-representations.md)
10+
1. [`Ref`, `TRef` and `Instance`](gdnative-overview/wrappers.md)

src/advanced-guides/data-representations.md renamed to src/gdnative-overview/data-representations.md

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
# Data representations
22

3-
The godot-rust library uses many different approaches to store and transport data. This chapter explains high-level concepts of related terminology used throughout the library and its documentation. It is not a usage guide however -- to see the concepts in action, check out other advanced topics such as [Exchanging data between GDScript and Rust](data-exchange.md).
4-
5-
* [Object and class](#object-and-class)
6-
* [Variant](#variant)
7-
* [Script](#script)
8-
* [Instance](#instance)
3+
The godot-rust library uses many different approaches to store and transport data. This chapter explains high-level concepts of related terminology used throughout the library and its documentation. It is not a usage guide however -- to see the concepts in action, check out [Binding to Rust code](../rust-binding.md).
94

105

116
## Object and class
127

13-
Godot is built around _classes_, object-oriented types in a hierarchy, with the base class `Object` at the top. When talking about classes, we explicitly mean classes in the `Object` hierarchy and not built-in types like `String`, `Vector2`, `Color`, even though they are technically classes in C++. In Rust, such classes are represented as structs.
8+
Godot is built around _classes_, object-oriented types in a hierarchy, with the base class `Object` at the top. When talking about classes, we explicitly mean classes in the `Object` hierarchy and not built-in types like `String`, `Vector2`, `Color`, even though they are technically classes in C++. In Rust, classes are represented as structs.
149

1510
Every user-defined class inherits `Object` directly or indirectly, and thus all methods defined in `Object` are accessible on _any_ instance of a user-defined class. This type includes functionality for:
1611
* object lifetime: `_init` (`new` in Rust), `free`
@@ -27,6 +22,9 @@ Every user-defined class inherits `Object` directly or indirectly, and thus all
2722
* **`Node`**
2823
Anything that's part of the scene tree, such as `Spatial` (3D), `CanvasItem` and `Node2D` (2D). Each node in the tree is responsible of its children and will deallocate them automatically when it is removed from the tree. At the latest, the entire tree will be destroyed when ending the application.
2924
**Important:** as long as a node is not attached to the scene tree, it behaves like an `Object` instance and must be freed manually. On the other hand, as long as it is part of the tree, it can be destroyed (e.g. when its parent is removed) and other references pointing to it become invalid.
25+
* **`Resource`**
26+
Data set that is loaded from disk and cached in memory, for example 3D meshes, materials, textures, fonts or music (see also [Godot tutorial](https://docs.godotengine.org/en/stable/getting_started/step_by_step/resources.html)).
27+
`Resource` inherits `Reference`, so in the context of godot-rust, it can be treated like a normal, reference-counted class.
3028

3129
When talking about inheritance, we always mean the relationship in GDScript code. Rust does not have inheritance, instead godot-rust implements `Deref` traits to allow implicit upcasts. This enables to invoke all parent methods and makes the godot-rust API very close to GDScript.
3230

@@ -60,15 +58,3 @@ Scripts _always_ inherit another class from Godot's `Object` hierarchy, either a
6058
_See `Script` in
6159
[godot-rust docs](https://docs.rs/gdnative/latest/gdnative/api/struct.Script.html),
6260
[Godot docs](https://docs.godotengine.org/en/latest/classes/class_script.html)_
63-
64-
65-
## Instance
66-
67-
In Rust, the `Instance` type stores a native script instance alongside its _base object_ (the instance of the Godot class that the script inherits).
68-
69-
For example, if you have a Rust struct `VictoryArea` extending Godot node type `Area`, then the instance will hold both the struct instance and the Godot `Area` instance (as Rust doesn't support inheritance, its struct object cannot directly hold the base object's data).
70-
71-
The traits `ToVariant`, `FromVariant` and `OwnedToVariant` are automatically implemented for `Instance` types. This allows to pass them from and to the Godot engine.
72-
73-
_See `Instance` in
74-
[godot-rust docs](https://docs.rs/gdnative/latest/gdnative/api/struct.Instance.html)_

src/gdnative-overview/wrappers.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# `Ref`, `TRef` and `Instance`
2+
3+
Objects from Godot, such as scene nodes, materials, or other resources are owned and maintained by the Godot engine. This means that your Rust code will store references to existing objects, not values. godot-rust provides special wrapper types to deal with these references, which are explained in this page.
4+
5+
These classes stand in contrast to value types like `bool`, `int`, `Vector2`, `Color` etc., which are copied in GDScript, not referenced. In Rust, those types either map to built-in Rust types or structs implementing the `Copy` trait.
6+
7+
## `Ref`: persistent reference
8+
9+
The generic smart pointer `gdnative::Ref<T, Access>` allows you to store `Object` instances in Rust. It comes with different access policies, depending on how the memory of the underlying object is managed (consult [the docs](https://docs.rs/gdnative/latest/gdnative/struct.Ref.html) for details). Most of the time, you will be working with `Ref<T>`, which is the same as `Ref<T, Shared>` and the only access policy that is explained here. Its memory management mirrors that of the underlying type:
10+
* for all Godot objects inheriting the `Reference` class, `Ref<T>` is reference-counted like `Arc<T>` and will clean up automatically.
11+
* for all other types (i.e. the type `Object` and inheritors of `Node`), `Ref<T>` behaves like a raw pointer with manual memory management.
12+
13+
For example, storing a reference to a Godot `Node2D` instance in a struct would look as follows:
14+
```rust
15+
struct GodotNode {
16+
node_ref: gd::Ref<gd::api::Node2D>,
17+
}
18+
```
19+
20+
_See `Ref` in
21+
[godot-rust docs](https://docs.rs/gdnative/latest/gdnative/struct.Ref.html)_
22+
23+
24+
## `TRef`: temporary reference
25+
26+
While `Ref` is a persistent pointer to retain references to Godot objects for an extended period of time, it doesn't grant access to the underlying Godot object. The reason for this is that `Ref` cannot generally guarantee that the underlying object, which is managed by the Godot engine, is valid at the time of the access. However, you as a user are in control of GDScript code and the scene tree, thus you can assert that an object is valid at a certain point in time by using `assume_safe()`. This is an unsafe function that returns a `gdnative::TRef<T, Access>` object, which allows you to call methods on the node. You are responsible for this assumption to be correct; violating it can lead to undefined behavior.
27+
28+
The following example demonstrates `TRef`. A node is stored inside a Rust struct, and its position is modified through `set_position()`. This approach could be used in an ECS (Entity-Component-System) architecture, where `GodotNode` is a component, updated by a system.
29+
```rust
30+
struct GodotNode {
31+
node_ref: gd::Ref<gd::api::Node2D>,
32+
}
33+
34+
fn update_position(node: &GodotNode) {
35+
let pos = gd::core_types::Vector2::new(20, 30);
36+
37+
// fetch temporary reference to the node
38+
let node: gd::TRef<gd::api::Node2D> = unsafe { node.node_ref.assume_safe() };
39+
40+
// call into the Godot engine
41+
// this implicitly invokes deref(), turning TRef<Node2D> into &Node2D
42+
node.set_position(pos);
43+
}
44+
```
45+
Note that the parameter type is `&GodotNode`, not `&mut GodotNode`. Then why is it possible to mutate the Godot object?
46+
47+
All Godot classes in Rust (`Object` and its subtypes) have only methods that operate on `&self`, not `&mut self`. The reason for this choice is that `&mut` is -- strictly speaking -- not a mutable reference, but rather an [_exclusive_ reference](https://docs.rs/dtolnay/latest/dtolnay/macro._02__reference_types.html). The one and only thing it guarantees is that while it exists, no other reference to the same object can exist (no aliasing). Since all the Godot classes can be shared with the Godot engine, which is written in C++ and allows free aliasing, using `&mut` references would potentially violate the exclusivity, leading to UB. This is why `&T` is used, and just like e.g. `&RefCell` it _does_ allow mutation.
48+
49+
This being said, it can still make sense to bring back some type safety on a higher level in your own code. For example, you could make the `update_position()` take a `&mut GodotNode` parameter, to make sure that access to this `GodotNode` object is exclusive.
50+
51+
52+
_See `TRef` in
53+
[godot-rust docs](https://docs.rs/gdnative/latest/gdnative/struct.TRef.html)_
54+
55+
56+
## `Instance`: reference with attached Rust class
57+
58+
When working with classes that are provided by the engine or defined in GDScript, the `Ref` smart pointer is the ideal type for interfacing between Rust and Godot. However, when defining a custom class in Rust, that is registered with the Godot engine, there are two parts that need to be stored together:
59+
60+
1. **GDNative script:** the Rust struct object that implements the entire custom logic. The Rust struct is written by you.
61+
1. **Base object:** the base class from which the script inherits, with its own state. This is always a Godot built-in class such as `Object`, `Reference` or `Node`.
62+
63+
The `Instance` class simply wraps the two parts into a single type.
64+
65+
When passing around your own Rust types, you will thus be working with `Instance`. The traits `ToVariant`, `FromVariant` and `OwnedToVariant` are automatically implemented for `Instance` types, allowing you to pass them from and to the Godot engine.
66+
67+
68+
### Construction
69+
70+
Let's use a straightforward example: a player with name and score. Exported methods and properties are omitted for simplicity; the full interfacing will be explained later in [Calling into GDScript from Rust](../rust-binding/calling-gdscript.md).
71+
```rust
72+
#[derive(gd::NativeClass)]
73+
// no #[inherit], thus inherits Reference by default
74+
pub struct Player {
75+
name: String,
76+
score: u32,
77+
}
78+
79+
#[gd::methods]
80+
impl Player {
81+
fn new(_owner: &gd::Reference) -> Self {
82+
Self {
83+
name: "New player".to_string(),
84+
score: 0
85+
}
86+
}
87+
}
88+
```
89+
90+
To create a default instance, use `Instance::new_instance()`.
91+
You can later use `map()` and `map_mut()` to access the `Instance` immutably and mutably.
92+
93+
```rust
94+
let instance: Instance<Reference, Unique> = Instance::new();
95+
// or:
96+
let instance = Player::new_instance();
97+
98+
// note: map_mut() takes &self, so above is not 'let mut'
99+
instance.map_mut(|p: &mut Player, _base: TRef<Reference, Unique>| {
100+
p.name = "Joe".to_string();
101+
p.score = 120;
102+
});
103+
```
104+
105+
If you don't need a Godot-enabled default constructor, use the `#[no_constructor]` attribute and define your own Rust `new()` constructor.
106+
```rust
107+
#[derive(gd::NativeClass)]
108+
#[no_constructor]
109+
pub struct Player {
110+
name: String,
111+
score: u32,
112+
}
113+
114+
#[gd::methods]
115+
impl Player {
116+
pub fn new(name: &str, score: u32) -> Self {
117+
Self { name: name.to_string(), score }
118+
}
119+
}
120+
```
121+
122+
In this case, you can construct an `Instance` from an existing Rust object using `Instance::emplace()`:
123+
```rust
124+
let player = Player::new("Joe", 120);
125+
126+
let instance = Instance::emplace(player);
127+
// or:
128+
let instance = player.emplace();
129+
```
130+
131+
132+
133+
134+
_See `Instance` in
135+
[godot-rust docs](https://docs.rs/gdnative/latest/gdnative/nativescript/struct.Instance.html)_
136+

src/rust-binding.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Binding to Rust code
2+
3+
This chapter provides an exhaustive list of mechanisms to pass data through the Rust GDNative binding, in both directions:
4+
* **GDScript -> Rust**, e.g. to react to an input event with custom Rust logic
5+
* **Rust -> GDScript**, e.g. to apply a game logic change to a graphics node in Godot
6+
7+
The goal is to serve as both an in-depth learning resource for newcomers and a reference to look up specific mechanisms at a later stage. Before delving into this chapter, make sure to read [An Overview of GDNative](gdnative-overview.md), which explains several fundamental concepts used here.
8+
9+
The subchapters are intended to be read in order, but you can navigate to them directly:
10+
11+
1. [Class registration](rust-binding/classes.md)
12+
1. [Exported methods](rust-binding/methods.md)
13+
1. [Exported properties](rust-binding/properties.md)
14+
1. [Calling into GDScript from Rust](./rust-binding/calling-gdscript.md)

0 commit comments

Comments
 (0)