Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update tutorial for selecting Godot versions #46

Merged
merged 2 commits into from
Jun 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/godot-api/builtins.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Here is an exhaustive list of all built-in types, by category. We use the GDScri
### Rust mapping

Rust types in the gdext API represent the corresponding Godot types in the closest way possible. They are used in parameter and return type
position of API functions, for example.
position of API functions, for example. They are accessible through `godot::builtin`, and most symbols are also part of the prelude.

Most builtins have a 1:1 equivalent (e.g. `Vector2f`, `Color` etc.). The following list highlights some noteworthy mappings:

Expand Down Expand Up @@ -178,7 +178,7 @@ let ints = PackedInt32Array::from(&[1, 2, 3]);

// Get/set individual elements using Index and IndexMut operators.
ints[1] = 5;
assert_eq!(ints[1], &5);
assert_eq!(ints[1], 5);

// Access as Rust shared/mutable slices.
let bytes_slice: &[u8] = b.as_slice();
Expand Down
2 changes: 1 addition & 1 deletion src/godot-api/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ GDScript, and this page will go into such differences.

## Godot classes

Godot classes are located in the `godot::engine` module. Some often-used ones like `Node`, `RefCounted`, `Node3D` etc. are additionally
Godot classes are located in the `godot::classes` module. Some often-used ones like `Node`, `RefCounted`, `Node3D` etc. are additionally
re-exported in `godot::prelude`.

The majority of Godot's functionality is exposed via functions inside classes. Please don't hesitate to check out the [API docs][api-classes].
Expand Down
8 changes: 4 additions & 4 deletions src/intro/hello-world.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ In this example, we declare a class called `Player`, which inherits [`Sprite2D`]

```rust
use godot::prelude::*;
use godot::engine::Sprite2D;
use godot::classes::Sprite2D;

#[derive(GodotClass)]
#[class(base=Sprite2D)]
Expand Down Expand Up @@ -298,7 +298,7 @@ Now let's add some logic. We start with overriding the `init` method, also known
This corresponds to GDScript's `_init()` function.

```rust
use godot::engine::ISprite2D;
use godot::classes::ISprite2D;

