mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-09 17:19:15 -10:00
Move a packet's entity removal out of remote.rs
remote.rs is over 3000 lines and may not grow (verify's "Big files don't grow" failed): remove_entity, remove_entities and their test move to game/remote_removal.rs, a child module of remote, unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
396353c6b1
commit
8cf1e6b4d6
2 files changed
+145
-131
No files matched your search
@@ -3389,56 +3389,6 @@ impl Game<'_> {
|
||||
index.get(mobs, id as u32).map(mob_tracking_body)
|
||||
});
|
||||
}
|
||||
fn remove_entity(&mut self, id: i32) {
|
||||
self.remove_entities(&[id]);
|
||||
}
|
||||
|
||||
fn remove_entities(&mut self, ids: &[i32]) {
|
||||
for &id in ids {
|
||||
self.detach_remote_tracking_entity(id);
|
||||
self.audio.remove_sound_entity(id as u32);
|
||||
// Entity.setRemoved detaches every passenger, even when no empty
|
||||
// SetPassengers packet preceded the vehicle's removal. Otherwise
|
||||
// mobs and other players keep their passenger pose after it is gone.
|
||||
self.remote_set_passengers(id, &[]);
|
||||
// Before the crystals go: the dragon's beam index follows them.
|
||||
self.remove_remote_dragon(id);
|
||||
self.crystals.retain(|c| c.id != id as u32);
|
||||
self.remove_remote_object(id as u32);
|
||||
self.remove_remote_small_projectiles(id as u32);
|
||||
self.persist.uuids.remove(&(id as u32));
|
||||
if let Some(r) = self.remote.as_mut() {
|
||||
r.glowing.remove(&id);
|
||||
r.entities.remove(&id);
|
||||
r.passenger_vehicles.remove(&(id as u32));
|
||||
r.lerps.remove(&id);
|
||||
r.cart_lerps.remove(&id);
|
||||
r.stepped.remove(&id);
|
||||
r.head_lerps.remove(&id);
|
||||
r.pending_items.remove(&id);
|
||||
}
|
||||
}
|
||||
let removed: horizon_client::fast_hash::FxHashSet<u32> = if ids.len() > 1 {
|
||||
ids.iter().map(|&id| id as u32).collect()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let keep = |id: u32| {
|
||||
if ids.len() > 1 {
|
||||
!removed.contains(&id)
|
||||
} else {
|
||||
ids.first().is_none_or(|&removed| id != removed as u32)
|
||||
}
|
||||
};
|
||||
self.mobs.retain(|m| keep(m.id));
|
||||
self.orbs.retain(|o| keep(o.id));
|
||||
self.dropped.retain(|d| keep(d.id));
|
||||
self.falling.retain(|f| keep(f.id));
|
||||
self.tnts.retain(|t| keep(t.id));
|
||||
if let Some(r) = self.remote.as_mut() {
|
||||
r.passenger_vehicles.retain(|_, v| keep(*v));
|
||||
}
|
||||
}
|
||||
/// Map a menu slot to Horizon's inventory screen slot ids.
|
||||
pub(super) fn menu_slot(menu: &str, slot: i32) -> Option<usize> {
|
||||
let s = usize::try_from(slot).ok()?;
|
||||
@@ -11944,6 +11894,9 @@ fn in_storage_range(view_distance: i32, center: (i32, i32), x: i32, z: i32) -> b
|
||||
x.abs_diff(center.0) <= range && z.abs_diff(center.1) <= range
|
||||
}
|
||||
|
||||
// A packet's removals (`RemoveEntities`), apart: this file may not grow.
|
||||
#[path = "remote_removal.rs"]
|
||||
mod removal;
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
@@ -12019,87 +11972,6 @@ mod tests {
|
||||
snap_target, synced_local_use,
|
||||
};
|
||||
|
||||
#[test]
|
||||
#[ignore = "needs prepared resources in HORIZON_TEST_RESOURCES and scratch homes/cwd"]
|
||||
fn remove_entities_filters_mixed_entity_lists() {
|
||||
use crate::game::{
|
||||
Audio, Game, Options, Progress, Settings, execution_role::ExecutionRole, load_resources,
|
||||
};
|
||||
use horizon_protocol::packets::play::{self, Clientbound as C};
|
||||
|
||||
let root = std::path::PathBuf::from(std::env::var_os("HORIZON_TEST_RESOURCES").unwrap());
|
||||
let settings = Settings::default();
|
||||
let loaded = load_resources(
|
||||
root.clone(),
|
||||
None,
|
||||
&root,
|
||||
&settings,
|
||||
std::time::Instant::now(),
|
||||
false,
|
||||
true,
|
||||
&Progress::new(),
|
||||
&mut |_, _| unreachable!(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut options = Options::parse(std::iter::empty()).unwrap().unwrap();
|
||||
options.resources = root.clone();
|
||||
options.execution_role = ExecutionRole::Client;
|
||||
let mut game = Game::new(
|
||||
&loaded.resources,
|
||||
loaded.pack,
|
||||
loaded.gui.unwrap(),
|
||||
options,
|
||||
settings,
|
||||
Audio::silent(&root),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let (client, _server) = horizon_protocol::connection::Pipe::pair();
|
||||
game.connect_local(client, "RemovalTest").unwrap();
|
||||
for (id, kind) in [
|
||||
(40, "minecraft:falling_block"),
|
||||
(41, "minecraft:falling_block"),
|
||||
(42, "minecraft:falling_block"),
|
||||
(43, "minecraft:tnt"),
|
||||
(44, "minecraft:tnt"),
|
||||
(45, "minecraft:experience_orb"),
|
||||
(46, "minecraft:experience_orb"),
|
||||
(47, "minecraft:zombie"),
|
||||
(48, "minecraft:zombie"),
|
||||
] {
|
||||
if matches!(kind, "minecraft:falling_block" | "minecraft:tnt") {
|
||||
assert!(game.spawn_remote_projectile(
|
||||
id,
|
||||
kind,
|
||||
[0.0, 64.0, 0.0],
|
||||
(0.0, 0.0),
|
||||
[0.0; 3],
|
||||
0
|
||||
));
|
||||
} else {
|
||||
game.spawn_entity(id, kind, [0.0, 64.0, 0.0], [0.0; 3]);
|
||||
}
|
||||
}
|
||||
for id in [49, 50] {
|
||||
game.dropped.push(horizon_client::entities::ItemEntity::new(
|
||||
id,
|
||||
horizon_client::items::ItemStack::new("minecraft:sand", 1),
|
||||
horizon_core::geometry::Vec3::new(0.0, 64.0, 0.0),
|
||||
horizon_core::geometry::Vec3::ZERO,
|
||||
0.0,
|
||||
));
|
||||
}
|
||||
game.handle_packet(C::RemoveEntities(play::RemoveEntities {
|
||||
ids: vec![40, 42, 43, 45, 47, 49],
|
||||
}))
|
||||
.unwrap();
|
||||
assert_eq!(game.falling.iter().map(|e| e.id).collect::<Vec<_>>(), [41]);
|
||||
assert_eq!(game.tnts.iter().map(|e| e.id).collect::<Vec<_>>(), [44]);
|
||||
assert_eq!(game.orbs.iter().map(|e| e.id).collect::<Vec<_>>(), [46]);
|
||||
assert_eq!(game.mobs.iter().map(|e| e.id).collect::<Vec<_>>(), [48]);
|
||||
assert_eq!(game.dropped.iter().map(|e| e.id).collect::<Vec<_>>(), [50]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "needs prepared resources in HORIZON_TEST_RESOURCES and scratch homes/cwd"]
|
||||
fn tracking_emitters_follow_the_packet_target() {
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
//! Removing entities a server dropped (`RemoveEntities`), out of remote.rs,
|
||||
//! which may not grow.
|
||||
use super::super::Game;
|
||||
|
||||
impl Game<'_> {
|
||||
pub(super) fn remove_entity(&mut self, id: i32) {
|
||||
self.remove_entities(&[id]);
|
||||
}
|
||||
|
||||
/// `ClientboundRemoveEntitiesPacket`: a packet's ids go together, the big
|
||||
/// entity lists filtered once (#13007), each id's own cleanup in order.
|
||||
pub(super) fn remove_entities(&mut self, ids: &[i32]) {
|
||||
for &id in ids {
|
||||
self.detach_remote_tracking_entity(id);
|
||||
self.audio.remove_sound_entity(id as u32);
|
||||
// Entity.setRemoved detaches every passenger, even when no empty
|
||||
// SetPassengers packet preceded the vehicle's removal. Otherwise
|
||||
// mobs and other players keep their passenger pose after it is gone.
|
||||
self.remote_set_passengers(id, &[]);
|
||||
// Before the crystals go: the dragon's beam index follows them.
|
||||
self.remove_remote_dragon(id);
|
||||
self.crystals.retain(|c| c.id != id as u32);
|
||||
self.remove_remote_object(id as u32);
|
||||
self.remove_remote_small_projectiles(id as u32);
|
||||
self.persist.uuids.remove(&(id as u32));
|
||||
if let Some(r) = self.remote.as_mut() {
|
||||
r.glowing.remove(&id);
|
||||
r.entities.remove(&id);
|
||||
r.passenger_vehicles.remove(&(id as u32));
|
||||
r.lerps.remove(&id);
|
||||
r.cart_lerps.remove(&id);
|
||||
r.stepped.remove(&id);
|
||||
r.head_lerps.remove(&id);
|
||||
r.pending_items.remove(&id);
|
||||
}
|
||||
}
|
||||
let removed: horizon_client::fast_hash::FxHashSet<u32> = if ids.len() > 1 {
|
||||
ids.iter().map(|&id| id as u32).collect()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let keep = |id: u32| {
|
||||
if ids.len() > 1 {
|
||||
!removed.contains(&id)
|
||||
} else {
|
||||
ids.first().is_none_or(|&removed| id != removed as u32)
|
||||
}
|
||||
};
|
||||
self.mobs.retain(|m| keep(m.id));
|
||||
self.orbs.retain(|o| keep(o.id));
|
||||
self.dropped.retain(|d| keep(d.id));
|
||||
self.falling.retain(|f| keep(f.id));
|
||||
self.tnts.retain(|t| keep(t.id));
|
||||
if let Some(r) = self.remote.as_mut() {
|
||||
r.passenger_vehicles.retain(|_, v| keep(*v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
#[ignore = "needs prepared resources in HORIZON_TEST_RESOURCES and scratch homes/cwd"]
|
||||
fn remove_entities_filters_mixed_entity_lists() {
|
||||
use crate::game::{
|
||||
Audio, Game, Options, Progress, Settings, execution_role::ExecutionRole, load_resources,
|
||||
};
|
||||
use horizon_protocol::packets::play::{self, Clientbound as C};
|
||||
|
||||
let root = std::path::PathBuf::from(std::env::var_os("HORIZON_TEST_RESOURCES").unwrap());
|
||||
let settings = Settings::default();
|
||||
let loaded = load_resources(
|
||||
root.clone(),
|
||||
None,
|
||||
&root,
|
||||
&settings,
|
||||
std::time::Instant::now(),
|
||||
false,
|
||||
true,
|
||||
&Progress::new(),
|
||||
&mut |_, _| unreachable!(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut options = Options::parse(std::iter::empty()).unwrap().unwrap();
|
||||
options.resources = root.clone();
|
||||
options.execution_role = ExecutionRole::Client;
|
||||
let mut game = Game::new(
|
||||
&loaded.resources,
|
||||
loaded.pack,
|
||||
loaded.gui.unwrap(),
|
||||
options,
|
||||
settings,
|
||||
Audio::silent(&root),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let (client, _server) = horizon_protocol::connection::Pipe::pair();
|
||||
game.connect_local(client, "RemovalTest").unwrap();
|
||||
for (id, kind) in [
|
||||
(40, "minecraft:falling_block"),
|
||||
(41, "minecraft:falling_block"),
|
||||
(42, "minecraft:falling_block"),
|
||||
(43, "minecraft:tnt"),
|
||||
(44, "minecraft:tnt"),
|
||||
(45, "minecraft:experience_orb"),
|
||||
(46, "minecraft:experience_orb"),
|
||||
(47, "minecraft:zombie"),
|
||||
(48, "minecraft:zombie"),
|
||||
] {
|
||||
if matches!(kind, "minecraft:falling_block" | "minecraft:tnt") {
|
||||
assert!(game.spawn_remote_projectile(
|
||||
id,
|
||||
kind,
|
||||
[0.0, 64.0, 0.0],
|
||||
(0.0, 0.0),
|
||||
[0.0; 3],
|
||||
0
|
||||
));
|
||||
} else {
|
||||
game.spawn_entity(id, kind, [0.0, 64.0, 0.0], [0.0; 3]);
|
||||
}
|
||||
}
|
||||
for id in [49, 50] {
|
||||
game.dropped.push(horizon_client::entities::ItemEntity::new(
|
||||
id,
|
||||
horizon_client::items::ItemStack::new("minecraft:sand", 1),
|
||||
horizon_core::geometry::Vec3::new(0.0, 64.0, 0.0),
|
||||
horizon_core::geometry::Vec3::ZERO,
|
||||
0.0,
|
||||
));
|
||||
}
|
||||
game.handle_packet(C::RemoveEntities(play::RemoveEntities {
|
||||
ids: vec![40, 42, 43, 45, 47, 49],
|
||||
}))
|
||||
.unwrap();
|
||||
assert_eq!(game.falling.iter().map(|e| e.id).collect::<Vec<_>>(), [41]);
|
||||
assert_eq!(game.tnts.iter().map(|e| e.id).collect::<Vec<_>>(), [44]);
|
||||
assert_eq!(game.orbs.iter().map(|e| e.id).collect::<Vec<_>>(), [46]);
|
||||
assert_eq!(game.mobs.iter().map(|e| e.id).collect::<Vec<_>>(), [48]);
|
||||
assert_eq!(game.dropped.iter().map(|e| e.id).collect::<Vec<_>>(), [50]);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user