Skip to content

Commit 419fc6e

Browse files
committed
Make NavigationServer backend engine selectable
Adds engine backend selection for NavigationServers, aka allows to swap navigation module for other backend implementations.
1 parent 07f4c06 commit 419fc6e

File tree

13 files changed

+439
-54
lines changed

13 files changed

+439
-54
lines changed

doc/classes/@GlobalScope.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,9 +1608,15 @@
16081608
<member name="NavigationServer2D" type="NavigationServer2D" setter="" getter="">
16091609
The [NavigationServer2D] singleton.
16101610
</member>
1611+
<member name="NavigationServer2DManager" type="NavigationServer2DManager" setter="" getter="">
1612+
The [NavigationServer2DManager] singleton.
1613+
</member>
16111614
<member name="NavigationServer3D" type="NavigationServer3D" setter="" getter="">
16121615
The [NavigationServer3D] singleton.
16131616
</member>
1617+
<member name="NavigationServer3DManager" type="NavigationServer3DManager" setter="" getter="">
1618+
The [NavigationServer3DManager] singleton.
1619+
</member>
16141620
<member name="OS" type="OS" setter="" getter="">
16151621
The [OS] singleton.
16161622
</member>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="NavigationServer2DManager" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
3+
<brief_description>
4+
A singleton for managing [NavigationServer2D] implementations.
5+
</brief_description>
6+
<description>
7+
[NavigationServer2DManager] is the API for registering [NavigationServer2D] implementations and setting the default implementation.
8+
[b]Note:[/b] It is not possible to switch servers at runtime. This class is only used on startup at the server initialization level.
9+
</description>
10+
<tutorials>
11+
</tutorials>
12+
<methods>
13+
<method name="register_server">
14+
<return type="void" />
15+
<param index="0" name="name" type="String" />
16+
<param index="1" name="create_callback" type="Callable" />
17+
<description>
18+
Registers a [NavigationServer2D] implementation by passing a [param name] and a [Callable] that returns a [NavigationServer2D] object.
19+
</description>
20+
</method>
21+
<method name="set_default_server">
22+
<return type="void" />
23+
<param index="0" name="name" type="String" />
24+
<param index="1" name="priority" type="int" />
25+
<description>
26+
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.
27+
</description>
28+
</method>
29+
</methods>
30+
</class>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="NavigationServer3DManager" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
3+
<brief_description>
4+
A singleton for managing [NavigationServer3D] implementations.
5+
</brief_description>
6+
<description>
7+
[NavigationServer3DManager] is the API for registering [NavigationServer3D] implementations and setting the default implementation.
8+
[b]Note:[/b] It is not possible to switch servers at runtime. This class is only used on startup at the server initialization level.
9+
</description>
10+
<tutorials>
11+
</tutorials>
12+
<methods>
13+
<method name="register_server">
14+
<return type="void" />
15+
<param index="0" name="name" type="String" />
16+
<param index="1" name="create_callback" type="Callable" />
17+
<description>
18+
Registers a [NavigationServer3D] implementation by passing a [param name] and a [Callable] that returns a [NavigationServer3D] object.
19+
</description>
20+
</method>
21+
<method name="set_default_server">
22+
<return type="void" />
23+
<param index="0" name="name" type="String" />
24+
<param index="1" name="priority" type="int" />
25+
<description>
26+
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.
27+
</description>
28+
</method>
29+
</methods>
30+
</class>

