Skip to content

Commit da5c2a1

Browse files
committed
Created "recipes" section that can be used to share various helpful samples.
Included the rust_panic_handler recipe that was graciously provided by setzer22
1 parent 55d8efe commit da5c2a1

File tree

3 files changed

+93
-0
lines changed

3 files changed

+93
-0
lines changed

src/SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
- [(TODO) Testing](./testing.md)
1717
- [Structuring Code for Testing](./testing/structure.md)
1818
- [Testing with the Engine](./testing/engine.md)
19+
- [Recipes](./recipes.md)
20+
- [Rust Panic Hook](./recipes/rust_panic_handler.md)
1921
- [Exporting](./exporting.md)
2022
- [Android](./exporting/android.md)
2123
- [(TODO) iOS](./exporting/ios.md)

src/recipes.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Godot Rust Recipes
2+
3+
This is a small collection of recipes and patterns that various contributors have found to be useful for game development.
4+
5+
These recipes cover general use cases and are not intended to be the *only* way to implement these patterns. Each recipe should be evaluated and customized to your specific use case.

src/recipes/rust_panic_handler.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Rust Panic Handler
2+
3+
When using GDNative, Rust panics are ignored by Godot by default. This recipe can be used to catch those panics in the Godot editor at runtime.
4+
5+
This recipe was written and tested with godot-rust 0.9.3 with Rust version 1.52.1
6+
7+
## GDScript Hook
8+
9+
First create a GDScript with the following code named "rust_panic_hook.gd"
10+
11+
```
12+
extends Node
13+
14+
func rust_panic_hook(error_msg: String) -> void:
15+
assert(false, error_msg)
16+
17+
```
18+
19+
In the Project Settings -> Autoload menu, create an autoload singleton referencing the script (in this case rust_panic_hook.gd).
20+
21+
Pick a unique name that identifies the autoload singleton. You will need to use this name to find the autoload singleton in Rust.
22+
23+
For this example, we are using the autoload name "rust_panic_hook".
24+
25+
At this point we have our GDScript based panic hook we can use in Rust.
26+
27+
## GDNative Panic Hook Initialization
28+
29+
In the GDNative library code's entry point (lib.rs by default).
30+
31+
```rust
32+
pub fn init_panic_hook() {
33+
// To enable backtrace, you will need the `backtrace` crate to be included in your cargo.toml, or
34+
// a version of rust where backtrace is included in the standard library (e.g. Rust nightly as of the date of publishing)
35+
// use backtrace::Backtrace;
36+
// use std::backtrace::Backtrace;
37+
let old_hook = std::panic::take_hook();
38+
std::panic::set_hook(Box::new(move |panic_info| {
39+
let loc_string;
40+
if let Some(location) = panic_info.location() {
41+
loc_string = format!("file '{}' at line {}", location.file(), location.line());
42+
} else {
43+
loc_string = "unknown location".to_owned()
44+
}
45+
46+
let error_message;
47+
if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
48+
error_message = format!("[RUST] {}: panic occurred: {:?}", loc_string, s);
49+
} else if let Some(s) = panic_info.payload().downcast_ref::<String>() {
50+
error_message = format!("[RUST] {}: panic occurred: {:?}", loc_string, s);
51+
} else {
52+
error_message = format!("[RUST] {}: unknown panic occurred", loc_string);
53+
}
54+
godot_error!("{}", error_message);
55+
// Uncomment the following line if backtrace crate is included as a dependency
56+
// godot_error!("Backtrace:\n{:?}", Backtrace::new());
57+
(*(old_hook.as_ref()))(panic_info);
58+
59+
unsafe {
60+
if let Some(gd_panic_hook) = gdnative::api::utils::autoload::<gdnative::api::Node>("rust_panic_hook") {
61+
gd_panic_hook.call("rust_panic_hook", &[GodotString::from_str(error_message).to_variant()]);
62+
}
63+
}
64+
}));
65+
}
66+
```
67+
68+
The details the process in the above code is as follows:
69+
1. Get the default panic hook from Rust
70+
2. Create a new panic hook closure to output to the Godot console
71+
3. Get the location string and error message from the `panic_info` closure parameter and print the message to the console
72+
4. Optionally, retreive and print the backtrace
73+
5. Execute the old panic hook so that the normal panic behavior still occurs
74+
6. Call the function defined on your GDScript panic hook script
75+
76+
The final step is to call `init_panic_hook()` at the end of the `init` function that you pass in the `godot_init(init)` macro such as in the following code.
77+
78+
```rust
79+
// GDnative entrypoint
80+
fn init(handle: InitHandle) {
81+
// -- class registration above
82+
init_panic_hook();
83+
}
84+
```
85+
86+
Now you can run your game and once it is fully initialized, any panics will pause the game execution and print the panic message in Godot's editor in the Debugger's Error tab.

0 commit comments

Comments
 (0)