From 90f0254519f5f7c7899d7d1fae2670f2b46962e3 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 1 Sep 2019 09:43:53 -0600 Subject: [PATCH 1/9] Add Util::broadcast() function This function adds an easy way to broadcast a packet to all players who are able to see a given chunk. For example, movement packets for a player can be sent to all players able to see that player's chunk. The function hasn't yet been used, but player action broadcast systems will soon be modified to use `Util::broadcast()`. Also see #78. --- server/src/util/mod.rs | 96 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/server/src/util/mod.rs b/server/src/util/mod.rs index a0fa9d94e..f068e9067 100644 --- a/server/src/util/mod.rs +++ b/server/src/util/mod.rs @@ -1,6 +1,6 @@ //! Assorted utilities for use in Feather's codebase. use bumpalo::Bump; -use feather_core::{ItemStack, Position}; +use feather_core::{ChunkPosition, ItemStack, Packet, Position}; use glm::DVec3; use spawn::Spawner; use thread_local::ThreadLocal; @@ -9,8 +9,12 @@ use thread_local::ThreadLocal; mod macros; mod spawn; +use crate::chunk_logic::ChunkHolders; +use crate::network::{send_packet_to_player, NetworkComponent}; pub use macros::*; pub use spawn::SpawnerSystem; +use specs::storage::GenericReadStorage; +use specs::Entity; /// Converts float-based velocity in blocks per tick /// to the obnoxious format used by the protocol. @@ -42,6 +46,9 @@ pub fn protocol_velocity(vel: DVec3) -> (i16, i16, i16) { /// needs. Note, however, that the entity isn't created /// until the handling dispatcher stage. These functions simply /// redirect to `entity::Spawner`. +/// * `broadcast` - broadcasts a packet to all players +/// who are able to see a given chunk. This can be used +/// to broadcast movement updates, for example. #[derive(Debug, Default)] pub struct Util { /// Thread-local bump allocator, reset @@ -82,4 +89,91 @@ impl Util { bump.reset(); } } + + /// Broadcasts a packet to all players who + /// are able to see a given chunk. + /// + /// The packet is sent instantly, not lazily, + /// unlike many of the `Util` functions. + /// + /// If `neq` is set to an entity, the packet + /// will not be sent to that player. + /// + /// This function runs in linear time with + /// regard to the number of players able to see + /// the chunk. + pub fn broadcast( + &self, + chunk_holders: &ChunkHolders, + networks: &N, + chunk: ChunkPosition, + packet: P, + neq: Option, + ) where + P: Packet + Clone + 'static, + N: GenericReadStorage, + { + // Iterate over entities in the chunk_holders + // entry for the chunk. If they are a player, + // send the packet. + // This works because any player able to see + // a chunk will always have a chunk holder on the chunk. + if let Some(holders) = chunk_holders.holders_for(chunk) { + for entity in holders { + if let Some(neq) = neq.as_ref() { + if *neq == *entity { + continue; + } + } + + // If the entity doesn't have a network component, + // skip it. + if let Some(network) = networks.get(*entity) { + send_packet_to_player(&network, packet.clone()); + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::testframework as t; + use feather_core::network::packet::implementation::EntityHeadLook; + use feather_core::PacketType; + use specs::WorldExt; + + #[test] + fn test_broadcast() { + let mut chunk_holders = ChunkHolders::default(); + + let chunk = ChunkPosition::new(0, 0); + let other_chunk = ChunkPosition::new(10, 1); + + let (mut world, _) = t::builder().build(); + let player1 = t::add_player(&mut world); + let player2 = t::add_player(&mut world); + let player3 = t::add_player(&mut world); + + chunk_holders.insert_holder(chunk, player1.entity); + chunk_holders.insert_holder(chunk, player2.entity); + chunk_holders.insert_holder(other_chunk, player3.entity); + + let packet = EntityHeadLook::default(); + + let util = Util::default(); + + util.broadcast( + &chunk_holders, + &world.read_component(), + chunk, + packet, + Some(player2.entity), + ); + + t::assert_packet_received(&player1, PacketType::EntityHeadLook); + t::assert_packet_not_received(&player2, PacketType::EntityHeadLook); + t::assert_packet_not_received(&player3, PacketType::EntityHeadLook); + } } From 5c47d95c43f6fdb9bc943a82fe8de0457e47c4f5 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 1 Sep 2019 09:55:00 -0600 Subject: [PATCH 2/9] Add `ChunkEntites::entities_within_view_distance()` This function efficiently finds all entities within the view distance of a player. It can be used to, for example, send initial entities to the player when they join. However, it should not be used for broadcasting packets; `Util::broadcast` is more efficient in this regard, since it does not have to iterate over `view_distance^2` chunks. See also #78. --- server/src/entity/chunk.rs | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index 888afad2a..157b2caa9 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -58,6 +58,28 @@ impl ChunkEntities { self.0.remove(&chunk); } } + + /// Returns a vector of all entities in all chunks + /// within the given view distance of another chunk. + pub fn entites_within_view_distance( + &self, + chunk: ChunkPosition, + view_distance: u8, + ) -> Vec { + let mut result = Vec::with_capacity(16); + + let view_distance = i32::from(view_distance); + + for x_offset in -view_distance..=view_distance { + for z_offset in -view_distance..=view_distance { + let chunk = ChunkPosition::new(chunk.x + x_offset, chunk.z + z_offset); + + result.extend_from_slice(self.entities_in_chunk(chunk)); + } + } + + result + } } /// System for updating the `ChunkEntities`. @@ -138,6 +160,7 @@ mod tests { use crate::entity::EntityType; use crate::testframework as t; use feather_core::Position; + use hashbrown::HashSet; use specs::{Builder, World, WorldExt}; #[test] @@ -223,4 +246,36 @@ mod tests { let chunk_entities = w.fetch::(); assert!(chunk_entities.entities_in_chunk(pos.chunk_pos()).is_empty()); } + + #[test] + fn test_entities_within_view_distance() { + let mut chunk_entities = ChunkEntities::default(); + + let mut world = World::new(); + let entity1 = world.create_entity().build(); + let entity2 = world.create_entity().build(); + let entity3 = world.create_entity().build(); + let entity4 = world.create_entity().build(); + + let chunk1 = ChunkPosition::new(0, 0); + let chunk2 = ChunkPosition::new(0, 4); + let chunk3 = ChunkPosition::new(0, 5); + let chunk4 = ChunkPosition::new(-4, -4); + + chunk_entities.add_to_chunk(chunk1, entity1); + chunk_entities.add_to_chunk(chunk2, entity2); + chunk_entities.add_to_chunk(chunk3, entity3); + chunk_entities.add_to_chunk(chunk4, entity4); + + let view_distance = 4; + let entities: HashSet = chunk_entities + .entites_within_view_distance(chunk1, view_distance) + .into_iter() + .collect(); + + assert!(entities.contains(&entity1)); + assert!(entities.contains(&entity2)); + assert!(!entities.contains(&entity3)); + assert!(entities.contains(&entity4)); + } } From bd15887ca19b430697576e5e4029f4639073623d Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 1 Sep 2019 23:55:33 -0600 Subject: [PATCH 3/9] Implement basic view distance-based entity creation and destruction on client --- core/src/network/packet/implementation.rs | 2 +- server/src/entity/broadcast.rs | 235 +++++++++++++++------- server/src/entity/chunk.rs | 13 +- server/src/entity/mod.rs | 11 +- server/src/player/mod.rs | 13 +- server/src/player/movement.rs | 23 +++ server/src/player/view.rs | 192 ++++++++++++++++++ server/src/systems.rs | 2 + server/src/testframework.rs | 14 ++ 9 files changed, 418 insertions(+), 87 deletions(-) create mode 100644 server/src/player/view.rs diff --git a/core/src/network/packet/implementation.rs b/core/src/network/packet/implementation.rs index 190478e6d..08f98ec5f 100644 --- a/core/src/network/packet/implementation.rs +++ b/core/src/network/packet/implementation.rs @@ -1521,7 +1521,7 @@ pub struct UseBed { #[derive(Default, AsAny, new, Clone)] pub struct DestroyEntities { - entity_ids: Vec, + pub entity_ids: Vec, } impl Packet for DestroyEntities { diff --git a/server/src/entity/broadcast.rs b/server/src/entity/broadcast.rs index e982ecd9e..d3795b5f1 100644 --- a/server/src/entity/broadcast.rs +++ b/server/src/entity/broadcast.rs @@ -1,20 +1,96 @@ //! Module for broadcasting when an entity comes within -//! range of a player. +//! range of a player. Also handles sending the correct +//! packet to spawn entities on the client. +//! +//! Sending entities to a client is handled lazily: +//! an internal queue is kept of entities to send to players, +//! and each tick, a system flushes this queue and sends +//! the correct packet. This is done because of the number +//! of components which need to be accessed to send an entity +//! to a player. +use crate::chunk_logic::ChunkHolders; use crate::entity::movement::degrees_to_stops; use crate::entity::{EntityType, VelocityComponent}; use crate::entity::{Metadata, NamedComponent, PositionComponent}; -use crate::network::{send_packet_to_all_players, NetworkComponent}; +use crate::network::{send_packet_boxed_to_player, send_packet_to_player, NetworkComponent}; use crate::util::protocol_velocity; +use crossbeam::queue::SegQueue; use feather_core::network::packet::implementation::SpawnObject; use feather_core::network::packet::implementation::{PacketEntityMetadata, SpawnPlayer}; +use feather_core::Packet; use shrev::EventChannel; -use specs::{ - Entities, Entity, Read, ReadStorage, ReaderId, System, SystemData, World, WriteStorage, -}; +use specs::{Entity, Read, ReadStorage, ReaderId, System, WriteStorage}; use uuid::Uuid; -//const ITEM_OBJECT_ID: i8 = 2; +/// Handles lazy sending of entities to a client. +#[derive(Debug, Default)] +pub struct EntitySender { + /// A queue of entities to lazily send. + queue: SegQueue, +} + +impl EntitySender { + /// Lazily sends an entity to a client. + pub fn send_entity_to_player(&self, player: Entity, entity: Entity) { + self.queue.push(SendRequest { player, entity }) + } +} + +/// An entity send request, containing +/// the player to send to and the entity +/// to send. +#[derive(Debug)] +struct SendRequest { + player: Entity, + entity: Entity, +} + +/// System for flushing the `EntitySender` queue +/// and sending the correct packets for the given +/// entities. +pub struct EntitySendSystem; + +impl<'a> System<'a> for EntitySendSystem { + type SystemData = ( + ReadStorage<'a, PositionComponent>, + ReadStorage<'a, NamedComponent>, + ReadStorage<'a, NetworkComponent>, + ReadStorage<'a, VelocityComponent>, + ReadStorage<'a, EntityType>, + WriteStorage<'a, Metadata>, + Read<'a, EntitySender>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (positions, nameds, networks, velocities, types, mut metadatas, entity_sender) = data; + + while let Ok(request) = entity_sender.queue.pop() { + let ty = types.get(request.entity).unwrap(); + let metadata = metadatas.get_mut(request.entity).unwrap(); + let position = positions.get(request.entity).unwrap(); + let velocity = velocities.get(request.entity); + let named = nameds.get(request.entity); + + let network = networks.get(request.player).unwrap(); + + debug!("Sending ({:?}, {:?}) to player", ty, named); + + // Send corresponding packet to player. + let packet = + packet_to_spawn_entity(request.entity, *ty, &position, metadata, velocity, named); + send_packet_boxed_to_player(&network, packet); + + // Send metadata. + let entity_metadata = PacketEntityMetadata { + entity_id: request.entity.id() as i32, + metadata: metadata.to_raw_metadata(), + }; + + send_packet_to_player(network, entity_metadata); + } + } +} /// Event triggered when an entity of any /// type is spawned. @@ -28,8 +104,8 @@ pub struct EntitySpawnEvent { /// System for broadcasting when an entity is spawned. /// -/// Different entity types require different packets -/// to send. +/// Broadcasts are lazily queued for sending +/// and are sent by `EntitySendSystem`. /// /// This system listens to `EntitySpawnEvent`s. #[derive(Default)] @@ -40,85 +116,102 @@ pub struct EntityBroadcastSystem { impl<'a> System<'a> for EntityBroadcastSystem { type SystemData = ( ReadStorage<'a, PositionComponent>, - ReadStorage<'a, NamedComponent>, ReadStorage<'a, NetworkComponent>, - ReadStorage<'a, VelocityComponent>, - WriteStorage<'a, Metadata>, + Read<'a, ChunkHolders>, + Read<'a, EntitySender>, Read<'a, EventChannel>, - Entities<'a>, ); fn run(&mut self, data: Self::SystemData) { - let (positions, nameds, networks, velocities, mut metadatas, events, entities) = data; - - for event in events.read(&mut self.reader.as_mut().unwrap()) { - let position = positions.get(event.entity).unwrap(); - let metadata = metadatas.get_mut(event.entity).unwrap(); - let velocity = velocities.get(event.entity).cloned().unwrap_or_default(); - let (velocity_x, velocity_y, velocity_z) = protocol_velocity(*velocity); - - // Send spawn packet to clients. - // The packet type depends on the type - // of entity. - - // The Player Info packet was already sent by `JoinBroadcastSystem`. - match event.ty { - EntityType::Player => { - let named = nameds.get(event.entity).unwrap(); - let packet = SpawnPlayer { - entity_id: event.entity.id() as i32, - player_uuid: named.uuid, - x: position.current.x, - y: position.current.y, - z: position.current.z, - yaw: degrees_to_stops(position.current.yaw), - pitch: degrees_to_stops(position.current.pitch), - metadata: metadata.to_raw_metadata(), - }; - - send_packet_to_all_players(&networks, &entities, packet, Some(event.entity)); - } - EntityType::Item => { - let packet = SpawnObject { - entity_id: event.entity.id() as i32, - object_uuid: Uuid::new_v4(), - ty: 2, // Type 2 for item stack - x: position.current.x, - y: position.current.y, - z: position.current.z, - pitch: degrees_to_stops(position.current.pitch), - yaw: degrees_to_stops(position.current.yaw), - data: 1, // Has velocity - velocity_x, - velocity_y, - velocity_z, - }; - - send_packet_to_all_players(&networks, &entities, packet, Some(event.entity)); + let (positions, networks, chunk_holders, entity_sender, spawn_events) = data; + + for event in spawn_events.read(self.reader.as_mut().unwrap()) { + // Broadcast entity to players who can see it. + let position = match positions.get(event.entity) { + Some(position) => position, + None => continue, + }; + let chunk = position.current.chunk_pos(); + + if let Some(holders) = chunk_holders.holders_for(chunk) { + for holder in holders { + if networks.get(*holder).is_none() { + // Not a player. + continue; + } + + // Don't send player to themself. + if *holder == event.entity { + continue; + } + + entity_sender.send_entity_to_player(*holder, event.entity); } - _ => unimplemented!(), } + } + } - // Send metadata. - let entity_metadata = PacketEntityMetadata { - entity_id: event.entity.id() as i32, + setup_impl!(reader); +} + +/// Returns the packet needed to spawn an entity +/// with given type, position, metadata, optional velocity, +/// and optional name. +fn packet_to_spawn_entity( + entity: Entity, + ty: EntityType, + position: &PositionComponent, + metadata: &mut Metadata, + velocity: Option<&VelocityComponent>, + named: Option<&NamedComponent>, +) -> Box { + let velocity = velocity.cloned().unwrap_or_default(); // Use default velocity of (0, 0, 0) + let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocity.0); + + // Different entity types require different + // packets to send. + match ty { + EntityType::Player => { + let named = named.unwrap(); + let packet = SpawnPlayer { + entity_id: entity.id() as i32, + player_uuid: named.uuid, + x: position.current.x, + y: position.current.y, + z: position.current.z, + yaw: degrees_to_stops(position.current.yaw), + pitch: degrees_to_stops(position.current.pitch), metadata: metadata.to_raw_metadata(), }; - send_packet_to_all_players(&networks, &entities, entity_metadata, None); - // Players should know their own metadata - } - } - fn setup(&mut self, world: &mut World) { - Self::SystemData::setup(world); + Box::new(packet) + } + EntityType::Item => { + let packet = SpawnObject { + entity_id: entity.id() as i32, + object_uuid: Uuid::new_v4(), + ty: 2, // Type 2 for item stack + x: position.current.x, + y: position.current.y, + z: position.current.z, + pitch: degrees_to_stops(position.current.pitch), + yaw: degrees_to_stops(position.current.yaw), + data: 1, // Has velocity + velocity_x, + velocity_y, + velocity_z, + }; - self.reader = Some(world.fetch_mut::>().register_reader()); + Box::new(packet) + } + _ => unimplemented!(), } } #[cfg(test)] mod tests { use super::*; + use crate::player::ChunkCrossSystem; use crate::testframework as t; use feather_core::network::cast_packet; use feather_core::network::packet::PacketType; @@ -152,7 +245,9 @@ mod tests { #[test] fn test_spawn_item() { let (mut w, mut d) = t::builder() - .with(EntityBroadcastSystem::default(), "") + .with(EntityBroadcastSystem::default(), "broadcast") + .with(ChunkCrossSystem::default(), "chunk_cross") + .with_dep(EntitySendSystem, "", &["broadcast", "chunk_cross"]) .build(); let player = t::add_player(&mut w); diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index 157b2caa9..491803c9d 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -5,6 +5,7 @@ use crate::entity::{EntityDestroyEvent, EntitySpawnEvent, PositionComponent}; use feather_core::world::ChunkPosition; use fnv::FnvHashMap; +use hashbrown::HashSet; use shrev::EventChannel; use specs::storage::ComponentEvent; use specs::{ @@ -65,8 +66,8 @@ impl ChunkEntities { &self, chunk: ChunkPosition, view_distance: u8, - ) -> Vec { - let mut result = Vec::with_capacity(16); + ) -> HashSet { + let mut result = HashSet::new(); let view_distance = i32::from(view_distance); @@ -74,7 +75,7 @@ impl ChunkEntities { for z_offset in -view_distance..=view_distance { let chunk = ChunkPosition::new(chunk.x + x_offset, chunk.z + z_offset); - result.extend_from_slice(self.entities_in_chunk(chunk)); + result.extend(self.entities_in_chunk(chunk)); } } @@ -160,7 +161,6 @@ mod tests { use crate::entity::EntityType; use crate::testframework as t; use feather_core::Position; - use hashbrown::HashSet; use specs::{Builder, World, WorldExt}; #[test] @@ -268,10 +268,7 @@ mod tests { chunk_entities.add_to_chunk(chunk4, entity4); let view_distance = 4; - let entities: HashSet = chunk_entities - .entites_within_view_distance(chunk1, view_distance) - .into_iter() - .collect(); + let entities = chunk_entities.entites_within_view_distance(chunk1, view_distance); assert!(entities.contains(&entity1)); assert!(entities.contains(&entity2)); diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 419639073..48c8753a2 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -12,10 +12,12 @@ mod movement; mod types; use crate::systems::{ - CHUNK_ENTITIES_UPDATE, ENTITY_DESTROY, ENTITY_DESTROY_BROADCAST, ENTITY_METADATA_BROADCAST, - ENTITY_MOVE_BROADCAST, ENTITY_SPAWN_BROADCAST, ENTITY_VELOCITY_BROADCAST, ITEM_COLLECT, - ITEM_MERGE, ITEM_SPAWN, JOIN_BROADCAST, + CHUNK_CROSS, CHUNK_ENTITIES_UPDATE, ENTITY_DESTROY, ENTITY_DESTROY_BROADCAST, + ENTITY_METADATA_BROADCAST, ENTITY_MOVE_BROADCAST, ENTITY_SEND, ENTITY_SPAWN_BROADCAST, + ENTITY_VELOCITY_BROADCAST, ITEM_COLLECT, ITEM_MERGE, ITEM_SPAWN, JOIN_BROADCAST, }; +pub use broadcast::EntitySendSystem; +pub use broadcast::EntitySender; pub use broadcast::EntitySpawnEvent; pub use chunk::ChunkEntities; pub use component::{NamedComponent, PlayerComponent, PositionComponent, VelocityComponent}; @@ -65,8 +67,9 @@ pub fn init_broadcast(dispatcher: &mut DispatcherBuilder) { dispatcher.add( EntityBroadcastSystem::default(), ENTITY_SPAWN_BROADCAST, - &[JOIN_BROADCAST], + &[JOIN_BROADCAST, CHUNK_CROSS], ); + dispatcher.add(EntitySendSystem, ENTITY_SEND, &[ENTITY_SPAWN_BROADCAST]); dispatcher.add( EntityVelocityBroadcastSystem::default(), ENTITY_VELOCITY_BROADCAST, diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index bb6e11b94..fb3a3a695 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -22,10 +22,13 @@ mod movement; /// Module for handling player block placements. mod placement; mod resource_pack; +mod view; pub use broadcast::PlayerDisconnectEvent; -pub use movement::{send_chunk_to_player, ChunkPendingComponent, LoadedChunksComponent}; +pub use movement::{ + send_chunk_to_player, ChunkCrossSystem, ChunkPendingComponent, LoadedChunksComponent, +}; pub use animation::PlayerAnimationEvent; @@ -35,11 +38,12 @@ pub use digging::{BlockUpdateCause, BlockUpdateEvent, PlayerItemDropEvent}; use crate::player::inventory::SetSlotSystem; use crate::player::placement::BlockPlacementSystem; +use crate::player::view::ViewUpdateSystem; use crate::systems::{ ANIMATION_BROADCAST, BLOCK_BREAK_BROADCAST, BLOCK_PLACEMENT, CHAT_BROADCAST, CHUNK_CROSS, CHUNK_SEND, CLIENT_CHUNK_UNLOAD, CREATIVE_INVENTORY, DISCONNECT_BROADCAST, EQUIPMENT_SEND, HELD_ITEM_BROADCAST, HELD_ITEM_CHANGE, JOIN_BROADCAST, NETWORK, PLAYER_ANIMATION, PLAYER_CHAT, - PLAYER_DIGGING, PLAYER_INIT, PLAYER_MOVEMENT, RESOURCE_PACK_SEND, SET_SLOT, + PLAYER_DIGGING, PLAYER_INIT, PLAYER_MOVEMENT, RESOURCE_PACK_SEND, SET_SLOT, VIEW_UPDATE, }; use animation::{AnimationBroadcastSystem, PlayerAnimationSystem}; use broadcast::{DisconnectBroadcastSystem, JoinBroadcastSystem}; @@ -50,8 +54,8 @@ use init::PlayerInitSystem; use inventory::{ CreativeInventorySystem, EquipmentSendSystem, HeldItemBroadcastSystem, HeldItemChangeSystem, }; -use movement::{ChunkCrossSystem, ChunkSendSystem, ClientChunkUnloadSystem, PlayerMovementSystem}; -pub use resource_pack::ResourcePackSendSystem; +use movement::{ChunkSendSystem, ClientChunkUnloadSystem, PlayerMovementSystem}; +use resource_pack::ResourcePackSendSystem; use specs::DispatcherBuilder; pub const PLAYER_EYE_HEIGHT: f64 = 1.62; @@ -74,6 +78,7 @@ pub fn init_handlers(dispatcher: &mut DispatcherBuilder) { } pub fn init_broadcast(dispatcher: &mut DispatcherBuilder) { + dispatcher.add(ViewUpdateSystem::default(), VIEW_UPDATE, &[]); dispatcher.add(HeldItemBroadcastSystem::default(), HELD_ITEM_BROADCAST, &[]); dispatcher.add(JoinBroadcastSystem::default(), JOIN_BROADCAST, &[]); dispatcher.add( diff --git a/server/src/player/movement.rs b/server/src/player/movement.rs index 7d28aebd4..ffa94dcb5 100644 --- a/server/src/player/movement.rs +++ b/server/src/player/movement.rs @@ -163,6 +163,19 @@ impl Component for ChunkPendingComponent { /// that it is unloaded. const CHUNK_UNLOAD_TIME: u64 = TPS * 5; // 5 seconds +/// Event which is triggered when a player crosses +/// chunk boundaries, causing their position's chunk +/// to change. +#[derive(Debug, Clone)] +pub struct ChunkCrossEvent { + /// The player affected by this event. + pub player: Entity, + /// The old chunk position. + pub old: ChunkPosition, + /// The new chunk position. + pub new: ChunkPosition, +} + /// System that checks when a player crosses chunk boundaries. /// When the player does so, the system sends Chunk Data packets /// for chunks within the view distance and also unloads @@ -183,6 +196,7 @@ impl<'a> System<'a> for ChunkCrossSystem { WriteStorage<'a, ChunkHolderComponent>, ReadStorage<'a, NetworkComponent>, Write<'a, ChunkHolders>, + Write<'a, EventChannel>, Read<'a, ChunkWorkerHandle>, Read<'a, LazyUpdate>, Entities<'a>, @@ -198,6 +212,7 @@ impl<'a> System<'a> for ChunkCrossSystem { mut chunk_holder_comps, net_comps, mut holders, + mut cross_events, chunk_handle, lazy, entities, @@ -264,6 +279,14 @@ impl<'a> System<'a> for ChunkCrossSystem { let time = tick_count.0 + CHUNK_UNLOAD_TIME; loaded_chunks.unload_queue.push_back((chunk, time)); } + + // Trigger chunk cross event. + let event = ChunkCrossEvent { + player, + old: old_chunk_pos, + new: new_chunk_pos, + }; + cross_events.single_write(event); } } } diff --git a/server/src/player/view.rs b/server/src/player/view.rs new file mode 100644 index 000000000..ac6df31af --- /dev/null +++ b/server/src/player/view.rs @@ -0,0 +1,192 @@ +//! This module implements creating and destroying +//! entities on the client when a player moves. +//! +//! When a player crosses chunk boundaries, the following +//! takes place: +//! * We send a `Destroy Entities` packet containing all +//! entities which are no longer within the view distance. +//! * We spawn an entity on the client for every entity +//! which is now within the view distance. +//! +//! This is handled by `ViewUpdateSystem`, which listens +//! to `ChunkCrossEvent`s. + +use crate::config::Config; +use crate::entity::{ChunkEntities, EntitySender}; +use crate::network::{send_packet_to_player, NetworkComponent}; +use crate::player::movement::ChunkCrossEvent; +use feather_core::network::packet::implementation::DestroyEntities; +use shrev::EventChannel; +use specs::{Read, ReadStorage, ReaderId, System}; +use std::sync::Arc; + +/// System for updating entities visible +/// by the client. +#[derive(Default)] +pub struct ViewUpdateSystem { + reader: Option>, +} + +impl<'a> System<'a> for ViewUpdateSystem { + type SystemData = ( + ReadStorage<'a, NetworkComponent>, + Read<'a, EventChannel>, + Read<'a, ChunkEntities>, + Read<'a, Arc>, + Read<'a, EntitySender>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (networks, cross_events, chunk_entities, config, entity_sender) = data; + + for event in cross_events.read(self.reader.as_mut().unwrap()) { + // Find new and old entities. + let old_entities = + chunk_entities.entites_within_view_distance(event.old, config.server.view_distance); + let new_entities = + chunk_entities.entites_within_view_distance(event.new, config.server.view_distance); + + let mut to_destroy = vec![]; + + // Compute entities which are only present in one of the sets. + // If an entity is only present in `old_entities` and not `new_entities`, + // it should be destroyed on the client. + // If an entity is only present in `new_entities`, it should be spawned. + for entity in old_entities.symmetric_difference(&new_entities) { + if *entity == event.player { + continue; + } + + if old_entities.contains(entity) { + // Entity is in `old_entities` but not in `new_entities`. + // Destroy it. If the entity is a player, also destroy this player + // on the client. + to_destroy.push(entity.id() as i32); + + debug!("Destroying {} on client", entity.id()); + + if let Some(network) = networks.get(*entity) { + let packet = DestroyEntities { + entity_ids: vec![event.player.id() as i32], + }; + send_packet_to_player(network, packet); + debug!("Destroying {} on client", event.player.id()); + } + } else { + // Entity is in `new_entities` but not in `old_entities`. + // Spawn it. If the entity is a player, also send this player + // to that entity. + entity_sender.send_entity_to_player(event.player, *entity); + + if networks.get(*entity).is_some() { + entity_sender.send_entity_to_player(*entity, event.player); + } + } + } + + if !to_destroy.is_empty() { + let packet = DestroyEntities { + entity_ids: to_destroy, + }; + send_packet_to_player(&networks.get(event.player).unwrap(), packet); + } + } + } + + setup_impl!(reader); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::entity::{EntitySendSystem, EntityType}; + use crate::testframework as t; + use feather_core::network::cast_packet; + use feather_core::network::packet::implementation::{SpawnObject, SpawnPlayer}; + use feather_core::{ChunkPosition, PacketType}; + use hashbrown::HashSet; + use specs::WorldExt; + + #[test] + fn test_view_update_system() { + let (mut world, mut dispatcher) = t::builder() + .with(ViewUpdateSystem::default(), "view") + .with_dep(EntitySendSystem, "", &["view"]) + .build(); + + let player_chunk = ChunkPosition::new(0, 0); + + let player1 = t::add_player(&mut world); + let player2 = t::add_player(&mut world); + + let entity1 = t::add_entity(&mut world, EntityType::Item, false); + let entity2 = t::add_entity(&mut world, EntityType::Item, false); + let entity3 = t::add_entity(&mut world, EntityType::Item, false); + let entity4 = t::add_entity(&mut world, EntityType::Item, false); + + let mut config = Config::default(); + config.server.view_distance = 4; + world.insert(Arc::new(config)); + + { + let mut chunk_entities = world.fetch_mut::(); + chunk_entities.add_to_chunk(player_chunk, player1.entity); + chunk_entities.add_to_chunk(player_chunk, player2.entity); + chunk_entities.add_to_chunk(ChunkPosition::new(4, -4), entity1); + chunk_entities.add_to_chunk(player_chunk, entity2); + chunk_entities.add_to_chunk(ChunkPosition::new(5, -4), entity3); + chunk_entities.add_to_chunk(ChunkPosition::new(100, 103), entity4); + } + + let event = ChunkCrossEvent { + player: player1.entity, + old: ChunkPosition::new(100, 103), + new: player_chunk, + }; + t::trigger_event(&world, event); + + dispatcher.dispatch(&world); + world.maintain(); + + let packets = t::received_packets(&player1, None); + + let mut received_spawns = HashSet::new(); + + for packet in packets { + match packet.ty() { + PacketType::DestroyEntities => { + let packet = cast_packet::(&*packet); + let destroyed = packet.entity_ids.iter().cloned().collect::>(); + assert_eq!(destroyed.len(), 1); + assert!(destroyed.contains(&(entity4.id() as i32))); + } + PacketType::SpawnObject => { + let packet = cast_packet::(&*packet); + received_spawns.insert(packet.entity_id); + } + PacketType::SpawnPlayer => { + let packet = cast_packet::(&*packet); + received_spawns.insert(packet.entity_id); + } + _ => (), + } + } + + println!("{:?}", received_spawns); + assert_eq!(received_spawns.len(), 3); + assert!(received_spawns.contains(&(entity1.id() as i32))); + assert!(received_spawns.contains(&(entity2.id() as i32))); + assert!(received_spawns.contains(&(player2.entity.id() as i32))); + + // Confirm that `player1` was sent to `player2`. + let packets = t::received_packets(&player2, None); + assert_eq!(packets.len(), 2); // One for Spawn Player, one for metadata + + let packet = packets + .into_iter() + .find(|packet| packet.ty() == PacketType::SpawnPlayer) + .unwrap(); + let packet = cast_packet::(&*packet); + assert_eq!(packet.entity_id, player1.entity.id() as i32); + } +} diff --git a/server/src/systems.rs b/server/src/systems.rs index 9732c2e6b..5ed258512 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -22,6 +22,7 @@ pub const CHUNK_CROSS: &str = "chunk_cross"; pub const PLAYER_INIT: &str = "player_init"; pub const CLIENT_CHUNK_UNLOAD: &str = "client_chunk_unload"; +pub const VIEW_UPDATE: &str = "view_update"; pub const HELD_ITEM_BROADCAST: &str = "held_item_broadcast"; pub const JOIN_BROADCAST: &str = "join_broadcast"; pub const DISCONNECT_BROADCAST: &str = "disconnect_broadcast"; @@ -44,6 +45,7 @@ pub const ITEM_MERGE: &str = "item_merge"; pub const ENTITY_MOVE_BROADCAST: &str = "entity_move_broadcast"; pub const ENTITY_SPAWN_BROADCAST: &str = "entity_spawn_broadcast"; +pub const ENTITY_SEND: &str = "entity_send"; pub const ENTITY_VELOCITY_BROADCAST: &str = "entity_velocity_broadcast"; pub const ENTITY_DESTROY_BROADCAST: &str = "entity_destroy_broadcast"; pub const ENTITY_METADATA_BROADCAST: &str = "entity_metadata_broadcast"; diff --git a/server/src/testframework.rs b/server/src/testframework.rs index 44e339116..4c34e7117 100644 --- a/server/src/testframework.rs +++ b/server/src/testframework.rs @@ -29,6 +29,7 @@ use shrev::EventChannel; use crate::entity::metadata::{self, Metadata}; +use crate::chunk_logic::ChunkHolders; use feather_core::world::block::Block; /// Initializes a Specs world and dispatcher @@ -81,8 +82,19 @@ pub fn add_player(world: &mut World) -> Player { }) .with(InventoryComponent::default()) .with(Metadata::Player(metadata::Player::default())) + .with(EntityType::Player) .build(); + let mut chunk_holders = world.fetch_mut::(); + + let view_distance = i32::from(world.fetch::>().server.view_distance); + + for x in -view_distance..=view_distance { + for z in -view_distance..=view_distance { + chunk_holders.insert_holder(ChunkPosition::new(x, z), e); + } + } + Player { entity: e, network_sender: ns2, @@ -358,6 +370,8 @@ impl<'a, 'b> TestBuilder<'a, 'b> { .insert(EventChannel::::new()); self.world.insert(EventChannel::::new()); self.world.insert(crate::time::Time(0)); + self.world.insert(ChunkHolders::default()); + self.world.insert(Arc::new(Config::default())); let mut dispatcher = self.dispatcher.build(); dispatcher.setup(&mut self.world); From 8a53f6aa522d158435c6e2f6473705349cb67f4b Mon Sep 17 00:00:00 2001 From: caelunshun Date: Tue, 3 Sep 2019 16:35:49 -0600 Subject: [PATCH 4/9] Break everything --- server/src/entity/broadcast.rs | 32 +++++++++- server/src/entity/item.rs | 5 +- server/src/entity/metadata.rs | 15 ++--- server/src/entity/movement.rs | 45 +++++++------- server/src/main.rs | 3 +- server/src/player/animation.rs | 18 ++---- server/src/player/broadcast.rs | 48 +++++++++------ server/src/player/digging.rs | 12 ++-- server/src/player/inventory.rs | 10 +-- server/src/systems.rs | 2 + server/src/testframework.rs | 25 +++++--- server/src/util/broadcaster.rs | 109 +++++++++++++++++++++++++++++++++ server/src/util/mod.rs | 74 ++++++++++------------ 13 files changed, 271 insertions(+), 127 deletions(-) create mode 100644 server/src/util/broadcaster.rs diff --git a/server/src/entity/broadcast.rs b/server/src/entity/broadcast.rs index d3795b5f1..18dd07c02 100644 --- a/server/src/entity/broadcast.rs +++ b/server/src/entity/broadcast.rs @@ -20,7 +20,7 @@ use feather_core::network::packet::implementation::SpawnObject; use feather_core::network::packet::implementation::{PacketEntityMetadata, SpawnPlayer}; use feather_core::Packet; use shrev::EventChannel; -use specs::{Entity, Read, ReadStorage, ReaderId, System, WriteStorage}; +use specs::{Entity, Read, ReadStorage, ReaderId, System, Write, WriteStorage}; use uuid::Uuid; /// Handles lazy sending of entities to a client. @@ -46,6 +46,17 @@ struct SendRequest { entity: Entity, } +/// Event which is triggered when an entity +/// is sent to a client. This can be used to send +/// associated information, such as entity equipment. +#[derive(Debug, Clone)] +pub struct EntitySendEvent { + /// The player for which this event was triggered. + pub player: Entity, + /// The entity which was sent to the player. + pub entity: Entity, +} + /// System for flushing the `EntitySender` queue /// and sending the correct packets for the given /// entities. @@ -59,11 +70,21 @@ impl<'a> System<'a> for EntitySendSystem { ReadStorage<'a, VelocityComponent>, ReadStorage<'a, EntityType>, WriteStorage<'a, Metadata>, + Write<'a, EventChannel>, Read<'a, EntitySender>, ); fn run(&mut self, data: Self::SystemData) { - let (positions, nameds, networks, velocities, types, mut metadatas, entity_sender) = data; + let ( + positions, + nameds, + networks, + velocities, + types, + mut metadatas, + mut send_events, + entity_sender, + ) = data; while let Ok(request) = entity_sender.queue.pop() { let ty = types.get(request.entity).unwrap(); @@ -88,6 +109,13 @@ impl<'a> System<'a> for EntitySendSystem { }; send_packet_to_player(network, entity_metadata); + + // Trigger event. + let event = EntitySendEvent { + player: request.player, + entity: request.entity, + }; + send_events.single_write(event); } } } diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index 71e50cf35..f5f7ab632 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -1,4 +1,5 @@ //! Logic for working with item entities. +use crate::chunk_logic::ChunkHolders; use crate::entity::metadata::{self, Metadata}; use crate::entity::{ChunkEntities, EntityDestroyEvent, PlayerComponent, PositionComponent}; use crate::network::{send_packet_to_all_players, NetworkComponent}; @@ -212,6 +213,7 @@ impl<'a> System<'a> for ItemCollectSystem { Write<'a, EventChannel>, Write<'a, EventChannel>, Read<'a, ChunkEntities>, + Read<'a, Util>, Read<'a, TickCount>, Entities<'a>, ); @@ -227,6 +229,7 @@ impl<'a> System<'a> for ItemCollectSystem { mut inventory_events, mut destroy_events, chunk_entities, + util, tick, entities, ) = data; @@ -284,7 +287,7 @@ impl<'a> System<'a> for ItemCollectSystem { collector: player.id() as i32, count: i32::from(stack.amount - amount_left), }; - send_packet_to_all_players(&networks, &entities, packet, None); + util.broadcast_entity(player, packet, None); if amount_left == 0 { entities.delete(other).unwrap(); diff --git a/server/src/entity/metadata.rs b/server/src/entity/metadata.rs index 6a4a6e665..293c4ae07 100644 --- a/server/src/entity/metadata.rs +++ b/server/src/entity/metadata.rs @@ -3,12 +3,13 @@ #![allow(clippy::too_many_arguments)] // TODO: builder patterm use crate::network::{send_packet_to_all_players, NetworkComponent}; +use crate::util::Util; use feather_core::packet::PacketEntityMetadata; use feather_core::{EntityMetadata, Slot}; use specs::storage::ComponentEvent; use specs::{ - BitSet, Component, Entities, FlaggedStorage, Join, ReadStorage, ReaderId, System, VecStorage, - WriteStorage, + BitSet, Component, Entities, FlaggedStorage, Join, Read, ReadStorage, ReaderId, System, + VecStorage, WriteStorage, }; bitflags! { @@ -61,14 +62,10 @@ pub struct MetadataBroadcastSystem { } impl<'a> System<'a> for MetadataBroadcastSystem { - type SystemData = ( - WriteStorage<'a, Metadata>, - ReadStorage<'a, NetworkComponent>, - Entities<'a>, - ); + type SystemData = (WriteStorage<'a, Metadata>, Read<'a, Util>, Entities<'a>); fn run(&mut self, data: Self::SystemData) { - let (mut metadatas, networks, entities) = data; + let (mut metadatas, util, entities) = data; self.dirty.clear(); @@ -85,7 +82,7 @@ impl<'a> System<'a> for MetadataBroadcastSystem { metadata: metadata.to_raw_metadata(), }; - send_packet_to_all_players(&networks, &entities, packet, None); + util.broadcast(entity, packet, None); } metadatas.set_event_emission(true); diff --git a/server/src/entity/movement.rs b/server/src/entity/movement.rs index 2249c0fa7..a5654c728 100644 --- a/server/src/entity/movement.rs +++ b/server/src/entity/movement.rs @@ -1,14 +1,15 @@ use specs::storage::ComponentEvent; -use specs::{BitSet, Entities, Entity, Join, ReadStorage, ReaderId, System}; +use specs::{BitSet, Entities, Entity, Join, Read, ReadStorage, ReaderId, System}; use feather_core::network::packet::implementation::{ EntityHeadLook, EntityLook, EntityLookAndRelativeMove, EntityRelativeMove, EntityVelocity, }; use feather_core::world::Position; +use crate::chunk_logic::ChunkHolders; use crate::entity::{PositionComponent, VelocityComponent}; -use crate::network::{send_packet_to_all_players, NetworkComponent}; -use crate::util::protocol_velocity; +use crate::network::NetworkComponent; +use crate::util::{protocol_velocity, Util}; /// System for broadcasting when an entity moves. #[derive(Default)] @@ -19,13 +20,15 @@ pub struct EntityMoveBroadcastSystem { impl<'a> System<'a> for EntityMoveBroadcastSystem { type SystemData = ( - ReadStorage<'a, NetworkComponent>, ReadStorage<'a, PositionComponent>, + ReadStorage<'a, NetworkComponent>, + Read<'a, Util>, + Read<'a, ChunkHolders>, Entities<'a>, ); fn run(&mut self, data: Self::SystemData) { - let (networks, positions, entities) = data; + let (positions, networks, util, chunk_holders, entities) = data; self.dirty.clear(); @@ -39,13 +42,7 @@ impl<'a> System<'a> for EntityMoveBroadcastSystem { } for (entity, position, _) in (&entities, &positions, &self.dirty).join() { - broadcast_entity_movement( - entity, - position.previous, - position.current, - &networks, - &entities, - ); + broadcast_entity_movement(entity, position.previous, position.current, &util); } } @@ -62,13 +59,14 @@ pub struct EntityVelocityBroadcastSystem { impl<'a> System<'a> for EntityVelocityBroadcastSystem { type SystemData = ( - ReadStorage<'a, NetworkComponent>, + ReadStorage<'a, PositionComponent>, ReadStorage<'a, VelocityComponent>, + Read<'a, Util>, Entities<'a>, ); fn run(&mut self, data: Self::SystemData) { - let (networks, velocities, entities) = data; + let (positions, networks, velocities, util, chunk_holders, entities) = data; self.dirty.clear(); @@ -81,7 +79,9 @@ impl<'a> System<'a> for EntityVelocityBroadcastSystem { } } - for (velocity, entity, _) in (&velocities, &entities, &self.dirty).join() { + for (position, velocity, entity, _) in + (&positions, &velocities, &entities, &self.dirty).join() + { let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocity.0); let packet = EntityVelocity { entity_id: entity.id() as i32, @@ -90,22 +90,21 @@ impl<'a> System<'a> for EntityVelocityBroadcastSystem { velocity_z, }; - send_packet_to_all_players(&networks, &entities, packet, Some(entity)); + util.broadcast(entity, packet, Some(entity)); } } flagged_setup_impl!(VelocityComponent, reader); } -/// Broadcasts to all joined players that an entity has moved. +/// Broadcasts to nearby players that an entity has moved. #[allow(clippy::too_many_arguments)] #[allow(clippy::float_cmp)] pub fn broadcast_entity_movement( entity: Entity, old_pos: Position, new_pos: Position, - networks: &ReadStorage, - entities: &Entities, + util: &Util, ) { if old_pos == new_pos { return; @@ -136,10 +135,10 @@ pub fn broadcast_entity_movement( degrees_to_stops(new_pos.pitch), new_pos.on_ground, ); - send_packet_to_all_players(networks, entities, packet, Some(entity)); + util.broadcast(entity, packet, Some(entity)); } else { let packet = EntityRelativeMove::new(entity.id() as i32, rx, ry, rz, new_pos.on_ground); - send_packet_to_all_players(networks, entities, packet, Some(entity)); + util.broadcast(entity, packet, Some(entity)); } } else { let packet = EntityLook::new( @@ -148,13 +147,13 @@ pub fn broadcast_entity_movement( degrees_to_stops(new_pos.pitch), new_pos.on_ground, ); - send_packet_to_all_players(networks, entities, packet, Some(entity)); + util.broadcast(entity, packet, Some(entity)); } // Entity Head Look also needs to be sent if the entity turned its head if has_looked { let packet = EntityHeadLook::new(entity.id() as i32, degrees_to_stops(new_pos.yaw)); - send_packet_to_all_players(networks, entities, packet, Some(entity)); + util.broadcast(entity, packet, Some(entity)); } } diff --git a/server/src/main.rs b/server/src/main.rs index be02d53c9..eb68a2ba6 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -42,7 +42,7 @@ use prelude::*; use crate::entity::{EntityDestroyEvent, NamedComponent}; use crate::network::send_packet_to_player; use crate::player::PlayerDisconnectEvent; -use crate::systems::{ITEM_SPAWN, JOIN_HANDLER, NETWORK, SPAWNER}; +use crate::systems::{BROADCASTER, ITEM_SPAWN, JOIN_HANDLER, NETWORK, SPAWNER}; use crate::util::Util; use backtrace::Backtrace; use feather_core::level; @@ -241,6 +241,7 @@ fn init_world<'a, 'b>( player::init_broadcast(&mut dispatcher); entity::init_broadcast(&mut dispatcher); + dispatcher.add(util::BroadcasterSystem, BROADCASTER, &[]); let mut dispatcher = dispatcher.build(); dispatcher.setup(&mut world); diff --git a/server/src/player/animation.rs b/server/src/player/animation.rs index 880c82000..39482a9db 100644 --- a/server/src/player/animation.rs +++ b/server/src/player/animation.rs @@ -1,4 +1,7 @@ +use crate::chunk_logic::ChunkHolders; +use crate::entity::PositionComponent; use crate::network::{send_packet_to_all_players, NetworkComponent, PacketQueue}; +use crate::util::Util; use feather_core::network::cast_packet; use feather_core::network::packet::implementation::{AnimationClientbound, AnimationServerbound}; use feather_core::network::packet::PacketType; @@ -53,25 +56,16 @@ pub struct AnimationBroadcastSystem { } impl<'a> System<'a> for AnimationBroadcastSystem { - type SystemData = ( - Read<'a, EventChannel>, - ReadStorage<'a, NetworkComponent>, - Entities<'a>, - ); + type SystemData = (Read<'a, EventChannel>, Read<'a, Util>); fn run(&mut self, data: Self::SystemData) { - let (events, net_comps, entities) = data; + let (events, util) = data; for event in events.read(&mut self.reader.as_mut().unwrap()) { // Broadcast animation let packet = AnimationClientbound::new(event.player.id() as i32, event.animation); - send_packet_to_all_players( - &net_comps, - &entities, - packet, - Some(event.player), // Don't send player their own animation - ); + util.broadcast(event.player, packet, Some(event.player)) } } diff --git a/server/src/player/broadcast.rs b/server/src/player/broadcast.rs index 99d7b65fa..d293ca489 100644 --- a/server/src/player/broadcast.rs +++ b/server/src/player/broadcast.rs @@ -1,12 +1,16 @@ -use crate::entity::{NamedComponent, PlayerComponent, PositionComponent}; +use crate::config::Config; +use crate::entity::{ + ChunkEntities, EntitySender, NamedComponent, PlayerComponent, PositionComponent, +}; use crate::joinhandler::PlayerJoinEvent; use crate::network::{send_packet_to_all_players, send_packet_to_player, NetworkComponent}; use crate::player::chat::ChatBroadcastEvent; -use feather_core::network::packet::implementation::{PlayerInfo, PlayerInfoAction, SpawnPlayer}; +use feather_core::network::packet::implementation::{PlayerInfo, PlayerInfoAction}; use feather_core::Gamemode; use shrev::EventChannel; use specs::SystemData; use specs::{Entities, Entity, Join, Read, ReadStorage, ReaderId, System, World, Write}; +use std::sync::Arc; use uuid::Uuid; /// System for broadcasting when a player joins @@ -29,11 +33,25 @@ impl<'a> System<'a> for JoinBroadcastSystem { ReadStorage<'a, PlayerComponent>, ReadStorage<'a, NetworkComponent>, Write<'a, EventChannel>, + Read<'a, ChunkEntities>, + Read<'a, EntitySender>, + Read<'a, Arc>, Entities<'a>, ); fn run(&mut self, data: Self::SystemData) { - let (join_events, positions, nameds, player_comps, net_comps, mut chat, entities) = data; + let ( + join_events, + positions, + nameds, + player_comps, + net_comps, + mut chat, + chunk_entities, + entity_sender, + config, + entities, + ) = data; for event in join_events.read(&mut self.reader.as_mut().unwrap()) { // Broadcast join @@ -55,18 +73,16 @@ impl<'a> System<'a> for JoinBroadcastSystem { let player_info = get_player_initialization_packet(position, named, player_comp); send_packet_to_player(net_comp, player_info); + } + } - let spawn_player = SpawnPlayer { - entity_id: entity.id() as i32, - player_uuid: named.uuid, - x: position.current.x, - y: position.current.y, - z: position.current.z, - yaw: degrees_to_stops(position.current.yaw), - pitch: degrees_to_stops(position.current.pitch), - metadata: Default::default(), - }; - send_packet_to_player(net_comp, spawn_player); + // Send entities within view distance to new player + for entity in chunk_entities.entites_within_view_distance( + position.current.chunk_pos(), + config.server.view_distance, + ) { + if entity != event.player { + entity_sender.send_entity_to_player(event.player, entity); } } @@ -186,7 +202,3 @@ impl<'a> System<'a> for DisconnectBroadcastSystem { ); } } - -fn degrees_to_stops(degs: f32) -> u8 { - ((degs / 360.0) * 256.0) as u8 -} diff --git a/server/src/player/digging.rs b/server/src/player/digging.rs index b4fd5d510..dd3f73c84 100644 --- a/server/src/player/digging.rs +++ b/server/src/player/digging.rs @@ -17,10 +17,12 @@ use feather_core::world::block::{Block, BlockExt}; use feather_core::world::{BlockPosition, ChunkMap}; use feather_core::{Gamemode, Item}; +use crate::chunk_logic::ChunkHolders; use crate::disconnect_player; use crate::entity::PlayerComponent; use crate::network::{send_packet_to_all_players, NetworkComponent, PacketQueue}; use crate::player::{InventoryComponent, InventoryUpdateEvent}; +use crate::util::Util; use feather_core::inventory::{ItemStack, SlotIndex, SLOT_HOTBAR_OFFSET}; use shrev::EventChannel; use specs::SystemData; @@ -247,14 +249,10 @@ pub struct BlockUpdateBroadcastSystem { } impl<'a> System<'a> for BlockUpdateBroadcastSystem { - type SystemData = ( - ReadStorage<'a, NetworkComponent>, - Read<'a, EventChannel>, - Entities<'a>, - ); + type SystemData = (Read<'a, EventChannel>, Read<'a, Util>); fn run(&mut self, data: Self::SystemData) { - let (networks, events, entities) = data; + let (events, util) = data; // Process events for event in events.read(&mut self.reader.as_mut().unwrap()) { @@ -268,7 +266,7 @@ impl<'a> System<'a> for BlockUpdateBroadcastSystem { }; let packet = BlockChange::new(event.pos, i32::from(event.new_block.native_state_id())); - send_packet_to_all_players(&networks, &entities, packet, neq); + util.broadcast_chunk(event.pos.chunk_pos(), packet, neq); } } diff --git a/server/src/player/inventory.rs b/server/src/player/inventory.rs index 9d4a81847..7ecee412a 100644 --- a/server/src/player/inventory.rs +++ b/server/src/player/inventory.rs @@ -1,10 +1,12 @@ +use crate::chunk_logic::ChunkHolders; use crate::disconnect_player; -use crate::entity::PlayerComponent; +use crate::entity::{PlayerComponent, PositionComponent}; use crate::joinhandler::PlayerJoinEvent; use crate::network::{ send_packet_to_all_players, send_packet_to_player, NetworkComponent, PacketQueue, }; use crate::player::digging::PlayerItemDropEvent; +use crate::util::Util; use feather_core::inventory::{ Inventory, InventoryType, SlotIndex, HOTBAR_SIZE, SLOT_ARMOR_CHEST, SLOT_ARMOR_FEET, SLOT_ARMOR_HEAD, SLOT_ARMOR_LEGS, SLOT_HOTBAR_OFFSET, SLOT_OFFHAND, @@ -260,11 +262,11 @@ impl<'a> System<'a> for HeldItemBroadcastSystem { ReadStorage<'a, NetworkComponent>, ReadStorage<'a, InventoryComponent>, Read<'a, EventChannel>, - Entities<'a>, + Read<'a, Util>, ); fn run(&mut self, data: Self::SystemData) { - let (networks, inventories, events, entities) = data; + let (networks, inventories, events, util) = data; for event in events.read(&mut self.reader.as_mut().unwrap()) { let inv = inventories.get(event.player).unwrap(); @@ -281,7 +283,7 @@ impl<'a> System<'a> for HeldItemBroadcastSystem { item, ); - send_packet_to_all_players(&networks, &entities, packet, Some(event.player)); + util.broadcast(event.player, packet, Some(event.player)); } } } diff --git a/server/src/systems.rs b/server/src/systems.rs index 5ed258512..2d615e36a 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -61,3 +61,5 @@ pub const NETWORK: &str = "network"; pub const TIME_INCREMENT: &str = "time_increment"; pub const TIME_SEND: &str = "time_send"; + +pub const BROADCASTER: &str = "broadcaster"; diff --git a/server/src/testframework.rs b/server/src/testframework.rs index 4c34e7117..e8f376fd1 100644 --- a/server/src/testframework.rs +++ b/server/src/testframework.rs @@ -4,16 +4,23 @@ use std::net::TcpListener; use std::sync::atomic::AtomicUsize; use std::sync::Arc; +use glm::DVec3; use mio_extras::channel::{channel, Receiver, Sender}; use rand::Rng; +use shrev::EventChannel; use specs::{Builder, Dispatcher, DispatcherBuilder, Entity, ReaderId, System, World, WorldExt}; use uuid::Uuid; +use feather_core::level::LevelData; use feather_core::network::packet::{Packet, PacketType}; +use feather_core::world::block::Block; +use feather_core::world::chunk::Chunk; use feather_core::world::{BlockPosition, ChunkMap, ChunkPosition, Position}; use feather_core::Gamemode; +use crate::chunk_logic::ChunkHolders; use crate::config::Config; +use crate::entity::metadata::{self, Metadata}; use crate::entity::{ EntityDestroyEvent, EntitySpawnEvent, EntityType, ItemComponent, NamedComponent, PlayerComponent, PositionComponent, VelocityComponent, @@ -21,16 +28,9 @@ use crate::entity::{ use crate::io::ServerToWorkerMessage; use crate::network::{NetworkComponent, PacketQueue}; use crate::player::{InventoryComponent, PlayerDisconnectEvent}; +use crate::systems::BROADCASTER; +use crate::util::BroadcasterSystem; use crate::PlayerCount; -use feather_core::level::LevelData; -use feather_core::world::chunk::Chunk; -use glm::DVec3; -use shrev::EventChannel; - -use crate::entity::metadata::{self, Metadata}; - -use crate::chunk_logic::ChunkHolders; -use feather_core::world::block::Block; /// Initializes a Specs world and dispatcher /// using default configuration options and an @@ -373,6 +373,10 @@ impl<'a, 'b> TestBuilder<'a, 'b> { self.world.insert(ChunkHolders::default()); self.world.insert(Arc::new(Config::default())); + // Insert the broadcaster system, since it is so commonly + // used that it should be used for all tests. + self.dispatcher.add_thread_local(BroadcasterSystem); + let mut dispatcher = self.dispatcher.build(); dispatcher.setup(&mut self.world); @@ -402,9 +406,10 @@ pub fn builder<'a, 'b>() -> TestBuilder<'a, 'b> { /// all other tests would fail if the testing /// framework didn't work. mod tests { + use feather_core::network::packet::implementation::{DisconnectPlay, LoginStart}; + use crate::entity::{PlayerComponent, PositionComponent}; use crate::network::{send_packet_to_player, NetworkComponent}; - use feather_core::network::packet::implementation::{DisconnectPlay, LoginStart}; use super::*; diff --git a/server/src/util/broadcaster.rs b/server/src/util/broadcaster.rs new file mode 100644 index 000000000..6b76bfef3 --- /dev/null +++ b/server/src/util/broadcaster.rs @@ -0,0 +1,109 @@ +//! Implements a broadcaster, used to lazily broadcast +//! packets to players able to see a given entity. + +use crate::chunk_logic::ChunkHolders; +use crate::entity::PositionComponent; +use crate::network::{send_packet_boxed_to_player, send_packet_to_player, NetworkComponent}; +use crossbeam::queue::SegQueue; +use feather_core::{ChunkPosition, Packet}; +use specs::{Entities, Entity, Read, ReadStorage, System}; + +/// Broadcaster used to lazily broadcast packets. +#[derive(Default, Debug)] +pub struct Broadcaster { + /// Internal queue of broadcasts to send. + queue: SegQueue, +} + +impl Broadcaster { + /// Lazily broadcasts a packet to all players + /// able to see a given entity. + pub fn broadcast_entity

