diff --git a/binding_generator.py b/binding_generator.py index 1a401a3b8..1be3624e7 100644 --- a/binding_generator.py +++ b/binding_generator.py @@ -1953,6 +1953,9 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us "\tGroupID add_native_group_task(void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String());" ) + if class_name == "SceneTree": + result.append("\tstatic SceneTree *get_singleton();") + if class_name == "Object": result.append("\ttemplate ") result.append("\tstatic T *cast_to(Object *p_object);") diff --git a/src/classes/low_level.cpp b/src/classes/low_level.cpp index a1e625e29..562c97c77 100644 --- a/src/classes/low_level.cpp +++ b/src/classes/low_level.cpp @@ -35,6 +35,17 @@ #include +// Unlike the classes above, `Engine` and `SceneTree` aren't always generated, since they may be left out of a build profile. +// Only define `SceneTree::get_singleton()` when both of them are available, so that build profiles don't have to include them. +#if __has_include() && __has_include() +#define GODOT_CPP_SCENE_TREE_SINGLETON + +#include +#include + +#include +#endif + namespace godot { Error XMLParser::_open_buffer(const uint8_t *p_buffer, size_t p_size) { return (Error)::godot::gdextension_interface::xml_parser_open_buffer(_owner, p_buffer, p_size); @@ -64,4 +75,10 @@ const uint8_t *Image::ptr() { return ::godot::gdextension_interface::image_ptr(_owner); } +#ifdef GODOT_CPP_SCENE_TREE_SINGLETON +SceneTree *SceneTree::get_singleton() { + return Object::cast_to(Engine::get_singleton()->get_main_loop()); +} +#endif + } // namespace godot diff --git a/test/build_profile.json b/test/build_profile.json index b4de43479..54bd2d6a1 100644 --- a/test/build_profile.json +++ b/test/build_profile.json @@ -1,11 +1,13 @@ { "enabled_classes": [ "Control", + "Engine", "InputEventKey", "Label", "MultiplayerAPI", "MultiplayerPeer", "OS", + "SceneTree", "TileMap", "TileSet", "Tween", diff --git a/test/project/main.gd b/test/project/main.gd index 52d7c0f13..d002bccc5 100644 --- a/test/project/main.gd +++ b/test/project/main.gd @@ -279,6 +279,9 @@ func _ready(): # Test that we can access an engine singleton. assert_equal(example.test_use_engine_singleton(), OS.get_name()) + # Test that we can access the SceneTree singleton. + assert_equal(example.test_use_scene_tree_singleton(), true) + if godot_target_version["minor"] >= 4: assert_equal(example.test_get_internal(1), 1) assert_equal(example.test_get_internal(true), -1) diff --git a/test/src/example.cpp b/test/src/example.cpp index 43f781c4e..0f42bf176 100644 --- a/test/src/example.cpp +++ b/test/src/example.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include using namespace godot; @@ -266,6 +267,7 @@ void Example::_bind_methods() { GDVIRTUAL_BIND(_do_something_virtual_with_control, "control"); ClassDB::bind_method(D_METHOD("test_use_engine_singleton"), &Example::test_use_engine_singleton); + ClassDB::bind_method(D_METHOD("test_use_scene_tree_singleton"), &Example::test_use_scene_tree_singleton); ClassDB::bind_method(D_METHOD("test_get_internal_class"), &Example::test_get_internal_class); @@ -759,6 +761,11 @@ String Example::test_use_engine_singleton() const { return OS::get_singleton()->get_name(); } +bool Example::test_use_scene_tree_singleton() const { + SceneTree *scene_tree = SceneTree::get_singleton(); + return scene_tree != nullptr && scene_tree == get_tree(); +} + String Example::test_library_path() { String library_path; ::godot::gdextension_interface::get_library_path(::godot::gdextension_interface::library, library_path._native_ptr()); diff --git a/test/src/example.h b/test/src/example.h index d75abbd8f..82361fe7d 100644 --- a/test/src/example.h +++ b/test/src/example.h @@ -214,6 +214,7 @@ class Example : public Control { GDVIRTUAL1(_do_something_virtual_with_control, Control *); String test_use_engine_singleton() const; + bool test_use_scene_tree_singleton() const; static String test_library_path();