Skip to content

Commit 54cb201

Browse files
bors[bot]Bromeon
andauthored
Merge #167
167: Call error messages, `gdext` rename r=Bromeon a=Bromeon Improves the diagnostics when calling into Godot APIs. Also updates documentation to reflect the rename to `gdext` (from whatever the name was before 😀). Makes `Debug` impl for `GodotString`, `StringName` and `NodePath` more consistent: * `"string"` * `&"name"` * `^"path"` Co-authored-by: Jan Haller <[email protected]>
2 parents 9353407 + 207c4e7 commit 54cb201

File tree

19 files changed

+202
-120
lines changed

19 files changed

+202
-120
lines changed

Contributing.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# Contributing to `gdextension`
1+
# Contributing to `gdext`
22

33
At this stage, we appreciate if users experiment with the library, use it in small projects and report issues and bugs they encounter.
44

5-
If you plan to make bigger contributions, make sure to discuss them in a [GitHub issue] first. Since the library is evolving quickly, this avoids that multiple people work on the same thing or implement features in a way that doesn't work with other parts. Also don't hesitate to talk to the developers in the `#dev-gdextension` channel on [Discord]!
5+
If you plan to make bigger contributions, make sure to discuss them in a [GitHub issue] first. Since the library is evolving quickly, this avoids that multiple people work on the same thing or implement features in a way that doesn't work with other parts. Also don't hesitate to talk to the developers in the `#contrib-gdext` channel on [Discord]!
66

77
## Check script
88

@@ -22,7 +22,7 @@ $ ln -sf check.sh .git/hooks/pre-commit
2222

2323
## Unit tests
2424

25-
Because most of `gdextension` interacts with the Godot engine, which is not available from the test executable, unit tests (using `cargo test` and the `#[test]` attribute) are pretty limited in scope.
25+
Because most of `gdext` interacts with the Godot engine, which is not available from the test executable, unit tests (using `cargo test` and the `#[test]` attribute) are pretty limited in scope.
2626

2727
Because additional flags might be needed, the preferred way to run unit tests is through the `check.sh` script:
2828

@@ -32,7 +32,7 @@ $ ./check.sh test
3232

3333
## Integration tests
3434

35-
The `itest/` directory contains a suite of integration tests that actually exercise `gdextension` from within Godot.
35+
The `itest/` directory contains a suite of integration tests that actually exercise `gdext` from within Godot.
3636

3737
The `itest/rust` directory is a Rust `cdylib` library project that can be loaded as a GDExtension in Godot, with an entry point for running integration tests. The `itest/godot` directory contains the Godot project that loads this library and invokes the test suite.
3838

@@ -71,5 +71,5 @@ To run the testing suite with `double-precision` enabled you may add `--double`
7171
$ check.sh --double
7272
```
7373

74-
[GitHub issue]: https://github.com/godot-rust/gdextension/issues
74+
[GitHub issue]: https://github.com/godot-rust/gdext/issues
7575
[Discord]: https://discord.gg/aKUCJ8rJsc

ReadMe.md

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
![logo.png](assets/gdextension-ferris.png)
1+
![logo.png](assets/gdext-ferris.png)
22

33
# Rust bindings for GDExtension
44

5-
This is an early-stage library to bind the **Rust** language to **Godot 4**.
5+
_[Discord] | [Mastodon] | [Twitter]_
6+
7+
**gdext** is an early-stage library to bind the **Rust** language to **Godot 4**.
68

79
[Godot] is an open-source game engine, whose upcoming version 4.0 brings several improvements.
810
Its _GDExtension_ API allows integrating third-party languages and libraries.
@@ -17,10 +19,10 @@ Its _GDExtension_ API allows integrating third-party languages and libraries.
1719
> * No stability guarantees. APIs will break frequently (for releases, we try to take SemVer seriously though).
1820
> Resolving the above two points has currently more weight than a stable API.
1921
20-
We do not recommend building a larger project in GDExtension-Rust yet.
22+
We do not recommend building a larger project in gdext yet.
2123
However, the library can serve as a playground for experimenting.
2224

23-
To get an overview of currently supported features, consult [#24](https://github.com/godot-rust/gdextension/issues/24).
25+
To get an overview of currently supported features, consult [#24](https://github.com/godot-rust/gdext/issues/24).
2426
At this point, there is **no** support for Android, iOS or WASM. Contributions are very welcome!
2527

2628

@@ -40,7 +42,7 @@ In your Cargo.toml, add:
4042

4143
```toml
4244
[dependencies]
43-
godot = { git = "https://github.com/godot-rust/gdextension", branch = "master" }
45+
godot = { git = "https://github.com/godot-rust/gdext", branch = "master" }
4446

