Skip to content

Commit 735cfd0

Browse files
committed
Make GodotClass::INIT_LEVEL more user-friendly (remove Option + document)
1 parent 8920adb commit 735cfd0

File tree

6 files changed

+37
-26
lines changed

6 files changed

+37
-26
lines changed

godot-codegen/src/class_generator.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,11 +651,12 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
651651
}
652652
impl crate::obj::GodotClass for #class_name {
653653
type Base = #base_ty;
654-
const INIT_LEVEL: Option<crate::init::InitLevel> = #init_level;
655654

656655
fn class_name() -> ClassName {
657656
ClassName::from_ascii_cstr(#class_name_cstr)
658657
}
658+
659+
const INIT_LEVEL: crate::init::InitLevel = #init_level;
659660
}
660661
unsafe impl crate::obj::Bounds for #class_name {
661662
type Memory = crate::obj::bounds::#assoc_memory;

godot-codegen/src/util.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ impl ClassCodegenLevel {
6565

6666
pub fn to_init_level(self) -> TokenStream {
6767
match self {
68-
Self::Servers => quote! { Some(crate::init::InitLevel::Servers) },
69-
Self::Scene => quote! { Some(crate::init::InitLevel::Scene) },
70-
Self::Editor => quote! { Some(crate::init::InitLevel::Editor) },
68+
Self::Servers => quote! { crate::init::InitLevel::Servers },
69+
Self::Scene => quote! { crate::init::InitLevel::Scene },
70+
Self::Editor => quote! { crate::init::InitLevel::Editor },
7171
}
7272
}
7373
}

godot-core/src/obj/bounds.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ pub(super) mod private {
108108
/// impl GodotClass for MyClass {
109109
/// type Base = Node;
110110
///
111-
/// # const INIT_LEVEL: Option<InitLevel> = None;
112111
/// fn class_name() -> ClassName {
113112
/// ClassName::from_ascii_cstr(b"MyClass\0")
114113
/// }

godot-core/src/obj/traits.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,17 @@ where
2525
/// The immediate superclass of `T`. This is always a Godot engine class.
2626
type Base: GodotClass; // not EngineClass because it can be ()
2727

28-
/// During which initialization level this class is available/should be initialized with Godot.
29-
///
30-
/// Is `None` if the class has complicated initialization requirements, and generally cannot be inherited
31-
/// from (currently only for `()`, the "base" of `Object`).
32-
const INIT_LEVEL: Option<InitLevel>;
33-
3428
/// The name of the class, under which it is registered in Godot.
3529
///
3630
/// This may deviate from the Rust struct name: `HttpRequest::class_name().as_str() == "HTTPRequest"`.
3731
fn class_name() -> ClassName;
3832

33+
/// Initialization level, during which this class should be initialized with Godot.
34+
///
35+
/// The default is a good choice in most cases; override only if you have very specific initialization requirements.
36+
/// It must not be less than `Base::INIT_LEVEL`.
37+
const INIT_LEVEL: InitLevel = <Self::Base as GodotClass>::INIT_LEVEL;
38+
3939
/// Returns whether `Self` inherits from `U`.
4040
///
4141
/// This is reflexive, i.e `Self` inherits from itself.
@@ -55,11 +55,12 @@ where
5555
/// Unit impl only exists to represent "no base", and is used for exactly one class: `Object`.
5656
impl GodotClass for () {
5757
type Base = ();
58-
const INIT_LEVEL: Option<InitLevel> = None;
5958

6059
fn class_name() -> ClassName {
6160
ClassName::none()
6261
}
62+
63+
const INIT_LEVEL: InitLevel = InitLevel::Core; // arbitrary; never read.
6364
}
6465

6566
/// Unit impl only exists to represent "no base", and is used for exactly one class: `Object`.

godot-core/src/registry.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use crate::builtin::meta::ClassName;
99
use crate::builtin::StringName;
1010
use crate::init::InitLevel;
11-
use crate::log;
1211
use crate::obj::*;
1312
use crate::out;
1413
use crate::private::as_storage;
@@ -39,7 +38,7 @@ pub struct ClassPlugin {
3938

4039
// Init-level is per ClassPlugin and not per PluginComponent, because all components of all classes are mixed together in one
4140
// huge linker list. There is no per-class aggregation going on, so this allows to easily filter relevant classes.
42-
pub init_level: Option<InitLevel>,
41+
pub init_level: InitLevel,
4342
}
4443

4544
/// Type-erased function object, holding a `register_class` function.
@@ -224,6 +223,11 @@ pub fn register_class<
224223
..default_creation_info()
225224
};
226225

226+
assert!(
227+
!T::class_name().as_str().is_empty(),
228+
"cannot register () or unnamed class"
229+
);
230+
227231
register_class_raw(ClassRegistrationInfo {
228232
class_name: T::class_name(),
229233
parent_class_name: Some(T::Base::class_name()),
@@ -235,9 +239,7 @@ pub fn register_class<
235239
user_virtual_fn: None,
236240
default_virtual_fn: None,
237241
godot_params,
238-
init_level: T::INIT_LEVEL.unwrap_or_else(|| {
239-
panic!("Unknown initialization level for class {}", T::class_name())
240-
}),
242+
init_level: T::INIT_LEVEL,
241243
is_editor_plugin: false,
242244
});
243245
}
@@ -256,13 +258,8 @@ pub fn auto_register_classes(init_level: InitLevel) {
256258
//out!("* Plugin: {elem:#?}");
257259

258260
// Filter per ClassPlugin and not PluginComponent, because all components of all classes are mixed together in one huge list.
259-
match elem.init_level {
260-
None => {
261-
log::godot_error!("Unknown initialization level for class {}", elem.class_name);
262-
return;
263-
}
264-
Some(elem_init_level) if elem_init_level != init_level => return,
265-
_ => { /* Nothing */ }
261+
if elem.init_level != init_level {
262+
return;
266263
}
267264

268265
let name = elem.class_name;

godot-macros/src/class/derive_godot_class.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ pub fn derive_godot_class(decl: Declaration) -> ParseResult<TokenStream> {
9797
Ok(quote! {
9898
impl ::godot::obj::GodotClass for #class_name {
9999
type Base = #base_class;
100-
const INIT_LEVEL: Option<::godot::init::InitLevel> = <#base_class as ::godot::obj::GodotClass>::INIT_LEVEL;
101100

102101
fn class_name() -> ::godot::builtin::meta::ClassName {
103102
::godot::builtin::meta::ClassName::from_ascii_cstr(#class_name_cstr)
@@ -127,7 +126,21 @@ pub fn derive_godot_class(decl: Declaration) -> ParseResult<TokenStream> {
127126
free_fn: #prv::callbacks::free::<#class_name>,
128127
default_get_virtual_fn: #default_get_virtual_fn,
129128
},
130-
init_level: <#class_name as ::godot::obj::GodotClass>::INIT_LEVEL,
129+
init_level: {
130+
let level = <#class_name as ::godot::obj::GodotClass>::INIT_LEVEL;
131+
let base_level = <#base_class as ::godot::obj::GodotClass>::INIT_LEVEL;
132+
133+
// Sanity check for init levels. Note that this does not cover cases where GodotClass is manually defined;
134+
// might make sense to add a run-time check during class registration.
135+
assert!(
136+
level >= base_level,
137+
"Class `{class}` has init level `{level:?}`, but its base class has init level `{base_level:?}`.\n\
138+
A class cannot be registered before its base class.",
139+
class = #class_name_str,
140+
);
141+
142+
level
143+
}
131144
});
132145

133146
#editor_plugin

0 commit comments

Comments
 (0)