From 17e6d76de62ec5a9a43c327925d18d027aa0eabc Mon Sep 17 00:00:00 2001 From: smix8 <52464204+smix8@users.noreply.github.com> Date: Mon, 5 Dec 2022 23:05:56 +0100 Subject: [PATCH] Fix Navigation agent callback wild pointer crash Fixes crash in sanitizer builds when callback agent or object are already freed. (cherry picked from commit 194c1c44e0a20faa4463e3a41bb12cf93a71fc03) --- doc/classes/Navigation2DServer.xml | 6 +++--- doc/classes/NavigationServer.xml | 6 +++--- modules/navigation/godot_navigation_server.cpp | 6 +++--- modules/navigation/godot_navigation_server.h | 2 +- scene/2d/navigation_agent_2d.cpp | 6 +++--- scene/3d/navigation_agent.cpp | 6 +++--- servers/navigation_2d_server.cpp | 12 ++++++------ servers/navigation_2d_server.h | 2 +- servers/navigation_server.cpp | 2 +- servers/navigation_server.h | 2 +- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/doc/classes/Navigation2DServer.xml b/doc/classes/Navigation2DServer.xml index cbb60ee7a22..2db55faf22d 100644 --- a/doc/classes/Navigation2DServer.xml +++ b/doc/classes/Navigation2DServer.xml @@ -40,12 +40,12 @@ - + - Callback called at the end of the RVO process. If a callback is created manually and the agent is placed on a navigation map it will calculate avoidance for the agent and dispatch the calculated [code]safe_velocity[/code] to the [code]receiver[/code] object with a signal to the chosen [code]method[/code] name. - [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]null[/code] object as the [code]receiver[/code]. + Sets the callback [code]object_id[/code] and [code]method[/code] that gets called after each avoidance processing step for the [code]agent[/code]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations. + [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]0[/code] ObjectID as the [code]object_id[/code]. diff --git a/doc/classes/NavigationServer.xml b/doc/classes/NavigationServer.xml index 663663898e9..9252e4566b0 100644 --- a/doc/classes/NavigationServer.xml +++ b/doc/classes/NavigationServer.xml @@ -40,12 +40,12 @@ - + - Callback called at the end of the RVO process. If a callback is created manually and the agent is placed on a navigation map it will calculate avoidance for the agent and dispatch the calculated [code]safe_velocity[/code] to the [code]receiver[/code] object with a signal to the chosen [code]method[/code] name. - [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]null[/code] object as the [code]receiver[/code]. + Sets the callback [code]object_id[/code] and [code]method[/code] that gets called after each avoidance processing step for the [code]agent[/code]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations. + [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]0[/code] ObjectID as the [code]object_id[/code]. diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp index d835cd13d6d..3267124ace9 100644 --- a/modules/navigation/godot_navigation_server.cpp +++ b/modules/navigation/godot_navigation_server.cpp @@ -537,14 +537,14 @@ bool GodotNavigationServer::agent_is_map_changed(RID p_agent) const { return agent->is_map_changed(); } -COMMAND_4(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_method, Variant, p_udata) { +COMMAND_4(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata) { RvoAgent *agent = agent_owner.getornull(p_agent); ERR_FAIL_COND(agent == nullptr); - agent->set_callback(p_receiver == nullptr ? 0 : p_receiver->get_instance_id(), p_method, p_udata); + agent->set_callback(p_object_id, p_method, p_udata); if (agent->get_map()) { - if (p_receiver == nullptr) { + if (p_object_id == ObjectID()) { agent->get_map()->remove_agent_as_controlled(agent); } else { agent->get_map()->set_agent_as_controlled(agent); diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h index bd20ae863e8..75cb59dcb42 100644 --- a/modules/navigation/godot_navigation_server.h +++ b/modules/navigation/godot_navigation_server.h @@ -147,7 +147,7 @@ public: COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position); COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore); virtual bool agent_is_map_changed(RID p_agent) const; - COMMAND_4_DEF(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_method, Variant, p_udata, Variant()); + COMMAND_4_DEF(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, Variant()); COMMAND_1(free, RID, p_object); diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index 635c8033606..68478ef54c7 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -211,9 +211,9 @@ NavigationAgent2D::~NavigationAgent2D() { void NavigationAgent2D::set_avoidance_enabled(bool p_enabled) { avoidance_enabled = p_enabled; if (avoidance_enabled) { - Navigation2DServer::get_singleton()->agent_set_callback(agent, this, "_avoidance_done"); + Navigation2DServer::get_singleton()->agent_set_callback(agent, get_instance_id(), "_avoidance_done"); } else { - Navigation2DServer::get_singleton()->agent_set_callback(agent, nullptr, "_avoidance_done"); + Navigation2DServer::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done"); } } @@ -223,7 +223,7 @@ bool NavigationAgent2D::get_avoidance_enabled() const { void NavigationAgent2D::set_agent_parent(Node *p_agent_parent) { // remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map - Navigation2DServer::get_singleton()->agent_set_callback(agent, nullptr, "_avoidance_done"); + Navigation2DServer::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done"); if (Object::cast_to(p_agent_parent) != nullptr) { // place agent on navigation map first or else the RVO agent callback creation fails silently later agent_parent = Object::cast_to(p_agent_parent); diff --git a/scene/3d/navigation_agent.cpp b/scene/3d/navigation_agent.cpp index e717f6d1d49..b42dfb62d54 100644 --- a/scene/3d/navigation_agent.cpp +++ b/scene/3d/navigation_agent.cpp @@ -214,9 +214,9 @@ NavigationAgent::~NavigationAgent() { void NavigationAgent::set_avoidance_enabled(bool p_enabled) { avoidance_enabled = p_enabled; if (avoidance_enabled) { - NavigationServer::get_singleton()->agent_set_callback(agent, this, "_avoidance_done"); + NavigationServer::get_singleton()->agent_set_callback(agent, get_instance_id(), "_avoidance_done"); } else { - NavigationServer::get_singleton()->agent_set_callback(agent, nullptr, "_avoidance_done"); + NavigationServer::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done"); } } @@ -245,7 +245,7 @@ Node *NavigationAgent::get_navigation_node() const { void NavigationAgent::set_agent_parent(Node *p_agent_parent) { // remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map - NavigationServer::get_singleton()->agent_set_callback(agent, nullptr, "_avoidance_done"); + NavigationServer::get_singleton()->agent_set_callback(agent, ObjectID(), "_avoidance_done"); if (Object::cast_to(p_agent_parent) != nullptr) { // place agent on navigation map first or else the RVO agent callback creation fails silently later agent_parent = Object::cast_to(p_agent_parent); diff --git a/servers/navigation_2d_server.cpp b/servers/navigation_2d_server.cpp index 074b92b46f0..728da55ab64 100644 --- a/servers/navigation_2d_server.cpp +++ b/servers/navigation_2d_server.cpp @@ -146,10 +146,6 @@ Transform trf2_to_trf3(const Transform2D &d) { return Transform(b, o); } -Object *obj_to_obj(Object *d) { - return d; -} - StringName sn_to_sn(StringName &d) { return d; } @@ -158,6 +154,10 @@ Variant var_to_var(Variant &d) { return d; } +ObjectID id_to_id(const ObjectID &id) { + return id; +} + Ref poly_to_mesh(Ref d) { if (d.is_valid()) { return d->get_mesh(); @@ -218,7 +218,7 @@ void Navigation2DServer::_bind_methods() { ClassDB::bind_method(D_METHOD("agent_set_target_velocity", "agent", "target_velocity"), &Navigation2DServer::agent_set_target_velocity); ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &Navigation2DServer::agent_set_position); ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &Navigation2DServer::agent_is_map_changed); - ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "receiver", "method", "userdata"), &Navigation2DServer::agent_set_callback, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "object_id", "method", "userdata"), &Navigation2DServer::agent_set_callback, DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("free_rid", "rid"), &Navigation2DServer::free); @@ -319,6 +319,6 @@ void FORWARD_2_C(agent_set_ignore_y, RID, p_agent, bool, p_ignore, rid_to_rid, b bool FORWARD_1_C(agent_is_map_changed, RID, p_agent, rid_to_rid); -void FORWARD_4_C(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_method, Variant, p_udata, rid_to_rid, obj_to_obj, sn_to_sn, var_to_var); +void FORWARD_4_C(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, rid_to_rid, id_to_id, sn_to_sn, var_to_var); void FORWARD_1_C(free, RID, p_object, rid_to_rid); diff --git a/servers/navigation_2d_server.h b/servers/navigation_2d_server.h index 41d7786be5d..63475a93beb 100644 --- a/servers/navigation_2d_server.h +++ b/servers/navigation_2d_server.h @@ -181,7 +181,7 @@ public: virtual bool agent_is_map_changed(RID p_agent) const; /// Callback called at the end of the RVO process - virtual void agent_set_callback(RID p_agent, Object *p_receiver, StringName p_method, Variant p_udata = Variant()) const; + virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) const; /// Destroy the `RID` virtual void free(RID p_object) const; diff --git a/servers/navigation_server.cpp b/servers/navigation_server.cpp index 9a585450089..1ca66755426 100644 --- a/servers/navigation_server.cpp +++ b/servers/navigation_server.cpp @@ -87,7 +87,7 @@ void NavigationServer::_bind_methods() { ClassDB::bind_method(D_METHOD("agent_set_target_velocity", "agent", "target_velocity"), &NavigationServer::agent_set_target_velocity); ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer::agent_set_position); ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer::agent_is_map_changed); - ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "receiver", "method", "userdata"), &NavigationServer::agent_set_callback, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("agent_set_callback", "agent", "object_id", "method", "userdata"), &NavigationServer::agent_set_callback, DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer::free); diff --git a/servers/navigation_server.h b/servers/navigation_server.h index 5e934352e80..bf40a075888 100644 --- a/servers/navigation_server.h +++ b/servers/navigation_server.h @@ -197,7 +197,7 @@ public: virtual bool agent_is_map_changed(RID p_agent) const = 0; /// Callback called at the end of the RVO process - virtual void agent_set_callback(RID p_agent, Object *p_receiver, StringName p_method, Variant p_udata = Variant()) const = 0; + virtual void agent_set_callback(RID p_agent, ObjectID p_object_id, StringName p_method, Variant p_udata = Variant()) const = 0; /// Destroy the `RID` virtual void free(RID p_object) const = 0;