4547
[lib]
4648
crate-type = ["cdylib"]
@@ -53,21 +55,25 @@ To register the GDExtension library with Godot, you need to create two files rel
5355

5456
The `[configuration]` section should be copied as-is.
5557
The `[libraries]` section should be updated to match the paths of your dynamic Rust libraries.
56-
```ini
57-
[configuration]
58-
entry_symbol = "gdextension_rust_init"
59-
60-
[libraries]
61-
linux.64 = "res://../rust/target/debug/lib{my_ext}.so"
62-
windows.64 = "res://../rust/target/debug/{my_ext}.dll"
63-
macos.64 = "res://../rust/target/debug/{my_ext}.dylib"
64-
```
58+
```ini
59+
[configuration]
60+
entry_symbol = "gdext_rust_init"
61+
62+
[libraries]
63+
linux.debug.x86_64 = "res://../rust/target/debug/lib{my_ext}.so"
64+
linux.release.x86_64 = "res://../rust/target/release/lib{my_ext}.so"
65+
windows.debug.x86_64 = "res://../rust/target/debug/{my_ext}.dll"
66+
windows.release.x86_64 = "res://../rust/target/release/{my_ext}.dll"
67+
macos.debug = "res://../rust/target/debug/{my_ext}.dylib"
68+
macos.release = "res://../rust/target/release/{my_ext}.dylib"
69+
```
70+
(Note that for exporting your project, you'll need to use paths inside `res://`).
6571

6672
2. A second file `res://.godot/extension_list.cfg` should be generated once you open the Godot editor for the first time.
6773
If not, you can also manually create it, simply containing the Godot path to your `.gdextension` file:
68-
```
69-
res://MyExt.gdextension
70-
```
74+
```
75+
res://MyExt.gdextension
76+
```
7177

7278
### Examples
7379

@@ -76,7 +82,7 @@ This integrates a small game with Godot and has all the necessary steps set up.
7682

7783
API documentation can be generated locally using `cargo doc -p godot --no-deps --open`.
7884

79-
If you need help, join our [Discord] server and ask in the `#help-gdextension` channel!
85+
If you need help, join our [Discord] server and ask in the `#help-gdext` channel!
8086

8187

8288
## License
@@ -96,3 +102,5 @@ Contributions are very welcome! If you want to help out, see [`Contributing.md`]
96102
[`gdnative`]: https://github.com/godot-rust/gdnative
97103
[mpl]: https://www.mozilla.org/en-US/MPL/
98104
[Discord]: https://discord.gg/aKUCJ8rJsc
105+
[Mastodon]: https://mastodon.gamedev.place/@GodotRust
106+
[Twitter]: https://twitter.com/GodotRust

assets/asset-licenses.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Asset licenses
22

3-
The godot-rust logos for both GDNative and GDExtension are derived from the following work,
3+
The godot-rust logos for both _gdnative_ and _gdext_ are derived from the following work,
44
with changes applied from members of the godot-rust community.
55

66
| Asset | Website | Author | License |
File renamed without changes.

check.sh

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
# Small utility to run tests locally
77
# Similar to minimal-ci
88

9-
# Note: at the moment, there is a lot of useless recompilation.
10-
# This should be better once unit tests and #[cfg] are sorted out.
9+
# Note: at the moment, there is some useless recompilation, which could be improved.
1110

1211
# --help menu
1312
for arg in $@; do
@@ -123,13 +122,13 @@ END='\033[0m'
123122
for cmd in "${cmds[@]}"; do
124123
echo "> $cmd"
125124
$cmd || {
126-
printf "$RED\n=========================="
127-
printf "\ngodot-rust checker FAILED."
128-
printf "\n==========================\n$END"
125+
printf "$RED\n====================="
126+
printf "\ngdext: checks FAILED."
127+
printf "\n=====================\n$END"
129128
exit 1
130129
}
131130
done
132131

133-
printf "$GREEN\n=============================="
134-
printf "\ngodot-rust checker SUCCESSFUL."
135-
printf "\n==============================\n$END"
132+
printf "$GREEN\n========================="
133+
printf "\ngdext: checks SUCCESSFUL."
134+
printf "\n=========================\n$END"

examples/dodge-the-creeps/godot/DodgeTheCreeps.gdextension

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[configuration]
2-
entry_symbol = "gdextension_rust_init"
2+
entry_symbol = "gdext_rust_init"
33

44
[libraries]
55
linux.64 = "res://../../../target/debug/libdodge_the_creeps.so"

godot-codegen/src/class_generator.rs

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//! Generates a file for each Godot engine + builtin class
88
99
use proc_macro2::{Ident, TokenStream};
10-
use quote::{format_ident, quote};
10+
use quote::{format_ident, quote, ToTokens};
1111
use std::path::{Path, PathBuf};
1212

1313
use crate::api_parser::*;
@@ -712,12 +712,43 @@ fn make_function_definition(
712712

713713
let is_varcall = variant_ffi.is_some();
714714
let fn_name = safe_ident(function_name);
715-
let (params, arg_exprs) = make_params(method_args, is_varcall, ctx);
715+
let [params, variant_types, arg_exprs, arg_names] = make_params(method_args, is_varcall, ctx);
716+
717+
let (prepare_arg_types, error_fn_context);
718+
if variant_ffi.is_some() {
719+
// varcall (using varargs)
720+
prepare_arg_types = quote! {
721+
let mut __arg_types = Vec::with_capacity(__explicit_args.len() + varargs.len());
722+
// __arg_types.extend(__explicit_args.iter().map(Variant::get_type));
723+
__arg_types.extend(varargs.iter().map(Variant::get_type));
724+
let __vararg_str = varargs.iter().map(|v| format!("{v}")).collect::<Vec<_>>().join(", ");
725+
};
726+
727+
let joined = arg_names
728+
.iter()
729+
.map(|n| format!("{{{n}:?}}"))
730+
.collect::<Vec<_>>()
731+
.join(", ");
732+
733+
let fmt = format!("{function_name}({joined}; {{__vararg_str}})");
734+
error_fn_context = quote! { &format!(#fmt) };
735+
} else {
736+
// ptrcall
737+
prepare_arg_types = quote! {
738+
let __arg_types = [
739+
#( #variant_types ),*
740+
];
741+
};
742+
error_fn_context = function_name.to_token_stream();
743+
};
744+
716745
let (return_decl, call_code) = make_return(
717746
return_value,
718747
variant_ffi.as_ref(),
719748
varcall_invocation,
720749
ptrcall_invocation,
750+
prepare_arg_types,
751+
error_fn_context,
721752
ctx,
722753
);
723754

@@ -733,7 +764,7 @@ fn make_function_definition(
733764
#( #arg_exprs ),*
734765
];
735766

736-
let mut __args = Vec::new();
767+
let mut __args = Vec::with_capacity(__explicit_args.len() + varargs.len());
737768
__args.extend(__explicit_args.iter().map(Variant::#sys_method));
738769
__args.extend(varargs.iter().map(Variant::#sys_method));
739770

@@ -789,39 +820,41 @@ fn make_params(
789820
method_args: &Option<Vec<MethodArg>>,
790821
is_varcall: bool,
791822
ctx: &mut Context,
792-
) -> (Vec<TokenStream>, Vec<TokenStream>) {
823+
) -> [Vec<TokenStream>; 4] {
793824
let empty = vec![];
794825
let method_args = method_args.as_ref().unwrap_or(&empty);
795826

796827
let mut params = vec![];
828+
let mut variant_types = vec![];
797829
let mut arg_exprs = vec![];
830+
let mut arg_names = vec![];
798831
for arg in method_args.iter() {
799832
let param_name = safe_ident(&arg.name);
800833
let param_ty = to_rust_type(&arg.type_, ctx);
801834

802-
params.push(quote! { #param_name: #param_ty });
803-
if is_varcall {
804-
arg_exprs.push(quote! {
805-
<#param_ty as ToVariant>::to_variant(&#param_name)
806-
});
807-
} else if let RustTy::EngineClass { tokens: path, .. } = param_ty {
808-
arg_exprs.push(quote! {
809-
<#path as AsArg>::as_arg_ptr(&#param_name)
810-
});
835+
let arg_expr = if is_varcall {
836+
quote! { <#param_ty as ToVariant>::to_variant(&#param_name) }
837+
} else if let RustTy::EngineClass { tokens: path, .. } = &param_ty {
838+
quote! { <#path as AsArg>::as_arg_ptr(&#param_name) }
811839
} else {
812-
arg_exprs.push(quote! {
813-
<#param_ty as sys::GodotFfi>::sys_const(&#param_name)
814-
});
815-
}
840+
quote! { <#param_ty as sys::GodotFfi>::sys_const(&#param_name) }
841+
};
842+
843+
params.push(quote! { #param_name: #param_ty });
844+
variant_types.push(quote! { <#param_ty as VariantMetadata>::variant_type() });
845+
arg_exprs.push(arg_expr);
846+
arg_names.push(quote! { #param_name });
816847
}
817-
(params, arg_exprs)
848+
[params, variant_types, arg_exprs, arg_names]
818849
}
819850

820851
fn make_return(
821852
return_value: Option<&MethodReturn>,
822853
variant_ffi: Option<&VariantFfi>,
823854
varcall_invocation: &TokenStream,
824855
ptrcall_invocation: &TokenStream,
856+
prepare_arg_types: TokenStream,
857+
error_fn_context: TokenStream, // only for panic message
825858
ctx: &mut Context,
826859
) -> (TokenStream, TokenStream) {
827860
let return_decl: TokenStream;
@@ -851,7 +884,10 @@ fn make_return(
851884
let variant = Variant::#from_sys_init_method(|return_ptr| {
852885
let mut __err = sys::default_call_error();
853886
#varcall_invocation
854-
sys::panic_on_call_error(&__err);
887+
if __err.error != sys::GDEXTENSION_CALL_OK {
888+
#prepare_arg_types
889+
sys::panic_call_error(&__err, #error_fn_context, &__arg_types);
890+
}
855891
});
856892
#return_expr
857893
}
@@ -863,7 +899,10 @@ fn make_return(
863899
let mut __err = sys::default_call_error();
864900
let return_ptr = std::ptr::null_mut();
865901
#varcall_invocation
866-
sys::panic_on_call_error(&__err);
902+
if __err.error != sys::GDEXTENSION_CALL_OK {
903+
#prepare_arg_types
904+
sys::panic_call_error(&__err, #error_fn_context, &__arg_types);
905+
}
867906
}
868907
}
869908
(None, Some(RustTy::EngineClass { tokens, .. })) => {

godot-core/src/builtin/node_path.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
66

7+
use std::fmt;
8+
79
use crate::builtin::GodotString;
810
use godot_ffi as sys;
911
use godot_ffi::{ffi_methods, GDExtensionTypePtr, GodotFfi};
10-
use std::fmt::{Display, Formatter, Result as FmtResult};
1112

1213
pub struct NodePath {
1314
opaque: sys::types::OpaqueNodePath,
@@ -59,10 +60,18 @@ impl From<&str> for NodePath {
5960
}
6061
}
6162

62-
impl Display for NodePath {
63-
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
63+
impl fmt::Display for NodePath {
64+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65+
let string = GodotString::from(self);
66+
<GodotString as fmt::Display>::fmt(&string, f)
67+
}
68+
}
69+
70+
/// Uses literal syntax from GDScript: `^"node_path"`
71+
impl fmt::Debug for NodePath {
72+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6473
let string = GodotString::from(self);
65-
<GodotString as Display>::fmt(&string, f)
74+
write!(f, "^\"{string}\"")
6675
}
6776
}
6877

godot-core/src/builtin/string.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,11 @@ impl fmt::Display for GodotString {
110110
}
111111
}
112112

113+
/// Uses literal syntax from GDScript: `"string"`
113114
impl fmt::Debug for GodotString {
114115
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115116
let s = String::from(self);
116-
write!(f, "GodotString(\"{s}\")")
117+
write!(f, "\"{s}\"")
117118
}
118119
}
119120

godot-core/src/builtin/string_name.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::builtin::GodotString;
88
use godot_ffi as sys;
99
use sys::{ffi_methods, GodotFfi};
1010

11-
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
11+
use std::fmt;
1212
use std::hash::{Hash, Hasher};
1313

1414
#[repr(C)]
@@ -68,21 +68,18 @@ impl Default for StringName {
6868
}
6969
}
7070

71-
impl Display for StringName {
72-
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
73-
// TODO consider using GDScript built-in to_string()
74-
71+
impl fmt::Display for StringName {
72+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7573
let s = GodotString::from(self);
76-
<GodotString as Display>::fmt(&s, f)
74+
<GodotString as fmt::Display>::fmt(&s, f)
7775
}
7876
}
7977

80-
impl Debug for StringName {
81-
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
82-
// TODO consider using GDScript built-in to_string()
83-
84-
let s = GodotString::from(self);
85-
<GodotString as Debug>::fmt(&s, f)
78+
/// Uses literal syntax from GDScript: `&"string_name"`
79+
impl fmt::Debug for StringName {
80+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81+
let string = GodotString::from(self);
82+
write!(f, "&\"{string}\"")
8683
}
8784
}
8885

0 commit comments

Comments
 (0)