(&self, entity: Entity, packet: P, neq: Option) + where + P: Packet + 'static, + { + self.queue.push(BroadcastRequest { + condition: BroadcastCondition::Entity(entity), + packet: Box::new(packet), + neq, + }); + } + + /// Lazily broadcasts a packet to all players + /// able to see a given chunk. + pub fn broadcast_chunk

(&self, chunk: ChunkPosition, packet: P, neq: Option) + where + P: Packet + 'static, + { + self.queue.push(BroadcastRequest { + condition: BroadcastCondition::Chunk(chunk), + packet: Box::new(packet), + neq, + }); + } +} + +/// A broadcast request. +#[derive(Debug)] +struct BroadcastRequest { + /// Packet will only be sent to players able to see + /// this entity or chunk. + condition: BroadcastCondition, + /// The packet to broadcast. + packet: Box, + /// Optional entity not to send to. + neq: Option, +} + +#[derive(Debug, Clone, Copy)] +enum BroadcastCondition { + Entity(Entity), + Chunk(ChunkPosition), +} + +/// System for flushing the `Broadcaster` queue and broadcasting +/// the necessary packets. +pub struct BroadcasterSystem; + +impl<'a> System<'a> for BroadcasterSystem { + type SystemData = ( + ReadStorage<'a, PositionComponent>, + ReadStorage<'a, NetworkComponent>, + Read<'a, Broadcaster>, + Read<'a, ChunkHolders>, + Entities<'a>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (positions, networks, broadcaster, chunk_holders, entities) = data; + + while let Ok(request) = broadcaster.queue.pop() { + // Prevents a panic if the entity was destroyed. + if !entities.is_alive(request.entity) { + continue; + } + + // Broadcast packet. + // Iterate over entities in the chunk_holders + // entry for the chunk. If they are a player, + // send the packet. + // This works because any player able to see + // a chunk will always have a chunk holder on the chunk. + if let Some(holders) = chunk_holders + .holders_for(positions.get(request.entity).unwrap().current.chunk_pos()) + { + for holder in holders { + if let Some(neq) = request.neq.as_ref() { + if *holder == *neq { + continue; + } + } + + if let Some(network) = networks.get(*holder) { + send_packet_boxed_to_player(network, request.packet.clone()); + } + } + } + } + } +} diff --git a/server/src/util/mod.rs b/server/src/util/mod.rs index f068e9067..857e0b310 100644 --- a/server/src/util/mod.rs +++ b/server/src/util/mod.rs @@ -7,10 +7,13 @@ use thread_local::ThreadLocal; #[macro_use] mod macros; +mod broadcaster; mod spawn; use crate::chunk_logic::ChunkHolders; use crate::network::{send_packet_to_player, NetworkComponent}; +use broadcaster::Broadcaster; +pub use broadcaster::BroadcasterSystem; pub use macros::*; pub use spawn::SpawnerSystem; use specs::storage::GenericReadStorage; @@ -46,7 +49,7 @@ pub fn protocol_velocity(vel: DVec3) -> (i16, i16, i16) { /// needs. Note, however, that the entity isn't created /// until the handling dispatcher stage. These functions simply /// redirect to `entity::Spawner`. -/// * `broadcast` - broadcasts a packet to all players +/// * `broadcast` - lazily broadcasts a packet to all players /// who are able to see a given chunk. This can be used /// to broadcast movement updates, for example. #[derive(Debug, Default)] @@ -58,6 +61,8 @@ pub struct Util { bump: ThreadLocal, /// The spawner, used to lazily spawn entities. spawner: Spawner, + /// The broadcaster, used to lazily broadcast packets. + broadcaster: Broadcaster, } impl Util { @@ -90,11 +95,28 @@ impl Util { } } + /// Broadcasts a packet to all players who + /// are able to see a given entity. + /// + /// The packet is sent lazily in a separate system. + /// + /// If `neq` is set to an entity, the packet + /// will not be sent to that player. + /// + /// This function runs in linear time with + /// regard to the number of players able to see + /// the entity. + pub fn broadcast_entity

(&self, entity: Entity, packet: P, neq: Option) + where + P: Packet + Clone + 'static, + { + self.broadcaster.broadcast_entity(entity, packet, neq); + } + /// Broadcasts a packet to all players who /// are able to see a given chunk. /// - /// The packet is sent instantly, not lazily, - /// unlike many of the `Util` functions. + /// The packet is sent lazily in a separate system. /// /// If `neq` is set to an entity, the packet /// will not be sent to that player. @@ -102,37 +124,11 @@ impl Util { /// This function runs in linear time with /// regard to the number of players able to see /// the chunk. - pub fn broadcast( - &self, - chunk_holders: &ChunkHolders, - networks: &N, - chunk: ChunkPosition, - packet: P, - neq: Option, - ) where + pub fn broadcast_chunk

(&self, chunk: ChunkPosition, packet: P, neq: Option) + where P: Packet + Clone + 'static, - N: GenericReadStorage, { - // Iterate over entities in the chunk_holders - // entry for the chunk. If they are a player, - // send the packet. - // This works because any player able to see - // a chunk will always have a chunk holder on the chunk. - if let Some(holders) = chunk_holders.holders_for(chunk) { - for entity in holders { - if let Some(neq) = neq.as_ref() { - if *neq == *entity { - continue; - } - } - - // If the entity doesn't have a network component, - // skip it. - if let Some(network) = networks.get(*entity) { - send_packet_to_player(&network, packet.clone()); - } - } - } + self.broadcaster.broadcast_chunk(chunk, packet, neq); } } @@ -140,6 +136,7 @@ impl Util { mod tests { use super::*; use crate::testframework as t; + use crate::util::broadcaster::BroadcasterSystem; use feather_core::network::packet::implementation::EntityHeadLook; use feather_core::PacketType; use specs::WorldExt; @@ -151,7 +148,7 @@ mod tests { let chunk = ChunkPosition::new(0, 0); let other_chunk = ChunkPosition::new(10, 1); - let (mut world, _) = t::builder().build(); + let (mut world, mut dispatcher) = t::builder().with(BroadcasterSystem, "").build(); let player1 = t::add_player(&mut world); let player2 = t::add_player(&mut world); let player3 = t::add_player(&mut world); @@ -164,13 +161,10 @@ mod tests { let util = Util::default(); - util.broadcast( - &chunk_holders, - &world.read_component(), - chunk, - packet, - Some(player2.entity), - ); + util.broadcast_entity(player1.entity, packet, Some(player2.entity)); + + dispatcher.dispatch(&world); + world.maintain(); t::assert_packet_received(&player1, PacketType::EntityHeadLook); t::assert_packet_not_received(&player2, PacketType::EntityHeadLook); From 44a569f171d1d44c0aa57e57498b81d62936e9f7 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Tue, 3 Sep 2019 18:08:15 -0600 Subject: [PATCH 5/9] More work --- codegen/src/lib.rs | 4 + core/src/network/packet/implementation.rs | 90 ++++++++++++++++++++++- core/src/network/packet/mod.rs | 3 + server/src/entity/chunk.rs | 35 ++++----- server/src/entity/component.rs | 2 +- server/src/entity/item.rs | 14 +--- server/src/entity/metadata.rs | 9 +-- server/src/entity/mod.rs | 2 +- server/src/entity/movement.rs | 25 +++---- server/src/player/animation.rs | 8 +- server/src/player/digging.rs | 7 +- server/src/player/inventory.rs | 12 +-- server/src/player/view.rs | 8 +- server/src/testframework.rs | 23 +++--- server/src/util/broadcaster.rs | 35 +++++---- server/src/util/mod.rs | 9 ++- 16 files changed, 185 insertions(+), 101 deletions(-) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 4dafd8661..e93176df5 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -205,6 +205,10 @@ pub fn derive_packet(_item: TokenStream) -> TokenStream { fn ty(&self) -> PacketType { PacketType::#ident } + + fn box_clone(&self) -> Box { + Box::new((*self).clone()) + } } }; diff --git a/core/src/network/packet/implementation.rs b/core/src/network/packet/implementation.rs index 08f98ec5f..bf36963df 100644 --- a/core/src/network/packet/implementation.rs +++ b/core/src/network/packet/implementation.rs @@ -79,7 +79,11 @@ lazy_static! { }; } -fn bla() {} +macro_rules! box_clone_impl { + ($this:ident) => { + return Box::new((*$this).clone()); + }; +} // SERVERBOUND @@ -114,6 +118,10 @@ impl Packet for Handshake { fn ty(&self) -> PacketType { PacketType::Handshake } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(PartialEq, Eq, Clone)] @@ -169,6 +177,10 @@ impl Packet for EncryptionResponse { fn ty(&self) -> PacketType { PacketType::EncryptionResponse } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Packet, Clone)] @@ -269,6 +281,10 @@ impl Packet for PluginMessageServerbound { fn ty(&self) -> PacketType { PacketType::PluginMessageServerbound } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Packet, Clone)] @@ -318,6 +334,10 @@ impl Packet for UseEntity { fn ty(&self) -> PacketType { PacketType::UseEntity } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(AsAny, new, Clone)] @@ -438,6 +458,10 @@ impl Packet for PlayerDigging { fn ty(&self) -> PacketType { PacketType::PlayerDigging } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)] @@ -481,6 +505,10 @@ impl Packet for EntityAction { fn ty(&self) -> PacketType { PacketType::EntityAction } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, FromPrimitive, ToPrimitive)] @@ -621,6 +649,10 @@ impl Packet for AnimationServerbound { fn ty(&self) -> PacketType { PacketType::AnimationServerbound } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Packet, Clone)] @@ -685,6 +717,10 @@ impl Packet for PlayerBlockPlacement { fn ty(&self) -> PacketType { PacketType::PlayerBlockPlacement } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Packet, Clone)] @@ -723,6 +759,10 @@ impl Packet for EncryptionRequest { fn ty(&self) -> PacketType { PacketType::EncryptionRequest } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Packet, Clone)] @@ -842,6 +882,10 @@ impl Packet for SpawnPlayer { fn ty(&self) -> PacketType { PacketType::SpawnPlayer } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Clone)] @@ -863,6 +907,10 @@ impl Packet for AnimationClientbound { fn ty(&self) -> PacketType { PacketType::AnimationClientbound } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Clone)] @@ -888,6 +936,10 @@ impl Packet for Statistics { fn ty(&self) -> PacketType { PacketType::Statistics } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Packet, Clone)] @@ -961,6 +1013,10 @@ impl Packet for BossBar { fn ty(&self) -> PacketType { PacketType::BossBar } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Clone, Debug)] @@ -1078,6 +1134,10 @@ impl Packet for WindowItems { fn ty(&self) -> PacketType { PacketType::WindowItems } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Packet, Clone)] @@ -1119,6 +1179,10 @@ impl Packet for PluginMessageClientbound { fn ty(&self) -> PacketType { PacketType::PluginMessageClientbound } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Packet, Clone)] @@ -1189,6 +1253,10 @@ impl Packet for Explosion { fn ty(&self) -> PacketType { PacketType::Explosion } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Packet, Clone)] @@ -1285,6 +1353,10 @@ impl Packet for ChunkData { fn ty(&self) -> PacketType { PacketType::ChunkData } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Packet, Clone)] @@ -1406,6 +1478,10 @@ impl Packet for CombatEvent { fn ty(&self) -> PacketType { unimplemented!() } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(new, Clone)] @@ -1469,6 +1545,10 @@ impl Packet for PlayerInfo { fn ty(&self) -> PacketType { PacketType::PlayerInfo } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Debug, Clone)] @@ -1540,6 +1620,10 @@ impl Packet for DestroyEntities { fn ty(&self) -> PacketType { PacketType::DestroyEntities } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Packet, Clone)] @@ -1587,6 +1671,10 @@ impl Packet for PacketEntityMetadata { fn ty(&self) -> PacketType { PacketType::EntityMetadata } + + fn box_clone(&self) -> Box { + box_clone_impl!(self); + } } #[derive(Default, AsAny, new, Packet, Clone)] diff --git a/core/src/network/packet/mod.rs b/core/src/network/packet/mod.rs index b888f8ffa..e1466b776 100644 --- a/core/src/network/packet/mod.rs +++ b/core/src/network/packet/mod.rs @@ -19,6 +19,9 @@ pub trait Packet: AsAny + Send { fn read_from(&mut self, buf: &mut dyn PacketBuf) -> Result<(), ()>; fn write_to(&self, buf: &mut ByteBuf); fn ty(&self) -> PacketType; + + /// Returns a clone of this packet in a dynamic box. + fn box_clone(&self) -> Box; } #[derive(Clone, Debug)] diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index 491803c9d..c865250cb 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -75,6 +75,8 @@ impl ChunkEntities { for z_offset in -view_distance..=view_distance { let chunk = ChunkPosition::new(chunk.x + x_offset, chunk.z + z_offset); + println!("{:?}", chunk); + result.extend(self.entities_in_chunk(chunk)); } } @@ -155,6 +157,8 @@ impl<'a> System<'a> for ChunkEntityUpdateSystem { } } +// Tests here cannot use the `testframework::add_entity` function +// because it automatically adds a ChunkEntities entry for the entity. #[cfg(test)] mod tests { use super::*; @@ -181,7 +185,16 @@ mod tests { let (mut w, mut d) = t::init_world(); let pos = position!(1.0, 64.0, 1003.5); - let entity = t::add_entity_with_pos(&mut w, EntityType::Player, pos, true); + let entity = w.create_entity().with(PositionComponent { + current: pos, + previous: pos, + }).build(); + + let event = EntitySpawnEvent { + entity, + ty: EntityType::Player, + }; + t::trigger_event(&w, event); d.dispatch(&w); w.maintain(); @@ -200,17 +213,10 @@ mod tests { let pos = position!(1.0, 64.0, -14.0); let old_pos = position!(1.0, 64.0, -18.0); - let entity = t::add_entity_with_pos(&mut w, EntityType::Player, pos, false); - - { - let mut chunk_entities = w.fetch_mut::(); - chunk_entities.add_to_chunk(old_pos.chunk_pos(), entity); - - w.write_component::() - .get_mut(entity) - .unwrap() - .previous = old_pos; - } + let entity = w.create_entity().with(PositionComponent { + current: pos, + previous: old_pos + }).build(); d.dispatch(&w); w.maintain(); @@ -232,11 +238,6 @@ mod tests { let pos = position!(100.0, -100.0, -100.0); let entity = t::add_entity_with_pos(&mut w, EntityType::Player, pos, false); - { - let mut chunk_entities = w.fetch_mut::(); - chunk_entities.add_to_chunk(pos.chunk_pos(), entity); - } - let event = EntityDestroyEvent { entity }; t::trigger_event(&w, event); diff --git a/server/src/entity/component.rs b/server/src/entity/component.rs index bf749e417..ce0fdd26d 100644 --- a/server/src/entity/component.rs +++ b/server/src/entity/component.rs @@ -16,7 +16,7 @@ impl Component for PlayerComponent { type Storage = BTreeStorage; } -#[derive(Debug, PartialEq)] +#[derive(Default, Debug, PartialEq)] pub struct PositionComponent { /// The current position of this entity. pub current: Position, diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index f5f7ab632..796af60d5 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -1,8 +1,6 @@ //! Logic for working with item entities. -use crate::chunk_logic::ChunkHolders; use crate::entity::metadata::{self, Metadata}; use crate::entity::{ChunkEntities, EntityDestroyEvent, PlayerComponent, PositionComponent}; -use crate::network::{send_packet_to_all_players, NetworkComponent}; use crate::physics::nearby_entities; use crate::player::{ InventoryComponent, InventoryUpdateEvent, PlayerItemDropEvent, PLAYER_EYE_HEIGHT, @@ -208,7 +206,6 @@ impl<'a> System<'a> for ItemCollectSystem { ReadStorage<'a, PositionComponent>, ReadStorage<'a, PlayerComponent>, ReadStorage<'a, ItemComponent>, - ReadStorage<'a, NetworkComponent>, WriteStorage<'a, Metadata>, Write<'a, EventChannel>, Write<'a, EventChannel>, @@ -224,7 +221,6 @@ impl<'a> System<'a> for ItemCollectSystem { positions, players, items, - networks, mut metadatas, mut inventory_events, mut destroy_events, @@ -330,10 +326,8 @@ pub fn item_meta(stack: ItemStack) -> Metadata { #[cfg(test)] mod tests { use super::*; - use crate::entity::chunk::ChunkEntityUpdateSystem; use crate::entity::EntitySpawnEvent; use crate::entity::EntityType; - use crate::systems::CHUNK_ENTITIES_UPDATE; use crate::testframework as t; use feather_core::inventory::SLOT_HOTBAR_OFFSET; use feather_core::network::cast_packet; @@ -381,12 +375,11 @@ mod tests { #[test] fn test_item_merge_system() { let (mut w, mut d) = t::builder() - .with(ChunkEntityUpdateSystem::default(), "chunk_entity_update") .with_dep( ItemMergeSystem::default(), "item_merge", - &["chunk_entity_update"], - ) // Required so nearby_entities() works + &[], + ) .build(); let item1 = @@ -422,8 +415,7 @@ mod tests { #[test] fn test_item_collect_system() { let (mut w, mut d) = t::builder() - .with(ChunkEntityUpdateSystem::default(), CHUNK_ENTITIES_UPDATE) - .with_dep(ItemCollectSystem::default(), "", &[CHUNK_ENTITIES_UPDATE]) + .with_dep(ItemCollectSystem::default(), "", &[]) .build(); let player = t::add_player(&mut w); diff --git a/server/src/entity/metadata.rs b/server/src/entity/metadata.rs index 293c4ae07..2c3bfae0c 100644 --- a/server/src/entity/metadata.rs +++ b/server/src/entity/metadata.rs @@ -2,14 +2,13 @@ #![allow(clippy::too_many_arguments)] // TODO: builder patterm -use crate::network::{send_packet_to_all_players, NetworkComponent}; use crate::util::Util; use feather_core::packet::PacketEntityMetadata; use feather_core::{EntityMetadata, Slot}; use specs::storage::ComponentEvent; use specs::{ - BitSet, Component, Entities, FlaggedStorage, Join, Read, ReadStorage, ReaderId, System, - VecStorage, WriteStorage, + BitSet, Component, Entities, FlaggedStorage, Join, Read, ReaderId, System, VecStorage, + WriteStorage, }; bitflags! { @@ -82,7 +81,7 @@ impl<'a> System<'a> for MetadataBroadcastSystem { metadata: metadata.to_raw_metadata(), }; - util.broadcast(entity, packet, None); + util.broadcast_entity(entity, packet, None); } metadatas.set_event_emission(true); @@ -135,7 +134,7 @@ mod tests { .build(); // Metadata is inserted here, which causes update event - let entity = t::add_entity(&mut w, EntityType::Test, false); + let entity = t::add_entity(&mut w, EntityType::Test, true); let player = t::add_player(&mut w); d.dispatch(&w); diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 48c8753a2..2eb651e20 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -26,12 +26,12 @@ pub use item::ItemComponent; pub use metadata::{EntityBitMask, Metadata}; pub use movement::broadcast_entity_movement; pub use types::EntityType; +pub use chunk::ChunkEntityUpdateSystem; use crate::entity::destroy::EntityDestroyBroadcastSystem; use crate::entity::item::ItemCollectSystem; use crate::entity::metadata::MetadataBroadcastSystem; use broadcast::EntityBroadcastSystem; -use chunk::ChunkEntityUpdateSystem; use component::ComponentResetSystem; use destroy::EntityDestroySystem; use item::{ItemMergeSystem, ItemSpawnSystem}; diff --git a/server/src/entity/movement.rs b/server/src/entity/movement.rs index a5654c728..38c6feaad 100644 --- a/server/src/entity/movement.rs +++ b/server/src/entity/movement.rs @@ -6,9 +6,7 @@ use feather_core::network::packet::implementation::{ }; use feather_core::world::Position; -use crate::chunk_logic::ChunkHolders; use crate::entity::{PositionComponent, VelocityComponent}; -use crate::network::NetworkComponent; use crate::util::{protocol_velocity, Util}; /// System for broadcasting when an entity moves. @@ -21,14 +19,12 @@ pub struct EntityMoveBroadcastSystem { impl<'a> System<'a> for EntityMoveBroadcastSystem { type SystemData = ( ReadStorage<'a, PositionComponent>, - ReadStorage<'a, NetworkComponent>, Read<'a, Util>, - Read<'a, ChunkHolders>, Entities<'a>, ); fn run(&mut self, data: Self::SystemData) { - let (positions, networks, util, chunk_holders, entities) = data; + let (positions, util, entities) = data; self.dirty.clear(); @@ -41,7 +37,7 @@ impl<'a> System<'a> for EntityMoveBroadcastSystem { } } - for (entity, position, _) in (&entities, &positions, &self.dirty).join() { + for (position, entity, _) in (&positions, &entities, &self.dirty).join() { broadcast_entity_movement(entity, position.previous, position.current, &util); } } @@ -59,14 +55,13 @@ pub struct EntityVelocityBroadcastSystem { impl<'a> System<'a> for EntityVelocityBroadcastSystem { type SystemData = ( - ReadStorage<'a, PositionComponent>, ReadStorage<'a, VelocityComponent>, Read<'a, Util>, Entities<'a>, ); fn run(&mut self, data: Self::SystemData) { - let (positions, networks, velocities, util, chunk_holders, entities) = data; + let (velocities, util, entities) = data; self.dirty.clear(); @@ -79,9 +74,7 @@ impl<'a> System<'a> for EntityVelocityBroadcastSystem { } } - for (position, velocity, entity, _) in - (&positions, &velocities, &entities, &self.dirty).join() - { + for (velocity, entity, _) in (&velocities, &entities, &self.dirty).join() { let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocity.0); let packet = EntityVelocity { entity_id: entity.id() as i32, @@ -90,7 +83,7 @@ impl<'a> System<'a> for EntityVelocityBroadcastSystem { velocity_z, }; - util.broadcast(entity, packet, Some(entity)); + util.broadcast_entity(entity, packet, Some(entity)); } } @@ -135,10 +128,10 @@ pub fn broadcast_entity_movement( degrees_to_stops(new_pos.pitch), new_pos.on_ground, ); - util.broadcast(entity, packet, Some(entity)); + util.broadcast_entity(entity, packet, Some(entity)); } else { let packet = EntityRelativeMove::new(entity.id() as i32, rx, ry, rz, new_pos.on_ground); - util.broadcast(entity, packet, Some(entity)); + util.broadcast_entity(entity, packet, Some(entity)); } } else { let packet = EntityLook::new( @@ -147,13 +140,13 @@ pub fn broadcast_entity_movement( degrees_to_stops(new_pos.pitch), new_pos.on_ground, ); - util.broadcast(entity, packet, Some(entity)); + util.broadcast_entity(entity, packet, Some(entity)); } // Entity Head Look also needs to be sent if the entity turned its head if has_looked { let packet = EntityHeadLook::new(entity.id() as i32, degrees_to_stops(new_pos.yaw)); - util.broadcast(entity, packet, Some(entity)); + util.broadcast_entity(entity, packet, Some(entity)); } } diff --git a/server/src/player/animation.rs b/server/src/player/animation.rs index 39482a9db..a91a6efee 100644 --- a/server/src/player/animation.rs +++ b/server/src/player/animation.rs @@ -1,6 +1,4 @@ -use crate::chunk_logic::ChunkHolders; -use crate::entity::PositionComponent; -use crate::network::{send_packet_to_all_players, NetworkComponent, PacketQueue}; +use crate::network::PacketQueue; use crate::util::Util; use feather_core::network::cast_packet; use feather_core::network::packet::implementation::{AnimationClientbound, AnimationServerbound}; @@ -8,7 +6,7 @@ use feather_core::network::packet::PacketType; use feather_core::{ClientboundAnimation, Hand}; use shrev::EventChannel; use specs::SystemData; -use specs::{Entities, Entity, Read, ReadStorage, ReaderId, System, World, Write}; +use specs::{Entity, Read, ReaderId, System, World, Write}; /// Event which is triggered when a player causes /// an animation. @@ -65,7 +63,7 @@ impl<'a> System<'a> for AnimationBroadcastSystem { // Broadcast animation let packet = AnimationClientbound::new(event.player.id() as i32, event.animation); - util.broadcast(event.player, packet, Some(event.player)) + util.broadcast_entity(event.player, packet, Some(event.player)) } } diff --git a/server/src/player/digging.rs b/server/src/player/digging.rs index dd3f73c84..9b7a2664a 100644 --- a/server/src/player/digging.rs +++ b/server/src/player/digging.rs @@ -4,9 +4,7 @@ //! for completely unrelated actions, including eating, shooting bows, //! swapping items out the the offhand, and dropping items. -use specs::{ - Entities, Entity, LazyUpdate, Read, ReadStorage, ReaderId, System, World, Write, WriteStorage, -}; +use specs::{Entity, LazyUpdate, Read, ReadStorage, ReaderId, System, World, Write, WriteStorage}; use feather_core::network::cast_packet; use feather_core::network::packet::implementation::{ @@ -17,10 +15,9 @@ use feather_core::world::block::{Block, BlockExt}; use feather_core::world::{BlockPosition, ChunkMap}; use feather_core::{Gamemode, Item}; -use crate::chunk_logic::ChunkHolders; use crate::disconnect_player; use crate::entity::PlayerComponent; -use crate::network::{send_packet_to_all_players, NetworkComponent, PacketQueue}; +use crate::network::PacketQueue; use crate::player::{InventoryComponent, InventoryUpdateEvent}; use crate::util::Util; use feather_core::inventory::{ItemStack, SlotIndex, SLOT_HOTBAR_OFFSET}; diff --git a/server/src/player/inventory.rs b/server/src/player/inventory.rs index 7ecee412a..52ab2eb88 100644 --- a/server/src/player/inventory.rs +++ b/server/src/player/inventory.rs @@ -1,10 +1,7 @@ -use crate::chunk_logic::ChunkHolders; use crate::disconnect_player; -use crate::entity::{PlayerComponent, PositionComponent}; +use crate::entity::PlayerComponent; use crate::joinhandler::PlayerJoinEvent; -use crate::network::{ - send_packet_to_all_players, send_packet_to_player, NetworkComponent, PacketQueue, -}; +use crate::network::{send_packet_to_player, NetworkComponent, PacketQueue}; use crate::player::digging::PlayerItemDropEvent; use crate::util::Util; use feather_core::inventory::{ @@ -259,14 +256,13 @@ pub struct HeldItemBroadcastSystem { impl<'a> System<'a> for HeldItemBroadcastSystem { type SystemData = ( - ReadStorage<'a, NetworkComponent>, ReadStorage<'a, InventoryComponent>, Read<'a, EventChannel>, Read<'a, Util>, ); fn run(&mut self, data: Self::SystemData) { - let (networks, inventories, events, util) = data; + let (inventories, events, util) = data; for event in events.read(&mut self.reader.as_mut().unwrap()) { let inv = inventories.get(event.player).unwrap(); @@ -283,7 +279,7 @@ impl<'a> System<'a> for HeldItemBroadcastSystem { item, ); - util.broadcast(event.player, packet, Some(event.player)); + util.broadcast_entity(event.player, packet, Some(event.player)); } } } diff --git a/server/src/player/view.rs b/server/src/player/view.rs index ac6df31af..43b93d817 100644 --- a/server/src/player/view.rs +++ b/server/src/player/view.rs @@ -119,10 +119,10 @@ mod tests { let player1 = t::add_player(&mut world); let player2 = t::add_player(&mut world); - let entity1 = t::add_entity(&mut world, EntityType::Item, false); - let entity2 = t::add_entity(&mut world, EntityType::Item, false); - let entity3 = t::add_entity(&mut world, EntityType::Item, false); - let entity4 = t::add_entity(&mut world, EntityType::Item, false); + let entity1 = t::add_entity(&mut world, EntityType::Item, true); + let entity2 = t::add_entity(&mut world, EntityType::Item, true); + let entity3 = t::add_entity(&mut world, EntityType::Item, true); + let entity4 = t::add_entity(&mut world, EntityType::Item, true); let mut config = Config::default(); config.server.view_distance = 4; diff --git a/server/src/testframework.rs b/server/src/testframework.rs index e8f376fd1..6def9cc74 100644 --- a/server/src/testframework.rs +++ b/server/src/testframework.rs @@ -21,14 +21,10 @@ use feather_core::Gamemode; use crate::chunk_logic::ChunkHolders; use crate::config::Config; use crate::entity::metadata::{self, Metadata}; -use crate::entity::{ - EntityDestroyEvent, EntitySpawnEvent, EntityType, ItemComponent, NamedComponent, - PlayerComponent, PositionComponent, VelocityComponent, -}; +use crate::entity::{EntityDestroyEvent, EntitySpawnEvent, EntityType, ItemComponent, NamedComponent, PlayerComponent, PositionComponent, VelocityComponent, ChunkEntities}; use crate::io::ServerToWorkerMessage; use crate::network::{NetworkComponent, PacketQueue}; use crate::player::{InventoryComponent, PlayerDisconnectEvent}; -use crate::systems::BROADCASTER; use crate::util::BroadcasterSystem; use crate::PlayerCount; @@ -65,7 +61,7 @@ pub struct Player { pub fn add_player(world: &mut World) -> Player { let (ns1, nr1) = channel(); let (ns2, nr2) = channel(); - let e = world + let entity = world .create_entity() .with(NetworkComponent::new(ns1, nr2)) .with(PlayerComponent { @@ -91,12 +87,15 @@ pub fn add_player(world: &mut World) -> Player { for x in -view_distance..=view_distance { for z in -view_distance..=view_distance { - chunk_holders.insert_holder(ChunkPosition::new(x, z), e); + chunk_holders.insert_holder(ChunkPosition::new(x, z), entity); } } + let mut chunk_entities = world.fetch_mut::(); + chunk_entities.add_to_chunk(ChunkPosition::new(0, 0), entity); + Player { - entity: e, + entity, network_sender: ns2, network_receiver: nr1, } @@ -272,6 +271,9 @@ pub fn add_entity_with_pos_and_vel( .unwrap(); } + let mut chunk_entities = world.fetch_mut::(); + chunk_entities.add_to_chunk(pos.chunk_pos(), entity); + if trigger_spawn_event { let event = EntitySpawnEvent { entity, ty }; trigger_event(&world, event); @@ -369,13 +371,16 @@ impl<'a, 'b> TestBuilder<'a, 'b> { self.world .insert(EventChannel::::new()); self.world.insert(EventChannel::::new()); + self.world.insert(EventChannel::::new()); self.world.insert(crate::time::Time(0)); self.world.insert(ChunkHolders::default()); + self.world.insert(ChunkEntities::default()); self.world.insert(Arc::new(Config::default())); // Insert the broadcaster system, since it is so commonly // used that it should be used for all tests. - self.dispatcher.add_thread_local(BroadcasterSystem); + self.dispatcher.add_barrier(); + self.dispatcher.add(BroadcasterSystem, "", &[]); let mut dispatcher = self.dispatcher.build(); dispatcher.setup(&mut self.world); diff --git a/server/src/util/broadcaster.rs b/server/src/util/broadcaster.rs index 6b76bfef3..9b8760010 100644 --- a/server/src/util/broadcaster.rs +++ b/server/src/util/broadcaster.rs @@ -3,13 +3,14 @@ use crate::chunk_logic::ChunkHolders; use crate::entity::PositionComponent; -use crate::network::{send_packet_boxed_to_player, send_packet_to_player, NetworkComponent}; +use crate::network::{send_packet_boxed_to_player, NetworkComponent}; use crossbeam::queue::SegQueue; use feather_core::{ChunkPosition, Packet}; use specs::{Entities, Entity, Read, ReadStorage, System}; +use crate::util::Util; /// Broadcaster used to lazily broadcast packets. -#[derive(Default, Debug)] +#[derive(Default)] pub struct Broadcaster { /// Internal queue of broadcasts to send. queue: SegQueue, @@ -44,7 +45,6 @@ impl Broadcaster { } /// A broadcast request. -#[derive(Debug)] struct BroadcastRequest { /// Packet will only be sent to players able to see /// this entity or chunk. @@ -69,29 +69,36 @@ impl<'a> System<'a> for BroadcasterSystem { type SystemData = ( ReadStorage<'a, PositionComponent>, ReadStorage<'a, NetworkComponent>, - Read<'a, Broadcaster>, + Read<'a, Util>, Read<'a, ChunkHolders>, Entities<'a>, ); fn run(&mut self, data: Self::SystemData) { - let (positions, networks, broadcaster, chunk_holders, entities) = data; + let (positions, networks, util, chunk_holders, entities) = data; - while let Ok(request) = broadcaster.queue.pop() { - // Prevents a panic if the entity was destroyed. - if !entities.is_alive(request.entity) { - continue; - } + let broadcaster = &util.broadcaster; + while let Ok(request) = broadcaster.queue.pop() { // Broadcast packet. // Iterate over entities in the chunk_holders // entry for the chunk. If they are a player, // send the packet. // This works because any player able to see // a chunk will always have a chunk holder on the chunk. - if let Some(holders) = chunk_holders - .holders_for(positions.get(request.entity).unwrap().current.chunk_pos()) - { + + let chunk = match request.condition { + BroadcastCondition::Entity(entity) => { + // Prevents a panic if the entity was destroyed. + if !entities.is_alive(entity) { + continue; + } + + positions.get(entity).unwrap().current.chunk_pos() + } + BroadcastCondition::Chunk(chunk) => chunk, + }; + if let Some(holders) = chunk_holders.holders_for(chunk) { for holder in holders { if let Some(neq) = request.neq.as_ref() { if *holder == *neq { @@ -100,7 +107,7 @@ impl<'a> System<'a> for BroadcasterSystem { } if let Some(network) = networks.get(*holder) { - send_packet_boxed_to_player(network, request.packet.clone()); + send_packet_boxed_to_player(network, request.packet.box_clone()); } } } diff --git a/server/src/util/mod.rs b/server/src/util/mod.rs index 857e0b310..831b6a2c3 100644 --- a/server/src/util/mod.rs +++ b/server/src/util/mod.rs @@ -10,13 +10,10 @@ mod macros; mod broadcaster; mod spawn; -use crate::chunk_logic::ChunkHolders; -use crate::network::{send_packet_to_player, NetworkComponent}; use broadcaster::Broadcaster; pub use broadcaster::BroadcasterSystem; pub use macros::*; pub use spawn::SpawnerSystem; -use specs::storage::GenericReadStorage; use specs::Entity; /// Converts float-based velocity in blocks per tick @@ -52,7 +49,7 @@ pub fn protocol_velocity(vel: DVec3) -> (i16, i16, i16) { /// * `broadcast` - lazily broadcasts a packet to all players /// who are able to see a given chunk. This can be used /// to broadcast movement updates, for example. -#[derive(Debug, Default)] +#[derive(Default)] pub struct Util { /// Thread-local bump allocator, reset /// every tick. @@ -140,6 +137,7 @@ mod tests { use feather_core::network::packet::implementation::EntityHeadLook; use feather_core::PacketType; use specs::WorldExt; + use crate::chunk_logic::ChunkHolders; #[test] fn test_broadcast() { @@ -163,6 +161,9 @@ mod tests { util.broadcast_entity(player1.entity, packet, Some(player2.entity)); + world.insert(util); + world.insert(chunk_holders); + dispatcher.dispatch(&world); world.maintain(); From e955bd5ac8ce7771ac992e883c17ec9799cbd47c Mon Sep 17 00:00:00 2001 From: caelunshun Date: Tue, 3 Sep 2019 19:59:14 -0600 Subject: [PATCH 6/9] Get tests to pass --- server/src/entity/chunk.rs | 24 +++++---- server/src/entity/item.rs | 6 +-- server/src/entity/mod.rs | 2 +- server/src/main.rs | 3 ++ server/src/player/view.rs | 12 ++--- server/src/testframework.rs | 94 ++++++++++++++++++++++++++++------ server/src/util/broadcaster.rs | 2 +- server/src/util/mod.rs | 2 +- 8 files changed, 104 insertions(+), 41 deletions(-) diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index c865250cb..25abc8ec9 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -75,8 +75,6 @@ impl ChunkEntities { for z_offset in -view_distance..=view_distance { let chunk = ChunkPosition::new(chunk.x + x_offset, chunk.z + z_offset); - println!("{:?}", chunk); - result.extend(self.entities_in_chunk(chunk)); } } @@ -185,10 +183,13 @@ mod tests { let (mut w, mut d) = t::init_world(); let pos = position!(1.0, 64.0, 1003.5); - let entity = w.create_entity().with(PositionComponent { - current: pos, - previous: pos, - }).build(); + let entity = w + .create_entity() + .with(PositionComponent { + current: pos, + previous: pos, + }) + .build(); let event = EntitySpawnEvent { entity, @@ -213,10 +214,13 @@ mod tests { let pos = position!(1.0, 64.0, -14.0); let old_pos = position!(1.0, 64.0, -18.0); - let entity = w.create_entity().with(PositionComponent { - current: pos, - previous: old_pos - }).build(); + let entity = w + .create_entity() + .with(PositionComponent { + current: pos, + previous: old_pos, + }) + .build(); d.dispatch(&w); w.maintain(); diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index 796af60d5..fe34f8957 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -375,11 +375,7 @@ mod tests { #[test] fn test_item_merge_system() { let (mut w, mut d) = t::builder() - .with_dep( - ItemMergeSystem::default(), - "item_merge", - &[], - ) + .with_dep(ItemMergeSystem::default(), "item_merge", &[]) .build(); let item1 = diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 2eb651e20..98ceafd57 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -20,13 +20,13 @@ pub use broadcast::EntitySendSystem; pub use broadcast::EntitySender; pub use broadcast::EntitySpawnEvent; pub use chunk::ChunkEntities; +pub use chunk::ChunkEntityUpdateSystem; pub use component::{NamedComponent, PlayerComponent, PositionComponent, VelocityComponent}; pub use destroy::EntityDestroyEvent; pub use item::ItemComponent; pub use metadata::{EntityBitMask, Metadata}; pub use movement::broadcast_entity_movement; pub use types::EntityType; -pub use chunk::ChunkEntityUpdateSystem; use crate::entity::destroy::EntityDestroyBroadcastSystem; use crate::entity::item::ItemCollectSystem; diff --git a/server/src/main.rs b/server/src/main.rs index eb68a2ba6..9e871371c 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -241,6 +241,9 @@ fn init_world<'a, 'b>( player::init_broadcast(&mut dispatcher); entity::init_broadcast(&mut dispatcher); + + // Broadcast system needs to run last. + dispatcher.add_barrier(); dispatcher.add(util::BroadcasterSystem, BROADCASTER, &[]); let mut dispatcher = dispatcher.build(); diff --git a/server/src/player/view.rs b/server/src/player/view.rs index 43b93d817..1c15cff41 100644 --- a/server/src/player/view.rs +++ b/server/src/player/view.rs @@ -116,13 +116,13 @@ mod tests { let player_chunk = ChunkPosition::new(0, 0); - let player1 = t::add_player(&mut world); - let player2 = t::add_player(&mut world); + let player1 = t::add_player_without_holder(&mut world); + let player2 = t::add_player_without_holder(&mut world); - let entity1 = t::add_entity(&mut world, EntityType::Item, true); - let entity2 = t::add_entity(&mut world, EntityType::Item, true); - let entity3 = t::add_entity(&mut world, EntityType::Item, true); - let entity4 = t::add_entity(&mut world, EntityType::Item, true); + let entity1 = t::add_entity_without_holder(&mut world, EntityType::Item, true); + let entity2 = t::add_entity_without_holder(&mut world, EntityType::Item, true); + let entity3 = t::add_entity_without_holder(&mut world, EntityType::Item, true); + let entity4 = t::add_entity_without_holder(&mut world, EntityType::Item, true); let mut config = Config::default(); config.server.view_distance = 4; diff --git a/server/src/testframework.rs b/server/src/testframework.rs index 6def9cc74..1e94c68d8 100644 --- a/server/src/testframework.rs +++ b/server/src/testframework.rs @@ -21,7 +21,10 @@ use feather_core::Gamemode; use crate::chunk_logic::ChunkHolders; use crate::config::Config; use crate::entity::metadata::{self, Metadata}; -use crate::entity::{EntityDestroyEvent, EntitySpawnEvent, EntityType, ItemComponent, NamedComponent, PlayerComponent, PositionComponent, VelocityComponent, ChunkEntities}; +use crate::entity::{ + ChunkEntities, EntityDestroyEvent, EntitySpawnEvent, EntityType, ItemComponent, NamedComponent, + PlayerComponent, PositionComponent, VelocityComponent, +}; use crate::io::ServerToWorkerMessage; use crate::network::{NetworkComponent, PacketQueue}; use crate::player::{InventoryComponent, PlayerDisconnectEvent}; @@ -58,7 +61,33 @@ pub struct Player { /// Adds a player to the world, inserting /// all the necessary components. Returns /// a number of useful channels. +/// +/// # Notes +/// * A `ChunkHolders` and `ChunkEntities` entry +/// is created for the player. If this behavior is not +/// desired, use `add_player_without_holder`. pub fn add_player(world: &mut World) -> Player { + let player = add_player_without_holder(world); + + let mut chunk_holders = world.fetch_mut::(); + + let view_distance = i32::from(world.fetch::>().server.view_distance); + + for x in -view_distance..=view_distance { + for z in -view_distance..=view_distance { + chunk_holders.insert_holder(ChunkPosition::new(x, z), player.entity); + } + } + + let mut chunk_entities = world.fetch_mut::(); + chunk_entities.add_to_chunk(ChunkPosition::new(0, 0), player.entity); + + player +} + +/// Adds a player to the world without adding the `ChunkHolders` +/// and `ChunkEntities` entries. +pub fn add_player_without_holder(world: &mut World) -> Player { let (ns1, nr1) = channel(); let (ns2, nr2) = channel(); let entity = world @@ -81,19 +110,6 @@ pub fn add_player(world: &mut World) -> Player { .with(EntityType::Player) .build(); - let mut chunk_holders = world.fetch_mut::(); - - let view_distance = i32::from(world.fetch::>().server.view_distance); - - for x in -view_distance..=view_distance { - for z in -view_distance..=view_distance { - chunk_holders.insert_holder(ChunkPosition::new(x, z), entity); - } - } - - let mut chunk_entities = world.fetch_mut::(); - chunk_entities.add_to_chunk(ChunkPosition::new(0, 0), entity); - Player { entity, network_sender: ns2, @@ -220,10 +236,26 @@ pub fn triggered_events( /// Creates an entity at the origin with zero /// velocity. +/// +/// +/// # Notes +/// * A `ChunkHolders` and `ChunkEntities` entry +/// is created for the entity. If this behavior is not +/// desired, use `add_entity_without_holder`. pub fn add_entity(world: &mut World, ty: EntityType, trigger_spawn_event: bool) -> Entity { add_entity_with_pos(world, ty, Position::default(), trigger_spawn_event) } +/// Creates an entity at the origin with zero velocity, without +/// adding a chunk holder or chunk entities entry for it. +pub fn add_entity_without_holder( + world: &mut World, + ty: EntityType, + trigger_spawn_event: bool, +) -> Entity { + add_entity_without_holder_with_pos(world, ty, Position::default(), trigger_spawn_event) +} + /// Creates an entity with the given position /// and zero velocity. pub fn add_entity_with_pos( @@ -241,6 +273,21 @@ pub fn add_entity_with_pos( ) } +pub fn add_entity_without_holder_with_pos( + world: &mut World, + ty: EntityType, + pos: Position, + trigger_spawn_event: bool, +) -> Entity { + add_entity_without_holder_with_pos_and_vel( + world, + ty, + pos, + glm::vec3(0.0, 0.0, 0.0), + trigger_spawn_event, + ) +} + /// Creates an entity with the given position and velocity. pub fn add_entity_with_pos_and_vel( world: &mut World, @@ -248,6 +295,22 @@ pub fn add_entity_with_pos_and_vel( pos: Position, vel: DVec3, trigger_spawn_event: bool, +) -> Entity { + let entity = + add_entity_without_holder_with_pos_and_vel(world, ty, pos, vel, trigger_spawn_event); + + let mut chunk_entities = world.fetch_mut::(); + chunk_entities.add_to_chunk(pos.chunk_pos(), entity); + + entity +} + +pub fn add_entity_without_holder_with_pos_and_vel( + world: &mut World, + ty: EntityType, + pos: Position, + vel: DVec3, + trigger_spawn_event: bool, ) -> Entity { let entity = world .create_entity() @@ -271,9 +334,6 @@ pub fn add_entity_with_pos_and_vel( .unwrap(); } - let mut chunk_entities = world.fetch_mut::(); - chunk_entities.add_to_chunk(pos.chunk_pos(), entity); - if trigger_spawn_event { let event = EntitySpawnEvent { entity, ty }; trigger_event(&world, event); diff --git a/server/src/util/broadcaster.rs b/server/src/util/broadcaster.rs index 9b8760010..47dc040ab 100644 --- a/server/src/util/broadcaster.rs +++ b/server/src/util/broadcaster.rs @@ -4,10 +4,10 @@ use crate::chunk_logic::ChunkHolders; use crate::entity::PositionComponent; use crate::network::{send_packet_boxed_to_player, NetworkComponent}; +use crate::util::Util; use crossbeam::queue::SegQueue; use feather_core::{ChunkPosition, Packet}; use specs::{Entities, Entity, Read, ReadStorage, System}; -use crate::util::Util; /// Broadcaster used to lazily broadcast packets. #[derive(Default)] diff --git a/server/src/util/mod.rs b/server/src/util/mod.rs index 831b6a2c3..9dab9dd7b 100644 --- a/server/src/util/mod.rs +++ b/server/src/util/mod.rs @@ -132,12 +132,12 @@ impl Util { #[cfg(test)] mod tests { use super::*; + use crate::chunk_logic::ChunkHolders; use crate::testframework as t; use crate::util::broadcaster::BroadcasterSystem; use feather_core::network::packet::implementation::EntityHeadLook; use feather_core::PacketType; use specs::WorldExt; - use crate::chunk_logic::ChunkHolders; #[test] fn test_broadcast() { From cfe9256c9a3495667488454b851005b94380aa42 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Tue, 3 Sep 2019 21:22:21 -0600 Subject: [PATCH 7/9] Rename Util::broadcast_entity to Util::broadcast_entity_update This better reflects what the function does. bla --- server/src/entity/broadcast.rs | 2 -- server/src/entity/chunk.rs | 19 +++++++++++-------- server/src/entity/item.rs | 2 +- server/src/entity/metadata.rs | 2 +- server/src/entity/movement.rs | 10 +++++----- server/src/player/animation.rs | 2 +- server/src/player/digging.rs | 2 +- server/src/player/inventory.rs | 2 +- server/src/player/mod.rs | 2 +- server/src/player/view.rs | 3 --- server/src/util/broadcaster.rs | 4 ++-- server/src/util/mod.rs | 11 ++++++----- 12 files changed, 30 insertions(+), 31 deletions(-) diff --git a/server/src/entity/broadcast.rs b/server/src/entity/broadcast.rs index 18dd07c02..9e3c39086 100644 --- a/server/src/entity/broadcast.rs +++ b/server/src/entity/broadcast.rs @@ -95,8 +95,6 @@ impl<'a> System<'a> for EntitySendSystem { let network = networks.get(request.player).unwrap(); - debug!("Sending ({:?}, {:?}) to player", ty, named); - // Send corresponding packet to player. let packet = packet_to_spawn_entity(request.entity, *ty, &position, metadata, velocity, named); diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index 25abc8ec9..7ad357395 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -69,7 +69,10 @@ impl ChunkEntities { ) -> HashSet { let mut result = HashSet::new(); - let view_distance = i32::from(view_distance); + // 1 is subtracted from the view distance because of some odd + // client-side glitch (or maybe it's our fault?) where the last chunk within the view distance + // is not loaded correctly. + let view_distance = i32::from(view_distance) - 1; for x_offset in -view_distance..=view_distance { for z_offset in -view_distance..=view_distance { @@ -110,7 +113,7 @@ impl<'a> System<'a> for ChunkEntityUpdateSystem { self.dirty.clear(); for event in positions.channel().read(self.move_reader.as_mut().unwrap()) { match event { - ComponentEvent::Inserted(id) | ComponentEvent::Modified(id) => { + ComponentEvent::Modified(id) => { self.dirty.add(*id); } _ => (), @@ -180,7 +183,7 @@ mod tests { #[test] fn test_new_entity() { - let (mut w, mut d) = t::init_world(); + let (mut w, mut d) = t::builder().with(ChunkEntityUpdateSystem::default(), "").build(); let pos = position!(1.0, 64.0, 1003.5); let entity = w @@ -209,7 +212,7 @@ mod tests { #[test] fn test_moved_entity() { - let (mut w, mut d) = t::init_world(); + let (mut w, mut d) = t::builder().with(ChunkEntityUpdateSystem::default(), "").build(); let pos = position!(1.0, 64.0, -14.0); let old_pos = position!(1.0, 64.0, -18.0); @@ -237,7 +240,7 @@ mod tests { #[test] fn test_destroyed_entity() { - let (mut w, mut d) = t::init_world(); + let (mut w, mut d) = t::builder().with(ChunkEntityUpdateSystem::default(), "").build(); let pos = position!(100.0, -100.0, -100.0); let entity = t::add_entity_with_pos(&mut w, EntityType::Player, pos, false); @@ -263,9 +266,9 @@ mod tests { let entity4 = world.create_entity().build(); let chunk1 = ChunkPosition::new(0, 0); - let chunk2 = ChunkPosition::new(0, 4); - let chunk3 = ChunkPosition::new(0, 5); - let chunk4 = ChunkPosition::new(-4, -4); + let chunk2 = ChunkPosition::new(0, 3); + let chunk3 = ChunkPosition::new(0, 4); + let chunk4 = ChunkPosition::new(-3, -3); chunk_entities.add_to_chunk(chunk1, entity1); chunk_entities.add_to_chunk(chunk2, entity2); diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index fe34f8957..44324a02a 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -283,7 +283,7 @@ impl<'a> System<'a> for ItemCollectSystem { collector: player.id() as i32, count: i32::from(stack.amount - amount_left), }; - util.broadcast_entity(player, packet, None); + util.broadcast_entity_update(player, packet, None); if amount_left == 0 { entities.delete(other).unwrap(); diff --git a/server/src/entity/metadata.rs b/server/src/entity/metadata.rs index 2c3bfae0c..65120218e 100644 --- a/server/src/entity/metadata.rs +++ b/server/src/entity/metadata.rs @@ -81,7 +81,7 @@ impl<'a> System<'a> for MetadataBroadcastSystem { metadata: metadata.to_raw_metadata(), }; - util.broadcast_entity(entity, packet, None); + util.broadcast_entity_update(entity, packet, None); } metadatas.set_event_emission(true); diff --git a/server/src/entity/movement.rs b/server/src/entity/movement.rs index 38c6feaad..597eb3487 100644 --- a/server/src/entity/movement.rs +++ b/server/src/entity/movement.rs @@ -83,7 +83,7 @@ impl<'a> System<'a> for EntityVelocityBroadcastSystem { velocity_z, }; - util.broadcast_entity(entity, packet, Some(entity)); + util.broadcast_entity_update(entity, packet, Some(entity)); } } @@ -128,10 +128,10 @@ pub fn broadcast_entity_movement( degrees_to_stops(new_pos.pitch), new_pos.on_ground, ); - util.broadcast_entity(entity, packet, Some(entity)); + util.broadcast_entity_update(entity, packet, Some(entity)); } else { let packet = EntityRelativeMove::new(entity.id() as i32, rx, ry, rz, new_pos.on_ground); - util.broadcast_entity(entity, packet, Some(entity)); + util.broadcast_entity_update(entity, packet, Some(entity)); } } else { let packet = EntityLook::new( @@ -140,13 +140,13 @@ pub fn broadcast_entity_movement( degrees_to_stops(new_pos.pitch), new_pos.on_ground, ); - util.broadcast_entity(entity, packet, Some(entity)); + util.broadcast_entity_update(entity, packet, Some(entity)); } // Entity Head Look also needs to be sent if the entity turned its head if has_looked { let packet = EntityHeadLook::new(entity.id() as i32, degrees_to_stops(new_pos.yaw)); - util.broadcast_entity(entity, packet, Some(entity)); + util.broadcast_entity_update(entity, packet, Some(entity)); } } diff --git a/server/src/player/animation.rs b/server/src/player/animation.rs index a91a6efee..acf600b75 100644 --- a/server/src/player/animation.rs +++ b/server/src/player/animation.rs @@ -63,7 +63,7 @@ impl<'a> System<'a> for AnimationBroadcastSystem { // Broadcast animation let packet = AnimationClientbound::new(event.player.id() as i32, event.animation); - util.broadcast_entity(event.player, packet, Some(event.player)) + util.broadcast_entity_update(event.player, packet, Some(event.player)) } } diff --git a/server/src/player/digging.rs b/server/src/player/digging.rs index 9b7a2664a..3c3c2c68a 100644 --- a/server/src/player/digging.rs +++ b/server/src/player/digging.rs @@ -263,7 +263,7 @@ impl<'a> System<'a> for BlockUpdateBroadcastSystem { }; let packet = BlockChange::new(event.pos, i32::from(event.new_block.native_state_id())); - util.broadcast_chunk(event.pos.chunk_pos(), packet, neq); + util.broadcast_chunk_update(event.pos.chunk_pos(), packet, neq); } } diff --git a/server/src/player/inventory.rs b/server/src/player/inventory.rs index 52ab2eb88..2e5cda605 100644 --- a/server/src/player/inventory.rs +++ b/server/src/player/inventory.rs @@ -279,7 +279,7 @@ impl<'a> System<'a> for HeldItemBroadcastSystem { item, ); - util.broadcast_entity(event.player, packet, Some(event.player)); + util.broadcast_entity_update(event.player, packet, Some(event.player)); } } } diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index fb3a3a695..c926dd86c 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -72,13 +72,13 @@ pub fn init_logic(dispatcher: &mut DispatcherBuilder) { } pub fn init_handlers(dispatcher: &mut DispatcherBuilder) { + dispatcher.add(ViewUpdateSystem::default(), VIEW_UPDATE, &[]); dispatcher.add(ChunkCrossSystem::default(), CHUNK_CROSS, &[]); dispatcher.add(ClientChunkUnloadSystem, CLIENT_CHUNK_UNLOAD, &[]); dispatcher.add(PlayerInitSystem::default(), PLAYER_INIT, &[]); } pub fn init_broadcast(dispatcher: &mut DispatcherBuilder) { - dispatcher.add(ViewUpdateSystem::default(), VIEW_UPDATE, &[]); dispatcher.add(HeldItemBroadcastSystem::default(), HELD_ITEM_BROADCAST, &[]); dispatcher.add(JoinBroadcastSystem::default(), JOIN_BROADCAST, &[]); dispatcher.add( diff --git a/server/src/player/view.rs b/server/src/player/view.rs index 1c15cff41..91e52828e 100644 --- a/server/src/player/view.rs +++ b/server/src/player/view.rs @@ -63,14 +63,11 @@ impl<'a> System<'a> for ViewUpdateSystem { // on the client. to_destroy.push(entity.id() as i32); - debug!("Destroying {} on client", entity.id()); - if let Some(network) = networks.get(*entity) { let packet = DestroyEntities { entity_ids: vec![event.player.id() as i32], }; send_packet_to_player(network, packet); - debug!("Destroying {} on client", event.player.id()); } } else { // Entity is in `new_entities` but not in `old_entities`. diff --git a/server/src/util/broadcaster.rs b/server/src/util/broadcaster.rs index 47dc040ab..58cdd448c 100644 --- a/server/src/util/broadcaster.rs +++ b/server/src/util/broadcaster.rs @@ -19,7 +19,7 @@ pub struct Broadcaster { impl Broadcaster { /// Lazily broadcasts a packet to all players /// able to see a given entity. - pub fn broadcast_entity

(&self, entity: Entity, packet: P, neq: Option) + pub fn broadcast_entity_update

(&self, entity: Entity, packet: P, neq: Option) where P: Packet + 'static, { @@ -32,7 +32,7 @@ impl Broadcaster { /// Lazily broadcasts a packet to all players /// able to see a given chunk. - pub fn broadcast_chunk

(&self, chunk: ChunkPosition, packet: P, neq: Option) + pub fn broadcast_chunk_update

(&self, chunk: ChunkPosition, packet: P, neq: Option) where P: Packet + 'static, { diff --git a/server/src/util/mod.rs b/server/src/util/mod.rs index 9dab9dd7b..328b20fec 100644 --- a/server/src/util/mod.rs +++ b/server/src/util/mod.rs @@ -103,11 +103,12 @@ impl Util { /// This function runs in linear time with /// regard to the number of players able to see /// the entity. - pub fn broadcast_entity

(&self, entity: Entity, packet: P, neq: Option) + pub fn broadcast_entity_update

(&self, entity: Entity, packet: P, neq: Option) where P: Packet + Clone + 'static, { - self.broadcaster.broadcast_entity(entity, packet, neq); + self.broadcaster + .broadcast_entity_update(entity, packet, neq); } /// Broadcasts a packet to all players who @@ -121,11 +122,11 @@ impl Util { /// This function runs in linear time with /// regard to the number of players able to see /// the chunk. - pub fn broadcast_chunk

(&self, chunk: ChunkPosition, packet: P, neq: Option) + pub fn broadcast_chunk_update

(&self, chunk: ChunkPosition, packet: P, neq: Option) where P: Packet + Clone + 'static, { - self.broadcaster.broadcast_chunk(chunk, packet, neq); + self.broadcaster.broadcast_chunk_update(chunk, packet, neq); } } @@ -159,7 +160,7 @@ mod tests { let util = Util::default(); - util.broadcast_entity(player1.entity, packet, Some(player2.entity)); + util.broadcast_entity_update(player1.entity, packet, Some(player2.entity)); world.insert(util); world.insert(chunk_holders); From 07d45a5a44ea5a8ebba92604ddf4978dec5a21cc Mon Sep 17 00:00:00 2001 From: caelunshun Date: Wed, 4 Sep 2019 21:05:15 -0600 Subject: [PATCH 8/9] Fix tests --- server/src/entity/chunk.rs | 20 ++++++++++++++++---- server/src/player/view.rs | 4 ++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index 7ad357395..cd2b201d8 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -183,7 +183,9 @@ mod tests { #[test] fn test_new_entity() { - let (mut w, mut d) = t::builder().with(ChunkEntityUpdateSystem::default(), "").build(); + let (mut w, mut d) = t::builder() + .with(ChunkEntityUpdateSystem::default(), "") + .build(); let pos = position!(1.0, 64.0, 1003.5); let entity = w @@ -212,7 +214,9 @@ mod tests { #[test] fn test_moved_entity() { - let (mut w, mut d) = t::builder().with(ChunkEntityUpdateSystem::default(), "").build(); + let (mut w, mut d) = t::builder() + .with(ChunkEntityUpdateSystem::default(), "") + .build(); let pos = position!(1.0, 64.0, -14.0); let old_pos = position!(1.0, 64.0, -18.0); @@ -220,11 +224,17 @@ mod tests { let entity = w .create_entity() .with(PositionComponent { - current: pos, + current: old_pos, previous: old_pos, }) .build(); + // Trigger flagged storage event. + w.write_component::() + .get_mut(entity) + .unwrap() + .current = pos; + d.dispatch(&w); w.maintain(); @@ -240,7 +250,9 @@ mod tests { #[test] fn test_destroyed_entity() { - let (mut w, mut d) = t::builder().with(ChunkEntityUpdateSystem::default(), "").build(); + let (mut w, mut d) = t::builder() + .with(ChunkEntityUpdateSystem::default(), "") + .build(); let pos = position!(100.0, -100.0, -100.0); let entity = t::add_entity_with_pos(&mut w, EntityType::Player, pos, false); diff --git a/server/src/player/view.rs b/server/src/player/view.rs index 91e52828e..a518dd3d0 100644 --- a/server/src/player/view.rs +++ b/server/src/player/view.rs @@ -129,9 +129,9 @@ mod tests { let mut chunk_entities = world.fetch_mut::(); chunk_entities.add_to_chunk(player_chunk, player1.entity); chunk_entities.add_to_chunk(player_chunk, player2.entity); - chunk_entities.add_to_chunk(ChunkPosition::new(4, -4), entity1); + chunk_entities.add_to_chunk(ChunkPosition::new(3, -3), entity1); chunk_entities.add_to_chunk(player_chunk, entity2); - chunk_entities.add_to_chunk(ChunkPosition::new(5, -4), entity3); + chunk_entities.add_to_chunk(ChunkPosition::new(4, -3), entity3); chunk_entities.add_to_chunk(ChunkPosition::new(100, 103), entity4); } From 5495df8a65c3fcfc0fd839d7010becd10917b594 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Thu, 5 Sep 2019 17:08:31 -0600 Subject: [PATCH 9/9] Fix clippy warning --- Cargo.lock | 16 ++++------------ server/src/entity/chunk.rs | 7 ++----- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5cb013a59..302118407 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -686,7 +686,7 @@ dependencies = [ "shrev 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "simple_logger 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "specs 0.15.0 (git+https://github.com/AndreaCatania/specs?branch=emsig)", + "specs 0.15.0 (git+https://github.com/slide-rs/specs)", "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1253,11 +1253,6 @@ name = "nodrop" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "nonzero_signed" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "num-bigint" version = "0.2.2" @@ -2021,15 +2016,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "specs" version = "0.15.0" -source = "git+https://github.com/AndreaCatania/specs?branch=emsig#1abeb1439186062c0ee3389950f7f74e65b98801" +source = "git+https://github.com/slide-rs/specs#48fa4f0a8c21c44b406f6498322cfbbdd79c3ea8" dependencies = [ "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "derivative 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "hibitset 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mopa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "nonzero_signed 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "shred 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "shrev 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2636,7 +2629,6 @@ dependencies = [ "checksum ncollide3d 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ee57cac70a2892e89fab7d5fd295b0ad544d1f877fa70fe8ae4be477514dd61" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum nonzero_signed 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "02783a0482333b0d3f5f5411b8fb60454a596696da041da0470ac9ef3e6e37d8" "checksum num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57450397855d951f1a41305e54851b1a7b8f5d2e349543a02a2effe25459f718" "checksum num-bigint-dig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3cd60678022301da54082fcc383647fc895cba2795f868c871d58d29c8922595" "checksum num-complex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fcb0cf31fb3ff77e6d2a6ebd6800df7fdcd106f2ad89113c9130bcd07f93dffc" @@ -2722,7 +2714,7 @@ dependencies = [ "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum slotmap 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "759fd553261805f128e2900bf69ab3d034260bc338caf7f0ee54dbf035c85acd" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" -"checksum specs 0.15.0 (git+https://github.com/AndreaCatania/specs?branch=emsig)" = "" +"checksum specs 0.15.0 (git+https://github.com/slide-rs/specs)" = "" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8861bc80f649f5b4c9bd38b696ae9af74499d479dbfb327f0607de6b326a36bc" "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index cd2b201d8..3bcebbbe5 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -112,11 +112,8 @@ impl<'a> System<'a> for ChunkEntityUpdateSystem { self.dirty.clear(); for event in positions.channel().read(self.move_reader.as_mut().unwrap()) { - match event { - ComponentEvent::Modified(id) => { - self.dirty.add(*id); - } - _ => (), + if let ComponentEvent::Modified(id) = event { + self.dirty.add(*id); } }