Make NavigationServer backend engine selectable

Adds engine backend selection for NavigationServers, aka allows to swap navigation module for other backend implementations.
This commit is contained in:
smix8
2025-04-30 00:27:39 +02:00
parent 07f4c06601
commit 419fc6e22d
13 changed files with 439 additions and 54 deletions

View File

@ -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<NavigationServer2D>(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<NavigationServer2D>(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);
}

View File

@ -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<ClassInfo> 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);

View File

@ -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<NavigationServer3D>(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<NavigationServer3D>(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);
}

View File

@ -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<ClassInfo> 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);

View File

@ -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