@@ -97,9 +97,6 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False):
97
97
files .append (str (source_filename .as_posix ()))
98
98
99
99
for engine_class in api ["classes" ]:
100
- # TODO: Properly setup this singleton since it conflicts with ClassDB in the bindings.
101
- if engine_class ["name" ] == "ClassDB" :
102
- continue
103
100
header_filename = include_gen_folder / "classes" / (camel_to_snake (engine_class ["name" ]) + ".hpp" )
104
101
source_filename = source_gen_folder / "classes" / (camel_to_snake (engine_class ["name" ]) + ".cpp" )
105
102
if headers :
@@ -1036,9 +1033,6 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
1036
1033
1037
1034
# First create map of classes and singletons.
1038
1035
for class_api in api ["classes" ]:
1039
- # TODO: Properly setup this singleton since it conflicts with ClassDB in the bindings.
1040
- if class_api ["name" ] == "ClassDB" :
1041
- continue
1042
1036
engine_classes [class_api ["name" ]] = class_api ["is_refcounted" ]
1043
1037
for native_struct in api ["native_structures" ]:
1044
1038
engine_classes [native_struct ["name" ]] = False
@@ -1048,9 +1042,6 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
1048
1042
singletons .append (singleton ["name" ])
1049
1043
1050
1044
for class_api in api ["classes" ]:
1051
- # TODO: Properly setup this singleton since it conflicts with ClassDB in the bindings.
1052
- if class_api ["name" ] == "ClassDB" :
1053
- continue
1054
1045
# Check used classes for header include.
1055
1046
used_classes = set ()
1056
1047
fully_used_classes = set ()
@@ -1219,6 +1210,7 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
1219
1210
class_name = class_api ["name" ]
1220
1211
snake_class_name = camel_to_snake (class_name ).upper ()
1221
1212
is_singleton = class_name in singletons
1213
+ is_class_db = class_name == "ClassDB"
1222
1214
1223
1215
add_header (f"{ snake_class_name .lower ()} .hpp" , result )
1224
1216
@@ -1239,14 +1231,18 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
1239
1231
result .append ("" )
1240
1232
1241
1233
if class_name != "Object" :
1242
- result .append ("#include <godot_cpp/core/class_db.hpp>" )
1243
- result .append ("" )
1244
- result .append ("#include <type_traits>" )
1245
- result .append ("" )
1234
+ if not is_class_db :
1235
+ result .append ("#include <godot_cpp/core/class_db.hpp>" )
1236
+ result .append ("" )
1237
+ result .append ("#include <type_traits>" )
1238
+ result .append ("" )
1246
1239
1247
1240
result .append ("namespace godot {" )
1248
1241
result .append ("" )
1249
1242
1243
+ if is_class_db :
1244
+ result .append ("#define CLASSDB_HEADER_DECLARE()" )
1245
+
1250
1246
for type_name in used_classes :
1251
1247
if is_struct_type (type_name ):
1252
1248
result .append (f"struct { type_name } ;" )
@@ -1257,7 +1253,8 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
1257
1253
result .append ("" )
1258
1254
1259
1255
inherits = class_api ["inherits" ] if "inherits" in class_api else "Wrapped"
1260
- result .append (f"class { class_name } : public { inherits } {{" )
1256
+ if not is_class_db :
1257
+ result .append (f"class { class_name } : public { inherits } {{" )
1261
1258
1262
1259
result .append (f"\t GDEXTENSION_CLASS({ class_name } , { inherits } )" )
1263
1260
result .append ("" )
@@ -1338,7 +1335,12 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
1338
1335
1339
1336
result .append ("\t }" )
1340
1337
result .append ("" )
1341
- result .append ("public:" )
1338
+
1339
+ if is_class_db :
1340
+ result .append ("private:" )
1341
+ result .append (get_class_db_macro_end_mark_line ("CLASSDB_HEADER_DECLARE" ))
1342
+ else :
1343
+ result .append ("public:" )
1342
1344
1343
1345
# Special cases.
1344
1346
if class_name == "XMLParser" :
@@ -1378,8 +1380,9 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
1378
1380
"\t T *get_node(const NodePath &p_path) const { return Object::cast_to<T>(get_node_internal(p_path)); }"
1379
1381
)
1380
1382
1381
- result .append ("" )
1382
- result .append ("};" )
1383
+ if not is_class_db :
1384
+ result .append ("" )
1385
+ result .append ("};" )
1383
1386
result .append ("" )
1384
1387
1385
1388
if class_name == "EditorPlugin" :
@@ -1403,6 +1406,9 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
1403
1406
result .append ("} // namespace godot" )
1404
1407
result .append ("" )
1405
1408
1409
+ if is_class_db :
1410
+ result .append ("#define CLASSDB_HEADER_ENUM()" )
1411
+
1406
1412
if "enums" in class_api and class_name != "Object" :
1407
1413
for enum_api in class_api ["enums" ]:
1408
1414
if enum_api ["is_bitfield" ]:
@@ -1411,8 +1417,17 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
1411
1417
result .append (f'VARIANT_ENUM_CAST({ class_name } ::{ enum_api ["name" ]} );' )
1412
1418
result .append ("" )
1413
1419
1420
+ if is_class_db :
1421
+ result .append (get_class_db_macro_end_mark_line ("CLASSDB_HEADER_ENUM" ))
1422
+ result .append ("" )
1423
+ result .append ("" )
1424
+ result .append (f"#include <godot_cpp/../../src/classes/{ snake_class_name .lower ()} .cpp>" )
1425
+
1414
1426
result .append (f"#endif // ! { header_guard } " )
1415
1427
1428
+ if is_class_db :
1429
+ append_backslash_to_class_db_macro (result )
1430
+
1416
1431
return "\n " .join (result )
1417
1432
1418
1433
@@ -1423,13 +1438,16 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
1423
1438
class_name = class_api ["name" ]
1424
1439
snake_class_name = camel_to_snake (class_name )
1425
1440
is_singleton = class_name in singletons
1441
+ is_class_db = class_name == "ClassDB"
1426
1442
1427
1443
add_header (f"{ snake_class_name } .cpp" , result )
1428
1444
1429
- result .append (f"#include <godot_cpp/classes/{ snake_class_name } .hpp>" )
1430
- result .append ("" )
1431
- result .append ("#include <godot_cpp/core/engine_ptrcall.hpp>" )
1432
- result .append ("#include <godot_cpp/core/error_macros.hpp>" )
1445
+ if not is_class_db :
1446
+ result .append (f"#include <godot_cpp/classes/{ snake_class_name } .hpp>" )
1447
+ result .append ("" )
1448
+ result .append ("#include <godot_cpp/core/engine_ptrcall.hpp>" )
1449
+ result .append ("#include <godot_cpp/core/error_macros.hpp>" )
1450
+
1433
1451
result .append ("" )
1434
1452
1435
1453
for included in used_classes :
@@ -1442,21 +1460,43 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
1442
1460
result .append ("" )
1443
1461
1444
1462
if is_singleton :
1445
- result .append (f"{ class_name } *{ class_name } ::get_singleton() {{" )
1446
- result .append (f"\t const StringName __class_name = { class_name } ::get_class_static();" )
1447
- result .append (
1448
- "\t static GDExtensionObjectPtr singleton_obj = internal::gdextension_interface_global_get_singleton(__class_name._native_ptr());"
1449
- )
1450
- result .append ("#ifdef DEBUG_ENABLED" )
1451
- result .append ("\t ERR_FAIL_COND_V(singleton_obj == nullptr, nullptr);" )
1452
- result .append ("#endif // DEBUG_ENABLED" )
1453
- result .append (
1454
- f"\t static { class_name } *singleton = reinterpret_cast<{ class_name } *>(internal::gdextension_interface_object_get_instance_binding(singleton_obj, internal::token, &{ class_name } ::___binding_callbacks));"
1455
- )
1456
- result .append ("\t return singleton;" )
1457
- result .append ("}" )
1463
+ for i in range (2 if is_class_db else 1 ):
1464
+ if is_class_db :
1465
+ if i == 0 :
1466
+ result .append ("#define CLASSDB_GET_SINGLETON_DEBUG()" )
1467
+ else :
1468
+ result .append ("#define CLASSDB_GET_SINGLETON()" )
1469
+
1470
+ result .append (f"{ class_name } *{ class_name } ::get_singleton() {{" )
1471
+ result .append (f"\t const StringName __class_name = { class_name } ::get_class_static();" )
1472
+ result .append (
1473
+ "\t static GDExtensionObjectPtr singleton_obj = internal::gdextension_interface_global_get_singleton(__class_name._native_ptr());" ,
1474
+ )
1475
+ if not is_class_db :
1476
+ result .append ("#ifdef DEBUG_ENABLED" )
1477
+ if i == 0 :
1478
+ result .append ("\t ERR_FAIL_COND_V(singleton_obj == nullptr, nullptr);" )
1479
+ if not is_class_db :
1480
+ result .append ("#endif // DEBUG_ENABLED" )
1481
+
1482
+ result .append (
1483
+ f"\t static { class_name } *singleton = reinterpret_cast<{ class_name } *>(internal::gdextension_interface_object_get_instance_binding(singleton_obj, internal::token, &{ class_name } ::___binding_callbacks));" ,
1484
+ )
1485
+ result .append ("\t return singleton;" )
1486
+ result .append ("}" )
1487
+
1488
+ if is_class_db :
1489
+ result .append (
1490
+ get_class_db_macro_end_mark_line (
1491
+ "CLASSDB_GET_SINGLETON_DEBUG" if i == 0 else "CLASSDB_GET_SINGLETON"
1492
+ )
1493
+ )
1494
+ result .append ("" )
1495
+
1458
1496
result .append ("" )
1459
1497
1498
+ if is_class_db :
1499
+ result .append ("#define CLASSDB_SOURCE_IMPLEMENT()" )
1460
1500
if "methods" in class_api :
1461
1501
for method in class_api ["methods" ]:
1462
1502
if method ["is_virtual" ]:
@@ -1569,9 +1609,15 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
1569
1609
result .append (method_signature )
1570
1610
result .append ("" )
1571
1611
1612
+ if is_class_db :
1613
+ result .append (get_class_db_macro_end_mark_line ("CLASSDB_SOURCE_IMPLEMENT" ))
1614
+
1572
1615
result .append ("" )
1573
1616
result .append ("} // namespace godot " )
1574
1617
1618
+ if is_class_db :
1619
+ append_backslash_to_class_db_macro (result )
1620
+
1575
1621
return "\n " .join (result )
1576
1622
1577
1623
@@ -2275,6 +2321,7 @@ def escape_identifier(id):
2275
2321
"operator" : "_operator" ,
2276
2322
"typeof" : "type_of" ,
2277
2323
"typename" : "type_name" ,
2324
+ "enum" : "_enum" ,
2278
2325
}
2279
2326
if id in cpp_keywords_map :
2280
2327
return cpp_keywords_map [id ]
@@ -2376,3 +2423,60 @@ def add_header(filename, lines):
2376
2423
2377
2424
lines .append ("// THIS FILE IS GENERATED. EDITS WILL BE LOST." )
2378
2425
lines .append ("" )
2426
+
2427
+
2428
+ def calculate_line_length_with_table (line , tab_len ):
2429
+ tabs_len = 0
2430
+ for c in line :
2431
+ if c == "\t " :
2432
+ tabs_len += tab_len - 1
2433
+ return len (line ) + tabs_len
2434
+
2435
+
2436
+ def add_backslash (line , backslash_pos , tab_len ):
2437
+ for i in range (calculate_line_length_with_table (line , tab_len ), backslash_pos ):
2438
+ line += " "
2439
+ return line + "\\ "
2440
+
2441
+
2442
+ def get_class_db_macro_end_mark ():
2443
+ return "// Macro end mark: "
2444
+
2445
+
2446
+ def get_class_db_macro_end_mark_line (macros_name ):
2447
+ return get_class_db_macro_end_mark () + macros_name
2448
+
2449
+
2450
+ def append_backslash_to_class_db_macro (lines , tab_len = 4 ):
2451
+ for i in range (len (lines )):
2452
+ if lines [i ].startswith ("#define CLASSDB_" ):
2453
+ # Find finish mark.
2454
+ finish_mark_line = - 1
2455
+ for j in range (i , len (lines )):
2456
+ if lines [j ].startswith (get_class_db_macro_end_mark ()):
2457
+ finish_mark_line = j
2458
+ break
2459
+ if finish_mark_line - i < 2 :
2460
+ continue
2461
+
2462
+ # Find backslash slash end line.
2463
+ backslash_end_line = finish_mark_line - 1
2464
+ for j in range (backslash_end_line , i , - 1 ):
2465
+ if lines [j ] != "" :
2466
+ backslash_end_line = j
2467
+ break
2468
+
2469
+ # Find the max length of code block.
2470
+ backslash_pos = 0
2471
+ for j in range (i , backslash_end_line ):
2472
+ length = calculate_line_length_with_table (lines [j ], tab_len )
2473
+ if length > backslash_pos :
2474
+ backslash_pos = length
2475
+ backslash_pos += tab_len - (backslash_pos % tab_len )
2476
+
2477
+ # Add backslash.
2478
+ for j in range (i , backslash_end_line ):
2479
+ lines [j ] = add_backslash (lines [j ], backslash_pos , tab_len )
2480
+
2481
+ # Skip to finish mark line.
2482
+ i = finish_mark_line
0 commit comments