diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f7e8b11..7ea10cbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - RTT, bytes per second and packet loss information for `RepliconClient` and `ConnectedClients`. - `ClientSet::Diagnostics` for systems that collect client diagnostics. +### Fixed + +- Sending removals and despawns for hidden entities. + ### Changed - Make `core::replication::replication_rules::ReplicationRules` public. diff --git a/src/server.rs b/src/server.rs index 2b2ef986..bc9ff94e 100644 --- a/src/server.rs +++ b/src/server.rs @@ -296,7 +296,12 @@ impl ServerPlugin { &mut replicated_clients, &mut set.p5(), )?; - collect_removals(&mut messages, &mut serialized, &removal_buffer)?; + collect_removals( + &mut messages, + &mut serialized, + &replicated_clients, + &removal_buffer, + )?; collect_changes( &mut messages, &mut serialized, @@ -425,8 +430,7 @@ fn collect_despawns( for entity in despawn_buffer.drain(..) { let entity_range = serialized.write_entity(entity)?; for ((message, _), client) in messages.iter_mut().zip(replicated_clients.iter_mut()) { - let visibility = client.visibility().state(entity); - if visibility != Visibility::Hidden { + if client.visibility().is_visible(entity) { message.add_despawn(entity_range.clone()); } client.remove_despawned(entity); @@ -447,14 +451,17 @@ fn collect_despawns( fn collect_removals( messages: &mut ReplicationMessages, serialized: &mut SerializedData, + replicated_clients: &ReplicatedClients, removal_buffer: &RemovalBuffer, ) -> bincode::Result<()> { for (&entity, remove_ids) in removal_buffer.iter() { - let entity = serialized.write_entity(entity)?; + let entity_range = serialized.write_entity(entity)?; let ids_len = remove_ids.len(); let fn_ids = serialized.write_fn_ids(remove_ids.iter().map(|&(_, fns_id)| fns_id))?; - for (message, _) in messages.iter_mut() { - message.add_removals(entity.clone(), ids_len, fn_ids.clone()); + for ((message, _), client) in messages.iter_mut().zip(replicated_clients.iter()) { + if client.visibility().is_visible(entity) { + message.add_removals(entity_range.clone(), ids_len, fn_ids.clone()); + } } } diff --git a/tests/despawn.rs b/tests/despawn.rs index 1d6e29fd..25b2b642 100644 --- a/tests/despawn.rs +++ b/tests/despawn.rs @@ -118,5 +118,42 @@ fn after_spawn() { assert!(client_app.world().entities().is_empty()); } +#[test] +fn hidden() { + let mut server_app = App::new(); + let mut client_app = App::new(); + for app in [&mut server_app, &mut client_app] { + app.add_plugins(( + MinimalPlugins, + RepliconPlugins.set(ServerPlugin { + tick_policy: TickPolicy::EveryFrame, + visibility_policy: VisibilityPolicy::Whitelist, // Hide all spawned entities by default. + ..Default::default() + }), + )); + } + + server_app.connect_client(&mut client_app); + + let server_entity = server_app.world_mut().spawn(Replicated).id(); + + server_app.update(); + server_app.exchange_with_client(&mut client_app); + client_app.update(); + server_app.exchange_with_client(&mut client_app); + + server_app.world_mut().despawn(server_entity); + + server_app.update(); + server_app.exchange_with_client(&mut client_app); + client_app.update(); + + assert_eq!( + client_app.world().entities().total_count(), + 0, + "client shouldn't spawn or despawn hidden entity" + ); +} + #[derive(Component, Deserialize, Serialize)] struct DummyComponent; diff --git a/tests/removal.rs b/tests/removal.rs index bd40e4a1..f2ee99f6 100644 --- a/tests/removal.rs +++ b/tests/removal.rs @@ -447,6 +447,49 @@ fn confirm_history() { assert_eq!(event.tick, tick); } +#[test] +fn hidden() { + let mut server_app = App::new(); + let mut client_app = App::new(); + for app in [&mut server_app, &mut client_app] { + app.add_plugins(( + MinimalPlugins, + RepliconPlugins.set(ServerPlugin { + tick_policy: TickPolicy::EveryFrame, + visibility_policy: VisibilityPolicy::Whitelist, // Hide all spawned entities by default. + ..Default::default() + }), + )) + .replicate::(); + } + + server_app.connect_client(&mut client_app); + + let server_entity = server_app + .world_mut() + .spawn((Replicated, DummyComponent)) + .id(); + + server_app.update(); + server_app.exchange_with_client(&mut client_app); + client_app.update(); + server_app.exchange_with_client(&mut client_app); + + server_app + .world_mut() + .entity_mut(server_entity) + .remove::(); + + server_app.update(); + server_app.exchange_with_client(&mut client_app); + client_app.update(); + + assert!( + client_app.world().entities().is_empty(), + "client shouldn't know about hidden entity" + ); +} + #[derive(Component, Deserialize, Serialize)] struct DummyComponent;