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