Skip to content

Commit 0036199

Browse files
committed
Don't panic when despawning hierarchy with signatures
1 parent 30bf0dc commit 0036199

3 files changed

Lines changed: 84 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- Don't panic when despawning hierarchy with signatures.
13+
1014
## [0.39.0] - 2026-02-24
1115

1216
### Added

src/client.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -437,16 +437,16 @@ fn apply_despawn(
437437
// with the last replication message, but the server might not yet have received confirmation
438438
// from the client and could include the deletion in the this message.
439439
let server_entity = postcard_utils::entity_from_buf(message)?;
440-
if let Some(client_entity) = params
441-
.entity_map
442-
.server_entry(server_entity)
443-
.remove()
444-
.and_then(|entity| world.get_entity_mut(entity).ok())
445-
{
446-
trace!("applying despawn for `{}`", client_entity.id());
447-
params.signature_map.remove(client_entity.id()); // Requires manual removal since the map is removed from the world and inaccessible to triggers.
448-
let ctx = DespawnCtx { message_tick };
449-
(params.registry.despawn)(&ctx, client_entity);
440+
if let Some(client_entity) = params.entity_map.server_entry(server_entity).remove() {
441+
// Requires manual removal since the map is removed from the world and inaccessible to triggers.
442+
// The entity can also be despawned via a relationship when applying
443+
// despawn to another entity, so we always need to remove it from the map.
444+
params.signature_map.remove(client_entity);
445+
if let Ok(client_entity) = world.get_entity_mut(client_entity) {
446+
trace!("applying despawn for `{}`", client_entity.id());
447+
let ctx = DespawnCtx { message_tick };
448+
(params.registry.despawn)(&ctx, client_entity);
449+
}
450450
}
451451

452452
Ok(())

tests/despawn.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,76 @@ fn signature() {
156156
assert!(client_app.world().get_entity(client_entity).is_err());
157157
}
158158

159+
#[test]
160+
fn signature_with_hierarchy() {
161+
let mut server_app = App::new();
162+
let mut client_app = App::new();
163+
for app in [&mut server_app, &mut client_app] {
164+
app.add_plugins((
165+
MinimalPlugins,
166+
StatesPlugin,
167+
RepliconPlugins.set(ServerPlugin::new(PostUpdate)),
168+
))
169+
.replicate::<TestComponent>()
170+
.finish();
171+
}
172+
173+
server_app.connect_client(&mut client_app);
174+
175+
let client_parent = client_app
176+
.world_mut()
177+
.spawn((Replicated, Signature::from(0)))
178+
.id();
179+
let client_child = client_app
180+
.world_mut()
181+
.spawn((Replicated, Signature::from(1), ChildOf(client_parent)))
182+
.id();
183+
184+
let server_parent = server_app
185+
.world_mut()
186+
.spawn((Replicated, Signature::from(0)))
187+
.id();
188+
server_app
189+
.world_mut()
190+
.spawn((Replicated, Signature::from(1), ChildOf(server_parent)));
191+
192+
server_app.update();
193+
server_app.exchange_with_client(&mut client_app);
194+
client_app.update();
195+
server_app.exchange_with_client(&mut client_app);
196+
197+
assert!(client_app.world().get_entity(client_parent).is_ok());
198+
assert!(client_app.world().get_entity(client_child).is_ok());
199+
200+
server_app.world_mut().despawn(server_parent);
201+
202+
server_app.update();
203+
server_app.exchange_with_client(&mut client_app);
204+
client_app.update();
205+
206+
assert!(client_app.world().get_entity(client_parent).is_err());
207+
assert!(client_app.world().get_entity(client_child).is_err());
208+
209+
let server_parent = server_app
210+
.world_mut()
211+
.spawn((Replicated, Signature::from(0)))
212+
.id();
213+
server_app
214+
.world_mut()
215+
.spawn((Replicated, Signature::from(1), ChildOf(server_parent)));
216+
217+
server_app.update();
218+
server_app.exchange_with_client(&mut client_app);
219+
client_app.update();
220+
221+
let mut remote = client_app.world_mut().query::<&Remote>();
222+
assert_eq!(
223+
remote.iter(client_app.world()).count(),
224+
2,
225+
"entities should be replicated as new due to removal from the signature map"
226+
);
227+
}
228+
159229
#[test]
160230
fn hidden_entity() {
161231
let mut server_app = App::new();

0 commit comments

Comments
 (0)