Skip to content

Commit 66946c4

Browse files
authored
Try #136:
2 parents f05915f + 73b1b6d commit 66946c4

File tree

19 files changed

+459
-135
lines changed

19 files changed

+459
-135
lines changed

examples/dodge-the-creeps/rust/src/hud.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use godot::engine::{Button, CanvasLayer, Label, Timer};
1+
use godot::engine::{Button, CanvasLayer, CanvasLayerVirtual, Label, Timer};
22
use godot::prelude::*;
33

44
#[derive(GodotClass)]
@@ -61,7 +61,7 @@ impl Hud {
6161
}
6262

6363
#[godot_api]
64-
impl GodotExt for Hud {
64+
impl CanvasLayerVirtual for Hud {
6565
fn init(base: Base<Self::Base>) -> Self {
6666
Self { base }
6767
}

examples/dodge-the-creeps/rust/src/main_scene.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ impl Main {
126126
}
127127

128128
#[godot_api]
129-
impl GodotExt for Main {
129+
impl NodeVirtual for Main {
130130
fn init(base: Base<Node>) -> Self {
131131
Main {
132132
mob_scene: PackedScene::new(),

examples/dodge-the-creeps/rust/src/mob.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use godot::engine::{AnimatedSprite2D, RigidBody2D};
1+
use godot::engine::{AnimatedSprite2D, RigidBody2D, RigidBody2DVirtual};
22
use godot::prelude::*;
33
use rand::seq::SliceRandom;
44

@@ -47,7 +47,7 @@ impl Mob {
4747
}
4848

4949
#[godot_api]
50-
impl GodotExt for Mob {
50+
impl RigidBody2DVirtual for Mob {
5151
fn init(base: Base<RigidBody2D>) -> Self {
5252
Mob {
5353
min_speed: 150.0,

examples/dodge-the-creeps/rust/src/player.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use godot::engine::{AnimatedSprite2D, Area2D, CollisionShape2D, PhysicsBody2D};
1+
use godot::engine::{AnimatedSprite2D, Area2D, Area2DVirtual, CollisionShape2D, PhysicsBody2D};
22
use godot::prelude::*;
33

44
#[derive(GodotClass)]
@@ -42,7 +42,7 @@ impl Player {
4242
}
4343

4444
#[godot_api]
45-
impl GodotExt for Player {
45+
impl Area2DVirtual for Player {
4646
fn init(base: Base<Area2D>) -> Self {
4747
Player {
4848
speed: 400.0,

godot-codegen/src/api_parser.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ pub struct BuiltinClassMethod {
167167
pub arguments: Option<Vec<MethodArg>>,
168168
}
169169

170-
#[derive(DeJson)]
170+
#[derive(DeJson, Clone)]
171171
pub struct ClassMethod {
172172
pub name: String,
173173
pub is_const: bool,
@@ -200,7 +200,7 @@ pub struct MethodArg {
200200
}
201201

202202
// Example: get_available_point_id -> {type: "int", meta: "int64"}
203-
#[derive(DeJson)]
203+
#[derive(DeJson, Clone)]
204204
pub struct MethodReturn {
205205
#[nserde(rename = "type")]
206206
pub type_: String,

godot-codegen/src/class_generator.rs

Lines changed: 143 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ fn make_constructor(class: &Class, ctx: &Context) -> TokenStream {
159159
fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> GeneratedClass {
160160
// Strings
161161
let godot_class_str = &class_name.godot_ty;
162+
let virtual_trait_str = class_name.virtual_trait_name();
162163

163164
// Idents and tokens
164165
let base = match class.inherits.as_ref() {
@@ -174,6 +175,7 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
174175
let enums = make_enums(&class.enums, class_name, ctx);
175176
let inherits_macro = format_ident!("inherits_transitive_{}", class_name.rust_ty);
176177
let all_bases = ctx.inheritance_tree().collect_all_bases(class_name);
178+
let virtual_trait = make_virtual_methods_trait(class, &all_bases, &virtual_trait_str, ctx);
177179

178180
let memory = if class_name.rust_ty == "Object" {
179181
ident("DynamicRefCount")
@@ -199,6 +201,7 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
199201
pub struct #class_name {
200202
object_ptr: sys::GDExtensionObjectPtr,
201203
}
204+
#virtual_trait
202205
impl #class_name {
203206
#constructor
204207
#methods
@@ -323,12 +326,14 @@ fn make_module_file(classes_and_modules: Vec<GeneratedClassModule>) -> TokenStre
323326
is_pub,
324327
..
325328
} = m;
329+
let virtual_trait_name = ident(&class_name.virtual_trait_name());
326330

327331
let vis = is_pub.then_some(quote! { pub });
328332

329333
quote! {
330334
#vis mod #module_name;
331335
pub use #module_name::re_export::#class_name;
336+
pub use #module_name::re_export::#virtual_trait_name;
332337
}
333338
});
334339

@@ -463,12 +468,15 @@ fn is_type_excluded(ty: &str, ctx: &mut Context) -> bool {
463468
}
464469
}
465470

466-
fn is_method_excluded(method: &ClassMethod, #[allow(unused_variables)] ctx: &mut Context) -> bool {
471+
fn is_method_excluded(
472+
method: &ClassMethod,
473+
is_virtual_impl: bool,
474+
#[allow(unused_variables)] ctx: &mut Context,
475+
) -> bool {
467476
// Currently excluded:
468477
//
469-
// * Private virtual methods designed for override; skip for now
470-
// E.g.: AudioEffectInstance::_process(const void*, AudioFrame*, int)
471-
// TODO decide what to do with them, overriding in a type-safe way?
478+
// * Private virtual methods are only included in a virtual
479+
// implementation.
472480
//
473481
// * Methods accepting pointers are often supplementary
474482
// E.g.: TextServer::font_set_data_ptr() -- in addition to TextServer::font_set_data().
@@ -490,11 +498,14 @@ fn is_method_excluded(method: &ClassMethod, #[allow(unused_variables)] ctx: &mut
490498
}
491499
// -- end.
492500

493-
method.name.starts_with('_')
494-
|| method
495-
.return_value
496-
.as_ref()
497-
.map_or(false, |ret| ret.type_.contains('*'))
501+
if method.name.starts_with('_') && !is_virtual_impl {
502+
return true;
503+
}
504+
505+
method
506+
.return_value
507+
.as_ref()
508+
.map_or(false, |ret| ret.type_.contains('*'))
498509
|| method
499510
.arguments
500511
.as_ref()
@@ -523,7 +534,8 @@ fn make_method_definition(
523534
class_name: &TyName,
524535
ctx: &mut Context,
525536
) -> TokenStream {
526-
if is_method_excluded(method, ctx) || special_cases::is_deleted(class_name, &method.name) {
537+
if is_method_excluded(method, false, ctx) || special_cases::is_deleted(class_name, &method.name)
538+
{
527539
return TokenStream::new();
528540
}
529541
/*if method.map_args(|args| args.is_empty()) {
@@ -799,13 +811,7 @@ fn make_receiver(
799811
is_const: bool,
800812
receiver_arg: TokenStream,
801813
) -> (TokenStream, TokenStream) {
802-
let receiver = if is_static {
803-
quote! {}
804-
} else if is_const {
805-
quote! { &self, }
806-
} else {
807-
quote! { &mut self, }
808-
};
814+
let receiver = make_receiver_self_param(is_static, is_const);
809815

810816
let receiver_arg = if is_static {
811817
quote! { std::ptr::null_mut() }
@@ -816,6 +822,16 @@ fn make_receiver(
816822
(receiver, receiver_arg)
817823
}
818824

825+
fn make_receiver_self_param(is_static: bool, is_const: bool) -> TokenStream {
826+
if is_static {
827+
quote! {}
828+
} else if is_const {
829+
quote! { &self, }
830+
} else {
831+
quote! { &mut self, }
832+
}
833+
}
834+
819835
fn make_params(
820836
method_args: &Option<Vec<MethodArg>>,
821837
is_varcall: bool,
@@ -930,3 +946,113 @@ fn make_return(
930946

931947
(return_decl, call)
932948
}
949+
950+
fn make_virtual_methods_trait(
951+
class: &Class,
952+
all_bases: &[TyName],
953+
trait_name: &str,
954+
ctx: &mut Context,
955+
) -> TokenStream {
956+
let trait_name = ident(trait_name);
957+
958+
let virtual_method_fns = make_all_virtual_methods(class, all_bases, ctx);
959+
let special_virtual_methods = special_virtual_methods();
960+
961+
quote! {
962+
#[allow(unused_variables)]
963+
#[allow(clippy::unimplemented)]
964+
pub trait #trait_name: crate::private::You_forgot_the_attribute__godot_api + crate::obj::GodotClass {
965+
#( #virtual_method_fns )*
966+
#special_virtual_methods
967+
}
968+
}
969+
}
970+
971+
fn special_virtual_methods() -> TokenStream {
972+
quote! {
973+
fn register_class(builder: &mut crate::builder::ClassBuilder<Self>) {
974+
unimplemented!()
975+
}
976+
fn init(base: crate::obj::Base<Self::Base>) -> Self {
977+
unimplemented!()
978+
}
979+
fn to_string(&self) -> crate::builtin::GodotString {
980+
unimplemented!()
981+
}
982+
}
983+
}
984+
985+
fn make_virtual_method(class_method: &ClassMethod, ctx: &mut Context) -> TokenStream {
986+
let method_name = ident(virtual_method_name(class_method));
987+
988+
// Virtual methods are never static.
989+
assert!(!class_method.is_static);
990+
991+
let receiver = make_receiver_self_param(false, class_method.is_const);
992+
let [params, _, _, _] = make_params(&class_method.arguments, class_method.is_vararg, ctx);
993+
994+
quote! {
995+
fn #method_name ( #receiver #( #params , )* ) {
996+
unimplemented!()
997+
}
998+
}
999+
}
1000+
1001+
fn make_all_virtual_methods(
1002+
class: &Class,
1003+
all_bases: &[TyName],
1004+
ctx: &mut Context,
1005+
) -> Vec<TokenStream> {
1006+
let mut all_virtuals = vec![];
1007+
let mut extend_virtuals = |class| {
1008+
all_virtuals.extend(
1009+
get_methods_in_class(class)
1010+
.iter()
1011+
.cloned()
1012+
.filter(|m| m.is_virtual),
1013+
);
1014+
};
1015+
1016+
// Get virtuals defined on the current class.
1017+
extend_virtuals(class);
1018+
// Add virtuals from superclasses.
1019+
for base in all_bases {
1020+
let superclass = ctx.get_engine_class(base);
1021+
extend_virtuals(superclass);
1022+
}
1023+
all_virtuals
1024+
.into_iter()
1025+
.filter_map(|method| {
1026+
if is_method_excluded(&method, true, ctx) {
1027+
None
1028+
} else {
1029+
Some(make_virtual_method(&method, ctx))
1030+
}
1031+
})
1032+
.collect()
1033+
}
1034+
1035+
fn get_methods_in_class(class: &Class) -> &[ClassMethod] {
1036+
match &class.methods {
1037+
None => &[],
1038+
Some(methods) => methods,
1039+
}
1040+
}
1041+
1042+
fn virtual_method_name(class_method: &ClassMethod) -> &str {
1043+
// Matching the C++ convention, we remove the leading underscore
1044+
// from virtual method names.
1045+
let method_name = class_method
1046+
.name
1047+
.strip_prefix('_')
1048+
.unwrap_or(&class_method.name);
1049+
1050+
// As a special exception, a few classes define a virtual method
1051+
// called "_init" (distinct from the constructor), so we rename
1052+
// those to avoid a name conflict in our trait.
1053+
if method_name == "init" {
1054+
"init_ext"
1055+
} else {
1056+
method_name
1057+
}
1058+
}

godot-codegen/src/context.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
66

7+
use crate::api_parser::Class;
78
use crate::{ExtensionApi, RustTy, TyName};
89
use std::collections::{HashMap, HashSet};
910

1011
#[derive(Default)]
1112
pub(crate) struct Context<'a> {
12-
engine_classes: HashSet<TyName>,
13+
engine_classes: HashMap<TyName, &'a Class>,
1314
builtin_types: HashSet<&'a str>,
1415
singletons: HashSet<&'a str>,
1516
inheritance_tree: InheritanceTree,
@@ -39,7 +40,7 @@ impl<'a> Context<'a> {
3940
}
4041

4142
println!("-- add engine class {}", class_name.description());
42-
ctx.engine_classes.insert(class_name.clone());
43+
ctx.engine_classes.insert(class_name.clone(), class);
4344

4445
if let Some(base) = class.inherits.as_ref() {
4546
let base_name = TyName::from_godot(base);
@@ -50,6 +51,10 @@ impl<'a> Context<'a> {
5051
ctx
5152
}
5253

54+
pub fn get_engine_class(&self, class_name: &TyName) -> &Class {
55+
self.engine_classes.get(class_name).unwrap()
56+
}
57+
5358
// pub fn is_engine_class(&self, class_name: &str) -> bool {
5459
// self.engine_classes.contains(class_name)
5560
// }

godot-codegen/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ impl TyName {
197197
format!("{} [renamed {}]", self.godot_ty, self.rust_ty)
198198
}
199199
}
200+
201+
fn virtual_trait_name(&self) -> String {
202+
format!("{}Virtual", self.rust_ty)
203+
}
200204
}
201205

202206
impl ToTokens for TyName {

0 commit comments

Comments
 (0)