Skip to main content

gdext_async/
lib.rs

1
2//! Provides utilities for bidirectional support between Rust and
3//! GDScript for `async` and `await`.
4//!
5//! This crate provides two macros, both intended for interoperability
6//! with GDScript: [`godot_async`] and [`godot_await`]. If you are
7//! writing a pure-Rust project that does not need to inter-operate
8//! with GDScript, then you probably do NOT need this crate.
9//!
10//! # Features
11//!
12//! * `macros` - Exposes the attribute macro `#[gdext_async]`. Enabled
13//! by default. This requires the `syn` crate as a dependency.
14
15use godot::prelude::*;
16
17use std::cell::OnceCell;
18use std::rc::Rc;
19
20#[derive(GodotClass, Debug)]
21#[class(no_init)]
22#[doc(hidden)] // Only public for use in macro.
23pub struct AsyncAwaitableTask {
24  base: Base<RefCounted>,
25  known_result: Rc<OnceCell<Variant>>,
26}
27
28impl AsyncAwaitableTask {
29  pub fn create<F>(future: F) -> Gd<Self>
30  where F: IntoFuture + 'static,
31        <F as IntoFuture>::Output: ToGodot {
32    let known_result = Rc::new(OnceCell::new());
33    let inst = Gd::from_init_fn(|base| {
34      AsyncAwaitableTask { base, known_result: Rc::clone(&known_result) }
35    });
36    {
37      let inst = inst.clone(); // RefCounted: Cheap clone
38      let _task_handle = godot::task::spawn(async move {
39        let res = future.into_future().await;
40        known_result.set(res.to_variant())
41          .expect("known_result was set twice!");
42        inst.signals().completed().emit(&res.to_variant());
43      });
44    }
45    inst
46  }
47
48  /// If the task is finished already, return its result. Else, return
49  /// a signal that will fire when it finishes.
50  pub fn get_maybe_awaitable_value(&mut self) -> Variant {
51    if let Some(res) = self.known_result.get() {
52      res.clone()
53    } else {
54      self.signals().completed().to_untyped().to_variant()
55    }
56  }
57}
58
59#[godot_api]
60impl AsyncAwaitableTask {
61  #[signal]
62  pub fn completed(result: Variant);
63}
64
65/// If the object is a `GDScriptFunctionState`, await it. If not,
66/// return it unmodified. Note that, unlike GDScript, this function
67/// does NOT attempt to detect and await signals. Only actual
68/// `GDScriptFunctionState` objects will be awaited.
69///
70/// This is the function used under-the-hood by the [`godot_await!`]
71/// macro, but it can also be used independently, for instance, if you
72/// need to store the future (as a [`Future`]) and not evaluate it
73/// yet.
74pub async fn resolve_gdscript_coroutine(variant: Variant) -> Variant {
75  // This function is adapted from
76  // [https://github.com/godot-rust/gdext/pull/1645/changes] and uses
77  // undocumented Godot trickery to adapt GDScript's `await` keyword
78  // into Rust.
79  if let Some(state) = to_gdscript_function_state(&variant) {
80    let signal = Signal::from_object_signal(&state, "completed");
81    let (result,) = signal.to_future::<(Variant,)>().await;
82    drop(state);
83    result
84  } else {
85    variant
86  }
87}
88
89fn to_gdscript_function_state(variant: &Variant) -> Option<Gd<Object>> {
90  let state = variant.try_to::<Gd<Object>>().ok()?;
91  if state.get_class() == "GDScriptFunctionState" {
92    Some(state)
93  } else {
94    None
95  }
96}
97
98/// Convert a block of Rust async code into a Godot task whose
99/// completion can safely be awaited from GDScript.
100///
101/// This macro returns a `Variant` which can be awaited on the
102/// GDScript side. If this macro is the entire function body, your
103/// function must return `Variant`.
104///
105/// It is strongly recommended that functions which use this macro
106/// take `this: Gd<Self>` via `#[func(gd_self)]` (as opposed to
107/// `&self` or `&mut self`) in order to avoid holding a `Gd` binding
108/// across an `await` point.
109///
110/// # Example Usage
111///
112/// ```rust
113/// # use godot::prelude::*;
114/// # use gdext_async::godot_async;
115/// // Rust
116///
117/// #[derive(GodotClass)]
118/// #[class(init, base=Node)]
119/// struct MyRustNode {}
120///
121/// #[godot_api]
122/// impl MyRustNode {
123///   #[func(gd_self)]
124///   pub fn wait_three_secs(this: Gd<Self>) -> Variant {
125///     godot_async! {
126///       godot_print!("Before Timer! (2)");
127///       let timer = this.get_tree().create_timer(3.0);
128///       timer.signals().timeout().to_future().await;
129///       godot_print!("After Timer! (3)");
130///     }
131///   }
132/// }
133/// ```
134///
135/// ```gdscript
136/// # GDScript
137/// extends Node
138///
139/// func _ready():
140///     var node = MyRustNode.new()
141///     add_child(node)
142///     print("Before Timer! (1)")
143///     await node.wait_three_secs()
144///     print("After Timer! (4)")
145/// ```
146#[macro_export]
147macro_rules! godot_async {
148  ($($block: tt)+) => {
149    $crate::AsyncAwaitableTask::create(async move {
150      $($block)+
151    }).bind_mut().get_maybe_awaitable_value()
152  }
153}
154
155/// Await (in Rust) a Godot value, using semantics similar to
156/// GDScript's `await`.
157///
158/// GDScript function state objects will be awaited, while any other
159/// value (**including signals**) will be returned verbatim.
160///
161/// # Example Usage
162///
163/// ```rust
164/// # use godot::prelude::*;
165/// # use gdext_async::godot_await;
166/// // Rust
167///
168/// // Note: Ordinary Rust async function, NOT exposed to GDScript.
169/// pub async fn fade_out_enemy(mut enemy_node: Gd<Node2D>) {
170///   godot_print!("Freeing enemy node ...");
171///   godot_await! { enemy_node.call("fade_out", &[]) };
172///   godot_print!("Enemy node freed.");
173/// }
174///
175/// ```
176///
177/// ```gdscript
178/// # GDScript
179/// extends Node2D
180///
181/// func fade_out():
182///     var tween = create_tween()
183///     tween.tween_property(self, "modulate:a", 0.0, 1.0)
184///     await tween.finished
185///     queue_free()
186/// ```
187#[macro_export]
188macro_rules! godot_await {
189  ($($block: tt)+) => {
190    $crate::resolve_gdscript_coroutine({ $($block)+ }).await
191  }
192}
193
194/// Attribute macro that can be applied to `impl` blocks (similar to
195/// [`godot_api`]) to enable optional functionality.
196///
197/// This macro can only be applied to an inherent impl block, not to a
198/// trait impl. This macro generates `#[godot_api(secondary)]` blocks,
199/// so the relevant struct must have a primary (i.e. non-`secondary`)
200/// `#[godot_api]` block.
201///
202/// ## Async Functions (`#[async_func]` / `#[async_func(gd_self)]`)
203///
204/// A function in a `gdext_async`-enabled impl block which is tagged
205/// with `#[async_func]` will be exported to Godot, similar to
206/// `#[func]` on `#[godot_api]`. The exported Godot-side function will
207/// be `await`-able in GDScript.
208///
209/// The annotated function must be `async` and its arguments and
210/// return type must be Godot-compatible. That is, arguments must be
211/// [`AsArg`](godot::meta::AsArg) and the result type must be
212/// [`ToGodot`]. Note that this is a stronger guarantee than
213/// `#[func]`, as it is not currently possible to return, for example,
214/// `Result<T, E>` from a `#[async_func]` since the type does not
215/// implement `ToGodot`.
216///
217/// `#[async_func]` can be called as `#[async_func(gd_self)]`, which
218/// causes the generated GDScript-function to accept `Gd<Self>` rather
219/// than an explicit `&self` or `&mut self`. If you are accepting a
220/// `self` argument, you almost always want `gd_self`, as holding a
221/// `Gd` binding across an `await` point is generally a very bad idea.
222///
223/// The function will be available both from Rust and from GDScript
224/// and can be awaited using the respective `await` keyword in each
225/// language.
226///
227/// `#[rpc]` functions are NOT currently supported by this macro.
228///
229/// ### Example
230///
231/// ```
232/// # use godot::prelude::*;
233/// # use gdext_async_macros::gdext_async;
234///
235/// #[derive(GodotClass)]
236/// #[class(init, base=Node2D)]
237/// pub struct Player {}
238///
239/// #[gdext_async]
240/// #[godot_api]
241/// impl Player {
242///   #[async_func(gd_self)]
243///   pub async fn play_item_get_animation(this: Gd<Self>, item_id: i32) -> i32 {
244///     // Function body ...
245///     unimplemented!()
246///   }
247///
248///   // Also works with static functions
249///   #[async_func]
250///   pub async fn calculate_item_damage(item_id: i32) -> f32 {
251///     unimplemented!()
252///   }
253/// }
254/// ```
255#[doc(alias = "async_func")]
256#[cfg(feature = "macros")]
257pub use gdext_async_macros::gdext_async;