Skip to main content

godot_core/obj/
on_ready.rs

1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8use std::fmt::{self, Debug, Formatter};
9use std::mem;
10
11use crate::builtin::{GString, NodePath};
12use crate::classes::{Node, Resource};
13use crate::meta::shape::GodotShape;
14use crate::meta::{AsArg, FromGodot, GodotConvert, arg_into_owned};
15use crate::obj::{Gd, Inherits};
16use crate::registry::property::Var;
17
18/// Ergonomic late-initialization container with `ready()` support.
19///
20/// While deferred initialization is generally seen as bad practice, it is often inevitable in game development.
21/// Godot in particular encourages initialization inside `ready()`, e.g. to access the scene tree after a node is inserted into it.
22/// The alternative to using this pattern is [`Option<T>`][option], which needs to be explicitly unwrapped with `unwrap()` or `expect()` each time.
23///
24/// If you have a value that you expect to be initialized in the Godot editor, use [`OnEditor<T>`][crate::obj::OnEditor] instead.
25/// As a general "maybe initialized" type, `Option<Gd<T>>` is always available, even if more verbose.
26///
27/// # Late-init semantics
28///
29/// `OnReady<T>` should always be used as a struct field. There are two modes to use it:
30///
31/// 1. **Automatic mode, using [`new()`](OnReady::new), [`from_base_fn()`](OnReady::from_base_fn),
32///    [`from_node()`][Self::from_node] or [`from_loaded()`][Self::from_loaded].**<br>
33///    Before `ready()` is called, all `OnReady` fields constructed with the above methods are automatically initialized,
34///    in the order of declaration. This means that you can safely access them in `ready()`.<br>
35/// 2. **Manual mode, using [`manual()`](Self::manual).**<br>
36///    These fields are left uninitialized until you call [`init()`][Self::init] on them. This is useful if you need more complex
37///    initialization scenarios than a closure allows. If you forget initialization, a panic will occur on first access.
38///
39/// Conceptually, `OnReady<T>` is very close to [once_cell's `Lazy<T>`][lazy], with additional hooks into the Godot lifecycle.
40/// The absence of methods to check initialization state is deliberate: you don't need them if you follow the above two patterns.
41/// This container is not designed as a general late-initialization solution, but tailored to the `ready()` semantics of Godot.
42///
43/// `OnReady<T>` cannot be used with `#[export]` fields, because `ready()` is typically not called in the editor (unless `#[class(tool)]`
44/// is specified). You can however use it with `#[var]` -- just make sure to access the fields in GDScript after `ready()`.
45///
46/// This type is not thread-safe. `ready()` runs on the main thread, and you are expected to access its value on the main thread, as well.
47///
48/// [option]: std::option::Option
49/// [lazy]: https://docs.rs/once_cell/1/once_cell/unsync/struct.Lazy.html
50///
51/// # Requirements
52/// - The class must have an explicit `Base` field (i.e. `base: Base<Node>`).
53/// - The class must inherit `Node` (otherwise `ready()` would not exist anyway).
54///
55/// # Example - user-defined `init`
56/// ```
57/// use godot::prelude::*;
58///
59/// #[derive(GodotClass)]
60/// #[class(base = Node)]
61/// struct MyClass {
62///    base: Base<Node>,
63///    auto: OnReady<i32>,
64///    manual: OnReady<i32>,
65/// }
66///
67/// #[godot_api]
68/// impl INode for MyClass {
69///     fn init(base: Base<Node>) -> Self {
70///        Self {
71///            base,
72///            auto: OnReady::new(|| 11),
73///            manual: OnReady::manual(),
74///        }
75///     }
76///
77///     fn ready(&mut self) {
78///        // self.auto is now ready with value 11.
79///        assert_eq!(*self.auto, 11);
80///
81///        // self.manual needs to be initialized manually.
82///        self.manual.init(22);
83///        assert_eq!(*self.manual, 22);
84///     }
85/// }
86/// ```
87///
88/// # Example - macro-generated `init`
89/// ```
90/// use godot::prelude::*;
91///
92/// #[derive(GodotClass)]
93/// #[class(init, base = Node)]
94/// struct MyClass {
95///    base: Base<Node>,
96///
97///    #[init(node = "ChildPath")]
98///    auto: OnReady<Gd<Node2D>>,
99///
100///    #[init(val = OnReady::manual())]
101///    manual: OnReady<i32>,
102/// }
103///
104/// #[godot_api]
105/// impl INode for MyClass {
106///     fn ready(&mut self) {
107///        // self.node is now ready with the node found at path `ChildPath`.
108///        assert_eq!(self.auto.get_name(), "ChildPath");
109///
110///        // self.manual needs to be initialized manually.
111///        self.manual.init(22);
112///        assert_eq!(*self.manual, 22);
113///     }
114/// }
115/// ```
116#[derive(Debug)]
117pub struct OnReady<T> {
118    state: InitState<T>,
119}
120
121impl<T: Inherits<Node>> OnReady<Gd<T>> {
122    /// Variant of [`OnReady::new()`], fetching the node located at `path` before `ready()`.
123    ///
124    /// This is the functional equivalent of:
125    /// - the GDScript pattern `@onready var node = $NODE_PATH`.
126    /// - the Rust method [`Node::get_node_as()`].
127    ///
128    /// When used with `#[class(init)]`, the field can be annotated with `#[init(node = "NODE_PATH")]` to call this constructor.
129    ///
130    /// # Panics (deferred)
131    /// - If `path` does not point to a valid node, or its type is not a `T` or a subclass.
132    ///
133    /// Note that the panic will only happen if and when the node enters the SceneTree for the first time
134    /// (i.e. it receives the `READY` notification).
135    pub fn from_node(path: impl AsArg<NodePath>) -> Self {
136        arg_into_owned!(path);
137
138        Self::from_base_fn(move |base| base.get_node_as(&path))
139    }
140}
141
142impl<T: Inherits<Resource>> OnReady<Gd<T>> {
143    /// Variant of [`OnReady::new()`], loading the resource stored at `path` before `ready()`.
144    ///
145    /// This is the functional equivalent of:
146    /// - the GDScript pattern `@onready var res = load(...)`.
147    /// - the Rust function [`tools::load()`][crate::tools::load].
148    ///
149    /// When used with `#[class(init)]`, the field can be annotated with `#[init(load = "FILE_PATH")]` to call this constructor.
150    ///
151    /// # Panics (deferred)
152    /// - If the resource does not exist at `path`, cannot be loaded or is not compatible with type `T`.
153    ///
154    /// Note that the panic will only happen if and when the node enters the SceneTree for the first time
155    /// (i.e. it receives the `READY` notification).
156    pub fn from_loaded(path: impl AsArg<GString>) -> Self {
157        arg_into_owned!(path);
158
159        Self::new(move || crate::tools::load(&path))
160    }
161}
162
163impl<T> OnReady<T> {
164    /// Schedule automatic initialization before `ready()`.
165    ///
166    /// This guarantees that the value is initialized once `ready()` starts running.
167    /// Until then, accessing the object may panic. In particular, the object is _not_ initialized on first use.
168    ///
169    /// The value is also initialized when you don't override `ready()`.
170    ///
171    /// For more control over initialization, use the [`OnReady::manual()`] constructor, followed by a [`self.init()`][OnReady::init]
172    /// call during `ready()`.
173    pub fn new<F>(init_fn: F) -> Self
174    where
175        F: FnOnce() -> T + 'static,
176    {
177        Self::from_base_fn(|_| init_fn())
178    }
179
180    /// Variant of [`OnReady::new()`], allowing access to `Base` when initializing.
181    pub fn from_base_fn<F>(init_fn: F) -> Self
182    where
183        F: FnOnce(&Gd<Node>) -> T + 'static,
184    {
185        Self {
186            state: InitState::AutoPrepared {
187                initializer: Box::new(init_fn),
188            },
189        }
190    }
191
192    /// Leave uninitialized, expects manual initialization during `ready()`.
193    ///
194    /// If you use this method, you _must_ call [`init()`][Self::init] during the `ready()` callback, otherwise a panic will occur.
195    pub fn manual() -> Self {
196        Self {
197            state: InitState::ManualUninitialized,
198        }
199    }
200
201    /// Runs manual initialization.
202    ///
203    /// # Panics
204    /// - If `init()` was called before.
205    /// - If this object was already provided with a closure during construction, in [`Self::new()`] or any other automatic constructor.
206    pub fn init(&mut self, value: T) {
207        match &self.state {
208            InitState::ManualUninitialized => {
209                self.state = InitState::Initialized { value };
210            }
211            InitState::AutoPrepared { .. } | InitState::AutoInitializationFailed => {
212                panic!("cannot call init() on auto-initialized OnReady objects")
213            }
214            InitState::Initialized { .. } => {
215                panic!("already initialized; did you call init() more than once?")
216            }
217        };
218    }
219
220    /// Runs initialization.
221    ///
222    /// # Panics
223    /// - If the value is already initialized.
224    /// - If previous auto initialization failed.
225    pub(crate) fn init_auto(&mut self, base: &Gd<Node>) {
226        // Two branches needed, because mem::replace() could accidentally overwrite an already initialized value.
227        match &self.state {
228            InitState::ManualUninitialized => return, // skipped
229            InitState::AutoPrepared { .. } => {}      // handled below
230            InitState::AutoInitializationFailed => {
231                panic!("OnReady automatic value initialization has already failed")
232            }
233            InitState::Initialized { .. } => panic!("OnReady object already initialized"),
234        };
235
236        // Temporarily replace with AutoInitializationFailed state which will be left in iff initialization fails.
237        let InitState::AutoPrepared { initializer } =
238            mem::replace(&mut self.state, InitState::AutoInitializationFailed)
239        else {
240            // SAFETY: condition checked above.
241            unsafe { std::hint::unreachable_unchecked() }
242        };
243
244        self.state = InitState::Initialized {
245            value: initializer(base),
246        };
247    }
248}
249
250// Panicking Deref is not best practice according to Rust, but constant get() calls are significantly less ergonomic and make it harder to
251// migrate between T and LateInit<T>, because all the accesses need to change.
252impl<T> std::ops::Deref for OnReady<T> {
253    type Target = T;
254
255    /// Returns a shared reference to the value.
256    ///
257    /// # Panics
258    /// If the value is not yet initialized.
259    fn deref(&self) -> &Self::Target {
260        match &self.state {
261            InitState::ManualUninitialized => {
262                panic!("OnReady manual value uninitialized, did you call init()?")
263            }
264            InitState::AutoPrepared { .. } => {
265                panic!("OnReady automatic value uninitialized, is only available in ready()")
266            }
267            InitState::AutoInitializationFailed => {
268                panic!("OnReady automatic value initialization failed")
269            }
270            InitState::Initialized { value } => value,
271        }
272    }
273}
274
275impl<T> std::ops::DerefMut for OnReady<T> {
276    /// Returns an exclusive reference to the value.
277    ///     
278    /// # Panics
279    /// If the value is not yet initialized.
280    fn deref_mut(&mut self) -> &mut Self::Target {
281        match &mut self.state {
282            InitState::Initialized { value } => value,
283            InitState::ManualUninitialized | InitState::AutoPrepared { .. } => {
284                panic!("value not yet initialized")
285            }
286            InitState::AutoInitializationFailed => {
287                panic!("OnReady automatic value initialization failed")
288            }
289        }
290    }
291}
292
293impl<T: GodotConvert> GodotConvert for OnReady<T> {
294    type Via = T::Via;
295
296    fn godot_shape() -> GodotShape {
297        T::godot_shape()
298    }
299}
300
301impl<T> Var for OnReady<T>
302where
303    T: Var<PubType = T> + FromGodot,
304{
305    type PubType = T;
306
307    // All following functions: Deref/DerefMut panics if not initialized, preserving the "single init point" invariant.
308
309    fn var_get(field: &Self) -> Self::Via {
310        let deref: &T = field;
311        T::var_get(deref)
312    }
313
314    fn var_set(field: &mut Self, value: Self::Via) {
315        let deref: &mut T = field;
316        T::var_set(deref, value);
317    }
318
319    fn var_pub_get(field: &Self) -> Self::PubType {
320        let deref: &T = field;
321        T::var_pub_get(deref)
322    }
323
324    fn var_pub_set(field: &mut Self, value: Self::PubType) {
325        let deref: &mut T = field;
326        T::var_pub_set(deref, value);
327    }
328}
329
330// ----------------------------------------------------------------------------------------------------------------------------------------------
331// Implementation
332
333type InitFn<T> = dyn FnOnce(&Gd<Node>) -> T;
334
335enum InitState<T> {
336    ManualUninitialized,
337    AutoPrepared { initializer: Box<InitFn<T>> },
338    AutoInitializationFailed,
339    Initialized { value: T },
340}
341
342impl<T: Debug> Debug for InitState<T> {
343    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
344        match self {
345            InitState::ManualUninitialized => fmt.debug_struct("ManualUninitialized").finish(),
346            InitState::AutoPrepared { .. } => {
347                fmt.debug_struct("AutoPrepared").finish_non_exhaustive()
348            }
349            InitState::AutoInitializationFailed => {
350                fmt.debug_struct("AutoInitializationFailed").finish()
351            }
352            InitState::Initialized { value } => fmt
353                .debug_struct("Initialized")
354                .field("value", value)
355                .finish(),
356        }
357    }
358}