#[godot_api]
impl ISprite2D for Player {
Expand Down Expand Up @@ -331,7 +331,7 @@ Now that initialization is sorted out, we can move on to actual logic. We would
the `process()` method. This corresponds to GDScript's `_process()`. If you need a fixed framerate, use `physics_process()` instead.

```rust
use godot::engine::ISprite2D;
use godot::classes::ISprite2D;

#[godot_api]
impl ISprite2D for Player {
Expand Down Expand Up @@ -379,7 +379,7 @@ Check out the [command-line tutorial][godot-command-line] for more information.
We now add a translation component to the sprite, following [the upstream tutorial][tutorial-full-script].

```rust
use godot::engine::ISprite2D;
use godot::classes::ISprite2D;

#[godot_api]
impl ISprite2D for Player {
Expand Down
2 changes: 1 addition & 1 deletion src/intro/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ This was necessary in the past due to `bindgen`, which [depends on LLVM][llvm-bi
However, we now provide pre-built artifacts, so that most users can simply add the Cargo dependency and start immediately.
This also significantly reduces initial compile times, as `bindgen` was quite heavyweight with its many transitive dependencies.

You will still need LLVM if you plan to use the `custom-godot` feature, for example if you have a forked version of Godot or custom
You will still need LLVM if you plan to use the `api-custom` feature, for example if you have a forked version of Godot or custom
modules. To just use a different API version of Godot, you do _not_ need LLVM though; see [Selecting a Godot version][godot-version].

LLVM binaries can be downloaded from [llvm.org][llvm]. Once installed, you can check whether LLVM's clang compiler is available:
Expand Down
4 changes: 2 additions & 2 deletions src/recipes/engine-singleton.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ users as well.
Read more about criticisms [here][singleton-crit].
```

An engine singleton is registered through [`godot::engine::Engine`][api-engine].
An engine singleton is registered through [`godot::classes::Engine`][api-engine].

Custom engine singletons in Godot:

Expand Down Expand Up @@ -128,7 +128,7 @@ func _ready() -> void:
You may also want to access your singleton from Rust as well.

```rust
godot::engine::Engine::singleton()
godot::classes::Engine::singleton()
.get_singleton(StringName::from("MyEditorSingleton"));
```

Expand Down
153 changes: 135 additions & 18 deletions src/toolchain/godot-version.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,146 @@

# Selecting a Godot version

By default, `gdext` uses the latest stable release of Godot. This is desired in most cases, but it means that you cannot run your extension in
an older Godot version. Furthermore, you cannot benefit from modified Godot versions (e.g. with custom modules).
Supporting multiple Godot versions is a key feature of gdext. Especially if you plan to share your extension with others (as a library or an
editor plugin), this page elaborates your choices and their trade-offs in detail. The previous chapter about [compatibility][compat] is
expected as a prerequisite.

If these are features you need, this page will walk you through the necessary steps.
Read [Compatibility and stability] first and make sure you understand the concept of API and runtime versions.

## Table of contents

## Older stable releases
<!-- toc -->

Building gdext against an older Godot API allows you to remain forward-compatible with all engine versions >= that version.
(For Godot 4.0.x, [== applies instead of >=][compat-guarantees].)

In a hypothetical example, building against API 4.1 allows you to run your extension in Godot 4.1.1, 4.1.2 or 4.2.
## Motivation

To choose a version (here `4.0`), add the following to your top-level (workspace) `Cargo.toml`:
To refresh, you have two Godot versions to consider:

- **API version**, against which gdext compiles.
- Affects Rust symbols (classes, methods, etc.) you have available at compile time.
- This sets a lower bound on the Godot binary you can run your extension in.

- **Runtime version**, the Godot engine version, in which you run the Rust extension.
- Affects the runtime behavior, e.g. newer versions may fix some bugs.
- It is advised to stay up-to-date with Godot releases, to benefit from new improvements.

GDExtension is designed to be backward-compatible, so an extension built with a certain API version can be run in all Godot binaries greater
than that version.[^compat-4-0] Therefore, the lower your API version, the more Godot versions you support.

```admonish abstract title="In other words:"
API version <= runtime version
```


### Why support multiple versions?

The choice you have in the context of gdext is the **API version**. If you just make a game on your own, the defaults are typically good enough.

Explicitly selecting an API version can be helpful in the following scenarios:

1. You run/test your application on different Godot **minor** versions.
2. You are collaborating in a team, or you want to give your Godot project to friends to experiment with.
3. You work on a library or plugin to share with the community, either open-source (distributed as code) or closed-source (distributed as
compiled dynamic library).

Especially in the last case, you may want your extension to be compatible with as many Godot versions as possible, to reach a broader audience.

```admonish tip title="Building an ecosystem"
At first glance, it may not seem obvious why a plugin would support anything but the latest Godot version. After all, users can just update,
right?

However, sometimes users cannot update their Godot version due to regressions, incompatibilities or project/company constraints.

Furthermore, imagine you want to use two GDExtension plugins: **X** (API level 4.3) and **Y** (4.2). Unfortunately, Y contains a bug that
causes some issues with Godot 4.3. This means you cannot use both together, and you are left with some suboptimal choices:
- Only use X on 4.3.
- Only use Y on 4.2.
- Help the author of Y to patch the bug. But they may just sail the Caribbean and not respond on their repo. Or worse, Y might even be a
closed-source plugin that you paid for.

Not only are you now left with a less-than-ideal situation, but you cannot build _your own tool_ Z which uses both X and Y, either.
Had X declared API 4.2, people could stick to that version until Y is fixed, and you too could release Z with API 4.2.

A longer compatibility range gives users more flexibility regarding _when_ they update _what_. It accounts for the fact that developers
iterate at varying pace, and enables projects to depend on each other. At scale, this enables a vibrant ecosystem of extensions around Godot.
```


### Cutting edge vs. compatibility

Lower API versions allow supporting a wider range of Godot versions. For example, if you set the API version to 4.2, you can run it in Godot
4.2, 4.2.2 or 4.3, but not Godot 4.1.

On the flip side, lower API versions reduce the API surface that you can statically[^dynamic-features] use in your Rust extension. If you
select 4.2, you will not see classes and functions introduced in 4.3.

This is the core trade-off, and you need to decide based on your use case. If you are unsure, you can always start with a conservatively low API
version, and bump it when you find yourself needing more recent features.


## Selecting the API version in gdext

Now that the _why_ part is clarified, let's get into _how_ you can choose the API version in gdext.


### Default version

By default, gdext uses the **current minor release** of Godot 4, with patch 0. This ensures that it can be run with all Godot patch versions
for that minor release.

Example: if the current release is Godot 4.3.5, then gdext will use API version 4.3.0.


### Lower minor version

To change the API level to a lower version, simply turn on the Cargo feature `api-4-x`, where `x` is the minor version you want to target.

Example in Cargo.toml:

```toml
[patch."https://github.com/godot-rust/godot4-prebuilt".godot4-prebuilt]
git = "https://github.com//godot-rust/godot4-prebuilt"
branch = "4.0"
[dependencies]
# API level 4.2
godot = { ..., features = ["api-4-2"] }
```

You can also explicitly set the current minor version (the same as the default). This has the advantage that you keep that compatibility,
even once gdext starts targeting a newer version by default.

```admonish note title="Mutual exclusivity"
Only one `api-*` feature can be active at any time.
```

(If you're interested in the `//` workaround, see <https://github.com/rust-lang/cargo/issues/5478>).

### Lower or higher patch version

gdext supports API version granularity on a patch level, if absolutely needed. This is rarely necessary and can cause confusion to users,
so only select a patch-level API if you have a very good reasons. Also note that GDExtension itself is only updated in minor releases.

Reasons to want this might be:

- Godot ships a bugfix in a patch version that is vital for your extension to function properly.
- A new API is introduced in a patch version, and you would like its class/function definitions. This happens quite rarely.

To require a minimum patch level, use a `api-4-x-y` feature:

```toml
[dependencies]
# API level 4.2.1
godot = { ..., features = ["api-4-2-1"] }
```


## Custom Godot versions

If you want to freely choose a Godot binary on your local machine from which the GDExtension API is generated, you can use the Cargo feature
`custom-godot`. If enabled, this will look for a Godot binary in two locations, in this order:
`api-custom`. If enabled, this will look for a Godot binary in two locations, in this order:

1. The environment variable `GODOT4_BIN`.
2. The binary `godot4` in your `PATH`.

Generated code inside the `godot::engine` and `godot::builtin` modules may now look different from stable releases.
Note that we do not give any support or compatibility guarantees for custom-built GDExtension APIs.
Generated code inside the `godot::builtin`, `godot::classes` and `godot::global` modules may now look different from stable releases.
Note that we [do not give any support or compatibility guarantees][no-custom-support] for custom-built GDExtension APIs.

Note that this requires the `bindgen`, as such you may need to install the LLVM toolchain.
Working with the `api-custom` feature requires the `bindgen` crate, as such you may need to install the LLVM toolchain.
Consult the [setup page][setup-llvm] for more information.


Expand Down Expand Up @@ -71,6 +174,20 @@ See [The Cargo Book](https://doc.rust-lang.org/cargo/reference/config.html) for
`config.toml`.


[Compatibility and stability]: compatibility.md
[api-gdext-build]: https://godot-rust.github.io/docs/gdext/master/godot/init/struct.GdextBuild.html
[compat-guarantees]: compatibility.md#current-guarantees
[compat]: compatibility.md
[no-custom-support]: compatibility.md#out-of-scope
[setup-llvm]: ../intro/setup.md#llvm

---

**Footnotes**

[^compat-4-0]: Godot 4.0 has been released before the GDExtension API committed to stability, so no single 4.0.x release is compatible with any
other release (not even patch versions among each other). We provide 4.0 API levels, but due to their limited utility, we will phase out
support very soon.

[^dynamic-features]: Even if your API level is 4.2, it is possible to access 4.3 features, but you need to do so dynamically. This can be
achieved using reflection APIs like `Object::call()`, but you lose the type safety and convenience of the statically generated API.
To obtain version information, check out the [`GdextBuild` API][api-gdext-build].
Loading