Skip to content

Commit 5a533f5

Browse files
committed
fix panic
1 parent 80ba58b commit 5a533f5

3 files changed

Lines changed: 39 additions & 72 deletions

File tree

lib/src/scene_runner/components/trigger_area.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ fn handle_body_monitor_event(
113113
};
114114

115115
let Some(&(scene_id, trigger_entity, collision_mask)) = monitor.registry.get(&area_rid) else {
116+
// Stale callback from a released area, ignore
116117
return;
117118
};
118119

@@ -161,6 +162,13 @@ fn handle_body_monitor_event(
161162
return; // No instance ID
162163
};
163164

165+
tracing::debug!(
166+
"[TriggerArea] {} trigger={:?}, collider={:?}",
167+
if is_enter { "ENTER" } else { "EXIT" },
168+
trigger_entity,
169+
collider_entity
170+
);
171+
164172
monitor.pending_events.push(PendingTriggerEvent {
165173
scene_id,
166174
trigger_entity,
@@ -214,6 +222,8 @@ impl TriggerAreaState {
214222
let mut physics_server = PhysicsServer3D::singleton();
215223
for (_, instance) in self.instances.drain() {
216224
unregister_trigger_area(instance.area_rid);
225+
// Clear monitor callback before freeing to prevent stale events
226+
physics_server.area_set_monitor_callback(instance.area_rid, Callable::invalid());
217227
physics_server.free_rid(instance.area_rid);
218228
physics_server.free_rid(instance.shape_rid);
219229
}

lib/src/scene_runner/object_pool.rs

Lines changed: 6 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::collections::VecDeque;
22

3+
use godot::builtin::Callable;
34
use godot::engine::PhysicsServer3D;
45
use godot::prelude::Rid;
56

@@ -90,72 +91,41 @@ impl Default for PhysicsAreaPool {
9091
#[allow(dead_code)]
9192
impl PhysicsAreaPool {
9293
pub fn acquire_area(&mut self) -> Rid {
93-
let (rid, reused) = self
94+
let (rid, _reused) = self
9495
.areas
9596
.acquire(|| PhysicsServer3D::singleton().area_create());
96-
let (created, in_use, pooled) = self.areas.stats();
97-
tracing::debug!(
98-
"[PhysicsAreaPool] ACQUIRE area: rid={:?}, reused={}, stats=(created={}, in_use={}, pooled={})",
99-
rid, reused, created, in_use, pooled
100-
);
10197
rid
10298
}
10399

104100
pub fn release_area(&mut self, rid: Rid) {
105101
let mut server = PhysicsServer3D::singleton();
102+
// Clear monitor callback to prevent stale events
103+
server.area_set_monitor_callback(rid, Callable::invalid());
106104
server.area_clear_shapes(rid);
107105
server.area_set_space(rid, Rid::Invalid);
108106
self.areas.release(rid);
109-
let (created, in_use, pooled) = self.areas.stats();
110-
tracing::debug!(
111-
"[PhysicsAreaPool] RELEASE area: rid={:?}, stats=(created={}, in_use={}, pooled={})",
112-
rid,
113-
created,
114-
in_use,
115-
pooled
116-
);
117107
}
118108

119109
pub fn acquire_box_shape(&mut self) -> Rid {
120-
let (rid, reused) = self
110+
let (rid, _reused) = self
121111
.shapes_box
122112
.acquire(|| PhysicsServer3D::singleton().box_shape_create());
123-
let (created, in_use, pooled) = self.shapes_box.stats();
124-
tracing::debug!(
125-
"[PhysicsAreaPool] ACQUIRE box_shape: rid={:?}, reused={}, stats=(created={}, in_use={}, pooled={})",
126-
rid, reused, created, in_use, pooled
127-
);
128113
rid
129114
}
130115

131116
pub fn acquire_sphere_shape(&mut self) -> Rid {
132-
let (rid, reused) = self
117+
let (rid, _reused) = self
133118
.shapes_sphere
134119
.acquire(|| PhysicsServer3D::singleton().sphere_shape_create());
135-
let (created, in_use, pooled) = self.shapes_sphere.stats();
136-
tracing::debug!(
137-
"[PhysicsAreaPool] ACQUIRE sphere_shape: rid={:?}, reused={}, stats=(created={}, in_use={}, pooled={})",
138-
rid, reused, created, in_use, pooled
139-
);
140120
rid
141121
}
142122

143123
pub fn release_box_shape(&mut self, rid: Rid) {
144124
self.shapes_box.release(rid);
145-
let (created, in_use, pooled) = self.shapes_box.stats();
146-
tracing::debug!(
147-
"[PhysicsAreaPool] RELEASE box_shape: rid={:?}, stats=(created={}, in_use={}, pooled={})",
148-
rid, created, in_use, pooled
149-
);
150125
}
151126

152127
pub fn release_sphere_shape(&mut self, rid: Rid) {
153128
self.shapes_sphere.release(rid);
154-
let (created, in_use, pooled) = self.shapes_sphere.stats();
155-
tracing::debug!(
156-
"[PhysicsAreaPool] RELEASE sphere_shape: rid={:?}, stats=(created={}, in_use={}, pooled={})",
157-
rid, created, in_use, pooled
158-
);
159129
}
160130

161131
pub fn cleanup(&mut self) {

lib/src/scene_runner/scene_manager.rs

Lines changed: 23 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -767,33 +767,23 @@ impl SceneManager {
767767
return None;
768768
}
769769

770-
let collider = raycast_result.get("collider")?;
770+
// Validate collider is still a valid object before calling methods on it
771+
// (object could be freed between raycast and method call during scene loading)
772+
let collider_obj: Gd<Object> = raycast_result.get("collider")?.try_to().ok()?;
773+
if !collider_obj.is_instance_valid() {
774+
return None;
775+
}
771776

772777
// The raycast returns the closest hit, so we just need to identify what type it is
773778
// Priority is naturally handled by distance - closer objects are returned first
774779

775780
// First check if this is a DCL entity (scene object)
776-
let has_dcl_entity_id = collider
777-
.call(
778-
StringName::from("has_meta"),
779-
&[Variant::from("dcl_entity_id")],
780-
)
781-
.booleanize();
781+
let has_dcl_entity_id = collider_obj.has_meta("dcl_entity_id".into());
782782

783783
if has_dcl_entity_id {
784784
// It's a scene entity, return it
785-
let dcl_entity_id = collider
786-
.call(
787-
StringName::from("get_meta"),
788-
&[Variant::from("dcl_entity_id")],
789-
)
790-
.to::<i32>();
791-
let dcl_scene_id = collider
792-
.call(
793-
StringName::from("get_meta"),
794-
&[Variant::from("dcl_scene_id")],
795-
)
796-
.to::<i32>();
785+
let dcl_entity_id = collider_obj.get_meta("dcl_entity_id".into()).to::<i32>();
786+
let dcl_scene_id = collider_obj.get_meta("dcl_scene_id".into()).to::<i32>();
797787

798788
let scene = self.scenes.get(&SceneId(dcl_scene_id))?;
799789
let scene_position = scene.godot_dcl_scene.root_node_3d.get_position();
@@ -812,12 +802,8 @@ impl SceneManager {
812802
}
813803

814804
// If not a scene entity, check if it's an avatar
815-
let is_avatar = collider
816-
.call(StringName::from("has_meta"), &[Variant::from("is_avatar")])
817-
.booleanize()
818-
&& collider
819-
.call(StringName::from("get_meta"), &[Variant::from("is_avatar")])
820-
.booleanize();
805+
let is_avatar = collider_obj.has_meta("is_avatar".into())
806+
&& collider_obj.get_meta("is_avatar".into()).booleanize();
821807

822808
if is_avatar {
823809
// Check distance for avatar interactions (limit to 10 meters)
@@ -831,19 +817,20 @@ impl SceneManager {
831817
// Only allow avatar interaction within the distance limit
832818
if distance <= MAX_AVATAR_INTERACTION_DISTANCE {
833819
// Walk up the node tree to find the DclAvatar node
834-
let mut node = collider;
835-
loop {
836-
// Try to cast to DclAvatar
837-
if let Ok(avatar) = node.try_to::<Gd<DclAvatar>>() {
838-
return Some(RaycastResult::Avatar(avatar));
839-
}
820+
// First try to cast collider_obj to Node for tree traversal
821+
if let Ok(mut current_node) = collider_obj.clone().try_cast::<Node>() {
822+
loop {
823+
// Try to cast to DclAvatar
824+
if let Ok(avatar) = current_node.clone().try_cast::<DclAvatar>() {
825+
return Some(RaycastResult::Avatar(avatar));
826+
}
840827

841-
// Try to get parent
842-
let parent_result = node.call(StringName::from("get_parent"), &[]);
843-
if parent_result.is_nil() {
844-
break;
828+
// Try to get parent
829+
match current_node.get_parent() {
830+
Some(parent) => current_node = parent,
831+
None => break,
832+
}
845833
}
846-
node = parent_result;
847834
}
848835
}
849836
}

0 commit comments

Comments
 (0)