doc/classes/ProjectSettings.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2348,6 +2348,13 @@
23482348
<member name="navigation/2d/merge_rasterizer_cell_scale" type="float" setter="" getter="" default="1.0">
23492349
Default merge rasterizer cell scale for 2D navigation maps. See [method NavigationServer2D.map_set_merge_rasterizer_cell_scale].
23502350
</member>
2351+
<member name="navigation/2d/navigation_engine" type="String" setter="" getter="" default="&quot;DEFAULT&quot;">
2352+
Sets which navigation engine to use for 2D navigation.
2353+
[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.
2354+
[b]GodotNavigation2D[/b] is Godot's internal 2D navigation engine.
2355+
[b]Dummy[/b] is a 2D navigation server that does nothing and returns only dummy values, effectively disabling all 2D navigation functionality.
2356+
Third-party modules can add other navigation engines to select with this setting.
2357+
</member>
23512358
<member name="navigation/2d/use_edge_connections" type="bool" setter="" getter="" default="true">
23522359
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.
23532360
</member>
@@ -2375,6 +2382,13 @@
23752382
<member name="navigation/3d/merge_rasterizer_cell_scale" type="float" setter="" getter="" default="1.0">
23762383
Default merge rasterizer cell scale for 3D navigation maps. See [method NavigationServer3D.map_set_merge_rasterizer_cell_scale].
23772384
</member>
2385+
<member name="navigation/3d/navigation_engine" type="String" setter="" getter="" default="&quot;DEFAULT&quot;">
2386+
Sets which navigation engine to use for 3D navigation.
2387+
[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.
2388+
[b]GodotNavigation3D[/b] is Godot's internal 3D navigation engine.
2389+
[b]Dummy[/b] is a 3D navigation server that does nothing and returns only dummy values, effectively disabling all 3D navigation functionality.
2390+
Third-party modules can add other navigation engines to select with this setting.
2391+
</member>
23782392
<member name="navigation/3d/use_edge_connections" type="bool" setter="" getter="" default="true">
23792393
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.
23802394
</member>

main/main.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,13 @@ Error Main::test_setup() {
746746
physics_server_2d_manager = memnew(PhysicsServer2DManager);
747747
#endif // PHYSICS_2D_DISABLED
748748

749+
#ifndef NAVIGATION_2D_DISABLED
750+
NavigationServer2DManager::initialize_server_manager();
751+
#endif // NAVIGATION_2D_DISABLED
752+
#ifndef NAVIGATION_3D_DISABLED
753+
NavigationServer3DManager::initialize_server_manager();
754+
#endif // NAVIGATION_3D_DISABLED
755+
749756
// From `Main::setup2()`.
750757
register_early_core_singletons();
751758
initialize_modules(MODULE_INITIALIZATION_LEVEL_CORE);
@@ -863,9 +870,11 @@ void Main::test_cleanup() {
863870

864871
#ifndef NAVIGATION_2D_DISABLED
865872
NavigationServer2DManager::finalize_server();
873+
NavigationServer2DManager::finalize_server_manager();
866874
#endif // NAVIGATION_2D_DISABLED
867875
#ifndef NAVIGATION_3D_DISABLED
868876
NavigationServer3DManager::finalize_server();
877+
NavigationServer3DManager::finalize_server_manager();
869878
#endif // NAVIGATION_3D_DISABLED
870879

871880
GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
@@ -3018,6 +3027,13 @@ Error Main::setup2(bool p_show_boot_logo) {
30183027
physics_server_2d_manager = memnew(PhysicsServer2DManager);
30193028
#endif // PHYSICS_2D_DISABLED
30203029

3030+
#ifndef NAVIGATION_2D_DISABLED
3031+
NavigationServer2DManager::initialize_server_manager();
3032+
#endif // NAVIGATION_2D_DISABLED
3033+
#ifndef NAVIGATION_3D_DISABLED
3034+
NavigationServer3DManager::initialize_server_manager();
3035+
#endif // NAVIGATION_3D_DISABLED
3036+
30213037
register_server_types();
30223038
{
30233039
OS::get_singleton()->benchmark_begin_measure("Servers", "Modules and Extensions");
@@ -4979,9 +4995,11 @@ void Main::cleanup(bool p_force) {
49794995
// Before deinitializing server extensions, finalize servers which may be loaded as extensions.
49804996
#ifndef NAVIGATION_2D_DISABLED
49814997
NavigationServer2DManager::finalize_server();
4998+
NavigationServer2DManager::finalize_server_manager();
49824999
#endif // NAVIGATION_2D_DISABLED
49835000
#ifndef NAVIGATION_3D_DISABLED
49845001
NavigationServer3DManager::finalize_server();
5002+
NavigationServer3DManager::finalize_server_manager();
49855003
#endif // NAVIGATION_3D_DISABLED
49865004
finalize_physics();
49875005

modules/navigation_2d/register_types.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,14 @@
4141
#include "editor/navigation_region_2d_editor_plugin.h"
4242
#endif
4343

44-
NavigationServer2D *new_navigation_server_2d() {
44+
static NavigationServer2D *_createGodotNavigation2DCallback() {
4545
return memnew(GodotNavigationServer2D);
4646
}
4747

4848
void initialize_navigation_2d_module(ModuleInitializationLevel p_level) {
4949
if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
50-
NavigationServer2DManager::set_default_server(new_navigation_server_2d);
50+
NavigationServer2DManager::get_singleton()->register_server("GodotNavigation2D", callable_mp_static(_createGodotNavigation2DCallback));
51+
NavigationServer2DManager::get_singleton()->set_default_server("GodotNavigation2D");
5152
}
5253

5354
#ifdef TOOLS_ENABLED

modules/navigation_3d/register_types.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,14 @@
4949
NavigationMeshGenerator *_nav_mesh_generator = nullptr;
5050
#endif // DISABLE_DEPRECATED
5151

52-
NavigationServer3D *new_navigation_server_3d() {
52+
static NavigationServer3D *_createGodotNavigation3DCallback() {
5353
return memnew(GodotNavigationServer3D);
5454
}
5555

5656
void initialize_navigation_3d_module(ModuleInitializationLevel p_level) {
5757
if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
58-
NavigationServer3DManager::set_default_server(new_navigation_server_3d);
58+
NavigationServer3DManager::get_singleton()->register_server("GodotNavigation3D", callable_mp_static(_createGodotNavigation3DCallback));
59+
NavigationServer3DManager::get_singleton()->set_default_server("GodotNavigation3D");
5960

6061
#ifndef DISABLE_DEPRECATED
6162
_nav_mesh_generator = memnew(NavigationMeshGenerator);

servers/navigation_2d/navigation_server_2d.cpp

Lines changed: 110 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,8 @@ NavigationServer2D::NavigationServer2D() {
256256
debug_navigation_avoidance_enable_obstacles_static = GLOBAL_DEF("debug/shapes/avoidance/2d/enable_obstacles_static", true);
257257

258258
if (Engine::get_singleton()->is_editor_hint()) {
259-
// enable NavigationServer3D when in Editor or else navigation mesh edge connections are invisible
260-
// on runtime tests SceneTree has "Visible Navigation" set and main iteration takes care of this.
259+
// Enable NavigationServer2D when in Editor or navigation mesh edge connections are invisible.
260+
// On runtime tests SceneTree has "Visible Navigation" set and main iteration takes care of this.
261261
set_debug_enabled(true);
262262
set_debug_navigation_enabled(true);
263263
set_debug_avoidance_enabled(true);
@@ -550,30 +550,24 @@ bool NavigationServer2D::get_debug_navigation_avoidance_enable_obstacles_static(
550550

551551
static NavigationServer2D *navigation_server_2d = nullptr;
552552

553-
NavigationServer2DCallback NavigationServer2DManager::create_callback = nullptr;
554-
555-
void NavigationServer2DManager::set_default_server(NavigationServer2DCallback p_callback) {
556-
create_callback = p_callback;
557-
}
558-
559-
NavigationServer2D *NavigationServer2DManager::new_default_server() {
560-
if (create_callback == nullptr) {
561-
return nullptr;
562-
}
563-
564-
return create_callback();
565-
}
566-
567553
void NavigationServer2DManager::initialize_server() {
568554
ERR_FAIL_COND(navigation_server_2d != nullptr);
569555

570-
// Init 2D Navigation Server
571-
navigation_server_2d = NavigationServer2DManager::new_default_server();
556+
// Init 2D Navigation Server.
557+
navigation_server_2d = NavigationServer2DManager::get_singleton()->new_server(
558+
GLOBAL_GET(NavigationServer2DManager::setting_property_name));
559+
if (!navigation_server_2d) {
560+
// Navigation server not found, use the default.
561+
navigation_server_2d = NavigationServer2DManager::get_singleton()->new_default_server();
562+
}
563+
564+
// Fall back to dummy if no default server has been registered.
572565
if (!navigation_server_2d) {
573566
WARN_VERBOSE("Failed to initialize NavigationServer2D. Fall back to dummy server.");
574567
navigation_server_2d = memnew(NavigationServer2DDummy);
575568
}
576569

570+
// Should be impossible, but make sure it's not null.
577571
ERR_FAIL_NULL_MSG(navigation_server_2d, "Failed to initialize NavigationServer2D.");
578572
navigation_server_2d->init();
579573
}
@@ -584,3 +578,101 @@ void NavigationServer2DManager::finalize_server() {
584578
memdelete(navigation_server_2d);
585579
navigation_server_2d = nullptr;
586580
}
581+
582+
const String NavigationServer2DManager::setting_property_name(PNAME("navigation/2d/navigation_engine"));
583+
584+
void NavigationServer2DManager::on_servers_changed() {
585+
String navigation_servers_enum_str("DEFAULT");
586+
for (int i = get_servers_count() - 1; 0 <= i; --i) {
587+
navigation_servers_enum_str += "," + get_server_name(i);
588+
}
589+
ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, setting_property_name, PROPERTY_HINT_ENUM, navigation_servers_enum_str));
590+
ProjectSettings::get_singleton()->set_restart_if_changed(setting_property_name, true);
591+
ProjectSettings::get_singleton()->set_as_basic(setting_property_name, true);
592+
}
593+
594+
void NavigationServer2DManager::_bind_methods() {
595+
ClassDB::bind_method(D_METHOD("register_server", "name", "create_callback"), &NavigationServer2DManager::register_server);
596+
ClassDB::bind_method(D_METHOD("set_default_server", "name", "priority"), &NavigationServer2DManager::set_default_server);
597+
}
598+
599+
NavigationServer2DManager *NavigationServer2DManager::get_singleton() {
600+
return singleton;
601+
}
602+
603+
void NavigationServer2DManager::register_server(const String &p_name, const Callable &p_create_callback) {
604+
ERR_FAIL_COND(find_server_id(p_name) != -1);
605+
navigation_servers.push_back(ClassInfo(p_name, p_create_callback));
606+
on_servers_changed();
607+
}
608+
609+
void NavigationServer2DManager::set_default_server(const String &p_name, int p_priority) {
610+
const int id = find_server_id(p_name);
611+
ERR_FAIL_COND(id == -1); // Not found
612+
if (default_server_priority < p_priority) {
613+
default_server_id = id;
614+
default_server_priority = p_priority;
615+
}
616+
}
617+
618+
int NavigationServer2DManager::find_server_id(const String &p_name) {
619+
for (int i = navigation_servers.size() - 1; 0 <= i; --i) {
620+
if (p_name == navigation_servers[i].name) {
621+
return i;
622+
}
623+
}
624+
return -1;
625+
}
626+
627+
int NavigationServer2DManager::get_servers_count() {
628+
return navigation_servers.size();
629+
}
630+
631+
String NavigationServer2DManager::get_server_name(int p_id) {
632+
ERR_FAIL_INDEX_V(p_id, get_servers_count(), "");
633+
return navigation_servers[p_id].name;
634+
}
635+
636+
NavigationServer2D *NavigationServer2DManager::new_default_server() {
637+
if (default_server_id == -1) {
638+
return nullptr;
639+
}
640+
Variant ret;
641+
Callable::CallError ce;
642+
navigation_servers[default_server_id].create_callback.callp(nullptr, 0, ret, ce);
643+
ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, nullptr);
644+
return Object::cast_to<NavigationServer2D>(ret.get_validated_object());
645+
}
646+
647+
NavigationServer2D *NavigationServer2DManager::new_server(const String &p_name) {
648+
int id = find_server_id(p_name);
649+
if (id == -1) {
650+
return nullptr;
651+
} else {
652+
Variant ret;
653+
Callable::CallError ce;
654+
navigation_servers[id].create_callback.callp(nullptr, 0, ret, ce);
655+
ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, nullptr);
656+
return Object::cast_to<NavigationServer2D>(ret.get_validated_object());
657+
}
658+
}
659+
660+
NavigationServer2D *NavigationServer2DManager::create_dummy_server_callback() {
661+
return memnew(NavigationServer2DDummy);
662+
}
663+
664+
NavigationServer2DManager::NavigationServer2DManager() {
665+
}
666+
667+
NavigationServer2DManager::~NavigationServer2DManager() {
668+
}
669+
670+
void NavigationServer2DManager::initialize_server_manager() {
671+
ERR_FAIL_COND(singleton != nullptr);
672+
singleton = memnew(NavigationServer2DManager);
673+
}
674+
675+
void NavigationServer2DManager::finalize_server_manager() {
676+
ERR_FAIL_NULL(singleton);
677+
memdelete(singleton);
678+
}

0 commit comments

Comments
 (0)