@@ -70,10 +70,9 @@ def generate_wrappers(target):
70
70
f .write (txt )
71
71
72
72
73
- def generate_virtual_version (argcount , const = False , returns = False ):
73
+ def generate_virtual_version (argcount , const = False , returns = False , required = False ):
74
74
s = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
75
75
::godot::StringName _gdvirtual_##m_name##_sn = #m_name;\\
76
- template <bool required>\\
77
76
_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST {\\
78
77
if (::godot::internal::gdextension_interface_object_has_script_method(_owner, &_gdvirtual_##m_name##_sn)) { \\
79
78
GDExtensionCallError ce;\\
@@ -85,10 +84,8 @@ def generate_virtual_version(argcount, const=False, returns=False):
85
84
return true;\\
86
85
}\\
87
86
}\\
88
- if (required) {\\
89
- ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\
90
- $RVOID\\
91
- }\\
87
+ $REQCHECK\\
88
+ $RVOID\\
92
89
return false;\\
93
90
}\\
94
91
_FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const {\\
@@ -106,6 +103,7 @@ def generate_virtual_version(argcount, const=False, returns=False):
106
103
107
104
sproto = str (argcount )
108
105
method_info = ""
106
+ method_flags = "METHOD_FLAG_VIRTUAL"
109
107
if returns :
110
108
sproto += "R"
111
109
s = s .replace ("$RET" , "m_ret," )
@@ -114,16 +112,26 @@ def generate_virtual_version(argcount, const=False, returns=False):
114
112
method_info += "\t \t method_info.return_val_metadata = ::godot::GetTypeInfo<m_ret>::METADATA;"
115
113
else :
116
114
s = s .replace ("$RET " , "" )
117
- s = s .replace ("\t \t \t $RVOID\\ \n " , "" )
115
+ s = s .replace ("\t \t $RVOID\\ \n " , "" )
118
116
119
117
if const :
120
118
sproto += "C"
119
+ method_flags += " | METHOD_FLAG_CONST"
121
120
s = s .replace ("$CONST" , "const" )
122
- s = s .replace ("$METHOD_FLAGS" , "::godot::METHOD_FLAG_VIRTUAL | ::godot::METHOD_FLAG_CONST" )
123
121
else :
124
122
s = s .replace ("$CONST " , "" )
125
- s = s .replace ("$METHOD_FLAGS" , "::godot::METHOD_FLAG_VIRTUAL" )
126
123
124
+ if required :
125
+ sproto += "_REQUIRED"
126
+ method_flags += " | METHOD_FLAG_VIRTUAL_REQUIRED"
127
+ s = s .replace (
128
+ "$REQCHECK" ,
129
+ 'ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");' ,
130
+ )
131
+ else :
132
+ s = s .replace ("\t \t $REQCHECK\\ \n " , "" )
133
+
134
+ s = s .replace ("$METHOD_FLAGS" , method_flags )
127
135
s = s .replace ("$VER" , sproto )
128
136
argtext = ""
129
137
callargtext = ""
@@ -190,20 +198,27 @@ def generate_virtuals(target):
190
198
txt += generate_virtual_version (i , False , True )
191
199
txt += generate_virtual_version (i , True , False )
192
200
txt += generate_virtual_version (i , True , True )
201
+ txt += generate_virtual_version (i , False , False , True )
202
+ txt += generate_virtual_version (i , False , True , True )
203
+ txt += generate_virtual_version (i , True , False , True )
204
+ txt += generate_virtual_version (i , True , True , True )
193
205
194
206
txt += "#endif // GDEXTENSION_GDVIRTUAL_GEN_H\n "
195
207
196
208
with open (target , "w" , encoding = "utf-8" ) as f :
197
209
f .write (txt )
198
210
199
211
200
- def get_file_list (api_filepath , output_dir , headers = False , sources = False , profile_filepath = "" ):
212
+ def get_file_list (api_filepath , output_dir , headers = False , sources = False ):
201
213
api = {}
202
- files = []
203
214
with open (api_filepath , encoding = "utf-8" ) as api_file :
204
215
api = json .load (api_file )
205
216
206
- build_profile = parse_build_profile (profile_filepath , api )
217
+ return _get_file_list (api , output_dir , headers , sources )
218
+
219
+
220
+ def _get_file_list (api , output_dir , headers = False , sources = False ):
221
+ files = []
207
222
208
223
core_gen_folder = Path (output_dir ) / "gen" / "include" / "godot_cpp" / "core"
209
224
include_gen_folder = Path (output_dir ) / "gen" / "include" / "godot_cpp"
@@ -235,7 +250,7 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False, profil
235
250
source_filename = source_gen_folder / "classes" / (camel_to_snake (engine_class ["name" ]) + ".cpp" )
236
251
if headers :
237
252
files .append (str (header_filename .as_posix ()))
238
- if sources and is_class_included ( engine_class [ "name" ], build_profile ) :
253
+ if sources :
239
254
files .append (str (source_filename .as_posix ()))
240
255
241
256
for native_struct in api ["native_structures" ]:
@@ -267,128 +282,19 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False, profil
267
282
return files
268
283
269
284
270
- def print_file_list (api_filepath , output_dir , headers = False , sources = False , profile_filepath = "" ):
271
- print (* get_file_list (api_filepath , output_dir , headers , sources , profile_filepath ), sep = ";" , end = None )
272
-
273
-
274
- def parse_build_profile (profile_filepath , api ):
275
- if profile_filepath == "" :
276
- return {}
277
- print ("Using feature build profile: " + profile_filepath )
278
-
279
- with open (profile_filepath , encoding = "utf-8" ) as profile_file :
280
- profile = json .load (profile_file )
281
-
282
- api_dict = {}
283
- parents = {}
284
- children = {}
285
- for engine_class in api ["classes" ]:
286
- api_dict [engine_class ["name" ]] = engine_class
287
- parent = engine_class .get ("inherits" , "" )
288
- child = engine_class ["name" ]
289
- parents [child ] = parent
290
- if parent == "" :
291
- continue
292
- children [parent ] = children .get (parent , [])
293
- children [parent ].append (child )
294
-
295
- # Parse methods dependencies
296
- deps = {}
297
- reverse_deps = {}
298
- for name , engine_class in api_dict .items ():
299
- ref_cls = set ()
300
- for method in engine_class .get ("methods" , []):
301
- rtype = method .get ("return_value" , {}).get ("type" , "" )
302
- args = [a ["type" ] for a in method .get ("arguments" , [])]
303
- if rtype in api_dict :
304
- ref_cls .add (rtype )
305
- elif is_enum (rtype ) and get_enum_class (rtype ) in api_dict :
306
- ref_cls .add (get_enum_class (rtype ))
307
- for arg in args :
308
- if arg in api_dict :
309
- ref_cls .add (arg )
310
- elif is_enum (arg ) and get_enum_class (arg ) in api_dict :
311
- ref_cls .add (get_enum_class (arg ))
312
- deps [engine_class ["name" ]] = set (filter (lambda x : x != name , ref_cls ))
313
- for acls in ref_cls :
314
- if acls == name :
315
- continue
316
- reverse_deps [acls ] = reverse_deps .get (acls , set ())
317
- reverse_deps [acls ].add (name )
318
-
319
- included = []
320
- front = list (profile .get ("enabled_classes" , []))
321
- if front :
322
- # These must always be included
323
- front .append ("WorkerThreadPool" )
324
- front .append ("ClassDB" )
325
- front .append ("ClassDBSingleton" )
326
- while front :
327
- cls = front .pop ()
328
- if cls in included :
329
- continue
330
- included .append (cls )
331
- parent = parents .get (cls , "" )
332
- if parent :
333
- front .append (parent )
334
- for rcls in deps .get (cls , set ()):
335
- if rcls in included or rcls in front :
336
- continue
337
- front .append (rcls )
338
-
339
- excluded = []
340
- front = list (profile .get ("disabled_classes" , []))
341
- while front :
342
- cls = front .pop ()
343
- if cls in excluded :
344
- continue
345
- excluded .append (cls )
346
- front += children .get (cls , [])
347
- for rcls in reverse_deps .get (cls , set ()):
348
- if rcls in excluded or rcls in front :
349
- continue
350
- front .append (rcls )
351
-
352
- if included and excluded :
353
- print (
354
- "WARNING: Cannot specify both 'enabled_classes' and 'disabled_classes' in build profile. 'disabled_classes' will be ignored."
355
- )
356
-
357
- return {
358
- "enabled_classes" : included ,
359
- "disabled_classes" : excluded ,
360
- }
361
-
362
-
363
- def scons_emit_files (target , source , env ):
364
- profile_filepath = env .get ("build_profile" , "" )
365
- if profile_filepath and not Path (profile_filepath ).is_absolute ():
366
- profile_filepath = str ((Path (env .Dir ("#" ).abspath ) / profile_filepath ).as_posix ())
367
-
368
- files = [env .File (f ) for f in get_file_list (str (source [0 ]), target [0 ].abspath , True , True , profile_filepath )]
369
- env .Clean (target , files )
370
- env ["godot_cpp_gen_dir" ] = target [0 ].abspath
371
- return files , source
372
-
373
-
374
- def scons_generate_bindings (target , source , env ):
375
- generate_bindings (
376
- str (source [0 ]),
377
- env ["generate_template_get_node" ],
378
- "32" if "32" in env ["arch" ] else "64" ,
379
- env ["precision" ],
380
- env ["godot_cpp_gen_dir" ],
381
- )
382
- return None
285
+ def print_file_list (api_filepath , output_dir , headers = False , sources = False ):
286
+ print (* get_file_list (api_filepath , output_dir , headers , sources ), sep = ";" , end = None )
383
287
384
288
385
289
def generate_bindings (api_filepath , use_template_get_node , bits = "64" , precision = "single" , output_dir = "." ):
386
- api = None
387
-
388
- target_dir = Path (output_dir ) / "gen"
389
-
290
+ api = {}
390
291
with open (api_filepath , encoding = "utf-8" ) as api_file :
391
292
api = json .load (api_file )
293
+ _generate_bindings (api , use_template_get_node , bits , precision , output_dir )
294
+
295
+
296
+ def _generate_bindings (api , use_template_get_node , bits = "64" , precision = "single" , output_dir = "." ):
297
+ target_dir = Path (output_dir ) / "gen"
392
298
393
299
shutil .rmtree (target_dir , ignore_errors = True )
394
300
target_dir .mkdir (parents = True )
@@ -1788,7 +1694,7 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
1788
1694
# condition returns false (in such cases it can't compile due to ambiguity).
1789
1695
f"\t \t if constexpr (!std::is_same_v<decltype(&B::{ method_name } ), decltype(&T::{ method_name } )>) {{"
1790
1696
)
1791
- result .append (f"\t \t \t BIND_VIRTUAL_METHOD(T, { method_name } );" )
1697
+ result .append (f"\t \t \t BIND_VIRTUAL_METHOD(T, { method_name } , { method [ 'hash' ] } );" )
1792
1698
result .append ("\t \t }" )
1793
1699
1794
1700
result .append ("\t }" )
@@ -2457,6 +2363,10 @@ def get_encoded_arg(arg_name, type_name, type_meta):
2457
2363
result .append (f"\t { get_gdextension_type (arg_type )} { name } _encoded;" )
2458
2364
result .append (f"\t PtrToArg<{ correct_type (type_name )} >::encode({ name } , &{ name } _encoded);" )
2459
2365
name = f"&{ name } _encoded"
2366
+ elif is_enum (type_name ) and not is_bitfield (type_name ):
2367
+ result .append (f"\t int64_t { name } _encoded;" )
2368
+ result .append (f"\t PtrToArg<int64_t>::encode({ name } , &{ name } _encoded);" )
2369
+ name = f"&{ name } _encoded"
2460
2370
elif is_engine_class (type_name ):
2461
2371
# `{name}` is a C++ wrapper, it contains a field which is the object's pointer Godot expects.
2462
2372
# We have to check `nullptr` because when the caller sends `nullptr`, the wrapper itself will be null.
@@ -2767,20 +2677,6 @@ def is_refcounted(type_name):
2767
2677
return type_name in engine_classes and engine_classes [type_name ]
2768
2678
2769
2679
2770
- def is_class_included (class_name , build_profile ):
2771
- """
2772
- Check if an engine class should be included.
2773
- This removes classes according to a build profile of enabled or disabled classes.
2774
- """
2775
- included = build_profile .get ("enabled_classes" , [])
2776
- excluded = build_profile .get ("disabled_classes" , [])
2777
- if included :
2778
- return class_name in included
2779
- if excluded :
2780
- return class_name not in excluded
2781
- return True
2782
-
2783
-
2784
2680
def is_included (type_name , current_type ):
2785
2681
"""
2786
2682
Check if a builtin type should be included.
0 commit comments