From 419fc6e22d6081b156834487a8ae78934c9e8ff9 Mon Sep 17 00:00:00 2001 From: smix8 <52464204+smix8@users.noreply.github.com> Date: Wed, 30 Apr 2025 00:27:39 +0200 Subject: [PATCH] Make NavigationServer backend engine selectable Adds engine backend selection for NavigationServers, aka allows to swap navigation module for other backend implementations. --- doc/classes/@GlobalScope.xml | 6 + doc/classes/NavigationServer2DManager.xml | 30 ++++ doc/classes/NavigationServer3DManager.xml | 30 ++++ doc/classes/ProjectSettings.xml | 14 ++ main/main.cpp | 18 +++ modules/navigation_2d/register_types.cpp | 5 +- modules/navigation_3d/register_types.cpp | 5 +- .../navigation_2d/navigation_server_2d.cpp | 128 +++++++++++++++--- servers/navigation_2d/navigation_server_2d.h | 57 +++++++- .../navigation_3d/navigation_server_3d.cpp | 121 ++++++++++++++--- servers/navigation_3d/navigation_server_3d.h | 57 +++++++- servers/register_server_types.cpp | 14 ++ tests/test_main.cpp | 8 +- 13 files changed, 439 insertions(+), 54 deletions(-) create mode 100644 doc/classes/NavigationServer2DManager.xml create mode 100644 doc/classes/NavigationServer3DManager.xml diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 66d31907bd5..bc5a969ba2b 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1608,9 +1608,15 @@ The [NavigationServer2D] singleton. + + The [NavigationServer2DManager] singleton. + The [NavigationServer3D] singleton. + + The [NavigationServer3DManager] singleton. + The [OS] singleton. diff --git a/doc/classes/NavigationServer2DManager.xml b/doc/classes/NavigationServer2DManager.xml new file mode 100644 index 00000000000..7b62e9b7464 --- /dev/null +++ b/doc/classes/NavigationServer2DManager.xml @@ -0,0 +1,30 @@ + + + + A singleton for managing [NavigationServer2D] implementations. + + + [NavigationServer2DManager] is the API for registering [NavigationServer2D] implementations and setting the default implementation. + [b]Note:[/b] It is not possible to switch servers at runtime. This class is only used on startup at the server initialization level. + + + + + + + + + + Registers a [NavigationServer2D] implementation by passing a [param name] and a [Callable] that returns a [NavigationServer2D] object. + + + + + + + + Sets the default [NavigationServer2D] implementation to the one identified by [param name], if [param priority] is greater than the priority of the current default implementation. + + + + diff --git a/doc/classes/NavigationServer3DManager.xml b/doc/classes/NavigationServer3DManager.xml new file mode 100644 index 00000000000..7e853f9ff4c --- /dev/null +++ b/doc/classes/NavigationServer3DManager.xml @@ -0,0 +1,30 @@ + + + + A singleton for managing [NavigationServer3D] implementations. + + + [NavigationServer3DManager] is the API for registering [NavigationServer3D] implementations and setting the default implementation. + [b]Note:[/b] It is not possible to switch servers at runtime. This class is only used on startup at the server initialization level. + + + + + + + + + + Registers a [NavigationServer3D] implementation by passing a [param name] and a [Callable] that returns a [NavigationServer3D] object. + + + + + + + + Sets the default [NavigationServer3D] implementation to the one identified by [param name], if [param priority] is greater than the priority of the current default implementation. + + + + diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 647aa20b992..d74b67cae02 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2348,6 +2348,13 @@ Default merge rasterizer cell scale for 2D navigation maps. See [method NavigationServer2D.map_set_merge_rasterizer_cell_scale]. + + Sets which navigation engine to use for 2D navigation. + [b]DEFAULT[/b] is equivalent to [b]GodotNavigation2D[/b], but may change in future releases. Select an explicit implementation if you want to ensure that your project stays on the same engine. + [b]GodotNavigation2D[/b] is Godot's internal 2D navigation engine. + [b]Dummy[/b] is a 2D navigation server that does nothing and returns only dummy values, effectively disabling all 2D navigation functionality. + Third-party modules can add other navigation engines to select with this setting. + If enabled 2D navigation regions will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. This setting only affects World2D default navigation maps. @@ -2375,6 +2382,13 @@ Default merge rasterizer cell scale for 3D navigation maps. See [method NavigationServer3D.map_set_merge_rasterizer_cell_scale]. + + Sets which navigation engine to use for 3D navigation. + [b]DEFAULT[/b] is equivalent to [b]GodotNavigation3D[/b], but may change in future releases. Select an explicit implementation if you want to ensure that your project stays on the same engine. + [b]GodotNavigation3D[/b] is Godot's internal 3D navigation engine. + [b]Dummy[/b] is a 3D navigation server that does nothing and returns only dummy values, effectively disabling all 3D navigation functionality. + Third-party modules can add other navigation engines to select with this setting. + If enabled 3D navigation regions will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. This setting only affects World3D default navigation maps. diff --git a/main/main.cpp b/main/main.cpp index de713937ef3..1dc73e60040 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -746,6 +746,13 @@ Error Main::test_setup() { physics_server_2d_manager = memnew(PhysicsServer2DManager); #endif // PHYSICS_2D_DISABLED +#ifndef NAVIGATION_2D_DISABLED + NavigationServer2DManager::initialize_server_manager(); +#endif // NAVIGATION_2D_DISABLED +#ifndef NAVIGATION_3D_DISABLED + NavigationServer3DManager::initialize_server_manager(); +#endif // NAVIGATION_3D_DISABLED + // From `Main::setup2()`. register_early_core_singletons(); initialize_modules(MODULE_INITIALIZATION_LEVEL_CORE); @@ -863,9 +870,11 @@ void Main::test_cleanup() { #ifndef NAVIGATION_2D_DISABLED NavigationServer2DManager::finalize_server(); + NavigationServer2DManager::finalize_server_manager(); #endif // NAVIGATION_2D_DISABLED #ifndef NAVIGATION_3D_DISABLED NavigationServer3DManager::finalize_server(); + NavigationServer3DManager::finalize_server_manager(); #endif // NAVIGATION_3D_DISABLED GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS); @@ -3018,6 +3027,13 @@ Error Main::setup2(bool p_show_boot_logo) { physics_server_2d_manager = memnew(PhysicsServer2DManager); #endif // PHYSICS_2D_DISABLED +#ifndef NAVIGATION_2D_DISABLED + NavigationServer2DManager::initialize_server_manager(); +#endif // NAVIGATION_2D_DISABLED +#ifndef NAVIGATION_3D_DISABLED + NavigationServer3DManager::initialize_server_manager(); +#endif // NAVIGATION_3D_DISABLED + register_server_types(); { OS::get_singleton()->benchmark_begin_measure("Servers", "Modules and Extensions"); @@ -4979,9 +4995,11 @@ void Main::cleanup(bool p_force) { // Before deinitializing server extensions, finalize servers which may be loaded as extensions. #ifndef NAVIGATION_2D_DISABLED NavigationServer2DManager::finalize_server(); + NavigationServer2DManager::finalize_server_manager(); #endif // NAVIGATION_2D_DISABLED #ifndef NAVIGATION_3D_DISABLED NavigationServer3DManager::finalize_server(); + NavigationServer3DManager::finalize_server_manager(); #endif // NAVIGATION_3D_DISABLED finalize_physics(); diff --git a/modules/navigation_2d/register_types.cpp b/modules/navigation_2d/register_types.cpp index bffd79b3dab..3fa091653fc 100644 --- a/modules/navigation_2d/register_types.cpp +++ b/modules/navigation_2d/register_types.cpp @@ -41,13 +41,14 @@ #include "editor/navigation_region_2d_editor_plugin.h" #endif -NavigationServer2D *new_navigation_server_2d() { +static NavigationServer2D *_createGodotNavigation2DCallback() { return memnew(GodotNavigationServer2D); } void initialize_navigation_2d_module(ModuleInitializationLevel p_level) { if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) { - NavigationServer2DManager::set_default_server(new_navigation_server_2d); + NavigationServer2DManager::get_singleton()->register_server("GodotNavigation2D", callable_mp_static(_createGodotNavigation2DCallback)); + NavigationServer2DManager::get_singleton()->set_default_server("GodotNavigation2D"); } #ifdef TOOLS_ENABLED diff --git a/modules/navigation_3d/register_types.cpp b/modules/navigation_3d/register_types.cpp index 644e6f3e6fa..e2ccd21bb99 100644 --- a/modules/navigation_3d/register_types.cpp +++ b/modules/navigation_3d/register_types.cpp @@ -49,13 +49,14 @@ NavigationMeshGenerator *_nav_mesh_generator = nullptr; #endif // DISABLE_DEPRECATED -NavigationServer3D *new_navigation_server_3d() { +static NavigationServer3D *_createGodotNavigation3DCallback() { return memnew(GodotNavigationServer3D); } void initialize_navigation_3d_module(ModuleInitializationLevel p_level) { if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) { - NavigationServer3DManager::set_default_server(new_navigation_server_3d); + NavigationServer3DManager::get_singleton()->register_server("GodotNavigation3D", callable_mp_static(_createGodotNavigation3DCallback)); + NavigationServer3DManager::get_singleton()->set_default_server("GodotNavigation3D"); #ifndef DISABLE_DEPRECATED _nav_mesh_generator = memnew(NavigationMeshGenerator); diff --git a/servers/navigation_2d/navigation_server_2d.cpp b/servers/navigation_2d/navigation_server_2d.cpp index 6f7f735440f..e462d1122ea 100644 --- a/servers/navigation_2d/navigation_server_2d.cpp +++ b/servers/navigation_2d/navigation_server_2d.cpp @@ -256,8 +256,8 @@ NavigationServer2D::NavigationServer2D() { debug_navigation_avoidance_enable_obstacles_static = GLOBAL_DEF("debug/shapes/avoidance/2d/enable_obstacles_static", true); if (Engine::get_singleton()->is_editor_hint()) { - // enable NavigationServer3D when in Editor or else navigation mesh edge connections are invisible - // on runtime tests SceneTree has "Visible Navigation" set and main iteration takes care of this. + // Enable NavigationServer2D when in Editor or navigation mesh edge connections are invisible. + // On runtime tests SceneTree has "Visible Navigation" set and main iteration takes care of this. set_debug_enabled(true); set_debug_navigation_enabled(true); set_debug_avoidance_enabled(true); @@ -550,30 +550,24 @@ bool NavigationServer2D::get_debug_navigation_avoidance_enable_obstacles_static( static NavigationServer2D *navigation_server_2d = nullptr; -NavigationServer2DCallback NavigationServer2DManager::create_callback = nullptr; - -void NavigationServer2DManager::set_default_server(NavigationServer2DCallback p_callback) { - create_callback = p_callback; -} - -NavigationServer2D *NavigationServer2DManager::new_default_server() { - if (create_callback == nullptr) { - return nullptr; - } - - return create_callback(); -} - void NavigationServer2DManager::initialize_server() { ERR_FAIL_COND(navigation_server_2d != nullptr); - // Init 2D Navigation Server - navigation_server_2d = NavigationServer2DManager::new_default_server(); + // Init 2D Navigation Server. + navigation_server_2d = NavigationServer2DManager::get_singleton()->new_server( + GLOBAL_GET(NavigationServer2DManager::setting_property_name)); + if (!navigation_server_2d) { + // Navigation server not found, use the default. + navigation_server_2d = NavigationServer2DManager::get_singleton()->new_default_server(); + } + + // Fall back to dummy if no default server has been registered. if (!navigation_server_2d) { WARN_VERBOSE("Failed to initialize NavigationServer2D. Fall back to dummy server."); navigation_server_2d = memnew(NavigationServer2DDummy); } + // Should be impossible, but make sure it's not null. ERR_FAIL_NULL_MSG(navigation_server_2d, "Failed to initialize NavigationServer2D."); navigation_server_2d->init(); } @@ -584,3 +578,101 @@ void NavigationServer2DManager::finalize_server() { memdelete(navigation_server_2d); navigation_server_2d = nullptr; } + +const String NavigationServer2DManager::setting_property_name(PNAME("navigation/2d/navigation_engine")); + +void NavigationServer2DManager::on_servers_changed() { + String navigation_servers_enum_str("DEFAULT"); + for (int i = get_servers_count() - 1; 0 <= i; --i) { + navigation_servers_enum_str += "," + get_server_name(i); + } + ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, setting_property_name, PROPERTY_HINT_ENUM, navigation_servers_enum_str)); + ProjectSettings::get_singleton()->set_restart_if_changed(setting_property_name, true); + ProjectSettings::get_singleton()->set_as_basic(setting_property_name, true); +} + +void NavigationServer2DManager::_bind_methods() { + ClassDB::bind_method(D_METHOD("register_server", "name", "create_callback"), &NavigationServer2DManager::register_server); + ClassDB::bind_method(D_METHOD("set_default_server", "name", "priority"), &NavigationServer2DManager::set_default_server); +} + +NavigationServer2DManager *NavigationServer2DManager::get_singleton() { + return singleton; +} + +void NavigationServer2DManager::register_server(const String &p_name, const Callable &p_create_callback) { + ERR_FAIL_COND(find_server_id(p_name) != -1); + navigation_servers.push_back(ClassInfo(p_name, p_create_callback)); + on_servers_changed(); +} + +void NavigationServer2DManager::set_default_server(const String &p_name, int p_priority) { + const int id = find_server_id(p_name); + ERR_FAIL_COND(id == -1); // Not found + if (default_server_priority < p_priority) { + default_server_id = id; + default_server_priority = p_priority; + } +} + +int NavigationServer2DManager::find_server_id(const String &p_name) { + for (int i = navigation_servers.size() - 1; 0 <= i; --i) { + if (p_name == navigation_servers[i].name) { + return i; + } + } + return -1; +} + +int NavigationServer2DManager::get_servers_count() { + return navigation_servers.size(); +} + +String NavigationServer2DManager::get_server_name(int p_id) { + ERR_FAIL_INDEX_V(p_id, get_servers_count(), ""); + return navigation_servers[p_id].name; +} + +NavigationServer2D *NavigationServer2DManager::new_default_server() { + if (default_server_id == -1) { + return nullptr; + } + Variant ret; + Callable::CallError ce; + navigation_servers[default_server_id].create_callback.callp(nullptr, 0, ret, ce); + ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, nullptr); + return Object::cast_to(ret.get_validated_object()); +} + +NavigationServer2D *NavigationServer2DManager::new_server(const String &p_name) { + int id = find_server_id(p_name); + if (id == -1) { + return nullptr; + } else { + Variant ret; + Callable::CallError ce; + navigation_servers[id].create_callback.callp(nullptr, 0, ret, ce); + ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, nullptr); + return Object::cast_to(ret.get_validated_object()); + } +} + +NavigationServer2D *NavigationServer2DManager::create_dummy_server_callback() { + return memnew(NavigationServer2DDummy); +} + +NavigationServer2DManager::NavigationServer2DManager() { +} + +NavigationServer2DManager::~NavigationServer2DManager() { +} + +void NavigationServer2DManager::initialize_server_manager() { + ERR_FAIL_COND(singleton != nullptr); + singleton = memnew(NavigationServer2DManager); +} + +void NavigationServer2DManager::finalize_server_manager() { + ERR_FAIL_NULL(singleton); + memdelete(singleton); +} diff --git a/servers/navigation_2d/navigation_server_2d.h b/servers/navigation_2d/navigation_server_2d.h index 5c6c43af704..9b637d59c9a 100644 --- a/servers/navigation_2d/navigation_server_2d.h +++ b/servers/navigation_2d/navigation_server_2d.h @@ -431,18 +431,63 @@ public: #endif // DEBUG_ENABLED }; -typedef NavigationServer2D *(*NavigationServer2DCallback)(); - /// Manager used for the server singleton registration -class NavigationServer2DManager { - static NavigationServer2DCallback create_callback; +class NavigationServer2DManager : public Object { + GDCLASS(NavigationServer2DManager, Object); + + static inline NavigationServer2DManager *singleton = nullptr; + + struct ClassInfo { + String name; + Callable create_callback; + + ClassInfo() {} + + ClassInfo(const String &p_name, const Callable &p_create_callback) : + name(p_name), + create_callback(p_create_callback) {} + + ClassInfo(const ClassInfo &p_ci) : + name(p_ci.name), + create_callback(p_ci.create_callback) {} + + void operator=(const ClassInfo &p_ci) { + name = p_ci.name; + create_callback = p_ci.create_callback; + } + }; + + Vector navigation_servers; + int default_server_id = -1; + int default_server_priority = -1; + + void on_servers_changed(); + +protected: + static void _bind_methods(); public: - static void set_default_server(NavigationServer2DCallback p_callback); - static NavigationServer2D *new_default_server(); + static const String setting_property_name; + + static NavigationServer2DManager *get_singleton(); + + void register_server(const String &p_name, const Callable &p_create_callback); + void set_default_server(const String &p_name, int p_priority = 0); + int find_server_id(const String &p_name); + int get_servers_count(); + String get_server_name(int p_id); + NavigationServer2D *new_default_server(); + NavigationServer2D *new_server(const String &p_name); + + NavigationServer2DManager(); + ~NavigationServer2DManager(); static void initialize_server(); static void finalize_server(); + + static void initialize_server_manager(); + static void finalize_server_manager(); + static NavigationServer2D *create_dummy_server_callback(); }; VARIANT_ENUM_CAST(NavigationServer2D::ProcessInfo); diff --git a/servers/navigation_3d/navigation_server_3d.cpp b/servers/navigation_3d/navigation_server_3d.cpp index 3ef551e017a..ed0f05ef2f1 100644 --- a/servers/navigation_3d/navigation_server_3d.cpp +++ b/servers/navigation_3d/navigation_server_3d.cpp @@ -1033,25 +1033,16 @@ bool NavigationServer3D::get_debug_avoidance_enabled() const { static NavigationServer3D *navigation_server_3d = nullptr; -NavigationServer3DCallback NavigationServer3DManager::create_callback = nullptr; - -void NavigationServer3DManager::set_default_server(NavigationServer3DCallback p_callback) { - create_callback = p_callback; -} - -NavigationServer3D *NavigationServer3DManager::new_default_server() { - if (create_callback == nullptr) { - return nullptr; - } - - return create_callback(); -} - void NavigationServer3DManager::initialize_server() { ERR_FAIL_COND(navigation_server_3d != nullptr); - // Init 3D Navigation Server - navigation_server_3d = NavigationServer3DManager::new_default_server(); + // Init 3D Navigation Server. + navigation_server_3d = NavigationServer3DManager::get_singleton()->new_server( + GLOBAL_GET(NavigationServer3DManager::setting_property_name)); + if (!navigation_server_3d) { + // Navigation server not found, use the default. + navigation_server_3d = NavigationServer3DManager::get_singleton()->new_default_server(); + } // Fall back to dummy if no default server has been registered. if (!navigation_server_3d) { @@ -1070,3 +1061,101 @@ void NavigationServer3DManager::finalize_server() { memdelete(navigation_server_3d); navigation_server_3d = nullptr; } + +const String NavigationServer3DManager::setting_property_name(PNAME("navigation/3d/navigation_engine")); + +void NavigationServer3DManager::on_servers_changed() { + String navigation_servers_enum_str("DEFAULT"); + for (int i = get_servers_count() - 1; 0 <= i; --i) { + navigation_servers_enum_str += "," + get_server_name(i); + } + ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, setting_property_name, PROPERTY_HINT_ENUM, navigation_servers_enum_str)); + ProjectSettings::get_singleton()->set_restart_if_changed(setting_property_name, true); + ProjectSettings::get_singleton()->set_as_basic(setting_property_name, true); +} + +void NavigationServer3DManager::_bind_methods() { + ClassDB::bind_method(D_METHOD("register_server", "name", "create_callback"), &NavigationServer3DManager::register_server); + ClassDB::bind_method(D_METHOD("set_default_server", "name", "priority"), &NavigationServer3DManager::set_default_server); +} + +NavigationServer3DManager *NavigationServer3DManager::get_singleton() { + return singleton; +} + +void NavigationServer3DManager::register_server(const String &p_name, const Callable &p_create_callback) { + ERR_FAIL_COND(find_server_id(p_name) != -1); + navigation_servers.push_back(ClassInfo(p_name, p_create_callback)); + on_servers_changed(); +} + +void NavigationServer3DManager::set_default_server(const String &p_name, int p_priority) { + const int id = find_server_id(p_name); + ERR_FAIL_COND(id == -1); // Not found + if (default_server_priority < p_priority) { + default_server_id = id; + default_server_priority = p_priority; + } +} + +int NavigationServer3DManager::find_server_id(const String &p_name) { + for (int i = navigation_servers.size() - 1; 0 <= i; --i) { + if (p_name == navigation_servers[i].name) { + return i; + } + } + return -1; +} + +int NavigationServer3DManager::get_servers_count() { + return navigation_servers.size(); +} + +String NavigationServer3DManager::get_server_name(int p_id) { + ERR_FAIL_INDEX_V(p_id, get_servers_count(), ""); + return navigation_servers[p_id].name; +} + +NavigationServer3D *NavigationServer3DManager::new_default_server() { + if (default_server_id == -1) { + return nullptr; + } + Variant ret; + Callable::CallError ce; + navigation_servers[default_server_id].create_callback.callp(nullptr, 0, ret, ce); + ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, nullptr); + return Object::cast_to(ret.get_validated_object()); +} + +NavigationServer3D *NavigationServer3DManager::new_server(const String &p_name) { + int id = find_server_id(p_name); + if (id == -1) { + return nullptr; + } else { + Variant ret; + Callable::CallError ce; + navigation_servers[id].create_callback.callp(nullptr, 0, ret, ce); + ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, nullptr); + return Object::cast_to(ret.get_validated_object()); + } +} + +NavigationServer3D *NavigationServer3DManager::create_dummy_server_callback() { + return memnew(NavigationServer3DDummy); +} + +NavigationServer3DManager::NavigationServer3DManager() { +} + +NavigationServer3DManager::~NavigationServer3DManager() { +} + +void NavigationServer3DManager::initialize_server_manager() { + ERR_FAIL_COND(singleton != nullptr); + singleton = memnew(NavigationServer3DManager); +} + +void NavigationServer3DManager::finalize_server_manager() { + ERR_FAIL_NULL(singleton); + memdelete(singleton); +} diff --git a/servers/navigation_3d/navigation_server_3d.h b/servers/navigation_3d/navigation_server_3d.h index 0fba3665e3a..cb02194a15e 100644 --- a/servers/navigation_3d/navigation_server_3d.h +++ b/servers/navigation_3d/navigation_server_3d.h @@ -519,18 +519,63 @@ public: #endif // DEBUG_ENABLED }; -typedef NavigationServer3D *(*NavigationServer3DCallback)(); - /// Manager used for the server singleton registration -class NavigationServer3DManager { - static NavigationServer3DCallback create_callback; +class NavigationServer3DManager : public Object { + GDCLASS(NavigationServer3DManager, Object); + + static inline NavigationServer3DManager *singleton = nullptr; + + struct ClassInfo { + String name; + Callable create_callback; + + ClassInfo() {} + + ClassInfo(const String &p_name, const Callable &p_create_callback) : + name(p_name), + create_callback(p_create_callback) {} + + ClassInfo(const ClassInfo &p_ci) : + name(p_ci.name), + create_callback(p_ci.create_callback) {} + + void operator=(const ClassInfo &p_ci) { + name = p_ci.name; + create_callback = p_ci.create_callback; + } + }; + + Vector navigation_servers; + int default_server_id = -1; + int default_server_priority = -1; + + void on_servers_changed(); + +protected: + static void _bind_methods(); public: - static void set_default_server(NavigationServer3DCallback p_callback); - static NavigationServer3D *new_default_server(); + static const String setting_property_name; + + static NavigationServer3DManager *get_singleton(); + + void register_server(const String &p_name, const Callable &p_create_callback); + void set_default_server(const String &p_name, int p_priority = 0); + int find_server_id(const String &p_name); + int get_servers_count(); + String get_server_name(int p_id); + NavigationServer3D *new_default_server(); + NavigationServer3D *new_server(const String &p_name); + + NavigationServer3DManager(); + ~NavigationServer3DManager(); static void initialize_server(); static void finalize_server(); + + static void initialize_server_manager(); + static void finalize_server_manager(); + static NavigationServer3D *create_dummy_server_callback(); }; VARIANT_ENUM_CAST(NavigationServer3D::ProcessInfo); diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index 5bdd02734f5..bee17012578 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -261,9 +261,16 @@ void register_server_types() { ServersDebugger::initialize(); #ifndef NAVIGATION_2D_DISABLED + GDREGISTER_CLASS(NavigationServer2DManager); + Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer2DManager", NavigationServer2DManager::get_singleton(), "NavigationServer2DManager")); + GDREGISTER_ABSTRACT_CLASS(NavigationServer2D); GDREGISTER_CLASS(NavigationPathQueryParameters2D); GDREGISTER_CLASS(NavigationPathQueryResult2D); + + GLOBAL_DEF(PropertyInfo(Variant::STRING, NavigationServer2DManager::setting_property_name, PROPERTY_HINT_ENUM, "DEFAULT"), "DEFAULT"); + + NavigationServer2DManager::get_singleton()->register_server("Dummy", callable_mp_static(NavigationServer2DManager::create_dummy_server_callback)); #endif // NAVIGATION_2D_DISABLED #ifndef PHYSICS_2D_DISABLED @@ -295,9 +302,16 @@ void register_server_types() { #endif // PHYSICS_2D_DISABLED #ifndef NAVIGATION_3D_DISABLED + GDREGISTER_CLASS(NavigationServer3DManager); + Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3DManager", NavigationServer3DManager::get_singleton(), "NavigationServer3DManager")); + GDREGISTER_ABSTRACT_CLASS(NavigationServer3D); GDREGISTER_CLASS(NavigationPathQueryParameters3D); GDREGISTER_CLASS(NavigationPathQueryResult3D); + + GLOBAL_DEF(PropertyInfo(Variant::STRING, NavigationServer3DManager::setting_property_name, PROPERTY_HINT_ENUM, "DEFAULT"), "DEFAULT"); + + NavigationServer3DManager::get_singleton()->register_server("Dummy", callable_mp_static(NavigationServer3DManager::create_dummy_server_callback)); #endif // NAVIGATION_3D_DISABLED #ifndef PHYSICS_3D_DISABLED diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 5788492b876..5951aa1c6eb 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -368,10 +368,10 @@ struct GodotTestCaseListener : public doctest::IReporter { ERR_PRINT_OFF; #ifndef NAVIGATION_3D_DISABLED - navigation_server_3d = NavigationServer3DManager::new_default_server(); + navigation_server_3d = NavigationServer3DManager::get_singleton()->new_default_server(); #endif // NAVIGATION_3D_DISABLED #ifndef NAVIGATION_2D_DISABLED - navigation_server_2d = NavigationServer2DManager::new_default_server(); + navigation_server_2d = NavigationServer2DManager::get_singleton()->new_default_server(); #endif // NAVIGATION_2D_DISABLED ERR_PRINT_ON; @@ -407,7 +407,7 @@ struct GodotTestCaseListener : public doctest::IReporter { #ifndef NAVIGATION_3D_DISABLED if (suite_name.contains("[Navigation3D]") && navigation_server_3d == nullptr) { ERR_PRINT_OFF; - navigation_server_3d = NavigationServer3DManager::new_default_server(); + navigation_server_3d = NavigationServer3DManager::get_singleton()->new_default_server(); ERR_PRINT_ON; return; } @@ -416,7 +416,7 @@ struct GodotTestCaseListener : public doctest::IReporter { #ifndef NAVIGATION_2D_DISABLED if (suite_name.contains("[Navigation2D]") && navigation_server_2d == nullptr) { ERR_PRINT_OFF; - navigation_server_2d = NavigationServer2DManager::new_default_server(); + navigation_server_2d = NavigationServer2DManager::get_singleton()->new_default_server(); ERR_PRINT_ON; return; }