Fix physics on_floor_body crash

Physics body previously stored the RID of a collision object and accessed it on the next frame, leading to a crash if the object had been deleted.
This PR stores the ObjectID in addition to the RID, and checks the object still exists prior to access.
This commit is contained in:
lawnjelly 2024-02-28 07:48:13 +00:00
parent 50fa1896b8
commit 9a9dccbaa2
2 changed files with 25 additions and 11 deletions

View file

@ -1077,9 +1077,17 @@ Vector3 KinematicBody::_move_and_slide_internal(const Vector3 &p_linear_velocity
float delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
Vector3 current_floor_velocity = floor_velocity;
if (on_floor && on_floor_body.is_valid()) {
if (on_floor && on_floor_body_rid.is_valid()) {
PhysicsDirectBodyState *bs = nullptr;
// We need to check the on_floor_body still exists before accessing.
// A valid RID is no guarantee that the object has not been deleted.
if (ObjectDB::get_instance(on_floor_body_id)) {
// This approach makes sure there is less delay between the actual body velocity and the one we saved.
PhysicsDirectBodyState *bs = PhysicsServer::get_singleton()->body_get_direct_state(on_floor_body);
bs = PhysicsServer::get_singleton()->body_get_direct_state(on_floor_body_rid);
}
if (bs) {
Transform gt = get_global_transform();
Vector3 local_position = gt.origin - bs->get_transform().origin;
@ -1087,7 +1095,8 @@ Vector3 KinematicBody::_move_and_slide_internal(const Vector3 &p_linear_velocity
} else {
// Body is removed or destroyed, invalidate floor.
current_floor_velocity = Vector3();
on_floor_body = RID();
on_floor_body_rid = RID();
on_floor_body_id = ObjectID();
}
}
@ -1098,17 +1107,18 @@ Vector3 KinematicBody::_move_and_slide_internal(const Vector3 &p_linear_velocity
floor_normal = Vector3();
floor_velocity = Vector3();
if (current_floor_velocity != Vector3() && on_floor_body.is_valid()) {
if (current_floor_velocity != Vector3() && on_floor_body_rid.is_valid()) {
Collision floor_collision;
Set<RID> exclude;
exclude.insert(on_floor_body);
exclude.insert(on_floor_body_rid);
if (move_and_collide(current_floor_velocity * delta, p_infinite_inertia, floor_collision, true, false, false, exclude)) {
colliders.push_back(floor_collision);
_set_collision_direction(floor_collision, up_direction, p_floor_max_angle);
}
}
on_floor_body = RID();
on_floor_body_rid = RID();
on_floor_body_id = ObjectID();
Vector3 motion = body_velocity * delta;
// No sliding on first attempt to keep floor motion stable when possible,
@ -1186,7 +1196,8 @@ Vector3 KinematicBody::_move_and_slide_internal(const Vector3 &p_linear_velocity
if (Math::acos(col.normal.dot(up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
on_floor = true;
floor_normal = col.normal;
on_floor_body = col.collider_rid;
on_floor_body_rid = col.collider_rid;
on_floor_body_id = col.collider;
floor_velocity = col.collider_vel;
if (p_stop_on_slope) {
// move and collide may stray the object a bit because of pre un-stucking,
@ -1237,7 +1248,8 @@ void KinematicBody::_set_collision_direction(const Collision &p_collision, const
if (Math::acos(p_collision.normal.dot(p_up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
on_floor = true;
floor_normal = p_collision.normal;
on_floor_body = p_collision.collider_rid;
on_floor_body_rid = p_collision.collider_rid;
on_floor_body_id = p_collision.collider;
floor_velocity = p_collision.collider_vel;
} else if (Math::acos(p_collision.normal.dot(-p_up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
on_ceiling = true;
@ -1429,7 +1441,8 @@ void KinematicBody::_notification(int p_what) {
// Reset move_and_slide() data.
on_floor = false;
on_floor_body = RID();
on_floor_body_rid = RID();
on_floor_body_id = ObjectID();
on_ceiling = false;
on_wall = false;
colliders.clear();

View file

@ -292,7 +292,8 @@ private:
Vector3 floor_normal;
Vector3 floor_velocity;
RID on_floor_body;
RID on_floor_body_rid;
ObjectID on_floor_body_id;
bool on_floor;
bool on_ceiling;
bool on_wall;