From 14aa9ff74589aa2e48c3a36c7c2efc8907b74969 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 6 Oct 2019 10:11:10 -0600 Subject: [PATCH 001/647] Add more items to `max_stack_size()` See also #158. --- core/src/inventory.rs | 92 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/core/src/inventory.rs b/core/src/inventory.rs index cb5ec2714..aa661453b 100644 --- a/core/src/inventory.rs +++ b/core/src/inventory.rs @@ -118,8 +118,96 @@ pub fn max_size(item: Item) -> u8 { | Item::Book | Item::WrittenBook | Item::WritableBook - | Item::FlintAndSteel => 1, - Item::EnderPearl => 16, + | Item::FlintAndSteel + | Item::WhiteBed + | Item::OrangeBed + | Item::MagentaBed + | Item::LightBlueBed + | Item::YellowBed + | Item::LimeBed + | Item::PinkBed + | Item::GrayBed + | Item::LightGrayBed + | Item::CyanBed + | Item::PurpleBed + | Item::BlueBed + | Item::BrownBed + | Item::GreenBed + | Item::RedBed + | Item::BlackBed + | Item::ShulkerBox + | Item::TurtleEgg + | Item::TurtleHelmet + | Item::FishingRod + | Item::EnchantedBook + | Item::Potion + | Item::LingeringPotion + | Item::SplashPotion + | Item::WaterBucket + | Item::LavaBucket + | Item::TropicalFishBucket + | Item::CodBucket + | Item::MilkBucket + | Item::PufferfishBucket + | Item::SalmonBucket + | Item::CarrotOnAStick + | Item::Elytra + | Item::Shield + | Item::Trident + | Item::MusicDisc13 + | Item::MusicDiscCat + | Item::MusicDiscBlocks + | Item::MusicDiscChirp + | Item::MusicDiscFar + | Item::MusicDiscMall + | Item::MusicDiscMellohi + | Item::MusicDiscStal + | Item::MusicDiscStrad + | Item::MusicDiscWard + | Item::MusicDisc11 + | Item::MusicDiscWait + | Item::TotemOfUndying + | Item::Shears + | Item::AcaciaBoat + | Item::DarkOakBoat + | Item::OakBoat + | Item::SpruceBoat + | Item::BirchBoat + | Item::JungleBoat + | Item::MushroomStew + | Item::BeetrootSoup + | Item::RabbitStew + | Item::Cake + | Item::Minecart + | Item::ChestMinecart + | Item::CommandBlockMinecart + | Item::FurnaceMinecart + | Item::HopperMinecart + | Item::TntMinecart + | Item::DiamondHorseArmor + | Item::GoldenHorseArmor + | Item::IronHorseArmor => 1, + Item::EnderPearl + | Item::Snowball + | Item::WhiteBanner + | Item::OrangeBanner + | Item::MagentaBanner + | Item::LightBlueBanner + | Item::YellowBanner + | Item::LimeBanner + | Item::PinkBanner + | Item::GrayBanner + | Item::LightGrayBanner + | Item::CyanBanner + | Item::PurpleBanner + | Item::BlueBanner + | Item::BrownBanner + | Item::GreenBanner + | Item::RedBanner + | Item::BlackBanner + | Item::Sign + | Item::ArmorStand + | Item::Egg => 16, _ => 64, // TODO: are we missing some here? } From 1740dc8ac0d87cdca97c8655a09975956f8ff16f Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 6 Oct 2019 22:11:47 -0600 Subject: [PATCH 002/647] Begin refactoring entities --- core/src/save/entity.rs | 19 ++ server/src/blocks/falling.rs | 17 +- server/src/entity/arrow.rs | 67 ----- server/src/entity/component.rs | 38 ++- server/src/entity/impls/arrow.rs | 137 +++++++++ .../src/entity/{ => impls}/falling_block.rs | 64 ++++- server/src/entity/{ => impls}/item.rs | 91 +++++- server/src/entity/impls/mod.rs | 23 ++ server/src/entity/mod.rs | 7 +- server/src/entity/types.rs | 44 --- server/src/lazy.rs | 28 ++ server/src/lib.rs | 2 + server/src/physics/block_bboxes.rs | 4 +- server/src/physics/component.rs | 153 +++++----- server/src/physics/mod.rs | 8 +- server/src/systems.rs | 2 - server/src/util/mod.rs | 33 --- server/src/util/spawn.rs | 264 ------------------ server/src/world_ext.rs | 18 ++ 19 files changed, 506 insertions(+), 513 deletions(-) delete mode 100644 server/src/entity/arrow.rs create mode 100644 server/src/entity/impls/arrow.rs rename server/src/entity/{ => impls}/falling_block.rs (52%) rename server/src/entity/{ => impls}/item.rs (83%) create mode 100644 server/src/entity/impls/mod.rs delete mode 100644 server/src/entity/types.rs create mode 100644 server/src/lazy.rs delete mode 100644 server/src/util/spawn.rs create mode 100644 server/src/world_ext.rs diff --git a/core/src/save/entity.rs b/core/src/save/entity.rs index ea2c848c6..8b156d0f4 100644 --- a/core/src/save/entity.rs +++ b/core/src/save/entity.rs @@ -70,6 +70,15 @@ impl BaseEntityData { } impl BaseEntityData { + /// Creates a `BaseEntityData` from a position and velocity. + pub fn new(pos: Position, velocity: glm::DVec3) -> Self { + Self { + position: vec![pos.x, pos.y, pos.z], + rotation: vec![pos.yaw, pos.pitch], + velocity: vec![velocity.x, velocity.y, velocity.z], + } + } + /// Reads the position and rotation fields. If the fields are invalid, None is returned. pub fn read_position(self: &BaseEntityData) -> Option { if self.position.len() == 3 && self.rotation.len() == 2 { @@ -248,4 +257,14 @@ mod tests { let vel = data.read_velocity(); assert!(vel.is_none()); } + + #[test] + fn test_new() { + let pos = position!(1.0, 10.0, 3.0, 115.0, -3.0); + let vel = glm::vec3(0.0, 1.0, 2.0); + + let data = BaseEntityData::new(pos, vel); + assert_eq!(data.read_position(), Some(pos)); + assert_eq!(data.read_velocity(), Some(vel)); + } } diff --git a/server/src/blocks/falling.rs b/server/src/blocks/falling.rs index 4a35f5441..dada3abfc 100644 --- a/server/src/blocks/falling.rs +++ b/server/src/blocks/falling.rs @@ -1,12 +1,14 @@ use shrev::ReaderId; use specs::shrev::EventChannel; -use specs::{Read, System, Write}; +use specs::{Builder, Entities, LazyUpdate, Read, System, Write}; use feather_core::world::ChunkMap; use feather_blocks::{Block, BlockExt}; use crate::blocks::{BlockNotifyEvent, BlockUpdateCause, BlockUpdateEvent}; +use crate::entity::{falling_block, PositionComponent, VelocityComponent}; +use crate::lazy::LazyUpdateExt; use crate::util::Util; use feather_core::Position; @@ -19,13 +21,14 @@ pub struct FallingBlockCreationSystem { impl<'a> System<'a> for FallingBlockCreationSystem { type SystemData = ( Read<'a, EventChannel>, - Read<'a, Util>, Write<'a, EventChannel>, Write<'a, ChunkMap>, + Read<'a, LazyUpdate>, + Entities<'a>, ); fn run(&mut self, data: Self::SystemData) { - let (events, util, mut block_update, mut chunk_map) = data; + let (events, mut block_update, mut chunk_map, lazy, entities) = data; // Process events for event in events.read(&mut self.reader.as_mut().unwrap()) { @@ -47,10 +50,16 @@ impl<'a> System<'a> for FallingBlockCreationSystem { block_update.single_write(update_event); let mut entity_pos: Position = event.pos.world_pos(); + // Center position on block entity_pos.x += 0.5; entity_pos.z += 0.5; - util.spawn_falling_block(entity_pos, glm::vec3(0.0, 0.0, 0.0), event.block) + falling_block::create(lazy.spawn_entity(&entities), event.block) + .with(PositionComponent { + current: entity_pos, + previous: entity_pos, + }) + .with(VelocityComponent::default()); } } _ => (), diff --git a/server/src/entity/arrow.rs b/server/src/entity/arrow.rs deleted file mode 100644 index 82da5a97b..000000000 --- a/server/src/entity/arrow.rs +++ /dev/null @@ -1,67 +0,0 @@ -use shrev::EventChannel; -use specs::{ - Component, Entity, NullStorage, Read, ReadStorage, ReaderId, System, SystemData, World, -}; - -use feather_core::{Item, Position}; - -use crate::entity::NamedComponent; -use crate::player::PLAYER_EYE_HEIGHT; -use crate::util::Util; - -/// Component for arrow entities. -#[derive(Default)] -pub struct ArrowComponent; - -impl Component for ArrowComponent { - type Storage = NullStorage; -} - -/// Event triggered when arrow is shot. -#[derive(Debug, Clone)] -pub struct ShootArrowEvent { - pub arrow_type: Item, - pub shooter: Option, - pub position: Position, - pub critical: bool, -} - -#[derive(Default)] -pub struct ShootArrowSystem { - reader: Option>, -} - -impl<'a> System<'a> for ShootArrowSystem { - type SystemData = ( - Read<'a, Util>, - Read<'a, EventChannel>, - ReadStorage<'a, NamedComponent>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (util, shoot_arrow_events, nameds) = data; - - for event in shoot_arrow_events.read(self.reader.as_mut().unwrap()) { - let mut pos = event.position - + glm::vec3(0.0, PLAYER_EYE_HEIGHT, 0.0) - + event.position.direction() * 1.5; - pos.on_ground = false; - - // TODO: Scale velocity based on power - let velocity = pos.direction(); - - let shooter = match event.shooter { - Some(e) => Some(nameds.get(e).unwrap().uuid), - None => None, - }; - - util.spawn_arrow(pos, velocity, event.critical, shooter); - } - } - - fn setup(&mut self, world: &mut World) { - Self::SystemData::setup(world); - - self.reader = Some(world.fetch_mut::>().register_reader()); - } -} diff --git a/server/src/entity/component.rs b/server/src/entity/component.rs index 28ab62137..b9501f47c 100644 --- a/server/src/entity/component.rs +++ b/server/src/entity/component.rs @@ -1,10 +1,14 @@ //! Various Specs components. +use feather_core::entity::EntityData; use feather_core::world::Position; -use feather_core::Gamemode; +use feather_core::{Gamemode, Packet}; use glm::DVec3; use specs::storage::BTreeStorage; -use specs::{Component, FlaggedStorage, Join, System, VecStorage, WriteStorage}; +use specs::{ + Component, DenseVecStorage, Entity, FlaggedStorage, Join, System, VecStorage, World, + WriteStorage, +}; use uuid::Uuid; pub struct PlayerComponent { @@ -65,6 +69,36 @@ impl Component for NamedComponent { type Storage = BTreeStorage; } +pub trait PacketCreator: Fn(&World, Entity) -> Box + Send + Sync {} + +impl Box + Send + Sync> PacketCreator for F {} + +/// Component containing a closure which returns the packet +/// needed to spawn an entity. +/// +/// The closure requires world access because it may need to access +/// arbitrary components. +pub struct PacketCreatorComponent(pub &'static dyn PacketCreator); + +impl Component for PacketCreatorComponent { + type Storage = VecStorage; +} + +pub trait EntitySerializer: Fn(&World, Entity) -> EntityData + Send + Sync {} + +impl EntityData + Send + Sync> EntitySerializer for F {} + +/// Component containing a closure which returns the `EntityData` +/// for an entity. +/// +/// The closure requires world access because it may need to access +/// arbitrary components. +pub struct SerializerComponent(pub &'static dyn EntitySerializer); + +impl Component for SerializerComponent { + type Storage = VecStorage; +} + /// System for resetting an entity's components /// at the end of the tick. pub struct ComponentResetSystem; diff --git a/server/src/entity/impls/arrow.rs b/server/src/entity/impls/arrow.rs new file mode 100644 index 000000000..b193a935e --- /dev/null +++ b/server/src/entity/impls/arrow.rs @@ -0,0 +1,137 @@ +use shrev::EventChannel; +use specs::{ + Builder, Component, Entities, Entity, LazyUpdate, NullStorage, Read, ReadStorage, ReaderId, + System, SystemData, World, +}; + +use feather_core::packet::SpawnObject; +use feather_core::{Item, Packet, Position}; + +use crate::entity::component::{PacketCreatorComponent, SerializerComponent}; +use crate::entity::metadata::Metadata; +use crate::entity::movement::degrees_to_stops; +use crate::entity::{NamedComponent, PositionComponent, VelocityComponent}; +use crate::lazy::LazyUpdateExt; +use crate::physics::PhysicsBuilder; +use crate::player::PLAYER_EYE_HEIGHT; +use crate::util::{protocol_velocity, Util}; +use feather_core::entity::{ArrowEntityData, BaseEntityData, EntityData}; +use specs::world::LazyBuilder; +use uuid::Uuid; + +/// Component for arrow entities. +#[derive(Default)] +pub struct ArrowComponent; + +impl Component for ArrowComponent { + type Storage = NullStorage; +} + +/// Event triggered when arrow is shot. +#[derive(Debug, Clone)] +pub struct ShootArrowEvent { + pub arrow_type: Item, + pub shooter: Option, + pub position: Position, + pub critical: bool, +} + +#[derive(Default)] +pub struct ShootArrowSystem { + reader: Option>, +} + +impl<'a> System<'a> for ShootArrowSystem { + type SystemData = ( + Read<'a, LazyUpdate>, + Read<'a, EventChannel>, + Entities<'a>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (lazy, shoot_arrow_events, entities) = data; + + for event in shoot_arrow_events.read(self.reader.as_mut().unwrap()) { + let mut pos = event.position + + glm::vec3(0.0, PLAYER_EYE_HEIGHT, 0.0) + + event.position.direction() * 1.5; + pos.on_ground = false; + + // TODO: Scale velocity based on power + let velocity = pos.direction(); + + // TODO: shooter + + create(lazy.spawn_entity(&entities), false) + .with(PositionComponent { + current: pos, + previous: pos, + }) + .with(VelocityComponent(velocity)) + .build(); + } + } + + setup_impl!(reader); +} + +pub fn create(builder: LazyBuilder, critical: bool) -> LazyBuilder { + let meta = { + let mut meta_arrow = crate::entity::metadata::Arrow::default(); + let mask = if critical { + crate::entity::metadata::ArrowBitMask::CRITICAL + } else { + crate::entity::metadata::ArrowBitMask::default() + }; + meta_arrow.set_arrow_bit_mask(mask.bits()); + // meta_arrow.set_shooter(shooter); TODO + Metadata::Arrow(meta_arrow) + }; + + builder + .with(ArrowComponent) + .with( + PhysicsBuilder::new() + .bbox(0.5, 0.5, 0.5) + .gravity(-0.05) + .drag(0.99) + .slip_multiplier(0.0) + .build(), + ) + .with(meta) + .with(PacketCreatorComponent(&create_packet)) + .with(SerializerComponent(&serialize)) +} + +fn create_packet(world: &World, entity: Entity) -> Box { + let position = world.get::().current; + let (velocity_x, velocity_y, velocity_z) = + protocol_velocity(world.get::().0); + + let packet = SpawnObject { + entity_id: entity.id() as i32, + object_uuid: Uuid::new_v4(), // TODO + ty: 60, + x: position.x, + y: position.y, + z: position.z, + pitch: degrees_to_stops(position.pitch), + yaw: degrees_to_stops(position.yaw), + data: 1, // TODO: Shooter entity ID + velocity_x, + velocity_y, + velocity_z, + }; + + Box::new(packet) +} + +fn serialize(world: &World, entity: Entity) -> EntityData { + EntityData::Arrow(ArrowEntityData { + entity: BaseEntityData::new( + world.get::(entity).current, + world.get::().0, + ), + critical: 0, // TODO + }) +} diff --git a/server/src/entity/falling_block.rs b/server/src/entity/impls/falling_block.rs similarity index 52% rename from server/src/entity/falling_block.rs rename to server/src/entity/impls/falling_block.rs index 33b83a2d3..c29469707 100644 --- a/server/src/entity/falling_block.rs +++ b/server/src/entity/impls/falling_block.rs @@ -1,13 +1,21 @@ use shrev::ReaderId; use specs::shrev::EventChannel; -use specs::{Component, DenseVecStorage, Read, ReadStorage, System, Write}; +use specs::{Builder, Component, DenseVecStorage, Entity, Read, ReadStorage, System, World, Write}; -use feather_blocks::Block; +use feather_blocks::{Block, BlockExt}; +use feather_core::packet::SpawnObject; use feather_core::world::ChunkMap; use crate::blocks::{BlockUpdateCause, BlockUpdateEvent}; -use crate::entity::{EntityDestroyEvent, EntityType}; -use crate::physics::EntityPhysicsLandEvent; +use crate::entity::component::{PacketCreatorComponent, SerializerComponent}; +use crate::entity::metadata::Metadata; +use crate::entity::movement::degrees_to_stops; +use crate::entity::{EntityDestroyEvent, EntityType, PositionComponent, VelocityComponent}; +use crate::physics::{EntityPhysicsLandEvent, PhysicsBuilder}; +use crate::util::protocol_velocity; +use feather_core::{Packet, Position}; +use specs::world::LazyBuilder; +use uuid::Uuid; /// Component for falling block entities. pub struct FallingBlockComponent { @@ -78,3 +86,51 @@ impl<'a> System<'a> for FallingBlockLandSystem { setup_impl!(reader); } + +pub fn create(builder: LazyBuilder, position: Position, block: Block) -> LazyBuilder { + let meta = { + let mut meta_falling_block = crate::entity::metadata::FallingBlock::default(); + meta_falling_block.set_spawn_position(position.block_pos()); + Metadata::FallingBlock(meta_falling_block) + }; + + builder + .with(FallingBlockComponent { block }) + .with( + PhysicsBuilder::new() + .gravity(-0.04) + .drag(0.98) + .bbox(0.98, 0.98, 0.98) + .build(), + ) + .with(meta) + .with(PacketCreatorComponent(&create_packet)) + //.with(SerializerComponent(&serialize)) TODO +} + +fn create_packet(world: &World, entity: Entity) -> Box { + let block = world + .get::(entity) + .block + .native_state_id(); + let position = world.get::().current; + let (velocity_x, velocity_y, velocity_z) = + protocol_velocity(world.get::().0); + + let packet = SpawnObject { + entity_id: entity.id() as i32, + object_uuid: Uuid::new_v4(), + ty: 70, + x: position.x, + y: position.y, + z: position.z, + pitch: degrees_to_stops(position.pitch), + yaw: degrees_to_stops(position.yaw), + data: i32::from(block), + velocity_x, + velocity_y, + velocity_z, + }; + + Box::new(packet) +} diff --git a/server/src/entity/item.rs b/server/src/entity/impls/item.rs similarity index 83% rename from server/src/entity/item.rs rename to server/src/entity/impls/item.rs index 1da51e7c1..b9f0a6e52 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/impls/item.rs @@ -1,24 +1,35 @@ //! Logic for working with item entities. +use crate::entity::metadata::Metadata; use crate::entity::metadata::{self, Metadata}; -use crate::entity::{ChunkEntities, EntityDestroyEvent, PlayerComponent, PositionComponent}; -use crate::physics::nearby_entities; +use crate::entity::{ + ChunkEntities, EntityDestroyEvent, PlayerComponent, PositionComponent, VelocityComponent, +}; +use crate::physics::{nearby_entities, PhysicsBuilder}; use crate::player::{ InventoryComponent, InventoryUpdateEvent, PlayerItemDropEvent, PLAYER_EYE_HEIGHT, }; -use crate::util::Util; -use crate::TickCount; +use crate::util::{protocol_velocity, Util}; +use crate::{TickCount, TPS}; use feather_core::network::packet::implementation::CollectItem; -use feather_core::ItemStack; +use feather_core::{ItemStack, Packet}; use rand::Rng; use shrev::EventChannel; use smallvec::SmallVec; use specs::storage::ComponentEvent; use specs::{ - BitSet, Component, DenseVecStorage, Entities, Entity, Join, Read, ReadStorage, ReaderId, - System, SystemData, World, Write, WriteStorage, + BitSet, Builder, Component, DenseVecStorage, Entities, Entity, Join, Read, ReadStorage, + ReaderId, System, SystemData, World, Write, WriteStorage, }; -/// Component for item entitties. +use crate::entity::component::{PacketCreatorComponent, SerializerComponent}; +use crate::entity::movement::degrees_to_stops; +use feather_blocks::Block::PetrifiedOakSlab; +use feather_core::entity::{BaseEntityData, EntityData, ItemData, ItemEntityData}; +use feather_core::packet::SpawnObject; +use specs::world::LazyBuilder; +use uuid::Uuid; + +/// Component for item entities. pub struct ItemComponent { /// The tick at which this item is collectable /// by a player. @@ -311,6 +322,70 @@ impl<'a> System<'a> for ItemCollectSystem { flagged_setup_impl!(PositionComponent, reader); } +pub fn create(builder: LazyBuilder, stack: ItemStack, tick: TickCount) -> LazyBuilder { + let meta = { + let mut meta_item = crate::entity::metadata::Item::default(); + meta_item.set_item(Some(stack.clone())); + Metadata::Item(meta_item) + }; + + builder + .with(ItemComponent { + stack, + collectable_at: tick.0 + TPS, + }) + .with( + PhysicsBuilder::new() + .bbox(0.25, 0.25, 0.25) + .gravity(-0.04) + .drag(0.98) + .build(), + ) + .with(meta) + .with(PacketCreatorComponent(&create_packet)) + .with(SerializerComponent(&serialize)) +} + +fn create_packet(world: &World, entity: Entity) -> Box { + let position = world.get::(entity).current; + + let (velocity_x, velocity_y, velocity_z) = + protocol_velocity(world.get::(entity).0); + + let packet = SpawnObject { + entity_id: entity.id() as i32, + object_uuid: Uuid::new_v4(), + ty: 2, // Type 2 for item stack + x: position.x, + y: position.y, + z: position.z, + pitch: degrees_to_stops(position.pitch), + yaw: degrees_to_stops(position.yaw), + data: 1, // Has velocity + velocity_x, + velocity_y, + velocity_z, + }; + + Box::new(packet) +} + +fn serialize(world: &World, entity: Entity) -> EntityData { + let item = world.get::(entity); + let position = world.get::(entity); + let velocity = world.get::(entity); + + EntityData::Item(ItemEntityData { + entity: BaseEntityData::new(position.current, velocity.0), + age: 0, // TODO + pickup_delay: 0, // TODO + item: ItemData { + item: item.stack.ty.identifier().to_string(), + count: item.stack.amount, + }, + }) +} + pub fn item_stack_from_meta(meta: &Metadata) -> ItemStack { match meta { Metadata::Item(item) => item.item().unwrap().clone(), diff --git a/server/src/entity/impls/mod.rs b/server/src/entity/impls/mod.rs new file mode 100644 index 000000000..da59bcba4 --- /dev/null +++ b/server/src/entity/impls/mod.rs @@ -0,0 +1,23 @@ +//! Entity implementations. +//! +//! Every entity implementation is expected to define +//! the following functions: +//! +//! * `create(LazyBuilder) -> LazyBuilder`. When a system spawns an entity +//! of a known type, it should call this function on the `LazyBuilder` +//! returned by `LazyUpdate::spawn_entity` to apply components, such as markers, +//! `SerializerComponent`, and `SpawnPacketComponent`. This function may +//! take parameters. This function should not apply generic components, +//! such as position and velocity: the callee is responsible for this. +//! * TODO: more? +//! +//! These functions should be invoked in the form `name::function`, e.g. +//! `arrow::apply_components` or `item::apply_components`. +//! +//! Entity implementations should also define systems related to the entity: for +//! example, most entities will have an update system which updates an entity +//! on each tick. + +pub mod arrow; +pub mod falling_block; +pub mod item; diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index e90395d82..a81dfc7c2 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -2,17 +2,16 @@ //! and `PlayerComponent`. In the future, will also //! provide entity-specific components and systems. -mod arrow; mod broadcast; mod chunk; mod component; mod destroy; -mod falling_block; -mod item; +mod impls; pub mod metadata; mod movement; mod save; -mod types; + +pub use impls::*; use crate::systems::{ BLOCK_FALLING_LANDING, CHUNK_CROSS, CHUNK_ENTITIES_LOAD, CHUNK_ENTITIES_UPDATE, CHUNK_SAVE, diff --git a/server/src/entity/types.rs b/server/src/entity/types.rs deleted file mode 100644 index 415979af4..000000000 --- a/server/src/entity/types.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! Module with various types representing entity types -//! and metadata. - -use specs::{Component, VecStorage}; - -/// The type of an entity. -/// -/// This is primarily used to determine -/// which packet to send to spawn the entity. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum EntityType { - Player, - Item, - ExperienceOrb, - Thunderbolt, - Arrow, - TippedArrow, - FallingBlock, - #[cfg(test)] - Test, - // TODO more... -} - -impl Component for EntityType { - type Storage = VecStorage; -} - -impl EntityType { - pub fn is_living(self) -> bool { - self == EntityType::Player - } - - pub fn is_item(self) -> bool { - self == EntityType::Item - } - - pub fn is_arrow(self) -> bool { - self == EntityType::Arrow || self == EntityType::TippedArrow - } - - pub fn is_other(self) -> bool { - self == EntityType::ExperienceOrb || self == EntityType::Thunderbolt - } -} diff --git a/server/src/lazy.rs b/server/src/lazy.rs new file mode 100644 index 000000000..4fcc9b194 --- /dev/null +++ b/server/src/lazy.rs @@ -0,0 +1,28 @@ +//! Extension methods for `LazyUpdate`. + +use crate::entity::EntitySpawnEvent; +use shrev::EventChannel; +use specs::world::{EntitiesRes, LazyBuilder}; +use specs::LazyUpdate; + +pub trait LazyUpdateExt { + /// Creates an entity and lazily inserts components. + /// + /// This should be used instead of `LazyUpdate::create_entity` + /// because it automatically triggers an `EntitySpawnEvent`. + fn spawn_entity(&self, entities: &EntitiesRes) -> LazyBuilder; +} + +impl LazyUpdateExt for LazyUpdate { + fn spawn_entity(&self, entities: &EntitiesRes) -> LazyBuilder { + let entity = entities.create(); + // Trigger event + self.exec(move |world| { + world + .fetch_mut::>() + .single_write(EntitySpawnEvent { entity }); + }); + + LazyBuilder { lazy: self, entity } + } +} diff --git a/server/src/lib.rs b/server/src/lib.rs index 3be07794f..d2d4b2c7b 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -71,6 +71,7 @@ pub mod config; pub mod entity; pub mod io; pub mod joinhandler; +pub mod lazy; pub mod network; pub mod physics; pub mod player; @@ -80,6 +81,7 @@ pub mod systems; #[cfg(test)] pub mod testframework; pub mod time; +pub mod world_ext; pub mod worldgen; pub const TPS: u64 = 20; diff --git a/server/src/physics/block_bboxes.rs b/server/src/physics/block_bboxes.rs index 12cee06ee..fa6f4e134 100644 --- a/server/src/physics/block_bboxes.rs +++ b/server/src/physics/block_bboxes.rs @@ -45,7 +45,7 @@ pub fn bbox_for_block(block: &Block) -> AABB { | Block::NetherBrickSlab(_) | Block::QuartzSlab(_) | Block::RedSandstoneSlab(_) - | Block::PurpurSlab(_) => bbox(1.0, 0.5), - _ => bbox(1.0, 1.0), + | Block::PurpurSlab(_) => bbox(1.0, 0.5, 1.0), + _ => bbox(1.0, 1.0, 1.0), } } diff --git a/server/src/physics/component.rs b/server/src/physics/component.rs index e123c9232..4683a2982 100644 --- a/server/src/physics/component.rs +++ b/server/src/physics/component.rs @@ -5,100 +5,103 @@ use crate::entity::{EntitySpawnEvent, EntityType}; use glm::DVec3; use ncollide3d::bounding_volume::AABB; use shrev::EventChannel; -use specs::{Component, DenseVecStorage, Read, ReaderId, System, WriteStorage}; +use specs::{Component, DenseVecStorage, VecStorage}; + +pub const DEFAULT_SLIP_MULTIPLIER: f64 = 0.6; + +/// Component for entities with physics applied to them. +/// +/// Physics will only be performed on entities with this component. +/// +/// Typically, this component should be constructed using `PhysicsBuilder`. +#[derive(Debug)] +pub struct PhysicsComponent { + /// This entity's bounding box. + pub bbox: AABB, + /// The drag coefficient for this entity. Each tick, + /// the entity's velocity will be multiplied by this amount + /// (so higher values cause less drag). + pub drag: f64, + /// Gravitational acceleration for this entity. Each tick, + /// this value will be added to the entity's Y speed. + /// + /// This value should generally be negative. + pub gravity: f64, + /// Slip multiplier for this entity. When on the ground, + /// the X and Z velocities will be multiplied by this amount + /// each tick. + /// + /// This value is `DEFAULT_SLIP_MULTIPLIER` for most entities. + pub slip_multiplier: f64, +} -/// An entity's bounding box. -#[derive(Debug, Clone, Deref, DerefMut)] -pub struct BoundingBoxComponent(pub AABB); +impl Component for PhysicsComponent { + type Storage = VecStorage; +} -impl Component for BoundingBoxComponent { - type Storage = DenseVecStorage; +/// Builder for physics components. +pub struct PhysicsBuilder { + comp: PhysicsComponent, } -impl BoundingBoxComponent { - /// Returns the difference between the two - /// corners of this bounding box. - pub fn size(&self) -> DVec3 { - self.0.maxs() - self.0.mins() +impl Default for PhysicsBuilder { + fn default() -> Self { + let comp = PhysicsComponent { + bbox: bbox(0.5, 0.5, 0.5), + drag: 0.98, + gravity: -0.08, + slip_multiplier: DEFAULT_SLIP_MULTIPLIER, + }; + Self { comp } } } -/// System for initializing new entities' -/// physics components. -#[derive(Default)] -pub struct PhysicsInitSystem { - reader: Option>, -} +impl PhysicsBuilder { + pub fn new() -> Self { + Self::default() + } -impl<'a> System<'a> for PhysicsInitSystem { - type SystemData = ( - WriteStorage<'a, BoundingBoxComponent>, - Read<'a, EventChannel>, - ); + pub fn bbox(mut self, x: f64, y: f64, z: f64) -> Self { + self.comp.bbox = bbox(x, y, z); + self + } + + pub fn drag(mut self, drag: f64) -> Self { + self.comp.drag = drag; + self + } - fn run(&mut self, data: Self::SystemData) { - let (mut bboxes, events) = data; + pub fn gravity(mut self, gravity: f64) -> Self { + self.comp.gravity = gravity; + self + } + + pub fn slip_multiplier(mut self, slip_multiplier: f64) -> Self { + self.comp.slip_multiplier = slip_multiplier; + self + } - for event in events.read(self.reader.as_mut().unwrap()) { - let bbox = bbox_for_type(event.ty); - if let Some(bbox) = bbox { - bboxes - .insert(event.entity, BoundingBoxComponent(bbox)) - .unwrap(); - } - } + pub fn build(self) -> PhysicsComponent { + self.comp } +} - setup_impl!(reader); +pub trait AABBExt { + /// Returns the difference between the two + /// corners of this bounding box. + fn size(&self) -> DVec3; } -/// Returns the bounding box for the given entity type. -fn bbox_for_type(ty: EntityType) -> Option> { - match ty { - EntityType::Item => Some(bbox(0.25, 0.25)), - EntityType::Player => Some(bbox(0.6, 1.7)), - EntityType::Arrow | EntityType::TippedArrow => Some(bbox(0.5, 0.5)), - EntityType::FallingBlock => Some(bbox(0.98, 0.98)), - _ => None, +impl AABBExt for AABB { + fn size(&self) -> DVec3 { + self.maxs() - self.mins() } } /// Returns a bounding box with the given width and height. -pub fn bbox(size_xz: f64, size_y: f64) -> AABB { +pub fn bbox(size_x: f64, size_y: f64, size_z: f64) -> AABB { AABB::new( glm::vec3(0.0, 0.0, 0.0).into(), - glm::vec3(size_xz, size_y, size_xz).into(), + glm::vec3(size_x, size_y, size_z).into(), ) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::testframework as t; - use specs::WorldExt; - - #[test] - fn test_physics_init() { - let (mut w, mut d) = t::init_world(); - - t::populate_with_air(&mut w); // Prevent entity from getting removed - - let entity = t::add_entity(&mut w, EntityType::Player, true); - - let event = EntitySpawnEvent { - entity, - ty: EntityType::Player, - }; - t::trigger_event(&w, event); - - d.dispatch(&w); - w.maintain(); - - let bbox = w - .read_component::() - .get(entity) - .unwrap() - .clone(); - assert_eq!(bbox.0, bbox_for_type(EntityType::Player).unwrap()); - } -} diff --git a/server/src/physics/mod.rs b/server/src/physics/mod.rs index cd02ace5c..59e3370b6 100644 --- a/server/src/physics/mod.rs +++ b/server/src/physics/mod.rs @@ -5,8 +5,8 @@ mod component; mod entity; mod math; -use crate::systems::{ENTITY_PHYSICS, PHYSICS_INIT}; -pub use component::{BoundingBoxComponent, PhysicsInitSystem}; +use crate::systems::ENTITY_PHYSICS; +pub use component::{PhysicsBuilder, PhysicsComponent}; pub use entity::{EntityPhysicsLandEvent, EntityPhysicsSystem}; pub use math::*; use specs::DispatcherBuilder; @@ -15,6 +15,6 @@ pub fn init_logic(dispatcher: &mut DispatcherBuilder) { dispatcher.add(EntityPhysicsSystem, ENTITY_PHYSICS, &[]); } -pub fn init_handlers(dispatcher: &mut DispatcherBuilder) { - dispatcher.add(PhysicsInitSystem::default(), PHYSICS_INIT, &[]); +pub fn init_handlers(_dispatcher: &mut DispatcherBuilder) { + // nothing } diff --git a/server/src/systems.rs b/server/src/systems.rs index b2f678aaf..5bf9aea13 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -60,8 +60,6 @@ pub const BLOCK_FALLING_LANDING: &str = "block_falling_landing"; // Physics pub const ENTITY_PHYSICS: &str = "entity_physics"; -pub const PHYSICS_INIT: &str = "physics_init"; - // Other pub const JOIN_HANDLER: &str = "join_handler"; pub const NETWORK: &str = "network"; diff --git a/server/src/util/mod.rs b/server/src/util/mod.rs index cd16fc11c..17c167fdd 100644 --- a/server/src/util/mod.rs +++ b/server/src/util/mod.rs @@ -2,19 +2,16 @@ use bumpalo::Bump; use feather_core::{ChunkPosition, ItemStack, Packet, Position}; use glm::DVec3; -use spawn::Spawner; use thread_local::ThreadLocal; #[macro_use] mod macros; mod broadcaster; -mod spawn; use broadcaster::Broadcaster; pub use broadcaster::BroadcasterSystem; use feather_blocks::Block; pub use macros::*; -pub use spawn::SpawnerSystem; use specs::Entity; use uuid::Uuid; @@ -42,12 +39,6 @@ pub fn protocol_velocity(vel: DVec3) -> (i16, i16, i16) { /// which will only be used inside a function, for example, /// this function should be used rather than allocating /// directly on the heap. -/// * `spawn_*` - functions to lazily spawn entities of -/// various types. This avoids the need to specify write -/// storage dependencies for each component the entity -/// needs. Note, however, that the entity isn't created -/// until the handling dispatcher stage. These functions simply -/// redirect to `entity::Spawner`. /// * `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. @@ -58,8 +49,6 @@ pub struct Util { /// /// This is used to reduce allocation frequency. bump: ThreadLocal, - /// The spawner, used to lazily spawn entities. - spawner: Spawner, /// The broadcaster, used to lazily broadcast packets. broadcaster: Broadcaster, } @@ -79,28 +68,6 @@ impl Util { self.bump().alloc(value) } - /// Queues an item to be spawned. - /// - /// This redirects to `Spawner::spawn`. - pub fn spawn_item(&self, position: Position, velocity: DVec3, item: ItemStack) { - self.spawner.spawn_item(position, velocity, item); - } - - pub fn spawn_arrow( - &self, - position: Position, - velocity: DVec3, - critical: bool, - shooter: Option, - ) { - self.spawner - .spawn_arrow(position, velocity, critical, shooter); - } - - pub fn spawn_falling_block(&self, position: Position, velocity: DVec3, block: Block) { - self.spawner.spawn_falling_block(position, velocity, block); - } - /// This should be called at the end of every tick. pub fn reset(&mut self) { // Reset bump allocators diff --git a/server/src/util/spawn.rs b/server/src/util/spawn.rs deleted file mode 100644 index 0da7fab2a..000000000 --- a/server/src/util/spawn.rs +++ /dev/null @@ -1,264 +0,0 @@ -use crate::entity::ItemComponent; -use crate::entity::Metadata; -use crate::entity::{ArrowComponent, FallingBlockComponent}; -use crate::entity::{EntitySpawnEvent, EntityType, PositionComponent, VelocityComponent}; -use crate::util::Util; -use crate::{TickCount, TPS}; -use crossbeam::queue::SegQueue; -use feather_blocks::Block; -use feather_core::{ItemStack, Position}; -use glm::DVec3; -use shrev::EventChannel; -use specs::{Entities, Read, System, Write, WriteStorage}; -use uuid::Uuid; - -/// This type implements a convenient -/// way to spawn entities without having to -/// add a ton of system dependencies. -/// -/// It works by queueing mob spawn requests -/// in an internal vector and lazily -/// creating the entities during the -/// handling phase of the dispatcher. -/// -/// # Note -/// This resource is used as a subset -/// of the `Util` struct. Never use the `Spawner` -/// directly. -#[derive(Default, Debug)] -pub struct Spawner { - /// The internal queue of spawn requests. - queue: SegQueue, -} - -impl Spawner { - /// Queues an item entity to be spawned. - pub fn spawn_item(&self, position: Position, velocity: DVec3, item: ItemStack) { - let meta = { - let mut meta_item = crate::entity::metadata::Item::default(); - meta_item.set_item(Some(item.clone())); - Metadata::Item(meta_item) - }; - let request = SpawnRequest { - ty: EntityType::Item, - position, - velocity, - meta, - - extra: Extra::Item(item), - }; - - self.queue.push(request); - } - - pub fn spawn_arrow( - &self, - position: Position, - velocity: DVec3, - critical: bool, - shooter: Option, - ) { - let meta = { - let mut meta_arrow = crate::entity::metadata::Arrow::default(); - let mask = if critical { - crate::entity::metadata::ArrowBitMask::CRITICAL - } else { - crate::entity::metadata::ArrowBitMask::default() - }; - meta_arrow.set_arrow_bit_mask(mask.bits()); - meta_arrow.set_shooter(shooter); - Metadata::Arrow(meta_arrow) - }; - let request = SpawnRequest { - ty: EntityType::Arrow, - position, - velocity, - meta, - - extra: Extra::Arrow, - }; - - self.queue.push(request); - } - - pub fn spawn_falling_block(&self, position: Position, velocity: DVec3, block: Block) { - let meta = { - let mut meta_falling_block = crate::entity::metadata::FallingBlock::default(); - meta_falling_block.set_spawn_position(position.block_pos()); - Metadata::FallingBlock(meta_falling_block) - }; - let request = SpawnRequest { - ty: EntityType::FallingBlock, - position, - velocity, - meta, - - extra: Extra::FallingBlock(block), - }; - - self.queue.push(request); - } -} - -#[derive(Debug, Clone)] -struct SpawnRequest { - ty: EntityType, - position: Position, - velocity: DVec3, - meta: Metadata, - - extra: Extra, -} - -#[derive(Debug, Clone)] -enum Extra { - Item(ItemStack), - Arrow, - FallingBlock(Block), -} - -/// System for spawning queued requests in the `Spawner`. -pub struct SpawnerSystem; - -impl<'a> System<'a> for SpawnerSystem { - type SystemData = ( - Read<'a, Util>, - WriteStorage<'a, PositionComponent>, - WriteStorage<'a, VelocityComponent>, - WriteStorage<'a, Metadata>, - WriteStorage<'a, EntityType>, - WriteStorage<'a, ItemComponent>, - WriteStorage<'a, ArrowComponent>, - WriteStorage<'a, FallingBlockComponent>, - Write<'a, EventChannel>, - Read<'a, TickCount>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - util, - mut positions, - mut velocities, - mut metadatas, - mut types, - mut item_markers, - mut arrow_markers, - mut falling_block_markers, - mut spawn_events, - tick, - entities, - ) = data; - - // Handle spawn requests - while let Ok(request) = util.spawner.queue.pop() { - let entity = entities.create(); - - positions - .insert( - entity, - PositionComponent { - current: request.position, - previous: request.position, - }, - ) - .unwrap(); - velocities - .insert(entity, VelocityComponent(request.velocity)) - .unwrap(); - metadatas.insert(entity, request.meta).unwrap(); - types.insert(entity, request.ty).unwrap(); - - match request.ty { - EntityType::Item => { - let stack = if let Extra::Item(stack) = request.extra { - stack - } else { - unreachable!() - }; - item_markers - .insert( - entity, - ItemComponent { - collectable_at: tick.0 + TPS, - stack, - }, - ) - .unwrap(); - } - EntityType::Arrow => { - arrow_markers.insert(entity, ArrowComponent {}).unwrap(); - } - EntityType::FallingBlock => { - falling_block_markers - .insert( - entity, - FallingBlockComponent { - block: match request.extra { - Extra::FallingBlock(block) => block, - _ => unreachable!(), - }, - }, - ) - .unwrap(); - } - _ => unimplemented!(), - } - - // Trigger event - let event = EntitySpawnEvent { - entity, - ty: request.ty, - }; - spawn_events.single_write(event); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::entity::EntitySpawnEvent; - use crate::testframework as t; - use feather_core::Item; - - #[test] - fn test_spawn_item() { - let spawner = Spawner::default(); - - let position = position!(0.0, 10.0, 1.04); - let velocity = glm::vec3(104.0, 4.0, 10.0); - let item = ItemStack::new(Item::EnderPearl, 4); - - spawner.spawn_item(position, velocity, item); - - let request = spawner.queue.pop().unwrap(); - assert_eq!(request.ty, EntityType::Item); - assert_eq!(request.position, position); - assert_eq!(request.velocity, velocity); - } - - #[test] - fn test_spawner_system() { - let (w, mut d) = t::builder().with(SpawnerSystem, "").build(); - - let position = position!(0.0, 10.0, 1.04); - let velocity = glm::vec3(104.0, 4.0, 10.0); - let item = ItemStack::new(Item::EnderPearl, 4); - - let mut reader = t::reader(&w); - - { - let util = w.fetch::(); - util.spawn_item(position, velocity, item); - } - - d.dispatch(&w); - - let events = t::triggered_events::(&w, &mut reader); - assert_eq!(events.len(), 1); - - let first = events.first().unwrap(); - assert_eq!(first.ty, EntityType::Item); - } -} diff --git a/server/src/world_ext.rs b/server/src/world_ext.rs new file mode 100644 index 000000000..a9d49ab6f --- /dev/null +++ b/server/src/world_ext.rs @@ -0,0 +1,18 @@ +use specs::{Component, Entity, World}; + +/// Extension trait on `World` with extra functions for convenience. +pub trait WorldExt { + /// Retrieves a component for an entity. + /// + /// # Panics + /// Panics if the component does not exist for this entity, + /// or if the entity is dead. + fn get(&self, entity: Entity) -> &C; +} + +impl WorldExt for World { + fn get(&self, entity: Entity) -> &C { + use specs::WorldExt; + self.read_component().get(entity).unwrap() + } +} From 33a86940496c0fe4d1937a0feeb6beace1d65236 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 6 Oct 2019 23:37:15 -0600 Subject: [PATCH 003/647] More refactoring; implement new chunk/entity saving --- server/src/blocks/falling.rs | 16 ++-- server/src/entity/broadcast.rs | 2 - server/src/entity/impls/arrow.rs | 9 ++- server/src/entity/impls/falling_block.rs | 9 ++- server/src/entity/impls/item.rs | 23 ++++-- server/src/entity/mod.rs | 6 +- server/src/entity/save.rs | 99 +++++++++--------------- server/src/joinhandler.rs | 5 +- server/src/lib.rs | 5 +- server/src/physics/entity.rs | 98 +++++------------------ server/src/physics/math.rs | 8 +- server/src/physics/mod.rs | 2 +- server/src/shutdown.rs | 16 ++-- server/src/systems.rs | 1 - 14 files changed, 109 insertions(+), 190 deletions(-) diff --git a/server/src/blocks/falling.rs b/server/src/blocks/falling.rs index dada3abfc..14ee46891 100644 --- a/server/src/blocks/falling.rs +++ b/server/src/blocks/falling.rs @@ -54,12 +54,16 @@ impl<'a> System<'a> for FallingBlockCreationSystem { entity_pos.x += 0.5; entity_pos.z += 0.5; - falling_block::create(lazy.spawn_entity(&entities), event.block) - .with(PositionComponent { - current: entity_pos, - previous: entity_pos, - }) - .with(VelocityComponent::default()); + falling_block::create( + lazy.spawn_entity(&entities), + entity_pos, + event.block, + ) + .with(PositionComponent { + current: entity_pos, + previous: entity_pos, + }) + .with(VelocityComponent::default()); } } _ => (), diff --git a/server/src/entity/broadcast.rs b/server/src/entity/broadcast.rs index 388b6db00..750b1ee01 100644 --- a/server/src/entity/broadcast.rs +++ b/server/src/entity/broadcast.rs @@ -151,8 +151,6 @@ impl<'a> System<'a> for EntitySendSystem { pub struct EntitySpawnEvent { /// The spawned entity. pub entity: Entity, - /// The type of the spawned entity. - pub ty: EntityType, } /// System for broadcasting when an entity is spawned. diff --git a/server/src/entity/impls/arrow.rs b/server/src/entity/impls/arrow.rs index b193a935e..ac03b9a26 100644 --- a/server/src/entity/impls/arrow.rs +++ b/server/src/entity/impls/arrow.rs @@ -15,6 +15,7 @@ use crate::lazy::LazyUpdateExt; use crate::physics::PhysicsBuilder; use crate::player::PLAYER_EYE_HEIGHT; use crate::util::{protocol_velocity, Util}; +use crate::world_ext::WorldExt; use feather_core::entity::{ArrowEntityData, BaseEntityData, EntityData}; use specs::world::LazyBuilder; use uuid::Uuid; @@ -75,7 +76,7 @@ impl<'a> System<'a> for ShootArrowSystem { setup_impl!(reader); } -pub fn create(builder: LazyBuilder, critical: bool) -> LazyBuilder { +pub fn create<'a>(builder: LazyBuilder<'a>, critical: bool) -> LazyBuilder<'a> { let meta = { let mut meta_arrow = crate::entity::metadata::Arrow::default(); let mask = if critical { @@ -104,9 +105,9 @@ pub fn create(builder: LazyBuilder, critical: bool) -> LazyBuilder { } fn create_packet(world: &World, entity: Entity) -> Box { - let position = world.get::().current; + let position = world.get::(entity).current; let (velocity_x, velocity_y, velocity_z) = - protocol_velocity(world.get::().0); + protocol_velocity(world.get::(entity).0); let packet = SpawnObject { entity_id: entity.id() as i32, @@ -130,7 +131,7 @@ fn serialize(world: &World, entity: Entity) -> EntityData { EntityData::Arrow(ArrowEntityData { entity: BaseEntityData::new( world.get::(entity).current, - world.get::().0, + world.get::(entity).0, ), critical: 0, // TODO }) diff --git a/server/src/entity/impls/falling_block.rs b/server/src/entity/impls/falling_block.rs index c29469707..03c4b1eb2 100644 --- a/server/src/entity/impls/falling_block.rs +++ b/server/src/entity/impls/falling_block.rs @@ -10,9 +10,10 @@ use crate::blocks::{BlockUpdateCause, BlockUpdateEvent}; use crate::entity::component::{PacketCreatorComponent, SerializerComponent}; use crate::entity::metadata::Metadata; use crate::entity::movement::degrees_to_stops; -use crate::entity::{EntityDestroyEvent, EntityType, PositionComponent, VelocityComponent}; +use crate::entity::{EntityDestroyEvent, PositionComponent, VelocityComponent}; use crate::physics::{EntityPhysicsLandEvent, PhysicsBuilder}; use crate::util::protocol_velocity; +use crate::world_ext::WorldExt; use feather_core::{Packet, Position}; use specs::world::LazyBuilder; use uuid::Uuid; @@ -87,7 +88,7 @@ impl<'a> System<'a> for FallingBlockLandSystem { setup_impl!(reader); } -pub fn create(builder: LazyBuilder, position: Position, block: Block) -> LazyBuilder { +pub fn create<'a>(builder: LazyBuilder<'a>, position: Position, block: Block) -> LazyBuilder<'a> { let meta = { let mut meta_falling_block = crate::entity::metadata::FallingBlock::default(); meta_falling_block.set_spawn_position(position.block_pos()); @@ -113,9 +114,9 @@ fn create_packet(world: &World, entity: Entity) -> Box { .get::(entity) .block .native_state_id(); - let position = world.get::().current; + let position = world.get::(entity).current; let (velocity_x, velocity_y, velocity_z) = - protocol_velocity(world.get::().0); + protocol_velocity(world.get::(entity).0); let packet = SpawnObject { entity_id: entity.id() as i32, diff --git a/server/src/entity/impls/item.rs b/server/src/entity/impls/item.rs index b9f0a6e52..76cf8436c 100644 --- a/server/src/entity/impls/item.rs +++ b/server/src/entity/impls/item.rs @@ -1,5 +1,4 @@ //! Logic for working with item entities. -use crate::entity::metadata::Metadata; use crate::entity::metadata::{self, Metadata}; use crate::entity::{ ChunkEntities, EntityDestroyEvent, PlayerComponent, PositionComponent, VelocityComponent, @@ -17,12 +16,14 @@ use shrev::EventChannel; use smallvec::SmallVec; use specs::storage::ComponentEvent; use specs::{ - BitSet, Builder, Component, DenseVecStorage, Entities, Entity, Join, Read, ReadStorage, - ReaderId, System, SystemData, World, Write, WriteStorage, + BitSet, Builder, Component, DenseVecStorage, Entities, Entity, Join, LazyUpdate, Read, + ReadStorage, ReaderId, System, SystemData, World, Write, WriteStorage, }; use crate::entity::component::{PacketCreatorComponent, SerializerComponent}; use crate::entity::movement::degrees_to_stops; +use crate::lazy::LazyUpdateExt; +use crate::world_ext::WorldExt; use feather_blocks::Block::PetrifiedOakSlab; use feather_core::entity::{BaseEntityData, EntityData, ItemData, ItemEntityData}; use feather_core::packet::SpawnObject; @@ -54,12 +55,14 @@ pub struct ItemSpawnSystem { impl<'a> System<'a> for ItemSpawnSystem { type SystemData = ( ReadStorage<'a, PositionComponent>, - Read<'a, Util>, + Read<'a, LazyUpdate>, + Entities<'a>, Read<'a, EventChannel>, + Read<'a, TickCount>, ); fn run(&mut self, data: Self::SystemData) { - let (positions, util, item_drop_events) = data; + let (positions, lazy, entities, item_drop_events, tick) = data; let mut rng = rand::thread_rng(); @@ -91,7 +94,13 @@ impl<'a> System<'a> for ItemSpawnSystem { vel }; - util.spawn_item(pos, velocity, event.stack.clone()); + create(lazy.spawn_entity(&entities), event.stack, &tick) + .with(PositionComponent { + current: pos, + previous: pos, + }) + .with(VelocityComponent(velocity)) + .build(); } } @@ -322,7 +331,7 @@ impl<'a> System<'a> for ItemCollectSystem { flagged_setup_impl!(PositionComponent, reader); } -pub fn create(builder: LazyBuilder, stack: ItemStack, tick: TickCount) -> LazyBuilder { +pub fn create<'a>(builder: LazyBuilder<'a>, stack: ItemStack, tick: &TickCount) -> LazyBuilder<'a> { let meta = { let mut meta_item = crate::entity::metadata::Item::default(); meta_item.set_item(Some(stack.clone())); diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index a81dfc7c2..b2930a32e 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -25,13 +25,15 @@ pub use broadcast::EntitySender; pub use broadcast::{EntitySendEvent, EntitySpawnEvent}; pub use chunk::ChunkEntities; pub use chunk::ChunkEntityUpdateSystem; -pub use component::{NamedComponent, PlayerComponent, PositionComponent, VelocityComponent}; +pub use component::{ + NamedComponent, PacketCreatorComponent, PlayerComponent, PositionComponent, + SerializerComponent, VelocityComponent, +}; pub use destroy::EntityDestroyEvent; pub use falling_block::FallingBlockComponent; pub use item::ItemComponent; pub use metadata::{EntityBitMask, Metadata}; pub use movement::LastKnownPositionComponent; -pub use types::EntityType; pub use save::save_chunks; diff --git a/server/src/entity/save.rs b/server/src/entity/save.rs index 085ed0ebd..504261904 100644 --- a/server/src/entity/save.rs +++ b/server/src/entity/save.rs @@ -4,13 +4,14 @@ use crate::chunk_logic; use crate::chunk_logic::{ChunkUnloadEvent, ChunkWorkerHandle}; use crate::config::Config; use crate::entity::{ - ArrowComponent, ChunkEntities, ItemComponent, PositionComponent, VelocityComponent, + ArrowComponent, ChunkEntities, ItemComponent, PositionComponent, SerializerComponent, + VelocityComponent, }; use feather_core::entity::{ArrowEntityData, BaseEntityData, EntityData, ItemData, ItemEntityData}; use feather_core::world::ChunkMap; use rayon::prelude::*; use shrev::{EventChannel, ReaderId}; -use specs::{Read, ReadExpect, ReadStorage, System, Write}; +use specs::{Entity, LazyUpdate, Read, ReadExpect, ReadStorage, System, WorldExt, Write}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Instant; @@ -40,11 +41,8 @@ impl<'a> System<'a> for ChunkSaveSystem { Read<'a, ChunkEntities>, Read<'a, EventChannel>, Read<'a, Arc>, + Read<'a, LazyUpdate>, ReadExpect<'a, ChunkWorkerHandle>, - ReadStorage<'a, PositionComponent>, - ReadStorage<'a, VelocityComponent>, - ReadStorage<'a, ItemComponent>, - ReadStorage<'a, ArrowComponent>, ); fn run(&mut self, data: Self::SystemData) { @@ -54,15 +52,10 @@ impl<'a> System<'a> for ChunkSaveSystem { chunk_entities, unload_events, config, + lazy, worker_handle, - positions, - velocities, - items, - arrows, ) = data; - // TODO: entities - for event in unload_events.read(self.reader.as_mut().unwrap()) { let entities = vec![]; // TODO chunk_logic::save_chunk(&worker_handle, Arc::clone(&event.chunk), entities); @@ -70,15 +63,7 @@ impl<'a> System<'a> for ChunkSaveSystem { if prev_save_time.0.elapsed() >= config.world.save_interval { // Save chunks - save_chunks( - &mut chunk_map, - &worker_handle, - &chunk_entities, - &positions, - &velocities, - &items, - &arrows, - ); + save_chunks(&mut chunk_map, &chunk_entities, &lazy); prev_save_time.0 = Instant::now(); } } @@ -87,14 +72,14 @@ impl<'a> System<'a> for ChunkSaveSystem { } /// Saves all modified chunks. +/// +/// The saves themselves are performed lazily and asynchronously. +/// +/// Returns the number of chunks queued for saving. pub fn save_chunks( chunk_map: &mut ChunkMap, - handle: &ChunkWorkerHandle, chunk_entities: &ChunkEntities, - positions: &ReadStorage, - velocities: &ReadStorage, - items: &ReadStorage, - arrows: &ReadStorage, + lazy: &LazyUpdate, ) -> u32 { let count = AtomicUsize::new(0); chunk_map @@ -113,44 +98,30 @@ pub fn save_chunks( return; } - let entity_data: Vec<_> = entities - .iter() - .filter_map(|entity| { - // Convert entity to entity data. - // If an entity doesn't have position and velocity, - // it won't be saved. This is normal behavior. - let pos = positions.get(*entity)?; - let vel = velocities.get(*entity)?; - let item = items.get(*entity); - let arrow = arrows.get(*entity); - - let base = BaseEntityData { - position: vec![pos.current.x, pos.current.y, pos.current.z], - velocity: vec![vel.x, vel.y, vel.z], - rotation: vec![pos.current.yaw, pos.current.pitch], - }; - if arrow.is_some() { - Some(EntityData::Arrow(ArrowEntityData { - entity: base, - critical: 0, - })) - } else if let Some(item) = item { - Some(EntityData::Item(ItemEntityData { - entity: base, - age: 0, // TODO - pickup_delay: 0, // TODO - item: ItemData { - item: item.stack.ty.identifier().to_string(), - count: item.stack.amount, - }, - })) - } else { - None - } - }) - .collect(); - - chunk_logic::save_chunk(&handle, Arc::new(chunk.clone()), entity_data); + // World access is required for entity serialization, + // so we perform the saving itself asynchronously. + let chunk = Arc::new(chunk.clone()); + let entities: Vec = entities.to_vec(); + lazy.exec(move |world| { + // Compute entity data. + let entity_data = entities + .into_iter() + .filter_map(|entity| { + let serializer = + match world.read_component::().get(entity) { + Some(serializer) => serializer, + None => return None, // Entity not serialized + }; + + let serialize = serializer.0; + Some(serialize(world, entity)) + }) + .collect(); + + let handle = world.fetch::(); + chunk_logic::save_chunk(&handle, chunk, entity_data); + }); + count.fetch_add(1, Ordering::Release); }); diff --git a/server/src/joinhandler.rs b/server/src/joinhandler.rs index 9112c906d..96299ae6d 100644 --- a/server/src/joinhandler.rs +++ b/server/src/joinhandler.rs @@ -214,10 +214,7 @@ impl<'a> System<'a> for JoinHandlerSystem { let event = PlayerJoinEvent { player }; join_events.single_write(event); - let event = EntitySpawnEvent { - entity: player, - ty: EntityType::Player, - }; + let event = EntitySpawnEvent { entity: player }; spawn_events.single_write(event); // Trigger inventory update event on the entire inventory diff --git a/server/src/lib.rs b/server/src/lib.rs index d2d4b2c7b..e22a7425f 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -43,7 +43,7 @@ use crate::chunk_logic::{ChunkHolders, ChunkWorkerHandle}; use crate::entity::{EntityDestroyEvent, NamedComponent}; use crate::network::send_packet_to_player; use crate::player::PlayerDisconnectEvent; -use crate::systems::{BROADCASTER, ITEM_SPAWN, JOIN_HANDLER, NETWORK, PLAYER_INIT, SPAWNER}; +use crate::systems::{BROADCASTER, JOIN_HANDLER, NETWORK, PLAYER_INIT}; use crate::util::Util; use crate::worldgen::{ ComposableGenerator, EmptyWorldGenerator, SuperflatWorldGenerator, WorldGenerator, @@ -156,7 +156,7 @@ pub fn main() { info!("Shutting down"); info!("Saving chunks"); - shutdown::save_chunks(&world); + shutdown::save_chunks(&mut world); info!("Saving level.dat"); shutdown::save_level(&world); info!("Saving player data"); @@ -379,7 +379,6 @@ fn init_world<'a, 'b>( blocks::init_handlers(&mut dispatcher); physics::init_handlers(&mut dispatcher); entity::init_handlers(&mut dispatcher); - dispatcher.add(util::SpawnerSystem, SPAWNER, &[ITEM_SPAWN]); player::init_handlers(&mut dispatcher); chunk_logic::init_handlers(&mut dispatcher); diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index d4ac324f9..e92156b8c 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -3,10 +3,10 @@ use specs::{Entities, Entity, Join, Read, ReadStorage, System, Write, WriteStorage}; -use crate::entity::{ - EntityDestroyEvent, EntityType, PlayerComponent, PositionComponent, VelocityComponent, +use crate::entity::{EntityDestroyEvent, PositionComponent, VelocityComponent}; +use crate::physics::{ + block_impacted_by_ray, blocks_intersecting_bbox, AABBExt, PhysicsComponent, Side, }; -use crate::physics::{block_impacted_by_ray, blocks_intersecting_bbox, BoundingBoxComponent, Side}; use feather_core::world::ChunkMap; use feather_core::Position; use feather_core::{Block, BlockExt}; @@ -26,9 +26,7 @@ impl<'a> System<'a> for EntityPhysicsSystem { type SystemData = ( WriteStorage<'a, PositionComponent>, WriteStorage<'a, VelocityComponent>, - ReadStorage<'a, BoundingBoxComponent>, - ReadStorage<'a, EntityType>, - ReadStorage<'a, PlayerComponent>, + ReadStorage<'a, PhysicsComponent>, Write<'a, EventChannel>, Write<'a, EventChannel>, Read<'a, ChunkMap>, @@ -39,9 +37,7 @@ impl<'a> System<'a> for EntityPhysicsSystem { let ( mut positions, mut velocities, - bounding_boxes, - types, - players, + physics, mut entity_destroy_events, mut entity_land_events, chunk_map, @@ -58,13 +54,11 @@ impl<'a> System<'a> for EntityPhysicsSystem { // A restricted storage is used for `velocity` so as to avoid // triggering a velocity update event when it is not actually // modified. - for (position, mut restrict_velocity, bounding_box, ty, entity, _) in ( + for (position, mut restrict_velocity, physics, entity) in ( &mut positions, &mut velocities.restrict_mut(), - &bounding_boxes, - &types, + &physics, &entities, - !&players, ) .join() { @@ -89,15 +83,15 @@ impl<'a> System<'a> for EntityPhysicsSystem { if face.contains(Side::EAST) || face.contains(Side::WEST) { velocity.x = 0.0; - pending_position.x = impact.x + bounding_box.size().x * face.as_vector().x; + pending_position.x = impact.x + physics.bbox.size().x * face.as_vector().x; } if face.contains(Side::NORTH) || face.contains(Side::SOUTH) { velocity.z = 0.0; - pending_position.z = impact.z + bounding_box.size().z * face.as_vector().z; + pending_position.z = impact.z + physics.bbox.size().z * face.as_vector().z; } if face.contains(Side::TOP) || face.contains(Side::BOTTOM) { velocity.y = 0.0; - pending_position.y = impact.y + bounding_box.size().y * face.as_vector().y; + pending_position.y = impact.y + physics.bbox.size().y * face.as_vector().y; } if face.contains(Side::TOP) { pending_position.on_ground = true; @@ -110,7 +104,7 @@ impl<'a> System<'a> for EntityPhysicsSystem { &chunk_map, position.current, pending_position, - bounding_box, + &physics.bbox, ); intersect.apply_to(&mut pending_position); @@ -143,7 +137,7 @@ impl<'a> System<'a> for EntityPhysicsSystem { pending_position.on_ground = match chunk_map.block_at( position!( pending_position.x, - pending_position.y - bounding_box.size().y / 2.0 - 0.01, + pending_position.y - physics.bbox.size().y / 2.0 - 0.01, pending_position.z ) .block_pos(), @@ -159,29 +153,27 @@ impl<'a> System<'a> for EntityPhysicsSystem { } // Apply drag and gravity. - let gravity = gravitational_acceleration(*ty); - let drag = drag_force(*ty); // In water and lava, gravity is four times less, and velocity is multiplied by a special drag force. let liquid_drag = 0.8; match block_at_pos { Block::Water(_) => { velocity.0 *= liquid_drag; - velocity.0.y += gravity / 4.0; + velocity.0.y += physics.gravity / 4.0; } Block::Lava(_) => { velocity.0 *= liquid_drag - 0.3; - velocity.0.y += gravity / 4.0; + velocity.0.y += physics.gravity / 4.0; } _ => { - let slip_multiplier = slip_multiplier(*ty); + let slip_multiplier = physics.slip_multiplier; if pending_position.on_ground { velocity.0.x *= slip_multiplier; velocity.0.z *= slip_multiplier; } else { - velocity.0.y = drag * velocity.0.y + gravity; - velocity.0.x *= drag; - velocity.0.z *= drag; + velocity.0.y = physics.drag * velocity.0.y + physics.gravity; + velocity.0.x *= physics.drag; + velocity.0.z *= physics.drag; } } } @@ -198,56 +190,6 @@ impl<'a> System<'a> for EntityPhysicsSystem { } } -fn slip_multiplier(ty: EntityType) -> f64 { - if ty.is_arrow() { - 0.0 - } else { - 0.6 - } -} - -/// Retrieves the gravitational acceleration in blocks per tick squared -/// for a given entity type. -/// -/// This information was fetched from -/// [the Minecraft wiki](https://minecraft.gamepedia.com/Entity#Motion_of_entities). -fn gravitational_acceleration(ty: EntityType) -> f64 { - if ty.is_living() { - -0.08 - } else if ty.is_item() || ty == EntityType::FallingBlock { - -0.04 - } else if ty.is_arrow() { - -0.05 - } else { - 0.0 - } -} - -/* -/// Retrieves the terminal velocity in blocks per tick -/// for a given entity type. -fn terminal_velocity(ty: EntityType) -> f32 { - if ty.is_living() { - -3.92 - } else if ty.is_item() { - -1.96 - } else { - 0.0 - } -} -*/ - -/// Retrieves the drag force for a given entity type. -fn drag_force(ty: EntityType) -> f64 { - if ty.is_living() || ty.is_item() || ty == EntityType::FallingBlock { - 0.98 - } else if ty.is_arrow() { - 0.99 - } else { - 0.0 - } -} - #[cfg(test)] mod tests { use super::*; @@ -268,7 +210,7 @@ mod tests { false, ); - let bbox = crate::physics::component::bbox(0.25, 0.25); + let bbox = crate::physics::component::bbox(0.25, 0.25, 0.25); w.write_component::() .insert(item, BoundingBoxComponent(bbox)) .unwrap(); @@ -296,7 +238,7 @@ mod tests { false, ); - let bbox = crate::physics::component::bbox(0.25, 0.25); + let bbox = crate::physics::component::bbox(0.25, 0.25, 0.25); w.write_component::() .insert(entity, BoundingBoxComponent(bbox)) .unwrap(); diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index af1a80e30..67bd5486e 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -3,7 +3,7 @@ use crate::entity::{ChunkEntities, PositionComponent}; use crate::physics::block_bboxes::bbox_for_block; -use crate::physics::BoundingBoxComponent; +use crate::physics::AABBExt; use feather_blocks::Block; use feather_core::world::{BlockPosition, ChunkMap, Position}; use feather_core::{BlockExt, ChunkPosition}; @@ -321,7 +321,7 @@ pub fn blocks_intersecting_bbox( chunk_map: &ChunkMap, mut from: Position, mut dest: Position, - bbox: &BoundingBoxComponent, + bbox: &AABB, ) -> BlockIntersect { let bbox_size = bbox.size() / 2.0; @@ -359,7 +359,7 @@ pub fn blocks_intersecting_bbox( // position to the block. If the time of impact is <= 1, the entity // has collided with the block; update the position accordingly. let velocity = (dest - from).as_vec(); - let bbox_shape = bbox_to_cuboid(&bbox.0); + let bbox_shape = bbox_to_cuboid(&bbox); for compound in blocks { let toi = match query::time_of_impact( @@ -424,7 +424,7 @@ pub fn blocks_intersecting_bbox( pub fn adjacent_to_bbox( axis: usize, sign: i32, - bbox: &BoundingBoxComponent, + bbox: &AABB, pos: Position, chunk_map: &ChunkMap, checked: &mut heapless::FnvIndexSet, diff --git a/server/src/physics/mod.rs b/server/src/physics/mod.rs index 59e3370b6..c3423d4c7 100644 --- a/server/src/physics/mod.rs +++ b/server/src/physics/mod.rs @@ -6,7 +6,7 @@ mod entity; mod math; use crate::systems::ENTITY_PHYSICS; -pub use component::{PhysicsBuilder, PhysicsComponent}; +pub use component::{AABBExt, PhysicsBuilder, PhysicsComponent}; pub use entity::{EntityPhysicsLandEvent, EntityPhysicsSystem}; pub use math::*; use specs::DispatcherBuilder; diff --git a/server/src/shutdown.rs b/server/src/shutdown.rs index bc4518f2d..0354dd231 100644 --- a/server/src/shutdown.rs +++ b/server/src/shutdown.rs @@ -21,18 +21,14 @@ pub fn init(tx: Sender<()>) { .unwrap(); } -pub fn save_chunks(world: &World) { +pub fn save_chunks(world: &mut World) { let mut chunk_map = world.fetch_mut::(); let handle = world.fetch::(); - let count = entity::save_chunks( - &mut chunk_map, - &handle, - &world.fetch(), - &world.read_component(), - &world.read_component(), - &world.read_component(), - &world.read_component(), - ); + let count = entity::save_chunks(&mut chunk_map, &world.fetch(), &world.fetch()); + + // Need to call `world.maintain()` for lazy chunk saving + // to take effect + world.maintain(); handle.sender.send(chunkworker::Request::ShutDown).unwrap(); diff --git a/server/src/systems.rs b/server/src/systems.rs index 5bf9aea13..43b4f620f 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -44,7 +44,6 @@ pub const CHUNK_ENTITIES_UPDATE: &str = "chunk_entities_update"; pub const CHUNK_ENTITIES_LOAD: &str = "chunk_entities_load"; pub const ENTITY_DESTROY: &str = "entity_destroy"; pub const ITEM_SPAWN: &str = "item_spawn"; -pub const SPAWNER: &str = "spawner"; pub const ITEM_MERGE: &str = "item_merge"; pub const SHOOT_ARROW: &str = "shoot_arrow"; pub const CHUNK_SAVE: &str = "chunk_save"; From f04f3a7599ff150ea60d54a0a5913421931f623d Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 6 Oct 2019 23:52:53 -0600 Subject: [PATCH 004/647] Switch entity sending system to new data-based model --- server/src/entity/broadcast.rs | 225 +++++---------------------------- server/src/entity/mod.rs | 4 +- server/src/lazy.rs | 10 +- server/src/player/broadcast.rs | 13 +- server/src/player/view.rs | 13 +- server/src/systems.rs | 1 - 6 files changed, 53 insertions(+), 213 deletions(-) diff --git a/server/src/entity/broadcast.rs b/server/src/entity/broadcast.rs index 750b1ee01..6af94ab3c 100644 --- a/server/src/entity/broadcast.rs +++ b/server/src/entity/broadcast.rs @@ -2,19 +2,18 @@ //! 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. +//! Sending entities to a client is handled lazily +//! through `LazyUpdate`, because arbitrary components +//! may need to be accessed. use crate::chunk_logic::ChunkHolders; use crate::entity::movement::degrees_to_stops; use crate::entity::{ - EntityType, FallingBlockComponent, LastKnownPositionComponent, VelocityComponent, + EntityType, FallingBlockComponent, LastKnownPositionComponent, PacketCreatorComponent, + VelocityComponent, }; use crate::entity::{Metadata, NamedComponent, PositionComponent}; +use crate::lazy::LazyUpdateExt; use crate::network::{send_packet_boxed_to_player, send_packet_to_player, NetworkComponent}; use crate::util::protocol_velocity; use crossbeam::queue::SegQueue; @@ -23,23 +22,12 @@ 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, Write, WriteStorage}; +use specs::{ + Entities, Entity, LazyUpdate, Read, ReadStorage, ReaderId, System, WorldExt, Write, + WriteStorage, +}; use uuid::Uuid; -/// 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. @@ -60,91 +48,6 @@ pub struct EntitySendEvent { pub 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>, - WriteStorage<'a, LastKnownPositionComponent>, - Write<'a, EventChannel>, - Read<'a, EntitySender>, - ReadStorage<'a, FallingBlockComponent>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - positions, - nameds, - networks, - velocities, - types, - mut metadatas, - mut last_positions, - mut send_events, - entity_sender, - falling_blocks, - entities, - ) = data; - - while let Ok(request) = entity_sender.queue.pop() { - if !entities.is_alive(request.entity) { - continue; // Entity was destroyed - } - - 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(); - - // Send corresponding packet to player. - let packet = packet_to_spawn_entity( - request.entity, - *ty, - &position, - metadata, - velocity, - named, - &falling_blocks, - ); - send_packet_boxed_to_player(&network, packet); - - // Send metadata. - let entity_metadata = PacketEntityMetadata { - entity_id: request.entity.id() as i32, - metadata: metadata.to_full_raw_metadata(), - }; - - send_packet_to_player(network, entity_metadata); - - // Set last known position of this entity to the current position. - last_positions - .get_mut(request.player) - .unwrap() - .0 - .insert(request.entity, position.current); - - // Trigger event. - let event = EntitySendEvent { - player: request.player, - entity: request.entity, - }; - send_events.single_write(event); - } - } -} - /// Event triggered when an entity of any /// type is spawned. #[derive(Debug, Clone)] @@ -169,12 +72,12 @@ impl<'a> System<'a> for EntityBroadcastSystem { ReadStorage<'a, PositionComponent>, ReadStorage<'a, NetworkComponent>, Read<'a, ChunkHolders>, - Read<'a, EntitySender>, Read<'a, EventChannel>, + Read<'a, LazyUpdate>, ); fn run(&mut self, data: Self::SystemData) { - let (positions, networks, chunk_holders, entity_sender, spawn_events) = data; + let (positions, networks, chunk_holders, spawn_events, lazy) = data; for event in spawn_events.read(self.reader.as_mut().unwrap()) { // Broadcast entity to players who can see it. @@ -196,7 +99,7 @@ impl<'a> System<'a> for EntityBroadcastSystem { continue; } - entity_sender.send_entity_to_player(*holder, event.entity); + lazy.send_entity_to_player(*holder, event.entity); } } } @@ -205,95 +108,27 @@ impl<'a> System<'a> for EntityBroadcastSystem { 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>, - falling_blocks: &ReadStorage, -) -> 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(), - }; - - 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, - }; +/// Lazily sends an entity to a player. +pub fn send_entity_to_player(lazy: &LazyUpdate, player: Entity, entity: Entity) { + lazy.exec(move |world| { + // Attempt to get the `PacketCreator` for the entity. + // If it doesn't exist, skip sending. + let packet_creator = match world.read_component::().get(entity) { + Some(packet_creator) => packet_creator, + None => return, + }; - Box::new(packet) - } - EntityType::Arrow => { - let packet = SpawnObject { - entity_id: entity.id() as i32, - object_uuid: Uuid::new_v4(), - ty: 60, - 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, // TODO: Shooter entity ID - velocity_x, - velocity_y, - velocity_z, - }; + let create_packet = packet_creator.0; + let packet = create_packet(world, entity); - Box::new(packet) + if let Some(network) = world.read_component::().get(player) { + send_packet_boxed_to_player(network, packet); } - EntityType::FallingBlock => { - let packet = SpawnObject { - entity_id: entity.id() as i32, - object_uuid: Uuid::new_v4(), - ty: 70, - 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: i32::from(falling_blocks.get(entity).unwrap().block.native_state_id()), - velocity_x, - velocity_y, - velocity_z, - }; - Box::new(packet) - } - _ => unimplemented!(), - } + // Trigger event + let event = EntitySendEvent { entity, player }; + world.fetch_mut::>().single_write(event); + }); } #[cfg(test)] diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index b2930a32e..349ddf25b 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -20,8 +20,7 @@ use crate::systems::{ ITEM_MERGE, ITEM_SPAWN, JOIN_BROADCAST, SHOOT_ARROW, }; pub use arrow::{ArrowComponent, ShootArrowEvent}; -pub use broadcast::EntitySendSystem; -pub use broadcast::EntitySender; +pub use broadcast::send_entity_to_player; pub use broadcast::{EntitySendEvent, EntitySpawnEvent}; pub use chunk::ChunkEntities; pub use chunk::ChunkEntityUpdateSystem; @@ -85,7 +84,6 @@ pub fn init_broadcast(dispatcher: &mut DispatcherBuilder) { ENTITY_SPAWN_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/lazy.rs b/server/src/lazy.rs index 4fcc9b194..c446a5f8b 100644 --- a/server/src/lazy.rs +++ b/server/src/lazy.rs @@ -3,7 +3,7 @@ use crate::entity::EntitySpawnEvent; use shrev::EventChannel; use specs::world::{EntitiesRes, LazyBuilder}; -use specs::LazyUpdate; +use specs::{Entity, LazyUpdate}; pub trait LazyUpdateExt { /// Creates an entity and lazily inserts components. @@ -11,6 +11,10 @@ pub trait LazyUpdateExt { /// This should be used instead of `LazyUpdate::create_entity` /// because it automatically triggers an `EntitySpawnEvent`. fn spawn_entity(&self, entities: &EntitiesRes) -> LazyBuilder; + + /// Lazily sends an entity to a player. This simply forwards + /// to `crate::entity::broadcast::send_entity_to_player`. + fn send_entity_to_player(&self, player: Entity, entity: Entity); } impl LazyUpdateExt for LazyUpdate { @@ -25,4 +29,8 @@ impl LazyUpdateExt for LazyUpdate { LazyBuilder { lazy: self, entity } } + + fn send_entity_to_player(&self, player: Entity, entity: Entity) { + crate::entity::send_entity_to_player(self, player, entity); + } } diff --git a/server/src/player/broadcast.rs b/server/src/player/broadcast.rs index d293ca489..40fe02c73 100644 --- a/server/src/player/broadcast.rs +++ b/server/src/player/broadcast.rs @@ -1,15 +1,14 @@ use crate::config::Config; -use crate::entity::{ - ChunkEntities, EntitySender, NamedComponent, PlayerComponent, PositionComponent, -}; +use crate::entity::{ChunkEntities, NamedComponent, PlayerComponent, PositionComponent}; use crate::joinhandler::PlayerJoinEvent; +use crate::lazy::LazyUpdateExt; 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}; use feather_core::Gamemode; use shrev::EventChannel; -use specs::SystemData; use specs::{Entities, Entity, Join, Read, ReadStorage, ReaderId, System, World, Write}; +use specs::{LazyUpdate, SystemData}; use std::sync::Arc; use uuid::Uuid; @@ -34,7 +33,7 @@ impl<'a> System<'a> for JoinBroadcastSystem { ReadStorage<'a, NetworkComponent>, Write<'a, EventChannel>, Read<'a, ChunkEntities>, - Read<'a, EntitySender>, + Read<'a, LazyUpdate>, Read<'a, Arc>, Entities<'a>, ); @@ -48,7 +47,7 @@ impl<'a> System<'a> for JoinBroadcastSystem { net_comps, mut chat, chunk_entities, - entity_sender, + lazy, config, entities, ) = data; @@ -82,7 +81,7 @@ impl<'a> System<'a> for JoinBroadcastSystem { config.server.view_distance, ) { if entity != event.player { - entity_sender.send_entity_to_player(event.player, entity); + lazy.send_entity_to_player(event.player, entity); } } diff --git a/server/src/player/view.rs b/server/src/player/view.rs index a518dd3d0..a7c5f3e86 100644 --- a/server/src/player/view.rs +++ b/server/src/player/view.rs @@ -12,12 +12,13 @@ //! to `ChunkCrossEvent`s. use crate::config::Config; -use crate::entity::{ChunkEntities, EntitySender}; +use crate::entity::ChunkEntities; +use crate::lazy::LazyUpdateExt; 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 specs::{LazyUpdate, Read, ReadStorage, ReaderId, System}; use std::sync::Arc; /// System for updating entities visible @@ -33,11 +34,11 @@ impl<'a> System<'a> for ViewUpdateSystem { Read<'a, EventChannel>, Read<'a, ChunkEntities>, Read<'a, Arc>, - Read<'a, EntitySender>, + Read<'a, LazyUpdate>, ); fn run(&mut self, data: Self::SystemData) { - let (networks, cross_events, chunk_entities, config, entity_sender) = data; + let (networks, cross_events, chunk_entities, config, lazy) = data; for event in cross_events.read(self.reader.as_mut().unwrap()) { // Find new and old entities. @@ -73,10 +74,10 @@ impl<'a> System<'a> for ViewUpdateSystem { // 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); + lazy.send_entity_to_player(event.player, *entity); if networks.get(*entity).is_some() { - entity_sender.send_entity_to_player(*entity, event.player); + lazy.send_entity_to_player(*entity, event.player); } } } diff --git a/server/src/systems.rs b/server/src/systems.rs index 43b4f620f..a216a294b 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -50,7 +50,6 @@ pub const CHUNK_SAVE: &str = "chunk_save"; 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"; From bb06cca73cea7941df4201bc326d059f909b108d Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 7 Oct 2019 10:55:22 -0600 Subject: [PATCH 005/647] Finish entity refactoring, get server to compile --- core/src/lib.rs | 4 +- server/src/blocks/falling.rs | 19 +++----- server/src/entity/broadcast.rs | 43 +++++++++------- server/src/entity/chunk.rs | 46 +++++------------- server/src/entity/component.rs | 5 +- server/src/entity/impls/arrow.rs | 58 ++++++++++++++++------ server/src/entity/impls/falling_block.rs | 47 ++++++++++-------- server/src/entity/impls/item.rs | 62 ++++++++++++++++++------ server/src/entity/impls/mod.rs | 11 +++-- server/src/entity/mod.rs | 4 +- server/src/entity/save.rs | 18 +++---- server/src/joinhandler.rs | 2 +- server/src/lib.rs | 8 ++- server/src/physics/component.rs | 4 +- server/src/player/init.rs | 7 +-- server/src/shutdown.rs | 4 ++ server/src/util/mod.rs | 4 +- server/src/world_ext.rs | 18 ------- 18 files changed, 194 insertions(+), 170 deletions(-) delete mode 100644 server/src/world_ext.rs diff --git a/core/src/lib.rs b/core/src/lib.rs index 07821ac99..52a3d6ee6 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -21,6 +21,8 @@ extern crate failure; extern crate nalgebra_glm as glm; +#[macro_use] +pub mod world; mod biomes; pub mod bytes_ext; pub mod entitymeta; @@ -28,8 +30,6 @@ pub mod inventory; pub mod network; pub mod prelude; mod save; -#[macro_use] -pub mod world; pub use biomes::Biome; pub use entitymeta::EntityMetadata; diff --git a/server/src/blocks/falling.rs b/server/src/blocks/falling.rs index 14ee46891..d048f9b13 100644 --- a/server/src/blocks/falling.rs +++ b/server/src/blocks/falling.rs @@ -8,8 +8,6 @@ use feather_blocks::{Block, BlockExt}; use crate::blocks::{BlockNotifyEvent, BlockUpdateCause, BlockUpdateEvent}; use crate::entity::{falling_block, PositionComponent, VelocityComponent}; -use crate::lazy::LazyUpdateExt; -use crate::util::Util; use feather_core::Position; /// This system listens to `BlockNotifyEvent`s. @@ -54,16 +52,13 @@ impl<'a> System<'a> for FallingBlockCreationSystem { entity_pos.x += 0.5; entity_pos.z += 0.5; - falling_block::create( - lazy.spawn_entity(&entities), - entity_pos, - event.block, - ) - .with(PositionComponent { - current: entity_pos, - previous: entity_pos, - }) - .with(VelocityComponent::default()); + falling_block::create(&lazy, &entities, event.block, entity_pos) + .with(PositionComponent { + current: entity_pos, + previous: entity_pos, + }) + .with(VelocityComponent::default()) + .build(); } } _ => (), diff --git a/server/src/entity/broadcast.rs b/server/src/entity/broadcast.rs index 6af94ab3c..97d207db4 100644 --- a/server/src/entity/broadcast.rs +++ b/server/src/entity/broadcast.rs @@ -7,26 +7,13 @@ //! may need to be accessed. use crate::chunk_logic::ChunkHolders; -use crate::entity::movement::degrees_to_stops; -use crate::entity::{ - EntityType, FallingBlockComponent, LastKnownPositionComponent, PacketCreatorComponent, - VelocityComponent, -}; -use crate::entity::{Metadata, NamedComponent, PositionComponent}; +use crate::entity::{LastKnownPositionComponent, PacketCreatorComponent}; +use crate::entity::{Metadata, PositionComponent}; use crate::lazy::LazyUpdateExt; use crate::network::{send_packet_boxed_to_player, send_packet_to_player, NetworkComponent}; -use crate::util::protocol_velocity; -use crossbeam::queue::SegQueue; -use feather_blocks::BlockExt; -use feather_core::network::packet::implementation::SpawnObject; -use feather_core::network::packet::implementation::{PacketEntityMetadata, SpawnPlayer}; -use feather_core::Packet; +use feather_core::network::packet::implementation::PacketEntityMetadata; use shrev::EventChannel; -use specs::{ - Entities, Entity, LazyUpdate, Read, ReadStorage, ReaderId, System, WorldExt, Write, - WriteStorage, -}; -use uuid::Uuid; +use specs::{Entity, LazyUpdate, Read, ReadStorage, ReaderId, System, WorldExt}; /// An entity send request, containing /// the player to send to and the entity @@ -113,7 +100,8 @@ pub fn send_entity_to_player(lazy: &LazyUpdate, player: Entity, entity: Entity) lazy.exec(move |world| { // Attempt to get the `PacketCreator` for the entity. // If it doesn't exist, skip sending. - let packet_creator = match world.read_component::().get(entity) { + let packet_creators = world.read_component::(); + let packet_creator = match packet_creators.get(entity) { Some(packet_creator) => packet_creator, None => return, }; @@ -123,6 +111,25 @@ pub fn send_entity_to_player(lazy: &LazyUpdate, player: Entity, entity: Entity) if let Some(network) = world.read_component::().get(player) { send_packet_boxed_to_player(network, packet); + + // If the entity has metadata, send it. + let metas = world.read_component::(); + if let Some(meta) = metas.get(entity) { + let packet = PacketEntityMetadata { + entity_id: entity.id() as i32, + metadata: meta.to_full_raw_metadata(), + }; + send_packet_to_player(network, packet); + } + } + + // Insert last known position + let positions = world.read_component::(); + let mut last_positions = world.write_component::(); + if let Some(last_positions) = last_positions.get_mut(player) { + if let Some(pos) = positions.get(entity) { + last_positions.0.insert(entity, pos.current); + } } // Trigger event diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index d6b929ea9..8af1445f8 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -3,16 +3,16 @@ //! entity queries and packet broadcasting. use crate::chunk_logic::ChunkLoadEvent; -use crate::entity::{EntityDestroyEvent, EntitySpawnEvent, PositionComponent}; -use crate::util::Util; +use crate::entity::{arrow, item, EntityDestroyEvent, EntitySpawnEvent, PositionComponent}; +use crate::TickCount; use feather_core::entity::EntityData; use feather_core::world::ChunkPosition; -use feather_core::{Item, ItemStack}; use hashbrown::{HashMap, HashSet}; use shrev::EventChannel; use specs::storage::ComponentEvent; use specs::{ - BitSet, Entities, Entity, Join, Read, ReadStorage, ReaderId, System, World, WorldExt, Write, + BitSet, Entities, Entity, Join, LazyUpdate, Read, ReadStorage, ReaderId, System, World, + WorldExt, Write, }; use std::sync::atomic::{AtomicBool, Ordering}; @@ -188,44 +188,24 @@ pub struct EntityChunkLoadSystem { } impl<'a> System<'a> for EntityChunkLoadSystem { - type SystemData = (Read<'a, EventChannel>, Read<'a, Util>); + type SystemData = ( + Read<'a, EventChannel>, + Read<'a, LazyUpdate>, + Entities<'a>, + Read<'a, TickCount>, + ); fn run(&mut self, data: Self::SystemData) { - let (load_events, util) = data; + let (load_events, lazy, entities, tick) = data; for event in load_events.read(self.reader.as_mut().unwrap()) { for entity in &event.entities { match entity { EntityData::Item(item_data) => { - let pos = item_data.entity.read_position(); - if let Some(pos) = pos { - util.spawn_item( - pos, - item_data - .entity - .read_velocity() - .unwrap_or_else(|| glm::vec3(0.0, 0.0, 0.0)), - ItemStack::new( - Item::from_identifier(item_data.item.item.as_str()) - .unwrap_or(Item::Stone), - item_data.item.count, - ), - ) - } + item::create_from_data(&lazy, &entities, item_data, &tick); } EntityData::Arrow(arrow_data) => { - let pos = arrow_data.entity.read_position(); - if let Some(pos) = pos { - util.spawn_arrow( - pos, - arrow_data - .entity - .read_velocity() - .unwrap_or_else(|| glm::vec3(0.0, 0.0, 0.0)), - arrow_data.critical > 0, - None, // TODO: Load shooter UUID - ); - } + arrow::create_from_data(&lazy, &entities, arrow_data); } // TODO: Spawn remaining entity types here. EntityData::Unknown => { diff --git a/server/src/entity/component.rs b/server/src/entity/component.rs index b9501f47c..d506f01fa 100644 --- a/server/src/entity/component.rs +++ b/server/src/entity/component.rs @@ -5,10 +5,7 @@ use feather_core::world::Position; use feather_core::{Gamemode, Packet}; use glm::DVec3; use specs::storage::BTreeStorage; -use specs::{ - Component, DenseVecStorage, Entity, FlaggedStorage, Join, System, VecStorage, World, - WriteStorage, -}; +use specs::{Component, Entity, FlaggedStorage, Join, System, VecStorage, World, WriteStorage}; use uuid::Uuid; pub struct PlayerComponent { diff --git a/server/src/entity/impls/arrow.rs b/server/src/entity/impls/arrow.rs index ac03b9a26..a9754583f 100644 --- a/server/src/entity/impls/arrow.rs +++ b/server/src/entity/impls/arrow.rs @@ -1,7 +1,7 @@ use shrev::EventChannel; use specs::{ - Builder, Component, Entities, Entity, LazyUpdate, NullStorage, Read, ReadStorage, ReaderId, - System, SystemData, World, + Builder, Component, Entities, Entity, LazyUpdate, NullStorage, Read, ReaderId, System, World, + WorldExt, }; use feather_core::packet::SpawnObject; @@ -10,14 +10,13 @@ use feather_core::{Item, Packet, Position}; use crate::entity::component::{PacketCreatorComponent, SerializerComponent}; use crate::entity::metadata::Metadata; use crate::entity::movement::degrees_to_stops; -use crate::entity::{NamedComponent, PositionComponent, VelocityComponent}; +use crate::entity::{PositionComponent, VelocityComponent}; use crate::lazy::LazyUpdateExt; use crate::physics::PhysicsBuilder; use crate::player::PLAYER_EYE_HEIGHT; -use crate::util::{protocol_velocity, Util}; -use crate::world_ext::WorldExt; +use crate::util::protocol_velocity; use feather_core::entity::{ArrowEntityData, BaseEntityData, EntityData}; -use specs::world::LazyBuilder; +use specs::world::{EntitiesRes, LazyBuilder}; use uuid::Uuid; /// Component for arrow entities. @@ -63,7 +62,7 @@ impl<'a> System<'a> for ShootArrowSystem { // TODO: shooter - create(lazy.spawn_entity(&entities), false) + create(&lazy, &entities, false) .with(PositionComponent { current: pos, previous: pos, @@ -76,7 +75,7 @@ impl<'a> System<'a> for ShootArrowSystem { setup_impl!(reader); } -pub fn create<'a>(builder: LazyBuilder<'a>, critical: bool) -> LazyBuilder<'a> { +pub fn create<'a>(lazy: &'a LazyUpdate, entities: &EntitiesRes, critical: bool) -> LazyBuilder<'a> { let meta = { let mut meta_arrow = crate::entity::metadata::Arrow::default(); let mask = if critical { @@ -89,7 +88,7 @@ pub fn create<'a>(builder: LazyBuilder<'a>, critical: bool) -> LazyBuilder<'a> { Metadata::Arrow(meta_arrow) }; - builder + lazy.spawn_entity(entities) .with(ArrowComponent) .with( PhysicsBuilder::new() @@ -104,10 +103,38 @@ pub fn create<'a>(builder: LazyBuilder<'a>, critical: bool) -> LazyBuilder<'a> { .with(SerializerComponent(&serialize)) } +pub fn create_from_data( + lazy: &LazyUpdate, + entities: &EntitiesRes, + data: &ArrowEntityData, +) -> Option { + let pos = data.entity.read_position()?; + let vel = data.entity.read_velocity()?; + + let critical = match data.critical { + 0 => false, + _ => true, + }; + + // TODO: load other attributes + + Some( + create(lazy, entities, critical) + .with(PositionComponent { + current: pos, + previous: pos, + }) + .with(VelocityComponent(vel)) + .build(), + ) +} + fn create_packet(world: &World, entity: Entity) -> Box { - let position = world.get::(entity).current; - let (velocity_x, velocity_y, velocity_z) = - protocol_velocity(world.get::(entity).0); + let positions = world.read_component::(); + let velocities = world.read_component::(); + + let position = positions.get(entity).unwrap().current; + let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocities.get(entity).unwrap().0); let packet = SpawnObject { entity_id: entity.id() as i32, @@ -128,10 +155,13 @@ fn create_packet(world: &World, entity: Entity) -> Box { } fn serialize(world: &World, entity: Entity) -> EntityData { + let positions = world.read_component::(); + let velocities = world.read_component::(); + EntityData::Arrow(ArrowEntityData { entity: BaseEntityData::new( - world.get::(entity).current, - world.get::(entity).0, + positions.get(entity).unwrap().current, + velocities.get(entity).unwrap().0, ), critical: 0, // TODO }) diff --git a/server/src/entity/impls/falling_block.rs b/server/src/entity/impls/falling_block.rs index 03c4b1eb2..18c59a2cf 100644 --- a/server/src/entity/impls/falling_block.rs +++ b/server/src/entity/impls/falling_block.rs @@ -1,21 +1,24 @@ use shrev::ReaderId; use specs::shrev::EventChannel; -use specs::{Builder, Component, DenseVecStorage, Entity, Read, ReadStorage, System, World, Write}; +use specs::{ + Builder, Component, DenseVecStorage, Entity, LazyUpdate, Read, ReadStorage, System, World, + WorldExt, Write, +}; use feather_blocks::{Block, BlockExt}; use feather_core::packet::SpawnObject; use feather_core::world::ChunkMap; use crate::blocks::{BlockUpdateCause, BlockUpdateEvent}; -use crate::entity::component::{PacketCreatorComponent, SerializerComponent}; +use crate::entity::component::PacketCreatorComponent; use crate::entity::metadata::Metadata; use crate::entity::movement::degrees_to_stops; use crate::entity::{EntityDestroyEvent, PositionComponent, VelocityComponent}; +use crate::lazy::LazyUpdateExt; use crate::physics::{EntityPhysicsLandEvent, PhysicsBuilder}; use crate::util::protocol_velocity; -use crate::world_ext::WorldExt; use feather_core::{Packet, Position}; -use specs::world::LazyBuilder; +use specs::world::{EntitiesRes, LazyBuilder}; use uuid::Uuid; /// Component for falling block entities. @@ -47,25 +50,22 @@ impl<'a> System<'a> for FallingBlockLandSystem { type SystemData = ( Read<'a, EventChannel>, ReadStorage<'a, FallingBlockComponent>, - ReadStorage<'a, EntityType>, Write<'a, EventChannel>, Write<'a, EventChannel>, Write<'a, ChunkMap>, ); fn run(&mut self, data: Self::SystemData) { - let (events, falling_blocks, types, mut destroy_events, mut block_updates, mut chunk_map) = - data; + let (events, falling_blocks, mut destroy_events, mut block_updates, mut chunk_map) = data; // Process events for event in events.read(&mut self.reader.as_mut().unwrap()) { let entity = event.entity; - let entity_type = types.get(entity).unwrap(); - if *entity_type != EntityType::FallingBlock { - return; - } - let falling_block = falling_blocks.get(entity).unwrap(); + let falling_block = match falling_blocks.get(entity) { + Some(block) => block, + None => continue, // Not a falling block + }; let destroy_event = EntityDestroyEvent { entity }; destroy_events.single_write(destroy_event); @@ -88,14 +88,19 @@ impl<'a> System<'a> for FallingBlockLandSystem { setup_impl!(reader); } -pub fn create<'a>(builder: LazyBuilder<'a>, position: Position, block: Block) -> LazyBuilder<'a> { +pub fn create<'a>( + lazy: &'a LazyUpdate, + entities: &EntitiesRes, + block: Block, + position: Position, +) -> LazyBuilder<'a> { let meta = { let mut meta_falling_block = crate::entity::metadata::FallingBlock::default(); meta_falling_block.set_spawn_position(position.block_pos()); Metadata::FallingBlock(meta_falling_block) }; - builder + lazy.spawn_entity(entities) .with(FallingBlockComponent { block }) .with( PhysicsBuilder::new() @@ -110,13 +115,13 @@ pub fn create<'a>(builder: LazyBuilder<'a>, position: Position, block: Block) -> } fn create_packet(world: &World, entity: Entity) -> Box { - let block = world - .get::(entity) - .block - .native_state_id(); - let position = world.get::(entity).current; - let (velocity_x, velocity_y, velocity_z) = - protocol_velocity(world.get::(entity).0); + let blocks = world.read_component::(); + let positions = world.read_component::(); + let velocities = world.read_component::(); + + let block = blocks.get(entity).unwrap().block.native_state_id(); + let position = positions.get(entity).unwrap().current; + let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocities.get(entity).unwrap().0); let packet = SpawnObject { entity_id: entity.id() as i32, diff --git a/server/src/entity/impls/item.rs b/server/src/entity/impls/item.rs index 76cf8436c..56280842e 100644 --- a/server/src/entity/impls/item.rs +++ b/server/src/entity/impls/item.rs @@ -10,24 +10,22 @@ use crate::player::{ use crate::util::{protocol_velocity, Util}; use crate::{TickCount, TPS}; use feather_core::network::packet::implementation::CollectItem; -use feather_core::{ItemStack, Packet}; +use feather_core::{Item, ItemStack, Packet}; use rand::Rng; use shrev::EventChannel; use smallvec::SmallVec; use specs::storage::ComponentEvent; use specs::{ BitSet, Builder, Component, DenseVecStorage, Entities, Entity, Join, LazyUpdate, Read, - ReadStorage, ReaderId, System, SystemData, World, Write, WriteStorage, + ReadStorage, ReaderId, System, SystemData, World, WorldExt, Write, WriteStorage, }; use crate::entity::component::{PacketCreatorComponent, SerializerComponent}; use crate::entity::movement::degrees_to_stops; use crate::lazy::LazyUpdateExt; -use crate::world_ext::WorldExt; -use feather_blocks::Block::PetrifiedOakSlab; use feather_core::entity::{BaseEntityData, EntityData, ItemData, ItemEntityData}; use feather_core::packet::SpawnObject; -use specs::world::LazyBuilder; +use specs::world::{EntitiesRes, LazyBuilder}; use uuid::Uuid; /// Component for item entities. @@ -94,7 +92,7 @@ impl<'a> System<'a> for ItemSpawnSystem { vel }; - create(lazy.spawn_entity(&entities), event.stack, &tick) + create(&lazy, &entities, event.stack.clone(), tick.0 + TPS) .with(PositionComponent { current: pos, previous: pos, @@ -331,17 +329,22 @@ impl<'a> System<'a> for ItemCollectSystem { flagged_setup_impl!(PositionComponent, reader); } -pub fn create<'a>(builder: LazyBuilder<'a>, stack: ItemStack, tick: &TickCount) -> LazyBuilder<'a> { +pub fn create<'a>( + lazy: &'a LazyUpdate, + entities: &EntitiesRes, + stack: ItemStack, + collectable_at: u64, +) -> LazyBuilder<'a> { let meta = { let mut meta_item = crate::entity::metadata::Item::default(); meta_item.set_item(Some(stack.clone())); Metadata::Item(meta_item) }; - builder + lazy.spawn_entity(entities) .with(ItemComponent { stack, - collectable_at: tick.0 + TPS, + collectable_at, }) .with( PhysicsBuilder::new() @@ -355,11 +358,36 @@ pub fn create<'a>(builder: LazyBuilder<'a>, stack: ItemStack, tick: &TickCount) .with(SerializerComponent(&serialize)) } +pub fn create_from_data( + lazy: &LazyUpdate, + entities: &EntitiesRes, + data: &ItemEntityData, + tick: &TickCount, +) -> Option { + let pos = data.entity.read_position()?; + let vel = data.entity.read_velocity()?; + + let stack = ItemStack::new(Item::from_identifier(&data.item.item)?, data.item.count); + + let collectable_at = data.pickup_delay as u64 + tick.0; + + Some( + create(lazy, entities, stack, collectable_at) + .with(PositionComponent { + current: pos, + previous: pos, + }) + .with(VelocityComponent(vel)) + .build(), + ) +} + fn create_packet(world: &World, entity: Entity) -> Box { - let position = world.get::(entity).current; + let positions = world.read_component::(); + let velocities = world.read_component::(); - let (velocity_x, velocity_y, velocity_z) = - protocol_velocity(world.get::(entity).0); + let position = positions.get(entity).unwrap().current; + let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocities.get(entity).unwrap().0); let packet = SpawnObject { entity_id: entity.id() as i32, @@ -380,9 +408,13 @@ fn create_packet(world: &World, entity: Entity) -> Box { } fn serialize(world: &World, entity: Entity) -> EntityData { - let item = world.get::(entity); - let position = world.get::(entity); - let velocity = world.get::(entity); + let positions = world.read_component::(); + let velocities = world.read_component::(); + let items = world.read_component::(); + + let item = items.get(entity).unwrap(); + let position = positions.get(entity).unwrap(); + let velocity = velocities.get(entity).unwrap(); EntityData::Item(ItemEntityData { entity: BaseEntityData::new(position.current, velocity.0), diff --git a/server/src/entity/impls/mod.rs b/server/src/entity/impls/mod.rs index da59bcba4..da3f6afbb 100644 --- a/server/src/entity/impls/mod.rs +++ b/server/src/entity/impls/mod.rs @@ -3,16 +3,17 @@ //! Every entity implementation is expected to define //! the following functions: //! -//! * `create(LazyBuilder) -> LazyBuilder`. When a system spawns an entity +//! * `create(&LazyUpdate, &EntitiesRes) -> LazyBuilder`. When a system spawns an entity //! of a known type, it should call this function on the `LazyBuilder` -//! returned by `LazyUpdate::spawn_entity` to apply components, such as markers, +//! returned by `LazyUpdate::spawn_entity` to apply components, such as markers, metadata, //! `SerializerComponent`, and `SpawnPacketComponent`. This function may //! take parameters. This function should not apply generic components, -//! such as position and velocity: the callee is responsible for this. -//! * TODO: more? +//! such as position and velocity; the callee is responsible for this. +//! * `create_from_data(&LazyUpdate, &EntitiesRes, &{Entity}Data) -> Option`. Spawns an +//! entity loaded from the given entity data. If the entity creation failed, `None` is returned. //! //! These functions should be invoked in the form `name::function`, e.g. -//! `arrow::apply_components` or `item::apply_components`. +//! `arrow::create` or `item::create_from_data`. //! //! Entity implementations should also define systems related to the entity: for //! example, most entities will have an update system which updates an entity diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 349ddf25b..fe49bfcd0 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -16,8 +16,8 @@ pub use impls::*; use crate::systems::{ BLOCK_FALLING_LANDING, CHUNK_CROSS, CHUNK_ENTITIES_LOAD, CHUNK_ENTITIES_UPDATE, CHUNK_SAVE, ENTITY_DESTROY, ENTITY_DESTROY_BROADCAST, ENTITY_METADATA_BROADCAST, ENTITY_MOVE_BROADCAST, - ENTITY_PHYSICS, ENTITY_SEND, ENTITY_SPAWN_BROADCAST, ENTITY_VELOCITY_BROADCAST, ITEM_COLLECT, - ITEM_MERGE, ITEM_SPAWN, JOIN_BROADCAST, SHOOT_ARROW, + ENTITY_PHYSICS, ENTITY_SPAWN_BROADCAST, ENTITY_VELOCITY_BROADCAST, ITEM_COLLECT, ITEM_MERGE, + ITEM_SPAWN, JOIN_BROADCAST, SHOOT_ARROW, }; pub use arrow::{ArrowComponent, ShootArrowEvent}; pub use broadcast::send_entity_to_player; diff --git a/server/src/entity/save.rs b/server/src/entity/save.rs index 504261904..03c70066f 100644 --- a/server/src/entity/save.rs +++ b/server/src/entity/save.rs @@ -3,15 +3,11 @@ use crate::chunk_logic; use crate::chunk_logic::{ChunkUnloadEvent, ChunkWorkerHandle}; use crate::config::Config; -use crate::entity::{ - ArrowComponent, ChunkEntities, ItemComponent, PositionComponent, SerializerComponent, - VelocityComponent, -}; -use feather_core::entity::{ArrowEntityData, BaseEntityData, EntityData, ItemData, ItemEntityData}; +use crate::entity::{ChunkEntities, SerializerComponent}; use feather_core::world::ChunkMap; use rayon::prelude::*; use shrev::{EventChannel, ReaderId}; -use specs::{Entity, LazyUpdate, Read, ReadExpect, ReadStorage, System, WorldExt, Write}; +use specs::{Entity, LazyUpdate, Read, ReadExpect, System, WorldExt, Write}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Instant; @@ -107,11 +103,11 @@ pub fn save_chunks( let entity_data = entities .into_iter() .filter_map(|entity| { - let serializer = - match world.read_component::().get(entity) { - Some(serializer) => serializer, - None => return None, // Entity not serialized - }; + let serializers = world.read_component::(); + let serializer = match serializers.get(entity) { + Some(serializer) => serializer, + None => return None, // Entity not serialized + }; let serialize = serializer.0; Some(serialize(world, entity)) diff --git a/server/src/joinhandler.rs b/server/src/joinhandler.rs index 96299ae6d..ad38803cb 100644 --- a/server/src/joinhandler.rs +++ b/server/src/joinhandler.rs @@ -23,7 +23,7 @@ use feather_core::{Difficulty, Dimension}; use crate::chunk_logic::{ChunkHolderComponent, ChunkHolders, ChunkWorkerHandle}; use crate::config::Config; -use crate::entity::{EntitySpawnEvent, EntityType, PlayerComponent, PositionComponent}; +use crate::entity::{EntitySpawnEvent, PlayerComponent, PositionComponent}; use crate::network::NetworkComponent; use crate::player::{ChunkPendingComponent, InventoryUpdateEvent, LoadedChunksComponent}; use crate::PlayerCount; diff --git a/server/src/lib.rs b/server/src/lib.rs index e22a7425f..abfcf52da 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -40,7 +40,9 @@ use feather_core::network::packet::implementation::DisconnectPlay; use prelude::*; use crate::chunk_logic::{ChunkHolders, ChunkWorkerHandle}; -use crate::entity::{EntityDestroyEvent, NamedComponent}; +use crate::entity::{ + EntityDestroyEvent, NamedComponent, PacketCreatorComponent, SerializerComponent, +}; use crate::network::send_packet_to_player; use crate::player::PlayerDisconnectEvent; use crate::systems::{BROADCASTER, JOIN_HANDLER, NETWORK, PLAYER_INIT}; @@ -81,7 +83,6 @@ pub mod systems; #[cfg(test)] pub mod testframework; pub mod time; -pub mod world_ext; pub mod worldgen; pub const TPS: u64 = 20; @@ -351,6 +352,9 @@ fn init_world<'a, 'b>( world.insert(ioman); world.insert(TickCount::default()); + world.register::(); + world.register::(); + let generator: Arc = match level.generator_type() { LevelGeneratorType::Flat => Arc::new(SuperflatWorldGenerator { options: level.clone().generator_options.unwrap_or_default(), diff --git a/server/src/physics/component.rs b/server/src/physics/component.rs index 4683a2982..f989072de 100644 --- a/server/src/physics/component.rs +++ b/server/src/physics/component.rs @@ -1,11 +1,9 @@ //! Assorted components relating to physics //! and systems to initialize them. -use crate::entity::{EntitySpawnEvent, EntityType}; use glm::DVec3; use ncollide3d::bounding_volume::AABB; -use shrev::EventChannel; -use specs::{Component, DenseVecStorage, VecStorage}; +use specs::{Component, VecStorage}; pub const DEFAULT_SLIP_MULTIPLIER: f64 = 0.6; diff --git a/server/src/player/init.rs b/server/src/player/init.rs index cc5281950..b77636468 100644 --- a/server/src/player/init.rs +++ b/server/src/player/init.rs @@ -1,4 +1,4 @@ -use crate::entity::{EntityType, LastKnownPositionComponent, PlayerComponent, VelocityComponent}; +use crate::entity::{LastKnownPositionComponent, PlayerComponent, VelocityComponent}; use crate::entity::{Metadata, NamedComponent, PositionComponent}; use crate::network::PlayerPreJoinEvent; use crate::player::{ChunkPendingComponent, InventoryComponent, LoadedChunksComponent}; @@ -30,7 +30,6 @@ impl<'a> System<'a> for PlayerInitSystem { WriteStorage<'a, ChunkPendingComponent>, WriteStorage<'a, LoadedChunksComponent>, WriteStorage<'a, InventoryComponent>, - WriteStorage<'a, EntityType>, WriteStorage<'a, Metadata>, WriteStorage<'a, LastKnownPositionComponent>, Read<'a, LevelData>, @@ -47,7 +46,6 @@ impl<'a> System<'a> for PlayerInitSystem { mut chunk_pending_comps, mut loaded_chunk_comps, mut inventory_comps, - mut entity_types, mut metadata, mut last_positions, level, @@ -131,9 +129,6 @@ impl<'a> System<'a> for PlayerInitSystem { let last_position = LastKnownPositionComponent::default(); last_positions.insert(event.player, last_position).unwrap(); - let ty = EntityType::Player; - entity_types.insert(event.player, ty).unwrap(); - let meta = Metadata::Player(crate::entity::metadata::Player::default()); metadata.insert(event.player, meta).unwrap(); } diff --git a/server/src/shutdown.rs b/server/src/shutdown.rs index 0354dd231..5bcb42a43 100644 --- a/server/src/shutdown.rs +++ b/server/src/shutdown.rs @@ -26,10 +26,14 @@ pub fn save_chunks(world: &mut World) { let handle = world.fetch::(); let count = entity::save_chunks(&mut chunk_map, &world.fetch(), &world.fetch()); + drop(chunk_map); + drop(handle); + // Need to call `world.maintain()` for lazy chunk saving // to take effect world.maintain(); + let handle = world.fetch::(); handle.sender.send(chunkworker::Request::ShutDown).unwrap(); let mut saved = 0; diff --git a/server/src/util/mod.rs b/server/src/util/mod.rs index 17c167fdd..e3798445a 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::{ChunkPosition, ItemStack, Packet, Position}; +use feather_core::{ChunkPosition, Packet}; use glm::DVec3; use thread_local::ThreadLocal; @@ -10,10 +10,8 @@ mod broadcaster; use broadcaster::Broadcaster; pub use broadcaster::BroadcasterSystem; -use feather_blocks::Block; pub use macros::*; use specs::Entity; -use uuid::Uuid; /// Converts float-based velocity in blocks per tick /// to the format used by the protocol. diff --git a/server/src/world_ext.rs b/server/src/world_ext.rs deleted file mode 100644 index a9d49ab6f..000000000 --- a/server/src/world_ext.rs +++ /dev/null @@ -1,18 +0,0 @@ -use specs::{Component, Entity, World}; - -/// Extension trait on `World` with extra functions for convenience. -pub trait WorldExt { - /// Retrieves a component for an entity. - /// - /// # Panics - /// Panics if the component does not exist for this entity, - /// or if the entity is dead. - fn get(&self, entity: Entity) -> &C; -} - -impl WorldExt for World { - fn get(&self, entity: Entity) -> &C { - use specs::WorldExt; - self.read_component().get(entity).unwrap() - } -} From 21e308632782686684eb9c07708157d708393f83 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 7 Oct 2019 11:22:11 -0600 Subject: [PATCH 006/647] Fix player sending --- server/src/entity/mod.rs | 2 +- server/src/player/init.rs | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index fe49bfcd0..9aa2ee75c 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -32,7 +32,7 @@ pub use destroy::EntityDestroyEvent; pub use falling_block::FallingBlockComponent; pub use item::ItemComponent; pub use metadata::{EntityBitMask, Metadata}; -pub use movement::LastKnownPositionComponent; +pub use movement::{degrees_to_stops, LastKnownPositionComponent}; pub use save::save_chunks; diff --git a/server/src/player/init.rs b/server/src/player/init.rs index b77636468..2df4b1721 100644 --- a/server/src/player/init.rs +++ b/server/src/player/init.rs @@ -1,14 +1,18 @@ -use crate::entity::{LastKnownPositionComponent, PlayerComponent, VelocityComponent}; +use crate::entity::{ + degrees_to_stops, LastKnownPositionComponent, PacketCreatorComponent, PlayerComponent, + VelocityComponent, +}; use crate::entity::{Metadata, NamedComponent, PositionComponent}; use crate::network::PlayerPreJoinEvent; use crate::player::{ChunkPendingComponent, InventoryComponent, LoadedChunksComponent}; use crate::prelude::*; use feather_core::level::LevelData; -use feather_core::Gamemode; +use feather_core::packet::SpawnPlayer; use feather_core::Position; +use feather_core::{Gamemode, Packet}; use hashbrown::HashSet; use shrev::{EventChannel, ReaderId}; -use specs::SystemData; +use specs::{Entity, SystemData, WorldExt}; use specs::{Read, System, World, WriteStorage}; use std::path::Path; use std::sync::Arc; @@ -32,6 +36,7 @@ impl<'a> System<'a> for PlayerInitSystem { WriteStorage<'a, InventoryComponent>, WriteStorage<'a, Metadata>, WriteStorage<'a, LastKnownPositionComponent>, + WriteStorage<'a, PacketCreatorComponent>, Read<'a, LevelData>, Read<'a, Arc>, ); @@ -48,6 +53,7 @@ impl<'a> System<'a> for PlayerInitSystem { mut inventory_comps, mut metadata, mut last_positions, + mut packet_creators, level, config, ) = data; @@ -131,6 +137,11 @@ impl<'a> System<'a> for PlayerInitSystem { let meta = Metadata::Player(crate::entity::metadata::Player::default()); metadata.insert(event.player, meta).unwrap(); + + let packet_creator = PacketCreatorComponent(&create_packet); + packet_creators + .insert(event.player, packet_creator) + .unwrap(); } } @@ -144,3 +155,24 @@ impl<'a> System<'a> for PlayerInitSystem { ); } } + +fn create_packet(world: &World, entity: Entity) -> Box { + let positions = world.read_component::(); + let nameds = world.read_component::(); + let metas = world.read_component::(); + + let position = positions.get(entity).unwrap(); + + let packet = SpawnPlayer { + entity_id: entity.id() as i32, + player_uuid: nameds.get(entity).unwrap().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: metas.get(entity).unwrap().to_full_raw_metadata(), + }; + + Box::new(packet) +} From 8181b2ef8139b637ce6e7bfe1a3a28d9c231591c Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 7 Oct 2019 16:00:21 -0600 Subject: [PATCH 007/647] Entity metadata: default main hand to right hand --- server/src/entity/metadata.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/entity/metadata.rs b/server/src/entity/metadata.rs index 949a45013..773348b65 100644 --- a/server/src/entity/metadata.rs +++ b/server/src/entity/metadata.rs @@ -56,7 +56,7 @@ entity_metadata! { additional_hearts: f32() = 11, score: VarInt() = 12, displayed_skin_parts: u8() = 13, - main_hand: u8() = 14, + main_hand: u8(1) = 14, }, Arrow: Entity { arrow_bit_mask: u8() = 6, From 6d5a23a8b0ee50461c932b51b3c5f4938c75caff Mon Sep 17 00:00:00 2001 From: Thijs Raymakers Date: Tue, 8 Oct 2019 21:34:33 +0200 Subject: [PATCH 008/647] Update dependencies --- Cargo.lock | 104 +++++++++++++++++++++--------------------- api/Cargo.toml | 2 +- blocks/Cargo.toml | 14 +++--- codegen/Cargo.toml | 14 +++--- core/Cargo.toml | 48 +++++++++---------- generator/Cargo.toml | 24 +++++----- item_block/Cargo.toml | 2 +- items/Cargo.toml | 4 +- server/Cargo.toml | 82 ++++++++++++++++----------------- 9 files changed, 147 insertions(+), 147 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ceeaebc6b..fe183b46b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,7 +36,7 @@ dependencies = [ [[package]] name = "ahash" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "const-random 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -462,7 +462,7 @@ name = "derive-new" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -492,7 +492,7 @@ dependencies = [ [[package]] name = "downcast-rs" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -542,7 +542,7 @@ dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "feather-codegen 0.5.0", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -552,10 +552,10 @@ version = "0.5.0" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -577,18 +577,18 @@ dependencies = [ "flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", "hash32 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "hash32-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "hematite-nbt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "multimap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "nalgebra-glm 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -604,7 +604,7 @@ dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", @@ -624,7 +624,7 @@ dependencies = [ name = "feather-items" version = "0.5.0" dependencies = [ - "num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -649,7 +649,7 @@ dependencies = [ "feather-item-block 0.5.0", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "heapless 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "hematite-nbt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "humantime-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -661,7 +661,7 @@ dependencies = [ "nalgebra-glm 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "ncollide3d 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint-dig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -678,7 +678,7 @@ dependencies = [ "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.1 (registry+https://github.com/rust-lang/crates.io-index)", - "strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", + "strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -712,7 +712,7 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -792,7 +792,7 @@ version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -816,7 +816,7 @@ version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -910,10 +910,10 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ahash 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "ahash 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1183,7 +1183,7 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1307,7 +1307,7 @@ dependencies = [ "alga 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "downcast-rs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "downcast-rs 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "nalgebra 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1389,12 +1389,12 @@ dependencies = [ [[package]] name = "num-derive" -version = "0.2.5" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1529,7 +1529,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1561,7 +1561,7 @@ name = "pin-project-internal" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1586,7 +1586,7 @@ name = "proc-macro-hack" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1614,7 +1614,7 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1646,7 +1646,7 @@ name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2047,7 +2047,7 @@ name = "serde_derive" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2084,7 +2084,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "mopa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2158,7 +2158,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.1 (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)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2200,18 +2200,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "strum" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "strum_macros" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2244,7 +2244,7 @@ name = "syn" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2650,7 +2650,7 @@ dependencies = [ "bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-shared 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2685,7 +2685,7 @@ name = "wasm-bindgen-macro-support" version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-backend 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2705,7 +2705,7 @@ dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-backend 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2825,7 +2825,7 @@ dependencies = [ "checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" "checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" "checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" -"checksum ahash 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b58aeefd9396419a4f4f2b9778f2d832a11851b55010e231c5390cf2b1c416b4" +"checksum ahash 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "b35dfc96a657c1842b4eb73180b65e37152d4b94d0eb5cb51708aee7826950b4" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum alga 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d708cb68c7106ed1844de68f50f0157a7788c2909a6926fad5a87546ef6a4ff8" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" @@ -2877,7 +2877,7 @@ dependencies = [ "checksum derive-new 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9" "checksum derive_deref 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11554fdb0aa42363a442e0c4278f51c9621e20c1ce3bac51d79e60646f3b8b8f" "checksum derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" -"checksum downcast-rs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b92dfd5c2f75260cbf750572f95d387e7ca0ba5e3fbe9e1a33f23025be020f" +"checksum downcast-rs 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5fe414cc2fd4447b7da94b27ddfb6831a8a06f35f6d077ab5613ec703866c49a" "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum encoding_rs 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)" = "87240518927716f79692c2ed85bfe6e98196d18c6401ec75355760233a7e12e9" @@ -2908,7 +2908,7 @@ dependencies = [ "checksum h2 0.2.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0f107db1419ef8271686187b1a5d47c6431af4a7f4d98b495e7b7fc249bb0a78" "checksum hash32 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12d790435639c06a7b798af9e1e331ae245b7ef915b92f70a39b4cf8c00686af" "checksum hash32-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebc0efbd154a17cddc3616d83faef479c0076d871a2143c157b310cc7ca799a2" -"checksum hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bcea5b597dd98e6d1f1ec171744cc5dee1a30d1c23c5b98e3cf9d4fbdf8a526" +"checksum hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6587d09be37fb98a11cb08b9000a3f592451c1b1b613ca69d949160e313a430a" "checksum heapless 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f339aa7d51777fc0af6aa7cbeb277dfc6e6c029cbdeda48d0fbb92c2337f0e69" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hematite-nbt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "98b407a33bb1715a4cf0276edfe8df52352c55b2a3703c5079adedf398b92932" @@ -2939,7 +2939,7 @@ dependencies = [ "checksum mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "dd1d63acd1b78403cc0c325605908475dd9b9a3acbf65ed8bcab97e27014afcf" "checksum mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0ed03949aef72dbdf3116a383d7b38b4768e6f960528cd6a6044aa9ed68599" "checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" -"checksum miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7108aff85b876d06f22503dcce091e29f76733b2bfdd91eebce81f5e68203a10" +"checksum miniz_oxide 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "304f66c19be2afa56530fa7c39796192eef38618da8d19df725ad7c6d6b2aaae" "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" @@ -2957,7 +2957,7 @@ dependencies = [ "checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a" "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" -"checksum num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2" +"checksum num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0c8b15b261814f992e33760b1fca9fe8b693d8a65299f20c9901688636cfb746" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" "checksum num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e" "checksum num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" @@ -2984,7 +2984,7 @@ dependencies = [ "checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" "checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc" +"checksum proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90cf5f418035b98e655e9cdb225047638296b862b42411c4e45bb88d700f7fc0" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" @@ -3051,8 +3051,8 @@ dependencies = [ "checksum stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5d1c33039533f051704951680f1adfd468fd37ac46816ded0d9ee068e60f05f" -"checksum strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "47cd23f5c7dee395a00fa20135e2ec0fffcdfa151c56182966d7a3261343432e" +"checksum strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6138f8f88a16d90134763314e3fc76fa3ed6a7db4725d6acf9a3ef95a3188d22" +"checksum strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81" "checksum subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab3af2eb31c42e8f0ccf43548232556c42737e01a96db6e1777b0be108e79799" "checksum syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)" = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" diff --git a/api/Cargo.toml b/api/Cargo.toml index 49699e3d7..f8c384e2c 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" authors = ["caelunshun "] edition = "2018" -[dependencies] \ No newline at end of file +[dependencies] diff --git a/blocks/Cargo.toml b/blocks/Cargo.toml index 7f7f25139..c9297a5b3 100644 --- a/blocks/Cargo.toml +++ b/blocks/Cargo.toml @@ -6,15 +6,15 @@ edition = "2018" [dependencies] feather-codegen = { path = "../codegen" } -lazy_static = "1.4.0" -byteorder = "1.3.2" -failure = "0.1.5" -num-traits = "0.2.8" -num-derive = "0.2.5" +lazy_static = "1.4" +byteorder = "1.3" +failure = "0.1" +num-traits = "0.2" +num-derive = "0.3" [dev-dependencies] -criterion = "0.3.0" +criterion = "0.3" [[bench]] name = "block_id_mappings" -harness = false \ No newline at end of file +harness = false diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 764001f48..3e516584f 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -8,10 +8,10 @@ edition = "2018" proc-macro = true [dependencies] -syn = { version = "1.0.5", features = ["full", "extra-traits", "derive"] } -quote = "1.0.2" -proc-macro2 = "1.0.3" -lazy_static = "1.4.0" -heck = "0.3.1" -strum = "0.15.0" -strum_macros = "0.15.0" \ No newline at end of file +syn = { version = "1.0", features = ["full", "extra-traits", "derive"] } +quote = "1.0" +proc-macro2 = "1.0" +lazy_static = "1.4" +heck = "0.3" +strum = "0.16" +strum_macros = "0.16" diff --git a/core/Cargo.toml b/core/Cargo.toml index 1bbcf687b..1e0549076 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -9,28 +9,28 @@ publish = false feather-codegen = { path = "../codegen" } feather-blocks = { path = "../blocks" } feather-items = { path = "../items" } -lazy_static = "1.4.0" -derive-new = "0.5.8" -uuid = "0.7.4" -cfb8 = "0.3.2" -aes = "0.3.2" -flate2 = "1.0.11" -bytes = "0.4.12" -log = "0.4.8" -serde = { version = "1.0.99", features = ["derive"] } -num-traits = "0.2.8" -num-derive = "0.2.5" -hashbrown = { version = "0.6.0", features = ["serde"] } -hematite-nbt = "0.4.1" -byteorder = "1.3.2" -nalgebra-glm = "0.4.2" -derive_more = "0.15.0" -smallvec = "0.6.10" -hash32 = "0.1.0" -hash32-derive = "0.1.0" -strum = "0.15.0" -strum_macros = "0.15.0" +lazy_static = "1.4" +derive-new = "0.5" +uuid = "0.7" +cfb8 = "0.3" +aes = "0.3" +flate2 = "1.0" +bytes = "0.4" +log = "0.4" +serde = { version = "1.0", features = ["derive"] } +num-traits = "0.2" +num-derive = "0.3" +hashbrown = { version = "0.6", features = ["serde"] } +hematite-nbt = "0.4" +byteorder = "1.3" +nalgebra-glm = "0.4" +derive_more = "0.15" +smallvec = "0.6" +hash32 = "0.1" +hash32-derive = "0.1" +strum = "0.16" +strum_macros = "0.16" tokio = "=0.2.0-alpha.6" -failure = "0.1.5" -bitvec = "0.15.2" -multimap = "0.6.0" +failure = "0.1" +bitvec = "0.15" +multimap = "0.6" diff --git a/generator/Cargo.toml b/generator/Cargo.toml index 8b46f7d2c..0543cbb9d 100644 --- a/generator/Cargo.toml +++ b/generator/Cargo.toml @@ -6,16 +6,16 @@ edition = "2018" description = "Code generators for Feather" [dependencies] -byteorder = "1.3.2" -clap = { version = "2.33.0", features = ["yaml"] } -serde = { version = "1.0.99", features = ["derive"] } -serde_json = "1.0.40" -simple_logger = "1.3.0" -log = "0.4.8" -failure = "0.1.5" -derive_deref = "1.1.0" -indexmap = { version = "1.2.0", features = ["serde-1"] } +byteorder = "1.3" +clap = { version = "2.33", features = ["yaml"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +simple_logger = "1.3" +log = "0.4" +failure = "0.1" +derive_deref = "1.1" +indexmap = { version = "1.2", features = ["serde-1"] } quote = "1.0.2" -syn = { version = "1.0.5", features = ["full"] } -heck = "0.3.1" -proc-macro2 = "1.0.3" \ No newline at end of file +syn = { version = "1.0", features = ["full"] } +heck = "0.3" +proc-macro2 = "1.0" diff --git a/item_block/Cargo.toml b/item_block/Cargo.toml index 56a7df26f..210f3c5da 100644 --- a/item_block/Cargo.toml +++ b/item_block/Cargo.toml @@ -8,4 +8,4 @@ edition = "2018" [dependencies] feather-blocks = { path = "../blocks" } -feather-items = { path = "../items" } \ No newline at end of file +feather-items = { path = "../items" } diff --git a/items/Cargo.toml b/items/Cargo.toml index 552e67d0a..dc0a96b67 100644 --- a/items/Cargo.toml +++ b/items/Cargo.toml @@ -5,5 +5,5 @@ authors = ["caelunshun "] edition = "2018" [dependencies] -num-traits = "0.2.8" -num-derive = "0.2.5" \ No newline at end of file +num-traits = "0.2" +num-derive = "0.3" diff --git a/server/Cargo.toml b/server/Cargo.toml index c5f64339e..1071f1790 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -16,55 +16,55 @@ path = "src/main.rs" feather-blocks = { path = "../blocks" } feather-core = { path = "../core" } feather-item-block = { path = "../item_block" } -crossbeam = "0.7.2" -log = "0.4.8" -simple_logger = "1.3.0" -uuid = { version = "0.7.4", features = ["v4"] } -derive-new = "0.5.8" -serde = { version = "1.0.100", features = ["derive"] } -serde_json = "1.0.40" -toml = "0.5.3" -rsa = "0.1.3" +crossbeam = "0.7" +log = "0.4" +simple_logger = "1.3" +uuid = { version = "0.7", features = ["v4"] } +derive-new = "0.5" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +toml = "0.5" +rsa = "0.1" # Match RSA git master num-bigint = { version = "0.4", features = ["rand", "i128", "u64_digit", "prime", "zeroize"], package = "num-bigint-dig" } -rsa-der = "0.2.1" -rand = "0.7.1" -rand_xorshift = "0.2.0" +rsa-der = "0.2" +rand = "0.7" +rand_xorshift = "0.2" rand-legacy = { path = "../util/rand-legacy" } -bytes = "0.4.12" -hashbrown = { version = "0.6.0", features = ["rayon"] } +bytes = "0.4" +hashbrown = { version = "0.6", features = ["rayon"] } mojang-api = { git = "https://github.com/caelunshun/mojang-api-rs", rev = "6525e910ad53953fa16028f0fce74b1a19855733" } -multimap = "0.6.0" -hematite-nbt = "0.4.1" -specs = { version = "0.15.1", features = ["storage-event-control"] } -rayon = "1.1.2" -shrev = "1.1.1" -failure = "0.1.5" -num-derive = "0.2.5" -num-traits = "0.2.8" -smallvec = "0.6.10" -lazy_static = "1.4.0" -nalgebra-glm = "0.4.2" -nalgebra = "0.18.1" -ncollide3d = "0.20.1" -derive_deref = "1.1.0" +multimap = "0.6" +hematite-nbt = "0.4" +specs = { version = "0.15", features = ["storage-event-control"] } +rayon = "1.2" +shrev = "1.1" +failure = "0.1" +num-derive = "0.3" +num-traits = "0.2" +smallvec = "0.6" +lazy_static = "1.4" +nalgebra-glm = "0.4" +nalgebra = "0.18" +ncollide3d = "0.20" +derive_deref = "1.1" feather-codegen = { path = "../codegen" } -bitflags = "1.1.0" -fnv = "1.0.6" -base64 = "0.10.1" -bumpalo = "2.6.0" -thread_local = "1.0.0" -parking_lot = "0.9.0" -heapless = "0.5.1" -strum = "0.15.0" -simdnoise = "3.1.1" -simdeez = "0.6.4" -bitvec = "0.15.1" +bitflags = "1.2" +fnv = "1.0" +base64 = "0.10" +bumpalo = "2.6" +thread_local = "1.0" +parking_lot = "0.9" +heapless = "0.5" +strum = "0.16" +simdnoise = "3.1" +simdeez = "0.6" +bitvec = "0.15" tokio = "=0.2.0-alpha.6" tokio-executor = "=0.2.0-alpha.6" futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } -humantime-serde = "0.1.1" -ctrlc = "3.1.3" +humantime-serde = "0.1" +ctrlc = "3.1" [dev-dependencies] criterion = "0.3.0" From 4d06f85ed725a2cc9def591391ad99b4b5d0d789 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 13 Oct 2019 21:23:23 -0600 Subject: [PATCH 009/647] Start fixing tests --- core/src/world/mod.rs | 2 +- server/src/entity/broadcast.rs | 10 ++- server/src/entity/chunk.rs | 22 ++--- server/src/entity/impls/item.rs | 41 ++++----- server/src/entity/impls/mod.rs | 2 + server/src/entity/impls/test.rs | 12 +++ server/src/entity/metadata.rs | 6 +- server/src/entity/movement.rs | 7 +- server/src/physics/entity.rs | 47 ++-------- server/src/physics/math.rs | 46 +++------- server/src/player/view.rs | 18 ++-- server/src/testframework.rs | 152 +++++--------------------------- 12 files changed, 107 insertions(+), 258 deletions(-) create mode 100644 server/src/entity/impls/test.rs diff --git a/core/src/world/mod.rs b/core/src/world/mod.rs index 07fa3a7d7..907b0c218 100644 --- a/core/src/world/mod.rs +++ b/core/src/world/mod.rs @@ -13,7 +13,7 @@ pub mod chunk; #[macro_export] macro_rules! position { ($x:expr, $y:expr, $z:expr, $pitch:expr, $yaw:expr, $on_ground:expr) => { - Position { + $crate::Position { x: $x, y: $y, z: $z, diff --git a/server/src/entity/broadcast.rs b/server/src/entity/broadcast.rs index 97d207db4..d60c13be5 100644 --- a/server/src/entity/broadcast.rs +++ b/server/src/entity/broadcast.rs @@ -141,11 +141,14 @@ pub fn send_entity_to_player(lazy: &LazyUpdate, player: Entity, entity: Entity) #[cfg(test)] mod tests { use super::*; + use crate::entity::{item, test}; use crate::player::ChunkCrossSystem; use crate::testframework as t; use feather_core::network::cast_packet; + use feather_core::network::packet::implementation::{SpawnObject, SpawnPlayer}; use feather_core::network::packet::PacketType; - use specs::WorldExt; + use feather_core::{Item, ItemStack}; + use specs::{Builder, WorldExt}; #[test] fn test_spawn_player() { @@ -156,7 +159,6 @@ mod tests { let event = EntitySpawnEvent { entity: player1.entity, - ty: EntityType::Player, }; w.fetch_mut::>().single_write(event); @@ -177,13 +179,13 @@ mod tests { let (mut w, mut d) = t::builder() .with(EntityBroadcastSystem::default(), "broadcast") .with(ChunkCrossSystem::default(), "chunk_cross") - .with_dep(EntitySendSystem, "", &["broadcast", "chunk_cross"]) .build(); let player = t::add_player(&mut w); - let item = t::add_entity(&mut w, EntityType::Item, true); + let item = item::create(&w.fetch(), &w.fetch(), ItemStack::new(Item::Stone, 1), 0).build(); + w.maintain(); d.dispatch(&w); w.maintain(); diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index 8af1445f8..377beb711 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -224,7 +224,7 @@ impl<'a> System<'a> for EntityChunkLoadSystem { #[cfg(test)] mod tests { use super::*; - use crate::entity::EntityType; + use crate::entity::{test, ArrowComponent, ItemComponent}; use crate::testframework as t; use feather_core::entity::{ArrowEntityData, ItemEntityData}; use feather_core::Position; @@ -260,10 +260,7 @@ mod tests { }) .build(); - let event = EntitySpawnEvent { - entity, - ty: EntityType::Player, - }; + let event = EntitySpawnEvent { entity }; t::trigger_event(&w, event); d.dispatch(&w); @@ -319,7 +316,7 @@ mod tests { .build(); let pos = position!(100.0, -100.0, -100.0); - let entity = t::add_entity_with_pos(&mut w, EntityType::Player, pos, false); + let entity = test::create(&mut w, pos).build(); let event = EntityDestroyEvent { entity }; t::trigger_event(&w, event); @@ -377,13 +374,12 @@ mod tests { d.dispatch(&w); w.maintain(); - // Confirm two spawn events were triggered - let events = t::triggered_events::(&w, &mut entity_spawn_reader); - assert_eq!(events.len(), 2); + // Confirm two entities were created: one arrow, one item + let mut events = t::triggered_events::(&w, &mut entity_spawn_reader); - let mut iter = events.iter(); - for ty in &[EntityType::Item, EntityType::Arrow] { - assert_eq!(iter.next().unwrap().ty, *ty); - } + let first = events.remove(0).entity; + assert!(w.read_component::().contains(first)); + let second = events.remove(0).entity; + assert!(w.read_component::().contains(second)); } } diff --git a/server/src/entity/impls/item.rs b/server/src/entity/impls/item.rs index 56280842e..ece258cd2 100644 --- a/server/src/entity/impls/item.rs +++ b/server/src/entity/impls/item.rs @@ -353,6 +353,7 @@ pub fn create<'a>( .drag(0.98) .build(), ) + .with(VelocityComponent::default()) .with(meta) .with(PacketCreatorComponent(&create_packet)) .with(SerializerComponent(&serialize)) @@ -444,7 +445,6 @@ pub fn item_meta(stack: ItemStack) -> Metadata { mod tests { use super::*; use crate::entity::EntitySpawnEvent; - use crate::entity::EntityType; use crate::testframework as t; use feather_core::inventory::SLOT_HOTBAR_OFFSET; use feather_core::network::cast_packet; @@ -478,7 +478,6 @@ mod tests { assert_eq!(events.len(), 1); let first = events.first().unwrap(); let entity = first.entity; - assert_eq!(first.ty, EntityType::Item); // Check position let pos = t::entity_pos(&w, entity); @@ -495,21 +494,20 @@ mod tests { .with_dep(ItemMergeSystem::default(), "item_merge", &[]) .build(); - let item1 = - t::add_entity_with_pos(&mut w, EntityType::Item, position!(0.0, 0.0, 0.0), true); - let item2 = - t::add_entity_with_pos(&mut w, EntityType::Item, position!(1.0, 0.4, 1.0), true); - - { - let mut metadatas = w.write_component::(); - - metadatas - .insert(item1, item_meta(ItemStack::new(Item::EnderPearl, 4))) - .unwrap(); - metadatas - .insert(item2, item_meta(ItemStack::new(Item::EnderPearl, 7))) - .unwrap(); - } + let item1 = create( + &w.fetch(), + &w.fetch(), + ItemStack::new(Item::EnderPearl, 4), + 0, + ) + .build(); + let item2 = create( + &w.fetch(), + &w.fetch(), + ItemStack::new(Item::EnderPearl, 7), + 0, + ) + .build(); d.dispatch(&w); w.maintain(); @@ -532,20 +530,15 @@ mod tests { .build(); let player = t::add_player(&mut w); - let item = t::add_entity(&mut w, EntityType::Item, true); let stack = ItemStack::new(Item::String, 4); + let item = create(&w.fetch(), &w.fetch(), stack.clone(), 0).build(); let mut destroy_reader = t::reader(&w); - { - let mut metadatas = w.write_component::(); - let metadata = item_meta(stack.clone()); - metadatas.insert(item, metadata).unwrap(); - } - // Allow item to be collected w.fetch_mut::().0 = 20; + w.maintain(); d.dispatch(&w); w.maintain(); diff --git a/server/src/entity/impls/mod.rs b/server/src/entity/impls/mod.rs index da3f6afbb..10ff43dca 100644 --- a/server/src/entity/impls/mod.rs +++ b/server/src/entity/impls/mod.rs @@ -22,3 +22,5 @@ pub mod arrow; pub mod falling_block; pub mod item; +#[cfg(test)] +pub mod test; diff --git a/server/src/entity/impls/test.rs b/server/src/entity/impls/test.rs new file mode 100644 index 000000000..8f74b12cd --- /dev/null +++ b/server/src/entity/impls/test.rs @@ -0,0 +1,12 @@ +//! A fake entity implementation for unit tests. + +use crate::entity::PositionComponent; +use feather_core::Position; +use specs::{Builder, EntityBuilder, World, WorldExt}; + +pub fn create(world: &mut World, pos: Position) -> EntityBuilder { + world.create_entity().with(PositionComponent { + current: pos, + previous: pos, + }) +} diff --git a/server/src/entity/metadata.rs b/server/src/entity/metadata.rs index 949a45013..38bde9e76 100644 --- a/server/src/entity/metadata.rs +++ b/server/src/entity/metadata.rs @@ -114,12 +114,12 @@ impl<'a> System<'a> for MetadataBroadcastSystem { #[cfg(test)] mod tests { use super::*; - use crate::entity::EntityType; + use crate::entity::test; use crate::testframework as t; use feather_core::entitymeta::MetaEntry; use feather_core::network::cast_packet; use feather_core::PacketType; - use specs::WorldExt; + use specs::{Builder, WorldExt}; #[test] fn test_basic() { @@ -155,7 +155,7 @@ mod tests { .build(); // Metadata is inserted here, which causes update event - let entity = t::add_entity(&mut w, EntityType::Test, true); + let entity = test::create(&mut w, position!(0.0, 0.0, 0.0)).build(); let player = t::add_player(&mut w); d.dispatch(&w); diff --git a/server/src/entity/movement.rs b/server/src/entity/movement.rs index febc25da7..f14028c4a 100644 --- a/server/src/entity/movement.rs +++ b/server/src/entity/movement.rs @@ -227,15 +227,16 @@ pub fn degrees_to_stops(degs: f32) -> u8 { #[cfg(test)] mod tests { - use specs::WorldExt; + use specs::{Builder, WorldExt}; use feather_core::network::cast_packet; use feather_core::network::packet::PacketType; - use crate::entity::EntityType; + use crate::entity::test; use crate::testframework as t; use super::*; + use feather_core::{Item, ItemStack}; #[test] fn test_velocity_broadcast_system() { @@ -245,7 +246,7 @@ mod tests { let player = t::add_player(&mut w); - let entity = t::add_entity(&mut w, EntityType::Item, false); + let entity = test::create(&mut w, position!(0.0, 0.0, 0.0)).build(); w.write_component::() .insert(entity, VelocityComponent(glm::vec3(0.0, 0.0, 0.0))) diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index e92156b8c..dd8a3dcd6 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -193,54 +193,21 @@ impl<'a> System<'a> for EntityPhysicsSystem { #[cfg(test)] mod tests { use super::*; + use crate::entity::test; + use crate::physics::PhysicsBuilder; use crate::testframework as t; - use specs::WorldExt; - - #[test] - fn test_physics_basic() { - let (mut w, mut d) = t::builder().with(EntityPhysicsSystem, "").build(); - - t::populate_with_air(&mut w); - - let item = t::add_entity_with_pos_and_vel( - &mut w, - EntityType::Item, - position!(0.0, 0.0, 0.0), - glm::vec3(0.0, 1.0, 0.0), - false, - ); - - let bbox = crate::physics::component::bbox(0.25, 0.25, 0.25); - w.write_component::() - .insert(item, BoundingBoxComponent(bbox)) - .unwrap(); - - d.dispatch(&w); - w.maintain(); - - let pos = t::entity_pos(&w, item); - let vel = t::entity_vel(&w, item).unwrap(); - - assert_pos_eq!(pos, position!(0.0, 1.0, 0.0)); - assert_float_eq!(vel.x, 0.0); - assert_float_eq!(vel.y, 0.94); - assert_float_eq!(vel.z, 0.0); - } + use feather_core::{Item, ItemStack}; + use specs::{Builder, WorldExt}; #[test] fn test_unloaded_chunk() { let (mut w, mut d) = t::builder().with(EntityPhysicsSystem, "").build(); - let entity = t::add_entity_with_pos( - &mut w, - EntityType::Item, - position!(1000.0, 100.0, 1000.0), - false, - ); + let entity = test::create(&mut w, position!(1000.0, 100.0, 1000.0)).build(); let bbox = crate::physics::component::bbox(0.25, 0.25, 0.25); - w.write_component::() - .insert(entity, BoundingBoxComponent(bbox)) + w.write_component::() + .insert(entity, PhysicsBuilder::new().build()) .unwrap(); d.dispatch(&w); diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index 67bd5486e..36806c468 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -614,12 +614,12 @@ pub fn bbox_to_cuboid(bbox: &AABB) -> Cuboid { #[cfg(test)] mod tests { use super::*; - use crate::entity::EntityType; + use crate::entity::test; use crate::testframework as t; use feather_core::world::chunk::Chunk; use feather_core::world::ChunkPosition; - use feather_core::Block; - use specs::WorldExt; + use feather_core::{Block, Item, ItemStack}; + use specs::{Builder, WorldExt}; use std::collections::HashSet; #[test] @@ -636,12 +636,12 @@ mod tests { ); assert_eq!( - block_impacted_by_ray(&map, vec3(0.0, 65.0, 0.0), vec3(0.0, 1.0, 0.0), 256.0,), + block_impacted_by_ray(&map, vec3(0.0, 65.0, 0.0), vec3(0.0, 1.0, 0.0), 256.0), None ); assert_eq!( - block_impacted_by_ray(&map, vec3(0.0, 70.0, 0.0), vec3(0.0, -1.0, 0.0), 5.0,), + block_impacted_by_ray(&map, vec3(0.0, 70.0, 0.0), vec3(0.0, -1.0, 0.0), 5.0), None ); @@ -686,25 +686,10 @@ mod tests { t::populate_with_air(&mut w); // Prevents entities from getting despawned for being outside loaded chunks - let e1 = t::add_entity_with_pos(&mut w, EntityType::Player, position!(0.0, 0.0, 0.0), true); - let e2 = t::add_entity_with_pos( - &mut w, - EntityType::Player, - position!(-100.0, 0.0, 50.0), - true, - ); - let e3 = t::add_entity_with_pos( - &mut w, - EntityType::Player, - position!(100.0, 50.0, 50.0), - true, - ); - let e4 = t::add_entity_with_pos( - &mut w, - EntityType::Player, - position!(100.0, 1.0, -50.0), - true, - ); + let e1 = test::create(&mut w, position!(0.0, 0.0, 0.0)).build(); + let e2 = test::create(&mut w, position!(-100.0, 0.0, 50.0)).build(); + let e3 = test::create(&mut w, position!(100.0, 50.0, 50.0)).build(); + let e4 = test::create(&mut w, position!(100.0, 1.0, -50.0)).build(); d.dispatch(&w); w.maintain(); @@ -816,7 +801,7 @@ mod tests { position!(0.0, 90.0, 0.0), ]; - let bbox = BoundingBoxComponent(crate::physics::component::bbox(0.25, 0.25)); + let bbox = crate::physics::component::bbox(0.25, 0.25, 0.25); for ((from, dest), result) in froms.iter().zip(&dests).zip(&results) { let intersect = blocks_intersecting_bbox(&chunk_map, *from, *dest, &bbox); @@ -831,7 +816,7 @@ mod tests { fn test_adjacent_to_bbox() { let chunk_map = chunk_map(); - let bbox = crate::physics::component::bbox(0.25, 0.25); + let bbox = crate::physics::component::bbox(0.25, 0.25, 0.25); let pos = position!(0.0, 65.0, 0.0); @@ -840,14 +825,7 @@ mod tests { let mut checked = heapless::FnvIndexSet::new(); - let _ = adjacent_to_bbox( - axis, - sign, - &BoundingBoxComponent(bbox), - pos, - &chunk_map, - &mut checked, - ); + let _ = adjacent_to_bbox(axis, sign, &bbox, pos, &chunk_map, &mut checked); assert!(checked.contains(&BlockPosition::new(0, 64, 0))); } diff --git a/server/src/player/view.rs b/server/src/player/view.rs index a7c5f3e86..85850822c 100644 --- a/server/src/player/view.rs +++ b/server/src/player/view.rs @@ -97,19 +97,18 @@ impl<'a> System<'a> for ViewUpdateSystem { #[cfg(test)] mod tests { use super::*; - use crate::entity::{EntitySendSystem, EntityType}; + use crate::entity::test; 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 feather_core::{ChunkPosition, Item, ItemStack, PacketType}; use hashbrown::HashSet; - use specs::WorldExt; + use specs::{Builder, 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); @@ -117,10 +116,10 @@ mod tests { let player1 = t::add_player_without_holder(&mut world); let player2 = t::add_player_without_holder(&mut world); - 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 entity1 = test::create(&mut world, position!(0.0, 0.0, 0.0)).build(); + let entity2 = test::create(&mut world, position!(0.0, 0.0, 0.0)).build(); + let entity3 = test::create(&mut world, position!(0.0, 0.0, 0.0)).build(); + let entity4 = test::create(&mut world, position!(0.0, 0.0, 0.0)).build(); let mut config = Config::default(); config.server.view_distance = 4; @@ -143,6 +142,9 @@ mod tests { }; t::trigger_event(&world, event); + world.maintain(); + dispatcher.dispatch(&world); + world.maintain(); dispatcher.dispatch(&world); world.maintain(); diff --git a/server/src/testframework.rs b/server/src/testframework.rs index 1a33e27ca..030ac7804 100644 --- a/server/src/testframework.rs +++ b/server/src/testframework.rs @@ -21,12 +21,13 @@ use crate::chunk_logic::{ChunkHolders, ChunkLoadSystem}; use crate::config::Config; use crate::entity::metadata::{self, Metadata}; use crate::entity::{ - ChunkEntities, EntityDestroyEvent, EntitySpawnEvent, EntityType, ItemComponent, - LastKnownPositionComponent, NamedComponent, PlayerComponent, PositionComponent, - VelocityComponent, + ArrowComponent, ChunkEntities, EntityDestroyEvent, EntitySpawnEvent, ItemComponent, + LastKnownPositionComponent, NamedComponent, PacketCreatorComponent, PlayerComponent, + PositionComponent, SerializerComponent, VelocityComponent, }; use crate::io::ServerToWorkerMessage; use crate::network::{NetworkComponent, PacketQueue}; +use crate::physics::PhysicsComponent; use crate::player::{InventoryComponent, PlayerDisconnectEvent}; use crate::util::BroadcasterSystem; use crate::worldgen::{EmptyWorldGenerator, WorldGenerator}; @@ -51,7 +52,9 @@ pub fn init_world<'a, 'b>() -> (World, Dispatcher<'a, 'b>) { ); let level = LevelData::default(); - super::init_world(config, player_count, ioman, level) + let (mut world, dispatcher) = super::init_world(config, player_count, ioman, level); + register_components(&mut world); + (world, dispatcher) } pub struct Player { @@ -109,7 +112,6 @@ pub fn add_player_without_holder(world: &mut World) -> Player { }) .with(InventoryComponent::default()) .with(Metadata::Player(metadata::Player::default())) - .with(EntityType::Player) .with(LastKnownPositionComponent::default()) .build(); @@ -237,119 +239,6 @@ pub fn triggered_events( channel.read(reader).cloned().collect() } -/// 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( - world: &mut World, - ty: EntityType, - pos: Position, - trigger_spawn_event: bool, -) -> Entity { - add_entity_with_pos_and_vel( - world, - ty, - pos, - glm::vec3(0.0, 0.0, 0.0), - trigger_spawn_event, - ) -} - -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, - ty: EntityType, - 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() - .with(PositionComponent { - current: pos, - previous: pos, - }) - .with(VelocityComponent(vel)) - .with(NamedComponent { - uuid: Uuid::new_v4(), - display_name: "bla".to_string(), - }) - .with(ty) - .with(Metadata::Entity(metadata::Entity::default())) - .build(); - - if ty == EntityType::Item { - world - .write_component::() - .insert( - entity, - ItemComponent { - collectable_at: 20, - stack: ItemStack::new(Item::Air, 0), - }, - ) - .unwrap(); - } - - if trigger_spawn_event { - let event = EntitySpawnEvent { entity, ty }; - trigger_event(&world, event); - } - entity -} - /// Populates a 15x15 area of chunks around the origin /// with air. pub fn populate_with_air(world: &mut World) { @@ -460,21 +349,28 @@ impl<'a, 'b> TestBuilder<'a, 'b> { let mut dispatcher = self.dispatcher.build(); dispatcher.setup(&mut self.world); - self.world.register::(); - self.world.register::(); - self.world.register::(); - self.world.register::(); - self.world.register::(); - self.world.register::(); - self.world.register::(); - self.world.register::(); - self.world.register::(); - self.world.register::(); + register_components(&mut self.world); (self.world, dispatcher) } } +fn register_components(world: &mut World) { + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); +} + pub fn builder<'a, 'b>() -> TestBuilder<'a, 'b> { TestBuilder { world: World::new(), From 8ec49fa3762df9ba8f5f417f890926fa3496f743 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 18 Oct 2019 23:55:34 -0600 Subject: [PATCH 010/647] After much pain... get tests to pass --- core/src/save/entity.rs | 13 ++++++++-- server/src/entity/broadcast.rs | 7 ++++-- server/src/entity/chunk.rs | 20 +++++++++++---- server/src/entity/impls/item.rs | 26 +++++++++++++++++--- server/src/entity/impls/test.rs | 21 ++++++++++++---- server/src/entity/metadata.rs | 10 +++++++- server/src/entity/save.rs | 1 + server/src/player/init.rs | 2 +- server/src/player/mod.rs | 1 + server/src/player/view.rs | 43 +++++++++++++++++++++++++++++---- server/src/testframework.rs | 13 +++++++--- 11 files changed, 129 insertions(+), 28 deletions(-) diff --git a/core/src/save/entity.rs b/core/src/save/entity.rs index 8b156d0f4..bd964fb96 100644 --- a/core/src/save/entity.rs +++ b/core/src/save/entity.rs @@ -1,4 +1,4 @@ -use crate::Position; +use crate::{Item, Position}; use nbt::Value; use std::collections::HashMap; @@ -120,7 +120,7 @@ impl Default for BaseEntityData { } /// Represents a single item, without slot information. -#[derive(Debug, Default, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ItemData { #[serde(rename = "Count")] pub count: u8, @@ -135,6 +135,15 @@ impl ItemData { } } +impl Default for ItemData { + fn default() -> Self { + Self { + count: 0, + item: Item::Air.identifier().to_string(), + } + } +} + /// Data for an Item entity (`minecraft:item`). #[derive(Clone, Default, Serialize, Deserialize, Debug)] pub struct ItemEntityData { diff --git a/server/src/entity/broadcast.rs b/server/src/entity/broadcast.rs index d60c13be5..7151c7de3 100644 --- a/server/src/entity/broadcast.rs +++ b/server/src/entity/broadcast.rs @@ -141,7 +141,7 @@ pub fn send_entity_to_player(lazy: &LazyUpdate, player: Entity, entity: Entity) #[cfg(test)] mod tests { use super::*; - use crate::entity::{item, test}; + use crate::entity::{item, test, VelocityComponent}; use crate::player::ChunkCrossSystem; use crate::testframework as t; use feather_core::network::cast_packet; @@ -183,7 +183,10 @@ mod tests { let player = t::add_player(&mut w); - let item = item::create(&w.fetch(), &w.fetch(), ItemStack::new(Item::Stone, 1), 0).build(); + let item = item::create(&w.fetch(), &w.fetch(), ItemStack::new(Item::Stone, 1), 0) + .with(PositionComponent::default()) + .with(VelocityComponent::default()) + .build(); w.maintain(); d.dispatch(&w); diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index 377beb711..0c33e0324 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -202,10 +202,16 @@ impl<'a> System<'a> for EntityChunkLoadSystem { for entity in &event.entities { match entity { EntityData::Item(item_data) => { - item::create_from_data(&lazy, &entities, item_data, &tick); + if item::create_from_data(&lazy, &entities, item_data, &tick).is_none() { + debug!("Error while loading item entity"); + dbg!(); + } } EntityData::Arrow(arrow_data) => { - arrow::create_from_data(&lazy, &entities, arrow_data); + if arrow::create_from_data(&lazy, &entities, arrow_data).is_none() { + debug!("Error while loading arrow entity"); + dbg!(); + } } // TODO: Spawn remaining entity types here. EntityData::Unknown => { @@ -359,7 +365,9 @@ mod tests { #[test] fn test_entities_loaded_in_chunk() { - let (mut w, mut d) = t::init_world(); + let (mut w, mut d) = t::builder() + .with(EntityChunkLoadSystem::default(), "") + .build(); let entities = vec![ EntityData::Item(ItemEntityData::default()), @@ -371,6 +379,8 @@ mod tests { let load_event = ChunkLoadEvent { pos, entities }; t::trigger_event(&w, load_event); + d.dispatch(&w); + w.maintain(); d.dispatch(&w); w.maintain(); @@ -378,8 +388,8 @@ mod tests { let mut events = t::triggered_events::(&w, &mut entity_spawn_reader); let first = events.remove(0).entity; - assert!(w.read_component::().contains(first)); let second = events.remove(0).entity; - assert!(w.read_component::().contains(second)); + assert!(w.read_component::().contains(first)); + assert!(w.read_component::().contains(second)); } } diff --git a/server/src/entity/impls/item.rs b/server/src/entity/impls/item.rs index ece258cd2..615fc0f72 100644 --- a/server/src/entity/impls/item.rs +++ b/server/src/entity/impls/item.rs @@ -444,7 +444,7 @@ pub fn item_meta(stack: ItemStack) -> Metadata { #[cfg(test)] mod tests { use super::*; - use crate::entity::EntitySpawnEvent; + use crate::entity::{ChunkEntityUpdateSystem, EntitySpawnEvent}; use crate::testframework as t; use feather_core::inventory::SLOT_HOTBAR_OFFSET; use feather_core::network::cast_packet; @@ -500,6 +500,7 @@ mod tests { ItemStack::new(Item::EnderPearl, 4), 0, ) + .with(PositionComponent::default()) .build(); let item2 = create( &w.fetch(), @@ -507,8 +508,17 @@ mod tests { ItemStack::new(Item::EnderPearl, 7), 0, ) + .with(PositionComponent::default()) .build(); + let mut updater = ChunkEntityUpdateSystem::default(); + updater.setup(&mut w); + + w.maintain(); + + // Update chunk entities so `nearby_entities` works + specs::RunNow::run_now(&mut updater, &w); + d.dispatch(&w); w.maintain(); @@ -531,14 +541,24 @@ mod tests { let player = t::add_player(&mut w); let stack = ItemStack::new(Item::String, 4); - let item = create(&w.fetch(), &w.fetch(), stack.clone(), 0).build(); + let item = create(&w.fetch(), &w.fetch(), stack.clone(), 0) + .with(PositionComponent::default()) + .build(); let mut destroy_reader = t::reader(&w); // Allow item to be collected - w.fetch_mut::().0 = 20; + w.fetch_mut::().0 = 0; + + let mut updater = ChunkEntityUpdateSystem::default(); + updater.setup(&mut w); w.maintain(); + + // Update chunk entities so `nearby_entities` works + + specs::RunNow::run_now(&mut updater, &w); + d.dispatch(&w); w.maintain(); diff --git a/server/src/entity/impls/test.rs b/server/src/entity/impls/test.rs index 8f74b12cd..21ef64b9f 100644 --- a/server/src/entity/impls/test.rs +++ b/server/src/entity/impls/test.rs @@ -1,12 +1,23 @@ //! A fake entity implementation for unit tests. -use crate::entity::PositionComponent; +use crate::entity::{EntitySpawnEvent, PositionComponent, VelocityComponent}; use feather_core::Position; +use shrev::EventChannel; use specs::{Builder, EntityBuilder, World, WorldExt}; pub fn create(world: &mut World, pos: Position) -> EntityBuilder { - world.create_entity().with(PositionComponent { - current: pos, - previous: pos, - }) + let builder = world + .create_entity() + .with(PositionComponent { + current: pos, + previous: pos, + }) + .with(VelocityComponent(glm::vec3(0.0, 0.0, 0.0))); + builder + .world + .fetch_mut::>() + .single_write(EntitySpawnEvent { + entity: builder.entity, + }); + builder } diff --git a/server/src/entity/metadata.rs b/server/src/entity/metadata.rs index 38bde9e76..a4dd09683 100644 --- a/server/src/entity/metadata.rs +++ b/server/src/entity/metadata.rs @@ -154,8 +154,16 @@ mod tests { .with(MetadataBroadcastSystem::default(), "") .build(); - // Metadata is inserted here, which causes update event let entity = test::create(&mut w, position!(0.0, 0.0, 0.0)).build(); + + // Insert metadata + { + let mut metadatas = w.write_component::(); + metadatas + .insert(entity, Metadata::Entity(Entity::default())) + .unwrap(); + } + let player = t::add_player(&mut w); d.dispatch(&w); diff --git a/server/src/entity/save.rs b/server/src/entity/save.rs index 03c70066f..c1d5cde5a 100644 --- a/server/src/entity/save.rs +++ b/server/src/entity/save.rs @@ -183,6 +183,7 @@ mod tests { .set_chunk_at(pos, Chunk::new(pos)); dispatcher.dispatch(&world); + world.maintain(); let msg = rx.try_recv().unwrap(); diff --git a/server/src/player/init.rs b/server/src/player/init.rs index 2df4b1721..f266687d6 100644 --- a/server/src/player/init.rs +++ b/server/src/player/init.rs @@ -156,7 +156,7 @@ impl<'a> System<'a> for PlayerInitSystem { } } -fn create_packet(world: &World, entity: Entity) -> Box { +pub fn create_packet(world: &World, entity: Entity) -> Box { let positions = world.read_component::(); let nameds = world.read_component::(); let metas = world.read_component::(); diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index bde0f03f7..2cdeeaee6 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -26,6 +26,7 @@ mod save; mod view; pub use broadcast::PlayerDisconnectEvent; +pub use init::create_packet; pub use movement::{ send_chunk_to_player, ChunkCrossSystem, ChunkPendingComponent, LoadedChunksComponent, diff --git a/server/src/player/view.rs b/server/src/player/view.rs index 85850822c..e77543e34 100644 --- a/server/src/player/view.rs +++ b/server/src/player/view.rs @@ -47,6 +47,8 @@ impl<'a> System<'a> for ViewUpdateSystem { let new_entities = chunk_entities.entites_within_view_distance(event.new, config.server.view_distance); + dbg!(old_entities.clone(), new_entities.clone()); + let mut to_destroy = vec![]; // Compute entities which are only present in one of the sets. @@ -68,6 +70,7 @@ impl<'a> System<'a> for ViewUpdateSystem { let packet = DestroyEntities { entity_ids: vec![event.player.id() as i32], }; + dbg!(); send_packet_to_player(network, packet); } } else { @@ -78,6 +81,7 @@ impl<'a> System<'a> for ViewUpdateSystem { if networks.get(*entity).is_some() { lazy.send_entity_to_player(*entity, event.player); + dbg!(); } } } @@ -97,7 +101,7 @@ impl<'a> System<'a> for ViewUpdateSystem { #[cfg(test)] mod tests { use super::*; - use crate::entity::test; + use crate::entity::{item, test, PositionComponent}; use crate::testframework as t; use feather_core::network::cast_packet; use feather_core::network::packet::implementation::{SpawnObject, SpawnPlayer}; @@ -116,10 +120,38 @@ mod tests { let player1 = t::add_player_without_holder(&mut world); let player2 = t::add_player_without_holder(&mut world); - let entity1 = test::create(&mut world, position!(0.0, 0.0, 0.0)).build(); - let entity2 = test::create(&mut world, position!(0.0, 0.0, 0.0)).build(); - let entity3 = test::create(&mut world, position!(0.0, 0.0, 0.0)).build(); - let entity4 = test::create(&mut world, position!(0.0, 0.0, 0.0)).build(); + let entity1 = item::create( + &world.fetch(), + &world.fetch(), + ItemStack::new(Item::Stone, 0), + 0, + ) + .with(PositionComponent::default()) + .build(); + let entity2 = item::create( + &world.fetch(), + &world.fetch(), + ItemStack::new(Item::Stone, 0), + 0, + ) + .with(PositionComponent::default()) + .build(); + let entity3 = item::create( + &world.fetch(), + &world.fetch(), + ItemStack::new(Item::Stone, 0), + 0, + ) + .with(PositionComponent::default()) + .build(); + let entity4 = item::create( + &world.fetch(), + &world.fetch(), + ItemStack::new(Item::Stone, 0), + 0, + ) + .with(PositionComponent::default()) + .build(); let mut config = Config::default(); config.server.view_distance = 4; @@ -153,6 +185,7 @@ mod tests { let mut received_spawns = HashSet::new(); for packet in packets { + dbg!(packet.ty()); match packet.ty() { PacketType::DestroyEntities => { let packet = cast_packet::(&*packet); diff --git a/server/src/testframework.rs b/server/src/testframework.rs index 030ac7804..7d40961f6 100644 --- a/server/src/testframework.rs +++ b/server/src/testframework.rs @@ -21,9 +21,9 @@ use crate::chunk_logic::{ChunkHolders, ChunkLoadSystem}; use crate::config::Config; use crate::entity::metadata::{self, Metadata}; use crate::entity::{ - ArrowComponent, ChunkEntities, EntityDestroyEvent, EntitySpawnEvent, ItemComponent, - LastKnownPositionComponent, NamedComponent, PacketCreatorComponent, PlayerComponent, - PositionComponent, SerializerComponent, VelocityComponent, + ArrowComponent, ChunkEntities, EntityDestroyEvent, EntitySendEvent, EntitySpawnEvent, + ItemComponent, LastKnownPositionComponent, NamedComponent, PacketCreatorComponent, + PlayerComponent, PositionComponent, SerializerComponent, VelocityComponent, }; use crate::io::ServerToWorkerMessage; use crate::network::{NetworkComponent, PacketQueue}; @@ -31,7 +31,7 @@ use crate::physics::PhysicsComponent; use crate::player::{InventoryComponent, PlayerDisconnectEvent}; use crate::util::BroadcasterSystem; use crate::worldgen::{EmptyWorldGenerator, WorldGenerator}; -use crate::PlayerCount; +use crate::{player, PlayerCount}; use bitflags::_core::cell::RefCell; /// Initializes a Specs world and dispatcher @@ -113,6 +113,7 @@ pub fn add_player_without_holder(world: &mut World) -> Player { .with(InventoryComponent::default()) .with(Metadata::Player(metadata::Player::default())) .with(LastKnownPositionComponent::default()) + .with(PacketCreatorComponent(&player::create_packet)) .build(); Player { @@ -369,6 +370,10 @@ fn register_components(world: &mut World) { world.register::(); world.register::(); world.register::(); + + world + .entry() + .or_insert(EventChannel::::default()); } pub fn builder<'a, 'b>() -> TestBuilder<'a, 'b> { From c4be747d92200aca8ba5bf602b7d9049cce7ec1a Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 19 Oct 2019 00:07:56 -0600 Subject: [PATCH 011/647] Fix warnings and clippy issues --- server/src/entity/broadcast.rs | 2 +- server/src/entity/chunk.rs | 1 - server/src/entity/impls/item.rs | 3 +-- server/src/entity/movement.rs | 1 - server/src/physics/entity.rs | 2 -- server/src/physics/math.rs | 2 +- server/src/player/chat.rs | 2 +- server/src/player/digging.rs | 4 ++-- server/src/player/init.rs | 1 - server/src/player/view.rs | 2 +- server/src/testframework.rs | 2 +- 11 files changed, 8 insertions(+), 14 deletions(-) diff --git a/server/src/entity/broadcast.rs b/server/src/entity/broadcast.rs index 7151c7de3..b2444f92e 100644 --- a/server/src/entity/broadcast.rs +++ b/server/src/entity/broadcast.rs @@ -141,7 +141,7 @@ pub fn send_entity_to_player(lazy: &LazyUpdate, player: Entity, entity: Entity) #[cfg(test)] mod tests { use super::*; - use crate::entity::{item, test, VelocityComponent}; + use crate::entity::{item, VelocityComponent}; use crate::player::ChunkCrossSystem; use crate::testframework as t; use feather_core::network::cast_packet; diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index 0c33e0324..a873b2e03 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -233,7 +233,6 @@ mod tests { use crate::entity::{test, ArrowComponent, ItemComponent}; use crate::testframework as t; use feather_core::entity::{ArrowEntityData, ItemEntityData}; - use feather_core::Position; use specs::{Builder, World, WorldExt}; #[test] diff --git a/server/src/entity/impls/item.rs b/server/src/entity/impls/item.rs index 615fc0f72..edc54b881 100644 --- a/server/src/entity/impls/item.rs +++ b/server/src/entity/impls/item.rs @@ -430,7 +430,7 @@ fn serialize(world: &World, entity: Entity) -> EntityData { pub fn item_stack_from_meta(meta: &Metadata) -> ItemStack { match meta { - Metadata::Item(item) => item.item().unwrap().clone(), + Metadata::Item(item) => item.item().unwrap(), _ => panic!(), } } @@ -448,7 +448,6 @@ mod tests { use crate::testframework as t; use feather_core::inventory::SLOT_HOTBAR_OFFSET; use feather_core::network::cast_packet; - use feather_core::world::Position; use feather_core::{Item, ItemStack, PacketType}; use specs::WorldExt; diff --git a/server/src/entity/movement.rs b/server/src/entity/movement.rs index f14028c4a..34a4d25b9 100644 --- a/server/src/entity/movement.rs +++ b/server/src/entity/movement.rs @@ -236,7 +236,6 @@ mod tests { use crate::testframework as t; use super::*; - use feather_core::{Item, ItemStack}; #[test] fn test_velocity_broadcast_system() { diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index dd8a3dcd6..3218c0766 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -196,7 +196,6 @@ mod tests { use crate::entity::test; use crate::physics::PhysicsBuilder; use crate::testframework as t; - use feather_core::{Item, ItemStack}; use specs::{Builder, WorldExt}; #[test] @@ -205,7 +204,6 @@ mod tests { let entity = test::create(&mut w, position!(1000.0, 100.0, 1000.0)).build(); - let bbox = crate::physics::component::bbox(0.25, 0.25, 0.25); w.write_component::() .insert(entity, PhysicsBuilder::new().build()) .unwrap(); diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index 36806c468..11f599df0 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -618,7 +618,7 @@ mod tests { use crate::testframework as t; use feather_core::world::chunk::Chunk; use feather_core::world::ChunkPosition; - use feather_core::{Block, Item, ItemStack}; + use feather_core::Block; use specs::{Builder, WorldExt}; use std::collections::HashSet; diff --git a/server/src/player/chat.rs b/server/src/player/chat.rs index c9504fa42..60e7db460 100644 --- a/server/src/player/chat.rs +++ b/server/src/player/chat.rs @@ -141,7 +141,7 @@ mod tests { message: String::from("test"), }; - t::trigger_event(&w, event.clone()); + t::trigger_event(&w, event); d.dispatch(&w); w.maintain(); diff --git a/server/src/player/digging.rs b/server/src/player/digging.rs index e9b2a2587..6b1d51466 100644 --- a/server/src/player/digging.rs +++ b/server/src/player/digging.rs @@ -274,7 +274,7 @@ fn handle_shoot_bow( // Consume arrow let (arrow_slot, arrow_stack) = arrow_to_consume.unwrap(); - let mut arrow_stack: ItemStack = arrow_stack.clone(); + let mut arrow_stack: ItemStack = arrow_stack; arrow_stack.amount -= 1; inventory.set_item_at(arrow_slot, arrow_stack); @@ -429,7 +429,7 @@ mod tests { .unwrap() .gamemode = Gamemode::Survival; - t::receive_packet(&player, &w, packet.clone()); + t::receive_packet(&player, &w, packet); d.dispatch(&w); w.maintain(); diff --git a/server/src/player/init.rs b/server/src/player/init.rs index f266687d6..a0c4d83aa 100644 --- a/server/src/player/init.rs +++ b/server/src/player/init.rs @@ -8,7 +8,6 @@ use crate::player::{ChunkPendingComponent, InventoryComponent, LoadedChunksCompo use crate::prelude::*; use feather_core::level::LevelData; use feather_core::packet::SpawnPlayer; -use feather_core::Position; use feather_core::{Gamemode, Packet}; use hashbrown::HashSet; use shrev::{EventChannel, ReaderId}; diff --git a/server/src/player/view.rs b/server/src/player/view.rs index e77543e34..cd6969f29 100644 --- a/server/src/player/view.rs +++ b/server/src/player/view.rs @@ -101,7 +101,7 @@ impl<'a> System<'a> for ViewUpdateSystem { #[cfg(test)] mod tests { use super::*; - use crate::entity::{item, test, PositionComponent}; + use crate::entity::{item, PositionComponent}; use crate::testframework as t; use feather_core::network::cast_packet; use feather_core::network::packet::implementation::{SpawnObject, SpawnPlayer}; diff --git a/server/src/testframework.rs b/server/src/testframework.rs index 7d40961f6..fef503cde 100644 --- a/server/src/testframework.rs +++ b/server/src/testframework.rs @@ -15,7 +15,7 @@ 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, Item, ItemStack}; +use feather_core::Gamemode; use crate::chunk_logic::{ChunkHolders, ChunkLoadSystem}; use crate::config::Config; From bc78aeb868e2f8337ff61a44e828763433367b5f Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 19 Oct 2019 00:15:20 -0600 Subject: [PATCH 012/647] Remove debug messages --- server/src/entity/chunk.rs | 2 -- server/src/player/view.rs | 4 ---- 2 files changed, 6 deletions(-) diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index a873b2e03..392b0f999 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -204,13 +204,11 @@ impl<'a> System<'a> for EntityChunkLoadSystem { EntityData::Item(item_data) => { if item::create_from_data(&lazy, &entities, item_data, &tick).is_none() { debug!("Error while loading item entity"); - dbg!(); } } EntityData::Arrow(arrow_data) => { if arrow::create_from_data(&lazy, &entities, arrow_data).is_none() { debug!("Error while loading arrow entity"); - dbg!(); } } // TODO: Spawn remaining entity types here. diff --git a/server/src/player/view.rs b/server/src/player/view.rs index cd6969f29..d0cf7504c 100644 --- a/server/src/player/view.rs +++ b/server/src/player/view.rs @@ -47,8 +47,6 @@ impl<'a> System<'a> for ViewUpdateSystem { let new_entities = chunk_entities.entites_within_view_distance(event.new, config.server.view_distance); - dbg!(old_entities.clone(), new_entities.clone()); - let mut to_destroy = vec![]; // Compute entities which are only present in one of the sets. @@ -70,7 +68,6 @@ impl<'a> System<'a> for ViewUpdateSystem { let packet = DestroyEntities { entity_ids: vec![event.player.id() as i32], }; - dbg!(); send_packet_to_player(network, packet); } } else { @@ -81,7 +78,6 @@ impl<'a> System<'a> for ViewUpdateSystem { if networks.get(*entity).is_some() { lazy.send_entity_to_player(*entity, event.player); - dbg!(); } } } From 992d49dcd0c840b069a255c9311925d61175c73f Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 19 Oct 2019 12:39:35 -0600 Subject: [PATCH 013/647] Implement chicken, cow, donkey, horse, llama, mooshroom, pig, rabbit, sheep, and squid --- core/src/save/entity.rs | 54 +++++++++++++++++- server/src/entity/chunk.rs | 1 + server/src/entity/component.rs | 4 +- server/src/entity/impls/animal/chicken.rs | 31 +++++++++++ server/src/entity/impls/animal/cow.rs | 31 +++++++++++ server/src/entity/impls/animal/donkey.rs | 35 ++++++++++++ server/src/entity/impls/animal/horse.rs | 35 ++++++++++++ server/src/entity/impls/animal/llama.rs | 31 +++++++++++ server/src/entity/impls/animal/mod.rs | 12 ++++ server/src/entity/impls/animal/mooshroom.rs | 31 +++++++++++ server/src/entity/impls/animal/pig.rs | 31 +++++++++++ server/src/entity/impls/animal/rabbit.rs | 31 +++++++++++ server/src/entity/impls/animal/sheep.rs | 31 +++++++++++ server/src/entity/impls/animal/squid.rs | 31 +++++++++++ server/src/entity/impls/mod.rs | 62 +++++++++++++++++++++ server/src/physics/component.rs | 6 ++ 16 files changed, 454 insertions(+), 3 deletions(-) create mode 100644 server/src/entity/impls/animal/chicken.rs create mode 100644 server/src/entity/impls/animal/cow.rs create mode 100644 server/src/entity/impls/animal/donkey.rs create mode 100644 server/src/entity/impls/animal/horse.rs create mode 100644 server/src/entity/impls/animal/llama.rs create mode 100644 server/src/entity/impls/animal/mod.rs create mode 100644 server/src/entity/impls/animal/mooshroom.rs create mode 100644 server/src/entity/impls/animal/pig.rs create mode 100644 server/src/entity/impls/animal/rabbit.rs create mode 100644 server/src/entity/impls/animal/sheep.rs create mode 100644 server/src/entity/impls/animal/squid.rs diff --git a/core/src/save/entity.rs b/core/src/save/entity.rs index bd964fb96..48b05e225 100644 --- a/core/src/save/entity.rs +++ b/core/src/save/entity.rs @@ -9,6 +9,26 @@ pub enum EntityData { Item(ItemEntityData), #[serde(rename = "minecraft:arrow")] Arrow(ArrowEntityData), + #[serde(rename = "minecraft:cow")] + Cow(AnimalData), + #[serde(rename = "minecraft:pig")] + Pig(AnimalData), + #[serde(rename = "minecraft:chicken")] + Chicken(AnimalData), + #[serde(rename = "minecraft:sheep")] + Sheep(AnimalData), + #[serde(rename = "minecraft:horse")] + Horse(AnimalData), + #[serde(rename = "minecraft:llama")] + Llama(AnimalData), + #[serde(rename = "minectaft:mooshroom")] + Mooshroom(AnimalData), + #[serde(rename = "minecraft:rabbit}")] + Rabbit(AnimalData), + #[serde(rename = "minecraft:squid")] + Squid(AnimalData), + #[serde(rename = "minecraft:donkey")] + Donkey(AnimalData), /// Fallback type for unknown entities #[serde(other)] @@ -25,6 +45,16 @@ impl EntityData { match self { EntityData::Item(_) => "minecraft:item", EntityData::Arrow(_) => "minecraft:arrow", + EntityData::Cow(_) => "minecraft:cow", + EntityData::Pig(_) => "minecraft:pig", + EntityData::Chicken(_) => "minecraft:chicken", + EntityData::Sheep(_) => "minecraft:sheep", + EntityData::Horse(_) => "minecraft:horse", + EntityData::Llama(_) => "minecraft:llama", + EntityData::Mooshroom(_) => "minecraft:mooshroom", + EntityData::Rabbit(_) => "minecraft:rabbit", + EntityData::Squid(_) => "minecraft:squid", + EntityData::Donkey(_) => "minecraft:donkey", EntityData::Unknown => panic!("Cannot write unknown entities"), } .to_string(), @@ -34,7 +64,17 @@ impl EntityData { match self { EntityData::Item(data) => data.write_to_map(&mut map), EntityData::Arrow(data) => data.write_to_map(&mut map), - EntityData::Unknown => panic!("Cannot write unknown entities"), + EntityData::Cow(data) => data.write_to_map(&mut map), + EntityData::Pig(data) => data.write_to_map(&mut map), + EntityData::Chicken(data) => data.write_to_map(&mut map), + EntityData::Sheep(data) => data.write_to_map(&mut map), + EntityData::Horse(data) => data.write_to_map(&mut map), + EntityData::Llama(data) => data.write_to_map(&mut map), + EntityData::Mooshroom(data) => data.write_to_map(&mut map), + EntityData::Rabbit(data) => data.write_to_map(&mut map), + EntityData::Squid(data) => data.write_to_map(&mut map), + EntityData::Donkey(data) => data.write_to_map(&mut map), + EntityData::Unknown => unreachable!(), } Value::Compound(map) @@ -119,6 +159,18 @@ impl Default for BaseEntityData { } } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AnimalData { + #[serde(flatten)] + pub base: BaseEntityData, +} + +impl AnimalData { + fn write_to_map(self, map: &mut HashMap) { + self.base.write_to_map(map); + } +} + /// Represents a single item, without slot information. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ItemData { diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index 392b0f999..154b24f57 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -215,6 +215,7 @@ impl<'a> System<'a> for EntityChunkLoadSystem { EntityData::Unknown => { trace!("Chunk {:?} contains an unknown entity type", event.pos); } + _ => todo!(), } } } diff --git a/server/src/entity/component.rs b/server/src/entity/component.rs index d506f01fa..35e2cf1b1 100644 --- a/server/src/entity/component.rs +++ b/server/src/entity/component.rs @@ -17,7 +17,7 @@ impl Component for PlayerComponent { type Storage = BTreeStorage; } -#[derive(Default, Debug, PartialEq)] +#[derive(Default, Debug, PartialEq, Clone, Copy)] pub struct PositionComponent { /// The current position of this entity. pub current: Position, @@ -43,7 +43,7 @@ impl Component for PositionComponent { /// /// Entities without this component are assumed /// to have a velocity of 0. -#[derive(Deref, DerefMut, Debug, PartialEq, Clone)] +#[derive(Deref, DerefMut, Debug, PartialEq, Clone, Copy)] pub struct VelocityComponent(pub DVec3); impl Component for VelocityComponent { diff --git a/server/src/entity/impls/animal/chicken.rs b/server/src/entity/impls/animal/chicken.rs new file mode 100644 index 000000000..0fd5f4b33 --- /dev/null +++ b/server/src/entity/impls/animal/chicken.rs @@ -0,0 +1,31 @@ +use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::lazy::LazyUpdateExt; +use crate::physics::PhysicsBuilder; +use feather_core::entity::{AnimalData, EntityData}; +use feather_core::Packet; +use specs::world::{EntitiesRes, LazyBuilder}; +use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; + +#[derive(Default)] +pub struct ChickenComponent; + +impl Component for ChickenComponent { + type Storage = NullStorage; +} + +pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { + lazy.spawn_entity(entities) + .with(ChickenComponent) + .with(PhysicsBuilder::for_living().bbox(0.4, 0.7, 0.4).build()) + .with(PacketCreatorComponent(&create_packet)) + .with(SerializerComponent(&serialize)) +} + +fn create_packet(world: &World, entity: Entity) -> Box { + create_mob_packet(world, entity, 7) +} + +fn serialize(world: &World, entity: Entity) -> EntityData { + let base = base_data(world, entity); + EntityData::Chicken(AnimalData { base }) +} diff --git a/server/src/entity/impls/animal/cow.rs b/server/src/entity/impls/animal/cow.rs new file mode 100644 index 000000000..7b61488d9 --- /dev/null +++ b/server/src/entity/impls/animal/cow.rs @@ -0,0 +1,31 @@ +use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::lazy::LazyUpdateExt; +use crate::physics::PhysicsBuilder; +use feather_core::entity::{AnimalData, EntityData}; +use feather_core::Packet; +use specs::world::{EntitiesRes, LazyBuilder}; +use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; + +#[derive(Default)] +pub struct CowComponent; + +impl Component for CowComponent { + type Storage = NullStorage; +} + +pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { + lazy.spawn_entity(entities) + .with(CowComponent) + .with(PhysicsBuilder::for_living().bbox(0.9, 1.4, 0.9).build()) + .with(PacketCreatorComponent(&create_packet)) + .with(SerializerComponent(&serialize)) +} + +fn create_packet(world: &World, entity: Entity) -> Box { + create_mob_packet(world, entity, 9) +} + +fn serialize(world: &World, entity: Entity) -> EntityData { + let base = base_data(world, entity); + EntityData::Cow(AnimalData { base }) +} diff --git a/server/src/entity/impls/animal/donkey.rs b/server/src/entity/impls/animal/donkey.rs new file mode 100644 index 000000000..8dee661ae --- /dev/null +++ b/server/src/entity/impls/animal/donkey.rs @@ -0,0 +1,35 @@ +use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::lazy::LazyUpdateExt; +use crate::physics::PhysicsBuilder; +use feather_core::entity::{AnimalData, EntityData}; +use feather_core::Packet; +use specs::world::{EntitiesRes, LazyBuilder}; +use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; + +#[derive(Default)] +pub struct DonkeyComponent; + +impl Component for DonkeyComponent { + type Storage = NullStorage; +} + +pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { + lazy.spawn_entity(entities) + .with(DonkeyComponent) + .with( + PhysicsBuilder::for_living() + .bbox(1.3964844, 1.6, 1.3964844) + .build(), + ) + .with(PacketCreatorComponent(&create_packet)) + .with(SerializerComponent(&serialize)) +} + +fn create_packet(world: &World, entity: Entity) -> Box { + create_mob_packet(world, entity, 11) +} + +fn serialize(world: &World, entity: Entity) -> EntityData { + let base = base_data(world, entity); + EntityData::Donkey(AnimalData { base }) +} diff --git a/server/src/entity/impls/animal/horse.rs b/server/src/entity/impls/animal/horse.rs new file mode 100644 index 000000000..f9fb46faa --- /dev/null +++ b/server/src/entity/impls/animal/horse.rs @@ -0,0 +1,35 @@ +use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::lazy::LazyUpdateExt; +use crate::physics::PhysicsBuilder; +use feather_core::entity::{AnimalData, EntityData}; +use feather_core::Packet; +use specs::world::{EntitiesRes, LazyBuilder}; +use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; + +#[derive(Default)] +pub struct HorseComponent; + +impl Component for HorseComponent { + type Storage = NullStorage; +} + +pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { + lazy.spawn_entity(entities) + .with(HorseComponent) + .with( + PhysicsBuilder::for_living() + .bbox(1.3964844, 1.6, 1.3964844) + .build(), + ) + .with(PacketCreatorComponent(&create_packet)) + .with(SerializerComponent(&serialize)) +} + +fn create_packet(world: &World, entity: Entity) -> Box { + create_mob_packet(world, entity, 29) +} + +fn serialize(world: &World, entity: Entity) -> EntityData { + let base = base_data(world, entity); + EntityData::Horse(AnimalData { base }) +} diff --git a/server/src/entity/impls/animal/llama.rs b/server/src/entity/impls/animal/llama.rs new file mode 100644 index 000000000..eda30bef9 --- /dev/null +++ b/server/src/entity/impls/animal/llama.rs @@ -0,0 +1,31 @@ +use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::lazy::LazyUpdateExt; +use crate::physics::PhysicsBuilder; +use feather_core::entity::{AnimalData, EntityData}; +use feather_core::Packet; +use specs::world::{EntitiesRes, LazyBuilder}; +use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; + +#[derive(Default)] +pub struct LlamaComponent; + +impl Component for LlamaComponent { + type Storage = NullStorage; +} + +pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { + lazy.spawn_entity(entities) + .with(LlamaComponent) + .with(PhysicsBuilder::for_living().bbox(0.9, 1.87, 0.9).build()) + .with(PacketCreatorComponent(&create_packet)) + .with(SerializerComponent(&serialize)) +} + +fn create_packet(world: &World, entity: Entity) -> Box { + create_mob_packet(world, entity, 36) +} + +fn serialize(world: &World, entity: Entity) -> EntityData { + let base = base_data(world, entity); + EntityData::Llama(AnimalData { base }) +} diff --git a/server/src/entity/impls/animal/mod.rs b/server/src/entity/impls/animal/mod.rs new file mode 100644 index 000000000..89ede779b --- /dev/null +++ b/server/src/entity/impls/animal/mod.rs @@ -0,0 +1,12 @@ +//! Implementations for animals: cows, pigs, chickens, etc. + +pub mod chicken; +pub mod cow; +pub mod donkey; +pub mod horse; +pub mod llama; +pub mod mooshroom; +pub mod pig; +pub mod rabbit; +pub mod sheep; +pub mod squid; diff --git a/server/src/entity/impls/animal/mooshroom.rs b/server/src/entity/impls/animal/mooshroom.rs new file mode 100644 index 000000000..765fee7ed --- /dev/null +++ b/server/src/entity/impls/animal/mooshroom.rs @@ -0,0 +1,31 @@ +use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::lazy::LazyUpdateExt; +use crate::physics::PhysicsBuilder; +use feather_core::entity::{AnimalData, EntityData}; +use feather_core::Packet; +use specs::world::{EntitiesRes, LazyBuilder}; +use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; + +#[derive(Default)] +pub struct MooshroomComponent; + +impl Component for MooshroomComponent { + type Storage = NullStorage; +} + +pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { + lazy.spawn_entity(entities) + .with(MooshroomComponent) + .with(PhysicsBuilder::for_living().bbox(0.9, 1.4, 0.9).build()) + .with(PacketCreatorComponent(&create_packet)) + .with(SerializerComponent(&serialize)) +} + +fn create_packet(world: &World, entity: Entity) -> Box { + create_mob_packet(world, entity, 47) +} + +fn serialize(world: &World, entity: Entity) -> EntityData { + let base = base_data(world, entity); + EntityData::Mooshroom(AnimalData { base }) +} diff --git a/server/src/entity/impls/animal/pig.rs b/server/src/entity/impls/animal/pig.rs new file mode 100644 index 000000000..f542459fa --- /dev/null +++ b/server/src/entity/impls/animal/pig.rs @@ -0,0 +1,31 @@ +use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::lazy::LazyUpdateExt; +use crate::physics::PhysicsBuilder; +use feather_core::entity::{AnimalData, EntityData}; +use feather_core::Packet; +use specs::world::{EntitiesRes, LazyBuilder}; +use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; + +#[derive(Default)] +pub struct PigComponent; + +impl Component for PigComponent { + type Storage = NullStorage; +} + +pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { + lazy.spawn_entity(entities) + .with(PigComponent) + .with(PhysicsBuilder::for_living().bbox(0.9, 0.9, 0.9).build()) + .with(PacketCreatorComponent(&create_packet)) + .with(SerializerComponent(&serialize)) +} + +fn create_packet(world: &World, entity: Entity) -> Box { + create_mob_packet(world, entity, 51) +} + +fn serialize(world: &World, entity: Entity) -> EntityData { + let base = base_data(world, entity); + EntityData::Pig(AnimalData { base }) +} diff --git a/server/src/entity/impls/animal/rabbit.rs b/server/src/entity/impls/animal/rabbit.rs new file mode 100644 index 000000000..dc0f49a68 --- /dev/null +++ b/server/src/entity/impls/animal/rabbit.rs @@ -0,0 +1,31 @@ +use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::lazy::LazyUpdateExt; +use crate::physics::PhysicsBuilder; +use feather_core::entity::{AnimalData, EntityData}; +use feather_core::Packet; +use specs::world::{EntitiesRes, LazyBuilder}; +use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; + +#[derive(Default)] +pub struct RabbitComponent; + +impl Component for RabbitComponent { + type Storage = NullStorage; +} + +pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { + lazy.spawn_entity(entities) + .with(RabbitComponent) + .with(PhysicsBuilder::for_living().bbox(0.4, 0.5, 0.4).build()) + .with(PacketCreatorComponent(&create_packet)) + .with(SerializerComponent(&serialize)) +} + +fn create_packet(world: &World, entity: Entity) -> Box { + create_mob_packet(world, entity, 56) +} + +fn serialize(world: &World, entity: Entity) -> EntityData { + let base = base_data(world, entity); + EntityData::Rabbit(AnimalData { base }) +} diff --git a/server/src/entity/impls/animal/sheep.rs b/server/src/entity/impls/animal/sheep.rs new file mode 100644 index 000000000..9d0bab55f --- /dev/null +++ b/server/src/entity/impls/animal/sheep.rs @@ -0,0 +1,31 @@ +use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::lazy::LazyUpdateExt; +use crate::physics::PhysicsBuilder; +use feather_core::entity::{AnimalData, EntityData}; +use feather_core::Packet; +use specs::world::{EntitiesRes, LazyBuilder}; +use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; + +#[derive(Default)] +pub struct SheepComponent; + +impl Component for SheepComponent { + type Storage = NullStorage; +} + +pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { + lazy.spawn_entity(entities) + .with(SheepComponent) + .with(PhysicsBuilder::for_living().bbox(0.9, 1.3, 0.9).build()) + .with(PacketCreatorComponent(&create_packet)) + .with(SerializerComponent(&serialize)) +} + +fn create_packet(world: &World, entity: Entity) -> Box { + create_mob_packet(world, entity, 58) +} + +fn serialize(world: &World, entity: Entity) -> EntityData { + let base = base_data(world, entity); + EntityData::Sheep(AnimalData { base }) +} diff --git a/server/src/entity/impls/animal/squid.rs b/server/src/entity/impls/animal/squid.rs new file mode 100644 index 000000000..b97705cad --- /dev/null +++ b/server/src/entity/impls/animal/squid.rs @@ -0,0 +1,31 @@ +use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::lazy::LazyUpdateExt; +use crate::physics::PhysicsBuilder; +use feather_core::entity::{AnimalData, EntityData}; +use feather_core::Packet; +use specs::world::{EntitiesRes, LazyBuilder}; +use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; + +#[derive(Default)] +pub struct SquidComponent; + +impl Component for SquidComponent { + type Storage = NullStorage; +} + +pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { + lazy.spawn_entity(entities) + .with(SquidComponent) + .with(PhysicsBuilder::for_living().bbox(0.8, 0.8, 0.8).build()) + .with(PacketCreatorComponent(&create_packet)) + .with(SerializerComponent(&serialize)) +} + +fn create_packet(world: &World, entity: Entity) -> Box { + create_mob_packet(world, entity, 70) +} + +fn serialize(world: &World, entity: Entity) -> EntityData { + let base = base_data(world, entity); + EntityData::Squid(AnimalData { base }) +} diff --git a/server/src/entity/impls/mod.rs b/server/src/entity/impls/mod.rs index 10ff43dca..e7ab09214 100644 --- a/server/src/entity/impls/mod.rs +++ b/server/src/entity/impls/mod.rs @@ -22,5 +22,67 @@ pub mod arrow; pub mod falling_block; pub mod item; + +mod animal; +pub use animal::*; + +use crate::entity::{degrees_to_stops, NamedComponent, PositionComponent, VelocityComponent}; +use crate::util::protocol_velocity; +use feather_core::entity::BaseEntityData; +use feather_core::network::packet::implementation::SpawnMob; +use feather_core::Packet; +use specs::{Entity, World, WorldExt}; +use uuid::Uuid; + #[cfg(test)] pub mod test; + +/// Returns a `Spawn Mob` packet with the given entity type ID. +pub fn create_mob_packet(world: &World, entity: Entity, type_id: i32) -> Box { + let entity_id = entity.id() as i32; + let entity_uuid = world + .read_component::() + .get(entity) + .map(|named| named.uuid) + .unwrap_or(Uuid::new_v4()); + + let positions = world.read_component::(); + let position = positions.get(entity).copied().unwrap_or_default(); + let velocities = world.read_component::(); + let velocity = velocities.get(entity).copied().unwrap_or_default(); + + let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocity.0); + + let packet = SpawnMob { + entity_id, + entity_uuid, + ty: type_id, + 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), + head_pitch: degrees_to_stops(position.current.pitch), // FIXME: is this correct? + velocity_x, + velocity_y, + velocity_z, + }; + + Box::new(packet) +} + +/// Creates a `BaseEntityData` for the given entity. +pub fn base_data(world: &World, entity: Entity) -> BaseEntityData { + let position = world + .read_component::() + .get(entity) + .copied() + .unwrap_or_default(); + let velocity = world + .read_component::() + .get(entity) + .copied() + .unwrap_or_default(); + + BaseEntityData::new(position.current, velocity.0) +} diff --git a/server/src/physics/component.rs b/server/src/physics/component.rs index f989072de..f10a707ec 100644 --- a/server/src/physics/component.rs +++ b/server/src/physics/component.rs @@ -59,6 +59,12 @@ impl PhysicsBuilder { Self::default() } + /// Returns a `PhysicsBuilder` with defaults set to the settings + /// for living entities. + pub fn for_living() -> Self { + Self::new().drag(0.98).gravity(-0.08).slip_multiplier(0.6) + } + pub fn bbox(mut self, x: f64, y: f64, z: f64) -> Self { self.comp.bbox = bbox(x, y, z); self From 3cdda36e70933268194d9224cc11481bfe3dc4aa Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 19 Oct 2019 15:08:38 -0600 Subject: [PATCH 014/647] Implement loading of animals; fix various problems with spawning --- codegen/src/lib.rs | 6 ++- core/src/entitymeta.rs | 22 ++++++-- core/src/network/packet/implementation.rs | 2 +- core/src/network/packet/mod.rs | 5 ++ server/src/entity/chunk.rs | 56 ++++++++++++++++++++- server/src/entity/impls/animal/chicken.rs | 24 ++++++++- server/src/entity/impls/animal/cow.rs | 24 ++++++++- server/src/entity/impls/animal/donkey.rs | 24 ++++++++- server/src/entity/impls/animal/horse.rs | 24 ++++++++- server/src/entity/impls/animal/llama.rs | 24 ++++++++- server/src/entity/impls/animal/mooshroom.rs | 24 ++++++++- server/src/entity/impls/animal/pig.rs | 24 ++++++++- server/src/entity/impls/animal/rabbit.rs | 24 ++++++++- server/src/entity/impls/animal/sheep.rs | 24 ++++++++- server/src/entity/impls/animal/squid.rs | 24 ++++++++- server/src/entity/impls/mod.rs | 9 +++- server/src/entity/metadata.rs | 4 ++ server/src/lib.rs | 25 +++++++++ 18 files changed, 351 insertions(+), 18 deletions(-) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 6916c78d4..ab6f68331 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -53,6 +53,7 @@ enum PacketParameterType { Uuid, Nbt, Slot, + EntityMetadata, } lazy_static! { @@ -77,6 +78,7 @@ lazy_static! { m.insert("Uuid", PacketParameterType::Uuid); m.insert("NbtTag", PacketParameterType::Nbt); m.insert("Slot", PacketParameterType::Slot); + m.insert("EntityMetadata", PacketParameterType::EntityMetadata); m }; @@ -102,6 +104,7 @@ lazy_static! { m.insert("uuid", PacketParameterType::Uuid); m.insert("nbt", PacketParameterType::Nbt); m.insert("slot", PacketParameterType::Slot); + m.insert("metadata", PacketParameterType::EntityMetadata); // I wrote them in the wrong order, so I'm just going to reverse // the map. @@ -165,6 +168,7 @@ pub fn derive_packet(_item: TokenStream) -> TokenStream { PacketParameterType::Uuid, PacketParameterType::Nbt, PacketParameterType::Slot, + PacketParameterType::EntityMetadata, ] .contains(parameter_type) }; @@ -193,7 +197,7 @@ pub fn derive_packet(_item: TokenStream) -> TokenStream { let r = quote! { impl Packet for #ident { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, mut buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { #(#read_code)* Ok(()) } diff --git a/core/src/entitymeta.rs b/core/src/entitymeta.rs index 440158109..d7ff0321b 100644 --- a/core/src/entitymeta.rs +++ b/core/src/entitymeta.rs @@ -6,8 +6,8 @@ use crate::bytes_ext::{BytesMutExt, TryGetError}; use crate::network::mctypes::McTypeWrite; use crate::world::BlockPosition; use crate::Slot; -use bytes::BytesMut; use hashbrown::HashMap; +use std::io::Cursor; use uuid::Uuid; type OptUuid = Option; @@ -147,7 +147,10 @@ pub trait EntityMetaIo { fn try_get_metadata(&mut self) -> Result; } -impl EntityMetaIo for BytesMut { +impl EntityMetaIo for B +where + B: BytesMutExt + McTypeWrite, +{ fn push_metadata(&mut self, meta: &EntityMetadata) { for (index, entry) in meta.values.iter() { self.push_u8(*index); @@ -163,7 +166,20 @@ impl EntityMetaIo for BytesMut { } } -fn write_entry_to_buf(entry: &MetaEntry, buf: &mut BytesMut) { +impl EntityMetaIo for &mut Cursor<&[u8]> { + fn push_metadata(&mut self, _meta: &EntityMetadata) { + unimplemented!() + } + + fn try_get_metadata(&mut self) -> Result { + unimplemented!() + } +} + +fn write_entry_to_buf(entry: &MetaEntry, buf: &mut B) +where + B: BytesMutExt + McTypeWrite, +{ match entry { MetaEntry::Byte(x) => buf.push_i8(*x), MetaEntry::VarInt(x) => { diff --git a/core/src/network/packet/implementation.rs b/core/src/network/packet/implementation.rs index 6b9cabb13..ed9261cfd 100644 --- a/core/src/network/packet/implementation.rs +++ b/core/src/network/packet/implementation.rs @@ -860,7 +860,7 @@ pub struct SpawnMob { pub velocity_x: i16, pub velocity_y: i16, pub velocity_z: i16, - // TODO metadata + pub meta: EntityMetadata, } #[derive(Default, AsAny, new, Packet, Clone)] diff --git a/core/src/network/packet/mod.rs b/core/src/network/packet/mod.rs index 5075763bc..d8bcc87ec 100644 --- a/core/src/network/packet/mod.rs +++ b/core/src/network/packet/mod.rs @@ -441,6 +441,11 @@ lazy_static! { PacketType::SpawnObject, ); + m.insert( + PacketId(0x03, PacketDirection::Clientbound, PacketStage::Play), + PacketType::SpawnMob, + ); + m.insert( PacketId(0x06, PacketDirection::Clientbound, PacketStage::Play), PacketType::AnimationClientbound, diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index 154b24f57..9623bc6ce 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -3,7 +3,10 @@ //! entity queries and packet broadcasting. use crate::chunk_logic::ChunkLoadEvent; -use crate::entity::{arrow, item, EntityDestroyEvent, EntitySpawnEvent, PositionComponent}; +use crate::entity::{ + arrow, chicken, cow, donkey, horse, item, llama, mooshroom, pig, rabbit, sheep, squid, + EntityDestroyEvent, EntitySpawnEvent, PositionComponent, +}; use crate::TickCount; use feather_core::entity::EntityData; use feather_core::world::ChunkPosition; @@ -211,11 +214,60 @@ impl<'a> System<'a> for EntityChunkLoadSystem { debug!("Error while loading arrow entity"); } } + EntityData::Cow(data) => { + if cow::create_from_data(&lazy, &entities, data).is_none() { + debug!("Error while loading cow entity") + } + } + EntityData::Pig(data) => { + if pig::create_from_data(&lazy, &entities, data).is_none() { + debug!("Error while loading pig entity") + } + } + EntityData::Chicken(data) => { + if chicken::create_from_data(&lazy, &entities, data).is_none() { + debug!("Error while loading chicken entity") + } + } + EntityData::Sheep(data) => { + if sheep::create_from_data(&lazy, &entities, data).is_none() { + debug!("Error while loading sheep entity") + } + } + EntityData::Horse(data) => { + if horse::create_from_data(&lazy, &entities, data).is_none() { + debug!("Error while loading horse entity") + } + } + EntityData::Llama(data) => { + if llama::create_from_data(&lazy, &entities, data).is_none() { + debug!("Error while loading llama entity") + } + } + EntityData::Mooshroom(data) => { + if mooshroom::create_from_data(&lazy, &entities, data).is_none() { + debug!("Error while loading mooshroom entity") + } + } + EntityData::Rabbit(data) => { + if rabbit::create_from_data(&lazy, &entities, data).is_none() { + debug!("Error while loading rabbit entity") + } + } + EntityData::Squid(data) => { + if squid::create_from_data(&lazy, &entities, data).is_none() { + debug!("Error while loading squid entity") + } + } + EntityData::Donkey(data) => { + if donkey::create_from_data(&lazy, &entities, data).is_none() { + debug!("Error while loading donkey entity") + } + } // TODO: Spawn remaining entity types here. EntityData::Unknown => { trace!("Chunk {:?} contains an unknown entity type", event.pos); } - _ => todo!(), } } } diff --git a/server/src/entity/impls/animal/chicken.rs b/server/src/entity/impls/animal/chicken.rs index 0fd5f4b33..a92e4b3d3 100644 --- a/server/src/entity/impls/animal/chicken.rs +++ b/server/src/entity/impls/animal/chicken.rs @@ -1,4 +1,7 @@ -use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::entity::{ + base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, + VelocityComponent, +}; use crate::lazy::LazyUpdateExt; use crate::physics::PhysicsBuilder; use feather_core::entity::{AnimalData, EntityData}; @@ -21,6 +24,25 @@ pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilde .with(SerializerComponent(&serialize)) } +pub fn create_from_data( + lazy: &LazyUpdate, + entities: &EntitiesRes, + data: &AnimalData, +) -> Option { + let position = data.base.read_position()?; + let velocity = data.base.read_velocity()?; + + Some( + create(lazy, entities) + .with(PositionComponent { + current: position, + previous: position, + }) + .with(VelocityComponent(velocity)) + .build(), + ) +} + fn create_packet(world: &World, entity: Entity) -> Box { create_mob_packet(world, entity, 7) } diff --git a/server/src/entity/impls/animal/cow.rs b/server/src/entity/impls/animal/cow.rs index 7b61488d9..e962c8daa 100644 --- a/server/src/entity/impls/animal/cow.rs +++ b/server/src/entity/impls/animal/cow.rs @@ -1,4 +1,7 @@ -use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::entity::{ + base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, + VelocityComponent, +}; use crate::lazy::LazyUpdateExt; use crate::physics::PhysicsBuilder; use feather_core::entity::{AnimalData, EntityData}; @@ -21,6 +24,25 @@ pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilde .with(SerializerComponent(&serialize)) } +pub fn create_from_data( + lazy: &LazyUpdate, + entities: &EntitiesRes, + data: &AnimalData, +) -> Option { + let position = data.base.read_position()?; + let velocity = data.base.read_velocity()?; + + Some( + create(lazy, entities) + .with(PositionComponent { + current: position, + previous: position, + }) + .with(VelocityComponent(velocity)) + .build(), + ) +} + fn create_packet(world: &World, entity: Entity) -> Box { create_mob_packet(world, entity, 9) } diff --git a/server/src/entity/impls/animal/donkey.rs b/server/src/entity/impls/animal/donkey.rs index 8dee661ae..5f3888751 100644 --- a/server/src/entity/impls/animal/donkey.rs +++ b/server/src/entity/impls/animal/donkey.rs @@ -1,4 +1,7 @@ -use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::entity::{ + base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, + VelocityComponent, +}; use crate::lazy::LazyUpdateExt; use crate::physics::PhysicsBuilder; use feather_core::entity::{AnimalData, EntityData}; @@ -25,6 +28,25 @@ pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilde .with(SerializerComponent(&serialize)) } +pub fn create_from_data( + lazy: &LazyUpdate, + entities: &EntitiesRes, + data: &AnimalData, +) -> Option { + let position = data.base.read_position()?; + let velocity = data.base.read_velocity()?; + + Some( + create(lazy, entities) + .with(PositionComponent { + current: position, + previous: position, + }) + .with(VelocityComponent(velocity)) + .build(), + ) +} + fn create_packet(world: &World, entity: Entity) -> Box { create_mob_packet(world, entity, 11) } diff --git a/server/src/entity/impls/animal/horse.rs b/server/src/entity/impls/animal/horse.rs index f9fb46faa..db61ddf73 100644 --- a/server/src/entity/impls/animal/horse.rs +++ b/server/src/entity/impls/animal/horse.rs @@ -1,4 +1,7 @@ -use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::entity::{ + base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, + VelocityComponent, +}; use crate::lazy::LazyUpdateExt; use crate::physics::PhysicsBuilder; use feather_core::entity::{AnimalData, EntityData}; @@ -25,6 +28,25 @@ pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilde .with(SerializerComponent(&serialize)) } +pub fn create_from_data( + lazy: &LazyUpdate, + entities: &EntitiesRes, + data: &AnimalData, +) -> Option { + let position = data.base.read_position()?; + let velocity = data.base.read_velocity()?; + + Some( + create(lazy, entities) + .with(PositionComponent { + current: position, + previous: position, + }) + .with(VelocityComponent(velocity)) + .build(), + ) +} + fn create_packet(world: &World, entity: Entity) -> Box { create_mob_packet(world, entity, 29) } diff --git a/server/src/entity/impls/animal/llama.rs b/server/src/entity/impls/animal/llama.rs index eda30bef9..29f06e68a 100644 --- a/server/src/entity/impls/animal/llama.rs +++ b/server/src/entity/impls/animal/llama.rs @@ -1,4 +1,7 @@ -use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::entity::{ + base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, + VelocityComponent, +}; use crate::lazy::LazyUpdateExt; use crate::physics::PhysicsBuilder; use feather_core::entity::{AnimalData, EntityData}; @@ -21,6 +24,25 @@ pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilde .with(SerializerComponent(&serialize)) } +pub fn create_from_data( + lazy: &LazyUpdate, + entities: &EntitiesRes, + data: &AnimalData, +) -> Option { + let position = data.base.read_position()?; + let velocity = data.base.read_velocity()?; + + Some( + create(lazy, entities) + .with(PositionComponent { + current: position, + previous: position, + }) + .with(VelocityComponent(velocity)) + .build(), + ) +} + fn create_packet(world: &World, entity: Entity) -> Box { create_mob_packet(world, entity, 36) } diff --git a/server/src/entity/impls/animal/mooshroom.rs b/server/src/entity/impls/animal/mooshroom.rs index 765fee7ed..830a147bf 100644 --- a/server/src/entity/impls/animal/mooshroom.rs +++ b/server/src/entity/impls/animal/mooshroom.rs @@ -1,4 +1,7 @@ -use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::entity::{ + base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, + VelocityComponent, +}; use crate::lazy::LazyUpdateExt; use crate::physics::PhysicsBuilder; use feather_core::entity::{AnimalData, EntityData}; @@ -25,6 +28,25 @@ fn create_packet(world: &World, entity: Entity) -> Box { create_mob_packet(world, entity, 47) } +pub fn create_from_data( + lazy: &LazyUpdate, + entities: &EntitiesRes, + data: &AnimalData, +) -> Option { + let position = data.base.read_position()?; + let velocity = data.base.read_velocity()?; + + Some( + create(lazy, entities) + .with(PositionComponent { + current: position, + previous: position, + }) + .with(VelocityComponent(velocity)) + .build(), + ) +} + fn serialize(world: &World, entity: Entity) -> EntityData { let base = base_data(world, entity); EntityData::Mooshroom(AnimalData { base }) diff --git a/server/src/entity/impls/animal/pig.rs b/server/src/entity/impls/animal/pig.rs index f542459fa..cfa88d29d 100644 --- a/server/src/entity/impls/animal/pig.rs +++ b/server/src/entity/impls/animal/pig.rs @@ -1,4 +1,7 @@ -use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::entity::{ + base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, + VelocityComponent, +}; use crate::lazy::LazyUpdateExt; use crate::physics::PhysicsBuilder; use feather_core::entity::{AnimalData, EntityData}; @@ -21,6 +24,25 @@ pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilde .with(SerializerComponent(&serialize)) } +pub fn create_from_data( + lazy: &LazyUpdate, + entities: &EntitiesRes, + data: &AnimalData, +) -> Option { + let position = data.base.read_position()?; + let velocity = data.base.read_velocity()?; + + Some( + create(lazy, entities) + .with(PositionComponent { + current: position, + previous: position, + }) + .with(VelocityComponent(velocity)) + .build(), + ) +} + fn create_packet(world: &World, entity: Entity) -> Box { create_mob_packet(world, entity, 51) } diff --git a/server/src/entity/impls/animal/rabbit.rs b/server/src/entity/impls/animal/rabbit.rs index dc0f49a68..b247c2f83 100644 --- a/server/src/entity/impls/animal/rabbit.rs +++ b/server/src/entity/impls/animal/rabbit.rs @@ -1,4 +1,7 @@ -use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::entity::{ + base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, + VelocityComponent, +}; use crate::lazy::LazyUpdateExt; use crate::physics::PhysicsBuilder; use feather_core::entity::{AnimalData, EntityData}; @@ -21,6 +24,25 @@ pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilde .with(SerializerComponent(&serialize)) } +pub fn create_from_data( + lazy: &LazyUpdate, + entities: &EntitiesRes, + data: &AnimalData, +) -> Option { + let position = data.base.read_position()?; + let velocity = data.base.read_velocity()?; + + Some( + create(lazy, entities) + .with(PositionComponent { + current: position, + previous: position, + }) + .with(VelocityComponent(velocity)) + .build(), + ) +} + fn create_packet(world: &World, entity: Entity) -> Box { create_mob_packet(world, entity, 56) } diff --git a/server/src/entity/impls/animal/sheep.rs b/server/src/entity/impls/animal/sheep.rs index 9d0bab55f..934456885 100644 --- a/server/src/entity/impls/animal/sheep.rs +++ b/server/src/entity/impls/animal/sheep.rs @@ -1,4 +1,7 @@ -use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::entity::{ + base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, + VelocityComponent, +}; use crate::lazy::LazyUpdateExt; use crate::physics::PhysicsBuilder; use feather_core::entity::{AnimalData, EntityData}; @@ -21,6 +24,25 @@ pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilde .with(SerializerComponent(&serialize)) } +pub fn create_from_data( + lazy: &LazyUpdate, + entities: &EntitiesRes, + data: &AnimalData, +) -> Option { + let position = data.base.read_position()?; + let velocity = data.base.read_velocity()?; + + Some( + create(lazy, entities) + .with(PositionComponent { + current: position, + previous: position, + }) + .with(VelocityComponent(velocity)) + .build(), + ) +} + fn create_packet(world: &World, entity: Entity) -> Box { create_mob_packet(world, entity, 58) } diff --git a/server/src/entity/impls/animal/squid.rs b/server/src/entity/impls/animal/squid.rs index b97705cad..76bd309d7 100644 --- a/server/src/entity/impls/animal/squid.rs +++ b/server/src/entity/impls/animal/squid.rs @@ -1,4 +1,7 @@ -use crate::entity::{base_data, create_mob_packet, PacketCreatorComponent, SerializerComponent}; +use crate::entity::{ + base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, + VelocityComponent, +}; use crate::lazy::LazyUpdateExt; use crate::physics::PhysicsBuilder; use feather_core::entity::{AnimalData, EntityData}; @@ -21,6 +24,25 @@ pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilde .with(SerializerComponent(&serialize)) } +pub fn create_from_data( + lazy: &LazyUpdate, + entities: &EntitiesRes, + data: &AnimalData, +) -> Option { + let position = data.base.read_position()?; + let velocity = data.base.read_velocity()?; + + Some( + create(lazy, entities) + .with(PositionComponent { + current: position, + previous: position, + }) + .with(VelocityComponent(velocity)) + .build(), + ) +} + fn create_packet(world: &World, entity: Entity) -> Box { create_mob_packet(world, entity, 70) } diff --git a/server/src/entity/impls/mod.rs b/server/src/entity/impls/mod.rs index e7ab09214..435d0065d 100644 --- a/server/src/entity/impls/mod.rs +++ b/server/src/entity/impls/mod.rs @@ -26,7 +26,10 @@ pub mod item; mod animal; pub use animal::*; -use crate::entity::{degrees_to_stops, NamedComponent, PositionComponent, VelocityComponent}; +use crate::entity::{ + degrees_to_stops, metadata::EMPTY_METADATA, Metadata, NamedComponent, PositionComponent, + VelocityComponent, +}; use crate::util::protocol_velocity; use feather_core::entity::BaseEntityData; use feather_core::network::packet::implementation::SpawnMob; @@ -53,6 +56,9 @@ pub fn create_mob_packet(world: &World, entity: Entity, type_id: i32) -> Box(); + let metadata = metadatas.get(entity).unwrap_or(&EMPTY_METADATA); + let packet = SpawnMob { entity_id, entity_uuid, @@ -66,6 +72,7 @@ pub fn create_mob_packet(world: &World, entity: Entity, type_id: i32) -> Box( let mut dispatcher = dispatcher.build(); dispatcher.setup(&mut world); + register_components(&mut world); + (world, dispatcher) } +fn register_components(world: &mut World) { + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); + world.register::(); +} + fn init_log(config: &Config) { let level = match config.log.level.as_str() { "trace" => log::Level::Trace, From bea3671b71a7301a5b576f21d52db7f94ac9fed9 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 19 Oct 2019 15:54:49 -0600 Subject: [PATCH 015/647] Fix clippy warnings --- server/src/entity/chunk.rs | 1 + server/src/entity/impls/animal/donkey.rs | 2 +- server/src/entity/impls/animal/horse.rs | 2 +- server/src/entity/impls/mod.rs | 2 +- server/src/physics/entity.rs | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs index 9623bc6ce..22490f8c9 100644 --- a/server/src/entity/chunk.rs +++ b/server/src/entity/chunk.rs @@ -198,6 +198,7 @@ impl<'a> System<'a> for EntityChunkLoadSystem { Read<'a, TickCount>, ); + #[allow(clippy::cognitive_complexity)] // Big match statement. Necessary fn run(&mut self, data: Self::SystemData) { let (load_events, lazy, entities, tick) = data; diff --git a/server/src/entity/impls/animal/donkey.rs b/server/src/entity/impls/animal/donkey.rs index 5f3888751..c7918ffcb 100644 --- a/server/src/entity/impls/animal/donkey.rs +++ b/server/src/entity/impls/animal/donkey.rs @@ -21,7 +21,7 @@ pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilde .with(DonkeyComponent) .with( PhysicsBuilder::for_living() - .bbox(1.3964844, 1.6, 1.3964844) + .bbox(1.396_484_4, 1.6, 1.396_484_4) .build(), ) .with(PacketCreatorComponent(&create_packet)) diff --git a/server/src/entity/impls/animal/horse.rs b/server/src/entity/impls/animal/horse.rs index db61ddf73..7b5d6f82f 100644 --- a/server/src/entity/impls/animal/horse.rs +++ b/server/src/entity/impls/animal/horse.rs @@ -21,7 +21,7 @@ pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilde .with(HorseComponent) .with( PhysicsBuilder::for_living() - .bbox(1.3964844, 1.6, 1.3964844) + .bbox(1.396_484_4, 1.6, 1.396_484_4) .build(), ) .with(PacketCreatorComponent(&create_packet)) diff --git a/server/src/entity/impls/mod.rs b/server/src/entity/impls/mod.rs index 435d0065d..f46a2d94d 100644 --- a/server/src/entity/impls/mod.rs +++ b/server/src/entity/impls/mod.rs @@ -47,7 +47,7 @@ pub fn create_mob_packet(world: &World, entity: Entity, type_id: i32) -> Box() .get(entity) .map(|named| named.uuid) - .unwrap_or(Uuid::new_v4()); + .unwrap_or_else(Uuid::new_v4); let positions = world.read_component::(); let position = positions.get(entity).copied().unwrap_or_default(); diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index 3218c0766..4e1b931bd 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -62,7 +62,7 @@ impl<'a> System<'a> for EntityPhysicsSystem { ) .join() { - let mut velocity = restrict_velocity.get_unchecked().clone(); + let mut velocity = *restrict_velocity.get_unchecked(); let mut pending_position = position.current + velocity.0; From f3270aff331479362b47d803dd66acebd946f24b Mon Sep 17 00:00:00 2001 From: Caelum van Ispelen Date: Wed, 23 Oct 2019 17:10:27 -0600 Subject: [PATCH 016/647] Block light calculation (#153) * Implement BlockExt::light_emission() * Implement getters and setters for chunk block and sky light * Start work on lighting algorithm implementation * Implement flood fill function * Initial lighting system implementation; implement light creation handling * Implement light removal handling * Implement opaque block creation handling --- Cargo.lock | 7 + blocks/src/lib.rs | 60 +++++ core/src/world/mod.rs | 27 +- server/Cargo.toml | 1 + server/src/lib.rs | 2 + server/src/lighting.rs | 514 +++++++++++++++++++++++++++++++++++++ server/src/physics/math.rs | 5 +- server/src/systems.rs | 2 + 8 files changed, 613 insertions(+), 5 deletions(-) create mode 100644 server/src/lighting.rs diff --git a/Cargo.lock b/Cargo.lock index fe183b46b..4a94d58bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,6 +90,11 @@ dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "as-slice" version = "0.1.0" @@ -632,6 +637,7 @@ dependencies = [ name = "feather-server" version = "0.5.0" dependencies = [ + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2832,6 +2838,7 @@ dependencies = [ "checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" "checksum as-slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "293dac66b274fab06f95e7efb05ec439a6b70136081ea522d270bc351ae5bb27" "checksum atom 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3c86699c3f02778ec07158376991c8f783dd1f2f95c579ffaf0738dc984b2fe2" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" diff --git a/blocks/src/lib.rs b/blocks/src/lib.rs index 5a21dab49..4f893625a 100644 --- a/blocks/src/lib.rs +++ b/blocks/src/lib.rs @@ -67,6 +67,13 @@ pub trait BlockExt { /// Returns whether this block is "solid." fn is_solid(&self) -> bool; + + /// Returns whether this block is opaque; i.e., whether + /// light will be stopped by this block. + fn is_opaque(&self) -> bool; + + /// Returns the light level emitted by this block. + fn light_emission(&self) -> u8; } impl BlockExt for Block { @@ -193,6 +200,59 @@ impl BlockExt for Block { _ => true, } } + + fn is_opaque(&self) -> bool { + if !self.is_solid() { + return false; + } + + // TODO + match self { + Block::Air | Block::Glass | Block::GlassPane(_) | Block::IronBars(_) => false, + _ => true, + } + } + + fn light_emission(&self) -> u8 { + match self { + Block::Beacon + | Block::EndGateway + | Block::EndPortal + | Block::Fire(_) + | Block::Glowstone + | Block::JackOLantern(_) + | Block::Lava(_) + | Block::RedstoneLamp(RedstoneLampData { lit: true }) + | Block::SeaLantern + | Block::SeaPickle(SeaPickleData { + waterlogged: true, + pickles: 4, + }) + | Block::Conduit(_) => 15, + Block::EndRod(_) | Block::Torch => 14, + Block::Furnace(_) => 13, + Block::SeaPickle(SeaPickleData { + waterlogged: true, + pickles: 3, + }) => 12, + Block::NetherPortal(_) => 11, + Block::SeaPickle(SeaPickleData { + waterlogged: true, + pickles: 2, + }) => 9, + Block::EnderChest(_) | Block::RedstoneTorch(_) => 7, + Block::SeaPickle(SeaPickleData { + waterlogged: true, + pickles: 1, + }) => 6, + Block::MagmaBlock => 3, + Block::BrewingStand(_) + | Block::BrownMushroom + | Block::DragonEgg + | Block::EndPortalFrame(_) => 1, + _ => 0, + } + } } /// Creates the internal ID -> native ID diff --git a/core/src/world/mod.rs b/core/src/world/mod.rs index 907b0c218..48cc94d98 100644 --- a/core/src/world/mod.rs +++ b/core/src/world/mod.rs @@ -219,7 +219,7 @@ impl Display for ChunkPosition { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Hash32, Default, new)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Hash32, Default)] pub struct BlockPosition { pub x: i32, pub y: i32, @@ -227,6 +227,10 @@ pub struct BlockPosition { } impl BlockPosition { + pub const fn new(x: i32, y: i32, z: i32) -> Self { + Self { x, y, z } + } + pub fn chunk_pos(&self) -> ChunkPosition { ChunkPosition::new(self.x >> 4, self.z >> 4) } @@ -234,6 +238,11 @@ impl BlockPosition { pub fn world_pos(&self) -> Position { position!(f64::from(self.x), f64::from(self.y), f64::from(self.z)) } + + /// Returns the Manhattan distance from this position to another. + pub fn manhattan_distance(self, other: BlockPosition) -> i32 { + (self.x - other.x).abs() + (self.y - other.y).abs() + (self.z - other.z).abs() + } } impl Add for BlockPosition { @@ -270,10 +279,20 @@ impl ChunkMap { /// If the chunk is not loaded, `None` will be returned. pub fn chunk_at(&self, pos: ChunkPosition) -> Option<&Chunk> { if let Some(chunk) = self.chunk_map.get(&pos) { - return Some(chunk); + Some(chunk) + } else { + None } + } - None + /// Retrieves the chunk at the specified location. + /// If the chunk is not loaded, `None` will be returned. + pub fn chunk_at_mut(&mut self, pos: ChunkPosition) -> Option<&mut Chunk> { + if let Some(chunk) = self.chunk_map.get_mut(&pos) { + Some(chunk) + } else { + None + } } /// Retrieves the block at the specified @@ -347,7 +366,7 @@ impl Default for ChunkMap { } } -fn chunk_relative_pos(block_pos: BlockPosition) -> (usize, usize, usize) { +pub fn chunk_relative_pos(block_pos: BlockPosition) -> (usize, usize, usize) { ( block_pos.x as usize & 0xf, block_pos.y as usize, diff --git a/server/Cargo.toml b/server/Cargo.toml index 1071f1790..7c76bc6f4 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -65,6 +65,7 @@ tokio-executor = "=0.2.0-alpha.6" futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } humantime-serde = "0.1" ctrlc = "3.1" +arrayvec = "0.5" [dev-dependencies] criterion = "0.3.0" diff --git a/server/src/lib.rs b/server/src/lib.rs index f7942747f..fa64af739 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -84,6 +84,7 @@ pub mod entity; pub mod io; pub mod joinhandler; pub mod lazy; +pub mod lighting; pub mod network; pub mod physics; pub mod player; @@ -387,6 +388,7 @@ fn init_world<'a, 'b>( player::init_logic(&mut dispatcher); chunk_logic::init_logic(&mut dispatcher); time::init_logic(&mut dispatcher); + lighting::init_logic(&mut dispatcher); dispatcher.add_barrier(); diff --git a/server/src/lighting.rs b/server/src/lighting.rs new file mode 100644 index 000000000..f862b2fdb --- /dev/null +++ b/server/src/lighting.rs @@ -0,0 +1,514 @@ +//! Calculation of block and sky light. +//! +//! # Algorithms: block light +//! For block light calculation, we define four types of block +//! updates for which to perform lighting: +//! +//! * Creation of a light-emitting block. We simply propagate +//! the light update using flood fill. +//! +//! * Removal of a light-emitting block. We first perform flood fill +//! and set any blocks which were previously affected by this block's +//! light to 0. Then, we recalculate lighting for light sources within +//! a range of 30 blocks based on algorithm #1. +//! +//! * Creation of an opaque, non-emitting block. We first set the created +//! block to air temporarily. We then query for nearby lights +//! within a range of 15 (the maximum distance travelled by light) and perform +//! algorithm #2 on them. Finally, we set the created block back to the correct +//! value and perform algorithm #1 on all lights. +//! +//! * Removal of an opaque, non-emitting block. In this case, +//! we set the new air block's light to the highest value of an +//! adjacent block minus 1. We then perform algorithm #1 on this new block. +//! +//! Each algorithm is implemented in a separate function, and `LightingSystem` +//! determines which to use based on the values of the block update event. +//! +//! If we are recalculating light for an entire chunk, e.g. when a chunk is generated, +//! we first zero out light, then find all light sources in the chunk and perform +//! algorithm #1 on them as if they had just been placed. + +use crate::blocks::BlockUpdateEvent; +use crate::chunk_logic::ChunkLoadEvent; +use crate::physics::chunks_within_distance; +use crate::systems::LIGHTING; +use arrayvec::ArrayVec; +use failure::_core::marker::PhantomData; +use feather_blocks::{Block, BlockExt}; +use feather_core::prelude::ChunkMap; +use feather_core::world::chunk_relative_pos; +use feather_core::{BlockPosition, Chunk, ChunkPosition}; +use hashbrown::HashSet; +use multimap::MultiMap; +use shrev::{EventChannel, ReaderId}; +use smallvec::SmallVec; +use specs::{DispatcherBuilder, Read, System, Write}; +use std::collections::VecDeque; + +const MAX_TRAVEL_DISTANCE: u8 = 15; + +/// Lighter context, used to cache things during +/// a lighting iteration. +struct Context<'a> { + /// Reference to the current cached chunk. + /// This is used to avoid repetitive hashmap + /// accesses in the chunk map when groups + /// of clustered blocks are queried for. + current_chunk: *mut Chunk, + /// Chunk map. Raw pointers are used to bypass the borrow + /// checker, since `current_chunk` refers to the chunk map, + /// which isn't allowed. + chunk_map: *mut ChunkMap, + _phantom: PhantomData<&'a ()>, +} + +impl<'a> Context<'a> { + fn new(chunk_map: &'a mut ChunkMap, start_chunk: ChunkPosition) -> Option { + let chunk_map = chunk_map as *mut ChunkMap; + + // Safety: `chunk_map` is a valid pointer + // made from a mutable reference. + // It has not been modified since. + let current_chunk = unsafe { (*chunk_map).chunk_at_mut(start_chunk)? as *mut Chunk }; + + Some(Self { + current_chunk, + chunk_map, + _phantom: PhantomData, + }) + } + + fn chunk_at_mut(&mut self, pos: ChunkPosition) -> Option<&'a mut Chunk> { + if pos == (unsafe { &*self.current_chunk }).position() { + Some(unsafe { &mut *self.current_chunk }) + } else { + // Safety: While `self.current_chunk` refers to the chunk map, + // it is never accessed between mutations of the chunk + // map itself, since `Context` holds a unique reference to the + // map and never mutates it. + self.current_chunk = unsafe { (*self.chunk_map).chunk_at_mut(pos)? }; + Some(unsafe { &mut *self.current_chunk }) + } + } + + fn block_light_at(&mut self, pos: BlockPosition) -> u8 { + match self.chunk_at_mut(pos.chunk_pos()) { + Some(chunk) => { + let (x, y, z) = chunk_relative_pos(pos); + chunk.block_light_at(x, y, z) + } + None => 0, + } + } + + fn set_block_light_at(&mut self, pos: BlockPosition, value: u8) { + if let Some(chunk) = self.chunk_at_mut(pos.chunk_pos()) { + let (x, y, z) = chunk_relative_pos(pos); + chunk.set_block_light_at(x, y, z, value); + } + } + + fn block_at(&mut self, pos: BlockPosition) -> Block { + match self.chunk_at_mut(pos.chunk_pos()) { + Some(chunk) => { + let (x, y, z) = chunk_relative_pos(pos); + chunk.block_at(x, y, z) + } + None => Block::Air, + } + } + + fn set_block_at(&mut self, pos: BlockPosition, block: Block) { + if let Some(chunk) = self.chunk_at_mut(pos.chunk_pos()) { + let (x, y, z) = chunk_relative_pos(pos); + chunk.set_block_at(x, y, z, block); + } + } +} + +/// Contains a map storing light sources for each chunk. +/// This is used to accelerate light calculation. +#[derive(Default)] +pub struct ChunkLights(MultiMap); + +impl ChunkLights { + fn lights_within_distance(&self, pos: BlockPosition, dist: u8) -> SmallVec<[BlockPosition; 9]> { + let dist_f64 = f64::from(dist); + let chunks = + chunks_within_distance(pos.world_pos(), glm::vec3(dist_f64, dist_f64, dist_f64)); + + chunks + .into_iter() + .map(|chunk| { + self.0 + .get_vec(&chunk) + .map(|vec| vec.as_slice()) + .unwrap_or(&[]) + .iter() + }) + .flatten() + .copied() + .collect() + } +} + +/// System for handling all lighting tasks. +#[derive(Default)] +pub struct LightingSystem { + update_reader: Option>, + load_reader: Option>, +} + +impl<'a> System<'a> for LightingSystem { + type SystemData = ( + Write<'a, ChunkMap>, + Write<'a, ChunkLights>, + Read<'a, EventChannel>, + Read<'a, EventChannel>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (mut chunk_map, mut chunk_lights, load_events, update_events) = data; + + // Update `ChunkLights` with newly loaded chunks + for load in load_events.read(self.load_reader.as_mut().unwrap()) { + // Find all lights within this chunk. + if let Some(chunk) = chunk_map.chunk_at(load.pos) { + let lights = find_lights_in_chunk(chunk); + lights + .into_iter() + .for_each(|light| chunk_lights.0.insert(load.pos, light)); + } + } + + // Perform lighting updates. + for event in update_events.read(self.update_reader.as_mut().unwrap()) { + let mut ctx = match Context::new(&mut chunk_map, event.pos.chunk_pos()) { + Some(ctx) => ctx, + None => continue, // Unloaded chunk + }; + + // Determine which algorithm to use. + if event.old_block.light_emission() < event.new_block.light_emission() { + ctx.set_block_light_at(event.pos, event.new_block.light_emission()); + emitting_creation(&mut ctx, event.pos); + } else if event.new_block.light_emission() == 0 && event.old_block.light_emission() > 0 + { + ctx.set_block_light_at(event.pos, 0); + emitting_removal(&mut ctx, &chunk_lights, event.pos, event.old_block); + } else if event.old_block.is_opaque() && !event.new_block.is_opaque() { + opaque_non_emitting_removal(&mut ctx, event.pos); + } else { + opaque_non_emitting_creation(&mut ctx, &chunk_lights, event.pos, event.new_block); + } + + // Update `ChunkLights`. + if event.old_block.light_emission() != event.new_block.light_emission() { + if event.new_block.light_emission() == 0 { + chunk_lights + .0 + .get_vec_mut(&event.pos.chunk_pos()) + .unwrap() + .retain(|pos| *pos != event.pos); + } else if event.old_block.light_emission() == 0 { + chunk_lights.0.insert(event.pos.chunk_pos(), event.pos); + } + } + } + } + + setup_impl!(update_reader, load_reader); +} + +pub fn init_logic(dispatcher: &mut DispatcherBuilder) { + dispatcher.add(LightingSystem::default(), LIGHTING, &[]); +} + +fn find_lights_in_chunk(chunk: &Chunk) -> Vec { + let mut res = vec![]; + + for x in 0..16 { + for y in 0..256 { + for z in 0..16 { + let block = chunk.block_at(x, y, z); + + let emission = block.light_emission(); + if emission > 0 { + res.push(BlockPosition::new(x as i32, y as i32, z as i32)); + } + } + } + } + + res +} + +/// Algorithm #1, as described in the module-level docs. +fn emitting_creation(context: &mut Context, position: BlockPosition) { + let emission = context.block_light_at(position); + // Perform flood fill starting from `position`. + // For each block, set the light value to the maximum light + // value of any adjacent block minus 1. + flood_fill(context, position, emission, |ctx, pos| { + let light = light_value_for_block(ctx, pos); + ctx.set_block_light_at(pos, light); + }); +} + +/// Algorithm #2, as described in the module-level docs. +fn emitting_removal( + context: &mut Context, + chunk_lights: &ChunkLights, + position: BlockPosition, + old_block: Block, +) { + // Perform flood fill and set all blocks affected by the old light to 0 light. + flood_fill(context, position, old_block.light_emission(), |ctx, pos| { + ctx.set_block_light_at(pos, 0); + }); + + // For all lights which could have affected the blocks we just set to 0, + // recalculate lighting using algorithm #1. + let nearby_lights = chunk_lights.lights_within_distance(position, MAX_TRAVEL_DISTANCE * 2); + + nearby_lights.into_iter().for_each(|light| { + if light != position { + emitting_creation(context, light); + } + }); +} + +/// Algorithm #3, as described in the module-level docs. +fn opaque_non_emitting_creation( + context: &mut Context, + chunk_lights: &ChunkLights, + position: BlockPosition, + new_block: Block, +) { + // Re-calculate all lights that could have affected this block. + // We ensure that all areas are correctly set to dark by first + // faking that the block was never created. + context.set_block_at(position, Block::Air); + + let nearby_lights = chunk_lights.lights_within_distance(position, MAX_TRAVEL_DISTANCE); + + nearby_lights.iter().for_each(|light| { + let block = context.block_at(*light); + emitting_removal(context, chunk_lights, *light, block); + }); + + // Set block back to correct value. + context.set_block_at(position, new_block); + + // Recalculate nearby lights. + nearby_lights.iter().for_each(|light| { + emitting_creation(context, *light); + }); +} + +/// Algorithm #4, as described in the module-level docs. +fn opaque_non_emitting_removal(context: &mut Context, position: BlockPosition) { + let value = light_value_for_block(context, position); + + context.set_block_light_at(position, value); + + // Propagate new light value for this block, as if it were a new light source. + if value > 0 { + emitting_creation(context, position); + } +} + +/// Returns the light value for the block at `position`, +/// equivalent to the maximum light value of an adjacent block +/// minus 1. +fn light_value_for_block(context: &mut Context, position: BlockPosition) -> u8 { + // Find highest light value of 6 adjacent blocks. + let adjacent = adjacent_blocks(position); + let mut value = adjacent + .into_iter() + .map(|pos| context.block_light_at(pos)) + .max() + .unwrap(); + + if value > 0 { + value -= 1; + } + + value +} + +/// Performs flood fill starting at `start` and travelling up +/// to `max_dist` blocks. +/// +/// For each block iterated over, the provided closure will be invoked. +/// No block will be iterated more than once. +fn flood_fill(context: &mut Context, start: BlockPosition, max_dist: u8, mut func: F) +where + F: FnMut(&mut Context, BlockPosition), +{ + // Don't iterate over same block more than once + let mut touched = HashSet::with_capacity(64); + touched.insert(start); + + // We use a queue-based algorithm rather than a recursive + // one. + let mut queue = VecDeque::with_capacity(64); + + queue.push_back(start); + + let mut finished = false; + + while let Some(pos) = queue.pop_front() { + if finished { + break; + } + + let blocks = adjacent_blocks(pos); + + blocks.into_iter().for_each(|pos| { + if pos.manhattan_distance(start) > max_dist as i32 { + // Finished + finished = true; + return; + } + + // Skip if we already went over this block + if !touched.insert(pos) { + return; + } + + let block = context.block_at(pos); + if block.is_opaque() { + return; // Stop iterating + } + + // Call closure + func(context, pos); + + // Add block to queue + queue.push_back(pos); + }); + } +} + +/// Returns the up to six adjacent blocks to a given block position. +fn adjacent_blocks(to: BlockPosition) -> ArrayVec<[BlockPosition; 6]> { + let offsets = [ + (-1, 0, 0), + (1, 0, 0), + (0, -1, 0), + (0, 1, 0), + (0, 0, -1), + (0, 0, 1), + ]; + offsets + .iter() + .map(|(x, y, z)| BlockPosition::new(to.x + *x, to.y + *y, to.z + *z)) + .filter(|pos| pos.y >= 0 && pos.y <= 256) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_context() { + let mut chunk_map = ChunkMap::new(); + + let pos = ChunkPosition::new(0, 0); + chunk_map.set_chunk_at(pos, Chunk::new(pos)); + let pos2 = ChunkPosition::new(0, 1); + chunk_map.set_chunk_at(pos2, Chunk::new(pos2)); + + let mut ctx = Context::new(&mut chunk_map, pos).unwrap(); + + assert_eq!(ctx.chunk_at_mut(pos).unwrap().position(), pos); + assert_eq!(ctx.chunk_at_mut(pos2).unwrap().position(), pos2); + assert_eq!(ctx.chunk_at_mut(pos).unwrap().position(), pos); + } + + #[test] + fn test_emitting_creation() { + let mut chunk_map = chunk_map(); + let mut ctx = Context::new(&mut chunk_map, ChunkPosition::new(0, 0)).unwrap(); + + let pos = BlockPosition::new(0, 100, 0); + ctx.set_block_at(pos, Block::Glowstone); + ctx.set_block_light_at(pos, Block::Glowstone.light_emission()); + + emitting_creation(&mut ctx, pos); + + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 99, 0)), 14); + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 99, 1)), 13); + } + + #[test] + fn test_opaque_non_emitting_removal() { + let mut chunk_map = chunk_map(); + let mut ctx = Context::new(&mut chunk_map, ChunkPosition::new(0, 0)).unwrap(); + + ctx.set_block_light_at(BlockPosition::new(0, 0, 0), 10); + ctx.set_block_light_at(BlockPosition::new(0, 2, 0), 9); + ctx.set_block_light_at(BlockPosition::new(1, 1, 0), 8); + ctx.set_block_light_at(BlockPosition::new(-1, 1, 0), 11); + ctx.set_block_light_at(BlockPosition::new(0, 1, 1), 0); + ctx.set_block_light_at(BlockPosition::new(0, 1, -1), 12); + ctx.set_block_light_at(BlockPosition::new(0, 1, 0), 15); + + opaque_non_emitting_removal(&mut ctx, BlockPosition::new(0, 1, 0)); + + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 1, 0)), 11); + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 1, 1)), 10); + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 1, 2)), 9); + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 1, 3)), 8); + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 1, 4)), 7); + // ... + } + + #[test] + fn test_flood_fill() { + let mut chunk_map = chunk_map(); + let mut ctx = Context::new(&mut chunk_map, ChunkPosition::new(0, 0)).unwrap(); + + let mut count = 0; + + flood_fill(&mut ctx, BlockPosition::new(100, 100, 100), 1, |_, _| { + count += 1 + }); + + assert_eq!(count, 6); + } + + #[test] + fn test_chunk_lights() { + let mut chunk_lights = ChunkLights::default(); + chunk_lights + .0 + .insert(ChunkPosition::new(0, 0), BlockPosition::new(0, 0, 0)); + chunk_lights + .0 + .insert(ChunkPosition::new(1, 0), BlockPosition::new(16, 0, 0)); + + assert_eq!( + chunk_lights + .lights_within_distance(BlockPosition::new(0, 0, 0), 16) + .as_slice(), + &[BlockPosition::new(0, 0, 0), BlockPosition::new(16, 0, 0)] + ); + } + + fn chunk_map() -> ChunkMap { + let mut chunk_map = ChunkMap::new(); + + for x in -1..=1 { + for z in -1..=1 { + let pos = ChunkPosition::new(x, z); + chunk_map.set_chunk_at(pos, Chunk::new(pos)); + } + } + + chunk_map + } +} diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index 11f599df0..18dcfc116 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -530,7 +530,10 @@ pub fn block_isometry(pos: BlockPosition) -> Isometry3 { /// of a position. /// /// The Y coordinate of `distance` is ignored. -fn chunks_within_distance(mut pos: Position, mut distance: DVec3) -> SmallVec<[ChunkPosition; 9]> { +pub fn chunks_within_distance( + mut pos: Position, + mut distance: DVec3, +) -> SmallVec<[ChunkPosition; 9]> { assert!(distance.x >= 0.0); assert!(distance.z >= 0.0); diff --git a/server/src/systems.rs b/server/src/systems.rs index a216a294b..b5d6155c4 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -66,3 +66,5 @@ pub const TIME_INCREMENT: &str = "time_increment"; pub const TIME_SEND: &str = "time_send"; pub const BROADCASTER: &str = "broadcaster"; + +pub const LIGHTING: &str = "lighting"; From 526dc9abb8e58c764d9e1d8cca3508bfecbe2a45 Mon Sep 17 00:00:00 2001 From: Thijs Raymakers Date: Tue, 15 Oct 2019 14:07:48 +0200 Subject: [PATCH 017/647] Add proxy_mode configuration option --- server/config/feather.toml | 14 +++++++++----- server/src/config.rs | 14 +++++++++++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/server/config/feather.toml b/server/config/feather.toml index c7144080a..07f8c100e 100644 --- a/server/config/feather.toml +++ b/server/config/feather.toml @@ -8,10 +8,6 @@ # Compressing packets reduces bandwidth usage but increases CPU activity. compression_threshold = 256 -[proxy] -# IP forwarding using either "bungee" (BungeeCord/Waterfall/Travertine) or "velocity" (Velocity) -proxy_mode = "none" # Unimplemented - [server] online_mode = true motd = "A Feather server" @@ -55,4 +51,12 @@ generator = "default" # will be converted using a hash function. seed = "" # Interval at which to save modified chunks. -save_interval = "1min" \ No newline at end of file +save_interval = "1min" + +[proxy] +# Select the IP forwarding mode that is used by proxies like BungeeCord or Velocity. +# Valid values are +# - "None" - for usage without a proxy +# - "BungeeCord" - for BungeeCord/Waterfall/Travertine +# - "Velocity" - for Velocity style proxies (unimplemented) +proxy_mode = "None" diff --git a/server/src/config.rs b/server/src/config.rs index f8c1e0f00..ca725b9e5 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -34,7 +34,9 @@ pub struct IO { } #[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Proxy {} +pub struct Proxy { + pub proxy_mode: ProxyMode +} #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Server { @@ -88,10 +90,13 @@ pub fn load(input: String) -> Result { Ok(config) } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub enum ProxyMode { + #[serde(alias = "none")] None, - Bungee, + #[serde(alias = "bungeecord")] + BungeeCord, + #[serde(alias = "velocity")] Velocity, } @@ -133,5 +138,8 @@ mod tests { assert_eq!(world.generator, "default"); assert_eq!(world.seed, ""); assert_eq!(world.save_interval.as_millis(), 1000 * 60); + + let proxy = &config.proxy; + assert_eq!(proxy.proxy_mode, ProxyMode::None); } } From 51c0ae10dc235248a025fe9669073650dfa703a2 Mon Sep 17 00:00:00 2001 From: Thijs Raymakers Date: Wed, 16 Oct 2019 00:31:52 +0200 Subject: [PATCH 018/647] Use offbranch mojang-api-rs crate --- server/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Cargo.toml b/server/Cargo.toml index 7c76bc6f4..9fcec30c6 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -33,7 +33,7 @@ rand_xorshift = "0.2" rand-legacy = { path = "../util/rand-legacy" } bytes = "0.4" hashbrown = { version = "0.6", features = ["rayon"] } -mojang-api = { git = "https://github.com/caelunshun/mojang-api-rs", rev = "6525e910ad53953fa16028f0fce74b1a19855733" } +mojang-api = { git = "https://github.com/ThijsRay/mojang-api-rs", rev = "e63818486685e708f3f0a56e8a84d0b4c8aa231e" } multimap = "0.6" hematite-nbt = "0.4" specs = { version = "0.15", features = ["storage-event-control"] } From 778dfef2946ae2149c0bde54309f929b81092bbc Mon Sep 17 00:00:00 2001 From: Thijs Raymakers Date: Wed, 16 Oct 2019 00:33:05 +0200 Subject: [PATCH 019/647] Implement BungeeCord style IP forwarding --- server/src/config.rs | 2 +- server/src/io/initialhandler.rs | 290 +++++++++++++++++++++++++++++--- server/src/io/worker.rs | 10 +- 3 files changed, 274 insertions(+), 28 deletions(-) diff --git a/server/src/config.rs b/server/src/config.rs index ca725b9e5..a96b5587e 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -35,7 +35,7 @@ pub struct IO { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Proxy { - pub proxy_mode: ProxyMode + pub proxy_mode: ProxyMode, } #[derive(Serialize, Deserialize, Debug, Clone)] diff --git a/server/src/io/initialhandler.rs b/server/src/io/initialhandler.rs index 6c44f689d..ec4af97a6 100644 --- a/server/src/io/initialhandler.rs +++ b/server/src/io/initialhandler.rs @@ -14,6 +14,7 @@ //! speeding up the login process and making the latency calculation in //! the server list ping as low as possible. +use std::net::IpAddr; use std::sync::atomic::Ordering; use std::sync::Arc; @@ -29,8 +30,9 @@ use feather_core::network::packet::implementation::{ }; use feather_core::network::packet::{Packet, PacketStage, PacketType}; -use crate::config::Config; +use crate::config::{Config, ProxyMode}; use crate::{PlayerCount, PROTOCOL_VERSION, SERVER_VERSION}; +use mojang_api::ProfileProperty; /// The key used for symmetric encryption. pub type Key = [u8; 16]; @@ -62,13 +64,31 @@ pub enum Action { } /// The type returned for when a player has completed the login process. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct JoinResult { - pub username: String, + pub username: Option, pub uuid: Uuid, pub props: Vec, } +impl JoinResult { + fn with_username(username: String) -> Self { + let mut join_result = JoinResult::default(); + join_result.username = Some(username); + join_result + } +} + +impl Default for JoinResult { + fn default() -> Self { + JoinResult { + username: None, + uuid: Uuid::new_v4(), + props: vec![], + } + } +} + /// An initial handler for a connection. /// /// When a packet is received from the client this initial @@ -89,6 +109,7 @@ pub struct InitialHandler { /// If set to a value, indicates that encryption /// should be enabled with the given key. key: Option, + /// If set to a value, indicates that compression /// should be enabled with the given threshold. compression_threshold: Option, @@ -103,10 +124,6 @@ pub struct InitialHandler { /// The server's icon, if any was loaded. server_icon: Arc>, - /// The username of the player, sent - /// in Login Start. - username: Option, - /// The player info, set to `Some` once /// the initial handler is finished and /// the player should join. @@ -134,8 +151,6 @@ impl InitialHandler { player_count, server_icon, - username: None, - info: None, stage: Stage::AwaitHandshake, @@ -154,11 +169,16 @@ impl InitialHandler { if let Err(e) = _handle_packet(self, packet).await { // Disconnect disconnect_login(self, &format!("{}", e)); - info!( - "Player {} disconnected: {}", - self.username.as_ref().unwrap_or(&"unknown".to_string()), - e - ); + + if let Some(info) = &self.info { + info!( + "Player {} disconnected: {}", + info.username.as_ref().unwrap_or(&"unknown".to_string()), + e + ); + } else { + info!("Player unknown disconnected: {}", e); + } } } @@ -206,6 +226,17 @@ fn handle_handshake(ih: &mut InitialHandler, packet: &Handshake) -> Result<(), E return Err(Error::InvalidProtocol(packet.protocol_version)); } + // If the server has BungeeCord proxy mode enabled, extract the data that is submitted + // by BungeeCord if IP forwarding is enabled. + if ih.config.proxy.proxy_mode == ProxyMode::BungeeCord { + let bungeecord_data = extract_bungeecord_data(packet)?; + ih.info = Some(JoinResult { + username: None, + uuid: bungeecord_data.uuid, + props: bungeecord_data.properties, + }); + } + ih.action_queue.push(Action::SetStage(PacketStage::Login)); Stage::AwaitLoginStart } @@ -214,6 +245,65 @@ fn handle_handshake(ih: &mut InitialHandler, packet: &Handshake) -> Result<(), E Ok(()) } +/// Tries to extract the player information that is sent in the `server_address` field of a +/// Handshake packet that originates from a BungeeCord style proxy. This is used to enable IP +/// forwarding for BungeeCord style proxies. +/// +/// The server address field should have 4 parts if a client is connecting via BungeeCord. The field +/// has the following format: +/// +/// format!("{}\0{}\0{}\0{}", host, address, uuid, mojang_response); +/// +/// | Variable | Definition | +/// |-----------------|-----------------------------------------------------| +/// | Host | The IP address of the BungeeCord instance | +/// | Address | The IP address of the connecting client | +/// | UUID | The UUID that is associated to the clients account | +/// | Mojang response | A JSON formatted version of the `properties` field +/// in [Mojangs response](https://wiki.vg/Protocol_Encryption#Server) | +fn extract_bungeecord_data(packet: &Handshake) -> Result { + let bungee_information: Vec<&str> = packet.server_address.split('\0').collect(); + Ok(BungeeCordData::from_vec(&bungee_information)?) +} + +#[derive(PartialEq, Debug)] +struct BungeeCordData { + host: IpAddr, + client: IpAddr, + uuid: Uuid, + properties: Vec, +} + +impl BungeeCordData { + pub fn from_vec(data: &[&str]) -> Result { + if data.len() != 4 { + return Err(Error::BungeeSpecMismatch("Incorrect length".to_string())); + } + + let host = data + .get(0) + .unwrap() + .parse::() + .map_err(|e| Error::BungeeSpecMismatch(e.to_string()))?; + let client = data + .get(1) + .unwrap() + .parse::() + .map_err(|e| Error::BungeeSpecMismatch(e.to_string()))?; + let uuid = Uuid::parse_str(*data.get(2).unwrap()) + .map_err(|e| Error::BungeeSpecMismatch(e.to_string()))?; + let properties = serde_json::from_str(data.get(3).unwrap()) + .map_err(|e| Error::BungeeSpecMismatch(e.to_string()))?; + + Ok(BungeeCordData { + host, + client, + uuid, + properties, + }) + } +} + fn handle_request(ih: &mut InitialHandler, packet: &Request) -> Result<(), Error> { check_stage(ih, Stage::AwaitRequest, packet.ty())?; let server_icon = (*ih.server_icon).clone().unwrap_or_default(); @@ -258,8 +348,6 @@ fn handle_ping(ih: &mut InitialHandler, packet: &Ping) -> Result<(), Error> { fn handle_login_start(ih: &mut InitialHandler, packet: &LoginStart) -> Result<(), Error> { check_stage(ih, Stage::AwaitLoginStart, packet.ty())?; - ih.username = Some(packet.username.clone()); - // If in online mode, encryption needs to be enabled, // and authentication needs to be performed. // If not in online mode, the login sequence is @@ -280,14 +368,26 @@ fn handle_login_start(ih: &mut InitialHandler, packet: &LoginStart) -> Result<() ); send_packet(ih, encryption_request); + let mut join_result = JoinResult::default(); + join_result.username = Some(packet.username.clone()); + ih.info = Some(JoinResult::with_username(packet.username.clone())); + ih.stage = Stage::AwaitEncryptionResponse; } else { - // Finished - set info and join - ih.info = Some(JoinResult { - username: ih.username.clone().unwrap(), - uuid: Uuid::new_v4(), - props: vec![], - }); + let username = packet.username.clone(); + + // Check if there is some info about the client available. This can be the case if the + // handshake is made by an IP forwarding proxy. + if ih.info.is_some() { + let mut info = ih.info.as_mut().unwrap(); + if info.username.is_none() { + info.username = Some(username); + } + } else { + // Finished - set info and join + ih.info = Some(JoinResult::with_username(username)) + } + finish(ih); } @@ -335,14 +435,18 @@ async fn handle_encryption_response( // Perform authentication let auth_result = mojang_api::server_auth( &mojang_api::server_hash("", ih.key.unwrap(), der.as_slice()), - ih.username.as_ref().unwrap(), + &ih.info + .clone() + .unwrap_or_default() + .username + .ok_or(Error::NoneError)?, ) .await; match auth_result { Ok(auth) => { let info = JoinResult { - username: auth.name, + username: Some(auth.name), uuid: auth.id, props: auth.properties, }; @@ -373,6 +477,7 @@ fn decrypt_using_rsa(data: &[u8], key: &RSAPrivateKey) -> Result, Error> /// * All other login processes have already run fn finish(ih: &mut InitialHandler) { assert!(ih.info.is_some()); + assert!(ih.info.as_ref().unwrap().username.is_some()); // Enable compression if necessary let compression_threshold = ih.config.io.compression_threshold; @@ -385,7 +490,7 @@ fn finish(ih: &mut InitialHandler) { // Send Login Success let login_success = LoginSuccess::new( info.uuid.to_hyphenated_ref().to_string(), - info.username.clone(), + info.username.as_ref().unwrap().to_string(), ); send_packet(ih, login_success); ih.action_queue.push(Action::SetStage(PacketStage::Play)); @@ -431,7 +536,7 @@ fn send_packet(ih: &mut InitialHandler, packet: P) { ih.action_queue.push(Action::SendPacket(Box::new(packet))); } -#[derive(Fail, Debug)] +#[derive(Fail, Debug, PartialEq)] enum Error { #[fail(display = "invalid packet type {:?} sent at stage {:?}", _0, _1)] InvalidPacket(PacketType, Stage), @@ -445,6 +550,15 @@ enum Error { BadSecretLength, #[fail(display = "authentication failure: {:?}", _0)] AuthenticationFailed(mojang_api::Error), + #[fail( + display = "received BungeeCord data does not match the specification: {}", + _0 + )] + BungeeSpecMismatch(String), + #[fail(display = "option that should not be None was None")] + /// An Error type than can be used as the error type of using the Try operator on Option + /// types. In rust-core, this is an unstable feature (issue #42327) + NoneError, } /// The stage of an initial handler. @@ -471,6 +585,130 @@ mod tests { use crate::PROTOCOL_VERSION; use super::*; + use mojang_api::ProfileProperty; + use std::net::Ipv4Addr; + + #[test] + fn extract_bungeecord_data_normal() { + let handshake = Handshake { + protocol_version: PROTOCOL_VERSION, + server_address: "192.168.1.87\0192.168.1.67\0905c7e4fb96b45139645d123225575e2\0[{\"name\":\"textures\",\"value\":\"textures_value\",\"signature\":\"textures_signature\"}]".to_string(), + server_port: 25565, + next_state: HandshakeState::Login, + }; + + assert_eq!( + extract_bungeecord_data(&handshake).unwrap(), + BungeeCordData { + host: IpAddr::V4(Ipv4Addr::new(192, 168, 1, 87)), + client: IpAddr::V4(Ipv4Addr::new(192, 168, 1, 67)), + uuid: Uuid::parse_str("905c7e4fb96b45139645d123225575e2").unwrap(), + properties: vec![ProfileProperty { + name: "textures".to_string(), + value: "textures_value".to_string(), + signature: "textures_signature".to_string(), + }], + } + ); + } + + #[test] + fn extract_bungeecord_data_too_short() { + let handshake = Handshake { + protocol_version: PROTOCOL_VERSION, + server_address: "192.168.1.87\0192.168.1.67\0905c7e4fb96b45139645d123225575e2" + .to_string(), + server_port: 25565, + next_state: HandshakeState::Login, + }; + + assert_eq!( + extract_bungeecord_data(&handshake).err().unwrap(), + Error::BungeeSpecMismatch("Incorrect length".to_string()) + ); + } + + #[test] + fn extract_bungeecord_data_too_long() { + let handshake = Handshake { + protocol_version: PROTOCOL_VERSION, + server_address: "192.168.1.87\0192.168.1.67\0905c7e4fb96b45139645d123225575e2\0a\0b" + .to_string(), + server_port: 25565, + next_state: HandshakeState::Login, + }; + + assert_eq!( + extract_bungeecord_data(&handshake).err().unwrap(), + Error::BungeeSpecMismatch("Incorrect length".to_string()) + ); + } + + #[test] + fn extract_bungeecord_data_invalid_host_ip() { + let handshake = Handshake { + protocol_version: PROTOCOL_VERSION, + server_address: "256.168.1.87\0192.168.1.67\0905c7e4fb96b45139645d123225575e2\0[{\"name\":\"textures\",\"value\":\"textures_value\",\"signature\":\"textures_signature\"}]".to_string(), + server_port: 25565, + next_state: HandshakeState::Login, + }; + + assert_eq!( + extract_bungeecord_data(&handshake).err().unwrap(), + Error::BungeeSpecMismatch("invalid IP address syntax".to_string()) + ); + } + + #[test] + fn extract_bungeecord_data_invalid_client_ip() { + let handshake = Handshake { + protocol_version: PROTOCOL_VERSION, + server_address: "192.168.1.87\0192.168.1.67.21\0905c7e4fb96b45139645d123225575e2\0[{\"name\":\"textures\",\"value\":\"textures_value\",\"signature\":\"textures_signature\"}]".to_string(), + server_port: 25565, + next_state: HandshakeState::Login, + }; + + let error = extract_bungeecord_data(&handshake).err().unwrap(); + if let Error::BungeeSpecMismatch(e) = error { + assert!(e.contains("IP")); + } else { + panic!(); + } + } + + #[test] + fn extract_bungeecord_data_invalid_uuid() { + let handshake = Handshake { + protocol_version: PROTOCOL_VERSION, + server_address: "192.168.1.87\0192.168.1.67\005c7e4fb9675e2\0[{\"name\":\"textures\",\"value\":\"textures_value\",\"signature\":\"textures_signature\"}]".to_string(), + server_port: 25565, + next_state: HandshakeState::Login, + }; + + let error = extract_bungeecord_data(&handshake).err().unwrap(); + if let Error::BungeeSpecMismatch(e) = error { + assert!(e.contains("invalid length")); + } else { + panic!(); + } + } + + #[test] + fn extract_bungeecord_data_invalid_properties() { + let handshake = Handshake { + protocol_version: PROTOCOL_VERSION, + server_address: "192.168.1.87\0192.168.1.67\0905c7e4fb96b45139645d123225575e2\0[{\"name\":\"textures\",\"value\":\"textures_value\",\"sinature\":\"textures_signature\"}]".to_string(), + server_port: 25565, + next_state: HandshakeState::Login, + }; + + let error = extract_bungeecord_data(&handshake).err().unwrap(); + if let Error::BungeeSpecMismatch(e) = error { + assert!(e.contains("missing field `signature`")); + } else { + panic!(); + } + } #[test] fn test_initial_handler_new() { diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index a312f6b39..40984563d 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -21,6 +21,14 @@ use tokio::codec::Framed; use tokio::net::TcpStream; use tokio::timer::Timeout; +#[derive(Fail, Debug)] +enum Error { + #[fail(display = "Option that should not be None was None")] + /// An Error type than can be used as the error type of using the Try operator on Option + /// types. In rust-core, this is an unstable feature (issue #42327) + NoneError, +} + /// Runs a worker task for the given client. pub async fn run_worker( stream: TcpStream, @@ -121,7 +129,7 @@ async fn _run_worker( Action::JoinGame(res) => { let info = NewClientInfo { ip, - username: res.username, + username: res.username.ok_or(Error::NoneError)?, profile: res.props, uuid: res.uuid, sender: tx_server_to_worker.clone(), From 4affa5ffad2f69a231a69c4edec30e5a29b61352 Mon Sep 17 00:00:00 2001 From: Thijs Raymakers Date: Wed, 16 Oct 2019 12:18:19 +0200 Subject: [PATCH 020/647] Rename NoneError to OptionIsNone --- server/src/io/worker.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index 40984563d..5ead52477 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -26,7 +26,7 @@ enum Error { #[fail(display = "Option that should not be None was None")] /// An Error type than can be used as the error type of using the Try operator on Option /// types. In rust-core, this is an unstable feature (issue #42327) - NoneError, + OptionIsNone, } /// Runs a worker task for the given client. @@ -129,7 +129,7 @@ async fn _run_worker( Action::JoinGame(res) => { let info = NewClientInfo { ip, - username: res.username.ok_or(Error::NoneError)?, + username: res.username.ok_or(Error::OptionIsNone)?, profile: res.props, uuid: res.uuid, sender: tx_server_to_worker.clone(), From 2b6fa59e1ef89ee5d32e6c219a6d180310f5681b Mon Sep 17 00:00:00 2001 From: Thijs Raymakers Date: Wed, 16 Oct 2019 12:42:16 +0200 Subject: [PATCH 021/647] Remove redundant clones --- server/src/io/initialhandler.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/server/src/io/initialhandler.rs b/server/src/io/initialhandler.rs index ec4af97a6..8d572b864 100644 --- a/server/src/io/initialhandler.rs +++ b/server/src/io/initialhandler.rs @@ -368,8 +368,6 @@ fn handle_login_start(ih: &mut InitialHandler, packet: &LoginStart) -> Result<() ); send_packet(ih, encryption_request); - let mut join_result = JoinResult::default(); - join_result.username = Some(packet.username.clone()); ih.info = Some(JoinResult::with_username(packet.username.clone())); ih.stage = Stage::AwaitEncryptionResponse; @@ -432,14 +430,18 @@ async fn handle_encryption_response( &BigInt::from_biguint(Plus, RSA_KEY.e().clone()).to_signed_bytes_be(), ); + // This unwrapping can be shorter with the use of .flatten() which will stabilize in Rust 1.40. + let username = ih + .info + .as_ref() + .map(|x| x.username.as_ref()) + .and_then(|x| x) + .ok_or(Error::OptionIsNone)?; + // Perform authentication let auth_result = mojang_api::server_auth( &mojang_api::server_hash("", ih.key.unwrap(), der.as_slice()), - &ih.info - .clone() - .unwrap_or_default() - .username - .ok_or(Error::NoneError)?, + username, ) .await; @@ -558,7 +560,7 @@ enum Error { #[fail(display = "option that should not be None was None")] /// An Error type than can be used as the error type of using the Try operator on Option /// types. In rust-core, this is an unstable feature (issue #42327) - NoneError, + OptionIsNone, } /// The stage of an initial handler. From c78a448dff137de87c16c7c5ab8df68b65054656 Mon Sep 17 00:00:00 2001 From: Thijs Raymakers Date: Wed, 16 Oct 2019 19:06:21 +0200 Subject: [PATCH 022/647] Revert back to regular mojang-api-rs crate --- Cargo.lock | 8 ++++---- server/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a94d58bc..1174b603d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -661,7 +661,7 @@ dependencies = [ "humantime-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mojang-api 0.3.0 (git+https://github.com/caelunshun/mojang-api-rs?rev=6525e910ad53953fa16028f0fce74b1a19855733)", + "mojang-api 0.4.0 (git+https://github.com/caelunshun/mojang-api-rs?rev=65bcc7691e2b2a537028b6c5aeb9de0621d5d461)", "multimap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "nalgebra 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", "nalgebra-glm 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1235,8 +1235,8 @@ dependencies = [ [[package]] name = "mojang-api" -version = "0.3.0" -source = "git+https://github.com/caelunshun/mojang-api-rs?rev=6525e910ad53953fa16028f0fce74b1a19855733#6525e910ad53953fa16028f0fce74b1a19855733" +version = "0.4.0" +source = "git+https://github.com/caelunshun/mojang-api-rs?rev=65bcc7691e2b2a537028b6c5aeb9de0621d5d461#65bcc7691e2b2a537028b6c5aeb9de0621d5d461" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2950,7 +2950,7 @@ dependencies = [ "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum mojang-api 0.3.0 (git+https://github.com/caelunshun/mojang-api-rs?rev=6525e910ad53953fa16028f0fce74b1a19855733)" = "" +"checksum mojang-api 0.4.0 (git+https://github.com/caelunshun/mojang-api-rs?rev=65bcc7691e2b2a537028b6c5aeb9de0621d5d461)" = "" "checksum mopa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915" "checksum multimap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de234f818d54830a7103b9be18ad0861d75aeb5e3c89759bc3f9a004cc39cfa3" "checksum nalgebra 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aaa9fddbc34c8c35dd2108515587b8ce0cab396f17977b8c738568e4edb521a2" diff --git a/server/Cargo.toml b/server/Cargo.toml index 9fcec30c6..1c85730e8 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -33,7 +33,7 @@ rand_xorshift = "0.2" rand-legacy = { path = "../util/rand-legacy" } bytes = "0.4" hashbrown = { version = "0.6", features = ["rayon"] } -mojang-api = { git = "https://github.com/ThijsRay/mojang-api-rs", rev = "e63818486685e708f3f0a56e8a84d0b4c8aa231e" } +mojang-api = { git = "https://github.com/caelunshun/mojang-api-rs", rev = "65bcc7691e2b2a537028b6c5aeb9de0621d5d461" } multimap = "0.6" hematite-nbt = "0.4" specs = { version = "0.15", features = ["storage-event-control"] } From 89a3b5bd87ad91062bd1596bdd1e362cd1dcb740 Mon Sep 17 00:00:00 2001 From: Thijs Raymakers Date: Fri, 18 Oct 2019 20:09:02 +0200 Subject: [PATCH 023/647] Use more idiomatic approach than if x.is_some() Based on the comment made on https://github.com/caelunshun/feather/pull/165/files/b8363f39db2ac05cf9baefd037f825b7a92d25ac#r336283743 --- server/src/io/initialhandler.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/src/io/initialhandler.rs b/server/src/io/initialhandler.rs index 8d572b864..f8a0a43a8 100644 --- a/server/src/io/initialhandler.rs +++ b/server/src/io/initialhandler.rs @@ -376,8 +376,7 @@ fn handle_login_start(ih: &mut InitialHandler, packet: &LoginStart) -> Result<() // Check if there is some info about the client available. This can be the case if the // handshake is made by an IP forwarding proxy. - if ih.info.is_some() { - let mut info = ih.info.as_mut().unwrap(); + if let Some(info) = ih.info.as_mut() { if info.username.is_none() { info.username = Some(username); } From 58fe7193e5628fb91ec0fd668c63a050d7516f35 Mon Sep 17 00:00:00 2001 From: Thijs Raymakers Date: Sun, 27 Oct 2019 14:26:33 +0100 Subject: [PATCH 024/647] Set BungeeCordData host & client IP type to String This fixes the issue mentioned in https://github.com/caelunshun/feather/pull/165#issuecomment-544109319 --- server/src/io/initialhandler.rs | 61 ++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/server/src/io/initialhandler.rs b/server/src/io/initialhandler.rs index f8a0a43a8..2fcd934bf 100644 --- a/server/src/io/initialhandler.rs +++ b/server/src/io/initialhandler.rs @@ -14,7 +14,6 @@ //! speeding up the login process and making the latency calculation in //! the server list ping as low as possible. -use std::net::IpAddr; use std::sync::atomic::Ordering; use std::sync::Arc; @@ -268,8 +267,8 @@ fn extract_bungeecord_data(packet: &Handshake) -> Result #[derive(PartialEq, Debug)] struct BungeeCordData { - host: IpAddr, - client: IpAddr, + host: String, + client: String, uuid: Uuid, properties: Vec, } @@ -280,16 +279,8 @@ impl BungeeCordData { return Err(Error::BungeeSpecMismatch("Incorrect length".to_string())); } - let host = data - .get(0) - .unwrap() - .parse::() - .map_err(|e| Error::BungeeSpecMismatch(e.to_string()))?; - let client = data - .get(1) - .unwrap() - .parse::() - .map_err(|e| Error::BungeeSpecMismatch(e.to_string()))?; + let host = data.get(0).unwrap().to_string(); + let client = data.get(1).unwrap().to_string(); let uuid = Uuid::parse_str(*data.get(2).unwrap()) .map_err(|e| Error::BungeeSpecMismatch(e.to_string()))?; let properties = serde_json::from_str(data.get(3).unwrap()) @@ -601,8 +592,8 @@ mod tests { assert_eq!( extract_bungeecord_data(&handshake).unwrap(), BungeeCordData { - host: IpAddr::V4(Ipv4Addr::new(192, 168, 1, 87)), - client: IpAddr::V4(Ipv4Addr::new(192, 168, 1, 67)), + host: "192.168.1.87".to_string(), + client: "192.168.1.67".to_string(), uuid: Uuid::parse_str("905c7e4fb96b45139645d123225575e2").unwrap(), properties: vec![ProfileProperty { name: "textures".to_string(), @@ -646,35 +637,51 @@ mod tests { } #[test] - fn extract_bungeecord_data_invalid_host_ip() { + fn extract_bungeecord_data_localhost_host_ip() { let handshake = Handshake { protocol_version: PROTOCOL_VERSION, - server_address: "256.168.1.87\0192.168.1.67\0905c7e4fb96b45139645d123225575e2\0[{\"name\":\"textures\",\"value\":\"textures_value\",\"signature\":\"textures_signature\"}]".to_string(), + server_address: "localhost\0192.168.1.67\0905c7e4fb96b45139645d123225575e2\0[{\"name\":\"textures\",\"value\":\"textures_value\",\"signature\":\"textures_signature\"}]".to_string(), server_port: 25565, next_state: HandshakeState::Login, }; assert_eq!( - extract_bungeecord_data(&handshake).err().unwrap(), - Error::BungeeSpecMismatch("invalid IP address syntax".to_string()) + extract_bungeecord_data(&handshake).unwrap(), + BungeeCordData { + host: "localhost".to_string(), + client: "192.168.1.67".to_string(), + uuid: Uuid::parse_str("905c7e4fb96b45139645d123225575e2").unwrap(), + properties: vec![ProfileProperty { + name: "textures".to_string(), + value: "textures_value".to_string(), + signature: "textures_signature".to_string(), + }], + } ); } #[test] - fn extract_bungeecord_data_invalid_client_ip() { + fn extract_bungeecord_data_localhost_client_ip() { let handshake = Handshake { protocol_version: PROTOCOL_VERSION, - server_address: "192.168.1.87\0192.168.1.67.21\0905c7e4fb96b45139645d123225575e2\0[{\"name\":\"textures\",\"value\":\"textures_value\",\"signature\":\"textures_signature\"}]".to_string(), + server_address: "192.168.1.87\0localhost\0905c7e4fb96b45139645d123225575e2\0[{\"name\":\"textures\",\"value\":\"textures_value\",\"signature\":\"textures_signature\"}]".to_string(), server_port: 25565, next_state: HandshakeState::Login, }; - let error = extract_bungeecord_data(&handshake).err().unwrap(); - if let Error::BungeeSpecMismatch(e) = error { - assert!(e.contains("IP")); - } else { - panic!(); - } + assert_eq!( + extract_bungeecord_data(&handshake).unwrap(), + BungeeCordData { + host: "192.168.1.87".to_string(), + client: "localhost".to_string(), + uuid: Uuid::parse_str("905c7e4fb96b45139645d123225575e2").unwrap(), + properties: vec![ProfileProperty { + name: "textures".to_string(), + value: "textures_value".to_string(), + signature: "textures_signature".to_string(), + }], + } + ); } #[test] From 4d174b448abe8b83c482f07869d877dd39a54f44 Mon Sep 17 00:00:00 2001 From: Thijs Raymakers Date: Wed, 16 Oct 2019 00:53:11 +0200 Subject: [PATCH 025/647] Update dependencies --- Cargo.lock | 523 +++++++++++++++++++++++----------------------- server/Cargo.toml | 3 +- 2 files changed, 264 insertions(+), 262 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1174b603d..81c38094b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,7 +36,7 @@ dependencies = [ [[package]] name = "ahash" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "const-random 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -84,10 +84,10 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -114,33 +114,33 @@ name = "atty" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "autocfg" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.38" +version = "0.3.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace-sys" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -153,7 +153,7 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -196,16 +196,15 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "c2-chacha" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -215,7 +214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cc" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -252,7 +251,7 @@ name = "chrono" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -265,7 +264,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -278,7 +277,7 @@ name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -296,7 +295,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "const-random-macro 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -304,7 +303,7 @@ name = "const-random-macro" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -314,7 +313,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -398,7 +397,7 @@ name = "crossbeam-epoch" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -431,7 +430,7 @@ dependencies = [ "bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -467,9 +466,9 @@ name = "derive-new" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -520,22 +519,22 @@ dependencies = [ [[package]] name = "failure" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure_derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -544,7 +543,7 @@ version = "0.5.0" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "feather-codegen 0.5.0", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -557,11 +556,11 @@ version = "0.5.0" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -575,14 +574,14 @@ dependencies = [ "cfb8 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "derive-new 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "feather-blocks 0.5.0", "feather-codegen 0.5.0", "feather-items 0.5.0", "flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", "hash32 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "hash32-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "hematite-nbt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -605,16 +604,16 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "derive_deref 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "simple_logger 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -639,7 +638,7 @@ version = "0.5.0" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -648,14 +647,14 @@ dependencies = [ "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "derive-new 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "derive_deref 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "feather-blocks 0.5.0", "feather-codegen 0.5.0", "feather-core 0.5.0", "feather-item-block 0.5.0", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "heapless 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "hematite-nbt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "humantime-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -666,7 +665,7 @@ dependencies = [ "nalgebra 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", "nalgebra-glm 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "ncollide3d 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint-dig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint-dig 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -674,7 +673,7 @@ dependencies = [ "rand-legacy 0.1.0", "rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rsa 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rsa 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "rsa-der 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", @@ -706,7 +705,7 @@ name = "flate2" version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -717,8 +716,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -749,7 +748,7 @@ name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -797,10 +796,10 @@ name = "futures-join-macro-preview" version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -821,10 +820,10 @@ name = "futures-select-macro-preview" version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -845,7 +844,7 @@ dependencies = [ "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -868,11 +867,11 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -886,8 +885,8 @@ dependencies = [ "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -916,11 +915,11 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ahash 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ahash 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -965,7 +964,7 @@ dependencies = [ [[package]] name = "http" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -979,7 +978,7 @@ version = "0.2.0-alpha.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1014,14 +1013,14 @@ dependencies = [ "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "h2 0.2.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", "http-body 0.2.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1056,19 +1055,19 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "iovec" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1086,10 +1085,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "js-sys" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1111,7 +1110,7 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.62" +version = "0.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1153,7 +1152,7 @@ name = "memchr" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1183,13 +1182,13 @@ name = "miniz-sys" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "miniz_oxide" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1202,9 +1201,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1217,8 +1216,8 @@ name = "mio-uds" version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1241,7 +1240,7 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.10.0-alpha.0 (git+https://github.com/seanmonstar/reqwest?rev=5b55aee1a9ddf785f82d9086c8befc50db268cb8)", + "reqwest 0.10.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1294,11 +1293,11 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.52 (registry+https://github.com/rust-lang/crates.io-index)", "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1312,7 +1311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "alga 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "downcast-rs 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "nalgebra 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1329,7 +1328,7 @@ version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1338,16 +1337,16 @@ name = "nix" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "nodrop" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1364,24 +1363,27 @@ name = "num-bigint" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-bigint-dig" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libm 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1389,7 +1391,7 @@ name = "num-complex" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1398,9 +1400,9 @@ name = "num-derive" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1408,7 +1410,7 @@ name = "num-integer" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1417,7 +1419,7 @@ name = "num-iter" version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1427,7 +1429,7 @@ name = "num-rational" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1445,7 +1447,7 @@ name = "num-traits" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1453,7 +1455,7 @@ name = "num_cpus" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1466,12 +1468,12 @@ name = "openssl" version = "0.10.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.52 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1481,12 +1483,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.50" +version = "0.9.52" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1513,7 +1515,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1526,7 +1528,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1534,10 +1536,10 @@ name = "paste-impl" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1556,20 +1558,20 @@ dependencies = [ [[package]] name = "pin-project" -version = "0.4.2" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "pin-project-internal 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-internal 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pin-project-internal" -version = "0.4.2" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1584,17 +1586,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ppv-lite86" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro-hack" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1620,7 +1622,7 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1652,7 +1654,7 @@ name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1661,7 +1663,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1672,8 +1674,8 @@ name = "rand" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1690,8 +1692,8 @@ name = "rand" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1709,7 +1711,7 @@ name = "rand_chacha" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1718,7 +1720,7 @@ name = "rand_chacha" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1740,7 +1742,7 @@ name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1772,7 +1774,7 @@ name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1784,7 +1786,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1795,7 +1797,7 @@ name = "rand_os" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1804,7 +1806,7 @@ name = "rand_pcg" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1906,19 +1908,19 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.10.0-alpha.0" -source = "git+https://github.com/seanmonstar/reqwest?rev=5b55aee1a9ddf785f82d9086c8befc50db268cb8#5b55aee1a9ddf785f82d9086c8befc50db268cb8" +version = "0.10.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "encoding_rs 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", "http-body 0.2.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.13.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-tls 0.4.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.29 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1932,9 +1934,9 @@ dependencies = [ "tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-futures 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.29 (registry+https://github.com/rust-lang/crates.io-index)", "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1945,19 +1947,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rsa" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint-dig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint-dig 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1983,7 +1985,7 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2015,7 +2017,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2053,9 +2055,9 @@ name = "serde_derive" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2064,7 +2066,7 @@ version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2089,8 +2091,8 @@ name = "shred" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "mopa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2164,7 +2166,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.2 (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)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2215,9 +2217,9 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2247,23 +2249,23 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "synstructure" -version = "0.10.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2272,7 +2274,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2308,7 +2310,7 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2340,7 +2342,7 @@ dependencies = [ "tokio-net 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-sync 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2370,7 +2372,7 @@ dependencies = [ "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-sync 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2395,7 +2397,7 @@ dependencies = [ "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2404,7 +2406,7 @@ version = "0.2.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2417,9 +2419,9 @@ dependencies = [ "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2429,7 +2431,7 @@ dependencies = [ "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-sync 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2489,28 +2491,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "tracing" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-attributes 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing-attributes 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tracing-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tracing-attributes" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tracing-core" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2641,25 +2643,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasm-bindgen" -version = "0.2.51" +version = "0.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.51" +version = "0.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2671,63 +2673,63 @@ dependencies = [ "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "futures-channel-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.29 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.51" +version = "0.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro-support 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.51" +version = "0.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.51" +version = "0.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasm-bindgen-webidl" -version = "0.2.51" +version = "0.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)", "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "web-sys" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.29 (registry+https://github.com/rust-lang/crates.io-index)", "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-webidl 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-webidl 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2810,20 +2812,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "zeroize" -version = "0.6.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "zeroize_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize_derive 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "zeroize_derive" -version = "0.1.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] @@ -2831,31 +2834,31 @@ dependencies = [ "checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" "checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" "checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" -"checksum ahash 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "b35dfc96a657c1842b4eb73180b65e37152d4b94d0eb5cb51708aee7826950b4" +"checksum ahash 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "2f00e10d4814aa20900e7948174384f79f1317f24f0ba7494e735111653fc330" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum alga 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d708cb68c7106ed1844de68f50f0157a7788c2909a6926fad5a87546ef6a4ff8" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" -"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" "checksum as-slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "293dac66b274fab06f95e7efb05ec439a6b70136081ea522d270bc351ae5bb27" "checksum atom 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3c86699c3f02778ec07158376991c8f783dd1f2f95c579ffaf0738dc984b2fe2" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" -"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" -"checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5" -"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +"checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" +"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" "checksum bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245" "checksum bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" +"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" "checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" -"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" +"checksum cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)" = "0213d356d3c4ea2c18c40b037c3be23cd639825c18f25ee670ac7813beeef99c" "checksum cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" "checksum cfb8 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1b310afa67a25a8d5189eacaf5b14418c8dc3d8bcc5755619d89cab87871260d" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" @@ -2888,8 +2891,8 @@ dependencies = [ "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum encoding_rs 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)" = "87240518927716f79692c2ed85bfe6e98196d18c6401ec75355760233a7e12e9" -"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" -"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" +"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" "checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" "checksum flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "e6234dd4468ae5d1e2dbb06fe2b058696fdc50a339c68a393aefbf00bc81e423" "checksum flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ad3c5233c9a940c8719031b423d7e6c16af66e031cb0420b0896f5245bf181d3" @@ -2911,16 +2914,16 @@ dependencies = [ "checksum futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "5ce968633c17e5f97936bd2797b6e38fb56cf16a7422319f7ec2e30d3c470e8d" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" -"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" +"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" "checksum h2 0.2.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0f107db1419ef8271686187b1a5d47c6431af4a7f4d98b495e7b7fc249bb0a78" "checksum hash32 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12d790435639c06a7b798af9e1e331ae245b7ef915b92f70a39b4cf8c00686af" "checksum hash32-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebc0efbd154a17cddc3616d83faef479c0076d871a2143c157b310cc7ca799a2" -"checksum hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6587d09be37fb98a11cb08b9000a3f592451c1b1b613ca69d949160e313a430a" +"checksum hashbrown 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3cd9867f119b19fecb08cd5c326ad4488d7a1da4bf75b4d95d71db742525aaab" "checksum heapless 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f339aa7d51777fc0af6aa7cbeb277dfc6e6c029cbdeda48d0fbb92c2337f0e69" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hematite-nbt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "98b407a33bb1715a4cf0276edfe8df52352c55b2a3703c5079adedf398b92932" "checksum hibitset 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "47e7292fd9f7fe89fa35c98048f2d0a69b79ed243604234d18f6f8a1aa6f408d" -"checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" +"checksum http 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "d7e06e336150b178206af098a055e3621e8336027e2b4d126bda0bc64824baaf" "checksum http-body 0.2.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1f3aef6f3de2bd8585f5b366f3f550b5774500b4764d00cf00f903c95749eec3" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" @@ -2928,14 +2931,14 @@ dependencies = [ "checksum hyper 0.13.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2d05aa523087ac0b9d8b93dd80d5d482a697308ed3b0dca7b0667511a7fa7cdc" "checksum hyper-tls 0.4.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "47cb3975f80cc809efe5dfcc52b73c9b281fde33f2df35a2e5f79f35e384ae7f" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a61202fbe46c4a951e9404a720a0180bcf3212c750d735cb5c4ba4dc551299f3" -"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" +"checksum indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2" +"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" -"checksum js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "2cc9a97d7cec30128fd8b28a7c1f9df1c001ceb9b441e2b755e24130a6b43c79" +"checksum js-sys 0.3.29 (registry+https://github.com/rust-lang/crates.io-index)" = "5061eb59a5afd4f6ff96dc565963e4e2737b915d070233cb26b88e3f58af41b4" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" +"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" "checksum libm 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" "checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" @@ -2946,7 +2949,7 @@ dependencies = [ "checksum mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "dd1d63acd1b78403cc0c325605908475dd9b9a3acbf65ed8bcab97e27014afcf" "checksum mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0ed03949aef72dbdf3116a383d7b38b4768e6f960528cd6a6044aa9ed68599" "checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" -"checksum miniz_oxide 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "304f66c19be2afa56530fa7c39796192eef38618da8d19df725ad7c6d6b2aaae" +"checksum miniz_oxide 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ab4b6e85c0d81267c29a95d600278b1e2272d8551e74e439982a34b2751a82b" "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" @@ -2959,10 +2962,10 @@ 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 nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" -"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" "checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a" -"checksum num-bigint-dig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3cd60678022301da54082fcc383647fc895cba2795f868c871d58d29c8922595" +"checksum num-bigint-dig 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7e8552a8edd6289764deab155204f86ccf3b0027e10f960a55d5a53deaf6688c" "checksum num-complex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fcb0cf31fb3ff77e6d2a6ebd6800df7fdcd106f2ad89113c9130bcd07f93dffc" "checksum num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0c8b15b261814f992e33760b1fca9fe8b693d8a65299f20c9901688636cfb746" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" @@ -2974,7 +2977,7 @@ dependencies = [ "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2f372b2b53ce10fb823a337aaa674e3a7d072b957c6264d0f4ff0bd86e657449" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)" = "2c42dcccb832556b5926bc9ae61e8775f2a61e725ab07ab3d1e7fcf8ae62c3b6" +"checksum openssl-sys 0.9.52 (registry+https://github.com/rust-lang/crates.io-index)" = "c977d08e1312e2f7e4b86f9ebaa0ed3b19d1daff75fae88bbb88108afbd801fc" "checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" @@ -2982,16 +2985,16 @@ dependencies = [ "checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" -"checksum pin-project 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d9156ea5979ae30ecc0460cd848738daf24cfb89eb11a41e0c369ba1f0e6aeb" -"checksum pin-project-internal 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1a375fffcd7bf53d8302fb95c1e2f3e0a1a92bd57edcab796f26f9e527c2f3da" +"checksum pin-project 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c5fce7042b4e4338a3f868e563fff394709c3ff62cf6908d407dd9e2caff96ed" +"checksum pin-project-internal 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7644b4721cc27235f667e735da8732f5b781c442157315674c0cb7f28b4cabf3" "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" "checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea" -"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" -"checksum proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "114cdf1f426eb7f550f01af5f53a33c0946156f6814aec939b3bd77e844f9a9d" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" "checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" "checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90cf5f418035b98e655e9cdb225047638296b862b42411c4e45bb88d700f7fc0" +"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" @@ -3023,13 +3026,13 @@ dependencies = [ "checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum reqwest 0.10.0-alpha.0 (git+https://github.com/seanmonstar/reqwest?rev=5b55aee1a9ddf785f82d9086c8befc50db268cb8)" = "" +"checksum reqwest 0.10.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d75dbf305ed1eb54d3c8564e3b746012166b40ec0841381df92b50a2052db71" "checksum rgb 0.8.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2089e4031214d129e201f8c3c8c2fe97cd7322478a0d1cdf78e7029b0042efdb" -"checksum rsa 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6ad8d3632f6745bb671c8637e2aa44015537c5e384789d2ea3235739301ed1e0" +"checksum rsa 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5108a8bbfb84fe77d829d77d5a89255dcd189dfe5c4de5a33d0a47f12808bb15" "checksum rsa-der 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1170c86c683547fa781a0e39e6e281ebaedd4515be8a806022984f427ea3d44d" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" +"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" "checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" "checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" @@ -3063,8 +3066,8 @@ dependencies = [ "checksum subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab3af2eb31c42e8f0ccf43548232556c42737e01a96db6e1777b0be108e79799" "checksum syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)" = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" -"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" +"checksum syn 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ebead3e516ca7fe682c71c3f235bf5b7d9e73268df8111c6edd9eb44091f2ebb" +"checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" @@ -3084,9 +3087,9 @@ dependencies = [ "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" -"checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" -"checksum tracing-attributes 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3ff978fd9c9afe2cc9c671e247713421c6406b3422305cbdce5de695d3ab4c3c" -"checksum tracing-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "528c8ebaaa16cdac34795180b046c031775b0d56402704d98c096788f33d646a" +"checksum tracing 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ff4e4f59e752cb3beb5b61c6d5e11191c7946231ba84faec2902c9efdd8691c5" +"checksum tracing-attributes 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a4263b12c3d3c403274493eb805966093b53214124796552d674ca1dd5d27c2b" +"checksum tracing-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bc913647c520c959b6d21e35ed8fa6984971deca9f0a2fcb8c51207e0c56af1d" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "checksum tuple_utils 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44834418e2c5b16f47bedf35c28e148db099187dd5feee6367fb2525863af4f1" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" @@ -3106,14 +3109,14 @@ dependencies = [ "checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" "checksum want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" "checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" -"checksum wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "cd34c5ba0d228317ce388e87724633c57edca3e7531feb4e25e35aaa07a656af" -"checksum wasm-bindgen-backend 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "927196b315c23eed2748442ba675a4c54a1a079d90d9bdc5ad16ce31cf90b15b" +"checksum wasm-bindgen 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)" = "637353fd57864c20f1968dc21680fe03985ca3a7ef6a5ce027777513bdecc282" +"checksum wasm-bindgen-backend 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)" = "c85481ca7d1aad8cf40e0140830b2197ce89184a80e54e307b55fd64d78ed63e" "checksum wasm-bindgen-futures 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)" = "83420b37346c311b9ed822af41ec2e82839bfe99867ec6c54e2da43b7538771c" -"checksum wasm-bindgen-macro 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "92c2442bf04d89792816650820c3fb407af8da987a9f10028d5317f5b04c2b4a" -"checksum wasm-bindgen-macro-support 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "9c075d27b7991c68ca0f77fe628c3513e64f8c477d422b859e03f28751b46fc5" -"checksum wasm-bindgen-shared 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "83d61fe986a7af038dd8b5ec660e5849cbd9f38e7492b9404cc48b2b4df731d1" -"checksum wasm-bindgen-webidl 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "9b979afb0535fe4749906a674082db1211de8aef466331d43232f63accb7c07c" -"checksum web-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "c84440699cd02ca23bed6f045ffb1497bc18a3c2628bd13e2093186faaaacf6b" +"checksum wasm-bindgen-macro 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)" = "9f627667b5f4f8bd923c93107b96907c60e7e8eb2636802499fce468c87e3689" +"checksum wasm-bindgen-macro-support 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)" = "a48f5147b0c049bc306d5b9e53c891056a1fd8c4e7311fffbce233e4f200d45e" +"checksum wasm-bindgen-shared 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)" = "1e272b0d31b78cdcaf5ad440d28276546d99b059a953e5afb387aefce66c3c5a" +"checksum wasm-bindgen-webidl 0.2.52 (registry+https://github.com/rust-lang/crates.io-index)" = "6965845db6189148d8b26387aee0bbf1c84f3da78f57ac543f364fc8ff7ab6e9" +"checksum web-sys 0.3.29 (registry+https://github.com/rust-lang/crates.io-index)" = "0a8b4b06314fd2ce36977e9487607ccff4030779129813f89d0e618710910146" "checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" @@ -3125,5 +3128,5 @@ dependencies = [ "checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" -"checksum zeroize 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e68403b858b6af538b11614e62dfe9ab2facba9f13a0cafb974855cfb495ec95" -"checksum zeroize_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3f07490820219949839d0027b965ffdd659d75be9220c00798762e36c6cd281" +"checksum zeroize 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4090487fa66630f7b166fba2bbb525e247a5449f41c468cc1d98f8ae6ac03120" +"checksum zeroize_derive 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc9ce59c69fc43078c6f4250b0c866bb06b9ff7ac955c7ddb82a8c189281dcae" diff --git a/server/Cargo.toml b/server/Cargo.toml index 1c85730e8..41f6d82a9 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -25,8 +25,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" toml = "0.5" rsa = "0.1" -# Match RSA git master -num-bigint = { version = "0.4", features = ["rand", "i128", "u64_digit", "prime", "zeroize"], package = "num-bigint-dig" } +num-bigint = { version = "0.5", package = "num-bigint-dig" } rsa-der = "0.2" rand = "0.7" rand_xorshift = "0.2" From f5e91ba736abda0a1e44f72b7baafb28db13705b Mon Sep 17 00:00:00 2001 From: Thijs Raymakers Date: Sun, 27 Oct 2019 14:55:57 +0100 Subject: [PATCH 026/647] Remove unused import --- Cargo.lock | 2 +- server/src/io/initialhandler.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81c38094b..ed6a8a745 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2840,8 +2840,8 @@ dependencies = [ "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" -"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" "checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" "checksum as-slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "293dac66b274fab06f95e7efb05ec439a6b70136081ea522d270bc351ae5bb27" "checksum atom 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3c86699c3f02778ec07158376991c8f783dd1f2f95c579ffaf0738dc984b2fe2" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" diff --git a/server/src/io/initialhandler.rs b/server/src/io/initialhandler.rs index 2fcd934bf..053bee8c9 100644 --- a/server/src/io/initialhandler.rs +++ b/server/src/io/initialhandler.rs @@ -578,7 +578,6 @@ mod tests { use super::*; use mojang_api::ProfileProperty; - use std::net::Ipv4Addr; #[test] fn extract_bungeecord_data_normal() { From ed51f2f03143df3950fd0d48c97f6027953ebf04 Mon Sep 17 00:00:00 2001 From: Thijs Raymakers Date: Sun, 27 Oct 2019 17:35:04 +0100 Subject: [PATCH 027/647] Fix stack sizes based on Minecraft implementation A search on the 1.14.4 client on items that have a defined maxCount variable gives a list of items that have a non-standard stack size. --- core/src/inventory.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/inventory.rs b/core/src/inventory.rs index aa661453b..7049e57db 100644 --- a/core/src/inventory.rs +++ b/core/src/inventory.rs @@ -115,8 +115,6 @@ pub fn max_size(item: Item) -> u8 { | Item::IronHelmet | Item::DiamondHelmet | Item::Bow - | Item::Book - | Item::WrittenBook | Item::WritableBook | Item::FlintAndSteel | Item::WhiteBed @@ -186,6 +184,9 @@ pub fn max_size(item: Item) -> u8 { | Item::TntMinecart | Item::DiamondHorseArmor | Item::GoldenHorseArmor + | Item::Saddle + | Item::KnowledgeBook + | Item::DebugStick | Item::IronHorseArmor => 1, Item::EnderPearl | Item::Snowball @@ -207,9 +208,10 @@ pub fn max_size(item: Item) -> u8 { | Item::BlackBanner | Item::Sign | Item::ArmorStand + | Item::Bucket + | Item::WrittenBook | Item::Egg => 16, _ => 64, - // TODO: are we missing some here? } } From 6814d9dd62d662d75d70c97802116ebf38d2dc46 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Tue, 12 Nov 2019 18:06:28 -0700 Subject: [PATCH 028/647] Use stable Rust: async/await is now stabilized --- .azure-pipelines.yml | 28 ++++++++++++++-------------- rust-toolchain | 1 - 2 files changed, 14 insertions(+), 15 deletions(-) delete mode 100644 rust-toolchain diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 19524a591..2c5b0b5fe 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -8,27 +8,27 @@ jobs: - job: 'CI' strategy: matrix: - #linux-stable: - # rustup_toolchain: stable - # image_name: 'ubuntu-16.04' + linux-stable: + rustup_toolchain: stable + image_name: 'ubuntu-16.04' linux-beta: rustup_toolchain: beta image_name: 'ubuntu-16.04' linux-nightly: rustup_toolchain: nightly-2019-09-28 image_name: 'ubuntu-16.04' - #windows-stable: - # rustup_toolchain: stable - # image_name: 'windows-latest' - windows-beta: - rustup_toolchain: beta-gnu + windows-stable: + rustup_toolchain: stable image_name: 'windows-latest' - windows-nightly: - rustup_toolchain: nightly-2019-09-28 - image_name: 'ubuntu-16.04' - #apple-stable: - # rustup_toolchain: stable - # image_name: 'macOS-10.13' + #windows-beta: + # rustup_toolchain: beta-gnu + # image_name: 'windows-latest' + #windows-nightly: + # rustup_toolchain: nightly-2019-09-28 + # image_name: 'ubuntu-16.04' + apple-stable: + rustup_toolchain: stable + image_name: 'macOS-10.13' #apple-beta: # rustup_toolchain: beta # image_name: 'macos-latest' diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index 65b2df87f..000000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -beta From 529c27b1e7815058741ff74985a1b84807990c0c Mon Sep 17 00:00:00 2001 From: caelunshun Date: Tue, 12 Nov 2019 18:06:28 -0700 Subject: [PATCH 029/647] Use stable Rust: async/await is now stabilized --- .azure-pipelines.yml | 28 ++++++++++++++-------------- rust-toolchain | 1 - 2 files changed, 14 insertions(+), 15 deletions(-) delete mode 100644 rust-toolchain diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 19524a591..2c5b0b5fe 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -8,27 +8,27 @@ jobs: - job: 'CI' strategy: matrix: - #linux-stable: - # rustup_toolchain: stable - # image_name: 'ubuntu-16.04' + linux-stable: + rustup_toolchain: stable + image_name: 'ubuntu-16.04' linux-beta: rustup_toolchain: beta image_name: 'ubuntu-16.04' linux-nightly: rustup_toolchain: nightly-2019-09-28 image_name: 'ubuntu-16.04' - #windows-stable: - # rustup_toolchain: stable - # image_name: 'windows-latest' - windows-beta: - rustup_toolchain: beta-gnu + windows-stable: + rustup_toolchain: stable image_name: 'windows-latest' - windows-nightly: - rustup_toolchain: nightly-2019-09-28 - image_name: 'ubuntu-16.04' - #apple-stable: - # rustup_toolchain: stable - # image_name: 'macOS-10.13' + #windows-beta: + # rustup_toolchain: beta-gnu + # image_name: 'windows-latest' + #windows-nightly: + # rustup_toolchain: nightly-2019-09-28 + # image_name: 'ubuntu-16.04' + apple-stable: + rustup_toolchain: stable + image_name: 'macOS-10.13' #apple-beta: # rustup_toolchain: beta # image_name: 'macos-latest' diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index 65b2df87f..000000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -beta From 53b389e9ce74cd2e7d00b2965dab47e4c39a8883 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Tue, 12 Nov 2019 21:06:14 -0700 Subject: [PATCH 030/647] Ow. --- Cargo.lock | 170 ++-- server/Cargo.toml | 6 +- server/src/blocks/falling.rs | 70 -- server/src/blocks/mod.rs | 128 --- server/src/entity/broadcast.rs | 201 ----- server/src/entity/chunk.rs | 446 ----------- server/src/entity/component.rs | 117 --- server/src/entity/destroy.rs | 82 -- server/src/entity/impls/animal/chicken.rs | 53 -- server/src/entity/impls/animal/cow.rs | 53 -- server/src/entity/impls/animal/donkey.rs | 57 -- server/src/entity/impls/animal/horse.rs | 57 -- server/src/entity/impls/animal/llama.rs | 53 -- server/src/entity/impls/animal/mod.rs | 12 - server/src/entity/impls/animal/mooshroom.rs | 53 -- server/src/entity/impls/animal/pig.rs | 53 -- server/src/entity/impls/animal/rabbit.rs | 53 -- server/src/entity/impls/animal/sheep.rs | 53 -- server/src/entity/impls/animal/squid.rs | 53 -- server/src/entity/impls/arrow.rs | 168 ---- server/src/entity/impls/falling_block.rs | 142 ---- server/src/entity/impls/item.rs | 582 -------------- server/src/entity/impls/mod.rs | 95 --- server/src/entity/impls/test.rs | 23 - server/src/entity/metadata.rs | 182 ----- server/src/entity/mod.rs | 103 --- server/src/entity/movement.rs | 264 ------- server/src/entity/save.rs | 198 ----- server/src/lazy.rs | 36 - server/src/lib.rs | 323 +------- server/src/player/animation.rs | 140 ---- server/src/player/broadcast.rs | 203 ----- server/src/player/chat.rs | 154 ---- server/src/player/digging.rs | 817 -------------------- server/src/player/init.rs | 177 ----- server/src/player/inventory.rs | 759 ------------------ server/src/player/mod.rs | 113 --- server/src/player/movement.rs | 469 ----------- server/src/player/placement.rs | 215 ------ server/src/player/resource_pack.rs | 86 --- server/src/player/save.rs | 101 --- server/src/player/view.rs | 221 ------ server/src/prelude.rs | 5 - server/src/systems.rs | 68 -- server/src/testframework.rs | 481 ------------ server/src/util/broadcaster.rs | 136 ---- server/src/util/macros.rs | 70 -- server/src/util/mod.rs | 169 ---- 48 files changed, 119 insertions(+), 8151 deletions(-) delete mode 100644 server/src/blocks/falling.rs delete mode 100644 server/src/blocks/mod.rs delete mode 100644 server/src/entity/broadcast.rs delete mode 100644 server/src/entity/chunk.rs delete mode 100644 server/src/entity/component.rs delete mode 100644 server/src/entity/destroy.rs delete mode 100644 server/src/entity/impls/animal/chicken.rs delete mode 100644 server/src/entity/impls/animal/cow.rs delete mode 100644 server/src/entity/impls/animal/donkey.rs delete mode 100644 server/src/entity/impls/animal/horse.rs delete mode 100644 server/src/entity/impls/animal/llama.rs delete mode 100644 server/src/entity/impls/animal/mod.rs delete mode 100644 server/src/entity/impls/animal/mooshroom.rs delete mode 100644 server/src/entity/impls/animal/pig.rs delete mode 100644 server/src/entity/impls/animal/rabbit.rs delete mode 100644 server/src/entity/impls/animal/sheep.rs delete mode 100644 server/src/entity/impls/animal/squid.rs delete mode 100644 server/src/entity/impls/arrow.rs delete mode 100644 server/src/entity/impls/falling_block.rs delete mode 100644 server/src/entity/impls/item.rs delete mode 100644 server/src/entity/impls/mod.rs delete mode 100644 server/src/entity/impls/test.rs delete mode 100644 server/src/entity/metadata.rs delete mode 100644 server/src/entity/mod.rs delete mode 100644 server/src/entity/movement.rs delete mode 100644 server/src/entity/save.rs delete mode 100644 server/src/lazy.rs delete mode 100644 server/src/player/animation.rs delete mode 100644 server/src/player/broadcast.rs delete mode 100644 server/src/player/chat.rs delete mode 100644 server/src/player/digging.rs delete mode 100644 server/src/player/init.rs delete mode 100644 server/src/player/inventory.rs delete mode 100644 server/src/player/mod.rs delete mode 100644 server/src/player/movement.rs delete mode 100644 server/src/player/placement.rs delete mode 100644 server/src/player/resource_pack.rs delete mode 100644 server/src/player/save.rs delete mode 100644 server/src/player/view.rs delete mode 100644 server/src/prelude.rs delete mode 100644 server/src/systems.rs delete mode 100644 server/src/testframework.rs delete mode 100644 server/src/util/broadcaster.rs delete mode 100644 server/src/util/macros.rs delete mode 100644 server/src/util/mod.rs diff --git a/Cargo.lock b/Cargo.lock index fe183b46b..05ac9fe2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,6 +90,11 @@ dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "as-slice" version = "0.1.0" @@ -99,11 +104,6 @@ dependencies = [ "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "atom" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "atty" version = "0.2.13" @@ -146,6 +146,19 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bit-set" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bit-vec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bitflags" version = "1.2.0" @@ -654,6 +667,7 @@ dependencies = [ "hematite-nbt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "humantime-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "legion 0.1.1 (git+https://github.com/TomGillen/legion?rev=2eab55dc5a3a84fbc8505cc16eb780ef0cdd019b)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mojang-api 0.3.0 (git+https://github.com/caelunshun/mojang-api-rs?rev=6525e910ad53953fa16028f0fce74b1a19855733)", "multimap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -672,17 +686,16 @@ dependencies = [ "rsa-der 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "shrev 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "simdeez 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "simdnoise 3.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.1 (registry+https://github.com/rust-lang/crates.io-index)", "strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tonks 0.1.0 (git+https://github.com/feather-rs/tonks)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -948,15 +961,6 @@ dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "hibitset" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "atom 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "http" version = "0.1.18" @@ -1065,6 +1069,14 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "itertools" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itertools" version = "0.8.0" @@ -1103,6 +1115,24 @@ dependencies = [ "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "legion" +version = "0.1.1" +source = "git+https://github.com/TomGillen/legion?rev=2eab55dc5a3a84fbc8505cc16eb780ef0cdd019b#2eab55dc5a3a84fbc8505cc16eb780ef0cdd019b" +dependencies = [ + "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "downcast-rs 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "shrinkwraprs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "libc" version = "0.2.62" @@ -1596,6 +1626,14 @@ name = "proc-macro-nested" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "proc-macro2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro2" version = "0.3.8" @@ -1625,6 +1663,14 @@ name = "quick-error" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "quote" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "0.5.2" @@ -2079,22 +2125,16 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "shred" -version = "0.9.3" +name = "shrinkwraprs" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "mopa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "shrev" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "simdeez" version = "0.6.4" @@ -2151,22 +2191,6 @@ name = "sourcefile" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "specs" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hashbrown 0.6.1 (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)", - "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "shred 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "shrev 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tuple_utils 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "spin" version = "0.5.2" @@ -2177,6 +2201,11 @@ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "stream-cipher" version = "0.3.2" @@ -2219,6 +2248,16 @@ name = "subtle" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "syn" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.13.11" @@ -2467,6 +2506,27 @@ dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tonks" +version = "0.1.0" +source = "git+https://github.com/feather-rs/tonks#5151057974bfd22317ef64b0e1eb455a0aa80442" +dependencies = [ + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "legion 0.1.1 (git+https://github.com/TomGillen/legion?rev=2eab55dc5a3a84fbc8505cc16eb780ef0cdd019b)", + "mopa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tower-make" version = "0.3.0-alpha.2a" @@ -2516,11 +2576,6 @@ name = "try-lock" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "tuple_utils" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "typenum" version = "1.11.2" @@ -2832,13 +2887,15 @@ dependencies = [ "checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" "checksum as-slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "293dac66b274fab06f95e7efb05ec439a6b70136081ea522d270bc351ae5bb27" -"checksum atom 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3c86699c3f02778ec07158376991c8f783dd1f2f95c579ffaf0738dc984b2fe2" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" "checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5" "checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +"checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80" +"checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb" "checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2" "checksum bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" @@ -2912,7 +2969,6 @@ dependencies = [ "checksum heapless 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f339aa7d51777fc0af6aa7cbeb277dfc6e6c029cbdeda48d0fbb92c2337f0e69" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hematite-nbt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "98b407a33bb1715a4cf0276edfe8df52352c55b2a3703c5079adedf398b92932" -"checksum hibitset 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "47e7292fd9f7fe89fa35c98048f2d0a69b79ed243604234d18f6f8a1aa6f408d" "checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" "checksum http-body 0.2.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1f3aef6f3de2bd8585f5b366f3f550b5774500b4764d00cf00f903c95749eec3" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" @@ -2923,11 +2979,13 @@ dependencies = [ "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" "checksum indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a61202fbe46c4a951e9404a720a0180bcf3212c750d735cb5c4ba4dc551299f3" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" +"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "2cc9a97d7cec30128fd8b28a7c1f9df1c001ceb9b441e2b755e24130a6b43c79" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum legion 0.1.1 (git+https://github.com/TomGillen/legion?rev=2eab55dc5a3a84fbc8505cc16eb780ef0cdd019b)" = "" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum libm 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" "checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" @@ -2982,10 +3040,12 @@ dependencies = [ "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "114cdf1f426eb7f550f01af5f53a33c0946156f6814aec939b3bd77e844f9a9d" "checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" +"checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" "checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90cf5f418035b98e655e9cdb225047638296b862b42411c4e45bb88d700f7fc0" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +"checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408" "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" @@ -3035,8 +3095,7 @@ dependencies = [ "checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" "checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" "checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" -"checksum shred 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d15d46c92f8c0aed110a132f3c68a8cdd390048f51fa547c89dc571ba1e01191" -"checksum shrev 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5752e017e03af9d735b4b069f53b7a7fd90fefafa04d8bd0c25581b0bff437f" +"checksum shrinkwraprs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7d5f047b90b2ca2d1526ff73d67cba61f86f4cf9a8afddc99dd96702ded8e684" "checksum simdeez 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4204ae48b2a871f428dc20426f005be250413fa6263e6f3d93094a36b29504dc" "checksum simdnoise 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "86a9e4c1c3369eab7105ac7e1582a601942fed0a63877cb2e1afcf57f34ed7b3" "checksum simple_asn1 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b25ecba7165254f0c97d6c22a64b1122a03634b18d20a34daf21e18f892e618" @@ -3045,15 +3104,16 @@ dependencies = [ "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 sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" -"checksum specs 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4943fde8c5d3d14c3d19d2a4c7abbd7b626c270a19e6cd35252294a48feb698c" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" "checksum stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6138f8f88a16d90134763314e3fc76fa3ed6a7db4725d6acf9a3ef95a3188d22" "checksum strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81" "checksum subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab3af2eb31c42e8f0ccf43548232556c42737e01a96db6e1777b0be108e79799" +"checksum syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97c05b8ebc34ddd6b967994d5c6e9852fa92f8b82b3858c39451f97346dcce5" "checksum syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)" = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" @@ -3075,13 +3135,13 @@ dependencies = [ "checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" "checksum tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" +"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks)" = "" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" "checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" "checksum tracing-attributes 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3ff978fd9c9afe2cc9c671e247713421c6406b3422305cbdce5de695d3ab4c3c" "checksum tracing-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "528c8ebaaa16cdac34795180b046c031775b0d56402704d98c096788f33d646a" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" -"checksum tuple_utils 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44834418e2c5b16f47bedf35c28e148db099187dd5feee6367fb2525863af4f1" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2e2e6bd1e59e56598518beb94fd6db628ded570326f0a98c679a304bd9f00150" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" diff --git a/server/Cargo.toml b/server/Cargo.toml index 1071f1790..1f2d0ee63 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -16,6 +16,8 @@ path = "src/main.rs" feather-blocks = { path = "../blocks" } feather-core = { path = "../core" } feather-item-block = { path = "../item_block" } +legion = { git = "https://github.com/TomGillen/legion", rev = "2eab55dc5a3a84fbc8505cc16eb780ef0cdd019b" } +tonks = { git = "https://github.com/feather-rs/tonks" } crossbeam = "0.7" log = "0.4" simple_logger = "1.3" @@ -36,9 +38,7 @@ hashbrown = { version = "0.6", features = ["rayon"] } mojang-api = { git = "https://github.com/caelunshun/mojang-api-rs", rev = "6525e910ad53953fa16028f0fce74b1a19855733" } multimap = "0.6" hematite-nbt = "0.4" -specs = { version = "0.15", features = ["storage-event-control"] } rayon = "1.2" -shrev = "1.1" failure = "0.1" num-derive = "0.3" num-traits = "0.2" @@ -74,4 +74,4 @@ name = "worldgen" harness = false [features] -nightly = ["specs/nightly", "parking_lot/nightly"] +nightly = ["hashbrown/nightly", "parking_lot/nightly"] diff --git a/server/src/blocks/falling.rs b/server/src/blocks/falling.rs deleted file mode 100644 index d048f9b13..000000000 --- a/server/src/blocks/falling.rs +++ /dev/null @@ -1,70 +0,0 @@ -use shrev::ReaderId; -use specs::shrev::EventChannel; -use specs::{Builder, Entities, LazyUpdate, Read, System, Write}; - -use feather_core::world::ChunkMap; - -use feather_blocks::{Block, BlockExt}; - -use crate::blocks::{BlockNotifyEvent, BlockUpdateCause, BlockUpdateEvent}; -use crate::entity::{falling_block, PositionComponent, VelocityComponent}; -use feather_core::Position; - -/// This system listens to `BlockNotifyEvent`s. -#[derive(Default)] -pub struct FallingBlockCreationSystem { - reader: Option>, -} - -impl<'a> System<'a> for FallingBlockCreationSystem { - type SystemData = ( - Read<'a, EventChannel>, - Write<'a, EventChannel>, - Write<'a, ChunkMap>, - Read<'a, LazyUpdate>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (events, mut block_update, mut chunk_map, lazy, entities) = data; - - // Process events - for event in events.read(&mut self.reader.as_mut().unwrap()) { - match event.block { - Block::Sand | Block::RedSand | Block::Gravel => { - let mut below = event.pos; - below.y -= 1; - - if !chunk_map.block_at(below).unwrap_or(Block::Air).is_solid() { - chunk_map.set_block_at(event.pos, Block::Air).unwrap(); - - let update_event = BlockUpdateEvent { - cause: BlockUpdateCause::FallingBlock, - pos: event.pos, - old_block: event.block, - new_block: Block::Air, - }; - - block_update.single_write(update_event); - - let mut entity_pos: Position = event.pos.world_pos(); - // Center position on block - entity_pos.x += 0.5; - entity_pos.z += 0.5; - - falling_block::create(&lazy, &entities, event.block, entity_pos) - .with(PositionComponent { - current: entity_pos, - previous: entity_pos, - }) - .with(VelocityComponent::default()) - .build(); - } - } - _ => (), - } - } - } - - setup_impl!(reader); -} diff --git a/server/src/blocks/mod.rs b/server/src/blocks/mod.rs deleted file mode 100644 index 67d43a4f0..000000000 --- a/server/src/blocks/mod.rs +++ /dev/null @@ -1,128 +0,0 @@ -mod falling; - -pub use falling::FallingBlockCreationSystem; - -use shrev::{EventChannel, ReaderId}; -use specs::{DispatcherBuilder, Entity, Read, System, Write}; - -use feather_blocks::Block; -use feather_core::world::{BlockPosition, ChunkMap}; - -use crate::systems::{BLOCK_FALLING_CREATION, BLOCK_UPDATE_PROPAGATE}; -use hashbrown::HashSet; - -lazy_static! { - /// List of block types that need to be notified - /// of adjacent block updates. - static ref BLOCKS_TO_NOTIFY: HashSet = { - let mut set = HashSet::new(); - // Falling blocks - set.insert(Block::Sand); - set.insert(Block::RedSand); - set.insert(Block::Gravel); - set - }; -} - -/// Event triggered when a block is updated. -/// -/// This event is triggered *after* the block is updated -/// in the chunk map. -#[derive(Debug, Clone)] -pub struct BlockUpdateEvent { - /// The cause of this block update event. - pub cause: BlockUpdateCause, - /// The location of the block which was updated. - pub pos: BlockPosition, - /// The block which was previously at the position. - pub old_block: Block, - /// The new block at the position. - pub new_block: Block, -} - -/// The possible causes of a block update event. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum BlockUpdateCause { - /// Indicates that a player updated the block. - Player(Entity), - /// Indicates that a falling block updated the block. - FallingBlock, - /// A test block update caused, used for unit testing. - Test, -} - -#[derive(Debug, Clone)] -pub struct BlockNotifyEvent { - pub block: Block, - pub pos: BlockPosition, - pub notified_by: BlockPosition, -} - -/// System for propagating block update -/// events to surrounding blocks. -/// -/// This system listens to `BlockUpdateEvent`s. -#[derive(Default)] -pub struct BlockUpdatePropagateSystem { - reader: Option>, -} - -impl<'a> System<'a> for BlockUpdatePropagateSystem { - type SystemData = ( - Read<'a, EventChannel>, - Read<'a, ChunkMap>, - Write<'a, EventChannel>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (events, chunk_map, mut notify) = data; - - // Process events - for event in events.read(&mut self.reader.as_mut().unwrap()) { - let mut notify_events: Vec = Vec::new(); - - for x in -1..=1 { - for y in -1..=1 { - for z in -1..=1 { - let mut adjacent = event.pos; - adjacent.x += x; - adjacent.y += y; - adjacent.z += z; - let block = chunk_map.block_at(adjacent); - if let Some(block) = block { - if BLOCKS_TO_NOTIFY.contains(&block) { - notify_events.push(BlockNotifyEvent { - block, - pos: adjacent, - notified_by: event.pos, - }); - } - } - } - } - } - - // Notify all adjacent blocks at once - notify.drain_vec_write(&mut notify_events); - } - } - - setup_impl!(reader); -} - -pub fn init_logic(_dispatcher: &mut DispatcherBuilder) { - // TODO -} - -pub fn init_handlers(dispatcher: &mut DispatcherBuilder) { - dispatcher.add( - BlockUpdatePropagateSystem::default(), - BLOCK_UPDATE_PROPAGATE, - &[], - ); - dispatcher.add( - FallingBlockCreationSystem::default(), - BLOCK_FALLING_CREATION, - &[BLOCK_UPDATE_PROPAGATE], - ); -} diff --git a/server/src/entity/broadcast.rs b/server/src/entity/broadcast.rs deleted file mode 100644 index b2444f92e..000000000 --- a/server/src/entity/broadcast.rs +++ /dev/null @@ -1,201 +0,0 @@ -//! Module for broadcasting when an entity comes within -//! range of a player. Also handles sending the correct -//! packet to spawn entities on the client. -//! -//! Sending entities to a client is handled lazily -//! through `LazyUpdate`, because arbitrary components -//! may need to be accessed. - -use crate::chunk_logic::ChunkHolders; -use crate::entity::{LastKnownPositionComponent, PacketCreatorComponent}; -use crate::entity::{Metadata, PositionComponent}; -use crate::lazy::LazyUpdateExt; -use crate::network::{send_packet_boxed_to_player, send_packet_to_player, NetworkComponent}; -use feather_core::network::packet::implementation::PacketEntityMetadata; -use shrev::EventChannel; -use specs::{Entity, LazyUpdate, Read, ReadStorage, ReaderId, System, WorldExt}; - -/// An entity send request, containing -/// the player to send to and the entity -/// to send. -#[derive(Debug)] -struct SendRequest { - player: Entity, - 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, -} - -/// Event triggered when an entity of any -/// type is spawned. -#[derive(Debug, Clone)] -pub struct EntitySpawnEvent { - /// The spawned entity. - pub entity: Entity, -} - -/// System for broadcasting when an entity is spawned. -/// -/// Broadcasts are lazily queued for sending -/// and are sent by `EntitySendSystem`. -/// -/// This system listens to `EntitySpawnEvent`s. -#[derive(Default)] -pub struct EntityBroadcastSystem { - reader: Option>, -} - -impl<'a> System<'a> for EntityBroadcastSystem { - type SystemData = ( - ReadStorage<'a, PositionComponent>, - ReadStorage<'a, NetworkComponent>, - Read<'a, ChunkHolders>, - Read<'a, EventChannel>, - Read<'a, LazyUpdate>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (positions, networks, chunk_holders, spawn_events, lazy) = 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; - } - - lazy.send_entity_to_player(*holder, event.entity); - } - } - } - } - - setup_impl!(reader); -} - -/// Lazily sends an entity to a player. -pub fn send_entity_to_player(lazy: &LazyUpdate, player: Entity, entity: Entity) { - lazy.exec(move |world| { - // Attempt to get the `PacketCreator` for the entity. - // If it doesn't exist, skip sending. - let packet_creators = world.read_component::(); - let packet_creator = match packet_creators.get(entity) { - Some(packet_creator) => packet_creator, - None => return, - }; - - let create_packet = packet_creator.0; - let packet = create_packet(world, entity); - - if let Some(network) = world.read_component::().get(player) { - send_packet_boxed_to_player(network, packet); - - // If the entity has metadata, send it. - let metas = world.read_component::(); - if let Some(meta) = metas.get(entity) { - let packet = PacketEntityMetadata { - entity_id: entity.id() as i32, - metadata: meta.to_full_raw_metadata(), - }; - send_packet_to_player(network, packet); - } - } - - // Insert last known position - let positions = world.read_component::(); - let mut last_positions = world.write_component::(); - if let Some(last_positions) = last_positions.get_mut(player) { - if let Some(pos) = positions.get(entity) { - last_positions.0.insert(entity, pos.current); - } - } - - // Trigger event - let event = EntitySendEvent { entity, player }; - world.fetch_mut::>().single_write(event); - }); -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::entity::{item, VelocityComponent}; - use crate::player::ChunkCrossSystem; - use crate::testframework as t; - use feather_core::network::cast_packet; - use feather_core::network::packet::implementation::{SpawnObject, SpawnPlayer}; - use feather_core::network::packet::PacketType; - use feather_core::{Item, ItemStack}; - use specs::{Builder, WorldExt}; - - #[test] - fn test_spawn_player() { - let (mut w, mut d) = t::init_world(); - - let player1 = t::add_player(&mut w); - let player2 = t::add_player(&mut w); - - let event = EntitySpawnEvent { - entity: player1.entity, - }; - - w.fetch_mut::>().single_write(event); - - d.dispatch(&w); - w.maintain(); - - t::assert_packet_not_received(&player1, PacketType::SpawnPlayer); // Player shouldn't have received packet for themselves - - let packet = t::assert_packet_received(&player2, PacketType::SpawnPlayer); - let packet = cast_packet::(&*packet); - - assert_eq!(packet.entity_id, player1.entity.id() as i32); - } - - #[test] - fn test_spawn_item() { - let (mut w, mut d) = t::builder() - .with(EntityBroadcastSystem::default(), "broadcast") - .with(ChunkCrossSystem::default(), "chunk_cross") - .build(); - - let player = t::add_player(&mut w); - - let item = item::create(&w.fetch(), &w.fetch(), ItemStack::new(Item::Stone, 1), 0) - .with(PositionComponent::default()) - .with(VelocityComponent::default()) - .build(); - - w.maintain(); - d.dispatch(&w); - w.maintain(); - - let spawn_entity = t::assert_packet_received(&player, PacketType::SpawnObject); - let spawn_entity = cast_packet::(&*spawn_entity); - - assert_eq!(spawn_entity.entity_id, item.id() as i32); - assert_eq!(spawn_entity.velocity_x, 0); - } -} diff --git a/server/src/entity/chunk.rs b/server/src/entity/chunk.rs deleted file mode 100644 index 22490f8c9..000000000 --- a/server/src/entity/chunk.rs +++ /dev/null @@ -1,446 +0,0 @@ -//! Maintains a list of entities which are in each -//! chunk, which allows for more efficient nearby -//! entity queries and packet broadcasting. - -use crate::chunk_logic::ChunkLoadEvent; -use crate::entity::{ - arrow, chicken, cow, donkey, horse, item, llama, mooshroom, pig, rabbit, sheep, squid, - EntityDestroyEvent, EntitySpawnEvent, PositionComponent, -}; -use crate::TickCount; -use feather_core::entity::EntityData; -use feather_core::world::ChunkPosition; -use hashbrown::{HashMap, HashSet}; -use shrev::EventChannel; -use specs::storage::ComponentEvent; -use specs::{ - BitSet, Entities, Entity, Join, LazyUpdate, Read, ReadStorage, ReaderId, System, World, - WorldExt, Write, -}; -use std::sync::atomic::{AtomicBool, Ordering}; - -/// Keeps track of which entities are in which chunk. -/// Also has a boolean for each chunk which indicates -/// whether its entities have been updated recently. -#[derive(Debug, Deref, DerefMut, Default)] -pub struct ChunkEntities(HashMap)>); - -lazy_static! { - static ref EMPTY_VEC: Vec = Vec::with_capacity(0); -} - -impl ChunkEntities { - /// Returns all entities in a given chunk. - pub fn entities_in_chunk(&self, chunk: ChunkPosition) -> &Vec { - if let Some((_, entities)) = self.0.get(&chunk) { - entities - } else { - &EMPTY_VEC - } - } - - /// Returns all entities in the chunk, in addition to - /// a boolean indicating whether the entities have been - /// updated since the last call to this function. - pub fn entities_in_chunk_and_modified(&self, chunk: ChunkPosition) -> (bool, &[Entity]) { - if let Some((dirty, entities)) = self.0.get(&chunk) { - let d = dirty.load(Ordering::SeqCst); - dirty.store(false, Ordering::SeqCst); - (d, entities) - } else { - (false, &[]) - } - } - - /// Adds an entity to a chunk. - pub fn add_to_chunk(&mut self, chunk: ChunkPosition, entity: Entity) { - self.0 - .entry(chunk) - .and_modify(|(dirty, vec)| { - dirty.store(true, Ordering::SeqCst); - vec.push(entity) - }) - .or_insert_with(|| (AtomicBool::new(true), vec![entity])); - } - - /// Removes an entity from a chunk. - /// - /// # Panics - /// May panic in some cases if the entity is not contained - /// within the given chunk. - pub fn remove_from_chunk(&mut self, chunk: ChunkPosition, entity: Entity) { - let (dirty, vec) = match self.0.get_mut(&chunk) { - Some(vec) => vec, - _ => return, - }; - - let (index, _) = match vec.iter().enumerate().find(|x| *x.1 == entity) { - Some(index) => index, - None => return, - }; - vec.swap_remove(index); - - dirty.store(true, Ordering::SeqCst); - - if vec.is_empty() { - 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, - ) -> HashSet { - let mut result = HashSet::new(); - - // 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 { - let chunk = ChunkPosition::new(chunk.x + x_offset, chunk.z + z_offset); - - result.extend(self.entities_in_chunk(chunk)); - } - } - - result - } -} - -/// System for updating the `ChunkEntities`. -/// -/// This system listens to `EntityMoveEvent`s, `EntitySpawnEvent`s, -/// and `EntityDestroyEvent`s. -#[derive(Default)] -pub struct ChunkEntityUpdateSystem { - dirty: BitSet, - move_reader: Option>, - spawn_reader: Option>, - destroy_reader: Option>, -} - -impl<'a> System<'a> for ChunkEntityUpdateSystem { - type SystemData = ( - ReadStorage<'a, PositionComponent>, - Write<'a, ChunkEntities>, - Read<'a, EventChannel>, - Read<'a, EventChannel>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (positions, mut entity_chunks, spawn_events, destroy_events, entities) = data; - - self.dirty.clear(); - for event in positions.channel().read(self.move_reader.as_mut().unwrap()) { - if let ComponentEvent::Modified(id) = event { - self.dirty.add(*id); - } - } - - for (position, entity, _) in (&positions, &entities, &self.dirty).join() { - let new_pos = position.current.chunk_pos(); - let old_pos = position.previous.chunk_pos(); - - if new_pos != old_pos { - entity_chunks.remove_from_chunk(old_pos, entity); - entity_chunks.add_to_chunk(new_pos, entity); - } - } - - for event in spawn_events.read(self.spawn_reader.as_mut().unwrap()) { - if let Some(pos) = positions.get(event.entity) { - entity_chunks.add_to_chunk(pos.current.chunk_pos(), event.entity); - } - } - - for event in destroy_events.read(self.destroy_reader.as_mut().unwrap()) { - if let Some(pos) = positions.get(event.entity) { - entity_chunks.remove_from_chunk(pos.current.chunk_pos(), event.entity); - } - } - } - - fn setup(&mut self, world: &mut World) { - use specs::SystemData; - - Self::SystemData::setup(world); - - self.move_reader = Some( - world - .write_component::() - .register_reader(), - ); - self.destroy_reader = Some(world.fetch_mut::>().register_reader()); - self.spawn_reader = Some(world.fetch_mut::>().register_reader()); - } -} - -/// System for spawning entities inside newly-loaded chunks. -/// -/// This system listens to `ChunkLoadEvent`s. -#[derive(Default)] -pub struct EntityChunkLoadSystem { - reader: Option>, -} - -impl<'a> System<'a> for EntityChunkLoadSystem { - type SystemData = ( - Read<'a, EventChannel>, - Read<'a, LazyUpdate>, - Entities<'a>, - Read<'a, TickCount>, - ); - - #[allow(clippy::cognitive_complexity)] // Big match statement. Necessary - fn run(&mut self, data: Self::SystemData) { - let (load_events, lazy, entities, tick) = data; - - for event in load_events.read(self.reader.as_mut().unwrap()) { - for entity in &event.entities { - match entity { - EntityData::Item(item_data) => { - if item::create_from_data(&lazy, &entities, item_data, &tick).is_none() { - debug!("Error while loading item entity"); - } - } - EntityData::Arrow(arrow_data) => { - if arrow::create_from_data(&lazy, &entities, arrow_data).is_none() { - debug!("Error while loading arrow entity"); - } - } - EntityData::Cow(data) => { - if cow::create_from_data(&lazy, &entities, data).is_none() { - debug!("Error while loading cow entity") - } - } - EntityData::Pig(data) => { - if pig::create_from_data(&lazy, &entities, data).is_none() { - debug!("Error while loading pig entity") - } - } - EntityData::Chicken(data) => { - if chicken::create_from_data(&lazy, &entities, data).is_none() { - debug!("Error while loading chicken entity") - } - } - EntityData::Sheep(data) => { - if sheep::create_from_data(&lazy, &entities, data).is_none() { - debug!("Error while loading sheep entity") - } - } - EntityData::Horse(data) => { - if horse::create_from_data(&lazy, &entities, data).is_none() { - debug!("Error while loading horse entity") - } - } - EntityData::Llama(data) => { - if llama::create_from_data(&lazy, &entities, data).is_none() { - debug!("Error while loading llama entity") - } - } - EntityData::Mooshroom(data) => { - if mooshroom::create_from_data(&lazy, &entities, data).is_none() { - debug!("Error while loading mooshroom entity") - } - } - EntityData::Rabbit(data) => { - if rabbit::create_from_data(&lazy, &entities, data).is_none() { - debug!("Error while loading rabbit entity") - } - } - EntityData::Squid(data) => { - if squid::create_from_data(&lazy, &entities, data).is_none() { - debug!("Error while loading squid entity") - } - } - EntityData::Donkey(data) => { - if donkey::create_from_data(&lazy, &entities, data).is_none() { - debug!("Error while loading donkey entity") - } - } - // TODO: Spawn remaining entity types here. - EntityData::Unknown => { - trace!("Chunk {:?} contains an unknown entity type", event.pos); - } - } - } - } - } - - setup_impl!(reader); -} - -// 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::*; - use crate::entity::{test, ArrowComponent, ItemComponent}; - use crate::testframework as t; - use feather_core::entity::{ArrowEntityData, ItemEntityData}; - use specs::{Builder, World, WorldExt}; - - #[test] - fn test_chunk_entities() { - let mut chunks = ChunkEntities::default(); - - let mut world = World::new(); - let entity = world.create_entity().build(); - - let pos = ChunkPosition::new(0, 0); - - chunks.add_to_chunk(pos, entity); - assert_eq!(chunks.entities_in_chunk(pos).as_slice(), &[entity]); - assert!(chunks.entities_in_chunk_and_modified(pos).0); - assert!(!chunks.entities_in_chunk_and_modified(pos).0); - } - - #[test] - fn test_new_entity() { - let (mut w, mut d) = t::builder() - .with(ChunkEntityUpdateSystem::default(), "") - .build(); - - let pos = position!(1.0, 64.0, 1003.5); - let entity = w - .create_entity() - .with(PositionComponent { - current: pos, - previous: pos, - }) - .build(); - - let event = EntitySpawnEvent { entity }; - t::trigger_event(&w, event); - - d.dispatch(&w); - w.maintain(); - - let chunk_entities = w.fetch::(); - assert_eq!( - chunk_entities.entities_in_chunk(pos.chunk_pos()).as_slice(), - &[entity] - ); - } - - #[test] - fn test_moved_entity() { - 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); - - let entity = w - .create_entity() - .with(PositionComponent { - 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(); - - let chunk_entities = w.fetch::(); - assert!(chunk_entities - .entities_in_chunk(old_pos.chunk_pos()) - .is_empty()); - assert_eq!( - chunk_entities.entities_in_chunk(pos.chunk_pos()).as_slice(), - &[entity] - ); - } - - #[test] - fn test_destroyed_entity() { - let (mut w, mut d) = t::builder() - .with(ChunkEntityUpdateSystem::default(), "") - .build(); - - let pos = position!(100.0, -100.0, -100.0); - let entity = test::create(&mut w, pos).build(); - - let event = EntityDestroyEvent { entity }; - t::trigger_event(&w, event); - - d.dispatch(&w); - w.maintain(); - - 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, 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); - chunk_entities.add_to_chunk(chunk3, entity3); - chunk_entities.add_to_chunk(chunk4, entity4); - - let view_distance = 4; - let entities = chunk_entities.entites_within_view_distance(chunk1, view_distance); - - assert!(entities.contains(&entity1)); - assert!(entities.contains(&entity2)); - assert!(!entities.contains(&entity3)); - assert!(entities.contains(&entity4)); - } - - #[test] - fn test_entities_loaded_in_chunk() { - let (mut w, mut d) = t::builder() - .with(EntityChunkLoadSystem::default(), "") - .build(); - - let entities = vec![ - EntityData::Item(ItemEntityData::default()), - EntityData::Arrow(ArrowEntityData::default()), - ]; - let pos = ChunkPosition::new(1, 2); - - let mut entity_spawn_reader = t::reader(&w); - let load_event = ChunkLoadEvent { pos, entities }; - t::trigger_event(&w, load_event); - - d.dispatch(&w); - w.maintain(); - d.dispatch(&w); - w.maintain(); - - // Confirm two entities were created: one arrow, one item - let mut events = t::triggered_events::(&w, &mut entity_spawn_reader); - - let first = events.remove(0).entity; - let second = events.remove(0).entity; - assert!(w.read_component::().contains(first)); - assert!(w.read_component::().contains(second)); - } -} diff --git a/server/src/entity/component.rs b/server/src/entity/component.rs deleted file mode 100644 index 35e2cf1b1..000000000 --- a/server/src/entity/component.rs +++ /dev/null @@ -1,117 +0,0 @@ -//! Various Specs components. - -use feather_core::entity::EntityData; -use feather_core::world::Position; -use feather_core::{Gamemode, Packet}; -use glm::DVec3; -use specs::storage::BTreeStorage; -use specs::{Component, Entity, FlaggedStorage, Join, System, VecStorage, World, WriteStorage}; -use uuid::Uuid; - -pub struct PlayerComponent { - pub profile_properties: Vec, - pub gamemode: Gamemode, -} - -impl Component for PlayerComponent { - type Storage = BTreeStorage; -} - -#[derive(Default, Debug, PartialEq, Clone, Copy)] -pub struct PositionComponent { - /// The current position of this entity. - pub current: Position, - /// The position of this entity on the previous - /// tick. At the end of each tick, `reset` should - /// be called. - pub previous: Position, -} - -impl PositionComponent { - /// Resets the current and previous position. - /// Should be called at the end of every tick. - pub fn reset(&mut self) { - self.previous = self.current; - } -} - -impl Component for PositionComponent { - type Storage = FlaggedStorage>; -} - -/// An entity's velocity, in blocks per tick. -/// -/// Entities without this component are assumed -/// to have a velocity of 0. -#[derive(Deref, DerefMut, Debug, PartialEq, Clone, Copy)] -pub struct VelocityComponent(pub DVec3); - -impl Component for VelocityComponent { - type Storage = FlaggedStorage>; -} - -impl Default for VelocityComponent { - fn default() -> Self { - Self(glm::vec3(0.0, 0.0, 0.0)) - } -} - -#[derive(Clone, Debug)] -pub struct NamedComponent { - pub display_name: String, - pub uuid: Uuid, -} - -impl Component for NamedComponent { - type Storage = BTreeStorage; -} - -pub trait PacketCreator: Fn(&World, Entity) -> Box + Send + Sync {} - -impl Box + Send + Sync> PacketCreator for F {} - -/// Component containing a closure which returns the packet -/// needed to spawn an entity. -/// -/// The closure requires world access because it may need to access -/// arbitrary components. -pub struct PacketCreatorComponent(pub &'static dyn PacketCreator); - -impl Component for PacketCreatorComponent { - type Storage = VecStorage; -} - -pub trait EntitySerializer: Fn(&World, Entity) -> EntityData + Send + Sync {} - -impl EntityData + Send + Sync> EntitySerializer for F {} - -/// Component containing a closure which returns the `EntityData` -/// for an entity. -/// -/// The closure requires world access because it may need to access -/// arbitrary components. -pub struct SerializerComponent(pub &'static dyn EntitySerializer); - -impl Component for SerializerComponent { - type Storage = VecStorage; -} - -/// System for resetting an entity's components -/// at the end of the tick. -pub struct ComponentResetSystem; - -impl<'a> System<'a> for ComponentResetSystem { - type SystemData = WriteStorage<'a, PositionComponent>; - - fn run(&mut self, mut positions: Self::SystemData) { - // Ensure that position update events are not triggered - // for this. See #81 - positions.set_event_emission(false); - - for position in (&mut positions).join() { - position.reset(); - } - - positions.set_event_emission(true); - } -} diff --git a/server/src/entity/destroy.rs b/server/src/entity/destroy.rs deleted file mode 100644 index 28aea7e9c..000000000 --- a/server/src/entity/destroy.rs +++ /dev/null @@ -1,82 +0,0 @@ -//! Module for broadcasting and handling entity destroy -//! events. - -use crate::util::Util; -use feather_core::network::packet::implementation::DestroyEntities; -use shrev::{EventChannel, ReaderId}; -use specs::SystemData; -use specs::{Entities, Entity, Read, System, World}; - -/// Event triggered when an entity -/// of any type is destroyed. -#[derive(Debug, Clone)] -pub struct EntityDestroyEvent { - /// Note that when this event is triggered, - /// the entity isn't actually removed from the world - /// yet. This allows systems to access the entity's - /// data before it is destroyed. - /// - /// `EntityDestroySystem` is responsible for removing - /// entities once the `EntityDestroyEvent` has been - /// handled by all readers. - pub entity: Entity, -} - -/// System for removing entities from the world when they -/// are destroyed. -#[derive(Default)] -pub struct EntityDestroySystem { - reader: Option>, -} - -impl<'a> System<'a> for EntityDestroySystem { - type SystemData = (Read<'a, EventChannel>, Entities<'a>); - - fn run(&mut self, data: Self::SystemData) { - let (events, entities) = data; - - for event in events.read(&mut self.reader.as_mut().unwrap()) { - let _ = entities.delete(event.entity); - } - } - - fn setup(&mut self, world: &mut World) { - Self::SystemData::setup(world); - - self.reader = Some( - world - .fetch_mut::>() - .register_reader(), - ); - } -} - -/// System for broadcasting when an entity is destroyed. -#[derive(Default)] -pub struct EntityDestroyBroadcastSystem { - reader: Option>, -} - -impl<'a> System<'a> for EntityDestroyBroadcastSystem { - type SystemData = (Read<'a, Util>, Read<'a, EventChannel>); - - fn run(&mut self, data: Self::SystemData) { - let (util, events) = data; - - for event in events.read(&mut self.reader.as_mut().unwrap()) { - let destroy_entities = DestroyEntities::new(vec![event.entity.id() as i32]); - - util.broadcast_entity_update(event.entity, destroy_entities, None); - } - } - - fn setup(&mut self, world: &mut World) { - Self::SystemData::setup(world); - - self.reader = Some( - world - .fetch_mut::>() - .register_reader(), - ); - } -} diff --git a/server/src/entity/impls/animal/chicken.rs b/server/src/entity/impls/animal/chicken.rs deleted file mode 100644 index a92e4b3d3..000000000 --- a/server/src/entity/impls/animal/chicken.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::entity::{ - base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, - VelocityComponent, -}; -use crate::lazy::LazyUpdateExt; -use crate::physics::PhysicsBuilder; -use feather_core::entity::{AnimalData, EntityData}; -use feather_core::Packet; -use specs::world::{EntitiesRes, LazyBuilder}; -use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; - -#[derive(Default)] -pub struct ChickenComponent; - -impl Component for ChickenComponent { - type Storage = NullStorage; -} - -pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { - lazy.spawn_entity(entities) - .with(ChickenComponent) - .with(PhysicsBuilder::for_living().bbox(0.4, 0.7, 0.4).build()) - .with(PacketCreatorComponent(&create_packet)) - .with(SerializerComponent(&serialize)) -} - -pub fn create_from_data( - lazy: &LazyUpdate, - entities: &EntitiesRes, - data: &AnimalData, -) -> Option { - let position = data.base.read_position()?; - let velocity = data.base.read_velocity()?; - - Some( - create(lazy, entities) - .with(PositionComponent { - current: position, - previous: position, - }) - .with(VelocityComponent(velocity)) - .build(), - ) -} - -fn create_packet(world: &World, entity: Entity) -> Box { - create_mob_packet(world, entity, 7) -} - -fn serialize(world: &World, entity: Entity) -> EntityData { - let base = base_data(world, entity); - EntityData::Chicken(AnimalData { base }) -} diff --git a/server/src/entity/impls/animal/cow.rs b/server/src/entity/impls/animal/cow.rs deleted file mode 100644 index e962c8daa..000000000 --- a/server/src/entity/impls/animal/cow.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::entity::{ - base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, - VelocityComponent, -}; -use crate::lazy::LazyUpdateExt; -use crate::physics::PhysicsBuilder; -use feather_core::entity::{AnimalData, EntityData}; -use feather_core::Packet; -use specs::world::{EntitiesRes, LazyBuilder}; -use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; - -#[derive(Default)] -pub struct CowComponent; - -impl Component for CowComponent { - type Storage = NullStorage; -} - -pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { - lazy.spawn_entity(entities) - .with(CowComponent) - .with(PhysicsBuilder::for_living().bbox(0.9, 1.4, 0.9).build()) - .with(PacketCreatorComponent(&create_packet)) - .with(SerializerComponent(&serialize)) -} - -pub fn create_from_data( - lazy: &LazyUpdate, - entities: &EntitiesRes, - data: &AnimalData, -) -> Option { - let position = data.base.read_position()?; - let velocity = data.base.read_velocity()?; - - Some( - create(lazy, entities) - .with(PositionComponent { - current: position, - previous: position, - }) - .with(VelocityComponent(velocity)) - .build(), - ) -} - -fn create_packet(world: &World, entity: Entity) -> Box { - create_mob_packet(world, entity, 9) -} - -fn serialize(world: &World, entity: Entity) -> EntityData { - let base = base_data(world, entity); - EntityData::Cow(AnimalData { base }) -} diff --git a/server/src/entity/impls/animal/donkey.rs b/server/src/entity/impls/animal/donkey.rs deleted file mode 100644 index c7918ffcb..000000000 --- a/server/src/entity/impls/animal/donkey.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::entity::{ - base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, - VelocityComponent, -}; -use crate::lazy::LazyUpdateExt; -use crate::physics::PhysicsBuilder; -use feather_core::entity::{AnimalData, EntityData}; -use feather_core::Packet; -use specs::world::{EntitiesRes, LazyBuilder}; -use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; - -#[derive(Default)] -pub struct DonkeyComponent; - -impl Component for DonkeyComponent { - type Storage = NullStorage; -} - -pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { - lazy.spawn_entity(entities) - .with(DonkeyComponent) - .with( - PhysicsBuilder::for_living() - .bbox(1.396_484_4, 1.6, 1.396_484_4) - .build(), - ) - .with(PacketCreatorComponent(&create_packet)) - .with(SerializerComponent(&serialize)) -} - -pub fn create_from_data( - lazy: &LazyUpdate, - entities: &EntitiesRes, - data: &AnimalData, -) -> Option { - let position = data.base.read_position()?; - let velocity = data.base.read_velocity()?; - - Some( - create(lazy, entities) - .with(PositionComponent { - current: position, - previous: position, - }) - .with(VelocityComponent(velocity)) - .build(), - ) -} - -fn create_packet(world: &World, entity: Entity) -> Box { - create_mob_packet(world, entity, 11) -} - -fn serialize(world: &World, entity: Entity) -> EntityData { - let base = base_data(world, entity); - EntityData::Donkey(AnimalData { base }) -} diff --git a/server/src/entity/impls/animal/horse.rs b/server/src/entity/impls/animal/horse.rs deleted file mode 100644 index 7b5d6f82f..000000000 --- a/server/src/entity/impls/animal/horse.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::entity::{ - base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, - VelocityComponent, -}; -use crate::lazy::LazyUpdateExt; -use crate::physics::PhysicsBuilder; -use feather_core::entity::{AnimalData, EntityData}; -use feather_core::Packet; -use specs::world::{EntitiesRes, LazyBuilder}; -use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; - -#[derive(Default)] -pub struct HorseComponent; - -impl Component for HorseComponent { - type Storage = NullStorage; -} - -pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { - lazy.spawn_entity(entities) - .with(HorseComponent) - .with( - PhysicsBuilder::for_living() - .bbox(1.396_484_4, 1.6, 1.396_484_4) - .build(), - ) - .with(PacketCreatorComponent(&create_packet)) - .with(SerializerComponent(&serialize)) -} - -pub fn create_from_data( - lazy: &LazyUpdate, - entities: &EntitiesRes, - data: &AnimalData, -) -> Option { - let position = data.base.read_position()?; - let velocity = data.base.read_velocity()?; - - Some( - create(lazy, entities) - .with(PositionComponent { - current: position, - previous: position, - }) - .with(VelocityComponent(velocity)) - .build(), - ) -} - -fn create_packet(world: &World, entity: Entity) -> Box { - create_mob_packet(world, entity, 29) -} - -fn serialize(world: &World, entity: Entity) -> EntityData { - let base = base_data(world, entity); - EntityData::Horse(AnimalData { base }) -} diff --git a/server/src/entity/impls/animal/llama.rs b/server/src/entity/impls/animal/llama.rs deleted file mode 100644 index 29f06e68a..000000000 --- a/server/src/entity/impls/animal/llama.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::entity::{ - base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, - VelocityComponent, -}; -use crate::lazy::LazyUpdateExt; -use crate::physics::PhysicsBuilder; -use feather_core::entity::{AnimalData, EntityData}; -use feather_core::Packet; -use specs::world::{EntitiesRes, LazyBuilder}; -use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; - -#[derive(Default)] -pub struct LlamaComponent; - -impl Component for LlamaComponent { - type Storage = NullStorage; -} - -pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { - lazy.spawn_entity(entities) - .with(LlamaComponent) - .with(PhysicsBuilder::for_living().bbox(0.9, 1.87, 0.9).build()) - .with(PacketCreatorComponent(&create_packet)) - .with(SerializerComponent(&serialize)) -} - -pub fn create_from_data( - lazy: &LazyUpdate, - entities: &EntitiesRes, - data: &AnimalData, -) -> Option { - let position = data.base.read_position()?; - let velocity = data.base.read_velocity()?; - - Some( - create(lazy, entities) - .with(PositionComponent { - current: position, - previous: position, - }) - .with(VelocityComponent(velocity)) - .build(), - ) -} - -fn create_packet(world: &World, entity: Entity) -> Box { - create_mob_packet(world, entity, 36) -} - -fn serialize(world: &World, entity: Entity) -> EntityData { - let base = base_data(world, entity); - EntityData::Llama(AnimalData { base }) -} diff --git a/server/src/entity/impls/animal/mod.rs b/server/src/entity/impls/animal/mod.rs deleted file mode 100644 index 89ede779b..000000000 --- a/server/src/entity/impls/animal/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! Implementations for animals: cows, pigs, chickens, etc. - -pub mod chicken; -pub mod cow; -pub mod donkey; -pub mod horse; -pub mod llama; -pub mod mooshroom; -pub mod pig; -pub mod rabbit; -pub mod sheep; -pub mod squid; diff --git a/server/src/entity/impls/animal/mooshroom.rs b/server/src/entity/impls/animal/mooshroom.rs deleted file mode 100644 index 830a147bf..000000000 --- a/server/src/entity/impls/animal/mooshroom.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::entity::{ - base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, - VelocityComponent, -}; -use crate::lazy::LazyUpdateExt; -use crate::physics::PhysicsBuilder; -use feather_core::entity::{AnimalData, EntityData}; -use feather_core::Packet; -use specs::world::{EntitiesRes, LazyBuilder}; -use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; - -#[derive(Default)] -pub struct MooshroomComponent; - -impl Component for MooshroomComponent { - type Storage = NullStorage; -} - -pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { - lazy.spawn_entity(entities) - .with(MooshroomComponent) - .with(PhysicsBuilder::for_living().bbox(0.9, 1.4, 0.9).build()) - .with(PacketCreatorComponent(&create_packet)) - .with(SerializerComponent(&serialize)) -} - -fn create_packet(world: &World, entity: Entity) -> Box { - create_mob_packet(world, entity, 47) -} - -pub fn create_from_data( - lazy: &LazyUpdate, - entities: &EntitiesRes, - data: &AnimalData, -) -> Option { - let position = data.base.read_position()?; - let velocity = data.base.read_velocity()?; - - Some( - create(lazy, entities) - .with(PositionComponent { - current: position, - previous: position, - }) - .with(VelocityComponent(velocity)) - .build(), - ) -} - -fn serialize(world: &World, entity: Entity) -> EntityData { - let base = base_data(world, entity); - EntityData::Mooshroom(AnimalData { base }) -} diff --git a/server/src/entity/impls/animal/pig.rs b/server/src/entity/impls/animal/pig.rs deleted file mode 100644 index cfa88d29d..000000000 --- a/server/src/entity/impls/animal/pig.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::entity::{ - base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, - VelocityComponent, -}; -use crate::lazy::LazyUpdateExt; -use crate::physics::PhysicsBuilder; -use feather_core::entity::{AnimalData, EntityData}; -use feather_core::Packet; -use specs::world::{EntitiesRes, LazyBuilder}; -use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; - -#[derive(Default)] -pub struct PigComponent; - -impl Component for PigComponent { - type Storage = NullStorage; -} - -pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { - lazy.spawn_entity(entities) - .with(PigComponent) - .with(PhysicsBuilder::for_living().bbox(0.9, 0.9, 0.9).build()) - .with(PacketCreatorComponent(&create_packet)) - .with(SerializerComponent(&serialize)) -} - -pub fn create_from_data( - lazy: &LazyUpdate, - entities: &EntitiesRes, - data: &AnimalData, -) -> Option { - let position = data.base.read_position()?; - let velocity = data.base.read_velocity()?; - - Some( - create(lazy, entities) - .with(PositionComponent { - current: position, - previous: position, - }) - .with(VelocityComponent(velocity)) - .build(), - ) -} - -fn create_packet(world: &World, entity: Entity) -> Box { - create_mob_packet(world, entity, 51) -} - -fn serialize(world: &World, entity: Entity) -> EntityData { - let base = base_data(world, entity); - EntityData::Pig(AnimalData { base }) -} diff --git a/server/src/entity/impls/animal/rabbit.rs b/server/src/entity/impls/animal/rabbit.rs deleted file mode 100644 index b247c2f83..000000000 --- a/server/src/entity/impls/animal/rabbit.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::entity::{ - base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, - VelocityComponent, -}; -use crate::lazy::LazyUpdateExt; -use crate::physics::PhysicsBuilder; -use feather_core::entity::{AnimalData, EntityData}; -use feather_core::Packet; -use specs::world::{EntitiesRes, LazyBuilder}; -use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; - -#[derive(Default)] -pub struct RabbitComponent; - -impl Component for RabbitComponent { - type Storage = NullStorage; -} - -pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { - lazy.spawn_entity(entities) - .with(RabbitComponent) - .with(PhysicsBuilder::for_living().bbox(0.4, 0.5, 0.4).build()) - .with(PacketCreatorComponent(&create_packet)) - .with(SerializerComponent(&serialize)) -} - -pub fn create_from_data( - lazy: &LazyUpdate, - entities: &EntitiesRes, - data: &AnimalData, -) -> Option { - let position = data.base.read_position()?; - let velocity = data.base.read_velocity()?; - - Some( - create(lazy, entities) - .with(PositionComponent { - current: position, - previous: position, - }) - .with(VelocityComponent(velocity)) - .build(), - ) -} - -fn create_packet(world: &World, entity: Entity) -> Box { - create_mob_packet(world, entity, 56) -} - -fn serialize(world: &World, entity: Entity) -> EntityData { - let base = base_data(world, entity); - EntityData::Rabbit(AnimalData { base }) -} diff --git a/server/src/entity/impls/animal/sheep.rs b/server/src/entity/impls/animal/sheep.rs deleted file mode 100644 index 934456885..000000000 --- a/server/src/entity/impls/animal/sheep.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::entity::{ - base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, - VelocityComponent, -}; -use crate::lazy::LazyUpdateExt; -use crate::physics::PhysicsBuilder; -use feather_core::entity::{AnimalData, EntityData}; -use feather_core::Packet; -use specs::world::{EntitiesRes, LazyBuilder}; -use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; - -#[derive(Default)] -pub struct SheepComponent; - -impl Component for SheepComponent { - type Storage = NullStorage; -} - -pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { - lazy.spawn_entity(entities) - .with(SheepComponent) - .with(PhysicsBuilder::for_living().bbox(0.9, 1.3, 0.9).build()) - .with(PacketCreatorComponent(&create_packet)) - .with(SerializerComponent(&serialize)) -} - -pub fn create_from_data( - lazy: &LazyUpdate, - entities: &EntitiesRes, - data: &AnimalData, -) -> Option { - let position = data.base.read_position()?; - let velocity = data.base.read_velocity()?; - - Some( - create(lazy, entities) - .with(PositionComponent { - current: position, - previous: position, - }) - .with(VelocityComponent(velocity)) - .build(), - ) -} - -fn create_packet(world: &World, entity: Entity) -> Box { - create_mob_packet(world, entity, 58) -} - -fn serialize(world: &World, entity: Entity) -> EntityData { - let base = base_data(world, entity); - EntityData::Sheep(AnimalData { base }) -} diff --git a/server/src/entity/impls/animal/squid.rs b/server/src/entity/impls/animal/squid.rs deleted file mode 100644 index 76bd309d7..000000000 --- a/server/src/entity/impls/animal/squid.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::entity::{ - base_data, create_mob_packet, PacketCreatorComponent, PositionComponent, SerializerComponent, - VelocityComponent, -}; -use crate::lazy::LazyUpdateExt; -use crate::physics::PhysicsBuilder; -use feather_core::entity::{AnimalData, EntityData}; -use feather_core::Packet; -use specs::world::{EntitiesRes, LazyBuilder}; -use specs::{Builder, Component, Entity, LazyUpdate, NullStorage, World}; - -#[derive(Default)] -pub struct SquidComponent; - -impl Component for SquidComponent { - type Storage = NullStorage; -} - -pub fn create<'a>(lazy: &'a LazyUpdate, entities: &'a EntitiesRes) -> LazyBuilder<'a> { - lazy.spawn_entity(entities) - .with(SquidComponent) - .with(PhysicsBuilder::for_living().bbox(0.8, 0.8, 0.8).build()) - .with(PacketCreatorComponent(&create_packet)) - .with(SerializerComponent(&serialize)) -} - -pub fn create_from_data( - lazy: &LazyUpdate, - entities: &EntitiesRes, - data: &AnimalData, -) -> Option { - let position = data.base.read_position()?; - let velocity = data.base.read_velocity()?; - - Some( - create(lazy, entities) - .with(PositionComponent { - current: position, - previous: position, - }) - .with(VelocityComponent(velocity)) - .build(), - ) -} - -fn create_packet(world: &World, entity: Entity) -> Box { - create_mob_packet(world, entity, 70) -} - -fn serialize(world: &World, entity: Entity) -> EntityData { - let base = base_data(world, entity); - EntityData::Squid(AnimalData { base }) -} diff --git a/server/src/entity/impls/arrow.rs b/server/src/entity/impls/arrow.rs deleted file mode 100644 index a9754583f..000000000 --- a/server/src/entity/impls/arrow.rs +++ /dev/null @@ -1,168 +0,0 @@ -use shrev::EventChannel; -use specs::{ - Builder, Component, Entities, Entity, LazyUpdate, NullStorage, Read, ReaderId, System, World, - WorldExt, -}; - -use feather_core::packet::SpawnObject; -use feather_core::{Item, Packet, Position}; - -use crate::entity::component::{PacketCreatorComponent, SerializerComponent}; -use crate::entity::metadata::Metadata; -use crate::entity::movement::degrees_to_stops; -use crate::entity::{PositionComponent, VelocityComponent}; -use crate::lazy::LazyUpdateExt; -use crate::physics::PhysicsBuilder; -use crate::player::PLAYER_EYE_HEIGHT; -use crate::util::protocol_velocity; -use feather_core::entity::{ArrowEntityData, BaseEntityData, EntityData}; -use specs::world::{EntitiesRes, LazyBuilder}; -use uuid::Uuid; - -/// Component for arrow entities. -#[derive(Default)] -pub struct ArrowComponent; - -impl Component for ArrowComponent { - type Storage = NullStorage; -} - -/// Event triggered when arrow is shot. -#[derive(Debug, Clone)] -pub struct ShootArrowEvent { - pub arrow_type: Item, - pub shooter: Option, - pub position: Position, - pub critical: bool, -} - -#[derive(Default)] -pub struct ShootArrowSystem { - reader: Option>, -} - -impl<'a> System<'a> for ShootArrowSystem { - type SystemData = ( - Read<'a, LazyUpdate>, - Read<'a, EventChannel>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (lazy, shoot_arrow_events, entities) = data; - - for event in shoot_arrow_events.read(self.reader.as_mut().unwrap()) { - let mut pos = event.position - + glm::vec3(0.0, PLAYER_EYE_HEIGHT, 0.0) - + event.position.direction() * 1.5; - pos.on_ground = false; - - // TODO: Scale velocity based on power - let velocity = pos.direction(); - - // TODO: shooter - - create(&lazy, &entities, false) - .with(PositionComponent { - current: pos, - previous: pos, - }) - .with(VelocityComponent(velocity)) - .build(); - } - } - - setup_impl!(reader); -} - -pub fn create<'a>(lazy: &'a LazyUpdate, entities: &EntitiesRes, critical: bool) -> LazyBuilder<'a> { - let meta = { - let mut meta_arrow = crate::entity::metadata::Arrow::default(); - let mask = if critical { - crate::entity::metadata::ArrowBitMask::CRITICAL - } else { - crate::entity::metadata::ArrowBitMask::default() - }; - meta_arrow.set_arrow_bit_mask(mask.bits()); - // meta_arrow.set_shooter(shooter); TODO - Metadata::Arrow(meta_arrow) - }; - - lazy.spawn_entity(entities) - .with(ArrowComponent) - .with( - PhysicsBuilder::new() - .bbox(0.5, 0.5, 0.5) - .gravity(-0.05) - .drag(0.99) - .slip_multiplier(0.0) - .build(), - ) - .with(meta) - .with(PacketCreatorComponent(&create_packet)) - .with(SerializerComponent(&serialize)) -} - -pub fn create_from_data( - lazy: &LazyUpdate, - entities: &EntitiesRes, - data: &ArrowEntityData, -) -> Option { - let pos = data.entity.read_position()?; - let vel = data.entity.read_velocity()?; - - let critical = match data.critical { - 0 => false, - _ => true, - }; - - // TODO: load other attributes - - Some( - create(lazy, entities, critical) - .with(PositionComponent { - current: pos, - previous: pos, - }) - .with(VelocityComponent(vel)) - .build(), - ) -} - -fn create_packet(world: &World, entity: Entity) -> Box { - let positions = world.read_component::(); - let velocities = world.read_component::(); - - let position = positions.get(entity).unwrap().current; - let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocities.get(entity).unwrap().0); - - let packet = SpawnObject { - entity_id: entity.id() as i32, - object_uuid: Uuid::new_v4(), // TODO - ty: 60, - x: position.x, - y: position.y, - z: position.z, - pitch: degrees_to_stops(position.pitch), - yaw: degrees_to_stops(position.yaw), - data: 1, // TODO: Shooter entity ID - velocity_x, - velocity_y, - velocity_z, - }; - - Box::new(packet) -} - -fn serialize(world: &World, entity: Entity) -> EntityData { - let positions = world.read_component::(); - let velocities = world.read_component::(); - - EntityData::Arrow(ArrowEntityData { - entity: BaseEntityData::new( - positions.get(entity).unwrap().current, - velocities.get(entity).unwrap().0, - ), - critical: 0, // TODO - }) -} diff --git a/server/src/entity/impls/falling_block.rs b/server/src/entity/impls/falling_block.rs deleted file mode 100644 index 18c59a2cf..000000000 --- a/server/src/entity/impls/falling_block.rs +++ /dev/null @@ -1,142 +0,0 @@ -use shrev::ReaderId; -use specs::shrev::EventChannel; -use specs::{ - Builder, Component, DenseVecStorage, Entity, LazyUpdate, Read, ReadStorage, System, World, - WorldExt, Write, -}; - -use feather_blocks::{Block, BlockExt}; -use feather_core::packet::SpawnObject; -use feather_core::world::ChunkMap; - -use crate::blocks::{BlockUpdateCause, BlockUpdateEvent}; -use crate::entity::component::PacketCreatorComponent; -use crate::entity::metadata::Metadata; -use crate::entity::movement::degrees_to_stops; -use crate::entity::{EntityDestroyEvent, PositionComponent, VelocityComponent}; -use crate::lazy::LazyUpdateExt; -use crate::physics::{EntityPhysicsLandEvent, PhysicsBuilder}; -use crate::util::protocol_velocity; -use feather_core::{Packet, Position}; -use specs::world::{EntitiesRes, LazyBuilder}; -use uuid::Uuid; - -/// Component for falling block entities. -pub struct FallingBlockComponent { - pub block: Block, -} - -impl Default for FallingBlockComponent { - fn default() -> Self { - FallingBlockComponent { - block: Block::Stone, - } - } -} - -impl Component for FallingBlockComponent { - type Storage = DenseVecStorage; -} - -/// This system listens to `EntityPhysicsLandEvent`s. -#[derive(Default)] -pub struct FallingBlockLandSystem { - reader: Option>, -} - -/// System for handling when a falling block lands -/// on the ground, destroying the entity and setting the block. -impl<'a> System<'a> for FallingBlockLandSystem { - type SystemData = ( - Read<'a, EventChannel>, - ReadStorage<'a, FallingBlockComponent>, - Write<'a, EventChannel>, - Write<'a, EventChannel>, - Write<'a, ChunkMap>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (events, falling_blocks, mut destroy_events, mut block_updates, mut chunk_map) = data; - - // Process events - for event in events.read(&mut self.reader.as_mut().unwrap()) { - let entity = event.entity; - - let falling_block = match falling_blocks.get(entity) { - Some(block) => block, - None => continue, // Not a falling block - }; - - let destroy_event = EntityDestroyEvent { entity }; - destroy_events.single_write(destroy_event); - - let pos = event.pos.block_pos(); - let old_block = chunk_map.block_at(pos).unwrap(); - chunk_map.set_block_at(pos, falling_block.block).unwrap(); - - let update_event = BlockUpdateEvent { - cause: BlockUpdateCause::FallingBlock, - pos, - old_block, - new_block: falling_block.block, - }; - - block_updates.single_write(update_event); - } - } - - setup_impl!(reader); -} - -pub fn create<'a>( - lazy: &'a LazyUpdate, - entities: &EntitiesRes, - block: Block, - position: Position, -) -> LazyBuilder<'a> { - let meta = { - let mut meta_falling_block = crate::entity::metadata::FallingBlock::default(); - meta_falling_block.set_spawn_position(position.block_pos()); - Metadata::FallingBlock(meta_falling_block) - }; - - lazy.spawn_entity(entities) - .with(FallingBlockComponent { block }) - .with( - PhysicsBuilder::new() - .gravity(-0.04) - .drag(0.98) - .bbox(0.98, 0.98, 0.98) - .build(), - ) - .with(meta) - .with(PacketCreatorComponent(&create_packet)) - //.with(SerializerComponent(&serialize)) TODO -} - -fn create_packet(world: &World, entity: Entity) -> Box { - let blocks = world.read_component::(); - let positions = world.read_component::(); - let velocities = world.read_component::(); - - let block = blocks.get(entity).unwrap().block.native_state_id(); - let position = positions.get(entity).unwrap().current; - let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocities.get(entity).unwrap().0); - - let packet = SpawnObject { - entity_id: entity.id() as i32, - object_uuid: Uuid::new_v4(), - ty: 70, - x: position.x, - y: position.y, - z: position.z, - pitch: degrees_to_stops(position.pitch), - yaw: degrees_to_stops(position.yaw), - data: i32::from(block), - velocity_x, - velocity_y, - velocity_z, - }; - - Box::new(packet) -} diff --git a/server/src/entity/impls/item.rs b/server/src/entity/impls/item.rs deleted file mode 100644 index edc54b881..000000000 --- a/server/src/entity/impls/item.rs +++ /dev/null @@ -1,582 +0,0 @@ -//! Logic for working with item entities. -use crate::entity::metadata::{self, Metadata}; -use crate::entity::{ - ChunkEntities, EntityDestroyEvent, PlayerComponent, PositionComponent, VelocityComponent, -}; -use crate::physics::{nearby_entities, PhysicsBuilder}; -use crate::player::{ - InventoryComponent, InventoryUpdateEvent, PlayerItemDropEvent, PLAYER_EYE_HEIGHT, -}; -use crate::util::{protocol_velocity, Util}; -use crate::{TickCount, TPS}; -use feather_core::network::packet::implementation::CollectItem; -use feather_core::{Item, ItemStack, Packet}; -use rand::Rng; -use shrev::EventChannel; -use smallvec::SmallVec; -use specs::storage::ComponentEvent; -use specs::{ - BitSet, Builder, Component, DenseVecStorage, Entities, Entity, Join, LazyUpdate, Read, - ReadStorage, ReaderId, System, SystemData, World, WorldExt, Write, WriteStorage, -}; - -use crate::entity::component::{PacketCreatorComponent, SerializerComponent}; -use crate::entity::movement::degrees_to_stops; -use crate::lazy::LazyUpdateExt; -use feather_core::entity::{BaseEntityData, EntityData, ItemData, ItemEntityData}; -use feather_core::packet::SpawnObject; -use specs::world::{EntitiesRes, LazyBuilder}; -use uuid::Uuid; - -/// Component for item entities. -pub struct ItemComponent { - /// The tick at which this item is collectable - /// by a player. - pub collectable_at: u64, - /// This item's stack. - pub stack: ItemStack, -} - -impl Component for ItemComponent { - type Storage = DenseVecStorage; -} - -/// System for spawning an item entity when -/// an item is dropped. -/// -/// This system listens to `PlayerItemDropEvent`s. -#[derive(Default)] -pub struct ItemSpawnSystem { - reader: Option>, -} - -impl<'a> System<'a> for ItemSpawnSystem { - type SystemData = ( - ReadStorage<'a, PositionComponent>, - Read<'a, LazyUpdate>, - Entities<'a>, - Read<'a, EventChannel>, - Read<'a, TickCount>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (positions, lazy, entities, item_drop_events, tick) = data; - - let mut rng = rand::thread_rng(); - - for event in item_drop_events.read(self.reader.as_mut().unwrap()) { - // Spawn item entity. - - // Position is player's eye height minus 0.3 - let mut pos = { - let player_pos = positions.get(event.player).unwrap().current - + glm::vec3(0.0, PLAYER_EYE_HEIGHT, 0.0); - player_pos - glm::vec3(0.0f64, 0.3, 0.0) - }; - - pos.on_ground = false; - - // This velocity calculation was sourced from Glowstone's - // work. See https://github.com/GlowstoneMC/Glowstone/blob/dev/src/main/java/net/glowstone/entity/GlowHumanEntity.java - // (method drop(ItemStack stack)) for their code. - let velocity = { - let mut vel = pos.direction() * 0.3; - let rand_offset = 0.02; - - let x = rng.gen_range(0.0, rand_offset) - rand_offset / 2.0; - let y = rng.gen_range(0.0, 0.12); - let z = rng.gen_range(0.0, rand_offset) - rand_offset / 2.0; - - vel += glm::vec3(x, y, z); - - vel - }; - - create(&lazy, &entities, event.stack.clone(), tick.0 + TPS) - .with(PositionComponent { - current: pos, - previous: pos, - }) - .with(VelocityComponent(velocity)) - .build(); - } - } - - fn setup(&mut self, world: &mut World) { - Self::SystemData::setup(world); - - self.reader = Some(world.fetch_mut::>().register_reader()); - } -} - -/// System for merging item entities of the same -/// type. -#[derive(Default)] -pub struct ItemMergeSystem { - dirty: BitSet, - reader: Option>, -} - -impl<'a> System<'a> for ItemMergeSystem { - type SystemData = ( - ReadStorage<'a, PositionComponent>, - ReadStorage<'a, ItemComponent>, - WriteStorage<'a, Metadata>, - Write<'a, EventChannel>, - Read<'a, ChunkEntities>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (positions, item_markers, mut metadatas, mut destroy_events, chunk_entities, entities) = - data; - - self.dirty.clear(); - - for event in positions.channel().read(self.reader.as_mut().unwrap()) { - match event { - ComponentEvent::Modified(id) | ComponentEvent::Inserted(id) => { - self.dirty.add(*id); - } - _ => (), - } - } - - let mut metadatas_to_update: SmallVec<[(Entity, Metadata); 2]> = smallvec![]; - // Used to not destroy both entities - let mut destroyed: SmallVec<[Entity; 2]> = smallvec![]; - - for (position, entity, _, _) in (&positions, &entities, &item_markers, &self.dirty).join() { - if !entities.is_alive(entity) { - continue; - } - - if destroyed.iter().any(|x| *x == entity) { - continue; - } - - let mut stack = item_stack_from_meta(metadatas.get(entity).unwrap()); - - // Find nearby entities and check if they are of the same item - // type. If so, merge the two item stacks. - let nearby = nearby_entities( - &chunk_entities, - &positions, - position.current, - glm::vec3(1.0, 0.5, 1.0), - ); - - for other in nearby { - // Skip entity if it's dead. - if !entities.is_alive(other) { - continue; - } - - if other == entity { - continue; - } - - // Skip if it's not an item. - if item_markers.get(other).is_none() { - continue; - } - - let other_stack = item_stack_from_meta(metadatas.get(other).unwrap()); - - if other_stack.ty != stack.ty { - continue; - } - - // Merge two stacks. - // This works by deleting `other` and adding - // together the amounts of the two item stacks. - entities.delete(other).unwrap(); - - let event = EntityDestroyEvent { entity: other }; - destroy_events.single_write(event); - - // TODO this could overflow... - stack.amount += other_stack.amount; - - metadatas_to_update.push((entity, item_meta(stack.clone()))); - destroyed.push(other); - } - } - - metadatas_to_update.into_iter().for_each(|(entity, meta)| { - metadatas.insert(entity, meta).unwrap(); - }); - } - - flagged_setup_impl!(PositionComponent, reader); -} - -/// System for collecting items when a player comes -/// near them. -#[derive(Default)] -pub struct ItemCollectSystem { - dirty: BitSet, - reader: Option>, -} - -impl<'a> System<'a> for ItemCollectSystem { - type SystemData = ( - WriteStorage<'a, InventoryComponent>, - ReadStorage<'a, PositionComponent>, - ReadStorage<'a, PlayerComponent>, - ReadStorage<'a, ItemComponent>, - WriteStorage<'a, Metadata>, - Write<'a, EventChannel>, - Write<'a, EventChannel>, - Read<'a, ChunkEntities>, - Read<'a, Util>, - Read<'a, TickCount>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - mut inventories, - positions, - players, - items, - mut metadatas, - mut inventory_events, - mut destroy_events, - chunk_entities, - util, - tick, - entities, - ) = data; - - self.dirty.clear(); - - read_flagged_events!(positions, self.reader, self.dirty); - - // For each player who has moved this tick, - // look for nearby items. - // We need to keep track of which items - // have already been collected to avoid - // having the same item being collected - // by two players at once; this would - // cause dupe exploits. - let mut collected_items: SmallVec<[Entity; 4]> = smallvec![]; - - for (position, inventory, player, _, _) in ( - &positions, - &mut inventories, - &entities, - &players, - &self.dirty, - ) - .join() - { - let nearby = nearby_entities( - &chunk_entities, - &positions, - position.current, - glm::vec3(1.0, 0.5, 1.0), - ); - - for other in nearby { - // If it's not an item, skip. - let item = continue_if_none!(items.get(other)); - - // Check if the item can be picked up yet. - if item.collectable_at > tick.0 { - continue; - } - - // If the item has already been collected, don't try it. - if collected_items.iter().any(|x| *x == other) { - continue; - } - - // Attempt to collect the item. - let mut stack = item_stack_from_meta(metadatas.get(other).unwrap()); - let (affected_slots, amount_left) = inventory.collect_item(stack.clone()); - - // Broadcast Collect Item packet, which gives an animation. - let packet = CollectItem { - collected: other.id() as i32, - collector: player.id() as i32, - count: i32::from(stack.amount - amount_left), - }; - util.broadcast_entity_update(player, packet, None); - - if amount_left == 0 { - entities.delete(other).unwrap(); - collected_items.push(other); - - let event = EntityDestroyEvent { entity: other }; - destroy_events.single_write(event); - } else { - stack.amount = amount_left; - let meta = item_meta(stack); - metadatas.insert(other, meta).unwrap(); - } - - // Trigger inventory update event. - let event = InventoryUpdateEvent { - slots: affected_slots, - player, - }; - inventory_events.single_write(event); - } - } - } - - flagged_setup_impl!(PositionComponent, reader); -} - -pub fn create<'a>( - lazy: &'a LazyUpdate, - entities: &EntitiesRes, - stack: ItemStack, - collectable_at: u64, -) -> LazyBuilder<'a> { - let meta = { - let mut meta_item = crate::entity::metadata::Item::default(); - meta_item.set_item(Some(stack.clone())); - Metadata::Item(meta_item) - }; - - lazy.spawn_entity(entities) - .with(ItemComponent { - stack, - collectable_at, - }) - .with( - PhysicsBuilder::new() - .bbox(0.25, 0.25, 0.25) - .gravity(-0.04) - .drag(0.98) - .build(), - ) - .with(VelocityComponent::default()) - .with(meta) - .with(PacketCreatorComponent(&create_packet)) - .with(SerializerComponent(&serialize)) -} - -pub fn create_from_data( - lazy: &LazyUpdate, - entities: &EntitiesRes, - data: &ItemEntityData, - tick: &TickCount, -) -> Option { - let pos = data.entity.read_position()?; - let vel = data.entity.read_velocity()?; - - let stack = ItemStack::new(Item::from_identifier(&data.item.item)?, data.item.count); - - let collectable_at = data.pickup_delay as u64 + tick.0; - - Some( - create(lazy, entities, stack, collectable_at) - .with(PositionComponent { - current: pos, - previous: pos, - }) - .with(VelocityComponent(vel)) - .build(), - ) -} - -fn create_packet(world: &World, entity: Entity) -> Box { - let positions = world.read_component::(); - let velocities = world.read_component::(); - - let position = positions.get(entity).unwrap().current; - let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocities.get(entity).unwrap().0); - - let packet = SpawnObject { - entity_id: entity.id() as i32, - object_uuid: Uuid::new_v4(), - ty: 2, // Type 2 for item stack - x: position.x, - y: position.y, - z: position.z, - pitch: degrees_to_stops(position.pitch), - yaw: degrees_to_stops(position.yaw), - data: 1, // Has velocity - velocity_x, - velocity_y, - velocity_z, - }; - - Box::new(packet) -} - -fn serialize(world: &World, entity: Entity) -> EntityData { - let positions = world.read_component::(); - let velocities = world.read_component::(); - let items = world.read_component::(); - - let item = items.get(entity).unwrap(); - let position = positions.get(entity).unwrap(); - let velocity = velocities.get(entity).unwrap(); - - EntityData::Item(ItemEntityData { - entity: BaseEntityData::new(position.current, velocity.0), - age: 0, // TODO - pickup_delay: 0, // TODO - item: ItemData { - item: item.stack.ty.identifier().to_string(), - count: item.stack.amount, - }, - }) -} - -pub fn item_stack_from_meta(meta: &Metadata) -> ItemStack { - match meta { - Metadata::Item(item) => item.item().unwrap(), - _ => panic!(), - } -} - -pub fn item_meta(stack: ItemStack) -> Metadata { - let mut item = metadata::Item::default(); - item.set_item(Some(stack)); - Metadata::Item(item) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::entity::{ChunkEntityUpdateSystem, EntitySpawnEvent}; - use crate::testframework as t; - use feather_core::inventory::SLOT_HOTBAR_OFFSET; - use feather_core::network::cast_packet; - use feather_core::{Item, ItemStack, PacketType}; - use specs::WorldExt; - - #[test] - fn test_item_spawn_system() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - t::set_entity_pos(&w, player.entity, position!(0.0, 1.0, 0.0)); - - let stack = ItemStack::new(Item::AcaciaBoat, 4); - - let mut entity_spawn_reader = t::reader(&w); - - let event = PlayerItemDropEvent { - slot: None, - stack, - player: player.entity, - }; - t::trigger_event(&w, event); - - d.dispatch(&w); - w.maintain(); - - // Confirm event was triggered - let events = t::triggered_events::(&w, &mut entity_spawn_reader); - assert_eq!(events.len(), 1); - let first = events.first().unwrap(); - let entity = first.entity; - - // Check position - let pos = t::entity_pos(&w, entity); - assert_float_eq!(pos.x, 0.0); - assert_float_eq!(pos.z, 0.0); - - // Confirm that velocity was created - let _vel = t::entity_vel(&w, entity).unwrap(); - } - - #[test] - fn test_item_merge_system() { - let (mut w, mut d) = t::builder() - .with_dep(ItemMergeSystem::default(), "item_merge", &[]) - .build(); - - let item1 = create( - &w.fetch(), - &w.fetch(), - ItemStack::new(Item::EnderPearl, 4), - 0, - ) - .with(PositionComponent::default()) - .build(); - let item2 = create( - &w.fetch(), - &w.fetch(), - ItemStack::new(Item::EnderPearl, 7), - 0, - ) - .with(PositionComponent::default()) - .build(); - - let mut updater = ChunkEntityUpdateSystem::default(); - updater.setup(&mut w); - - w.maintain(); - - // Update chunk entities so `nearby_entities` works - specs::RunNow::run_now(&mut updater, &w); - - d.dispatch(&w); - w.maintain(); - - assert!(!w.is_alive(item2)); - assert!(w.is_alive(item1)); - - let metadatas = w.read_component::(); - let metadata = metadatas.get(item1).unwrap(); - - let stack = item_stack_from_meta(&metadata); - assert_eq!(stack.ty, Item::EnderPearl); - assert_eq!(stack.amount, 11); - } - - #[test] - fn test_item_collect_system() { - let (mut w, mut d) = t::builder() - .with_dep(ItemCollectSystem::default(), "", &[]) - .build(); - - let player = t::add_player(&mut w); - let stack = ItemStack::new(Item::String, 4); - let item = create(&w.fetch(), &w.fetch(), stack.clone(), 0) - .with(PositionComponent::default()) - .build(); - - let mut destroy_reader = t::reader(&w); - - // Allow item to be collected - w.fetch_mut::().0 = 0; - - let mut updater = ChunkEntityUpdateSystem::default(); - updater.setup(&mut w); - - w.maintain(); - - // Update chunk entities so `nearby_entities` works - - specs::RunNow::run_now(&mut updater, &w); - - d.dispatch(&w); - w.maintain(); - - let destroy_events = t::triggered_events::(&w, &mut destroy_reader); - let first = destroy_events.first().unwrap(); - assert_eq!(first.entity, item); - - assert!(!w.is_alive(item)); - - let inventories = w.read_component::(); - let inventory = inventories.get(player.entity).unwrap(); - - assert_eq!(inventory.item_at(SLOT_HOTBAR_OFFSET), Some(&stack)); - - let packet = t::assert_packet_received(&player, PacketType::CollectItem); - let packet = cast_packet::(&*packet); - - assert_eq!(packet.collector, player.entity.id() as i32); - assert_eq!(packet.collected, item.id() as i32); - assert_eq!(packet.count, 4); - } -} diff --git a/server/src/entity/impls/mod.rs b/server/src/entity/impls/mod.rs deleted file mode 100644 index f46a2d94d..000000000 --- a/server/src/entity/impls/mod.rs +++ /dev/null @@ -1,95 +0,0 @@ -//! Entity implementations. -//! -//! Every entity implementation is expected to define -//! the following functions: -//! -//! * `create(&LazyUpdate, &EntitiesRes) -> LazyBuilder`. When a system spawns an entity -//! of a known type, it should call this function on the `LazyBuilder` -//! returned by `LazyUpdate::spawn_entity` to apply components, such as markers, metadata, -//! `SerializerComponent`, and `SpawnPacketComponent`. This function may -//! take parameters. This function should not apply generic components, -//! such as position and velocity; the callee is responsible for this. -//! * `create_from_data(&LazyUpdate, &EntitiesRes, &{Entity}Data) -> Option`. Spawns an -//! entity loaded from the given entity data. If the entity creation failed, `None` is returned. -//! -//! These functions should be invoked in the form `name::function`, e.g. -//! `arrow::create` or `item::create_from_data`. -//! -//! Entity implementations should also define systems related to the entity: for -//! example, most entities will have an update system which updates an entity -//! on each tick. - -pub mod arrow; -pub mod falling_block; -pub mod item; - -mod animal; -pub use animal::*; - -use crate::entity::{ - degrees_to_stops, metadata::EMPTY_METADATA, Metadata, NamedComponent, PositionComponent, - VelocityComponent, -}; -use crate::util::protocol_velocity; -use feather_core::entity::BaseEntityData; -use feather_core::network::packet::implementation::SpawnMob; -use feather_core::Packet; -use specs::{Entity, World, WorldExt}; -use uuid::Uuid; - -#[cfg(test)] -pub mod test; - -/// Returns a `Spawn Mob` packet with the given entity type ID. -pub fn create_mob_packet(world: &World, entity: Entity, type_id: i32) -> Box { - let entity_id = entity.id() as i32; - let entity_uuid = world - .read_component::() - .get(entity) - .map(|named| named.uuid) - .unwrap_or_else(Uuid::new_v4); - - let positions = world.read_component::(); - let position = positions.get(entity).copied().unwrap_or_default(); - let velocities = world.read_component::(); - let velocity = velocities.get(entity).copied().unwrap_or_default(); - - let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocity.0); - - let metadatas = world.read_component::(); - let metadata = metadatas.get(entity).unwrap_or(&EMPTY_METADATA); - - let packet = SpawnMob { - entity_id, - entity_uuid, - ty: type_id, - 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), - head_pitch: degrees_to_stops(position.current.pitch), // FIXME: is this correct? - velocity_x, - velocity_y, - velocity_z, - meta: metadata.to_full_raw_metadata(), - }; - - Box::new(packet) -} - -/// Creates a `BaseEntityData` for the given entity. -pub fn base_data(world: &World, entity: Entity) -> BaseEntityData { - let position = world - .read_component::() - .get(entity) - .copied() - .unwrap_or_default(); - let velocity = world - .read_component::() - .get(entity) - .copied() - .unwrap_or_default(); - - BaseEntityData::new(position.current, velocity.0) -} diff --git a/server/src/entity/impls/test.rs b/server/src/entity/impls/test.rs deleted file mode 100644 index 21ef64b9f..000000000 --- a/server/src/entity/impls/test.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! A fake entity implementation for unit tests. - -use crate::entity::{EntitySpawnEvent, PositionComponent, VelocityComponent}; -use feather_core::Position; -use shrev::EventChannel; -use specs::{Builder, EntityBuilder, World, WorldExt}; - -pub fn create(world: &mut World, pos: Position) -> EntityBuilder { - let builder = world - .create_entity() - .with(PositionComponent { - current: pos, - previous: pos, - }) - .with(VelocityComponent(glm::vec3(0.0, 0.0, 0.0))); - builder - .world - .fetch_mut::>() - .single_write(EntitySpawnEvent { - entity: builder.entity, - }); - builder -} diff --git a/server/src/entity/metadata.rs b/server/src/entity/metadata.rs deleted file mode 100644 index 82f3d1c3f..000000000 --- a/server/src/entity/metadata.rs +++ /dev/null @@ -1,182 +0,0 @@ -//! Definition for entity metadata enum. - -#![allow(clippy::too_many_arguments)] // TODO: builder patterm - -use crate::util::Util; -use feather_core::packet::PacketEntityMetadata; -use feather_core::{BlockPosition, EntityMetadata, Slot}; -use specs::storage::ComponentEvent; -use specs::{ - BitSet, Component, Entities, FlaggedStorage, Join, Read, ReaderId, System, VecStorage, - WriteStorage, -}; -use uuid::Uuid; - -type OptUuid = Option; - -bitflags! { - pub struct EntityBitMask: u8 { - const ON_FIRE = 0x01; - const CROUCHED = 0x02; - const SPRITING = 0x08; - const SWIMMING = 0x10; - const INVISIBLE = 0x20; - const GLOWING_EFFECT = 0x40; - const FLYING_WITH_ELYTRA = 0x80; - } -} - -bitflags! { - #[derive(Default)] - pub struct ArrowBitMask: u8 { - const CRITICAL = 0x01; - const NO_CLIP = 0x02; - } -} - -lazy_static! { - pub static ref EMPTY_METADATA: Metadata = { Metadata::Entity(Entity::default()) }; -} - -entity_metadata! { - Metadata, - Entity { - bit_mask: u8() = 0, - air: VarInt() = 1, - silent: bool() = 4, - no_gravity: bool() = 5, - }, - Item: Entity { - item: Slot() = 6, - }, - Living: Entity { - hand_states: u8() = 6, - health: f32(1.0) = 7, - potion_effect_color: VarInt() = 8, - potion_effect_ambient: bool() = 9, - arrows: VarInt() = 10, - }, - Player: Living { - additional_hearts: f32() = 11, - score: VarInt() = 12, - displayed_skin_parts: u8() = 13, - main_hand: u8(1) = 14, - }, - Arrow: Entity { - arrow_bit_mask: u8() = 6, - shooter: OptUuid() = 7, - }, - TippedArrow: Arrow { - color: VarInt() = 8, - }, - FallingBlock: Entity { - spawn_position: BlockPosition() = 6, - }, -} - -impl Component for Metadata { - type Storage = FlaggedStorage>; -} - -/// System for broadcasting entity metadata updates. -#[derive(Default)] -pub struct MetadataBroadcastSystem { - dirty: BitSet, - reader: Option>, -} - -impl<'a> System<'a> for MetadataBroadcastSystem { - type SystemData = (WriteStorage<'a, Metadata>, Read<'a, Util>, Entities<'a>); - - fn run(&mut self, data: Self::SystemData) { - let (mut metadatas, util, entities) = data; - - self.dirty.clear(); - - read_flagged_events!(metadatas, self.reader, self.dirty); - - // Ensure that metadata update events are not - // triggered for this mutation of the storage. - metadatas.set_event_emission(false); - - // Go through updated metadata and broadcast changes. - for (metadata, entity, _) in (&mut metadatas, &entities, &self.dirty).join() { - let packet = PacketEntityMetadata { - entity_id: entity.id() as i32, - metadata: metadata.to_raw_metadata(), - }; - - util.broadcast_entity_update(entity, packet, None); - } - - metadatas.set_event_emission(true); - } - - flagged_setup_impl!(Metadata, reader); -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::entity::test; - use crate::testframework as t; - use feather_core::entitymeta::MetaEntry; - use feather_core::network::cast_packet; - use feather_core::PacketType; - use specs::{Builder, WorldExt}; - - #[test] - fn test_basic() { - let mut meta = Metadata::Entity(Entity::new( - (EntityBitMask::ON_FIRE | EntityBitMask::CROUCHED).bits(), - 0, - false, - false, - )); - - let raw = meta.to_raw_metadata(); - - assert_eq!(raw.get(0), Some(MetaEntry::Byte(0b0000_0011))); - assert_eq!(raw.get(5), Some(MetaEntry::Boolean(false))); - assert_eq!(raw.get(6), None); - } - - #[test] - fn test_inheritance() { - let _meta = Metadata::Item(Item::new( - (EntityBitMask::ON_FIRE).bits(), - 0, - false, - false, - None, - )); - } - - #[test] - fn test_metadata_update_system() { - let (mut w, mut d) = t::builder() - .with(MetadataBroadcastSystem::default(), "") - .build(); - - let entity = test::create(&mut w, position!(0.0, 0.0, 0.0)).build(); - - // Insert metadata - { - let mut metadatas = w.write_component::(); - metadatas - .insert(entity, Metadata::Entity(Entity::default())) - .unwrap(); - } - - let player = t::add_player(&mut w); - - d.dispatch(&w); - w.maintain(); - - // Ensure that packet was sent - let packet = t::assert_packet_received(&player, PacketType::EntityMetadata); - let packet = cast_packet::(&*packet); - - assert_eq!(packet.entity_id, entity.id() as i32); - } -} diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs deleted file mode 100644 index 9aa2ee75c..000000000 --- a/server/src/entity/mod.rs +++ /dev/null @@ -1,103 +0,0 @@ -//! Provides several useful components, including `EntityComponent` -//! and `PlayerComponent`. In the future, will also -//! provide entity-specific components and systems. - -mod broadcast; -mod chunk; -mod component; -mod destroy; -mod impls; -pub mod metadata; -mod movement; -mod save; - -pub use impls::*; - -use crate::systems::{ - BLOCK_FALLING_LANDING, CHUNK_CROSS, CHUNK_ENTITIES_LOAD, CHUNK_ENTITIES_UPDATE, CHUNK_SAVE, - ENTITY_DESTROY, ENTITY_DESTROY_BROADCAST, ENTITY_METADATA_BROADCAST, ENTITY_MOVE_BROADCAST, - ENTITY_PHYSICS, ENTITY_SPAWN_BROADCAST, ENTITY_VELOCITY_BROADCAST, ITEM_COLLECT, ITEM_MERGE, - ITEM_SPAWN, JOIN_BROADCAST, SHOOT_ARROW, -}; -pub use arrow::{ArrowComponent, ShootArrowEvent}; -pub use broadcast::send_entity_to_player; -pub use broadcast::{EntitySendEvent, EntitySpawnEvent}; -pub use chunk::ChunkEntities; -pub use chunk::ChunkEntityUpdateSystem; -pub use component::{ - NamedComponent, PacketCreatorComponent, PlayerComponent, PositionComponent, - SerializerComponent, VelocityComponent, -}; -pub use destroy::EntityDestroyEvent; -pub use falling_block::FallingBlockComponent; -pub use item::ItemComponent; -pub use metadata::{EntityBitMask, Metadata}; -pub use movement::{degrees_to_stops, LastKnownPositionComponent}; - -pub use save::save_chunks; - -use crate::entity::arrow::ShootArrowSystem; -use crate::entity::chunk::EntityChunkLoadSystem; -use crate::entity::destroy::EntityDestroyBroadcastSystem; -use crate::entity::falling_block::FallingBlockLandSystem; -use crate::entity::item::ItemCollectSystem; -use crate::entity::metadata::MetadataBroadcastSystem; -use crate::entity::save::ChunkSaveSystem; -use broadcast::EntityBroadcastSystem; -use component::ComponentResetSystem; -use destroy::EntityDestroySystem; -use item::{ItemMergeSystem, ItemSpawnSystem}; -use movement::{EntityMoveBroadcastSystem, EntityVelocityBroadcastSystem}; -use specs::DispatcherBuilder; - -pub fn init_logic(dispatcher: &mut DispatcherBuilder) { - dispatcher.add(ItemCollectSystem::default(), ITEM_COLLECT, &[]); -} - -pub fn init_handlers(dispatcher: &mut DispatcherBuilder) { - dispatcher.add( - ChunkEntityUpdateSystem::default(), - CHUNK_ENTITIES_UPDATE, - &[], - ); - dispatcher.add(EntityChunkLoadSystem::default(), CHUNK_ENTITIES_LOAD, &[]); - dispatcher.add(EntityDestroySystem::default(), ENTITY_DESTROY, &[]); - dispatcher.add(ItemSpawnSystem::default(), ITEM_SPAWN, &[]); - dispatcher.add(ItemMergeSystem::default(), ITEM_MERGE, &[]); - dispatcher.add( - MetadataBroadcastSystem::default(), - ENTITY_METADATA_BROADCAST, - &[], - ); - dispatcher.add(ShootArrowSystem::default(), SHOOT_ARROW, &[]); - dispatcher.add(ChunkSaveSystem::default(), CHUNK_SAVE, &[]); -} - -pub fn init_broadcast(dispatcher: &mut DispatcherBuilder) { - dispatcher.add( - EntityMoveBroadcastSystem::default(), - ENTITY_MOVE_BROADCAST, - &[], - ); - dispatcher.add( - EntityBroadcastSystem::default(), - ENTITY_SPAWN_BROADCAST, - &[JOIN_BROADCAST, CHUNK_CROSS], - ); - dispatcher.add( - EntityVelocityBroadcastSystem::default(), - ENTITY_VELOCITY_BROADCAST, - &[], - ); - dispatcher.add( - EntityDestroyBroadcastSystem::default(), - ENTITY_DESTROY_BROADCAST, - &[], - ); - dispatcher.add( - FallingBlockLandSystem::default(), - BLOCK_FALLING_LANDING, - &[ENTITY_PHYSICS], - ); - dispatcher.add_thread_local(ComponentResetSystem); -} diff --git a/server/src/entity/movement.rs b/server/src/entity/movement.rs deleted file mode 100644 index 34a4d25b9..000000000 --- a/server/src/entity/movement.rs +++ /dev/null @@ -1,264 +0,0 @@ -use specs::storage::ComponentEvent; -use specs::{ - BitSet, Component, DenseVecStorage, Entities, Entity, Join, Read, ReadStorage, ReaderId, - System, WriteStorage, -}; - -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_boxed_to_player, NetworkComponent}; -use crate::util::{protocol_velocity, Util}; -use feather_core::Packet; -use hashbrown::HashMap; -use smallvec::SmallVec; - -/// Component which stores the last known position for any given entity -/// for a player. -/// -/// This is used to ensure position remains synced across clients, since -/// relative movement packets are used. -#[derive(Default, Debug)] -pub struct LastKnownPositionComponent(pub HashMap); - -impl Component for LastKnownPositionComponent { - type Storage = DenseVecStorage; -} - -/// System for broadcasting when an entity moves. -#[derive(Default)] -pub struct EntityMoveBroadcastSystem { - dirty: BitSet, - reader: Option>, - held: BitSet, -} - -impl<'a> System<'a> for EntityMoveBroadcastSystem { - type SystemData = ( - ReadStorage<'a, PositionComponent>, - WriteStorage<'a, LastKnownPositionComponent>, - ReadStorage<'a, NetworkComponent>, - Read<'a, ChunkHolders>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (positions, mut last_positions, networks, chunk_holders, entities) = data; - - self.dirty.clear(); - - for event in positions.channel().read(&mut self.reader.as_mut().unwrap()) { - match event { - ComponentEvent::Modified(index) | ComponentEvent::Inserted(index) => { - self.dirty.add(*index); - } - _ => (), - } - } - - for (position, entity, _) in (&positions, &entities, &self.dirty).join() { - // Populate `self.held` with chunk holders for this entity's chunk - for entity in chunk_holders - .holders_for(position.current.chunk_pos()) - .unwrap_or(&[]) - { - self.held.add(entity.id()); - } - - // For each player which can see this entity's chunk, send a movement update packet. - for (network, last_positions, _) in (&networks, &mut last_positions, &self.held).join() - { - let last_known_position = match last_positions.0.get(&entity) { - Some(pos) => pos, - None => continue, // Player hasn't yet known this entity - }; - - if let Some(packets) = - packet_for_movement_update(entity, *last_known_position, position.current) - { - packets - .into_iter() - .for_each(|packet| send_packet_boxed_to_player(network, packet)); - } - - last_positions.0.insert(entity, position.current); - } - - self.held.clear(); - } - } - - flagged_setup_impl!(PositionComponent, reader); -} - -/// Returns the packet needed to notify a client -/// of a position update, from the old position to the new one. -#[allow(clippy::float_cmp)] -pub fn packet_for_movement_update( - entity: Entity, - old_pos: Position, - new_pos: Position, -) -> Option; 2]>> { - if old_pos == new_pos { - return None; - } - - let mut packets = smallvec![]; - - let has_moved = old_pos.x != new_pos.x || old_pos.y != new_pos.y || old_pos.z != new_pos.z; - let has_looked = old_pos.pitch != new_pos.pitch || old_pos.yaw != new_pos.yaw; - - if has_moved { - let (rx, ry, rz) = calculate_relative_move(old_pos, new_pos); - - if (rx == 0 && ry == 0 && rz == 0) && !has_looked { - // Because of floating point errors, - // the physics system may trigger an - // event when the distance moved is minuscule, - // which causes jittering on the client. - // Don't send the packet if it has no effect. - return None; - } - - if has_looked { - let packet: Box = Box::new(EntityLookAndRelativeMove::new( - entity.id() as i32, - rx, - ry, - rz, - degrees_to_stops(new_pos.yaw), - degrees_to_stops(new_pos.pitch), - new_pos.on_ground, - )); - packets.push(packet); - } else { - let packet: Box = Box::new(EntityRelativeMove::new( - entity.id() as i32, - rx, - ry, - rz, - new_pos.on_ground, - )); - packets.push(packet); - } - } else { - let packet: Box = Box::new(EntityLook::new( - entity.id() as i32, - degrees_to_stops(new_pos.yaw), - degrees_to_stops(new_pos.pitch), - new_pos.on_ground, - )); - packets.push(packet); - } - - // Entity Head Look also needs to be sent if the entity turned its head - if has_looked { - let packet: Box = Box::new(EntityHeadLook::new( - entity.id() as i32, - degrees_to_stops(new_pos.yaw), - )); - packets.push(packet); - } - - Some(packets) -} - -/// System for broadcasting when an entity's velocity -/// is updated. -#[derive(Default)] -pub struct EntityVelocityBroadcastSystem { - dirty: BitSet, - reader: Option>, -} - -impl<'a> System<'a> for EntityVelocityBroadcastSystem { - type SystemData = ( - ReadStorage<'a, VelocityComponent>, - Read<'a, Util>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (velocities, util, entities) = data; - - self.dirty.clear(); - - for event in velocities.channel().read(self.reader.as_mut().unwrap()) { - match event { - ComponentEvent::Modified(index) | ComponentEvent::Inserted(index) => { - self.dirty.add(*index); - } - _ => (), - } - } - - 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, - velocity_x, - velocity_y, - velocity_z, - }; - - util.broadcast_entity_update(entity, packet, Some(entity)); - } - } - - flagged_setup_impl!(VelocityComponent, reader); -} - -/// Calculates the relative move fields -/// as used in the Entity Relative Move packets. -pub fn calculate_relative_move(old: Position, current: Position) -> (i16, i16, i16) { - let x = ((current.x * 32.0 - old.x * 32.0) * 128.0) as i16; - let y = ((current.y * 32.0 - old.y * 32.0) * 128.0) as i16; - let z = ((current.z * 32.0 - old.z * 32.0) * 128.0) as i16; - (x, y, z) -} - -pub fn degrees_to_stops(degs: f32) -> u8 { - ((degs / 360.0) * 256.0) as u8 -} - -#[cfg(test)] -mod tests { - use specs::{Builder, WorldExt}; - - use feather_core::network::cast_packet; - use feather_core::network::packet::PacketType; - - use crate::entity::test; - use crate::testframework as t; - - use super::*; - - #[test] - fn test_velocity_broadcast_system() { - let (mut w, mut d) = t::builder() - .with(EntityVelocityBroadcastSystem::default(), "") - .build(); - - let player = t::add_player(&mut w); - - let entity = test::create(&mut w, position!(0.0, 0.0, 0.0)).build(); - - w.write_component::() - .insert(entity, VelocityComponent(glm::vec3(0.0, 0.0, 0.0))) - .unwrap(); - - d.dispatch(&w); - w.maintain(); - - let packet = t::assert_packet_received(&player, PacketType::EntityVelocity); - let packet = cast_packet::(&*packet); - assert_eq!(packet.entity_id, entity.id() as i32); - assert_eq!(packet.velocity_x, 0); - assert_eq!(packet.velocity_y, 0); - assert_eq!(packet.velocity_z, 0); - } -} diff --git a/server/src/entity/save.rs b/server/src/entity/save.rs deleted file mode 100644 index c1d5cde5a..000000000 --- a/server/src/entity/save.rs +++ /dev/null @@ -1,198 +0,0 @@ -//! Saving of entity data (and chunk data along with it). - -use crate::chunk_logic; -use crate::chunk_logic::{ChunkUnloadEvent, ChunkWorkerHandle}; -use crate::config::Config; -use crate::entity::{ChunkEntities, SerializerComponent}; -use feather_core::world::ChunkMap; -use rayon::prelude::*; -use shrev::{EventChannel, ReaderId}; -use specs::{Entity, LazyUpdate, Read, ReadExpect, System, WorldExt, Write}; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Arc; -use std::time::Instant; - -/// System to save chunk and entity data upon a chunk unload -/// and periodically. -/// -/// This system listens to `ChunkUnloadEvent`s. -#[derive(Default)] -pub struct ChunkSaveSystem { - reader: Option>, -} - -/// Previous time at which chunks were saved. -pub struct PreviousSaveTime(Instant); - -impl Default for PreviousSaveTime { - fn default() -> Self { - Self(Instant::now()) - } -} - -impl<'a> System<'a> for ChunkSaveSystem { - type SystemData = ( - Write<'a, PreviousSaveTime>, - Write<'a, ChunkMap>, - Read<'a, ChunkEntities>, - Read<'a, EventChannel>, - Read<'a, Arc>, - Read<'a, LazyUpdate>, - ReadExpect<'a, ChunkWorkerHandle>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - mut prev_save_time, - mut chunk_map, - chunk_entities, - unload_events, - config, - lazy, - worker_handle, - ) = data; - - for event in unload_events.read(self.reader.as_mut().unwrap()) { - let entities = vec![]; // TODO - chunk_logic::save_chunk(&worker_handle, Arc::clone(&event.chunk), entities); - } - - if prev_save_time.0.elapsed() >= config.world.save_interval { - // Save chunks - save_chunks(&mut chunk_map, &chunk_entities, &lazy); - prev_save_time.0 = Instant::now(); - } - } - - setup_impl!(reader); -} - -/// Saves all modified chunks. -/// -/// The saves themselves are performed lazily and asynchronously. -/// -/// Returns the number of chunks queued for saving. -pub fn save_chunks( - chunk_map: &mut ChunkMap, - chunk_entities: &ChunkEntities, - lazy: &LazyUpdate, -) -> u32 { - let count = AtomicUsize::new(0); - chunk_map - .chunks_mut() - .par_iter_mut() - .map(|(_, chunk)| { - let (dirty, entities) = chunk_entities.entities_in_chunk_and_modified(chunk.position()); - (chunk, entities, dirty) - }) - .for_each(|(chunk, entities, dirty)| { - // If all of the following are true, don't save the chunk: - // * The chunk has not been modified since the last save. - // * The entities in the chunk are empty (if they weren't, it is likely they were modified) - // * The entities in the chunk haven't changed. - if !chunk.check_modified() && (entities.is_empty() && !dirty) { - return; - } - - // World access is required for entity serialization, - // so we perform the saving itself asynchronously. - let chunk = Arc::new(chunk.clone()); - let entities: Vec = entities.to_vec(); - lazy.exec(move |world| { - // Compute entity data. - let entity_data = entities - .into_iter() - .filter_map(|entity| { - let serializers = world.read_component::(); - let serializer = match serializers.get(entity) { - Some(serializer) => serializer, - None => return None, // Entity not serialized - }; - - let serialize = serializer.0; - Some(serialize(world, entity)) - }) - .collect(); - - let handle = world.fetch::(); - chunk_logic::save_chunk(&handle, chunk, entity_data); - }); - - count.fetch_add(1, Ordering::Release); - }); - - let count = count.load(Ordering::Acquire); - debug!("Saving {} chunks", count); - count as u32 -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{chunkworker, testframework as t}; - use failure::_core::time::Duration; - use feather_core::{Chunk, ChunkPosition}; - - #[test] - fn test_chunk_unload() { - let (mut world, mut dispatcher) = t::builder().with(ChunkSaveSystem::default(), "").build(); - - let (tx, rx) = crossbeam::unbounded(); - let (_tx2, rx2) = crossbeam::unbounded(); - world.insert(ChunkWorkerHandle { - sender: tx, - receiver: rx2, - }); - - let event = ChunkUnloadEvent { - chunk: Arc::new(Chunk::default()), - }; - - t::trigger_event(&world, event); - - dispatcher.dispatch(&world); - - let msg = rx.try_recv().unwrap(); - - match msg { - chunkworker::Request::SaveChunk(chunk, entities) => { - assert_eq!(chunk.position(), ChunkPosition::new(0, 0)); - assert!(entities.is_empty()); // TODO - } - _ => panic!(), - } - } - - #[test] - fn test_periodic() { - let (mut world, mut dispatcher) = t::builder().with(ChunkSaveSystem::default(), "").build(); - - let (tx, rx) = crossbeam::unbounded(); - let (_tx2, rx2) = crossbeam::unbounded(); - world.insert(ChunkWorkerHandle { - sender: tx, - receiver: rx2, - }); - - let last_save_time = PreviousSaveTime(Instant::now() - Duration::from_secs(120)); - world.insert(last_save_time); - - let pos = ChunkPosition::new(0, 0); - world - .fetch_mut::() - .set_chunk_at(pos, Chunk::new(pos)); - - dispatcher.dispatch(&world); - world.maintain(); - - let msg = rx.try_recv().unwrap(); - - match msg { - chunkworker::Request::SaveChunk(chunk, entities) => { - assert_eq!(chunk.position(), pos); - assert!(entities.is_empty()); // TODO - } - _ => panic!(), - } - } -} diff --git a/server/src/lazy.rs b/server/src/lazy.rs deleted file mode 100644 index c446a5f8b..000000000 --- a/server/src/lazy.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! Extension methods for `LazyUpdate`. - -use crate::entity::EntitySpawnEvent; -use shrev::EventChannel; -use specs::world::{EntitiesRes, LazyBuilder}; -use specs::{Entity, LazyUpdate}; - -pub trait LazyUpdateExt { - /// Creates an entity and lazily inserts components. - /// - /// This should be used instead of `LazyUpdate::create_entity` - /// because it automatically triggers an `EntitySpawnEvent`. - fn spawn_entity(&self, entities: &EntitiesRes) -> LazyBuilder; - - /// Lazily sends an entity to a player. This simply forwards - /// to `crate::entity::broadcast::send_entity_to_player`. - fn send_entity_to_player(&self, player: Entity, entity: Entity); -} - -impl LazyUpdateExt for LazyUpdate { - fn spawn_entity(&self, entities: &EntitiesRes) -> LazyBuilder { - let entity = entities.create(); - // Trigger event - self.exec(move |world| { - world - .fetch_mut::>() - .single_write(EntitySpawnEvent { entity }); - }); - - LazyBuilder { lazy: self, entity } - } - - fn send_entity_to_player(&self, player: Entity, entity: Entity) { - crate::entity::send_entity_to_player(self, player, entity); - } -} diff --git a/server/src/lib.rs b/server/src/lib.rs index f7942747f..acd131abe 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -37,61 +37,34 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use specs::{Builder, Dispatcher, DispatcherBuilder, Entity, LazyUpdate, World, WorldExt}; use feather_core::network::packet::implementation::DisconnectPlay; -use prelude::*; use crate::chunk_logic::{ChunkHolders, ChunkWorkerHandle}; -use crate::entity::chicken::ChickenComponent; -use crate::entity::cow::CowComponent; -use crate::entity::donkey::DonkeyComponent; -use crate::entity::horse::HorseComponent; -use crate::entity::llama::LlamaComponent; -use crate::entity::mooshroom::MooshroomComponent; -use crate::entity::pig::PigComponent; -use crate::entity::rabbit::RabbitComponent; -use crate::entity::sheep::SheepComponent; -use crate::entity::squid::SquidComponent; -use crate::entity::{ - EntityDestroyEvent, NamedComponent, PacketCreatorComponent, SerializerComponent, -}; -use crate::network::send_packet_to_player; -use crate::player::PlayerDisconnectEvent; -use crate::systems::{BROADCASTER, JOIN_HANDLER, NETWORK, PLAYER_INIT}; -use crate::util::Util; use crate::worldgen::{ ComposableGenerator, EmptyWorldGenerator, SuperflatWorldGenerator, WorldGenerator, }; use feather_core::level; use feather_core::level::{deserialize_level_file, save_level_file, LevelData, LevelGeneratorType}; use rand::Rng; -use shrev::EventChannel; use std::collections::hash_map::DefaultHasher; use std::fs::File; use std::hash::{Hash, Hasher}; use std::io::{Read, Write}; use std::path::Path; use std::process::exit; +use crate::config::Config; #[global_allocator] static ALLOC: System = System; -#[macro_use] -pub mod util; -pub mod blocks; pub mod chunk_logic; pub mod chunkworker; pub mod config; -pub mod entity; pub mod io; pub mod joinhandler; -pub mod lazy; pub mod network; pub mod physics; -pub mod player; -pub mod prelude; pub mod shutdown; -pub mod systems; #[cfg(test)] -pub mod testframework; pub mod time; pub mod worldgen; @@ -256,297 +229,3 @@ fn hash_seed(seed_raw: &str) -> i64 { seed_raw.hash(&mut hasher); hasher.finish() as i64 } - -/// Loads the level.dat file for the world. -fn load_level(path: &Path) -> Result { - let file = File::open(path)?; - let data = deserialize_level_file(file)?; - Ok(data) -} - -/// Loads the chunks around the spawn area and creates -/// a chunk hold on those chunks to prevent them from -/// being unloaded. -/// -/// Note that these chunks are loaded asynchronously, -/// and this function will return before loading is complete. -fn load_spawn_chunks(world: &mut World) { - let view_distance = i32::from(world.fetch::>().server.view_distance); - - // Create an entity for the server and - // add chunk holders using it. - let server_entity = world.create_entity().build(); - - let mut chunk_holders = world.fetch_mut::(); - let chunk_worker_handle = world.fetch::(); - - let level = world.fetch::(); - let offset_x = level.spawn_x / 16; - let offset_z = level.spawn_z / 16; - for x in -view_distance..=view_distance { - for z in -view_distance..=view_distance { - let chunk = ChunkPosition::new(x + offset_x, z + offset_z); - - chunk_logic::load_chunk(&chunk_worker_handle, chunk); - chunk_holders.insert_holder(chunk, server_entity); - } - } -} - -/// Runs the server loop, blocking until the server -/// is shut down. -fn run_loop(world: &mut World, dispatcher: &mut Dispatcher, shutdown_rx: Receiver<()>) { - loop { - if shutdown_rx.try_recv().is_ok() { - // Shut down - return; - } - - let start_time = current_time_in_millis(); - - dispatcher.dispatch(&world); - world.maintain(); - - world.fetch_mut::().reset(); - - // Increment tick count - let mut tick_count = world.write_resource::(); - tick_count.0 += 1; - - // Sleep correct amount - let end_time = current_time_in_millis(); - let elapsed = end_time - start_time; - if elapsed > TICK_TIME { - debug!("Running behind! Starting next tick immediately"); - continue; // Behind - start next tick immediately - } - - // Sleep in 1ms increments until we've slept enough - let mut sleep_time = (TICK_TIME - elapsed) as i64; - let mut last_sleep_time = current_time_in_millis(); - while sleep_time > 0 { - std::thread::sleep(Duration::from_millis(1)); - sleep_time -= (current_time_in_millis() - last_sleep_time) as i64; - last_sleep_time = current_time_in_millis(); - } - } -} - -/// Starts the IO threads. -fn init_io_manager( - config: Arc, - player_count: Arc, - server_icon: Arc>, -) -> io::NetworkIoManager { - io::NetworkIoManager::start( - format!("{}:{}", config.server.address, config.server.port) - .parse() - .unwrap(), - config, - player_count, - server_icon, - ) -} - -/// Initializes the Specs world and dispatchers. -fn init_world<'a, 'b>( - config: Arc, - player_count: Arc, - ioman: io::NetworkIoManager, - level: LevelData, -) -> (World, Dispatcher<'a, 'b>) { - let mut world = World::new(); - time::init_time(&mut world, &level); - world.insert(config); - world.insert(player_count); - world.insert(ioman); - world.insert(TickCount::default()); - - world.register::(); - world.register::(); - - let generator: Arc = match level.generator_type() { - LevelGeneratorType::Flat => Arc::new(SuperflatWorldGenerator { - options: level.clone().generator_options.unwrap_or_default(), - }), - LevelGeneratorType::Default => { - Arc::new(ComposableGenerator::default_with_seed(level.seed as u64)) - } - _ => Arc::new(EmptyWorldGenerator {}), - }; - world.insert(level); - world.insert(generator); - - let mut dispatcher = DispatcherBuilder::new(); - - dispatcher.add(network::NetworkSystem, NETWORK, &[]); - - blocks::init_logic(&mut dispatcher); - physics::init_logic(&mut dispatcher); - entity::init_logic(&mut dispatcher); - player::init_logic(&mut dispatcher); - chunk_logic::init_logic(&mut dispatcher); - time::init_logic(&mut dispatcher); - - dispatcher.add_barrier(); - - blocks::init_handlers(&mut dispatcher); - physics::init_handlers(&mut dispatcher); - entity::init_handlers(&mut dispatcher); - player::init_handlers(&mut dispatcher); - chunk_logic::init_handlers(&mut dispatcher); - - // Player init dependency is so that player position is loaded - // before the join handle runs. - dispatcher.add( - joinhandler::JoinHandlerSystem, - JOIN_HANDLER, - &[NETWORK, PLAYER_INIT], - ); - - dispatcher.add_barrier(); - - 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(); - dispatcher.setup(&mut world); - - register_components(&mut world); - - (world, dispatcher) -} - -fn register_components(world: &mut World) { - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); -} - -fn init_log(config: &Config) { - let level = match config.log.level.as_str() { - "trace" => log::Level::Trace, - "debug" => log::Level::Debug, - "info" => log::Level::Info, - "warn" => log::Level::Warn, - "error" => log::Level::Error, - _ => panic!("Unknown log level {}", config.log.level), - }; - - simple_logger::init_with_level(level).unwrap(); -} - -/// Tries to load a server icon from the current directory. -fn load_server_icon() -> Option { - let icon_file: Option = match File::open("server-icon.png") { - Ok(file) => Some(file), - Err(_) => None, - }; - - let mut icon_file = icon_file?; - - let mut data = Vec::new(); - if icon_file.read_to_end(&mut data).is_err() { - warn!("Failed to load server icon."); - return None; - } - - let b64_icon = base64::encode(&data); - Some(format!("data:image/png;base64,{}", b64_icon)) -} - -/// Retrieves the current time in seconds -/// since the UNIX epoch. -pub fn current_time_in_secs() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs() -} - -/// Retrieves the current time in milleseconds -/// since the UNIX epoch. -pub fn current_time_in_millis() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis() as u64 -} - -/// Disconnects the given player, removing them from the world. -/// This operation is performed lazily. -pub fn disconnect_player(player: Entity, reason: String, lazy: &LazyUpdate) { - lazy.exec_mut(move |world| { - let json = json!({ - "text": reason, - }); - - let packet = DisconnectPlay::new(json.to_string()); - send_packet_to_player(world.read_component().get(player).unwrap(), packet); - - disconnect_player_without_packet(player, world, reason); - }) -} - -/// Disconnects a player without sending Disconnect Play. -/// This should be used when the client disconnects. -pub fn disconnect_player_without_packet(player: Entity, world: &mut World, reason: String) { - let nameds = world.write_component::(); - let named = nameds.get(player).unwrap(); - - info!("Disconnecting player {}: {}", named.display_name, reason); - - // Decrement player count - let player_count = world.fetch_mut::>(); - player_count.0.fetch_sub(1, Ordering::SeqCst); - - // Trigger disconnect event - let event = PlayerDisconnectEvent { - player, - uuid: named.uuid, - reason, - }; - world - .fetch_mut::>() - .single_write(event); - - // Trigger entity destroy event - let event = EntityDestroyEvent { entity: player }; - world - .fetch_mut::>() - .single_write(event); - - // The entity is removed from the world by `entity::EntityDestroySystem`. -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_init_world() { - let config = Arc::new(Config::default()); - let player_count = Arc::new(PlayerCount(AtomicUsize::new(0))); - let server_icon = Arc::new(Some(String::from("server_icon"))); - let ioman = init_io_manager( - Arc::clone(&config), - Arc::clone(&player_count), - Arc::clone(&server_icon), - ); - let level = LevelData::default(); - - let (world, mut dispatcher) = init_world(config, player_count, ioman, level); - dispatcher.dispatch(&world); - } -} diff --git a/server/src/player/animation.rs b/server/src/player/animation.rs deleted file mode 100644 index acf600b75..000000000 --- a/server/src/player/animation.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::network::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; -use feather_core::{ClientboundAnimation, Hand}; -use shrev::EventChannel; -use specs::SystemData; -use specs::{Entity, Read, ReaderId, System, World, Write}; - -/// Event which is triggered when a player causes -/// an animation. -#[derive(Debug, Clone)] -pub struct PlayerAnimationEvent { - pub player: Entity, - pub animation: ClientboundAnimation, -} - -/// System for handling Animation Serverbound packets -/// and then triggering a `PlayerAnimationEvent`. -pub struct PlayerAnimationSystem; - -impl<'a> System<'a> for PlayerAnimationSystem { - type SystemData = ( - Write<'a, EventChannel>, - Read<'a, PacketQueue>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (mut events, packet_queue) = data; - - // Handle Animation Serverbound packets. - let packets = packet_queue.for_packet(PacketType::AnimationServerbound); - - for (player, packet) in packets { - let packet = cast_packet::(&*packet); - - let animation = match packet.hand { - Hand::Main => ClientboundAnimation::SwingMainArm, - Hand::Off => ClientboundAnimation::SwingOffhand, - }; - - let event = PlayerAnimationEvent { player, animation }; - events.single_write(event); - } - } -} - -/// System for broadcasting when a player causes an animation. -/// This system listens to `PlayerAnimationEvent`s. -#[derive(Default)] -pub struct AnimationBroadcastSystem { - reader: Option>, -} - -impl<'a> System<'a> for AnimationBroadcastSystem { - type SystemData = (Read<'a, EventChannel>, Read<'a, Util>); - - fn run(&mut self, data: Self::SystemData) { - 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); - - util.broadcast_entity_update(event.player, packet, Some(event.player)) - } - } - - fn setup(&mut self, world: &mut World) { - Self::SystemData::setup(world); - - self.reader = Some( - world - .fetch_mut::>() - .register_reader(), - ); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::testframework as t; - use feather_core::network::packet::implementation::{ - AnimationClientbound, AnimationServerbound, - }; - use specs::WorldExt; - - #[test] - fn test_animation_system() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let packet = AnimationServerbound::new(Hand::Main); - t::receive_packet(&player, &w, packet); - - let mut event_reader = t::reader::(&w); - - d.dispatch(&w); - w.maintain(); - - let channel = w.fetch::>(); - - let events = channel.read(&mut event_reader).collect::>(); - assert_eq!(events.len(), 1); - let first = events.first().unwrap(); - - assert_eq!(first.player, player.entity); - assert_eq!(first.animation, ClientboundAnimation::SwingMainArm); - } - - #[test] - fn test_animation_broadcast_system() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - let player2 = t::add_player(&mut w); - - let event = PlayerAnimationEvent { - player: player.entity, - animation: ClientboundAnimation::SwingMainArm, - }; - - t::trigger_event(&w, event.clone()); - - d.dispatch(&w); - w.maintain(); - - // Make sure animation wasn't sent to the player itself - t::assert_packet_not_received(&player, PacketType::AnimationClientbound); - - let packet = t::assert_packet_received(&player2, PacketType::AnimationClientbound); - let packet = cast_packet::(&*packet); - - assert_eq!(packet.entity_id, event.player.id() as i32); - assert_eq!(packet.animation, event.animation); - } -} diff --git a/server/src/player/broadcast.rs b/server/src/player/broadcast.rs deleted file mode 100644 index 40fe02c73..000000000 --- a/server/src/player/broadcast.rs +++ /dev/null @@ -1,203 +0,0 @@ -use crate::config::Config; -use crate::entity::{ChunkEntities, NamedComponent, PlayerComponent, PositionComponent}; -use crate::joinhandler::PlayerJoinEvent; -use crate::lazy::LazyUpdateExt; -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}; -use feather_core::Gamemode; -use shrev::EventChannel; -use specs::{Entities, Entity, Join, Read, ReadStorage, ReaderId, System, World, Write}; -use specs::{LazyUpdate, SystemData}; -use std::sync::Arc; -use uuid::Uuid; - -/// System for broadcasting when a player joins -/// the game. -/// -/// This system only broadcasts the -/// Player Info packet necessary to view to player -/// in the tablist - the `EntityBroadcastSystem` handles -/// the Spawn Player packet. -#[derive(Default)] -pub struct JoinBroadcastSystem { - reader: Option>, -} - -impl<'a> System<'a> for JoinBroadcastSystem { - type SystemData = ( - Read<'a, EventChannel>, - ReadStorage<'a, PositionComponent>, - ReadStorage<'a, NamedComponent>, - ReadStorage<'a, PlayerComponent>, - ReadStorage<'a, NetworkComponent>, - Write<'a, EventChannel>, - Read<'a, ChunkEntities>, - Read<'a, LazyUpdate>, - Read<'a, Arc>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - join_events, - positions, - nameds, - player_comps, - net_comps, - mut chat, - chunk_entities, - lazy, - config, - entities, - ) = data; - - for event in join_events.read(&mut self.reader.as_mut().unwrap()) { - // Broadcast join - let position = positions.get(event.player).unwrap(); - let named = nameds.get(event.player).unwrap(); - let player_comp = player_comps.get(event.player).unwrap(); - - let player_info = get_player_initialization_packet(position, named, player_comp); - - send_packet_to_all_players(&net_comps, &entities, player_info, None); - - let net_comp = net_comps.get(event.player).unwrap(); - - // Send existing players to new player - for (position, named, player_comp, entity) in - (&positions, &nameds, &player_comps, &entities).join() - { - if entity != event.player { - let player_info = - get_player_initialization_packet(position, named, player_comp); - send_packet_to_player(net_comp, player_info); - } - } - - // 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 { - lazy.send_entity_to_player(event.player, entity); - } - } - - // Broadcast join message in chat - let message = json!({ - "translate": "multiplayer.player.joined", - "color": "yellow", - "with": [ - {"text": named.display_name}, - ], - }) - .to_string(); - chat.single_write(ChatBroadcastEvent { message }); - } - } - - fn setup(&mut self, world: &mut World) { - Self::SystemData::setup(world); - - self.reader = Some( - world - .fetch_mut::>() - .register_reader(), - ); - } -} - -/// Returns the player info packet -/// for the given player. -fn get_player_initialization_packet( - _position: &PositionComponent, - named: &NamedComponent, - pcomp: &PlayerComponent, -) -> PlayerInfo { - let display_name = json!({ - "text": named.display_name - }) - .to_string(); - - let mut props = vec![]; - for prop in pcomp.profile_properties.iter() { - props.push(( - prop.name.clone(), - prop.value.clone(), - prop.signature.clone(), - )); - } - - let action = PlayerInfoAction::AddPlayer( - named.display_name.clone(), - props, - Gamemode::Creative, - 50, - display_name, - ); - PlayerInfo::new(action, named.uuid) -} - -/// Event which is called when a player disconnected. -pub struct PlayerDisconnectEvent { - pub player: Entity, - pub reason: String, - pub uuid: Uuid, -} - -/// System for broadcasting when a player disconnects. -#[derive(Default)] -pub struct DisconnectBroadcastSystem { - reader: Option>, -} - -impl<'a> System<'a> for DisconnectBroadcastSystem { - type SystemData = ( - ReadStorage<'a, NamedComponent>, - ReadStorage<'a, NetworkComponent>, - Write<'a, EventChannel>, - Read<'a, EventChannel>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (nameds, networks, mut chat, disconnect_events) = data; - - for event in disconnect_events.read(&mut self.reader.as_mut().unwrap()) { - // Broadcast disconnect. - // Note that the Destroy Entity packet is sent - // in a separate system (crate::entity::EntityDestroyBroadcastSystem). - // This system only updates the tablist for all clients. - let player_info = PlayerInfo::new(PlayerInfoAction::RemovePlayer, event.uuid); - - for net in (&networks).join() { - send_packet_to_player(net, player_info.clone()); - } - - let named = nameds.get(event.player).unwrap(); - - // Broadcast chat message. - let message = json!({ - "translate": "multiplayer.player.left", - "color": "yellow", - "with": [ - {"text": named.display_name}, - ], - }) - .to_string(); - let event = ChatBroadcastEvent { message }; - chat.single_write(event); - } - } - - fn setup(&mut self, world: &mut World) { - Self::SystemData::setup(world); - - self.reader = Some( - world - .fetch_mut::>() - .register_reader(), - ); - } -} diff --git a/server/src/player/chat.rs b/server/src/player/chat.rs deleted file mode 100644 index 60e7db460..000000000 --- a/server/src/player/chat.rs +++ /dev/null @@ -1,154 +0,0 @@ -use shrev::EventChannel; -use specs::SystemData; -use specs::{Entities, Read, ReadStorage, ReaderId, System, World, Write}; - -use feather_core::network::cast_packet; -use feather_core::network::packet::implementation::{ - ChatMessageClientbound, ChatMessageServerbound, -}; -use feather_core::network::packet::PacketType; - -use crate::entity::NamedComponent; -use crate::network::{send_packet_to_all_players, NetworkComponent, PacketQueue}; - -/// Event which is triggered when a new chat message is to be broadcasted to the whole server. -#[derive(Debug, Clone)] -pub struct ChatBroadcastEvent { - pub message: String, -} - -/// System for handling Chat Message Serverbound packets -/// and then triggering a `ChatBroadcastEvent`. -pub struct PlayerChatSystem; - -impl<'a> System<'a> for PlayerChatSystem { - type SystemData = ( - Write<'a, EventChannel>, - ReadStorage<'a, NamedComponent>, - Read<'a, PacketQueue>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (mut events, nameds, packet_queue) = data; - - // Handle Chat Message Serverbound packets. - let packets = packet_queue.for_packet(PacketType::ChatMessageServerbound); - - for (player, packet) in packets { - let packet = cast_packet::(&*packet); - let message = packet.message.clone(); - let player_name = &nameds.get(player).unwrap().display_name; - - // TODO: could use a more robust chat-component library. - let message_json = json!({ - "translate": "chat.type.text", - "with": [ - {"text": player_name}, - {"text": message}, - ], - }) - .to_string(); - - let event = ChatBroadcastEvent { - message: message_json, - }; - events.single_write(event); - - // Log in the console - info!("<{}> {}", player_name, message); - } - } -} - -/// System for broadcasting chat messages. -/// This system listens to `ChatBroadcastEvent`s. -#[derive(Default)] -pub struct ChatBroadcastSystem { - reader: Option>, -} - -impl<'a> System<'a> for ChatBroadcastSystem { - type SystemData = ( - Read<'a, EventChannel>, - ReadStorage<'a, NetworkComponent>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (events, networks, entities) = data; - - for event in events.read(&mut self.reader.as_mut().unwrap()) { - let message = event.message.clone(); - - // Broadcast chat message - let packet = ChatMessageClientbound { - json_data: message, - position: 0, - }; - - send_packet_to_all_players(&networks, &entities, packet, None); - } - } - - fn setup(&mut self, world: &mut World) { - Self::SystemData::setup(world); - - self.reader = Some( - world - .fetch_mut::>() - .register_reader(), - ); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::testframework as t; - use feather_core::network::packet::implementation::ChatMessageServerbound; - use specs::WorldExt; - - #[test] - fn test_chat_system() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let packet = ChatMessageServerbound { - message: String::from("test"), - }; - t::receive_packet(&player, &w, packet); - - let mut event_reader = t::reader::(&w); - - d.dispatch(&w); - w.maintain(); - - let channel = w.fetch::>(); - - let events = channel.read(&mut event_reader).collect::>(); - assert_eq!(events.len(), 1); - } - - #[test] - fn test_chat_broadcast_system() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - let player2 = t::add_player(&mut w); - - let event = ChatBroadcastEvent { - message: String::from("test"), - }; - - t::trigger_event(&w, event); - - d.dispatch(&w); - w.maintain(); - - t::assert_packet_received(&player, PacketType::ChatMessageClientbound); - let packet = t::assert_packet_received(&player2, PacketType::ChatMessageClientbound); - let packet = cast_packet::(&*packet); - assert_eq!(packet.json_data, String::from("test")); - } -} diff --git a/server/src/player/digging.rs b/server/src/player/digging.rs deleted file mode 100644 index 6b1d51466..000000000 --- a/server/src/player/digging.rs +++ /dev/null @@ -1,817 +0,0 @@ -//! This module handles the monolithic Player Digging packet. -//! -//! The packet's name is rather misleading, as it is also sent -//! for completely unrelated actions, including eating, shooting bows, -//! swapping items out the the offhand, and dropping items. - -use specs::{Entity, LazyUpdate, Read, ReadStorage, ReaderId, System, World, Write, WriteStorage}; - -use feather_core::network::cast_packet; -use feather_core::network::packet::implementation::{ - BlockChange, PlayerDigging, PlayerDiggingStatus, -}; -use feather_core::network::packet::PacketType; -use feather_core::world::block::{Block, BlockExt}; -use feather_core::world::ChunkMap; -use feather_core::{Gamemode, Item, Position}; - -use crate::blocks::{BlockUpdateCause, BlockUpdateEvent}; -use crate::disconnect_player; -use crate::entity::{PlayerComponent, PositionComponent, ShootArrowEvent}; -use crate::network::PacketQueue; -use crate::player::{InventoryComponent, InventoryUpdateEvent}; -use crate::util::Util; -use feather_core::inventory::{ItemStack, SlotIndex, SLOT_HOTBAR_OFFSET, SLOT_OFFHAND}; -use shrev::EventChannel; -use specs::SystemData; - -/// Event triggered when a player drops an item. -/// -/// Before this event is triggered, the item -/// is removed from the player's inventory. -#[derive(Debug, Clone)] -pub struct PlayerItemDropEvent { - /// The slot from which the item was dropped, - /// if known. - pub slot: Option, - /// The item stack which was dropped. - pub stack: ItemStack, - /// The player who dropped the item. - pub player: Entity, -} - -/// System responsible for polling for PlayerDigging -/// packets and writing the corresponding events. -pub struct PlayerDiggingSystem; - -impl<'a> System<'a> for PlayerDiggingSystem { - type SystemData = ( - WriteStorage<'a, InventoryComponent>, - ReadStorage<'a, PlayerComponent>, // For gamemodes - ReadStorage<'a, PositionComponent>, - Write<'a, EventChannel>, - Write<'a, EventChannel>, - Write<'a, EventChannel>, - Write<'a, EventChannel>, - Write<'a, ChunkMap>, - Read<'a, PacketQueue>, - Read<'a, LazyUpdate>, - ); - - fn run(&mut self, data: Self::SystemData) { - use PlayerDiggingStatus::*; - - let ( - mut inventories, - players, - positions, - mut block_breaks, - mut item_drops, - mut inventory_updates, - mut shoot_arrow_events, - mut chunk_map, - packet_queue, - lazy, - ) = data; - - let packets = packet_queue.for_packet(PacketType::PlayerDigging); - - for (player, packet) in packets { - let packet = cast_packet::(&*packet); - - match packet.status { - StartedDigging | FinishedDigging | CancelledDigging => handle_digging( - packet, - players.get(player).unwrap(), - inventories.get(player).unwrap().item_in_main_hand(), - player, - &mut block_breaks, - &mut chunk_map, - &lazy, - ), - DropItem | DropItemStack => handle_drop_item_stack( - packet, - player, - &mut inventory_updates, - &mut item_drops, - inventories.get_mut(player).unwrap(), - ), - ConsumeItem => handle_consume_item( - packet, - players.get(player).unwrap(), - player, - inventories.get_mut(player).unwrap(), - &mut inventory_updates, - positions.get(player).unwrap().current, - &mut shoot_arrow_events, - ), - status => warn!("Unhandled Player Digging status {:?}", status), - } - } - } -} - -fn handle_digging( - packet: &PlayerDigging, - player: &PlayerComponent, - item_in_main_hand: Option<&ItemStack>, - entity: Entity, - events: &mut EventChannel, - chunk_map: &mut ChunkMap, - lazy: &LazyUpdate, -) { - // Return early if needed - match packet.status { - PlayerDiggingStatus::StartedDigging => { - if player.gamemode != Gamemode::Creative { - return; - } - } - PlayerDiggingStatus::CancelledDigging => return, - _ => (), - } - - // Don't break block if player is holding a sword in creative mode. - if player.gamemode == Gamemode::Creative { - if let Some(item_in_main_hand) = item_in_main_hand { - match item_in_main_hand.ty { - Item::WoodenSword - | Item::StoneSword - | Item::GoldenSword - | Item::IronSword - | Item::DiamondSword => return, - _ => (), - } - } - } - - let old = chunk_map.block_at(packet.location); - - if chunk_map.set_block_at(packet.location, Block::Air).is_err() { - disconnect_player( - entity, - "Attempted to break block in unloaded chunk".to_string(), - lazy, - ); - return; - } - - let event = BlockUpdateEvent { - cause: BlockUpdateCause::Player(entity), - pos: packet.location, - old_block: old.unwrap(), // We checked that the location was valid above - new_block: Block::Air, - }; - - events.single_write(event); -} - -fn handle_drop_item_stack( - packet: &PlayerDigging, - entity: Entity, - inventory_updates: &mut EventChannel, - item_drops: &mut EventChannel, - inventory: &mut InventoryComponent, -) { - assert!( - packet.status == PlayerDiggingStatus::DropItem - || packet.status == PlayerDiggingStatus::DropItemStack - ); - - let slot = inventory.held_item + SLOT_HOTBAR_OFFSET; - - let stack = { - if let Some(item) = inventory.item_at(slot) { - item.clone() - } else { - // Silently fail - no item stack to drop - return; - } - }; - - let amnt = match packet.status { - PlayerDiggingStatus::DropItem => { - if stack.amount == 0 { - inventory.clear_item_at(slot); - 0 - } else if stack.amount == 1 { - inventory.clear_item_at(slot); - 1 - } else { - inventory.set_item_at(slot, ItemStack::new(stack.ty, stack.amount - 1)); - 1 - } - } - PlayerDiggingStatus::DropItemStack => { - inventory.clear_item_at(slot); - stack.amount - } - _ => unreachable!(), // Assertion above - }; - - let inv_update = InventoryUpdateEvent { - slots: smallvec![slot], - player: entity, - }; - inventory_updates.single_write(inv_update); - - if amnt != 0 { - let item_drop = PlayerItemDropEvent { - slot: Some(slot), - stack: ItemStack::new(stack.ty, amnt), - player: entity, - }; - item_drops.single_write(item_drop); - } -} - -/// Handles food consumption and shooting arrows. -fn handle_consume_item( - packet: &PlayerDigging, - player: &PlayerComponent, - entity: Entity, - inventory: &mut InventoryComponent, - inventory_updates: &mut EventChannel, - position: Position, - shoot_arrow_events: &mut EventChannel, -) { - assert_eq!(packet.status, PlayerDiggingStatus::ConsumeItem); - - // TODO: Fallback to off-hand if main-hand is not a consumable - let used_item = inventory.item_in_main_hand(); - - if let Some(item) = used_item { - if item.ty == Item::Bow { - handle_shoot_bow( - player, - entity, - inventory, - inventory_updates, - position, - shoot_arrow_events, - ); - } - // TODO: Food, potions - } -} - -fn handle_shoot_bow( - player: &PlayerComponent, - entity: Entity, - inventory: &mut InventoryComponent, - inventory_updates: &mut EventChannel, - position: Position, - shoot_arrow_events: &mut EventChannel, -) { - let arrow_to_consume: Option<(SlotIndex, ItemStack)> = find_arrow(&inventory); - if player.gamemode == Gamemode::Survival || player.gamemode == Gamemode::Adventure { - // If no arrow was found, don't shoot - let arrow_to_consume = arrow_to_consume.clone(); - if arrow_to_consume.is_none() { - debug!("Tried to shoot bow with no arrows."); - return; - } - - // Consume arrow - let (arrow_slot, arrow_stack) = arrow_to_consume.unwrap(); - let mut arrow_stack: ItemStack = arrow_stack; - arrow_stack.amount -= 1; - - inventory.set_item_at(arrow_slot, arrow_stack); - inventory_updates.single_write(InventoryUpdateEvent { - slots: smallvec![arrow_slot], - player: entity, - }); - } - - let arrow_type: Item = match arrow_to_consume { - None => Item::Arrow, // Default to generic arrow in creative mode with none in inventory - Some((_, arrow_stack)) => arrow_stack.ty, - }; - - shoot_arrow_events.single_write(ShootArrowEvent { - shooter: Some(entity), - position, - arrow_type, - critical: false, // TODO: Determine critical based on how long bow was pulled back - }); -} - -fn find_arrow(inventory: &InventoryComponent) -> Option<(SlotIndex, ItemStack)> { - // Order of priority is: off-hand, hotbar (0 to 8), rest of inventory - - if let Some(offhand) = inventory.item_at(SLOT_OFFHAND) { - if is_arrow_item(offhand.ty) { - return Some((SLOT_OFFHAND, offhand.clone())); - } - } - - for hotbar_slot in 0..9 { - if let Some(hotbar_stack) = inventory.item_at(SLOT_HOTBAR_OFFSET + hotbar_slot) { - if is_arrow_item(hotbar_stack.ty) { - return Some((SLOT_HOTBAR_OFFSET + hotbar_slot, hotbar_stack.clone())); - } - } - } - - for inv_slot in 9..=35 { - if let Some(inv_stack) = inventory.item_at(inv_slot) { - if is_arrow_item(inv_stack.ty) { - return Some((inv_slot, inv_stack.clone())); - } - } - } - None -} - -fn is_arrow_item(item: Item) -> bool { - match item { - Item::Arrow | Item::SpectralArrow | Item::TippedArrow => true, - _ => false, - } -} - -/// System for broadcasting block update -/// events to all clients. -/// -/// This system listens to `BlockUpdateEvent`s. -#[derive(Default)] -pub struct BlockUpdateBroadcastSystem { - reader: Option>, -} - -impl<'a> System<'a> for BlockUpdateBroadcastSystem { - type SystemData = (Read<'a, EventChannel>, Read<'a, Util>); - - fn run(&mut self, data: Self::SystemData) { - let (events, util) = data; - - // Process events - for event in events.read(&mut self.reader.as_mut().unwrap()) { - // Send Block Change packet to every player, - // except for the one that performed the update - // (if any) - let neq = if let BlockUpdateCause::Player(player) = event.cause { - Some(player) - } else { - None - }; - - let packet = BlockChange::new(event.pos, i32::from(event.new_block.native_state_id())); - util.broadcast_chunk_update(event.pos.chunk_pos(), packet, neq); - } - } - - fn setup(&mut self, world: &mut World) { - Self::SystemData::setup(world); - - self.reader = Some(world.fetch_mut::>().register_reader()); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::blocks::BlockUpdateEvent; - use crate::testframework as t; - use feather_core::item::Item; - use feather_core::world::chunk::Chunk; - use feather_core::world::{BlockPosition, ChunkPosition}; - use specs::WorldExt; - - #[test] - fn test_started_digging() { - let (mut w, mut d) = t::init_world(); - - let cpos = ChunkPosition::new(0, 0); - let bpos = BlockPosition::new(0, 0, 0); - let mut chunk = Chunk::new(cpos); - chunk.set_block_at(0, 0, 0, Block::Stone); - w.fetch_mut::().set_chunk_at(cpos, chunk); - - let mut event_reader = t::reader(&w); - - // Creative mode - - let player = t::add_player(&mut w); - - let packet = PlayerDigging::new(PlayerDiggingStatus::StartedDigging, bpos, 0); - - t::receive_packet(&player, &w, packet.clone()); - - d.dispatch(&w); - w.maintain(); - - t::assert_not_disconnected(&player); - - { - let mut chunk_map = w.fetch_mut::(); - - assert_eq!(chunk_map.block_at(bpos).unwrap(), Block::Air); - - chunk_map.set_block_at(bpos, Block::Stone).unwrap(); - - let channel = w.fetch_mut::>(); - let events = channel.read(&mut event_reader).collect::>(); - assert_eq!(events.len(), 1); - - let first = events.first().unwrap(); - assert_eq!(first.old_block, Block::Stone); - assert_eq!(first.new_block, Block::Air); - assert_eq!(first.cause, BlockUpdateCause::Player(player.entity)); - assert_eq!(first.pos, bpos); - } - - // Survival mode - let player = t::add_player(&mut w); - w.write_component::() - .get_mut(player.entity) - .unwrap() - .gamemode = Gamemode::Survival; - - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - t::assert_not_disconnected(&player); - - let chunk_map = w.fetch::(); - assert_eq!(chunk_map.block_at(bpos).unwrap(), Block::Stone); - - let channel = w.fetch_mut::>(); - let events = channel.read(&mut event_reader).collect::>(); - assert_eq!(events.len(), 0); - } - - // This should be a no-op. - #[test] - fn test_cancelled_digging() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let mut event_reader = t::reader(&w); - - let packet = PlayerDigging::new( - PlayerDiggingStatus::CancelledDigging, - BlockPosition::default(), - 0, - ); - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - t::assert_not_disconnected(&player); - - let channel = w.fetch::>(); - let events = channel.read(&mut event_reader).collect::>(); - assert!(events.is_empty()); - } - - #[test] - fn test_finished_digging() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - w.write_component::() - .get_mut(player.entity) - .unwrap() - .gamemode = Gamemode::Survival; - - let mut event_reader = t::reader(&w); - - let bpos = BlockPosition::new(0, 0, 0); - let cpos = bpos.chunk_pos(); - - let mut chunk = Chunk::new(cpos); - chunk.set_block_at(0, 0, 0, Block::Stone); - - w.fetch_mut::().set_chunk_at(cpos, chunk); - - let packet = PlayerDigging::new(PlayerDiggingStatus::FinishedDigging, bpos, 0); - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - t::assert_not_disconnected(&player); - - let chunk_map = w.fetch::(); - - assert_eq!(chunk_map.block_at(bpos).unwrap(), Block::Air); - - let channel = w.fetch_mut::>(); - let events = channel.read(&mut event_reader).collect::>(); - assert_eq!(events.len(), 1); - - let first = events.first().unwrap(); - assert_eq!(first.old_block, Block::Stone); - assert_eq!(first.new_block, Block::Air); - assert_eq!(first.cause, BlockUpdateCause::Player(player.entity)); - assert_eq!(first.pos, bpos); - } - - #[test] - fn test_block_break_in_unloaded_chunk() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let mut event_reader = t::reader(&w); - - let bpos = BlockPosition::new(1000, 25, 1000); - - let packet = PlayerDigging::new(PlayerDiggingStatus::FinishedDigging, bpos, 0); - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - t::assert_disconnected(&player); - - let channel = w.fetch::>(); - let events = channel.read(&mut event_reader).collect::>(); - assert!(events.is_empty()); - } - - #[test] - fn test_drop_item() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let slot = SLOT_HOTBAR_OFFSET; - { - let mut invs = w.write_component::(); - let inv = invs.get_mut(player.entity).unwrap(); - inv.held_item = 0; - inv.set_item_at(slot, ItemStack::new(Item::CookedBeef, 4)); - } - - let mut drop_reader = t::reader(&w); - let mut update_reader = t::reader(&w); - - let packet = PlayerDigging::new(PlayerDiggingStatus::DropItem, BlockPosition::default(), 0); - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - let drop_channel = w.fetch::>(); - let update_channel = w.fetch::>(); - - // Check that events are correct - let drop_events = drop_channel.read(&mut drop_reader).collect::>(); - assert_eq!(drop_events.len(), 1); - let first = drop_events.first().unwrap(); - assert_eq!(first.player, player.entity); - assert_eq!(first.slot, Some(slot)); - assert_eq!(first.stack, ItemStack::new(Item::CookedBeef, 1)); // 1 beef was dropped - - let update_events = update_channel.read(&mut update_reader).collect::>(); - assert_eq!(update_events.len(), 1); - let first = update_events.first().unwrap(); - assert_eq!(first.player, player.entity); - assert_eq!(first.slots.as_slice(), &[slot]); - - // Check that inventory was updated correctly - let invs = w.read_component::(); - let inv = invs.get(player.entity).unwrap(); - assert_eq!( - inv.item_at(slot).unwrap(), - &ItemStack::new(Item::CookedBeef, 3) - ); // 1 was removed - } - - #[test] - fn test_drop_item_no_stack() { - // This should be a no-op. - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let mut drop_reader = t::reader(&w); - let mut update_reader = t::reader(&w); - - let packet = PlayerDigging::new(PlayerDiggingStatus::DropItem, BlockPosition::default(), 0); - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - t::assert_not_disconnected(&player); - - let drop_channel = w.fetch::>(); - let update_channel = w.fetch::>(); - - let drop_events = drop_channel.read(&mut drop_reader).collect::>(); - assert!(drop_events.is_empty()); - let update_events = update_channel.read(&mut update_reader).collect::>(); - assert!(update_events.is_empty()); - } - - #[test] - fn test_drop_item_stack() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let mut drop_reader = t::reader(&w); - let mut update_reader = t::reader(&w); - - let slot = SLOT_HOTBAR_OFFSET; - let amnt = 32; - { - let mut invs = w.write_component::(); - let inv = invs.get_mut(player.entity).unwrap(); - inv.set_item_at(slot, ItemStack::new(Item::CookedBeef, amnt)); - } - - let packet = PlayerDigging::new( - PlayerDiggingStatus::DropItemStack, - BlockPosition::default(), - 0, - ); - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - let drop_channel = w.fetch::>(); - let update_channel = w.fetch::>(); - - let update_events = update_channel.read(&mut update_reader).collect::>(); - assert_eq!(update_events.len(), 1); - let first = update_events.first().unwrap(); - assert_eq!(first.player, player.entity); - assert_eq!(first.slots.as_slice(), &[slot]); - - let drop_events = drop_channel.read(&mut drop_reader).collect::>(); - assert_eq!(drop_events.len(), 1); - let first = drop_events.first().unwrap(); - assert_eq!(first.player, player.entity); - assert_eq!(first.slot, Some(slot)); - assert_eq!(first.stack, ItemStack::new(Item::CookedBeef, amnt)); - - let invs = w.read_component::(); - let inv = invs.get(player.entity).unwrap(); - assert_eq!(inv.item_at(slot), None); - } - - #[test] - fn test_block_update_broadcast_system() { - let (mut w, mut d) = t::init_world(); - - let player1 = t::add_player(&mut w); - let player2 = t::add_player(&mut w); - - let pos = BlockPosition::default(); - let block = Block::Sand; - - let event = BlockUpdateEvent { - cause: BlockUpdateCause::Player(player1.entity), - pos, - old_block: block, - new_block: Block::Air, - }; - w.fetch_mut::>().single_write(event); - - d.dispatch(&w); - w.maintain(); - - let block_change = t::assert_packet_received(&player2, PacketType::BlockChange); - let block_change = cast_packet::(&*block_change); - assert_eq!(block_change.location, pos); - assert_eq!( - block_change.block_id, - i32::from(Block::Air.native_state_id()) - ); - - t::assert_packet_not_received(&player1, PacketType::BlockChange); // Don't send update to own player - - // Now handle an event not caused by a player - let event = BlockUpdateEvent { - cause: BlockUpdateCause::Test, - pos, - old_block: block, - new_block: Block::Air, - }; - w.fetch_mut::>().single_write(event); - - d.dispatch(&w); - w.maintain(); - - // Packet should be sent to both players - t::assert_packet_received(&player1, PacketType::BlockChange); - t::assert_packet_received(&player2, PacketType::BlockChange); - } - - #[test] - pub fn test_find_arrow() { - let mut inv = InventoryComponent::new(); - inv.set_item_at( - SLOT_OFFHAND, - ItemStack { - ty: Item::Arrow, - amount: 1, - }, - ); - inv.set_item_at( - SLOT_HOTBAR_OFFSET, - ItemStack { - ty: Item::Arrow, - amount: 1, - }, - ); - inv.set_item_at( - 9, - ItemStack { - ty: Item::Arrow, - amount: 1, - }, - ); - - // 1. Off-hand - let (slot, stack) = find_arrow(&inv).unwrap(); - assert_eq!(slot, SLOT_OFFHAND); - assert_eq!(stack.ty, Item::Arrow); - inv.clear_item_at(SLOT_OFFHAND); - - // 2. Hot-bar - let (slot, stack) = find_arrow(&inv).unwrap(); - assert_eq!(slot, SLOT_HOTBAR_OFFSET); - assert_eq!(stack.ty, Item::Arrow); - inv.clear_item_at(SLOT_HOTBAR_OFFSET); - - // 3. Rest of inventory - let (slot, stack) = find_arrow(&inv).unwrap(); - assert_eq!(slot, 9); - assert_eq!(stack.ty, Item::Arrow); - inv.clear_item_at(9); - - // 4. No arrow found - assert!(find_arrow(&inv).is_none()); - } - - #[test] - pub fn test_shoot_arrow() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let mut shoot_reader = t::reader(&w); - let mut update_reader = t::reader(&w); - - let slot = SLOT_HOTBAR_OFFSET; - let amnt = 32; - { - let mut invs = w.write_component::(); - let inv = invs.get_mut(player.entity).unwrap(); - inv.set_item_at(slot, ItemStack::new(Item::Bow, 1)); - inv.set_item_at(slot + 1, ItemStack::new(Item::Arrow, amnt)); - } - - // Change to survival - w.write_component::() - .insert( - player.entity, - PlayerComponent { - gamemode: Gamemode::Survival, - profile_properties: vec![], - }, - ) - .unwrap(); - - let packet = PlayerDigging::new( - PlayerDiggingStatus::ConsumeItem, - BlockPosition::default(), - 0, - ); - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - let shoot_channel = w.fetch::>(); - let update_channel = w.fetch::>(); - - let update_events = update_channel.read(&mut update_reader).collect::>(); - assert_eq!(update_events.len(), 1); - let first = update_events.first().unwrap(); - assert_eq!(first.player, player.entity); - assert_eq!(first.slots.as_slice(), &[slot + 1]); - - let shoot_events = shoot_channel.read(&mut shoot_reader).collect::>(); - assert_eq!(shoot_events.len(), 1); - let first = shoot_events.first().unwrap(); - assert_eq!(first.shooter.unwrap(), player.entity); - assert_eq!(first.arrow_type, Item::Arrow); - - // In survival, check if amount of arrow stack decreased. - let invs = w.read_component::(); - let inv = invs.get(player.entity).unwrap(); - assert_eq!(inv.item_at(slot + 1).unwrap().amount, amnt - 1); - } -} diff --git a/server/src/player/init.rs b/server/src/player/init.rs deleted file mode 100644 index a0c4d83aa..000000000 --- a/server/src/player/init.rs +++ /dev/null @@ -1,177 +0,0 @@ -use crate::entity::{ - degrees_to_stops, LastKnownPositionComponent, PacketCreatorComponent, PlayerComponent, - VelocityComponent, -}; -use crate::entity::{Metadata, NamedComponent, PositionComponent}; -use crate::network::PlayerPreJoinEvent; -use crate::player::{ChunkPendingComponent, InventoryComponent, LoadedChunksComponent}; -use crate::prelude::*; -use feather_core::level::LevelData; -use feather_core::packet::SpawnPlayer; -use feather_core::{Gamemode, Packet}; -use hashbrown::HashSet; -use shrev::{EventChannel, ReaderId}; -use specs::{Entity, SystemData, WorldExt}; -use specs::{Read, System, World, WriteStorage}; -use std::path::Path; -use std::sync::Arc; - -/// System for initializing the necessary components -/// when a player joins. -#[derive(Default)] -pub struct PlayerInitSystem { - join_event_reader: Option>, -} - -impl<'a> System<'a> for PlayerInitSystem { - type SystemData = ( - Read<'a, EventChannel>, - WriteStorage<'a, PlayerComponent>, - WriteStorage<'a, PositionComponent>, - WriteStorage<'a, VelocityComponent>, - WriteStorage<'a, NamedComponent>, - WriteStorage<'a, ChunkPendingComponent>, - WriteStorage<'a, LoadedChunksComponent>, - WriteStorage<'a, InventoryComponent>, - WriteStorage<'a, Metadata>, - WriteStorage<'a, LastKnownPositionComponent>, - WriteStorage<'a, PacketCreatorComponent>, - Read<'a, LevelData>, - Read<'a, Arc>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - join_events, - mut player_comps, - mut positions, - mut velocities, - mut nameds, - mut chunk_pending_comps, - mut loaded_chunk_comps, - mut inventory_comps, - mut metadata, - mut last_positions, - mut packet_creators, - level, - config, - ) = data; - - // Run through events - for event in join_events.read(&mut self.join_event_reader.as_mut().unwrap()) { - // Load player data - let uuid = event.uuid; - // If this is a new player, set gamemode to server's default (config) - let default_gamemode = &config.server.default_gamemode.clone(); - let world_dir = Path::new(&config.world.name); - - debug!("Loading player data for UUID {}", uuid); - let (gamemode, pos, velocity, inventory_slots) = - match feather_core::player_data::load_player_data(world_dir, uuid) { - Ok(data) => ( - Gamemode::from_id(data.gamemode as u8), - data.entity.read_position(), - data.entity.read_velocity(), - data.inventory, - ), - Err(_) => ( - Gamemode::from_string(default_gamemode.as_str()), - None, // Invalid position will default to world spawn - None, - vec![], // Empty inventory - ), - }; - - let player_comp = PlayerComponent { - profile_properties: event.profile_properties.clone(), - gamemode, - }; - player_comps.insert(event.player, player_comp).unwrap(); - - let spawn_pos = pos.unwrap_or(position!( - f64::from(level.spawn_x), - f64::from(level.spawn_y), - f64::from(level.spawn_z) - )); - let position = PositionComponent { - current: spawn_pos, - previous: spawn_pos, - }; - positions.insert(event.player, position).unwrap(); - - let velocity = VelocityComponent(velocity.unwrap_or_else(|| glm::vec3(0.0, 0.0, 0.0))); - velocities.insert(event.player, velocity).unwrap(); - - let named = NamedComponent { - display_name: event.username.clone(), - uuid: event.uuid, - }; - nameds.insert(event.player, named).unwrap(); - - let chunk_pending_comp = ChunkPendingComponent { - pending: HashSet::new(), - }; - chunk_pending_comps - .insert(event.player, chunk_pending_comp) - .unwrap(); - - let loaded_chunk_comp = LoadedChunksComponent::default(); - loaded_chunk_comps - .insert(event.player, loaded_chunk_comp) - .unwrap(); - - let mut inventory_comp = InventoryComponent::new(); - for slot in inventory_slots { - let slot_index = slot.convert_index(); - if let Some(slot_index) = slot_index { - inventory_comp.set_item_at(slot_index, slot.to_stack()); - } - } - inventory_comps - .insert(event.player, inventory_comp) - .unwrap(); - - let last_position = LastKnownPositionComponent::default(); - last_positions.insert(event.player, last_position).unwrap(); - - let meta = Metadata::Player(crate::entity::metadata::Player::default()); - metadata.insert(event.player, meta).unwrap(); - - let packet_creator = PacketCreatorComponent(&create_packet); - packet_creators - .insert(event.player, packet_creator) - .unwrap(); - } - } - - fn setup(&mut self, world: &mut World) { - Self::SystemData::setup(world); - - self.join_event_reader = Some( - world - .fetch_mut::>() - .register_reader(), - ); - } -} - -pub fn create_packet(world: &World, entity: Entity) -> Box { - let positions = world.read_component::(); - let nameds = world.read_component::(); - let metas = world.read_component::(); - - let position = positions.get(entity).unwrap(); - - let packet = SpawnPlayer { - entity_id: entity.id() as i32, - player_uuid: nameds.get(entity).unwrap().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: metas.get(entity).unwrap().to_full_raw_metadata(), - }; - - Box::new(packet) -} diff --git a/server/src/player/inventory.rs b/server/src/player/inventory.rs deleted file mode 100644 index cfed37950..000000000 --- a/server/src/player/inventory.rs +++ /dev/null @@ -1,759 +0,0 @@ -use crate::disconnect_player; -use crate::entity::{EntitySendEvent, PlayerComponent}; -use crate::network::{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, -}; -use feather_core::network::cast_packet; -use feather_core::network::packet::implementation::{ - CreativeInventoryAction, EntityEquipment, HeldItemChangeServerbound, SetSlot, -}; -use feather_core::network::packet::PacketType; -use feather_core::{Gamemode, ItemStack}; -use num_traits::ToPrimitive; -use shrev::EventChannel; -use smallvec::SmallVec; -use specs::{Component, LazyUpdate, Read, ReadStorage, ReaderId, World, WriteStorage}; -use specs::{DenseVecStorage, SystemData}; -use specs::{Entity, System, Write}; -use std::ops::{Deref, DerefMut}; - -/// Component for storing a player's inventory. -#[derive(Clone, Debug)] -pub struct InventoryComponent { - pub inventory: Inventory, - /// The player's held item. - /// This is stored as an index in the range 0..9. - pub held_item: SlotIndex, -} - -impl InventoryComponent { - pub fn new() -> Self { - Self { - inventory: Inventory::new(InventoryType::Player, 46), - held_item: 0, - } - } - - /// Returns the item in this inventory's - /// main hand. - pub fn item_in_main_hand(&self) -> Option<&ItemStack> { - self.inventory.item_at(SLOT_HOTBAR_OFFSET + self.held_item) - } - - /// Sets the item in this inventory's main hand. - pub fn set_item_in_main_hand(&mut self, item: ItemStack) { - self.inventory - .set_item_at(SLOT_HOTBAR_OFFSET + self.held_item, item); - } -} - -impl Default for InventoryComponent { - fn default() -> Self { - Self::new() - } -} - -impl Deref for InventoryComponent { - type Target = Inventory; - - fn deref(&self) -> &Self::Target { - &self.inventory - } -} - -impl DerefMut for InventoryComponent { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inventory - } -} - -impl Component for InventoryComponent { - type Storage = DenseVecStorage; -} - -/// An equipment slot, with variants -/// listed in the order of the Entity Equipment -/// IDs to allow for easy conversion using `ToPrimitive`/`FromPrimitive`. -#[derive(Debug, Clone, Copy, ToPrimitive, FromPrimitive, PartialEq, Eq, Hash)] -pub enum Equipment { - MainHand, - OffHand, - Boots, - Leggings, - Chestplate, - Helmet, -} - -impl Equipment { - pub fn from_slot_index(index: SlotIndex) -> Option { - match index { - SLOT_OFFHAND => Some(Equipment::OffHand), - SLOT_ARMOR_FEET => Some(Equipment::Boots), - SLOT_ARMOR_LEGS => Some(Equipment::Leggings), - SLOT_ARMOR_CHEST => Some(Equipment::Chestplate), - SLOT_ARMOR_HEAD => Some(Equipment::Helmet), - _ => None, - } - } - - pub fn slot_index(self, held_item: SlotIndex) -> SlotIndex { - match self { - Equipment::MainHand => held_item + SLOT_HOTBAR_OFFSET, - Equipment::OffHand => SLOT_OFFHAND, - Equipment::Boots => SLOT_ARMOR_FEET, - Equipment::Leggings => SLOT_ARMOR_LEGS, - Equipment::Chestplate => SLOT_ARMOR_CHEST, - Equipment::Helmet => SLOT_ARMOR_HEAD, - } - } -} - -/// Event which is triggered when a player -/// updates their inventory. -/// -/// This event could also be triggered when the player -/// changes their held item. -#[derive(Debug, Clone)] -pub struct InventoryUpdateEvent { - /// The slot(s) affected by the update. - /// - /// Multiple slots could be affected when, for - /// example, a player uses the "drag" inventory interaction. - pub slots: SmallVec<[SlotIndex; 2]>, - /// The player owning the updated inventory. - pub player: Entity, -} - -/// System for handling Creative Inventory Action packets. -pub struct CreativeInventorySystem; - -impl<'a> System<'a> for CreativeInventorySystem { - type SystemData = ( - WriteStorage<'a, InventoryComponent>, - ReadStorage<'a, PlayerComponent>, - Write<'a, EventChannel>, - Write<'a, EventChannel>, - Read<'a, PacketQueue>, - Read<'a, LazyUpdate>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (mut inventories, players, mut update_events, mut drop_events, packet_queue, lazy) = - data; - - let packets = packet_queue.for_packet(PacketType::CreativeInventoryAction); - - for (player, packet) in packets { - // Creative Inventory Action can only be used in creative - // mode. - let player_comp = players.get(player).unwrap(); - if player_comp.gamemode != Gamemode::Creative { - disconnect_player( - player, - "Attempted to use Creative Inventory Action while not in creative mode" - .to_string(), - &lazy, - ); - continue; - } - - let packet = cast_packet::(&*packet); - - let inventory = inventories.get_mut(player).unwrap(); - - // Slot -1 means that the user clicked outside the window, - // dropping the item. - if packet.slot == -1 { - match &packet.clicked_item { - Some(stack) => { - let event = PlayerItemDropEvent { - slot: None, - stack: stack.clone(), - player, - }; - drop_events.single_write(event); - - // No need to update inventory - continue; - } - None => (), - } - } - - if packet.slot >= inventory.slot_count() as i16 || packet.slot < -1 { - disconnect_player(player, "Slot index out of bounds".to_string(), &lazy); - continue; - } - - match packet.clicked_item.as_ref() { - Some(item) => { - inventory.set_item_at(packet.slot as usize, item.clone()); - } - None => { - inventory.clear_item_at(packet.slot as usize); - } - } - - // Trigger inventory update event - let event = InventoryUpdateEvent { - slots: smallvec![packet.slot as usize], - player, - }; - update_events.single_write(event); - } - } -} - -/// System for handling Held Item Change packets. -pub struct HeldItemChangeSystem; - -impl<'a> System<'a> for HeldItemChangeSystem { - type SystemData = ( - WriteStorage<'a, InventoryComponent>, - Write<'a, EventChannel>, - Read<'a, PacketQueue>, - Read<'a, LazyUpdate>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (mut inventories, mut events, packet_queue, lazy) = data; - - let packets = packet_queue.for_packet(PacketType::HeldItemChangeServerbound); - - for (player, packet) in packets { - let packet = cast_packet::(&*packet); - - if packet.slot as usize >= HOTBAR_SIZE { - disconnect_player(player, "Hotbar index out of bounds".to_string(), &lazy); - continue; - } - - let inventory = inventories.get_mut(player).unwrap(); - inventory.held_item = packet.slot as usize; - - // Trigger event - let event = InventoryUpdateEvent { - slots: smallvec![inventory.held_item as usize + SLOT_HOTBAR_OFFSET], - player, - }; - events.single_write(event); - } - } -} - -/// System for broadcasting equipment updates. -#[derive(Default)] -pub struct HeldItemBroadcastSystem { - reader: Option>, -} - -impl<'a> System<'a> for HeldItemBroadcastSystem { - type SystemData = ( - ReadStorage<'a, InventoryComponent>, - Read<'a, EventChannel>, - Read<'a, Util>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (inventories, events, util) = data; - - for event in events.read(&mut self.reader.as_mut().unwrap()) { - let inv = inventories.get(event.player).unwrap(); - - for slot in &event.slots { - // Skip this slot if it is not an equipment update. - if let Ok(equipment) = is_equipment_update(&inv, *slot) { - let slot = equipment.slot_index(inv.held_item); - let item = inv.item_at(slot).cloned(); - - let packet = EntityEquipment::new( - event.player.id() as i32, - equipment.to_i32().unwrap(), - item, - ); - - util.broadcast_entity_update(event.player, packet, Some(event.player)); - } - } - } - } - - fn setup(&mut self, world: &mut World) { - Self::SystemData::setup(world); - - self.reader = Some( - world - .fetch_mut::>() - .register_reader(), - ); - } -} - -/// System which listens to `EntitySendEvent`s and -/// sends entity equipment alongside. -#[derive(Default)] -pub struct EquipmentSendSystem { - reader: Option>, -} - -impl<'a> System<'a> for EquipmentSendSystem { - type SystemData = ( - ReadStorage<'a, InventoryComponent>, - ReadStorage<'a, NetworkComponent>, - Read<'a, EventChannel>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (inventories, networks, send_events) = data; - - for event in send_events.read(&mut self.reader.as_mut().unwrap()) { - let network = networks.get(event.player).unwrap(); - let inventory = match inventories.get(event.entity) { - Some(inv) => inv, - None => continue, - }; - - let equipments = [ - Equipment::MainHand, - Equipment::Boots, - Equipment::Leggings, - Equipment::Chestplate, - Equipment::Helmet, - Equipment::OffHand, - ]; - - for equipment in equipments.iter() { - let item = { - let slot = equipment.slot_index(inventory.held_item); - inventory.item_at(slot).cloned() - }; - - let equipment_slot = equipment.to_i32().unwrap(); - - let packet = EntityEquipment::new(event.entity.id() as i32, equipment_slot, item); - send_packet_to_player(network, packet); - } - } - } - - fn setup(&mut self, world: &mut World) { - Self::SystemData::setup(world); - - self.reader = Some(world.fetch_mut::>().register_reader()); - } -} - -/// System for sending the Set Slot packet -/// when a player's inventory is updated. -#[derive(Default)] -pub struct SetSlotSystem { - reader: Option>, -} - -impl<'a> System<'a> for SetSlotSystem { - type SystemData = ( - ReadStorage<'a, InventoryComponent>, - ReadStorage<'a, NetworkComponent>, - Read<'a, EventChannel>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (inventories, networks, events) = data; - - for event in events.read(self.reader.as_mut().unwrap()) { - let inv = inventories.get(event.player).unwrap(); - let network = networks.get(event.player).unwrap(); - - for slot in &event.slots { - let packet = SetSlot { - window_id: 0, - slot: *slot as i16, - slot_data: inv.item_at(*slot as usize).cloned(), - }; - - send_packet_to_player(&network, packet); - } - } - } - - setup_impl!(reader); -} - -/// Returns whether the given update to an inventory -/// is an equipment update. -fn is_equipment_update(inv: &InventoryComponent, slot: SlotIndex) -> Result { - if slot >= SLOT_HOTBAR_OFFSET && slot - SLOT_HOTBAR_OFFSET == inv.held_item { - Ok(Equipment::MainHand) - } else if let Some(equipment) = Equipment::from_slot_index(slot) { - Ok(equipment) - } else { - Err(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::testframework as t; - use feather_core::inventory::{ItemStack, SLOT_ENTITY_EQUIPMENT_MAIN_HAND}; - use feather_core::item::Item; - use specs::WorldExt; - - #[test] - fn test_creative_inventory_system() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let packet = CreativeInventoryAction::new( - SLOT_HOTBAR_OFFSET as i16, - Some(ItemStack::new(Item::IronSword, 1)), - ); - - t::receive_packet(&player, &w, packet); - - let mut update_reader = t::reader(&w); - let mut drop_reader = t::reader(&w); - - d.dispatch(&w); - w.maintain(); - - t::assert_not_disconnected(&player); - - let inv_storage = w.read_component::(); - let inv = inv_storage.get(player.entity).unwrap(); - assert_eq!( - inv.item_at(SLOT_HOTBAR_OFFSET).unwrap(), - &ItemStack::new(Item::IronSword, 1) - ); - - // Confirm that event was triggered - { - let channel = w.fetch::>(); - let events = channel.read(&mut update_reader).collect::>(); - assert_eq!(events.len(), 1); - let first = events.first().unwrap(); - assert_eq!(first.player, player.entity); - assert_eq!(first.slots.as_slice(), &[SLOT_HOTBAR_OFFSET]); - - assert!(w - .fetch::>() - .read(&mut drop_reader) - .next() - .is_none()); - } - - drop(inv_storage); - - let packet = CreativeInventoryAction::new(0, None); - - t::receive_packet(&player, &w, packet.clone()); - - d.dispatch(&w); - w.maintain(); - - t::assert_not_disconnected(&player); - - let inv_storage = w.read_component::(); - let inv = inv_storage.get(player.entity).unwrap(); - assert_eq!(inv.item_at(0), None); - - drop(inv_storage); - - // Now with a survival mode player... - w.write_component::() - .get_mut(player.entity) - .unwrap() - .gamemode = Gamemode::Survival; - - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - t::assert_disconnected(&player); - } - - #[test] - fn test_creative_inventory_slot_out_of_bounds() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let packet = CreativeInventoryAction::new(46, Some(ItemStack::new(Item::IronSword, 1))); - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - t::assert_disconnected(&player); - } - - #[test] - fn test_creative_inventory_armor() { - let equipments = [ - Equipment::OffHand, - Equipment::Boots, - Equipment::Leggings, - Equipment::Chestplate, - Equipment::Helmet, - ]; - - for equipment in equipments.iter() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let mut event_reader = t::reader(&w); - - let packet = CreativeInventoryAction::new( - equipment.slot_index(0) as i16, - Some(ItemStack::new(Item::IronSword, 1)), - ); - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - let ch = w.fetch::>(); - let events = ch.read(&mut event_reader).collect::>(); - - assert_eq!(events.len(), 1); - let first = events.first().unwrap(); - - assert_eq!(first.slots.as_slice(), &[equipment.slot_index(0)]); - assert_eq!(first.player, player.entity); - } - } - - #[test] - fn test_creative_inventory_system_drop_item() { - // Drop item - slot index -1 - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let stack = ItemStack::new(Item::CookedBeef, 1); - - let mut drop_reader = t::reader(&w); - let mut update_reader = t::reader(&w); - - let packet = CreativeInventoryAction { - slot: -1, - clicked_item: Some(stack.clone()), - }; - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - t::assert_not_disconnected(&player); - - let channel = w.fetch::>(); - let events = channel.read(&mut update_reader).collect::>(); - assert!(events.is_empty()); - - let channel = w.fetch::>(); - let events = channel.read(&mut drop_reader).collect::>(); - - assert_eq!(events.len(), 1); - let first = events.first().unwrap(); - assert_eq!(first.stack, stack); - assert_eq!(first.player, player.entity); - assert_eq!(first.slot, None); - } - - #[test] - fn test_held_item_change_system() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let slot = 4; - - let mut event_reader = t::reader(&w); - - let packet = HeldItemChangeServerbound::new(slot); - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - let channel = w.fetch::>(); - let events = channel.read(&mut event_reader).collect::>(); - - assert_eq!(events.len(), 1); - - let first = events.first().unwrap(); - assert_eq!(first.player, player.entity); - assert_eq!( - first.slots.as_slice(), - &[slot as usize + SLOT_HOTBAR_OFFSET] - ); - - let inventories = w.read_component::(); - let inv = inventories.get(player.entity).unwrap(); - - assert_eq!(inv.held_item, slot as usize); - } - - #[test] - fn test_held_item_change_system_out_of_bounds() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let slot = 9; - - let packet = HeldItemChangeServerbound::new(slot); - t::receive_packet(&player, &w, packet); - - d.dispatch(&w); - w.maintain(); - - t::assert_disconnected(&player); // Slot out of bounds - } - - #[test] - fn test_held_item_broadcast_system() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - let player2 = t::add_player(&mut w); - - { - let mut invs = w.write_component::(); - let inv = invs.get_mut(player.entity).unwrap(); - inv.held_item = 0; - inv.set_item_at(SLOT_HOTBAR_OFFSET, ItemStack::new(Item::IronSword, 1)); - } - - let event = InventoryUpdateEvent { - player: player.entity, - slots: smallvec![SLOT_HOTBAR_OFFSET], - }; - - w.fetch_mut::>() - .single_write(event); - - d.dispatch(&w); - w.maintain(); - - let packet = t::assert_packet_received(&player2, PacketType::EntityEquipment); - - let packet = cast_packet::(&*packet); - assert_eq!(packet.slot, SLOT_ENTITY_EQUIPMENT_MAIN_HAND as i32); - assert_eq!(packet.entity_id, player.entity.id() as i32); - assert_eq!(packet.item, Some(ItemStack::new(Item::IronSword, 1))); - - t::assert_packet_not_received(&player, PacketType::EntityEquipment); - } - - #[test] - fn test_equipment_send_system() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - let player2 = t::add_player(&mut w); - - let event = EntitySendEvent { - player: player.entity, - entity: player2.entity, - }; - - w.fetch_mut::>().single_write(event); - - { - let mut invs = w.write_component::(); - let inv = invs.get_mut(player2.entity).unwrap(); - - inv.held_item = 1; - inv.set_item_at(SLOT_HOTBAR_OFFSET + 1, ItemStack::new(Item::IronSword, 1)); - inv.set_item_at(SLOT_ARMOR_HEAD, ItemStack::new(Item::DiamondHelmet, 1)); - } - - d.dispatch(&w); - w.maintain(); - - let packets = t::received_packets(&player, None); - - let packets = packets - .into_iter() - .filter(|packet| packet.ty() == PacketType::EntityEquipment) - .collect::>(); - - assert_eq!(packets.len(), 6); - - for packet in packets { - let packet = cast_packet::(&*packet); - assert_eq!(packet.entity_id, player2.entity.id() as i32); - } - } - - #[test] - fn test_set_slot_system() { - let (mut w, mut d) = t::builder().with(SetSlotSystem::default(), "").build(); - - let player = t::add_player(&mut w); - let stack = ItemStack::new(Item::EnderPearl, 8); - { - let mut inventories = w.write_component::(); - inventories - .get_mut(player.entity) - .unwrap() - .set_item_at(0, stack.clone()); - - let event = InventoryUpdateEvent { - slots: smallvec![0], - player: player.entity, - }; - t::trigger_event(&w, event); - } - - d.dispatch(&w); - w.maintain(); - - let packet = t::assert_packet_received(&player, PacketType::SetSlot); - let packet = cast_packet::(&*packet); - - assert_eq!(packet.window_id, 0); - assert_eq!(packet.slot_data, Some(stack)); - assert_eq!(packet.slot, 0); - } - - #[test] - fn test_is_equipment_update() { - let mut inv = InventoryComponent::default(); - inv.held_item = 0; - - assert!(is_equipment_update(&inv, 21).is_err()); - assert_eq!( - is_equipment_update(&inv, SLOT_HOTBAR_OFFSET), - Ok(Equipment::MainHand) - ); - assert_eq!( - is_equipment_update(&inv, SLOT_ARMOR_HEAD), - Ok(Equipment::Helmet) - ); - assert_eq!( - is_equipment_update(&inv, SLOT_ARMOR_CHEST), - Ok(Equipment::Chestplate) - ); - assert_eq!( - is_equipment_update(&inv, SLOT_ARMOR_LEGS), - Ok(Equipment::Leggings) - ); - assert_eq!( - is_equipment_update(&inv, SLOT_ARMOR_FEET), - Ok(Equipment::Boots) - ); - assert_eq!( - is_equipment_update(&inv, SLOT_OFFHAND), - Ok(Equipment::OffHand) - ); - } -} diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs deleted file mode 100644 index 2cdeeaee6..000000000 --- a/server/src/player/mod.rs +++ /dev/null @@ -1,113 +0,0 @@ -//! This module provides systems and components -//! relating to players, including player movement -//! and inventory handling. - -/// Module for handling player animation broadcasting -/// (e.g. when a player swings their arm). -mod animation; -/// Module for broadcasting when a player joins and leaves. -mod broadcast; -/// Module for handling and broadcasting chat messages. -mod chat; -/// Module for handling the Player Digging packet. -mod digging; -/// Module for initializing the necessary components -/// when a player joins. -mod init; -/// Module for handling player inventory. -mod inventory; -/// Module for handling player movement packets. -/// Also handles loading/unloading chunks when necessary. -mod movement; -/// Module for handling player block placements. -mod placement; -mod resource_pack; -mod save; -mod view; - -pub use broadcast::PlayerDisconnectEvent; -pub use init::create_packet; - -pub use movement::{ - send_chunk_to_player, ChunkCrossSystem, ChunkPendingComponent, LoadedChunksComponent, -}; - -pub use animation::PlayerAnimationEvent; - -pub use digging::PlayerItemDropEvent; -pub use inventory::{InventoryComponent, InventoryUpdateEvent}; -pub use save::save_player_data; - -use crate::player::inventory::SetSlotSystem; -use crate::player::placement::BlockPlacementSystem; -use crate::player::save::PlayerDataSaveSystem; -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_DATA_SAVE, PLAYER_DIGGING, PLAYER_INIT, PLAYER_MOVEMENT, RESOURCE_PACK_SEND, SET_SLOT, - VIEW_UPDATE, -}; -use animation::{AnimationBroadcastSystem, PlayerAnimationSystem}; -use broadcast::{DisconnectBroadcastSystem, JoinBroadcastSystem}; -use chat::{ChatBroadcastSystem, PlayerChatSystem}; -use digging::BlockUpdateBroadcastSystem; -use digging::PlayerDiggingSystem; -use init::PlayerInitSystem; -use inventory::{ - CreativeInventorySystem, EquipmentSendSystem, HeldItemBroadcastSystem, HeldItemChangeSystem, -}; -use movement::{ChunkSendSystem, ClientChunkUnloadSystem, PlayerMovementSystem}; -use resource_pack::ResourcePackSendSystem; -use specs::DispatcherBuilder; - -pub const PLAYER_EYE_HEIGHT: f64 = 1.62; -pub const PLAYER_EYE_HEIGHT_WHILE_SNEAKING: f64 = 1.54; - -pub fn init_logic(dispatcher: &mut DispatcherBuilder) { - dispatcher.add(PlayerDiggingSystem, PLAYER_DIGGING, &[NETWORK]); - dispatcher.add(PlayerAnimationSystem, PLAYER_ANIMATION, &[NETWORK]); - dispatcher.add(CreativeInventorySystem, CREATIVE_INVENTORY, &[NETWORK]); - dispatcher.add(HeldItemChangeSystem, HELD_ITEM_CHANGE, &[NETWORK]); - dispatcher.add(PlayerMovementSystem, PLAYER_MOVEMENT, &[NETWORK]); - dispatcher.add(PlayerChatSystem, PLAYER_CHAT, &[NETWORK]); - dispatcher.add(BlockPlacementSystem, BLOCK_PLACEMENT, &[NETWORK]); - dispatcher.add( - PlayerDataSaveSystem::default(), - PLAYER_DATA_SAVE, - &[NETWORK], - ); -} - -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(HeldItemBroadcastSystem::default(), HELD_ITEM_BROADCAST, &[]); - dispatcher.add(JoinBroadcastSystem::default(), JOIN_BROADCAST, &[]); - dispatcher.add( - DisconnectBroadcastSystem::default(), - DISCONNECT_BROADCAST, - &[], - ); - dispatcher.add( - AnimationBroadcastSystem::default(), - ANIMATION_BROADCAST, - &[], - ); - dispatcher.add(EquipmentSendSystem::default(), EQUIPMENT_SEND, &[]); - dispatcher.add(ResourcePackSendSystem::default(), RESOURCE_PACK_SEND, &[]); - dispatcher.add(ChunkSendSystem::default(), CHUNK_SEND, &[]); - dispatcher.add( - BlockUpdateBroadcastSystem::default(), - BLOCK_BREAK_BROADCAST, - &[], - ); - dispatcher.add(SetSlotSystem::default(), SET_SLOT, &[]); - dispatcher.add(ChatBroadcastSystem::default(), CHAT_BROADCAST, &[]); -} diff --git a/server/src/player/movement.rs b/server/src/player/movement.rs deleted file mode 100644 index 919847c85..000000000 --- a/server/src/player/movement.rs +++ /dev/null @@ -1,469 +0,0 @@ -use std::collections::VecDeque; -use std::ops::{Deref, DerefMut}; -use std::sync::Arc; - -use hashbrown::HashSet; -use rayon::prelude::*; -use shrev::{EventChannel, ReaderId}; -use specs::storage::{BTreeStorage, ComponentEvent}; -use specs::{ - BitSet, Component, Entities, Entity, Join, LazyUpdate, ParJoin, Read, ReadExpect, ReadStorage, - System, WorldExt, Write, WriteStorage, -}; - -use feather_core::network::cast_packet; -use feather_core::network::packet::implementation::{ - ChunkData, PlayerLook, PlayerPosition, PlayerPositionAndLookServerbound, UnloadChunk, -}; -use feather_core::network::packet::{Packet, PacketType}; -use feather_core::world::chunk::Chunk; -use feather_core::world::{ChunkMap, ChunkPosition, Position}; - -use crate::chunk_logic::{ - load_chunk, ChunkHolderComponent, ChunkHolderReleaseEvent, ChunkHolders, ChunkLoadEvent, - ChunkLoadFailEvent, ChunkWorkerHandle, -}; -use crate::config::Config; -use crate::entity::PositionComponent; -use crate::network::{send_packet_to_player, NetworkComponent, PacketQueue}; -use crate::{TickCount, TPS}; - -// MOVEMENT HANDLING - -/// System for handling player movement -/// packets. -pub struct PlayerMovementSystem; - -impl<'a> System<'a> for PlayerMovementSystem { - type SystemData = (WriteStorage<'a, PositionComponent>, Read<'a, PacketQueue>); - - fn run(&mut self, data: Self::SystemData) { - let (mut positions, packet_queue) = data; - - // Take movement packets - let mut packets = vec![]; - packets.append(&mut packet_queue.for_packet(PacketType::PlayerPosition)); - packets.append(&mut packet_queue.for_packet(PacketType::PlayerPositionAndLookServerbound)); - packets.append(&mut packet_queue.for_packet(PacketType::PlayerLook)); - - // Handle movement packets - for (player, packet) in packets { - let position = positions.get(player).unwrap(); - - // Get position using packet and old position - let new_pos = new_pos_from_packet(position.previous, packet); - - // Set new position - positions.get_mut(player).unwrap().current = new_pos; - } - } -} - -fn new_pos_from_packet(old_pos: Position, packet: Box) -> Position { - match packet.ty() { - PacketType::PlayerPosition => { - let packet = cast_packet::(&*packet); - - position!( - packet.x, - packet.feet_y, - packet.z, - old_pos.pitch, - old_pos.yaw, - packet.on_ground - ) - } - PacketType::PlayerLook => { - let packet = cast_packet::(&*packet); - - position!( - old_pos.x, - old_pos.y, - old_pos.z, - packet.pitch, - packet.yaw, - packet.on_ground - ) - } - PacketType::PlayerPositionAndLookServerbound => { - let packet = cast_packet::(&*packet); - - position!( - packet.x, - packet.feet_y, - packet.z, - packet.pitch, - packet.yaw, - packet.on_ground - ) - } - _ => panic!(), - } -} - -// CHUNK LOAD/UNLOAD HANDLING - -/// Component for storing which chunks a client -/// has loaded and which are queued to be unloaded -/// on the client. -#[derive(Clone, Default, Debug)] -pub struct LoadedChunksComponent { - /// All chunks which are loaded on the client, i.e. - /// which have had a Chunk Data packet sent. - loaded_chunks: HashSet, - /// Chunks queued for unloading on the client. - /// - /// Note that that these chunks will not be unloaded - /// on the server - all that will happen is that an Unload - /// Chunk packet will be sent to the client. This avoids client-side - /// memory leaks. - unload_queue: VecDeque<(ChunkPosition, u64)>, -} - -impl Component for LoadedChunksComponent { - type Storage = BTreeStorage; -} - -/// Component storing what chunks are pending -/// to send to a player. -#[derive(Clone, Debug)] -pub struct ChunkPendingComponent { - pub pending: HashSet, -} - -impl Deref for ChunkPendingComponent { - type Target = HashSet; - - fn deref(&self) -> &Self::Target { - &self.pending - } -} - -impl DerefMut for ChunkPendingComponent { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.pending - } -} - -impl Component for ChunkPendingComponent { - type Storage = BTreeStorage; -} - -/// Time after a player can no longer see a chunk -/// 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 -/// chunks no longer within the player's view distance. -#[derive(Default)] -pub struct ChunkCrossSystem { - dirty: BitSet, - reader: Option>, -} - -impl<'a> System<'a> for ChunkCrossSystem { - type SystemData = ( - ReadStorage<'a, PositionComponent>, - Read<'a, ChunkMap>, - Read<'a, TickCount>, - Read<'a, Arc>, - WriteStorage<'a, LoadedChunksComponent>, - WriteStorage<'a, ChunkHolderComponent>, - ReadStorage<'a, NetworkComponent>, - Write<'a, ChunkHolders>, - Write<'a, EventChannel>, - ReadExpect<'a, ChunkWorkerHandle>, - Read<'a, LazyUpdate>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - positions, - chunk_map, - tick_count, - config, - mut loaded_chunks_comps, - mut chunk_holder_comps, - net_comps, - mut holders, - mut cross_events, - chunk_handle, - lazy, - entities, - ) = data; - - self.dirty.clear(); - - for event in positions.channel().read(self.reader.as_mut().unwrap()) { - match event { - ComponentEvent::Modified(id) | ComponentEvent::Inserted(id) => { - self.dirty.add(*id); - } - _ => (), - } - } - - // Go through events and handle them accordingly - for (position, net, chunk_holder, loaded_chunks, player, _) in ( - &positions, - &net_comps, - &mut chunk_holder_comps, - &mut loaded_chunks_comps, - &entities, - &self.dirty, - ) - .join() - { - let old_chunk_pos = position.previous.chunk_pos(); - let new_chunk_pos = position.current.chunk_pos(); - - if old_chunk_pos != new_chunk_pos { - // Player has moved across chunk boundaries. Handle accordingly. - let chunks = chunks_within_view_distance(&config, new_chunk_pos); - - for chunk in &chunks { - if loaded_chunks.loaded_chunks.contains(chunk) { - // Already sent - nothing to do. - continue; - } - - send_chunk_to_player( - *chunk, - net, - player, - &chunk_map, - &chunk_handle, - &mut holders, - chunk_holder, - loaded_chunks, - &lazy, - ); - } - - // Now, queue all chunks which need to be unloaded for unloading. - let old_chunks = chunks_within_view_distance(&config, old_chunk_pos); - - for chunk in old_chunks { - if chunks.contains(&chunk) { - // Chunk should remain loaded. Nothing to do - continue; - } - - // Queue chunk for unloading. - 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); - } - } - } - - flagged_setup_impl!(PositionComponent, reader); -} - -/// System for sending chunks to players once they're loaded. -/// -/// This system listens to `ChunkLoadEvent`s. -#[derive(Default)] -pub struct ChunkSendSystem { - load_event_reader: Option>, - fail_event_reader: Option>, -} - -impl<'a> System<'a> for ChunkSendSystem { - type SystemData = ( - WriteStorage<'a, ChunkPendingComponent>, - ReadStorage<'a, NetworkComponent>, - Read<'a, ChunkMap>, - Read<'a, EventChannel>, - Read<'a, EventChannel>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (mut pendings, netcomps, chunk_map, load_events, fail_events) = data; - - for event in load_events.read(&mut self.load_event_reader.as_mut().unwrap()) { - // TODO perhaps this is slightly inefficient? - (&netcomps, &mut pendings) - .par_join() - .for_each(|(net, pending)| { - if pending.contains(&event.pos) { - // It's safe to unwrap the chunk value now, - // because we know it's been loaded. - let chunk = chunk_map.chunk_at(event.pos).unwrap(); - send_chunk_data(chunk, net); - - pending.remove(&event.pos); - } - }); - } - - for event in fail_events.read(self.fail_event_reader.as_mut().unwrap()) { - (&mut pendings).par_join().for_each(|pending| { - if pending.contains(&event.pos) { - // The chunk failed to load - skip sending it. - // See issue #71 - pending.remove(&event.pos); - } - }); - } - } - - setup_impl!(load_event_reader, fail_event_reader); -} - -/// System for sending the Unload Chunk packet when the time comes. -pub struct ClientChunkUnloadSystem; - -impl<'a> System<'a> for ClientChunkUnloadSystem { - type SystemData = ( - WriteStorage<'a, LoadedChunksComponent>, - ReadStorage<'a, NetworkComponent>, - ReadStorage<'a, PositionComponent>, - WriteStorage<'a, ChunkHolderComponent>, - Entities<'a>, - Write<'a, ChunkHolders>, - Read<'a, TickCount>, - Read<'a, Arc>, - Write<'a, EventChannel>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - mut loaded_chunks_comps, - net_comps, - positions, - mut chunk_holder_comps, - entities, - mut chunk_holders, - tick_count, - config, - mut holder_release_events, - ) = data; - - ( - &mut loaded_chunks_comps, - &net_comps, - &positions, - &mut chunk_holder_comps, - &entities, - ) - .join() - .for_each( - |(loaded_chunks_comp, net_comp, position, chunk_holder_comp, player)| { - // Go through queue and see if it's time to unload any chunks. - while let Some((chunk, time)) = loaded_chunks_comp.unload_queue.front() { - let chunk = *chunk; - if tick_count.0 >= *time { - // Unload if needed. - - let chunks_within_view_distance = - chunks_within_view_distance(&config, position.current.chunk_pos()); - - if chunks_within_view_distance.contains(&chunk) { - // Chunk is within view distance again - don't unload it. - loaded_chunks_comp.unload_queue.pop_front(); - continue; - } - - let unload_chunk = UnloadChunk::new(chunk.x, chunk.z); - send_packet_to_player(net_comp, unload_chunk); - - // Remove chunk from queue. - loaded_chunks_comp.unload_queue.pop_front(); - // Remove from loaded chunk list. - loaded_chunks_comp.loaded_chunks.remove(&chunk); - // Remove hold on chunk so it can be unloaded. - chunk_holders.remove_holder(chunk, player, &mut holder_release_events); - // Remove hold from chunk holder component. - chunk_holder_comp.holds.remove(&chunk); - } else { - // No more chunks in queue that should - // be unloaded - finished. - break; - } - } - }, - ); - } -} - -/// Returns the set of all chunk positions -/// within the server view distance of a given -/// chunk. -fn chunks_within_view_distance(config: &Config, chunk: ChunkPosition) -> HashSet { - let view_distance = i32::from(config.server.view_distance); - let mut results = HashSet::with_capacity((view_distance * view_distance) as usize); - - for x in -view_distance..=view_distance { - for z in -view_distance..=view_distance { - results.insert(ChunkPosition::new(chunk.x + x, chunk.z + z)); - } - } - - results -} - -/// Attempts to send the chunk at the given position to -/// the given player. If the chunk is not loaded, it will -/// be loaded and sent at a later time as soon as it is -/// loaded. -#[allow(clippy::too_many_arguments)] // TODO: get rid of LoadedChunksComponent -pub fn send_chunk_to_player( - chunk_pos: ChunkPosition, - net: &NetworkComponent, - player: Entity, - chunk_map: &ChunkMap, - chunk_handle: &ChunkWorkerHandle, - holders: &mut ChunkHolders, - holder: &mut ChunkHolderComponent, - loaded_chunks: &mut LoadedChunksComponent, - lazy: &LazyUpdate, -) { - holders.insert_holder(chunk_pos, player); - holder.holds.insert(chunk_pos); - loaded_chunks.loaded_chunks.insert(chunk_pos); - - if let Some(chunk) = chunk_map.chunk_at(chunk_pos) { - send_chunk_data(chunk, net); - } else { - // Queue for loading - load_chunk(chunk_handle, chunk_pos); - lazy.exec_mut(move |world| { - world - .write_component::() - .get_mut(player) - .unwrap() - .pending - .insert(chunk_pos); - }); - } -} - -fn send_chunk_data(chunk: &Chunk, net: &NetworkComponent) { - let packet = ChunkData::new(chunk.clone()); - send_packet_to_player(net, packet); -} diff --git a/server/src/player/placement.rs b/server/src/player/placement.rs deleted file mode 100644 index 4974554f2..000000000 --- a/server/src/player/placement.rs +++ /dev/null @@ -1,215 +0,0 @@ -use crate::blocks::{BlockUpdateCause, BlockUpdateEvent}; -use crate::disconnect_player; -use crate::entity::PlayerComponent; -use crate::network::PacketQueue; -use crate::player::{InventoryComponent, InventoryUpdateEvent}; -use crate::prelude::Gamemode; -use feather_core::inventory::SLOT_HOTBAR_OFFSET; -use feather_core::network::cast_packet; -use feather_core::network::packet::implementation::PlayerBlockPlacement; -use feather_core::world::ChunkMap; -use feather_core::{Block, ItemStack, PacketType}; -use feather_item_block::ItemToBlock; -use shrev::EventChannel; -use specs::{LazyUpdate, Read, ReadStorage, System, Write, WriteStorage}; - -/// System for handling Player Block Placement packets -/// and updating the world accordingly. -pub struct BlockPlacementSystem; - -impl<'a> System<'a> for BlockPlacementSystem { - type SystemData = ( - WriteStorage<'a, InventoryComponent>, - ReadStorage<'a, PlayerComponent>, - Write<'a, ChunkMap>, - Write<'a, EventChannel>, - Write<'a, EventChannel>, - Read<'a, PacketQueue>, - Read<'a, LazyUpdate>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - mut inventories, - players, - mut chunk_map, - mut block_update_events, - mut inventory_update_events, - packet_queue, - lazy, - ) = data; - - let packets = packet_queue.for_packet(PacketType::PlayerBlockPlacement); - - for (player, packet) in packets { - let packet = cast_packet::(&*packet); - - // TODO: handle slabs, blocks with directions, etc. - let inventory = inventories.get_mut(player).unwrap(); - - let item = continue_if_none!(inventory.item_in_main_hand()); - - let block = continue_if_none!(item.ty.to_block()); - - let placed_on = match chunk_map.block_at(packet.location) { - Some(block) => block, - None => { - disconnect_player( - player, - String::from("Attempted to place block in unloaded chunk"), - &lazy, - ); - continue; - } - }; - - // TODO: waterlogged blocks, more - let pos = match placed_on { - Block::Grass | Block::TallGrass(_) | Block::Water(_) | Block::Lava(_) => { - packet.location - } - _ => packet.location + packet.face.placement_offset(), - }; - - let old = match chunk_map.block_at(pos) { - Some(block) => block, - None => { - disconnect_player( - player, - String::from("Attempted to place block in unloaded chunk"), - &lazy, - ); - continue; - } - }; - - chunk_map.set_block_at(pos, block).unwrap(); - - let event = BlockUpdateEvent { - cause: BlockUpdateCause::Player(player), - pos, - old_block: old, - new_block: block, - }; - - block_update_events.single_write(event); - - let gamemode = players.get(player).unwrap().gamemode; - - // Update player's inventory if in survival - if gamemode == Gamemode::Survival { - let item = ItemStack::new(item.ty, item.amount - 1); - inventory.set_item_in_main_hand(item); - - let event = InventoryUpdateEvent { - slots: smallvec![SLOT_HOTBAR_OFFSET + inventory.held_item], - player, - }; - inventory_update_events.single_write(event); - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::testframework as t; - use feather_core::inventory::SLOT_HOTBAR_OFFSET; - use feather_core::network::packet::implementation::Face; - use feather_core::{Block, BlockPosition, Item, ItemStack}; - use specs::WorldExt; - - #[test] - fn test_block_placement_system() { - let (mut w, mut d) = t::builder().with(BlockPlacementSystem, "").build(); - - t::populate_with_air(&mut w); - - let player = t::add_player(&mut w); - - { - let mut inventories = w.write_component::(); - inventories - .get_mut(player.entity) - .unwrap() - .set_item_at(SLOT_HOTBAR_OFFSET, ItemStack::new(Item::Cobblestone, 1)); - - let mut players = w.write_component::(); - players.get_mut(player.entity).unwrap().gamemode = Gamemode::Survival; - } - - let pos = BlockPosition::new(10, 20, 30); - - let packet = PlayerBlockPlacement { - location: pos, - face: Face::Top, - hand: 0, - cursor_position_x: 0.0, - cursor_position_y: 0.0, - cursor_position_z: 0.0, - }; - t::receive_packet(&player, &w, packet); - - let mut reader = t::reader(&w); - - d.dispatch(&w); - w.maintain(); - - let events = t::triggered_events::(&w, &mut reader); - let first = events.first().unwrap(); - - assert_eq!(first.cause, BlockUpdateCause::Player(player.entity)); - assert_eq!(first.old_block, Block::Air); - assert_eq!(first.new_block, Block::Cobblestone); - assert_eq!(first.pos, pos + BlockPosition::new(0, 1, 0)); - - let inventory = w - .read_component::() - .get(player.entity) - .unwrap() - .clone(); - assert_eq!(inventory.item_in_main_hand(), None); - } - - #[test] - fn test_block_placement_system_unloaded_chunk() { - let (mut w, mut d) = t::builder().with(BlockPlacementSystem, "").build(); - - t::populate_with_air(&mut w); - - let player = t::add_player(&mut w); - - { - let mut inventories = w.write_component::(); - inventories - .get_mut(player.entity) - .unwrap() - .set_item_at(SLOT_HOTBAR_OFFSET, ItemStack::new(Item::Cobblestone, 1)); - - let mut players = w.write_component::(); - players.get_mut(player.entity).unwrap().gamemode = Gamemode::Survival; - } - - let pos = BlockPosition::new(1000, 100, 2000); - - let packet = PlayerBlockPlacement { - location: pos, - face: Face::Top, - hand: 0, - cursor_position_x: 0.0, - cursor_position_y: 0.0, - cursor_position_z: 0.0, - }; - t::receive_packet(&player, &w, packet); - - let mut reader = t::reader(&w); - - d.dispatch(&w); - w.maintain(); - - t::assert_disconnected(&player); - - assert!(t::triggered_events::(&w, &mut reader).is_empty()); - } -} diff --git a/server/src/player/resource_pack.rs b/server/src/player/resource_pack.rs deleted file mode 100644 index 917536250..000000000 --- a/server/src/player/resource_pack.rs +++ /dev/null @@ -1,86 +0,0 @@ -//! System for sending resource pack to new players. - -use crate::config::Config; -use crate::joinhandler::PlayerJoinEvent; -use crate::network::{send_packet_to_player, NetworkComponent}; -use feather_core::network::packet::implementation::ResourcePackSend; -use shrev::{EventChannel, ReaderId}; -use specs::{Read, ReadStorage, System}; -use std::sync::Arc; - -/// System for sending resource pack to new players, -/// if enabled. -/// -/// This system listens to `PlayerJoinEvent`s. -#[derive(Default)] -pub struct ResourcePackSendSystem { - reader: Option>, -} - -impl<'a> System<'a> for ResourcePackSendSystem { - type SystemData = ( - ReadStorage<'a, NetworkComponent>, - Read<'a, Arc>, - Read<'a, EventChannel>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (networks, config, join_events) = data; - if config.resource_pack.url.is_empty() { - return; // Resource pack not enabled - } - - for event in join_events.read(self.reader.as_mut().unwrap()) { - let network = networks.get(event.player).unwrap(); - - let packet = ResourcePackSend { - url: config.resource_pack.url.clone(), - hash: config.resource_pack.hash.to_lowercase(), - }; - - send_packet_to_player(&network, packet); - } - } - - setup_impl!(reader); -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::testframework as t; - use feather_core::network::cast_packet; - use feather_core::network::packet::PacketType; - use specs::WorldExt; - - #[test] - fn test_resource_pack_send_system() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let event = PlayerJoinEvent { - player: player.entity, - }; - t::trigger_event(&w, event); - - let url = "https://rust-lang.org/".to_string(); - let hash = "bLa".to_string(); - - { - let mut config = Config::clone(&w.fetch::>()); - config.resource_pack.url = url.clone(); - config.resource_pack.hash = hash.clone(); - w.insert(Arc::new(config)); - } - - d.dispatch(&w); - w.maintain(); - - let packet = t::assert_packet_received(&player, PacketType::ResourcePackSend); - let packet = cast_packet::(&*packet); - - assert_eq!(packet.url, url); - assert_eq!(packet.hash, hash.to_lowercase()); - } -} diff --git a/server/src/player/save.rs b/server/src/player/save.rs deleted file mode 100644 index df1ac1140..000000000 --- a/server/src/player/save.rs +++ /dev/null @@ -1,101 +0,0 @@ -//! Saving of player data files and a system to save -//! player data on disconnect. - -use crate::entity::{NamedComponent, PlayerComponent, PositionComponent}; -use crate::player::{InventoryComponent, PlayerDisconnectEvent}; -use crate::prelude::Config; -use crossbeam::Receiver; -use feather_core::entity::BaseEntityData; -use feather_core::inventory::Inventory; -use feather_core::player_data::{InventorySlot, PlayerData}; -use feather_core::{player_data, Gamemode, Position}; -use shrev::{EventChannel, ReaderId}; -use specs::{Read, ReadStorage, System}; -use std::path::Path; -use std::sync::Arc; -use uuid::Uuid; - -/// System to save player data upon disconnect. -/// -/// This system listens to `PlayerDisconnectEvent`s. -#[derive(Default)] -pub struct PlayerDataSaveSystem { - reader: Option>, -} - -impl<'a> System<'a> for PlayerDataSaveSystem { - type SystemData = ( - Read<'a, Arc>, - ReadStorage<'a, PositionComponent>, - ReadStorage<'a, PlayerComponent>, - ReadStorage<'a, NamedComponent>, - ReadStorage<'a, InventoryComponent>, - Read<'a, EventChannel>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (config, positions, players, nameds, inventories, disconnect_events) = data; - - for event in disconnect_events.read(self.reader.as_mut().unwrap()) { - let player = players.get(event.player).unwrap(); - save_player_data( - &config, - positions.get(event.player).unwrap().current, - player.gamemode, - &inventories.get(event.player).unwrap().inventory, - nameds.get(event.player).unwrap().uuid, - ); - } - } - - setup_impl!(reader); -} - -/// Saves a player's data. -/// -/// This operation is performed asynchronously, -/// and a channel is returned which will receive -/// a message upon completion. -pub fn save_player_data( - config: &Config, - position: Position, - gamemode: Gamemode, - inventory: &Inventory, - uuid: Uuid, -) -> Receiver<()> { - let data = PlayerData { - entity: BaseEntityData { - velocity: vec![0.0; 3], // Player velocity has no effect - position: vec![position.x, position.y, position.z], - rotation: vec![position.yaw, position.pitch], - }, - gamemode: gamemode.get_id() as i32, - inventory: inventory - .items() - .iter() - .enumerate() - .filter_map(|(index, item)| match item.clone() { - Some(item) => Some((index, item)), - None => None, - }) - .map(|(index, item)| InventorySlot::from_network_index(index, item)) - .collect(), - }; - - // Channel used to communicate with Tokio task - let (tx, rx) = crossbeam::bounded(1); - - let world_dir = Path::new(&config.world.name).to_owned(); - - tokio_executor::blocking::run(move || { - if let Err(e) = player_data::save_player_data(world_dir.as_path(), uuid, data) { - error!("Failed to save player data for UUID {}: {:?}", uuid, e); - } else { - debug!("Saved player data for UUID {}", uuid); - } - - let _ = tx.send(()); // Channel could have been dropped, so ignore result - }); - - rx -} diff --git a/server/src/player/view.rs b/server/src/player/view.rs deleted file mode 100644 index d0cf7504c..000000000 --- a/server/src/player/view.rs +++ /dev/null @@ -1,221 +0,0 @@ -//! 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; -use crate::lazy::LazyUpdateExt; -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::{LazyUpdate, 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, LazyUpdate>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (networks, cross_events, chunk_entities, config, lazy) = 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); - - if let Some(network) = networks.get(*entity) { - let packet = DestroyEntities { - entity_ids: vec![event.player.id() as i32], - }; - send_packet_to_player(network, packet); - } - } 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. - lazy.send_entity_to_player(event.player, *entity); - - if networks.get(*entity).is_some() { - lazy.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::{item, PositionComponent}; - use crate::testframework as t; - use feather_core::network::cast_packet; - use feather_core::network::packet::implementation::{SpawnObject, SpawnPlayer}; - use feather_core::{ChunkPosition, Item, ItemStack, PacketType}; - use hashbrown::HashSet; - use specs::{Builder, WorldExt}; - - #[test] - fn test_view_update_system() { - let (mut world, mut dispatcher) = t::builder() - .with(ViewUpdateSystem::default(), "view") - .build(); - - let player_chunk = ChunkPosition::new(0, 0); - - let player1 = t::add_player_without_holder(&mut world); - let player2 = t::add_player_without_holder(&mut world); - - let entity1 = item::create( - &world.fetch(), - &world.fetch(), - ItemStack::new(Item::Stone, 0), - 0, - ) - .with(PositionComponent::default()) - .build(); - let entity2 = item::create( - &world.fetch(), - &world.fetch(), - ItemStack::new(Item::Stone, 0), - 0, - ) - .with(PositionComponent::default()) - .build(); - let entity3 = item::create( - &world.fetch(), - &world.fetch(), - ItemStack::new(Item::Stone, 0), - 0, - ) - .with(PositionComponent::default()) - .build(); - let entity4 = item::create( - &world.fetch(), - &world.fetch(), - ItemStack::new(Item::Stone, 0), - 0, - ) - .with(PositionComponent::default()) - .build(); - - 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(3, -3), entity1); - chunk_entities.add_to_chunk(player_chunk, entity2); - chunk_entities.add_to_chunk(ChunkPosition::new(4, -3), 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); - - world.maintain(); - dispatcher.dispatch(&world); - world.maintain(); - dispatcher.dispatch(&world); - world.maintain(); - - let packets = t::received_packets(&player1, None); - - let mut received_spawns = HashSet::new(); - - for packet in packets { - dbg!(packet.ty()); - 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/prelude.rs b/server/src/prelude.rs deleted file mode 100644 index b656211e1..000000000 --- a/server/src/prelude.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub use super::TPS; -pub use crate::config::Config; -pub use feather_core::prelude::*; -pub use std::cell::RefCell; -pub use std::rc::Rc; diff --git a/server/src/systems.rs b/server/src/systems.rs deleted file mode 100644 index a216a294b..000000000 --- a/server/src/systems.rs +++ /dev/null @@ -1,68 +0,0 @@ -//! Constant string names for each system. -//! This file does not actually contain the systems -//! themselves. - -// Chunk logic -pub const CHUNK_LOAD: &str = "chunk_load"; -pub const CHUNK_OPTIMIZE: &str = "chunk_optimize"; - -pub const CHUNK_UNLOAD: &str = "chunk_unload"; -pub const CHUNK_HOLD_REMOVE: &str = "chunk_hold_remove"; - -// Player -pub const PLAYER_DIGGING: &str = "player_digging"; -pub const PLAYER_ANIMATION: &str = "player_animation"; -pub const CREATIVE_INVENTORY: &str = "creative_inventory"; -pub const HELD_ITEM_CHANGE: &str = "held_item_change"; -pub const PLAYER_MOVEMENT: &str = "player_movement"; -pub const PLAYER_CHAT: &str = "player_chat"; -pub const BLOCK_PLACEMENT: &str = "block_placement"; -pub const PLAYER_DATA_SAVE: &str = "player_data_save"; - -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"; -pub const ANIMATION_BROADCAST: &str = "animation_broadcast"; -pub const EQUIPMENT_SEND: &str = "equipment_send"; -pub const RESOURCE_PACK_SEND: &str = "resource_pack_send"; -pub const CHUNK_SEND: &str = "chunk_send"; -pub const BLOCK_BREAK_BROADCAST: &str = "block_break_broadcast"; -pub const BLOCK_UPDATE_PROPAGATE: &str = "block_update_propagate"; -pub const BLOCK_FALLING_CREATION: &str = "block_falling_creation"; -pub const SET_SLOT: &str = "set_slot"; -pub const CHAT_BROADCAST: &str = "chat_broadcast"; - -// Entity -pub const ITEM_COLLECT: &str = "item_collect"; - -pub const CHUNK_ENTITIES_UPDATE: &str = "chunk_entities_update"; -pub const CHUNK_ENTITIES_LOAD: &str = "chunk_entities_load"; -pub const ENTITY_DESTROY: &str = "entity_destroy"; -pub const ITEM_SPAWN: &str = "item_spawn"; -pub const ITEM_MERGE: &str = "item_merge"; -pub const SHOOT_ARROW: &str = "shoot_arrow"; -pub const CHUNK_SAVE: &str = "chunk_save"; - -pub const ENTITY_MOVE_BROADCAST: &str = "entity_move_broadcast"; -pub const ENTITY_SPAWN_BROADCAST: &str = "entity_spawn_broadcast"; -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"; -pub const BLOCK_FALLING_LANDING: &str = "block_falling_landing"; - -// Physics -pub const ENTITY_PHYSICS: &str = "entity_physics"; - -// Other -pub const JOIN_HANDLER: &str = "join_handler"; -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 deleted file mode 100644 index fef503cde..000000000 --- a/server/src/testframework.rs +++ /dev/null @@ -1,481 +0,0 @@ -//! Helper framework for writing unit tests. - -use std::net::TcpListener; -use std::sync::atomic::AtomicUsize; -use std::sync::Arc; - -use glm::DVec3; -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, ChunkLoadSystem}; -use crate::config::Config; -use crate::entity::metadata::{self, Metadata}; -use crate::entity::{ - ArrowComponent, ChunkEntities, EntityDestroyEvent, EntitySendEvent, EntitySpawnEvent, - ItemComponent, LastKnownPositionComponent, NamedComponent, PacketCreatorComponent, - PlayerComponent, PositionComponent, SerializerComponent, VelocityComponent, -}; -use crate::io::ServerToWorkerMessage; -use crate::network::{NetworkComponent, PacketQueue}; -use crate::physics::PhysicsComponent; -use crate::player::{InventoryComponent, PlayerDisconnectEvent}; -use crate::util::BroadcasterSystem; -use crate::worldgen::{EmptyWorldGenerator, WorldGenerator}; -use crate::{player, PlayerCount}; -use bitflags::_core::cell::RefCell; - -/// Initializes a Specs world and dispatcher -/// using default configuration options and an -/// available server port. -pub fn init_world<'a, 'b>() -> (World, Dispatcher<'a, 'b>) { - let mut config = Config::default(); - config.server.port = find_open_port().unwrap(); - - let config = Arc::new(config); - - let player_count = Arc::new(PlayerCount(AtomicUsize::new(0))); - let server_icon = Arc::new(None); - let ioman = super::init_io_manager( - Arc::clone(&config), - Arc::clone(&player_count), - Arc::clone(&server_icon), - ); - let level = LevelData::default(); - - let (mut world, dispatcher) = super::init_world(config, player_count, ioman, level); - register_components(&mut world); - (world, dispatcher) -} - -pub struct Player { - pub entity: Entity, - pub network_sender: crossbeam::Sender, - pub network_receiver: RefCell>, -} - -/// 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) = futures::channel::mpsc::unbounded(); - let (ns2, nr2) = crossbeam::unbounded(); - let entity = world - .create_entity() - .with(NetworkComponent::new(ns1, nr2)) - .with(PlayerComponent { - gamemode: Gamemode::Creative, - profile_properties: vec![], - }) - .with(PositionComponent { - current: position!(0.0, 0.0, 0.0), - previous: position!(0.0, 0.0, 0.0), - }) - .with(NamedComponent { - display_name: "".to_string(), - uuid: Uuid::new_v4(), - }) - .with(InventoryComponent::default()) - .with(Metadata::Player(metadata::Player::default())) - .with(LastKnownPositionComponent::default()) - .with(PacketCreatorComponent(&player::create_packet)) - .build(); - - Player { - entity, - network_sender: ns2, - network_receiver: RefCell::new(nr1), - } -} - -/// Asserts that the given player has received -/// a packet of the given type, returning the packet. -pub fn assert_packet_received(player: &Player, ty: PacketType) -> Box { - while let Ok(Some(msg)) = player.network_receiver.borrow_mut().try_next() { - if let ServerToWorkerMessage::SendPacket(packet) = msg { - if packet.ty() == ty { - return packet; - } - } - } - - panic!(); -} - -/// Asserts that a player did not receive -/// any packets of the given type. -/// Panics if not. -pub fn assert_packet_not_received(player: &Player, ty: PacketType) { - while let Ok(Some(msg)) = player.network_receiver.borrow_mut().try_next() { - if let ServerToWorkerMessage::SendPacket(packet) = msg { - assert_ne!(packet.ty(), ty); - } - } -} - -/// Retrieves up to `cap` packets sent to a player, if any. -/// If `cap` is set to `None`, all packets will be read. -/// -/// Note that this function consumes messages in -/// the network channel until enough packets have been read. -pub fn received_packets(player: &Player, cap: Option) -> Vec> { - let mut result = vec![]; - - while let Ok(Some(msg)) = player.network_receiver.borrow_mut().try_next() { - if let ServerToWorkerMessage::SendPacket(pack) = msg { - result.push(pack); - } - if let Some(cap) = cap.as_ref() { - if result.len() >= *cap { - break; - } - } - } - - result -} - -/// Adds a received packet to the packet queue -/// for a given player. -pub fn receive_packet(player: &Player, world: &World, packet: P) { - let queue = world.fetch_mut::(); - queue.add_for_packet(player.entity, Box::new(packet)); -} - -/// Attempts to find an available port. -fn find_open_port() -> Option { - let start = rand::thread_rng().gen_range(10000, 30000); - (start..60000).find(|port| TcpListener::bind(("127.0.0.1", *port)).is_ok()) -} - -/// Asserts that a player was disconnected, panicking if not. -pub fn assert_disconnected(player: &Player) { - let mut disconnected = false; - for packet in received_packets(player, None) { - if packet.ty() == PacketType::DisconnectPlay { - disconnected = true; - } - } - - assert!(disconnected); -} - -/// Asserts that a player was not disconnected, panicking -/// if they were. -pub fn assert_not_disconnected(player: &Player) { - let mut disconnected = false; - for packet in received_packets(player, None) { - if packet.ty() == PacketType::DisconnectPlay { - disconnected = true; - } - } - - assert!(!disconnected); -} - -/// Sends a packet to the player. -pub fn send_packet(player: &Player, packet: P) { - player - .network_sender - .send(ServerToWorkerMessage::NotifyPacketReceived(Box::new( - packet, - ))) - .unwrap(); -} - -/// Registers a reader for events of the given type. -pub fn reader(w: &World) -> ReaderId { - let mut channel = w.fetch_mut::>(); - channel.register_reader() -} - -/// Triggers the given event, writing it to -/// the corresponding `EventChannel`. -pub fn trigger_event(world: &World, event: E) { - let mut channel = world.fetch_mut::>(); - channel.single_write(event); -} - -/// Returns all triggered events of a given type. -pub fn triggered_events( - world: &World, - reader: &mut ReaderId, -) -> Vec { - let channel = world.fetch::>(); - channel.read(reader).cloned().collect() -} - -/// Populates a 15x15 area of chunks around the origin -/// with air. -pub fn populate_with_air(world: &mut World) { - for x in -15..=15 { - for z in -15..=15 { - let chunk = Chunk::new(ChunkPosition::new(x, z)); - world - .fetch_mut::() - .set_chunk_at(chunk.position(), chunk); - } - } -} - -/// Asserts that an entity was not removed. -pub fn assert_not_removed(world: &World, entity: Entity) { - assert!(world.entities().is_alive(entity)); -} - -/// Asserts that an entity was removed. -pub fn assert_removed(world: &World, entity: Entity) { - assert!(!world.entities().is_alive(entity)); -} - -/// Retrieves the position of an entity. -pub fn entity_pos(world: &World, entity: Entity) -> Position { - world - .read_component::() - .get(entity) - .unwrap() - .current -} - -/// Retrieves the velocity of an entity. -pub fn entity_vel(world: &World, entity: Entity) -> Option { - if let Some(comp) = world.read_component::().get(entity) { - Some(comp.0) - } else { - None - } -} - -/// Sets an entity's position. -pub fn set_entity_pos(world: &World, entity: Entity, pos: Position) { - let mut storage = world.write_component::(); - storage.get_mut(entity).unwrap().current = pos; -} - -/// Sets an entity's velocity. -pub fn set_entity_velocity(world: &World, entity: Entity, vel: DVec3) { - let mut storage = world.write_component::(); - storage.get_mut(entity).unwrap().0 = vel; -} - -/// Sets the block at the given position in the world. -pub fn set_block(x: i32, y: i32, z: i32, block: Block, world: &World) { - let mut chunk_map = world.fetch_mut::(); - chunk_map - .set_block_at(BlockPosition::new(x, y, z), block) - .unwrap(); -} - -/// A dispatcher builder for isolating tests. -pub struct TestBuilder<'a, 'b> { - world: World, - dispatcher: DispatcherBuilder<'a, 'b>, -} - -impl<'a, 'b> TestBuilder<'a, 'b> { - pub fn with(mut self, system: S, name: &'static str) -> Self - where - S: for<'c> System<'c> + Send + 'a, - { - self.dispatcher.add(system, name, &[]); - self - } - - pub fn with_dep(mut self, system: S, name: &'static str, deps: &[&'static str]) -> Self - where - S: for<'c> System<'c> + Send + 'a, - { - self.dispatcher.add(system, name, deps); - self - } - - pub fn build(mut self) -> (World, Dispatcher<'a, 'b>) { - self.world - .insert(Arc::new(PlayerCount(AtomicUsize::new(0)))); - 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())); - - let generator: Arc = Arc::new(EmptyWorldGenerator {}); - self.world.insert(generator); - - let mut chunk_system = ChunkLoadSystem {}; - chunk_system.setup(&mut self.world); - - // Insert the broadcaster system, since it is so commonly - // used that it should be used for all tests. - self.dispatcher.add_barrier(); - self.dispatcher.add(BroadcasterSystem, "", &[]); - - let mut dispatcher = self.dispatcher.build(); - dispatcher.setup(&mut self.world); - - register_components(&mut self.world); - - (self.world, dispatcher) - } -} - -fn register_components(world: &mut World) { - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - - world - .entry() - .or_insert(EventChannel::::default()); -} - -pub fn builder<'a, 'b>() -> TestBuilder<'a, 'b> { - TestBuilder { - world: World::new(), - dispatcher: DispatcherBuilder::new(), - } -} - -/// Heh... tests for the testing framework. -/// Not sure what the point of this is, since -/// 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 super::*; - - #[test] - fn test_find_open_port() { - let port = find_open_port().unwrap(); - println!("Found open port: {}", port); - assert!(TcpListener::bind(("127.0.0.1", port)).is_ok()); - } - - #[test] - fn test_init_world() { - // Check that initializing the world doesn't cause - // a panic. - let (w, mut d) = init_world(); - - // Check that running the dispatcher works fine - d.dispatch(&w); - } - - #[test] - fn test_add_player() { - let (mut w, _) = init_world(); - - let entity = add_player(&mut w).entity; - - assert!(w.read_component::().get(entity).is_some()); - assert!(w - .read_component::() - .get(entity) - .is_some()); - assert!(w.read_component::().get(entity).is_some()); - } - - #[test] - fn test_received_packets() { - let (mut w, _) = init_world(); - - let player = add_player(&mut w); - - let cap = 1; - send_packet_to_player( - w.read_component().get(player.entity).unwrap(), - LoginStart::new("".to_string()), - ); - send_packet_to_player( - w.read_component().get(player.entity).unwrap(), - LoginStart::new("".to_string()), - ); - - let packets = received_packets(&player, Some(cap)); - assert_eq!(packets.len(), 1); - - let packets = received_packets(&player, Some(cap)); - assert_eq!(packets.len(), 1); - } - - #[test] - #[should_panic] - fn test_assert_packet_received() { - let (mut w, _) = init_world(); - - let player = add_player(&mut w); - assert_packet_received(&player, PacketType::Handshake); - } - - #[test] - #[should_panic] - fn test_assert_disconnected() { - let (mut w, _) = init_world(); - - let player = add_player(&mut w); - assert_disconnected(&player); - } - - #[test] - #[should_panic] - fn test_assert_not_disconnected() { - let (mut w, _) = init_world(); - - let disconnect = DisconnectPlay::new("bla".to_string()); - - let player = add_player(&mut w); - send_packet_to_player(w.read_component().get(player.entity).unwrap(), disconnect); - assert_not_disconnected(&player); - } -} diff --git a/server/src/util/broadcaster.rs b/server/src/util/broadcaster.rs deleted file mode 100644 index 7da99c586..000000000 --- a/server/src/util/broadcaster.rs +++ /dev/null @@ -1,136 +0,0 @@ -//! 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, NetworkComponent}; -use crate::util::Util; -use crossbeam::queue::SegQueue; -use feather_core::{ChunkPosition, Packet}; -use specs::{Entities, Entity, Read, ReadStorage, System}; - -/// Broadcaster used to lazily broadcast packets. -#[derive(Default)] -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_update

(&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_update

(&self, chunk: ChunkPosition, packet: P, neq: Option) - where - P: Packet + 'static, - { - self.queue.push(BroadcastRequest { - condition: BroadcastCondition::Chunk(chunk), - packet: Box::new(packet), - neq, - }); - } - - /// Lazily sends a packet to a player. - pub fn lazy_send_packet_to_player

(&self, player: Entity, packet: P) - where - P: Packet + 'static, - { - self.queue.push(BroadcastRequest { - condition: BroadcastCondition::ToPlayer(player), - packet: Box::new(packet), - neq: None, // No effect - }); - } -} - -/// A broadcast request. -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), - ToPlayer(Entity), -} - -/// 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, Util>, - Read<'a, ChunkHolders>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (positions, networks, util, chunk_holders, entities) = data; - - 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. - - 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, - BroadcastCondition::ToPlayer(player) => { - if entities.is_alive(player) { - send_packet_boxed_to_player(networks.get(player).unwrap(), request.packet); - } - - continue; // Special case - no 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 { - continue; - } - } - - if let Some(network) = networks.get(*holder) { - send_packet_boxed_to_player(network, request.packet.box_clone()); - } - } - } - } - } -} diff --git a/server/src/util/macros.rs b/server/src/util/macros.rs deleted file mode 100644 index 66e6b841a..000000000 --- a/server/src/util/macros.rs +++ /dev/null @@ -1,70 +0,0 @@ -/// Asserts that a floating-point value is within -/// a certain range of the expected value. -#[cfg(test)] -macro_rules! assert_float_eq { - ($left:expr, $right:expr) => { - assert_float_eq!($left, $right, 0.001); - }; - ($left:expr, $right:expr, $range:expr) => { - let range = ($left - $range)..($left + $range); - assert!(range.contains(&$right)); - }; -} - -/// Checks that two positions are approximately equivalent. -#[cfg(test)] -macro_rules! assert_pos_eq { - ($left:expr, $right:expr) => { - assert_float_eq!($left.x, $right.x, 0.01); - assert_float_eq!($left.y, $right.y, 0.01); - assert_float_eq!($left.z, $right.z, 0.011); - }; -} - -/// Generates a setup() implementation for a system -/// which initializes an internal event reader. -macro_rules! setup_impl { - ($($reader:ident),+) => { - fn setup(&mut self, world: &mut specs::World) { - use specs::SystemData; - use shrev::EventChannel; - Self::SystemData::setup(world); - - $(self.$reader = Some(world.fetch_mut::>().register_reader());)+ - } - }; -} - -macro_rules! flagged_setup_impl { - ($component:ident, $reader:ident) => { - fn setup(&mut self, world: &mut specs::World) { - use specs::{SystemData, WorldExt}; - Self::SystemData::setup(world); - - self.$reader = Some(world.write_component::<$component>().register_reader()); - } - } -} - -macro_rules! read_flagged_events { - ($storage:ident, $reader:expr, $dirty:expr) => { - for event in $storage.channel().read($reader.as_mut().unwrap()) { - match event { - ComponentEvent::Inserted(id) | ComponentEvent::Modified(id) => { - $dirty.add(*id); - } - _ => (), - } - } - }; -} - -macro_rules! continue_if_none { - ($option:expr) => { - if let Some(value) = $option { - value - } else { - continue; - } - }; -} diff --git a/server/src/util/mod.rs b/server/src/util/mod.rs deleted file mode 100644 index e3798445a..000000000 --- a/server/src/util/mod.rs +++ /dev/null @@ -1,169 +0,0 @@ -//! Assorted utilities for use in Feather's codebase. -use bumpalo::Bump; -use feather_core::{ChunkPosition, Packet}; -use glm::DVec3; -use thread_local::ThreadLocal; - -#[macro_use] -mod macros; -mod broadcaster; - -use broadcaster::Broadcaster; -pub use broadcaster::BroadcasterSystem; -pub use macros::*; -use specs::Entity; - -/// Converts float-based velocity in blocks per tick -/// to the format used by the protocol. -pub fn protocol_velocity(vel: DVec3) -> (i16, i16, i16) { - // Apparently, these are in units of 1/8000 block per tick. - ( - (vel.x * 8000.0) as i16, - (vel.y * 8000.0) as i16, - (vel.z * 8000.0) as i16, - ) -} - -/// General-purpose utility resource, used to -/// ergonomically perform various actions without -/// specifying ridiculous amounts of system dependencies. -/// -/// This struct is thread-safe and should never be used -/// as a `Write` dependency. -/// -/// # Available functions -/// * `alloc` - used to allocate temporary values -/// using a bump allocator. When allocating values -/// which will only be used inside a function, for example, -/// this function should be used rather than allocating -/// directly on the heap. -/// * `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(Default)] -pub struct Util { - /// Thread-local bump allocator, reset - /// every tick. - /// - /// This is used to reduce allocation frequency. - bump: ThreadLocal, - /// The broadcaster, used to lazily broadcast packets. - broadcaster: Broadcaster, -} - -impl Util { - /// Returns the thread-local bump allocator. - pub fn bump(&self) -> &Bump { - self.bump.get_or_default() - } - - /// Allocates a value using the bump allocator - /// for this thread. - /// - /// This is equivalent to `Util::bump().alloc(value)`. - #[allow(clippy::mut_from_ref)] // This is sound—it just redirects to `bumpalo`. - pub fn alloc(&self, value: T) -> &mut T { - self.bump().alloc(value) - } - - /// This should be called at the end of every tick. - pub fn reset(&mut self) { - // Reset bump allocators - for bump in self.bump.iter_mut() { - bump.reset(); - } - } - - /// 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_update

(&self, entity: Entity, packet: P, neq: Option) - where - P: Packet + 'static, - { - self.broadcaster - .broadcast_entity_update(entity, packet, neq); - } - - /// Broadcasts a packet to all players who - /// are able to see a given chunk. - /// - /// 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 chunk. - pub fn broadcast_chunk_update

(&self, chunk: ChunkPosition, packet: P, neq: Option) - where - P: Packet + 'static, - { - self.broadcaster.broadcast_chunk_update(chunk, packet, neq); - } - - /// Similar to `broadcast_*_update`, but only sends to the specified - /// player. - /// - /// This can be used when a packet needs to be sent to a specified - /// player before another packet is broadcasted. - pub fn lazy_send_packet_to_player

(&self, player: Entity, packet: P) - where - P: Packet + 'static, - { - self.broadcaster.lazy_send_packet_to_player(player, packet); - } -} - -#[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; - - #[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, 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); - - 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_entity_update(player1.entity, packet, Some(player2.entity)); - - world.insert(util); - world.insert(chunk_holders); - - dispatcher.dispatch(&world); - world.maintain(); - - t::assert_packet_received(&player1, PacketType::EntityHeadLook); - t::assert_packet_not_received(&player2, PacketType::EntityHeadLook); - t::assert_packet_not_received(&player3, PacketType::EntityHeadLook); - } -} From c1b9b78f6f1fe15f1c08cf57cc8f0a3ee45e622b Mon Sep 17 00:00:00 2001 From: caelunshun Date: Thu, 14 Nov 2019 13:07:48 -0700 Subject: [PATCH 031/647] Reimplement most of the network system --- Cargo.lock | 18 +- core/src/network/mod.rs | 4 +- server/Cargo.toml | 2 +- server/src/joinhandler.rs | 237 ------------------- server/src/lib.rs | 5 +- server/src/network.rs | 483 +++++++++----------------------------- 6 files changed, 131 insertions(+), 618 deletions(-) delete mode 100644 server/src/joinhandler.rs diff --git a/Cargo.lock b/Cargo.lock index 05ac9fe2e..faded77b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -695,7 +695,7 @@ dependencies = [ "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks 0.1.0 (git+https://github.com/feather-rs/tonks)", + "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=0691131efcffea7c0225f9d6203b58eb2cc27cd7)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2509,7 +2509,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks#5151057974bfd22317ef64b0e1eb455a0aa80442" +source = "git+https://github.com/feather-rs/tonks?rev=0691131efcffea7c0225f9d6203b58eb2cc27cd7#0691131efcffea7c0225f9d6203b58eb2cc27cd7" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2525,6 +2525,17 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=0691131efcffea7c0225f9d6203b58eb2cc27cd7)", +] + +[[package]] +name = "tonks-macros" +version = "0.1.0" +source = "git+https://github.com/feather-rs/tonks?rev=0691131efcffea7c0225f9d6203b58eb2cc27cd7#0691131efcffea7c0225f9d6203b58eb2cc27cd7" +dependencies = [ + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3135,7 +3146,8 @@ dependencies = [ "checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" "checksum tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" -"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks)" = "" +"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=0691131efcffea7c0225f9d6203b58eb2cc27cd7)" = "" +"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=0691131efcffea7c0225f9d6203b58eb2cc27cd7)" = "" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" "checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" diff --git a/core/src/network/mod.rs b/core/src/network/mod.rs index 732b62454..dfc244b64 100644 --- a/core/src/network/mod.rs +++ b/core/src/network/mod.rs @@ -2,6 +2,6 @@ pub mod codec; pub mod mctypes; pub mod packet; -pub fn cast_packet(packet: &dyn packet::Packet) -> &P { - packet.as_any().downcast_ref().unwrap() +pub fn cast_packet(packet: Box) -> P { + packet.downcast().unwrap() } diff --git a/server/Cargo.toml b/server/Cargo.toml index 1f2d0ee63..feaa9a857 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -17,7 +17,7 @@ feather-blocks = { path = "../blocks" } feather-core = { path = "../core" } feather-item-block = { path = "../item_block" } legion = { git = "https://github.com/TomGillen/legion", rev = "2eab55dc5a3a84fbc8505cc16eb780ef0cdd019b" } -tonks = { git = "https://github.com/feather-rs/tonks" } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "0691131efcffea7c0225f9d6203b58eb2cc27cd7" } crossbeam = "0.7" log = "0.4" simple_logger = "1.3" diff --git a/server/src/joinhandler.rs b/server/src/joinhandler.rs deleted file mode 100644 index ad38803cb..000000000 --- a/server/src/joinhandler.rs +++ /dev/null @@ -1,237 +0,0 @@ -//! The join handler, in contrast to the initial handler, -//! takes over after the login sequence has completed. -//! It's responsible for asyncrhonously loading the player's -//! data (inventory, chunks, etc.) and then sending the necessary -//! packets to join the player. After completion, the component is -//! removed. - -use std::sync::atomic::Ordering; -use std::sync::Arc; - -use shrev::EventChannel; -use specs::{ - Component, Entities, Entity, HashMapStorage, Join, LazyUpdate, Read, ReadExpect, ReadStorage, - System, Write, WriteStorage, -}; - -use feather_core::level::LevelData; -use feather_core::network::packet::implementation::{ - JoinGame, PlayerPositionAndLookClientbound, SpawnPosition, -}; -use feather_core::world::{BlockPosition, ChunkMap, ChunkPosition}; -use feather_core::{Difficulty, Dimension}; - -use crate::chunk_logic::{ChunkHolderComponent, ChunkHolders, ChunkWorkerHandle}; -use crate::config::Config; -use crate::entity::{EntitySpawnEvent, PlayerComponent, PositionComponent}; -use crate::network::NetworkComponent; -use crate::player::{ChunkPendingComponent, InventoryUpdateEvent, LoadedChunksComponent}; -use crate::PlayerCount; - -#[derive(Default)] -pub struct JoinHandlerComponent { - stage: Stage, -} - -impl JoinHandlerComponent { - pub fn new() -> Self { - Self { - stage: Stage::Initial, - } - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -enum Stage { - Initial, - AwaitChunkSends, -} - -impl Default for Stage { - fn default() -> Self { - Stage::Initial - } -} - -impl Component for JoinHandlerComponent { - type Storage = HashMapStorage; -} - -/// Event which is triggered when a player -/// completes the join process (i.e. when -/// all chunks have been sent). -pub struct PlayerJoinEvent { - pub player: Entity, -} - -/// System for join handling. -pub struct JoinHandlerSystem; - -impl<'a> System<'a> for JoinHandlerSystem { - type SystemData = ( - WriteStorage<'a, JoinHandlerComponent>, - ReadStorage<'a, NetworkComponent>, - ReadStorage<'a, ChunkPendingComponent>, - Write<'a, EventChannel>, - Write<'a, EventChannel>, - Write<'a, EventChannel>, - ReadExpect<'a, ChunkWorkerHandle>, - Entities<'a>, - Read<'a, LazyUpdate>, - Read<'a, Arc>, - Read<'a, Arc>, - Read<'a, ChunkMap>, - Write<'a, ChunkHolders>, - Read<'a, LevelData>, - WriteStorage<'a, ChunkHolderComponent>, - WriteStorage<'a, LoadedChunksComponent>, - ReadStorage<'a, PlayerComponent>, - ReadStorage<'a, PositionComponent>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - mut joincomps, - netcomps, - pending_chunks, - mut join_events, - mut spawn_events, - mut inv_events, - worker_handle, - entities, - lazy, - config, - player_count, - chunk_map, - mut holders, - level, - mut holder_comps, - mut loaded_chunks_comps, - playercomps, - positions, - ) = data; - - let mut to_remove = vec![]; - - for (player, net, join_handler, pending_chunks) in - (&entities, &netcomps, &mut joincomps, &pending_chunks).join() - { - match join_handler.stage { - Stage::Initial => { - let playercomp = playercomps.get(player).unwrap(); - - let level_type = &level.generator_name; - - // Send Join Game, then queue chunks for loading + sending. - let join_game = JoinGame::new( - player.id() as i32, - playercomp.gamemode.get_id(), - Dimension::Overwold.get_id(), - Difficulty::Medium.get_id(), - 0, // Max players - not used - level_type.to_string(), - false, // Reduced debug info - ); - crate::network::send_packet_to_player(net, join_game); - - let mut holder_comp = ChunkHolderComponent::new(); - let mut loaded_chunks_comp = LoadedChunksComponent::default(); - - let player_pos = positions.get(player).unwrap().current.chunk_pos(); - - // Offsets from the origin to center view distance on - let chunk_offset_x = player_pos.x; - let chunk_offset_z = player_pos.z; - - // Queue chunks for sending. - let view_distance = i32::from(config.server.view_distance); - let mut chunks = Vec::with_capacity((view_distance * view_distance) as usize); - for x in -view_distance..=view_distance { - for z in -view_distance..=view_distance { - let chunk = ChunkPosition::new(x + chunk_offset_x, z + chunk_offset_z); - chunks.push(chunk); - } - } - - // Sort chunks so that closest chunks are sent first. - chunks.sort_unstable_by(|a, b| { - a.manhattan_distance(player_pos) - .cmp(&b.manhattan_distance(player_pos)) - }); - - // Queue chunks for loading + sending - chunks.into_iter().for_each(|chunk| { - crate::player::send_chunk_to_player( - chunk, - net, - player, - &chunk_map, - &worker_handle, - &mut holders, - &mut holder_comp, - &mut loaded_chunks_comp, - &lazy, - ); - }); - - holder_comps.insert(player, holder_comp).unwrap(); - loaded_chunks_comps - .insert(player, loaded_chunks_comp) - .unwrap(); - - // Increment player count - player_count.0.fetch_add(1, Ordering::SeqCst); - - join_handler.stage = Stage::AwaitChunkSends; - } - Stage::AwaitChunkSends => { - // If 0 chunks have yet to be sent, join the player by sending spawn position. - // See https://wiki.vg/Protocol_FAQ - if pending_chunks.len() != 0 { - continue; - } - - // SpawnPosition packet: world spawn (used for compass) - let level_spawn_block_pos = - BlockPosition::new(level.spawn_x, level.spawn_y, level.spawn_z); - let level_spawn_position = SpawnPosition::new(level_spawn_block_pos); - crate::network::send_packet_to_player(net, level_spawn_position); - - // Initial position/rotation for the player when they spawn - let player_pos = positions.get(player).unwrap().current; - let position_and_look = PlayerPositionAndLookClientbound::new( - player_pos.x, - player_pos.y, - player_pos.z, - player_pos.yaw, - player_pos.pitch, - 0, // Flags - unused by us - 0, // Teleport ID - unused by us - ); - crate::network::send_packet_to_player(net, position_and_look); - - // Trigger events - let event = PlayerJoinEvent { player }; - join_events.single_write(event); - - let event = EntitySpawnEvent { entity: player }; - spawn_events.single_write(event); - - // Trigger inventory update event on the entire inventory - let event = InventoryUpdateEvent { - slots: (0..46).collect(), - player, - }; - inv_events.single_write(event); - - // We're finished here. - to_remove.push(player); - } - } - } - - to_remove.into_iter().for_each(|player| { - joincomps.remove(player); - }); - } -} diff --git a/server/src/lib.rs b/server/src/lib.rs index acd131abe..5bbc8850c 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -25,6 +25,8 @@ extern crate feather_codegen; extern crate bitflags; #[macro_use] extern crate feather_core; +#[macro_use] +extern crate tonks; extern crate nalgebra_glm as glm; @@ -39,6 +41,7 @@ use specs::{Builder, Dispatcher, DispatcherBuilder, Entity, LazyUpdate, World, W use feather_core::network::packet::implementation::DisconnectPlay; use crate::chunk_logic::{ChunkHolders, ChunkWorkerHandle}; +use crate::config::Config; use crate::worldgen::{ ComposableGenerator, EmptyWorldGenerator, SuperflatWorldGenerator, WorldGenerator, }; @@ -51,7 +54,6 @@ use std::hash::{Hash, Hasher}; use std::io::{Read, Write}; use std::path::Path; use std::process::exit; -use crate::config::Config; #[global_allocator] static ALLOC: System = System; @@ -60,7 +62,6 @@ pub mod chunk_logic; pub mod chunkworker; pub mod config; pub mod io; -pub mod joinhandler; pub mod network; pub mod physics; pub mod shutdown; diff --git a/server/src/network.rs b/server/src/network.rs index fdf2b13fa..513a7b6cc 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -1,417 +1,154 @@ -use parking_lot::Mutex; +//! Network logic. This module includes: +//! * `Network`, a component assigned to entities +//! which can send and receive packets. (This is only +//! added for players, obviously.) +//! * `PacketQueue`, which stores packets received +//! from players and allows systems to poll for packets +//! received of a given type. -use crossbeam::Receiver; -use futures::channel::mpsc::UnboundedSender as Sender; -use shrev::EventChannel; -use specs::{ - Component, DenseVecStorage, Entities, Entity, Join, LazyUpdate, Read, ReadStorage, System, - WorldExt, Write, WriteStorage, -}; - -use feather_core::network::packet::{implementation::*, Packet, PacketType}; - -use crate::entity::PlayerComponent; use crate::io::{ListenerToServerMessage, NetworkIoManager, ServerToWorkerMessage}; -use crate::joinhandler::JoinHandlerComponent; -use crate::prelude::*; -use crate::{disconnect_player_without_packet, TickCount}; -use strum::EnumCount; - -//const MAX_KEEP_ALIVE_TIME: u64 = 30; -//const HEAD_OFFSET: f64 = 1.62; // Offset from feet pos to head pos - -/// A packet received from a player. -pub type QueuedPacket = (Entity, Box); - -/// Vector of `QueuedPacket`. -type QueuedPackets = Vec; - -/// A component which contains the received packets -/// for this tick. +use crossbeam::Receiver; +use feather_core::network::cast_packet; +use feather_core::{Packet, PacketType}; +use futures::channel::mpsc::UnboundedSender; +use legion::entity::Entity; +use legion::query::Read; +use parking_lot::{Mutex, MutexGuard}; +use std::iter; +use std::vec::Drain; +use tonks::{PreparedQuery, PreparedWorld, Query}; + +type QueuedPackets = Vec<(Entity, Box)>; + +/// The packet queue. This type allows systems to poll for +/// received packets of a given type. +/// +/// A system should never require mutable access to this type. pub struct PacketQueue { - /// Vector of packet queues. For any given packet - /// type, the queued packets of that type can - /// be found by indexing into this vector with the ordinal - /// of the packet type. - /// - /// A locked `Vec` is used rather than a `SegQueue` because - /// there is typically no contention when accessing the queue - /// for a single packet type (there is at most one system handling - /// each packet type). As a result, there is no need for a lock-free - /// data structure. + /// Vector of queued packets. This vector is indexed + /// by the ordinal of the packet type, and each + /// queue contains only packets of its type. queue: Vec>, } -impl PacketQueue { - /// Returns the packets queued for handling - /// of the given type, draining the queue of this - /// type of packet. - pub fn for_packet(&self, ty: PacketType) -> Vec { - let ordinal = ty.ordinal(); +impl Default for PacketQueue { + fn default() -> Self { + Self::new() + } +} - let mut queued_packets = self.queue[ordinal].lock(); +impl PacketQueue { + /// Creates a new, empty `PacketQueue`. + pub fn new() -> Self { + Self { + queue: iter::repeat_with(|| Mutex::new(vec![])) + .take(PacketType::count() + 1) + .collect(), + } + } - let mut new_queue = vec![]; - std::mem::swap(&mut new_queue, &mut queued_packets); + /// Returns an iterator over packets of a given type. + pub fn received(&self) -> MutexGuard> { + let queue = self.queue[P::ty().ordinal()].lock(); - new_queue + MutexGuard::map(queue, |queue| { + queue + .drain(..) + .map(|(entity, packet)| (entity, cast_packet::

(packet))) + }) } /// Adds a packet to the queue. - pub fn add_for_packet(&self, player: Entity, packet: Box) { + pub fn push(&self, packet: Box, entity: Entity) { let ordinal = packet.ty().ordinal(); - let mut queued_packets = self.queue[ordinal].lock(); - queued_packets.push((player, packet)); - } -} - -impl Default for PacketQueue { - fn default() -> Self { - // Initialize with an empty queue for each packet type. - // Packet type ordinals start at 1 (who decided this? FIXME), - // so we have to use an inclusive range. - Self { - queue: (0..=PacketType::count()) - .map(|_| Mutex::new(vec![])) - .collect(), - } + self.queue[ordinal].lock().push((packet, entity)); } } -pub struct NetworkComponent { - sender: Sender, +/// Network component containing channels to send and receive packets. +/// +/// Systems should call `Self::send` to send a packet to this entity (player). +pub struct Network { + sender: UnboundedSender, receiver: Receiver, - /// A vector of all chunks that are currently - /// being loaded and should be sent to the player - /// once they have been loaded. - pub chunks_to_send: Vec, - //last_keep_alive_time: u64, } -impl NetworkComponent { - pub fn new( - sender: Sender, - receiver: Receiver, - ) -> Self { - Self { - sender, - receiver, - chunks_to_send: vec![], - } +impl Network { + /// Sends a packet to this player. + pub fn send

(&self, packet: P) { + self.send_boxed(Box::new(packet)); } -} -impl Component for NetworkComponent { - type Storage = DenseVecStorage; -} - -/// The network system, responsible for -/// receiving and buffering packets received -/// from players. Received packets -/// are added to a queue (`PacketQueue`) so that -/// other systems can handle them. -pub struct NetworkSystem; - -/// Event which is triggered when a player joins -/// but before the join handler is completed. -pub struct PlayerPreJoinEvent { - pub player: Entity, - pub username: String, - pub uuid: Uuid, - pub profile_properties: Vec, + /// Sends a boxed packet to this player. + pub fn send_boxed(&self, packet: Box) { + // Discard error in case the channel was disconnected + // (e.g. if the player disconnected and its worker task + // shut down, and the disconnect was not yet registered + // by the server) + let _ = self + .sender + .unbounded_send(ServerToWorkerMessage::SendPacket(packet)); + } } -impl<'a> System<'a> for NetworkSystem { - type SystemData = ( - WriteStorage<'a, NetworkComponent>, - ReadStorage<'a, PlayerComponent>, - Write<'a, EventChannel>, - Write<'a, PacketQueue>, - Read<'a, NetworkIoManager>, - Entities<'a>, - Read<'a, TickCount>, - Read<'a, LazyUpdate>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - mut netcomps, - pcomps, - mut join_events, - packet_queue, - ioman, - entities, - tick_count, - lazy, - ) = data; - // Poll for new connections - while let Ok(msg) = ioman.receiver.try_recv() { +/// The network system. This system is responsible for: +/// * Handling player disconnects. +/// * Pushing received packets to the packet queue. +/// * Accepting new clients and creating entities for them. +#[system] +pub fn network( + io: &NetworkIoManager, + packet_queue: &PacketQueue, + mut query: PreparedQuery>, + world: &mut PreparedWorld, +) { + /// For each `Network`, handle any disconnects and received packets. + query.par_entities_for_each(world, |(entity, network): (Entity, Network)| { + while let Ok(msg) = network.receiver.try_recv() { match msg { - ListenerToServerMessage::NewClient(info) => { - // New connection - handle it - info!("Accepting connection from {}", info.ip); - let netcomp = NetworkComponent::new(info.sender, info.receiver); - - // Create entity - let new_entity = entities.create(); - netcomps.insert(new_entity, netcomp).unwrap(); - - // Create join handler - let join_handler = JoinHandlerComponent::new(); - lazy.exec_mut(move |world| { - world - .write_component::() - .insert(new_entity, join_handler) - .unwrap(); - }); - - // Queue event - let event = PlayerPreJoinEvent { - player: new_entity, - username: info.username.clone(), - uuid: info.uuid, - profile_properties: info.profile.clone(), - }; - join_events.single_write(event); - } - } - } - - // Receive packets + disconnects from players - for (player, netcomp) in (&entities, &netcomps).join() { - while let Ok(msg) = netcomp.receiver.try_recv() { - match msg { - ServerToWorkerMessage::NotifyPacketReceived(packet) => { - packet_queue.add_for_packet(player, packet); - } - ServerToWorkerMessage::NotifyDisconnect(reason) => { - lazy.exec_mut(move |world| { - disconnect_player_without_packet(player, world, reason) - }); - break; - } - _ => panic!("Network system received invalid message from IO worker}"), + ServerToWorkerMessage::NotifyDisconnect(reason) => unimplemented!(), + ServerToWorkerMessage::SendPacket(packet) => { + packet_queue.push(packet, entity); } + _ => unreachable!(), } } + }); - // Send keepalives every second. The dependency on the player - // component is required because keepalives should - // only be sent to players who have joined (completed - // the login process). - // TODO check that player hasn't timed out - if tick_count.0 % TPS == 0 { - for (netcomp, _) in (&netcomps, &pcomps).join() { - send_packet_to_player(netcomp, KeepAliveClientbound::new(0)); - } + // Handle new clients. + while let Ok(msg) = io.receiver.try_recv() { + match msg { + ListenerToServerMessage::NewClient(info) => unimplemented!(), } } } -/// Sends a packet to all players on the server, excluding -/// `neq`, if it exists. -pub fn send_packet_to_all_players( - net_comps: &ReadStorage, - entities: &Entities, - packet: P, - neq: Option, -) { - for (entity, net) in (entities, net_comps).join() { - if let Some(e) = neq.as_ref() { - if *e == entity { - continue; // Exclude this entity - } - } - - send_packet_to_player(net, packet.clone()); - } -} - -/// Sends a packet to the given player. -pub fn send_packet_to_player(comp: &NetworkComponent, packet: P) { - send_packet_boxed_to_player(comp, Box::new(packet)); -} - -/// Sends a packet to the given player. -pub fn send_packet_boxed_to_player(comp: &NetworkComponent, packet: Box) { - let _ = comp - .sender - .unbounded_send(ServerToWorkerMessage::SendPacket(packet)); -} - #[cfg(test)] mod tests { use super::*; - use crate::io::NewClientInfo; - use crate::player::PlayerDisconnectEvent; - use crate::testframework as t; - use std::net::SocketAddr; - - #[test] - fn test_packet_queue() { - let queue = PacketQueue::default(); - - let (mut w, _) = t::init_world(); - let player = t::add_player(&mut w); - - let packet = LoginStart::new("test".to_string()); - queue.add_for_packet(player.entity, Box::new(packet)); - - let packets = queue.for_packet(PacketType::LoginStart); - assert_eq!(packets.len(), 1); - - let (entity, packet) = packets.first().unwrap(); - assert_eq!(*entity, player.entity); - assert_eq!(packet.ty(), PacketType::LoginStart); - } - - #[test] - fn test_new_client() { - let (mut w, mut d) = t::init_world(); - - let ioman = w.fetch_mut::(); - - let (send1, _recv1) = futures::channel::mpsc::unbounded(); - let (_send2, recv2) = crossbeam::unbounded(); - - let new_client = NewClientInfo { - ip: SocketAddr::new("127.0.0.1".parse().unwrap(), 25565), - username: "".to_string(), - profile: vec![], - uuid: Uuid::new_v4(), - sender: send1, - receiver: recv2, - }; - - let msg = ListenerToServerMessage::NewClient(new_client); - ioman.listener_sender.send(msg).unwrap(); - - let mut event_reader = t::reader(&w); - - drop(ioman); - - // Call the network system - d.dispatch(&w); - - w.maintain(); - - // Confirm that an entity was created - let mut count = 0; - let mut entity = None; - for (e, _, _) in ( - &*w.entities(), - &w.read_component::(), - &w.read_component::(), - ) - .join() - { - entity = Some(e); - count += 1; - } - assert_eq!(count, 1); - - // Confirm that playerprejoinevent was queued - let channel = w.fetch_mut::>(); - let join_events: Vec<&PlayerPreJoinEvent> = channel.read(&mut event_reader).collect(); - assert_eq!(join_events.len(), 1); - let event = join_events.first().unwrap(); - - assert_eq!(event.player, entity.unwrap()); - } - - #[test] - fn test_packet_receive() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - // Send a packet - let packet = LoginStart::new("".to_string()); - t::send_packet(&player, packet); - - // Run system - d.dispatch(&w); - - w.maintain(); - - // Confirm that packet was received properly - let queue = w.fetch::(); - let packets = queue.for_packet(PacketType::LoginStart); - - assert_eq!(packets.len(), 1); - - let (entity, _packet) = packets.first().unwrap(); - assert_eq!(*entity, player.entity); - } - - #[test] - fn test_disconnect() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - - let mut event_reader = t::reader(&w); - - player - .network_sender - .send(ServerToWorkerMessage::NotifyDisconnect( - "reason".to_string(), - )) - .unwrap(); - - d.dispatch(&w); - - w.maintain(); - - let channel = w.fetch::>(); - let events = channel.read(&mut event_reader).collect::>(); - - assert_eq!(events.len(), 1); - let first = events.first().unwrap(); - assert_eq!(first.player, player.entity); - } + use feather_core::network::packet::implementation::Handshake; + use feather_core::network::packet::PacketType::SpawnObject; + use legion::world::World; #[test] - fn test_keep_alives() { - let (mut w, mut d) = t::init_world(); - - let player = t::add_player(&mut w); - let player2 = t::add_player(&mut w); - - d.dispatch(&w); - - t::assert_packet_received(&player, PacketType::KeepAliveClientbound); - t::assert_packet_received(&player2, PacketType::KeepAliveClientbound); - } - - #[test] - fn test_send_packet_to_all_players() { - let (mut w, _) = t::init_world(); - - let player1 = t::add_player(&mut w); - let player2 = t::add_player(&mut w); - let player3 = t::add_player(&mut w); - - let packet = LoginStart::new("test".to_string()); + fn packet_queue() { + let queue = PacketQueue::new(); - send_packet_to_all_players( - &w.read_component(), - &w.entities(), - packet, - Some(player1.entity), - ); + let mut world = World::new(); + let entities = world.insert((), vec![(), ()]); - dbg!(); + queue.push(Box::new(Handshake::default()), entities[0]); + queue.push(Box::new(SpawnObject::default()), entities[1]); + queue.push(Handshake::default(), entities[1]); - t::assert_packet_received(&player2, PacketType::LoginStart); - dbg!(); - t::assert_packet_received(&player3, PacketType::LoginStart); - dbg!(); + let mut handshakes = queue.received::(); + assert_eq!(handshakes.next().unwrap().0, entities[0]); + assert_eq!(handshakes.next().unwrap().0, entities[1]); + assert!(handshakes.next().is_none()); - // Check that exclusion was not sent - let sent = t::received_packets(&player1, None); - dbg!(); - assert!(sent.is_empty()); + let mut spawn_objects = queue.received::(); + assert_eq!(spawn_objects.next().unwrap().0, entities[1]); + assert!(spawn_objects.next().is_none()); } } From 9b21375967f5b607b1c44d2a4aa89452e345894e Mon Sep 17 00:00:00 2001 From: caelunshun Date: Thu, 14 Nov 2019 13:37:32 -0700 Subject: [PATCH 032/647] Add crate-level docs on ECS --- server/src/lib.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 4 deletions(-) diff --git a/server/src/lib.rs b/server/src/lib.rs index 5bbc8850c..8a419d4cf 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -1,7 +1,95 @@ -// Specs systems tend to have very long -// tuples as their SystemData, and Clippy -// doesn't seem to like this. -#![allow(clippy::type_complexity)] +//! Feather, a Minecraft server implementation in Rust. +//! +//! This is the developer documenation, and anyone wishing to contribute +//! should read this first. +//! +//! The core of Feather is based on [`legion`](https://github.com/TomGillen/legion), +//! a fast ECS for Rust, and [`tonks`](https://github.com/feather-rs/tonks), a system +//! scheduler built on Legion. As a result, we use the ECS architecture: the +//! entire server consists of _entities_, simple IDs with no data; _components_, +//! arbitrary data, such as positions, which can be attached to an entity; +//! and _systems_, functions which can run logic over entities and components. +//! +//! The benefit of this design is the splitting between data and logic. With a traditional +//! object-oriented design, there would be an Entity class from which other entities +//! inherit and can override logic. However, this model does not work well with Rust's +//! borrow checker (as we found out in the early days of Feather, when this design was +//! used), and more importantly, it reduces flexibility. Say, for example, that a plugin +//! wants to modify the physics behavior of a cow by increasing gravity. With the object-oriented +//! design, it would have to somehow modify the `run_physics` method on `Cow`, which is not +//! possible in a native language (although it can be done in some languages using class rewriting). +//! On the other hand, using Feather, there is a `Physics` component which stores gravity, +//! drag, etc. for an entity, and all the plugin has to do is modify that component. +//! +//! Another benefit of the ECS architecture is performance. With the OO design, entities +//! would likely be stored in a `Vec>`, which is horribly inefficient +//! with regards to cache locality and iteration performance. Legion, however, stores +//! entities in an efficient manner such that many of the same type of component +//! are stored contiguously, which is excellent for cache performance. +//! +//! Here is a more in-depth description of each concept in Feather. +//! +//! ## Entities +//! Entities, or `legion::entity::Entity`, are simple numerical IDs: they store no +//! data, but components can be attached to an entity. See the systems section +//! for information on how to access this data. +//! +//! ## Components +//! Components store data associated with an entity, such as `Position`. +//! Arbitrary amounts of components can be associated with any given entity. +//! +//! ## Resources +//! Resources are a branching off from the pure ECS concept. Like components, they +//! store arbitrary data in the form of structs; however, they are not associated +//! with any entity. An example of a resource might be the chunk map, which allows +//! access to blocks in the world. +//! +//! ## Systems +//! The systems concept is where things become more complex. `tonks`, the library +//! which runs systems, runs them _in parallel_, effectively multithreading the +//! entire server. This is still safe because the scheduler ensures that no +//! two systems which write to the same data run at the same time. +//! +//! A consequence of the above is that systems must explicitly state which +//! resources and components they might access. If a system needs access to positions, +//! it needs to state this upfront so the scheduler can ensure memory safety. +//! +//! Systems can be written by creating a function annotated with the `system` attribute. +//! `tonks` will automatically detect which resources are accessed based on the function +//! parameters, and it will register them to the system scheduler without you +//! having to do anything. (How does this work? Don't even bother.) +//! +//! ## Events +//! Events are another concept unique to Feather, at least in the way they are implemented +//! here. The name states what they are—BlockChangeEvent, for example, is triggered when +//! a block is updated. +//! +//! A system can trigger an event by specifying an `&mut Trigger` resource. +//! +//! ## Event handlers +//! Event handlers are similar to systems, but they only run when the event they +//! handle is triggered. Use the `event_handler` attribute on a function to register +//! it as an event handler. +//! +//! # Networking model +//! Feather consists of two key parts: the server threads, which run systems +//! over entities, and the networking tasks, which run on [`tokio`](https://github.com/tokio-rs/tokio). +//! The networking tasks will accept connections and parse any packets received +//! from the player. +//! +//! When a connection is first made to the server's TCP listener, the networking +//! task will spawn another task to handle the connection. It's important to note that +//! at this time, _the server thread is totally unaware of the connection_—networking +//! runs entirely isolated from the rest of the program. +//! +//! At this point, the initial handler takes over, which runs on the networking task. +//! It will handle the login sequence or status pings, perform authentication, etc. +//! If successful, the server is notified of the new player through a channel. +//! +//! When the server is notified of a new player, it's essential to realize +//! that the player still hasn't been sent important data, such as +//! chunk packets, inventory, time, nearby entities, etc. `PlayerJoinEvent` +//! is used to send this data. #[macro_use] extern crate log; From 106ff04b4841934b4ba4c1754fb3523cc0837976 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 15 Nov 2019 13:54:58 -0700 Subject: [PATCH 033/647] New state type for combining lots of resources; use `RwLock` to store chunks --- Cargo.lock | 1 + core/Cargo.toml | 1 + core/src/world/mod.rs | 116 +++++++++++++-------------------------- server/src/entity/mod.rs | 27 +++++++++ server/src/lazy.rs | 74 +++++++++++++++++++++++++ server/src/lib.rs | 5 +- server/src/network.rs | 19 +++++-- server/src/player/mod.rs | 33 +++++++++++ server/src/state.rs | 62 +++++++++++++++++++++ 9 files changed, 256 insertions(+), 82 deletions(-) create mode 100644 server/src/entity/mod.rs create mode 100644 server/src/lazy.rs create mode 100644 server/src/player/mod.rs create mode 100644 server/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index faded77b6..4d834ea7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -598,6 +598,7 @@ dependencies = [ "nalgebra-glm 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/core/Cargo.toml b/core/Cargo.toml index 1e0549076..3f67ffed3 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -34,3 +34,4 @@ tokio = "=0.2.0-alpha.6" failure = "0.1" bitvec = "0.15" multimap = "0.6" +parking_lot = "0.9.0" diff --git a/core/src/world/mod.rs b/core/src/world/mod.rs index 907b0c218..17fa87a62 100644 --- a/core/src/world/mod.rs +++ b/core/src/world/mod.rs @@ -2,9 +2,11 @@ use crate::world::block::*; use crate::world::chunk::Chunk; use glm::{DVec3, Vec3}; use hashbrown::HashMap; +use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use std::fmt; use std::fmt::{Display, Formatter}; use std::ops::{Add, Sub}; +use std::sync::Arc; pub mod block; #[allow(clippy::cast_lossless)] @@ -247,97 +249,57 @@ impl Add for BlockPosition { } } -pub struct ChunkMap { - chunk_map: HashMap, -} - -impl ChunkMap { - pub fn new() -> Self { - Self { - chunk_map: HashMap::new(), - } - } +pub type ChunkMapInner = HashMap>>; - pub fn inner(&self) -> &HashMap { - &self.chunk_map - } +/// The chunk map. +/// +/// This struct stores all the chunks on the server, +/// so it allows access to blocks and lighting data. +/// +/// Chunks are internally wrapped in `Arc`, +/// allowing multiple systems to access different parts +/// of the world in parallel. Mutable access to this +/// type is only required for inserting and removing +/// chunks. +pub struct ChunkMap(ChunkMapInner); - pub fn inner_mut(&mut self) -> &mut HashMap { - &mut self.chunk_map +impl ChunkMap { + /// Retrieves a handle to the chunk at the given + /// position, or `None` if it is not loaded. + pub fn chunk_at(&self, pos: ChunkPosition) -> Option> { + self.0.get(&pos).map(|lock| lock.read()) } - /// Retrieves the chunk at the specified location. - /// If the chunk is not loaded, `None` will be returned. - pub fn chunk_at(&self, pos: ChunkPosition) -> Option<&Chunk> { - if let Some(chunk) = self.chunk_map.get(&pos) { - return Some(chunk); - } - - None + /// Retrieves a handle to the chunk at the given + /// position, or `None` if it is not loaded. + pub fn chunk_at_mut(&self, pos: ChunkPosition) -> Option> { + self.0.get(&pos).map(|lock| lock.write()) } - /// Retrieves the block at the specified - /// location. If the chunk in which the block - /// exists is not laoded, `None` is returned. + /// Retrieves the block at the given position, + /// or `None` if its chunk is not loaded. pub fn block_at(&self, pos: BlockPosition) -> Option { - if pos.y > 255 || pos.y < 0 { - return None; - } - - let chunk_pos = pos.chunk_pos(); - - if let Some(chunk) = self.chunk_at(chunk_pos) { - let rpos = chunk_relative_pos(pos); - Some(chunk.block_at(rpos.0, rpos.1, rpos.2)) - } else { - None - } + let (x, y, z) = chunk_relative_pos(pos); + self.chunk_at(pos.chunk_pos()) + .map(|chunk| chunk.block_at(x, y, z)) } /// Sets the block at the given position. - /// If the chunk in which the position resides - /// does not exist, `Err` is returned. In all - /// other cases, `Ok` is returned. /// - /// Note that on the server side, calling this function - /// does not broadcast the update in any way. As such, - /// the according function should be called instead. - pub fn set_block_at(&mut self, pos: BlockPosition, block: Block) -> Result<(), ()> { - if pos.y > 255 || pos.y < 0 { - return Err(()); - } - - let chunk_pos = pos.chunk_pos(); - - if let Some(chunk) = self.chunk_map.get_mut(&chunk_pos) { - let (x, y, z) = chunk_relative_pos(pos); - chunk.set_block_at(x, y, z, block); - Ok(()) - } else { - Err(()) - } - } - - /// Sets the chunk at the given location. - pub fn set_chunk_at(&mut self, pos: ChunkPosition, chunk: Chunk) { - self.chunk_map.insert(pos, chunk); - } - - /// Removes the chunk at the given location, - /// effectively unloading it. - pub fn unload_chunk_at(&mut self, pos: ChunkPosition) -> Option { - self.chunk_map.remove(&pos) - } + /// Returns `true` if the block was set, or `false` + /// if its chunk was not loaded and thus no operation + /// was performed. + pub fn set_block_at(&self, pos: BlockPosition, block: Block) -> bool { + let (x, y, z) = chunk_relative_pos(pos); - /// Returns an immutable reference to the internal map. - pub fn chunks(&self) -> &HashMap { - &self.chunk_map + self.chunk_at_mut(pos.chunk_pos()) + .map(|mut chunk| chunk.set_block_at(x, y, z, block)) + .is_ok() } - /// Returns a mutable reference to the internal - /// map. - pub fn chunks_mut(&mut self) -> &mut HashMap { - &mut self.chunk_map + /// Returns an iterator over chunks. + pub fn iter_chunks(&self) -> impl IntoIterator>> { + self.0.iter() } } diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs new file mode 100644 index 000000000..87d23831a --- /dev/null +++ b/server/src/entity/mod.rs @@ -0,0 +1,27 @@ +//! Dealing with entities. + +use crate::lazy::EntityBuilder; +use crate::state::State; +use feather_core::Position; + +/// The velocity of an entity. +#[derive(Default, Debug, PartialEq, Clone, Copy)] +pub struct Velocity(pub glm::DVec3); + +/// The display name of the entity. +/// +/// Note that unnamed entities do not have this component. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct NameComponent(pub String); + +/// Inserts the base components for an entity into an `EntityBuilder`. +/// +/// This currently includes: +/// * Position +/// * Velocity (0) +pub fn base(state: &State, position: Position) -> EntityBuilder { + state + .create_entity() + .with_component(position) + .with_component(Velocity::default()) +} diff --git a/server/src/lazy.rs b/server/src/lazy.rs new file mode 100644 index 000000000..245ce91e5 --- /dev/null +++ b/server/src/lazy.rs @@ -0,0 +1,74 @@ +use crossbeam::queue::SegQueue; +use legion::entity::Entity; +use legion::storage::{Component, Tag}; +use legion::world::World; +use smallvec::SmallVec; + +/// Resource which allows lazy creation of entities +/// or execution of functions with world access. +pub struct Lazy { + /// Internal queue of actions to perform. + queue: SegQueue, +} + +impl Lazy { + /// Lazily executes a closure with world access. + pub fn exec(&self, f: impl FnOnce(&mut World)) { + self.queue.push(Action::Exec(Box::new(f))); + } + + /// Creates an `EntityBuilder` which can be used to lazily + /// create an entity. + pub fn create_entity(&self) -> EntityBuilder { + EntityBuilder { + lazy: self, + fns: smallvec![], + } + } + + /// Performs all queued actions. + pub fn flush(&self, world: &mut World) { + while let Ok(action) = self.queue.pop() { + match action { + Action::Exec(f) => f(world), + } + } + } +} + +/// An action which the lazy updater may perform. +enum Action { + Exec(Box), +} + +/// Builder for lazily creating entities. +pub struct EntityBuilder<'a> { + lazy: &'a Lazy, + fns: SmallVec<[Box; 8]>, +} + +impl<'a> EntityBuilder<'a> { + pub fn with_component(mut self, component: C) -> Self { + self.fns.push(move |world, entity| { + world.add_component(entity, component); + }); + self + } + + pub fn with_tag(mut self, tag: T) -> Self { + self.fns.push(move |world, entity| { + world.add_tag(entity, tag); + }); + self + } + + pub fn build(self) { + self.lazy.exec(move |world| { + let entity = world.insert((), [()].iter().copied())[0]; + + for f in self.fns { + f(world, entity); + } + }) + } +} diff --git a/server/src/lib.rs b/server/src/lib.rs index 8a419d4cf..6900869bb 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -149,11 +149,14 @@ static ALLOC: System = System; pub mod chunk_logic; pub mod chunkworker; pub mod config; +pub mod entity; pub mod io; +pub mod lazy; pub mod network; pub mod physics; +pub mod player; pub mod shutdown; -#[cfg(test)] +pub mod state; pub mod time; pub mod worldgen; diff --git a/server/src/network.rs b/server/src/network.rs index 513a7b6cc..5efc4b494 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -7,6 +7,10 @@ //! received of a given type. use crate::io::{ListenerToServerMessage, NetworkIoManager, ServerToWorkerMessage}; +use crate::lazy::Lazy; +use crate::player; +use crate::player::Player; +use crate::state::State; use crossbeam::Receiver; use feather_core::network::cast_packet; use feather_core::{Packet, PacketType}; @@ -70,8 +74,8 @@ impl PacketQueue { /// /// Systems should call `Self::send` to send a packet to this entity (player). pub struct Network { - sender: UnboundedSender, - receiver: Receiver, + pub sender: UnboundedSender, + pub receiver: Receiver, } impl Network { @@ -98,6 +102,7 @@ impl Network { /// * Accepting new clients and creating entities for them. #[system] pub fn network( + state: &State, io: &NetworkIoManager, packet_queue: &PacketQueue, mut query: PreparedQuery>, @@ -107,7 +112,11 @@ pub fn network( query.par_entities_for_each(world, |(entity, network): (Entity, Network)| { while let Ok(msg) = network.receiver.try_recv() { match msg { - ServerToWorkerMessage::NotifyDisconnect(reason) => unimplemented!(), + ServerToWorkerMessage::NotifyDisconnect(reason) => { + state.exec(move |world| { + debug_assert!(world.delete(entity), "player already deleted"); + }); + } ServerToWorkerMessage::SendPacket(packet) => { packet_queue.push(packet, entity); } @@ -119,7 +128,9 @@ pub fn network( // Handle new clients. while let Ok(msg) = io.receiver.try_recv() { match msg { - ListenerToServerMessage::NewClient(info) => unimplemented!(), + ListenerToServerMessage::NewClient(info) => { + player::create(state, info); + } } } } diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs new file mode 100644 index 000000000..7d6e26b0b --- /dev/null +++ b/server/src/player/mod.rs @@ -0,0 +1,33 @@ +//! Systems and components specific to player entities. + +use crate::entity; +use crate::entity::NameComponent; +use crate::io::NewClientInfo; +use crate::network::Network; +use crate::state::State; +use mojang_api::ProfileProperty; + +/// Profile properties of a player. +#[derive(Debug, Clone)] +pub struct ProfileProperties(pub Vec); + +/// Tag used to mark a player. +/// +/// Note that this is a _tag_, not a component. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Player; + +/// Creates a new player from the given `NewClientInfo`. +pub fn create(state: &State, info: NewClientInfo) { + entity::base(state, info.position) + .with_tag(Player) + .with_component(info.uuid) + .with_component(Network { + sender: info.sender, + receiver: info.receiver, + }) + .with_component(info.ip) + .with_component(ProfileProperties(info.profile)) + .with_component(NameComponent(info.username)) + .build(); +} diff --git a/server/src/state.rs b/server/src/state.rs new file mode 100644 index 000000000..4d7a89b16 --- /dev/null +++ b/server/src/state.rs @@ -0,0 +1,62 @@ +use crate::config::Config; +use crate::lazy::{EntityBuilder, Lazy}; +use feather_blocks::Block; +use feather_core::world::ChunkMap; +use feather_core::{BlockPosition, Chunk, ChunkPosition}; +use legion::world::World; +use parking_lot::RwLockReadGuard; +use std::sync::Arc; + +/// The state of the server. +/// +/// This state wraps numerous commonly-used resources, +/// including the chunk map (block access), config, and +/// various cached data structures, among others. +/// +/// Systems should never require mutable access to the +/// state; it is designed for read-only use. (The chunk +/// map uses `RwLock` internally, so write access isn't +/// needed to update blocks.) +pub struct State { + pub config: Arc, + + chunk_map: ChunkMap, + lazy: Lazy, +} + +impl State { + /// See `Lazy::exec()`. + pub fn exec(&self, f: impl FnOnce(&mut World)) { + self.lazy.exec(f) + } + + /// See `Lazy::create_entity()`. + pub fn create_entity(&self) -> EntityBuilder { + self.lazy.create_entity() + } + + /// See `Lazy::flush()`. + pub fn flush(&self, world: &mut World) { + self.lazy.flush(world); + } + + /// Retrieves the block at the given position, + /// or `None` if the block's chunk is not loaded. + pub fn block_at(&self, pos: BlockPosition) -> Option { + self.chunk_map.block_at(pos) + } + + /// Sets the block at the given position. + /// + /// If the block's chunk's is not loaded, returns `false`; + /// otherwise, returns `true`. + pub fn set_block_at(&self, pos: BlockPosition, block: Block) -> bool { + self.chunk_map.set_block_at(pos, block).is_ok() + } + + /// Retrieves a reference to the chunk at the given position, + /// or `None` if it not loaded. + pub fn chunk_at(&self, pos: ChunkPosition) -> Option> { + self.chunk_map.chunk_at(pos) + } +} From 9ff1e9ed74bcda8440aeacb05039c381e212b056 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 15 Nov 2019 14:09:30 -0700 Subject: [PATCH 034/647] Translate entity physics system --- server/src/physics/component.rs | 13 +- server/src/physics/entity.rs | 304 +++++++++++++------------------- server/src/physics/math.rs | 15 +- server/src/physics/mod.rs | 2 +- 4 files changed, 130 insertions(+), 204 deletions(-) diff --git a/server/src/physics/component.rs b/server/src/physics/component.rs index f10a707ec..979991bde 100644 --- a/server/src/physics/component.rs +++ b/server/src/physics/component.rs @@ -3,7 +3,6 @@ use glm::DVec3; use ncollide3d::bounding_volume::AABB; -use specs::{Component, VecStorage}; pub const DEFAULT_SLIP_MULTIPLIER: f64 = 0.6; @@ -13,7 +12,7 @@ pub const DEFAULT_SLIP_MULTIPLIER: f64 = 0.6; /// /// Typically, this component should be constructed using `PhysicsBuilder`. #[derive(Debug)] -pub struct PhysicsComponent { +pub struct Physics { /// This entity's bounding box. pub bbox: AABB, /// The drag coefficient for this entity. Each tick, @@ -33,18 +32,14 @@ pub struct PhysicsComponent { pub slip_multiplier: f64, } -impl Component for PhysicsComponent { - type Storage = VecStorage; -} - /// Builder for physics components. pub struct PhysicsBuilder { - comp: PhysicsComponent, + comp: Physics, } impl Default for PhysicsBuilder { fn default() -> Self { - let comp = PhysicsComponent { + let comp = Physics { bbox: bbox(0.5, 0.5, 0.5), drag: 0.98, gravity: -0.08, @@ -85,7 +80,7 @@ impl PhysicsBuilder { self } - pub fn build(self) -> PhysicsComponent { + pub fn build(self) -> Physics { self.comp } } diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index 4e1b931bd..cb71d8f9e 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -1,17 +1,16 @@ //! Module for performing entity physics, including velocity, drag //! and position updates each tick. -use specs::{Entities, Entity, Join, Read, ReadStorage, System, Write, WriteStorage}; - -use crate::entity::{EntityDestroyEvent, PositionComponent, VelocityComponent}; -use crate::physics::{ - block_impacted_by_ray, blocks_intersecting_bbox, AABBExt, PhysicsComponent, Side, -}; -use feather_core::world::ChunkMap; +use crate::entity::Velocity; +use crate::physics::{block_impacted_by_ray, blocks_intersecting_bbox, AABBExt, Physics, Side}; +use crate::state::State; use feather_core::Position; use feather_core::{Block, BlockExt}; -use shrev::EventChannel; +use legion::entity::Entity; +use parking_lot::Mutex; +use tonks::{PreparedQuery, PreparedWorld, Read, Trigger, Write}; +/// Event triggered when an entity lands on the ground. #[derive(Debug, Clone)] pub struct EntityPhysicsLandEvent { pub entity: Entity, @@ -20,197 +19,130 @@ pub struct EntityPhysicsLandEvent { /// System for updating all entities' positions and velocities /// each tick. -pub struct EntityPhysicsSystem; - -impl<'a> System<'a> for EntityPhysicsSystem { - type SystemData = ( - WriteStorage<'a, PositionComponent>, - WriteStorage<'a, VelocityComponent>, - ReadStorage<'a, PhysicsComponent>, - Write<'a, EventChannel>, - Write<'a, EventChannel>, - Read<'a, ChunkMap>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - mut positions, - mut velocities, - physics, - mut entity_destroy_events, - mut entity_land_events, - chunk_map, - entities, - ) = data; - // Go through entities and update their positions according - // to their velocities. - - // Unfortunately, we are currently not able to parallel - // join over the position storage due to slide-rs/specs#541. - // When this issue is resolved, the join below should be switched to a parallel - // join. - - // A restricted storage is used for `velocity` so as to avoid - // triggering a velocity update event when it is not actually - // modified. - for (position, mut restrict_velocity, physics, entity) in ( - &mut positions, - &mut velocities.restrict_mut(), - &physics, - &entities, - ) - .join() - { - let mut velocity = *restrict_velocity.get_unchecked(); - - let mut pending_position = position.current + velocity.0; - - // Check for blocks along path between old position and pending position. - // This prevents entities from flying through blocks when their - // velocity is sufficiently high. - let origin = position.current.into(); - let direction = (pending_position - position.current).into(); - let distance_squared = pending_position.distance_squared(position.current); - - if let Some(impacted) = - block_impacted_by_ray(&chunk_map, origin, direction, distance_squared) - { - // Set velocities along correct axis to 0 and then set position - // to just before the bbox would have impacted the block. - let face = impacted.face; - let impact = impacted.pos; - - if face.contains(Side::EAST) || face.contains(Side::WEST) { - velocity.x = 0.0; - pending_position.x = impact.x + physics.bbox.size().x * face.as_vector().x; - } - if face.contains(Side::NORTH) || face.contains(Side::SOUTH) { - velocity.z = 0.0; - pending_position.z = impact.z + physics.bbox.size().z * face.as_vector().z; - } - if face.contains(Side::TOP) || face.contains(Side::BOTTOM) { - velocity.y = 0.0; - pending_position.y = impact.y + physics.bbox.size().y * face.as_vector().y; - } - if face.contains(Side::TOP) { - pending_position.on_ground = true; - } - } - - // Check for blocks around the bbox and apply offset - // to position to stop the bbox from intersecting blocks. - let intersect = blocks_intersecting_bbox( - &chunk_map, - position.current, - pending_position, - &physics.bbox, - ); - intersect.apply_to(&mut pending_position); - - if intersect.x_affected() { +#[system] +fn physics( + state: &State, + mut query: PreparedQuery<(Write, Write, Read)>, + mut world: PreparedWorld, + land_events: Trigger, +) { + // Using a mutex is fine, since land events are written very rairly + // and thus contention is low. + let land_events = Mutex::new(land_events); + + // Go through entities and update their positions according + // to their velocities. + query.par_entities_for_each(&mut world, |(entity, position, velocity, physics)| { + let mut velocity = *restrict_velocity.get_unchecked(); + + let mut pending_position = position.current + velocity.0; + + // Check for blocks along path between old position and pending position. + // This prevents entities from flying through blocks when their + // velocity is sufficiently high. + let origin = position.current.into(); + let direction = (pending_position - position.current).into(); + let distance_squared = pending_position.distance_squared(position.current); + + if let Some(impacted) = block_impacted_by_ray(&state, origin, direction, distance_squared) { + // Set velocities along correct axis to 0 and then set position + // to just before the bbox would have impacted the block. + let face = impacted.face; + let impact = impacted.pos; + + if face.contains(Side::EAST) || face.contains(Side::WEST) { velocity.x = 0.0; + pending_position.x = impact.x + physics.bbox.size().x * face.as_vector().x; } - - if intersect.y_affected() { - velocity.y = 0.0; - } - - if intersect.z_affected() { + if face.contains(Side::NORTH) || face.contains(Side::SOUTH) { velocity.z = 0.0; + pending_position.z = impact.z + physics.bbox.size().z * face.as_vector().z; } - - // Delete entity if it has gone into unloaded chunks. - let block_at_pos = match chunk_map.block_at(pending_position.block_pos()) { - Some(block) => block, - None => { - // Delete entity. - let event = EntityDestroyEvent { entity }; - entity_destroy_events.single_write(event); - - entities.delete(entity).unwrap(); - continue; - } - }; - - // Set on ground status - pending_position.on_ground = match chunk_map.block_at( - position!( - pending_position.x, - pending_position.y - physics.bbox.size().y / 2.0 - 0.01, - pending_position.z - ) - .block_pos(), - ) { - Some(block) => block.is_solid(), - None => false, - }; - if pending_position.on_ground && !position.current.on_ground { - entity_land_events.single_write(EntityPhysicsLandEvent { - entity, - pos: pending_position, - }); + if face.contains(Side::TOP) || face.contains(Side::BOTTOM) { + velocity.y = 0.0; + pending_position.y = impact.y + physics.bbox.size().y * face.as_vector().y; } - - // Apply drag and gravity. - - // In water and lava, gravity is four times less, and velocity is multiplied by a special drag force. - let liquid_drag = 0.8; - match block_at_pos { - Block::Water(_) => { - velocity.0 *= liquid_drag; - velocity.0.y += physics.gravity / 4.0; - } - Block::Lava(_) => { - velocity.0 *= liquid_drag - 0.3; - velocity.0.y += physics.gravity / 4.0; - } - _ => { - let slip_multiplier = physics.slip_multiplier; - if pending_position.on_ground { - velocity.0.x *= slip_multiplier; - velocity.0.z *= slip_multiplier; - } else { - velocity.0.y = physics.drag * velocity.0.y + physics.gravity; - velocity.0.x *= physics.drag; - velocity.0.z *= physics.drag; - } - } + if face.contains(Side::TOP) { + pending_position.on_ground = true; } + } - // Set new position. - // A move event is triggered through FlaggedStorage. - position.current = pending_position; + // Check for blocks around the bbox and apply offset + // to position to stop the bbox from intersecting blocks. + let intersect = + blocks_intersecting_bbox(&state, position.current, pending_position, &physics.bbox); + intersect.apply_to(&mut pending_position); - // Update velocity, if it changed. - if velocity != *restrict_velocity.get_unchecked() { - *restrict_velocity.get_mut_unchecked() = velocity; - } + if intersect.x_affected() { + velocity.x = 0.0; } - } -} -#[cfg(test)] -mod tests { - use super::*; - use crate::entity::test; - use crate::physics::PhysicsBuilder; - use crate::testframework as t; - use specs::{Builder, WorldExt}; + if intersect.y_affected() { + velocity.y = 0.0; + } - #[test] - fn test_unloaded_chunk() { - let (mut w, mut d) = t::builder().with(EntityPhysicsSystem, "").build(); + if intersect.z_affected() { + velocity.z = 0.0; + } - let entity = test::create(&mut w, position!(1000.0, 100.0, 1000.0)).build(); + // Delete entity if it has gone into unloaded chunks. + let block_at_pos = match state.block_at(pending_position.block_pos()) { + Some(block) => block, + None => { + // Delete entity. + state.exec(move |world| { + world.delete(entity); + }); + return; + } + }; + + // Set on ground status. + pending_position.on_ground = match state.block_at( + position!( + pending_position.x, + pending_position.y - physics.bbox.size().y / 2.0 - 0.01, + pending_position.z + ) + .block_pos(), + ) { + Some(block) => block.is_solid(), + None => false, + }; + if pending_position.on_ground && !position.current.on_ground { + land_events.lock().trigger(EntityPhysicsLandEvent { + entity, + pos: pending_position, + }); + } - w.write_component::() - .insert(entity, PhysicsBuilder::new().build()) - .unwrap(); + // Apply drag and gravity. - d.dispatch(&w); - w.maintain(); + // In water and lava, gravity is four times less, and velocity is multiplied by a special drag force. + let liquid_drag = 0.8; + match block_at_pos { + Block::Water(_) => { + velocity.0 *= liquid_drag; + velocity.0.y += physics.gravity / 4.0; + } + Block::Lava(_) => { + velocity.0 *= liquid_drag - 0.3; + velocity.0.y += physics.gravity / 4.0; + } + _ => { + let slip_multiplier = physics.slip_multiplier; + if pending_position.on_ground { + velocity.0.x *= slip_multiplier; + velocity.0.z *= slip_multiplier; + } else { + velocity.0.y = physics.drag * velocity.0.y + physics.gravity; + velocity.0.x *= physics.drag; + velocity.0.z *= physics.drag; + } + } + } - t::assert_removed(&w, entity); - } + // Set new position. + position.current = pending_position; + }); } diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index 11f599df0..4d02d2473 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -4,6 +4,7 @@ use crate::entity::{ChunkEntities, PositionComponent}; use crate::physics::block_bboxes::bbox_for_block; use crate::physics::AABBExt; +use crate::state::State; use feather_blocks::Block; use feather_core::world::{BlockPosition, ChunkMap, Position}; use feather_core::{BlockExt, ChunkPosition}; @@ -15,8 +16,6 @@ use ncollide3d::query; use ncollide3d::query::{Ray, RayCast}; use ncollide3d::shape::{Compound, Cuboid, ShapeHandle}; use smallvec::SmallVec; -use specs::storage::GenericReadStorage; -use specs::Entity; use std::cmp::Ordering; use std::f64::INFINITY; @@ -89,7 +88,7 @@ pub struct RayImpact { /// Traces up to `max_distance` before returning `None` /// if no block was found. pub fn block_impacted_by_ray( - chunk_map: &ChunkMap, + state: &State, origin: DVec3, ray: DVec3, max_distance_squared: f64, @@ -167,7 +166,7 @@ pub fn block_impacted_by_ray( let mut current_pos = Position::from(origin).block_pos(); while dist_traveled.magnitude_squared() < max_distance_squared { - if let Some(block) = chunk_map.block_at(current_pos) { + if let Some(block) = state.block_at(current_pos) { if block.is_solid() { // Calculate world-space position of // impact using `ncollide`. @@ -318,7 +317,7 @@ impl BlockIntersect { /// than 1 are not supported. If the bounding box's size /// is more than 1, this function will panic. pub fn blocks_intersecting_bbox( - chunk_map: &ChunkMap, + state: &State, mut from: Position, mut dest: Position, bbox: &AABB, @@ -351,7 +350,7 @@ pub fn blocks_intersecting_bbox( let mut checked = heapless::FnvIndexSet::new(); for (axis, sign) in &axis { - let compound = adjacent_to_bbox(*axis, *sign, bbox, dest, &chunk_map, &mut checked); + let compound = adjacent_to_bbox(*axis, *sign, bbox, dest, &state, &mut checked); blocks.push(compound); } @@ -426,7 +425,7 @@ pub fn adjacent_to_bbox( sign: i32, bbox: &AABB, pos: Position, - chunk_map: &ChunkMap, + state: &State, checked: &mut heapless::FnvIndexSet, ) -> Compound { assert!(axis <= 2); @@ -486,7 +485,7 @@ pub fn adjacent_to_bbox( continue; } - match chunk_map.block_at(block_pos) { + match state.block_at(block_pos) { Some(block) => { if block.is_solid() { checked.insert(block_pos).unwrap(); diff --git a/server/src/physics/mod.rs b/server/src/physics/mod.rs index c3423d4c7..3e66be0c4 100644 --- a/server/src/physics/mod.rs +++ b/server/src/physics/mod.rs @@ -6,7 +6,7 @@ mod entity; mod math; use crate::systems::ENTITY_PHYSICS; -pub use component::{AABBExt, PhysicsBuilder, PhysicsComponent}; +pub use component::{AABBExt, Physics, PhysicsBuilder}; pub use entity::{EntityPhysicsLandEvent, EntityPhysicsSystem}; pub use math::*; use specs::DispatcherBuilder; From 0e509c4f6db1fc281a6fb30daf1156afd758916f Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 15 Nov 2019 17:00:42 -0700 Subject: [PATCH 035/647] Begin translating chunk logic --- Cargo.lock | 29 ++++-- core/src/world/mod.rs | 11 +++ server/Cargo.toml | 4 +- server/src/chunk_entities.rs | 43 +++++++++ server/src/chunk_logic.rs | 95 +++++++------------ .../src/{chunkworker.rs => chunk_worker.rs} | 0 server/src/entity/mod.rs | 10 +- .../{initialhandler.rs => initial_handler.rs} | 0 server/src/io/mod.rs | 4 +- server/src/io/worker.rs | 2 +- server/src/lazy.rs | 13 ++- server/src/lib.rs | 3 +- server/src/physics/mod.rs | 12 +-- server/src/shutdown.rs | 6 +- server/src/state.rs | 27 +++++- 15 files changed, 168 insertions(+), 91 deletions(-) create mode 100644 server/src/chunk_entities.rs rename server/src/{chunkworker.rs => chunk_worker.rs} (100%) rename server/src/io/{initialhandler.rs => initial_handler.rs} (100%) diff --git a/Cargo.lock b/Cargo.lock index 4d834ea7e..6ccac05f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -526,6 +526,14 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "evmap" +version = "7.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "failure" version = "0.1.5" @@ -646,6 +654,7 @@ dependencies = [ name = "feather-server" version = "0.5.0" dependencies = [ + "ahash 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -656,6 +665,7 @@ dependencies = [ "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "derive-new 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "derive_deref 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evmap 7.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "feather-blocks 0.5.0", "feather-codegen 0.5.0", @@ -696,7 +706,7 @@ dependencies = [ "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=0691131efcffea7c0225f9d6203b58eb2cc27cd7)", + "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=3fcc5368e6ee072c530762fa2b08d020a542a809)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2187,6 +2197,11 @@ name = "smallvec" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "smallvec" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "sourcefile" version = "0.1.4" @@ -2510,7 +2525,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=0691131efcffea7c0225f9d6203b58eb2cc27cd7#0691131efcffea7c0225f9d6203b58eb2cc27cd7" +source = "git+https://github.com/feather-rs/tonks?rev=3fcc5368e6ee072c530762fa2b08d020a542a809#3fcc5368e6ee072c530762fa2b08d020a542a809" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2526,13 +2541,13 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=0691131efcffea7c0225f9d6203b58eb2cc27cd7)", + "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=3fcc5368e6ee072c530762fa2b08d020a542a809)", ] [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=0691131efcffea7c0225f9d6203b58eb2cc27cd7#0691131efcffea7c0225f9d6203b58eb2cc27cd7" +source = "git+https://github.com/feather-rs/tonks?rev=3fcc5368e6ee072c530762fa2b08d020a542a809#3fcc5368e6ee072c530762fa2b08d020a542a809" dependencies = [ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2950,6 +2965,7 @@ dependencies = [ "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum encoding_rs 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)" = "87240518927716f79692c2ed85bfe6e98196d18c6401ec75355760233a7e12e9" +"checksum evmap 7.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4115e31301bb9dafa8ea8549432b14a51e19eecaa9e5c6041f099545e955c225" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" @@ -3115,6 +3131,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 smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86" "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" @@ -3147,8 +3164,8 @@ dependencies = [ "checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" "checksum tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" -"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=0691131efcffea7c0225f9d6203b58eb2cc27cd7)" = "" -"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=0691131efcffea7c0225f9d6203b58eb2cc27cd7)" = "" +"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=3fcc5368e6ee072c530762fa2b08d020a542a809)" = "" +"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=3fcc5368e6ee072c530762fa2b08d020a542a809)" = "" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" "checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" diff --git a/core/src/world/mod.rs b/core/src/world/mod.rs index 17fa87a62..39d61812d 100644 --- a/core/src/world/mod.rs +++ b/core/src/world/mod.rs @@ -301,6 +301,17 @@ impl ChunkMap { pub fn iter_chunks(&self) -> impl IntoIterator>> { self.0.iter() } + + /// Inserts a new chunk into the chunk map. + pub fn insert(&mut self, chunk: Chunk) { + self.0 + .insert(chunk.position(), Arc::new(RwLock::new(chunk))); + } + + /// Removes the chunk at the given position, returning `true` if it existed. + pub fn remove(&mut self, pos: ChunkPosition) -> bool { + self.0.remove(&pos).is_some() + } } impl Default for ChunkMap { diff --git a/server/Cargo.toml b/server/Cargo.toml index feaa9a857..b6d700ba3 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -17,7 +17,7 @@ feather-blocks = { path = "../blocks" } feather-core = { path = "../core" } feather-item-block = { path = "../item_block" } legion = { git = "https://github.com/TomGillen/legion", rev = "2eab55dc5a3a84fbc8505cc16eb780ef0cdd019b" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "0691131efcffea7c0225f9d6203b58eb2cc27cd7" } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "3fcc5368e6ee072c530762fa2b08d020a542a809" } crossbeam = "0.7" log = "0.4" simple_logger = "1.3" @@ -65,6 +65,8 @@ tokio-executor = "=0.2.0-alpha.6" futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } humantime-serde = "0.1" ctrlc = "3.1" +evmap = "7.1" +ahash = "0.2" [dev-dependencies] criterion = "0.3.0" diff --git a/server/src/chunk_entities.rs b/server/src/chunk_entities.rs new file mode 100644 index 000000000..16cdbcd4a --- /dev/null +++ b/server/src/chunk_entities.rs @@ -0,0 +1,43 @@ +use ahash::ABuildHasher; +use evmap::shallow_copy::CopyValue; +use evmap::{ReadHandle, ReadHandleFactory, WriteHandle}; +use feather_core::ChunkPosition; +use legion::entity::Entity; +use thread_local::ThreadLocal; + +/// Stores which entities belong to every given chunk. +/// +/// This data structure can be used to accelerate certain +/// operations, such as querying for entities +/// within some distance of a position. In addition, +/// it can be used to send all entities in a chunk +/// to a player. +/// +/// This structure is internally stored in `State`, using +/// `evmap` for concurrent map access. +/// +/// Do note that the information in this structure is not necessarily up to date, +/// although a best effort is made to update the data. +pub struct ChunkEntities { + writer: WriteHandle, (), ABuildHasher>, + factory: ReadHandleFactory, (), ABuildHasher>, + readers: ThreadLocal, (), ABuildHasher>>, +} + +impl ChunkEntities { + pub fn new() -> Self { + let (reader, writer) = evmap::with_hasher((), ABuildHasher); + Self { + writer, + factory: reader.factory(), + readers: ThreadLocal::new(), + } + } + /// Returns a slice of entities in the given chunk. + pub fn entities_in_chunk(&self, chunk: ChunkPosition) -> &[CopyValue] { + self.readers + .get_or(|| self.factory.handle()) + .get_and(&chunk, |slice| slice) + .unwrap_or(&[]) + } +} diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index 2212903ef..9331fc3bc 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -3,10 +3,6 @@ //! //! Also handles unloading chunks when unused. use crossbeam::channel::{Receiver, Sender}; -use shrev::{EventChannel, ReaderId}; -use specs::{ - Component, DispatcherBuilder, Entity, Read, ReadExpect, ReadStorage, System, World, Write, -}; use std::sync::atomic::{AtomicU32, Ordering}; use feather_core::world::{ChunkMap, ChunkPosition}; @@ -14,25 +10,25 @@ use feather_core::world::{ChunkMap, ChunkPosition}; use rayon::prelude::*; use crate::config::Config; -use crate::entity::EntityDestroyEvent; -use crate::systems::{CHUNK_HOLD_REMOVE, CHUNK_LOAD, CHUNK_OPTIMIZE, CHUNK_UNLOAD}; +use crate::state::State; use crate::worldgen::WorldGenerator; -use crate::{chunkworker, current_time_in_millis, TickCount, TPS}; +use crate::{chunk_worker, current_time_in_millis, TickCount, TPS}; use feather_core::entity::EntityData; use feather_core::Chunk; use hashbrown::HashSet; +use legion::entity::Entity; use multimap::MultiMap; -use specs::storage::BTreeStorage; use std::collections::VecDeque; use std::path::Path; use std::sync::Arc; +use tonks::Trigger; /// A handle for interacting with the chunk /// worker thread. #[derive(Debug, Clone)] pub struct ChunkWorkerHandle { - pub sender: Sender, - pub receiver: Receiver, + pub sender: Sender, + pub receiver: Receiver, } /// Event which is triggered when a chunk is loaded. @@ -56,54 +52,33 @@ pub struct ChunkUnloadEvent { } /// System for receiving loaded chunks from the chunk worker thread. -pub struct ChunkLoadSystem; - -impl<'a> System<'a> for ChunkLoadSystem { - type SystemData = ( - Write<'a, ChunkMap>, - Write<'a, EventChannel>, - Write<'a, EventChannel>, - ReadExpect<'a, ChunkWorkerHandle>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (mut chunk_map, mut load_events, mut fail_events, handle) = data; - - while let Ok(reply) = handle.receiver.try_recv() { - if let chunkworker::Reply::LoadedChunk(pos, result) = reply { - match result { - Ok((chunk, entities)) => { - chunk_map.set_chunk_at(pos, chunk); - - // Trigger event - let event = ChunkLoadEvent { pos, entities }; - load_events.single_write(event); - - trace!("Loaded chunk at {:?}", pos); - } - Err(err) => { - warn!("Failed to load chunk at {:?}: {}", pos, err); - let event = ChunkLoadFailEvent { pos }; - fail_events.single_write(event); - } +#[system] +fn chunk_load_system( + state: &State, + handle: &ChunkWorkerHandle, + mut load_events: Trigger, + mut fail_events: Trigger, +) { + while let Ok(reply) = handle.receiver.try_recv() { + if let chunk_worker::Reply::LoadedChunk(pos, result) = reply { + match result { + Ok((chunk, entities)) => { + state.lazy_insert_chunk(chunk); + + // Trigger event + let event = ChunkLoadEvent { pos, entities }; + load_events.trigger(event); + + trace!("Loaded chunk at {:?}", pos); + } + Err(err) => { + warn!("Failed to load chunk at {:?}: {}", pos, err); + let event = ChunkLoadFailEvent { pos }; + fail_events.trigger(event); } } } } - - fn setup(&mut self, world: &mut World) { - use specs::prelude::SystemData; - - let generator = world.fetch_mut::>().clone(); - let world_name = &world.fetch_mut::>().world.name.clone(); - let world_dir = Path::new(world_name); - - info!("Starting chunk worker thread"); - let (sender, receiver) = chunkworker::start(world_dir, generator); - world.insert(ChunkWorkerHandle { sender, receiver }); - - Self::SystemData::setup(world); - } } /// Asynchronously loads the chunk at the given position. @@ -116,7 +91,7 @@ pub fn load_chunk(handle: &ChunkWorkerHandle, pos: ChunkPosition) { // Send request to chunk worker thread handle .sender - .send(chunkworker::Request::LoadChunk(pos)) + .send(chunk_worker::Request::LoadChunk(pos)) .unwrap(); } @@ -124,7 +99,7 @@ pub fn load_chunk(handle: &ChunkWorkerHandle, pos: ChunkPosition) { pub fn save_chunk(handle: &ChunkWorkerHandle, chunk: Arc, entities: Vec) { handle .sender - .send(chunkworker::Request::SaveChunk(chunk, entities)) + .send(chunk_worker::Request::SaveChunk(chunk, entities)) .unwrap(); } @@ -163,7 +138,7 @@ impl ChunkHolders { &mut self, chunk: ChunkPosition, holder: Entity, - events: &mut EventChannel, + trigger: &mut Trigger, ) { if let Some(vec) = self.inner.get_vec_mut(&chunk) { let index = vec.iter().position(|e| *e == holder); @@ -175,7 +150,7 @@ impl ChunkHolders { entity: holder, chunk, }; - events.single_write(event); + trigger.trigger(event); } } } @@ -474,7 +449,7 @@ mod tests { let chunk_map = ChunkMap::new(); let pos = ChunkPosition::new(0, 0); send2 - .send(chunkworker::Reply::LoadedChunk( + .send(chunk_worker::Reply::LoadedChunk( pos, Ok((Chunk::new(pos), vec![])), )) @@ -515,7 +490,7 @@ mod tests { let recv = recv1.try_recv().unwrap(); match recv { - chunkworker::Request::LoadChunk(recv_pos) => assert_eq!(recv_pos, pos), + chunk_worker::Request::LoadChunk(recv_pos) => assert_eq!(recv_pos, pos), _ => panic!(), } } diff --git a/server/src/chunkworker.rs b/server/src/chunk_worker.rs similarity index 100% rename from server/src/chunkworker.rs rename to server/src/chunk_worker.rs diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 87d23831a..1efc715d0 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -2,7 +2,15 @@ use crate::lazy::EntityBuilder; use crate::state::State; -use feather_core::Position; +use feather_core::{ChunkPosition, Position}; +use legion::prelude::Entity; +use parking_lot::Mutex; + +/// Event triggered when an entity is removed. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EntityDeleteEvent { + entity: Entity, +} /// The velocity of an entity. #[derive(Default, Debug, PartialEq, Clone, Copy)] diff --git a/server/src/io/initialhandler.rs b/server/src/io/initial_handler.rs similarity index 100% rename from server/src/io/initialhandler.rs rename to server/src/io/initial_handler.rs diff --git a/server/src/io/mod.rs b/server/src/io/mod.rs index 106f04480..71ec0e32f 100644 --- a/server/src/io/mod.rs +++ b/server/src/io/mod.rs @@ -5,7 +5,7 @@ use std::net::SocketAddr; use std::sync::Arc; use uuid::Uuid; -mod initialhandler; +mod initial_handler; mod listener; mod worker; @@ -75,7 +75,7 @@ impl Default for NetworkIoManager { /// Initializes certain static variables. pub fn init() { - lazy_static::initialize(&initialhandler::RSA_KEY); + lazy_static::initialize(&initial_handler::RSA_KEY); } async fn run_listener( diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index a312f6b39..d9a966f04 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -7,7 +7,7 @@ //! to the worker for any given client. use crate::config::Config; -use crate::io::initialhandler::{Action, InitialHandler}; +use crate::io::initial_handler::{Action, InitialHandler}; use crate::io::{ListenerToServerMessage, NewClientInfo, ServerToWorkerMessage}; use crate::PlayerCount; use feather_core::network::codec::MinecraftCodec; diff --git a/server/src/lazy.rs b/server/src/lazy.rs index 245ce91e5..265c61b94 100644 --- a/server/src/lazy.rs +++ b/server/src/lazy.rs @@ -3,6 +3,7 @@ use legion::entity::Entity; use legion::storage::{Component, Tag}; use legion::world::World; use smallvec::SmallVec; +use tonks::Scheduler; /// Resource which allows lazy creation of entities /// or execution of functions with world access. @@ -14,6 +15,12 @@ pub struct Lazy { impl Lazy { /// Lazily executes a closure with world access. pub fn exec(&self, f: impl FnOnce(&mut World)) { + self.exec_with_scheduler(move |world, _| f(world)); + } + + /// Lazily executes a closure with world and scheduler (resource) + /// access. + pub fn exec_with_scheduler(&self, f: impl FnOnce(&mut World, &mut Scheduler)) { self.queue.push(Action::Exec(Box::new(f))); } @@ -27,10 +34,10 @@ impl Lazy { } /// Performs all queued actions. - pub fn flush(&self, world: &mut World) { + pub fn flush(&self, world: &mut World, scheduler: &mut Scheduler) { while let Ok(action) = self.queue.pop() { match action { - Action::Exec(f) => f(world), + Action::Exec(f) => f(world, scheduler), } } } @@ -38,7 +45,7 @@ impl Lazy { /// An action which the lazy updater may perform. enum Action { - Exec(Box), + Exec(Box), } /// Builder for lazily creating entities. diff --git a/server/src/lib.rs b/server/src/lib.rs index 6900869bb..4b3b1f753 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -146,8 +146,9 @@ use std::process::exit; #[global_allocator] static ALLOC: System = System; +pub mod chunk_entities; pub mod chunk_logic; -pub mod chunkworker; +pub mod chunk_worker; pub mod config; pub mod entity; pub mod io; diff --git a/server/src/physics/mod.rs b/server/src/physics/mod.rs index 3e66be0c4..aeec599ef 100644 --- a/server/src/physics/mod.rs +++ b/server/src/physics/mod.rs @@ -5,16 +5,6 @@ mod component; mod entity; mod math; -use crate::systems::ENTITY_PHYSICS; pub use component::{AABBExt, Physics, PhysicsBuilder}; -pub use entity::{EntityPhysicsLandEvent, EntityPhysicsSystem}; +pub use entity::EntityPhysicsLandEvent; pub use math::*; -use specs::DispatcherBuilder; - -pub fn init_logic(dispatcher: &mut DispatcherBuilder) { - dispatcher.add(EntityPhysicsSystem, ENTITY_PHYSICS, &[]); -} - -pub fn init_handlers(_dispatcher: &mut DispatcherBuilder) { - // nothing -} diff --git a/server/src/shutdown.rs b/server/src/shutdown.rs index 5bcb42a43..ab85578da 100644 --- a/server/src/shutdown.rs +++ b/server/src/shutdown.rs @@ -6,7 +6,7 @@ use crate::entity::{NamedComponent, PlayerComponent, PositionComponent}; use crate::player; use crate::player::InventoryComponent; use crate::time::Time; -use crate::{chunkworker, entity}; +use crate::{chunk_worker, entity}; use crossbeam::Sender; use feather_core::level::{save_level_file, LevelData, Root}; use feather_core::prelude::ChunkMap; @@ -34,12 +34,12 @@ pub fn save_chunks(world: &mut World) { world.maintain(); let handle = world.fetch::(); - handle.sender.send(chunkworker::Request::ShutDown).unwrap(); + handle.sender.send(chunk_worker::Request::ShutDown).unwrap(); let mut saved = 0; // Wait for chunks to finish saving while let Ok(msg) = handle.receiver.recv() { - if let chunkworker::Reply::SavedChunk(_) = msg { + if let chunk_worker::Reply::SavedChunk(_) = msg { saved += 1; } diff --git a/server/src/state.rs b/server/src/state.rs index 4d7a89b16..7310db1a2 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -6,6 +6,7 @@ use feather_core::{BlockPosition, Chunk, ChunkPosition}; use legion::world::World; use parking_lot::RwLockReadGuard; use std::sync::Arc; +use tonks::Scheduler; /// The state of the server. /// @@ -36,8 +37,8 @@ impl State { } /// See `Lazy::flush()`. - pub fn flush(&self, world: &mut World) { - self.lazy.flush(world); + pub fn flush(&self, world: &mut World, scheduler: &mut Scheduler) { + self.lazy.flush(world, scheduler); } /// Retrieves the block at the given position, @@ -59,4 +60,26 @@ impl State { pub fn chunk_at(&self, pos: ChunkPosition) -> Option> { self.chunk_map.chunk_at(pos) } + + /// Lazily inserts the given chunk into the chunk map. + pub fn lazy_insert_chunk(&self, chunk: Chunk) { + self.lazy.exec_with_scheduler(move |_, scheduler| { + scheduler + .resources() + .get_mut::() + .chunk_map + .insert(chunk); + }); + } + + /// Lazily removes the given chunk from the chunk map. + pub fn lazy_remove_chunk(&self, pos: ChunkPosition) { + self.lazy.exec_with_scheduler(move |_, scheduler| { + scheduler + .resources() + .get_mut::() + .chunk_map + .remove(pos); + }); + } } From ffcb46e4357cca597fe98e758e5949d13de9680a Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 15 Nov 2019 18:27:39 -0700 Subject: [PATCH 036/647] Reimplement the rest of chunk logic --- Cargo.lock | 1 + core/Cargo.toml | 3 +- core/src/world/mod.rs | 6 + server/src/chunk_logic.rs | 336 +++++++++----------------------------- server/src/entity/mod.rs | 2 +- server/src/state.rs | 2 +- 6 files changed, 89 insertions(+), 261 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ccac05f8..67398fad3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -607,6 +607,7 @@ dependencies = [ "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/core/Cargo.toml b/core/Cargo.toml index 3f67ffed3..b13027a10 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -20,7 +20,7 @@ log = "0.4" serde = { version = "1.0", features = ["derive"] } num-traits = "0.2" num-derive = "0.3" -hashbrown = { version = "0.6", features = ["serde"] } +hashbrown = { version = "0.6", features = ["serde", "rayon"] } hematite-nbt = "0.4" byteorder = "1.3" nalgebra-glm = "0.4" @@ -35,3 +35,4 @@ failure = "0.1" bitvec = "0.15" multimap = "0.6" parking_lot = "0.9.0" +rayon = "1.2.0" diff --git a/core/src/world/mod.rs b/core/src/world/mod.rs index 39d61812d..c7d2824d1 100644 --- a/core/src/world/mod.rs +++ b/core/src/world/mod.rs @@ -3,6 +3,7 @@ use crate::world::chunk::Chunk; use glm::{DVec3, Vec3}; use hashbrown::HashMap; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use rayon::iter::ParallelIterator; use std::fmt; use std::fmt::{Display, Formatter}; use std::ops::{Add, Sub}; @@ -302,6 +303,11 @@ impl ChunkMap { self.0.iter() } + /// Returns a parallel iterator over chunks. + pub fn par_iter_chunks(&self) -> impl ParallelIterator>> { + self.0.par_iter() + } + /// Inserts a new chunk into the chunk map. pub fn insert(&mut self, chunk: Chunk) { self.0 diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index 9331fc3bc..61ab314ef 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -10,6 +10,7 @@ use feather_core::world::{ChunkMap, ChunkPosition}; use rayon::prelude::*; use crate::config::Config; +use crate::entity::EntityDeleteEvent; use crate::state::State; use crate::worldgen::WorldGenerator; use crate::{chunk_worker, current_time_in_millis, TickCount, TPS}; @@ -21,7 +22,7 @@ use multimap::MultiMap; use std::collections::VecDeque; use std::path::Path; use std::sync::Arc; -use tonks::Trigger; +use tonks::{PreparedQuery, PreparedWorld, Query, Read, Trigger}; /// A handle for interacting with the chunk /// worker thread. @@ -44,13 +45,6 @@ pub struct ChunkLoadFailEvent { pub pos: ChunkPosition, } -/// Event which is triggered when a chunk is unloaded. -#[derive(Clone)] -pub struct ChunkUnloadEvent { - /// The chunk which was unloaded. - pub chunk: Arc, -} - /// System for receiving loaded chunks from the chunk worker thread. #[system] fn chunk_load_system( @@ -166,7 +160,7 @@ pub struct ChunkHolderReleaseEvent { } /// The queue of chunks to be unloaded. -/// See `ChunkUnloadSystem` for details. +/// See `chunk_unload` for details. #[derive(Clone, Debug, Default)] pub struct ChunkUnloadQueue { /// The internal queue. @@ -187,13 +181,7 @@ struct ChunkUnload { const CHUNK_UNLOAD_TIME: u64 = TPS * 5; // 5 seconds - TODO make this configurable /// System for unloading chunks when they have no holders. -/// This system performs multiple actions: -/// -/// * It listens to `ChunkHolderReleaseEvent` and -/// checks if a chunk has no holders. If so, it queues -/// the chunk to be unloaded after some period of time -/// (defined by a constant). -/// * It goes through chunks which are currently +/// This system through chunks which are currently /// queued to be loaded and unloads them if the /// period of time has elapsed. /// @@ -203,91 +191,51 @@ const CHUNK_UNLOAD_TIME: u64 = TPS * 5; // 5 seconds - TODO make this configurab /// could quickly move between chunk boundaries, causing /// chunks at the edge of their view distance /// to be loaded and unloaded at an alarming rate. -#[derive(Default)] -pub struct ChunkUnloadSystem { - reader: Option>, -} - -impl ChunkUnloadSystem { - pub fn new() -> Self { - Self { reader: None } - } -} - -impl<'a> System<'a> for ChunkUnloadSystem { - type SystemData = ( - Write<'a, ChunkMap>, - Write<'a, EventChannel>, - Read<'a, EventChannel>, - Write<'a, ChunkUnloadQueue>, - Read<'a, ChunkHolders>, - Read<'a, TickCount>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - mut chunk_map, - mut unload_events, - release_events, - mut unload_queue, - holders, - tick_count, - ) = data; - - // Handle holder release events. - for event in release_events.read(&mut self.reader.as_mut().unwrap()) { - // If the chunk now has zero holders, queue it for unloading. - if !holders.chunk_has_holders(event.chunk) { - let unload = ChunkUnload { - chunk: event.chunk, - time: tick_count.0 + CHUNK_UNLOAD_TIME, - }; - unload_queue.queue.push_back(unload); - } - } - - // Unload chunks which are finished in the queue. - - // Since chunks are queued in the back and taken out - // from the front, the chunks in the front of the vector - // were queued the longest time ago. Because of this, - // we go through the unloads in the front of the queue - // to find which chunks to unload. - while let Some(unload) = unload_queue.queue.front() { - if tick_count.0 >= unload.time { - // Don't unload if new chunk holders have appeared. - if holders.chunk_has_holders(unload.chunk) { - unload_queue.queue.pop_front(); - continue; - } - - // Unload chunk and pop from queue. - if let Some(chunk) = chunk_map.unload_chunk_at(unload.chunk) { - let event = ChunkUnloadEvent { - chunk: Arc::new(chunk), - }; - unload_events.single_write(event); - } - +#[system] +fn chunk_unload(state: &State, unload_queue: &mut ChunkUnloadQueue, holders: &ChunkHolders) { + // Unload chunks which are finished in the queue. + + // Since chunks are queued in the back and taken out + // from the front, the chunks in the front of the vector + // were queued the longest time ago. Because of this, + // we go through the unloads in the front of the queue + // to find which chunks to unload. + while let Some(unload) = unload_queue.queue.front() { + if tick_count.0 >= unload.time { + // Don't unload if new chunk holders have appeared. + if holders.chunk_has_holders(unload.chunk) { unload_queue.queue.pop_front(); - } else { - // We're done - all chunks farther up in - // the queue were queued before this one, - // so it isn't time to unload any of those. - break; + continue; } + + // Unload chunk and pop from queue. + state.lazy_remove_chunk(unload.chunk); + unload_queue.queue.pop_front(); + } else { + // We're done - all chunks farther up in + // the queue were queued before this one, + // so it isn't time to unload any of those. + break; } } +} - fn setup(&mut self, world: &mut World) { - use specs::SystemData; - Self::SystemData::setup(world); - - self.reader = Some( - world - .fetch_mut::>() - .register_reader(), - ); +/// Event handler which handles holder release events. If +/// a chunk has no more holders, then a chunk unload is queued. +#[event_handler] +pub fn chunk_unload_no_holders( + event: &ChunkHolderReleaseEvent, + holders: &ChunkHolders, + unload_queue: &mut ChunkUnloadQueue, +) { + // Handle holder release events. + // If the chunk now has zero holders, queue it for unloading. + if !holders.chunk_has_holders(event.chunk) { + let unload = ChunkUnload { + chunk: event.chunk, + time: tick_count.0 + CHUNK_UNLOAD_TIME, + }; + unload_queue.queue.push_back(unload); } } @@ -299,7 +247,8 @@ impl<'a> System<'a> for ChunkUnloadSystem { /// stored in the `ChunkHolders` resource, /// using this component allows for efficiently /// finding which chunks a given entity has -/// a hold on. +/// a hold on, rather than having +/// to linear search all chunks (obviously ridiculous). #[derive(Default)] pub struct ChunkHolderComponent { pub holds: HashSet, @@ -307,61 +256,24 @@ pub struct ChunkHolderComponent { impl ChunkHolderComponent { pub fn new() -> Self { - Self { - holds: HashSet::new(), - } + Self::default() } } -impl Component for ChunkHolderComponent { - type Storage = BTreeStorage; -} - /// System for removing an entity's chunk holds /// once it is destroyed. -#[derive(Default)] -pub struct ChunkHoldRemoveSystem { - reader: Option>, -} - -impl ChunkHoldRemoveSystem { - pub fn new() -> Self { - Self { reader: None } - } -} - -impl<'a> System<'a> for ChunkHoldRemoveSystem { - type SystemData = ( - Read<'a, EventChannel>, - Write<'a, ChunkHolders>, - ReadStorage<'a, ChunkHolderComponent>, - Write<'a, EventChannel>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (events, mut holders, holder_comps, mut release_events) = data; - - for event in events.read(&mut self.reader.as_mut().unwrap()) { - // If entity had chunk holds, remove them all - if let Some(holder_comp) = holder_comps.get(event.entity) { - debug!("Removing chunk holds for entity {:?}", event.entity); - holder_comp.holds.iter().for_each(|chunk| { - holders.remove_holder(*chunk, event.entity, &mut release_events); - }); - } - } - } - - fn setup(&mut self, world: &mut World) { - use specs::SystemData; - - Self::SystemData::setup(world); - - self.reader = Some( - world - .fetch_mut::>() - .register_reader(), - ); +#[event_handler] +fn chunk_holder_remove( + event: &EntityDeleteEvent, + mut query: PreparedQuery>, + mut world: PreparedWorld, +) { + // If entity had chunk holds, remove them all + if let Ok(holder_comp) = query.find(event.entity, &mut world) { + debug!("Removing chunk holds for entity {:?}", event.entity); + holder_comp.holds.iter().for_each(|chunk| { + holders.remove_holder(*chunk, event.entity, &mut release_events); + }); } } @@ -377,121 +289,29 @@ const CHUNK_OPTIMIZE_INTERVAL: u64 = TPS * 60 * 5; // 5 minutes /// For optimal performance, this system is fully /// concurrent - each chunk optimization is split /// into a separate job and fed into `rayon`. -pub struct ChunkOptimizeSystem; - -impl<'a> System<'a> for ChunkOptimizeSystem { - type SystemData = (Write<'a, ChunkMap>, Read<'a, TickCount>); - - fn run(&mut self, data: Self::SystemData) { - let (mut chunk_map, tick_count) = data; - - // Only run every CHUNK_OPTIMIZE_INTERVAL ticks - if tick_count.0 % CHUNK_OPTIMIZE_INTERVAL != 0 { - return; - } - - let chunks = chunk_map.chunks_mut(); - - // Don't run if there aren't any chunks loaded - if chunks.is_empty() { - return; - } - - debug!("Optimizing chunks"); - - let start_time = current_time_in_millis(); - let count = AtomicU32::new(0); - - chunks.par_iter_mut().for_each(|(_, chunk)| { - count.fetch_add(chunk.optimize(), Ordering::SeqCst); - }); - - let end_time = current_time_in_millis(); - let elapsed = end_time - start_time; - - debug!( - "Optimized {} chunk sections (took {}ms - {:.2}ms/section)", - count.load(Ordering::SeqCst), - elapsed, - elapsed as f64 / f64::from(count.load(Ordering::SeqCst)) - ); +#[system] +fn chunk_optimize(state: &State, tick_count: &TickCount) { + // Only run every CHUNK_OPTIMIZE_INTERVAL ticks + if tick_count.0 % CHUNK_OPTIMIZE_INTERVAL != 0 { + return; } -} -pub fn init_logic(dispatcher: &mut DispatcherBuilder) { - dispatcher.add(ChunkLoadSystem, CHUNK_LOAD, &[]); - dispatcher.add(ChunkOptimizeSystem, CHUNK_OPTIMIZE, &[]); -} - -pub fn init_handlers(dispatcher: &mut DispatcherBuilder) { - dispatcher.add(ChunkUnloadSystem::default(), CHUNK_UNLOAD, &[]); - dispatcher.add(ChunkHoldRemoveSystem::default(), CHUNK_HOLD_REMOVE, &[]); -} + debug!("Optimizing chunks"); -#[cfg(test)] -mod tests { - use specs::{RunNow, World, WorldExt}; + let start_time = current_time_in_millis(); + let count = AtomicU32::new(0); - use feather_core::world::chunk::Chunk; - use feather_core::world::ChunkPosition; + state.chunk_map.par_iter_chunks().for_each(|chunk| { + count.fetch_add(chunk.write().optimize(), Ordering::Relaxed); + }); - use super::*; - - #[test] - fn test_chunk_system() { - let (send1, _recv1) = crossbeam::channel::unbounded(); - let (send2, recv2) = crossbeam::channel::unbounded(); - let handle = ChunkWorkerHandle { - sender: send1, - receiver: recv2, - }; + let end_time = current_time_in_millis(); + let elapsed = end_time - start_time; - let chunk_map = ChunkMap::new(); - let pos = ChunkPosition::new(0, 0); - send2 - .send(chunk_worker::Reply::LoadedChunk( - pos, - Ok((Chunk::new(pos), vec![])), - )) - .unwrap(); - - let load_event_channel = EventChannel::::new(); - let fail_event_channel = EventChannel::::new(); - - let mut system = ChunkLoadSystem; - let mut world = World::new(); - world.insert(chunk_map); - world.insert(handle); - world.insert(load_event_channel); - world.insert(fail_event_channel); - - system.run_now(&world); - - // Confirm that chunk was loaded - let chunk_map = world.read_resource::(); - let chunk = chunk_map.chunk_at(pos); - - assert!(chunk.is_some()); - assert!(chunk.unwrap().position() == pos); - } - - #[test] - fn test_load_chunk() { - let (send1, recv1) = crossbeam::channel::unbounded(); - let (_send2, recv2) = crossbeam::channel::unbounded(); - let handle = ChunkWorkerHandle { - sender: send1, - receiver: recv2, - }; - - let pos = ChunkPosition::new(0, 0); - - load_chunk(&handle, pos); - - let recv = recv1.try_recv().unwrap(); - match recv { - chunk_worker::Request::LoadChunk(recv_pos) => assert_eq!(recv_pos, pos), - _ => panic!(), - } - } + debug!( + "Optimized {} chunk sections (took {}ms - {:.2}ms/section)", + count.load(Ordering::Relaxed), + elapsed, + elapsed as f64 / f64::from(count.load(Ordering::Relaxed)) + ); } diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 1efc715d0..010a24ae6 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -9,7 +9,7 @@ use parking_lot::Mutex; /// Event triggered when an entity is removed. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct EntityDeleteEvent { - entity: Entity, + pub(crate) entity: Entity, } /// The velocity of an entity. diff --git a/server/src/state.rs b/server/src/state.rs index 7310db1a2..9f5418ee1 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -20,8 +20,8 @@ use tonks::Scheduler; /// needed to update blocks.) pub struct State { pub config: Arc, + pub chunk_map: ChunkMap, - chunk_map: ChunkMap, lazy: Lazy, } From 93880f34c52ef498b62dde45bc2d2cdc557c62a0 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 15 Nov 2019 18:28:18 -0700 Subject: [PATCH 037/647] Rename ChunkHolderComponent -> ChunkHolder --- server/src/chunk_logic.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index 61ab314ef..78dfcd4c0 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -250,11 +250,11 @@ pub fn chunk_unload_no_holders( /// a hold on, rather than having /// to linear search all chunks (obviously ridiculous). #[derive(Default)] -pub struct ChunkHolderComponent { +pub struct ChunkHolder { pub holds: HashSet, } -impl ChunkHolderComponent { +impl ChunkHolder { pub fn new() -> Self { Self::default() } @@ -265,7 +265,7 @@ impl ChunkHolderComponent { #[event_handler] fn chunk_holder_remove( event: &EntityDeleteEvent, - mut query: PreparedQuery>, + mut query: PreparedQuery>, mut world: PreparedWorld, ) { // If entity had chunk holds, remove them all From fec8ea3a6b8711c49d4edc5d0eb9fe30f3261e77 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 16 Nov 2019 10:56:45 -0700 Subject: [PATCH 038/647] Convert time sending --- Cargo.lock | 28 ++++++--- server/Cargo.toml | 82 +++++++++++++++--------- server/src/player/mod.rs | 6 ++ server/src/time.rs | 132 +++++++-------------------------------- 4 files changed, 98 insertions(+), 150 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67398fad3..36f30de11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -664,7 +664,6 @@ dependencies = [ "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "derive-new 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "derive_deref 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "evmap 7.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -681,8 +680,8 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "legion 0.1.1 (git+https://github.com/TomGillen/legion?rev=2eab55dc5a3a84fbc8505cc16eb780ef0cdd019b)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mojang-api 0.3.0 (git+https://github.com/caelunshun/mojang-api-rs?rev=6525e910ad53953fa16028f0fce74b1a19855733)", - "multimap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "mojang-api 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "multimap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "nalgebra 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", "nalgebra-glm 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "ncollide3d 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1271,13 +1270,13 @@ dependencies = [ [[package]] name = "mojang-api" -version = "0.3.0" -source = "git+https://github.com/caelunshun/mojang-api-rs?rev=6525e910ad53953fa16028f0fce74b1a19855733#6525e910ad53953fa16028f0fce74b1a19855733" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.10.0-alpha.0 (git+https://github.com/seanmonstar/reqwest?rev=5b55aee1a9ddf785f82d9086c8befc50db268cb8)", + "reqwest 0.10.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1297,6 +1296,14 @@ dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "multimap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nalgebra" version = "0.18.1" @@ -1958,8 +1965,8 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.10.0-alpha.0" -source = "git+https://github.com/seanmonstar/reqwest?rev=5b55aee1a9ddf785f82d9086c8befc50db268cb8#5b55aee1a9ddf785f82d9086c8befc50db268cb8" +version = "0.10.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3030,9 +3037,10 @@ dependencies = [ "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum mojang-api 0.3.0 (git+https://github.com/caelunshun/mojang-api-rs?rev=6525e910ad53953fa16028f0fce74b1a19855733)" = "" +"checksum mojang-api 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f301933426b04d4557b52f7715a1e62dd9c43bc64625c645cbb50de70d5692" "checksum mopa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915" "checksum multimap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de234f818d54830a7103b9be18ad0861d75aeb5e3c89759bc3f9a004cc39cfa3" +"checksum multimap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97b1a404699e1fa9fa1665e045c1dd23e6663e8e9ff883faa349c90e71aa7ce9" "checksum nalgebra 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aaa9fddbc34c8c35dd2108515587b8ce0cab396f17977b8c738568e4edb521a2" "checksum nalgebra-glm 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a4cd007520d46d2ca24002ddf538fce40ea2a34ed0ffcd3e2ae0b6ba18811d3" "checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" @@ -3105,7 +3113,7 @@ dependencies = [ "checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum reqwest 0.10.0-alpha.0 (git+https://github.com/seanmonstar/reqwest?rev=5b55aee1a9ddf785f82d9086c8befc50db268cb8)" = "" +"checksum reqwest 0.10.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d75dbf305ed1eb54d3c8564e3b746012166b40ec0841381df92b50a2052db71" "checksum rgb 0.8.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2089e4031214d129e201f8c3c8c2fe97cd7322478a0d1cdf78e7029b0042efdb" "checksum rsa 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6ad8d3632f6745bb671c8637e2aa44015537c5e384789d2ea3235739301ed1e0" "checksum rsa-der 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1170c86c683547fa781a0e39e6e281ebaedd4515be8a806022984f427ea3d44d" diff --git a/server/Cargo.toml b/server/Cargo.toml index b6d700ba3..e548e9861 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -13,63 +13,85 @@ name = "feather-server" path = "src/main.rs" [dependencies] +# Feather crates feather-blocks = { path = "../blocks" } feather-core = { path = "../core" } feather-item-block = { path = "../item_block" } +feather-codegen = { path = "../codegen" } + +# Core ECS + systems legion = { git = "https://github.com/TomGillen/legion", rev = "2eab55dc5a3a84fbc8505cc16eb780ef0cdd019b" } tonks = { git = "https://github.com/feather-rs/tonks", rev = "3fcc5368e6ee072c530762fa2b08d020a542a809" } + +# Concurrency/threading crossbeam = "0.7" +rayon = "1.2" +parking_lot = "0.9" +evmap = "7.1" +thread_local = "1.0" + +# Netorking/IO +tokio = "=0.2.0-alpha.6" +tokio-executor = "=0.2.0-alpha.6" +futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } +bytes = "0.4" +mojang-api = "0.4" + +# Crypto +rsa = "0.1" +rsa-der = "0.2" +# Match RSA git master +num-bigint = { version = "0.4", features = ["rand", "i128", "u64_digit", "prime", "zeroize"], package = "num-bigint-dig" } + +# Hash functions +ahash = "0.2" +fnv = "1.0" +base64 = "0.10" + +# Math and physics +nalgebra-glm = "0.4" +nalgebra = "0.18" +ncollide3d = "0.20" + +# Other data structures +hashbrown = { version = "0.6", features = ["rayon"] } +bitvec = "0.15" +bitflags = "1.2" +heapless = "0.5" +uuid = { version = "0.7", features = ["v4"] } +multimap = "0.7" +smallvec = "0.6" + +# Logging log = "0.4" simple_logger = "1.3" -uuid = { version = "0.7", features = ["v4"] } -derive-new = "0.5" + +# Serialization/deserialization serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" toml = "0.5" -rsa = "0.1" -# Match RSA git master -num-bigint = { version = "0.4", features = ["rand", "i128", "u64_digit", "prime", "zeroize"], package = "num-bigint-dig" } -rsa-der = "0.2" +hematite-nbt = "0.4" + +# RNGs rand = "0.7" rand_xorshift = "0.2" rand-legacy = { path = "../util/rand-legacy" } -bytes = "0.4" -hashbrown = { version = "0.6", features = ["rayon"] } -mojang-api = { git = "https://github.com/caelunshun/mojang-api-rs", rev = "6525e910ad53953fa16028f0fce74b1a19855733" } -multimap = "0.6" -hematite-nbt = "0.4" -rayon = "1.2" + +# Other failure = "0.1" num-derive = "0.3" num-traits = "0.2" -smallvec = "0.6" lazy_static = "1.4" -nalgebra-glm = "0.4" -nalgebra = "0.18" -ncollide3d = "0.20" derive_deref = "1.1" -feather-codegen = { path = "../codegen" } -bitflags = "1.2" -fnv = "1.0" -base64 = "0.10" bumpalo = "2.6" -thread_local = "1.0" -parking_lot = "0.9" -heapless = "0.5" strum = "0.16" simdnoise = "3.1" simdeez = "0.6" -bitvec = "0.15" -tokio = "=0.2.0-alpha.6" -tokio-executor = "=0.2.0-alpha.6" -futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } humantime-serde = "0.1" ctrlc = "3.1" -evmap = "7.1" -ahash = "0.2" [dev-dependencies] -criterion = "0.3.0" +criterion = "0.3" [[bench]] name = "worldgen" diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index 7d6e26b0b..41887c563 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -11,6 +11,12 @@ use mojang_api::ProfileProperty; #[derive(Debug, Clone)] pub struct ProfileProperties(pub Vec); +/// Event triggered when a player joins. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PlayerJoinEvent { + pub player: Entity, +} + /// Tag used to mark a player. /// /// Note that this is a _tag_, not a component. diff --git a/server/src/time.rs b/server/src/time.rs index 450fc47e6..7333c6b77 100644 --- a/server/src/time.rs +++ b/server/src/time.rs @@ -1,12 +1,10 @@ //! Handles world time. -use crate::joinhandler::PlayerJoinEvent; -use crate::network::{send_packet_to_player, NetworkComponent}; -use crate::systems::{TIME_INCREMENT, TIME_SEND}; +use crate::network::Network; +use crate::player::PlayerJoinEvent; use feather_core::level::LevelData; use feather_core::packet::TimeUpdate; -use shrev::EventChannel; -use specs::{DispatcherBuilder, Read, ReadStorage, ReaderId, System, World, Write}; +use tonks::{PreparedQuery, PreparedWorld, Read}; /// The current time of the world. #[derive(Debug, Clone, Copy, PartialEq, Eq, Deref, DerefMut, Default)] @@ -25,112 +23,26 @@ impl Time { } } -/// Initializes systems for this module. -pub fn init_logic(dispatcher: &mut DispatcherBuilder) { - dispatcher.add(TimeIncrementSystem, TIME_INCREMENT, &[]); - dispatcher.add(TimeSendSystem::default(), TIME_SEND, &[]); -} - -/// Initializes the time for the world, given the -/// level file. -pub fn init_time(world: &mut World, level: &LevelData) { - world.insert(Time(level.time as u64)) -} - /// System for incrementing time each tick. -pub struct TimeIncrementSystem; - -impl<'a> System<'a> for TimeIncrementSystem { - type SystemData = Write<'a, Time>; - - fn run(&mut self, mut time: Self::SystemData) { - time.0 += 1; - } -} - -/// System for sending world time to players -/// upon joining. -/// -/// This system listens to `PlayerJoinEvent`s. -#[derive(Default)] -pub struct TimeSendSystem { - reader: Option>, -} - -impl<'a> System<'a> for TimeSendSystem { - type SystemData = ( - ReadStorage<'a, NetworkComponent>, - Read<'a, EventChannel>, - Read<'a, Time>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (networks, join_events, time) = data; - - for event in join_events.read(self.reader.as_mut().unwrap()) { - let network = networks.get(event.player).unwrap(); - - // Send time to player. - let packet = TimeUpdate { - world_age: time.world_age() as i64, - time_of_day: time.time_of_day() as i64, - }; - - send_packet_to_player(network, packet); - } - } - - setup_impl!(reader); +#[system] +pub fn time_increment(time: &mut Time) { + time.0 += 1; } -#[cfg(test)] -mod tests { - use super::*; - use crate::testframework as t; - use feather_core::{network::cast_packet, PacketType}; - use shrev::EventChannel; - use specs::WorldExt; - - #[test] - fn test_time_init() { - let mut level = LevelData::default(); - let time = 29456; - level.time = time as i64; - - let mut world = World::new(); - init_time(&mut world, &level); - - assert_eq!(*world.fetch::

(&self, packet: P) { + pub fn send

(&self, packet: P) + where + P: Packet, + { self.send_boxed(Box::new(packet)); } diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index 74ff0ebd1..652666693 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -33,7 +33,7 @@ fn physics( // Go through entities and update their positions according // to their velocities. - query.par_entities_for_each(world, |(entity, position, velocity, physics)| { + query.par_entities_for_each(world, |(entity, (position, velocity, physics))| { let mut pending_position = position.current + velocity.0; // Check for blocks along path between old position and pending position. diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index 6a8160b0b..34f846467 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -255,7 +255,7 @@ pub fn nearby_entities( .iter() .copied() .filter(|e| { - let epos = query.find_immutable(e, world).unwrap(); + let epos = query.find_immutable(*e, world).unwrap(); if let Some(epos) = epos { let epos = epos.current; (epos.x - pos.x).abs() <= radius.x @@ -265,7 +265,7 @@ pub fn nearby_entities( false } }) - .for_each(|e| result.push(e)); + .for_each(|e| result.push(*e)); } result From f5048d1f4a0792b0c08445a73305689beb174d7d Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Dec 2019 12:57:59 -0700 Subject: [PATCH 047/647] Reduce error count from 49 to 12! --- Cargo.lock | 105 +++++++++++---- codegen/src/lib.rs | 4 + core/src/network/packet/implementation.rs | 149 +++++++++++++++++++++- core/src/network/packet/mod.rs | 3 + server/Cargo.toml | 5 +- server/src/chunk_entities.rs | 35 ++--- server/src/entity/mod.rs | 23 +++- server/src/lazy.rs | 21 +-- server/src/network.rs | 56 ++++++-- server/src/physics/entity.rs | 16 +-- server/src/state.rs | 2 +- 11 files changed, 343 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7f2be3a7a..4b396461c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -431,6 +431,16 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-utils" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "csv" version = "1.1.1" @@ -469,6 +479,26 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dashmap" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dashmap-shard 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dashmap-shard" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "derivative" version = "1.0.3" @@ -535,14 +565,6 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "evmap" -version = "7.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "failure" version = "0.1.5" @@ -673,8 +695,8 @@ dependencies = [ "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dashmap 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "derive_deref 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "evmap 7.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "feather-blocks 0.5.0", "feather-codegen 0.5.0", @@ -689,6 +711,7 @@ dependencies = [ "inventory 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "legion 0.2.1 (git+https://github.com/TomGillen/legion?rev=cfb31727d9f4c8f069c559c0cdf05f56547fd9d4)", + "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mojang-api 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "multimap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -716,7 +739,7 @@ dependencies = [ "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=98a5b3a3a68a49027da04fb8f112cdd1e555f671)", + "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=726498c53d4f4ea4814e9935f2df4412b2c01b35)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -812,7 +835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1000,6 +1023,14 @@ dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hermit-abi" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "http" version = "0.1.18" @@ -1542,9 +1573,10 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "hermit-abi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1598,6 +1630,15 @@ dependencies = [ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parking_lot" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot_core" version = "0.6.2" @@ -1612,6 +1653,19 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parking_lot_core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "paste" version = "0.1.6" @@ -1964,7 +2018,7 @@ dependencies = [ "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2437,7 +2491,7 @@ dependencies = [ "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-fs 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2473,7 +2527,7 @@ dependencies = [ "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-sync 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2528,7 +2582,7 @@ dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2582,7 +2636,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=98a5b3a3a68a49027da04fb8f112cdd1e555f671#98a5b3a3a68a49027da04fb8f112cdd1e555f671" +source = "git+https://github.com/feather-rs/tonks?rev=726498c53d4f4ea4814e9935f2df4412b2c01b35#726498c53d4f4ea4814e9935f2df4412b2c01b35" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2599,13 +2653,13 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=98a5b3a3a68a49027da04fb8f112cdd1e555f671)", + "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=726498c53d4f4ea4814e9935f2df4412b2c01b35)", ] [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=98a5b3a3a68a49027da04fb8f112cdd1e555f671#98a5b3a3a68a49027da04fb8f112cdd1e555f671" +source = "git+https://github.com/feather-rs/tonks?rev=726498c53d4f4ea4814e9935f2df4412b2c01b35#726498c53d4f4ea4814e9935f2df4412b2c01b35" dependencies = [ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3012,10 +3066,13 @@ dependencies = [ "checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" "checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" "checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" "checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" "checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f" +"checksum dashmap 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cb7ebab7705baa489c3f36e494b59bc16edb4fc5b3341921197270a11259d6a" +"checksum dashmap-shard 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c94b718728139bf8d0f822d63e0b65f4ed781562d5c28f6b84d8e1a66938ae70" "checksum derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "942ca430eef7a3806595a6737bc388bf51adb888d3fc0dd1b50f1c170167ee3a" "checksum derive-new 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9" "checksum derive_deref 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11554fdb0aa42363a442e0c4278f51c9621e20c1ce3bac51d79e60646f3b8b8f" @@ -3024,7 +3081,6 @@ dependencies = [ "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum encoding_rs 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)" = "87240518927716f79692c2ed85bfe6e98196d18c6401ec75355760233a7e12e9" -"checksum evmap 7.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4115e31301bb9dafa8ea8549432b14a51e19eecaa9e5c6041f099545e955c225" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" @@ -3058,6 +3114,7 @@ dependencies = [ "checksum heapless 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f339aa7d51777fc0af6aa7cbeb277dfc6e6c029cbdeda48d0fbb92c2337f0e69" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hematite-nbt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "98b407a33bb1715a4cf0276edfe8df52352c55b2a3703c5079adedf398b92932" +"checksum hermit-abi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f629dc602392d3ec14bfc8a09b5e644d7ffd725102b48b81e59f90f2633621d7" "checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" "checksum http-body 0.2.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1f3aef6f3de2bd8585f5b366f3f550b5774500b4764d00cf00f903c95749eec3" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" @@ -3113,14 +3170,16 @@ dependencies = [ "checksum num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" +"checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2f372b2b53ce10fb823a337aaa674e3a7d072b957c6264d0f4ff0bd86e657449" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)" = "2c42dcccb832556b5926bc9ae61e8775f2a61e725ab07ab3d1e7fcf8ae62c3b6" "checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" +"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +"checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" "checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" "checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" @@ -3228,8 +3287,8 @@ dependencies = [ "checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" "checksum tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" -"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=98a5b3a3a68a49027da04fb8f112cdd1e555f671)" = "" -"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=98a5b3a3a68a49027da04fb8f112cdd1e555f671)" = "" +"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=726498c53d4f4ea4814e9935f2df4412b2c01b35)" = "" +"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=726498c53d4f4ea4814e9935f2df4412b2c01b35)" = "" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" "checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index ab6f68331..ed4a27415 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -210,6 +210,10 @@ pub fn derive_packet(_item: TokenStream) -> TokenStream { PacketType::#ident } + fn ty_sized() -> PacketType where Self: Sized { + 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 ed9261cfd..6b127460b 100644 --- a/core/src/network/packet/implementation.rs +++ b/core/src/network/packet/implementation.rs @@ -138,6 +138,13 @@ impl Packet for Handshake { PacketType::Handshake } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::Handshake + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -197,6 +204,13 @@ impl Packet for EncryptionResponse { PacketType::EncryptionResponse } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::EncryptionResponse + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -302,6 +316,13 @@ impl Packet for PluginMessageServerbound { PacketType::PluginMessageServerbound } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::PluginMessageServerbound + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -355,6 +376,13 @@ impl Packet for UseEntity { PacketType::UseEntity } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::UseEntity + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -479,6 +507,13 @@ impl Packet for PlayerDigging { PacketType::PlayerDigging } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::PlayerDigging + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -528,6 +563,13 @@ impl Packet for EntityAction { PacketType::EntityAction } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::EntityAction + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -672,6 +714,13 @@ impl Packet for AnimationServerbound { PacketType::AnimationServerbound } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::AnimationServerbound + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -741,6 +790,13 @@ impl Packet for PlayerBlockPlacement { PacketType::PlayerBlockPlacement } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::PlayerBlockPlacement + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -783,6 +839,13 @@ impl Packet for EncryptionRequest { PacketType::EncryptionRequest } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::EncryptionRequest + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -906,6 +969,13 @@ impl Packet for SpawnPlayer { PacketType::SpawnPlayer } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::SpawnPlayer + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -931,6 +1001,13 @@ impl Packet for AnimationClientbound { PacketType::AnimationClientbound } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::AnimationClientbound + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -960,6 +1037,13 @@ impl Packet for Statistics { PacketType::Statistics } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::Statistics + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -1037,6 +1121,13 @@ impl Packet for BossBar { PacketType::BossBar } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::BossBar + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -1158,6 +1249,13 @@ impl Packet for WindowItems { PacketType::WindowItems } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::WindowItems + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -1203,6 +1301,13 @@ impl Packet for PluginMessageClientbound { PacketType::PluginMessageClientbound } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::PluginMessageClientbound + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -1277,6 +1382,13 @@ impl Packet for Explosion { PacketType::Explosion } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::Explosion + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -1387,6 +1499,13 @@ impl Packet for ChunkData { PacketType::ChunkData } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::ChunkData + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -1509,7 +1628,14 @@ impl Packet for CombatEvent { } fn ty(&self) -> PacketType { - unimplemented!() + PacketType::CombatEvent + } + + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::CombatEvent } fn box_clone(&self) -> Box { @@ -1581,6 +1707,13 @@ impl Packet for PlayerInfo { PacketType::PlayerInfo } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::PlayerInfo + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -1656,6 +1789,13 @@ impl Packet for DestroyEntities { PacketType::DestroyEntities } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::DestroyEntities + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } @@ -1707,6 +1847,13 @@ impl Packet for PacketEntityMetadata { PacketType::EntityMetadata } + fn ty_sized() -> PacketType + where + Self: Sized, + { + PacketType::EntityMetadata + } + fn box_clone(&self) -> Box { box_clone_impl!(self); } diff --git a/core/src/network/packet/mod.rs b/core/src/network/packet/mod.rs index fd045889f..538d5cb30 100644 --- a/core/src/network/packet/mod.rs +++ b/core/src/network/packet/mod.rs @@ -27,6 +27,9 @@ pub trait Packet: AsAny + IntoAny + Send + Sync + Any { fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error>; fn write_to(&self, buf: &mut BytesMut); fn ty(&self) -> PacketType; + fn ty_sized() -> PacketType + where + Self: Sized; /// Returns a clone of this packet in a dynamic box. fn box_clone(&self) -> Box; diff --git a/server/Cargo.toml b/server/Cargo.toml index 753c13985..e9bb4e4e5 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,13 +21,13 @@ feather-codegen = { path = "../codegen" } # Core ECS + systems legion = { git = "https://github.com/TomGillen/legion", rev = "cfb31727d9f4c8f069c559c0cdf05f56547fd9d4" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "98a5b3a3a68a49027da04fb8f112cdd1e555f671", features = ["system-registry"] } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "726498c53d4f4ea4814e9935f2df4412b2c01b35", features = ["system-registry"] } # Concurrency/threading crossbeam = "0.7" rayon = "1.2" parking_lot = "0.9" -evmap = "7.1" +lock_api = "0.3" thread_local = "1.0" # Netorking/IO @@ -61,6 +61,7 @@ heapless = "0.5" uuid = { version = "0.7", features = ["v4"] } multimap = "0.7" smallvec = "0.6" +dashmap = "2.1" # Logging log = "0.4" diff --git a/server/src/chunk_entities.rs b/server/src/chunk_entities.rs index 63382abb8..30b0b1271 100644 --- a/server/src/chunk_entities.rs +++ b/server/src/chunk_entities.rs @@ -1,11 +1,11 @@ -use ahash::ABuildHasher; -use evmap::shallow_copy::CopyValue; -use evmap::{ReadHandle, ReadHandleFactory, WriteHandle}; +use dashmap::DashMap; use feather_core::ChunkPosition; use legion::entity::Entity; use parking_lot::Mutex; use thread_local::ThreadLocal; +static EMPTY_VEC: Vec = Vec::new(); + /// Stores which entities belong to every given chunk. /// /// This data structure can be used to accelerate certain @@ -15,33 +15,26 @@ use thread_local::ThreadLocal; /// to a player. /// /// This structure is internally stored in `State`, using -/// `evmap` for concurrent map access. +/// `dashmap` for concurrent access. /// /// Do note that the information in this structure is not necessarily up to date, /// although a best effort is made to update the data. #[derive(Resource)] -pub struct ChunkEntities { - writer: Mutex, (), ABuildHasher>>, - factory: ReadHandleFactory, (), ABuildHasher>, - readers: ThreadLocal, (), ABuildHasher>>, -} +pub struct ChunkEntities(DashMap>); impl ChunkEntities { pub fn new() -> Self { - let (reader, writer) = evmap::with_hasher((), ABuildHasher::default()); - Self { - writer, - factory: reader.factory(), - readers: ThreadLocal::new(), - } + Self(DashMap::default()) } + /// Returns a slice of entities in the given chunk. - pub fn entities_in_chunk(&self, chunk: ChunkPosition) -> &[CopyValue] { - unimplemented!(); - self.readers - .get_or(|| self.factory.handle()) - .get_and(&chunk, |slice| slice) - .unwrap_or(&[]) + pub fn entities_in_chunk(&self, chunk: ChunkPosition) -> &[Entity] { + todo!("implement chunk entities properly"); + if let Some(vec) = self.0.get(&chunk) { + vec.as_slice() + } else { + &EMPTY_VEC + } } } diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 13e051a49..524f18038 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -7,6 +7,7 @@ use legion::prelude::Entity; use legion::query::{Read, Write}; use parking_lot::Mutex; use rayon::prelude::*; +use std::ops::{Deref, DerefMut}; use tonks::{PreparedWorld, Query}; /// Event triggered when an entity is removed. @@ -23,9 +24,29 @@ pub struct EntityMoveEvent { } /// The velocity of an entity. -#[derive(Default, Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct Velocity(pub glm::DVec3); +impl Default for Velocity { + fn default() -> Self { + Self(glm::vec3(0.0, 0.0, 0.0)) + } +} + +impl Deref for Velocity { + type Target = glm::DVec3; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Velocity { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + /// The display name of the entity. /// /// Note that unnamed entities do not have this component. diff --git a/server/src/lazy.rs b/server/src/lazy.rs index 1b7c8e583..296fd6717 100644 --- a/server/src/lazy.rs +++ b/server/src/lazy.rs @@ -24,13 +24,13 @@ pub struct Lazy { impl Lazy { /// Lazily executes a closure with world access. - pub fn exec(&self, f: impl LazyFn) { + pub fn exec(&self, f: impl FnOnce(&mut World) + Send + 'static) { self.exec_with_scheduler(move |world, _| f(world)); } /// Lazily executes a closure with world and scheduler (resource) /// access. - pub fn exec_with_scheduler(&self, f: impl LazyFnWithScheduler) { + pub fn exec_with_scheduler(&self, f: impl FnOnce(&mut World, &mut Scheduler) + Send + 'static) { self.queue.push(Action::Exec(Box::new(f))); } @@ -66,24 +66,27 @@ pub struct EntityBuilder<'a> { impl<'a> EntityBuilder<'a> { pub fn with_component(mut self, component: C) -> Self { - self.fns.push(move |world, entity| { - world.add_component(entity, component); - }); + self.fns + .push(Box::new(move |world: &mut World, entity: Entity| { + world.add_component(entity, component); + })); self } pub fn with_tag(mut self, tag: T) -> Self { - self.fns.push(move |world, entity| { - world.add_tag(entity, tag); - }); + self.fns + .push(Box::new(move |world: &mut World, entity: Entity| { + world.add_tag(entity, tag); + })); self } pub fn build(self) { + let fns = self.fns; self.lazy.exec(move |world| { let entity = world.insert((), [()].iter().copied())[0]; - for f in self.fns { + for f in fns { f(world, entity); } }) diff --git a/server/src/network.rs b/server/src/network.rs index fc970d195..fdf4490e1 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -17,13 +17,44 @@ use feather_core::{Packet, PacketType}; use futures::channel::mpsc::UnboundedSender; use legion::entity::Entity; use legion::query::Read; +use lock_api::RawMutex; use parking_lot::{MappedMutexGuard, Mutex, MutexGuard}; +use serde::de::value::MapAccessDeserializer; use std::iter; use std::vec::Drain; +use strum::EnumCount; use tonks::{PreparedWorld, Query}; type QueuedPackets = Vec<(Entity, Box)>; +pub struct DrainedPackets<'a, I> { + mutex: &'a parking_lot::RawMutex, + value: I, +} + +impl<'a, I> DrainedPackets<'a, I> { + unsafe fn new(mutex: &'a parking_lot::RawMutex, value: I) -> Self { + Self { mutex, value } + } +} + +impl<'a, I> Iterator for DrainedPackets<'a, I> +where + I: Iterator, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + self.value.next() + } +} + +impl<'a, I> Drop for DrainedPackets<'a, I> { + fn drop(&mut self) { + self.mutex.unlock(); + } +} + /// The packet queue. This type allows systems to poll for /// received packets of a given type. /// @@ -53,14 +84,19 @@ impl PacketQueue { } /// Returns an iterator over packets of a given type. - pub fn received(&self) -> MappedMutexGuard> { - let queue = self.queue[P::ty().ordinal()].lock(); - - MutexGuard::map(queue, |queue| { - queue - .drain(..) - .map(|(entity, packet)| (entity, cast_packet::

(packet))) - }) + pub fn received(&self) -> impl Iterator + '_ { + let mut queue = self.queue[P::ty_sized().ordinal()].lock(); + + // Hack to map to draining iterator. + unsafe { + let raw = MutexGuard::mutex(&queue).raw(); + DrainedPackets::new( + raw, + queue + .drain(..) + .map(|(entity, packet)| (entity, cast_packet::

(packet))), + ) + } } /// Adds a packet to the queue. @@ -105,7 +141,7 @@ impl Network { /// * Pushing received packets to the packet queue. /// * Accepting new clients and creating entities for them. #[system] -pub fn network( +pub fn network_( state: &State, io: &NetworkIoManager, packet_queue: &PacketQueue, @@ -113,7 +149,7 @@ pub fn network( world: &mut PreparedWorld, ) { // For each `Network`, handle any disconnects and received packets. - query.par_entities_for_each(world, |(entity, network): (Entity, Network)| { + query.par_entities_for_each(world, |(entity, network)| { while let Ok(msg) = network.receiver.try_recv() { match msg { ServerToWorkerMessage::NotifyDisconnect(reason) => { diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index 652666693..fec635ee3 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -21,7 +21,7 @@ pub struct EntityPhysicsLandEvent { /// System for updating all entities' positions and velocities /// each tick. #[system] -fn physics( +fn entity_physics( state: &State, query: &mut Query<(Write, Write, Read)>, world: &mut PreparedWorld, @@ -34,14 +34,14 @@ fn physics( // Go through entities and update their positions according // to their velocities. query.par_entities_for_each(world, |(entity, (position, velocity, physics))| { - let mut pending_position = position.current + velocity.0; + let mut pending_position = *position + velocity.0; // Check for blocks along path between old position and pending position. // This prevents entities from flying through blocks when their // velocity is sufficiently high. - let origin = position.current.into(); - let direction = (pending_position - position.current).into(); - let distance_squared = pending_position.distance_squared(position.current); + let origin = (*position).into(); + let direction = (pending_position - *position).into(); + let distance_squared = pending_position.distance_squared(*position); if let Some(impacted) = block_impacted_by_ray(&state, origin, direction, distance_squared) { // Set velocities along correct axis to 0 and then set position @@ -69,7 +69,7 @@ fn physics( // Check for blocks around the bbox and apply offset // to position to stop the bbox from intersecting blocks. let intersect = - blocks_intersecting_bbox(&state, position.current, pending_position, &physics.bbox); + blocks_intersecting_bbox(&state, *position, pending_position, &physics.bbox); intersect.apply_to(&mut pending_position); if intersect.x_affected() { @@ -108,7 +108,7 @@ fn physics( Some(block) => block.is_solid(), None => false, }; - if pending_position.on_ground && !position.current.on_ground { + if pending_position.on_ground && !position.on_ground { land_events.lock().trigger(EntityPhysicsLandEvent { entity, pos: pending_position, @@ -142,6 +142,6 @@ fn physics( } // Set new position. - position.current = pending_position; + *position = pending_position; }); } diff --git a/server/src/state.rs b/server/src/state.rs index 53c69a542..5eb2afa7b 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -36,7 +36,7 @@ impl State { } /// See `Lazy::exec()`. - pub fn exec(&self, f: impl LazyFn) { + pub fn exec(&self, f: impl FnOnce(&mut World) + Send + 'static) { self.lazy.exec(f) } From 798f09b282c7aa5ced7df8f6eebaf24df634199d Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Dec 2019 15:39:18 -0700 Subject: [PATCH 048/647] Fix unused imports; fix 6 more errors' --- Cargo.lock | 12 ++++++------ server/Cargo.toml | 2 +- server/src/chunk_entities.rs | 7 ------- server/src/chunk_logic.rs | 9 +++------ server/src/entity/mod.rs | 11 ++++++----- server/src/lib.rs | 12 +++--------- server/src/network.rs | 6 +----- server/src/physics/math.rs | 11 ++++------- server/src/state.rs | 2 +- server/src/time.rs | 5 ++--- server/src/view.rs | 8 +++++--- 11 files changed, 32 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b396461c..743e495e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -739,7 +739,7 @@ dependencies = [ "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=726498c53d4f4ea4814e9935f2df4412b2c01b35)", + "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=066ec3001870596ee45524549985610b1a11f464)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2636,7 +2636,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=726498c53d4f4ea4814e9935f2df4412b2c01b35#726498c53d4f4ea4814e9935f2df4412b2c01b35" +source = "git+https://github.com/feather-rs/tonks?rev=066ec3001870596ee45524549985610b1a11f464#066ec3001870596ee45524549985610b1a11f464" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2653,13 +2653,13 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=726498c53d4f4ea4814e9935f2df4412b2c01b35)", + "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=066ec3001870596ee45524549985610b1a11f464)", ] [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=726498c53d4f4ea4814e9935f2df4412b2c01b35#726498c53d4f4ea4814e9935f2df4412b2c01b35" +source = "git+https://github.com/feather-rs/tonks?rev=066ec3001870596ee45524549985610b1a11f464#066ec3001870596ee45524549985610b1a11f464" dependencies = [ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3287,8 +3287,8 @@ dependencies = [ "checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" "checksum tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" -"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=726498c53d4f4ea4814e9935f2df4412b2c01b35)" = "" -"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=726498c53d4f4ea4814e9935f2df4412b2c01b35)" = "" +"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=066ec3001870596ee45524549985610b1a11f464)" = "" +"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=066ec3001870596ee45524549985610b1a11f464)" = "" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" "checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" diff --git a/server/Cargo.toml b/server/Cargo.toml index e9bb4e4e5..b3587c75f 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,7 +21,7 @@ feather-codegen = { path = "../codegen" } # Core ECS + systems legion = { git = "https://github.com/TomGillen/legion", rev = "cfb31727d9f4c8f069c559c0cdf05f56547fd9d4" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "726498c53d4f4ea4814e9935f2df4412b2c01b35", features = ["system-registry"] } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "066ec3001870596ee45524549985610b1a11f464", features = ["system-registry"] } # Concurrency/threading crossbeam = "0.7" diff --git a/server/src/chunk_entities.rs b/server/src/chunk_entities.rs index 30b0b1271..3cf9399dc 100644 --- a/server/src/chunk_entities.rs +++ b/server/src/chunk_entities.rs @@ -1,8 +1,6 @@ use dashmap::DashMap; use feather_core::ChunkPosition; use legion::entity::Entity; -use parking_lot::Mutex; -use thread_local::ThreadLocal; static EMPTY_VEC: Vec = Vec::new(); @@ -30,11 +28,6 @@ impl ChunkEntities { /// Returns a slice of entities in the given chunk. pub fn entities_in_chunk(&self, chunk: ChunkPosition) -> &[Entity] { todo!("implement chunk entities properly"); - if let Some(vec) = self.0.get(&chunk) { - vec.as_slice() - } else { - &EMPTY_VEC - } } } diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index aec7f344d..e28d8acba 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -5,14 +5,12 @@ use crossbeam::channel::{Receiver, Sender}; use std::sync::atomic::{AtomicU32, Ordering}; -use feather_core::world::{ChunkMap, ChunkPosition}; +use feather_core::world::ChunkPosition; use rayon::prelude::*; -use crate::config::Config; use crate::entity::EntityDeleteEvent; use crate::state::State; -use crate::worldgen::WorldGenerator; use crate::{chunk_worker, current_time_in_millis, TickCount, TPS}; use feather_core::entity::EntityData; use feather_core::Chunk; @@ -21,7 +19,6 @@ use legion::entity::Entity; use legion::query::Read; use multimap::MultiMap; use std::collections::VecDeque; -use std::path::Path; use std::sync::Arc; use tonks::{PreparedWorld, Query, Trigger}; @@ -272,13 +269,13 @@ impl ChunkHolder { #[event_handler] fn chunk_holder_remove( event: &EntityDeleteEvent, - query: &mut Query>, + _query: &mut Query>, world: &mut PreparedWorld, holders: &mut ChunkHolders, release_events: &mut Trigger, ) { // If entity had chunk holds, remove them all - if let Ok(holder_comp) = query.find(event.entity, world) { + if let Some(holder_comp) = world.get_component::(event.entity) { debug!("Removing chunk holds for entity {:?}", event.entity); holder_comp.holds.iter().for_each(|chunk| { holders.remove_holder(*chunk, event.entity, release_events); diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 524f18038..4c1240c29 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -2,11 +2,9 @@ use crate::lazy::EntityBuilder; use crate::state::State; -use feather_core::{ChunkPosition, Position}; +use feather_core::Position; use legion::prelude::Entity; use legion::query::{Read, Write}; -use parking_lot::Mutex; -use rayon::prelude::*; use std::ops::{Deref, DerefMut}; use tonks::{PreparedWorld, Query}; @@ -62,11 +60,14 @@ pub struct PreviousPosition(pub Position); #[event_handler] pub fn position_reset( events: &[EntityMoveEvent], - query: &mut Query<(Read, Write)>, + _query: &mut Query<(Read, Write)>, world: &mut PreparedWorld, ) { events.iter().for_each(|event| { - let (pos, mut prev_pos) = query.find(event.entity).unwrap(); + let pos = *world.get_component::(event.entity).unwrap(); + let prev_pos = world + .get_component_mut::(event.entity) + .unwrap(); prev_pos.0 = pos; }); diff --git a/server/src/lib.rs b/server/src/lib.rs index 60f9402ea..82f0054cb 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -100,33 +100,27 @@ extern crate serde_json; #[macro_use] extern crate failure; #[macro_use] -extern crate num_derive; -#[macro_use] extern crate smallvec; #[macro_use] extern crate lazy_static; #[macro_use] extern crate derive_deref; #[macro_use] -extern crate feather_codegen; +extern crate feather_core; #[macro_use] extern crate bitflags; #[macro_use] -extern crate feather_core; -#[macro_use] extern crate tonks; extern crate nalgebra_glm as glm; use crossbeam::Receiver; use std::alloc::System; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::atomic::AtomicUsize; use std::sync::Arc; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use feather_core::network::packet::implementation::DisconnectPlay; - -use crate::chunk_logic::{ChunkHolders, ChunkWorkerHandle}; +use crate::chunk_logic::ChunkWorkerHandle; use crate::config::Config; use crate::state::State; use crate::worldgen::{ diff --git a/server/src/network.rs b/server/src/network.rs index fdf4490e1..71b3fe53d 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -7,9 +7,7 @@ //! received of a given type. use crate::io::{ListenerToServerMessage, NetworkIoManager, ServerToWorkerMessage}; -use crate::lazy::Lazy; use crate::player; -use crate::player::Player; use crate::state::State; use crossbeam::Receiver; use feather_core::network::cast_packet; @@ -18,10 +16,8 @@ use futures::channel::mpsc::UnboundedSender; use legion::entity::Entity; use legion::query::Read; use lock_api::RawMutex; -use parking_lot::{MappedMutexGuard, Mutex, MutexGuard}; -use serde::de::value::MapAccessDeserializer; +use parking_lot::{Mutex, MutexGuard}; use std::iter; -use std::vec::Drain; use strum::EnumCount; use tonks::{PreparedWorld, Query}; diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index 34f846467..bae308495 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -6,12 +6,11 @@ use crate::physics::block_bboxes::bbox_for_block; use crate::physics::AABBExt; use crate::state::State; use feather_blocks::Block; -use feather_core::world::{BlockPosition, ChunkMap, Position}; +use feather_core::world::{BlockPosition, Position}; use feather_core::{BlockExt, ChunkPosition}; use glm::{vec3, DVec3, Vec3}; use heapless::consts::*; use legion::entity::Entity; -use legion::query::Read; use nalgebra::{Isometry3, Point3}; use ncollide3d::bounding_volume::AABB; use ncollide3d::query; @@ -20,7 +19,7 @@ use ncollide3d::shape::{Compound, Cuboid, ShapeHandle}; use smallvec::SmallVec; use std::cmp::Ordering; use std::f64::INFINITY; -use tonks::{PreparedWorld, Query}; +use tonks::PreparedWorld; // TODO is a bitflag really the most // idiomatic way to do this? @@ -238,7 +237,6 @@ pub fn block_impacted_by_ray( /// Panics if either coordinate of the radius is negative. pub fn nearby_entities( chunk_entities: &ChunkEntities, - query: &Query>, world: &PreparedWorld, pos: Position, radius: DVec3, @@ -255,9 +253,8 @@ pub fn nearby_entities( .iter() .copied() .filter(|e| { - let epos = query.find_immutable(*e, world).unwrap(); + let epos = world.get_component::(*e); if let Some(epos) = epos { - let epos = epos.current; (epos.x - pos.x).abs() <= radius.x && (epos.y - pos.y).abs() <= radius.y && (epos.z - pos.z).abs() <= radius.z @@ -265,7 +262,7 @@ pub fn nearby_entities( false } }) - .for_each(|e| result.push(*e)); + .for_each(|e| result.push(e)); } result diff --git a/server/src/state.rs b/server/src/state.rs index 5eb2afa7b..ae74abe54 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -1,5 +1,5 @@ use crate::config::Config; -use crate::lazy::{EntityBuilder, Lazy, LazyFn}; +use crate::lazy::{EntityBuilder, Lazy}; use feather_blocks::Block; use feather_core::world::ChunkMap; use feather_core::{BlockPosition, Chunk, ChunkPosition}; diff --git a/server/src/time.rs b/server/src/time.rs index 5190d76e5..de0d503a8 100644 --- a/server/src/time.rs +++ b/server/src/time.rs @@ -2,7 +2,6 @@ use crate::network::Network; use crate::player::PlayerJoinEvent; -use feather_core::level::LevelData; use feather_core::packet::TimeUpdate; use legion::query::Read; use tonks::{PreparedWorld, Query}; @@ -35,10 +34,10 @@ pub fn time_increment(time: &mut Time) { pub fn time_send( time: &Time, event: &PlayerJoinEvent, - query: &mut Query>, + _query: &mut Query>, world: &mut PreparedWorld, ) { - let network = query.find(event.player, &mut world).unwrap(); + let network = world.get_component::(event.player).unwrap(); // Send time to player. let packet = TimeUpdate { diff --git a/server/src/view.rs b/server/src/view.rs index c62161983..85fe39a6b 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -18,11 +18,9 @@ //! This includes systems to load/unload chunks and send entities. use crate::entity::{EntityMoveEvent, PreviousPosition}; -use crate::player::Player; use feather_core::{ChunkPosition, Position}; use legion::entity::Entity; use legion::query::Read; -use rayon::prelude::*; use tonks::{PreparedWorld, Query, Trigger}; /// Event triggered when a player's view is updated, i.e. when they @@ -46,7 +44,11 @@ fn view_update( trigger: &mut Trigger, ) { events.iter().for_each(|event| { - let (pos, prev_pos) = query.find_immutable(event.entity, &world).unwrap(); + let pos = *world.get_component::(event.entity).unwrap(); + let prev_pos = world + .get_component::(event.entity) + .unwrap() + .0; if pos.chunk_pos() != prev_pos.chunk_pos() { // New chunk: trigger view update. From e3d405f34973b8cbef2ab855828b927c16995194 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Dec 2019 16:05:11 -0700 Subject: [PATCH 049/647] Reimplement player data loading --- core/src/save/player_data.rs | 14 +++++++++----- server/src/io/mod.rs | 4 ++++ server/src/io/worker.rs | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/core/src/save/player_data.rs b/core/src/save/player_data.rs index ca331ce2c..30bf8d81c 100644 --- a/core/src/save/player_data.rs +++ b/core/src/save/player_data.rs @@ -10,6 +10,8 @@ use feather_items::Item; use std::fs; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; +use tokio::io::AsyncReadExt; +use tokio::prelude::AsyncRead; use uuid::Uuid; /// Represents the contents of a player data file. @@ -92,14 +94,16 @@ impl InventorySlot { } } -fn load_from_file(reader: R) -> Result { - nbt::from_gzip_reader::<_, PlayerData>(reader) +async fn load_from_file(mut reader: R) -> Result { + let mut buf = vec![]; + reader.read(&mut buf).await?; + nbt::from_gzip_reader(buf.as_slice()) } -pub fn load_player_data(world_dir: &Path, uuid: Uuid) -> Result { +pub async fn load_player_data(world_dir: &Path, uuid: Uuid) -> Result { let file_path = file_path(world_dir, uuid); - let file = File::open(file_path)?; - let data = load_from_file(file)?; + let file = tokio::fs::File::open(file_path).await?; + let data = load_from_file(file).await?; Ok(data) } diff --git a/server/src/io/mod.rs b/server/src/io/mod.rs index db57a4320..291d49ab3 100644 --- a/server/src/io/mod.rs +++ b/server/src/io/mod.rs @@ -1,6 +1,8 @@ use crate::config::Config; use crate::PlayerCount; use feather_core::network::packet::Packet; +use feather_core::player_data::PlayerData; +use feather_core::Position; use std::net::SocketAddr; use std::sync::Arc; use uuid::Uuid; @@ -28,6 +30,8 @@ pub struct NewClientInfo { pub username: String, pub profile: Vec, pub uuid: Uuid, + pub data: PlayerData, + pub position: Position, pub sender: futures::channel::mpsc::UnboundedSender, pub receiver: crossbeam::Receiver, diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index d9a966f04..5963f2614 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -12,14 +12,23 @@ use crate::io::{ListenerToServerMessage, NewClientInfo, ServerToWorkerMessage}; use crate::PlayerCount; use feather_core::network::codec::MinecraftCodec; use feather_core::network::packet::PacketDirection; +use feather_core::player_data::PlayerData; use futures::{select, StreamExt}; use futures::{FutureExt, SinkExt}; use std::net::SocketAddr; +use std::path::Path; use std::sync::Arc; use std::time::Duration; use tokio::codec::Framed; use tokio::net::TcpStream; use tokio::timer::Timeout; +use uuid::Uuid; + +#[derive(Debug, Fail)] +pub enum Error { + #[fail(display = "failed to read player data")] + PlayerData, +} /// Runs a worker task for the given client. pub async fn run_worker( @@ -119,6 +128,7 @@ async fn _run_worker( } Action::SetStage(stage) => framed.codec_mut().set_stage(stage), Action::JoinGame(res) => { + let data = load_player_data(&config, res.uuid).await?; let info = NewClientInfo { ip, username: res.username, @@ -126,6 +136,11 @@ async fn _run_worker( uuid: res.uuid, sender: tx_server_to_worker.clone(), receiver: rx_worker_to_server.take().unwrap(), + position: data + .entity + .read_position() + .ok_or_else(|| Error::PlayerData)?, + data, }; global_sender .send(ListenerToServerMessage::NewClient(info))?; @@ -144,3 +159,7 @@ async fn _run_worker( } } } + +async fn load_player_data(config: &Config, uuid: Uuid) -> Result { + feather_core::player_data::load_player_data(Path::new(&config.world.name), uuid).await +} From d751bc4fda3173f5a6c60c13308cd9eb1386026f Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Dec 2019 17:59:46 -0700 Subject: [PATCH 050/647] Fix ALL errors and warnings. I'm scared to run this... --- Cargo.lock | 12 +++++------ core/src/save/player_data.rs | 2 +- server/Cargo.toml | 2 +- server/src/chunk_entities.rs | 4 +--- server/src/entity/mod.rs | 2 +- server/src/io/worker.rs | 6 +++++- server/src/lib.rs | 9 +++++--- server/src/network.rs | 42 ++++++++++++++++++++++++++++++------ server/src/physics/entity.rs | 2 +- server/src/shutdown.rs | 6 +++--- server/src/state.rs | 8 +++---- server/src/time.rs | 2 +- server/src/view.rs | 2 +- 13 files changed, 66 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 743e495e7..64d7e2cd9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -739,7 +739,7 @@ dependencies = [ "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=066ec3001870596ee45524549985610b1a11f464)", + "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=a090d8631232751f97f07d4ee7e15d1028afb658)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2636,7 +2636,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=066ec3001870596ee45524549985610b1a11f464#066ec3001870596ee45524549985610b1a11f464" +source = "git+https://github.com/feather-rs/tonks?rev=a090d8631232751f97f07d4ee7e15d1028afb658#a090d8631232751f97f07d4ee7e15d1028afb658" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2653,13 +2653,13 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=066ec3001870596ee45524549985610b1a11f464)", + "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=a090d8631232751f97f07d4ee7e15d1028afb658)", ] [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=066ec3001870596ee45524549985610b1a11f464#066ec3001870596ee45524549985610b1a11f464" +source = "git+https://github.com/feather-rs/tonks?rev=a090d8631232751f97f07d4ee7e15d1028afb658#a090d8631232751f97f07d4ee7e15d1028afb658" dependencies = [ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3287,8 +3287,8 @@ dependencies = [ "checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" "checksum tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" -"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=066ec3001870596ee45524549985610b1a11f464)" = "" -"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=066ec3001870596ee45524549985610b1a11f464)" = "" +"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=a090d8631232751f97f07d4ee7e15d1028afb658)" = "" +"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=a090d8631232751f97f07d4ee7e15d1028afb658)" = "" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" "checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" diff --git a/core/src/save/player_data.rs b/core/src/save/player_data.rs index 30bf8d81c..d9cae5f0b 100644 --- a/core/src/save/player_data.rs +++ b/core/src/save/player_data.rs @@ -8,7 +8,7 @@ use crate::inventory::{ use crate::ItemStack; use feather_items::Item; use std::fs; -use std::io::{Read, Write}; +use std::io::Write; use std::path::{Path, PathBuf}; use tokio::io::AsyncReadExt; use tokio::prelude::AsyncRead; diff --git a/server/Cargo.toml b/server/Cargo.toml index b3587c75f..56a597c36 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,7 +21,7 @@ feather-codegen = { path = "../codegen" } # Core ECS + systems legion = { git = "https://github.com/TomGillen/legion", rev = "cfb31727d9f4c8f069c559c0cdf05f56547fd9d4" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "066ec3001870596ee45524549985610b1a11f464", features = ["system-registry"] } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "a090d8631232751f97f07d4ee7e15d1028afb658", features = ["system-registry"] } # Concurrency/threading crossbeam = "0.7" diff --git a/server/src/chunk_entities.rs b/server/src/chunk_entities.rs index 3cf9399dc..8edd8831c 100644 --- a/server/src/chunk_entities.rs +++ b/server/src/chunk_entities.rs @@ -2,8 +2,6 @@ use dashmap::DashMap; use feather_core::ChunkPosition; use legion::entity::Entity; -static EMPTY_VEC: Vec = Vec::new(); - /// Stores which entities belong to every given chunk. /// /// This data structure can be used to accelerate certain @@ -26,7 +24,7 @@ impl ChunkEntities { } /// Returns a slice of entities in the given chunk. - pub fn entities_in_chunk(&self, chunk: ChunkPosition) -> &[Entity] { + pub fn entities_in_chunk(&self, _chunk: ChunkPosition) -> &[Entity] { todo!("implement chunk entities properly"); } } diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 4c1240c29..124c814d2 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -65,7 +65,7 @@ pub fn position_reset( ) { events.iter().for_each(|event| { let pos = *world.get_component::(event.entity).unwrap(); - let prev_pos = world + let mut prev_pos = world .get_component_mut::(event.entity) .unwrap(); diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index 5963f2614..8978cd0bc 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -75,7 +75,11 @@ async fn _run_worker( let mut framed = Framed::new(stream, codec); - let mut initial_handler = Some(InitialHandler::new(config, player_count, server_icon)); + let mut initial_handler = Some(InitialHandler::new( + Arc::clone(&config), + player_count, + server_icon, + )); let (tx_server_to_worker, mut rx_server_to_worker) = futures::channel::mpsc::unbounded(); let mut rx_worker_to_server = Some(rx_worker_to_server); diff --git a/server/src/lib.rs b/server/src/lib.rs index 82f0054cb..f892fa705 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -122,6 +122,7 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use crate::chunk_logic::ChunkWorkerHandle; use crate::config::Config; +use crate::io::NetworkIoManager; use crate::state::State; use crate::worldgen::{ ComposableGenerator, EmptyWorldGenerator, SuperflatWorldGenerator, WorldGenerator, @@ -209,9 +210,9 @@ pub fn main() { exit(1) }); - let chunk_worker_handle = init_chunk_worker(&config, world_dir, &level); + let chunk_worker_handle = init_chunk_worker(world_dir, &level); - let mut scheduler = init_scheduler(Arc::clone(&config), chunk_worker_handle, level); + let mut scheduler = init_scheduler(Arc::clone(&config), chunk_worker_handle, level, io_manager); let mut world = World::new(); // Channel used by the shutdown handler to notify the server thread. @@ -280,6 +281,7 @@ fn init_scheduler( config: Arc, chunk_worker_handle: ChunkWorkerHandle, level: LevelData, + io_manager: NetworkIoManager, ) -> Scheduler { // Insert resources which don't have a `Default` impl. let mut resources = Resources::new(); @@ -287,12 +289,13 @@ fn init_scheduler( resources.insert(State::new(config, chunk_map)); resources.insert(chunk_worker_handle); resources.insert(level); + resources.insert(io_manager); tonks::build_scheduler().build(Resources::default()) } /// Initializes the chunk worker. -fn init_chunk_worker(config: &Config, world_dir: &Path, level: &LevelData) -> ChunkWorkerHandle { +fn init_chunk_worker(world_dir: &Path, level: &LevelData) -> ChunkWorkerHandle { let generator: Arc = match level.generator_type() { LevelGeneratorType::Flat => Arc::new(SuperflatWorldGenerator { options: level.clone().generator_options.unwrap_or_default(), diff --git a/server/src/network.rs b/server/src/network.rs index 71b3fe53d..b2d9dd7fd 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -23,6 +23,26 @@ use tonks::{PreparedWorld, Query}; type QueuedPackets = Vec<(Entity, Box)>; +struct UnsafeDrain { + ptr: *const T, + len: usize, + pos: usize, +} + +impl Iterator for UnsafeDrain { + type Item = T; + + fn next(&mut self) -> Option { + if self.pos == self.len { + return None; + } + + let value = unsafe { std::ptr::read(self.ptr.offset(self.pos as isize)) }; + self.pos += 1; + Some(value) + } +} + pub struct DrainedPackets<'a, I> { mutex: &'a parking_lot::RawMutex, value: I, @@ -86,12 +106,20 @@ impl PacketQueue { // Hack to map to draining iterator. unsafe { let raw = MutexGuard::mutex(&queue).raw(); - DrainedPackets::new( - raw, - queue - .drain(..) - .map(|(entity, packet)| (entity, cast_packet::

(packet))), - ) + + let drain = UnsafeDrain { + ptr: queue.as_ptr(), + len: queue.len(), + pos: 0, + }; + + // Safety: the vector cannot be accessed as long as the returned `UnsafeDrain` + // has not been dropped, since the mutex is acquired. + queue.set_len(0); + + let iter = drain.map(|(entity, packet)| (entity, cast_packet::

(packet))); + + DrainedPackets::new(raw, iter) } } @@ -148,7 +176,7 @@ pub fn network_( query.par_entities_for_each(world, |(entity, network)| { while let Ok(msg) = network.receiver.try_recv() { match msg { - ServerToWorkerMessage::NotifyDisconnect(reason) => { + ServerToWorkerMessage::NotifyDisconnect(_) => { state.exec(move |world| { debug_assert!(world.delete(entity), "player already deleted"); }); diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index fec635ee3..4a372e4d6 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -33,7 +33,7 @@ fn entity_physics( // Go through entities and update their positions according // to their velocities. - query.par_entities_for_each(world, |(entity, (position, velocity, physics))| { + query.par_entities_for_each(world, |(entity, (mut position, mut velocity, physics))| { let mut pending_position = *position + velocity.0; // Check for blocks along path between old position and pending position. diff --git a/server/src/shutdown.rs b/server/src/shutdown.rs index 56da66895..53ba1f5e2 100644 --- a/server/src/shutdown.rs +++ b/server/src/shutdown.rs @@ -9,14 +9,14 @@ pub fn init(tx: Sender<()>) { .unwrap(); } -pub fn save_chunks(world: &mut World) { +pub fn save_chunks(_world: &mut World) { unimplemented!() } -pub fn save_level(world: &World) { +pub fn save_level(_world: &World) { unimplemented!() } -pub fn save_player_data(world: &World) { +pub fn save_player_data(_world: &World) { unimplemented!() } diff --git a/server/src/state.rs b/server/src/state.rs index ae74abe54..264b4fa06 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -72,10 +72,10 @@ impl State { /// Lazily inserts the given chunk into the chunk map. pub fn lazy_insert_chunk(&self, chunk: Chunk) { - self.lazy.exec_with_scheduler(move |_, scheduler| { + self.lazy.exec_with_scheduler(move |_, scheduler| unsafe { scheduler .resources() - .get_mut::() + .get_mut_unchecked::(tonks::resource_id_for::()) .chunk_map .insert(chunk); }); @@ -84,10 +84,10 @@ impl State { /// Lazily removes the given chunk from the chunk map. pub fn lazy_remove_chunk(&self, pos: ChunkPosition) { self.lazy - .exec_with_scheduler(move |_: &mut World, scheduler: &mut Scheduler| { + .exec_with_scheduler(move |_: &mut World, scheduler: &mut Scheduler| unsafe { scheduler .resources() - .get_mut::() + .get_mut_unchecked::(tonks::resource_id_for::()) .chunk_map .remove(pos); }); diff --git a/server/src/time.rs b/server/src/time.rs index de0d503a8..8b591271c 100644 --- a/server/src/time.rs +++ b/server/src/time.rs @@ -32,8 +32,8 @@ pub fn time_increment(time: &mut Time) { /// Event handler for sending world time to players. #[event_handler] pub fn time_send( - time: &Time, event: &PlayerJoinEvent, + time: &Time, _query: &mut Query>, world: &mut PreparedWorld, ) { diff --git a/server/src/view.rs b/server/src/view.rs index 85fe39a6b..a45b37dd0 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -39,7 +39,7 @@ pub struct ViewUpdateEvent { #[event_handler] fn view_update( events: &[EntityMoveEvent], - query: &mut Query<(Read, Read)>, + _query: &mut Query<(Read, Read)>, world: &mut PreparedWorld, trigger: &mut Trigger, ) { From de0f3ffb94f74af09a27d86c680c3866a99dd6e2 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Dec 2019 18:52:57 -0700 Subject: [PATCH 051/647] Get server to start without panic --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- server/Cargo.toml | 3 ++- server/src/io/mod.rs | 6 ------ server/src/lib.rs | 8 +++++++- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 64d7e2cd9..c2c4cde1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -739,7 +739,7 @@ dependencies = [ "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=a090d8631232751f97f07d4ee7e15d1028afb658)", + "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=226d04de294008a1f615bae40f4d19a890583f0b)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2636,7 +2636,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=a090d8631232751f97f07d4ee7e15d1028afb658#a090d8631232751f97f07d4ee7e15d1028afb658" +source = "git+https://github.com/feather-rs/tonks?rev=226d04de294008a1f615bae40f4d19a890583f0b#226d04de294008a1f615bae40f4d19a890583f0b" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2653,13 +2653,13 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=a090d8631232751f97f07d4ee7e15d1028afb658)", + "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=226d04de294008a1f615bae40f4d19a890583f0b)", ] [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=a090d8631232751f97f07d4ee7e15d1028afb658#a090d8631232751f97f07d4ee7e15d1028afb658" +source = "git+https://github.com/feather-rs/tonks?rev=226d04de294008a1f615bae40f4d19a890583f0b#226d04de294008a1f615bae40f4d19a890583f0b" dependencies = [ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3287,8 +3287,8 @@ dependencies = [ "checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" "checksum tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" -"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=a090d8631232751f97f07d4ee7e15d1028afb658)" = "" -"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=a090d8631232751f97f07d4ee7e15d1028afb658)" = "" +"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=226d04de294008a1f615bae40f4d19a890583f0b)" = "" +"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=226d04de294008a1f615bae40f4d19a890583f0b)" = "" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" "checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" diff --git a/Cargo.toml b/Cargo.toml index 4c9bd66dd..5fa8c4a44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,4 @@ members = [ "codegen", "generator", "util/rand-legacy", -] \ No newline at end of file +] diff --git a/server/Cargo.toml b/server/Cargo.toml index 56a597c36..5760f6455 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,7 +21,8 @@ feather-codegen = { path = "../codegen" } # Core ECS + systems legion = { git = "https://github.com/TomGillen/legion", rev = "cfb31727d9f4c8f069c559c0cdf05f56547fd9d4" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "a090d8631232751f97f07d4ee7e15d1028afb658", features = ["system-registry"] } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "226d04de294008a1f615bae40f4d19a890583f0b", features = ["system-registry"] } +# tonks = { path = "../../../dev/tonks", features = ["system-registry"] } # Concurrency/threading crossbeam = "0.7" diff --git a/server/src/io/mod.rs b/server/src/io/mod.rs index 291d49ab3..fd506d6d8 100644 --- a/server/src/io/mod.rs +++ b/server/src/io/mod.rs @@ -72,12 +72,6 @@ impl NetworkIoManager { } } -impl Default for NetworkIoManager { - fn default() -> Self { - panic!("Don't try this"); - } -} - /// Initializes certain static variables. pub fn init() { lazy_static::initialize(&initial_handler::RSA_KEY); diff --git a/server/src/lib.rs b/server/src/lib.rs index f892fa705..6f5b0ebcb 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -170,6 +170,12 @@ pub struct PlayerCount(AtomicUsize); #[derive(Default, Debug, Resource)] pub struct TickCount(u64); +/// System to increment tick count each tick. +#[system] +fn tick_count_increment(tick: &mut TickCount) { + tick.0 += 1; +} + pub fn main() { let config = Arc::new(load_config()); init_log(&config); @@ -291,7 +297,7 @@ fn init_scheduler( resources.insert(level); resources.insert(io_manager); - tonks::build_scheduler().build(Resources::default()) + tonks::build_scheduler().build(resources) } /// Initializes the chunk worker. From 74bb4f07e5bd93f6bc4590b43d4f1b097b148929 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 21 Dec 2019 12:55:31 -0700 Subject: [PATCH 052/647] Work on getting chunk sending + view updates to work --- .gitignore | 3 +- Cargo.lock | 8 +- core/src/world/mod.rs | 11 ++ server/Cargo.toml | 4 +- server/src/chunk_logic.rs | 79 +++++++++++---- server/src/io/worker.rs | 3 +- server/src/lazy.rs | 31 ++++-- server/src/lib.rs | 6 ++ server/src/network.rs | 1 + server/src/player/mod.rs | 5 + server/src/state.rs | 5 + server/src/view.rs | 207 ++++++++++++++++++++++++++++++++++++-- 12 files changed, 313 insertions(+), 50 deletions(-) diff --git a/.gitignore b/.gitignore index c132a9511..85d7f7da6 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ **/*.rs.bk **/.idea feather.toml -massif.out* \ No newline at end of file +massif.out* +.cargo diff --git a/Cargo.lock b/Cargo.lock index c2c4cde1f..5c2c76dea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -739,7 +739,7 @@ dependencies = [ "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=226d04de294008a1f615bae40f4d19a890583f0b)", + "tonks 0.1.0", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2636,7 +2636,6 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=226d04de294008a1f615bae40f4d19a890583f0b#226d04de294008a1f615bae40f4d19a890583f0b" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2653,13 +2652,12 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=226d04de294008a1f615bae40f4d19a890583f0b)", + "tonks-macros 0.1.0", ] [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=226d04de294008a1f615bae40f4d19a890583f0b#226d04de294008a1f615bae40f4d19a890583f0b" dependencies = [ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3287,8 +3285,6 @@ dependencies = [ "checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" "checksum tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" -"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=226d04de294008a1f615bae40f4d19a890583f0b)" = "" -"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=226d04de294008a1f615bae40f4d19a890583f0b)" = "" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" "checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" diff --git a/core/src/world/mod.rs b/core/src/world/mod.rs index a13c125ec..2192e447d 100644 --- a/core/src/world/mod.rs +++ b/core/src/world/mod.rs @@ -222,6 +222,17 @@ impl Display for ChunkPosition { } } +impl Add for ChunkPosition { + type Output = ChunkPosition; + + fn add(self, rhs: ChunkPosition) -> Self::Output { + ChunkPosition { + x: self.x + rhs.x, + z: self.z + rhs.z, + } + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Hash32, Default, new)] pub struct BlockPosition { pub x: i32, diff --git a/server/Cargo.toml b/server/Cargo.toml index 5760f6455..42dc10294 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,8 +21,8 @@ feather-codegen = { path = "../codegen" } # Core ECS + systems legion = { git = "https://github.com/TomGillen/legion", rev = "cfb31727d9f4c8f069c559c0cdf05f56547fd9d4" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "226d04de294008a1f615bae40f4d19a890583f0b", features = ["system-registry"] } -# tonks = { path = "../../../dev/tonks", features = ["system-registry"] } +# tonks = { git = "https://github.com/feather-rs/tonks", rev = "226d04de294008a1f615bae40f4d19a890583f0b", features = ["system-registry"] } +tonks = { path = "../../../dev/tonks", features = ["system-registry"] } # Concurrency/threading crossbeam = "0.7" diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index e28d8acba..2c4ac1115 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -73,28 +73,6 @@ fn chunk_load_system( } } -/// Asynchronously loads the chunk at the given position. -/// At some point in time after this function is called, -/// the chunk will appear in the chunk map. -/// -/// In the event that the requested chunk does not exist -/// in the world save, it will be generated asynchronously. -pub fn load_chunk(handle: &ChunkWorkerHandle, pos: ChunkPosition) { - // Send request to chunk worker thread - handle - .sender - .send(chunk_worker::Request::LoadChunk(pos)) - .unwrap(); -} - -/// Asynchronously saves the chunk at the given position. -pub fn save_chunk(handle: &ChunkWorkerHandle, chunk: Arc, entities: Vec) { - handle - .sender - .send(chunk_worker::Request::SaveChunk(chunk, entities)) - .unwrap(); -} - /// The chunk holder map contains a mapping /// of chunk positions to any number of entities, called "holders." /// When a chunk position has no holders, it will be queued @@ -321,3 +299,60 @@ fn chunk_optimize(state: &State, tick_count: &TickCount) { elapsed as f64 / f64::from(count.load(Ordering::Relaxed)) ); } + +/// Adds a hold for a chunk for the given entity. +pub fn hold_chunk( + entity: Entity, + holder: &mut ChunkHolder, + holders: &mut ChunkHolders, + chunk: ChunkPosition, +) { + holder.holds.insert(chunk); + holders.inner.insert(chunk, entity); +} + +/// Releases a hold for a chunk for the given entity. +pub fn release_chunk( + entity: Entity, + holder: &mut ChunkHolder, + holders: &mut ChunkHolders, + chunk: ChunkPosition, + trigger: &mut Trigger, +) { + holder.holds.remove(&chunk); + if let Some(vec) = holders.inner.get_vec_mut(&chunk) { + let mut index = None; + for (i, e) in vec.iter().enumerate() { + if *e == entity { + index = Some(i); + } + } + + if let Some(index) = index { + vec.swap_remove(index); + } + } + trigger.trigger(ChunkHolderReleaseEvent { entity, chunk }) +} + +/// Asynchronously loads the chunk at the given position. +/// At some point in time after this function is called, +/// the chunk will appear in the chunk map. +/// +/// In the event that the requested chunk does not exist +/// in the world save, it will be generated asynchronously. +pub fn load_chunk(handle: &ChunkWorkerHandle, pos: ChunkPosition) { + // Send request to chunk worker thread + handle + .sender + .send(chunk_worker::Request::LoadChunk(pos)) + .unwrap(); +} + +/// Asynchronously saves the chunk at the given position. +pub fn save_chunk(handle: &ChunkWorkerHandle, chunk: Arc, entities: Vec) { + handle + .sender + .send(chunk_worker::Request::SaveChunk(chunk, entities)) + .unwrap(); +} diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index 8978cd0bc..7bbdfeeef 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -132,7 +132,8 @@ async fn _run_worker( } Action::SetStage(stage) => framed.codec_mut().set_stage(stage), Action::JoinGame(res) => { - let data = load_player_data(&config, res.uuid).await?; + // let data = load_player_data(&config, res.uuid).await?; + let data = PlayerData::default(); let info = NewClientInfo { ip, username: res.username, diff --git a/server/src/lazy.rs b/server/src/lazy.rs index 296fd6717..385f09422 100644 --- a/server/src/lazy.rs +++ b/server/src/lazy.rs @@ -11,8 +11,8 @@ impl LazyFnWithScheduler for F where F: FnOnce(&mut World, &mut Scheduler) + pub trait LazyFn: FnOnce(&mut World) + Send {} impl LazyFn for F where F: FnOnce(&mut World) + Send {} -pub trait LazyEntityFn: FnOnce(&mut World, Entity) + Send {} -impl LazyEntityFn for F where F: FnOnce(&mut World, Entity) + Send {} +pub trait LazyEntityFn: FnOnce(&mut World, &mut Scheduler, Entity) + Send {} +impl LazyEntityFn for F where F: FnOnce(&mut World, &mut Scheduler, Entity) + Send {} /// Resource which allows lazy creation of entities /// or execution of functions with world access. @@ -66,28 +66,39 @@ pub struct EntityBuilder<'a> { impl<'a> EntityBuilder<'a> { pub fn with_component(mut self, component: C) -> Self { - self.fns - .push(Box::new(move |world: &mut World, entity: Entity| { + self.fns.push(Box::new( + move |world: &mut World, _: &mut Scheduler, entity: Entity| { world.add_component(entity, component); - })); + }, + )); self } pub fn with_tag(mut self, tag: T) -> Self { - self.fns - .push(Box::new(move |world: &mut World, entity: Entity| { + self.fns.push(Box::new( + move |world: &mut World, _: &mut Scheduler, entity: Entity| { world.add_tag(entity, tag); - })); + }, + )); + self + } + + /// Executes a function with the entity after it is created. + pub fn with_exec( + mut self, + f: impl FnOnce(&mut World, &mut Scheduler, Entity) + Send + 'static, + ) -> Self { + self.fns.push(Box::new(f)); self } pub fn build(self) { let fns = self.fns; - self.lazy.exec(move |world| { + self.lazy.exec_with_scheduler(move |world, scheduler| { let entity = world.insert((), [()].iter().copied())[0]; for f in fns { - f(world, entity); + f(world, scheduler, entity); } }) } diff --git a/server/src/lib.rs b/server/src/lib.rs index 6f5b0ebcb..d2e4d9cbb 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -263,6 +263,12 @@ fn run_loop(world: &mut World, scheduler: &mut Scheduler, shutdown_rx: Receiver< scheduler.execute(world); world.defrag(None); // TODO: do this at interval rate? + // Run lazily-executed closures. TODO: remove unsafe + unsafe { + let state = scheduler.resources().get::() as *const State; + (&*state).flush(world, scheduler); + } + // Sleep correct amount let end_time = current_time_in_millis(); let elapsed = end_time - start_time; diff --git a/server/src/network.rs b/server/src/network.rs index b2d9dd7fd..bd7699fe1 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -193,6 +193,7 @@ pub fn network_( while let Ok(msg) = io.receiver.try_recv() { match msg { ListenerToServerMessage::NewClient(info) => { + debug!("Server received connection from {}", info.username); player::create(state, info); } } diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index d575ce438..1aee5cae9 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -25,6 +25,8 @@ pub struct PlayerJoinEvent { pub struct Player; /// Creates a new player from the given `NewClientInfo`. +/// +/// This function also triggers the `PlayerJoinEvent` for this player. pub fn create(state: &State, info: NewClientInfo) { entity::base(state, info.position) .with_tag(Player) @@ -36,5 +38,8 @@ pub fn create(state: &State, info: NewClientInfo) { .with_component(info.ip) .with_component(ProfileProperties(info.profile)) .with_component(NameComponent(info.username)) + .with_exec(|world, scheduler, player| { + scheduler.trigger(PlayerJoinEvent { player }, world); + }) .build(); } diff --git a/server/src/state.rs b/server/src/state.rs index 264b4fa06..1b75b0e5f 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -40,6 +40,11 @@ impl State { self.lazy.exec(f) } + /// See `Lazy::exec_with_scheduler()`. + pub fn exec_with_scheduler(&self, f: impl FnOnce(&mut World, &mut Scheduler) + Send + 'static) { + self.lazy.exec_with_scheduler(f) + } + /// See `Lazy::create_entity()`. pub fn create_entity(&self) -> EntityBuilder { self.lazy.create_entity() diff --git a/server/src/view.rs b/server/src/view.rs index a45b37dd0..b42c27473 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -17,21 +17,36 @@ //! * Various systems listen to `ViewUpdateEvent` and send necessary packets. //! This includes systems to load/unload chunks and send entities. +use crate::chunk_logic; +use crate::chunk_logic::{ + ChunkHolder, ChunkHolderReleaseEvent, ChunkHolders, ChunkLoadEvent, ChunkWorkerHandle, +}; +use crate::config::Config; use crate::entity::{EntityMoveEvent, PreviousPosition}; -use feather_core::{ChunkPosition, Position}; +use crate::network::Network; +use crate::player::PlayerJoinEvent; +use crate::state::State; +use dashmap::DashMap; +use feather_core::network::packet::implementation::{ChunkData, UnloadChunk}; +use feather_core::{Chunk, ChunkPosition, Position}; +use hashbrown::HashSet; use legion::entity::Entity; -use legion::query::Read; +use legion::query::{Read, Write}; +use parking_lot::Mutex; +use rayon::prelude::*; +use smallvec::SmallVec; use tonks::{PreparedWorld, Query, Trigger}; /// Event triggered when a player's view is updated, i.e. when they -/// cross into a new chunk. +/// cross into a new chunk or when they join. pub struct ViewUpdateEvent { /// The player whose view was updated. pub player: Entity, /// The new chunk. pub new_chunk: ChunkPosition, - /// The old chunk. - pub old_chunk: ChunkPosition, + /// The old chunk, or `None` if there was no old chunk + /// (i.e. this player just joined). + pub old_chunk: Option, } /// System which checks for players crossing chunk boundaries @@ -43,7 +58,8 @@ fn view_update( world: &mut PreparedWorld, trigger: &mut Trigger, ) { - events.iter().for_each(|event| { + let trigger = Mutex::new(trigger); + events.par_iter().for_each(|event| { let pos = *world.get_component::(event.entity).unwrap(); let prev_pos = world .get_component::(event.entity) @@ -55,9 +71,184 @@ fn view_update( let event = ViewUpdateEvent { player: event.entity, new_chunk: pos.chunk_pos(), - old_chunk: prev_pos.chunk_pos(), + old_chunk: Some(prev_pos.chunk_pos()), }; - trigger.trigger(event); + trigger.lock().trigger(event); } }); } + +/// System which triggers `ViewUpdateEvent`s on player join. +#[event_handler] +fn view_update_on_join( + event: &PlayerJoinEvent, + _query: &mut Query>, + world: &mut PreparedWorld, + trigger: &mut Trigger, +) { + dbg!(); + let position = *world.get_component::(event.player).unwrap(); + + trigger.trigger(ViewUpdateEvent { + player: event.player, + new_chunk: position.chunk_pos(), + old_chunk: None, + }); +} + +/// System which sends new chunks and unloads old chunks on the client +/// when the view is updated. +#[event_handler] +fn view_handle_chunks( + events: &[ViewUpdateEvent], + _query: &mut Query<(Read, Write)>, + world: &mut PreparedWorld, + holders: &mut ChunkHolders, + state: &State, + chunks_to_send: &ChunksToSend, + handle: &ChunkWorkerHandle, + trigger: &mut Trigger, +) { + events.iter().for_each(|event| { + // Find the old chunks and new chunks. + let new_chunks = chunks_within_view_distance(&state.config, event.new_chunk); + let old_chunks = match event.old_chunk { + Some(chunk) => chunks_within_view_distance(&state.config, chunk), + None => HashSet::new(), + }; + + let to_send = new_chunks.difference(&old_chunks); + let to_unload = old_chunks.difference(&new_chunks); + + let network = world.get_component::(event.player).unwrap(); + let mut holder = + unsafe { world.get_component_mut_unchecked::(event.player) }.unwrap(); + + to_send.into_iter().for_each(|chunk| { + send_chunk_to_player( + state, + event.player, + &network, + &mut holder, + holders, + *chunk, + chunks_to_send, + handle, + ); + }); + + to_unload.into_iter().for_each(|chunk| { + unload_chunk_for_player( + event.player, + &network, + trigger, + &mut holder, + holders, + *chunk, + ); + }); + }); +} + +/// Resource containing a mapping from chunks -> sets of players indicating +/// which chunks are pending to send to a given player. +#[derive(Default, Resource)] +pub struct ChunksToSend(DashMap>); + +/// Asynchronously sends a chunk to a player. +fn send_chunk_to_player( + state: &State, + player: Entity, + network: &Network, + holder: &mut ChunkHolder, + holders: &mut ChunkHolders, + chunk: ChunkPosition, + chunks_to_send: &ChunksToSend, + handle: &ChunkWorkerHandle, +) { + // Ensure that the chunk isn't unloaded while the player has it loaded. + chunk_logic::hold_chunk(player, holder, holders, chunk); + + // If the chunk is already loaded, send it. Otherwise, we need to + // queue it for loading. + if let Some(chunk) = state.chunk_at(chunk) { + network.send(create_chunk_data(&chunk)); + } else { + let contains = chunks_to_send.0.contains_key(&chunk); + chunks_to_send + .0 + .entry(chunk) + .or_insert_with(|| smallvec![]) + .push(player); + + if !contains { + // Queue chunk for loading if it isn't already. + chunk_logic::load_chunk(handle, chunk); + } + } +} + +/// Unloads a chunk on a client. +fn unload_chunk_for_player( + player: Entity, + network: &Network, + trigger: &mut Trigger, + holder: &mut ChunkHolder, + holders: &mut ChunkHolders, + chunk: ChunkPosition, +) { + // Release hold on chunk so it can be unloaded on the server + chunk_logic::release_chunk(player, holder, holders, chunk, trigger); + + // Send Unload Chunk packet. + network.send(UnloadChunk { + chunk_x: chunk.x, + chunk_z: chunk.z, + }); +} + +/// System which sends chunks to pending players when a chunk is loaded. +#[event_handler] +fn chunk_send( + event: &ChunkLoadEvent, + state: &State, + to_send: &ChunksToSend, + _query: &mut Query>, + world: &mut PreparedWorld, +) { + if let Some(players) = to_send.0.get(&event.pos) { + let chunk = state + .chunk_at(event.pos) + .expect("chunk not loaded, but load event was triggered"); + players.value().par_iter().for_each(|player| { + let network = world.get_component::(*player).unwrap(); + network.send(create_chunk_data(&chunk)); + }); + } + + to_send.0.remove(&event.pos); +} + +/// Creates a chunk data packet for the given chunk. +fn create_chunk_data(chunk: &Chunk) -> ChunkData { + ChunkData { + chunk: chunk.clone(), // TODO: optimize + } +} + +/// Finds all chunks within the view distance of a given chunk. +fn chunks_within_view_distance(config: &Config, position: ChunkPosition) -> HashSet { + let view_distance = config.server.view_distance as i32; + + let dimensions = view_distance * 2 + 1; + + let mut set = HashSet::with_capacity((dimensions * dimensions) as usize); + + for x in -view_distance..=view_distance { + for z in -view_distance..=view_distance { + set.insert(position + ChunkPosition::new(x, z)); + } + } + + set +} From 8ab3ebec7686b4ed8353534a6348fdce0b94951f Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 21 Dec 2019 14:46:56 -0700 Subject: [PATCH 053/647] Wrestle through a number of Legion bugs to get chunk sending to work properly --- Cargo.lock | 107 +++++++++++++++++--------------------- server/Cargo.toml | 3 +- server/src/chunk_logic.rs | 7 +-- server/src/lib.rs | 3 +- server/src/player/mod.rs | 2 + server/src/view.rs | 20 ++++--- 6 files changed, 71 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5c2c76dea..f85b8180e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -255,6 +255,15 @@ dependencies = [ "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "chashmap" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "chrono" version = "0.4.9" @@ -692,6 +701,7 @@ dependencies = [ "bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "chashmap 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -710,7 +720,7 @@ dependencies = [ "humantime-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "inventory 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "legion 0.2.1 (git+https://github.com/TomGillen/legion?rev=cfb31727d9f4c8f069c559c0cdf05f56547fd9d4)", + "legion 0.2.1 (git+https://github.com/feather-rs/legion?rev=15d2b5c47b2a935dbf238698b9d7c299f3824280)", "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mojang-api 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1159,14 +1169,6 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "itertools" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "itertools" version = "0.8.0" @@ -1208,7 +1210,7 @@ dependencies = [ [[package]] name = "legion" version = "0.2.1" -source = "git+https://github.com/TomGillen/legion?rev=cfb31727d9f4c8f069c559c0cdf05f56547fd9d4#cfb31727d9f4c8f069c559c0cdf05f56547fd9d4" +source = "git+https://github.com/feather-rs/legion?rev=15d2b5c47b2a935dbf238698b9d7c299f3824280#15d2b5c47b2a935dbf238698b9d7c299f3824280" dependencies = [ "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1219,7 +1221,6 @@ dependencies = [ "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "shrinkwraprs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1620,6 +1621,23 @@ name = "ordermap" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "owning_ref" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot" version = "0.9.0" @@ -1639,6 +1657,17 @@ dependencies = [ "parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parking_lot_core" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot_core" version = "0.6.2" @@ -1662,7 +1691,7 @@ dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1748,14 +1777,6 @@ name = "proc-macro-nested" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "proc-macro2" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "proc-macro2" version = "0.3.8" @@ -1785,14 +1806,6 @@ name = "quick-error" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "quote" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "quote" version = "0.5.2" @@ -2246,17 +2259,6 @@ name = "sha1" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "shrinkwraprs" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "simdeez" version = "0.6.4" @@ -2310,7 +2312,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smallvec" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2375,16 +2377,6 @@ name = "subtle" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "syn" -version = "0.12.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "syn" version = "0.13.11" @@ -2645,7 +2637,7 @@ dependencies = [ "hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "inventory 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "legion 0.2.1 (git+https://github.com/TomGillen/legion?rev=cfb31727d9f4c8f069c559c0cdf05f56547fd9d4)", + "legion 0.2.1 (git+https://github.com/feather-rs/legion?rev=15d2b5c47b2a935dbf238698b9d7c299f3824280)", "mopa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3047,6 +3039,7 @@ dependencies = [ "checksum cfb8 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1b310afa67a25a8d5189eacaf5b14418c8dc3d8bcc5755619d89cab87871260d" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64a4b57c8f4e3a2e9ac07e0f6abc9c24b6fc9e1b54c3478cfb598f3d0023e51c" +"checksum chashmap 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff41a3c2c1e39921b9003de14bf0439c7b63a9039637c291e1a64925d8ddfa45" "checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" @@ -3125,13 +3118,12 @@ dependencies = [ "checksum inventory 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f4cece20baea71d9f3435e7bbe9adf4765f091c5fe404975f844006964a71299" "checksum inventory-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2869bf972e998977b1cb87e60df70341d48e48dca0823f534feb91ea44adaf9" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" -"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "2cc9a97d7cec30128fd8b28a7c1f9df1c001ceb9b441e2b755e24130a6b43c79" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum legion 0.2.1 (git+https://github.com/TomGillen/legion?rev=cfb31727d9f4c8f069c559c0cdf05f56547fd9d4)" = "" +"checksum legion 0.2.1 (git+https://github.com/feather-rs/legion?rev=15d2b5c47b2a935dbf238698b9d7c299f3824280)" = "" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum libm 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" "checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" @@ -3174,8 +3166,11 @@ dependencies = [ "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)" = "2c42dcccb832556b5926bc9ae61e8775f2a61e725ab07ab3d1e7fcf8ae62c3b6" "checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" +"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" +"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" "checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" @@ -3189,12 +3184,10 @@ dependencies = [ "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "114cdf1f426eb7f550f01af5f53a33c0946156f6814aec939b3bd77e844f9a9d" "checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" -"checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" "checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90cf5f418035b98e655e9cdb225047638296b862b42411c4e45bb88d700f7fc0" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" -"checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408" "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" @@ -3244,7 +3237,6 @@ dependencies = [ "checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" "checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" "checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" -"checksum shrinkwraprs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7d5f047b90b2ca2d1526ff73d67cba61f86f4cf9a8afddc99dd96702ded8e684" "checksum simdeez 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4204ae48b2a871f428dc20426f005be250413fa6263e6f3d93094a36b29504dc" "checksum simdnoise 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "86a9e4c1c3369eab7105ac7e1582a601942fed0a63877cb2e1afcf57f34ed7b3" "checksum simple_asn1 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b25ecba7165254f0c97d6c22a64b1122a03634b18d20a34daf21e18f892e618" @@ -3252,7 +3244,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 smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86" +"checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" @@ -3263,7 +3255,6 @@ dependencies = [ "checksum strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6138f8f88a16d90134763314e3fc76fa3ed6a7db4725d6acf9a3ef95a3188d22" "checksum strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81" "checksum subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab3af2eb31c42e8f0ccf43548232556c42737e01a96db6e1777b0be108e79799" -"checksum syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97c05b8ebc34ddd6b967994d5c6e9852fa92f8b82b3858c39451f97346dcce5" "checksum syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)" = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" diff --git a/server/Cargo.toml b/server/Cargo.toml index 42dc10294..15fd121a4 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -20,7 +20,7 @@ feather-item-block = { path = "../item_block" } feather-codegen = { path = "../codegen" } # Core ECS + systems -legion = { git = "https://github.com/TomGillen/legion", rev = "cfb31727d9f4c8f069c559c0cdf05f56547fd9d4" } +legion = { git = "https://github.com/feather-rs/legion", rev = "15d2b5c47b2a935dbf238698b9d7c299f3824280" } # tonks = { git = "https://github.com/feather-rs/tonks", rev = "226d04de294008a1f615bae40f4d19a890583f0b", features = ["system-registry"] } tonks = { path = "../../../dev/tonks", features = ["system-registry"] } @@ -62,6 +62,7 @@ heapless = "0.5" uuid = { version = "0.7", features = ["v4"] } multimap = "0.7" smallvec = "0.6" +chashmap = "2.2" dashmap = "2.1" # Logging diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index 2c4ac1115..3066a48b0 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -48,7 +48,6 @@ pub struct ChunkLoadFailEvent { fn chunk_load_system( state: &State, handle: &ChunkWorkerHandle, - load_events: &mut Trigger, fail_events: &mut Trigger, ) { while let Ok(reply) = handle.receiver.try_recv() { @@ -57,9 +56,11 @@ fn chunk_load_system( Ok((chunk, entities)) => { state.lazy_insert_chunk(chunk); - // Trigger event + // Trigger event - lazily so it happens after the chunk is inserted into the chunk map let event = ChunkLoadEvent { pos, entities }; - load_events.trigger(event); + state.exec_with_scheduler(move |world, scheduler| { + scheduler.trigger(event, world); + }); trace!("Loaded chunk at {:?}", pos); } diff --git a/server/src/lib.rs b/server/src/lib.rs index d2e4d9cbb..5642f4d6d 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -261,7 +261,8 @@ fn run_loop(world: &mut World, scheduler: &mut Scheduler, shutdown_rx: Receiver< let start_time = current_time_in_millis(); scheduler.execute(world); - world.defrag(None); // TODO: do this at interval rate? + // https://github.com/TomGillen/legion/issues/60 + // world.defrag(None); // TODO: do this at interval rate? // Run lazily-executed closures. TODO: remove unsafe unsafe { diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index 1aee5cae9..e3dcc76d1 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -1,5 +1,6 @@ //! Systems and components specific to player entities. +use crate::chunk_logic::ChunkHolder; use crate::entity; use crate::entity::NameComponent; use crate::io::NewClientInfo; @@ -38,6 +39,7 @@ pub fn create(state: &State, info: NewClientInfo) { .with_component(info.ip) .with_component(ProfileProperties(info.profile)) .with_component(NameComponent(info.username)) + .with_component(ChunkHolder::default()) .with_exec(|world, scheduler, player| { scheduler.trigger(PlayerJoinEvent { player }, world); }) diff --git a/server/src/view.rs b/server/src/view.rs index b42c27473..0789023f0 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -26,7 +26,7 @@ use crate::entity::{EntityMoveEvent, PreviousPosition}; use crate::network::Network; use crate::player::PlayerJoinEvent; use crate::state::State; -use dashmap::DashMap; +use chashmap::CHashMap; use feather_core::network::packet::implementation::{ChunkData, UnloadChunk}; use feather_core::{Chunk, ChunkPosition, Position}; use hashbrown::HashSet; @@ -153,7 +153,7 @@ fn view_handle_chunks( /// Resource containing a mapping from chunks -> sets of players indicating /// which chunks are pending to send to a given player. #[derive(Default, Resource)] -pub struct ChunksToSend(DashMap>); +pub struct ChunksToSend(CHashMap>); /// Asynchronously sends a chunk to a player. fn send_chunk_to_player( @@ -175,11 +175,15 @@ fn send_chunk_to_player( network.send(create_chunk_data(&chunk)); } else { let contains = chunks_to_send.0.contains_key(&chunk); - chunks_to_send - .0 - .entry(chunk) - .or_insert_with(|| smallvec![]) - .push(player); + + let mut vec = match chunks_to_send.0.get_mut(&chunk) { + Some(vec) => vec, + None => { + chunks_to_send.0.insert(chunk, smallvec![]); + chunks_to_send.0.get_mut(&chunk).unwrap() + } + }; + vec.push(player); if !contains { // Queue chunk for loading if it isn't already. @@ -220,7 +224,7 @@ fn chunk_send( let chunk = state .chunk_at(event.pos) .expect("chunk not loaded, but load event was triggered"); - players.value().par_iter().for_each(|player| { + players.par_iter().for_each(|player| { let network = world.get_component::(*player).unwrap(); network.send(create_chunk_data(&chunk)); }); From edec45b399efe9c88bb2a67f078f9e404f840e88 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 21 Dec 2019 16:40:07 -0700 Subject: [PATCH 054/647] Player can now join and see chunks. --- Cargo.lock | 22 ++++++++- server/Cargo.toml | 2 +- server/src/chunk_logic.rs | 4 +- server/src/entity/mod.rs | 12 ++++- server/src/io/worker.rs | 9 ++-- server/src/join.rs | 100 ++++++++++++++++++++++++++++++++++++++ server/src/lib.rs | 4 +- server/src/network.rs | 2 +- server/src/player/mod.rs | 6 ++- server/src/state.rs | 5 +- server/src/view.rs | 35 +++++++++++-- 11 files changed, 182 insertions(+), 19 deletions(-) create mode 100644 server/src/join.rs diff --git a/Cargo.lock b/Cargo.lock index f85b8180e..93cc34629 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,6 +193,11 @@ name = "bumpalo" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bumpalo" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "1.3.2" @@ -1673,12 +1678,15 @@ name = "parking_lot_core" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2439,6 +2447,16 @@ dependencies = [ "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thread-id" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thread_local" version = "0.3.6" @@ -2631,7 +2649,7 @@ version = "0.1.0" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3030,6 +3048,7 @@ dependencies = [ "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" "checksum bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245" "checksum bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708" +"checksum bumpalo 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c2636342f603744010322b36bc245a35d1a9eecadc29fbbb532be90415bdc35d" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" @@ -3261,6 +3280,7 @@ dependencies = [ "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "88ddf1ad580c7e3d1efff877d972bcc93f995556b9087a5a259630985c88ceab" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" diff --git a/server/Cargo.toml b/server/Cargo.toml index 15fd121a4..c55173a89 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -27,7 +27,7 @@ tonks = { path = "../../../dev/tonks", features = ["system-registry"] } # Concurrency/threading crossbeam = "0.7" rayon = "1.2" -parking_lot = "0.9" +parking_lot = { version = "0.9", features = ["deadlock_detection"] } lock_api = "0.3" thread_local = "1.0" diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index 3066a48b0..134a31482 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -58,8 +58,8 @@ fn chunk_load_system( // Trigger event - lazily so it happens after the chunk is inserted into the chunk map let event = ChunkLoadEvent { pos, entities }; - state.exec_with_scheduler(move |world, scheduler| { - scheduler.trigger(event, world); + state.exec_with_scheduler(move |_, scheduler| { + scheduler.trigger(event); }); trace!("Loaded chunk at {:?}", pos); diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 124c814d2..9ac618301 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -1,4 +1,4 @@ -//! Dealing with entities. +//! Dealing with entities, including associated components and events. use crate::lazy::EntityBuilder; use crate::state::State; @@ -6,8 +6,16 @@ use feather_core::Position; use legion::prelude::Entity; use legion::query::{Read, Write}; use std::ops::{Deref, DerefMut}; +use std::sync::atomic::{AtomicI32, Ordering}; use tonks::{PreparedWorld, Query}; +/// ID of an entity. This value is generally unique. +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub struct EntityId(pub i32); + +/// Entity ID counter, used to create new entity IDs. +pub static ENTITY_ID_COUNTER: AtomicI32 = AtomicI32::new(0); + /// Event triggered when an entity is removed. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct EntityDeleteEvent { @@ -79,8 +87,10 @@ pub fn position_reset( /// * Position /// * Velocity (0) pub fn base(state: &State, position: Position) -> EntityBuilder { + let id = ENTITY_ID_COUNTER.fetch_add(1, Ordering::Relaxed); state .create_entity() + .with_component(EntityId(id)) .with_component(position) .with_component(PreviousPosition(position)) .with_component(Velocity::default()) diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index 7bbdfeeef..546038598 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -141,10 +141,11 @@ async fn _run_worker( uuid: res.uuid, sender: tx_server_to_worker.clone(), receiver: rx_worker_to_server.take().unwrap(), - position: data - .entity - .read_position() - .ok_or_else(|| Error::PlayerData)?, + /*position: data + .entity + .read_position() + .ok_or_else(|| Error::PlayerData)?,*/ + position: position!(0.0, 80.0, 0.0), data, }; global_sender diff --git a/server/src/join.rs b/server/src/join.rs new file mode 100644 index 000000000..f6ed07d35 --- /dev/null +++ b/server/src/join.rs @@ -0,0 +1,100 @@ +//! After chunks are sent to a client, we complete the login sequence +//! by sending Spawn Position, Player Position and Look, and inventory, +//! among others. This is handled by the event handler `join`. + +use crate::entity::EntityId; +use crate::network::Network; +use crate::player::PlayerJoinEvent; +use crate::state::State; +use crate::view::ChunkSendEvent; +use feather_core::network::packet::implementation::{ + JoinGame, PlayerPositionAndLookClientbound, SpawnPosition, +}; +use feather_core::{BlockPosition, Gamemode, Position}; +use legion::query::{Read, Write}; +use parking_lot::RwLock; +use rayon::prelude::*; +use tonks::{PreparedWorld, Query}; + +/// Component indicating whether a player has completed the join sequence. +#[derive(Default, Debug)] +pub struct Joined(pub bool); + +/// System to run the join sequence. To determine when a player is ready to join, +/// we wait for the chunk that the player is in to be sent—this appears to work +/// well with the client. +#[event_handler] +fn join( + events: &[ChunkSendEvent], + _query: &mut Query<(Write, Read, Read)>, + world: &mut PreparedWorld, + state: &State, +) { + let world = RwLock::new(world); + events.par_iter().for_each(|event| { + let pos = { + let world = world.read(); + + let pos = world.get_component::(event.player).unwrap(); + let joined = world.get_component::(event.player).unwrap(); + + if pos.chunk_pos() != event.chunk || joined.0 { + return; + } + + *pos + }; + + // Run the join sequence. TODO: inventory. + world + .write() + .get_component_mut::(event.player) + .unwrap() + .0 = true; + + let world = world.read(); + let network = world.get_component::(event.player).unwrap(); + + let packet = SpawnPosition { + location: BlockPosition::new( + state.level.spawn_x, + state.level.spawn_y, + state.level.spawn_z, + ), + }; + network.send(packet); + + let packet = PlayerPositionAndLookClientbound { + x: pos.x, + y: pos.y, + z: pos.z, + yaw: pos.yaw, + pitch: pos.pitch, + flags: 0, + teleport_id: 0, + }; + network.send(packet); + }); +} + +#[event_handler] +fn send_join_game( + event: &PlayerJoinEvent, + _query: &mut Query<(Read, Read)>, + world: &mut PreparedWorld, +) { + let network = world.get_component::(event.player).unwrap(); + let id = world.get_component::(event.player).unwrap(); + + // TODO + let packet = JoinGame { + entity_id: id.0, + gamemode: Gamemode::Creative.get_id(), + dimension: 0, + difficulty: 0, + max_players: 0, + level_type: "default".to_string(), + reduced_debug_info: false, + }; + network.send(packet); +} diff --git a/server/src/lib.rs b/server/src/lib.rs index 5642f4d6d..4abed94d5 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -149,6 +149,7 @@ pub mod chunk_worker; pub mod config; pub mod entity; pub mod io; +pub mod join; pub mod lazy; pub mod network; pub mod physics; @@ -299,9 +300,8 @@ fn init_scheduler( // Insert resources which don't have a `Default` impl. let mut resources = Resources::new(); let chunk_map = ChunkMap::new(); - resources.insert(State::new(config, chunk_map)); + resources.insert(State::new(config, chunk_map, level)); resources.insert(chunk_worker_handle); - resources.insert(level); resources.insert(io_manager); tonks::build_scheduler().build(resources) diff --git a/server/src/network.rs b/server/src/network.rs index bd7699fe1..fb0e77b65 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -181,7 +181,7 @@ pub fn network_( debug_assert!(world.delete(entity), "player already deleted"); }); } - ServerToWorkerMessage::SendPacket(packet) => { + ServerToWorkerMessage::NotifyPacketReceived(packet) => { packet_queue.push(packet, entity); } _ => unreachable!(), diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index e3dcc76d1..d7877b524 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -4,6 +4,7 @@ use crate::chunk_logic::ChunkHolder; use crate::entity; use crate::entity::NameComponent; use crate::io::NewClientInfo; +use crate::join::Joined; use crate::network::Network; use crate::state::State; use legion::entity::Entity; @@ -40,8 +41,9 @@ pub fn create(state: &State, info: NewClientInfo) { .with_component(ProfileProperties(info.profile)) .with_component(NameComponent(info.username)) .with_component(ChunkHolder::default()) - .with_exec(|world, scheduler, player| { - scheduler.trigger(PlayerJoinEvent { player }, world); + .with_component(Joined(false)) + .with_exec(|_, scheduler, player| { + scheduler.trigger(PlayerJoinEvent { player }); }) .build(); } diff --git a/server/src/state.rs b/server/src/state.rs index 1b75b0e5f..ad2df82da 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -1,6 +1,7 @@ use crate::config::Config; use crate::lazy::{EntityBuilder, Lazy}; use feather_blocks::Block; +use feather_core::level::LevelData; use feather_core::world::ChunkMap; use feather_core::{BlockPosition, Chunk, ChunkPosition}; use legion::world::World; @@ -22,15 +23,17 @@ use tonks::Scheduler; pub struct State { pub config: Arc, pub chunk_map: ChunkMap, + pub level: LevelData, lazy: Lazy, } impl State { - pub fn new(config: Arc, chunk_map: ChunkMap) -> Self { + pub fn new(config: Arc, chunk_map: ChunkMap, level: LevelData) -> Self { Self { config, chunk_map, + level, lazy: Lazy::default(), } } diff --git a/server/src/view.rs b/server/src/view.rs index 0789023f0..cd90392d7 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -49,6 +49,13 @@ pub struct ViewUpdateEvent { pub old_chunk: Option, } +/// Event triggered when a chunk is sent to a player. +#[derive(Debug)] +pub struct ChunkSendEvent { + pub chunk: ChunkPosition, + pub player: Entity, +} + /// System which checks for players crossing chunk boundaries /// and triggers `ViewUpdateEvent`s. #[event_handler] @@ -107,7 +114,8 @@ fn view_handle_chunks( state: &State, chunks_to_send: &ChunksToSend, handle: &ChunkWorkerHandle, - trigger: &mut Trigger, + holder_release_trigger: &mut Trigger, + chunk_send_trigger: &mut Trigger, ) { events.iter().for_each(|event| { // Find the old chunks and new chunks. @@ -124,6 +132,13 @@ fn view_handle_chunks( let mut holder = unsafe { world.get_component_mut_unchecked::(event.player) }.unwrap(); + // Sort sent chunks so that closer chunks are sent first. + let mut to_send = to_send.into_iter().copied().collect::>(); + to_send.sort_unstable_by_key(|chunk| { + chunk.manhattan_distance(event.new_chunk); + }); + + // Send new chunks. to_send.into_iter().for_each(|chunk| { send_chunk_to_player( state, @@ -131,17 +146,19 @@ fn view_handle_chunks( &network, &mut holder, holders, - *chunk, + chunk, chunks_to_send, handle, + chunk_send_trigger, ); }); + // Unload old chunks on client. to_unload.into_iter().for_each(|chunk| { unload_chunk_for_player( event.player, &network, - trigger, + holder_release_trigger, &mut holder, holders, *chunk, @@ -165,6 +182,7 @@ fn send_chunk_to_player( chunk: ChunkPosition, chunks_to_send: &ChunksToSend, handle: &ChunkWorkerHandle, + trigger: &mut Trigger, ) { // Ensure that the chunk isn't unloaded while the player has it loaded. chunk_logic::hold_chunk(player, holder, holders, chunk); @@ -173,6 +191,10 @@ fn send_chunk_to_player( // queue it for loading. if let Some(chunk) = state.chunk_at(chunk) { network.send(create_chunk_data(&chunk)); + trigger.trigger(ChunkSendEvent { + chunk: chunk.position(), + player, + }); } else { let contains = chunks_to_send.0.contains_key(&chunk); @@ -219,14 +241,19 @@ fn chunk_send( to_send: &ChunksToSend, _query: &mut Query>, world: &mut PreparedWorld, + trigger: &mut Trigger, ) { if let Some(players) = to_send.0.get(&event.pos) { let chunk = state .chunk_at(event.pos) .expect("chunk not loaded, but load event was triggered"); - players.par_iter().for_each(|player| { + players.iter().for_each(|player| { let network = world.get_component::(*player).unwrap(); network.send(create_chunk_data(&chunk)); + trigger.trigger(ChunkSendEvent { + chunk: chunk.position(), + player: *player, + }); }); } From 8cada32be0b3dd0604ac38520ffce30ddf1444b5 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 21 Dec 2019 17:08:56 -0700 Subject: [PATCH 055/647] Reimplement movement packets --- Cargo.lock | 22 +++------ server/Cargo.toml | 6 +-- server/src/lib.rs | 1 + server/src/network.rs | 2 + server/src/packet_handlers/mod.rs | 3 ++ server/src/packet_handlers/movement.rs | 68 ++++++++++++++++++++++++++ server/src/view.rs | 1 - 7 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 server/src/packet_handlers/mod.rs create mode 100644 server/src/packet_handlers/movement.rs diff --git a/Cargo.lock b/Cargo.lock index 93cc34629..38169a2f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -754,7 +754,7 @@ dependencies = [ "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks 0.1.0", + "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=f91a0639e55feb25a70c67b0679903868575f2d2)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1678,15 +1678,12 @@ name = "parking_lot_core" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2447,16 +2444,6 @@ dependencies = [ "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "thread-id" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "thread_local" version = "0.3.6" @@ -2646,6 +2633,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" +source = "git+https://github.com/feather-rs/tonks?rev=f91a0639e55feb25a70c67b0679903868575f2d2#f91a0639e55feb25a70c67b0679903868575f2d2" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2662,12 +2650,13 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks-macros 0.1.0", + "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=f91a0639e55feb25a70c67b0679903868575f2d2)", ] [[package]] name = "tonks-macros" version = "0.1.0" +source = "git+https://github.com/feather-rs/tonks?rev=f91a0639e55feb25a70c67b0679903868575f2d2#f91a0639e55feb25a70c67b0679903868575f2d2" dependencies = [ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3280,7 +3269,6 @@ dependencies = [ "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"checksum thread-id 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "88ddf1ad580c7e3d1efff877d972bcc93f995556b9087a5a259630985c88ceab" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" @@ -3296,6 +3284,8 @@ dependencies = [ "checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" "checksum tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" +"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=f91a0639e55feb25a70c67b0679903868575f2d2)" = "" +"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=f91a0639e55feb25a70c67b0679903868575f2d2)" = "" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" "checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" diff --git a/server/Cargo.toml b/server/Cargo.toml index c55173a89..ef1d0eada 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,13 +21,13 @@ feather-codegen = { path = "../codegen" } # Core ECS + systems legion = { git = "https://github.com/feather-rs/legion", rev = "15d2b5c47b2a935dbf238698b9d7c299f3824280" } -# tonks = { git = "https://github.com/feather-rs/tonks", rev = "226d04de294008a1f615bae40f4d19a890583f0b", features = ["system-registry"] } -tonks = { path = "../../../dev/tonks", features = ["system-registry"] } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "f91a0639e55feb25a70c67b0679903868575f2d2", features = ["system-registry"] } +# tonks = { path = "../../../dev/tonks", features = ["system-registry"] } # Concurrency/threading crossbeam = "0.7" rayon = "1.2" -parking_lot = { version = "0.9", features = ["deadlock_detection"] } +parking_lot = "0.9" lock_api = "0.3" thread_local = "1.0" diff --git a/server/src/lib.rs b/server/src/lib.rs index 4abed94d5..feb10d04d 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -152,6 +152,7 @@ pub mod io; pub mod join; pub mod lazy; pub mod network; +pub mod packet_handlers; pub mod physics; pub mod player; pub mod shutdown; diff --git a/server/src/network.rs b/server/src/network.rs index fb0e77b65..c15b43767 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -113,6 +113,8 @@ impl PacketQueue { pos: 0, }; + // Ensure mutex is not released; we will do it manually in `UnsafeDrain` + std::mem::forget(queue); // Safety: the vector cannot be accessed as long as the returned `UnsafeDrain` // has not been dropped, since the mutex is acquired. queue.set_len(0); diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs new file mode 100644 index 000000000..f25137873 --- /dev/null +++ b/server/src/packet_handlers/mod.rs @@ -0,0 +1,3 @@ +//! Systems which handle packets through `crate::network::PacketQueue`. + +mod movement; diff --git a/server/src/packet_handlers/movement.rs b/server/src/packet_handlers/movement.rs new file mode 100644 index 000000000..ecd230a32 --- /dev/null +++ b/server/src/packet_handlers/movement.rs @@ -0,0 +1,68 @@ +use crate::entity::EntityMoveEvent; +use crate::network::PacketQueue; +use feather_core::network::packet::implementation::{ + PlayerLook, PlayerPosition, PlayerPositionAndLookServerbound, +}; +use feather_core::Position; +use legion::entity::Entity; +use legion::query::Write; +use tonks::{PreparedWorld, Query, Trigger}; + +#[derive(Default, Resource)] +struct Buf(Vec<(Entity, Position)>); + +/// Handles player movement packets. +#[system] +fn movement( + queue: &PacketQueue, + _query: &mut Query>, + world: &mut PreparedWorld, + buf: &mut Buf, + trigger: &mut Trigger, +) { + let positions = queue.received::().map(|(player, packet)| { + let old = *world.get_component::(player).unwrap(); + ( + player, + position!( + packet.x, + packet.feet_y, + packet.z, + old.pitch, + old.yaw, + packet.on_ground + ), + ) + }); + + let looks = queue.received::().map(|(player, packet)| { + let mut old = *world.get_component::(player).unwrap(); + old.pitch = packet.pitch; + old.yaw = packet.yaw; + old.on_ground = packet.on_ground; + (player, old) + }); + + let pos_looks = queue + .received::() + .map(|(player, packet)| { + ( + player, + position!( + packet.x, + packet.feet_y, + packet.z, + packet.pitch, + packet.yaw, + packet.on_ground + ), + ) + }); + + buf.0.extend(positions.chain(looks).chain(pos_looks)); + + buf.0.drain(..).for_each(|(player, new_pos)| { + *world.get_component_mut::(player).unwrap() = new_pos; + trigger.trigger(EntityMoveEvent { entity: player }); + }); +} diff --git a/server/src/view.rs b/server/src/view.rs index cd90392d7..3e119ead0 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -93,7 +93,6 @@ fn view_update_on_join( world: &mut PreparedWorld, trigger: &mut Trigger, ) { - dbg!(); let position = *world.get_component::(event.player).unwrap(); trigger.trigger(ViewUpdateEvent { From 4254453ac3f3da45b6060e901ea88f139ba2659a Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 21 Dec 2019 22:28:31 -0700 Subject: [PATCH 056/647] Fix compilation --- server/src/network.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/network.rs b/server/src/network.rs index c15b43767..534b5de63 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -113,11 +113,11 @@ impl PacketQueue { pos: 0, }; - // Ensure mutex is not released; we will do it manually in `UnsafeDrain` - std::mem::forget(queue); // Safety: the vector cannot be accessed as long as the returned `UnsafeDrain` // has not been dropped, since the mutex is acquired. queue.set_len(0); + // Ensure mutex is not released; we will do it manually in `UnsafeDrain` + std::mem::forget(queue); let iter = drain.map(|(entity, packet)| (entity, cast_packet::

(packet))); From de214dd7b3d0e08a0ff644752ab5f9e24c9a5295 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 21 Dec 2019 22:35:24 -0700 Subject: [PATCH 057/647] Fix assorted Clippy errors --- server/src/network.rs | 4 ++-- server/src/view.rs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/server/src/network.rs b/server/src/network.rs index 534b5de63..8f2deee72 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -37,7 +37,7 @@ impl Iterator for UnsafeDrain { return None; } - let value = unsafe { std::ptr::read(self.ptr.offset(self.pos as isize)) }; + let value = unsafe { std::ptr::read(self.ptr.add(self.pos)) }; self.pos += 1; Some(value) } @@ -180,7 +180,7 @@ pub fn network_( match msg { ServerToWorkerMessage::NotifyDisconnect(_) => { state.exec(move |world| { - debug_assert!(world.delete(entity), "player already deleted"); + assert!(world.delete(entity), "player already deleted"); }); } ServerToWorkerMessage::NotifyPacketReceived(packet) => { diff --git a/server/src/view.rs b/server/src/view.rs index 3e119ead0..298123a59 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -132,7 +132,7 @@ fn view_handle_chunks( unsafe { world.get_component_mut_unchecked::(event.player) }.unwrap(); // Sort sent chunks so that closer chunks are sent first. - let mut to_send = to_send.into_iter().copied().collect::>(); + let mut to_send = to_send.copied().collect::>(); to_send.sort_unstable_by_key(|chunk| { chunk.manhattan_distance(event.new_chunk); }); @@ -153,7 +153,7 @@ fn view_handle_chunks( }); // Unload old chunks on client. - to_unload.into_iter().for_each(|chunk| { + to_unload.for_each(|chunk| { unload_chunk_for_player( event.player, &network, @@ -172,6 +172,7 @@ fn view_handle_chunks( pub struct ChunksToSend(CHashMap>); /// Asynchronously sends a chunk to a player. +#[allow(clippy::too_many_arguments)] fn send_chunk_to_player( state: &State, player: Entity, From 96d6895f8ecb9646d24c01004f220e3c38a3fbd0 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 22 Dec 2019 13:39:14 -0700 Subject: [PATCH 058/647] Implement ChunkEntities cache --- Cargo.lock | 76 +++--------------------------- server/Cargo.toml | 3 +- server/src/chunk_entities.rs | 90 +++++++++++++++++++++++++++++++++--- server/src/entity/mod.rs | 12 ++++- server/src/lib.rs | 2 + server/src/network.rs | 10 +++- server/src/state.rs | 3 ++ 7 files changed, 113 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 38169a2f6..75079414b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -445,16 +445,6 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "crossbeam-utils" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "csv" version = "1.1.1" @@ -493,26 +483,6 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "dashmap" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dashmap-shard 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "dashmap-shard" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "derivative" version = "1.0.3" @@ -710,7 +680,6 @@ dependencies = [ "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dashmap 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "derive_deref 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "feather-blocks 0.5.0", @@ -754,7 +723,7 @@ dependencies = [ "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=f91a0639e55feb25a70c67b0679903868575f2d2)", + "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=15f8aa965a667a6b7160e60828f935b17c6ac7e6)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1653,15 +1622,6 @@ dependencies = [ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "parking_lot" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "parking_lot_core" version = "0.2.14" @@ -1687,19 +1647,6 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "parking_lot_core" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "paste" version = "0.1.6" @@ -2315,11 +2262,6 @@ name = "smallvec" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "smallvec" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "sourcefile" version = "0.1.4" @@ -2633,7 +2575,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=f91a0639e55feb25a70c67b0679903868575f2d2#f91a0639e55feb25a70c67b0679903868575f2d2" +source = "git+https://github.com/feather-rs/tonks?rev=15f8aa965a667a6b7160e60828f935b17c6ac7e6#15f8aa965a667a6b7160e60828f935b17c6ac7e6" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2650,13 +2592,13 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=f91a0639e55feb25a70c67b0679903868575f2d2)", + "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=15f8aa965a667a6b7160e60828f935b17c6ac7e6)", ] [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=f91a0639e55feb25a70c67b0679903868575f2d2#f91a0639e55feb25a70c67b0679903868575f2d2" +source = "git+https://github.com/feather-rs/tonks?rev=15f8aa965a667a6b7160e60828f935b17c6ac7e6#15f8aa965a667a6b7160e60828f935b17c6ac7e6" dependencies = [ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3065,13 +3007,10 @@ dependencies = [ "checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" "checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" "checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" "checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" "checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f" -"checksum dashmap 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cb7ebab7705baa489c3f36e494b59bc16edb4fc5b3341921197270a11259d6a" -"checksum dashmap-shard 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c94b718728139bf8d0f822d63e0b65f4ed781562d5c28f6b84d8e1a66938ae70" "checksum derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "942ca430eef7a3806595a6737bc388bf51adb888d3fc0dd1b50f1c170167ee3a" "checksum derive-new 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9" "checksum derive_deref 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11554fdb0aa42363a442e0c4278f51c9621e20c1ce3bac51d79e60646f3b8b8f" @@ -3175,12 +3114,10 @@ dependencies = [ "checksum openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)" = "2c42dcccb832556b5926bc9ae61e8775f2a61e725ab07ab3d1e7fcf8ae62c3b6" "checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" -"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" "checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -"checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" "checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" "checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" @@ -3252,7 +3189,6 @@ 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 smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" @@ -3284,8 +3220,8 @@ dependencies = [ "checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" "checksum tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" -"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=f91a0639e55feb25a70c67b0679903868575f2d2)" = "" -"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=f91a0639e55feb25a70c67b0679903868575f2d2)" = "" +"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=15f8aa965a667a6b7160e60828f935b17c6ac7e6)" = "" +"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=15f8aa965a667a6b7160e60828f935b17c6ac7e6)" = "" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" "checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" diff --git a/server/Cargo.toml b/server/Cargo.toml index ef1d0eada..afdf8899f 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,7 +21,7 @@ feather-codegen = { path = "../codegen" } # Core ECS + systems legion = { git = "https://github.com/feather-rs/legion", rev = "15d2b5c47b2a935dbf238698b9d7c299f3824280" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "f91a0639e55feb25a70c67b0679903868575f2d2", features = ["system-registry"] } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "15f8aa965a667a6b7160e60828f935b17c6ac7e6", features = ["system-registry"] } # tonks = { path = "../../../dev/tonks", features = ["system-registry"] } # Concurrency/threading @@ -63,7 +63,6 @@ uuid = { version = "0.7", features = ["v4"] } multimap = "0.7" smallvec = "0.6" chashmap = "2.2" -dashmap = "2.1" # Logging log = "0.4" diff --git a/server/src/chunk_entities.rs b/server/src/chunk_entities.rs index 8edd8831c..75a2bd88d 100644 --- a/server/src/chunk_entities.rs +++ b/server/src/chunk_entities.rs @@ -1,6 +1,14 @@ -use dashmap::DashMap; -use feather_core::ChunkPosition; +use crate::entity::{EntityCreateEvent, EntityDeleteEvent, EntityMoveEvent, PreviousPosition}; +use crate::state::State; +use feather_core::{ChunkPosition, Position}; +use hashbrown::HashMap; use legion::entity::Entity; +use legion::query::Read; +use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard}; +use rayon::prelude::*; +use tonks::{PreparedWorld, Query}; + +static EMPTY_VEC: Vec = Vec::new(); /// Stores which entities belong to every given chunk. /// @@ -11,21 +19,29 @@ use legion::entity::Entity; /// to a player. /// /// This structure is internally stored in `State`, using -/// `dashmap` for concurrent access. +/// a `RwLock` for concurrent access. (TODO: remove lock.) /// /// Do note that the information in this structure is not necessarily up to date, /// although a best effort is made to update the data. #[derive(Resource)] -pub struct ChunkEntities(DashMap>); +pub struct ChunkEntities(RwLock>>); impl ChunkEntities { pub fn new() -> Self { - Self(DashMap::default()) + Self(RwLock::new(HashMap::new())) } /// Returns a slice of entities in the given chunk. - pub fn entities_in_chunk(&self, _chunk: ChunkPosition) -> &[Entity] { - todo!("implement chunk entities properly"); + pub fn entities_in_chunk(&self, chunk: ChunkPosition) -> MappedRwLockReadGuard<[Entity]> { + let map = self.0.read(); + + RwLockReadGuard::map(map, move |map| { + if let Some(vec) = map.get(&chunk) { + vec.as_slice() + } else { + &EMPTY_VEC + } + }) } } @@ -34,3 +50,63 @@ impl Default for ChunkEntities { Self::new() } } + +/// System to update ChunkEntities when entities move into new chunks. +#[event_handler] +fn chunk_entities_handle_movement( + events: &[EntityMoveEvent], + state: &State, + _query: &mut Query<(Read, Read)>, + world: &mut PreparedWorld, +) { + events.par_iter().for_each(|event| { + let old_pos = world + .get_component::(event.entity) + .unwrap() + .0; + let new_pos = *world.get_component::(event.entity).unwrap(); + + let old_chunk = old_pos.chunk_pos(); + let new_chunk = new_pos.chunk_pos(); + + if old_chunk != new_chunk { + // Update chunk entities + let mut map = state.chunk_entities.0.write(); + map.entry(new_chunk) + .or_insert_with(|| vec![]) + .push(event.entity); + map.entry(old_chunk).and_modify(|vec| { + vec.remove_item(&event.entity); + }); + } + }) +} + +#[event_handler] +fn chunk_entities_insert( + event: &EntityCreateEvent, + state: &State, + _query: &mut Query>, + world: &mut PreparedWorld, +) { + if let Some(position) = world.get_component::(event.entity) { + let chunk = position.chunk_pos(); + let mut map = state.chunk_entities.0.write(); + + map.entry(chunk) + .or_insert_with(|| vec![]) + .push(event.entity); + } +} + +#[event_handler] +fn chunk_entities_remove(event: &EntityDeleteEvent, state: &State) { + if let Some(position) = event.position { + let chunk = position.chunk_pos(); + let mut map = state.chunk_entities.0.write(); + + map.entry(chunk) + .or_insert_with(|| vec![]) + .remove_item(&event.entity); + } +} diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 9ac618301..ceaf88f98 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -16,10 +16,17 @@ pub struct EntityId(pub i32); /// Entity ID counter, used to create new entity IDs. pub static ENTITY_ID_COUNTER: AtomicI32 = AtomicI32::new(0); -/// Event triggered when an entity is removed. +/// Event triggered when an entity is created. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EntityCreateEvent { + pub entity: Entity, +} + +/// Event triggered when an entity is removed. +#[derive(Debug, Clone, Copy, PartialEq)] pub struct EntityDeleteEvent { - pub(crate) entity: Entity, + pub entity: Entity, + pub position: Option, } /// Event triggered when an entity moves. @@ -94,4 +101,5 @@ pub fn base(state: &State, position: Position) -> EntityBuilder { .with_component(position) .with_component(PreviousPosition(position)) .with_component(Velocity::default()) + .with_exec(|_, scheduler, entity| scheduler.trigger(EntityCreateEvent { entity })) } diff --git a/server/src/lib.rs b/server/src/lib.rs index feb10d04d..c51ed7241 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -91,6 +91,8 @@ //! chunk packets, inventory, time, nearby entities, etc. `PlayerJoinEvent` //! is used to send this data. +#![feature(vec_remove_item)] + #[macro_use] extern crate log; #[macro_use] diff --git a/server/src/network.rs b/server/src/network.rs index 8f2deee72..f45c4faaa 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -6,12 +6,13 @@ //! from players and allows systems to poll for packets //! received of a given type. +use crate::entity::EntityDeleteEvent; use crate::io::{ListenerToServerMessage, NetworkIoManager, ServerToWorkerMessage}; use crate::player; use crate::state::State; use crossbeam::Receiver; use feather_core::network::cast_packet; -use feather_core::{Packet, PacketType}; +use feather_core::{Packet, PacketType, Position}; use futures::channel::mpsc::UnboundedSender; use legion::entity::Entity; use legion::query::Read; @@ -179,8 +180,13 @@ pub fn network_( while let Ok(msg) = network.receiver.try_recv() { match msg { ServerToWorkerMessage::NotifyDisconnect(_) => { - state.exec(move |world| { + state.exec_with_scheduler(move |world, scheduler| { assert!(world.delete(entity), "player already deleted"); + let position = *world.get_component::(entity).unwrap(); + scheduler.trigger(EntityDeleteEvent { + entity, + position: Some(position), + }); }); } ServerToWorkerMessage::NotifyPacketReceived(packet) => { diff --git a/server/src/state.rs b/server/src/state.rs index ad2df82da..92f893bc4 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -1,3 +1,4 @@ +use crate::chunk_entities::ChunkEntities; use crate::config::Config; use crate::lazy::{EntityBuilder, Lazy}; use feather_blocks::Block; @@ -24,6 +25,7 @@ pub struct State { pub config: Arc, pub chunk_map: ChunkMap, pub level: LevelData, + pub chunk_entities: ChunkEntities, lazy: Lazy, } @@ -34,6 +36,7 @@ impl State { config, chunk_map, level, + chunk_entities: ChunkEntities::default(), lazy: Lazy::default(), } } From b51e2d4046ea9f70c7c5358ed489dcdc95858320 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 22 Dec 2019 13:48:26 -0700 Subject: [PATCH 059/647] Implement State broadcast methods --- server/src/state.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/server/src/state.rs b/server/src/state.rs index 92f893bc4..1db31a49f 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -1,10 +1,14 @@ use crate::chunk_entities::ChunkEntities; +use crate::chunk_logic::ChunkHolders; use crate::config::Config; use crate::lazy::{EntityBuilder, Lazy}; +use crate::network::Network; use feather_blocks::Block; use feather_core::level::LevelData; use feather_core::world::ChunkMap; -use feather_core::{BlockPosition, Chunk, ChunkPosition}; +use feather_core::{BlockPosition, Chunk, ChunkPosition, Packet, Position}; +use legion::entity::Entity; +use legion::query::{IntoQuery, Read}; use legion::world::World; use parking_lot::RwLockReadGuard; use std::sync::Arc; @@ -56,6 +60,50 @@ impl State { self.lazy.create_entity() } + /// Lazily broadcasts a packet to all clients able to see the given entity. + /// + /// The packet will not be sent to `neq`. + pub fn broadcast_entity_update( + &self, + entity: Entity, + packet: P, + neq: Option, + ) { + self.exec_with_scheduler(move |world, scheduler| { + // Use ChunkHolders to determine which players have a hold on the entity's + // chunk, which would allow them to see the entity. + let chunk_holders = scheduler.resources().get::(); + + if let Some(position) = world.get_component::(entity) { + let holders = chunk_holders.holders_for(position.chunk_pos()); + + holders.map(|entities| { + for entity in entities { + if let Some(network) = world.get_component::(*entity) { + if neq.map_or(true, |neq| *entity != neq) { + network.send(packet.clone()); + } + } + } + }); + } + }); + } + + /// Lazily broadcasts a packet to all clients. + pub fn broadcast_global(&self, packet: P, neq: Option) { + self.exec(move |world| { + // Standard Legion queries! How rare. + let query = >::query(); + + query.par_entities_for_each(world, |(entity, network)| { + if neq.map_or(true, |neq| entity != neq) { + network.send(packet.clone()); + } + }); + }); + } + /// See `Lazy::flush()`. pub fn flush(&self, world: &mut World, scheduler: &mut Scheduler) { self.lazy.flush(world, scheduler); From 9562e4f4ece2f17350998549dedf07043f22f130 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 23 Dec 2019 11:57:19 -0700 Subject: [PATCH 060/647] Implement movement broadcaster --- Cargo.lock | 52 +++++++-- server/Cargo.toml | 4 +- server/src/broadcasters/mod.rs | 7 ++ server/src/broadcasters/movement.rs | 159 ++++++++++++++++++++++++++++ server/src/lib.rs | 1 + 5 files changed, 210 insertions(+), 13 deletions(-) create mode 100644 server/src/broadcasters/mod.rs create mode 100644 server/src/broadcasters/movement.rs diff --git a/Cargo.lock b/Cargo.lock index 75079414b..1bd82fba4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -406,6 +406,14 @@ dependencies = [ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-channel" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-deque" version = "0.7.1" @@ -436,6 +444,14 @@ dependencies = [ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-queue" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-utils" version = "0.6.6" @@ -445,6 +461,16 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-utils" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "csv" version = "1.1.1" @@ -694,7 +720,7 @@ dependencies = [ "humantime-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "inventory 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "legion 0.2.1 (git+https://github.com/feather-rs/legion?rev=15d2b5c47b2a935dbf238698b9d7c299f3824280)", + "legion 0.2.1 (git+https://github.com/TomGillen/legion?rev=0f67adc237af35799df173f31a2c238b3d8010a2)", "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mojang-api 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -723,7 +749,7 @@ dependencies = [ "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=15f8aa965a667a6b7160e60828f935b17c6ac7e6)", + "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=8f9cae2b2787406f28b5a4c4ef874228f018af90)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1184,10 +1210,11 @@ dependencies = [ [[package]] name = "legion" version = "0.2.1" -source = "git+https://github.com/feather-rs/legion?rev=15d2b5c47b2a935dbf238698b9d7c299f3824280#15d2b5c47b2a935dbf238698b9d7c299f3824280" +source = "git+https://github.com/TomGillen/legion?rev=0f67adc237af35799df173f31a2c238b3d8010a2#0f67adc237af35799df173f31a2c238b3d8010a2" dependencies = [ "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "downcast-rs 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2575,7 +2602,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=15f8aa965a667a6b7160e60828f935b17c6ac7e6#15f8aa965a667a6b7160e60828f935b17c6ac7e6" +source = "git+https://github.com/feather-rs/tonks?rev=8f9cae2b2787406f28b5a4c4ef874228f018af90#8f9cae2b2787406f28b5a4c4ef874228f018af90" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2585,20 +2612,20 @@ dependencies = [ "hashbrown 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "inventory 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "legion 0.2.1 (git+https://github.com/feather-rs/legion?rev=15d2b5c47b2a935dbf238698b9d7c299f3824280)", + "legion 0.2.1 (git+https://github.com/TomGillen/legion?rev=0f67adc237af35799df173f31a2c238b3d8010a2)", "mopa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=15f8aa965a667a6b7160e60828f935b17c6ac7e6)", + "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=8f9cae2b2787406f28b5a4c4ef874228f018af90)", ] [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=15f8aa965a667a6b7160e60828f935b17c6ac7e6#15f8aa965a667a6b7160e60828f935b17c6ac7e6" +source = "git+https://github.com/feather-rs/tonks?rev=8f9cae2b2787406f28b5a4c4ef874228f018af90#8f9cae2b2787406f28b5a4c4ef874228f018af90" dependencies = [ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3003,10 +3030,13 @@ dependencies = [ "checksum criterion-plot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eccdc6ce8bbe352ca89025bee672aa6d24f4eb8c53e3a8b5d1bc58011da072a2" "checksum crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c" "checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" +"checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" "checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" "checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" +"checksum crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dfd6515864a82d2f877b42813d4553292c6659498c9a2aa31bab5a15243c2700" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" "checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" "checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" "checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" @@ -3070,7 +3100,7 @@ dependencies = [ "checksum js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "2cc9a97d7cec30128fd8b28a7c1f9df1c001ceb9b441e2b755e24130a6b43c79" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum legion 0.2.1 (git+https://github.com/feather-rs/legion?rev=15d2b5c47b2a935dbf238698b9d7c299f3824280)" = "" +"checksum legion 0.2.1 (git+https://github.com/TomGillen/legion?rev=0f67adc237af35799df173f31a2c238b3d8010a2)" = "" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum libm 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" "checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" @@ -3220,8 +3250,8 @@ dependencies = [ "checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" "checksum tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" -"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=15f8aa965a667a6b7160e60828f935b17c6ac7e6)" = "" -"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=15f8aa965a667a6b7160e60828f935b17c6ac7e6)" = "" +"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=8f9cae2b2787406f28b5a4c4ef874228f018af90)" = "" +"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=8f9cae2b2787406f28b5a4c4ef874228f018af90)" = "" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" "checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" diff --git a/server/Cargo.toml b/server/Cargo.toml index afdf8899f..81d8f766f 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -20,8 +20,8 @@ feather-item-block = { path = "../item_block" } feather-codegen = { path = "../codegen" } # Core ECS + systems -legion = { git = "https://github.com/feather-rs/legion", rev = "15d2b5c47b2a935dbf238698b9d7c299f3824280" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "15f8aa965a667a6b7160e60828f935b17c6ac7e6", features = ["system-registry"] } +legion = { git = "https://github.com/TomGillen/legion", rev = "0f67adc237af35799df173f31a2c238b3d8010a2" } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "8f9cae2b2787406f28b5a4c4ef874228f018af90", features = ["system-registry"] } # tonks = { path = "../../../dev/tonks", features = ["system-registry"] } # Concurrency/threading diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs new file mode 100644 index 000000000..d792cd490 --- /dev/null +++ b/server/src/broadcasters/mod.rs @@ -0,0 +1,7 @@ +//! Systems which broadcast packets. +//! +//! There are two types of broadcasters: +//! * Those which broadcast packets to all online clients through `State::broadcast_global()`. +//! * Those which broadcast packets to all clients who can see a given entity through `State::broadcast_entity_update()`. + +pub mod movement; diff --git a/server/src/broadcasters/movement.rs b/server/src/broadcasters/movement.rs new file mode 100644 index 000000000..cb258dbb3 --- /dev/null +++ b/server/src/broadcasters/movement.rs @@ -0,0 +1,159 @@ +//! Broadcasting of movement updates. + +use crate::chunk_logic::ChunkHolders; +use crate::entity::{EntityId, EntityMoveEvent}; +use crate::network::Network; +use crossbeam::atomic::AtomicCell; +use feather_core::network::packet::implementation::{ + EntityHeadLook, EntityLook, EntityLookAndRelativeMove, EntityRelativeMove, +}; +use feather_core::{Packet, Position}; +use hashbrown::HashMap; +use legion::entity::Entity; +use legion::query::{Read, Write}; +use rayon::prelude::*; +use smallvec::SmallVec; +use tonks::{PreparedWorld, Query}; + +/// Component containing the last sent positions of all entities for a given client. +/// This component is used to determine +/// the relative movement for an entity. +pub struct LastKnownPositions(HashMap>); + +/// System to broadcast when an entity moves. +#[event_handler] +fn broadcast_move( + events: &[EntityMoveEvent], + _query: &mut Query<( + Read, + Read, + Write, + Read, + )>, + world: &mut PreparedWorld, + chunk_holders: &ChunkHolders, +) { + events.par_iter().for_each(|event: &EntityMoveEvent| { + // Find position of entity. + let pos = *world.get_component::(event.entity).unwrap(); + + // Find clients which can see the entity. + let chunk = pos.chunk_pos(); + let clients = chunk_holders.holders_for(chunk).unwrap_or(&[]); + + let entity_id = world.get_component::(event.entity).unwrap().0; + + // For each client, send the position update relative to the client's last known + // position for the entity. If no `LastKnownPositions` entry exists for the entity, + // then the entity has not yet been sent to the client, so we do not send a position + // update. (When an entity is spawned on a client, the `LastKnownPositions` entry + // is inserted with the starting position.) + clients.par_iter().copied().for_each(|client: Entity| { + // Don't sent player's position to themself + if client == event.entity { + return; + } + + let last_known_positions = world.get_component::(client).unwrap(); + if let Some(last_position) = last_known_positions.0.get(&event.entity) { + let old_pos = last_position.load(); + + let packets = packets_for_movement_update(entity_id, old_pos, pos); + + let network = world.get_component::(client).unwrap(); + + packets.into_iter().for_each(|packet| { + network.send_boxed(packet); + }); + + // Update last known position. + last_position.store(pos); + } + }); + }); +} + +/// Returns the packet needed to notify a client +/// of a position update, from the old position to the new one. +#[allow(clippy::float_cmp)] +fn packets_for_movement_update( + entity_id: i32, + old_pos: Position, + new_pos: Position, +) -> SmallVec<[Box; 2]> { + if old_pos == new_pos { + return smallvec![]; + } + + let mut packets = smallvec![]; + + let has_moved = old_pos.x != new_pos.x || old_pos.y != new_pos.y || old_pos.z != new_pos.z; + let has_looked = old_pos.pitch != new_pos.pitch || old_pos.yaw != new_pos.yaw; + + if has_moved { + let (rx, ry, rz) = calculate_relative_move(old_pos, new_pos); + + if (rx == 0 && ry == 0 && rz == 0) && !has_looked { + // Because of floating point errors, + // the physics system may trigger an + // event when the distance moved is minuscule, + // which causes jittering on the client. + // Don't send the packet if it has no effect. + return smallvec![]; + } + + if has_looked { + let packet: Box = Box::new(EntityLookAndRelativeMove::new( + entity_id, + rx, + ry, + rz, + degrees_to_stops(new_pos.yaw), + degrees_to_stops(new_pos.pitch), + new_pos.on_ground, + )); + packets.push(packet); + } else { + let packet: Box = Box::new(EntityRelativeMove::new( + entity_id, + rx, + ry, + rz, + new_pos.on_ground, + )); + packets.push(packet); + } + } else { + let packet: Box = Box::new(EntityLook::new( + entity_id, + degrees_to_stops(new_pos.yaw), + degrees_to_stops(new_pos.pitch), + new_pos.on_ground, + )); + packets.push(packet); + } + + // Entity Head Look also needs to be sent if the entity turned its head + if has_looked { + let packet: Box = Box::new(EntityHeadLook::new( + entity_id, + degrees_to_stops(new_pos.yaw), + )); + packets.push(packet); + } + + packets +} + +/// Calculates the relative move fields +/// as used in the Entity Relative Move packets. +fn calculate_relative_move(old: Position, current: Position) -> (i16, i16, i16) { + let x = ((current.x * 32.0 - old.x * 32.0) * 128.0) as i16; + let y = ((current.y * 32.0 - old.y * 32.0) * 128.0) as i16; + let z = ((current.z * 32.0 - old.z * 32.0) * 128.0) as i16; + (x, y, z) +} + +fn degrees_to_stops(degs: f32) -> u8 { + ((degs / 360.0) * 256.0) as u8 +} diff --git a/server/src/lib.rs b/server/src/lib.rs index c51ed7241..908ef372c 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -145,6 +145,7 @@ use tonks::{Resources, Scheduler}; #[global_allocator] static ALLOC: System = System; +pub mod broadcasters; pub mod chunk_entities; pub mod chunk_logic; pub mod chunk_worker; From 195544f97dcb6d58a2f477c834a1fbbe94ec4190 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 23 Dec 2019 13:33:02 -0700 Subject: [PATCH 061/647] Start on broadcasting entities --- Cargo.lock | 12 +-- core/src/network/packet/implementation.rs | 4 +- server/Cargo.toml | 2 +- server/src/broadcasters/entity_creation.rs | 24 +++++ server/src/broadcasters/mod.rs | 3 +- server/src/broadcasters/movement.rs | 15 +-- server/src/entity/mod.rs | 53 ++++++++++- server/src/lib.rs | 1 + server/src/player/mod.rs | 67 +++++++++++++- server/src/state.rs | 44 +++++++++ server/src/util.rs | 17 ++++ server/src/view.rs | 101 ++++++++++++++++++--- 12 files changed, 303 insertions(+), 40 deletions(-) create mode 100644 server/src/broadcasters/entity_creation.rs create mode 100644 server/src/util.rs diff --git a/Cargo.lock b/Cargo.lock index 1bd82fba4..d33272ac8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -749,7 +749,7 @@ dependencies = [ "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=8f9cae2b2787406f28b5a4c4ef874228f018af90)", + "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=39793d2bf2643802df006725ebff2f71795cd41e)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2602,7 +2602,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=8f9cae2b2787406f28b5a4c4ef874228f018af90#8f9cae2b2787406f28b5a4c4ef874228f018af90" +source = "git+https://github.com/feather-rs/tonks?rev=39793d2bf2643802df006725ebff2f71795cd41e#39793d2bf2643802df006725ebff2f71795cd41e" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2619,13 +2619,13 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=8f9cae2b2787406f28b5a4c4ef874228f018af90)", + "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=39793d2bf2643802df006725ebff2f71795cd41e)", ] [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=8f9cae2b2787406f28b5a4c4ef874228f018af90#8f9cae2b2787406f28b5a4c4ef874228f018af90" +source = "git+https://github.com/feather-rs/tonks?rev=39793d2bf2643802df006725ebff2f71795cd41e#39793d2bf2643802df006725ebff2f71795cd41e" dependencies = [ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3250,8 +3250,8 @@ dependencies = [ "checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" "checksum tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" -"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=8f9cae2b2787406f28b5a4c4ef874228f018af90)" = "" -"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=8f9cae2b2787406f28b5a4c4ef874228f018af90)" = "" +"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=39793d2bf2643802df006725ebff2f71795cd41e)" = "" +"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=39793d2bf2643802df006725ebff2f71795cd41e)" = "" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" "checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" diff --git a/core/src/network/packet/implementation.rs b/core/src/network/packet/implementation.rs index 6b127460b..72a7a5ca0 100644 --- a/core/src/network/packet/implementation.rs +++ b/core/src/network/packet/implementation.rs @@ -1658,8 +1658,8 @@ impl Default for CombatEventType { #[derive(AsAny, new, Clone)] pub struct PlayerInfo { - action: PlayerInfoAction, - uuid: Uuid, + pub action: PlayerInfoAction, + pub uuid: Uuid, } impl Packet for PlayerInfo { diff --git a/server/Cargo.toml b/server/Cargo.toml index 81d8f766f..039eaf9e0 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,7 +21,7 @@ feather-codegen = { path = "../codegen" } # Core ECS + systems legion = { git = "https://github.com/TomGillen/legion", rev = "0f67adc237af35799df173f31a2c238b3d8010a2" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "8f9cae2b2787406f28b5a4c4ef874228f018af90", features = ["system-registry"] } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "39793d2bf2643802df006725ebff2f71795cd41e", features = ["system-registry"] } # tonks = { path = "../../../dev/tonks", features = ["system-registry"] } # Concurrency/threading diff --git a/server/src/broadcasters/entity_creation.rs b/server/src/broadcasters/entity_creation.rs new file mode 100644 index 000000000..6f48dc1e2 --- /dev/null +++ b/server/src/broadcasters/entity_creation.rs @@ -0,0 +1,24 @@ +use crate::entity::{CreationPacketCreator, EntityCreateEvent}; +use crate::state::State; +use legion::query::Read; +use rayon::prelude::*; +use tonks::{PreparedWorld, QueryAccessor}; + +/// When an entity is created and has a `CreationPacketCreator`, +/// broadcasts the packet to all online clients. +#[event_handler] +fn broadcast_entity_creation( + events: &[EntityCreateEvent], + state: &State, + accessor: &QueryAccessor>, + world: &mut PreparedWorld, +) { + events.par_iter().for_each(|event: &EntityCreateEvent| { + if let Some(accessor) = accessor.find(event.entity) { + if let Some(packet_creator) = accessor.get_component::(world) { + let packet = packet_creator.get(&accessor, world); + state.broadcast_global_boxed(packet, None); + } + } + }); +} diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index d792cd490..643b5e2fb 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -1,7 +1,8 @@ -//! Systems which broadcast packets. +//! Systems which broadcast packets based on events. //! //! There are two types of broadcasters: //! * Those which broadcast packets to all online clients through `State::broadcast_global()`. //! * Those which broadcast packets to all clients who can see a given entity through `State::broadcast_entity_update()`. +pub mod entity_creation; pub mod movement; diff --git a/server/src/broadcasters/movement.rs b/server/src/broadcasters/movement.rs index cb258dbb3..0393486d8 100644 --- a/server/src/broadcasters/movement.rs +++ b/server/src/broadcasters/movement.rs @@ -3,6 +3,7 @@ use crate::chunk_logic::ChunkHolders; use crate::entity::{EntityId, EntityMoveEvent}; use crate::network::Network; +use crate::util::{calculate_relative_move, degrees_to_stops}; use crossbeam::atomic::AtomicCell; use feather_core::network::packet::implementation::{ EntityHeadLook, EntityLook, EntityLookAndRelativeMove, EntityRelativeMove, @@ -18,6 +19,7 @@ use tonks::{PreparedWorld, Query}; /// Component containing the last sent positions of all entities for a given client. /// This component is used to determine /// the relative movement for an entity. +#[derive(Default)] pub struct LastKnownPositions(HashMap>); /// System to broadcast when an entity moves. @@ -144,16 +146,3 @@ fn packets_for_movement_update( packets } - -/// Calculates the relative move fields -/// as used in the Entity Relative Move packets. -fn calculate_relative_move(old: Position, current: Position) -> (i16, i16, i16) { - let x = ((current.x * 32.0 - old.x * 32.0) * 128.0) as i16; - let y = ((current.y * 32.0 - old.y * 32.0) * 128.0) as i16; - let z = ((current.z * 32.0 - old.z * 32.0) * 128.0) as i16; - (x, y, z) -} - -fn degrees_to_stops(degs: f32) -> u8 { - ((degs / 360.0) * 256.0) as u8 -} diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index ceaf88f98..d83c9d4a2 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -2,12 +2,12 @@ use crate::lazy::EntityBuilder; use crate::state::State; -use feather_core::Position; +use feather_core::{Packet, Position}; use legion::prelude::Entity; use legion::query::{Read, Write}; use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicI32, Ordering}; -use tonks::{PreparedWorld, Query}; +use tonks::{EntityAccessor, PreparedWorld, Query}; /// ID of an entity. This value is generally unique. #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] @@ -64,7 +64,7 @@ impl DerefMut for Velocity { /// /// Note that unnamed entities do not have this component. #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct NameComponent(pub String); +pub struct Name(pub String); /// Position of an entity on the last tick. /// @@ -72,6 +72,53 @@ pub struct NameComponent(pub String); #[derive(Debug, Clone, Copy)] pub struct PreviousPosition(pub Position); +pub trait PacketCreatorFn: + Fn(&EntityAccessor, &PreparedWorld) -> Box + Send + Sync + 'static +{ +} +impl PacketCreatorFn for F where + F: Fn(&EntityAccessor, &PreparedWorld) -> Box + Send + Sync + 'static +{ +} + +/// Component which defines a function returning a packet to send +/// to clients when the entity comes within range. This packet +/// spawns the entity on the client. +pub struct SpawnPacketCreator(pub &'static dyn PacketCreatorFn); + +impl SpawnPacketCreator { + /// Returns the packet to send to clients when the entity is to be + /// sent to the client. + pub fn get(&self, accessor: &EntityAccessor, world: &PreparedWorld) -> Box { + let f = self.0; + + f(accessor, world) + } +} + +/// Component which defines a function returning a packet to send +/// to _all_ clients when the entity is created or the client joins. +/// This packet is sent before that returned by `SpawnPacketCreator`, +/// and it differs in that the packet is broadcasted globally +/// rather than to nearby clients. +/// +/// Another difference is that the packet from `SpawnPacketCreator` is not sent +/// to its own entity, while that from `CreationPacketCreator` is. +/// +/// An example of a use case for this packet is the `PlayerInfo` packet +/// sent when a player joins—it is sent to all players, not just those +/// that are able to see the player. +pub struct CreationPacketCreator(pub &'static dyn PacketCreatorFn); + +impl CreationPacketCreator { + /// Returns the packet to send to clients when the entity is created. + pub fn get(&self, accessor: &EntityAccessor, world: &PreparedWorld) -> Box { + let f = self.0; + + f(accessor, world) + } +} + #[event_handler] pub fn position_reset( events: &[EntityMoveEvent], diff --git a/server/src/lib.rs b/server/src/lib.rs index 908ef372c..480452e3e 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -161,6 +161,7 @@ pub mod player; pub mod shutdown; pub mod state; pub mod time; +pub mod util; pub mod view; pub mod worldgen; diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index d7877b524..4e6f1a41f 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -1,14 +1,20 @@ //! Systems and components specific to player entities. +use crate::broadcasters::movement::LastKnownPositions; use crate::chunk_logic::ChunkHolder; use crate::entity; -use crate::entity::NameComponent; +use crate::entity::{CreationPacketCreator, EntityId, Name, SpawnPacketCreator}; use crate::io::NewClientInfo; use crate::join::Joined; use crate::network::Network; use crate::state::State; +use crate::util::degrees_to_stops; +use feather_core::network::packet::implementation::{PlayerInfo, PlayerInfoAction, SpawnPlayer}; +use feather_core::{Gamemode, Packet, Position}; use legion::entity::Entity; use mojang_api::ProfileProperty; +use tonks::{EntityAccessor, PreparedWorld}; +use uuid::Uuid; /// Profile properties of a player. #[derive(Debug, Clone)] @@ -39,11 +45,68 @@ pub fn create(state: &State, info: NewClientInfo) { }) .with_component(info.ip) .with_component(ProfileProperties(info.profile)) - .with_component(NameComponent(info.username)) + .with_component(Name(info.username)) .with_component(ChunkHolder::default()) .with_component(Joined(false)) + .with_component(LastKnownPositions::default()) + .with_component(SpawnPacketCreator(&create_spawn_packet)) + .with_component(CreationPacketCreator(&create_initialization_packet)) .with_exec(|_, scheduler, player| { scheduler.trigger(PlayerJoinEvent { player }); }) .build(); } + +/// Function to create a `SpawnPlayer` packet to spawn the player. +fn create_spawn_packet(accessor: &EntityAccessor, world: &PreparedWorld) -> Box { + let entity_id = accessor.get_component::(world).unwrap().0; + let player_uuid = *accessor.get_component::(world).unwrap(); + let pos = *accessor.get_component::(world).unwrap(); + + // TODO: metadata + + let packet = SpawnPlayer { + entity_id, + player_uuid, + x: pos.x, + y: pos.y, + z: pos.z, + yaw: degrees_to_stops(pos.yaw), + pitch: degrees_to_stops(pos.pitch), + metadata: Default::default(), + }; + Box::new(packet) +} + +/// Function to create a `PlayerInfo` packet to broadcast when the player joins. +fn create_initialization_packet( + accessor: &EntityAccessor, + world: &PreparedWorld, +) -> Box { + let name = accessor.get_component::(world).unwrap(); + let props = accessor.get_component::(world).unwrap(); + let uuid = *accessor.get_component::(world).unwrap(); + + let props = props + .0 + .iter() + .map(|prop| { + ( + prop.name.clone(), + prop.value.clone(), + prop.signature.clone(), + ) + }) + .collect::>(); + + let display_name = json!({ + "text": name.0 + }) + .to_string(); + + let action = + PlayerInfoAction::AddPlayer(name.0.clone(), props, Gamemode::Creative, 50, display_name); + + let packet = PlayerInfo { action, uuid }; + Box::new(packet) +} diff --git a/server/src/state.rs b/server/src/state.rs index 1db31a49f..7fc3252e1 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -90,6 +90,36 @@ impl State { }); } + /// Lazily broadcasts a boxed packet to all clients able to see the given entity. + /// + /// The packet will not be sent to `neq`. + pub fn broadcast_entity_update_boxed( + &self, + entity: Entity, + packet: Box, + neq: Option, + ) { + self.exec_with_scheduler(move |world, scheduler| { + // Use ChunkHolders to determine which players have a hold on the entity's + // chunk, which would allow them to see the entity. + let chunk_holders = scheduler.resources().get::(); + + if let Some(position) = world.get_component::(entity) { + let holders = chunk_holders.holders_for(position.chunk_pos()); + + holders.map(|entities| { + for entity in entities { + if let Some(network) = world.get_component::(*entity) { + if neq.map_or(true, |neq| *entity != neq) { + network.send_boxed(packet.box_clone()); + } + } + } + }); + } + }); + } + /// Lazily broadcasts a packet to all clients. pub fn broadcast_global(&self, packet: P, neq: Option) { self.exec(move |world| { @@ -104,6 +134,20 @@ impl State { }); } + /// Lazily broadcasts a boxed packet to all clients. + pub fn broadcast_global_boxed(&self, packet: Box, neq: Option) { + self.exec(move |world| { + // Standard Legion queries! How rare. + let query = >::query(); + + query.par_entities_for_each(world, |(entity, network)| { + if neq.map_or(true, |neq| entity != neq) { + network.send_boxed(packet.box_clone()); + } + }); + }); + } + /// See `Lazy::flush()`. pub fn flush(&self, world: &mut World, scheduler: &mut Scheduler) { self.lazy.flush(world, scheduler); diff --git a/server/src/util.rs b/server/src/util.rs new file mode 100644 index 000000000..6567e5d57 --- /dev/null +++ b/server/src/util.rs @@ -0,0 +1,17 @@ +//! Assorted utility functions. + +use feather_core::Position; + +/// Calculates the relative move fields +/// as used in the Entity Relative Move packets. +pub fn calculate_relative_move(old: Position, current: Position) -> (i16, i16, i16) { + let x = ((current.x * 32.0 - old.x * 32.0) * 128.0) as i16; + let y = ((current.y * 32.0 - old.y * 32.0) * 128.0) as i16; + let z = ((current.z * 32.0 - old.z * 32.0) * 128.0) as i16; + (x, y, z) +} + +/// Converts degrees to stops as used in the protocol. +pub fn degrees_to_stops(degs: f32) -> u8 { + ((degs / 360.0) * 256.0) as u8 +} diff --git a/server/src/view.rs b/server/src/view.rs index 298123a59..510e93c0e 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -22,12 +22,12 @@ use crate::chunk_logic::{ ChunkHolder, ChunkHolderReleaseEvent, ChunkHolders, ChunkLoadEvent, ChunkWorkerHandle, }; use crate::config::Config; -use crate::entity::{EntityMoveEvent, PreviousPosition}; +use crate::entity::{EntityId, EntityMoveEvent, PreviousPosition, SpawnPacketCreator}; use crate::network::Network; use crate::player::PlayerJoinEvent; use crate::state::State; use chashmap::CHashMap; -use feather_core::network::packet::implementation::{ChunkData, UnloadChunk}; +use feather_core::network::packet::implementation::{ChunkData, DestroyEntities, UnloadChunk}; use feather_core::{Chunk, ChunkPosition, Position}; use hashbrown::HashSet; use legion::entity::Entity; @@ -35,7 +35,7 @@ use legion::query::{Read, Write}; use parking_lot::Mutex; use rayon::prelude::*; use smallvec::SmallVec; -use tonks::{PreparedWorld, Query, Trigger}; +use tonks::{PreparedWorld, Query, QueryAccessor, Trigger}; /// Event triggered when a player's view is updated, i.e. when they /// cross into a new chunk or when they join. @@ -47,6 +47,10 @@ pub struct ViewUpdateEvent { /// The old chunk, or `None` if there was no old chunk /// (i.e. this player just joined). pub old_chunk: Option, + /// Old visible chunks. + pub visible_old: HashSet, + /// New visible chunks. + pub visible_new: HashSet, } /// Event triggered when a chunk is sent to a player. @@ -63,6 +67,7 @@ fn view_update( events: &[EntityMoveEvent], _query: &mut Query<(Read, Read)>, world: &mut PreparedWorld, + state: &State, trigger: &mut Trigger, ) { let trigger = Mutex::new(trigger); @@ -73,12 +78,18 @@ fn view_update( .unwrap() .0; + // Find the old chunks and new chunks. + let visible_new = chunks_within_view_distance(&state.config, pos.chunk_pos()); + let visible_old = chunks_within_view_distance(&state.config, prev_pos.chunk_pos()); + if pos.chunk_pos() != prev_pos.chunk_pos() { // New chunk: trigger view update. let event = ViewUpdateEvent { player: event.entity, new_chunk: pos.chunk_pos(), old_chunk: Some(prev_pos.chunk_pos()), + visible_old, + visible_new, }; trigger.lock().trigger(event); } @@ -92,13 +103,19 @@ fn view_update_on_join( _query: &mut Query>, world: &mut PreparedWorld, trigger: &mut Trigger, + state: &State, ) { let position = *world.get_component::(event.player).unwrap(); + // Find the visible chunks. + let visible_new = chunks_within_view_distance(&state.config, position.chunk_pos()); + trigger.trigger(ViewUpdateEvent { player: event.player, new_chunk: position.chunk_pos(), old_chunk: None, + visible_new, + visible_old: HashSet::new(), // No chunks were previously visible, since the player just joined }); } @@ -117,15 +134,8 @@ fn view_handle_chunks( chunk_send_trigger: &mut Trigger, ) { events.iter().for_each(|event| { - // Find the old chunks and new chunks. - let new_chunks = chunks_within_view_distance(&state.config, event.new_chunk); - let old_chunks = match event.old_chunk { - Some(chunk) => chunks_within_view_distance(&state.config, chunk), - None => HashSet::new(), - }; - - let to_send = new_chunks.difference(&old_chunks); - let to_unload = old_chunks.difference(&new_chunks); + let to_send = event.visible_new.difference(&event.visible_old); + let to_unload = event.visible_old.difference(&event.visible_new); let network = world.get_component::(event.player).unwrap(); let mut holder = @@ -166,6 +176,73 @@ fn view_handle_chunks( }); } +/// System which sends new entities and removes +/// old entities on the client when the player's +/// view is updated. +/// +/// Before this event handler is run, `crate::broadcast::entity_creation::broadcast_entity_creation` +/// will run, sending entity initialization packets before spawn packets as dictated +/// by the protocol. +#[event_handler] +fn view_handle_entities( + events: &[ViewUpdateEvent], + state: &State, + _query: &mut Query<(Read, Read)>, + accessor: &QueryAccessor>, + world: &mut PreparedWorld, +) { + events.par_iter().for_each(|event: &ViewUpdateEvent| { + let to_send = event.visible_new.difference(&event.visible_old); + let to_unload = event.visible_old.difference(&event.visible_new); + + let network = world.get_component::(event.player).unwrap(); + + // Send new entities. + to_send.copied().for_each(|chunk| { + let entities = state.chunk_entities.entities_in_chunk(chunk); + + entities.iter().copied().for_each(|entity| { + // Don't send client to themself. + if entity == event.player { + return; + } + + // Attempt to create spawn packet for this entity. + if let Some(accessor) = accessor.find(entity) { + if let Some(packet_creator) = + accessor.get_component::(world) + { + // Send packet. + let packet = packet_creator.get(&accessor, world); + network.send_boxed(packet); + } + } + }); + }); + + // Remove old entities. + let mut to_delete = vec![]; + for chunk in to_unload.copied() { + for entity in state + .chunk_entities + .entities_in_chunk(chunk) + .iter() + .copied() + { + let id = world.get_component::(entity).unwrap().0; + to_delete.push(id); + } + } + + if !to_delete.is_empty() { + let packet = DestroyEntities { + entity_ids: to_delete, + }; + network.send(packet); + } + }); +} + /// Resource containing a mapping from chunks -> sets of players indicating /// which chunks are pending to send to a given player. #[derive(Default, Resource)] From a78e2830e793d22e6912509a34daa0fb2ddebcac Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 4 Jan 2020 20:03:20 -0700 Subject: [PATCH 062/647] Implement keepalives; fix movement + entity broadcasting --- server/src/broadcasters/entity_creation.rs | 63 +++++++++++++++++++--- server/src/broadcasters/keepalive.rs | 12 +++++ server/src/broadcasters/mod.rs | 1 + server/src/broadcasters/movement.rs | 2 +- server/src/state.rs | 20 +++++++ server/src/view.rs | 6 +++ 6 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 server/src/broadcasters/keepalive.rs diff --git a/server/src/broadcasters/entity_creation.rs b/server/src/broadcasters/entity_creation.rs index 6f48dc1e2..88751b5d7 100644 --- a/server/src/broadcasters/entity_creation.rs +++ b/server/src/broadcasters/entity_creation.rs @@ -1,24 +1,75 @@ -use crate::entity::{CreationPacketCreator, EntityCreateEvent}; +use crate::chunk_logic::ChunkHolders; +use crate::entity::{CreationPacketCreator, EntityCreateEvent, SpawnPacketCreator}; +use crate::network::Network; +use crate::player::PlayerJoinEvent; use crate::state::State; +use feather_core::Position; use legion::query::Read; use rayon::prelude::*; -use tonks::{PreparedWorld, QueryAccessor}; +use tonks::{PreparedWorld, Query, QueryAccessor}; -/// When an entity is created and has a `CreationPacketCreator`, -/// broadcasts the packet to all online clients. +/// When an entity is created and has a `CreationPacketCreator` and/or `SpawnPacketCreator`, +/// broadcasts the packets to all online clients. #[event_handler] fn broadcast_entity_creation( events: &[EntityCreateEvent], state: &State, - accessor: &QueryAccessor>, + accessor1: &QueryAccessor>, + accessor2: &QueryAccessor>, + _query: &mut Query>, world: &mut PreparedWorld, + holders: &ChunkHolders, ) { events.par_iter().for_each(|event: &EntityCreateEvent| { - if let Some(accessor) = accessor.find(event.entity) { + if let Some(accessor) = accessor1.find(event.entity) { if let Some(packet_creator) = accessor.get_component::(world) { let packet = packet_creator.get(&accessor, world); state.broadcast_global_boxed(packet, None); } } + + if let Some(accessor) = accessor2.find(event.entity) { + if let Some(packet_creator) = accessor.get_component::(world) { + let packet = packet_creator.get(&accessor, world); + state.broadcast_entity_update_boxed(event.entity, packet, Some(event.entity)); + } + } + + // Register entity sends + let chunk = world + .get_component::(event.entity) + .unwrap() + .chunk_pos(); + for entity in holders.holders_for(chunk).unwrap_or(&[]) { + state.register_entity_send(event.entity, *entity); + } + }); +} + +/// Wehn a player joins, sends existing entities to the player. +/// +/// This only handles init packets (PlayerInfo, etc.)—spawn packets +/// are handled by the view update mechanism. +#[event_handler] +fn broadcast_existing_entities( + events: &[PlayerJoinEvent], + accessor: &QueryAccessor>, + query: &mut Query>, + _query2: &mut Query>, + world: &mut PreparedWorld, + state: &State, +) { + // TODO: change to par_iter when legion implements immutable queries + events.iter().for_each(|event: &PlayerJoinEvent| { + // Send init packets for all entities with a `CreationPacketCreator`. + let network = world.get_component::(event.player).unwrap(); + + query.par_entities_for_each_immutable(world, |(entity, packet_creator)| { + if let Some(accessor) = accessor.find(entity) { + let packet = packet_creator.get(&accessor, world); + network.send_boxed(packet); + state.register_entity_send(entity, event.player); + } + }); }); } diff --git a/server/src/broadcasters/keepalive.rs b/server/src/broadcasters/keepalive.rs new file mode 100644 index 000000000..8a10a8a7d --- /dev/null +++ b/server/src/broadcasters/keepalive.rs @@ -0,0 +1,12 @@ +use crate::state::State; +use crate::{TickCount, TPS}; +use feather_core::network::packet::implementation::KeepAliveClientbound; + +/// Broadcasts keep alives every second. +#[system] +fn broadcast_keepalive(state: &State, tick: &TickCount) { + if tick.0 % TPS == 0 { + let packet = KeepAliveClientbound { keep_alive_id: 0 }; + state.broadcast_global(packet, None); + } +} diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index 643b5e2fb..a5238912f 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -5,4 +5,5 @@ //! * Those which broadcast packets to all clients who can see a given entity through `State::broadcast_entity_update()`. pub mod entity_creation; +pub mod keepalive; pub mod movement; diff --git a/server/src/broadcasters/movement.rs b/server/src/broadcasters/movement.rs index 0393486d8..d43870d8e 100644 --- a/server/src/broadcasters/movement.rs +++ b/server/src/broadcasters/movement.rs @@ -20,7 +20,7 @@ use tonks::{PreparedWorld, Query}; /// This component is used to determine /// the relative movement for an entity. #[derive(Default)] -pub struct LastKnownPositions(HashMap>); +pub struct LastKnownPositions(pub HashMap>); /// System to broadcast when an entity moves. #[event_handler] diff --git a/server/src/state.rs b/server/src/state.rs index 7fc3252e1..3b81d4c57 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -1,8 +1,10 @@ +use crate::broadcasters::movement::LastKnownPositions; use crate::chunk_entities::ChunkEntities; use crate::chunk_logic::ChunkHolders; use crate::config::Config; use crate::lazy::{EntityBuilder, Lazy}; use crate::network::Network; +use crossbeam::atomic::AtomicCell; use feather_blocks::Block; use feather_core::level::LevelData; use feather_core::world::ChunkMap; @@ -195,4 +197,22 @@ impl State { .remove(pos); }); } + + /// Registers that an entity was sent to a player, updating some + /// data structures, such as LastKnownPositions. + pub fn register_entity_send(&self, entity: Entity, to: Entity) { + self.exec(move |world| { + let pos = *world.get_component(entity).unwrap(); + let mut positions = world.get_component_mut::(to).unwrap(); + positions.0.insert(entity, AtomicCell::new(pos)); + }); + } + + /// The opposite of `register_entity_send`. + pub fn register_entity_unload(&self, entity: Entity, on: Entity) { + self.exec(move |world| { + let mut positions = world.get_component_mut::(on).unwrap(); + positions.0.remove(&entity); + }) + } } diff --git a/server/src/view.rs b/server/src/view.rs index 510e93c0e..eec0f42f7 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -201,6 +201,10 @@ fn view_handle_entities( to_send.copied().for_each(|chunk| { let entities = state.chunk_entities.entities_in_chunk(chunk); + if !entities.is_empty() { + dbg!(event.player, &entities); + } + entities.iter().copied().for_each(|entity| { // Don't send client to themself. if entity == event.player { @@ -215,6 +219,7 @@ fn view_handle_entities( // Send packet. let packet = packet_creator.get(&accessor, world); network.send_boxed(packet); + state.register_entity_send(entity, event.player); } } }); @@ -231,6 +236,7 @@ fn view_handle_entities( { let id = world.get_component::(entity).unwrap().0; to_delete.push(id); + state.register_entity_unload(entity, event.player); } } From f90ce5592e2cfd53b80b43a26f209971c4e764f6 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 4 Jan 2020 20:38:12 -0700 Subject: [PATCH 063/647] Broadcast entity deletion; fix a bunch of panics --- Cargo.lock | 12 +++--- server/Cargo.toml | 2 +- server/src/broadcasters/entity_deletion.rs | 46 ++++++++++++++++++++++ server/src/broadcasters/mod.rs | 1 + server/src/broadcasters/movement.rs | 4 ++ server/src/entity/mod.rs | 3 ++ server/src/network.rs | 9 ++++- server/src/state.rs | 10 +++-- 8 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 server/src/broadcasters/entity_deletion.rs diff --git a/Cargo.lock b/Cargo.lock index d33272ac8..460225941 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -749,7 +749,7 @@ dependencies = [ "tokio 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.2.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=39793d2bf2643802df006725ebff2f71795cd41e)", + "tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=ccdecae02e11fc6694bd252d48cce5794499736f)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2602,7 +2602,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=39793d2bf2643802df006725ebff2f71795cd41e#39793d2bf2643802df006725ebff2f71795cd41e" +source = "git+https://github.com/feather-rs/tonks?rev=ccdecae02e11fc6694bd252d48cce5794499736f#ccdecae02e11fc6694bd252d48cce5794499736f" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2619,13 +2619,13 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=39793d2bf2643802df006725ebff2f71795cd41e)", + "tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=ccdecae02e11fc6694bd252d48cce5794499736f)", ] [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=39793d2bf2643802df006725ebff2f71795cd41e#39793d2bf2643802df006725ebff2f71795cd41e" +source = "git+https://github.com/feather-rs/tonks?rev=ccdecae02e11fc6694bd252d48cce5794499736f#ccdecae02e11fc6694bd252d48cce5794499736f" dependencies = [ "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3250,8 +3250,8 @@ dependencies = [ "checksum tokio-timer 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" "checksum tokio-tls 0.3.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" -"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=39793d2bf2643802df006725ebff2f71795cd41e)" = "" -"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=39793d2bf2643802df006725ebff2f71795cd41e)" = "" +"checksum tonks 0.1.0 (git+https://github.com/feather-rs/tonks?rev=ccdecae02e11fc6694bd252d48cce5794499736f)" = "" +"checksum tonks-macros 0.1.0 (git+https://github.com/feather-rs/tonks?rev=ccdecae02e11fc6694bd252d48cce5794499736f)" = "" "checksum tower-make 0.3.0-alpha.2a (registry+https://github.com/rust-lang/crates.io-index)" = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" "checksum tower-service 0.3.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" "checksum tracing 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ff9457accc293386c20e8f754d0b059e67e325edf2284f04230d125d7e5ff" diff --git a/server/Cargo.toml b/server/Cargo.toml index 039eaf9e0..c39e4e0ed 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,7 +21,7 @@ feather-codegen = { path = "../codegen" } # Core ECS + systems legion = { git = "https://github.com/TomGillen/legion", rev = "0f67adc237af35799df173f31a2c238b3d8010a2" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "39793d2bf2643802df006725ebff2f71795cd41e", features = ["system-registry"] } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "ccdecae02e11fc6694bd252d48cce5794499736f", features = ["system-registry"] } # tonks = { path = "../../../dev/tonks", features = ["system-registry"] } # Concurrency/threading diff --git a/server/src/broadcasters/entity_deletion.rs b/server/src/broadcasters/entity_deletion.rs new file mode 100644 index 000000000..e5848c921 --- /dev/null +++ b/server/src/broadcasters/entity_deletion.rs @@ -0,0 +1,46 @@ +use crate::chunk_logic::ChunkHolders; +use crate::entity::EntityDeleteEvent; +use crate::network::Network; +use crate::player::Player; +use crate::state::State; +use feather_core::network::packet::implementation::{ + DestroyEntities, PlayerInfo, PlayerInfoAction, +}; +use legion::query::Read; +use rayon::prelude::*; +use tonks::{PreparedWorld, Query}; + +/// Broadcasts when an entity is deleted. +#[event_handler] +fn broadcast_entity_deletion( + events: &[EntityDeleteEvent], + holders: &ChunkHolders, + _query: &mut Query<(Read, Read)>, + world: &mut PreparedWorld, + state: &State, +) { + events.par_iter().for_each(|event: &EntityDeleteEvent| { + if let Some(pos) = event.position { + let chunk = pos.chunk_pos(); + + for entity in holders.holders_for(chunk).unwrap_or(&[]) { + if let Some(network) = world.get_component::(*entity) { + network.send(DestroyEntities { + entity_ids: vec![event.id.0], + }); + state.register_entity_unload(event.entity, *entity); + } + } + } + + // If entity was a player, broadcast PlayerInfo with delete status + if world.get_component::(event.entity).is_some() { + let packet = PlayerInfo { + action: PlayerInfoAction::RemovePlayer, + uuid: event.uuid, + }; + + state.broadcast_global(packet, None); + } + }); +} diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index a5238912f..a1d8be0e4 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -5,5 +5,6 @@ //! * Those which broadcast packets to all clients who can see a given entity through `State::broadcast_entity_update()`. pub mod entity_creation; +pub mod entity_deletion; pub mod keepalive; pub mod movement; diff --git a/server/src/broadcasters/movement.rs b/server/src/broadcasters/movement.rs index d43870d8e..ce057595e 100644 --- a/server/src/broadcasters/movement.rs +++ b/server/src/broadcasters/movement.rs @@ -56,6 +56,10 @@ fn broadcast_move( return; } + if !world.is_alive(client) { + return; + } + let last_known_positions = world.get_component::(client).unwrap(); if let Some(last_position) = last_known_positions.0.get(&event.entity) { let old_pos = last_position.load(); diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index d83c9d4a2..727852dce 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -8,6 +8,7 @@ use legion::query::{Read, Write}; use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicI32, Ordering}; use tonks::{EntityAccessor, PreparedWorld, Query}; +use uuid::Uuid; /// ID of an entity. This value is generally unique. #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] @@ -27,6 +28,8 @@ pub struct EntityCreateEvent { pub struct EntityDeleteEvent { pub entity: Entity, pub position: Option, + pub id: EntityId, + pub uuid: Uuid, } /// Event triggered when an entity moves. diff --git a/server/src/network.rs b/server/src/network.rs index f45c4faaa..2921b7192 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -6,7 +6,7 @@ //! from players and allows systems to poll for packets //! received of a given type. -use crate::entity::EntityDeleteEvent; +use crate::entity::{EntityDeleteEvent, EntityId}; use crate::io::{ListenerToServerMessage, NetworkIoManager, ServerToWorkerMessage}; use crate::player; use crate::state::State; @@ -21,6 +21,7 @@ use parking_lot::{Mutex, MutexGuard}; use std::iter; use strum::EnumCount; use tonks::{PreparedWorld, Query}; +use uuid::Uuid; type QueuedPackets = Vec<(Entity, Box)>; @@ -181,12 +182,16 @@ pub fn network_( match msg { ServerToWorkerMessage::NotifyDisconnect(_) => { state.exec_with_scheduler(move |world, scheduler| { - assert!(world.delete(entity), "player already deleted"); let position = *world.get_component::(entity).unwrap(); + let id = *world.get_component::(entity).unwrap(); + let uuid = *world.get_component::(entity).unwrap(); scheduler.trigger(EntityDeleteEvent { entity, position: Some(position), + id, + uuid, }); + assert!(world.delete(entity), "player already deleted"); }); } ServerToWorkerMessage::NotifyPacketReceived(packet) => { diff --git a/server/src/state.rs b/server/src/state.rs index 3b81d4c57..1d0a8d676 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -203,16 +203,18 @@ impl State { pub fn register_entity_send(&self, entity: Entity, to: Entity) { self.exec(move |world| { let pos = *world.get_component(entity).unwrap(); - let mut positions = world.get_component_mut::(to).unwrap(); - positions.0.insert(entity, AtomicCell::new(pos)); + if let Some(mut positions) = world.get_component_mut::(to) { + positions.0.insert(entity, AtomicCell::new(pos)); + } }); } /// The opposite of `register_entity_send`. pub fn register_entity_unload(&self, entity: Entity, on: Entity) { self.exec(move |world| { - let mut positions = world.get_component_mut::(on).unwrap(); - positions.0.remove(&entity); + if let Some(mut positions) = world.get_component_mut::(on) { + positions.0.remove(&entity); + } }) } } From ae94e6e0c0f46dd85d967fe4586b67b55a247847 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 4 Jan 2020 20:45:26 -0700 Subject: [PATCH 064/647] Implement player hand animations --- server/src/broadcasters/animation.rs | 21 +++++++++++++++++++++ server/src/broadcasters/mod.rs | 1 + server/src/packet_handlers/animation.rs | 20 ++++++++++++++++++++ server/src/packet_handlers/mod.rs | 1 + server/src/packet_handlers/movement.rs | 2 +- server/src/player/mod.rs | 9 ++++++++- 6 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 server/src/broadcasters/animation.rs create mode 100644 server/src/packet_handlers/animation.rs diff --git a/server/src/broadcasters/animation.rs b/server/src/broadcasters/animation.rs new file mode 100644 index 000000000..a239e3806 --- /dev/null +++ b/server/src/broadcasters/animation.rs @@ -0,0 +1,21 @@ +use crate::entity::EntityId; +use crate::player::PlayerAnimationEvent; +use crate::state::State; +use feather_core::network::packet::implementation::AnimationClientbound; +use legion::query::Read; +use tonks::{PreparedWorld, Query}; + +/// Broadcasts animations. +#[event_handler] +fn broadcast_animation( + event: &PlayerAnimationEvent, + state: &State, + _query: &mut Query>, + world: &mut PreparedWorld, +) { + let packet = AnimationClientbound { + entity_id: world.get_component::(event.player).unwrap().0, + animation: event.animation, + }; + state.broadcast_entity_update(event.player, packet, Some(event.player)); +} diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index a1d8be0e4..7da636247 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -4,6 +4,7 @@ //! * Those which broadcast packets to all online clients through `State::broadcast_global()`. //! * Those which broadcast packets to all clients who can see a given entity through `State::broadcast_entity_update()`. +mod animation; pub mod entity_creation; pub mod entity_deletion; pub mod keepalive; diff --git a/server/src/packet_handlers/animation.rs b/server/src/packet_handlers/animation.rs new file mode 100644 index 000000000..e42a10d3f --- /dev/null +++ b/server/src/packet_handlers/animation.rs @@ -0,0 +1,20 @@ +use crate::network::PacketQueue; +use crate::player::PlayerAnimationEvent; +use feather_core::network::packet::implementation::AnimationServerbound; +use feather_core::{ClientboundAnimation, Hand}; +use tonks::Trigger; + +/// Handles animation packets. +#[system] +fn handle_animation(queue: &PacketQueue, trigger: &mut Trigger) { + queue + .received::() + .for_each(|(player, packet)| { + let animation = match packet.hand { + Hand::Main => ClientboundAnimation::SwingMainArm, + Hand::Off => ClientboundAnimation::SwingOffhand, + }; + + trigger.trigger(PlayerAnimationEvent { player, animation }); + }); +} diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index f25137873..3af94b557 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -1,3 +1,4 @@ //! Systems which handle packets through `crate::network::PacketQueue`. +mod animation; mod movement; diff --git a/server/src/packet_handlers/movement.rs b/server/src/packet_handlers/movement.rs index ecd230a32..41240d4a1 100644 --- a/server/src/packet_handlers/movement.rs +++ b/server/src/packet_handlers/movement.rs @@ -13,7 +13,7 @@ struct Buf(Vec<(Entity, Position)>); /// Handles player movement packets. #[system] -fn movement( +fn handle_movement( queue: &PacketQueue, _query: &mut Query>, world: &mut PreparedWorld, diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index 4e6f1a41f..67bdab92a 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -10,7 +10,7 @@ use crate::network::Network; use crate::state::State; use crate::util::degrees_to_stops; use feather_core::network::packet::implementation::{PlayerInfo, PlayerInfoAction, SpawnPlayer}; -use feather_core::{Gamemode, Packet, Position}; +use feather_core::{ClientboundAnimation, Gamemode, Packet, Position}; use legion::entity::Entity; use mojang_api::ProfileProperty; use tonks::{EntityAccessor, PreparedWorld}; @@ -26,6 +26,13 @@ pub struct PlayerJoinEvent { pub player: Entity, } +/// Event triggered when a player causes an animation. +#[derive(Debug, Clone)] +pub struct PlayerAnimationEvent { + pub player: Entity, + pub animation: ClientboundAnimation, +} + /// Tag used to mark a player. /// /// Note that this is a _tag_, not a component. From f14870ee39069d62f51b753438dbdca98f617205 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 5 Jan 2020 11:48:21 -0700 Subject: [PATCH 065/647] Fix weird movement behavior by not using parallel iterators in movement broadcaster --- server/src/broadcasters/keepalive.rs | 2 +- server/src/broadcasters/movement.rs | 22 +++++++++++----------- server/src/state.rs | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/server/src/broadcasters/keepalive.rs b/server/src/broadcasters/keepalive.rs index 8a10a8a7d..ddf748ab3 100644 --- a/server/src/broadcasters/keepalive.rs +++ b/server/src/broadcasters/keepalive.rs @@ -2,7 +2,7 @@ use crate::state::State; use crate::{TickCount, TPS}; use feather_core::network::packet::implementation::KeepAliveClientbound; -/// Broadcasts keep alives every second. +/// Broadcasts keepalives every second. #[system] fn broadcast_keepalive(state: &State, tick: &TickCount) { if tick.0 % TPS == 0 { diff --git a/server/src/broadcasters/movement.rs b/server/src/broadcasters/movement.rs index ce057595e..c0d3be05b 100644 --- a/server/src/broadcasters/movement.rs +++ b/server/src/broadcasters/movement.rs @@ -4,7 +4,6 @@ use crate::chunk_logic::ChunkHolders; use crate::entity::{EntityId, EntityMoveEvent}; use crate::network::Network; use crate::util::{calculate_relative_move, degrees_to_stops}; -use crossbeam::atomic::AtomicCell; use feather_core::network::packet::implementation::{ EntityHeadLook, EntityLook, EntityLookAndRelativeMove, EntityRelativeMove, }; @@ -20,7 +19,7 @@ use tonks::{PreparedWorld, Query}; /// This component is used to determine /// the relative movement for an entity. #[derive(Default)] -pub struct LastKnownPositions(pub HashMap>); +pub struct LastKnownPositions(pub HashMap); /// System to broadcast when an entity moves. #[event_handler] @@ -35,7 +34,7 @@ fn broadcast_move( world: &mut PreparedWorld, chunk_holders: &ChunkHolders, ) { - events.par_iter().for_each(|event: &EntityMoveEvent| { + events.iter().for_each(|event: &EntityMoveEvent| { // Find position of entity. let pos = *world.get_component::(event.entity).unwrap(); @@ -50,7 +49,7 @@ fn broadcast_move( // then the entity has not yet been sent to the client, so we do not send a position // update. (When an entity is spawned on a client, the `LastKnownPositions` entry // is inserted with the starting position.) - clients.par_iter().copied().for_each(|client: Entity| { + clients.iter().copied().for_each(|client: Entity| { // Don't sent player's position to themself if client == event.entity { return; @@ -60,11 +59,10 @@ fn broadcast_move( return; } - let last_known_positions = world.get_component::(client).unwrap(); - if let Some(last_position) = last_known_positions.0.get(&event.entity) { - let old_pos = last_position.load(); - - let packets = packets_for_movement_update(entity_id, old_pos, pos); + let mut last_known_positions = + unsafe { world.get_component_mut_unchecked::(client) }.unwrap(); + if let Some(old_pos) = last_known_positions.0.get_mut(&event.entity) { + let packets = packets_for_movement_update(entity_id, *old_pos, pos); let network = world.get_component::(client).unwrap(); @@ -73,7 +71,7 @@ fn broadcast_move( }); // Update last known position. - last_position.store(pos); + *old_pos = pos; } }); }); @@ -94,7 +92,9 @@ fn packets_for_movement_update( let mut packets = smallvec![]; let has_moved = old_pos.x != new_pos.x || old_pos.y != new_pos.y || old_pos.z != new_pos.z; - let has_looked = old_pos.pitch != new_pos.pitch || old_pos.yaw != new_pos.yaw; + let has_looked = old_pos.pitch != new_pos.pitch + || old_pos.yaw != new_pos.yaw + || old_pos.on_ground != new_pos.on_ground; if has_moved { let (rx, ry, rz) = calculate_relative_move(old_pos, new_pos); diff --git a/server/src/state.rs b/server/src/state.rs index 1d0a8d676..c13605acd 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -204,7 +204,7 @@ impl State { self.exec(move |world| { let pos = *world.get_component(entity).unwrap(); if let Some(mut positions) = world.get_component_mut::(to) { - positions.0.insert(entity, AtomicCell::new(pos)); + positions.0.insert(entity, pos); } }); } From 4d199a4cc448747787db1b2d998f54326323cc88 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 5 Jan 2020 12:06:18 -0700 Subject: [PATCH 066/647] Add opt-level=1 to profile.dev --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 5fa8c4a44..d9d83091f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,6 @@ members = [ "generator", "util/rand-legacy", ] + +[profile.dev] +opt-level = 1 From 0e92f1fe7e37a6af68ec04e9fe19e0d3ec278fe5 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 12 Jan 2020 15:27:30 -0700 Subject: [PATCH 067/647] Reimplement all original inventory functionality --- server/src/broadcasters/inventory.rs | 116 ++++++++++++++++++++++++ server/src/broadcasters/mod.rs | 5 +- server/src/broadcasters/movement.rs | 1 - server/src/entity/mod.rs | 8 ++ server/src/lib.rs | 3 + server/src/p_inventory.rs | 111 +++++++++++++++++++++++ server/src/packet_handlers/inventory.rs | 113 +++++++++++++++++++++++ server/src/packet_handlers/mod.rs | 1 + server/src/player/mod.rs | 3 + server/src/state.rs | 6 +- server/src/util.rs | 46 ++++++++++ 11 files changed, 409 insertions(+), 4 deletions(-) create mode 100644 server/src/broadcasters/inventory.rs create mode 100644 server/src/p_inventory.rs create mode 100644 server/src/packet_handlers/inventory.rs diff --git a/server/src/broadcasters/inventory.rs b/server/src/broadcasters/inventory.rs new file mode 100644 index 000000000..2b22f34e0 --- /dev/null +++ b/server/src/broadcasters/inventory.rs @@ -0,0 +1,116 @@ +//! Broadcasting of inventory-related events. + +use crate::entity::{EntityId, EntitySendEvent}; +use crate::network::Network; +use crate::p_inventory::{EntityInventory, Equipment, InventoryUpdateEvent}; +use crate::state::State; +use feather_core::inventory::{SlotIndex, SLOT_HOTBAR_OFFSET}; +use feather_core::network::packet::implementation::{EntityEquipment, SetSlot}; +use legion::query::Read; +use num_traits::ToPrimitive; +use tonks::{PreparedWorld, Query}; + +/// System for broadcasting equipment updates. +#[event_handler] +fn broadcast_equipment_updates( + event: &InventoryUpdateEvent, + state: &State, + _query: &mut Query<(Read, Read)>, + world: &mut PreparedWorld, +) { + let inv = world + .get_component::(event.player) + .unwrap(); + + for slot in &event.slots { + // Skip this slot if it is not an equipment update. + if let Ok(equipment) = is_equipment_update(&inv, *slot) { + let slot = equipment.slot_index(inv.held_item); + let item = inv.item_at(slot).cloned(); + + let packet = EntityEquipment { + entity_id: world.get_component::(event.player).unwrap().0, + slot: equipment.to_i32().unwrap(), + item, + }; + + state.broadcast_entity_update(event.player, packet, Some(event.player)); + } + } +} + +/// System which listens to `EntitySendEvent`s and +/// sends entity equipment alongside. +#[event_handler] +fn send_entity_equipment( + event: &EntitySendEvent, + _query: &mut Query<(Read, Read, Read)>, + world: &mut PreparedWorld, +) { + let network = world.get_component::(event.to).unwrap(); + let inventory = match world.get_component::(event.entity) { + Some(inv) => inv, + None => return, + }; + + let equipments = [ + Equipment::MainHand, + Equipment::Boots, + Equipment::Leggings, + Equipment::Chestplate, + Equipment::Helmet, + Equipment::OffHand, + ]; + + for equipment in equipments.iter() { + let item = { + let slot = equipment.slot_index(inventory.held_item); + inventory.item_at(slot).cloned() + }; + + let equipment_slot = equipment.to_i32().unwrap(); + + let packet = EntityEquipment { + entity_id: world.get_component::(event.entity).unwrap().0, + slot: equipment_slot, + item, + }; + network.send(packet); + } +} + +/// System for sending the Set Slot packet +/// when a player's inventory is updated. +#[event_handler] +fn send_set_slot( + event: &InventoryUpdateEvent, + _query: &mut Query<(Read, Read)>, + world: &mut PreparedWorld, +) { + let inv = world + .get_component::(event.player) + .unwrap(); + let network = world.get_component::(event.player).unwrap(); + + for slot in &event.slots { + let packet = SetSlot { + window_id: 0, + slot: *slot as i16, + slot_data: inv.item_at(*slot as usize).cloned(), + }; + + network.send(packet); + } +} + +/// Returns whether the given update to an inventory +/// is an equipment update. +fn is_equipment_update(inv: &EntityInventory, slot: SlotIndex) -> Result { + if slot >= SLOT_HOTBAR_OFFSET && slot - SLOT_HOTBAR_OFFSET == inv.held_item { + Ok(Equipment::MainHand) + } else if let Some(equipment) = Equipment::from_slot_index(slot) { + Ok(equipment) + } else { + Err(()) + } +} diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index 7da636247..f714a22b1 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -1,11 +1,14 @@ //! Systems which broadcast packets based on events. //! -//! There are two types of broadcasters: +//! There are three types of broadcasters: //! * Those which broadcast packets to all online clients through `State::broadcast_global()`. //! * Those which broadcast packets to all clients who can see a given entity through `State::broadcast_entity_update()`. +//! * Those which send additional packets, such as equipment, etc. after entity spawning +//! packets have been sent. This is done through `EntitySendEvent`. mod animation; pub mod entity_creation; pub mod entity_deletion; +mod inventory; pub mod keepalive; pub mod movement; diff --git a/server/src/broadcasters/movement.rs b/server/src/broadcasters/movement.rs index c0d3be05b..26a0ee7f5 100644 --- a/server/src/broadcasters/movement.rs +++ b/server/src/broadcasters/movement.rs @@ -11,7 +11,6 @@ use feather_core::{Packet, Position}; use hashbrown::HashMap; use legion::entity::Entity; use legion::query::{Read, Write}; -use rayon::prelude::*; use smallvec::SmallVec; use tonks::{PreparedWorld, Query}; diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 727852dce..064098527 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -23,6 +23,14 @@ pub struct EntityCreateEvent { pub entity: Entity, } +/// Event triggered when an entity is spawned +/// on a client. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EntitySendEvent { + pub entity: Entity, + pub to: Entity, +} + /// Event triggered when an entity is removed. #[derive(Debug, Clone, Copy, PartialEq)] pub struct EntityDeleteEvent { diff --git a/server/src/lib.rs b/server/src/lib.rs index 480452e3e..2482324e8 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -113,6 +113,8 @@ extern crate feather_core; extern crate bitflags; #[macro_use] extern crate tonks; +#[macro_use] +extern crate num_derive; extern crate nalgebra_glm as glm; @@ -155,6 +157,7 @@ pub mod io; pub mod join; pub mod lazy; pub mod network; +pub mod p_inventory; // Prefixed to avoid conflict with inventory crate pub mod packet_handlers; pub mod physics; pub mod player; diff --git a/server/src/p_inventory.rs b/server/src/p_inventory.rs new file mode 100644 index 000000000..bf23f1aaf --- /dev/null +++ b/server/src/p_inventory.rs @@ -0,0 +1,111 @@ +use feather_core::inventory::{ + Inventory, InventoryType, SlotIndex, SLOT_ARMOR_CHEST, SLOT_ARMOR_FEET, SLOT_ARMOR_HEAD, + SLOT_ARMOR_LEGS, SLOT_HOTBAR_OFFSET, SLOT_OFFHAND, +}; +use feather_core::ItemStack; +use legion::entity::Entity; +use smallvec::SmallVec; +use std::ops::{Deref, DerefMut}; + +/// Component for storing a player's inventory. +#[derive(Clone, Debug)] +pub struct EntityInventory { + pub inventory: Inventory, + /// The player's held item. + /// This is stored as an index in the range 0..9. + pub held_item: SlotIndex, +} + +impl EntityInventory { + pub fn new() -> Self { + Self { + inventory: Inventory::new(InventoryType::Player, 46), + held_item: 0, + } + } + + /// Returns the item in this inventory's + /// main hand. + pub fn item_in_main_hand(&self) -> Option<&ItemStack> { + self.inventory.item_at(SLOT_HOTBAR_OFFSET + self.held_item) + } + + /// Sets the item in this inventory's main hand. + pub fn set_item_in_main_hand(&mut self, item: ItemStack) { + self.inventory + .set_item_at(SLOT_HOTBAR_OFFSET + self.held_item, item); + } +} + +impl Default for EntityInventory { + fn default() -> Self { + Self::new() + } +} + +impl Deref for EntityInventory { + type Target = Inventory; + + fn deref(&self) -> &Self::Target { + &self.inventory + } +} + +impl DerefMut for EntityInventory { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inventory + } +} + +/// An equipment slot, with variants +/// listed in the order of the Entity Equipment +/// IDs to allow for easy conversion using `ToPrimitive`/`FromPrimitive`. +#[derive(Debug, Clone, Copy, ToPrimitive, FromPrimitive, PartialEq, Eq, Hash)] +pub enum Equipment { + MainHand, + OffHand, + Boots, + Leggings, + Chestplate, + Helmet, +} + +impl Equipment { + pub fn from_slot_index(index: SlotIndex) -> Option { + match index { + SLOT_OFFHAND => Some(Equipment::OffHand), + SLOT_ARMOR_FEET => Some(Equipment::Boots), + SLOT_ARMOR_LEGS => Some(Equipment::Leggings), + SLOT_ARMOR_CHEST => Some(Equipment::Chestplate), + SLOT_ARMOR_HEAD => Some(Equipment::Helmet), + _ => None, + } + } + + pub fn slot_index(self, held_item: SlotIndex) -> SlotIndex { + match self { + Equipment::MainHand => held_item + SLOT_HOTBAR_OFFSET, + Equipment::OffHand => SLOT_OFFHAND, + Equipment::Boots => SLOT_ARMOR_FEET, + Equipment::Leggings => SLOT_ARMOR_LEGS, + Equipment::Chestplate => SLOT_ARMOR_CHEST, + Equipment::Helmet => SLOT_ARMOR_HEAD, + } + } +} + +/// Event which is triggered when a player +/// updates their inventory. +/// +/// This event could also be triggered when the player +/// changes their held item. +#[derive(Debug, Clone)] +pub struct InventoryUpdateEvent { + /// The slot(s) affected by the update. + /// + /// Multiple slots could be affected when, for + /// example, a player uses the "drag" inventory interaction. + pub slots: SmallVec<[SlotIndex; 2]>, + /// The player owning the updated inventory. + pub player: Entity, +} diff --git a/server/src/packet_handlers/inventory.rs b/server/src/packet_handlers/inventory.rs new file mode 100644 index 000000000..239498f07 --- /dev/null +++ b/server/src/packet_handlers/inventory.rs @@ -0,0 +1,113 @@ +//! Handling of inventory update packets. +//! This currently includes Creative Inventory Action and Held Item Change. + +use crate::network::PacketQueue; +use crate::p_inventory::{EntityInventory, InventoryUpdateEvent}; +use crate::state::State; +use crate::util::disconnect_player; +use feather_core::inventory::{HOTBAR_SIZE, SLOT_HOTBAR_OFFSET}; +use feather_core::network::packet::implementation::{ + CreativeInventoryAction, HeldItemChangeServerbound, +}; +use feather_core::Gamemode; +use legion::prelude::Read; +use legion::query::Write; +use tonks::{PreparedWorld, Query, Trigger}; + +/// System for handling Creative Inventory Action packets. +#[system] +fn handle_creative_inventory_action( + state: &State, + queue: &PacketQueue, + _query: &mut Query<(Read, Write)>, + world: &mut PreparedWorld, + trigger: &mut Trigger, +) { + let packets = queue.received::(); + + for (player, packet) in packets { + // Creative Inventory Action can only be used in creative + // mode. + let gamemode = *world.get_component::(player).unwrap(); + if gamemode != Gamemode::Creative { + disconnect_player( + state, + player, + "Attempted to use Creative Inventory Action while not in creative mode", + ); + continue; + } + + let mut inventory = world.get_component_mut::(player).unwrap(); + + // Slot -1 means that the user clicked outside the window, + // dropping the item. + // TODO: implement this + if packet.slot == -1 { + match &packet.clicked_item { + Some(_) => { + /*let event = PlayerItemDropEvent { + slot: None, + stack: stack.clone(), + player, + }; + drop_events.single_write(event); + + // No need to update inventory + continue;*/ + } + None => (), + } + } + + if packet.slot >= inventory.slot_count() as i16 || packet.slot < -1 { + disconnect_player(state, player, "Slot index out of bounds"); + continue; + } + + match packet.clicked_item.as_ref() { + Some(item) => { + inventory.set_item_at(packet.slot as usize, item.clone()); + } + None => { + inventory.clear_item_at(packet.slot as usize); + } + } + + // Trigger inventory update event + let event = InventoryUpdateEvent { + slots: smallvec![packet.slot as usize], + player, + }; + trigger.trigger(event); + } +} + +/// System for handling Held Item Change packets. +#[system] +fn handle_held_item_change( + state: &State, + queue: &PacketQueue, + _query: &mut Query>, + world: &mut PreparedWorld, + trigger: &mut Trigger, +) { + let packets = queue.received::(); + + for (player, packet) in packets { + if packet.slot as usize >= HOTBAR_SIZE { + disconnect_player(state, player, "Hotbar index out of bounds"); + continue; + } + + let mut inventory = world.get_component_mut::(player).unwrap(); + inventory.held_item = packet.slot as usize; + + // Trigger event + let event = InventoryUpdateEvent { + slots: smallvec![inventory.held_item as usize + SLOT_HOTBAR_OFFSET], + player, + }; + trigger.trigger(event); + } +} diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index 3af94b557..c34a9a4fa 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -1,4 +1,5 @@ //! Systems which handle packets through `crate::network::PacketQueue`. mod animation; +mod inventory; mod movement; diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index 67bdab92a..8e379af77 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -7,6 +7,7 @@ use crate::entity::{CreationPacketCreator, EntityId, Name, SpawnPacketCreator}; use crate::io::NewClientInfo; use crate::join::Joined; use crate::network::Network; +use crate::p_inventory::EntityInventory; use crate::state::State; use crate::util::degrees_to_stops; use feather_core::network::packet::implementation::{PlayerInfo, PlayerInfoAction, SpawnPlayer}; @@ -58,6 +59,8 @@ pub fn create(state: &State, info: NewClientInfo) { .with_component(LastKnownPositions::default()) .with_component(SpawnPacketCreator(&create_spawn_packet)) .with_component(CreationPacketCreator(&create_initialization_packet)) + .with_component(Gamemode::Creative) // TOOD: proper gamemode handling + .with_component(EntityInventory::default()) .with_exec(|_, scheduler, player| { scheduler.trigger(PlayerJoinEvent { player }); }) diff --git a/server/src/state.rs b/server/src/state.rs index c13605acd..d59f42da6 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -2,9 +2,9 @@ use crate::broadcasters::movement::LastKnownPositions; use crate::chunk_entities::ChunkEntities; use crate::chunk_logic::ChunkHolders; use crate::config::Config; +use crate::entity::EntitySendEvent; use crate::lazy::{EntityBuilder, Lazy}; use crate::network::Network; -use crossbeam::atomic::AtomicCell; use feather_blocks::Block; use feather_core::level::LevelData; use feather_core::world::ChunkMap; @@ -201,11 +201,13 @@ impl State { /// Registers that an entity was sent to a player, updating some /// data structures, such as LastKnownPositions. pub fn register_entity_send(&self, entity: Entity, to: Entity) { - self.exec(move |world| { + self.exec_with_scheduler(move |world, scheduler| { let pos = *world.get_component(entity).unwrap(); if let Some(mut positions) = world.get_component_mut::(to) { positions.0.insert(entity, pos); } + + scheduler.trigger(EntitySendEvent { entity, to }); }); } diff --git a/server/src/util.rs b/server/src/util.rs index 6567e5d57..378c17a9f 100644 --- a/server/src/util.rs +++ b/server/src/util.rs @@ -1,6 +1,13 @@ //! Assorted utility functions. +use crate::entity::{EntityDeleteEvent, EntityId, Name}; +use crate::io::ServerToWorkerMessage; +use crate::network::Network; +use crate::state::State; use feather_core::Position; +use legion::entity::Entity; +use std::borrow::Cow; +use uuid::Uuid; /// Calculates the relative move fields /// as used in the Entity Relative Move packets. @@ -15,3 +22,42 @@ pub fn calculate_relative_move(old: Position, current: Position) -> (i16, i16, i pub fn degrees_to_stops(degs: f32) -> u8 { ((degs / 360.0) * 256.0) as u8 } + +/// Disconnects a player. +pub fn disconnect_player(state: &State, player: Entity, reason: impl Into>) { + let reason = reason.into(); + + state.exec_with_scheduler(move |world, scheduler| { + { + let username = world.get_component::(player).unwrap(); + info!("Disconnecting player {}: {}", username.0, reason); + + let network = world.get_component::(player).unwrap(); + network + .sender + .unbounded_send(ServerToWorkerMessage::Disconnect) + .unwrap(); + + let position = *world.get_component::(player).unwrap(); + let id = *world.get_component::(player).unwrap(); + let uuid = *world.get_component::(player).unwrap(); + + scheduler.trigger(EntityDeleteEvent { + entity: player, + position: Some(position), + id, + uuid, + }); + + let event = EntityDeleteEvent { + entity: player, + position: Some(position), + id, + uuid, + }; + scheduler.trigger(event); + } + + world.delete(player); + }); +} From 1a8934c7a4973ed70e94e034459c04390d54b797 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 12 Jan 2020 15:57:39 -0700 Subject: [PATCH 068/647] Implement block update infrastructure --- Cargo.lock | 4 +- server/Cargo.toml | 2 +- server/src/block.rs | 27 +++++ server/src/lib.rs | 7 +- server/src/packet_handlers/block.rs | 24 ++++ server/src/packet_handlers/mod.rs | 1 + server/src/state.rs | 175 ++++++++++++++++++++++++---- 7 files changed, 213 insertions(+), 27 deletions(-) create mode 100644 server/src/block.rs create mode 100644 server/src/packet_handlers/block.rs diff --git a/Cargo.lock b/Cargo.lock index e987f7520..696a7a1b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2867,7 +2867,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=ccdecae02e11fc6694bd252d48cce5794499736f#ccdecae02e11fc6694bd252d48cce5794499736f" +source = "git+https://github.com/feather-rs/tonks?rev=a8a39d0f8d51a86a935b708090ec44debfe7bce8#a8a39d0f8d51a86a935b708090ec44debfe7bce8" dependencies = [ "arrayvec", "bit-set", @@ -2890,7 +2890,7 @@ dependencies = [ [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=ccdecae02e11fc6694bd252d48cce5794499736f#ccdecae02e11fc6694bd252d48cce5794499736f" +source = "git+https://github.com/feather-rs/tonks?rev=a8a39d0f8d51a86a935b708090ec44debfe7bce8#a8a39d0f8d51a86a935b708090ec44debfe7bce8" dependencies = [ "proc-macro2 1.0.7", "quote 1.0.2", diff --git a/server/Cargo.toml b/server/Cargo.toml index 941570ad7..ed1d36e0a 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,7 +21,7 @@ feather-codegen = { path = "../codegen" } # Core ECS + systems legion = { git = "https://github.com/TomGillen/legion", rev = "0f67adc237af35799df173f31a2c238b3d8010a2" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "ccdecae02e11fc6694bd252d48cce5794499736f", features = ["system-registry"] } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "a8a39d0f8d51a86a935b708090ec44debfe7bce8", features = ["system-registry"] } # tonks = { path = "../../../dev/tonks", features = ["system-registry"] } # Concurrency/threading diff --git a/server/src/block.rs b/server/src/block.rs new file mode 100644 index 000000000..c6e357d48 --- /dev/null +++ b/server/src/block.rs @@ -0,0 +1,27 @@ +use feather_core::{Block, BlockPosition}; +use legion::entity::Entity; + +/// Event triggered when a block is updated. +/// +/// This event is triggered *after* the block is updated +/// in the chunk map. +#[derive(Debug, Clone)] +pub struct BlockUpdateEvent { + /// The cause of this block update event. + pub cause: BlockUpdateCause, + /// The location of the block which was updated. + pub pos: BlockPosition, + /// The block which was previously at the position. + pub old_block: Block, + /// The new block at the position. + pub new_block: Block, +} + +/// The possible causes of a block update event. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum BlockUpdateCause { + /// Indicates that a player updated the block. + Player(Entity), + /// Indicates that a falling block updated the block. + FallingBlock, +} diff --git a/server/src/lib.rs b/server/src/lib.rs index 2482324e8..9637033ac 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -127,7 +127,7 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use crate::chunk_logic::ChunkWorkerHandle; use crate::config::Config; use crate::io::NetworkIoManager; -use crate::state::State; +use crate::state::StateInner; use crate::worldgen::{ ComposableGenerator, EmptyWorldGenerator, SuperflatWorldGenerator, WorldGenerator, }; @@ -147,6 +147,7 @@ use tonks::{Resources, Scheduler}; #[global_allocator] static ALLOC: System = System; +pub mod block; pub mod broadcasters; pub mod chunk_entities; pub mod chunk_logic; @@ -275,7 +276,7 @@ fn run_loop(world: &mut World, scheduler: &mut Scheduler, shutdown_rx: Receiver< // Run lazily-executed closures. TODO: remove unsafe unsafe { - let state = scheduler.resources().get::() as *const State; + let state = scheduler.resources().get::() as *const StateInner; (&*state).flush(world, scheduler); } @@ -308,7 +309,7 @@ fn init_scheduler( // Insert resources which don't have a `Default` impl. let mut resources = Resources::new(); let chunk_map = ChunkMap::new(); - resources.insert(State::new(config, chunk_map, level)); + resources.insert(StateInner::new(config, chunk_map, level)); resources.insert(chunk_worker_handle); resources.insert(io_manager); diff --git a/server/src/packet_handlers/block.rs b/server/src/packet_handlers/block.rs new file mode 100644 index 000000000..54bd44c63 --- /dev/null +++ b/server/src/packet_handlers/block.rs @@ -0,0 +1,24 @@ +//! Broadcasting of block updates, i.e. when a block is changed to another. + +use crate::block::{BlockUpdateCause, BlockUpdateEvent}; +use crate::state::State; +use feather_core::network::packet::implementation::BlockChange; +use feather_core::BlockExt; + +/// System for broadcasting block update +/// events to all clients. +#[event_handler] +fn broadcast_block_update(event: &BlockUpdateEvent, state: &State) { + // Broadcast Block Change packet. + let neq = if let BlockUpdateCause::Player(player) = event.cause { + Some(player) + } else { + None + }; + + let packet = BlockChange { + location: event.pos, + block_id: event.new_block.native_state_id() as i32, + }; + state.broadcast_chunk_update(event.pos.chunk_pos(), packet, neq); +} diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index c34a9a4fa..bb8cdb27c 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -1,5 +1,6 @@ //! Systems which handle packets through `crate::network::PacketQueue`. mod animation; +mod block; mod inventory; mod movement; diff --git a/server/src/state.rs b/server/src/state.rs index d59f42da6..c879dd97f 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -1,3 +1,4 @@ +use crate::block::{BlockUpdateCause, BlockUpdateEvent}; use crate::broadcasters::movement::LastKnownPositions; use crate::chunk_entities::ChunkEntities; use crate::chunk_logic::ChunkHolders; @@ -9,25 +10,21 @@ use feather_blocks::Block; use feather_core::level::LevelData; use feather_core::world::ChunkMap; use feather_core::{BlockPosition, Chunk, ChunkPosition, Packet, Position}; +use legion::borrow::AtomicRefCell; use legion::entity::Entity; use legion::query::{IntoQuery, Read}; +use legion::storage::ComponentTypeId; use legion::world::World; use parking_lot::RwLockReadGuard; +use std::ops::{Deref, DerefMut}; use std::sync::Arc; -use tonks::Scheduler; +use tonks::{ + MacroData, ResourceId, Resources, Scheduler, SystemCtx, SystemData, SystemDataOutput, Trigger, +}; -/// The state of the server. -/// -/// This state wraps numerous commonly-used resources, -/// including the chunk map (block access), config, and -/// various cached data structures, among others. -/// -/// Systems should never require mutable access to the -/// state; it is designed for read-only use. (The chunk -/// map uses `RwLock` internally, so write access isn't -/// needed to update blocks.) +/// Resource used internally by `State`. #[derive(Resource)] -pub struct State { +pub struct StateInner { pub config: Arc, pub chunk_map: ChunkMap, pub level: LevelData, @@ -36,7 +33,7 @@ pub struct State { lazy: Lazy, } -impl State { +impl StateInner { pub fn new(config: Arc, chunk_map: ChunkMap, level: LevelData) -> Self { Self { config, @@ -47,6 +44,92 @@ impl State { } } + /// See `Lazy::flush()`. + pub fn flush(&self, world: &mut World, scheduler: &mut Scheduler) { + self.lazy.flush(world, scheduler); + } +} + +/// The state of the server. +/// +/// This state wraps numerous commonly-used resources, +/// including the chunk map (block access), config, and +/// various cached data structures, among others. +/// +/// Systems should never require mutable access to the +/// state; it is designed for read-only use. (The chunk +/// map uses `RwLock` internally, so write access isn't +/// needed to update blocks.) +/// +/// # Internal details +/// A custom `tonks::SystemData` implementation +/// is utilized so that functions on `State` can automatically +/// trigger events. For example, the methods which update +/// blocks automatically trigger `BlockUpdateEvent`s. +/// +/// An implication of this implementation is that `State` itself +/// is not a resource; however, `StateInner` is. +pub struct State { + inner: *mut StateInner, + trigger: AtomicRefCell>, // TODO: optimize +} + +unsafe impl Send for State {} +unsafe impl Sync for State {} + +impl<'a> SystemData<'a> for State { + type Output = &'a Self; + + unsafe fn load_from_resources( + resources: &mut Resources, + ctx: SystemCtx, + world: &World, + ) -> Self { + let inner = resources + .get_mut_unchecked::(tonks::resource_id_for::()) + as *mut StateInner; + let trigger = Trigger::load_from_resources(resources, ctx, world); + + Self { + inner, + trigger: AtomicRefCell::new(trigger), + } + } + + fn resource_reads() -> Vec { + vec![tonks::resource_id_for::()] + } + + fn resource_writes() -> Vec { + vec![] + } + + fn component_reads() -> Vec { + vec![] + } + + fn component_writes() -> Vec { + vec![] + } + + fn before_execution(&'a mut self) -> Self::Output { + self + } + + fn after_execution(&mut self) { + self.trigger.get_mut().after_execution() + } +} + +impl<'a> SystemDataOutput<'a> for &'a State { + type SystemData = State; +} + +impl MacroData for &'static State { + type SystemData = State; +} + +impl State { /// See `Lazy::exec()`. pub fn exec(&self, f: impl FnOnce(&mut World) + Send + 'static) { self.lazy.exec(f) @@ -122,6 +205,34 @@ impl State { }); } + /// Lazily broadcasts a packet to all players able to see the given chunk. + /// + /// The packet will not be sent to `neq`. + pub fn broadcast_chunk_update( + &self, + chunk: ChunkPosition, + packet: impl Packet + Clone, + neq: Option, + ) { + self.exec_with_scheduler(move |world, scheduler| { + // Use ChunkHolders to determine which players have a hold on the + // chunk, which would allow them to see the entity. + let chunk_holders = scheduler.resources().get::(); + + let holders = chunk_holders.holders_for(chunk); + + holders.map(|entities| { + for entity in entities { + if let Some(network) = world.get_component::(*entity) { + if neq.map_or(true, |neq| *entity != neq) { + network.send(packet.clone()); + } + } + } + }); + }); + } + /// Lazily broadcasts a packet to all clients. pub fn broadcast_global(&self, packet: P, neq: Option) { self.exec(move |world| { @@ -150,11 +261,6 @@ impl State { }); } - /// See `Lazy::flush()`. - pub fn flush(&self, world: &mut World, scheduler: &mut Scheduler) { - self.lazy.flush(world, scheduler); - } - /// Retrieves the block at the given position, /// or `None` if the block's chunk is not loaded. pub fn block_at(&self, pos: BlockPosition) -> Option { @@ -165,7 +271,20 @@ impl State { /// /// If the block's chunk's is not loaded, returns `false`; /// otherwise, returns `true`. - pub fn set_block_at(&self, pos: BlockPosition, block: Block) -> bool { + pub fn set_block_at(&self, pos: BlockPosition, block: Block, cause: BlockUpdateCause) -> bool { + let old_block = match self.block_at(pos) { + Some(block) => block, + None => return false, + }; + + let event = BlockUpdateEvent { + cause, + pos, + old_block, + new_block: block, + }; + self.trigger.get_mut().trigger(event); + self.chunk_map.set_block_at(pos, block) } @@ -180,7 +299,7 @@ impl State { self.lazy.exec_with_scheduler(move |_, scheduler| unsafe { scheduler .resources() - .get_mut_unchecked::(tonks::resource_id_for::()) + .get_mut_unchecked::(tonks::resource_id_for::()) .chunk_map .insert(chunk); }); @@ -192,7 +311,7 @@ impl State { .exec_with_scheduler(move |_: &mut World, scheduler: &mut Scheduler| unsafe { scheduler .resources() - .get_mut_unchecked::(tonks::resource_id_for::()) + .get_mut_unchecked::(tonks::resource_id_for::()) .chunk_map .remove(pos); }); @@ -220,3 +339,17 @@ impl State { }) } } + +impl Deref for State { + type Target = StateInner; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.inner } + } +} + +impl DerefMut for State { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.inner } + } +} From 45d587ad14073e6fb9c6c3d63af8a561ed784d59 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 12 Jan 2020 15:58:19 -0700 Subject: [PATCH 069/647] Fix module location of block broadcasting --- server/src/broadcasters/block.rs | 24 ++++++++++++++++++++++++ server/src/broadcasters/mod.rs | 1 + server/src/packet_handlers/block.rs | 23 ----------------------- 3 files changed, 25 insertions(+), 23 deletions(-) create mode 100644 server/src/broadcasters/block.rs diff --git a/server/src/broadcasters/block.rs b/server/src/broadcasters/block.rs new file mode 100644 index 000000000..54bd44c63 --- /dev/null +++ b/server/src/broadcasters/block.rs @@ -0,0 +1,24 @@ +//! Broadcasting of block updates, i.e. when a block is changed to another. + +use crate::block::{BlockUpdateCause, BlockUpdateEvent}; +use crate::state::State; +use feather_core::network::packet::implementation::BlockChange; +use feather_core::BlockExt; + +/// System for broadcasting block update +/// events to all clients. +#[event_handler] +fn broadcast_block_update(event: &BlockUpdateEvent, state: &State) { + // Broadcast Block Change packet. + let neq = if let BlockUpdateCause::Player(player) = event.cause { + Some(player) + } else { + None + }; + + let packet = BlockChange { + location: event.pos, + block_id: event.new_block.native_state_id() as i32, + }; + state.broadcast_chunk_update(event.pos.chunk_pos(), packet, neq); +} diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index f714a22b1..eece446c2 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -7,6 +7,7 @@ //! packets have been sent. This is done through `EntitySendEvent`. mod animation; +mod block; pub mod entity_creation; pub mod entity_deletion; mod inventory; diff --git a/server/src/packet_handlers/block.rs b/server/src/packet_handlers/block.rs index 54bd44c63..8b1378917 100644 --- a/server/src/packet_handlers/block.rs +++ b/server/src/packet_handlers/block.rs @@ -1,24 +1 @@ -//! Broadcasting of block updates, i.e. when a block is changed to another. -use crate::block::{BlockUpdateCause, BlockUpdateEvent}; -use crate::state::State; -use feather_core::network::packet::implementation::BlockChange; -use feather_core::BlockExt; - -/// System for broadcasting block update -/// events to all clients. -#[event_handler] -fn broadcast_block_update(event: &BlockUpdateEvent, state: &State) { - // Broadcast Block Change packet. - let neq = if let BlockUpdateCause::Player(player) = event.cause { - Some(player) - } else { - None - }; - - let packet = BlockChange { - location: event.pos, - block_id: event.new_block.native_state_id() as i32, - }; - state.broadcast_chunk_update(event.pos.chunk_pos(), packet, neq); -} From 7944bb7c5a896fba4e69c55213dd212ab6150240 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 12 Jan 2020 16:14:19 -0700 Subject: [PATCH 070/647] Reimplement block placement --- server/src/packet_handlers/block.rs | 1 - server/src/packet_handlers/mod.rs | 2 +- server/src/packet_handlers/placement.rs | 80 +++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) delete mode 100644 server/src/packet_handlers/block.rs create mode 100644 server/src/packet_handlers/placement.rs diff --git a/server/src/packet_handlers/block.rs b/server/src/packet_handlers/block.rs deleted file mode 100644 index 8b1378917..000000000 --- a/server/src/packet_handlers/block.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index bb8cdb27c..b39bb5d54 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -1,6 +1,6 @@ //! Systems which handle packets through `crate::network::PacketQueue`. mod animation; -mod block; mod inventory; mod movement; +mod placement; diff --git a/server/src/packet_handlers/placement.rs b/server/src/packet_handlers/placement.rs new file mode 100644 index 000000000..7999aa724 --- /dev/null +++ b/server/src/packet_handlers/placement.rs @@ -0,0 +1,80 @@ +//! Handling of player block placement packets. + +use crate::block::BlockUpdateCause; +use crate::network::PacketQueue; +use crate::p_inventory::{EntityInventory, InventoryUpdateEvent}; +use crate::state::State; +use crate::util::disconnect_player; +use feather_core::inventory::SLOT_HOTBAR_OFFSET; +use feather_core::network::packet::implementation::PlayerBlockPlacement; +use feather_core::{Block, Gamemode, ItemStack}; +use feather_item_block::ItemToBlock; +use legion::query::{Read, Write}; +use tonks::{PreparedWorld, Query, Trigger}; + +/// System for handling Player Block Placement packets +/// and updating the world accordingly. +#[system] +fn handle_player_block_placement( + state: &State, + queue: &PacketQueue, + _query: &mut Query<(Write, Read)>, + world: &mut PreparedWorld, + inventory_update_events: &mut Trigger, +) { + let packets = queue.received::(); + + for (player, packet) in packets { + // TODO: handle slabs, blocks with directions, etc. + let gamemode = *world.get_component::(player).unwrap(); + let mut inventory = world.get_component_mut::(player).unwrap(); + + let item = match inventory.item_in_main_hand() { + Some(item) => item, + None => continue, // No block to place + }; + + let block = match item.ty.to_block() { + Some(block) => block, + None => continue, // Item is not a block + }; + + let placed_on = match state.block_at(packet.location) { + Some(block) => block, + None => { + disconnect_player(state, player, "Attempted to place block in unloaded chunk"); + continue; + } + }; + + // TODO: waterlogged blocks, more + let pos = match placed_on { + Block::Grass | Block::TallGrass(_) | Block::Water(_) | Block::Lava(_) => { + packet.location + } + _ => packet.location + packet.face.placement_offset(), + }; + + state.set_block_at(pos, block, BlockUpdateCause::Player(player)); + + // Update player's inventory if in survival + if gamemode == Gamemode::Survival { + if item.amount == 0 { + disconnect_player( + state, + player, + "Attempted to place block with 0-sized item stack", + ); + } + + let item = ItemStack::new(item.ty, item.amount - 1); + inventory.set_item_in_main_hand(item); + + let event = InventoryUpdateEvent { + slots: smallvec![SLOT_HOTBAR_OFFSET + inventory.held_item], + player, + }; + inventory_update_events.trigger(event); + } + } +} From 5bcd79fddd59c585d63ab4aecac647f2717c2edd Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 12 Jan 2020 16:20:31 -0700 Subject: [PATCH 071/647] Remove rand-legacy crate --- Cargo.lock | 7 ------- Cargo.toml | 1 - util/rand-legacy/Cargo.toml | 9 --------- util/rand-legacy/src/lib.rs | 1 - 4 files changed, 18 deletions(-) delete mode 100644 util/rand-legacy/Cargo.toml delete mode 100644 util/rand-legacy/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 696a7a1b3..c18312c1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2029,13 +2029,6 @@ dependencies = [ "rand_hc 0.2.0", ] -[[package]] -name = "rand-legacy" -version = "0.1.0" -dependencies = [ - "rand 0.6.5", -] - [[package]] name = "rand_chacha" version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index d9d83091f..3422bd56f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ members = [ "item_block", "codegen", "generator", - "util/rand-legacy", ] [profile.dev] diff --git a/util/rand-legacy/Cargo.toml b/util/rand-legacy/Cargo.toml deleted file mode 100644 index 50146e4aa..000000000 --- a/util/rand-legacy/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "rand-legacy" -version = "0.1.0" -authors = ["caelunshun "] -edition = "2018" -description = "Exports 0.6.5 rand API for use with old libraries" - -[dependencies] -rand = "0.6.5" \ No newline at end of file diff --git a/util/rand-legacy/src/lib.rs b/util/rand-legacy/src/lib.rs deleted file mode 100644 index 41891cf16..000000000 --- a/util/rand-legacy/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub use rand::*; From 1536701c61fa5b243867c1225a2ea22ce0c14f20 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 12 Jan 2020 16:27:17 -0700 Subject: [PATCH 072/647] Update .azure-pipelines.yml to reflect move to organization --- .azure-pipelines.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 2c5b0b5fe..3acb318ec 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -178,8 +178,8 @@ jobs: - task: GithubRelease@0 inputs: - gitHubConnection: 'caelunshun_pat' - repositoryName: 'caelunshun/feather' + gitHubConnection: 'feather-rs' + repositoryName: 'feather-rs/feather' action: 'edit' target: '$(build.sourceVersion)' tagSource: 'manual' @@ -192,8 +192,8 @@ jobs: - task: GithubRelease@0 inputs: - gitHubConnection: 'caelunshun_pat' - repositoryName: 'caelunshun/feather' + gitHubConnection: 'feather-rs' + repositoryName: 'feather-rs/feather' action: 'edit' target: '$(build.sourceVersion)' tagSource: 'manual' From 19dc58925a2148bcc7502f3a1be345cf91f2e7e3 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 12 Jan 2020 21:27:30 -0700 Subject: [PATCH 073/647] Implement block breaking --- server/src/packet_handlers/digging.rs | 273 ++++++++++++++++++++++++++ server/src/packet_handlers/mod.rs | 1 + 2 files changed, 274 insertions(+) create mode 100644 server/src/packet_handlers/digging.rs diff --git a/server/src/packet_handlers/digging.rs b/server/src/packet_handlers/digging.rs new file mode 100644 index 000000000..c5d5cd814 --- /dev/null +++ b/server/src/packet_handlers/digging.rs @@ -0,0 +1,273 @@ +//! This module handles the monolithic Player Digging packet. +//! +//! The packet's name is rather misleading, as it is also sent +//! for actions mostly unrelated to digging including eating, shooting bows, +//! swapping items out to the offhand, and dropping items. + +use crate::block::BlockUpdateCause; +use crate::network::PacketQueue; +use crate::p_inventory::EntityInventory; +use crate::state::State; +use crate::util::disconnect_player; +use feather_core::network::packet::implementation::{PlayerDigging, PlayerDiggingStatus}; +use feather_core::{Block, Gamemode, Item, ItemStack, Position}; +use legion::entity::Entity; +use legion::query::{Read, Write}; +use tonks::{PreparedWorld, Query}; + +/// System responsible for polling for PlayerDigging +/// packets and writing the corresponding events. +#[system] +fn handle_player_digging( + state: &State, + queue: &PacketQueue, + _query: &mut Query<(Write, Read, Read)>, + world: &mut PreparedWorld, +) { + use PlayerDiggingStatus::*; + + let packets = queue.received::(); + + for (player, packet) in packets { + let gamemode = *world.get_component::(player).unwrap(); + let inventory = world.get_component_mut::(player).unwrap(); + + match packet.status { + StartedDigging | FinishedDigging | CancelledDigging => handle_digging( + packet, + state, + player, + gamemode, + inventory.item_in_main_hand(), + ), + /* + DropItem | DropItemStack => handle_drop_item_stack( + packet, + player, + &mut inventory_updates, + &mut item_drops, + inventories.get_mut(player).unwrap(), + ), + ConsumeItem => handle_consume_item( + packet, + players.get(player).unwrap(), + player, + inventories.get_mut(player).unwrap(), + &mut inventory_updates, + positions.get(player).unwrap().current, + &mut shoot_arrow_events, + ), + */ + status => warn!("Unhandled Player Digging status {:?}", status), + } + } +} + +fn handle_digging( + packet: PlayerDigging, + state: &State, + player: Entity, + gamemode: Gamemode, + item_in_main_hand: Option<&ItemStack>, +) { + // Return early if needed + match packet.status { + PlayerDiggingStatus::StartedDigging => { + if gamemode != Gamemode::Creative { + return; + } + } + PlayerDiggingStatus::CancelledDigging => return, + _ => (), + } + + // Don't break block if player is holding a sword in creative mode. + if gamemode == Gamemode::Creative { + if let Some(item_in_main_hand) = item_in_main_hand { + match item_in_main_hand.ty { + Item::WoodenSword + | Item::StoneSword + | Item::GoldenSword + | Item::IronSword + | Item::DiamondSword => return, + _ => (), + } + } + } + + if !state.set_block_at( + packet.location, + Block::Air, + BlockUpdateCause::Player(player), + ) { + disconnect_player(state, player, "Attempted to break block in unloaded chunk"); + return; + } +} + +/* +fn handle_drop_item_stack( + packet: &PlayerDigging, + entity: Entity, + inventory_updates: &mut EventChannel, + item_drops: &mut EventChannel, + inventory: &mut InventoryComponent, +) { + assert!( + packet.status == PlayerDiggingStatus::DropItem + || packet.status == PlayerDiggingStatus::DropItemStack + ); + + let slot = inventory.held_item + SLOT_HOTBAR_OFFSET; + + let stack = { + if let Some(item) = inventory.item_at(slot) { + item.clone() + } else { + // Silently fail - no item stack to drop + return; + } + }; + + let amnt = match packet.status { + PlayerDiggingStatus::DropItem => { + if stack.amount == 0 { + inventory.clear_item_at(slot); + 0 + } else if stack.amount == 1 { + inventory.clear_item_at(slot); + 1 + } else { + inventory.set_item_at(slot, ItemStack::new(stack.ty, stack.amount - 1)); + 1 + } + } + PlayerDiggingStatus::DropItemStack => { + inventory.clear_item_at(slot); + stack.amount + } + _ => unreachable!(), // Assertion above + }; + + let inv_update = InventoryUpdateEvent { + slots: smallvec![slot], + player: entity, + }; + inventory_updates.single_write(inv_update); + + if amnt != 0 { + let item_drop = PlayerItemDropEvent { + slot: Some(slot), + stack: ItemStack::new(stack.ty, amnt), + player: entity, + }; + item_drops.single_write(item_drop); + } +} + +/// Handles food consumption and shooting arrows. +fn handle_consume_item( + packet: &PlayerDigging, + player: &PlayerComponent, + entity: Entity, + inventory: &mut InventoryComponent, + inventory_updates: &mut EventChannel, + position: Position, + shoot_arrow_events: &mut EventChannel, +) { + assert_eq!(packet.status, PlayerDiggingStatus::ConsumeItem); + + // TODO: Fallback to off-hand if main-hand is not a consumable + let used_item = inventory.item_in_main_hand(); + + if let Some(item) = used_item { + if item.ty == Item::Bow { + handle_shoot_bow( + player, + entity, + inventory, + inventory_updates, + position, + shoot_arrow_events, + ); + } + // TODO: Food, potions + } +} + +fn handle_shoot_bow( + player: &PlayerComponent, + entity: Entity, + inventory: &mut InventoryComponent, + inventory_updates: &mut EventChannel, + position: Position, + shoot_arrow_events: &mut EventChannel, +) { + let arrow_to_consume: Option<(SlotIndex, ItemStack)> = find_arrow(&inventory); + if player.gamemode == Gamemode::Survival || player.gamemode == Gamemode::Adventure { + // If no arrow was found, don't shoot + let arrow_to_consume = arrow_to_consume.clone(); + if arrow_to_consume.is_none() { + debug!("Tried to shoot bow with no arrows."); + return; + } + + // Consume arrow + let (arrow_slot, arrow_stack) = arrow_to_consume.unwrap(); + let mut arrow_stack: ItemStack = arrow_stack; + arrow_stack.amount -= 1; + + inventory.set_item_at(arrow_slot, arrow_stack); + inventory_updates.single_write(InventoryUpdateEvent { + slots: smallvec![arrow_slot], + player: entity, + }); + } + + let arrow_type: Item = match arrow_to_consume { + None => Item::Arrow, // Default to generic arrow in creative mode with none in inventory + Some((_, arrow_stack)) => arrow_stack.ty, + }; + + shoot_arrow_events.single_write(ShootArrowEvent { + shooter: Some(entity), + position, + arrow_type, + critical: false, // TODO: Determine critical based on how long bow was pulled back + }); +} + +fn find_arrow(inventory: &InventoryComponent) -> Option<(SlotIndex, ItemStack)> { + // Order of priority is: off-hand, hotbar (0 to 8), rest of inventory + + if let Some(offhand) = inventory.item_at(SLOT_OFFHAND) { + if is_arrow_item(offhand.ty) { + return Some((SLOT_OFFHAND, offhand.clone())); + } + } + + for hotbar_slot in 0..9 { + if let Some(hotbar_stack) = inventory.item_at(SLOT_HOTBAR_OFFSET + hotbar_slot) { + if is_arrow_item(hotbar_stack.ty) { + return Some((SLOT_HOTBAR_OFFSET + hotbar_slot, hotbar_stack.clone())); + } + } + } + + for inv_slot in 9..=35 { + if let Some(inv_stack) = inventory.item_at(inv_slot) { + if is_arrow_item(inv_stack.ty) { + return Some((inv_slot, inv_stack.clone())); + } + } + } + None +} + +fn is_arrow_item(item: Item) -> bool { + match item { + Item::Arrow | Item::SpectralArrow | Item::TippedArrow => true, + _ => false, + } +} +*/ diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index b39bb5d54..178f4f7f9 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -1,6 +1,7 @@ //! Systems which handle packets through `crate::network::PacketQueue`. mod animation; +mod digging; mod inventory; mod movement; mod placement; From e77cb4b93aabc3d75834988a1a3f38e3703ec968 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 12 Jan 2020 21:46:41 -0700 Subject: [PATCH 074/647] Reimplement base item entity functionality --- server/src/broadcasters/entity_deletion.rs | 13 ++- server/src/entity/item.rs | 120 +++++++++++++++++++++ server/src/entity/mod.rs | 9 +- server/src/player/mod.rs | 9 +- server/src/util.rs | 12 +++ 5 files changed, 148 insertions(+), 15 deletions(-) create mode 100644 server/src/entity/item.rs diff --git a/server/src/broadcasters/entity_deletion.rs b/server/src/broadcasters/entity_deletion.rs index e5848c921..1e9931db0 100644 --- a/server/src/broadcasters/entity_deletion.rs +++ b/server/src/broadcasters/entity_deletion.rs @@ -1,11 +1,8 @@ use crate::chunk_logic::ChunkHolders; use crate::entity::EntityDeleteEvent; use crate::network::Network; -use crate::player::Player; use crate::state::State; -use feather_core::network::packet::implementation::{ - DestroyEntities, PlayerInfo, PlayerInfoAction, -}; +use feather_core::network::packet::implementation::DestroyEntities; use legion::query::Read; use rayon::prelude::*; use tonks::{PreparedWorld, Query}; @@ -15,7 +12,7 @@ use tonks::{PreparedWorld, Query}; fn broadcast_entity_deletion( events: &[EntityDeleteEvent], holders: &ChunkHolders, - _query: &mut Query<(Read, Read)>, + _query: &mut Query>, world: &mut PreparedWorld, state: &State, ) { @@ -33,7 +30,9 @@ fn broadcast_entity_deletion( } } - // If entity was a player, broadcast PlayerInfo with delete status + // If entity was a player, broadcast PlayerInfo with delete status. + // TODO: fix + /* if world.get_component::(event.entity).is_some() { let packet = PlayerInfo { action: PlayerInfoAction::RemovePlayer, @@ -41,6 +40,6 @@ fn broadcast_entity_deletion( }; state.broadcast_global(packet, None); - } + }*/ }); } diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs new file mode 100644 index 000000000..2bca3fa71 --- /dev/null +++ b/server/src/entity/item.rs @@ -0,0 +1,120 @@ +//! Handling of item entities. + +use crate::entity::{EntityId, SpawnPacketCreator, Velocity}; +use crate::lazy::EntityBuilder; +use crate::player::PLAYER_EYE_HEIGHT; +use crate::state::State; +use crate::util::{degrees_to_stops, protocol_velocity}; +use crate::{entity, TickCount, TPS}; +use feather_core::inventory::SlotIndex; +use feather_core::network::packet::implementation::SpawnObject; +use feather_core::{ItemStack, Packet, Position}; +use legion::entity::Entity; +use legion::query::Read; +use rand::Rng; +use tonks::{EntityAccessor, PreparedWorld, Query}; +use uuid::Uuid; + +/// Event triggered when an item is dropped. +/// +/// Before this event is triggered, the item +/// is removed from the player's inventory. +#[derive(Debug, Clone)] +pub struct ItemDropEvent { + /// The slot from which the item was dropped, + /// if known. + pub slot: Option, + /// The item stack which was dropped. + pub stack: ItemStack, + /// The player who dropped the item. + pub player: Entity, +} + +/// Component storing the tick at which an item becomes collectable. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct CollectableAt(pub u64); + +// Item stack of an item entity is stored in `ItemStack` component + +/// System for spawning an item entity when +/// an item is dropped. +#[event_handler] +pub fn item_spawn( + event: &ItemDropEvent, + state: &State, + _query: &mut Query>, + world: &mut PreparedWorld, + tick: &TickCount, +) { + let mut rng = rand::thread_rng(); + + // Spawn item entity. + + // Position is player's eye height minus 0.3 + let mut pos = { + let player_pos = *world.get_component::(event.player).unwrap() + + glm::vec3(0.0, PLAYER_EYE_HEIGHT, 0.0); + player_pos - glm::vec3(0.0f64, 0.3, 0.0) + }; + + pos.on_ground = false; + + // This velocity calculation was sourced from Glowstone's + // work. See https://github.com/GlowstoneMC/Glowstone/blob/dev/src/main/java/net/glowstone/entity/GlowHumanEntity.java + // (method drop(ItemStack stack)) for their code. + let velocity = { + let mut vel = pos.direction() * 0.3; + let rand_offset = 0.02; + + let x = rng.gen_range(0.0, rand_offset) - rand_offset / 2.0; + let y = rng.gen_range(0.0, 0.12); + let z = rng.gen_range(0.0, rand_offset) - rand_offset / 2.0; + + vel += glm::vec3(x, y, z); + + vel + }; + + create(state, pos, event.stack.clone(), tick.0 + TPS) + .with_component(velocity) + .build(); +} + +/// Returns an entity builder to create an item entity +/// with the given stack and collectable tick. +pub fn create( + state: &State, + pos: Position, + stack: ItemStack, + collectable_at: u64, +) -> EntityBuilder { + entity::base(state, pos) + .with_component(stack) + .with_component(CollectableAt(collectable_at)) + .with_component(SpawnPacketCreator(&create_spawn_packet)) +} + +fn create_spawn_packet(accessor: &EntityAccessor, world: &PreparedWorld) -> Box { + let position = *accessor.get_component::(world).unwrap(); + let velocity = *accessor.get_component::(world).unwrap(); + let entity_id = accessor.get_component::(world).unwrap().0; + + let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocity.0); + + let packet = SpawnObject { + entity_id, + object_uuid: Uuid::new_v4(), + ty: 2, // Type 2 for item stack + x: position.x, + y: position.y, + z: position.z, + pitch: degrees_to_stops(position.pitch), + yaw: degrees_to_stops(position.yaw), + data: 1, // Has velocity + velocity_x, + velocity_y, + velocity_z, + }; + + Box::new(packet) +} diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 064098527..e06486884 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -1,4 +1,9 @@ //! Dealing with entities, including associated components and events. +//! Submodules here are implementations of specific entities, such as items, +//! block entities, monsters, etc. Player entities are handled in `crate::player`, +//! not here. + +mod item; use crate::lazy::EntityBuilder; use crate::state::State; @@ -149,8 +154,10 @@ pub fn position_reset( /// Inserts the base components for an entity into an `EntityBuilder`. /// /// This currently includes: -/// * Position /// * Velocity (0) +/// * Entity ID +/// * Position and previous position +/// * Triggers `EntityCreateEvent` pub fn base(state: &State, position: Position) -> EntityBuilder { let id = ENTITY_ID_COUNTER.fetch_add(1, Ordering::Relaxed); state diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index 8e379af77..9beb7c0b8 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -17,6 +17,8 @@ use mojang_api::ProfileProperty; use tonks::{EntityAccessor, PreparedWorld}; use uuid::Uuid; +pub const PLAYER_EYE_HEIGHT: f64 = 1.62; + /// Profile properties of a player. #[derive(Debug, Clone)] pub struct ProfileProperties(pub Vec); @@ -34,18 +36,11 @@ pub struct PlayerAnimationEvent { pub animation: ClientboundAnimation, } -/// Tag used to mark a player. -/// -/// Note that this is a _tag_, not a component. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Player; - /// Creates a new player from the given `NewClientInfo`. /// /// This function also triggers the `PlayerJoinEvent` for this player. pub fn create(state: &State, info: NewClientInfo) { entity::base(state, info.position) - .with_tag(Player) .with_component(info.uuid) .with_component(Network { sender: info.sender, diff --git a/server/src/util.rs b/server/src/util.rs index 378c17a9f..ca38c3a27 100644 --- a/server/src/util.rs +++ b/server/src/util.rs @@ -5,6 +5,7 @@ use crate::io::ServerToWorkerMessage; use crate::network::Network; use crate::state::State; use feather_core::Position; +use glm::DVec3; use legion::entity::Entity; use std::borrow::Cow; use uuid::Uuid; @@ -23,6 +24,17 @@ pub fn degrees_to_stops(degs: f32) -> u8 { ((degs / 360.0) * 256.0) as u8 } +/// Converts float-based velocity in blocks per tick +/// to the format used by the protocol. +pub fn protocol_velocity(vel: DVec3) -> (i16, i16, i16) { + // Apparently, these are in units of 1/8000 block per tick. + ( + (vel.x * 8000.0) as i16, + (vel.y * 8000.0) as i16, + (vel.z * 8000.0) as i16, + ) +} + /// Disconnects a player. pub fn disconnect_player(state: &State, player: Entity, reason: impl Into>) { let reason = reason.into(); From e42abf432d036f32bb58c22aae07761270747520 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 13 Jan 2020 16:37:35 -0700 Subject: [PATCH 075/647] Implement item drop (excluding entity metadata) --- server/src/broadcasters/inventory.rs | 4 ++++ server/src/entity/mod.rs | 2 +- server/src/packet_handlers/inventory.rs | 16 +++++++++------- server/src/state.rs | 1 + server/src/view.rs | 4 ---- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/server/src/broadcasters/inventory.rs b/server/src/broadcasters/inventory.rs index 2b22f34e0..e62ffdb2f 100644 --- a/server/src/broadcasters/inventory.rs +++ b/server/src/broadcasters/inventory.rs @@ -47,6 +47,10 @@ fn send_entity_equipment( _query: &mut Query<(Read, Read, Read)>, world: &mut PreparedWorld, ) { + if !world.is_alive(event.to) { + return; + } + let network = world.get_component::(event.to).unwrap(); let inventory = match world.get_component::(event.entity) { Some(inv) => inv, diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index e06486884..74f35a2c7 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -3,7 +3,7 @@ //! block entities, monsters, etc. Player entities are handled in `crate::player`, //! not here. -mod item; +pub mod item; use crate::lazy::EntityBuilder; use crate::state::State; diff --git a/server/src/packet_handlers/inventory.rs b/server/src/packet_handlers/inventory.rs index 239498f07..35ba82d66 100644 --- a/server/src/packet_handlers/inventory.rs +++ b/server/src/packet_handlers/inventory.rs @@ -1,6 +1,7 @@ //! Handling of inventory update packets. //! This currently includes Creative Inventory Action and Held Item Change. +use crate::entity::item::ItemDropEvent; use crate::network::PacketQueue; use crate::p_inventory::{EntityInventory, InventoryUpdateEvent}; use crate::state::State; @@ -21,7 +22,8 @@ fn handle_creative_inventory_action( queue: &PacketQueue, _query: &mut Query<(Read, Write)>, world: &mut PreparedWorld, - trigger: &mut Trigger, + trigger_inventory: &mut Trigger, + trigger_drop: &mut Trigger, ) { let packets = queue.received::(); @@ -42,19 +44,19 @@ fn handle_creative_inventory_action( // Slot -1 means that the user clicked outside the window, // dropping the item. - // TODO: implement this if packet.slot == -1 { match &packet.clicked_item { - Some(_) => { - /*let event = PlayerItemDropEvent { + Some(stack) => { + // Cause item to be dropped + let event = ItemDropEvent { slot: None, stack: stack.clone(), player, }; - drop_events.single_write(event); + trigger_drop.trigger(event); // No need to update inventory - continue;*/ + continue; } None => (), } @@ -79,7 +81,7 @@ fn handle_creative_inventory_action( slots: smallvec![packet.slot as usize], player, }; - trigger.trigger(event); + trigger_inventory.trigger(event); } } diff --git a/server/src/state.rs b/server/src/state.rs index c879dd97f..dca0d1862 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -75,6 +75,7 @@ pub struct State { } unsafe impl Send for State {} + unsafe impl Sync for State {} impl<'a> SystemData<'a> for State { diff --git a/server/src/view.rs b/server/src/view.rs index eec0f42f7..0a8f71483 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -201,10 +201,6 @@ fn view_handle_entities( to_send.copied().for_each(|chunk| { let entities = state.chunk_entities.entities_in_chunk(chunk); - if !entities.is_empty() { - dbg!(event.player, &entities); - } - entities.iter().copied().for_each(|entity| { // Don't send client to themself. if entity == event.player { From d428af12bf898603641b4a0de7846f2ab8c843c9 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 31 Jan 2020 20:14:55 -0700 Subject: [PATCH 076/647] Work toward getting item entities to work --- Cargo.lock | 27 +++++++-- core/src/entitymeta.rs | 2 +- core/src/network/packet/implementation.rs | 2 +- server/Cargo.toml | 2 +- server/src/broadcasters/metadata.rs | 29 ++++++++++ server/src/broadcasters/mod.rs | 1 + server/src/entity/item.rs | 12 +++- server/src/lib.rs | 3 + server/src/metadata.rs | 70 +++++++++++++++++++++++ 9 files changed, 140 insertions(+), 8 deletions(-) create mode 100644 server/src/broadcasters/metadata.rs create mode 100644 server/src/metadata.rs diff --git a/Cargo.lock b/Cargo.lock index c18312c1f..531c2971d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -781,7 +781,7 @@ dependencies = [ "humantime-serde", "inventory", "lazy_static", - "legion", + "legion 0.2.1 (git+https://github.com/TomGillen/legion?rev=0f67adc237af35799df173f31a2c238b3d8010a2)", "lock_api", "log", "mojang-api", @@ -1335,6 +1335,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "legion" +version = "0.2.1" +source = "git+https://github.com/TomGillen/legion?rev=940ef3bfcb77e5d074ee3184b776ff1600da228d#940ef3bfcb77e5d074ee3184b776ff1600da228d" +dependencies = [ + "bit-set", + "crossbeam-channel 0.4.0", + "crossbeam-queue 0.2.1", + "derivative", + "downcast-rs", + "fxhash", + "itertools", + "parking_lot 0.9.0", + "paste", + "rayon", + "smallvec 0.6.13", + "tracing", +] + [[package]] name = "libc" version = "0.2.66" @@ -2860,7 +2879,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=a8a39d0f8d51a86a935b708090ec44debfe7bce8#a8a39d0f8d51a86a935b708090ec44debfe7bce8" +source = "git+https://github.com/feather-rs/tonks?rev=60066a3c5cd521f697b39b2e0006578d2c6f806e#60066a3c5cd521f697b39b2e0006578d2c6f806e" dependencies = [ "arrayvec", "bit-set", @@ -2870,7 +2889,7 @@ dependencies = [ "hashbrown", "inventory", "lazy_static", - "legion", + "legion 0.2.1 (git+https://github.com/TomGillen/legion?rev=940ef3bfcb77e5d074ee3184b776ff1600da228d)", "mopa", "parking_lot 0.9.0", "rayon", @@ -2883,7 +2902,7 @@ dependencies = [ [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=a8a39d0f8d51a86a935b708090ec44debfe7bce8#a8a39d0f8d51a86a935b708090ec44debfe7bce8" +source = "git+https://github.com/feather-rs/tonks?rev=60066a3c5cd521f697b39b2e0006578d2c6f806e#60066a3c5cd521f697b39b2e0006578d2c6f806e" dependencies = [ "proc-macro2 1.0.7", "quote 1.0.2", diff --git a/core/src/entitymeta.rs b/core/src/entitymeta.rs index d7ff0321b..7d55a6241 100644 --- a/core/src/entitymeta.rs +++ b/core/src/entitymeta.rs @@ -107,7 +107,7 @@ impl IntoMetaEntry for BlockPosition { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct EntityMetadata { values: HashMap, } diff --git a/core/src/network/packet/implementation.rs b/core/src/network/packet/implementation.rs index 72a7a5ca0..0d8fad720 100644 --- a/core/src/network/packet/implementation.rs +++ b/core/src/network/packet/implementation.rs @@ -1827,7 +1827,7 @@ pub struct EntityHeadLook { pub head_yaw: u8, } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, new, Clone, Debug)] pub struct PacketEntityMetadata { pub entity_id: VarInt, pub metadata: EntityMetadata, diff --git a/server/Cargo.toml b/server/Cargo.toml index ed1d36e0a..8cab9f43e 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,7 +21,7 @@ feather-codegen = { path = "../codegen" } # Core ECS + systems legion = { git = "https://github.com/TomGillen/legion", rev = "0f67adc237af35799df173f31a2c238b3d8010a2" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "a8a39d0f8d51a86a935b708090ec44debfe7bce8", features = ["system-registry"] } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "60066a3c5cd521f697b39b2e0006578d2c6f806e", features = ["system-registry"] } # tonks = { path = "../../../dev/tonks", features = ["system-registry"] } # Concurrency/threading diff --git a/server/src/broadcasters/metadata.rs b/server/src/broadcasters/metadata.rs new file mode 100644 index 000000000..942bbdc7f --- /dev/null +++ b/server/src/broadcasters/metadata.rs @@ -0,0 +1,29 @@ +//! Sending of entity metadata. + +use crate::entity::{EntityId, EntitySendEvent}; +use crate::metadata::Metadata; +use crate::network::Network; +use feather_core::network::packet::implementation::PacketEntityMetadata; +use legion::query::Read; +use tonks::{PreparedWorld, Query}; + +/// System which sends entity metadata when an entity +/// is sent to a player. +#[event_handler] +fn send_entity_metadata( + event: &EntitySendEvent, + _query: &mut Query<(Read, Read, Read)>, + world: &mut PreparedWorld, +) { + if let Some(meta) = world.get_component::(event.entity) { + if let Some(network) = world.get_component::(event.to) { + let entity_id = world.get_component::(event.entity).unwrap().0; + let packet = PacketEntityMetadata { + entity_id, + metadata: meta.to_full_raw_metadata(), + }; + dbg!(&packet); + network.send(packet); + } + } +} diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index eece446c2..9c3182ad9 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -12,4 +12,5 @@ pub mod entity_creation; pub mod entity_deletion; mod inventory; pub mod keepalive; +mod metadata; pub mod movement; diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index 2bca3fa71..040538c13 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -2,6 +2,7 @@ use crate::entity::{EntityId, SpawnPacketCreator, Velocity}; use crate::lazy::EntityBuilder; +use crate::metadata::Metadata; use crate::player::PLAYER_EYE_HEIGHT; use crate::state::State; use crate::util::{degrees_to_stops, protocol_velocity}; @@ -76,7 +77,7 @@ pub fn item_spawn( }; create(state, pos, event.stack.clone(), tick.0 + TPS) - .with_component(velocity) + .with_component(Velocity(velocity)) .build(); } @@ -88,10 +89,17 @@ pub fn create( stack: ItemStack, collectable_at: u64, ) -> EntityBuilder { + let meta = { + let mut meta_item = crate::metadata::Item::default(); + meta_item.set_item(Some(stack.clone())); + Metadata::Item(meta_item) + }; + entity::base(state, pos) .with_component(stack) .with_component(CollectableAt(collectable_at)) .with_component(SpawnPacketCreator(&create_spawn_packet)) + .with_component(meta) } fn create_spawn_packet(accessor: &EntityAccessor, world: &PreparedWorld) -> Box { @@ -116,5 +124,7 @@ fn create_spawn_packet(accessor: &EntityAccessor, world: &PreparedWorld) -> Box< velocity_z, }; + dbg!(&packet); + Box::new(packet) } diff --git a/server/src/lib.rs b/server/src/lib.rs index 9637033ac..e083c3c58 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -115,6 +115,8 @@ extern crate bitflags; extern crate tonks; #[macro_use] extern crate num_derive; +#[macro_use] +extern crate feather_codegen; extern crate nalgebra_glm as glm; @@ -157,6 +159,7 @@ pub mod entity; pub mod io; pub mod join; pub mod lazy; +pub mod metadata; pub mod network; pub mod p_inventory; // Prefixed to avoid conflict with inventory crate pub mod packet_handlers; diff --git a/server/src/metadata.rs b/server/src/metadata.rs new file mode 100644 index 000000000..d11c2548e --- /dev/null +++ b/server/src/metadata.rs @@ -0,0 +1,70 @@ +//! Entity metadata implementation. + +use feather_core::entitymeta::EntityMetadata; +use feather_core::inventory::Slot; +use feather_core::world::BlockPosition; +use uuid::Uuid; + +type OptUuid = Option; + +bitflags! { + pub struct EntityBitMask: u8 { + const ON_FIRE = 0x01; + const CROUCHED = 0x02; + const SPRINTING = 0x08; + const SWIMMING = 0x10; + const INVISIBLE = 0x20; + const GLOWING_EFFECT = 0x40; + const FLYING_WITH_ELYTRA = 0x80; + } +} + +bitflags! { + #[derive(Default)] + pub struct ArrowBitMask: u8 { + const CRITICAL = 0x01; + const NO_CLIP = 0x02; + } +} + +lazy_static! { + pub static ref EMPTY_METADATA: Metadata = { Metadata::Entity(Entity::default()) }; +} + +pub type Metadata = _Metadata; + +entity_metadata! { + _Metadata, + Entity { + bit_mask: u8() = 0, + air: VarInt() = 1, + silent: bool() = 4, + no_gravity: bool() = 5, + }, + Item: Entity { + item: Slot() = 6, + }, + Living: Entity { + hand_states: u8() = 6, + health: f32(1.0) = 7, + potion_effect_color: VarInt() = 8, + potion_effect_ambient: bool() = 9, + arrows: VarInt() = 10, + }, + Player: Living { + additional_hearts: f32() = 11, + score: VarInt() = 12, + displayed_skin_parts: u8() = 13, + main_hand: u8(1) = 14, + }, + Arrow: Entity { + arrow_bit_mask: u8() = 6, + shooter: OptUuid() = 7, + }, + TippedArrow: Arrow { + color: VarInt() = 8, + }, + FallingBlock: Entity { + spawn_position: BlockPosition() = 6, + }, +} From 450bf46e28097a0b146102fe3ee916f2c00700c7 Mon Sep 17 00:00:00 2001 From: Wazner Date: Mon, 10 Feb 2020 20:58:02 +0100 Subject: [PATCH 077/647] Add back basic chat implementation --- server/src/broadcasters/chat.rs | 19 ++++++++++ server/src/broadcasters/mod.rs | 1 + server/src/packet_handlers/chat.rs | 16 ++++++++ server/src/packet_handlers/mod.rs | 1 + server/src/player/chat.rs | 59 ++++++++++++++++++++++++++++++ server/src/player/mod.rs | 2 + 6 files changed, 98 insertions(+) create mode 100644 server/src/broadcasters/chat.rs create mode 100644 server/src/packet_handlers/chat.rs create mode 100644 server/src/player/chat.rs diff --git a/server/src/broadcasters/chat.rs b/server/src/broadcasters/chat.rs new file mode 100644 index 000000000..2f2dfecbb --- /dev/null +++ b/server/src/broadcasters/chat.rs @@ -0,0 +1,19 @@ +//! Broadcasting of chat messages + +use crate::state::State; +use crate::player::chat::{ ChatBroadcastEvent, ChatPosition }; +use feather_core::network::packet::implementation::ChatMessageClientbound; + +/// System that broadcasts chat messages to all players +#[event_handler] +fn broadcast_chat(event: &ChatBroadcastEvent, state: &State) { + let packet = ChatMessageClientbound { + json_data: event.json_data.clone(), + position: match event.position { + ChatPosition::Chat => 0, + ChatPosition::SystemMessage => 1, + ChatPosition::GameInfo => 2 + } + }; + state.broadcast_global(packet, None); +} \ No newline at end of file diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index 9c3182ad9..3c0f6f9fa 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -14,3 +14,4 @@ mod inventory; pub mod keepalive; mod metadata; pub mod movement; +mod chat; diff --git a/server/src/packet_handlers/chat.rs b/server/src/packet_handlers/chat.rs new file mode 100644 index 000000000..cf64b21c4 --- /dev/null +++ b/server/src/packet_handlers/chat.rs @@ -0,0 +1,16 @@ +use crate::network::PacketQueue; +use crate::player::chat::PlayerChatEvent; +use feather_core::network::packet::implementation::ChatMessageServerbound; +use tonks::Trigger; + +/// Handles animation packets. +#[system] +fn handle_chat(queue: &PacketQueue, trigger: &mut Trigger) { + queue + .received::() + .for_each(|(player, packet)| { + let message = packet.message; + + trigger.trigger(PlayerChatEvent { player, message }); + }); +} diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index 178f4f7f9..a55def828 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -5,3 +5,4 @@ mod digging; mod inventory; mod movement; mod placement; +mod chat; \ No newline at end of file diff --git a/server/src/player/chat.rs b/server/src/player/chat.rs new file mode 100644 index 000000000..0be43b177 --- /dev/null +++ b/server/src/player/chat.rs @@ -0,0 +1,59 @@ +use legion::entity::Entity; +use crate::entity::Name; +use legion::query::Read; +use tonks::{PreparedWorld, Query, Trigger}; + +/// Event triggered when a player sends a chat message +#[derive(Debug, Clone)] +pub struct PlayerChatEvent { + /// The player that sent the chat message + pub player: Entity, + + /// The raw message that was sent + pub message: String +} + +/// Event that will result in a chat message being broadcasted +pub struct ChatBroadcastEvent { + // TODO: Use composable chat component here + /// A JSON string representing the Chat component to sent + pub json_data: String, + + /// The position + pub position: ChatPosition +} + +/// Different positions a chat message can be displayed +pub enum ChatPosition { + /// Simple message displayed in the chat box + Chat, + + /// System message displayed in the chat box + SystemMessage, + + /// A text displayed above the hotbar + GameInfo +} + +/// System that broadcasts chat messages to all players +#[event_handler] +fn broadcast_chat(event: &PlayerChatEvent, world: &mut PreparedWorld, _query: &mut Query>, trigger: &mut Trigger) { + let player_name = &world.get_component::(event.player) + .unwrap() + .0; + + let json_data = json!({ + "translate": "chat.type.text", + "with": [ + {"text": player_name}, + {"text": event.message} + ] + }).to_string(); + + trigger.trigger(ChatBroadcastEvent { + json_data, + position: ChatPosition::Chat + }); + + info!("<{}> {}", player_name, event.message); +} diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index 9beb7c0b8..7eedf86e3 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -17,6 +17,8 @@ use mojang_api::ProfileProperty; use tonks::{EntityAccessor, PreparedWorld}; use uuid::Uuid; +pub mod chat; + pub const PLAYER_EYE_HEIGHT: f64 = 1.62; /// Profile properties of a player. From 023550814cc92855704102055de731d001d2a5a4 Mon Sep 17 00:00:00 2001 From: Wazner Date: Mon, 10 Feb 2020 21:06:17 +0100 Subject: [PATCH 078/647] Run rustfmt --- server/src/broadcasters/chat.rs | 8 ++++---- server/src/broadcasters/mod.rs | 2 +- server/src/packet_handlers/mod.rs | 2 +- server/src/player/chat.rs | 28 ++++++++++++++++------------ 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/server/src/broadcasters/chat.rs b/server/src/broadcasters/chat.rs index 2f2dfecbb..549c46874 100644 --- a/server/src/broadcasters/chat.rs +++ b/server/src/broadcasters/chat.rs @@ -1,7 +1,7 @@ //! Broadcasting of chat messages +use crate::player::chat::{ChatBroadcastEvent, ChatPosition}; use crate::state::State; -use crate::player::chat::{ ChatBroadcastEvent, ChatPosition }; use feather_core::network::packet::implementation::ChatMessageClientbound; /// System that broadcasts chat messages to all players @@ -12,8 +12,8 @@ fn broadcast_chat(event: &ChatBroadcastEvent, state: &State) { position: match event.position { ChatPosition::Chat => 0, ChatPosition::SystemMessage => 1, - ChatPosition::GameInfo => 2 - } + ChatPosition::GameInfo => 2, + }, }; state.broadcast_global(packet, None); -} \ No newline at end of file +} diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index 3c0f6f9fa..06ad56c66 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -8,10 +8,10 @@ mod animation; mod block; +mod chat; pub mod entity_creation; pub mod entity_deletion; mod inventory; pub mod keepalive; mod metadata; pub mod movement; -mod chat; diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index a55def828..d5f573fb1 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -1,8 +1,8 @@ //! Systems which handle packets through `crate::network::PacketQueue`. mod animation; +mod chat; mod digging; mod inventory; mod movement; mod placement; -mod chat; \ No newline at end of file diff --git a/server/src/player/chat.rs b/server/src/player/chat.rs index 0be43b177..2e1643633 100644 --- a/server/src/player/chat.rs +++ b/server/src/player/chat.rs @@ -1,5 +1,5 @@ -use legion::entity::Entity; use crate::entity::Name; +use legion::entity::Entity; use legion::query::Read; use tonks::{PreparedWorld, Query, Trigger}; @@ -10,7 +10,7 @@ pub struct PlayerChatEvent { pub player: Entity, /// The raw message that was sent - pub message: String + pub message: String, } /// Event that will result in a chat message being broadcasted @@ -19,8 +19,8 @@ pub struct ChatBroadcastEvent { /// A JSON string representing the Chat component to sent pub json_data: String, - /// The position - pub position: ChatPosition + /// The position + pub position: ChatPosition, } /// Different positions a chat message can be displayed @@ -32,15 +32,18 @@ pub enum ChatPosition { SystemMessage, /// A text displayed above the hotbar - GameInfo + GameInfo, } /// System that broadcasts chat messages to all players #[event_handler] -fn broadcast_chat(event: &PlayerChatEvent, world: &mut PreparedWorld, _query: &mut Query>, trigger: &mut Trigger) { - let player_name = &world.get_component::(event.player) - .unwrap() - .0; +fn broadcast_chat( + event: &PlayerChatEvent, + world: &mut PreparedWorld, + _query: &mut Query>, + trigger: &mut Trigger, +) { + let player_name = &world.get_component::(event.player).unwrap().0; let json_data = json!({ "translate": "chat.type.text", @@ -48,11 +51,12 @@ fn broadcast_chat(event: &PlayerChatEvent, world: &mut PreparedWorld, _query: &m {"text": player_name}, {"text": event.message} ] - }).to_string(); - + }) + .to_string(); + trigger.trigger(ChatBroadcastEvent { json_data, - position: ChatPosition::Chat + position: ChatPosition::Chat, }); info!("<{}> {}", player_name, event.message); From 6895f444008d73909d60958ee03065f8702b34eb Mon Sep 17 00:00:00 2001 From: caelunshun Date: Tue, 18 Feb 2020 21:09:21 -0700 Subject: [PATCH 079/647] Enable physics for items; remove debug messages; update tonks --- Cargo.lock | 27 +++-------------- core/src/save/player_data.rs | 2 -- server/Cargo.toml | 2 +- server/src/broadcasters/entity_creation.rs | 34 ++++++++++++++++++++-- server/src/broadcasters/metadata.rs | 1 - server/src/entity/item.rs | 10 +++++-- server/src/metadata.rs | 2 +- server/src/physics/entity.rs | 15 +++++++++- 8 files changed, 60 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 531c2971d..4497f71b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -781,7 +781,7 @@ dependencies = [ "humantime-serde", "inventory", "lazy_static", - "legion 0.2.1 (git+https://github.com/TomGillen/legion?rev=0f67adc237af35799df173f31a2c238b3d8010a2)", + "legion", "lock_api", "log", "mojang-api", @@ -1335,25 +1335,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "legion" -version = "0.2.1" -source = "git+https://github.com/TomGillen/legion?rev=940ef3bfcb77e5d074ee3184b776ff1600da228d#940ef3bfcb77e5d074ee3184b776ff1600da228d" -dependencies = [ - "bit-set", - "crossbeam-channel 0.4.0", - "crossbeam-queue 0.2.1", - "derivative", - "downcast-rs", - "fxhash", - "itertools", - "parking_lot 0.9.0", - "paste", - "rayon", - "smallvec 0.6.13", - "tracing", -] - [[package]] name = "libc" version = "0.2.66" @@ -2879,7 +2860,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=60066a3c5cd521f697b39b2e0006578d2c6f806e#60066a3c5cd521f697b39b2e0006578d2c6f806e" +source = "git+https://github.com/feather-rs/tonks?rev=b75a10c4a96ceddf089e0e2332255840568a2a5d#b75a10c4a96ceddf089e0e2332255840568a2a5d" dependencies = [ "arrayvec", "bit-set", @@ -2889,7 +2870,7 @@ dependencies = [ "hashbrown", "inventory", "lazy_static", - "legion 0.2.1 (git+https://github.com/TomGillen/legion?rev=940ef3bfcb77e5d074ee3184b776ff1600da228d)", + "legion", "mopa", "parking_lot 0.9.0", "rayon", @@ -2902,7 +2883,7 @@ dependencies = [ [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=60066a3c5cd521f697b39b2e0006578d2c6f806e#60066a3c5cd521f697b39b2e0006578d2c6f806e" +source = "git+https://github.com/feather-rs/tonks?rev=b75a10c4a96ceddf089e0e2332255840568a2a5d#b75a10c4a96ceddf089e0e2332255840568a2a5d" dependencies = [ "proc-macro2 1.0.7", "quote 1.0.2", diff --git a/core/src/save/player_data.rs b/core/src/save/player_data.rs index d9cae5f0b..9ff1c31d8 100644 --- a/core/src/save/player_data.rs +++ b/core/src/save/player_data.rs @@ -183,8 +183,6 @@ mod tests { map.insert(x, x as usize); } - dbg!(map.clone()); - // Check all valid slots for (src, expected) in map { let slot = InventorySlot { diff --git a/server/Cargo.toml b/server/Cargo.toml index 8cab9f43e..98661e223 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,7 +21,7 @@ feather-codegen = { path = "../codegen" } # Core ECS + systems legion = { git = "https://github.com/TomGillen/legion", rev = "0f67adc237af35799df173f31a2c238b3d8010a2" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "60066a3c5cd521f697b39b2e0006578d2c6f806e", features = ["system-registry"] } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "b75a10c4a96ceddf089e0e2332255840568a2a5d", features = ["system-registry"] } # tonks = { path = "../../../dev/tonks", features = ["system-registry"] } # Concurrency/threading diff --git a/server/src/broadcasters/entity_creation.rs b/server/src/broadcasters/entity_creation.rs index 88751b5d7..598d8c3a5 100644 --- a/server/src/broadcasters/entity_creation.rs +++ b/server/src/broadcasters/entity_creation.rs @@ -16,7 +16,12 @@ fn broadcast_entity_creation( state: &State, accessor1: &QueryAccessor>, accessor2: &QueryAccessor>, - _query: &mut Query>, + _query: &mut Query<( + Read, + Read, + Read, + Read, + )>, world: &mut PreparedWorld, holders: &ChunkHolders, ) { @@ -31,7 +36,32 @@ fn broadcast_entity_creation( if let Some(accessor) = accessor2.find(event.entity) { if let Some(packet_creator) = accessor.get_component::(world) { let packet = packet_creator.get(&accessor, world); - state.broadcast_entity_update_boxed(event.entity, packet, Some(event.entity)); + // state.broadcast_entity_update_boxed(event.entity, packet, Some(event.entity)); + + // state.broadcast_entity_update_boxed(event.entity, packet, Some(event.entity)); + if let Some(meta) = world.get_component::(event.entity) { + let chunk = world + .get_component::(event.entity) + .unwrap() + .chunk_pos(); + for entity in holders.holders_for(chunk).unwrap_or(&[]) { + if let Some(network) = + world.get_component::(*entity) + { + use feather_core::network::packet::implementation::PacketEntityMetadata; + network.send_boxed(packet.box_clone()); + let entity_id = world + .get_component::(event.entity) + .unwrap() + .0; + let packet = PacketEntityMetadata { + entity_id, + metadata: meta.to_full_raw_metadata(), + }; + network.send(packet); + } + } + } } } diff --git a/server/src/broadcasters/metadata.rs b/server/src/broadcasters/metadata.rs index 942bbdc7f..cad4cc27b 100644 --- a/server/src/broadcasters/metadata.rs +++ b/server/src/broadcasters/metadata.rs @@ -22,7 +22,6 @@ fn send_entity_metadata( entity_id, metadata: meta.to_full_raw_metadata(), }; - dbg!(&packet); network.send(packet); } } diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index 040538c13..2081398ef 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -3,6 +3,7 @@ use crate::entity::{EntityId, SpawnPacketCreator, Velocity}; use crate::lazy::EntityBuilder; use crate::metadata::Metadata; +use crate::physics::PhysicsBuilder; use crate::player::PLAYER_EYE_HEIGHT; use crate::state::State; use crate::util::{degrees_to_stops, protocol_velocity}; @@ -100,6 +101,13 @@ pub fn create( .with_component(CollectableAt(collectable_at)) .with_component(SpawnPacketCreator(&create_spawn_packet)) .with_component(meta) + .with_component( + PhysicsBuilder::new() + .bbox(0.25, 0.25, 0.25) + .drag(0.98) + .gravity(-0.04) + .build(), + ) } fn create_spawn_packet(accessor: &EntityAccessor, world: &PreparedWorld) -> Box { @@ -124,7 +132,5 @@ fn create_spawn_packet(accessor: &EntityAccessor, world: &PreparedWorld) -> Box< velocity_z, }; - dbg!(&packet); - Box::new(packet) } diff --git a/server/src/metadata.rs b/server/src/metadata.rs index d11c2548e..fec660367 100644 --- a/server/src/metadata.rs +++ b/server/src/metadata.rs @@ -37,7 +37,7 @@ entity_metadata! { _Metadata, Entity { bit_mask: u8() = 0, - air: VarInt() = 1, + air: VarInt(300) = 1, silent: bool() = 4, no_gravity: bool() = 5, }, diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index 4a372e4d6..cf790b888 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -1,9 +1,10 @@ //! Module for performing entity physics, including velocity, drag //! and position updates each tick. -use crate::entity::Velocity; +use crate::entity::{EntityMoveEvent, Velocity}; use crate::physics::{block_impacted_by_ray, blocks_intersecting_bbox, AABBExt, Physics, Side}; use crate::state::State; +use crossbeam::queue::SegQueue; use feather_core::Position; use feather_core::{Block, BlockExt}; use legion::entity::Entity; @@ -26,11 +27,15 @@ fn entity_physics( query: &mut Query<(Write, Write, Read)>, world: &mut PreparedWorld, land_events: &mut Trigger, + move_events: &mut Trigger, ) { // Using a mutex is fine, since land events are written very rarely // and thus contention is low. let land_events = Mutex::new(land_events); + // For move events, we use a `SegQueue`. (TODO: switch to ripstruct's SegBuffer after audit) + let move_event_queue = SegQueue::new(); + // Go through entities and update their positions according // to their velocities. query.par_entities_for_each(world, |(entity, (mut position, mut velocity, physics))| { @@ -143,5 +148,13 @@ fn entity_physics( // Set new position. *position = pending_position; + + // Queue move event. + move_event_queue.push(EntityMoveEvent { entity }); }); + + // Copy move events to `Trigger` instance. + while let Ok(ev) = move_event_queue.pop() { + move_events.trigger(ev); + } } From e4fbde56c3c47a9b992fb45b7f2f978531198518 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Wed, 26 Feb 2020 16:25:02 -0700 Subject: [PATCH 080/647] Update tonks - fixes #176 --- Cargo.lock | 4 ++-- server/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4497f71b1..39d1bf631 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2860,7 +2860,7 @@ dependencies = [ [[package]] name = "tonks" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=b75a10c4a96ceddf089e0e2332255840568a2a5d#b75a10c4a96ceddf089e0e2332255840568a2a5d" +source = "git+https://github.com/feather-rs/tonks?rev=0ed28a624a21d044011058f74771461dd0b35c2a#0ed28a624a21d044011058f74771461dd0b35c2a" dependencies = [ "arrayvec", "bit-set", @@ -2883,7 +2883,7 @@ dependencies = [ [[package]] name = "tonks-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=b75a10c4a96ceddf089e0e2332255840568a2a5d#b75a10c4a96ceddf089e0e2332255840568a2a5d" +source = "git+https://github.com/feather-rs/tonks?rev=0ed28a624a21d044011058f74771461dd0b35c2a#0ed28a624a21d044011058f74771461dd0b35c2a" dependencies = [ "proc-macro2 1.0.7", "quote 1.0.2", diff --git a/server/Cargo.toml b/server/Cargo.toml index 98661e223..a1494702a 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,7 +21,7 @@ feather-codegen = { path = "../codegen" } # Core ECS + systems legion = { git = "https://github.com/TomGillen/legion", rev = "0f67adc237af35799df173f31a2c238b3d8010a2" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "b75a10c4a96ceddf089e0e2332255840568a2a5d", features = ["system-registry"] } +tonks = { git = "https://github.com/feather-rs/tonks", rev = "0ed28a624a21d044011058f74771461dd0b35c2a", features = ["system-registry"] } # tonks = { path = "../../../dev/tonks", features = ["system-registry"] } # Concurrency/threading From aa359bf8d9d0cc365495f0c5cafd66b3f0cbf4a7 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 28 Feb 2020 17:23:25 -0700 Subject: [PATCH 081/647] Reimplement item collection --- core/src/inventory.rs | 2 +- server/src/entity/item.rs | 95 +++++++++++++++++++++++++-- server/src/lazy.rs | 28 ++++++++ server/src/packet_handlers/digging.rs | 36 +++++----- server/src/player/mod.rs | 5 ++ server/src/state.rs | 5 ++ server/src/view.rs | 9 ++- 7 files changed, 157 insertions(+), 23 deletions(-) diff --git a/core/src/inventory.rs b/core/src/inventory.rs index 7049e57db..91856889a 100644 --- a/core/src/inventory.rs +++ b/core/src/inventory.rs @@ -361,7 +361,7 @@ impl Inventory { /// Represents an item stack. /// /// An item stack includes a type, an amount, and a bunch of properties (enchantments, etc.) -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ItemStack { /// The type of this item. pub ty: Item, diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index 2081398ef..30b617672 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -1,9 +1,10 @@ //! Handling of item entities. -use crate::entity::{EntityId, SpawnPacketCreator, Velocity}; +use crate::entity::{EntityId, EntityMoveEvent, SpawnPacketCreator, Velocity}; use crate::lazy::EntityBuilder; use crate::metadata::Metadata; -use crate::physics::PhysicsBuilder; +use crate::p_inventory::{EntityInventory, InventoryUpdateEvent}; +use crate::physics::{nearby_entities, PhysicsBuilder}; use crate::player::PLAYER_EYE_HEIGHT; use crate::state::State; use crate::util::{degrees_to_stops, protocol_velocity}; @@ -12,9 +13,11 @@ use feather_core::inventory::SlotIndex; use feather_core::network::packet::implementation::SpawnObject; use feather_core::{ItemStack, Packet, Position}; use legion::entity::Entity; -use legion::query::Read; +use legion::query::{Read, Write}; use rand::Rng; -use tonks::{EntityAccessor, PreparedWorld, Query}; +use std::ops::DerefMut; +use std::sync::atomic::{AtomicBool, Ordering}; +use tonks::{EntityAccessor, PreparedWorld, Query, Trigger}; use uuid::Uuid; /// Event triggered when an item is dropped. @@ -36,6 +39,9 @@ pub struct ItemDropEvent { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct CollectableAt(pub u64); +/// Component storing if an item stack has been collected and queued for removal. +pub struct IsRemoved(AtomicBool); + // Item stack of an item entity is stored in `ItemStack` component /// System for spawning an item entity when @@ -82,6 +88,86 @@ pub fn item_spawn( .build(); } +/// System to add items to entity inventories. +#[event_handler] +pub fn item_collect( + events: &[EntityMoveEvent], + state: &State, + _query: &mut Query<( + Write, + Read, + Write, + Write, + Read, + )>, + world: &mut PreparedWorld, + trigger: &mut Trigger, +) { + // TODO: switch to par_iter + events.iter().for_each(|event: &EntityMoveEvent| { + if world + .get_component::(event.entity) + .is_none() + { + return; + } + + let pos = *world.get_component::(event.entity).unwrap(); + // Find nearby items. + let nearby_entities = + nearby_entities(&state.chunk_entities, world, pos, glm::vec3(1.0, 0.5, 1.0)); + + for other in nearby_entities { + if let Some(item_stack) = world.get_component::(other).map(|item| *item) { + // Ensure that this item hasn't already been collected, to avoid duplication. + { + let is_removed = world.get_component::(other).unwrap(); + if is_removed + .0 + .compare_and_swap(false, true, Ordering::Relaxed) + { + continue; + } + } + + let (affected_slots, items_left) = { + let mut inventory = world + .get_component_mut::(event.entity) + .unwrap(); + inventory.collect_item(item_stack) + }; + + trigger.trigger(InventoryUpdateEvent { + slots: affected_slots, + player: event.entity, + }); + + if items_left == 0 { + state.delete_entity(other); + } else { + // Update item stack + let new_stack = ItemStack::new(item_stack.ty, items_left); + *world.get_component_mut::(other).unwrap() = new_stack; + match world + .get_component_mut::(other) + .unwrap() + .deref_mut() + { + Metadata::Item(ref mut meta_item) => meta_item.set_item(Some(new_stack)), + _ => unreachable!(), + } + + world + .get_component::(other) + .unwrap() + .0 + .store(false, Ordering::Relaxed); + } + } + } + }); +} + /// Returns an entity builder to create an item entity /// with the given stack and collectable tick. pub fn create( @@ -101,6 +187,7 @@ pub fn create( .with_component(CollectableAt(collectable_at)) .with_component(SpawnPacketCreator(&create_spawn_packet)) .with_component(meta) + .with_component(IsRemoved(AtomicBool::new(false))) .with_component( PhysicsBuilder::new() .bbox(0.25, 0.25, 0.25) diff --git a/server/src/lazy.rs b/server/src/lazy.rs index 385f09422..27fd1f1fb 100644 --- a/server/src/lazy.rs +++ b/server/src/lazy.rs @@ -1,9 +1,12 @@ +use crate::entity::{EntityDeleteEvent, EntityId}; use crossbeam::queue::SegQueue; +use feather_core::Position; use legion::entity::Entity; use legion::storage::{Component, Tag}; use legion::world::World; use smallvec::SmallVec; use tonks::Scheduler; +use uuid::Uuid; pub trait LazyFnWithScheduler: FnOnce(&mut World, &mut Scheduler) + Send {} impl LazyFnWithScheduler for F where F: FnOnce(&mut World, &mut Scheduler) + Send {} @@ -43,6 +46,31 @@ impl Lazy { } } + /// Deletes an entity, triggering the necessary event as well. + pub fn delete_entity(&self, entity: Entity) { + self.exec_with_scheduler(move |world, scheduler| { + if !world.is_alive(entity) { + return; + } + + let position = world.get_component::(entity).map(|pos| *pos); + let id = *world.get_component::(entity).unwrap(); + let uuid = world + .get_component::(entity) + .map(|u| *u) + .unwrap_or(Uuid::new_v4()); + + scheduler.trigger(EntityDeleteEvent { + entity, + position, + id, + uuid, + }); + + world.delete(entity); + }); + } + /// Performs all queued actions. pub fn flush(&self, world: &mut World, scheduler: &mut Scheduler) { while let Ok(action) = self.queue.pop() { diff --git a/server/src/packet_handlers/digging.rs b/server/src/packet_handlers/digging.rs index c5d5cd814..032c7b1f9 100644 --- a/server/src/packet_handlers/digging.rs +++ b/server/src/packet_handlers/digging.rs @@ -5,15 +5,17 @@ //! swapping items out to the offhand, and dropping items. use crate::block::BlockUpdateCause; +use crate::entity::item::ItemDropEvent; use crate::network::PacketQueue; -use crate::p_inventory::EntityInventory; +use crate::p_inventory::{EntityInventory, InventoryUpdateEvent}; use crate::state::State; use crate::util::disconnect_player; +use feather_core::inventory::SLOT_HOTBAR_OFFSET; use feather_core::network::packet::implementation::{PlayerDigging, PlayerDiggingStatus}; use feather_core::{Block, Gamemode, Item, ItemStack, Position}; use legion::entity::Entity; use legion::query::{Read, Write}; -use tonks::{PreparedWorld, Query}; +use tonks::{PreparedWorld, Query, Trigger}; /// System responsible for polling for PlayerDigging /// packets and writing the corresponding events. @@ -23,6 +25,8 @@ fn handle_player_digging( queue: &PacketQueue, _query: &mut Query<(Write, Read, Read)>, world: &mut PreparedWorld, + inventory_updates: &mut Trigger, + item_drops: &mut Trigger, ) { use PlayerDiggingStatus::*; @@ -30,7 +34,7 @@ fn handle_player_digging( for (player, packet) in packets { let gamemode = *world.get_component::(player).unwrap(); - let inventory = world.get_component_mut::(player).unwrap(); + let mut inventory = world.get_component_mut::(player).unwrap(); match packet.status { StartedDigging | FinishedDigging | CancelledDigging => handle_digging( @@ -40,14 +44,14 @@ fn handle_player_digging( gamemode, inventory.item_in_main_hand(), ), - /* DropItem | DropItemStack => handle_drop_item_stack( packet, player, - &mut inventory_updates, - &mut item_drops, - inventories.get_mut(player).unwrap(), + inventory_updates, + item_drops, + &mut inventory, ), + /* ConsumeItem => handle_consume_item( packet, players.get(player).unwrap(), @@ -105,13 +109,12 @@ fn handle_digging( } } -/* fn handle_drop_item_stack( - packet: &PlayerDigging, + packet: PlayerDigging, entity: Entity, - inventory_updates: &mut EventChannel, - item_drops: &mut EventChannel, - inventory: &mut InventoryComponent, + inventory_updates: &mut Trigger, + item_drops: &mut Trigger, + inventory: &mut EntityInventory, ) { assert!( packet.status == PlayerDiggingStatus::DropItem @@ -122,7 +125,7 @@ fn handle_drop_item_stack( let stack = { if let Some(item) = inventory.item_at(slot) { - item.clone() + *item } else { // Silently fail - no item stack to drop return; @@ -153,18 +156,19 @@ fn handle_drop_item_stack( slots: smallvec![slot], player: entity, }; - inventory_updates.single_write(inv_update); + inventory_updates.trigger(inv_update); if amnt != 0 { - let item_drop = PlayerItemDropEvent { + let item_drop = ItemDropEvent { slot: Some(slot), stack: ItemStack::new(stack.ty, amnt), player: entity, }; - item_drops.single_write(item_drop); + item_drops.trigger(item_drop); } } +/* /// Handles food consumption and shooting arrows. fn handle_consume_item( packet: &PlayerDigging, diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index 7eedf86e3..e9f6235ca 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -25,6 +25,10 @@ pub const PLAYER_EYE_HEIGHT: f64 = 1.62; #[derive(Debug, Clone)] pub struct ProfileProperties(pub Vec); +/// Zero-sized component used to mark players. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Player; + /// Event triggered when a player joins. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct PlayerJoinEvent { @@ -58,6 +62,7 @@ pub fn create(state: &State, info: NewClientInfo) { .with_component(CreationPacketCreator(&create_initialization_packet)) .with_component(Gamemode::Creative) // TOOD: proper gamemode handling .with_component(EntityInventory::default()) + .with_component(Player) .with_exec(|_, scheduler, player| { scheduler.trigger(PlayerJoinEvent { player }); }) diff --git a/server/src/state.rs b/server/src/state.rs index dca0d1862..2de53cc7a 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -146,6 +146,11 @@ impl State { self.lazy.create_entity() } + /// See `Lazy::delete_entity()`. + pub fn delete_entity(&self, entity: Entity) { + self.lazy.delete_entity(entity) + } + /// Lazily broadcasts a packet to all clients able to see the given entity. /// /// The packet will not be sent to `neq`. diff --git a/server/src/view.rs b/server/src/view.rs index 0a8f71483..690ea7e93 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -24,7 +24,7 @@ use crate::chunk_logic::{ use crate::config::Config; use crate::entity::{EntityId, EntityMoveEvent, PreviousPosition, SpawnPacketCreator}; use crate::network::Network; -use crate::player::PlayerJoinEvent; +use crate::player::{Player, PlayerJoinEvent}; use crate::state::State; use chashmap::CHashMap; use feather_core::network::packet::implementation::{ChunkData, DestroyEntities, UnloadChunk}; @@ -65,13 +65,18 @@ pub struct ChunkSendEvent { #[event_handler] fn view_update( events: &[EntityMoveEvent], - _query: &mut Query<(Read, Read)>, + _query: &mut Query<(Read, Read, Read)>, world: &mut PreparedWorld, state: &State, trigger: &mut Trigger, ) { let trigger = Mutex::new(trigger); events.par_iter().for_each(|event| { + // Only process view for players. + if world.get_component::(event.entity).is_none() { + return; + } + let pos = *world.get_component::(event.entity).unwrap(); let prev_pos = world .get_component::(event.entity) From 62cf5638db1520eeb1e35ce6474c31df43c51f6f Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 28 Feb 2020 17:35:45 -0700 Subject: [PATCH 082/647] Send item collect packets when an item is picked up --- server/src/broadcasters/item_collect.rs | 24 ++++++++++++++++++++++++ server/src/broadcasters/mod.rs | 1 + server/src/entity/item.rs | 22 ++++++++++++++++++++-- 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 server/src/broadcasters/item_collect.rs diff --git a/server/src/broadcasters/item_collect.rs b/server/src/broadcasters/item_collect.rs new file mode 100644 index 000000000..77cfbf1cd --- /dev/null +++ b/server/src/broadcasters/item_collect.rs @@ -0,0 +1,24 @@ +use crate::entity::item::ItemCollectEvent; +use crate::entity::EntityId; +use crate::state::State; +use feather_core::network::packet::implementation::CollectItem; +use legion::query::Read; +use tonks::{PreparedWorld, Query}; + +/// Sends `CollectItem` packet when an item is collected. +#[event_handler] +pub fn broadcast_item_collect( + event: &ItemCollectEvent, + state: &State, + _query: &mut Query>, + world: &mut PreparedWorld, +) { + let packet = CollectItem { + collected: world.get_component::(event.item).unwrap().0, + collector: world.get_component::(event.item).unwrap().0, + count: event.amount as i32, + }; + + // TODO: broadcast for item instead + state.broadcast_entity_update(event.collector, packet, None); +} diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index 06ad56c66..603f11e0a 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -12,6 +12,7 @@ mod chat; pub mod entity_creation; pub mod entity_deletion; mod inventory; +mod item_collect; pub mod keepalive; mod metadata; pub mod movement; diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index 30b617672..9b0740978 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -35,6 +35,17 @@ pub struct ItemDropEvent { pub player: Entity, } +/// Event triggered when an item is collected. +#[derive(Debug, Clone)] +pub struct ItemCollectEvent { + /// Item entity which was collected. + pub item: Entity, + /// Entity which collected the item. + pub collector: Entity, + /// Number of the item which was picked up. + pub amount: u8, +} + /// Component storing the tick at which an item becomes collectable. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct CollectableAt(pub u64); @@ -101,7 +112,8 @@ pub fn item_collect( Read, )>, world: &mut PreparedWorld, - trigger: &mut Trigger, + inventory_updates: &mut Trigger, + item_collects: &mut Trigger, ) { // TODO: switch to par_iter events.iter().for_each(|event: &EntityMoveEvent| { @@ -137,11 +149,17 @@ pub fn item_collect( inventory.collect_item(item_stack) }; - trigger.trigger(InventoryUpdateEvent { + inventory_updates.trigger(InventoryUpdateEvent { slots: affected_slots, player: event.entity, }); + item_collects.trigger(ItemCollectEvent { + item: other, + collector: event.entity, + amount: item_stack.amount - items_left, + }); + if items_left == 0 { state.delete_entity(other); } else { From 86c8f1b1ce08502a4c88e70aad72d19c09c70bc3 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 28 Feb 2020 17:55:34 -0700 Subject: [PATCH 083/647] Broadcast entity velocity updates --- server/src/broadcasters/movement.rs | 29 ++++++++++++++++++++++++++--- server/src/entity/mod.rs | 7 +++++++ server/src/physics/entity.rs | 11 +++++++++-- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/server/src/broadcasters/movement.rs b/server/src/broadcasters/movement.rs index 26a0ee7f5..f1e34e41a 100644 --- a/server/src/broadcasters/movement.rs +++ b/server/src/broadcasters/movement.rs @@ -1,11 +1,12 @@ //! Broadcasting of movement updates. use crate::chunk_logic::ChunkHolders; -use crate::entity::{EntityId, EntityMoveEvent}; +use crate::entity::{EntityId, EntityMoveEvent, Velocity, VelocityUpdateEvent}; use crate::network::Network; -use crate::util::{calculate_relative_move, degrees_to_stops}; +use crate::state::State; +use crate::util::{calculate_relative_move, degrees_to_stops, protocol_velocity}; use feather_core::network::packet::implementation::{ - EntityHeadLook, EntityLook, EntityLookAndRelativeMove, EntityRelativeMove, + EntityHeadLook, EntityLook, EntityLookAndRelativeMove, EntityRelativeMove, EntityVelocity, }; use feather_core::{Packet, Position}; use hashbrown::HashMap; @@ -76,6 +77,28 @@ fn broadcast_move( }); } +/// Broadcasts an entity's velocity. +#[event_handler] +pub fn broadcast_velocity( + event: &VelocityUpdateEvent, + _query: &mut Query<(Read, Read)>, + world: &mut PreparedWorld, + state: &State, +) { + let entity_id = world.get_component::(event.entity).unwrap().0; + let vel = *world.get_component::(event.entity).unwrap(); + + let (velocity_x, velocity_y, velocity_z) = protocol_velocity(vel.0); + + let packet = EntityVelocity { + entity_id, + velocity_x, + velocity_y, + velocity_z, + }; + state.broadcast_entity_update(event.entity, packet, None); +} + /// Returns the packet needed to notify a client /// of a position update, from the old position to the new one. #[allow(clippy::float_cmp)] diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 74f35a2c7..7fdc59fd8 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -52,6 +52,13 @@ pub struct EntityMoveEvent { pub entity: Entity, } +/// Event triggered when an entity's velocity changes. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct VelocityUpdateEvent { + /// Entity whose velocity changed. + pub entity: Entity, +} + /// The velocity of an entity. #[derive(Debug, PartialEq, Clone, Copy)] pub struct Velocity(pub glm::DVec3); diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index cf790b888..2b07efa7a 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -1,7 +1,7 @@ //! Module for performing entity physics, including velocity, drag //! and position updates each tick. -use crate::entity::{EntityMoveEvent, Velocity}; +use crate::entity::{EntityMoveEvent, Velocity, VelocityUpdateEvent}; use crate::physics::{block_impacted_by_ray, blocks_intersecting_bbox, AABBExt, Physics, Side}; use crate::state::State; use crossbeam::queue::SegQueue; @@ -28,6 +28,7 @@ fn entity_physics( world: &mut PreparedWorld, land_events: &mut Trigger, move_events: &mut Trigger, + velocity_events: &mut Trigger, ) { // Using a mutex is fine, since land events are written very rarely // and thus contention is low. @@ -35,6 +36,7 @@ fn entity_physics( // For move events, we use a `SegQueue`. (TODO: switch to ripstruct's SegBuffer after audit) let move_event_queue = SegQueue::new(); + let velocity_event_queue = SegQueue::new(); // Go through entities and update their positions according // to their velocities. @@ -149,12 +151,17 @@ fn entity_physics( // Set new position. *position = pending_position; - // Queue move event. + // Queue move event + velocity event. move_event_queue.push(EntityMoveEvent { entity }); + velocity_event_queue.push(VelocityUpdateEvent { entity }); }); // Copy move events to `Trigger` instance. while let Ok(ev) = move_event_queue.pop() { move_events.trigger(ev); } + + while let Ok(ev) = velocity_event_queue.pop() { + velocity_events.trigger(ev); + } } From c71ae17ddf31edd8784ce3e17b13150ed09d4cf2 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 29 Feb 2020 12:25:43 -0700 Subject: [PATCH 084/647] Update tokio, bytes, reqwest, mojang-api to stable async-await --- Cargo.lock | 503 ++++++++++++++++---------------------- core/Cargo.toml | 5 +- core/src/bytes_ext.rs | 32 +-- core/src/network/codec.rs | 16 +- core/src/world/mod.rs | 8 +- server/Cargo.toml | 10 +- server/src/io/mod.rs | 2 +- server/src/io/worker.rs | 5 +- 8 files changed, 244 insertions(+), 337 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 39d1bf631..b19a48d1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,6 +92,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arc-swap" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff" + [[package]] name = "arrayvec" version = "0.5.1" @@ -163,6 +169,12 @@ dependencies = [ "byteorder", ] +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "bit-set" version = "0.5.1" @@ -239,6 +251,12 @@ dependencies = [ "iovec", ] +[[package]] +name = "bytes" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" + [[package]] name = "c2-chacha" version = "0.2.3" @@ -429,20 +447,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e" dependencies = [ "cfg-if", - "crossbeam-channel 0.4.0", + "crossbeam-channel", "crossbeam-deque", "crossbeam-epoch", - "crossbeam-queue 0.2.1", - "crossbeam-utils 0.7.0", -] - -[[package]] -name = "crossbeam-channel" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" -dependencies = [ - "crossbeam-utils 0.6.6", + "crossbeam-queue", + "crossbeam-utils", ] [[package]] @@ -451,7 +460,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" dependencies = [ - "crossbeam-utils 0.7.0", + "crossbeam-utils", ] [[package]] @@ -461,7 +470,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" dependencies = [ "crossbeam-epoch", - "crossbeam-utils 0.7.0", + "crossbeam-utils", ] [[package]] @@ -472,21 +481,12 @@ checksum = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" dependencies = [ "autocfg 0.1.7", "cfg-if", - "crossbeam-utils 0.7.0", + "crossbeam-utils", "lazy_static", "memoffset", "scopeguard", ] -[[package]] -name = "crossbeam-queue" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -dependencies = [ - "crossbeam-utils 0.6.6", -] - [[package]] name = "crossbeam-queue" version = "0.2.1" @@ -494,17 +494,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" dependencies = [ "cfg-if", - "crossbeam-utils 0.7.0", -] - -[[package]] -name = "crossbeam-utils" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -dependencies = [ - "cfg-if", - "lazy_static", + "crossbeam-utils", ] [[package]] @@ -689,7 +679,7 @@ dependencies = [ "aes", "bitvec", "byteorder", - "bytes", + "bytes 0.5.4", "cfb8", "derive-new", "derive_more", @@ -715,6 +705,7 @@ dependencies = [ "strum", "strum_macros", "tokio", + "tokio-util", "uuid", ] @@ -758,11 +749,11 @@ name = "feather-server" version = "0.5.0" dependencies = [ "ahash", - "base64", + "base64 0.10.1", "bitflags", "bitvec", "bumpalo 2.6.0", - "bytes", + "bytes 0.5.4", "chashmap", "criterion", "crossbeam", @@ -774,7 +765,7 @@ dependencies = [ "feather-core", "feather-item-block", "fnv", - "futures-preview", + "futures", "hashbrown", "heapless", "hematite-nbt", @@ -807,7 +798,7 @@ dependencies = [ "strum", "thread_local", "tokio", - "tokio-executor", + "tokio-util", "toml", "tonks", "uuid", @@ -890,48 +881,57 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.1.29" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" +checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] [[package]] -name = "futures-channel-preview" -version = "0.3.0-alpha.19" +name = "futures-channel" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5e5f4df964fa9c1c2f8bddeb5c3611631cacd93baf810fc8bb2fb4b495c263a" +checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" dependencies = [ - "futures-core-preview", - "futures-sink-preview", + "futures-core", + "futures-sink", ] [[package]] -name = "futures-core-preview" -version = "0.3.0-alpha.19" +name = "futures-core" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35b6263fb1ef523c3056565fa67b1d16f0a8604ff12b11b08c25f28a734c60a" +checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" [[package]] -name = "futures-executor-preview" -version = "0.3.0-alpha.19" +name = "futures-executor" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75236e88bd9fe88e5e8bfcd175b665d0528fe03ca4c5207fabc028c8f9d93e98" +checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" dependencies = [ - "futures-core-preview", - "futures-util-preview", - "num_cpus", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "futures-io-preview" -version = "0.3.0-alpha.19" +name = "futures-io" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4914ae450db1921a56c91bde97a27846287d062087d4a652efc09bb3a01ebda" +checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" [[package]] -name = "futures-join-macro-preview" -version = "0.3.0-alpha.19" +name = "futures-macro" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e260e6b48ce7d99936c40a7088d782a499a67bef41da5481d21b439454bcea" +checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" dependencies = [ "proc-macro-hack", "proc-macro2 1.0.7", @@ -940,49 +940,29 @@ dependencies = [ ] [[package]] -name = "futures-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b1dce2a0267ada5c6ff75a8ba864b4e679a9e2aa44262af7a3b5516d530d76e" -dependencies = [ - "futures-channel-preview", - "futures-core-preview", - "futures-executor-preview", - "futures-io-preview", - "futures-sink-preview", - "futures-util-preview", -] - -[[package]] -name = "futures-select-macro-preview" -version = "0.3.0-alpha.19" +name = "futures-sink" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2ae43560eb10b5e50604c53bead6c9c75eade7081390cd3cce66e1582958f7" -dependencies = [ - "proc-macro-hack", - "proc-macro2 1.0.7", - "quote 1.0.2", - "syn 1.0.13", -] +checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" [[package]] -name = "futures-sink-preview" -version = "0.3.0-alpha.19" +name = "futures-task" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f148ef6b69f75bb610d4f9a2336d4fc88c4b5b67129d1a340dd0fd362efeec" +checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" [[package]] -name = "futures-util-preview" -version = "0.3.0-alpha.19" +name = "futures-util" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce968633c17e5f97936bd2797b6e38fb56cf16a7422319f7ec2e30d3c470e8d" +checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" dependencies = [ - "futures-channel-preview", - "futures-core-preview", - "futures-io-preview", - "futures-join-macro-preview", - "futures-select-macro-preview", - "futures-sink-preview", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", "memchr", "pin-utils", "proc-macro-hack", @@ -1041,23 +1021,21 @@ dependencies = [ [[package]] name = "h2" -version = "0.2.0-alpha.3" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f107db1419ef8271686187b1a5d47c6431af4a7f4d98b495e7b7fc249bb0a78" +checksum = "b9433d71e471c1736fd5a61b671fc0b148d7a2992f666c958d03cd8feb3b88d1" dependencies = [ - "bytes", + "bytes 0.5.4", "fnv", - "futures-core-preview", - "futures-sink-preview", - "futures-util-preview", + "futures-core", + "futures-sink", + "futures-util", "http", "indexmap", "log", "slab", - "string", - "tokio-codec", - "tokio-io", - "tokio-sync", + "tokio", + "tokio-util", ] [[package]] @@ -1135,22 +1113,22 @@ dependencies = [ [[package]] name = "http" -version = "0.1.21" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" +checksum = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" dependencies = [ - "bytes", + "bytes 0.5.4", "fnv", "itoa", ] [[package]] name = "http-body" -version = "0.2.0-alpha.3" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3aef6f3de2bd8585f5b366f3f550b5774500b4764d00cf00f903c95749eec3" +checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ - "bytes", + "bytes 0.5.4", "http", ] @@ -1181,43 +1159,38 @@ dependencies = [ [[package]] name = "hyper" -version = "0.13.0-alpha.4" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d05aa523087ac0b9d8b93dd80d5d482a697308ed3b0dca7b0667511a7fa7cdc" +checksum = "fa1c527bbc634be72aa7ba31e4e4def9bbb020f5416916279b7c705cd838893e" dependencies = [ - "bytes", - "futures-channel-preview", - "futures-core-preview", - "futures-util-preview", + "bytes 0.5.4", + "futures-channel", + "futures-core", + "futures-util", "h2", "http", "http-body", "httparse", - "iovec", "itoa", "log", "net2", "pin-project", "time", - "tokio-executor", - "tokio-io", - "tokio-net", - "tokio-sync", - "tokio-timer", - "tower-make", + "tokio", "tower-service", "want", ] [[package]] name = "hyper-tls" -version = "0.4.0-alpha.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47cb3975f80cc809efe5dfcc52b73c9b281fde33f2df35a2e5f79f35e384ae7f" +checksum = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa" dependencies = [ + "bytes 0.5.4", "hyper", "native-tls", - "tokio-io", + "tokio", "tokio-tls", ] @@ -1322,8 +1295,8 @@ version = "0.2.1" source = "git+https://github.com/TomGillen/legion?rev=0f67adc237af35799df173f31a2c238b3d8010a2#0f67adc237af35799df173f31a2c238b3d8010a2" dependencies = [ "bit-set", - "crossbeam-channel 0.4.0", - "crossbeam-queue 0.2.1", + "crossbeam-channel", + "crossbeam-queue", "derivative", "downcast-rs", "fxhash", @@ -1458,12 +1431,24 @@ dependencies = [ "kernel32-sys", "libc", "log", - "miow", + "miow 0.2.1", "net2", "slab", "winapi 0.2.8", ] +[[package]] +name = "mio-named-pipes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" +dependencies = [ + "log", + "mio", + "miow 0.3.3", + "winapi 0.3.8", +] + [[package]] name = "mio-uds" version = "0.6.7" @@ -1487,13 +1472,23 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "miow" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" +dependencies = [ + "socket2", + "winapi 0.3.8", +] + [[package]] name = "mojang-api" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f301933426b04d4557b52f7715a1e62dd9c43bc64625c645cbb50de70d5692" +checksum = "4bf9caa940ff4f28757e9414f51ca1a1dbb70a6e18d4dda462504344cb00fec1" dependencies = [ - "bytes", + "bytes 0.4.12", "log", "num-bigint", "reqwest", @@ -1889,6 +1884,12 @@ dependencies = [ "syn 1.0.13", ] +[[package]] +name = "pin-project-lite" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" + [[package]] name = "pin-utils" version = "0.1.0-alpha.4" @@ -2196,8 +2197,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" dependencies = [ "crossbeam-deque", - "crossbeam-queue 0.2.1", - "crossbeam-utils 0.7.0", + "crossbeam-queue", + "crossbeam-utils", "lazy_static", "num_cpus", ] @@ -2255,33 +2256,33 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.10.0-alpha.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d75dbf305ed1eb54d3c8564e3b746012166b40ec0841381df92b50a2052db71" +checksum = "a9f62f24514117d09a8fc74b803d3d65faa27cea1c7378fb12b0d002913f3831" dependencies = [ - "base64", - "bytes", + "base64 0.11.0", + "bytes 0.5.4", "encoding_rs", - "futures-core-preview", - "futures-util-preview", + "futures-core", + "futures-util", "http", "http-body", "hyper", "hyper-tls", "js-sys", + "lazy_static", "log", "mime", "mime_guess", "native-tls", "percent-encoding", + "pin-project-lite", "serde", "serde_urlencoded", "time", "tokio", - "tokio-executor", "tokio-tls", "url", - "uuid", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -2446,6 +2447,16 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +[[package]] +name = "signal-hook-registry" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" +dependencies = [ + "arc-swap", + "libc", +] + [[package]] name = "simdeez" version = "0.6.6" @@ -2525,6 +2536,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" +[[package]] +name = "socket2" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.8", +] + [[package]] name = "sourcefile" version = "0.1.4" @@ -2558,15 +2581,6 @@ dependencies = [ "generic-array 0.12.3", ] -[[package]] -name = "string" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" -dependencies = [ - "bytes", -] - [[package]] name = "strsim" version = "0.8.0" @@ -2697,155 +2711,61 @@ dependencies = [ [[package]] name = "tokio" -version = "0.2.0-alpha.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f17f5d6ab0f35c1506678b28fb1798bdf74fcb737e9843c7b17b73e426eba38" -dependencies = [ - "bytes", - "futures-core-preview", - "futures-sink-preview", - "futures-util-preview", - "num_cpus", - "tokio-codec", - "tokio-executor", - "tokio-fs", - "tokio-io", - "tokio-macros", - "tokio-net", - "tokio-sync", - "tokio-timer", - "tracing-core", -] - -[[package]] -name = "tokio-codec" -version = "0.2.0-alpha.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f5d22fd1e84bd4045d28813491cb7d7caae34d45c80517c2213f09a85e8787a" -dependencies = [ - "bytes", - "futures-core-preview", - "futures-sink-preview", - "log", - "tokio-io", -] - -[[package]] -name = "tokio-executor" -version = "0.2.0-alpha.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee9ceecf69145923834ea73f32ba40c790fd877b74a7817dd0b089f1eb9c7c8" -dependencies = [ - "crossbeam-channel 0.3.9", - "crossbeam-deque", - "crossbeam-queue 0.1.2", - "crossbeam-utils 0.6.6", - "futures-core-preview", - "futures-util-preview", - "lazy_static", - "num_cpus", - "slab", - "tokio-sync", - "tracing", -] - -[[package]] -name = "tokio-fs" -version = "0.2.0-alpha.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf85e16971e06e680c622e0c1b455be94b086275c5ddcd6d4a83a2bfbb83cda" -dependencies = [ - "futures-core-preview", - "futures-util-preview", - "lazy_static", - "tokio-executor", - "tokio-io", - "tokio-sync", -] - -[[package]] -name = "tokio-io" -version = "0.2.0-alpha.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112784d5543df30660b04a72ca423bfbd90e8bb32f94dcf610f15401218b22c5" -dependencies = [ - "bytes", - "futures-core-preview", - "log", - "memchr", - "pin-project", -] - -[[package]] -name = "tokio-macros" -version = "0.2.0-alpha.6" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b616374bcdadd95974e1f0dfca07dc913f1163c53840c0d664aca35114964e" +checksum = "0fa5e81d6bc4e67fe889d5783bd2a128ab2e0cfa487e0be16b6a8d177b101616" dependencies = [ - "quote 1.0.2", - "syn 1.0.13", -] - -[[package]] -name = "tokio-net" -version = "0.2.0-alpha.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a441682cd32f3559383112c4a7f372f5c9fa1950c5cf8c8dd05274a2ce8c2654" -dependencies = [ - "bytes", - "crossbeam-utils 0.6.6", - "futures-core-preview", - "futures-sink-preview", - "futures-util-preview", + "bytes 0.5.4", + "fnv", + "futures-core", "iovec", "lazy_static", "libc", + "memchr", "mio", + "mio-named-pipes", "mio-uds", "num_cpus", - "parking_lot 0.9.0", + "pin-project-lite", + "signal-hook-registry", "slab", - "tokio-codec", - "tokio-executor", - "tokio-io", - "tokio-sync", - "tracing", + "tokio-macros", + "winapi 0.3.8", ] [[package]] -name = "tokio-sync" -version = "0.2.0-alpha.6" +name = "tokio-macros" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1aaeb685540f7407ea0e27f1c9757d258c7c6bf4e3eb19da6fc59b747239d2" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" dependencies = [ - "fnv", - "futures-core-preview", - "futures-sink-preview", - "futures-util-preview", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] -name = "tokio-timer" -version = "0.3.0-alpha.6" +name = "tokio-tls" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97c1587fe71018eb245a4a9daa13a5a3b681bbc1f7fdadfe24720e141472c13" +checksum = "7bde02a3a5291395f59b06ec6945a3077602fac2b07eeeaf0dee2122f3619828" dependencies = [ - "crossbeam-utils 0.6.6", - "futures-core-preview", - "futures-util-preview", - "slab", - "tokio-executor", - "tokio-sync", + "native-tls", + "tokio", ] [[package]] -name = "tokio-tls" -version = "0.3.0-alpha.6" +name = "tokio-util" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566b4086589c7eebb86aa625d302ab80720ef2aa088649dcae18ec4d754cbd16" +checksum = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" dependencies = [ - "native-tls", - "tokio-io", + "bytes 0.5.4", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", ] [[package]] @@ -2890,21 +2810,11 @@ dependencies = [ "syn 1.0.13", ] -[[package]] -name = "tower-make" -version = "0.3.0-alpha.2a" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "316d47dd40cde4ac5d88110eaf9a10a4e2a68612d9c056cd2aa24e37dcb484cd" -dependencies = [ - "tokio-io", - "tower-service", -] - [[package]] name = "tower-service" -version = "0.3.0-alpha.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63ff37396cd966ce43bea418bfa339f802857495f797dafa00bea5b7221ebdfa" +checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" [[package]] name = "tracing" @@ -2913,7 +2823,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6de6a8590a29d3f401eab60470c699efa0adf7b4f0352055bf24df2b69849b40" dependencies = [ "cfg-if", - "log", "tracing-attributes", "tracing-core", ] @@ -3085,6 +2994,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c" dependencies = [ "cfg-if", + "serde", + "serde_json", "wasm-bindgen-macro", ] @@ -3105,16 +3016,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.3.27" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83420b37346c311b9ed822af41ec2e82839bfe99867ec6c54e2da43b7538771c" +checksum = "8bbdd49e3e28b40dec6a9ba8d17798245ce32b019513a845369c641b275135d9" dependencies = [ "cfg-if", - "futures", - "futures-channel-preview", - "futures-util-preview", "js-sys", - "lazy_static", "wasm-bindgen", "web-sys", ] diff --git a/core/Cargo.toml b/core/Cargo.toml index b13027a10..dfe40af66 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,7 +15,7 @@ uuid = "0.7" cfb8 = "0.3" aes = "0.3" flate2 = "1.0" -bytes = "0.4" +bytes = "0.5" log = "0.4" serde = { version = "1.0", features = ["derive"] } num-traits = "0.2" @@ -30,7 +30,8 @@ hash32 = "0.1" hash32-derive = "0.1" strum = "0.16" strum_macros = "0.16" -tokio = "=0.2.0-alpha.6" +tokio = { version = "0.2", features = ["full"] } +tokio-util = { version = "0.2", features = ["codec"] } failure = "0.1" bitvec = "0.15" multimap = "0.6" diff --git a/core/src/bytes_ext.rs b/core/src/bytes_ext.rs index ff2142d66..a55f3e1fc 100644 --- a/core/src/bytes_ext.rs +++ b/core/src/bytes_ext.rs @@ -50,23 +50,23 @@ impl BytesExt for B { } fn try_get_i16(&mut self) -> Result { - try_get_impl!(self, 2, get_i16_be); + try_get_impl!(self, 2, get_i16); } fn try_get_i32(&mut self) -> Result { - try_get_impl!(self, 4, get_i32_be); + try_get_impl!(self, 4, get_i32); } fn try_get_i64(&mut self) -> Result { - try_get_impl!(self, 8, get_i64_be); + try_get_impl!(self, 8, get_i64); } fn try_get_f32(&mut self) -> Result { - try_get_impl!(self, 4, get_f32_be); + try_get_impl!(self, 4, get_f32); } fn try_get_f64(&mut self) -> Result { - try_get_impl!(self, 8, get_f64_be); + try_get_impl!(self, 8, get_f64); } fn try_get_u8(&mut self) -> Result { @@ -74,15 +74,15 @@ impl BytesExt for B { } fn try_get_u16(&mut self) -> Result { - try_get_impl!(self, 2, get_u16_be); + try_get_impl!(self, 2, get_u16); } fn try_get_u32(&mut self) -> Result { - try_get_impl!(self, 4, get_u32_be); + try_get_impl!(self, 4, get_u32); } fn try_get_u64(&mut self) -> Result { - try_get_impl!(self, 8, get_u64_be); + try_get_impl!(self, 8, get_u64); } } @@ -112,27 +112,27 @@ impl BytesMutExt for BytesMut { fn push_i16(&mut self, x: i16) { self.reserve(2); - self.put_i16_be(x); + self.put_i16(x); } fn push_i32(&mut self, x: i32) { self.reserve(4); - self.put_i32_be(x); + self.put_i32(x); } fn push_i64(&mut self, x: i64) { self.reserve(8); - self.put_i64_be(x); + self.put_i64(x); } fn push_f32(&mut self, x: f32) { self.reserve(4); - self.put_f32_be(x); + self.put_f32(x); } fn push_f64(&mut self, x: f64) { self.reserve(8); - self.put_f64_be(x); + self.put_f64(x); } fn push_u8(&mut self, x: u8) { @@ -142,16 +142,16 @@ impl BytesMutExt for BytesMut { fn push_u16(&mut self, x: u16) { self.reserve(2); - self.put_u16_be(x); + self.put_u16(x); } fn push_u32(&mut self, x: u32) { self.reserve(4); - self.put_u32_be(x); + self.put_u32(x); } fn push_u64(&mut self, x: u64) { self.reserve(8); - self.put_u64_be(x); + self.put_u64(x); } } diff --git a/core/src/network/codec.rs b/core/src/network/codec.rs index 41fd09141..5f40e4352 100644 --- a/core/src/network/codec.rs +++ b/core/src/network/codec.rs @@ -3,15 +3,16 @@ use crate::network::mctypes::{McTypeRead, McTypeWrite}; use crate::network::packet::{PacketDirection, PacketId, PacketStage}; use crate::{Packet, PacketType}; use aes::Aes128; -use bytes::{Buf, BufMut, BytesMut}; +use bytes::buf::BufMutExt; +use bytes::{Buf, BytesMut}; use cfb8::stream_cipher::{NewStreamCipher, StreamCipher}; use cfb8::Cfb8; use flate2::read::ZlibDecoder; use flate2::write::ZlibEncoder; use flate2::Compression; use std::io::{Cursor, Read, Write}; -use tokio::codec::{Decoder, Encoder}; use tokio::io; +use tokio_util::codec::{Decoder, Encoder}; type AesCfb8 = Cfb8; @@ -100,12 +101,12 @@ impl Encoder for MinecraftCodec { // we reserve the maximum size and copy the header in with a correct offset. assert!(dst.is_empty()); dst.reserve(HEADER_SIZE); + + // Zero out the header. + dst.extend_from_slice(&[0u8; HEADER_SIZE]); + let mut header = dst.split_to(HEADER_SIZE); assert!(dst.is_empty()); - assert!(header.is_empty()); - - // Zero out `header`. - header.extend_from_slice(&[0u8; HEADER_SIZE]); // Write raw packet data to `dst`. let ty = packet.ty(); @@ -126,6 +127,7 @@ impl Encoder for MinecraftCodec { dst.reserve(HEADER_SIZE); let uncompressed = dst.split_to(data_len); + dst.extend_from_slice(&[0u8; HEADER_SIZE]); header = dst.split_to(HEADER_SIZE); assert!(dst.is_empty()); @@ -163,7 +165,7 @@ impl Encoder for MinecraftCodec { // Offset into `header` to write to. let header_offset = HEADER_SIZE - self.header_buffer.len(); // Discard unused header bytes. - header.split_to(header_offset); + header.advance(header_offset); header.clear(); // Write into header. diff --git a/core/src/world/mod.rs b/core/src/world/mod.rs index 552d718f0..1888c39d8 100644 --- a/core/src/world/mod.rs +++ b/core/src/world/mod.rs @@ -406,7 +406,7 @@ mod tests { let mut chunk = Chunk::new(ChunkPosition::new(0, 0)); FlatChunkGenerator {}.generate(&mut chunk); - world.chunk_map.insert(ChunkPosition::new(0, 0), chunk); + world.insert(chunk); let chunk = world.chunk_at(ChunkPosition::new(0, 0)).unwrap(); @@ -427,12 +427,10 @@ mod tests { let mut chunk = Chunk::new(ChunkPosition::new(0, 0)); GridChunkGenerator {}.generate(&mut chunk); - world.chunk_map.insert(ChunkPosition::new(0, 0), chunk); + world.insert(chunk); println!("-----"); - world - .set_block_at(BlockPosition::new(1, 63, 1), Block::Air) - .unwrap(); + world.set_block_at(BlockPosition::new(1, 63, 1), Block::Air); println!("-----"); assert_eq!( diff --git a/server/Cargo.toml b/server/Cargo.toml index a1494702a..1049e0506 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -32,11 +32,11 @@ lock_api = "0.3" thread_local = "1.0" # Netorking/IO -tokio = "=0.2.0-alpha.6" -tokio-executor = "=0.2.0-alpha.6" -futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } -bytes = "0.4" -mojang-api = "0.4" +tokio = {version = "0.2", features = ["full"] } +tokio-util = { version = "0.2", features = ["codec"] } +futures = "0.3" +bytes = "0.5" +mojang-api = "0.5" # Crypto rsa = "0.2" diff --git a/server/src/io/mod.rs b/server/src/io/mod.rs index fd506d6d8..ff5c9b452 100644 --- a/server/src/io/mod.rs +++ b/server/src/io/mod.rs @@ -59,7 +59,7 @@ impl NetworkIoManager { let future = run_listener(addr, sender.clone(), config, player_count, server_icon); if cfg!(test) { - let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap(); + let rt = tokio::runtime::Runtime::new().unwrap(); rt.spawn(future); } else { tokio::spawn(future); diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index c6d9405c3..030880e32 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -19,9 +19,8 @@ use std::net::SocketAddr; use std::path::Path; use std::sync::Arc; use std::time::Duration; -use tokio::codec::Framed; use tokio::net::TcpStream; -use tokio::timer::Timeout; +use tokio_util::codec::Framed; use uuid::Uuid; #[derive(Debug, Fail)] @@ -90,7 +89,7 @@ async fn _run_worker( select! { msg = rx_server_to_worker.next().fuse() => server_message = Some(msg), - packet = Timeout::new(framed.next(), Duration::from_millis(10000)).fuse() => received_packet = Some(packet), + packet = tokio::time::timeout(Duration::from_millis(10000), framed.next()).fuse() => received_packet = Some(packet), } if let Some(msg) = server_message { From 39d47519110e61df6c6f666b7bebdf902b631ae5 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 1 Mar 2020 16:29:52 -0700 Subject: [PATCH 085/647] Fix UUID decoding bug, implement some clientbound packets, and improve networking errors. --- core/src/bytes_ext.rs | 4 +- core/src/network/codec.rs | 21 +- core/src/network/mctypes.rs | 9 +- core/src/network/packet/implementation.rs | 274 ++++++++++++++++++---- 4 files changed, 249 insertions(+), 59 deletions(-) diff --git a/core/src/bytes_ext.rs b/core/src/bytes_ext.rs index a55f3e1fc..2b565049c 100644 --- a/core/src/bytes_ext.rs +++ b/core/src/bytes_ext.rs @@ -10,8 +10,8 @@ pub enum TryGetError { NotEnoughBytes, #[fail(display = "value too large")] ValueTooLarge, - #[fail(display = "invalid value")] - InvalidValue, + #[fail(display = "invalid value {}", _0)] + InvalidValue(i32), } type Result = std::result::Result; diff --git a/core/src/network/codec.rs b/core/src/network/codec.rs index 5f40e4352..e0445d929 100644 --- a/core/src/network/codec.rs +++ b/core/src/network/codec.rs @@ -73,10 +73,12 @@ impl MinecraftCodec { } pub fn enable_compression(&mut self, threshold: usize) { + trace!("Enabling compression with threshold {}", threshold); self.compression_threshold = Some(threshold); } pub fn enable_encryption(&mut self, key: [u8; 16]) { + trace!("Enabling encryption"); // This is the toppoint of security: using the same IV // for every packet. Typical for Mojang. self.encrypter = Some(AesCfb8::new_var(&key, &key).unwrap()); @@ -84,6 +86,7 @@ impl MinecraftCodec { } pub fn set_stage(&mut self, stage: PacketStage) { + trace!("Setting packet stage to {:?}", stage); self.stage = stage; } } @@ -250,9 +253,21 @@ impl Decoder for MinecraftCodec { // Read packet. let id = cursor.try_get_var_int()? as u32; - let packet_type = - PacketType::get_from_id(PacketId(id, self.incoming_direction, self.stage)) - .map_err(|_| Error::InvalidPacketId(id, self.stage))?; + // If we don't know this packet type, skip the packet. + let packet_type = { + match PacketType::get_from_id(PacketId(id, self.incoming_direction, self.stage)) { + Ok(ty) => ty, + Err(_) => { + // Advance buffer and stop. + trace!("Received packet type with unknown ID 0x{:x}; skipping", id); + src.advance(length); + self.decrypt_index = src.len(); + return Ok(None); + } + } + }; + + trace!("Decoding packet with type {:?}", packet_type); let mut packet = packet_type.get_implementation(); packet.read_from(&mut cursor)?; diff --git a/core/src/network/mctypes.rs b/core/src/network/mctypes.rs index d47a7174d..94e6becf9 100644 --- a/core/src/network/mctypes.rs +++ b/core/src/network/mctypes.rs @@ -134,7 +134,7 @@ impl McTypeRead for B { } let read = self.try_get_u8()?; let value = i32::from(read & 0b0111_1111); - result |= value << (7 * num_read); + result |= value.overflowing_shl(7u32 * num_read).0; num_read += 1; if num_read > 5 { @@ -183,15 +183,16 @@ impl McTypeRead for B { match byte { 0 => Ok(false), 1 => Ok(true), - _ => Err(TryGetError::InvalidValue), + x => Err(TryGetError::InvalidValue(i32::from(x))), } } fn try_get_uuid(&mut self) -> Result { let mut bytes = [0u8; 16]; self.bytes() - .read(&mut bytes) + .read_exact(&mut bytes) .map_err(|_| TryGetError::NotEnoughBytes)?; + self.advance(bytes.len()); Ok(Uuid::from_bytes(bytes)) } @@ -207,7 +208,7 @@ impl McTypeRead for B { } let id = self.try_get_var_int()?; - let ty = Item::from_native_protocol_id(id).ok_or(TryGetError::InvalidValue)?; + let ty = Item::from_native_protocol_id(id).ok_or(TryGetError::InvalidValue(id))?; let amount = self.try_get_i8()? as u8; // TODO NBT support diff --git a/core/src/network/packet/implementation.rs b/core/src/network/packet/implementation.rs index 0d8fad720..670b3fad7 100644 --- a/core/src/network/packet/implementation.rs +++ b/core/src/network/packet/implementation.rs @@ -18,6 +18,21 @@ type VarInt = i32; type VarLong = i64; type Slot = Option; +macro_rules! insert_packet { + ($map:ident, $ty:ident) => { + $map.insert( + PacketType::$ty, + PacketBuilder::with(|| Box::new($ty::default())), + ); + }; +} + +macro_rules! insert_packets { + ($map:ident, $($ty:ident ,)+) => { + $(insert_packet!($map, $ty));+ + } +} + lazy_static! { pub static ref IMPL_MAP: HashMap = { let mut m = HashMap::new(); @@ -76,6 +91,74 @@ lazy_static! { m.insert(PacketType::PlayerBlockPlacement, PacketBuilder::with(|| Box::new(PlayerBlockPlacement::default()))); m.insert(PacketType::UseItem, PacketBuilder::with(|| Box::new(UseItem::default()))); + // Clientbound + + m.insert(PacketType::EntityMetadata, PacketBuilder::with(|| Box::new(PacketEntityMetadata::default()))); + + insert_packets!(m, + DisconnectLogin, + EncryptionRequest, + LoginSuccess, + SetCompression, + + SpawnObject, + SpawnExperienceOrb, + SpawnGlobalEntity, + SpawnMob, + SpawnPainting, + SpawnPlayer, + AnimationClientbound, + Statistics, + BlockBreakAnimation, + UpdateBlockEntity, + BlockAction, + BlockChange, + BossBar, + ServerDifficulty, + ChatMessageClientbound, + OpenWindow, + WindowItems, + WindowProperty, + SetSlot, + SetCooldown, + PluginMessageClientbound, + NamedSoundEffect, + DisconnectPlay, + EntityStatus, + NBTQueryResponse, + Explosion, + UnloadChunk, + ChangeGameState, + KeepAliveClientbound, + ChunkData, + Effect, + Particle, + JoinGame, + EntityRelativeMove, + EntityLookAndRelativeMove, + EntityLook, + VehicleMoveClientbound, + OpenSignEditor, + CraftRecipeResponse, + CombatEvent, + PlayerInfo, + PlayerPositionAndLookClientbound, + UseBed, + DestroyEntities, + RemoveEntityEffect, + ResourcePackSend, + Respawn, + EntityHeadLook, + EntityVelocity, + EntityEquipment, + SpawnPosition, + TimeUpdate, + CollectItem, + + Response, + Pong, + ); + m }; } @@ -131,7 +214,15 @@ impl Packet for Handshake { } fn write_to(&self, mut buf: &mut BytesMut) { - unimplemented!() + buf.push_var_int(self.protocol_version as i32); + buf.push_string(&self.server_address); + buf.push_u16(self.server_port); + + let state_id = match self.next_state { + HandshakeState::Status => 1, + HandshakeState::Login => 2, + }; + buf.push_var_int(state_id); } fn ty(&self) -> PacketType { @@ -197,7 +288,10 @@ impl Packet for EncryptionResponse { } fn write_to(&self, mut buf: &mut BytesMut) { - unimplemented!() + buf.push_var_int(self.secret.len() as i32); + buf.put(self.secret.as_slice()); + buf.push_var_int(self.verify_token.len() as i32); + buf.put(self.verify_token.as_slice()); } fn ty(&self) -> PacketType { @@ -309,7 +403,9 @@ impl Packet for PluginMessageServerbound { } fn write_to(&self, buf: &mut BytesMut) { - unimplemented!() + buf.push_string(&self.channel); + + buf.put(self.data.as_slice()); } fn ty(&self) -> PacketType { @@ -369,7 +465,21 @@ impl Packet for UseEntity { } fn write_to(&self, buf: &mut BytesMut) { - unimplemented!() + buf.push_var_int(self.target); + + let ty_id = match self.ty { + UseEntityType::Interact => 0, + UseEntityType::Attack => 1, + UseEntityType::InteractAt(_, _, _, _) => 2, + }; + buf.push_var_int(ty_id); + + if let UseEntityType::InteractAt(x, y, z, hand) = self.ty { + buf.push_f32(x); + buf.push_f32(y); + buf.push_f32(z); + buf.push_var_int(hand); + } } fn ty(&self) -> PacketType { @@ -500,7 +610,10 @@ impl Packet for PlayerDigging { } fn write_to(&self, buf: &mut BytesMut) { - unimplemented!() + let id = self.status as i32; + buf.push_var_int(id); + buf.push_position(&self.location); + buf.push_i8(self.face); } fn ty(&self) -> PacketType { @@ -556,7 +669,10 @@ impl Packet for EntityAction { } fn write_to(&self, buf: &mut BytesMut) { - unimplemented!() + buf.push_var_int(self.entity_id); + let action_id = self.action_id.to_i32().unwrap(); + buf.push_var_int(action_id); + buf.push_var_int(self.jump_boost); } fn ty(&self) -> PacketType { @@ -707,7 +823,7 @@ impl Packet for AnimationServerbound { } fn write_to(&self, buf: &mut BytesMut) { - unimplemented!() + buf.push_var_int(self.hand.to_i32().unwrap()); } fn ty(&self) -> PacketType { @@ -731,7 +847,7 @@ pub struct Spectate { pub target_player: Uuid, } -#[derive(Debug, Clone, Copy, FromPrimitive)] +#[derive(Debug, Clone, Copy, FromPrimitive, ToPrimitive)] pub enum Face { Bottom, Top, @@ -783,7 +899,12 @@ impl Packet for PlayerBlockPlacement { } fn write_to(&self, buf: &mut BytesMut) { - unimplemented!() + buf.push_position(&self.location); + buf.push_var_int(self.face.to_i32().unwrap()); + buf.push_var_int(self.hand); + buf.push_f32(self.cursor_position_x); + buf.push_f32(self.cursor_position_y); + buf.push_f32(self.cursor_position_z); } fn ty(&self) -> PacketType { @@ -822,7 +943,19 @@ pub struct EncryptionRequest { impl Packet for EncryptionRequest { fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { - unimplemented!() + self.server_id = buf.try_get_string()?; + + let pubkey_len = buf.try_get_var_int()?; + for _ in 0..pubkey_len { + self.public_key.push(buf.try_get_u8()?); + } + + let token_len = buf.try_get_var_int()?; + for _ in 0..token_len { + self.verify_token.push(buf.try_get_u8()?); + } + + Ok(()) } fn write_to(&self, mut buf: &mut BytesMut) { @@ -936,7 +1069,7 @@ pub struct SpawnPainting { } #[allow(clippy::too_many_arguments)] -#[derive(AsAny, new, Clone)] +#[derive(AsAny, new, Clone, Default, Packet)] pub struct SpawnPlayer { pub entity_id: VarInt, pub player_uuid: Uuid, @@ -948,39 +1081,6 @@ pub struct SpawnPlayer { pub metadata: EntityMetadata, } -impl Packet for SpawnPlayer { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { - unimplemented!() - } - - fn write_to(&self, buf: &mut BytesMut) { - buf.push_var_int(self.entity_id); - buf.push_uuid(&self.player_uuid); - buf.push_f64(self.x); - buf.push_f64(self.y); - buf.push_f64(self.z); - buf.push_u8(self.yaw); - buf.push_u8(self.pitch); - - buf.push_metadata(&self.metadata); - } - - fn ty(&self) -> PacketType { - PacketType::SpawnPlayer - } - - fn ty_sized() -> PacketType - where - Self: Sized, - { - PacketType::SpawnPlayer - } - - fn box_clone(&self) -> Box { - box_clone_impl!(self); - } -} - #[derive(Default, AsAny, new, Clone)] pub struct AnimationClientbound { pub entity_id: VarInt, @@ -989,7 +1089,10 @@ pub struct AnimationClientbound { impl Packet for AnimationClientbound { fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { - unimplemented!() + self.entity_id = buf.try_get_var_int()?; + self.animation = + ClientboundAnimation::from_u8(buf.try_get_u8()?).ok_or(Error::InvalidUseEntity(0))?; + Ok(()) } fn write_to(&self, buf: &mut BytesMut) { @@ -1021,7 +1124,18 @@ pub struct Statistics { impl Packet for Statistics { fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { - unimplemented!() + let num_statistics = buf.try_get_var_int()?; + + if num_statistics > 255 { + return Err(Error::InsufficientArrayLength.into()); + } + + for _ in 0..num_statistics { + self.statistics + .push((buf.try_get_var_int()?, buf.try_get_var_int()?)); + } + self.value = buf.try_get_var_int()?; + Ok(()) } fn write_to(&self, buf: &mut BytesMut) { @@ -1233,7 +1347,14 @@ pub struct WindowItems { impl Packet for WindowItems { fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { - unimplemented!() + self.window_id = buf.try_get_u8()?; + let num_slots = buf.try_get_i16()?; + + for _ in 0..num_slots { + self.slots.push(buf.try_get_slot()?); + } + + Ok(()) } fn write_to(&self, buf: &mut BytesMut) { @@ -1289,7 +1410,11 @@ pub struct PluginMessageClientbound { impl Packet for PluginMessageClientbound { fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { - unimplemented!() + self.channel = buf.try_get_string()?; + + self.data.extend_from_slice(*buf.get_ref()); + + Ok(()) } fn write_to(&self, buf: &mut BytesMut) { @@ -1656,7 +1781,7 @@ impl Default for CombatEventType { } } -#[derive(AsAny, new, Clone)] +#[derive(AsAny, new, Clone, Default)] pub struct PlayerInfo { pub action: PlayerInfoAction, pub uuid: Uuid, @@ -1664,7 +1789,50 @@ pub struct PlayerInfo { impl Packet for PlayerInfo { fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { - unimplemented!() + let id = buf.try_get_var_int()?; + let _ = buf.try_get_var_int()?; + self.uuid = buf.try_get_uuid()?; + + self.action = match id { + 0 => { + let name = buf.try_get_string()?; + let num_props = buf.try_get_var_int()?; + + let mut props = vec![]; + for _ in 0..num_props { + let s0 = buf.try_get_string()?; + let s1 = buf.try_get_string()?; + let s2 = if buf.try_get_bool()? { + buf.try_get_string()? + } else { + String::default() + }; + props.push((s0, s1, s2)); + } + + let gamemode = Gamemode::from_id(buf.try_get_var_int()? as u8); + let ping = buf.try_get_var_int()?; + let display_name = if buf.try_get_bool()? { + buf.try_get_string()? + } else { + String::default() + }; + + PlayerInfoAction::AddPlayer(name, props, gamemode, ping, display_name) + } + 1 => PlayerInfoAction::UpdateGamemode(Gamemode::from_id(buf.try_get_u8()?)), + 2 => PlayerInfoAction::UpdateLatency(buf.try_get_var_int()?), + 3 => { + if buf.try_get_bool()? { + PlayerInfoAction::UpdateDisplayName(buf.try_get_string()?) + } else { + PlayerInfoAction::UpdateDisplayName(String::default()) + } + } + _ => PlayerInfoAction::RemovePlayer, + }; + + Ok(()) } fn write_to(&self, buf: &mut BytesMut) { @@ -1746,6 +1914,12 @@ impl PlayerInfoAction { } } +impl Default for PlayerInfoAction { + fn default() -> Self { + PlayerInfoAction::RemovePlayer + } +} + // TODO Face Player #[derive(Default, AsAny, new, Packet, Clone)] From 350e55978035e862929bb661a1960cb1d4164690 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 14 Mar 2020 18:06:41 -0600 Subject: [PATCH 086/647] Assorted minor changes in networking code; fix feather-blocks throwing warnings --- blocks/src/lib.rs | 1 + core/src/entitymeta.rs | 96 ++++++++++++++++++----- core/src/network/mctypes.rs | 20 +++-- core/src/network/packet/implementation.rs | 92 ++++++++++++++++++++-- core/src/world/chunk.rs | 15 ++-- 5 files changed, 183 insertions(+), 41 deletions(-) diff --git a/blocks/src/lib.rs b/blocks/src/lib.rs index 4f893625a..469232a1c 100644 --- a/blocks/src/lib.rs +++ b/blocks/src/lib.rs @@ -33,6 +33,7 @@ extern crate failure; extern crate num_derive; #[allow(clippy::all)] // No, generated code isn't idiomatic. Too bad +#[allow(warnings)] mod blocks; mod mappings; diff --git a/core/src/entitymeta.rs b/core/src/entitymeta.rs index 7d55a6241..fd95b999a 100644 --- a/core/src/entitymeta.rs +++ b/core/src/entitymeta.rs @@ -2,12 +2,13 @@ //! metadata format. See https://wiki.vg/Entity_metadata //! for the specification. -use crate::bytes_ext::{BytesMutExt, TryGetError}; -use crate::network::mctypes::McTypeWrite; +use crate::bytes_ext::{BytesExt, BytesMutExt, TryGetError}; +use crate::network::mctypes::{McTypeRead, McTypeWrite}; use crate::world::BlockPosition; use crate::Slot; +use bytes::Buf; use hashbrown::HashMap; -use std::io::Cursor; +use num_traits::FromPrimitive; use uuid::Uuid; type OptUuid = Option; @@ -28,7 +29,7 @@ pub enum MetaEntry { Direction(Direction), OptUuid(OptUuid), OptBlockId(Option), - Nbt, // TODO + Nbt(nbt::Blob), Particle, // TODO } @@ -49,7 +50,7 @@ impl MetaEntry { MetaEntry::Direction(_) => 11, MetaEntry::OptUuid(_) => 12, MetaEntry::OptBlockId(_) => 13, - MetaEntry::Nbt => 14, + MetaEntry::Nbt(_) => 14, MetaEntry::Particle => 15, } } @@ -142,12 +143,15 @@ impl Default for EntityMetadata { } } -pub trait EntityMetaIo { +pub trait EntityMetaWrite { fn push_metadata(&mut self, meta: &EntityMetadata); - fn try_get_metadata(&mut self) -> Result; } -impl EntityMetaIo for B +pub trait EntityMetaRead { + fn try_get_metadata(&mut self) -> Result; +} + +impl EntityMetaWrite for B where B: BytesMutExt + McTypeWrite, { @@ -160,19 +164,27 @@ where self.push_u8(0xff); // End of metadata } - - fn try_get_metadata(&mut self) -> Result { - unimplemented!() - } } -impl EntityMetaIo for &mut Cursor<&[u8]> { - fn push_metadata(&mut self, _meta: &EntityMetadata) { - unimplemented!() - } +impl EntityMetaRead for B +where + B: Buf + std::io::Read, +{ + fn try_get_metadata(&mut self) -> Result { + let mut values = HashMap::new(); - fn try_get_metadata(&mut self) -> Result { - unimplemented!() + while self.has_remaining() { + let index = self.try_get_u8()?; + + if index == 0xFF { + break; + } + + let entry = try_get_entry(self)?; + values.insert(index, entry); + } + + Ok(EntityMetadata { values }) } } @@ -232,12 +244,56 @@ where buf.push_var_int(0); // No value implies air } } - MetaEntry::Nbt => unimplemented!(), + MetaEntry::Nbt(val) => buf.push_nbt(val), MetaEntry::Particle => unimplemented!(), } } -#[derive(Clone, Debug, PartialEq, Eq)] +fn try_get_entry(buf: &mut B) -> Result +where + B: Buf + McTypeRead, +{ + let id = buf.try_get_var_int()?; + + Ok(match id { + 0 => MetaEntry::Byte(buf.try_get_i8()?), + 1 => MetaEntry::VarInt(buf.try_get_var_int()?), + 2 => MetaEntry::Float(buf.try_get_f32()?), + 3 => MetaEntry::String(buf.try_get_string()?), + 4 => MetaEntry::Chat(buf.try_get_string()?), + 5 => MetaEntry::OptChat(if buf.try_get_bool()? { + Some(buf.try_get_string()?) + } else { + None + }), + 6 => MetaEntry::Slot(buf.try_get_slot()?), + 7 => MetaEntry::Boolean(buf.try_get_bool()?), + 8 => MetaEntry::Rotation(buf.try_get_f32()?, buf.try_get_f32()?, buf.try_get_f32()?), + 9 => MetaEntry::Position(buf.try_get_position()?), + 10 => MetaEntry::OptPosition(if buf.try_get_bool()? { + Some(buf.try_get_position()?) + } else { + None + }), + 11 => MetaEntry::Direction( + Direction::from_i32(buf.try_get_var_int()?).ok_or(TryGetError::InvalidValue(0))?, + ), + 12 => MetaEntry::OptUuid(if buf.try_get_bool()? { + Some(buf.try_get_uuid()?) + } else { + None + }), + 13 => MetaEntry::OptBlockId(if buf.try_get_bool()? { + Some(buf.try_get_var_int()?) + } else { + None + }), + 14 => MetaEntry::Nbt(buf.try_get_nbt()?), + x => return Err(TryGetError::InvalidValue(x).into()), + }) +} + +#[derive(Clone, Debug, PartialEq, Eq, FromPrimitive)] pub enum Direction { Down, Up, diff --git a/core/src/network/mctypes.rs b/core/src/network/mctypes.rs index 94e6becf9..341d64cbf 100644 --- a/core/src/network/mctypes.rs +++ b/core/src/network/mctypes.rs @@ -4,7 +4,8 @@ use crate::prelude::*; use crate::world::BlockPosition; use bytes::{Buf, BytesMut}; use feather_items::{Item, ItemExt}; -use serde::{Deserialize, Serialize}; +use serde::de::DeserializeOwned; +use serde::Serialize; use std::io::Read; /// Identifies a type to which Minecraft-specific @@ -46,9 +47,9 @@ pub trait McTypeRead { fn try_get_bool(&mut self) -> Result; - fn try_get_uuid(&mut self) -> Result; + fn try_get_uuid(&mut self) -> Result; - fn try_get_nbt<'de, T: Deserialize<'de>>(&mut self) -> Result; + fn try_get_nbt(&mut self) -> Result; fn try_get_slot(&mut self) -> Result, TryGetError>; } @@ -121,7 +122,7 @@ impl McTypeWrite for BytesMut { } } -impl McTypeRead for B { +impl McTypeRead for B { /// Reads a `VarInt` from this object, returning /// `Some(x)` if successful or `None` if the object /// does not contain a valid `VarInt`. @@ -187,17 +188,14 @@ impl McTypeRead for B { } } - fn try_get_uuid(&mut self) -> Result { + fn try_get_uuid(&mut self) -> Result { let mut bytes = [0u8; 16]; - self.bytes() - .read_exact(&mut bytes) - .map_err(|_| TryGetError::NotEnoughBytes)?; - self.advance(bytes.len()); + self.read_exact(&mut bytes)?; Ok(Uuid::from_bytes(bytes)) } - fn try_get_nbt<'de, D: Deserialize<'de>>(&mut self) -> Result { - unimplemented!() + fn try_get_nbt(&mut self) -> Result { + nbt::from_reader(self) } fn try_get_slot(&mut self) -> Result, TryGetError> { diff --git a/core/src/network/packet/implementation.rs b/core/src/network/packet/implementation.rs index 670b3fad7..8e2ec737c 100644 --- a/core/src/network/packet/implementation.rs +++ b/core/src/network/packet/implementation.rs @@ -1,12 +1,12 @@ use super::super::mctypes::{McTypeRead, McTypeWrite}; use super::*; use crate::bytes_ext::{BytesExt, BytesMutExt}; -use crate::entitymeta::{EntityMetaIo, EntityMetadata}; +use crate::entitymeta::{EntityMetaRead, EntityMetaWrite, EntityMetadata}; use crate::inventory::ItemStack; use crate::network::packet::PacketStage::Play; use crate::prelude::*; -use crate::world::chunk::Chunk; -use crate::{Biome, ClientboundAnimation, Hand}; +use crate::world::chunk::{BitArray, Chunk}; +use crate::{Biome, ChunkSection, ClientboundAnimation, Hand}; use bytes::{Buf, BufMut}; use hashbrown::HashMap; use num_traits::{FromPrimitive, ToPrimitive}; @@ -1536,6 +1536,12 @@ pub struct KeepAliveClientbound { pub keep_alive_id: u64, } +#[derive(Debug, Fail)] +enum ChunkDataError { + #[fail(display = "invalid bits per block value {} for section {}", _0, _1)] + InvalidBitsPerBlock(u8, usize), +} + #[derive(Default, AsAny, new, Clone)] pub struct ChunkData { pub chunk: Chunk, @@ -1543,7 +1549,81 @@ pub struct ChunkData { impl Packet for ChunkData { fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { - unimplemented!() + use crate::world::chunk::{self, BitArray}; + + self.chunk + .set_position(ChunkPosition::new(buf.try_get_i32()?, buf.try_get_i32()?)); + if buf.try_get_bool()? { + let primary_mask = buf.try_get_var_int()?; + let temp_length = buf.try_get_var_int()?; + + let mut temp_buf = &mut std::iter::repeat(0u8) + .take(temp_length as _) + .collect::>()[..]; + buf.copy_to_slice(&mut temp_buf); + + let mut buf = Cursor::new(temp_buf); + for i in 0..(chunk::NUM_SECTIONS - 1) { + if primary_mask & (1 << i) != 0 { + let (bits_per_block, palette) = match buf.try_get_u8()? { + 0..=chunk::MIN_BITS_PER_BLOCK => ( + chunk::MIN_BITS_PER_BLOCK, + Some({ + let palette_number = buf.try_get_var_int()? as usize; + let mut palette = Vec::<_>::with_capacity(palette_number); + for _ in 0..palette_number { + palette.push(buf.try_get_var_int()? as u16); + } + palette + }), + ), + x @ chunk::MIN_BITS_PER_BLOCK..=chunk::MAX_BITS_PER_BLOCK => ( + x, + Some({ + /* Todo: Remove duplicate. */ + let palette_number = buf.try_get_var_int()? as usize; + let mut palette = Vec::<_>::with_capacity(palette_number); + for _ in 0..palette_number { + palette.push(buf.try_get_var_int()? as u16); + } + palette + }), + ), + _ => (chunk::GLOBAL_BITS_PER_BLOCK, None), + }; + + /* 63 and 64 because Vec is */ + let data_number = buf.try_get_var_int()? as usize; + assert_eq!(bits_per_block as usize * 64, data_number); + let mut data = Vec::<_>::with_capacity(data_number); + for _ in 0..data_number { + data.push(buf.try_get_u64()?); + } + let data = BitArray::from_raw(data, bits_per_block, chunk::SECTION_VOLUME); + + const DATA_NUMBER: usize = (63 + chunk::SECTION_VOLUME * 4) / 64; + let mut light_data = Vec::<_>::with_capacity(DATA_NUMBER); + for _ in 0..DATA_NUMBER { + light_data.push(buf.try_get_u64()?); + } + let block_light = BitArray::from_raw(light_data, 4, chunk::SECTION_VOLUME); + + let mut sky_data = Vec::<_>::with_capacity(DATA_NUMBER); + for _ in 0..DATA_NUMBER { + /* If outsidd! */ + sky_data.push(buf.try_get_u64()?); + } + let sky_light = chunk::BitArray::from_raw(sky_data, 4, chunk::SECTION_VOLUME); + + let section = chunk::ChunkSection::new(data, palette, block_light, sky_light); + self.chunk.set_section_at(i, Some(section)); + } + } + + Ok(()) + } else { + unimplemented!(); + } } fn write_to(&self, buf: &mut BytesMut) { @@ -2009,7 +2089,9 @@ pub struct PacketEntityMetadata { impl Packet for PacketEntityMetadata { fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { - unimplemented!() + self.entity_id = buf.try_get_var_int()?; + self.metadata = buf.try_get_metadata()?; + Ok(()) } fn write_to(&self, buf: &mut BytesMut) { diff --git a/core/src/world/chunk.rs b/core/src/world/chunk.rs index 7ecd51e8f..fe788fcd7 100644 --- a/core/src/world/chunk.rs +++ b/core/src/world/chunk.rs @@ -5,19 +5,19 @@ use multimap::MultiMap; /// The number of bits used for each block /// in the global palette. -const GLOBAL_BITS_PER_BLOCK: u8 = 14; +pub(crate) const GLOBAL_BITS_PER_BLOCK: u8 = 14; /// The minimum bits per block allowed when /// using a section palette. /// Bits per block values lower than this /// value will be offsetted to this value. -const MIN_BITS_PER_BLOCK: u8 = 4; +pub(crate) const MIN_BITS_PER_BLOCK: u8 = 4; /// The maximum number of bits per block /// allowed when using a section palette. /// Values above this will use the global palette /// instead. -const MAX_BITS_PER_BLOCK: u8 = 8; +pub(crate) const MAX_BITS_PER_BLOCK: u8 = 8; /// The height in blocks of a chunk column. const CHUNK_HEIGHT: usize = 256; @@ -31,10 +31,10 @@ const SECTION_HEIGHT: usize = 16; const SECTION_WIDTH: usize = CHUNK_WIDTH; /// The volume in blocks of a chunk section. -const SECTION_VOLUME: usize = (SECTION_HEIGHT * SECTION_WIDTH * SECTION_WIDTH) as usize; +pub(crate) const SECTION_VOLUME: usize = (SECTION_HEIGHT * SECTION_WIDTH * SECTION_WIDTH) as usize; /// The number of chunk sections in a column. -const NUM_SECTIONS: usize = 16; +pub(crate) const NUM_SECTIONS: usize = 16; /// A chunk column consisting /// of a 16x256x16 section of blocks. @@ -217,6 +217,11 @@ impl Chunk { self.location } + /// Sets the position of this chunk. + pub fn set_position(&mut self, pos: ChunkPosition) { + self.location = pos + } + /// Returns a reference to the chunk section at the given /// Y offset. The Y offset must be between 0 and 15, inclusive; /// each Y offset value corresponds to 16 blocks vertically. From ee2040e3a9047a8ea4b477a46a2f7e3db00227a9 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 14 Mar 2020 21:29:07 -0600 Subject: [PATCH 087/647] Perform major refactoring on feather-core Summary of changes: * Update all dependencies * Remove derive-new; replace with struct initializers for packet structs * Replace `failure` with `anyhow` and `thiserror` * Move toward replacing `nalgebra-glm` with `vek` * Move some modules into more logical directories --- Cargo.lock | 525 +++++++++------------ codegen/src/lib.rs | 2 +- core/Cargo.toml | 51 +- core/src/bytes_ext.rs | 9 +- core/src/{world => }/chunk.rs | 21 +- core/src/entitymeta.rs | 46 +- core/src/lib.rs | 31 +- core/src/math_types.rs | 31 ++ core/src/network/codec.rs | 14 +- core/src/network/mctypes.rs | 6 +- core/src/network/packet/implementation.rs | 305 ++++++------ core/src/network/packet/mod.rs | 2 +- core/src/prelude.rs | 6 - core/src/save/entity.rs | 14 +- core/src/save/player_data.rs | 2 +- core/src/save/region/mod.rs | 9 +- core/src/{world/mod.rs => world.rs} | 277 +++++------ core/src/world/block.rs | 1 - server/Cargo.toml | 45 +- server/src/broadcasters/block.rs | 2 +- server/src/broadcasters/entity_creation.rs | 13 +- server/src/broadcasters/entity_deletion.rs | 2 +- server/src/broadcasters/movement.rs | 46 +- server/src/chunk_entities.rs | 8 +- server/src/chunk_worker.rs | 3 +- server/src/config.rs | 13 +- server/src/entity/item.rs | 2 +- server/src/io/initial_handler.rs | 127 ++--- server/src/io/worker.rs | 8 +- server/src/join.rs | 4 +- server/src/lib.rs | 6 +- server/src/metadata.rs | 2 +- server/src/physics/entity.rs | 6 +- server/src/physics/math.rs | 15 +- server/src/state.rs | 4 +- server/src/time.rs | 17 +- server/src/view.rs | 16 +- server/src/worldgen/composition.rs | 7 +- server/src/worldgen/density_map/density.rs | 8 +- server/src/worldgen/density_map/height.rs | 8 +- server/src/worldgen/mod.rs | 10 +- 41 files changed, 847 insertions(+), 877 deletions(-) rename core/src/{world => }/chunk.rs (98%) create mode 100644 core/src/math_types.rs delete mode 100644 core/src/prelude.rs rename core/src/{world/mod.rs => world.rs} (61%) delete mode 100644 core/src/world/block.rs diff --git a/Cargo.lock b/Cargo.lock index b19a48d1a..5a6493fae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,12 +48,12 @@ dependencies = [ ] [[package]] -name = "aho-corasick" -version = "0.7.6" +name = "ahash" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" +checksum = "0989268a37e128d4d7a8028f1c60099430113fdbc70419010601ce51a228e4fe" dependencies = [ - "memchr", + "const-random", ] [[package]] @@ -62,10 +62,10 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658f9468113d34781f6ca9d014d174c74b73de870f1e0e3ad32079bbab253b19" dependencies = [ - "approx", + "approx 0.3.2", "libm 0.1.4", "num-complex", - "num-traits", + "num-traits 0.2.11", ] [[package]] @@ -83,13 +83,19 @@ version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" +[[package]] +name = "approx" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" + [[package]] name = "approx" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" dependencies = [ - "num-traits", + "num-traits 0.2.11", ] [[package]] @@ -162,18 +168,15 @@ dependencies = [ [[package]] name = "base64" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder", -] +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" [[package]] name = "base64" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" +checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3" [[package]] name = "bit-set" @@ -198,9 +201,13 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitvec" -version = "0.15.2" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" +checksum = "fea3f6954750547b48aa42185a8fd6ef09e3ecaff57901e386d5f0df79b3b99d" +dependencies = [ + "either", + "radium", +] [[package]] name = "block-cipher-trait" @@ -225,15 +232,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "2.6.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708" - -[[package]] -name = "bumpalo" -version = "3.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb8038c1ddc0a5f73787b130f4cc75151e96ed33e417fde765eb5a81e3532f4" +checksum = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" [[package]] name = "byteorder" @@ -241,16 +242,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -dependencies = [ - "byteorder", - "iovec", -] - [[package]] name = "bytes" version = "0.5.4" @@ -320,7 +311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" dependencies = [ "num-integer", - "num-traits", + "num-traits 0.2.11", "time", ] @@ -418,9 +409,9 @@ dependencies = [ "csv", "itertools", "lazy_static", - "num-traits", + "num-traits 0.2.11", "rand_core 0.5.1", - "rand_os 0.2.2", + "rand_os", "rand_xoshiro", "rayon", "serde", @@ -561,17 +552,6 @@ dependencies = [ "syn 0.15.44", ] -[[package]] -name = "derive-new" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9" -dependencies = [ - "proc-macro2 1.0.7", - "quote 1.0.2", - "syn 1.0.13", -] - [[package]] name = "derive_deref" version = "1.1.0" @@ -583,20 +563,6 @@ dependencies = [ "syn 0.15.44", ] -[[package]] -name = "derive_more" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" -dependencies = [ - "lazy_static", - "proc-macro2 0.4.30", - "quote 0.6.13", - "regex", - "rustc_version", - "syn 0.15.44", -] - [[package]] name = "downcast-rs" version = "1.1.1" @@ -656,7 +622,7 @@ dependencies = [ "feather-codegen", "lazy_static", "num-derive", - "num-traits", + "num-traits 0.2.11", ] [[package]] @@ -667,8 +633,8 @@ dependencies = [ "lazy_static", "proc-macro2 1.0.7", "quote 1.0.2", - "strum", - "strum_macros", + "strum 0.16.0", + "strum_macros 0.16.0", "syn 1.0.13", ] @@ -677,36 +643,36 @@ name = "feather-core" version = "0.5.0" dependencies = [ "aes", + "anyhow", "bitvec", "byteorder", - "bytes 0.5.4", + "bytes", "cfb8", - "derive-new", - "derive_more", - "failure", "feather-blocks", "feather-codegen", "feather-items", "flate2 1.0.13", "hash32", "hash32-derive", - "hashbrown", + "hashbrown 0.7.0", "hematite-nbt", "lazy_static", "log", - "multimap 0.6.0", + "multimap", "nalgebra-glm", "num-derive", - "num-traits", - "parking_lot 0.9.0", + "num-traits 0.2.11", + "parking_lot 0.10.0", "rayon", "serde", - "smallvec 0.6.13", - "strum", - "strum_macros", + "smallvec 1.2.0", + "strum 0.18.0", + "strum_macros 0.18.0", + "thiserror", "tokio", "tokio-util", "uuid", + "vek", ] [[package]] @@ -741,32 +707,30 @@ name = "feather-items" version = "0.5.0" dependencies = [ "num-derive", - "num-traits", + "num-traits 0.2.11", ] [[package]] name = "feather-server" version = "0.5.0" dependencies = [ - "ahash", - "base64 0.10.1", + "ahash 0.3.2", + "anyhow", + "base64 0.12.0", "bitflags", "bitvec", - "bumpalo 2.6.0", - "bytes 0.5.4", + "bumpalo", + "bytes", "chashmap", "criterion", "crossbeam", "ctrlc", - "derive_deref", - "failure", "feather-blocks", "feather-codegen", "feather-core", "feather-item-block", - "fnv", "futures", - "hashbrown", + "hashbrown 0.7.0", "heapless", "hematite-nbt", "humantime-serde", @@ -776,26 +740,27 @@ dependencies = [ "lock_api", "log", "mojang-api", - "multimap 0.7.0", + "multimap", "nalgebra", "nalgebra-glm", "ncollide3d", "num-bigint-dig", "num-derive", - "num-traits", - "parking_lot 0.9.0", + "num-traits 0.2.11", + "parking_lot 0.10.0", "rand 0.7.3", - "rand_xorshift 0.2.0", + "rand_xorshift", "rayon", "rsa", "rsa-der", "serde", "serde_json", - "simdeez 0.6.6", + "simdeez", "simdnoise", "simple_logger", - "smallvec 0.6.13", - "strum", + "smallvec 1.2.0", + "strum 0.18.0", + "thiserror", "thread_local", "tokio", "tokio-util", @@ -810,9 +775,9 @@ version = "0.1.0" [[package]] name = "fixedbitset" -version = "0.1.9" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" [[package]] name = "flate2" @@ -1025,7 +990,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9433d71e471c1736fd5a61b671fc0b148d7a2992f666c958d03cd8feb3b88d1" dependencies = [ - "bytes 0.5.4", + "bytes", "fnv", "futures-core", "futures-sink", @@ -1064,8 +1029,18 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" dependencies = [ - "ahash", + "ahash 0.2.18", "autocfg 0.1.7", +] + +[[package]] +name = "hashbrown" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728e7d31e63d53c436094370f1e6fa249f60a4bb318cc5dfbbbe0aa2bc5a29d7" +dependencies = [ + "ahash 0.3.2", + "autocfg 1.0.0", "rayon", "serde", ] @@ -1117,7 +1092,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" dependencies = [ - "bytes 0.5.4", + "bytes", "fnv", "itoa", ] @@ -1128,7 +1103,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ - "bytes 0.5.4", + "bytes", "http", ] @@ -1140,18 +1115,15 @@ checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" [[package]] name = "humantime" -version = "1.3.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] +checksum = "b9b6c53306532d3c8e8087b44e6580e10db51a023cf9b433cea2ac38066b92da" [[package]] name = "humantime-serde" -version = "0.1.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f59e8a805c18bc9ded3f4e596cb5f0157d88a235e875480a7593b5926f95065" +checksum = "f1c57351e2c81b7a03e82a6c8f4198b309c158f6d67c4adb707af6af7d91465a" dependencies = [ "humantime", "serde", @@ -1163,7 +1135,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa1c527bbc634be72aa7ba31e4e4def9bbb020f5416916279b7c705cd838893e" dependencies = [ - "bytes 0.5.4", + "bytes", "futures-channel", "futures-core", "futures-util", @@ -1187,7 +1159,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa" dependencies = [ - "bytes 0.5.4", + "bytes", "hyper", "native-tls", "tokio", @@ -1484,11 +1456,11 @@ dependencies = [ [[package]] name = "mojang-api" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf9caa940ff4f28757e9414f51ca1a1dbb70a6e18d4dda462504344cb00fec1" +checksum = "35d5dd99508e0d8e2f16ceab7288cb06394e7d2838d08fdc35fd1551e8ec2f12" dependencies = [ - "bytes 0.4.12", + "lazy_static", "log", "num-bigint", "reqwest", @@ -1506,49 +1478,41 @@ checksum = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915" [[package]] name = "multimap" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de234f818d54830a7103b9be18ad0861d75aeb5e3c89759bc3f9a004cc39cfa3" -dependencies = [ - "serde", -] - -[[package]] -name = "multimap" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b1a404699e1fa9fa1665e045c1dd23e6663e8e9ff883faa349c90e71aa7ce9" +checksum = "a97fbd5d00e0e37bfb10f433af8f5aaf631e739368dc9fc28286ca81ca4948dc" dependencies = [ "serde", ] [[package]] name = "nalgebra" -version = "0.18.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaa9fddbc34c8c35dd2108515587b8ce0cab396f17977b8c738568e4edb521a2" +checksum = "c6511777ed3da44b6a11e732a66a7d6274dfbbcd68ad968e64b778dcb829d94a" dependencies = [ "alga", - "approx", - "generic-array 0.12.3", + "approx 0.3.2", + "generic-array 0.13.2", "matrixmultiply", "num-complex", "num-rational", - "num-traits", - "rand 0.6.5", + "num-traits 0.2.11", + "rand 0.7.3", + "rand_distr", "typenum", ] [[package]] name = "nalgebra-glm" -version = "0.4.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a4cd007520d46d2ca24002ddf538fce40ea2a34ed0ffcd3e2ae0b6ba18811d3" +checksum = "5ae78da13d67be0d2e4ab567490477e4c9f314fc151ddfff6713b15bdbb2aa72" dependencies = [ "alga", - "approx", + "approx 0.3.2", "nalgebra", - "num-traits", + "num-traits 0.2.11", ] [[package]] @@ -1571,21 +1535,21 @@ dependencies = [ [[package]] name = "ncollide3d" -version = "0.20.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee57cac70a2892e89fab7d5fd295b0ad544d1f877fa70fe8ae4be477514dd61" +checksum = "cf6b0aa58474dc252fd96782f46315e9a3ae7ad18014a65d70975865e1d19754" dependencies = [ "alga", - "approx", + "approx 0.3.2", "bitflags", "downcast-rs", "either", "nalgebra", - "num-traits", + "num-traits 0.2.11", "petgraph", "slab", "slotmap", - "smallvec 0.6.13", + "smallvec 1.2.0", ] [[package]] @@ -1630,7 +1594,7 @@ checksum = "f6f115de20ad793e857f76da2563ff4a09fbcfd6fe93cca0c5d996ab5f3ee38d" dependencies = [ "autocfg 1.0.0", "num-integer", - "num-traits", + "num-traits 0.2.11", ] [[package]] @@ -1645,10 +1609,10 @@ dependencies = [ "libm 0.2.1", "num-integer", "num-iter", - "num-traits", + "num-traits 0.2.11", "rand 0.7.3", "serde", - "smallvec 1.1.0", + "smallvec 1.2.0", "zeroize", ] @@ -1659,7 +1623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" dependencies = [ "autocfg 1.0.0", - "num-traits", + "num-traits 0.2.11", ] [[package]] @@ -1680,7 +1644,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ "autocfg 1.0.0", - "num-traits", + "num-traits 0.2.11", ] [[package]] @@ -1691,7 +1655,7 @@ checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" dependencies = [ "autocfg 1.0.0", "num-integer", - "num-traits", + "num-traits 0.2.11", ] [[package]] @@ -1702,7 +1666,16 @@ checksum = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" dependencies = [ "autocfg 1.0.0", "num-integer", - "num-traits", + "num-traits 0.2.11", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.11", ] [[package]] @@ -1763,12 +1736,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "ordermap" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" - [[package]] name = "owning_ref" version = "0.3.3" @@ -1799,6 +1766,16 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "parking_lot" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" +dependencies = [ + "lock_api", + "parking_lot_core 0.7.0", +] + [[package]] name = "parking_lot_core" version = "0.2.14" @@ -1826,6 +1803,20 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "parking_lot_core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" +dependencies = [ + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "smallvec 1.2.0", + "winapi 0.3.8", +] + [[package]] name = "paste" version = "0.1.6" @@ -1856,12 +1847,12 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "petgraph" -version = "0.4.13" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" +checksum = "29c127eea4a29ec6c85d153c59dc1213f33ec74cead30fe4730aecc88cc1fd92" dependencies = [ "fixedbitset", - "ordermap", + "indexmap", ] [[package]] @@ -1952,12 +1943,6 @@ dependencies = [ "unicode-xid 0.2.0", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "0.5.2" @@ -1985,6 +1970,12 @@ dependencies = [ "proc-macro2 1.0.7", ] +[[package]] +name = "radium" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" + [[package]] name = "rand" version = "0.4.6" @@ -1998,25 +1989,6 @@ dependencies = [ "winapi 0.3.8", ] -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.7", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc 0.1.0", - "rand_isaac", - "rand_jitter", - "rand_os 0.1.3", - "rand_pcg", - "rand_xorshift 0.1.1", - "winapi 0.3.8", -] - [[package]] name = "rand" version = "0.7.3" @@ -2025,19 +1997,9 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom", "libc", - "rand_chacha 0.2.1", + "rand_chacha", "rand_core 0.5.1", - "rand_hc 0.2.0", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.7", - "rand_core 0.3.1", + "rand_hc", ] [[package]] @@ -2075,12 +2037,12 @@ dependencies = [ ] [[package]] -name = "rand_hc" -version = "0.1.0" +name = "rand_distr" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +checksum = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2" dependencies = [ - "rand_core 0.3.1", + "rand 0.7.3", ] [[package]] @@ -2092,40 +2054,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi 0.3.8", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi 0.3.8", -] - [[package]] name = "rand_os" version = "0.2.2" @@ -2136,25 +2064,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.7", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "rand_xorshift" version = "0.2.0" @@ -2218,18 +2127,6 @@ version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -[[package]] -name = "regex" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", - "thread_local", -] - [[package]] name = "regex-automata" version = "0.1.8" @@ -2239,12 +2136,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "regex-syntax" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" - [[package]] name = "remove_dir_all" version = "0.5.2" @@ -2261,7 +2152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9f62f24514117d09a8fc74b803d3d65faa27cea1c7378fb12b0d002913f3831" dependencies = [ "base64 0.11.0", - "bytes 0.5.4", + "bytes", "encoding_rs", "futures-core", "futures-util", @@ -2301,7 +2192,7 @@ dependencies = [ "num-bigint-dig", "num-integer", "num-iter", - "num-traits", + "num-traits 0.2.11", "rand 0.7.3", "subtle", "zeroize", @@ -2457,15 +2348,6 @@ dependencies = [ "libc", ] -[[package]] -name = "simdeez" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2547b5815deedf2a9051b81bd459997f2cfdeb37cee24c2fb4017ae77f5a7d2" -dependencies = [ - "paste", -] - [[package]] name = "simdeez" version = "1.0.0" @@ -2482,7 +2364,7 @@ version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d12c11e4f55bf8237d46300c7b616068bff75f061927816f41355e1d0addb02" dependencies = [ - "simdeez 1.0.0", + "simdeez", ] [[package]] @@ -2493,14 +2375,14 @@ checksum = "2b25ecba7165254f0c97d6c22a64b1122a03634b18d20a34daf21e18f892e618" dependencies = [ "chrono", "num-bigint", - "num-traits", + "num-traits 0.2.11", ] [[package]] name = "simple_logger" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109facdf91db4b79de557313b5e031f0f8a86373e316bf01158190aa68bcc74e" +checksum = "fea0c4611f32f4c2bac73754f22dca1f57e6c1945e0590dae4e5f2a077b92367" dependencies = [ "atty", "chrono", @@ -2517,9 +2399,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "slotmap" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fd553261805f128e2900bf69ab3d034260bc338caf7f0ee54dbf035c85acd" +checksum = "c46a3482db8f247956e464d783693ece164ca056e6e67563ee5505bdb86452cd" [[package]] name = "smallvec" @@ -2532,9 +2414,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" +checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" [[package]] name = "socket2" @@ -2566,6 +2448,12 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +[[package]] +name = "static_assertions" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2593,6 +2481,12 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6138f8f88a16d90134763314e3fc76fa3ed6a7db4725d6acf9a3ef95a3188d22" +[[package]] +name = "strum" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" + [[package]] name = "strum_macros" version = "0.16.0" @@ -2605,6 +2499,18 @@ dependencies = [ "syn 1.0.13", ] +[[package]] +name = "strum_macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" +dependencies = [ + "heck", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", +] + [[package]] name = "subtle" version = "2.2.2" @@ -2679,6 +2585,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee14bf8e6767ab4c687c9e8bc003879e042a96fd67a3ba5934eadb6536bef4db" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7b51e1fbc44b5a0840be594fbc0f960be09050f2617e61e6aa43bef97cd3ef4" +dependencies = [ + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", +] + [[package]] name = "thread_local" version = "1.0.1" @@ -2715,7 +2641,7 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa5e81d6bc4e67fe889d5783bd2a128ab2e0cfa487e0be16b6a8d177b101616" dependencies = [ - "bytes 0.5.4", + "bytes", "fnv", "futures-core", "iovec", @@ -2760,7 +2686,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" dependencies = [ - "bytes 0.5.4", + "bytes", "futures-core", "futures-sink", "log", @@ -2784,10 +2710,10 @@ source = "git+https://github.com/feather-rs/tonks?rev=0ed28a624a21d044011058f747 dependencies = [ "arrayvec", "bit-set", - "bumpalo 3.1.2", + "bumpalo", "crossbeam", "derivative", - "hashbrown", + "hashbrown 0.6.3", "inventory", "lazy_static", "legion", @@ -2795,7 +2721,7 @@ dependencies = [ "parking_lot 0.9.0", "rayon", "smallvec 0.6.13", - "static_assertions", + "static_assertions 1.1.0", "thread_local", "tonks-macros", ] @@ -2882,7 +2808,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf" dependencies = [ - "smallvec 1.1.0", + "smallvec 1.2.0", ] [[package]] @@ -2922,11 +2848,11 @@ dependencies = [ [[package]] name = "uuid" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" dependencies = [ - "rand 0.6.5", + "rand 0.7.3", "serde", ] @@ -2942,6 +2868,19 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +[[package]] +name = "vek" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b833a133490ae98e9e3db1c77fc28e844f8e51b12eb35b4ab8a2082cb7cb441a" +dependencies = [ + "approx 0.1.1", + "num-integer", + "num-traits 0.1.43", + "rustc_version", + "static_assertions 0.2.5", +] + [[package]] name = "version_check" version = "0.1.5" @@ -3005,7 +2944,7 @@ version = "0.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45" dependencies = [ - "bumpalo 3.1.2", + "bumpalo", "lazy_static", "log", "proc-macro2 1.0.7", diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index ed4a27415..9dadde27e 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -197,7 +197,7 @@ pub fn derive_packet(_item: TokenStream) -> TokenStream { let r = quote! { impl Packet for #ident { - fn read_from(&mut self, mut buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, mut buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { #(#read_code)* Ok(()) } diff --git a/core/Cargo.toml b/core/Cargo.toml index dfe40af66..bc1189a65 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -6,34 +6,47 @@ edition = "2018" publish = false [dependencies] +# Feather crates feather-codegen = { path = "../codegen" } feather-blocks = { path = "../blocks" } feather-items = { path = "../items" } -lazy_static = "1.4" -derive-new = "0.5" -uuid = "0.7" + +# Networking +tokio = { version = "0.2", features = ["full"] } +tokio-util = { version = "0.2", features = ["codec"] } cfb8 = "0.3" aes = "0.3" flate2 = "1.0" bytes = "0.5" +byteorder = "1.3" + +# Misc. +vek = "0.9" +nalgebra-glm = "0.6" +lazy_static = "1.4" +uuid = "0.8" log = "0.4" -serde = { version = "1.0", features = ["derive"] } num-traits = "0.2" num-derive = "0.3" -hashbrown = { version = "0.6", features = ["serde", "rayon"] } -hematite-nbt = "0.4" -byteorder = "1.3" -nalgebra-glm = "0.4" -derive_more = "0.15" -smallvec = "0.6" +strum = "0.18" +strum_macros = "0.18" + +# Data structures +hashbrown = { version = "0.7", features = ["serde", "rayon"] } +smallvec = "1.2" +multimap = "0.8" +bitvec = "0.17" hash32 = "0.1" hash32-derive = "0.1" -strum = "0.16" -strum_macros = "0.16" -tokio = { version = "0.2", features = ["full"] } -tokio-util = { version = "0.2", features = ["codec"] } -failure = "0.1" -bitvec = "0.15" -multimap = "0.6" -parking_lot = "0.9.0" -rayon = "1.2.0" + +# Serialization +serde = { version = "1.0", features = ["derive"] } +hematite-nbt = "0.4" + +# Concurrency +parking_lot = "0.10" +rayon = "1.3" + +# Error handling +thiserror = "1.0" +anyhow = "1.0" diff --git a/core/src/bytes_ext.rs b/core/src/bytes_ext.rs index 2b565049c..0bea05460 100644 --- a/core/src/bytes_ext.rs +++ b/core/src/bytes_ext.rs @@ -1,16 +1,17 @@ use bytes::{Buf, BufMut, BytesMut}; +use thiserror::Error; /// An error which occurred while attempting /// to get a value from a `Buf.` -#[derive(Clone, Copy, Debug, PartialEq, Eq, Fail)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)] pub enum TryGetError { /// Indicates that there were not enough remaining /// bytes in the buffer to read a value. - #[fail(display = "not enough bytes left in buffer")] + #[error("not enough bytes left in buffer")] NotEnoughBytes, - #[fail(display = "value too large")] + #[error("value too large")] ValueTooLarge, - #[fail(display = "invalid value {}", _0)] + #[error("invalid value {0}")] InvalidValue(i32), } diff --git a/core/src/world/chunk.rs b/core/src/chunk.rs similarity index 98% rename from core/src/world/chunk.rs rename to core/src/chunk.rs index fe788fcd7..39a3581b6 100644 --- a/core/src/world/chunk.rs +++ b/core/src/chunk.rs @@ -1,40 +1,39 @@ -use super::block::*; -use super::ChunkPosition; use crate::Biome; +use crate::{Block, BlockExt, ChunkPosition}; use multimap::MultiMap; /// The number of bits used for each block /// in the global palette. -pub(crate) const GLOBAL_BITS_PER_BLOCK: u8 = 14; +pub const GLOBAL_BITS_PER_BLOCK: u8 = 14; /// The minimum bits per block allowed when /// using a section palette. /// Bits per block values lower than this /// value will be offsetted to this value. -pub(crate) const MIN_BITS_PER_BLOCK: u8 = 4; +pub const MIN_BITS_PER_BLOCK: u8 = 4; /// The maximum number of bits per block /// allowed when using a section palette. /// Values above this will use the global palette /// instead. -pub(crate) const MAX_BITS_PER_BLOCK: u8 = 8; +pub const MAX_BITS_PER_BLOCK: u8 = 8; /// The height in blocks of a chunk column. -const CHUNK_HEIGHT: usize = 256; +pub const CHUNK_HEIGHT: usize = 256; /// The width in blocks of a chunk column. -const CHUNK_WIDTH: usize = 16; +pub const CHUNK_WIDTH: usize = 16; /// The height in blocks of a chunk section. -const SECTION_HEIGHT: usize = 16; +pub const SECTION_HEIGHT: usize = 16; /// The width in blocks of a chunk section. -const SECTION_WIDTH: usize = CHUNK_WIDTH; +pub const SECTION_WIDTH: usize = CHUNK_WIDTH; /// The volume in blocks of a chunk section. -pub(crate) const SECTION_VOLUME: usize = (SECTION_HEIGHT * SECTION_WIDTH * SECTION_WIDTH) as usize; +pub const SECTION_VOLUME: usize = (SECTION_HEIGHT * SECTION_WIDTH * SECTION_WIDTH) as usize; /// The number of chunk sections in a column. -pub(crate) const NUM_SECTIONS: usize = 16; +pub const NUM_SECTIONS: usize = 16; /// A chunk column consisting /// of a 16x256x16 section of blocks. diff --git a/core/src/entitymeta.rs b/core/src/entitymeta.rs index fd95b999a..f9467d15a 100644 --- a/core/src/entitymeta.rs +++ b/core/src/entitymeta.rs @@ -56,54 +56,54 @@ impl MetaEntry { } } -pub trait IntoMetaEntry { - fn into_meta_entry(&self) -> MetaEntry; +pub trait ToMetaEntry { + fn to_meta_entry(&self) -> MetaEntry; } -impl IntoMetaEntry for u8 { - fn into_meta_entry(&self) -> MetaEntry { +impl ToMetaEntry for u8 { + fn to_meta_entry(&self) -> MetaEntry { MetaEntry::Byte(*self as i8) } } -impl IntoMetaEntry for i8 { - fn into_meta_entry(&self) -> MetaEntry { +impl ToMetaEntry for i8 { + fn to_meta_entry(&self) -> MetaEntry { MetaEntry::Byte(*self) } } -impl IntoMetaEntry for i32 { - fn into_meta_entry(&self) -> MetaEntry { +impl ToMetaEntry for i32 { + fn to_meta_entry(&self) -> MetaEntry { MetaEntry::VarInt(*self) } } -impl IntoMetaEntry for bool { - fn into_meta_entry(&self) -> MetaEntry { +impl ToMetaEntry for bool { + fn to_meta_entry(&self) -> MetaEntry { MetaEntry::Boolean(*self) } } -impl IntoMetaEntry for Slot { - fn into_meta_entry(&self) -> MetaEntry { +impl ToMetaEntry for Slot { + fn to_meta_entry(&self) -> MetaEntry { MetaEntry::Slot(self.clone()) } } -impl IntoMetaEntry for f32 { - fn into_meta_entry(&self) -> MetaEntry { +impl ToMetaEntry for f32 { + fn to_meta_entry(&self) -> MetaEntry { MetaEntry::Float(*self) } } -impl IntoMetaEntry for OptUuid { - fn into_meta_entry(&self) -> MetaEntry { +impl ToMetaEntry for OptUuid { + fn to_meta_entry(&self) -> MetaEntry { MetaEntry::OptUuid(*self) } } -impl IntoMetaEntry for BlockPosition { - fn into_meta_entry(&self) -> MetaEntry { +impl ToMetaEntry for BlockPosition { + fn to_meta_entry(&self) -> MetaEntry { MetaEntry::Position(*self) } } @@ -128,8 +128,8 @@ impl EntityMetadata { self } - pub fn set(&mut self, index: u8, entry: E) { - self.values.insert(index, entry.into_meta_entry()); + pub fn set(&mut self, index: u8, entry: E) { + self.values.insert(index, entry.to_meta_entry()); } pub fn get(&self, index: u8) -> Option { @@ -148,7 +148,7 @@ pub trait EntityMetaWrite { } pub trait EntityMetaRead { - fn try_get_metadata(&mut self) -> Result; + fn try_get_metadata(&mut self) -> anyhow::Result; } impl EntityMetaWrite for B @@ -170,7 +170,7 @@ impl EntityMetaRead for B where B: Buf + std::io::Read, { - fn try_get_metadata(&mut self) -> Result { + fn try_get_metadata(&mut self) -> anyhow::Result { let mut values = HashMap::new(); while self.has_remaining() { @@ -249,7 +249,7 @@ where } } -fn try_get_entry(buf: &mut B) -> Result +fn try_get_entry(buf: &mut B) -> anyhow::Result where B: Buf + McTypeRead, { diff --git a/core/src/lib.rs b/core/src/lib.rs index 52a3d6ee6..c0300e6c1 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,8 +1,6 @@ #[macro_use] extern crate lazy_static; #[macro_use] -extern crate derive_new; -#[macro_use] extern crate log; #[macro_use] extern crate serde; @@ -16,33 +14,32 @@ extern crate smallvec; extern crate hash32_derive; #[macro_use] extern crate strum_macros; -#[macro_use] -extern crate failure; - -extern crate nalgebra_glm as glm; #[macro_use] pub mod world; mod biomes; -pub mod bytes_ext; -pub mod entitymeta; +mod bytes_ext; +pub mod chunk; +mod entitymeta; pub mod inventory; +mod math_types; pub mod network; -pub mod prelude; mod save; +extern crate nalgebra_glm as glm; + pub use biomes::Biome; +pub use chunk::{BitArray, Chunk, ChunkSection}; pub use entitymeta::EntityMetadata; +pub use feather_blocks::*; pub use feather_items as item; pub use inventory::{ItemStack, Slot}; pub use item::{Item, ItemExt}; +pub use math_types::*; pub use network::packet::{implementation as packet, Packet, PacketType}; +pub use network::{cast_packet, mctypes}; pub use save::{entity, level, player_data, region}; -pub use world::{ - block::{self, Block, BlockExt}, - chunk::{Chunk, ChunkSection}, - BlockPosition, ChunkPosition, Position, -}; +pub use world::{BlockPosition, ChunkPosition, Position}; #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Gamemode { @@ -53,7 +50,7 @@ pub enum Gamemode { } impl Gamemode { - pub fn get_id(self) -> u8 { + pub fn id(self) -> u8 { match self { Gamemode::Survival => 0, Gamemode::Creative => 1, @@ -92,7 +89,7 @@ pub enum Difficulty { } impl Difficulty { - pub fn get_id(self) -> u8 { + pub fn id(self) -> u8 { match self { Difficulty::Peaceful => 0, Difficulty::Easy => 1, @@ -110,7 +107,7 @@ pub enum Dimension { } impl Dimension { - pub fn get_id(self) -> i32 { + pub fn id(self) -> i32 { match self { Dimension::Nether => -1, Dimension::Overwold => 0, diff --git a/core/src/math_types.rs b/core/src/math_types.rs new file mode 100644 index 000000000..8fbc87f49 --- /dev/null +++ b/core/src/math_types.rs @@ -0,0 +1,31 @@ +pub type Vec2f = vek::Vec2; +pub type Vec3f = vek::Vec3; +pub type Vec4f = vek::Vec4; + +pub type Vec2d = vek::Vec2; +pub type Vec3d = vek::Vec3; +pub type Vec4d = vek::Vec4; + +pub type Vec2i = vek::Vec2; +pub type Vec3i = vek::Vec3; +pub type Vec4i = vek::Vec4; + +pub type Mat2f = vek::mat::column_major::Mat2; +pub type Mat3f = vek::mat::column_major::Mat3; +pub type Mat4f = vek::mat::column_major::Mat4; + +pub type Mat2d = vek::mat::column_major::Mat2; +pub type Mat3d = vek::mat::column_major::Mat3; +pub type Mat4d = vek::mat::column_major::Mat4; + +pub fn vec2(x: T, y: T) -> vek::Vec2 { + vek::Vec2::new(x, y) +} + +pub fn vec3(x: T, y: T, z: T) -> vek::Vec3 { + vek::Vec3::new(x, y, z) +} + +pub fn vek4(x: T, y: T, z: T, w: T) -> vek::Vec4 { + vek::Vec4::new(x, y, z, w) +} diff --git a/core/src/network/codec.rs b/core/src/network/codec.rs index e0445d929..231b124f4 100644 --- a/core/src/network/codec.rs +++ b/core/src/network/codec.rs @@ -11,6 +11,7 @@ use flate2::read::ZlibDecoder; use flate2::write::ZlibEncoder; use flate2::Compression; use std::io::{Cursor, Read, Write}; +use thiserror::Error; use tokio::io; use tokio_util::codec::{Decoder, Encoder}; @@ -23,16 +24,13 @@ const MAX_PACKET_LEN: usize = 1_048_576; // One MB /// Maximum possible size of a packet header. const HEADER_SIZE: usize = MAX_VAR_INT_SIZE * 2; -#[derive(Debug, Fail)] +#[derive(Debug, Error)] pub enum Error { - #[fail( - display = "Packet of length {} (under compression threshold {}) was sent compressed", - _0, _1 - )] + #[error("Packet of length {0} (under compression threshold {1}) was sent compressed")] CompressedPacketTooSmall(usize, usize), - #[fail(display = "Packet length {} is too large", _0)] + #[error("Packet length {0} is too large")] PacketTooLarge(usize), - #[fail(display = "Invalid packet ID {} for stage {:?}", _0, _1)] + #[error("Invalid packet ID {0} for stage {1:?}")] InvalidPacketId(u32, PacketStage), } @@ -190,7 +188,7 @@ impl Encoder for MinecraftCodec { impl Decoder for MinecraftCodec { type Item = Box; - type Error = failure::Error; + type Error = anyhow::Error; fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { // If encryption is enabled, decrypt undecrypted data. diff --git a/core/src/network/mctypes.rs b/core/src/network/mctypes.rs index 341d64cbf..55904aa4e 100644 --- a/core/src/network/mctypes.rs +++ b/core/src/network/mctypes.rs @@ -1,12 +1,12 @@ use crate::bytes_ext::{BytesExt, BytesMutExt, TryGetError}; use crate::inventory::ItemStack; -use crate::prelude::*; use crate::world::BlockPosition; use bytes::{Buf, BytesMut}; use feather_items::{Item, ItemExt}; use serde::de::DeserializeOwned; use serde::Serialize; use std::io::Read; +use uuid::Uuid; /// Identifies a type to which Minecraft-specific /// types (`VarInt`, `VarLong`, etc.) can be written. @@ -47,7 +47,7 @@ pub trait McTypeRead { fn try_get_bool(&mut self) -> Result; - fn try_get_uuid(&mut self) -> Result; + fn try_get_uuid(&mut self) -> anyhow::Result; fn try_get_nbt(&mut self) -> Result; @@ -188,7 +188,7 @@ impl McTypeRead for B { } } - fn try_get_uuid(&mut self) -> Result { + fn try_get_uuid(&mut self) -> anyhow::Result { let mut bytes = [0u8; 16]; self.read_exact(&mut bytes)?; Ok(Uuid::from_bytes(bytes)) diff --git a/core/src/network/packet/implementation.rs b/core/src/network/packet/implementation.rs index 8e2ec737c..d33d91964 100644 --- a/core/src/network/packet/implementation.rs +++ b/core/src/network/packet/implementation.rs @@ -1,18 +1,21 @@ -use super::super::mctypes::{McTypeRead, McTypeWrite}; -use super::*; use crate::bytes_ext::{BytesExt, BytesMutExt}; use crate::entitymeta::{EntityMetaRead, EntityMetaWrite, EntityMetadata}; use crate::inventory::ItemStack; -use crate::network::packet::PacketStage::Play; -use crate::prelude::*; -use crate::world::chunk::{BitArray, Chunk}; -use crate::{Biome, ChunkSection, ClientboundAnimation, Hand}; -use bytes::{Buf, BufMut}; +use crate::mctypes::{McTypeRead, McTypeWrite}; +use crate::network::packet::{AsAny, PacketBuilder, PacketStage}; +use crate::{ + chunk, Biome, BitArray, BlockPosition, Chunk, ChunkPosition, ChunkSection, + ClientboundAnimation, Gamemode, Hand, Packet, PacketType, +}; +use bytes::{Buf, BufMut, BytesMut}; use hashbrown::HashMap; use num_traits::{FromPrimitive, ToPrimitive}; +use std::any::Any; use std::io::Cursor; use std::io::Read; use std::io::Write; +use thiserror::Error; +use uuid::Uuid; type VarInt = i32; type VarLong = i64; @@ -169,27 +172,27 @@ macro_rules! box_clone_impl { }; } -#[derive(Clone, Copy, Fail, Debug)] +#[derive(Clone, Copy, Error, Debug)] pub enum Error { - #[fail(display = "invalid face value {}", _0)] + #[error("invalid face value {0}")] InvalidFace(i32), - #[fail(display = "invalid hand value {}", _0)] + #[error("invalid hand value {0}")] InvalidHand(i32), - #[fail(display = "invalid entity action type {}", _0)] + #[error("invalid entity action type {0}")] InvalidEntityAction(i32), - #[fail(display = "invalid player digging status {}", _0)] + #[error("invalid player digging status {0}")] InvalidPlayerDiggingStatus(i32), - #[fail(display = "invalid use entity value {}", _0)] + #[error("invalid use entity value {0}")] InvalidUseEntity(i32), - #[fail(display = "insufficient array length")] + #[error("insufficient array length")] InsufficientArrayLength, - #[fail(display = "invalid handshake state {}", _0)] + #[error("invalid handshake next state {0}")] InvalidHandshakeState(i32), } // SERVERBOUND -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct Handshake { pub protocol_version: u32, pub server_address: String, @@ -198,7 +201,7 @@ pub struct Handshake { } impl Packet for Handshake { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { self.protocol_version = buf.try_get_var_int()? as u32; self.server_address = buf.try_get_string()?; self.server_port = buf.try_get_u16()?; @@ -253,12 +256,12 @@ impl Default for HandshakeState { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct LoginStart { pub username: String, } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct EncryptionResponse { pub secret_length: VarInt, pub secret: Vec, @@ -267,7 +270,7 @@ pub struct EncryptionResponse { } impl Packet for EncryptionResponse { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { self.secret_length = buf.try_get_var_int()?; let mut secret = vec![]; @@ -310,37 +313,37 @@ impl Packet for EncryptionResponse { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct Request {} -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct Ping { pub payload: u64, } // PLAY -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct TeleportConfirm { pub teleport_id: VarInt, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct QueryBlockNBT { pub transaction_id: VarInt, pub location: BlockPosition, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct ChatMessageServerbound { pub message: String, // Raw string, not a chat component } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct ClientStatus { pub action_id: VarInt, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct ClientSettings { pub locale: String, pub view_distance: u8, @@ -350,26 +353,26 @@ pub struct ClientSettings { pub main_hand: VarInt, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct TabCompleteServerbound { pub transaction_id: VarInt, pub text: String, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct ConfirmTransactionServerbound { pub window_id: u8, pub action_number: u16, pub accepted: bool, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct EnchantItem { pub window_id: u8, pub enchantment: u8, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct ClickWindow { pub window_id: u8, pub slot: u16, @@ -379,19 +382,19 @@ pub struct ClickWindow { pub clicked_item: Slot, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct CloseWindowServerbound { pub window_id: u8, } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct PluginMessageServerbound { pub channel: String, pub data: Vec, } impl Packet for PluginMessageServerbound { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { self.channel = buf.try_get_string()?; let mut data = Vec::with_capacity(buf.remaining()); @@ -424,27 +427,27 @@ impl Packet for PluginMessageServerbound { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct EditBook { pub new_book: Slot, pub is_signing: bool, pub hand: VarInt, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct QueryEntityNBT { pub transaction_id: VarInt, pub entity_id: VarInt, } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct UseEntity { pub target: VarInt, pub ty: UseEntityType, } impl Packet for UseEntity { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { self.target = buf.try_get_var_int()?; let ty_id = buf.try_get_var_int()?; @@ -498,7 +501,7 @@ impl Packet for UseEntity { } } -#[derive(AsAny, new, Clone)] +#[derive(AsAny, Clone)] pub enum UseEntityType { Interact, Attack, @@ -511,17 +514,17 @@ impl Default for UseEntityType { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct KeepAliveServerbound { pub id: i64, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct Player { pub on_ground: bool, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct PlayerPosition { pub x: f64, pub feet_y: f64, @@ -529,7 +532,7 @@ pub struct PlayerPosition { pub on_ground: bool, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct PlayerPositionAndLookServerbound { pub x: f64, pub feet_y: f64, @@ -539,14 +542,14 @@ pub struct PlayerPositionAndLookServerbound { pub on_ground: bool, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct PlayerLook { pub yaw: f32, pub pitch: f32, pub on_ground: bool, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct VehicleMoveServerbound { pub x: f64, pub y: f64, @@ -555,32 +558,32 @@ pub struct VehicleMoveServerbound { pub pitch: f32, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct SteerBoat { pub left_paddle_turning: bool, pub right_paddle_turning: bool, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct PickItem { pub slot_to_use: VarInt, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct CraftRecipeRequest { pub window_id: i8, pub recipe: String, pub make_all: bool, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct PlayerAbilitiesServerbound { pub flags: u8, pub flying_speed: f32, pub walking_speed: f32, } -#[derive(AsAny, new, Clone, Default)] +#[derive(AsAny, Clone, Default)] pub struct PlayerDigging { pub status: PlayerDiggingStatus, pub location: BlockPosition, @@ -588,7 +591,7 @@ pub struct PlayerDigging { } impl Packet for PlayerDigging { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { self.status = { let id = buf.try_get_var_int()?; match id { @@ -650,7 +653,7 @@ impl Default for PlayerDiggingStatus { } } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct EntityAction { pub entity_id: VarInt, pub action_id: EntityActionType, @@ -658,7 +661,7 @@ pub struct EntityAction { } impl Packet for EntityAction { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { self.entity_id = buf.try_get_var_int()?; let action_id = buf.try_get_var_int()?; self.action_id = @@ -710,52 +713,52 @@ impl Default for EntityActionType { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct SteerVehicle { pub sideways: f32, pub forward: f32, pub flags: u8, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct RecipeBookData { pub ty: VarInt, // TODO } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct NameItem { pub item_name: String, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct ResourcePackStatus { pub result: VarInt, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct AdvancementTab { pub action: VarInt, pub tab_id: String, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct SelectTrade { pub selected_slot: VarInt, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct SetBeaconEffect { pub primary_effect: VarInt, pub secondary_effect: VarInt, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct HeldItemChangeServerbound { pub slot: i16, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct UpdateCommandBlock { pub location: BlockPosition, pub command: String, @@ -763,21 +766,21 @@ pub struct UpdateCommandBlock { pub flags: u8, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct UpdateCommandBlockMinecart { pub entity_id: VarInt, pub command: String, pub track_output: bool, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct CreativeInventoryAction { pub slot: i16, pub clicked_item: Slot, } #[allow(clippy::too_many_arguments)] -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct UpdateStructureBlock { pub location: BlockPosition, pub action: VarInt, @@ -797,7 +800,7 @@ pub struct UpdateStructureBlock { pub flags: u8, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct UpdateSign { pub location: BlockPosition, pub line_1: String, @@ -806,13 +809,13 @@ pub struct UpdateSign { pub line_4: String, } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct AnimationServerbound { pub hand: Hand, } impl Packet for AnimationServerbound { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { let hand_id = buf.try_get_var_int()?; self.hand = match Hand::from_i32(hand_id) { Some(hand) => hand, @@ -842,7 +845,7 @@ impl Packet for AnimationServerbound { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct Spectate { pub target_player: Uuid, } @@ -876,7 +879,7 @@ impl Default for Face { } } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct PlayerBlockPlacement { pub location: BlockPosition, pub face: Face, @@ -887,7 +890,7 @@ pub struct PlayerBlockPlacement { } impl Packet for PlayerBlockPlacement { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { self.location = buf.try_get_position()?; let face_id = buf.try_get_var_int()?; self.face = Face::from_i32(face_id).ok_or(Error::InvalidFace(face_id))?; @@ -923,18 +926,18 @@ impl Packet for PlayerBlockPlacement { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct UseItem { pub hand: VarInt, } // CLIENTBOUND -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct DisconnectLogin { pub reason: String, } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct EncryptionRequest { pub server_id: String, pub public_key: Vec, @@ -942,7 +945,7 @@ pub struct EncryptionRequest { } impl Packet for EncryptionRequest { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { self.server_id = buf.try_get_string()?; let pubkey_len = buf.try_get_var_int()?; @@ -984,30 +987,30 @@ impl Packet for EncryptionRequest { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct LoginSuccess { pub uuid: String, pub username: String, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct SetCompression { pub threshold: VarInt, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct Response { pub json_response: String, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct Pong { pub payload: u64, } // PLAY #[allow(clippy::too_many_arguments)] -#[derive(Default, AsAny, new, Packet, Clone, Debug)] +#[derive(Default, AsAny, Packet, Clone, Debug)] pub struct SpawnObject { pub entity_id: VarInt, pub object_uuid: Uuid, @@ -1023,7 +1026,7 @@ pub struct SpawnObject { pub velocity_z: i16, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct SpawnExperienceOrb { pub entity_id: VarInt, pub x: f64, @@ -1032,7 +1035,7 @@ pub struct SpawnExperienceOrb { pub count: i16, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct SpawnGlobalEntity { pub entity_id: VarInt, pub ty: u8, @@ -1042,7 +1045,7 @@ pub struct SpawnGlobalEntity { } #[allow(clippy::too_many_arguments)] -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct SpawnMob { pub entity_id: VarInt, pub entity_uuid: Uuid, @@ -1059,7 +1062,7 @@ pub struct SpawnMob { pub meta: EntityMetadata, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct SpawnPainting { pub entity_id: VarInt, pub entity_uuid: Uuid, @@ -1069,7 +1072,7 @@ pub struct SpawnPainting { } #[allow(clippy::too_many_arguments)] -#[derive(AsAny, new, Clone, Default, Packet)] +#[derive(AsAny, Clone, Default, Packet)] pub struct SpawnPlayer { pub entity_id: VarInt, pub player_uuid: Uuid, @@ -1081,14 +1084,14 @@ pub struct SpawnPlayer { pub metadata: EntityMetadata, } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct AnimationClientbound { pub entity_id: VarInt, pub animation: ClientboundAnimation, } impl Packet for AnimationClientbound { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { self.entity_id = buf.try_get_var_int()?; self.animation = ClientboundAnimation::from_u8(buf.try_get_u8()?).ok_or(Error::InvalidUseEntity(0))?; @@ -1116,14 +1119,14 @@ impl Packet for AnimationClientbound { } } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct Statistics { pub statistics: Vec<(VarInt, VarInt)>, pub value: VarInt, } impl Packet for Statistics { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { let num_statistics = buf.try_get_var_int()?; if num_statistics > 255 { @@ -1163,21 +1166,21 @@ impl Packet for Statistics { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct BlockBreakAnimation { pub entity_id: VarInt, pub location: BlockPosition, pub destroy_stage: i8, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct UpdateBlockEntity { pub location: BlockPosition, pub action: u8, // TODO pub data: NbtTag } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct BlockAction { pub location: BlockPosition, pub action_id: u8, @@ -1185,20 +1188,20 @@ pub struct BlockAction { pub block_type: VarInt, // NOTE: block type ID, not the block state ID } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct BlockChange { pub location: BlockPosition, pub block_id: VarInt, } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct BossBar { pub uuid: Uuid, pub action: BossBarAction, } impl Packet for BossBar { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { unimplemented!() } @@ -1308,12 +1311,12 @@ impl Default for BossBarDivision { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct ServerDifficulty { pub difficulty: u8, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct ChatMessageClientbound { pub json_data: String, pub position: u8, @@ -1323,14 +1326,14 @@ pub struct ChatMessageClientbound { // TODO TabCompleteClientbound // TODO DeclareCommands -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct ConfirmTransactionClientbound { pub window_id: i8, pub action_number: i16, pub accepted: bool, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct OpenWindow { pub window_id: u8, pub window_type: String, @@ -1339,14 +1342,14 @@ pub struct OpenWindow { pub entity_id: i32, } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct WindowItems { pub window_id: u8, pub slots: Vec, } impl Packet for WindowItems { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { self.window_id = buf.try_get_u8()?; let num_slots = buf.try_get_i16()?; @@ -1382,34 +1385,34 @@ impl Packet for WindowItems { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct WindowProperty { pub window_id: u8, pub property: i16, pub value: i16, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct SetSlot { pub window_id: i8, pub slot: i16, pub slot_data: Slot, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct SetCooldown { pub item_id: VarInt, pub cooldown_ticks: VarInt, } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct PluginMessageClientbound { pub channel: String, pub data: Vec, } impl Packet for PluginMessageClientbound { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { self.channel = buf.try_get_string()?; self.data.extend_from_slice(*buf.get_ref()); @@ -1438,7 +1441,7 @@ impl Packet for PluginMessageClientbound { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct NamedSoundEffect { pub sound_name: String, pub sound_category: VarInt, @@ -1449,25 +1452,25 @@ pub struct NamedSoundEffect { pub pitch: f32, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct DisconnectPlay { pub reason: String, // Chat } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct EntityStatus { pub entity_id: i32, pub entity_status: i8, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct NBTQueryResponse { pub transaction_id: VarInt, // TODO pub nbt: NbtTag, } #[allow(clippy::too_many_arguments)] -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct Explosion { pub x: f32, pub y: f32, @@ -1480,7 +1483,7 @@ pub struct Explosion { } impl Packet for Explosion { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { unimplemented!() } @@ -1519,38 +1522,36 @@ impl Packet for Explosion { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct UnloadChunk { pub chunk_x: i32, pub chunk_z: i32, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct ChangeGameState { pub reason: u8, pub value: f32, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct KeepAliveClientbound { pub keep_alive_id: u64, } -#[derive(Debug, Fail)] +#[derive(Debug, Error)] enum ChunkDataError { - #[fail(display = "invalid bits per block value {} for section {}", _0, _1)] + #[error("invalid bits per block value {0} for section {1}")] InvalidBitsPerBlock(u8, usize), } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct ChunkData { pub chunk: Chunk, } impl Packet for ChunkData { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { - use crate::world::chunk::{self, BitArray}; - + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { self.chunk .set_position(ChunkPosition::new(buf.try_get_i32()?, buf.try_get_i32()?)); if buf.try_get_bool()? { @@ -1716,7 +1717,7 @@ impl Packet for ChunkData { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct Effect { pub effect_id: i32, pub location: BlockPosition, @@ -1724,7 +1725,7 @@ pub struct Effect { pub disable_relative_volume: bool, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct Particle { pub particle_id: i32, pub long_distance: bool, @@ -1738,7 +1739,7 @@ pub struct Particle { // TODO data } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct JoinGame { pub entity_id: i32, pub gamemode: u8, @@ -1752,7 +1753,7 @@ pub struct JoinGame { // TODO MapData // TODO EntityPacket -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct EntityRelativeMove { pub entity_id: VarInt, pub delta_x: i16, @@ -1761,7 +1762,7 @@ pub struct EntityRelativeMove { pub on_ground: bool, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct EntityLookAndRelativeMove { pub entity_id: VarInt, pub delta_x: i16, @@ -1772,7 +1773,7 @@ pub struct EntityLookAndRelativeMove { pub on_ground: bool, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct EntityLook { pub entity_id: VarInt, pub yaw: u8, @@ -1780,7 +1781,7 @@ pub struct EntityLook { pub on_ground: bool, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct VehicleMoveClientbound { pub x: f64, pub y: f64, @@ -1789,31 +1790,31 @@ pub struct VehicleMoveClientbound { pub pitch: f32, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct OpenSignEditor { pub location: BlockPosition, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct CraftRecipeResponse { pub window_id: i8, pub recipe: String, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct PlayerAbilitiesClientbound { flags: u8, flying_speed: f32, field_of_view_modifier: f32, } -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct CombatEvent { pub event: CombatEventType, } impl Packet for CombatEvent { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { unimplemented!() } @@ -1848,7 +1849,7 @@ impl Packet for CombatEvent { } } -#[derive(new, Clone)] +#[derive(Clone)] pub enum CombatEventType { EnterCombat, EndCombat(VarInt, i32), @@ -1861,14 +1862,14 @@ impl Default for CombatEventType { } } -#[derive(AsAny, new, Clone, Default)] +#[derive(AsAny, Clone, Default)] pub struct PlayerInfo { pub action: PlayerInfoAction, pub uuid: Uuid, } impl Packet for PlayerInfo { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { let id = buf.try_get_var_int()?; let _ = buf.try_get_var_int()?; self.uuid = buf.try_get_uuid()?; @@ -1932,13 +1933,13 @@ impl Packet for PlayerInfo { buf.push_string(&prop.2); } - buf.push_var_int(i32::from(gamemode.get_id())); + buf.push_var_int(i32::from(gamemode.id())); buf.push_var_int(*ping); buf.push_bool(true); buf.push_string(display_name); } PlayerInfoAction::UpdateGamemode(gamemode) => { - buf.push_var_int(i32::from(gamemode.get_id())); + buf.push_var_int(i32::from(gamemode.id())); } PlayerInfoAction::UpdateLatency(ping) => { buf.push_var_int(*ping); @@ -2002,7 +2003,7 @@ impl Default for PlayerInfoAction { // TODO Face Player -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct PlayerPositionAndLookClientbound { pub x: f64, pub y: f64, @@ -2013,7 +2014,7 @@ pub struct PlayerPositionAndLookClientbound { pub teleport_id: VarInt, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct UseBed { pub entity_id: VarInt, pub location: BlockPosition, @@ -2021,13 +2022,13 @@ pub struct UseBed { // TODO Unlock Recipes -#[derive(Default, AsAny, new, Clone)] +#[derive(Default, AsAny, Clone)] pub struct DestroyEntities { pub entity_ids: Vec, } impl Packet for DestroyEntities { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { unimplemented!() } @@ -2055,19 +2056,19 @@ impl Packet for DestroyEntities { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct RemoveEntityEffect { pub entity_id: VarInt, pub effect_id: i8, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct ResourcePackSend { pub url: String, pub hash: String, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct Respawn { pub dimension: i32, pub difficulty: u8, @@ -2075,20 +2076,20 @@ pub struct Respawn { pub level_type: String, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct EntityHeadLook { pub entity_id: VarInt, pub head_yaw: u8, } -#[derive(Default, AsAny, new, Clone, Debug)] +#[derive(Default, AsAny, Clone, Debug)] pub struct PacketEntityMetadata { pub entity_id: VarInt, pub metadata: EntityMetadata, } impl Packet for PacketEntityMetadata { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error> { + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { self.entity_id = buf.try_get_var_int()?; self.metadata = buf.try_get_metadata()?; Ok(()) @@ -2115,7 +2116,7 @@ impl Packet for PacketEntityMetadata { } } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct EntityVelocity { pub entity_id: VarInt, pub velocity_x: i16, @@ -2123,7 +2124,7 @@ pub struct EntityVelocity { pub velocity_z: i16, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct EntityEquipment { pub entity_id: VarInt, pub slot: VarInt, @@ -2133,7 +2134,7 @@ pub struct EntityEquipment { // TODO Select Advancement Tab // TODO World Border -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct SpawnPosition { pub location: BlockPosition, } @@ -2144,7 +2145,7 @@ pub struct TimeUpdate { pub time_of_day: i64, } -#[derive(Default, AsAny, new, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone)] pub struct CollectItem { pub collected: VarInt, pub collector: VarInt, diff --git a/core/src/network/packet/mod.rs b/core/src/network/packet/mod.rs index 538d5cb30..997dcc602 100644 --- a/core/src/network/packet/mod.rs +++ b/core/src/network/packet/mod.rs @@ -24,7 +24,7 @@ where } pub trait Packet: AsAny + IntoAny + Send + Sync + Any { - fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> Result<(), failure::Error>; + fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()>; fn write_to(&self, buf: &mut BytesMut); fn ty(&self) -> PacketType; fn ty_sized() -> PacketType diff --git a/core/src/prelude.rs b/core/src/prelude.rs deleted file mode 100644 index 23662a0f0..000000000 --- a/core/src/prelude.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub use super::{ - world::{block::*, BlockPosition, ChunkMap, ChunkPosition, Position}, - Difficulty, Dimension, Gamemode, PvpStyle, -}; -pub use crate::network::cast_packet; -pub use uuid::Uuid; diff --git a/core/src/save/entity.rs b/core/src/save/entity.rs index 48b05e225..9a926c349 100644 --- a/core/src/save/entity.rs +++ b/core/src/save/entity.rs @@ -1,4 +1,4 @@ -use crate::{Item, Position}; +use crate::{vec3, Item, Position, Vec3d}; use nbt::Value; use std::collections::HashMap; @@ -111,7 +111,7 @@ impl BaseEntityData { impl BaseEntityData { /// Creates a `BaseEntityData` from a position and velocity. - pub fn new(pos: Position, velocity: glm::DVec3) -> Self { + pub fn new(pos: Position, velocity: Vec3d) -> Self { Self { position: vec![pos.x, pos.y, pos.z], rotation: vec![pos.yaw, pos.pitch], @@ -136,13 +136,9 @@ impl BaseEntityData { } /// Reads the velocity field. If the field is invalid, None is returned. - pub fn read_velocity(self: &BaseEntityData) -> Option { + pub fn read_velocity(self: &BaseEntityData) -> Option { if self.velocity.len() == 3 { - Some(glm::vec3( - self.velocity[0], - self.velocity[1], - self.velocity[2], - )) + Some(vec3(self.velocity[0], self.velocity[1], self.velocity[2])) } else { None } @@ -322,7 +318,7 @@ mod tests { #[test] fn test_new() { let pos = position!(1.0, 10.0, 3.0, 115.0, -3.0); - let vel = glm::vec3(0.0, 1.0, 2.0); + let vel = vec3(0.0, 1.0, 2.0); let data = BaseEntityData::new(pos, vel); assert_eq!(data.read_position(), Some(pos)); diff --git a/core/src/save/player_data.rs b/core/src/save/player_data.rs index 9ff1c31d8..aef30ccfe 100644 --- a/core/src/save/player_data.rs +++ b/core/src/save/player_data.rs @@ -134,7 +134,7 @@ mod tests { let cursor = Cursor::new(include_bytes!("player.dat").to_vec()); let player = load_from_file(cursor).unwrap(); - assert_eq!(player.gamemode, i32::from(Gamemode::Creative.get_id())); + assert_eq!(player.gamemode, i32::from(Gamemode::Creative.id())); } #[test] diff --git a/core/src/save/region/mod.rs b/core/src/save/region/mod.rs index b471652c1..3c8deecd2 100644 --- a/core/src/save/region/mod.rs +++ b/core/src/save/region/mod.rs @@ -13,13 +13,10 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use serde::Deserialize; use crate::save::entity::EntityData; -use crate::world::block::*; -use crate::world::chunk::{BitArray, Chunk, ChunkSection}; use crate::world::ChunkPosition; use crate::Biome; -use bitvec::bitvec; -use bitvec::vec::BitVec; -use feather_blocks::Block; +use crate::{BitArray, Block, BlockExt, Chunk, ChunkSection}; +use bitvec::{bitvec, vec::BitVec}; mod blob; @@ -467,7 +464,7 @@ impl SectorAllocator { let mut length = 0; for (index, is_used) in self.used_sectors.iter().enumerate() { - if is_used { + if *is_used { start = 0; length = 0; } else { diff --git a/core/src/world/mod.rs b/core/src/world.rs similarity index 61% rename from core/src/world/mod.rs rename to core/src/world.rs index 1888c39d8..4109f88af 100644 --- a/core/src/world/mod.rs +++ b/core/src/world.rs @@ -1,6 +1,5 @@ -use crate::world::block::*; -use crate::world::chunk::Chunk; -use glm::{DVec3, Vec3}; +use crate::Chunk; +use crate::{vec3, Block, Vec3d, Vec3i}; use hashbrown::HashMap; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use rayon::iter::ParallelIterator; @@ -9,10 +8,6 @@ use std::fmt::{Display, Formatter}; use std::ops::{Add, Sub}; use std::sync::Arc; -pub mod block; -#[allow(clippy::cast_lossless)] -pub mod chunk; - #[macro_export] macro_rules! position { ($x:expr, $y:expr, $z:expr, $pitch:expr, $yaw:expr, $on_ground:expr) => { @@ -47,34 +42,18 @@ pub struct Position { } impl Position { - pub fn distance(&self, other: Position) -> f64 { - self.distance_squared(other).sqrt() + pub fn distance_to(&self, other: Position) -> f64 { + self.distance_squared_to(other).sqrt() } - pub fn distance_squared(&self, other: Position) -> f64 { + pub fn distance_squared_to(&self, other: Position) -> f64 { square(self.x - other.x) + square(self.y - other.y) + square(self.z - other.z) } - /// Returns the position of the chunk - /// this position is in. - pub fn chunk_pos(&self) -> ChunkPosition { - ChunkPosition::new(self.x.floor() as i32 / 16, self.z.floor() as i32 / 16) - } - - /// Retrieves the position of the block - /// this position is in. - pub fn block_pos(&self) -> BlockPosition { - BlockPosition::new( - self.x.floor() as i32, - self.y.floor() as i32, - self.z.floor() as i32, - ) - } - /// Returns a unit vector representing /// the direction of this position's pitch /// and yaw. - pub fn direction(&self) -> DVec3 { + pub fn direction(&self) -> Vec3d { let rotation_x = f64::from(self.yaw.to_radians()); let rotation_y = f64::from(self.pitch.to_radians()); @@ -85,29 +64,37 @@ impl Position { let x = -xz * rotation_x.sin(); let z = xz * rotation_x.cos(); - glm::vec3(x, y, z) + vec3(x, y, z) + } + + pub fn chunk(self) -> ChunkPosition { + self.into() + } + + pub fn block(self) -> BlockPosition { + self.into() } - pub fn as_vec(&self) -> DVec3 { + pub fn as_vec(&self) -> Vec3d { (*self).into() } } -impl Add for Position { +impl Add for Position { type Output = Position; - fn add(mut self, vec: Vec3) -> Self::Output { - self.x += f64::from(vec.x); - self.y += f64::from(vec.y); - self.z += f64::from(vec.z); + fn add(mut self, rhs: Vec3d) -> Self::Output { + self.x += rhs.x; + self.y += rhs.y; + self.z += rhs.z; self } } -impl Add for Position { +impl Add for Position { type Output = Position; - fn add(mut self, rhs: DVec3) -> Self::Output { + fn add(mut self, rhs: glm::DVec3) -> Self::Output { self.x += rhs.x; self.y += rhs.y; self.z += rhs.z; @@ -128,24 +115,24 @@ impl Add for Position { } } -impl Sub for Position { +impl Sub for Position { type Output = Position; - fn sub(mut self, vec: Vec3) -> Self::Output { - self.x -= f64::from(vec.x); - self.y -= f64::from(vec.y); - self.z -= f64::from(vec.z); + fn sub(mut self, rhs: Vec3d) -> Self::Output { + self.x -= rhs.x; + self.y -= rhs.y; + self.z -= rhs.z; self } } -impl Sub for Position { +impl Sub for Position { type Output = Position; - fn sub(mut self, vec: DVec3) -> Self::Output { - self.x -= vec.x; - self.y -= vec.y; - self.z -= vec.z; + fn sub(mut self, rhs: glm::DVec3) -> Self::Output { + self.x -= rhs.x; + self.y -= rhs.y; + self.z -= rhs.z; self } } @@ -161,37 +148,52 @@ impl Sub for Position { } } -impl Into for Position { - fn into(self) -> Vec3 { - glm::vec3(self.x as f32, self.y as f32, self.z as f32) +impl Into for Position { + fn into(self) -> Vec3d { + vec3(self.x, self.y, self.z) } } -impl Into for Position { - fn into(self) -> DVec3 { +impl Into for Position { + fn into(self) -> glm::DVec3 { glm::vec3(self.x, self.y, self.z) } } -impl From for Position { - fn from(vec: Vec3) -> Self { - position!(f64::from(vec.x), f64::from(vec.y), f64::from(vec.z)) +impl From for Position { + fn from(vec: Vec3d) -> Self { + position!(vec.x, vec.y, vec.z) } } -impl From for Position { - fn from(vec: DVec3) -> Self { +impl From for Position { + fn from(vec: glm::DVec3) -> Self { position!(vec.x, vec.y, vec.z) } } +impl Into for Position { + fn into(self) -> ChunkPosition { + ChunkPosition { + x: self.x.floor() as i32 / 16, + z: self.z.floor() as i32 / 16, + } + } +} + +impl Into for Position { + fn into(self) -> BlockPosition { + BlockPosition { + x: self.x.floor() as i32, + y: self.y.floor() as i32, + z: self.z.floor() as i32, + } + } +} + impl Display for Position { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { - write!( - f, - "({:.2}, {:.2}, {:.2}), ({:.2}, {:.2}), on_ground: {}", - self.x, self.y, self.z, self.pitch, self.yaw, self.on_ground - ) + write!(f, "({:.2}, {:.2}, {:.2})", self.x, self.y, self.z,) } } @@ -211,7 +213,7 @@ impl ChunkPosition { } /// Computes the Manhattan distance from this chunk to another. - pub fn manhattan_distance(self, other: ChunkPosition) -> i32 { + pub fn manhattan_distance_to(self, other: ChunkPosition) -> i32 { (self.x - other.z).abs() + (self.z - other.z).abs() } } @@ -245,14 +247,6 @@ impl BlockPosition { Self { x, y, z } } - pub fn chunk_pos(&self) -> ChunkPosition { - ChunkPosition::new(self.x >> 4, self.z >> 4) - } - - pub fn world_pos(&self) -> Position { - position!(f64::from(self.x), f64::from(self.y), f64::from(self.z)) - } - /// Returns the Manhattan distance from this position to another. pub fn manhattan_distance(self, other: BlockPosition) -> i32 { (self.x - other.x).abs() + (self.y - other.y).abs() + (self.z - other.z).abs() @@ -270,6 +264,64 @@ impl Add for BlockPosition { } } +impl Add for BlockPosition { + type Output = Self; + + fn add(self, rhs: Vec3i) -> Self::Output { + self + BlockPosition::from(rhs) + } +} + +impl Sub for BlockPosition { + type Output = Self; + + fn sub(mut self, rhs: BlockPosition) -> Self::Output { + self.x -= rhs.x; + self.y -= rhs.y; + self.z -= rhs.z; + self + } +} + +impl Sub for BlockPosition { + type Output = Self; + + fn sub(self, rhs: Vec3i) -> Self::Output { + self - BlockPosition::from(rhs) + } +} + +impl Into for BlockPosition { + fn into(self) -> Vec3i { + vec3(self.x, self.y, self.z) + } +} + +impl From for BlockPosition { + fn from(vec: Vec3i) -> Self { + BlockPosition { + x: vec.x, + y: vec.y, + z: vec.z, + } + } +} + +impl Into for BlockPosition { + fn into(self) -> Position { + position!(self.x as f64, self.y as f64, self.z as f64) + } +} + +impl Into for BlockPosition { + fn into(self) -> ChunkPosition { + ChunkPosition { + x: self.x >> 4, + z: self.z >> 4, + } + } +} + pub type ChunkMapInner = HashMap>>; /// The chunk map. @@ -306,7 +358,7 @@ impl ChunkMap { /// exists is not laoded, `None` is returned. pub fn block_at(&self, pos: BlockPosition) -> Option { let (x, y, z) = chunk_relative_pos(pos); - self.chunk_at(pos.chunk_pos()) + self.chunk_at(pos.into()) .map(|chunk| chunk.block_at(x, y, z)) } @@ -318,7 +370,7 @@ impl ChunkMap { pub fn set_block_at(&self, pos: BlockPosition, block: Block) -> bool { let (x, y, z) = chunk_relative_pos(pos); - self.chunk_at_mut(pos.chunk_pos()) + self.chunk_at_mut(pos.into()) .map(|mut chunk| chunk.set_block_at(x, y, z, block)) .is_some() } @@ -358,84 +410,3 @@ pub fn chunk_relative_pos(block_pos: BlockPosition) -> (usize, usize, usize) { block_pos.z as usize & 0xf, ) } - -pub trait ChunkGenerator { - fn generate(&self, chunk: &mut Chunk); -} - -pub struct FlatChunkGenerator {} - -impl ChunkGenerator for FlatChunkGenerator { - fn generate(&self, chunk: &mut Chunk) { - for x in 0..16 { - for y in 0..64 { - for z in 0..16 { - chunk.set_block_at(x, y, z, Block::Stone); - } - } - } - } -} - -pub struct GridChunkGenerator {} - -impl ChunkGenerator for GridChunkGenerator { - fn generate(&self, chunk: &mut Chunk) { - for x in 0..15 { - for y in 0..64 { - for z in 0..15 { - chunk.set_block_at(x, y, z, Block::Stone); - } - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_chunk_map() { - let mut world = ChunkMap::new(); - - let chunk = world.chunk_at(ChunkPosition::new(0, 0)); - if chunk.is_some() { - panic!(); - } - - let mut chunk = Chunk::new(ChunkPosition::new(0, 0)); - FlatChunkGenerator {}.generate(&mut chunk); - world.insert(chunk); - - let chunk = world.chunk_at(ChunkPosition::new(0, 0)).unwrap(); - - for x in 0..15 { - for y in 0..64 { - for z in 0..15 { - assert_eq!(chunk.block_at(x, y, z), Block::Stone); - } - } - } - - assert_eq!(chunk.block_at(8, 64, 8), Block::Air); - } - - #[test] - fn test_set_block_at() { - let mut world = ChunkMap::new(); - - let mut chunk = Chunk::new(ChunkPosition::new(0, 0)); - GridChunkGenerator {}.generate(&mut chunk); - world.insert(chunk); - - println!("-----"); - world.set_block_at(BlockPosition::new(1, 63, 1), Block::Air); - - println!("-----"); - assert_eq!( - world.block_at(BlockPosition::new(1, 63, 1)).unwrap(), - Block::Air - ); - } -} diff --git a/core/src/world/block.rs b/core/src/world/block.rs deleted file mode 100644 index 0e25cfce8..000000000 --- a/core/src/world/block.rs +++ /dev/null @@ -1 +0,0 @@ -pub use feather_blocks::*; diff --git a/server/Cargo.toml b/server/Cargo.toml index 1049e0506..c4284a9af 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -26,17 +26,17 @@ tonks = { git = "https://github.com/feather-rs/tonks", rev = "0ed28a624a21d04401 # Concurrency/threading crossbeam = "0.7" -rayon = "1.2" -parking_lot = "0.9" +rayon = "1.3" +parking_lot = "0.10" lock_api = "0.3" thread_local = "1.0" # Netorking/IO -tokio = {version = "0.2", features = ["full"] } +tokio = { version = "0.2", features = ["full"] } tokio-util = { version = "0.2", features = ["codec"] } futures = "0.3" bytes = "0.5" -mojang-api = "0.5" +mojang-api = "0.6" # Crypto rsa = "0.2" @@ -45,28 +45,27 @@ rsa-der = "0.2" num-bigint = { version = "0.6", features = ["rand", "i128", "u64_digit"], package = "num-bigint-dig" } # Hash functions -ahash = "0.2" -fnv = "1.0" -base64 = "0.10" +ahash = "0.3" +base64 = "0.12" # Math and physics -nalgebra-glm = "0.4" -nalgebra = "0.18" -ncollide3d = "0.20" +nalgebra-glm = "0.6" +nalgebra = "0.20" +ncollide3d = "0.22" # Other data structures -hashbrown = { version = "0.6", features = ["rayon"] } -bitvec = "0.15" +hashbrown = { version = "0.7", features = ["rayon"] } +bitvec = "0.17" bitflags = "1.2" heapless = "0.5" -uuid = { version = "0.7", features = ["v4"] } -multimap = "0.7" -smallvec = "0.6" +uuid = { version = "0.8", features = ["v4"] } +multimap = "0.8" +smallvec = "1.2" chashmap = "2.2" # Logging log = "0.4" -simple_logger = "1.3" +simple_logger = "1.6" # Serialization/deserialization serde = { version = "1.0", features = ["derive"] } @@ -79,19 +78,21 @@ rand = "0.7" rand_xorshift = "0.2" # Other -failure = "0.1" num-derive = "0.3" num-traits = "0.2" lazy_static = "1.4" -derive_deref = "1.1" -bumpalo = "2.6" -strum = "0.16" +bumpalo = "3.2" +strum = "0.18" simdnoise = "3.1" -simdeez = "0.6" -humantime-serde = "0.1" +simdeez = "1.0" +humantime-serde = "1.0" ctrlc = "3.1" inventory = "0.1" +# Error handling +thiserror = "1.0" +anyhow = "1.0" + [dev-dependencies] criterion = "0.3" diff --git a/server/src/broadcasters/block.rs b/server/src/broadcasters/block.rs index 54bd44c63..4b24f2225 100644 --- a/server/src/broadcasters/block.rs +++ b/server/src/broadcasters/block.rs @@ -20,5 +20,5 @@ fn broadcast_block_update(event: &BlockUpdateEvent, state: &State) { location: event.pos, block_id: event.new_block.native_state_id() as i32, }; - state.broadcast_chunk_update(event.pos.chunk_pos(), packet, neq); + state.broadcast_chunk_update(event.pos.into(), packet, neq); } diff --git a/server/src/broadcasters/entity_creation.rs b/server/src/broadcasters/entity_creation.rs index 598d8c3a5..55a780966 100644 --- a/server/src/broadcasters/entity_creation.rs +++ b/server/src/broadcasters/entity_creation.rs @@ -3,7 +3,7 @@ use crate::entity::{CreationPacketCreator, EntityCreateEvent, SpawnPacketCreator use crate::network::Network; use crate::player::PlayerJoinEvent; use crate::state::State; -use feather_core::Position; +use feather_core::{ChunkPosition, Position}; use legion::query::Read; use rayon::prelude::*; use tonks::{PreparedWorld, Query, QueryAccessor}; @@ -40,10 +40,8 @@ fn broadcast_entity_creation( // state.broadcast_entity_update_boxed(event.entity, packet, Some(event.entity)); if let Some(meta) = world.get_component::(event.entity) { - let chunk = world - .get_component::(event.entity) - .unwrap() - .chunk_pos(); + let chunk: ChunkPosition = + (*world.get_component::(event.entity).unwrap()).into(); for entity in holders.holders_for(chunk).unwrap_or(&[]) { if let Some(network) = world.get_component::(*entity) @@ -66,10 +64,7 @@ fn broadcast_entity_creation( } // Register entity sends - let chunk = world - .get_component::(event.entity) - .unwrap() - .chunk_pos(); + let chunk = (*world.get_component::(event.entity).unwrap()).into(); for entity in holders.holders_for(chunk).unwrap_or(&[]) { state.register_entity_send(event.entity, *entity); } diff --git a/server/src/broadcasters/entity_deletion.rs b/server/src/broadcasters/entity_deletion.rs index 1e9931db0..c729f2304 100644 --- a/server/src/broadcasters/entity_deletion.rs +++ b/server/src/broadcasters/entity_deletion.rs @@ -18,7 +18,7 @@ fn broadcast_entity_deletion( ) { events.par_iter().for_each(|event: &EntityDeleteEvent| { if let Some(pos) = event.position { - let chunk = pos.chunk_pos(); + let chunk = pos.into(); for entity in holders.holders_for(chunk).unwrap_or(&[]) { if let Some(network) = world.get_component::(*entity) { diff --git a/server/src/broadcasters/movement.rs b/server/src/broadcasters/movement.rs index f1e34e41a..b5b474a8d 100644 --- a/server/src/broadcasters/movement.rs +++ b/server/src/broadcasters/movement.rs @@ -39,7 +39,7 @@ fn broadcast_move( let pos = *world.get_component::(event.entity).unwrap(); // Find clients which can see the entity. - let chunk = pos.chunk_pos(); + let chunk = pos.into(); let clients = chunk_holders.holders_for(chunk).unwrap_or(&[]); let entity_id = world.get_component::(event.entity).unwrap().0; @@ -131,42 +131,42 @@ fn packets_for_movement_update( } if has_looked { - let packet: Box = Box::new(EntityLookAndRelativeMove::new( + let packet: Box = Box::new(EntityLookAndRelativeMove { entity_id, - rx, - ry, - rz, - degrees_to_stops(new_pos.yaw), - degrees_to_stops(new_pos.pitch), - new_pos.on_ground, - )); + delta_x: rx, + delta_y: ry, + delta_z: rz, + yaw: degrees_to_stops(new_pos.yaw), + pitch: degrees_to_stops(new_pos.pitch), + on_ground: new_pos.on_ground, + }); packets.push(packet); } else { - let packet: Box = Box::new(EntityRelativeMove::new( + let packet: Box = Box::new(EntityRelativeMove { entity_id, - rx, - ry, - rz, - new_pos.on_ground, - )); + delta_x: rx, + delta_y: ry, + delta_z: rz, + on_ground: new_pos.on_ground, + }); packets.push(packet); } } else { - let packet: Box = Box::new(EntityLook::new( + let packet: Box = Box::new(EntityLook { entity_id, - degrees_to_stops(new_pos.yaw), - degrees_to_stops(new_pos.pitch), - new_pos.on_ground, - )); + yaw: degrees_to_stops(new_pos.yaw), + pitch: degrees_to_stops(new_pos.pitch), + on_ground: new_pos.on_ground, + }); packets.push(packet); } // Entity Head Look also needs to be sent if the entity turned its head if has_looked { - let packet: Box = Box::new(EntityHeadLook::new( + let packet: Box = Box::new(EntityHeadLook { entity_id, - degrees_to_stops(new_pos.yaw), - )); + head_yaw: degrees_to_stops(new_pos.yaw), + }); packets.push(packet); } diff --git a/server/src/chunk_entities.rs b/server/src/chunk_entities.rs index 75a2bd88d..a336e515a 100644 --- a/server/src/chunk_entities.rs +++ b/server/src/chunk_entities.rs @@ -66,8 +66,8 @@ fn chunk_entities_handle_movement( .0; let new_pos = *world.get_component::(event.entity).unwrap(); - let old_chunk = old_pos.chunk_pos(); - let new_chunk = new_pos.chunk_pos(); + let old_chunk = old_pos.chunk(); + let new_chunk = new_pos.chunk(); if old_chunk != new_chunk { // Update chunk entities @@ -90,7 +90,7 @@ fn chunk_entities_insert( world: &mut PreparedWorld, ) { if let Some(position) = world.get_component::(event.entity) { - let chunk = position.chunk_pos(); + let chunk = position.chunk(); let mut map = state.chunk_entities.0.write(); map.entry(chunk) @@ -102,7 +102,7 @@ fn chunk_entities_insert( #[event_handler] fn chunk_entities_remove(event: &EntityDeleteEvent, state: &State) { if let Some(position) = event.position { - let chunk = position.chunk_pos(); + let chunk = position.chunk(); let mut map = state.chunk_entities.0.write(); map.entry(chunk) diff --git a/server/src/chunk_worker.rs b/server/src/chunk_worker.rs index 358a5b799..73aac51ef 100644 --- a/server/src/chunk_worker.rs +++ b/server/src/chunk_worker.rs @@ -9,8 +9,7 @@ use crossbeam::channel::{Receiver, Sender}; use feather_core::entity::EntityData; use feather_core::region; use feather_core::region::{RegionHandle, RegionPosition}; -use feather_core::world::chunk::Chunk; -use feather_core::world::ChunkPosition; +use feather_core::{Chunk, ChunkPosition}; use hashbrown::HashMap; use std::path::{Path, PathBuf}; use std::sync::Arc; diff --git a/server/src/config.rs b/server/src/config.rs index a96b5587e..7c680a399 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -1,12 +1,13 @@ -use failure::_core::time::Duration; use std::fs::read_to_string; +use std::time::Duration; +use thiserror::Error; -#[derive(Debug, Fail)] +#[derive(Debug, Error)] pub enum ConfigError { - #[fail(display = "Badly formatted configuration file: {}", _0)] - Parse(#[fail(cause)] toml::de::Error), - #[fail(display = "Failed to read configuration file: {}", _0)] - Io(#[fail(cause)] std::io::Error), + #[error("Badly formatted configuration file: {0}")] + Parse(toml::de::Error), + #[error("Failed to read configuration file: {0}")] + Io(std::io::Error), } #[derive(Serialize, Deserialize, Debug, Clone)] diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index 9b0740978..368bc263d 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -82,7 +82,7 @@ pub fn item_spawn( // work. See https://github.com/GlowstoneMC/Glowstone/blob/dev/src/main/java/net/glowstone/entity/GlowHumanEntity.java // (method drop(ItemStack stack)) for their code. let velocity = { - let mut vel = pos.direction() * 0.3; + let mut vel = glm::DVec3::from_column_slice(&(pos.direction() * 0.3).into_array()); let rand_offset = 0.02; let x = rng.gen_range(0.0, rand_offset) - rand_offset / 2.0; diff --git a/server/src/io/initial_handler.rs b/server/src/io/initial_handler.rs index 27e9ec615..6079c5e63 100644 --- a/server/src/io/initial_handler.rs +++ b/server/src/io/initial_handler.rs @@ -22,6 +22,8 @@ use rsa::{PaddingScheme, PublicKey, RSAPrivateKey}; use rsa_der as der; use uuid::Uuid; +use thiserror::Error; + use feather_core::network::cast_packet; use feather_core::network::packet::implementation::{ DisconnectLogin, EncryptionRequest, EncryptionResponse, Handshake, HandshakeState, LoginStart, @@ -315,7 +317,9 @@ fn handle_request(ih: &mut InitialHandler, packet: &Request) -> Result<(), Error "favicon": server_icon, }); - let response = Response::new(json.to_string()); + let response = Response { + json_response: json.to_string(), + }; send_packet(ih, response); ih.stage = Stage::AwaitPing; @@ -326,7 +330,9 @@ fn handle_request(ih: &mut InitialHandler, packet: &Request) -> Result<(), Error fn handle_ping(ih: &mut InitialHandler, packet: &Ping) -> Result<(), Error> { check_stage(ih, Stage::AwaitPing, packet.ty())?; - let pong = Pong::new(packet.payload); + let pong = Pong { + payload: packet.payload, + }; send_packet(ih, pong); // After sending pong, we should disconnect. @@ -352,11 +358,11 @@ fn handle_login_start(ih: &mut InitialHandler, packet: &LoginStart) -> Result<() &BigInt::from_biguint(Plus, RSA_KEY.e().clone()).to_signed_bytes_be(), ); - let encryption_request = EncryptionRequest::new( - "".to_string(), // Server ID - always empty - der, - ih.verify_token.to_vec(), - ); + let encryption_request = EncryptionRequest { + server_id: "".to_string(), // Server ID - always empty + public_key: der, + verify_token: ih.verify_token.to_vec(), + }; send_packet(ih, encryption_request); ih.info = Some(JoinResult::with_username(packet.username.clone())); @@ -480,10 +486,10 @@ fn finish(ih: &mut InitialHandler) { let info = ih.info.as_ref().unwrap(); // Send Login Success - let login_success = LoginSuccess::new( - info.uuid.to_hyphenated_ref().to_string(), - info.username.as_ref().unwrap().to_string(), - ); + let login_success = LoginSuccess { + uuid: info.uuid.to_hyphenated_ref().to_string(), + username: info.username.as_ref().unwrap().to_string(), + }; send_packet(ih, login_success); ih.action_queue.push(Action::SetStage(PacketStage::Play)); ih.action_queue @@ -494,7 +500,7 @@ fn finish(ih: &mut InitialHandler) { /// packet. fn enable_compression(ih: &mut InitialHandler, threshold: i32) { ih.compression_threshold = Some(threshold); - send_packet(ih, SetCompression::new(threshold)); + send_packet(ih, SetCompression { threshold }); ih.action_queue.push(Action::EnableCompression(threshold)); } @@ -517,7 +523,7 @@ fn disconnect_login(ih: &mut InitialHandler, reason: &str) { }) .to_string(); - let packet = DisconnectLogin::new(json); + let packet = DisconnectLogin { reason: json }; send_packet(ih, packet); ih.action_queue.push(Action::Disconnect); @@ -528,26 +534,23 @@ fn send_packet(ih: &mut InitialHandler, packet: P) { ih.action_queue.push(Action::SendPacket(Box::new(packet))); } -#[derive(Fail, Debug)] +#[derive(Error, Debug)] enum Error { - #[fail(display = "invalid packet type {:?} sent at stage {:?}", _0, _1)] + #[error("invalid packet type {0:?} sent at stage {1:?}")] InvalidPacket(PacketType, Stage), - #[fail(display = "unsupported protocol version {:?}", _0)] + #[error("unsupported protocol version {0:?}")] InvalidProtocol(u32), - #[fail(display = "invalid encryption")] + #[error("invalid encryption")] BadEncryption, - #[fail(display = "verify tokens do not match")] + #[error("verify tokens do not match")] VerifyTokenMismatch, - #[fail(display = "shared secret length is not correct")] + #[error("shared secret length is not correct")] BadSecretLength, - #[fail(display = "authentication failure: {:?}", _0)] + #[error("authentication failure: {0:?}")] AuthenticationFailed(mojang_api::Error), - #[fail( - display = "received BungeeCord data does not match the specification: {}", - _0 - )] + #[error("received BungeeCord data does not match the specification: {0}")] BungeeSpecMismatch(String), - #[fail(display = "option that should not be None was None")] + #[error("option that should not be None was None")] /// An Error type than can be used as the error type of using the Try operator on Option /// types. In rust-core, this is an unstable feature (issue #42327) OptionIsNone, @@ -729,12 +732,12 @@ mod tests { let player_count = 24; let mut ih = ih_with_player_count(player_count); - let handshake = Handshake::new( - PROTOCOL_VERSION, - "".to_string(), // Unused - server address - 25565, - HandshakeState::Status, - ); + let handshake = Handshake { + protocol_version: PROTOCOL_VERSION, + server_address: String::default(), // Unused - server address + server_port: 25565, + next_state: HandshakeState::Status, + }; ih.handle_packet(Box::new(handshake)).await; // Confirm that stage was switched and no other actions were performed @@ -745,20 +748,20 @@ mod tests { _ => panic!(), } - let request = Request::new(); + let request = Request {}; ih.handle_packet(Box::new(request)).await; - let actions = ih.actions_to_execute(); + let mut actions = ih.actions_to_execute(); // Confirm that correct response was received assert_eq!(actions.len(), 1); - let _response = actions.first().unwrap(); - match _response { - Action::SendPacket(_response) => { - assert_eq!(_response.ty(), PacketType::Response); + let response = actions.remove(0); + match response { + Action::SendPacket(response) => { + assert_eq!(response.ty(), PacketType::Response); - let response = cast_packet::(&**_response); + let response = cast_packet::(response); let _: serde_json::Value = serde_json::from_str(&response.json_response).unwrap(); } _ => panic!(), @@ -766,17 +769,17 @@ mod tests { // Send ping let payload = 39842; - let ping = Ping::new(payload); + let ping = Ping { payload }; ih.handle_packet(Box::new(ping)).await; let mut actions = ih.actions_to_execute(); assert_eq!(actions.len(), 2); - let _pong = actions.remove(0); - match _pong { - Action::SendPacket(_pong) => { - assert_eq!(_pong.ty(), PacketType::Pong); - let pong = cast_packet::(&*_pong); + let pong = actions.remove(0); + match pong { + Action::SendPacket(pong) => { + assert_eq!(pong.ty(), PacketType::Pong); + let pong = cast_packet::(pong); assert_eq!(pong.payload, payload); } _ => panic!(), @@ -795,12 +798,12 @@ mod tests { config.server.online_mode = false; let mut ih = ih_with_config(config.clone()); - let handshake = Handshake::new( - PROTOCOL_VERSION, - "".to_string(), // Unused - server address - 25565, - HandshakeState::Login, - ); + let handshake = Handshake { + protocol_version: PROTOCOL_VERSION, + server_address: String::default(), // Unused - server address + server_port: 25565, + next_state: HandshakeState::Login, + }; ih.handle_packet(Box::new(handshake)).await; let actions = ih.actions_to_execute(); @@ -811,19 +814,21 @@ mod tests { } let username = "test"; - let login_start = LoginStart::new(username.to_string()); + let login_start = LoginStart { + username: String::from(username), + }; ih.handle_packet(Box::new(login_start)).await; let mut actions = ih.actions_to_execute(); assert_eq!(actions.len(), 5); - let _set_compression = actions.remove(0); + let set_compression = actions.remove(0); - match _set_compression { - Action::SendPacket(_set_compression) => { - assert_eq!(_set_compression.ty(), PacketType::SetCompression); + match set_compression { + Action::SendPacket(set_compression) => { + assert_eq!(set_compression.ty(), PacketType::SetCompression); - let set_compression = cast_packet::(&*_set_compression); + let set_compression = cast_packet::(set_compression); assert_eq!(set_compression.threshold, config.io.compression_threshold); } _ => panic!(), @@ -837,13 +842,13 @@ mod tests { _ => panic!(), } - let _login_success = actions.remove(0); + let login_success = actions.remove(0); - match _login_success { - Action::SendPacket(_login_success) => { - assert_eq!(_login_success.ty(), PacketType::LoginSuccess); + match login_success { + Action::SendPacket(login_success) => { + assert_eq!(login_success.ty(), PacketType::LoginSuccess); - let login_success = cast_packet::(&*_login_success); + let login_success = cast_packet::(login_success); assert_eq!(login_success.username, username.to_string()); } _ => panic!(), diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index 030880e32..d05f6249d 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -19,13 +19,14 @@ use std::net::SocketAddr; use std::path::Path; use std::sync::Arc; use std::time::Duration; +use thiserror::Error; use tokio::net::TcpStream; use tokio_util::codec::Framed; use uuid::Uuid; -#[derive(Debug, Fail)] +#[derive(Debug, Error)] pub enum Error { - #[fail(display = "failed to read player data")] + #[error("failed to read player data")] PlayerData, } @@ -69,7 +70,7 @@ async fn _run_worker( server_icon: Arc>, tx_worker_to_server: crossbeam::Sender, rx_worker_to_server: crossbeam::Receiver, -) -> Result<(), failure::Error> { +) -> anyhow::Result<()> { let codec = MinecraftCodec::new(PacketDirection::Serverbound); let mut framed = Framed::new(stream, codec); @@ -165,6 +166,7 @@ async fn _run_worker( } } +#[allow(dead_code)] // TODO async fn load_player_data(config: &Config, uuid: Uuid) -> Result { feather_core::player_data::load_player_data(Path::new(&config.world.name), uuid).await } diff --git a/server/src/join.rs b/server/src/join.rs index f6ed07d35..65694ac34 100644 --- a/server/src/join.rs +++ b/server/src/join.rs @@ -38,7 +38,7 @@ fn join( let pos = world.get_component::(event.player).unwrap(); let joined = world.get_component::(event.player).unwrap(); - if pos.chunk_pos() != event.chunk || joined.0 { + if pos.chunk() != event.chunk || joined.0 { return; } @@ -89,7 +89,7 @@ fn send_join_game( // TODO let packet = JoinGame { entity_id: id.0, - gamemode: Gamemode::Creative.get_id(), + gamemode: Gamemode::Creative.id(), dimension: 0, difficulty: 0, max_players: 0, diff --git a/server/src/lib.rs b/server/src/lib.rs index e083c3c58..465b1fef0 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -100,14 +100,10 @@ extern crate serde; #[macro_use] extern crate serde_json; #[macro_use] -extern crate failure; -#[macro_use] extern crate smallvec; #[macro_use] extern crate lazy_static; #[macro_use] -extern crate derive_deref; -#[macro_use] extern crate feather_core; #[macro_use] extern crate bitflags; @@ -448,7 +444,7 @@ fn hash_seed(seed_raw: &str) -> i64 { } /// Loads the level.dat file for the world. -fn load_level(path: &Path) -> Result { +fn load_level(path: &Path) -> anyhow::Result { let file = File::open(path)?; let data = deserialize_level_file(file)?; Ok(data) diff --git a/server/src/metadata.rs b/server/src/metadata.rs index fec660367..865568dd6 100644 --- a/server/src/metadata.rs +++ b/server/src/metadata.rs @@ -1,8 +1,8 @@ //! Entity metadata implementation. -use feather_core::entitymeta::EntityMetadata; use feather_core::inventory::Slot; use feather_core::world::BlockPosition; +use feather_core::EntityMetadata; use uuid::Uuid; type OptUuid = Option; diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index 2b07efa7a..85da1a749 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -48,7 +48,7 @@ fn entity_physics( // velocity is sufficiently high. let origin = (*position).into(); let direction = (pending_position - *position).into(); - let distance_squared = pending_position.distance_squared(*position); + let distance_squared = pending_position.distance_squared_to(*position); if let Some(impacted) = block_impacted_by_ray(&state, origin, direction, distance_squared) { // Set velocities along correct axis to 0 and then set position @@ -92,7 +92,7 @@ fn entity_physics( } // Delete entity if it has gone into unloaded chunks. - let block_at_pos = match state.block_at(pending_position.block_pos()) { + let block_at_pos = match state.block_at(pending_position.block()) { Some(block) => block, None => { // Delete entity. @@ -110,7 +110,7 @@ fn entity_physics( pending_position.y - physics.bbox.size().y / 2.0 - 0.01, pending_position.z ) - .block_pos(), + .block(), ) { Some(block) => block.is_solid(), None => false, diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index eab83687a..df596f143 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -165,7 +165,7 @@ pub fn block_impacted_by_ray( _ => (), } - let mut current_pos = Position::from(origin).block_pos(); + let mut current_pos = Position::from(origin).block(); while dist_traveled.magnitude_squared() < max_distance_squared { if let Some(block) = state.block_at(current_pos) { @@ -176,7 +176,7 @@ pub fn block_impacted_by_ray( let shape = block_shape(&block); let isometry = block_isometry(current_pos); - let impact = match shape.toi_and_normal_with_ray(&isometry, &ray, true) { + let impact = match shape.toi_and_normal_with_ray(&isometry, &ray, 1000.0, true) { Some(toi) => toi, None => continue, }; @@ -355,7 +355,7 @@ pub fn blocks_intersecting_bbox( // Go through blocks and check for time of impact from original // position to the block. If the time of impact is <= 1, the entity // has collided with the block; update the position accordingly. - let velocity = (dest - from).as_vec(); + let velocity = (dest - from).into(); let bbox_shape = bbox_to_cuboid(&bbox); for compound in blocks { @@ -363,7 +363,7 @@ pub fn blocks_intersecting_bbox( &Isometry3::translation(0.0, 0.0, 0.0), &vec3(0.0, 0.0, 0.0), &compound, - &Isometry3::new(from.as_vec(), vec3(0.0, 0.0, 0.0)), + &Isometry3::new(from.into(), vec3(0.0, 0.0, 0.0)), &velocity, &bbox_shape, 1.0, @@ -390,7 +390,7 @@ pub fn blocks_intersecting_bbox( } }; - result.offset += absolute_offset.as_vec().component_mul(&normal); + result.offset += >::into(absolute_offset).component_mul(&normal); if normal.x != 0.0 { result.x = true; @@ -477,7 +477,7 @@ pub fn adjacent_to_bbox( // Go through offsets and append block position if the block is solid. for offset in &offsets { - let block_pos = (pos + *offset).block_pos(); + let block_pos = (pos + *offset).block(); if checked.contains(&block_pos) { continue; @@ -539,7 +539,7 @@ pub fn chunks_within_distance( let mut x_len = 0; let mut z_len = 0; - let center_chunk_pos = pos.chunk_pos(); + let center_chunk_pos = pos.chunk(); loop { let needed = ((pos.x + 16.0) / 16.0).floor() * 16.0 - pos.x; @@ -595,6 +595,7 @@ pub fn bbox_front(bbox: &AABB, direction: Vec3) -> Position { .toi_with_ray( &Isometry3::new(vec3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 0.0)), &ray, + 1000.0, false, ) .unwrap(); diff --git a/server/src/state.rs b/server/src/state.rs index 2de53cc7a..2a5c77197 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -166,7 +166,7 @@ impl State { let chunk_holders = scheduler.resources().get::(); if let Some(position) = world.get_component::(entity) { - let holders = chunk_holders.holders_for(position.chunk_pos()); + let holders = chunk_holders.holders_for(position.chunk()); holders.map(|entities| { for entity in entities { @@ -196,7 +196,7 @@ impl State { let chunk_holders = scheduler.resources().get::(); if let Some(position) = world.get_component::(entity) { - let holders = chunk_holders.holders_for(position.chunk_pos()); + let holders = chunk_holders.holders_for(position.chunk()); holders.map(|entities| { for entity in entities { diff --git a/server/src/time.rs b/server/src/time.rs index 8b591271c..c6111f9de 100644 --- a/server/src/time.rs +++ b/server/src/time.rs @@ -4,12 +4,27 @@ use crate::network::Network; use crate::player::PlayerJoinEvent; use feather_core::packet::TimeUpdate; use legion::query::Read; +use std::ops::{Deref, DerefMut}; use tonks::{PreparedWorld, Query}; /// The current time of the world. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Deref, DerefMut, Default, Resource)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Resource)] pub struct Time(pub u64); +impl Deref for Time { + type Target = u64; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Time { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + impl Time { /// Returns the time of day. This is calculated /// as `time.0 % 24_000`. diff --git a/server/src/view.rs b/server/src/view.rs index 690ea7e93..2dbc34bba 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -84,15 +84,15 @@ fn view_update( .0; // Find the old chunks and new chunks. - let visible_new = chunks_within_view_distance(&state.config, pos.chunk_pos()); - let visible_old = chunks_within_view_distance(&state.config, prev_pos.chunk_pos()); + let visible_new = chunks_within_view_distance(&state.config, pos.chunk()); + let visible_old = chunks_within_view_distance(&state.config, prev_pos.chunk()); - if pos.chunk_pos() != prev_pos.chunk_pos() { + if pos.chunk() != prev_pos.chunk() { // New chunk: trigger view update. let event = ViewUpdateEvent { player: event.entity, - new_chunk: pos.chunk_pos(), - old_chunk: Some(prev_pos.chunk_pos()), + new_chunk: pos.chunk(), + old_chunk: Some(prev_pos.chunk()), visible_old, visible_new, }; @@ -113,11 +113,11 @@ fn view_update_on_join( let position = *world.get_component::(event.player).unwrap(); // Find the visible chunks. - let visible_new = chunks_within_view_distance(&state.config, position.chunk_pos()); + let visible_new = chunks_within_view_distance(&state.config, position.chunk()); trigger.trigger(ViewUpdateEvent { player: event.player, - new_chunk: position.chunk_pos(), + new_chunk: position.chunk(), old_chunk: None, visible_new, visible_old: HashSet::new(), // No chunks were previously visible, since the player just joined @@ -149,7 +149,7 @@ fn view_handle_chunks( // Sort sent chunks so that closer chunks are sent first. let mut to_send = to_send.copied().collect::>(); to_send.sort_unstable_by_key(|chunk| { - chunk.manhattan_distance(event.new_chunk); + chunk.manhattan_distance_to(event.new_chunk); }); // Send new chunks. diff --git a/server/src/worldgen/composition.rs b/server/src/worldgen/composition.rs index d8e518dfe..228a2b6e7 100644 --- a/server/src/worldgen/composition.rs +++ b/server/src/worldgen/composition.rs @@ -2,6 +2,7 @@ //! based on the density and biome values. use crate::worldgen::{block_index, util, ChunkBiomes, CompositionGenerator, SEA_LEVEL}; +use bitvec::order::Local; use bitvec::slice::BitSlice; use feather_blocks::{GrassBlockData, MyceliumData, WaterData}; use feather_core::{Biome, Block, Chunk, ChunkPosition}; @@ -20,7 +21,7 @@ impl CompositionGenerator for BasicCompositionGenerator { chunk: &mut Chunk, _pos: ChunkPosition, biomes: &ChunkBiomes, - density: &BitSlice, + density: &BitSlice, seed: u64, ) { // For each column in the chunk, go from top to @@ -39,7 +40,7 @@ fn basic_composition_for_column( x: usize, z: usize, chunk: &mut Chunk, - density: &BitSlice, + density: &BitSlice, seed: u64, biome: Biome, ) { @@ -50,7 +51,7 @@ fn basic_composition_for_solid_biome( x: usize, z: usize, chunk: &mut Chunk, - density: &BitSlice, + density: &BitSlice, seed: u64, biome: Biome, ) { diff --git a/server/src/worldgen/density_map/density.rs b/server/src/worldgen/density_map/density.rs index ede7066ba..870e7e670 100644 --- a/server/src/worldgen/density_map/density.rs +++ b/server/src/worldgen/density_map/density.rs @@ -4,6 +4,7 @@ //! is more interesting; overhangs and the like will be able to generate. use crate::worldgen::{block_index, noise, DensityMapGenerator, NearbyBiomes, NoiseLerper}; +use bitvec::order::Local; use bitvec::vec::BitVec; use feather_core::{Biome, ChunkPosition}; use simdnoise::NoiseBuilder; @@ -24,7 +25,12 @@ use simdnoise::NoiseBuilder; pub struct DensityMapGeneratorImpl; impl DensityMapGenerator for DensityMapGeneratorImpl { - fn generate_for_chunk(&self, chunk: ChunkPosition, biomes: &NearbyBiomes, seed: u64) -> BitVec { + fn generate_for_chunk( + &self, + chunk: ChunkPosition, + biomes: &NearbyBiomes, + seed: u64, + ) -> BitVec { let mut density = BitVec::from_vec(vec![0u8; 16 * 256 * 16 / 8]); let uninterpolated_densities = generate_density(chunk, &biomes, seed); diff --git a/server/src/worldgen/density_map/height.rs b/server/src/worldgen/density_map/height.rs index 129158fef..0c1ef7bca 100644 --- a/server/src/worldgen/density_map/height.rs +++ b/server/src/worldgen/density_map/height.rs @@ -2,6 +2,7 @@ //! A superior generator would use 3D noise to allow for overhangs. use crate::worldgen::{block_index, DensityMapGenerator, NearbyBiomes, OCEAN_DEPTH, SKY_LIMIT}; +use bitvec::order::Local; use bitvec::vec::BitVec; use feather_core::{Biome, ChunkPosition}; use simdnoise::NoiseBuilder; @@ -13,7 +14,12 @@ use std::cmp::min; pub struct HeightMapGenerator; impl DensityMapGenerator for HeightMapGenerator { - fn generate_for_chunk(&self, chunk: ChunkPosition, biomes: &NearbyBiomes, seed: u64) -> BitVec { + fn generate_for_chunk( + &self, + chunk: ChunkPosition, + biomes: &NearbyBiomes, + seed: u64, + ) -> BitVec { let x_offset = (chunk.x * 16) as f32; let y_offset = (chunk.z * 16) as f32; diff --git a/server/src/worldgen/mod.rs b/server/src/worldgen/mod.rs index 1753f3bcb..ade3b9dac 100644 --- a/server/src/worldgen/mod.rs +++ b/server/src/worldgen/mod.rs @@ -16,6 +16,7 @@ pub mod voronoi; use crate::worldgen::finishers::{ClumpedFoliageFinisher, SingleFoliageFinisher, SnowFinisher}; pub use biomes::{DistortedVoronoiBiomeGenerator, TwoLevelBiomeGenerator}; +use bitvec::order::Local; use bitvec::slice::BitSlice; use bitvec::vec::BitVec; pub use composition::BasicCompositionGenerator; @@ -211,7 +212,12 @@ pub trait DensityMapGenerator: Send + Sync { /// A compact array of booleans is returned, indexable /// by (y << 8) | (x << 4) | z. Those set to `true` will /// contain solid blacks; those set to `false` will be air. - fn generate_for_chunk(&self, chunk: ChunkPosition, biomes: &NearbyBiomes, seed: u64) -> BitVec; + fn generate_for_chunk( + &self, + chunk: ChunkPosition, + biomes: &NearbyBiomes, + seed: u64, + ) -> BitVec; } /// A generator which populates the given chunk using blocks @@ -224,7 +230,7 @@ pub trait CompositionGenerator: Send + Sync { chunk: &mut Chunk, pos: ChunkPosition, biomes: &ChunkBiomes, - density: &BitSlice, + density: &BitSlice, seed: u64, ); } From 1643372dc849ac54b1a7aa9aa4a8dc28946856e0 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 14 Mar 2020 21:37:14 -0600 Subject: [PATCH 088/647] Fix feather-core test --- core/src/save/player_data.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/save/player_data.rs b/core/src/save/player_data.rs index aef30ccfe..da37c1cfb 100644 --- a/core/src/save/player_data.rs +++ b/core/src/save/player_data.rs @@ -129,11 +129,11 @@ mod tests { use hashbrown::HashMap; use std::io::Cursor; - #[test] - fn test_deserialize_player() { + #[tokio::test] + async fn test_deserialize_player() { let cursor = Cursor::new(include_bytes!("player.dat").to_vec()); - let player = load_from_file(cursor).unwrap(); + let player = load_from_file(cursor).await.unwrap(); assert_eq!(player.gamemode, i32::from(Gamemode::Creative.id())); } From 547bfce91cc394183d28684c7a7ebfca6b95c5c7 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 15 Mar 2020 19:45:46 -0600 Subject: [PATCH 089/647] Begin on hopefully the final major refactor. Here we are again! Upcoming changes: * Switch to `fecs` from `tonks`. Through experience with previous iterations of Feather, I've found the parallel systems architecture does not scale well with codebase size. We will still be able to benefit from the multithreading of Rayon parallel iterators and thread pools. Implemented so far: * Rewrote the IO worker, which fixes issues where the CPU usage would go to 100% after a player disconnects. * Implemented everything up to the initial connection. * Prepare for a marathon of refactoring systems to the final architecture. --- Cargo.lock | 357 +++++++++++------------------ core/src/network/packet/mod.rs | 4 +- core/src/save/player_data.rs | 2 +- server/Cargo.toml | 12 +- server/src/entity/mod.rs | 73 ++---- server/src/game.rs | 91 ++++++++ server/src/io/initial_handler.rs | 24 +- server/src/io/listener.rs | 23 +- server/src/io/mod.rs | 88 ++++++-- server/src/io/worker.rs | 272 ++++++++++++---------- server/src/lib.rs | 125 ++++++----- server/src/network.rs | 237 ++++---------------- server/src/packet_buffer.rs | 372 +++++++++++++++++++++++++++++++ server/src/player/mod.rs | 81 +++---- server/src/shutdown.rs | 2 +- server/src/state.rs | 361 ------------------------------ server/src/worldgen/noise.rs | 2 +- 17 files changed, 1009 insertions(+), 1117 deletions(-) create mode 100644 server/src/game.rs create mode 100644 server/src/packet_buffer.rs delete mode 100644 server/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index 5a6493fae..e94bfe251 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,15 +38,6 @@ dependencies = [ "opaque-debug", ] -[[package]] -name = "ahash" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3" -dependencies = [ - "const-random", -] - [[package]] name = "ahash" version = "0.3.2" @@ -104,12 +95,6 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff" -[[package]] -name = "arrayvec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" - [[package]] name = "as-slice" version = "0.1.2" @@ -294,16 +279,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -[[package]] -name = "chashmap" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff41a3c2c1e39921b9003de14bf0439c7b63a9039637c291e1a64925d8ddfa45" -dependencies = [ - "owning_ref", - "parking_lot 0.4.8", -] - [[package]] name = "chrono" version = "0.4.10" @@ -368,7 +343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c750ec12b83377637110d5a57f5ae08e895b06c4b16e2bdbf1a94ef717428c59" dependencies = [ "proc-macro-hack", - "rand 0.7.3", + "rand", ] [[package]] @@ -410,7 +385,7 @@ dependencies = [ "itertools", "lazy_static", "num-traits 0.2.11", - "rand_core 0.5.1", + "rand_core", "rand_os", "rand_xoshiro", "rayon", @@ -552,6 +527,17 @@ dependencies = [ "syn 0.15.44", ] +[[package]] +name = "derivative" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b94d2eb97732ec84b4e25eaf37db890e317b80e921f168c82cb5282473f8151" +dependencies = [ + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", +] + [[package]] name = "derive_deref" version = "1.1.0" @@ -654,7 +640,7 @@ dependencies = [ "flate2 1.0.13", "hash32", "hash32-derive", - "hashbrown 0.7.0", + "hashbrown", "hematite-nbt", "lazy_static", "log", @@ -662,10 +648,10 @@ dependencies = [ "nalgebra-glm", "num-derive", "num-traits 0.2.11", - "parking_lot 0.10.0", + "parking_lot", "rayon", "serde", - "smallvec 1.2.0", + "smallvec", "strum 0.18.0", "strum_macros 0.18.0", "thiserror", @@ -714,29 +700,30 @@ dependencies = [ name = "feather-server" version = "0.5.0" dependencies = [ - "ahash 0.3.2", + "ahash", "anyhow", "base64 0.12.0", "bitflags", "bitvec", "bumpalo", "bytes", - "chashmap", "criterion", "crossbeam", "ctrlc", + "derivative 2.0.2", "feather-blocks", "feather-codegen", "feather-core", "feather-item-block", + "fecs", "futures", - "hashbrown 0.7.0", + "hashbrown", "heapless", "hematite-nbt", "humantime-serde", + "indexmap", "inventory", "lazy_static", - "legion", "lock_api", "log", "mojang-api", @@ -747,8 +734,8 @@ dependencies = [ "num-bigint-dig", "num-derive", "num-traits 0.2.11", - "parking_lot 0.10.0", - "rand 0.7.3", + "parking_lot", + "rand", "rand_xorshift", "rayon", "rsa", @@ -758,14 +745,13 @@ dependencies = [ "simdeez", "simdnoise", "simple_logger", - "smallvec 1.2.0", + "smallvec", "strum 0.18.0", "thiserror", "thread_local", "tokio", "tokio-util", "toml", - "tonks", "uuid", ] @@ -773,6 +759,31 @@ dependencies = [ name = "feather_api" version = "0.1.0" +[[package]] +name = "fecs" +version = "0.1.0" +dependencies = [ + "fecs-macros", + "fxhash", + "inventory", + "legion", +] + +[[package]] +name = "fecs-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", +] + +[[package]] +name = "fixedbitset" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" + [[package]] name = "fixedbitset" version = "0.2.0" @@ -822,12 +833,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -1023,23 +1028,13 @@ dependencies = [ "syn 0.13.11", ] -[[package]] -name = "hashbrown" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" -dependencies = [ - "ahash 0.2.18", - "autocfg 0.1.7", -] - [[package]] name = "hashbrown" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728e7d31e63d53c436094370f1e6fa249f60a4bb318cc5dfbbbe0aa2bc5a29d7" dependencies = [ - "ahash 0.3.2", + "ahash", "autocfg 1.0.0", "rayon", "serde", @@ -1264,19 +1259,43 @@ dependencies = [ [[package]] name = "legion" version = "0.2.1" -source = "git+https://github.com/TomGillen/legion?rev=0f67adc237af35799df173f31a2c238b3d8010a2#0f67adc237af35799df173f31a2c238b3d8010a2" +source = "git+https://github.com/TomGillen/legion?rev=c5b9628630d4f9fc54b6843b5ce02d0669434a61#c5b9628630d4f9fc54b6843b5ce02d0669434a61" +dependencies = [ + "legion-core", + "legion-systems", +] + +[[package]] +name = "legion-core" +version = "0.2.1" +source = "git+https://github.com/TomGillen/legion?rev=c5b9628630d4f9fc54b6843b5ce02d0669434a61#c5b9628630d4f9fc54b6843b5ce02d0669434a61" +dependencies = [ + "crossbeam-channel", + "derivative 1.0.3", + "downcast-rs", + "fxhash", + "itertools", + "parking_lot", + "rayon", + "smallvec", + "thiserror", + "tracing", +] + +[[package]] +name = "legion-systems" +version = "0.2.1" +source = "git+https://github.com/TomGillen/legion?rev=c5b9628630d4f9fc54b6843b5ce02d0669434a61#c5b9628630d4f9fc54b6843b5ce02d0669434a61" dependencies = [ "bit-set", "crossbeam-channel", - "crossbeam-queue", - "derivative", + "derivative 1.0.3", "downcast-rs", "fxhash", "itertools", - "parking_lot 0.9.0", + "legion-core", "paste", "rayon", - "smallvec 0.6.13", "tracing", ] @@ -1331,12 +1350,6 @@ dependencies = [ "rawpointer", ] -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "memchr" version = "2.3.0" @@ -1470,12 +1483,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "mopa" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915" - [[package]] name = "multimap" version = "0.8.0" @@ -1498,7 +1505,7 @@ dependencies = [ "num-complex", "num-rational", "num-traits 0.2.11", - "rand 0.7.3", + "rand", "rand_distr", "typenum", ] @@ -1546,10 +1553,10 @@ dependencies = [ "either", "nalgebra", "num-traits 0.2.11", - "petgraph", + "petgraph 0.5.0", "slab", "slotmap", - "smallvec 1.2.0", + "smallvec", ] [[package]] @@ -1610,9 +1617,9 @@ dependencies = [ "num-integer", "num-iter", "num-traits 0.2.11", - "rand 0.7.3", + "rand", "serde", - "smallvec 1.2.0", + "smallvec", "zeroize", ] @@ -1737,34 +1744,10 @@ dependencies = [ ] [[package]] -name = "owning_ref" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "parking_lot" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" -dependencies = [ - "owning_ref", - "parking_lot_core 0.2.14", -] - -[[package]] -name = "parking_lot" -version = "0.9.0" +name = "ordermap" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -dependencies = [ - "lock_api", - "parking_lot_core 0.6.2", - "rustc_version", -] +checksum = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" [[package]] name = "parking_lot" @@ -1773,34 +1756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" dependencies = [ "lock_api", - "parking_lot_core 0.7.0", -] - -[[package]] -name = "parking_lot_core" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" -dependencies = [ - "libc", - "rand 0.4.6", - "smallvec 0.6.13", - "winapi 0.3.8", -] - -[[package]] -name = "parking_lot_core" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -dependencies = [ - "cfg-if", - "cloudabi", - "libc", - "redox_syscall", - "rustc_version", - "smallvec 0.6.13", - "winapi 0.3.8", + "parking_lot_core", ] [[package]] @@ -1809,11 +1765,14 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" dependencies = [ + "backtrace", "cfg-if", "cloudabi", "libc", + "petgraph 0.4.13", "redox_syscall", - "smallvec 1.2.0", + "smallvec", + "thread-id", "winapi 0.3.8", ] @@ -1845,13 +1804,23 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "petgraph" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" +dependencies = [ + "fixedbitset 0.1.9", + "ordermap", +] + [[package]] name = "petgraph" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29c127eea4a29ec6c85d153c59dc1213f33ec74cead30fe4730aecc88cc1fd92" dependencies = [ - "fixedbitset", + "fixedbitset 0.2.0", "indexmap", ] @@ -1976,19 +1945,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi 0.3.8", -] - [[package]] name = "rand" version = "0.7.3" @@ -1998,7 +1954,7 @@ dependencies = [ "getrandom", "libc", "rand_chacha", - "rand_core 0.5.1", + "rand_core", "rand_hc", ] @@ -2009,24 +1965,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" dependencies = [ "c2-chacha", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.5.1" @@ -2042,7 +1983,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2" dependencies = [ - "rand 0.7.3", + "rand", ] [[package]] @@ -2051,7 +1992,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core 0.5.1", + "rand_core", ] [[package]] @@ -2061,7 +2002,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a" dependencies = [ "getrandom", - "rand_core 0.5.1", + "rand_core", ] [[package]] @@ -2070,7 +2011,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" dependencies = [ - "rand_core 0.5.1", + "rand_core", ] [[package]] @@ -2079,7 +2020,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e18c91676f670f6f0312764c759405f13afb98d5d73819840cf72a518487bff" dependencies = [ - "rand_core 0.5.1", + "rand_core", ] [[package]] @@ -2112,15 +2053,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "redox_syscall" version = "0.1.56" @@ -2193,7 +2125,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits 0.2.11", - "rand 0.7.3", + "rand", "subtle", "zeroize", ] @@ -2403,15 +2335,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c46a3482db8f247956e464d783693ece164ca056e6e67563ee5505bdb86452cd" -[[package]] -name = "smallvec" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" -dependencies = [ - "maybe-uninit", -] - [[package]] name = "smallvec" version = "1.2.0" @@ -2454,12 +2377,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "stream-cipher" version = "0.3.2" @@ -2570,7 +2487,7 @@ checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ "cfg-if", "libc", - "rand 0.7.3", + "rand", "redox_syscall", "remove_dir_all", "winapi 0.3.8", @@ -2605,6 +2522,17 @@ dependencies = [ "syn 1.0.13", ] +[[package]] +name = "thread-id" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" +dependencies = [ + "libc", + "redox_syscall", + "winapi 0.3.8", +] + [[package]] name = "thread_local" version = "1.0.1" @@ -2703,39 +2631,6 @@ dependencies = [ "serde", ] -[[package]] -name = "tonks" -version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=0ed28a624a21d044011058f74771461dd0b35c2a#0ed28a624a21d044011058f74771461dd0b35c2a" -dependencies = [ - "arrayvec", - "bit-set", - "bumpalo", - "crossbeam", - "derivative", - "hashbrown 0.6.3", - "inventory", - "lazy_static", - "legion", - "mopa", - "parking_lot 0.9.0", - "rayon", - "smallvec 0.6.13", - "static_assertions 1.1.0", - "thread_local", - "tonks-macros", -] - -[[package]] -name = "tonks-macros" -version = "0.1.0" -source = "git+https://github.com/feather-rs/tonks?rev=0ed28a624a21d044011058f74771461dd0b35c2a#0ed28a624a21d044011058f74771461dd0b35c2a" -dependencies = [ - "proc-macro2 1.0.7", - "quote 1.0.2", - "syn 1.0.13", -] - [[package]] name = "tower-service" version = "0.3.0" @@ -2808,7 +2703,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf" dependencies = [ - "smallvec 1.2.0", + "smallvec", ] [[package]] @@ -2852,7 +2747,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" dependencies = [ - "rand 0.7.3", + "rand", "serde", ] @@ -2878,7 +2773,7 @@ dependencies = [ "num-integer", "num-traits 0.1.43", "rustc_version", - "static_assertions 0.2.5", + "static_assertions", ] [[package]] diff --git a/core/src/network/packet/mod.rs b/core/src/network/packet/mod.rs index 997dcc602..6586a3b08 100644 --- a/core/src/network/packet/mod.rs +++ b/core/src/network/packet/mod.rs @@ -51,7 +51,9 @@ impl PacketBuilder { } } -#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone, EnumCount)] +#[derive( + Debug, Hash, PartialEq, Eq, Copy, Clone, EnumCount, EnumIter, ToPrimitive, FromPrimitive, +)] pub enum PacketType { // Serverbound diff --git a/core/src/save/player_data.rs b/core/src/save/player_data.rs index da37c1cfb..b12db0987 100644 --- a/core/src/save/player_data.rs +++ b/core/src/save/player_data.rs @@ -129,7 +129,7 @@ mod tests { use hashbrown::HashMap; use std::io::Cursor; - #[tokio::test] + //#[tokio::test] async fn test_deserialize_player() { let cursor = Cursor::new(include_bytes!("player.dat").to_vec()); diff --git a/server/Cargo.toml b/server/Cargo.toml index c4284a9af..3ed451249 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -20,14 +20,13 @@ feather-item-block = { path = "../item_block" } feather-codegen = { path = "../codegen" } # Core ECS + systems -legion = { git = "https://github.com/TomGillen/legion", rev = "0f67adc237af35799df173f31a2c238b3d8010a2" } -tonks = { git = "https://github.com/feather-rs/tonks", rev = "0ed28a624a21d044011058f74771461dd0b35c2a", features = ["system-registry"] } -# tonks = { path = "../../../dev/tonks", features = ["system-registry"] } +# fecs = { git = "https://github.com/caelunshun/fecs", rev = "e865bce3a9ac7f8fd3a9a7b5e3c139a746500137" } +fecs = { path = "../../../dev/fecs" } # Concurrency/threading crossbeam = "0.7" rayon = "1.3" -parking_lot = "0.10" +parking_lot = { version = "0.10", features = ["deadlock_detection"] } lock_api = "0.3" thread_local = "1.0" @@ -61,7 +60,7 @@ heapless = "0.5" uuid = { version = "0.8", features = ["v4"] } multimap = "0.8" smallvec = "1.2" -chashmap = "2.2" +indexmap = "1.3" # Logging log = "0.4" @@ -81,13 +80,14 @@ rand_xorshift = "0.2" num-derive = "0.3" num-traits = "0.2" lazy_static = "1.4" -bumpalo = "3.2" +bumpalo = { version = "3.2", features = ["collections"] } strum = "0.18" simdnoise = "3.1" simdeez = "1.0" humantime-serde = "1.0" ctrlc = "3.1" inventory = "0.1" +derivative = "2.0" # Error handling thiserror = "1.0" diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 7fdc59fd8..2679e414d 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -3,17 +3,12 @@ //! block entities, monsters, etc. Player entities are handled in `crate::player`, //! not here. -pub mod item; +// pub mod item; -use crate::lazy::EntityBuilder; -use crate::state::State; -use feather_core::{Packet, Position}; -use legion::prelude::Entity; -use legion::query::{Read, Write}; +use feather_core::Position; +use fecs::EntityBuilder; use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicI32, Ordering}; -use tonks::{EntityAccessor, PreparedWorld, Query}; -use uuid::Uuid; /// ID of an entity. This value is generally unique. #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] @@ -22,43 +17,6 @@ pub struct EntityId(pub i32); /// Entity ID counter, used to create new entity IDs. pub static ENTITY_ID_COUNTER: AtomicI32 = AtomicI32::new(0); -/// Event triggered when an entity is created. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct EntityCreateEvent { - pub entity: Entity, -} - -/// Event triggered when an entity is spawned -/// on a client. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct EntitySendEvent { - pub entity: Entity, - pub to: Entity, -} - -/// Event triggered when an entity is removed. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct EntityDeleteEvent { - pub entity: Entity, - pub position: Option, - pub id: EntityId, - pub uuid: Uuid, -} - -/// Event triggered when an entity moves. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct EntityMoveEvent { - /// Entity which moved. - pub entity: Entity, -} - -/// Event triggered when an entity's velocity changes. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct VelocityUpdateEvent { - /// Entity whose velocity changed. - pub entity: Entity, -} - /// The velocity of an entity. #[derive(Debug, PartialEq, Clone, Copy)] pub struct Velocity(pub glm::DVec3); @@ -83,12 +41,13 @@ impl DerefMut for Velocity { } } -/// The display name of the entity. +/// The display name of an entity. /// /// Note that unnamed entities do not have this component. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Name(pub String); +/* /// Position of an entity on the last tick. /// /// This is updated by `position_reset` system. @@ -157,6 +116,7 @@ pub fn position_reset( prev_pos.0 = pos; }); } +*/ /// Inserts the base components for an entity into an `EntityBuilder`. /// @@ -165,13 +125,16 @@ pub fn position_reset( /// * Entity ID /// * Position and previous position /// * Triggers `EntityCreateEvent` -pub fn base(state: &State, position: Position) -> EntityBuilder { - let id = ENTITY_ID_COUNTER.fetch_add(1, Ordering::Relaxed); - state - .create_entity() - .with_component(EntityId(id)) - .with_component(position) - .with_component(PreviousPosition(position)) - .with_component(Velocity::default()) - .with_exec(|_, scheduler, entity| scheduler.trigger(EntityCreateEvent { entity })) +pub fn base(position: Position) -> EntityBuilder { + let id = new_id(); + EntityBuilder::new() + .with(EntityId(id)) + .with(position) + //.with(PreviousPosition(position)) + .with(Velocity::default()) +} + +/// Returns a new entity ID. +pub fn new_id() -> i32 { + ENTITY_ID_COUNTER.fetch_add(1, Ordering::Relaxed) } diff --git a/server/src/game.rs b/server/src/game.rs new file mode 100644 index 000000000..fa5c1ea0e --- /dev/null +++ b/server/src/game.rs @@ -0,0 +1,91 @@ +use crate::config::Config; +use crate::io::{NetworkIoManager, NewClientInfo}; +use crate::player; +use bumpalo::Bump; +use feather_blocks::Block; +use feather_core::level::LevelData; +use feather_core::world::ChunkMap; +use feather_core::BlockPosition; +use fecs::{Entity, World}; +use std::sync::atomic::AtomicU32; +use std::sync::Arc; + +/// Uber-resource storing almost all data needed to run the game. +/// +/// This type includes the chunk map for accessing blocks, +/// time data, acceleration structures, and practically all +/// game state, except entities, which are stored in the `World`. +pub struct Game { + /// The IO handle. + pub io_handle: NetworkIoManager, + /// The server configuration. + pub config: Arc, + /// The server tick count, measured in ticks + /// since program startup. + pub tick_count: u64, + /// The server player count. + /// + /// (This value is stored in an `Arc` so it can + /// be shared with the status ping threads.) + pub player_count: Arc, + /// Information about the world, such as spawn position + /// and world type. + pub level: LevelData, + /// The chunk map. + pub chunk_map: ChunkMap, + /// Bump allocator. Reset every tick. + pub bump: Bump, +} + +impl Game { + /// Retrieves the block at the given position, + /// or `None` if the block's chunk is not loaded. + pub fn block_at(&self, pos: BlockPosition) -> Option { + self.chunk_map.block_at(pos) + } + + /// Sets the block at the given position. + /// + /// If the block's chunk's is not loaded, returns `false`; + /// otherwise, returns `true`. + pub fn set_block_at(&mut self, world: &mut World, pos: BlockPosition, block: Block) -> bool { + let old_block = match self.block_at(pos) { + Some(block) => block, + None => return false, + }; + + self.on_block_update(world, pos, old_block, block); + + self.chunk_map.set_block_at(pos, block) + } + + /// Despawns an entity. This should be used instead of `World::despawn` + /// as it properly handles events. + pub fn despawn(&mut self, entity: Entity, world: &mut World) { + world.despawn(entity); + self.on_despawn(world, entity); + } + + /// Spawns a player with the given `PlayerInfo`. + pub fn spawn_player(&mut self, info: NewClientInfo, world: &mut World) { + let entity = player::create(world, info); + self.on_player_join(world, entity); + } + + /* EVENT HANDLERS */ + /// Called when a block is updated. + pub fn on_block_update( + &mut self, + _world: &mut World, + _pos: BlockPosition, + _old: Block, + _new: Block, + ) { + } + + /// Called when an entity is despawned/removed. + pub fn on_despawn(&mut self, _world: &mut World, _entity: Entity) {} + + /// Called when a player joins. + pub fn on_player_join(&mut self, _world: &mut World, _player: Entity) {} +} diff --git a/server/src/io/initial_handler.rs b/server/src/io/initial_handler.rs index 6079c5e63..7ad518252 100644 --- a/server/src/io/initial_handler.rs +++ b/server/src/io/initial_handler.rs @@ -14,7 +14,7 @@ //! speeding up the login process and making the latency calculation in //! the server list ping as low as possible. -use std::sync::atomic::Ordering; +use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; use rand::rngs::OsRng; @@ -32,7 +32,7 @@ use feather_core::network::packet::implementation::{ use feather_core::network::packet::{Packet, PacketStage, PacketType}; use crate::config::{Config, ProxyMode}; -use crate::{PlayerCount, PROTOCOL_VERSION, SERVER_VERSION}; +use crate::{PROTOCOL_VERSION, SERVER_VERSION}; use mojang_api::ProfileProperty; /// The key used for symmetric encryption. @@ -121,7 +121,7 @@ pub struct InitialHandler { /// The server's configuration. config: Arc, /// The server's player count. - player_count: Arc, + player_count: Arc, /// The server's icon, if any was loaded. server_icon: Arc>, @@ -137,7 +137,7 @@ pub struct InitialHandler { impl InitialHandler { pub fn new( config: Arc, - player_count: Arc, + player_count: Arc, server_icon: Arc>, ) -> Self { Self { @@ -267,7 +267,7 @@ fn extract_bungeecord_data(packet: &Handshake) -> Result Ok(BungeeCordData::from_vec(&bungee_information)?) } -#[derive(Debug)] +#[derive(Debug, PartialEq)] struct BungeeCordData { host: String, client: String, @@ -309,7 +309,7 @@ fn handle_request(ih: &mut InitialHandler, packet: &Request) -> Result<(), Error }, "players": { "max": ih.config.server.max_players, - "online": ih.player_count.0.load(Ordering::SeqCst), + "online": ih.player_count.load(Ordering::SeqCst), }, "description": { "text": ih.config.server.motd, @@ -534,7 +534,7 @@ fn send_packet(ih: &mut InitialHandler, packet: P) { ih.action_queue.push(Action::SendPacket(Box::new(packet))); } -#[derive(Error, Debug)] +#[derive(Error, Debug, PartialEq)] enum Error { #[error("invalid packet type {0:?} sent at stage {1:?}")] InvalidPacket(PacketType, Stage), @@ -569,8 +569,6 @@ enum Stage { #[cfg(test)] mod tests { - use std::sync::atomic::AtomicUsize; - use feather_core::network::cast_packet; use feather_core::network::packet::implementation::{ Handshake, HandshakeState, LoginSuccess, Ping, Pong, Request, Response, SetCompression, @@ -869,15 +867,15 @@ mod tests { fn ih() -> InitialHandler { InitialHandler::new( Arc::new(Config::default()), - Arc::new(PlayerCount(AtomicUsize::new(0))), + Arc::new(AtomicU32::new(0)), Arc::new(Some(String::from("test"))), ) } - fn ih_with_player_count(count: usize) -> InitialHandler { + fn ih_with_player_count(count: u32) -> InitialHandler { InitialHandler::new( Arc::new(Config::default()), - Arc::new(PlayerCount(AtomicUsize::new(count))), + Arc::new(AtomicU32::new(count)), Arc::new(Some(String::from("test"))), ) } @@ -885,7 +883,7 @@ mod tests { fn ih_with_config(config: Config) -> InitialHandler { InitialHandler::new( Arc::new(config), - Arc::new(PlayerCount(AtomicUsize::new(0))), + Arc::new(AtomicU32::new(0)), Arc::new(Some(String::from("test"))), ) } diff --git a/server/src/io/listener.rs b/server/src/io/listener.rs index 0090c1052..490c6afd3 100644 --- a/server/src/io/listener.rs +++ b/server/src/io/listener.rs @@ -5,40 +5,49 @@ use crate::config::Config; use crate::io::worker::run_worker; -use crate::io::ListenerToServerMessage; -use crate::PlayerCount; +use crate::io::{ListenerToServerMessage, ServerToListenerMessage}; +use crate::packet_buffer::PacketBuffers; +use futures::channel::mpsc; use std::net::SocketAddr; +use std::sync::atomic::AtomicU32; use std::sync::Arc; use tokio::io; use tokio::net::TcpListener; +use tokio::sync::Mutex; pub async fn run_listener( address: SocketAddr, - sender: crossbeam::Sender, + tx: crossbeam::Sender, + rx: mpsc::UnboundedReceiver, config: Arc, - player_count: Arc, + player_count: Arc, server_icon: Arc>, + packet_buffers: Arc, ) -> Result<(), io::Error> { let mut listener = TcpListener::bind(address).await?; + let rx = Arc::new(Mutex::new(rx)); + loop { let (stream, ip) = match listener.accept().await { Ok(res) => res, Err(e) => { - debug!("Failed to accept connection: {:?}", e); + info!("Failed to accept connection: {:?}", e); continue; } }; - debug!("Connection received from {}", ip); + info!("Connection received from {}", ip); tokio::spawn(run_worker( stream, ip, - sender.clone(), + tx.clone(), + Arc::clone(&rx), Arc::clone(&config), Arc::clone(&player_count), Arc::clone(&server_icon), + Arc::clone(&packet_buffers), )); } } diff --git a/server/src/io/mod.rs b/server/src/io/mod.rs index ff5c9b452..b27c94a14 100644 --- a/server/src/io/mod.rs +++ b/server/src/io/mod.rs @@ -1,9 +1,13 @@ use crate::config::Config; -use crate::PlayerCount; +use crate::packet_buffer::PacketBuffers; +use derivative::Derivative; use feather_core::network::packet::Packet; use feather_core::player_data::PlayerData; use feather_core::Position; +use fecs::Entity; +use futures::channel::mpsc; use std::net::SocketAddr; +use std::sync::atomic::AtomicU32; use std::sync::Arc; use uuid::Uuid; @@ -11,20 +15,37 @@ mod initial_handler; mod listener; mod worker; -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -pub struct Client(usize); - pub enum ServerToWorkerMessage { + /// Requests that a packet be sent to the client. SendPacket(Box), - NotifyPacketReceived(Box), - NotifyDisconnect(String), - Disconnect, } +#[derive(Debug)] +pub enum WorkerToServerMessage { + /// Notifies the server thread that the player disconnected. + NotifyDisconnected { reason: String }, +} + +#[derive(Debug)] pub enum ListenerToServerMessage { + /// Notifies the server thread that a new client connected. + /// + /// This message is sent after initial handling completes. NewClient(NewClientInfo), + /// Requests that the server create an empty `Entity` and send + /// it to the listener. This entity will later be used as a player. + RequestEntity, } +#[derive(Debug)] +pub enum ServerToListenerMessage { + /// Sends an entity to the listener as a response + /// to `ListenerToServerMessage::RequestEntity`. + Entity(Entity), +} + +#[derive(Derivative)] +#[derivative(Debug)] pub struct NewClientInfo { pub ip: SocketAddr, pub username: String, @@ -33,15 +54,19 @@ pub struct NewClientInfo { pub data: PlayerData, pub position: Position, - pub sender: futures::channel::mpsc::UnboundedSender, - pub receiver: crossbeam::Receiver, + #[derivative(Debug = "ignore")] + pub sender: mpsc::UnboundedSender, + #[derivative(Debug = "ignore")] + pub receiver: crossbeam::Receiver, + + pub entity: Entity, } -#[derive(Resource)] pub struct NetworkIoManager { - pub receiver: crossbeam::Receiver, + pub rx: crossbeam::Receiver, + pub tx: mpsc::UnboundedSender, /// Used for testing - pub listener_sender: crossbeam::Sender, + pub listener_tx: crossbeam::Sender, } impl NetworkIoManager { @@ -49,14 +74,24 @@ impl NetworkIoManager { pub fn start( addr: SocketAddr, config: Arc, - player_count: Arc, + player_count: Arc, server_icon: Arc>, + packet_buffers: Arc, ) -> Self { info!("Starting IO listener on {}", addr,); - let (sender, receiver) = crossbeam::unbounded(); + let (listener_tx, rx) = crossbeam::unbounded(); + let (tx, listener_rx) = mpsc::unbounded(); - let future = run_listener(addr, sender.clone(), config, player_count, server_icon); + let future = run_listener( + addr, + listener_tx.clone(), + listener_rx, + config, + player_count, + server_icon, + packet_buffers, + ); if cfg!(test) { let rt = tokio::runtime::Runtime::new().unwrap(); @@ -66,8 +101,9 @@ impl NetworkIoManager { } Self { - receiver, - listener_sender: sender, + rx, + tx, + listener_tx, } } } @@ -79,12 +115,24 @@ pub fn init() { async fn run_listener( addr: SocketAddr, - sender: crossbeam::Sender, + tx: crossbeam::Sender, + rx: mpsc::UnboundedReceiver, config: Arc, - player_count: Arc, + player_count: Arc, server_icon: Arc>, + packet_buffers: Arc, ) { - if let Err(e) = listener::run_listener(addr, sender, config, player_count, server_icon).await { + if let Err(e) = listener::run_listener( + addr, + tx, + rx, + config, + player_count, + server_icon, + packet_buffers, + ) + .await + { error!("An error occurred while binding to socket: {:?}", e); std::process::exit(1); } diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index d05f6249d..57ff24d2a 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -8,162 +8,202 @@ use crate::config::Config; use crate::io::initial_handler::{Action, InitialHandler}; -use crate::io::{ListenerToServerMessage, NewClientInfo, ServerToWorkerMessage}; -use crate::PlayerCount; +use crate::io::{ + ListenerToServerMessage, NewClientInfo, ServerToListenerMessage, ServerToWorkerMessage, + WorkerToServerMessage, +}; +use crate::packet_buffer::PacketBuffers; use feather_core::network::codec::MinecraftCodec; use feather_core::network::packet::PacketDirection; use feather_core::player_data::PlayerData; -use futures::{select, StreamExt}; -use futures::{FutureExt, SinkExt}; +use feather_core::Packet; +use fecs::Entity; +use futures::channel::mpsc; +use futures::future::Either; +use futures::SinkExt; +use futures::StreamExt; use std::net::SocketAddr; use std::path::Path; +use std::sync::atomic::AtomicU32; use std::sync::Arc; -use std::time::Duration; -use thiserror::Error; use tokio::net::TcpStream; +use tokio::sync::Mutex; use tokio_util::codec::Framed; use uuid::Uuid; -#[derive(Debug, Error)] -pub enum Error { - #[error("failed to read player data")] - PlayerData, +struct Worker { + framed: Framed, + ip: SocketAddr, + /// The listener's sender to send the initial `NewClient` message + /// to the server. Also used to request an entity for the player. + listener_tx: crossbeam::Sender, + /// Packet buffers to which we write packets received from the client. + packet_buffers: Arc, + /// The channel which will be used by the server thread + /// to send messages to `rx`. + server_tx: mpsc::UnboundedSender, + /// Channel to receive messages from the server, linked to `server_tx`. + rx: mpsc::UnboundedReceiver, + /// The channel which will be used by the server thread + /// to receive messages from the worker. + server_rx: crossbeam::Receiver, + /// Channel to send messages to the sserver, linked to `server_rx`. + tx: crossbeam::Sender, + /// Initial handler, set to `None` after the player has completed + /// the login process. + initial_handler: Option, + /// The entity for the player on the server thread. + entity: Entity, } /// Runs a worker task for the given client. pub async fn run_worker( stream: TcpStream, ip: SocketAddr, - global_sender: crossbeam::Sender, + listener_tx: crossbeam::Sender, + listener_rx: Arc>>, config: Arc, - player_count: Arc, + player_count: Arc, server_icon: Arc>, + packet_buffers: Arc, ) { - let (tx_worker_to_server, rx_worker_to_server) = crossbeam::unbounded(); + let (server_tx, rx) = mpsc::unbounded(); + let (tx, server_rx) = crossbeam::unbounded(); + + let initial_handler = Some(InitialHandler::new( + Arc::clone(&config), + Arc::clone(&player_count), + Arc::clone(&server_icon), + )); + + let codec = MinecraftCodec::new(PacketDirection::Serverbound); + let framed = Framed::new(stream, codec); - let msg = match _run_worker( - stream, + let entity = request_entity(&listener_tx, &mut *listener_rx.lock().await).await; + + let mut worker = Worker { + framed, ip, - global_sender, - config, - player_count, - server_icon, - tx_worker_to_server.clone(), - rx_worker_to_server.clone(), - ) - .await - { + listener_tx, + packet_buffers, + server_tx, + rx, + server_rx, + tx, + initial_handler, + entity, + }; + + let msg = match run_worker_impl(&mut worker).await { Ok(()) => "normal disconnect".to_string(), Err(e) => format!("{}", e), }; - let _ = tx_worker_to_server.send(ServerToWorkerMessage::NotifyDisconnect(msg)); + let _ = worker + .tx + .send(WorkerToServerMessage::NotifyDisconnected { reason: msg }); } -#[allow(clippy::too_many_arguments)] -async fn _run_worker( - stream: TcpStream, - ip: SocketAddr, - global_sender: crossbeam::Sender, - config: Arc, - player_count: Arc, - server_icon: Arc>, - tx_worker_to_server: crossbeam::Sender, - rx_worker_to_server: crossbeam::Receiver, -) -> anyhow::Result<()> { - let codec = MinecraftCodec::new(PacketDirection::Serverbound); +async fn request_entity( + listener_tx: &crossbeam::Sender, + listener_rx: &mut mpsc::UnboundedReceiver, +) -> Entity { + let _ = listener_tx.send(ListenerToServerMessage::RequestEntity); - let mut framed = Framed::new(stream, codec); + let recv = listener_rx.next().await.expect("server disconnected"); - let mut initial_handler = Some(InitialHandler::new( - Arc::clone(&config), - player_count, - server_icon, - )); - - let (tx_server_to_worker, mut rx_server_to_worker) = futures::channel::mpsc::unbounded(); - let mut rx_worker_to_server = Some(rx_worker_to_server); + match recv { + ServerToListenerMessage::Entity(entity) => entity, + } +} +async fn run_worker_impl(worker: &mut Worker) -> anyhow::Result<()> { loop { - let mut server_message = None; - let mut received_packet = None; + let received_message = worker.rx.next(); + let received_packet = worker.framed.next(); - select! { - msg = rx_server_to_worker.next().fuse() => server_message = Some(msg), - packet = tokio::time::timeout(Duration::from_millis(10000), framed.next()).fuse() => received_packet = Some(packet), - } + let select = futures::future::select(received_message, received_packet); - if let Some(msg) = server_message { - if let Some(msg) = msg { - match msg { - ServerToWorkerMessage::SendPacket(packet) => framed.send(packet).await?, - ServerToWorkerMessage::Disconnect => return Ok(()), - _ => unreachable!(), + match select.await { + Either::Left((msg, _)) => { + if let Some(msg) = msg { + handle_server_to_worker_message(worker, msg).await?; } } + Either::Right((packet_res, _)) => { + let packet_res = packet_res.ok_or(anyhow::anyhow!("packet was None"))?; + + let packet = packet_res?; + + handle_packet(worker, packet).await?; + } } + } +} - if let Some(packet_result) = received_packet { - if let Some(packet_result) = packet_result? { - match packet_result { - Ok(packet) => { - if let Some(ih) = initial_handler.as_mut() { - ih.handle_packet(packet).await; - let actions = ih.actions_to_execute(); - - for action in actions { - match action { - Action::Disconnect => return Ok(()), - Action::SendPacket(packet) => framed.send(packet).await?, - Action::EnableCompression(threshold) => { - if threshold > 0 { - trace!( - "Enabling compression with threshold {}", - threshold - ); - framed - .codec_mut() - .enable_compression(threshold as usize); - } - } - Action::EnableEncryption(key) => { - trace!("Enabling encryption"); - framed.codec_mut().enable_encryption(key) - } - Action::SetStage(stage) => framed.codec_mut().set_stage(stage), - Action::JoinGame(res) => { - // let data = load_player_data(&config, res.uuid).await?; - let data = PlayerData::default(); - let info = NewClientInfo { - ip, - username: res.username.ok_or(Error::PlayerData)?, - profile: res.props, - uuid: res.uuid, - sender: tx_server_to_worker.clone(), - receiver: rx_worker_to_server.take().unwrap(), - /*position: data - .entity - .read_position() - .ok_or_else(|| Error::PlayerData)?,*/ - position: position!(0.0, 80.0, 0.0), - data, - }; - global_sender - .send(ListenerToServerMessage::NewClient(info))?; - initial_handler = None; - } - } - } - } else { - let _ = tx_worker_to_server - .send(ServerToWorkerMessage::NotifyPacketReceived(packet)); - } - } - Err(e) => return Err(e), - } +async fn handle_server_to_worker_message( + worker: &mut Worker, + msg: ServerToWorkerMessage, +) -> anyhow::Result<()> { + match msg { + ServerToWorkerMessage::SendPacket(packet) => worker.framed.send(packet).await?, + } + + Ok(()) +} + +async fn handle_packet(worker: &mut Worker, packet: Box) -> anyhow::Result<()> { + if let Some(ref mut ih) = worker.initial_handler { + ih.handle_packet(packet).await; + + handle_ih_actions(worker).await?; + } else { + worker.packet_buffers.push(worker.entity, packet); + } + + Ok(()) +} + +async fn handle_ih_actions(worker: &mut Worker) -> anyhow::Result<()> { + for action in worker + .initial_handler + .as_mut() + .unwrap() + .actions_to_execute() + { + match action { + Action::SendPacket(packet) => worker.framed.send(packet).await?, + Action::EnableCompression(threshold) => worker + .framed + .codec_mut() + .enable_compression(threshold as usize), + Action::EnableEncryption(key) => worker.framed.codec_mut().enable_encryption(key), + Action::Disconnect => anyhow::bail!("initial handler requested disconnect"), + Action::SetStage(stage) => worker.framed.codec_mut().set_stage(stage), + Action::JoinGame(info) => { + let info = NewClientInfo { + ip: worker.ip, + username: info.username.unwrap_or(String::from("undefined")), + profile: info.props, + uuid: info.uuid, + data: Default::default(), // TODO + position: position!(0.0, 70.0, 0.0), // TODO + sender: worker.server_tx.clone(), + receiver: worker.server_rx.clone(), + entity: worker.entity, + }; + + let _ = worker + .listener_tx + .send(ListenerToServerMessage::NewClient(info)); + + worker.initial_handler = None; + return Ok(()); } } } + + Ok(()) } #[allow(dead_code)] // TODO diff --git a/server/src/lib.rs b/server/src/lib.rs index 465b1fef0..173291336 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -91,7 +91,7 @@ //! chunk packets, inventory, time, nearby entities, etc. `PlayerJoinEvent` //! is used to send this data. -#![feature(vec_remove_item)] +#![feature(alloc_layout_extra)] #[macro_use] extern crate log; @@ -108,7 +108,7 @@ extern crate feather_core; #[macro_use] extern crate bitflags; #[macro_use] -extern crate tonks; +extern crate fecs; #[macro_use] extern crate num_derive; #[macro_use] @@ -118,21 +118,22 @@ extern crate nalgebra_glm as glm; use crossbeam::Receiver; use std::alloc::System; -use std::sync::atomic::AtomicUsize; +use std::sync::atomic::{AtomicU32, AtomicUsize}; use std::sync::Arc; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use crate::chunk_logic::ChunkWorkerHandle; use crate::config::Config; +use crate::game::Game; use crate::io::NetworkIoManager; -use crate::state::StateInner; +use crate::packet_buffer::PacketBuffers; use crate::worldgen::{ ComposableGenerator, EmptyWorldGenerator, SuperflatWorldGenerator, WorldGenerator, }; +use bumpalo::Bump; use feather_core::level; use feather_core::level::{deserialize_level_file, save_level_file, LevelData, LevelGeneratorType}; use feather_core::world::ChunkMap; -use legion::world::World; +use fecs::{Executor, Resources, World}; use rand::Rng; use std::collections::hash_map::DefaultHasher; use std::fs::File; @@ -140,51 +141,41 @@ use std::hash::{Hash, Hasher}; use std::io::{Read, Write}; use std::path::Path; use std::process::exit; -use tonks::{Resources, Scheduler}; #[global_allocator] static ALLOC: System = System; -pub mod block; -pub mod broadcasters; -pub mod chunk_entities; -pub mod chunk_logic; +// pub mod block; +// pub mod broadcasters; +// pub mod chunk_entities; +// pub mod chunk_logic; pub mod chunk_worker; pub mod config; pub mod entity; pub mod io; -pub mod join; -pub mod lazy; -pub mod metadata; +// pub mod join; +// pub mod lazy; +// pub mod metadata; pub mod network; -pub mod p_inventory; // Prefixed to avoid conflict with inventory crate -pub mod packet_handlers; -pub mod physics; +// pub mod p_inventory; // Prefixed to avoid conflict with inventory crate +// pub mod packet_handlers; +// pub mod physics; pub mod player; pub mod shutdown; -pub mod state; -pub mod time; -pub mod util; -pub mod view; +// pub mod time; +// pub mod util; +// pub mod view; +pub mod game; +pub mod packet_buffer; pub mod worldgen; +pub type BumpVec<'a, T> = bumpalo::collections::Vec<'a, T>; + pub const TPS: u64 = 20; pub const PROTOCOL_VERSION: u32 = 404; pub const SERVER_VERSION: &str = "Feather 1.13.2"; pub const TICK_TIME: u64 = 1000 / TPS; -#[derive(Default, Debug, Resource)] -pub struct PlayerCount(AtomicUsize); - -#[derive(Default, Debug, Resource)] -pub struct TickCount(u64); - -/// System to increment tick count each tick. -#[system] -fn tick_count_increment(tick: &mut TickCount) { - tick.0 += 1; -} - pub fn main() { let config = Arc::new(load_config()); init_log(&config); @@ -193,12 +184,15 @@ pub fn main() { let server_icon = Arc::new(load_server_icon()); - let player_count = Arc::new(PlayerCount(AtomicUsize::new(0))); + let player_count = Arc::new(AtomicU32::new(0)); + + let packet_buffers = Arc::new(PacketBuffers::new()); - let io_manager = init_io_manager( + let io_handle = init_io_manager( Arc::clone(&config), Arc::clone(&player_count), Arc::clone(&server_icon), + Arc::clone(&packet_buffers), ); let world_name = &config.world.name; @@ -225,9 +219,19 @@ pub fn main() { exit(1) }); - let chunk_worker_handle = init_chunk_worker(world_dir, &level); + // let chunk_worker_handle = init_chunk_worker(world_dir, &level); + + let game = Game { + io_handle, + config, + tick_count: 0, + player_count, + level, + chunk_map: ChunkMap::new(), + bump: Bump::new(), + }; - let mut scheduler = init_scheduler(Arc::clone(&config), chunk_worker_handle, level, io_manager); + let (executor, resources) = init_executor(game); let mut world = World::new(); // Channel used by the shutdown handler to notify the server thread. @@ -244,7 +248,7 @@ pub fn main() { // load_spawn_chunks(&mut world); TODO info!("Server started"); - run_loop(&mut world, &mut scheduler, shutdown_rx); + run_loop(&mut world, &resources, &executor, shutdown_rx); info!("Shutting down"); @@ -260,7 +264,12 @@ pub fn main() { } /// Runs the main game loop. -fn run_loop(world: &mut World, scheduler: &mut Scheduler, shutdown_rx: Receiver<()>) { +fn run_loop( + world: &mut World, + resources: &Resources, + executor: &Executor, + shutdown_rx: Receiver<()>, +) { loop { if shutdown_rx.try_recv().is_ok() { // Shut down @@ -269,15 +278,9 @@ fn run_loop(world: &mut World, scheduler: &mut Scheduler, shutdown_rx: Receiver< let start_time = current_time_in_millis(); - scheduler.execute(world); - // https://github.com/TomGillen/legion/issues/60 - // world.defrag(None); // TODO: do this at interval rate? + executor.execute(resources, world); - // Run lazily-executed closures. TODO: remove unsafe - unsafe { - let state = scheduler.resources().get::() as *const StateInner; - (&*state).flush(world, scheduler); - } + world.defrag(Some(256)); // should this be done at an interval rate? // Sleep correct amount let end_time = current_time_in_millis(); @@ -298,23 +301,22 @@ fn run_loop(world: &mut World, scheduler: &mut Scheduler, shutdown_rx: Receiver< } } -/// Initializes the scheduler and resources. -fn init_scheduler( - config: Arc, - chunk_worker_handle: ChunkWorkerHandle, - level: LevelData, - io_manager: NetworkIoManager, -) -> Scheduler { +/// Initializes the executor and resources. +fn init_executor(game: Game) -> (Executor, Resources) { // Insert resources which don't have a `Default` impl. let mut resources = Resources::new(); - let chunk_map = ChunkMap::new(); - resources.insert(StateInner::new(config, chunk_map, level)); - resources.insert(chunk_worker_handle); - resources.insert(io_manager); + resources.insert(game); + + // todo: https://github.com/dtolnay/inventory/issues/9yutfyy5fttyhfrgthgftjhdgthyhdjfjtcynjncdtnhgd + let mut executor = Executor::new(); + + executor.add(network::poll_new_clients); + executor.add(network::poll_player_disconnect); - tonks::build_scheduler().build(resources) + (executor, resources) } +/* /// Initializes the chunk worker. fn init_chunk_worker(world_dir: &Path, level: &LevelData) -> ChunkWorkerHandle { let generator: Arc = match level.generator_type() { @@ -333,6 +335,7 @@ fn init_chunk_worker(world_dir: &Path, level: &LevelData) -> ChunkWorkerHandle { receiver: rx, } } +*/ /// Loads the configuration file, creating a default /// one if it does not exist. @@ -359,8 +362,9 @@ fn load_config() -> Config { /// Starts the IO threads. fn init_io_manager( config: Arc, - player_count: Arc, + player_count: Arc, server_icon: Arc>, + packet_buffers: Arc, ) -> io::NetworkIoManager { io::NetworkIoManager::start( format!("{}:{}", config.server.address, config.server.port) @@ -369,6 +373,7 @@ fn init_io_manager( config, player_count, server_icon, + packet_buffers, ) } diff --git a/server/src/network.rs b/server/src/network.rs index 2921b7192..8f60a6250 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -6,141 +6,23 @@ //! from players and allows systems to poll for packets //! received of a given type. -use crate::entity::{EntityDeleteEvent, EntityId}; -use crate::io::{ListenerToServerMessage, NetworkIoManager, ServerToWorkerMessage}; -use crate::player; -use crate::state::State; +use crate::game::Game; +use crate::io::{ + ListenerToServerMessage, ServerToListenerMessage, ServerToWorkerMessage, WorkerToServerMessage, +}; +use crate::BumpVec; use crossbeam::Receiver; -use feather_core::network::cast_packet; -use feather_core::{Packet, PacketType, Position}; +use feather_core::Packet; +use fecs::{IntoQuery, Read, World}; use futures::channel::mpsc::UnboundedSender; -use legion::entity::Entity; -use legion::query::Read; -use lock_api::RawMutex; -use parking_lot::{Mutex, MutexGuard}; use std::iter; -use strum::EnumCount; -use tonks::{PreparedWorld, Query}; -use uuid::Uuid; - -type QueuedPackets = Vec<(Entity, Box)>; - -struct UnsafeDrain { - ptr: *const T, - len: usize, - pos: usize, -} - -impl Iterator for UnsafeDrain { - type Item = T; - - fn next(&mut self) -> Option { - if self.pos == self.len { - return None; - } - - let value = unsafe { std::ptr::read(self.ptr.add(self.pos)) }; - self.pos += 1; - Some(value) - } -} - -pub struct DrainedPackets<'a, I> { - mutex: &'a parking_lot::RawMutex, - value: I, -} - -impl<'a, I> DrainedPackets<'a, I> { - unsafe fn new(mutex: &'a parking_lot::RawMutex, value: I) -> Self { - Self { mutex, value } - } -} - -impl<'a, I> Iterator for DrainedPackets<'a, I> -where - I: Iterator, -{ - type Item = I::Item; - - fn next(&mut self) -> Option { - self.value.next() - } -} - -impl<'a, I> Drop for DrainedPackets<'a, I> { - fn drop(&mut self) { - self.mutex.unlock(); - } -} - -/// The packet queue. This type allows systems to poll for -/// received packets of a given type. -/// -/// A system should never require mutable access to this type. -#[derive(Resource)] -pub struct PacketQueue { - /// Vector of queued packets. This vector is indexed - /// by the ordinal of the packet type, and each - /// queue contains only packets of its type. - queue: Vec>, -} - -impl Default for PacketQueue { - fn default() -> Self { - Self::new() - } -} - -impl PacketQueue { - /// Creates a new, empty `PacketQueue`. - pub fn new() -> Self { - Self { - queue: iter::repeat_with(|| Mutex::new(vec![])) - .take(PacketType::count() + 1) - .collect(), - } - } - - /// Returns an iterator over packets of a given type. - pub fn received(&self) -> impl Iterator + '_ { - let mut queue = self.queue[P::ty_sized().ordinal()].lock(); - - // Hack to map to draining iterator. - unsafe { - let raw = MutexGuard::mutex(&queue).raw(); - - let drain = UnsafeDrain { - ptr: queue.as_ptr(), - len: queue.len(), - pos: 0, - }; - - // Safety: the vector cannot be accessed as long as the returned `UnsafeDrain` - // has not been dropped, since the mutex is acquired. - queue.set_len(0); - // Ensure mutex is not released; we will do it manually in `UnsafeDrain` - std::mem::forget(queue); - - let iter = drain.map(|(entity, packet)| (entity, cast_packet::

(packet))); - - DrainedPackets::new(raw, iter) - } - } - - /// Adds a packet to the queue. - pub fn push(&self, packet: Box, entity: Entity) { - let ordinal = packet.ty().ordinal(); - - self.queue[ordinal].lock().push((entity, packet)); - } -} /// Network component containing channels to send and receive packets. /// -/// Systems should call `Self::send` to send a packet to this entity (player). +/// Systems should call `Network::send` to send a packet to this entity (player). pub struct Network { - pub sender: UnboundedSender, - pub receiver: Receiver, + pub tx: UnboundedSender, + pub rx: Receiver, } impl Network { @@ -159,85 +41,50 @@ impl Network { // shut down, and the disconnect was not yet registered // by the server) let _ = self - .sender + .tx .unbounded_send(ServerToWorkerMessage::SendPacket(packet)); } } -/// The network system. This system is responsible for: -/// * Handling player disconnects. -/// * Pushing received packets to the packet queue. -/// * Accepting new clients and creating entities for them. +/// System which polls for player disconnects. #[system] -pub fn network_( - state: &State, - io: &NetworkIoManager, - packet_queue: &PacketQueue, - query: &mut Query>, - world: &mut PreparedWorld, -) { - // For each `Network`, handle any disconnects and received packets. - query.par_entities_for_each(world, |(entity, network)| { - while let Ok(msg) = network.receiver.try_recv() { - match msg { - ServerToWorkerMessage::NotifyDisconnect(_) => { - state.exec_with_scheduler(move |world, scheduler| { - let position = *world.get_component::(entity).unwrap(); - let id = *world.get_component::(entity).unwrap(); - let uuid = *world.get_component::(entity).unwrap(); - scheduler.trigger(EntityDeleteEvent { - entity, - position: Some(position), - id, - uuid, - }); - assert!(world.delete(entity), "player already deleted"); - }); +pub fn poll_player_disconnect(game: &mut Game, world: &mut World) { + // For each player with a Network component, + // check their channel for disconnects. + let mut to_despawn = BumpVec::new_in(&game.bump); + >::query() + .iter_entities(world.inner()) + .for_each(|(entity, network)| { + while let Ok(msg) = network.rx.try_recv() { + match msg { + WorkerToServerMessage::NotifyDisconnected { reason } => { + log::debug!("Server observed player disconnect: caused by {}", reason); + to_despawn.push(entity); + } } - ServerToWorkerMessage::NotifyPacketReceived(packet) => { - packet_queue.push(packet, entity); - } - _ => unreachable!(), } - } + }); + + to_despawn.into_iter().for_each(|entity| { + world.despawn(entity); }); +} - // Handle new clients. - while let Ok(msg) = io.receiver.try_recv() { +/// System which polls for new clients from the listener task. +#[system] +pub fn poll_new_clients(game: &mut Game, world: &mut World) { + while let Ok(msg) = game.io_handle.rx.try_recv() { match msg { ListenerToServerMessage::NewClient(info) => { - debug!("Server received connection from {}", info.username); - player::create(state, info); + game.spawn_player(info, world); + } + ListenerToServerMessage::RequestEntity => { + let entity = world.spawn(iter::once(()))[0]; + let _ = game + .io_handle + .tx + .unbounded_send(ServerToListenerMessage::Entity(entity)); } } } } - -#[cfg(test)] -mod tests { - use super::*; - use feather_core::network::packet::implementation::Handshake; - use feather_core::network::packet::PacketType::SpawnObject; - use legion::world::World; - - #[test] - fn packet_queue() { - let queue = PacketQueue::new(); - - let mut world = World::new(); - let entities = world.insert((), vec![(), ()]); - - queue.push(Box::new(Handshake::default()), entities[0]); - queue.push(Box::new(SpawnObject::default()), entities[1]); - queue.push(Box::new(Handshake::default()), entities[1]); - - let mut handshakes = queue.received::(); - assert_eq!(handshakes.next().unwrap().0, entities[0]); - assert_eq!(handshakes.next().unwrap().0, entities[1]); - assert!(handshakes.next().is_none()); - - let mut spawn_objects = queue.received::(); - assert_eq!(spawn_objects.next().unwrap().0, entities[1]); - assert!(spawn_objects.next().is_none()); - } -} diff --git a/server/src/packet_buffer.rs b/server/src/packet_buffer.rs new file mode 100644 index 000000000..87e20fde0 --- /dev/null +++ b/server/src/packet_buffer.rs @@ -0,0 +1,372 @@ +//! Two implementations of a packet buffer. +//! +//! A packet buffer is used to hold the packets of a given type received +//! from players. When a packet is received, the IO threads +//! push the packet onto the buffer, and systems on the server +//! thread poll these packets out of the buffer. +//! +//! We provide two implementations, optimized for different cases: +//! * A buffer based on a large array, with two slots for each player. +//! This buffer works well for cases when packets of this type are received +//! very often, such as position updates. +//! * A buffer based on `crossbeam-channel`, best for cases where fewer +//! packets of this type are received. +//! +//! The former is not yet implemented, and we are currently using a `DashMap; 4]>>`. + +use ahash::AHashMap; +use feather_core::{cast_packet, Packet, PacketType}; +use fecs::Entity; +use num_traits::ToPrimitive; +use parking_lot::{Mutex, RwLock}; +use smallvec::SmallVec; +use std::iter; +use strum::IntoEnumIterator; + +/// The global packet store, storing packet buffers for packets of each type. +pub struct PacketBuffers { + /// Packet buffers, indexed by the `ToPrimitive` implementation + /// of `PacketType`. + buffers: Vec, +} + +/// The set of buffers which use a `MapBuffer` instead of an `ArrayBuffer`. +lazy_static! { + static ref USE_MAP_FOR: indexmap::IndexSet = indexmap::indexset![]; +} + +impl PacketBuffers { + /// Creates a new packet store with buffers allocated for all packet types. + pub fn new() -> Self { + Self { + buffers: PacketType::iter() + .map(|ty| { + if USE_MAP_FOR.contains(&ty) { + PacketBuffer::Map(MapBuffer::default()) + } else { + PacketBuffer::Channel(ChannelBuffer::new()) + } + }) + .collect(), + } + } + + /// Pushes a received packet onto the packet buffer for the packet's + /// type. + pub fn push(&self, entity: Entity, packet: Box) { + let index = packet.ty().to_usize().unwrap(); + + self.buffers[index].push(entity, packet); + } + + /// Returns an iterator over packets received with type `T`. + /// + /// # Panics + /// Panics if the underlying buffer is not a `ChannelBuffer`. + /// `received_for()` should be used instead if using an `ArrayBuffer`. + pub fn received<'a, T>(&'a self) -> impl Iterator + 'a + where + T: Packet, + { + let ty = T::ty_sized(); + + let index = ty.to_usize().unwrap(); + + self.buffers[index] + .poll() + .map(|(player, boxed)| (player, cast_packet(boxed))) + } + + /// Returns an iterator over packets of type `T` received by the given player. + /// + /// # Panics + /// Panics if the underlying buffer for this packet type is not a `MapBuffer` or an + /// `ArrayBuffer`. Use `received` instead. + pub fn received_for(&self, player: Entity) -> impl Iterator + where + T: Packet, + { + let ty = T::ty_sized(); + + let index = ty.to_usize().unwrap(); + + self.buffers[index] + .received_for(player) + .map(|boxed| cast_packet(boxed)) + } +} + +/// One of two packet buffer implementations. +pub enum PacketBuffer { + Channel(ChannelBuffer), + Map(MapBuffer), +} + +impl PacketBuffer { + /// Polls this buffer for newly received packets. + /// + /// # Panics + /// Panics if the underlying buffer is not a `ChannelBuffer`. + /// `received_for()` should be used instead if using an `ArrayBuffer`. + pub fn poll<'a>(&'a self) -> impl Iterator)> + 'a { + match self { + PacketBuffer::Channel(chan) => chan.poll(), + PacketBuffer::Map(_) => panic!("cannot poll a map-based packet buffer"), + } + } + + /// Pushes a packet onto this buffer. + pub fn push(&self, player: Entity, packet: Box) { + match self { + PacketBuffer::Channel(chan) => chan.push(player, packet), + PacketBuffer::Map(map) => map.push(player, packet), + } + } + + /// Drains packets received by the given player. + /// + /// # Panics + /// Panics if the underlying buffer is not a `MapBuffer` or an `ArrayBuffer`. + pub fn received_for(&self, player: Entity) -> impl Iterator> { + match self { + PacketBuffer::Map(map) => map.received_for(player), + PacketBuffer::Channel(_) => { + panic!("cannot use received_for for a channel-based packet buffer") + } + } + } +} + +/// A packet buffer based on an MPMC channel. Best for packet types +/// which are received less frequently. +pub struct ChannelBuffer { + sender: crossbeam::Sender<(Entity, Box)>, + receiver: crossbeam::Receiver<(Entity, Box)>, +} + +impl ChannelBuffer { + fn new() -> Self { + let (sender, receiver) = crossbeam::unbounded(); + Self { sender, receiver } + } + + fn push(&self, player: Entity, packet: Box) { + let _ = self.sender.send((player, packet)); + } + + fn poll<'a>(&'a self) -> impl Iterator)> + 'a { + self.receiver.try_iter() + } +} + +enum Either { + Left(A), + Right(B), +} + +impl Iterator for Either +where + A: Iterator, + B: Iterator, +{ + type Item = I; + + fn next(&mut self) -> Option { + match self { + Either::Left(a) => a.next(), + Either::Right(b) => b.next(), + } + } +} + +type MapBufferVec = SmallVec<[Box; 2]>; +type MapBufferInner = AHashMap>; + +#[derive(Default)] +pub struct MapBuffer(RwLock); + +impl MapBuffer { + fn push(&self, player: Entity, packet: Box) { + let guard = self.0.read(); + if let Some(vec) = guard.get(&player) { + vec.lock().push(packet); + } else { + drop(guard); + self.0.write().insert(player, Mutex::new(smallvec![packet])); + } + } + + fn received_for(&self, player: Entity) -> impl Iterator> { + let map_guard = self.0.read(); + + if let Some(vec) = map_guard.get(&player) { + let vec = vec + .lock() + .drain(..) + .collect::; 2]>>(); + + Either::Left(vec.into_iter()) + } else { + Either::Right(iter::empty()) + } + } +} + +/* TODO: audit this implementation. +/// A packet buffer using an array of slots. +pub struct ArrayBuffer { + /// Internal array of length `2 * (num_players rounded up to the next power of two)`. + /// Packets received for a player with index `i` will + /// be located at `array[i]` and `array[n + i]`, where `n` is the number + /// of players rounded up to the next power of two. + /// + /// Note that this array is type-erased; we do this as an optimization + /// to store the packets directly in the array instead of going through a + /// `Box`. + array: RwLock>, + /// Memory layout of `array`. + array_layout: Mutex, + /// Layout of a single packet. + single_packet: Layout, + /// Number of players for which this buffer has capacity. + max_players: AtomicUsize, + /// Pointer to the `None` value for the packet. + none_ptr: NonNull, + /// Length of the `None` value for the packet. + none_len: usize, +} + +impl ArrayBuffer { + /// Creates a new, empty `ArrayBuffer` for packets of type `T`. + pub fn new() -> Self { + let starting_n = 8; + + let none = Box::new(Option::::None); + let none_ptr = NonNull::new(Box::into_raw(none).cast()).expect("box has null pointer"); + + let (array, array_layout) = + unsafe { Self::allocate_for(Layout::new::>(), starting_n, none_ptr) }; + + Self { + array: RwLock::new(array), + array_layout: Mutex::new(array_layout), + single_packet: Layout::new::>(), + max_players: AtomicUsize::new(starting_n), + none_ptr, + none_len: std::mem::size_of::>(), + } + } + + /// Returns the number of players supported by this buffer. + pub fn max_players(&self) -> usize { + self.max_players.load(Ordering::Acquire) + } + + /// Extends this array buffer to support at least `n` __more__ players. + pub fn reserve(&self, extra: usize) { + let mut old_array = self.array.write(); + let mut old_layout = self.array_layout.lock(); + + let current_max = self.max_players.load(Ordering::Acquire); + let new_max = (current_max + extra).next_power_of_two(); + + let (new_array, new_layout) = + unsafe { Self::allocate_for(self.single_packet, new_max, self.none_ptr) }; + + assert!(new_layout.size() > old_layout.size()); + assert_eq!(new_layout.size() % 2, 0); + assert_eq!(old_layout.size() % 2, 0); + + // copy existing packets to new array + unsafe { + std::ptr::copy_nonoverlapping( + old_array.as_ptr(), + new_array.as_ptr(), + old_layout.size() / 2, + ); + std::ptr::copy_nonoverlapping( + old_array.as_ptr().offset((old_layout.size() / 2) as isize), + new_array.as_ptr().offset((new_layout.size() / 2) as isize), + old_layout.size() / 2, + ); + } + + *old_array = new_array; + *old_layout = new_layout; + self.max_players.store(new_max, Ordering::Release); + } + + unsafe fn allocate_for( + single_packet: Layout, + max_players: usize, + none_ptr: NonNull, + ) -> (NonNull, Layout) { + let new_layout = single_packet + .repeat(max_players * 2) + .map(|(layout, offset)| { + assert_eq!(offset, single_packet.size()); + layout + }) + .expect("invalid packet buffer layout"); + + let new_array = + NonNull::new(std::alloc::alloc(new_layout)).expect("allocator returned null pointer"); + + // fill array with `None` values + for i in 0..max_players * 2 { + let ptr = new_array + .as_ptr() + .offset((i * single_packet.size()) as isize); + std::ptr::copy_nonoverlapping(none_ptr.as_ptr(), ptr, single_packet.size()); + } + + (new_array, new_layout) + } +} +*/ + +#[cfg(test)] +mod tests { + use super::*; + use feather_core::network::packet::implementation::Request; + use fecs::{EntityBuilder, World}; + + #[test] + fn map_buffer() { + let buffer = MapBuffer::default(); + + let mut world = World::new(); + let entity = EntityBuilder::new().build().spawn_in(&mut world); + + dbg!(); + buffer.push(entity, Box::new(Request {})); + dbg!(); + + let mut received = buffer.received_for(entity).collect::>(); + dbg!(); + + assert_eq!(received.len(), 1); + + let first = received.remove(0); + let _ = cast_packet::(first); + } + + #[test] + fn channel_buffer() { + let buffer = ChannelBuffer::new(); + + let mut world = World::new(); + let entity = EntityBuilder::new().build().spawn_in(&mut world); + + buffer.push(entity, Box::new(Request {})); + + let mut received = buffer.poll().collect::>(); + + assert_eq!(received.len(), 1); + + let (rentity, first) = received.remove(0); + let _ = cast_packet::(first); + + assert_eq!(entity, rentity); + } +} diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index e9f6235ca..a6a049185 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -1,23 +1,12 @@ //! Systems and components specific to player entities. -use crate::broadcasters::movement::LastKnownPositions; -use crate::chunk_logic::ChunkHolder; -use crate::entity; -use crate::entity::{CreationPacketCreator, EntityId, Name, SpawnPacketCreator}; use crate::io::NewClientInfo; -use crate::join::Joined; use crate::network::Network; -use crate::p_inventory::EntityInventory; -use crate::state::State; -use crate::util::degrees_to_stops; -use feather_core::network::packet::implementation::{PlayerInfo, PlayerInfoAction, SpawnPlayer}; -use feather_core::{ClientboundAnimation, Gamemode, Packet, Position}; -use legion::entity::Entity; +use feather_core::Gamemode; +use fecs::{Entity, World}; use mojang_api::ProfileProperty; -use tonks::{EntityAccessor, PreparedWorld}; -use uuid::Uuid; -pub mod chat; +// pub mod chat; pub const PLAYER_EYE_HEIGHT: f64 = 1.62; @@ -29,46 +18,39 @@ pub struct ProfileProperties(pub Vec); #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Player; -/// Event triggered when a player joins. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct PlayerJoinEvent { - pub player: Entity, -} - -/// Event triggered when a player causes an animation. -#[derive(Debug, Clone)] -pub struct PlayerAnimationEvent { - pub player: Entity, - pub animation: ClientboundAnimation, -} - /// Creates a new player from the given `NewClientInfo`. /// /// This function also triggers the `PlayerJoinEvent` for this player. -pub fn create(state: &State, info: NewClientInfo) { - entity::base(state, info.position) - .with_component(info.uuid) - .with_component(Network { - sender: info.sender, - receiver: info.receiver, - }) - .with_component(info.ip) - .with_component(ProfileProperties(info.profile)) - .with_component(Name(info.username)) - .with_component(ChunkHolder::default()) - .with_component(Joined(false)) - .with_component(LastKnownPositions::default()) - .with_component(SpawnPacketCreator(&create_spawn_packet)) - .with_component(CreationPacketCreator(&create_initialization_packet)) - .with_component(Gamemode::Creative) // TOOD: proper gamemode handling - .with_component(EntityInventory::default()) - .with_component(Player) - .with_exec(|_, scheduler, player| { - scheduler.trigger(PlayerJoinEvent { player }); - }) - .build(); +pub fn create(world: &mut World, info: NewClientInfo) -> Entity { + // TODO: blocked on https://github.com/TomGillen/legion/issues/36 + let entity = info.entity; + world.add(entity, info.position).unwrap(); + world.add(entity, info.uuid).unwrap(); + world.add(entity, info.uuid).unwrap(); + world + .add( + entity, + Network { + tx: info.sender, + rx: info.receiver, + }, + ) + .unwrap(); + world.add(entity, info.ip).unwrap(); + world.add(entity, ProfileProperties(info.profile)).unwrap(); + //world.add(entity, Name(info.username)).unwrap(); + //world.add(entity, ChunkHolder::default()).unwrap(); + //world.add(entity, Joined(false)).unwrap(); + //world.add(entity, LastKnownPositions::default()).unwrap(); + //world.add(entity, SpawnPacketCreator(&create_spawn_packet)).unwrap(); + //world.add(entity, CreationPacketCreator(&create_initialization_packet)).unwrap(); + world.add(entity, Gamemode::Creative).unwrap(); // TODO: proper gamemode handling + //world.add(entity, EntityInventory::default()) + world.add(entity, Player).unwrap(); + entity } +/* /// Function to create a `SpawnPlayer` packet to spawn the player. fn create_spawn_packet(accessor: &EntityAccessor, world: &PreparedWorld) -> Box { let entity_id = accessor.get_component::(world).unwrap().0; @@ -122,3 +104,4 @@ fn create_initialization_packet( let packet = PlayerInfo { action, uuid }; Box::new(packet) } +*/ diff --git a/server/src/shutdown.rs b/server/src/shutdown.rs index 53ba1f5e2..276e84fdf 100644 --- a/server/src/shutdown.rs +++ b/server/src/shutdown.rs @@ -1,6 +1,6 @@ //! Shutdown behavior. use crossbeam::Sender; -use legion::world::World; +use fecs::World; pub fn init(tx: Sender<()>) { ctrlc::set_handler(move || { diff --git a/server/src/state.rs b/server/src/state.rs deleted file mode 100644 index 2a5c77197..000000000 --- a/server/src/state.rs +++ /dev/null @@ -1,361 +0,0 @@ -use crate::block::{BlockUpdateCause, BlockUpdateEvent}; -use crate::broadcasters::movement::LastKnownPositions; -use crate::chunk_entities::ChunkEntities; -use crate::chunk_logic::ChunkHolders; -use crate::config::Config; -use crate::entity::EntitySendEvent; -use crate::lazy::{EntityBuilder, Lazy}; -use crate::network::Network; -use feather_blocks::Block; -use feather_core::level::LevelData; -use feather_core::world::ChunkMap; -use feather_core::{BlockPosition, Chunk, ChunkPosition, Packet, Position}; -use legion::borrow::AtomicRefCell; -use legion::entity::Entity; -use legion::query::{IntoQuery, Read}; -use legion::storage::ComponentTypeId; -use legion::world::World; -use parking_lot::RwLockReadGuard; -use std::ops::{Deref, DerefMut}; -use std::sync::Arc; -use tonks::{ - MacroData, ResourceId, Resources, Scheduler, SystemCtx, SystemData, SystemDataOutput, Trigger, -}; - -/// Resource used internally by `State`. -#[derive(Resource)] -pub struct StateInner { - pub config: Arc, - pub chunk_map: ChunkMap, - pub level: LevelData, - pub chunk_entities: ChunkEntities, - - lazy: Lazy, -} - -impl StateInner { - pub fn new(config: Arc, chunk_map: ChunkMap, level: LevelData) -> Self { - Self { - config, - chunk_map, - level, - chunk_entities: ChunkEntities::default(), - lazy: Lazy::default(), - } - } - - /// See `Lazy::flush()`. - pub fn flush(&self, world: &mut World, scheduler: &mut Scheduler) { - self.lazy.flush(world, scheduler); - } -} - -/// The state of the server. -/// -/// This state wraps numerous commonly-used resources, -/// including the chunk map (block access), config, and -/// various cached data structures, among others. -/// -/// Systems should never require mutable access to the -/// state; it is designed for read-only use. (The chunk -/// map uses `RwLock` internally, so write access isn't -/// needed to update blocks.) -/// -/// # Internal details -/// A custom `tonks::SystemData` implementation -/// is utilized so that functions on `State` can automatically -/// trigger events. For example, the methods which update -/// blocks automatically trigger `BlockUpdateEvent`s. -/// -/// An implication of this implementation is that `State` itself -/// is not a resource; however, `StateInner` is. -pub struct State { - inner: *mut StateInner, - trigger: AtomicRefCell>, // TODO: optimize -} - -unsafe impl Send for State {} - -unsafe impl Sync for State {} - -impl<'a> SystemData<'a> for State { - type Output = &'a Self; - - unsafe fn load_from_resources( - resources: &mut Resources, - ctx: SystemCtx, - world: &World, - ) -> Self { - let inner = resources - .get_mut_unchecked::(tonks::resource_id_for::()) - as *mut StateInner; - let trigger = Trigger::load_from_resources(resources, ctx, world); - - Self { - inner, - trigger: AtomicRefCell::new(trigger), - } - } - - fn resource_reads() -> Vec { - vec![tonks::resource_id_for::()] - } - - fn resource_writes() -> Vec { - vec![] - } - - fn component_reads() -> Vec { - vec![] - } - - fn component_writes() -> Vec { - vec![] - } - - fn before_execution(&'a mut self) -> Self::Output { - self - } - - fn after_execution(&mut self) { - self.trigger.get_mut().after_execution() - } -} - -impl<'a> SystemDataOutput<'a> for &'a State { - type SystemData = State; -} - -impl MacroData for &'static State { - type SystemData = State; -} - -impl State { - /// See `Lazy::exec()`. - pub fn exec(&self, f: impl FnOnce(&mut World) + Send + 'static) { - self.lazy.exec(f) - } - - /// See `Lazy::exec_with_scheduler()`. - pub fn exec_with_scheduler(&self, f: impl FnOnce(&mut World, &mut Scheduler) + Send + 'static) { - self.lazy.exec_with_scheduler(f) - } - - /// See `Lazy::create_entity()`. - pub fn create_entity(&self) -> EntityBuilder { - self.lazy.create_entity() - } - - /// See `Lazy::delete_entity()`. - pub fn delete_entity(&self, entity: Entity) { - self.lazy.delete_entity(entity) - } - - /// Lazily broadcasts a packet to all clients able to see the given entity. - /// - /// The packet will not be sent to `neq`. - pub fn broadcast_entity_update( - &self, - entity: Entity, - packet: P, - neq: Option, - ) { - self.exec_with_scheduler(move |world, scheduler| { - // Use ChunkHolders to determine which players have a hold on the entity's - // chunk, which would allow them to see the entity. - let chunk_holders = scheduler.resources().get::(); - - if let Some(position) = world.get_component::(entity) { - let holders = chunk_holders.holders_for(position.chunk()); - - holders.map(|entities| { - for entity in entities { - if let Some(network) = world.get_component::(*entity) { - if neq.map_or(true, |neq| *entity != neq) { - network.send(packet.clone()); - } - } - } - }); - } - }); - } - - /// Lazily broadcasts a boxed packet to all clients able to see the given entity. - /// - /// The packet will not be sent to `neq`. - pub fn broadcast_entity_update_boxed( - &self, - entity: Entity, - packet: Box, - neq: Option, - ) { - self.exec_with_scheduler(move |world, scheduler| { - // Use ChunkHolders to determine which players have a hold on the entity's - // chunk, which would allow them to see the entity. - let chunk_holders = scheduler.resources().get::(); - - if let Some(position) = world.get_component::(entity) { - let holders = chunk_holders.holders_for(position.chunk()); - - holders.map(|entities| { - for entity in entities { - if let Some(network) = world.get_component::(*entity) { - if neq.map_or(true, |neq| *entity != neq) { - network.send_boxed(packet.box_clone()); - } - } - } - }); - } - }); - } - - /// Lazily broadcasts a packet to all players able to see the given chunk. - /// - /// The packet will not be sent to `neq`. - pub fn broadcast_chunk_update( - &self, - chunk: ChunkPosition, - packet: impl Packet + Clone, - neq: Option, - ) { - self.exec_with_scheduler(move |world, scheduler| { - // Use ChunkHolders to determine which players have a hold on the - // chunk, which would allow them to see the entity. - let chunk_holders = scheduler.resources().get::(); - - let holders = chunk_holders.holders_for(chunk); - - holders.map(|entities| { - for entity in entities { - if let Some(network) = world.get_component::(*entity) { - if neq.map_or(true, |neq| *entity != neq) { - network.send(packet.clone()); - } - } - } - }); - }); - } - - /// Lazily broadcasts a packet to all clients. - pub fn broadcast_global(&self, packet: P, neq: Option) { - self.exec(move |world| { - // Standard Legion queries! How rare. - let query = >::query(); - - query.par_entities_for_each(world, |(entity, network)| { - if neq.map_or(true, |neq| entity != neq) { - network.send(packet.clone()); - } - }); - }); - } - - /// Lazily broadcasts a boxed packet to all clients. - pub fn broadcast_global_boxed(&self, packet: Box, neq: Option) { - self.exec(move |world| { - // Standard Legion queries! How rare. - let query = >::query(); - - query.par_entities_for_each(world, |(entity, network)| { - if neq.map_or(true, |neq| entity != neq) { - network.send_boxed(packet.box_clone()); - } - }); - }); - } - - /// Retrieves the block at the given position, - /// or `None` if the block's chunk is not loaded. - pub fn block_at(&self, pos: BlockPosition) -> Option { - self.chunk_map.block_at(pos) - } - - /// Sets the block at the given position. - /// - /// If the block's chunk's is not loaded, returns `false`; - /// otherwise, returns `true`. - pub fn set_block_at(&self, pos: BlockPosition, block: Block, cause: BlockUpdateCause) -> bool { - let old_block = match self.block_at(pos) { - Some(block) => block, - None => return false, - }; - - let event = BlockUpdateEvent { - cause, - pos, - old_block, - new_block: block, - }; - self.trigger.get_mut().trigger(event); - - self.chunk_map.set_block_at(pos, block) - } - - /// Retrieves a reference to the chunk at the given position, - /// or `None` if it not loaded. - pub fn chunk_at(&self, pos: ChunkPosition) -> Option> { - self.chunk_map.chunk_at(pos) - } - - /// Lazily inserts the given chunk into the chunk map. - pub fn lazy_insert_chunk(&self, chunk: Chunk) { - self.lazy.exec_with_scheduler(move |_, scheduler| unsafe { - scheduler - .resources() - .get_mut_unchecked::(tonks::resource_id_for::()) - .chunk_map - .insert(chunk); - }); - } - - /// Lazily removes the given chunk from the chunk map. - pub fn lazy_remove_chunk(&self, pos: ChunkPosition) { - self.lazy - .exec_with_scheduler(move |_: &mut World, scheduler: &mut Scheduler| unsafe { - scheduler - .resources() - .get_mut_unchecked::(tonks::resource_id_for::()) - .chunk_map - .remove(pos); - }); - } - - /// Registers that an entity was sent to a player, updating some - /// data structures, such as LastKnownPositions. - pub fn register_entity_send(&self, entity: Entity, to: Entity) { - self.exec_with_scheduler(move |world, scheduler| { - let pos = *world.get_component(entity).unwrap(); - if let Some(mut positions) = world.get_component_mut::(to) { - positions.0.insert(entity, pos); - } - - scheduler.trigger(EntitySendEvent { entity, to }); - }); - } - - /// The opposite of `register_entity_send`. - pub fn register_entity_unload(&self, entity: Entity, on: Entity) { - self.exec(move |world| { - if let Some(mut positions) = world.get_component_mut::(on) { - positions.0.remove(&entity); - } - }) - } -} - -impl Deref for State { - type Target = StateInner; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.inner } - } -} - -impl DerefMut for State { - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *self.inner } - } -} diff --git a/server/src/worldgen/noise.rs b/server/src/worldgen/noise.rs index d12e45ec4..d203bece3 100644 --- a/server/src/worldgen/noise.rs +++ b/server/src/worldgen/noise.rs @@ -219,7 +219,7 @@ mod tests { assert_eq!(chunk.len(), 16 * 256 * 16); for x in chunk { - assert_float_eq!(x, 0.0); + assert_eq!(x, 0.0); } } } From a8b8551231bc4bda17c29df2ae0d0afbd8615634 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 15 Mar 2020 20:42:00 -0600 Subject: [PATCH 090/647] Convert chunk_logic systems --- server/src/chunk_logic.rs | 161 ++++++++++++------------------------ server/src/game.rs | 28 ++++++- server/src/io/worker.rs | 4 +- server/src/lib.rs | 19 ++--- server/src/packet_buffer.rs | 2 +- server/src/player/mod.rs | 3 +- server/src/view.rs | 8 +- 7 files changed, 93 insertions(+), 132 deletions(-) diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index 134a31482..18c9f6a9a 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -9,22 +9,19 @@ use feather_core::world::ChunkPosition; use rayon::prelude::*; -use crate::entity::EntityDeleteEvent; -use crate::state::State; -use crate::{chunk_worker, current_time_in_millis, TickCount, TPS}; +use crate::game::Game; +use crate::{chunk_worker, current_time_in_millis, TPS}; use feather_core::entity::EntityData; use feather_core::Chunk; +use fecs::{Entity, World}; use hashbrown::HashSet; -use legion::entity::Entity; -use legion::query::Read; use multimap::MultiMap; use std::collections::VecDeque; use std::sync::Arc; -use tonks::{PreparedWorld, Query, Trigger}; /// A handle for interacting with the chunk /// worker thread. -#[derive(Debug, Clone, Resource)] +#[derive(Debug, Clone)] pub struct ChunkWorkerHandle { pub sender: Sender, pub receiver: Receiver, @@ -45,29 +42,22 @@ pub struct ChunkLoadFailEvent { /// System for receiving loaded chunks from the chunk worker thread. #[system] -fn chunk_load_system( - state: &State, - handle: &ChunkWorkerHandle, - fail_events: &mut Trigger, -) { - while let Ok(reply) = handle.receiver.try_recv() { +fn chunk_load_system(game: &mut Game, world: &mut World) { + while let Ok(reply) = game.chunk_worker_handle.receiver.try_recv() { if let chunk_worker::Reply::LoadedChunk(pos, result) = reply { match result { - Ok((chunk, entities)) => { - state.lazy_insert_chunk(chunk); + Ok((chunk, _entities)) => { + game.chunk_map.insert(chunk); + + game.on_chunk_load(world, pos); - // Trigger event - lazily so it happens after the chunk is inserted into the chunk map - let event = ChunkLoadEvent { pos, entities }; - state.exec_with_scheduler(move |_, scheduler| { - scheduler.trigger(event); - }); + // TODO: entities trace!("Loaded chunk at {:?}", pos); } Err(err) => { warn!("Failed to load chunk at {:?}: {}", pos, err); - let event = ChunkLoadFailEvent { pos }; - fail_events.trigger(event); + game.on_chunk_load_fail(world, pos); } } } @@ -85,67 +75,50 @@ fn chunk_load_system( /// the movement, while other players would be outside of the view /// distance. This technique allows for higher performance and /// avoids constant nearby entity queries. -#[derive(Default, Clone, Debug, Resource)] +#[derive(Default, Clone, Debug)] pub struct ChunkHolders { inner: MultiMap, } impl ChunkHolders { - pub fn holders_for(&self, chunk: ChunkPosition) -> Option<&[Entity]> { - self.inner.get_vec(&chunk).map(|holders| holders.as_slice()) + pub fn holders_for(&self, chunk: ChunkPosition) -> &[Entity] { + self.inner + .get_vec(&chunk) + .map(|holders| holders.as_slice()) + .unwrap_or(&[]) } pub fn chunk_has_holders(&self, chunk: ChunkPosition) -> bool { let holders = self.holders_for(chunk); - !(holders.is_none() || holders.unwrap().is_empty()) + !holders.is_empty() } pub fn insert_holder(&mut self, chunk: ChunkPosition, holder: Entity) { self.inner.insert(chunk, holder); } +} +pub fn remove_chunk_holder(game: &mut Game, chunk: ChunkPosition, holder: Entity) { + if let Some(vec) = game.chunk_holders.inner.get_vec_mut(&chunk) { + let index = vec.iter().position(|e| *e == holder); + if let Some(index) = index { + vec.remove(index); - pub fn remove_holder( - &mut self, - chunk: ChunkPosition, - holder: Entity, - trigger: &mut Trigger, - ) { - if let Some(vec) = self.inner.get_vec_mut(&chunk) { - let index = vec.iter().position(|e| *e == holder); - if let Some(index) = index { - vec.remove(index); - - // Trigger event - let event = ChunkHolderReleaseEvent { - entity: holder, - chunk, - }; - trigger.trigger(event); - } + game.on_chunk_holder_release(chunk, holder); } } } -/// Event triggered when a chunk holder is released. -#[derive(Clone, Debug)] -pub struct ChunkHolderReleaseEvent { - /// The entity which previously held the chunk. - pub entity: Entity, - /// The chunk which the holder was released from. - pub chunk: ChunkPosition, -} - /// The queue of chunks to be unloaded. /// See `chunk_unload` for details. -#[derive(Clone, Debug, Default, Resource)] +#[derive(Clone, Debug, Default)] pub struct ChunkUnloadQueue { /// The internal queue. queue: VecDeque, } /// A chunk to be unloaded. -#[derive(Clone, Debug, Default, Resource)] +#[derive(Clone, Debug, Default)] struct ChunkUnload { /// The position of this chunk. chunk: ChunkPosition, @@ -169,12 +142,7 @@ const CHUNK_UNLOAD_TIME: u64 = TPS * 5; // 5 seconds - TODO make this configurab /// chunks at the edge of their view distance /// to be loaded and unloaded at an alarming rate. #[system] -fn chunk_unload( - state: &State, - unload_queue: &mut ChunkUnloadQueue, - holders: &ChunkHolders, - tick_count: &TickCount, -) { +fn chunk_unload(game: &mut Game) { // Unload chunks which are finished in the queue. // Since chunks are queued in the back and taken out @@ -182,17 +150,17 @@ fn chunk_unload( // were queued the longest time ago. Because of this, // we go through the unloads in the front of the queue // to find which chunks to unload. - while let Some(unload) = unload_queue.queue.front() { - if tick_count.0 >= unload.time { + while let Some(unload) = game.chunk_unload_queue.queue.front() { + if game.tick_count >= unload.time { // Don't unload if new chunk holders have appeared. - if holders.chunk_has_holders(unload.chunk) { - unload_queue.queue.pop_front(); + if game.chunk_holders.chunk_has_holders(unload.chunk) { + game.chunk_unload_queue.queue.pop_front(); continue; } // Unload chunk and pop from queue. - state.lazy_remove_chunk(unload.chunk); - unload_queue.queue.pop_front(); + game.chunk_map.remove(unload.chunk); + game.chunk_unload_queue.queue.pop_front(); } else { // We're done - all chunks farther up in // the queue were queued before this one, @@ -204,21 +172,15 @@ fn chunk_unload( /// Event handler which handles holder release events. If /// a chunk has no more holders, then a chunk unload is queued. -#[event_handler] -pub fn chunk_unload_no_holders( - event: &ChunkHolderReleaseEvent, - holders: &ChunkHolders, - unload_queue: &mut ChunkUnloadQueue, - tick_count: &TickCount, -) { +pub fn on_chunk_holder_release_unload_chunk(game: &mut Game, chunk: ChunkPosition) { // Handle holder release events. // If the chunk now has zero holders, queue it for unloading. - if !holders.chunk_has_holders(event.chunk) { + if !game.chunk_holders.chunk_has_holders(chunk) { let unload = ChunkUnload { - chunk: event.chunk, - time: tick_count.0 + CHUNK_UNLOAD_TIME, + chunk, + time: game.tick_count + CHUNK_UNLOAD_TIME, }; - unload_queue.queue.push_back(unload); + game.chunk_unload_queue.queue.push_back(unload); } } @@ -245,19 +207,12 @@ impl ChunkHolder { /// System for removing an entity's chunk holds /// once it is destroyed. -#[event_handler] -fn chunk_holder_remove( - event: &EntityDeleteEvent, - _query: &mut Query>, - world: &mut PreparedWorld, - holders: &mut ChunkHolders, - release_events: &mut Trigger, -) { +pub fn on_entity_despawn_remove_chunk_holder(game: &mut Game, world: &mut World, entity: Entity) { // If entity had chunk holds, remove them all - if let Some(holder_comp) = world.get_component::(event.entity) { - debug!("Removing chunk holds for entity {:?}", event.entity); + if let Some(holder_comp) = world.try_get::(entity) { + debug!("Removing chunk holds for entity {:?}", entity); holder_comp.holds.iter().for_each(|chunk| { - holders.remove_holder(*chunk, event.entity, release_events); + remove_chunk_holder(game, *chunk, entity); }); } } @@ -275,9 +230,9 @@ const CHUNK_OPTIMIZE_INTERVAL: u64 = TPS * 60 * 5; // 5 minutes /// concurrent - each chunk optimization is split /// into a separate job and fed into `rayon`. #[system] -fn chunk_optimize(state: &State, tick_count: &TickCount) { +fn chunk_optimize(game: &mut Game) { // Only run every CHUNK_OPTIMIZE_INTERVAL ticks - if tick_count.0 % CHUNK_OPTIMIZE_INTERVAL != 0 { + if game.tick_count % CHUNK_OPTIMIZE_INTERVAL != 0 { return; } @@ -286,7 +241,7 @@ fn chunk_optimize(state: &State, tick_count: &TickCount) { let start_time = current_time_in_millis(); let count = AtomicU32::new(0); - state.chunk_map.par_iter_chunks().for_each(|chunk| { + game.chunk_map.par_iter_chunks().for_each(|chunk| { count.fetch_add(chunk.write().optimize(), Ordering::Relaxed); }); @@ -302,26 +257,16 @@ fn chunk_optimize(state: &State, tick_count: &TickCount) { } /// Adds a hold for a chunk for the given entity. -pub fn hold_chunk( - entity: Entity, - holder: &mut ChunkHolder, - holders: &mut ChunkHolders, - chunk: ChunkPosition, -) { +pub fn hold_chunk(game: &mut Game, holder: &mut ChunkHolder, chunk: ChunkPosition, entity: Entity) { holder.holds.insert(chunk); - holders.inner.insert(chunk, entity); + game.chunk_holders.inner.insert(chunk, entity); } /// Releases a hold for a chunk for the given entity. -pub fn release_chunk( - entity: Entity, - holder: &mut ChunkHolder, - holders: &mut ChunkHolders, - chunk: ChunkPosition, - trigger: &mut Trigger, -) { +pub fn release_chunk(game: &mut Game, world: &mut World, chunk: ChunkPosition, entity: Entity) { + let mut holder = world.get_mut::(entity); holder.holds.remove(&chunk); - if let Some(vec) = holders.inner.get_vec_mut(&chunk) { + if let Some(vec) = game.chunk_holders.inner.get_vec_mut(&chunk) { let mut index = None; for (i, e) in vec.iter().enumerate() { if *e == entity { @@ -333,7 +278,7 @@ pub fn release_chunk( vec.swap_remove(index); } } - trigger.trigger(ChunkHolderReleaseEvent { entity, chunk }) + game.on_chunk_holder_release(chunk, entity); } /// Asynchronously loads the chunk at the given position. diff --git a/server/src/game.rs b/server/src/game.rs index fa5c1ea0e..845702d13 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,11 +1,12 @@ +use crate::chunk_logic::{ChunkHolders, ChunkUnloadQueue, ChunkWorkerHandle}; use crate::config::Config; use crate::io::{NetworkIoManager, NewClientInfo}; -use crate::player; +use crate::{chunk_logic, player}; use bumpalo::Bump; use feather_blocks::Block; use feather_core::level::LevelData; use feather_core::world::ChunkMap; -use feather_core::BlockPosition; +use feather_core::{BlockPosition, ChunkPosition}; use fecs::{Entity, World}; use std::sync::atomic::AtomicU32; use std::sync::Arc; @@ -35,6 +36,12 @@ pub struct Game { pub chunk_map: ChunkMap, /// Bump allocator. Reset every tick. pub bump: Bump, + /// Chunk worker handle used for communication with + /// the chunk worker. + pub chunk_worker_handle: ChunkWorkerHandle, + /// Queue of chunks to be unloaded. + pub chunk_unload_queue: ChunkUnloadQueue, + pub chunk_holders: ChunkHolders, } impl Game { @@ -63,7 +70,7 @@ impl Game { /// as it properly handles events. pub fn despawn(&mut self, entity: Entity, world: &mut World) { world.despawn(entity); - self.on_despawn(world, entity); + self.on_entity_despawn(world, entity); } /// Spawns a player with the given `PlayerInfo`. @@ -84,8 +91,21 @@ impl Game { } /// Called when an entity is despawned/removed. - pub fn on_despawn(&mut self, _world: &mut World, _entity: Entity) {} + pub fn on_entity_despawn(&mut self, world: &mut World, entity: Entity) { + chunk_logic::on_entity_despawn_remove_chunk_holder(self, world, entity); + } /// Called when a player joins. pub fn on_player_join(&mut self, _world: &mut World, _player: Entity) {} + + /// Called when a chunk loads successfully. + pub fn on_chunk_load(&mut self, _world: &mut World, _chunk: ChunkPosition) {} + + /// Called when a chunk fails to load. + pub fn on_chunk_load_fail(&mut self, _world: &mut World, _chunk: ChunkPosition) {} + + /// Called when a chunk holder is released. + pub fn on_chunk_holder_release(&mut self, chunk: ChunkPosition, _holder: Entity) { + chunk_logic::on_chunk_holder_release_unload_chunk(self, chunk); + } } diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index 57ff24d2a..88468c56a 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -95,7 +95,7 @@ pub async fn run_worker( }; let msg = match run_worker_impl(&mut worker).await { - Ok(()) => "normal disconnect".to_string(), + Ok(()) => String::from("client disconnected"), Err(e) => format!("{}", e), }; @@ -131,7 +131,7 @@ async fn run_worker_impl(worker: &mut Worker) -> anyhow::Result<()> { } } Either::Right((packet_res, _)) => { - let packet_res = packet_res.ok_or(anyhow::anyhow!("packet was None"))?; + let packet_res = packet_res.ok_or(anyhow::anyhow!("client disconnected"))?; let packet = packet_res?; diff --git a/server/src/lib.rs b/server/src/lib.rs index 173291336..00d28958e 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -106,25 +106,19 @@ extern crate lazy_static; #[macro_use] extern crate feather_core; #[macro_use] -extern crate bitflags; -#[macro_use] extern crate fecs; -#[macro_use] -extern crate num_derive; -#[macro_use] -extern crate feather_codegen; extern crate nalgebra_glm as glm; use crossbeam::Receiver; use std::alloc::System; -use std::sync::atomic::{AtomicU32, AtomicUsize}; +use std::sync::atomic::AtomicU32; use std::sync::Arc; use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use crate::chunk_logic::ChunkWorkerHandle; use crate::config::Config; use crate::game::Game; -use crate::io::NetworkIoManager; use crate::packet_buffer::PacketBuffers; use crate::worldgen::{ ComposableGenerator, EmptyWorldGenerator, SuperflatWorldGenerator, WorldGenerator, @@ -148,7 +142,7 @@ static ALLOC: System = System; // pub mod block; // pub mod broadcasters; // pub mod chunk_entities; -// pub mod chunk_logic; +pub mod chunk_logic; pub mod chunk_worker; pub mod config; pub mod entity; @@ -219,7 +213,7 @@ pub fn main() { exit(1) }); - // let chunk_worker_handle = init_chunk_worker(world_dir, &level); + let chunk_worker_handle = init_chunk_worker(world_dir, &level); let game = Game { io_handle, @@ -229,6 +223,9 @@ pub fn main() { level, chunk_map: ChunkMap::new(), bump: Bump::new(), + chunk_worker_handle, + chunk_unload_queue: Default::default(), + chunk_holders: Default::default(), }; let (executor, resources) = init_executor(game); @@ -316,7 +313,6 @@ fn init_executor(game: Game) -> (Executor, Resources) { (executor, resources) } -/* /// Initializes the chunk worker. fn init_chunk_worker(world_dir: &Path, level: &LevelData) -> ChunkWorkerHandle { let generator: Arc = match level.generator_type() { @@ -335,7 +331,6 @@ fn init_chunk_worker(world_dir: &Path, level: &LevelData) -> ChunkWorkerHandle { receiver: rx, } } -*/ /// Loads the configuration file, creating a default /// one if it does not exist. diff --git a/server/src/packet_buffer.rs b/server/src/packet_buffer.rs index 87e20fde0..7656807ff 100644 --- a/server/src/packet_buffer.rs +++ b/server/src/packet_buffer.rs @@ -30,8 +30,8 @@ pub struct PacketBuffers { buffers: Vec, } -/// The set of buffers which use a `MapBuffer` instead of an `ArrayBuffer`. lazy_static! { + /// The set of buffers which use a `MapBuffer` instead of an `ArrayBuffer`. static ref USE_MAP_FOR: indexmap::IndexSet = indexmap::indexset![]; } diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index a6a049185..8dc9cef7b 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -1,5 +1,6 @@ //! Systems and components specific to player entities. +use crate::chunk_logic::ChunkHolder; use crate::io::NewClientInfo; use crate::network::Network; use feather_core::Gamemode; @@ -39,7 +40,7 @@ pub fn create(world: &mut World, info: NewClientInfo) -> Entity { world.add(entity, info.ip).unwrap(); world.add(entity, ProfileProperties(info.profile)).unwrap(); //world.add(entity, Name(info.username)).unwrap(); - //world.add(entity, ChunkHolder::default()).unwrap(); + world.add(entity, ChunkHolder::default()).unwrap(); //world.add(entity, Joined(false)).unwrap(); //world.add(entity, LastKnownPositions::default()).unwrap(); //world.add(entity, SpawnPacketCreator(&create_spawn_packet)).unwrap(); diff --git a/server/src/view.rs b/server/src/view.rs index 2dbc34bba..a98d0854e 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -12,10 +12,10 @@ //! delete old ones. //! //! This is handled as follows: -//! * A system listens for player move events and checks if the player -//! crossed a chunk boundary. If so, a `ViewUpdateEvent` is triggered. -//! * Various systems listen to `ViewUpdateEvent` and send necessary packets. -//! This includes systems to load/unload chunks and send entities. +//! * A system queries all position components which have changed +//! and adds a `CrossedChunk` component to these entities. +//! * Other systems query for added `CrossedChunk` components +//! and perform updates on these players' views. use crate::chunk_logic; use crate::chunk_logic::{ From d4028eb1abc95017c2197b66c29f7da7965f7fd4 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 00:28:39 -0600 Subject: [PATCH 091/647] Refactor view and chunk logic --- Cargo.lock | 18 +- server/Cargo.toml | 1 + server/src/chunk_logic.rs | 6 +- server/src/entity/mod.rs | 30 ++- server/src/game.rs | 36 +++- server/src/join.rs | 110 +++++------ server/src/lib.rs | 19 +- server/src/network.rs | 4 +- server/src/player/mod.rs | 4 +- server/src/shutdown.rs | 12 +- server/src/view.rs | 388 +++++++++++--------------------------- 11 files changed, 237 insertions(+), 391 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e94bfe251..4d6ad901d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -382,7 +382,7 @@ dependencies = [ "clap", "criterion-plot", "csv", - "itertools", + "itertools 0.8.2", "lazy_static", "num-traits 0.2.11", "rand_core", @@ -403,7 +403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eccdc6ce8bbe352ca89025bee672aa6d24f4eb8c53e3a8b5d1bc58011da072a2" dependencies = [ "cast", - "itertools", + "itertools 0.8.2", ] [[package]] @@ -723,6 +723,7 @@ dependencies = [ "humantime-serde", "indexmap", "inventory", + "itertools 0.9.0", "lazy_static", "lock_api", "log", @@ -1222,6 +1223,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.4" @@ -1274,7 +1284,7 @@ dependencies = [ "derivative 1.0.3", "downcast-rs", "fxhash", - "itertools", + "itertools 0.8.2", "parking_lot", "rayon", "smallvec", @@ -1292,7 +1302,7 @@ dependencies = [ "derivative 1.0.3", "downcast-rs", "fxhash", - "itertools", + "itertools 0.8.2", "legion-core", "paste", "rayon", diff --git a/server/Cargo.toml b/server/Cargo.toml index 3ed451249..5e03a43e3 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -88,6 +88,7 @@ humantime-serde = "1.0" ctrlc = "3.1" inventory = "0.1" derivative = "2.0" +itertools = "0.9" # Error handling thiserror = "1.0" diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index 18c9f6a9a..a44f1bb33 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -42,7 +42,7 @@ pub struct ChunkLoadFailEvent { /// System for receiving loaded chunks from the chunk worker thread. #[system] -fn chunk_load_system(game: &mut Game, world: &mut World) { +pub fn chunk_load(game: &mut Game, world: &mut World) { while let Ok(reply) = game.chunk_worker_handle.receiver.try_recv() { if let chunk_worker::Reply::LoadedChunk(pos, result) = reply { match result { @@ -142,7 +142,7 @@ const CHUNK_UNLOAD_TIME: u64 = TPS * 5; // 5 seconds - TODO make this configurab /// chunks at the edge of their view distance /// to be loaded and unloaded at an alarming rate. #[system] -fn chunk_unload(game: &mut Game) { +pub fn chunk_unload(game: &mut Game) { // Unload chunks which are finished in the queue. // Since chunks are queued in the back and taken out @@ -230,7 +230,7 @@ const CHUNK_OPTIMIZE_INTERVAL: u64 = TPS * 60 * 5; // 5 minutes /// concurrent - each chunk optimization is split /// into a separate job and fed into `rayon`. #[system] -fn chunk_optimize(game: &mut Game) { +pub fn chunk_optimize(game: &mut Game) { // Only run every CHUNK_OPTIMIZE_INTERVAL ticks if game.tick_count % CHUNK_OPTIMIZE_INTERVAL != 0 { return; diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 2679e414d..b1ec9ffdd 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -6,7 +6,7 @@ // pub mod item; use feather_core::Position; -use fecs::EntityBuilder; +use fecs::{EntityBuilder, IntoQuery, Read, World, Write}; use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicI32, Ordering}; @@ -47,13 +47,13 @@ impl DerefMut for Velocity { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Name(pub String); -/* /// Position of an entity on the last tick. /// /// This is updated by `position_reset` system. #[derive(Debug, Clone, Copy)] pub struct PreviousPosition(pub Position); +/* pub trait PacketCreatorFn: Fn(&EntityAccessor, &PreparedWorld) -> Box + Send + Sync + 'static { @@ -100,23 +100,17 @@ impl CreationPacketCreator { f(accessor, world) } } +*/ -#[event_handler] -pub fn position_reset( - events: &[EntityMoveEvent], - _query: &mut Query<(Read, Write)>, - world: &mut PreparedWorld, -) { - events.iter().for_each(|event| { - let pos = *world.get_component::(event.entity).unwrap(); - let mut prev_pos = world - .get_component_mut::(event.entity) - .unwrap(); - - prev_pos.0 = pos; - }); +#[system] +pub fn position_reset(world: &mut World) { + <(Read, Write)>::query().par_for_each_mut( + world.inner_mut(), + |(pos, mut previous_pos)| { + previous_pos.0 = *pos; + }, + ); } -*/ /// Inserts the base components for an entity into an `EntityBuilder`. /// @@ -130,7 +124,7 @@ pub fn base(position: Position) -> EntityBuilder { EntityBuilder::new() .with(EntityId(id)) .with(position) - //.with(PreviousPosition(position)) + .with(PreviousPosition(position)) .with(Velocity::default()) } diff --git a/server/src/game.rs b/server/src/game.rs index 845702d13..7ec928af1 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,6 +1,11 @@ use crate::chunk_logic::{ChunkHolders, ChunkUnloadQueue, ChunkWorkerHandle}; use crate::config::Config; use crate::io::{NetworkIoManager, NewClientInfo}; +use crate::join::{on_chunk_send_join_player, on_player_join_send_join_game}; +use crate::view::{ + on_chunk_cross_update_chunks, on_chunk_load_send_to_clients, + on_player_join_trigger_chunk_cross, ChunksToSend, +}; use crate::{chunk_logic, player}; use bumpalo::Bump; use feather_blocks::Block; @@ -42,6 +47,7 @@ pub struct Game { /// Queue of chunks to be unloaded. pub chunk_unload_queue: ChunkUnloadQueue, pub chunk_holders: ChunkHolders, + pub chunks_to_send: ChunksToSend, } impl Game { @@ -96,10 +102,15 @@ impl Game { } /// Called when a player joins. - pub fn on_player_join(&mut self, _world: &mut World, _player: Entity) {} + pub fn on_player_join(&mut self, world: &mut World, player: Entity) { + on_player_join_trigger_chunk_cross(self, world, player); + on_player_join_send_join_game(self, world, player); + } /// Called when a chunk loads successfully. - pub fn on_chunk_load(&mut self, _world: &mut World, _chunk: ChunkPosition) {} + pub fn on_chunk_load(&mut self, world: &mut World, chunk: ChunkPosition) { + on_chunk_load_send_to_clients(self, world, chunk); + } /// Called when a chunk fails to load. pub fn on_chunk_load_fail(&mut self, _world: &mut World, _chunk: ChunkPosition) {} @@ -108,4 +119,25 @@ impl Game { pub fn on_chunk_holder_release(&mut self, chunk: ChunkPosition, _holder: Entity) { chunk_logic::on_chunk_holder_release_unload_chunk(self, chunk); } + + /// Called when an entity crosses into a new chunk. + pub fn on_chunk_cross( + &mut self, + world: &mut World, + entity: Entity, + old: Option, + new: ChunkPosition, + ) { + on_chunk_cross_update_chunks(self, world, entity, old, new); + } + + /// Called when a chunk is sent to a client. + pub fn on_chunk_send(&self, world: &mut World, chunk: ChunkPosition, player: Entity) { + on_chunk_send_join_player(self, world, chunk, player); + } +} + +#[system] +pub fn increment_tick_count(game: &mut Game) { + game.tick_count += 1; } diff --git a/server/src/join.rs b/server/src/join.rs index 65694ac34..a306fca96 100644 --- a/server/src/join.rs +++ b/server/src/join.rs @@ -3,97 +3,75 @@ //! among others. This is handled by the event handler `join`. use crate::entity::EntityId; +use crate::game::Game; use crate::network::Network; -use crate::player::PlayerJoinEvent; -use crate::state::State; -use crate::view::ChunkSendEvent; use feather_core::network::packet::implementation::{ JoinGame, PlayerPositionAndLookClientbound, SpawnPosition, }; -use feather_core::{BlockPosition, Gamemode, Position}; -use legion::query::{Read, Write}; -use parking_lot::RwLock; -use rayon::prelude::*; -use tonks::{PreparedWorld, Query}; +use feather_core::{BlockPosition, ChunkPosition, Difficulty, Dimension, Gamemode, Position}; +use fecs::{Entity, World}; -/// Component indicating whether a player has completed the join sequence. +/// Component indicating that a player has completed the join sequence. #[derive(Default, Debug)] -pub struct Joined(pub bool); +pub struct Joined; /// System to run the join sequence. To determine when a player is ready to join, /// we wait for the chunk that the player is in to be sent—this appears to work /// well with the client. -#[event_handler] -fn join( - events: &[ChunkSendEvent], - _query: &mut Query<(Write, Read, Read)>, - world: &mut PreparedWorld, - state: &State, +pub fn on_chunk_send_join_player( + game: &Game, + world: &mut World, + chunk: ChunkPosition, + player: Entity, ) { - let world = RwLock::new(world); - events.par_iter().for_each(|event| { - let pos = { - let world = world.read(); + if world.try_get::(player).is_some() { + return; // already joined + } - let pos = world.get_component::(event.player).unwrap(); - let joined = world.get_component::(event.player).unwrap(); + let pos = { + let pos = world.get::(player); - if pos.chunk() != event.chunk || joined.0 { - return; - } + if pos.chunk() != chunk { + return; + } - *pos - }; + *pos + }; - // Run the join sequence. TODO: inventory. - world - .write() - .get_component_mut::(event.player) - .unwrap() - .0 = true; + // Run the join sequence. + world.add(player, Joined).unwrap(); - let world = world.read(); - let network = world.get_component::(event.player).unwrap(); + let network = world.get::(player); - let packet = SpawnPosition { - location: BlockPosition::new( - state.level.spawn_x, - state.level.spawn_y, - state.level.spawn_z, - ), - }; - network.send(packet); + let packet = SpawnPosition { + location: BlockPosition::new(game.level.spawn_x, game.level.spawn_y, game.level.spawn_z), + }; + network.send(packet); - let packet = PlayerPositionAndLookClientbound { - x: pos.x, - y: pos.y, - z: pos.z, - yaw: pos.yaw, - pitch: pos.pitch, - flags: 0, - teleport_id: 0, - }; - network.send(packet); - }); + let packet = PlayerPositionAndLookClientbound { + x: pos.x, + y: pos.y, + z: pos.z, + yaw: pos.yaw, + pitch: pos.pitch, + flags: 0, + teleport_id: 0, + }; + network.send(packet); } -#[event_handler] -fn send_join_game( - event: &PlayerJoinEvent, - _query: &mut Query<(Read, Read)>, - world: &mut PreparedWorld, -) { - let network = world.get_component::(event.player).unwrap(); - let id = world.get_component::(event.player).unwrap(); +pub fn on_player_join_send_join_game(game: &Game, world: &World, player: Entity) { + let network = world.get::(player); + let id = world.get::(player); // TODO let packet = JoinGame { entity_id: id.0, gamemode: Gamemode::Creative.id(), - dimension: 0, - difficulty: 0, - max_players: 0, - level_type: "default".to_string(), + dimension: Dimension::Overwold.id(), + difficulty: Difficulty::Medium.id(), + max_players: game.config.server.max_players as u8, + level_type: game.level.generator_name.clone(), reduced_debug_info: false, }; network.send(packet); diff --git a/server/src/lib.rs b/server/src/lib.rs index 00d28958e..d8ef097e0 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -147,7 +147,7 @@ pub mod chunk_worker; pub mod config; pub mod entity; pub mod io; -// pub mod join; +mod join; // pub mod lazy; // pub mod metadata; pub mod network; @@ -158,9 +158,9 @@ pub mod player; pub mod shutdown; // pub mod time; // pub mod util; -// pub mod view; pub mod game; pub mod packet_buffer; +mod view; pub mod worldgen; pub type BumpVec<'a, T> = bumpalo::collections::Vec<'a, T>; @@ -226,6 +226,7 @@ pub fn main() { chunk_worker_handle, chunk_unload_queue: Default::default(), chunk_holders: Default::default(), + chunks_to_send: Default::default(), }; let (executor, resources) = init_executor(game); @@ -304,11 +305,15 @@ fn init_executor(game: Game) -> (Executor, Resources) { let mut resources = Resources::new(); resources.insert(game); - // todo: https://github.com/dtolnay/inventory/issues/9yutfyy5fttyhfrgthgftjhdgthyhdjfjtcynjncdtnhgd - let mut executor = Executor::new(); - - executor.add(network::poll_new_clients); - executor.add(network::poll_player_disconnect); + let executor = Executor::new() + .with(network::poll_new_clients) + .with(network::poll_player_disconnect) + .with(chunk_logic::chunk_load) + .with(chunk_logic::chunk_unload) + .with(chunk_logic::chunk_optimize) + .with(view::check_crossed_chunks) + .with(game::increment_tick_count) + .with(entity::position_reset); // should be at end (executor, resources) } diff --git a/server/src/network.rs b/server/src/network.rs index 8f60a6250..2fadb77cb 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -58,7 +58,7 @@ pub fn poll_player_disconnect(game: &mut Game, world: &mut World) { while let Ok(msg) = network.rx.try_recv() { match msg { WorkerToServerMessage::NotifyDisconnected { reason } => { - log::debug!("Server observed player disconnect: caused by {}", reason); + log::debug!("Server observed player disconnect: {}", reason); to_despawn.push(entity); } } @@ -66,7 +66,7 @@ pub fn poll_player_disconnect(game: &mut Game, world: &mut World) { }); to_despawn.into_iter().for_each(|entity| { - world.despawn(entity); + game.despawn(entity, world); }); } diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index 8dc9cef7b..b30a4673c 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -1,6 +1,8 @@ //! Systems and components specific to player entities. use crate::chunk_logic::ChunkHolder; +use crate::entity; +use crate::entity::EntityId; use crate::io::NewClientInfo; use crate::network::Network; use feather_core::Gamemode; @@ -25,6 +27,7 @@ pub struct Player; pub fn create(world: &mut World, info: NewClientInfo) -> Entity { // TODO: blocked on https://github.com/TomGillen/legion/issues/36 let entity = info.entity; + world.add(entity, EntityId(entity::new_id())).unwrap(); world.add(entity, info.position).unwrap(); world.add(entity, info.uuid).unwrap(); world.add(entity, info.uuid).unwrap(); @@ -41,7 +44,6 @@ pub fn create(world: &mut World, info: NewClientInfo) -> Entity { world.add(entity, ProfileProperties(info.profile)).unwrap(); //world.add(entity, Name(info.username)).unwrap(); world.add(entity, ChunkHolder::default()).unwrap(); - //world.add(entity, Joined(false)).unwrap(); //world.add(entity, LastKnownPositions::default()).unwrap(); //world.add(entity, SpawnPacketCreator(&create_spawn_packet)).unwrap(); //world.add(entity, CreationPacketCreator(&create_initialization_packet)).unwrap(); diff --git a/server/src/shutdown.rs b/server/src/shutdown.rs index 276e84fdf..19cec101e 100644 --- a/server/src/shutdown.rs +++ b/server/src/shutdown.rs @@ -9,14 +9,8 @@ pub fn init(tx: Sender<()>) { .unwrap(); } -pub fn save_chunks(_world: &mut World) { - unimplemented!() -} +pub fn save_chunks(_world: &mut World) {} -pub fn save_level(_world: &World) { - unimplemented!() -} +pub fn save_level(_world: &World) {} -pub fn save_player_data(_world: &World) { - unimplemented!() -} +pub fn save_player_data(_world: &World) {} diff --git a/server/src/view.rs b/server/src/view.rs index a98d0854e..d0de5bce0 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -17,331 +17,166 @@ //! * Other systems query for added `CrossedChunk` components //! and perform updates on these players' views. -use crate::chunk_logic; -use crate::chunk_logic::{ - ChunkHolder, ChunkHolderReleaseEvent, ChunkHolders, ChunkLoadEvent, ChunkWorkerHandle, -}; -use crate::config::Config; -use crate::entity::{EntityId, EntityMoveEvent, PreviousPosition, SpawnPacketCreator}; +use crate::entity::PreviousPosition; +use crate::game::Game; use crate::network::Network; -use crate::player::{Player, PlayerJoinEvent}; -use crate::state::State; -use chashmap::CHashMap; -use feather_core::network::packet::implementation::{ChunkData, DestroyEntities, UnloadChunk}; +use crate::player::Player; +use crate::{chunk_logic, BumpVec}; +use ahash::AHashMap; +use feather_core::network::packet::implementation::{ChunkData, UnloadChunk}; use feather_core::{Chunk, ChunkPosition, Position}; -use hashbrown::HashSet; -use legion::entity::Entity; -use legion::query::{Read, Write}; -use parking_lot::Mutex; -use rayon::prelude::*; +use fecs::{Entity, IntoQuery, Read, World}; +use itertools::Either; use smallvec::SmallVec; -use tonks::{PreparedWorld, Query, QueryAccessor, Trigger}; - -/// Event triggered when a player's view is updated, i.e. when they -/// cross into a new chunk or when they join. -pub struct ViewUpdateEvent { - /// The player whose view was updated. - pub player: Entity, - /// The new chunk. - pub new_chunk: ChunkPosition, - /// The old chunk, or `None` if there was no old chunk - /// (i.e. this player just joined). - pub old_chunk: Option, - /// Old visible chunks. - pub visible_old: HashSet, - /// New visible chunks. - pub visible_new: HashSet, -} - -/// Event triggered when a chunk is sent to a player. -#[derive(Debug)] -pub struct ChunkSendEvent { - pub chunk: ChunkPosition, - pub player: Entity, -} - -/// System which checks for players crossing chunk boundaries -/// and triggers `ViewUpdateEvent`s. -#[event_handler] -fn view_update( - events: &[EntityMoveEvent], - _query: &mut Query<(Read, Read, Read)>, - world: &mut PreparedWorld, - state: &State, - trigger: &mut Trigger, -) { - let trigger = Mutex::new(trigger); - events.par_iter().for_each(|event| { - // Only process view for players. - if world.get_component::(event.entity).is_none() { - return; +use std::iter; +use std::ops::Add; + +/// System which polls for updated positions and +/// calls `Game::on_chunk_cross()` accordingly. +#[system] +pub fn check_crossed_chunks(world: &mut World, game: &mut Game) { + let mut crossed = BumpVec::new_in(&game.bump); + for (entity, (pos, prev_pos)) in + <(Read, Read)>::query().iter_entities(world.inner()) + { + if pos.chunk() != prev_pos.0.chunk() { + crossed.push((entity, pos.chunk(), prev_pos.0.chunk())); } + } - let pos = *world.get_component::(event.entity).unwrap(); - let prev_pos = world - .get_component::(event.entity) - .unwrap() - .0; - - // Find the old chunks and new chunks. - let visible_new = chunks_within_view_distance(&state.config, pos.chunk()); - let visible_old = chunks_within_view_distance(&state.config, prev_pos.chunk()); - - if pos.chunk() != prev_pos.chunk() { - // New chunk: trigger view update. - let event = ViewUpdateEvent { - player: event.entity, - new_chunk: pos.chunk(), - old_chunk: Some(prev_pos.chunk()), - visible_old, - visible_new, - }; - trigger.lock().trigger(event); - } - }); + for (entity, new, old) in crossed { + game.on_chunk_cross(world, entity, Some(old), new); + } } -/// System which triggers `ViewUpdateEvent`s on player join. -#[event_handler] -fn view_update_on_join( - event: &PlayerJoinEvent, - _query: &mut Query>, - world: &mut PreparedWorld, - trigger: &mut Trigger, - state: &State, -) { - let position = *world.get_component::(event.player).unwrap(); - - // Find the visible chunks. - let visible_new = chunks_within_view_distance(&state.config, position.chunk()); - - trigger.trigger(ViewUpdateEvent { - player: event.player, - new_chunk: position.chunk(), - old_chunk: None, - visible_new, - visible_old: HashSet::new(), // No chunks were previously visible, since the player just joined - }); +/// Triggers a chunk cross when a new player joins. +pub fn on_player_join_trigger_chunk_cross(game: &mut Game, world: &mut World, player: Entity) { + let chunk = world.get::(player).chunk(); + game.on_chunk_cross(world, player, None, chunk); } /// System which sends new chunks and unloads old chunks on the client /// when the view is updated. -#[event_handler] -fn view_handle_chunks( - events: &[ViewUpdateEvent], - _query: &mut Query<(Read, Write)>, - world: &mut PreparedWorld, - holders: &mut ChunkHolders, - state: &State, - chunks_to_send: &ChunksToSend, - handle: &ChunkWorkerHandle, - holder_release_trigger: &mut Trigger, - chunk_send_trigger: &mut Trigger, +pub fn on_chunk_cross_update_chunks( + game: &mut Game, + world: &mut World, + entity: Entity, + old: Option, + new: ChunkPosition, ) { - events.iter().for_each(|event| { - let to_send = event.visible_new.difference(&event.visible_old); - let to_unload = event.visible_old.difference(&event.visible_new); - - let network = world.get_component::(event.player).unwrap(); - let mut holder = - unsafe { world.get_component_mut_unchecked::(event.player) }.unwrap(); - - // Sort sent chunks so that closer chunks are sent first. - let mut to_send = to_send.copied().collect::>(); - to_send.sort_unstable_by_key(|chunk| { - chunk.manhattan_distance_to(event.new_chunk); - }); + if world.try_get::(entity).is_none() { + return; + } - // Send new chunks. - to_send.into_iter().for_each(|chunk| { - send_chunk_to_player( - state, - event.player, - &network, - &mut holder, - holders, - chunk, - chunks_to_send, - handle, - chunk_send_trigger, - ); - }); + for chunk in find_new_chunks(old, new, game.config.server.view_distance) { + dbg!(chunk); + send_chunk_to_player(game, world, entity, chunk); + } - // Unload old chunks on client. - to_unload.for_each(|chunk| { - unload_chunk_for_player( - event.player, - &network, - holder_release_trigger, - &mut holder, - holders, - *chunk, - ); - }); - }); + for chunk in find_old_chunks(old, new, game.config.server.view_distance) { + unload_chunk_for_player(game, world, chunk, entity); + } } -/// System which sends new entities and removes -/// old entities on the client when the player's -/// view is updated. -/// -/// Before this event handler is run, `crate::broadcast::entity_creation::broadcast_entity_creation` -/// will run, sending entity initialization packets before spawn packets as dictated -/// by the protocol. -#[event_handler] -fn view_handle_entities( - events: &[ViewUpdateEvent], - state: &State, - _query: &mut Query<(Read, Read)>, - accessor: &QueryAccessor>, - world: &mut PreparedWorld, -) { - events.par_iter().for_each(|event: &ViewUpdateEvent| { - let to_send = event.visible_new.difference(&event.visible_old); - let to_unload = event.visible_old.difference(&event.visible_new); - - let network = world.get_component::(event.player).unwrap(); - - // Send new entities. - to_send.copied().for_each(|chunk| { - let entities = state.chunk_entities.entities_in_chunk(chunk); - - entities.iter().copied().for_each(|entity| { - // Don't send client to themself. - if entity == event.player { - return; - } - - // Attempt to create spawn packet for this entity. - if let Some(accessor) = accessor.find(entity) { - if let Some(packet_creator) = - accessor.get_component::(world) - { - // Send packet. - let packet = packet_creator.get(&accessor, world); - network.send_boxed(packet); - state.register_entity_send(entity, event.player); - } - } - }); - }); - - // Remove old entities. - let mut to_delete = vec![]; - for chunk in to_unload.copied() { - for entity in state - .chunk_entities - .entities_in_chunk(chunk) - .iter() - .copied() - { - let id = world.get_component::(entity).unwrap().0; - to_delete.push(id); - state.register_entity_unload(entity, event.player); - } - } +/// Returns new chunks visible from a new chunk position. +fn find_new_chunks( + old: Option, + new: ChunkPosition, + view_distance: u8, +) -> impl Iterator { + let within_view_distance = chunks_within_view_distance(new, view_distance); + if let Some(old) = old { + Either::Left(within_view_distance.filter(move |chunk| { + chunk.x - old.x <= view_distance as i32 && chunk.z - old.z <= view_distance as i32 + })) + } else { + Either::Right(within_view_distance) + } +} - if !to_delete.is_empty() { - let packet = DestroyEntities { - entity_ids: to_delete, - }; - network.send(packet); - } - }); +/// Returns chunks which are no longer visible from a new chunk position. +fn find_old_chunks( + old: Option, + new: ChunkPosition, + view_distance: u8, +) -> impl Iterator { + let within_view_distance = chunks_within_view_distance(new, view_distance); + if let Some(old) = old { + Either::Left(within_view_distance.filter(move |chunk| { + chunk.x - old.x >= view_distance as i32 && chunk.z - old.z >= view_distance as i32 + })) + } else { + Either::Right(iter::empty()) + } } /// Resource containing a mapping from chunks -> sets of players indicating /// which chunks are pending to send to a given player. -#[derive(Default, Resource)] -pub struct ChunksToSend(CHashMap>); +#[derive(Default)] +pub struct ChunksToSend(AHashMap>); /// Asynchronously sends a chunk to a player. -#[allow(clippy::too_many_arguments)] -fn send_chunk_to_player( - state: &State, - player: Entity, - network: &Network, - holder: &mut ChunkHolder, - holders: &mut ChunkHolders, - chunk: ChunkPosition, - chunks_to_send: &ChunksToSend, - handle: &ChunkWorkerHandle, - trigger: &mut Trigger, -) { +fn send_chunk_to_player(game: &mut Game, world: &mut World, player: Entity, chunk: ChunkPosition) { // Ensure that the chunk isn't unloaded while the player has it loaded. - chunk_logic::hold_chunk(player, holder, holders, chunk); + chunk_logic::hold_chunk(game, &mut *world.get_mut(player), chunk, player); // If the chunk is already loaded, send it. Otherwise, we need to // queue it for loading. - if let Some(chunk) = state.chunk_at(chunk) { - network.send(create_chunk_data(&chunk)); - trigger.trigger(ChunkSendEvent { - chunk: chunk.position(), - player, - }); + if let Some(chunk) = game.chunk_map.chunk_at(chunk) { + world.get::(player).send(create_chunk_data(&chunk)); + game.on_chunk_send(world, chunk.position(), player); } else { - let contains = chunks_to_send.0.contains_key(&chunk); + let contains = game.chunks_to_send.0.contains_key(&chunk); - let mut vec = match chunks_to_send.0.get_mut(&chunk) { + let vec = match game.chunks_to_send.0.get_mut(&chunk) { Some(vec) => vec, None => { - chunks_to_send.0.insert(chunk, smallvec![]); - chunks_to_send.0.get_mut(&chunk).unwrap() + game.chunks_to_send.0.insert(chunk, smallvec![]); + game.chunks_to_send.0.get_mut(&chunk).unwrap() } }; vec.push(player); if !contains { // Queue chunk for loading if it isn't already. - chunk_logic::load_chunk(handle, chunk); + chunk_logic::load_chunk(&game.chunk_worker_handle, chunk); } } } /// Unloads a chunk on a client. fn unload_chunk_for_player( - player: Entity, - network: &Network, - trigger: &mut Trigger, - holder: &mut ChunkHolder, - holders: &mut ChunkHolders, + game: &mut Game, + world: &mut World, chunk: ChunkPosition, + player: Entity, ) { // Release hold on chunk so it can be unloaded on the server - chunk_logic::release_chunk(player, holder, holders, chunk, trigger); + chunk_logic::release_chunk(game, world, chunk, player); // Send Unload Chunk packet. - network.send(UnloadChunk { + world.get::(player).send(UnloadChunk { chunk_x: chunk.x, chunk_z: chunk.z, }); } /// System which sends chunks to pending players when a chunk is loaded. -#[event_handler] -fn chunk_send( - event: &ChunkLoadEvent, - state: &State, - to_send: &ChunksToSend, - _query: &mut Query>, - world: &mut PreparedWorld, - trigger: &mut Trigger, -) { - if let Some(players) = to_send.0.get(&event.pos) { - let chunk = state - .chunk_at(event.pos) +pub fn on_chunk_load_send_to_clients(game: &mut Game, world: &mut World, chunk: ChunkPosition) { + if let Some(players) = game.chunks_to_send.0.get(&chunk) { + let chunk = game + .chunk_map + .chunk_at(chunk) .expect("chunk not loaded, but load event was triggered"); players.iter().for_each(|player| { - let network = world.get_component::(*player).unwrap(); - network.send(create_chunk_data(&chunk)); - trigger.trigger(ChunkSendEvent { - chunk: chunk.position(), - player: *player, - }); + world + .get::(*player) + .send(create_chunk_data(&chunk)); + game.on_chunk_send(world, chunk.position(), *player); }); } - to_send.0.remove(&event.pos); + game.chunks_to_send.0.remove(&chunk); } /// Creates a chunk data packet for the given chunk. @@ -352,18 +187,13 @@ fn create_chunk_data(chunk: &Chunk) -> ChunkData { } /// Finds all chunks within the view distance of a given chunk. -fn chunks_within_view_distance(config: &Config, position: ChunkPosition) -> HashSet { - let view_distance = config.server.view_distance as i32; - - let dimensions = view_distance * 2 + 1; - - let mut set = HashSet::with_capacity((dimensions * dimensions) as usize); - - for x in -view_distance..=view_distance { - for z in -view_distance..=view_distance { - set.insert(position + ChunkPosition::new(x, z)); - } - } +fn chunks_within_view_distance( + chunk: ChunkPosition, + view_distance: u8, +) -> impl Iterator { + let view_distance = i32::from(view_distance); - set + (-view_distance..=view_distance).flat_map(move |x| { + (-view_distance..=view_distance).map(move |z| chunk.add(ChunkPosition::new(x, z))) + }) } From 4835c7c307090f47523ecaac2b0e7e54f61036b0 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 00:41:03 -0600 Subject: [PATCH 092/647] Implement chunk entities --- server/src/broadcasters/mod.rs | 9 +-- server/src/chunk_entities.rs | 120 ++++++++++----------------------- server/src/game.rs | 11 ++- server/src/lib.rs | 3 +- 4 files changed, 54 insertions(+), 89 deletions(-) diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index 603f11e0a..2dfa9c8a2 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -1,10 +1,11 @@ -//! Systems which broadcast packets based on events. +//! Systems which send packets based on events. //! -//! There are three types of broadcasters: -//! * Those which broadcast packets to all online clients through `State::broadcast_global()`. -//! * Those which broadcast packets to all clients who can see a given entity through `State::broadcast_entity_update()`. +//! There are four types of broadcasters: +//! * Those which broadcast packets to all online clients through `Game::broadcast_global()`. +//! * Those which broadcast packets to all clients who can see a given entity through `Game::broadcast_entity_update()`. //! * Those which send additional packets, such as equipment, etc. after entity spawning //! packets have been sent. This is done through `EntitySendEvent`. +//! * Those which just send a packet to a single player. mod animation; mod block; diff --git a/server/src/chunk_entities.rs b/server/src/chunk_entities.rs index a336e515a..3413f02ae 100644 --- a/server/src/chunk_entities.rs +++ b/server/src/chunk_entities.rs @@ -1,14 +1,9 @@ -use crate::entity::{EntityCreateEvent, EntityDeleteEvent, EntityMoveEvent, PreviousPosition}; -use crate::state::State; +use crate::game::Game; +use ahash::AHashMap; use feather_core::{ChunkPosition, Position}; -use hashbrown::HashMap; -use legion::entity::Entity; -use legion::query::Read; -use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard}; -use rayon::prelude::*; -use tonks::{PreparedWorld, Query}; - -static EMPTY_VEC: Vec = Vec::new(); +use fecs::{Entity, World}; +use itertools::Itertools; +use smallvec::SmallVec; /// Stores which entities belong to every given chunk. /// @@ -18,95 +13,54 @@ static EMPTY_VEC: Vec = Vec::new(); /// it can be used to send all entities in a chunk /// to a player. /// -/// This structure is internally stored in `State`, using -/// a `RwLock` for concurrent access. (TODO: remove lock.) -/// /// Do note that the information in this structure is not necessarily up to date, /// although a best effort is made to update the data. -#[derive(Resource)] -pub struct ChunkEntities(RwLock>>); +#[derive(Default)] +pub struct ChunkEntities(AHashMap>); impl ChunkEntities { pub fn new() -> Self { - Self(RwLock::new(HashMap::new())) + Self::default() } /// Returns a slice of entities in the given chunk. - pub fn entities_in_chunk(&self, chunk: ChunkPosition) -> MappedRwLockReadGuard<[Entity]> { - let map = self.0.read(); - - RwLockReadGuard::map(map, move |map| { - if let Some(vec) = map.get(&chunk) { - vec.as_slice() - } else { - &EMPTY_VEC - } - }) - } -} - -impl Default for ChunkEntities { - fn default() -> Self { - Self::new() + pub fn entities_in_chunk(&self, chunk: ChunkPosition) -> &[Entity] { + self.0.get(&chunk).map(|vec| vec.as_slice()).unwrap_or(&[]) } } /// System to update ChunkEntities when entities move into new chunks. -#[event_handler] -fn chunk_entities_handle_movement( - events: &[EntityMoveEvent], - state: &State, - _query: &mut Query<(Read, Read)>, - world: &mut PreparedWorld, +pub fn on_chunk_cross_update_chunk_entities( + game: &mut Game, + entity: Entity, + old: Option, + new: ChunkPosition, ) { - events.par_iter().for_each(|event| { - let old_pos = world - .get_component::(event.entity) - .unwrap() - .0; - let new_pos = *world.get_component::(event.entity).unwrap(); - - let old_chunk = old_pos.chunk(); - let new_chunk = new_pos.chunk(); - - if old_chunk != new_chunk { - // Update chunk entities - let mut map = state.chunk_entities.0.write(); - map.entry(new_chunk) - .or_insert_with(|| vec![]) - .push(event.entity); - map.entry(old_chunk).and_modify(|vec| { - vec.remove_item(&event.entity); - }); + if let Some(old) = old { + if let Some(vec) = game.chunk_entities.0.get_mut(&old) { + let index = vec + .iter() + .find_position(|e| **e == entity) + .map(|(index, _)| index); + if let Some(index) = index { + vec.swap_remove(index); + } } - }) -} - -#[event_handler] -fn chunk_entities_insert( - event: &EntityCreateEvent, - state: &State, - _query: &mut Query>, - world: &mut PreparedWorld, -) { - if let Some(position) = world.get_component::(event.entity) { - let chunk = position.chunk(); - let mut map = state.chunk_entities.0.write(); - - map.entry(chunk) - .or_insert_with(|| vec![]) - .push(event.entity); } -} -#[event_handler] -fn chunk_entities_remove(event: &EntityDeleteEvent, state: &State) { - if let Some(position) = event.position { - let chunk = position.chunk(); - let mut map = state.chunk_entities.0.write(); + game.chunk_entities.0.entry(new).or_default().push(entity); +} - map.entry(chunk) - .or_insert_with(|| vec![]) - .remove_item(&event.entity); +pub fn on_entity_despawn_update_chunk_entities(game: &mut Game, world: &World, entity: Entity) { + if let Some(pos) = world.try_get::(entity) { + if let Some(vec) = game.chunk_entities.0.get_mut(&pos.chunk()) { + let index = vec + .iter() + .find_position(|e| **e == entity) + .map(|(index, _)| index); + if let Some(index) = index { + vec.swap_remove(index); + } + } } } diff --git a/server/src/game.rs b/server/src/game.rs index 7ec928af1..f42e43c00 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,3 +1,6 @@ +use crate::chunk_entities::{ + on_chunk_cross_update_chunk_entities, on_entity_despawn_update_chunk_entities, ChunkEntities, +}; use crate::chunk_logic::{ChunkHolders, ChunkUnloadQueue, ChunkWorkerHandle}; use crate::config::Config; use crate::io::{NetworkIoManager, NewClientInfo}; @@ -48,6 +51,7 @@ pub struct Game { pub chunk_unload_queue: ChunkUnloadQueue, pub chunk_holders: ChunkHolders, pub chunks_to_send: ChunksToSend, + pub chunk_entities: ChunkEntities, } impl Game { @@ -75,8 +79,8 @@ impl Game { /// Despawns an entity. This should be used instead of `World::despawn` /// as it properly handles events. pub fn despawn(&mut self, entity: Entity, world: &mut World) { - world.despawn(entity); self.on_entity_despawn(world, entity); + world.despawn(entity); } /// Spawns a player with the given `PlayerInfo`. @@ -97,8 +101,12 @@ impl Game { } /// Called when an entity is despawned/removed. + /// + /// Note that this is called __before__ the entity is deleted from the world. + /// As such, components of the entity can still be accessed. pub fn on_entity_despawn(&mut self, world: &mut World, entity: Entity) { chunk_logic::on_entity_despawn_remove_chunk_holder(self, world, entity); + on_entity_despawn_update_chunk_entities(self, world, entity); } /// Called when a player joins. @@ -129,6 +137,7 @@ impl Game { new: ChunkPosition, ) { on_chunk_cross_update_chunks(self, world, entity, old, new); + on_chunk_cross_update_chunk_entities(self, entity, old, new); } /// Called when a chunk is sent to a client. diff --git a/server/src/lib.rs b/server/src/lib.rs index d8ef097e0..a2bf74c2a 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -141,7 +141,7 @@ static ALLOC: System = System; // pub mod block; // pub mod broadcasters; -// pub mod chunk_entities; +mod chunk_entities; pub mod chunk_logic; pub mod chunk_worker; pub mod config; @@ -227,6 +227,7 @@ pub fn main() { chunk_unload_queue: Default::default(), chunk_holders: Default::default(), chunks_to_send: Default::default(), + chunk_entities: Default::default(), }; let (executor, resources) = init_executor(game); From 4d5cfba11afb075e57be61d5144adffd4c202061 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 00:53:25 -0600 Subject: [PATCH 093/647] Reimplement broadcast_entity_update and broadcast_chunk_update for `Game` --- server/src/game.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/server/src/game.rs b/server/src/game.rs index f42e43c00..46ebdec54 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -5,6 +5,7 @@ use crate::chunk_logic::{ChunkHolders, ChunkUnloadQueue, ChunkWorkerHandle}; use crate::config::Config; use crate::io::{NetworkIoManager, NewClientInfo}; use crate::join::{on_chunk_send_join_player, on_player_join_send_join_game}; +use crate::network::Network; use crate::view::{ on_chunk_cross_update_chunks, on_chunk_load_send_to_clients, on_player_join_trigger_chunk_cross, ChunksToSend, @@ -14,8 +15,8 @@ use bumpalo::Bump; use feather_blocks::Block; use feather_core::level::LevelData; use feather_core::world::ChunkMap; -use feather_core::{BlockPosition, ChunkPosition}; -use fecs::{Entity, World}; +use feather_core::{BlockPosition, ChunkPosition, Packet, Position}; +use fecs::{Entity, IntoQuery, Read, World}; use std::sync::atomic::AtomicU32; use std::sync::Arc; @@ -89,6 +90,83 @@ impl Game { self.on_player_join(world, entity); } + /* UTILITY FUNCTIONS */ + /// Broadcasts a packet to all online players. + pub fn broadcast_global(&self, world: &World, packet: impl Packet, neq: Option) { + self.broadcast_global_boxed(world, Box::new(packet), neq); + } + + /// Broadcasts a boxed packet to all online players. + pub fn broadcast_global_boxed( + &self, + world: &World, + packet: Box, + neq: Option, + ) { + for (entity, network) in >::query().iter_entities(world.inner()) { + if neq.map(|neq| neq == entity).unwrap_or(false) { + continue; + } + + network.send_boxed(packet.box_clone()); + } + } + + /// Broadcasts a packet to all players able to see a given chunk. + pub fn broadcast_chunk_update( + &self, + world: &World, + packet: impl Packet, + chunk: ChunkPosition, + neq: Option, + ) { + self.broadcast_chunk_update_boxed(world, Box::new(packet), chunk, neq); + } + + /// Broadcasts a boxed packet to all players able to see a given chunk. + pub fn broadcast_chunk_update_boxed( + &self, + world: &World, + packet: Box, + chunk: ChunkPosition, + neq: Option, + ) { + // we can use the chunk holders structure to accelerate this + for entity in self.chunk_holders.holders_for(chunk) { + if neq.map(|neq| neq == *entity).unwrap_or(false) { + continue; + } + + if let Some(network) = world.try_get::(*entity) { + network.send_boxed(packet.box_clone()); + } + } + } + + /// Broadcasts a packet to all players able to see a given entity. + pub fn broadcast_entity_update( + &self, + world: &World, + packet: impl Packet, + entity: Entity, + neq: Option, + ) { + self.broadcast_entity_update_boxed(world, Box::new(packet), entity, neq); + } + + /// Broadcasts a boxed packet to all players able to see a given entity. + pub fn broadcast_entity_update_boxed( + &self, + world: &World, + packet: Box, + entity: Entity, + neq: Option, + ) { + // Send the packet to all players who have a hold on the entity's chunk. + let entity_chunk = world.get::(entity).chunk(); + self.broadcast_chunk_update_boxed(world, packet, entity_chunk, neq); + } + /* EVENT HANDLERS */ /// Called when a block is updated. pub fn on_block_update( From 2734ee0a224dc449a317c1f66f47ad2c0b6eafb6 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 11:08:12 -0600 Subject: [PATCH 094/647] Handle position updates; fix view logic --- core/src/network/packet/mod.rs | 284 ++++++++++++------------- server/src/chunk_logic.rs | 1 + server/src/game.rs | 39 +++- server/src/io/mod.rs | 7 + server/src/io/worker.rs | 4 + server/src/lib.rs | 10 +- server/src/network.rs | 8 +- server/src/packet_buffer.rs | 6 +- server/src/packet_handlers/mod.rs | 14 +- server/src/packet_handlers/movement.rs | 86 +++----- server/src/player/mod.rs | 3 +- server/src/view.rs | 40 ++-- 12 files changed, 268 insertions(+), 234 deletions(-) diff --git a/core/src/network/packet/mod.rs b/core/src/network/packet/mod.rs index 6586a3b08..6ffecebe3 100644 --- a/core/src/network/packet/mod.rs +++ b/core/src/network/packet/mod.rs @@ -58,61 +58,61 @@ pub enum PacketType { // Serverbound // Handshake - Handshake = 1, + Handshake, // Login - LoginStart = 2, - EncryptionResponse = 3, - LoginPluginResponse = 4, + LoginStart, + EncryptionResponse, + LoginPluginResponse, // Play - TeleportConfirm = 5, - QueryBlockNBT = 6, - ChatMessageServerbound = 7, - ClientStatus = 8, - ClientSettings = 9, - TabCompleteServerbound = 10, - ConfirmTransactionServerbound = 11, - EnchantItem = 12, - ClickWindow = 13, - CloseWindowServerbound = 14, - PluginMessageServerbound = 15, - EditBook = 16, - QueryEntityNBT = 17, - UseEntity = 18, - KeepAliveServerbound = 19, - Player = 20, - PlayerPosition = 21, - PlayerPositionAndLookServerbound = 22, - PlayerLook = 23, - VehicleMoveServerbound = 24, - SteerBoat = 25, - PickItem = 26, - CraftRecipeRequest = 27, - PlayerAbilitiesServerbound = 28, - PlayerDigging = 29, - EntityAction = 30, - SteerVehicle = 31, - RecipeBookData = 32, - NameItem = 33, - ResourcePackStatus = 34, - AdvancementTab = 35, - SelectTrade = 36, - SetBeaconEffect = 37, - HeldItemChangeServerbound = 38, - UpdateCommandBlock = 39, - UpdateCommandBlockMinecart = 40, - CreativeInventoryAction = 41, - UpdateStructureBlock = 42, - UpdateSign = 43, - AnimationServerbound = 44, - Spectate = 45, - PlayerBlockPlacement = 46, - UseItem = 47, + TeleportConfirm, + QueryBlockNBT, + ChatMessageServerbound, + ClientStatus, + ClientSettings, + TabCompleteServerbound, + ConfirmTransactionServerbound, + EnchantItem, + ClickWindow, + CloseWindowServerbound, + PluginMessageServerbound, + EditBook, + QueryEntityNBT, + UseEntity, + KeepAliveServerbound, + Player, + PlayerPosition, + PlayerPositionAndLookServerbound, + PlayerLook, + VehicleMoveServerbound, + SteerBoat, + PickItem, + CraftRecipeRequest, + PlayerAbilitiesServerbound, + PlayerDigging, + EntityAction, + SteerVehicle, + RecipeBookData, + NameItem, + ResourcePackStatus, + AdvancementTab, + SelectTrade, + SetBeaconEffect, + HeldItemChangeServerbound, + UpdateCommandBlock, + UpdateCommandBlockMinecart, + CreativeInventoryAction, + UpdateStructureBlock, + UpdateSign, + AnimationServerbound, + Spectate, + PlayerBlockPlacement, + UseItem, // Status - Request = 48, - Ping = 49, + Request, + Ping, // Clientbound @@ -120,103 +120,103 @@ pub enum PacketType { // (none) // Login - DisconnectLogin = 50, - EncryptionRequest = 51, - LoginSuccess = 52, - SetCompression = 53, - LoginPluginRequest = 54, + DisconnectLogin, + EncryptionRequest, + LoginSuccess, + SetCompression, + LoginPluginRequest, // Play - SpawnObject = 55, - SpawnExperienceOrb = 56, - SpawnGlobalOrb = 57, - SpawnGlobalEntity = 58, - SpawnMob = 59, - SpawnPainting = 60, - SpawnPlayer = 61, - AnimationClientbound = 62, - Statistics = 63, - BlockBreakAnimation = 64, - UpdateBlockEntity = 65, - BlockAction = 66, - BlockChange = 67, - BossBar = 68, - ServerDifficulty = 69, - ChatMessageClientbound = 70, - MultiBlockChange = 71, - TabCompleteClientbound = 72, - DeclareCommands = 73, - ConfirmTransactionClientbound = 74, - CloseWindowClientbound = 75, - OpenWindow = 76, - WindowItems = 77, - WindowProperty = 78, - SetSlot = 79, - SetCooldown = 80, - PluginMessageClientbound = 81, - NamedSoundEffect = 82, - DisconnectPlay = 83, - EntityStatus = 84, - NBTQueryResponse = 85, - Explosion = 86, - UnloadChunk = 87, - ChangeGameState = 88, - KeepAliveClientbound = 89, - ChunkData = 90, - Effect = 91, - Particle = 92, - JoinGame = 93, - MapData = 94, - Entity = 95, - EntityRelativeMove = 96, - EntityLookAndRelativeMove = 97, - EntityLook = 98, - VehicleMoveClientbound = 99, - OpenSignEditor = 100, - CraftRecipeResponse = 101, - PlayerAbilitiesClientbound = 102, - CombatEvent = 103, - PlayerInfo = 104, - FacePlayer = 105, - PlayerPositionAndLookClientbound = 106, - UseBed = 107, - UnlockRecipes = 108, - DestroyEntities = 109, - RemoveEntityEffect = 110, - ResourcePackSend = 111, - Respawn = 112, - EntityHeadLook = 113, - SelectAdvancementTab = 114, - WorldBorder = 115, - Camera = 116, - HeldItemChangeClientbound = 117, - DisplayScoreboard = 118, - EntityMetadata = 119, - AttachEntity = 120, - EntityVelocity = 121, - EntityEquipment = 122, - SetExperience = 123, - UpdateHealth = 124, - ScoreboardObjective = 125, - SetPassengers = 126, - Teams = 127, - UpdateScore = 128, - SpawnPosition = 129, - TimeUpdate = 130, - StopSound = 131, - SoundEffect = 132, - PlayerListHeaderAndFooter = 133, - CollectItem = 134, - EntityTeleport = 135, - Advancements = 136, - EntityProperties = 137, - EntityEffect = 138, - DeclareRecipes = 139, - Tags = 140, + SpawnObject, + SpawnExperienceOrb, + SpawnGlobalOrb, + SpawnGlobalEntity, + SpawnMob, + SpawnPainting, + SpawnPlayer, + AnimationClientbound, + Statistics, + BlockBreakAnimation, + UpdateBlockEntity, + BlockAction, + BlockChange, + BossBar, + ServerDifficulty, + ChatMessageClientbound, + MultiBlockChange, + TabCompleteClientbound, + DeclareCommands, + ConfirmTransactionClientbound, + CloseWindowClientbound, + OpenWindow, + WindowItems, + WindowProperty, + SetSlot, + SetCooldown, + PluginMessageClientbound, + NamedSoundEffect, + DisconnectPlay, + EntityStatus, + NBTQueryResponse, + Explosion, + UnloadChunk, + ChangeGameState, + KeepAliveClientbound, + ChunkData, + Effect, + Particle, + JoinGame, + MapData, + Entity, + EntityRelativeMove, + EntityLookAndRelativeMove, + EntityLook, + VehicleMoveClientbound, + OpenSignEditor, + CraftRecipeResponse, + PlayerAbilitiesClientbound, + CombatEvent, + PlayerInfo, + FacePlayer, + PlayerPositionAndLookClientbound, + UseBed, + UnlockRecipes, + DestroyEntities, + RemoveEntityEffect, + ResourcePackSend, + Respawn, + EntityHeadLook, + SelectAdvancementTab, + WorldBorder, + Camera, + HeldItemChangeClientbound, + DisplayScoreboard, + EntityMetadata, + AttachEntity, + EntityVelocity, + EntityEquipment, + SetExperience, + UpdateHealth, + ScoreboardObjective, + SetPassengers, + Teams, + UpdateScore, + SpawnPosition, + TimeUpdate, + StopSound, + SoundEffect, + PlayerListHeaderAndFooter, + CollectItem, + EntityTeleport, + Advancements, + EntityProperties, + EntityEffect, + DeclareRecipes, + Tags, // Status - Response = 141, - Pong = 142, + Response, + Pong, } lazy_static! { diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index a44f1bb33..040acd0e4 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -160,6 +160,7 @@ pub fn chunk_unload(game: &mut Game) { // Unload chunk and pop from queue. game.chunk_map.remove(unload.chunk); + trace!("Unloaded chunk at {}", unload.chunk); game.chunk_unload_queue.queue.pop_front(); } else { // We're done - all chunks farther up in diff --git a/server/src/game.rs b/server/src/game.rs index 46ebdec54..c540aeceb 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -6,6 +6,7 @@ use crate::config::Config; use crate::io::{NetworkIoManager, NewClientInfo}; use crate::join::{on_chunk_send_join_player, on_player_join_send_join_game}; use crate::network::Network; +use crate::packet_buffer::PacketBuffers; use crate::view::{ on_chunk_cross_update_chunks, on_chunk_load_send_to_clients, on_player_join_trigger_chunk_cross, ChunksToSend, @@ -19,6 +20,7 @@ use feather_core::{BlockPosition, ChunkPosition, Packet, Position}; use fecs::{Entity, IntoQuery, Read, World}; use std::sync::atomic::AtomicU32; use std::sync::Arc; +use thread_local::CachedThreadLocal; /// Uber-resource storing almost all data needed to run the game. /// @@ -28,6 +30,8 @@ use std::sync::Arc; pub struct Game { /// The IO handle. pub io_handle: NetworkIoManager, + /// Packet buffers used to poll for received packets. + pub packet_buffers: Arc, /// The server configuration. pub config: Arc, /// The server tick count, measured in ticks @@ -44,7 +48,7 @@ pub struct Game { /// The chunk map. pub chunk_map: ChunkMap, /// Bump allocator. Reset every tick. - pub bump: Bump, + pub bump: CachedThreadLocal, /// Chunk worker handle used for communication with /// the chunk worker. pub chunk_worker_handle: ChunkWorkerHandle, @@ -90,7 +94,38 @@ impl Game { self.on_player_join(world, entity); } - /* UTILITY FUNCTIONS */ + /// Returns a bump allocator. + pub fn bump(&self) -> &Bump { + self.bump.get_or_default() + } + + /* PACKET HANDLING FUNCTIONS */ + /// Returns all packets of type `T` received by `player`. + /// + /// # Panics + /// Panics if the packet buffer for packets of type `T` is not + /// a `MapBuffer` or an `ArrayBuffer`. + pub fn received_for<'a, T>(&'a self, player: Entity) -> impl Iterator + 'a + where + T: Packet, + { + self.packet_buffers.received_for(player) + } + + /// Returns all packets of type `T` received, along + /// with the players that received them. + /// + /// # Panics + /// Panics if the packet buffer for packets of type `T` is not + /// a `ChannelBuffer`. + pub fn received<'a, T>(&'a self) -> impl Iterator + 'a + where + T: Packet, + { + self.packet_buffers.received() + } + + /* BROADCAST FUNCTIONS */ /// Broadcasts a packet to all online players. pub fn broadcast_global(&self, world: &World, packet: impl Packet, neq: Option) { self.broadcast_global_boxed(world, Box::new(packet), neq); diff --git a/server/src/io/mod.rs b/server/src/io/mod.rs index b27c94a14..17dbe416d 100644 --- a/server/src/io/mod.rs +++ b/server/src/io/mod.rs @@ -35,6 +35,13 @@ pub enum ListenerToServerMessage { /// Requests that the server create an empty `Entity` and send /// it to the listener. This entity will later be used as a player. RequestEntity, + /// Tells the server that a requested entity is no longer needed + /// and may be deleted. + /// + /// This typically happens when a connection comes in the form + /// of a status ping, where the entity is no longer needed + /// but has never served any purpose. + DeleteEntity(Entity), } #[derive(Debug)] diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index 88468c56a..36e2449b9 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -99,6 +99,10 @@ pub async fn run_worker( Err(e) => format!("{}", e), }; + let _ = worker + .listener_tx + .send(ListenerToServerMessage::DeleteEntity(worker.entity)); + let _ = worker .tx .send(WorkerToServerMessage::NotifyDisconnected { reason: msg }); diff --git a/server/src/lib.rs b/server/src/lib.rs index a2bf74c2a..89e4aee06 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -123,7 +123,6 @@ use crate::packet_buffer::PacketBuffers; use crate::worldgen::{ ComposableGenerator, EmptyWorldGenerator, SuperflatWorldGenerator, WorldGenerator, }; -use bumpalo::Bump; use feather_core::level; use feather_core::level::{deserialize_level_file, save_level_file, LevelData, LevelGeneratorType}; use feather_core::world::ChunkMap; @@ -135,6 +134,7 @@ use std::hash::{Hash, Hasher}; use std::io::{Read, Write}; use std::path::Path; use std::process::exit; +use thread_local::CachedThreadLocal; #[global_allocator] static ALLOC: System = System; @@ -152,7 +152,7 @@ mod join; // pub mod metadata; pub mod network; // pub mod p_inventory; // Prefixed to avoid conflict with inventory crate -// pub mod packet_handlers; +mod packet_handlers; // pub mod physics; pub mod player; pub mod shutdown; @@ -217,12 +217,13 @@ pub fn main() { let game = Game { io_handle, + packet_buffers, config, tick_count: 0, player_count, level, chunk_map: ChunkMap::new(), - bump: Bump::new(), + bump: CachedThreadLocal::new(), chunk_worker_handle, chunk_unload_queue: Default::default(), chunk_holders: Default::default(), @@ -307,8 +308,9 @@ fn init_executor(game: Game) -> (Executor, Resources) { resources.insert(game); let executor = Executor::new() - .with(network::poll_new_clients) .with(network::poll_player_disconnect) + .with(network::poll_new_clients) + .with(packet_handlers::handle_movement_packets) .with(chunk_logic::chunk_load) .with(chunk_logic::chunk_unload) .with(chunk_logic::chunk_optimize) diff --git a/server/src/network.rs b/server/src/network.rs index 2fadb77cb..d3710d06d 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -51,7 +51,7 @@ impl Network { pub fn poll_player_disconnect(game: &mut Game, world: &mut World) { // For each player with a Network component, // check their channel for disconnects. - let mut to_despawn = BumpVec::new_in(&game.bump); + let mut to_despawn = BumpVec::new_in(game.bump()); >::query() .iter_entities(world.inner()) .for_each(|(entity, network)| { @@ -85,6 +85,12 @@ pub fn poll_new_clients(game: &mut Game, world: &mut World) { .tx .unbounded_send(ServerToListenerMessage::Entity(entity)); } + ListenerToServerMessage::DeleteEntity(entity) => { + // no need to use `Game::despawn` here as + // the entity hasn't actually "existed" yet; + // it has no components + world.despawn(entity); + } } } } diff --git a/server/src/packet_buffer.rs b/server/src/packet_buffer.rs index 7656807ff..be61b794f 100644 --- a/server/src/packet_buffer.rs +++ b/server/src/packet_buffer.rs @@ -32,7 +32,11 @@ pub struct PacketBuffers { lazy_static! { /// The set of buffers which use a `MapBuffer` instead of an `ArrayBuffer`. - static ref USE_MAP_FOR: indexmap::IndexSet = indexmap::indexset![]; + static ref USE_MAP_FOR: indexmap::IndexSet = indexmap::indexset![ + PacketType::PlayerPosition, + PacketType::PlayerPositionAndLookServerbound, + PacketType::PlayerLook, + ]; } impl PacketBuffers { diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index d5f573fb1..85778eb9c 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -1,8 +1,10 @@ -//! Systems which handle packets through `crate::network::PacketQueue`. +//! Systems which handle packets. -mod animation; -mod chat; -mod digging; -mod inventory; +// mod animation; +// mod chat; +// mod digging; +// mod inventory; mod movement; -mod placement; +// mod placement; + +pub use movement::handle_movement_packets; diff --git a/server/src/packet_handlers/movement.rs b/server/src/packet_handlers/movement.rs index 41240d4a1..421520143 100644 --- a/server/src/packet_handlers/movement.rs +++ b/server/src/packet_handlers/movement.rs @@ -1,68 +1,38 @@ -use crate::entity::EntityMoveEvent; -use crate::network::PacketQueue; +use crate::game::Game; +use crate::network::Network; use feather_core::network::packet::implementation::{ PlayerLook, PlayerPosition, PlayerPositionAndLookServerbound, }; use feather_core::Position; -use legion::entity::Entity; -use legion::query::Write; -use tonks::{PreparedWorld, Query, Trigger}; +use fecs::{component, IntoQuery, World, Write}; -#[derive(Default, Resource)] -struct Buf(Vec<(Entity, Position)>); - -/// Handles player movement packets. +/// System to handle player movement updates. #[system] -fn handle_movement( - queue: &PacketQueue, - _query: &mut Query>, - world: &mut PreparedWorld, - buf: &mut Buf, - trigger: &mut Trigger, -) { - let positions = queue.received::().map(|(player, packet)| { - let old = *world.get_component::(player).unwrap(); - ( - player, - position!( - packet.x, - packet.feet_y, - packet.z, - old.pitch, - old.yaw, - packet.on_ground - ), - ) - }); +pub fn handle_movement_packets(game: &Game, world: &mut World) { + >::query() + .filter(component::()) + .par_entities_for_each_mut(world.inner_mut(), |(player, mut position)| { + let mut position: &mut Position = &mut *position; + for position_and_look in game.received_for::(player) { + position.x = position_and_look.x; + position.y = position_and_look.feet_y; + position.z = position_and_look.z; + position.pitch = position_and_look.pitch; + position.yaw = position_and_look.yaw; + position.on_ground = position_and_look.on_ground; + } - let looks = queue.received::().map(|(player, packet)| { - let mut old = *world.get_component::(player).unwrap(); - old.pitch = packet.pitch; - old.yaw = packet.yaw; - old.on_ground = packet.on_ground; - (player, old) - }); + for position_update in game.received_for::(player) { + position.x = position_update.x; + position.y = position_update.feet_y; + position.z = position_update.z; + position.on_ground = position_update.on_ground; + } - let pos_looks = queue - .received::() - .map(|(player, packet)| { - ( - player, - position!( - packet.x, - packet.feet_y, - packet.z, - packet.pitch, - packet.yaw, - packet.on_ground - ), - ) + for look in game.received_for::(player) { + position.pitch = look.pitch; + position.yaw = look.yaw; + position.on_ground = look.on_ground; + } }); - - buf.0.extend(positions.chain(looks).chain(pos_looks)); - - buf.0.drain(..).for_each(|(player, new_pos)| { - *world.get_component_mut::(player).unwrap() = new_pos; - trigger.trigger(EntityMoveEvent { entity: player }); - }); } diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index b30a4673c..e07788bd0 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -2,7 +2,7 @@ use crate::chunk_logic::ChunkHolder; use crate::entity; -use crate::entity::EntityId; +use crate::entity::{EntityId, PreviousPosition}; use crate::io::NewClientInfo; use crate::network::Network; use feather_core::Gamemode; @@ -29,6 +29,7 @@ pub fn create(world: &mut World, info: NewClientInfo) -> Entity { let entity = info.entity; world.add(entity, EntityId(entity::new_id())).unwrap(); world.add(entity, info.position).unwrap(); + world.add(entity, PreviousPosition(info.position)).unwrap(); world.add(entity, info.uuid).unwrap(); world.add(entity, info.uuid).unwrap(); world diff --git a/server/src/view.rs b/server/src/view.rs index d0de5bce0..7c32a1f91 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -35,7 +35,7 @@ use std::ops::Add; /// calls `Game::on_chunk_cross()` accordingly. #[system] pub fn check_crossed_chunks(world: &mut World, game: &mut Game) { - let mut crossed = BumpVec::new_in(&game.bump); + let mut crossed = BumpVec::new_in(game.bump()); for (entity, (pos, prev_pos)) in <(Read, Read)>::query().iter_entities(world.inner()) { @@ -69,7 +69,6 @@ pub fn on_chunk_cross_update_chunks( } for chunk in find_new_chunks(old, new, game.config.server.view_distance) { - dbg!(chunk); send_chunk_to_player(game, world, entity, chunk); } @@ -87,7 +86,8 @@ fn find_new_chunks( let within_view_distance = chunks_within_view_distance(new, view_distance); if let Some(old) = old { Either::Left(within_view_distance.filter(move |chunk| { - chunk.x - old.x <= view_distance as i32 && chunk.z - old.z <= view_distance as i32 + (chunk.x - old.x).abs() <= view_distance as i32 + && (chunk.z - old.z).abs() <= view_distance as i32 })) } else { Either::Right(within_view_distance) @@ -100,16 +100,30 @@ fn find_old_chunks( new: ChunkPosition, view_distance: u8, ) -> impl Iterator { - let within_view_distance = chunks_within_view_distance(new, view_distance); if let Some(old) = old { - Either::Left(within_view_distance.filter(move |chunk| { - chunk.x - old.x >= view_distance as i32 && chunk.z - old.z >= view_distance as i32 - })) + Either::Left( + chunks_within_view_distance(old, view_distance).filter(move |chunk| { + (chunk.x - new.x).abs() > view_distance as i32 + || (chunk.z - new.z).abs() > view_distance as i32 + }), + ) } else { Either::Right(iter::empty()) } } +/// Finds all chunks within the view distance of a given chunk. +fn chunks_within_view_distance( + chunk: ChunkPosition, + view_distance: u8, +) -> impl Iterator { + let view_distance = i32::from(view_distance); + + (-view_distance..=view_distance).flat_map(move |x| { + (-view_distance..=view_distance).map(move |z| chunk.add(ChunkPosition::new(x, z))) + }) +} + /// Resource containing a mapping from chunks -> sets of players indicating /// which chunks are pending to send to a given player. #[derive(Default)] @@ -185,15 +199,3 @@ fn create_chunk_data(chunk: &Chunk) -> ChunkData { chunk: chunk.clone(), // TODO: optimize } } - -/// Finds all chunks within the view distance of a given chunk. -fn chunks_within_view_distance( - chunk: ChunkPosition, - view_distance: u8, -) -> impl Iterator { - let view_distance = i32::from(view_distance); - - (-view_distance..=view_distance).flat_map(move |x| { - (-view_distance..=view_distance).map(move |z| chunk.add(ChunkPosition::new(x, z))) - }) -} From bf3ae3e25317d29f0e5f9d418d965a4fb360a322 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 11:19:45 -0600 Subject: [PATCH 095/647] Re-add SpawnPacketCreator and CreationPacketCreator --- .azure-pipelines.yml | 2 +- server/src/entity/mod.rs | 24 +++++++------------- server/src/lib.rs | 2 +- server/src/player/mod.rs | 38 ++++++++++++++++--------------- server/src/util.rs | 48 +--------------------------------------- 5 files changed, 31 insertions(+), 83 deletions(-) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 3acb318ec..3801e2d74 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -28,7 +28,7 @@ jobs: # image_name: 'ubuntu-16.04' apple-stable: rustup_toolchain: stable - image_name: 'macOS-10.13' + image_name: 'macOS-latest' #apple-beta: # rustup_toolchain: beta # image_name: 'macos-latest' diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index b1ec9ffdd..c059f04fc 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -5,8 +5,8 @@ // pub mod item; -use feather_core::Position; -use fecs::{EntityBuilder, IntoQuery, Read, World, Write}; +use feather_core::{Packet, Position}; +use fecs::{EntityBuilder, EntityRef, IntoQuery, Read, World, Write}; use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicI32, Ordering}; @@ -53,15 +53,8 @@ pub struct Name(pub String); #[derive(Debug, Clone, Copy)] pub struct PreviousPosition(pub Position); -/* -pub trait PacketCreatorFn: - Fn(&EntityAccessor, &PreparedWorld) -> Box + Send + Sync + 'static -{ -} -impl PacketCreatorFn for F where - F: Fn(&EntityAccessor, &PreparedWorld) -> Box + Send + Sync + 'static -{ -} +pub trait PacketCreatorFn: Fn(&EntityRef) -> Box + Send + Sync + 'static {} +impl PacketCreatorFn for F where F: Fn(&EntityRef) -> Box + Send + Sync + 'static {} /// Component which defines a function returning a packet to send /// to clients when the entity comes within range. This packet @@ -71,10 +64,10 @@ pub struct SpawnPacketCreator(pub &'static dyn PacketCreatorFn); impl SpawnPacketCreator { /// Returns the packet to send to clients when the entity is to be /// sent to the client. - pub fn get(&self, accessor: &EntityAccessor, world: &PreparedWorld) -> Box { + pub fn get(&self, accessor: &EntityRef) -> Box { let f = self.0; - f(accessor, world) + f(accessor) } } @@ -94,13 +87,12 @@ pub struct CreationPacketCreator(pub &'static dyn PacketCreatorFn); impl CreationPacketCreator { /// Returns the packet to send to clients when the entity is created. - pub fn get(&self, accessor: &EntityAccessor, world: &PreparedWorld) -> Box { + pub fn get(&self, accessor: &EntityRef) -> Box { let f = self.0; - f(accessor, world) + f(accessor) } } -*/ #[system] pub fn position_reset(world: &mut World) { diff --git a/server/src/lib.rs b/server/src/lib.rs index 89e4aee06..6eb758086 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -157,9 +157,9 @@ mod packet_handlers; pub mod player; pub mod shutdown; // pub mod time; -// pub mod util; pub mod game; pub mod packet_buffer; +pub mod util; mod view; pub mod worldgen; diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index e07788bd0..d6745ef33 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -2,12 +2,15 @@ use crate::chunk_logic::ChunkHolder; use crate::entity; -use crate::entity::{EntityId, PreviousPosition}; +use crate::entity::{CreationPacketCreator, EntityId, Name, PreviousPosition, SpawnPacketCreator}; use crate::io::NewClientInfo; use crate::network::Network; -use feather_core::Gamemode; -use fecs::{Entity, World}; +use crate::util::degrees_to_stops; +use feather_core::network::packet::implementation::{PlayerInfo, PlayerInfoAction, SpawnPlayer}; +use feather_core::{Gamemode, Packet, Position}; +use fecs::{Entity, EntityRef, World}; use mojang_api::ProfileProperty; +use uuid::Uuid; // pub mod chat; @@ -46,20 +49,23 @@ pub fn create(world: &mut World, info: NewClientInfo) -> Entity { //world.add(entity, Name(info.username)).unwrap(); world.add(entity, ChunkHolder::default()).unwrap(); //world.add(entity, LastKnownPositions::default()).unwrap(); - //world.add(entity, SpawnPacketCreator(&create_spawn_packet)).unwrap(); - //world.add(entity, CreationPacketCreator(&create_initialization_packet)).unwrap(); + world + .add(entity, SpawnPacketCreator(&create_spawn_packet)) + .unwrap(); + world + .add(entity, CreationPacketCreator(&create_initialization_packet)) + .unwrap(); world.add(entity, Gamemode::Creative).unwrap(); // TODO: proper gamemode handling //world.add(entity, EntityInventory::default()) world.add(entity, Player).unwrap(); entity } -/* /// Function to create a `SpawnPlayer` packet to spawn the player. -fn create_spawn_packet(accessor: &EntityAccessor, world: &PreparedWorld) -> Box { - let entity_id = accessor.get_component::(world).unwrap().0; - let player_uuid = *accessor.get_component::(world).unwrap(); - let pos = *accessor.get_component::(world).unwrap(); +fn create_spawn_packet(accessor: &EntityRef) -> Box { + let entity_id = accessor.get::().0; + let player_uuid = *accessor.get::(); + let pos = *accessor.get::(); // TODO: metadata @@ -77,13 +83,10 @@ fn create_spawn_packet(accessor: &EntityAccessor, world: &PreparedWorld) -> Box< } /// Function to create a `PlayerInfo` packet to broadcast when the player joins. -fn create_initialization_packet( - accessor: &EntityAccessor, - world: &PreparedWorld, -) -> Box { - let name = accessor.get_component::(world).unwrap(); - let props = accessor.get_component::(world).unwrap(); - let uuid = *accessor.get_component::(world).unwrap(); +fn create_initialization_packet(accessor: &EntityRef) -> Box { + let name = accessor.get::(); + let props = accessor.get::(); + let uuid = *accessor.get::(); let props = props .0 @@ -108,4 +111,3 @@ fn create_initialization_packet( let packet = PlayerInfo { action, uuid }; Box::new(packet) } -*/ diff --git a/server/src/util.rs b/server/src/util.rs index ca38c3a27..a5b6a0a18 100644 --- a/server/src/util.rs +++ b/server/src/util.rs @@ -1,14 +1,7 @@ //! Assorted utility functions. -use crate::entity::{EntityDeleteEvent, EntityId, Name}; -use crate::io::ServerToWorkerMessage; -use crate::network::Network; -use crate::state::State; use feather_core::Position; use glm::DVec3; -use legion::entity::Entity; -use std::borrow::Cow; -use uuid::Uuid; /// Calculates the relative move fields /// as used in the Entity Relative Move packets. @@ -27,49 +20,10 @@ pub fn degrees_to_stops(degs: f32) -> u8 { /// Converts float-based velocity in blocks per tick /// to the format used by the protocol. pub fn protocol_velocity(vel: DVec3) -> (i16, i16, i16) { - // Apparently, these are in units of 1/8000 block per tick. + // These are in units of 1/8000 block per tick. ( (vel.x * 8000.0) as i16, (vel.y * 8000.0) as i16, (vel.z * 8000.0) as i16, ) } - -/// Disconnects a player. -pub fn disconnect_player(state: &State, player: Entity, reason: impl Into>) { - let reason = reason.into(); - - state.exec_with_scheduler(move |world, scheduler| { - { - let username = world.get_component::(player).unwrap(); - info!("Disconnecting player {}: {}", username.0, reason); - - let network = world.get_component::(player).unwrap(); - network - .sender - .unbounded_send(ServerToWorkerMessage::Disconnect) - .unwrap(); - - let position = *world.get_component::(player).unwrap(); - let id = *world.get_component::(player).unwrap(); - let uuid = *world.get_component::(player).unwrap(); - - scheduler.trigger(EntityDeleteEvent { - entity: player, - position: Some(position), - id, - uuid, - }); - - let event = EntityDeleteEvent { - entity: player, - position: Some(position), - id, - uuid, - }; - scheduler.trigger(event); - } - - world.delete(player); - }); -} From ef76180d59e0039366da92c1dcec6da9559dd47b Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 12:21:06 -0600 Subject: [PATCH 096/647] Reimplement sending entities --- server/src/broadcasters/entity_creation.rs | 123 +++++++-------------- server/src/broadcasters/keepalive.rs | 15 ++- server/src/broadcasters/mod.rs | 24 ++-- server/src/chunk_entities.rs | 10 +- server/src/game.rs | 40 ++++++- server/src/lib.rs | 5 +- server/src/metadata.rs | 70 ------------ server/src/player/mod.rs | 2 +- server/src/view.rs | 73 +++++++++++- 9 files changed, 177 insertions(+), 185 deletions(-) delete mode 100644 server/src/metadata.rs diff --git a/server/src/broadcasters/entity_creation.rs b/server/src/broadcasters/entity_creation.rs index 55a780966..a8ec2481a 100644 --- a/server/src/broadcasters/entity_creation.rs +++ b/server/src/broadcasters/entity_creation.rs @@ -1,100 +1,53 @@ -use crate::chunk_logic::ChunkHolders; -use crate::entity::{CreationPacketCreator, EntityCreateEvent, SpawnPacketCreator}; +use crate::entity::{CreationPacketCreator, SpawnPacketCreator}; +use crate::game::Game; use crate::network::Network; -use crate::player::PlayerJoinEvent; -use crate::state::State; -use feather_core::{ChunkPosition, Position}; -use legion::query::Read; -use rayon::prelude::*; -use tonks::{PreparedWorld, Query, QueryAccessor}; +use crate::BumpVec; +use feather_core::Position; +use fecs::{Entity, IntoQuery, Read, World}; /// When an entity is created and has a `CreationPacketCreator` and/or `SpawnPacketCreator`, /// broadcasts the packets to all online clients. -#[event_handler] -fn broadcast_entity_creation( - events: &[EntityCreateEvent], - state: &State, - accessor1: &QueryAccessor>, - accessor2: &QueryAccessor>, - _query: &mut Query<( - Read, - Read, - Read, - Read, - )>, - world: &mut PreparedWorld, - holders: &ChunkHolders, -) { - events.par_iter().for_each(|event: &EntityCreateEvent| { - if let Some(accessor) = accessor1.find(event.entity) { - if let Some(packet_creator) = accessor.get_component::(world) { - let packet = packet_creator.get(&accessor, world); - state.broadcast_global_boxed(packet, None); - } - } +pub fn on_entity_spawn_send_to_clients(game: &mut Game, world: &mut World, entity: Entity) { + let accessor = world.entity(entity).expect("entity does not exist"); + + if let Some(creator) = world.try_get::(entity) { + let packet = creator.get(&accessor); + game.broadcast_global_boxed(world, packet, None); + } + let mut to_trigger = BumpVec::new_in(game.bump()); + + if let Some(creator) = world.try_get::(entity) { + let packet = creator.get(&accessor); + game.broadcast_entity_update_boxed(world, packet, entity, Some(entity)); - if let Some(accessor) = accessor2.find(event.entity) { - if let Some(packet_creator) = accessor.get_component::(world) { - let packet = packet_creator.get(&accessor, world); - // state.broadcast_entity_update_boxed(event.entity, packet, Some(event.entity)); + let chunk = world.get::(entity).chunk(); - // state.broadcast_entity_update_boxed(event.entity, packet, Some(event.entity)); - if let Some(meta) = world.get_component::(event.entity) { - let chunk: ChunkPosition = - (*world.get_component::(event.entity).unwrap()).into(); - for entity in holders.holders_for(chunk).unwrap_or(&[]) { - if let Some(network) = - world.get_component::(*entity) - { - use feather_core::network::packet::implementation::PacketEntityMetadata; - network.send_boxed(packet.box_clone()); - let entity_id = world - .get_component::(event.entity) - .unwrap() - .0; - let packet = PacketEntityMetadata { - entity_id, - metadata: meta.to_full_raw_metadata(), - }; - network.send(packet); - } - } - } + drop(creator); + + // trigger on_entity_send + for player in game.chunk_holders.holders_for(chunk) { + if world.try_get::(*player).is_some() { + to_trigger.push(*player); } } + } - // Register entity sends - let chunk = (*world.get_component::(event.entity).unwrap()).into(); - for entity in holders.holders_for(chunk).unwrap_or(&[]) { - state.register_entity_send(event.entity, *entity); - } - }); + for client in to_trigger { + game.on_entity_send(world, entity, client); + } } /// Wehn a player joins, sends existing entities to the player. /// /// This only handles init packets (PlayerInfo, etc.)—spawn packets -/// are handled by the view update mechanism. -#[event_handler] -fn broadcast_existing_entities( - events: &[PlayerJoinEvent], - accessor: &QueryAccessor>, - query: &mut Query>, - _query2: &mut Query>, - world: &mut PreparedWorld, - state: &State, -) { - // TODO: change to par_iter when legion implements immutable queries - events.iter().for_each(|event: &PlayerJoinEvent| { - // Send init packets for all entities with a `CreationPacketCreator`. - let network = world.get_component::(event.player).unwrap(); - - query.par_entities_for_each_immutable(world, |(entity, packet_creator)| { - if let Some(accessor) = accessor.find(entity) { - let packet = packet_creator.get(&accessor, world); - network.send_boxed(packet); - state.register_entity_send(entity, event.player); - } - }); - }); +/// are handled by the view update mechanism in `crate::view`. +pub fn on_player_join_send_existing_entities(world: &mut World, player: Entity) { + let network = world.get::(player); + for (entity, creator) in >::query().iter_entities(world.inner()) { + let accessor = world + .entity(entity) + .expect("query yielded entity which does not exist"); + let packet = creator.get(&accessor); + network.send_boxed(packet); + } } diff --git a/server/src/broadcasters/keepalive.rs b/server/src/broadcasters/keepalive.rs index ddf748ab3..12c1d0b78 100644 --- a/server/src/broadcasters/keepalive.rs +++ b/server/src/broadcasters/keepalive.rs @@ -1,12 +1,15 @@ -use crate::state::State; -use crate::{TickCount, TPS}; +use crate::game::Game; +use crate::TPS; use feather_core::network::packet::implementation::KeepAliveClientbound; +use fecs::World; /// Broadcasts keepalives every second. #[system] -fn broadcast_keepalive(state: &State, tick: &TickCount) { - if tick.0 % TPS == 0 { - let packet = KeepAliveClientbound { keep_alive_id: 0 }; - state.broadcast_global(packet, None); +pub fn broadcast_keepalive(game: &Game, world: &mut World) { + if game.tick_count % TPS == 0 { + let packet = KeepAliveClientbound { + keep_alive_id: game.tick_count, + }; + game.broadcast_global(world, packet, None); } } diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index 2dfa9c8a2..4401a6aed 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -7,13 +7,17 @@ //! packets have been sent. This is done through `EntitySendEvent`. //! * Those which just send a packet to a single player. -mod animation; -mod block; -mod chat; -pub mod entity_creation; -pub mod entity_deletion; -mod inventory; -mod item_collect; -pub mod keepalive; -mod metadata; -pub mod movement; +// mod animation; +// mod block; +// mod chat; +mod entity_creation; +// pub mod entity_deletion; +// mod inventory; +// mod item_collect; +mod keepalive; +// mod metadata; +// pub mod movement; + +pub use entity_creation::on_entity_spawn_send_to_clients; +pub use entity_creation::on_player_join_send_existing_entities; +pub use keepalive::broadcast_keepalive; diff --git a/server/src/chunk_entities.rs b/server/src/chunk_entities.rs index 3413f02ae..7dba8fbf3 100644 --- a/server/src/chunk_entities.rs +++ b/server/src/chunk_entities.rs @@ -46,9 +46,9 @@ pub fn on_chunk_cross_update_chunk_entities( vec.swap_remove(index); } } - } - game.chunk_entities.0.entry(new).or_default().push(entity); + game.chunk_entities.0.entry(new).or_default().push(entity); + } } pub fn on_entity_despawn_update_chunk_entities(game: &mut Game, world: &World, entity: Entity) { @@ -64,3 +64,9 @@ pub fn on_entity_despawn_update_chunk_entities(game: &mut Game, world: &World, e } } } + +pub fn on_entity_spawn_update_chunk_entities(game: &mut Game, world: &World, entity: Entity) { + if let Some(chunk) = world.try_get::(entity).map(|pos| pos.chunk()) { + game.chunk_entities.0.entry(chunk).or_default().push(entity); + } +} diff --git a/server/src/game.rs b/server/src/game.rs index c540aeceb..4d0dba16a 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,5 +1,7 @@ +use crate::broadcasters::{on_entity_spawn_send_to_clients, on_player_join_send_existing_entities}; use crate::chunk_entities::{ - on_chunk_cross_update_chunk_entities, on_entity_despawn_update_chunk_entities, ChunkEntities, + on_chunk_cross_update_chunk_entities, on_entity_despawn_update_chunk_entities, + on_entity_spawn_update_chunk_entities, ChunkEntities, }; use crate::chunk_logic::{ChunkHolders, ChunkUnloadQueue, ChunkWorkerHandle}; use crate::config::Config; @@ -7,8 +9,9 @@ use crate::io::{NetworkIoManager, NewClientInfo}; use crate::join::{on_chunk_send_join_player, on_player_join_send_join_game}; use crate::network::Network; use crate::packet_buffer::PacketBuffers; +use crate::player::Player; use crate::view::{ - on_chunk_cross_update_chunks, on_chunk_load_send_to_clients, + on_chunk_cross_update_chunks, on_chunk_cross_update_entities, on_chunk_load_send_to_clients, on_player_join_trigger_chunk_cross, ChunksToSend, }; use crate::{chunk_logic, player}; @@ -18,7 +21,7 @@ use feather_core::level::LevelData; use feather_core::world::ChunkMap; use feather_core::{BlockPosition, ChunkPosition, Packet, Position}; use fecs::{Entity, IntoQuery, Read, World}; -use std::sync::atomic::AtomicU32; +use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; use thread_local::CachedThreadLocal; @@ -91,6 +94,7 @@ impl Game { /// Spawns a player with the given `PlayerInfo`. pub fn spawn_player(&mut self, info: NewClientInfo, world: &mut World) { let entity = player::create(world, info); + self.on_entity_spawn(world, entity); self.on_player_join(world, entity); } @@ -220,12 +224,34 @@ impl Game { pub fn on_entity_despawn(&mut self, world: &mut World, entity: Entity) { chunk_logic::on_entity_despawn_remove_chunk_holder(self, world, entity); on_entity_despawn_update_chunk_entities(self, world, entity); + if world.try_get::(entity).is_some() { + self.on_player_leave(world, entity); + } + } + + /// Called when an entity of any type is spawned/created. + pub fn on_entity_spawn(&mut self, world: &mut World, entity: Entity) { + on_entity_spawn_update_chunk_entities(self, world, entity); + on_entity_spawn_send_to_clients(self, world, entity); } + /// Called when an entity is spawned on a client. + pub fn on_entity_send(&self, _world: &mut World, _entity: Entity, _client: Entity) {} + /// Called when a player joins. pub fn on_player_join(&mut self, world: &mut World, player: Entity) { - on_player_join_trigger_chunk_cross(self, world, player); + self.player_count.fetch_add(1, Ordering::Relaxed); on_player_join_send_join_game(self, world, player); + on_player_join_send_existing_entities(world, player); + on_player_join_trigger_chunk_cross(self, world, player) + } + + /// Called when a player leaves. + /// + /// As with `on_entity_despawn`, this function is called __before__ + /// `player` is removed from the world. + pub fn on_player_leave(&mut self, _world: &mut World, _player: Entity) { + self.player_count.fetch_sub(1, Ordering::Relaxed); } /// Called when a chunk loads successfully. @@ -251,6 +277,7 @@ impl Game { ) { on_chunk_cross_update_chunks(self, world, entity, old, new); on_chunk_cross_update_chunk_entities(self, entity, old, new); + on_chunk_cross_update_entities(self, world, entity, old, new); } /// Called when a chunk is sent to a client. @@ -263,3 +290,8 @@ impl Game { pub fn increment_tick_count(game: &mut Game) { game.tick_count += 1; } + +#[system] +pub fn reset_bump_allocators(game: &mut Game) { + game.bump.iter_mut().for_each(Bump::reset); +} diff --git a/server/src/lib.rs b/server/src/lib.rs index 6eb758086..7f1718a78 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -140,7 +140,7 @@ use thread_local::CachedThreadLocal; static ALLOC: System = System; // pub mod block; -// pub mod broadcasters; +mod broadcasters; mod chunk_entities; pub mod chunk_logic; pub mod chunk_worker; @@ -149,7 +149,6 @@ pub mod entity; pub mod io; mod join; // pub mod lazy; -// pub mod metadata; pub mod network; // pub mod p_inventory; // Prefixed to avoid conflict with inventory crate mod packet_handlers; @@ -315,6 +314,8 @@ fn init_executor(game: Game) -> (Executor, Resources) { .with(chunk_logic::chunk_unload) .with(chunk_logic::chunk_optimize) .with(view::check_crossed_chunks) + .with(broadcasters::broadcast_keepalive) + .with(game::reset_bump_allocators) .with(game::increment_tick_count) .with(entity::position_reset); // should be at end diff --git a/server/src/metadata.rs b/server/src/metadata.rs deleted file mode 100644 index 865568dd6..000000000 --- a/server/src/metadata.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! Entity metadata implementation. - -use feather_core::inventory::Slot; -use feather_core::world::BlockPosition; -use feather_core::EntityMetadata; -use uuid::Uuid; - -type OptUuid = Option; - -bitflags! { - pub struct EntityBitMask: u8 { - const ON_FIRE = 0x01; - const CROUCHED = 0x02; - const SPRINTING = 0x08; - const SWIMMING = 0x10; - const INVISIBLE = 0x20; - const GLOWING_EFFECT = 0x40; - const FLYING_WITH_ELYTRA = 0x80; - } -} - -bitflags! { - #[derive(Default)] - pub struct ArrowBitMask: u8 { - const CRITICAL = 0x01; - const NO_CLIP = 0x02; - } -} - -lazy_static! { - pub static ref EMPTY_METADATA: Metadata = { Metadata::Entity(Entity::default()) }; -} - -pub type Metadata = _Metadata; - -entity_metadata! { - _Metadata, - Entity { - bit_mask: u8() = 0, - air: VarInt(300) = 1, - silent: bool() = 4, - no_gravity: bool() = 5, - }, - Item: Entity { - item: Slot() = 6, - }, - Living: Entity { - hand_states: u8() = 6, - health: f32(1.0) = 7, - potion_effect_color: VarInt() = 8, - potion_effect_ambient: bool() = 9, - arrows: VarInt() = 10, - }, - Player: Living { - additional_hearts: f32() = 11, - score: VarInt() = 12, - displayed_skin_parts: u8() = 13, - main_hand: u8(1) = 14, - }, - Arrow: Entity { - arrow_bit_mask: u8() = 6, - shooter: OptUuid() = 7, - }, - TippedArrow: Arrow { - color: VarInt() = 8, - }, - FallingBlock: Entity { - spawn_position: BlockPosition() = 6, - }, -} diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index d6745ef33..9bc497394 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -46,7 +46,7 @@ pub fn create(world: &mut World, info: NewClientInfo) -> Entity { .unwrap(); world.add(entity, info.ip).unwrap(); world.add(entity, ProfileProperties(info.profile)).unwrap(); - //world.add(entity, Name(info.username)).unwrap(); + world.add(entity, Name(info.username)).unwrap(); world.add(entity, ChunkHolder::default()).unwrap(); //world.add(entity, LastKnownPositions::default()).unwrap(); world diff --git a/server/src/view.rs b/server/src/view.rs index 7c32a1f91..4d1b33d49 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -17,13 +17,13 @@ //! * Other systems query for added `CrossedChunk` components //! and perform updates on these players' views. -use crate::entity::PreviousPosition; +use crate::entity::{EntityId, PreviousPosition, SpawnPacketCreator}; use crate::game::Game; use crate::network::Network; use crate::player::Player; use crate::{chunk_logic, BumpVec}; use ahash::AHashMap; -use feather_core::network::packet::implementation::{ChunkData, UnloadChunk}; +use feather_core::network::packet::implementation::{ChunkData, DestroyEntities, UnloadChunk}; use feather_core::{Chunk, ChunkPosition, Position}; use fecs::{Entity, IntoQuery, Read, World}; use itertools::Either; @@ -68,7 +68,13 @@ pub fn on_chunk_cross_update_chunks( return; } - for chunk in find_new_chunks(old, new, game.config.server.view_distance) { + // The client likes it if we send closer chunks first, + // so we'll sort on the Manhattan distance to the player. + let mut chunks_to_send = BumpVec::new_in(game.bump()); + chunks_to_send.extend(find_new_chunks(old, new, game.config.server.view_distance)); + chunks_to_send.sort_unstable_by_key(|chunk| chunk.manhattan_distance_to(new)); + + for chunk in chunks_to_send { send_chunk_to_player(game, world, entity, chunk); } @@ -77,6 +83,59 @@ pub fn on_chunk_cross_update_chunks( } } +/// System which sends new entities and removes old entities +/// when a player crosses into a new view. +pub fn on_chunk_cross_update_entities( + game: &mut Game, + world: &mut World, + entity: Entity, + old: Option, + new: ChunkPosition, +) { + let network = match world.try_get::(entity) { + Some(net) => net, + None => return, // not a player + }; + + // Send newly visible entities. + let mut to_trigger = BumpVec::new_in(game.bump()); + for other in find_new_chunks(old, new, game.config.server.view_distance) + .flat_map(|chunk| game.chunk_entities.entities_in_chunk(chunk)) + .filter(|other| **other != entity) + // don't send player to themselves! + { + if let Some(creator) = world.try_get::(*other) { + let accessor = world + .entity(*other) + .expect("entity in chunk entities does not exist"); + let packet = creator.get(&accessor); + + network.send_boxed(packet); + to_trigger.push(*other); + } + } + + // Tell the client to despawn entities which are no longer visible. + let to_destroy = find_old_chunks(old, new, game.config.server.view_distance) + .flat_map(|chunk| game.chunk_entities.entities_in_chunk(chunk)) + .map(|entity| world.get::(*entity).0) + .collect::>(); + + if !to_destroy.is_empty() { + let packet = DestroyEntities { + entity_ids: to_destroy, + }; + network.send(packet); + } + + drop(network); + + // Trigger on_entity_send + for other in to_trigger { + game.on_entity_send(world, other, entity); + } +} + /// Returns new chunks visible from a new chunk position. fn find_new_chunks( old: Option, @@ -182,12 +241,16 @@ pub fn on_chunk_load_send_to_clients(game: &mut Game, world: &mut World, chunk: .chunk_map .chunk_at(chunk) .expect("chunk not loaded, but load event was triggered"); - players.iter().for_each(|player| { + for player in players { + if !world.is_alive(*player) { + continue; + } + world .get::(*player) .send(create_chunk_data(&chunk)); game.on_chunk_send(world, chunk.position(), *player); - }); + } } game.chunks_to_send.0.remove(&chunk); From a27a66e3f50e72c9822d70fa9d82619dd4e824d2 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 12:26:51 -0600 Subject: [PATCH 097/647] Broadcast entity despawns --- server/src/broadcasters/entity_deletion.rs | 63 +++++++++------------- server/src/broadcasters/mod.rs | 3 +- server/src/game.rs | 6 ++- 3 files changed, 31 insertions(+), 41 deletions(-) diff --git a/server/src/broadcasters/entity_deletion.rs b/server/src/broadcasters/entity_deletion.rs index c729f2304..b4616342b 100644 --- a/server/src/broadcasters/entity_deletion.rs +++ b/server/src/broadcasters/entity_deletion.rs @@ -1,45 +1,30 @@ -use crate::chunk_logic::ChunkHolders; -use crate::entity::EntityDeleteEvent; -use crate::network::Network; -use crate::state::State; -use feather_core::network::packet::implementation::DestroyEntities; -use legion::query::Read; -use rayon::prelude::*; -use tonks::{PreparedWorld, Query}; +use crate::entity::EntityId; +use crate::game::Game; +use crate::player::Player; +use feather_core::network::packet::implementation::{ + DestroyEntities, PlayerInfo, PlayerInfoAction, +}; +use fecs::{Entity, World}; +use uuid::Uuid; /// Broadcasts when an entity is deleted. -#[event_handler] -fn broadcast_entity_deletion( - events: &[EntityDeleteEvent], - holders: &ChunkHolders, - _query: &mut Query>, - world: &mut PreparedWorld, - state: &State, -) { - events.par_iter().for_each(|event: &EntityDeleteEvent| { - if let Some(pos) = event.position { - let chunk = pos.into(); +pub fn on_entity_despawn_broadcast_despawn(game: &mut Game, world: &mut World, entity: Entity) { + let id = world.get::(entity).0; + let packet = DestroyEntities { + entity_ids: vec![id], + }; - for entity in holders.holders_for(chunk).unwrap_or(&[]) { - if let Some(network) = world.get_component::(*entity) { - network.send(DestroyEntities { - entity_ids: vec![event.id.0], - }); - state.register_entity_unload(event.entity, *entity); - } - } - } + game.broadcast_entity_update(world, packet, entity, Some(entity)); - // If entity was a player, broadcast PlayerInfo with delete status. - // TODO: fix - /* - if world.get_component::(event.entity).is_some() { - let packet = PlayerInfo { - action: PlayerInfoAction::RemovePlayer, - uuid: event.uuid, - }; + // If the entity was a player, send Player Info to + // remove them from the tablist. + if world.has::(entity) { + let uuid = *world.get::(entity); + let packet = PlayerInfo { + action: PlayerInfoAction::RemovePlayer, + uuid, + }; - state.broadcast_global(packet, None); - }*/ - }); + game.broadcast_global(world, packet, None); + } } diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index 4401a6aed..0f74bb1d0 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -11,7 +11,7 @@ // mod block; // mod chat; mod entity_creation; -// pub mod entity_deletion; +mod entity_deletion; // mod inventory; // mod item_collect; mod keepalive; @@ -20,4 +20,5 @@ mod keepalive; pub use entity_creation::on_entity_spawn_send_to_clients; pub use entity_creation::on_player_join_send_existing_entities; +pub use entity_deletion::on_entity_despawn_broadcast_despawn; pub use keepalive::broadcast_keepalive; diff --git a/server/src/game.rs b/server/src/game.rs index 4d0dba16a..d4e55ba0b 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,4 +1,7 @@ -use crate::broadcasters::{on_entity_spawn_send_to_clients, on_player_join_send_existing_entities}; +use crate::broadcasters::{ + on_entity_despawn_broadcast_despawn, on_entity_spawn_send_to_clients, + on_player_join_send_existing_entities, +}; use crate::chunk_entities::{ on_chunk_cross_update_chunk_entities, on_entity_despawn_update_chunk_entities, on_entity_spawn_update_chunk_entities, ChunkEntities, @@ -224,6 +227,7 @@ impl Game { pub fn on_entity_despawn(&mut self, world: &mut World, entity: Entity) { chunk_logic::on_entity_despawn_remove_chunk_holder(self, world, entity); on_entity_despawn_update_chunk_entities(self, world, entity); + on_entity_despawn_broadcast_despawn(self, world, entity); if world.try_get::(entity).is_some() { self.on_player_leave(world, entity); } From 54a48d3cfa454e682c99070b53c4cae251277031 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 13:01:28 -0600 Subject: [PATCH 098/647] Broadcast player movements --- Cargo.lock | 12 +++ server/Cargo.toml | 1 + server/src/broadcasters/mod.rs | 6 +- server/src/broadcasters/movement.rs | 111 ++++++++++++++-------------- server/src/game.rs | 12 ++- server/src/lib.rs | 2 +- server/src/player/mod.rs | 3 +- server/src/view.rs | 34 +++++++-- 8 files changed, 113 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d6ad901d..2df9444ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -516,6 +516,17 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "dashmap" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "010ef3f25ed5bb93505a3238d19957622190268640526aab07174c66ccf5d611" +dependencies = [ + "ahash", + "cfg-if", + "num_cpus", +] + [[package]] name = "derivative" version = "1.0.3" @@ -710,6 +721,7 @@ dependencies = [ "criterion", "crossbeam", "ctrlc", + "dashmap", "derivative 2.0.2", "feather-blocks", "feather-codegen", diff --git a/server/Cargo.toml b/server/Cargo.toml index 5e03a43e3..b35854b1a 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -61,6 +61,7 @@ uuid = { version = "0.8", features = ["v4"] } multimap = "0.8" smallvec = "1.2" indexmap = "1.3" +dashmap = "3.7" # Logging log = "0.4" diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index 0f74bb1d0..92a3ee99b 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -16,9 +16,13 @@ mod entity_deletion; // mod item_collect; mod keepalive; // mod metadata; -// pub mod movement; +mod movement; pub use entity_creation::on_entity_spawn_send_to_clients; pub use entity_creation::on_player_join_send_existing_entities; pub use entity_deletion::on_entity_despawn_broadcast_despawn; pub use keepalive::broadcast_keepalive; +pub use movement::{ + broadcast_entity_movement, on_entity_client_remove_update_last_known_positions, + on_entity_send_update_last_known_positions, LastKnownPositions, +}; diff --git a/server/src/broadcasters/movement.rs b/server/src/broadcasters/movement.rs index b5b474a8d..0eed53b6a 100644 --- a/server/src/broadcasters/movement.rs +++ b/server/src/broadcasters/movement.rs @@ -1,82 +1,78 @@ //! Broadcasting of movement updates. -use crate::chunk_logic::ChunkHolders; -use crate::entity::{EntityId, EntityMoveEvent, Velocity, VelocityUpdateEvent}; +use crate::entity::{EntityId, PreviousPosition}; +use crate::game::Game; use crate::network::Network; -use crate::state::State; -use crate::util::{calculate_relative_move, degrees_to_stops, protocol_velocity}; +use crate::util::{calculate_relative_move, degrees_to_stops}; +use dashmap::DashMap; use feather_core::network::packet::implementation::{ - EntityHeadLook, EntityLook, EntityLookAndRelativeMove, EntityRelativeMove, EntityVelocity, + EntityHeadLook, EntityLook, EntityLookAndRelativeMove, EntityRelativeMove, }; use feather_core::{Packet, Position}; -use hashbrown::HashMap; -use legion::entity::Entity; -use legion::query::{Read, Write}; +use fecs::{changed, Entity, IntoQuery, Read, World}; use smallvec::SmallVec; -use tonks::{PreparedWorld, Query}; +use std::ops::Deref; /// Component containing the last sent positions of all entities for a given client. /// This component is used to determine /// the relative movement for an entity. #[derive(Default)] -pub struct LastKnownPositions(pub HashMap); +pub struct LastKnownPositions(pub DashMap); /// System to broadcast when an entity moves. -#[event_handler] -fn broadcast_move( - events: &[EntityMoveEvent], - _query: &mut Query<( - Read, - Read, - Write, - Read, - )>, - world: &mut PreparedWorld, - chunk_holders: &ChunkHolders, -) { - events.iter().for_each(|event: &EntityMoveEvent| { - // Find position of entity. - let pos = *world.get_component::(event.entity).unwrap(); - - // Find clients which can see the entity. - let chunk = pos.into(); - let clients = chunk_holders.holders_for(chunk).unwrap_or(&[]); - - let entity_id = world.get_component::(event.entity).unwrap().0; - - // For each client, send the position update relative to the client's last known - // position for the entity. If no `LastKnownPositions` entry exists for the entity, - // then the entity has not yet been sent to the client, so we do not send a position - // update. (When an entity is spawned on a client, the `LastKnownPositions` entry - // is inserted with the starting position.) - clients.iter().copied().for_each(|client: Entity| { - // Don't sent player's position to themself - if client == event.entity { +#[system] +pub fn broadcast_entity_movement(game: &mut Game, world: &mut World) { + <(Read, Read, Read)>::query() + .filter(changed::()) + .par_entities_for_each(world.inner(), |(entity, (pos, prev_pos, id))| { + let pos: Position = *pos; + let prev_pos: Position = prev_pos.0; + + if pos == prev_pos { return; } - if !world.is_alive(client) { - return; + let entity_id = id.0; + + let chunk = pos.chunk(); + let players = game.chunk_holders.holders_for(chunk); + + for player in players { + if let Some(network) = world.try_get::(*player) { + let last_known_positions = world.get::(*player); + let last_known_positions = last_known_positions.deref(); + if let Some(mut last_known_pos) = last_known_positions.0.get_mut(&entity) { + for packet in + packets_for_movement_update(entity_id, *last_known_pos.value(), pos) + { + network.send_boxed(packet); + } + + *last_known_pos.value_mut() = pos; + }; + } } + }); +} - let mut last_known_positions = - unsafe { world.get_component_mut_unchecked::(client) }.unwrap(); - if let Some(old_pos) = last_known_positions.0.get_mut(&event.entity) { - let packets = packets_for_movement_update(entity_id, *old_pos, pos); - - let network = world.get_component::(client).unwrap(); - - packets.into_iter().for_each(|packet| { - network.send_boxed(packet); - }); +pub fn on_entity_send_update_last_known_positions(world: &World, entity: Entity, client: Entity) { + if let Some(last_known_positions) = world.try_get::(client) { + let pos = *world.get::(entity); + last_known_positions.0.insert(entity, pos); + } +} - // Update last known position. - *old_pos = pos; - } - }); - }); +pub fn on_entity_client_remove_update_last_known_positions( + world: &World, + entity: Entity, + client: Entity, +) { + if let Some(last_known_positions) = world.try_get::(client) { + last_known_positions.0.remove(&entity); + } } +/* /// Broadcasts an entity's velocity. #[event_handler] pub fn broadcast_velocity( @@ -98,6 +94,7 @@ pub fn broadcast_velocity( }; state.broadcast_entity_update(event.entity, packet, None); } +*/ /// Returns the packet needed to notify a client /// of a position update, from the old position to the new one. diff --git a/server/src/game.rs b/server/src/game.rs index d4e55ba0b..e647999f2 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,5 +1,6 @@ use crate::broadcasters::{ - on_entity_despawn_broadcast_despawn, on_entity_spawn_send_to_clients, + on_entity_client_remove_update_last_known_positions, on_entity_despawn_broadcast_despawn, + on_entity_send_update_last_known_positions, on_entity_spawn_send_to_clients, on_player_join_send_existing_entities, }; use crate::chunk_entities::{ @@ -240,7 +241,14 @@ impl Game { } /// Called when an entity is spawned on a client. - pub fn on_entity_send(&self, _world: &mut World, _entity: Entity, _client: Entity) {} + pub fn on_entity_send(&self, world: &mut World, entity: Entity, client: Entity) { + on_entity_send_update_last_known_positions(world, entity, client); + } + + /// Called when an entity is removed on a client (Destroy Entities packet) + pub fn on_entity_client_remove(&mut self, world: &mut World, entity: Entity, client: Entity) { + on_entity_client_remove_update_last_known_positions(world, entity, client); + } /// Called when a player joins. pub fn on_player_join(&mut self, world: &mut World, player: Entity) { diff --git a/server/src/lib.rs b/server/src/lib.rs index 7f1718a78..f3d893642 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -302,7 +302,6 @@ fn run_loop( /// Initializes the executor and resources. fn init_executor(game: Game) -> (Executor, Resources) { - // Insert resources which don't have a `Default` impl. let mut resources = Resources::new(); resources.insert(game); @@ -315,6 +314,7 @@ fn init_executor(game: Game) -> (Executor, Resources) { .with(chunk_logic::chunk_optimize) .with(view::check_crossed_chunks) .with(broadcasters::broadcast_keepalive) + .with(broadcasters::broadcast_entity_movement) .with(game::reset_bump_allocators) .with(game::increment_tick_count) .with(entity::position_reset); // should be at end diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index 9bc497394..9edb0cca3 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -1,5 +1,6 @@ //! Systems and components specific to player entities. +use crate::broadcasters::LastKnownPositions; use crate::chunk_logic::ChunkHolder; use crate::entity; use crate::entity::{CreationPacketCreator, EntityId, Name, PreviousPosition, SpawnPacketCreator}; @@ -48,7 +49,7 @@ pub fn create(world: &mut World, info: NewClientInfo) -> Entity { world.add(entity, ProfileProperties(info.profile)).unwrap(); world.add(entity, Name(info.username)).unwrap(); world.add(entity, ChunkHolder::default()).unwrap(); - //world.add(entity, LastKnownPositions::default()).unwrap(); + world.add(entity, LastKnownPositions::default()).unwrap(); world .add(entity, SpawnPacketCreator(&create_spawn_packet)) .unwrap(); diff --git a/server/src/view.rs b/server/src/view.rs index 4d1b33d49..c30fa74e8 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -98,7 +98,7 @@ pub fn on_chunk_cross_update_entities( }; // Send newly visible entities. - let mut to_trigger = BumpVec::new_in(game.bump()); + let mut sends_to_trigger = BumpVec::new_in(game.bump()); for other in find_new_chunks(old, new, game.config.server.view_distance) .flat_map(|chunk| game.chunk_entities.entities_in_chunk(chunk)) .filter(|other| **other != entity) @@ -111,13 +111,30 @@ pub fn on_chunk_cross_update_entities( let packet = creator.get(&accessor); network.send_boxed(packet); - to_trigger.push(*other); + sends_to_trigger.push((*other, entity)); + } + + // if this `other` is a player, also send `entity` to other + if let Some(network) = world.try_get::(*other) { + if let Some(creator) = world.try_get::(entity) { + let accessor = world.entity(entity).expect("entity does not exist"); + let packet = creator.get(&accessor); + + network.send_boxed(packet); + sends_to_trigger.push((entity, *other)); + } } } // Tell the client to despawn entities which are no longer visible. - let to_destroy = find_old_chunks(old, new, game.config.server.view_distance) - .flat_map(|chunk| game.chunk_entities.entities_in_chunk(chunk)) + let mut to_client_remove_trigger = BumpVec::new_in(game.bump()); + to_client_remove_trigger.extend( + find_old_chunks(old, new, game.config.server.view_distance) + .flat_map(|chunk| game.chunk_entities.entities_in_chunk(chunk)), + ); + + let to_destroy = to_client_remove_trigger + .iter() .map(|entity| world.get::(*entity).0) .collect::>(); @@ -131,8 +148,13 @@ pub fn on_chunk_cross_update_entities( drop(network); // Trigger on_entity_send - for other in to_trigger { - game.on_entity_send(world, other, entity); + for (entity, client) in sends_to_trigger { + game.on_entity_send(world, entity, client); + } + + // Trigger on_entity_client_remmove + for other in to_client_remove_trigger { + game.on_entity_client_remove(world, other, entity); } } From 8b40f4868d61bfa7862822ad4b14fdce51b8c9f1 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 13:04:44 -0600 Subject: [PATCH 099/647] Move system definitions to separate module --- server/src/lib.rs | 15 ++------------- server/src/systems.rs | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 server/src/systems.rs diff --git a/server/src/lib.rs b/server/src/lib.rs index f3d893642..c8d22c7df 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -158,6 +158,7 @@ pub mod shutdown; // pub mod time; pub mod game; pub mod packet_buffer; +mod systems; pub mod util; mod view; pub mod worldgen; @@ -305,19 +306,7 @@ fn init_executor(game: Game) -> (Executor, Resources) { let mut resources = Resources::new(); resources.insert(game); - let executor = Executor::new() - .with(network::poll_player_disconnect) - .with(network::poll_new_clients) - .with(packet_handlers::handle_movement_packets) - .with(chunk_logic::chunk_load) - .with(chunk_logic::chunk_unload) - .with(chunk_logic::chunk_optimize) - .with(view::check_crossed_chunks) - .with(broadcasters::broadcast_keepalive) - .with(broadcasters::broadcast_entity_movement) - .with(game::reset_bump_allocators) - .with(game::increment_tick_count) - .with(entity::position_reset); // should be at end + let executor = systems::build_executor(); (executor, resources) } diff --git a/server/src/systems.rs b/server/src/systems.rs new file mode 100644 index 000000000..95a2817dc --- /dev/null +++ b/server/src/systems.rs @@ -0,0 +1,20 @@ +//! Defines all systems and the order in which they are executed. + +use super::*; +use fecs::Executor; + +pub fn build_executor() -> Executor { + Executor::new() + .with(network::poll_player_disconnect) + .with(network::poll_new_clients) + .with(packet_handlers::handle_movement_packets) + .with(chunk_logic::chunk_load) + .with(chunk_logic::chunk_unload) + .with(chunk_logic::chunk_optimize) + .with(view::check_crossed_chunks) + .with(broadcasters::broadcast_keepalive) + .with(broadcasters::broadcast_entity_movement) + .with(game::reset_bump_allocators) + .with(game::increment_tick_count) + .with(entity::position_reset) // should be at end +} From ad50171b248ed8bf3126fd14073c12bdea2f5f68 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 13:28:59 -0600 Subject: [PATCH 100/647] Fix clippy and tests --- Cargo.lock | 3 +++ codegen/src/lib.rs | 1 - core/src/entitymeta.rs | 4 ++-- core/src/inventory.rs | 8 ++++---- core/src/network/mctypes.rs | 4 ++-- core/src/network/packet/implementation.rs | 2 +- core/src/save/player_data.rs | 5 ++--- server/Cargo.toml | 4 ++-- server/src/io/initial_handler.rs | 4 ++-- server/src/io/mod.rs | 3 +++ server/src/io/worker.rs | 6 ++++-- server/src/packet_buffer.rs | 6 ++++++ server/src/worldgen/noise.rs | 2 +- 13 files changed, 32 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2df9444ae..993eda7c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -713,6 +713,7 @@ version = "0.5.0" dependencies = [ "ahash", "anyhow", + "approx 0.3.2", "base64 0.12.0", "bitflags", "bitvec", @@ -775,6 +776,7 @@ version = "0.1.0" [[package]] name = "fecs" version = "0.1.0" +source = "git+https://github.com/feather-rs/fecs?rev=7e4365c6fbf3a89676075659763daa4ecbd492c8#7e4365c6fbf3a89676075659763daa4ecbd492c8" dependencies = [ "fecs-macros", "fxhash", @@ -785,6 +787,7 @@ dependencies = [ [[package]] name = "fecs-macros" version = "0.1.0" +source = "git+https://github.com/feather-rs/fecs?rev=7e4365c6fbf3a89676075659763daa4ecbd492c8#7e4365c6fbf3a89676075659763daa4ecbd492c8" dependencies = [ "proc-macro2 1.0.7", "quote 1.0.2", diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 9dadde27e..4cb097f39 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -167,7 +167,6 @@ pub fn derive_packet(_item: TokenStream) -> TokenStream { PacketParameterType::String, PacketParameterType::Uuid, PacketParameterType::Nbt, - PacketParameterType::Slot, PacketParameterType::EntityMetadata, ] .contains(parameter_type) diff --git a/core/src/entitymeta.rs b/core/src/entitymeta.rs index f9467d15a..ea4f3972a 100644 --- a/core/src/entitymeta.rs +++ b/core/src/entitymeta.rs @@ -86,7 +86,7 @@ impl ToMetaEntry for bool { impl ToMetaEntry for Slot { fn to_meta_entry(&self) -> MetaEntry { - MetaEntry::Slot(self.clone()) + MetaEntry::Slot(*self) } } @@ -209,7 +209,7 @@ where } } MetaEntry::Slot(slot) => { - buf.push_slot(slot); + buf.push_slot(*slot); } MetaEntry::Boolean(x) => buf.push_bool(*x), MetaEntry::Rotation(x, y, z) => { diff --git a/core/src/inventory.rs b/core/src/inventory.rs index 91856889a..a5667fb35 100644 --- a/core/src/inventory.rs +++ b/core/src/inventory.rs @@ -299,7 +299,7 @@ impl Inventory { for slot in COLLECT_SEARCH_ORDER.iter() { if let Some(slot_item) = self.item_at(*slot).cloned() { if slot_item.ty == item.ty { - self.add_to_stack(&mut item, &slot_item, *slot, &mut affected_slots); + self.add_to_stack(&mut item, slot_item, *slot, &mut affected_slots); if item.amount == 0 { return (affected_slots, 0); @@ -312,7 +312,7 @@ impl Inventory { let slot_item = self.item_at(*slot).cloned(); if slot_item.is_none() { let fake = ItemStack::new(item.ty, 0); - self.add_to_stack(&mut item, &fake, *slot, &mut affected_slots); + self.add_to_stack(&mut item, fake, *slot, &mut affected_slots); if item.amount == 0 { return (affected_slots, 0); } @@ -320,7 +320,7 @@ impl Inventory { if let Some(slot_item) = slot_item { if slot_item.ty == item.ty { - self.add_to_stack(&mut item, &slot_item, *slot, &mut affected_slots); + self.add_to_stack(&mut item, slot_item, *slot, &mut affected_slots); if item.amount == 0 { return (affected_slots, 0); @@ -336,7 +336,7 @@ impl Inventory { fn add_to_stack>( &mut self, item: &mut ItemStack, - slot_item: &ItemStack, + slot_item: ItemStack, slot: SlotIndex, affected_slots: &mut SmallVec, ) { diff --git a/core/src/network/mctypes.rs b/core/src/network/mctypes.rs index 55904aa4e..fb51feb35 100644 --- a/core/src/network/mctypes.rs +++ b/core/src/network/mctypes.rs @@ -30,7 +30,7 @@ pub trait McTypeWrite { fn push_nbt(&mut self, x: &T); - fn push_slot(&mut self, slot: &Option); + fn push_slot(&mut self, slot: Option); } /// Identifies a type from which Minecraft-specified @@ -111,7 +111,7 @@ impl McTypeWrite for BytesMut { self.extend_from_slice(&temp); } - fn push_slot(&mut self, slot: &Option) { + fn push_slot(&mut self, slot: Option) { self.push_bool(slot.is_some()); if let Some(slot) = slot.as_ref() { diff --git a/core/src/network/packet/implementation.rs b/core/src/network/packet/implementation.rs index d33d91964..0bad4064e 100644 --- a/core/src/network/packet/implementation.rs +++ b/core/src/network/packet/implementation.rs @@ -1365,7 +1365,7 @@ impl Packet for WindowItems { buf.push_i16(self.slots.len() as i16); for slot in &self.slots { - buf.push_slot(slot); + buf.push_slot(*slot); } } diff --git a/core/src/save/player_data.rs b/core/src/save/player_data.rs index b12db0987..31a5bed2c 100644 --- a/core/src/save/player_data.rs +++ b/core/src/save/player_data.rs @@ -10,7 +10,6 @@ use feather_items::Item; use std::fs; use std::io::Write; use std::path::{Path, PathBuf}; -use tokio::io::AsyncReadExt; use tokio::prelude::AsyncRead; use uuid::Uuid; @@ -96,7 +95,7 @@ impl InventorySlot { async fn load_from_file(mut reader: R) -> Result { let mut buf = vec![]; - reader.read(&mut buf).await?; + tokio::io::copy(&mut reader, &mut buf).await?; nbt::from_gzip_reader(buf.as_slice()) } @@ -129,7 +128,7 @@ mod tests { use hashbrown::HashMap; use std::io::Cursor; - //#[tokio::test] + #[tokio::test] async fn test_deserialize_player() { let cursor = Cursor::new(include_bytes!("player.dat").to_vec()); diff --git a/server/Cargo.toml b/server/Cargo.toml index b35854b1a..1e8fcd83d 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -20,8 +20,7 @@ feather-item-block = { path = "../item_block" } feather-codegen = { path = "../codegen" } # Core ECS + systems -# fecs = { git = "https://github.com/caelunshun/fecs", rev = "e865bce3a9ac7f8fd3a9a7b5e3c139a746500137" } -fecs = { path = "../../../dev/fecs" } +fecs = { git = "https://github.com/feather-rs/fecs", rev = "7e4365c6fbf3a89676075659763daa4ecbd492c8" } # Concurrency/threading crossbeam = "0.7" @@ -97,6 +96,7 @@ anyhow = "1.0" [dev-dependencies] criterion = "0.3" +approx = "0.3" [[bench]] name = "worldgen" diff --git a/server/src/io/initial_handler.rs b/server/src/io/initial_handler.rs index 7ad518252..5cf25b7d4 100644 --- a/server/src/io/initial_handler.rs +++ b/server/src/io/initial_handler.rs @@ -281,8 +281,8 @@ impl BungeeCordData { return Err(Error::BungeeSpecMismatch("Incorrect length".to_string())); } - let host = data.get(0).unwrap().to_string(); - let client = data.get(1).unwrap().to_string(); + let host = (*data.get(0).unwrap()).to_string(); + let client = (*data.get(1).unwrap()).to_string(); let uuid = Uuid::parse_str(*data.get(2).unwrap()) .map_err(|e| Error::BungeeSpecMismatch(e.to_string()))?; let properties = serde_json::from_str(data.get(3).unwrap()) diff --git a/server/src/io/mod.rs b/server/src/io/mod.rs index 17dbe416d..16200c7f6 100644 --- a/server/src/io/mod.rs +++ b/server/src/io/mod.rs @@ -1,3 +1,5 @@ +#![allow(clippy::match_single_binding)] // https://github.com/mcarton/rust-derivative/issues/58 + use crate::config::Config; use crate::packet_buffer::PacketBuffers; use derivative::Derivative; @@ -27,6 +29,7 @@ pub enum WorkerToServerMessage { } #[derive(Debug)] +#[allow(clippy::large_enum_variant)] pub enum ListenerToServerMessage { /// Notifies the server thread that a new client connected. /// diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index 36e2449b9..fca30174f 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -57,6 +57,7 @@ struct Worker { } /// Runs a worker task for the given client. +#[allow(clippy::too_many_arguments)] pub async fn run_worker( stream: TcpStream, ip: SocketAddr, @@ -135,7 +136,8 @@ async fn run_worker_impl(worker: &mut Worker) -> anyhow::Result<()> { } } Either::Right((packet_res, _)) => { - let packet_res = packet_res.ok_or(anyhow::anyhow!("client disconnected"))?; + let packet_res = + packet_res.ok_or_else(|| anyhow::anyhow!("client disconnected"))?; let packet = packet_res?; @@ -187,7 +189,7 @@ async fn handle_ih_actions(worker: &mut Worker) -> anyhow::Result<()> { Action::JoinGame(info) => { let info = NewClientInfo { ip: worker.ip, - username: info.username.unwrap_or(String::from("undefined")), + username: info.username.unwrap_or_else(|| String::from("undefined")), profile: info.props, uuid: info.uuid, data: Default::default(), // TODO diff --git a/server/src/packet_buffer.rs b/server/src/packet_buffer.rs index be61b794f..9dc0cebf2 100644 --- a/server/src/packet_buffer.rs +++ b/server/src/packet_buffer.rs @@ -39,6 +39,12 @@ lazy_static! { ]; } +impl Default for PacketBuffers { + fn default() -> Self { + Self::new() + } +} + impl PacketBuffers { /// Creates a new packet store with buffers allocated for all packet types. pub fn new() -> Self { diff --git a/server/src/worldgen/noise.rs b/server/src/worldgen/noise.rs index d203bece3..372cd6d7a 100644 --- a/server/src/worldgen/noise.rs +++ b/server/src/worldgen/noise.rs @@ -219,7 +219,7 @@ mod tests { assert_eq!(chunk.len(), 16 * 256 * 16); for x in chunk { - assert_eq!(x, 0.0); + approx::assert_relative_eq!(x, 0.0); } } } From 455951bc693075be66fdf365370d37654f328143 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 13:44:11 -0600 Subject: [PATCH 101/647] Fix all chunks being resent to player when their view updates --- server/src/view.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/view.rs b/server/src/view.rs index c30fa74e8..c8b5e4905 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -167,8 +167,8 @@ fn find_new_chunks( let within_view_distance = chunks_within_view_distance(new, view_distance); if let Some(old) = old { Either::Left(within_view_distance.filter(move |chunk| { - (chunk.x - old.x).abs() <= view_distance as i32 - && (chunk.z - old.z).abs() <= view_distance as i32 + (chunk.x - old.x).abs() >= view_distance as i32 + || (chunk.z - old.z).abs() >= view_distance as i32 })) } else { Either::Right(within_view_distance) From ca2abd94aed76778b6293964d9e38e18c93cdaa0 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 13:51:17 -0600 Subject: [PATCH 102/647] Improve chunk sending performance by not cloning `Chunk`s --- core/src/network/packet/implementation.rs | 21 ++++++----- core/src/world.rs | 6 +++ server/src/view.rs | 45 +++++++++++++---------- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/core/src/network/packet/implementation.rs b/core/src/network/packet/implementation.rs index 0bad4064e..b8ec91d8c 100644 --- a/core/src/network/packet/implementation.rs +++ b/core/src/network/packet/implementation.rs @@ -10,10 +10,12 @@ use crate::{ use bytes::{Buf, BufMut, BytesMut}; use hashbrown::HashMap; use num_traits::{FromPrimitive, ToPrimitive}; +use parking_lot::RwLock; use std::any::Any; use std::io::Cursor; use std::io::Read; use std::io::Write; +use std::sync::Arc; use thiserror::Error; use uuid::Uuid; @@ -1547,13 +1549,13 @@ enum ChunkDataError { #[derive(Default, AsAny, Clone)] pub struct ChunkData { - pub chunk: Chunk, + pub chunk: Arc>, } impl Packet for ChunkData { fn read_from(&mut self, buf: &mut Cursor<&[u8]>) -> anyhow::Result<()> { - self.chunk - .set_position(ChunkPosition::new(buf.try_get_i32()?, buf.try_get_i32()?)); + let mut chunk = self.chunk.write(); + chunk.set_position(ChunkPosition::new(buf.try_get_i32()?, buf.try_get_i32()?)); if buf.try_get_bool()? { let primary_mask = buf.try_get_var_int()?; let temp_length = buf.try_get_var_int()?; @@ -1617,7 +1619,7 @@ impl Packet for ChunkData { let sky_light = chunk::BitArray::from_raw(sky_data, 4, chunk::SECTION_VOLUME); let section = chunk::ChunkSection::new(data, palette, block_light, sky_light); - self.chunk.set_section_at(i, Some(section)); + chunk.set_section_at(i, Some(section)); } } @@ -1628,14 +1630,15 @@ impl Packet for ChunkData { } fn write_to(&self, buf: &mut BytesMut) { - buf.push_i32(self.chunk.position().x); - buf.push_i32(self.chunk.position().z); + let chunk = self.chunk.read(); + buf.push_i32(chunk.position().x); + buf.push_i32(chunk.position().z); buf.push_bool(true); // Full chunk - assume true // Produce primary bit mask let mut primary_mask = { let mut r = 0; - for (i, section) in self.chunk.sections().iter().enumerate() { + for (i, section) in chunk.sections().iter().enumerate() { if section.is_some() { r |= 1 << i; } @@ -1648,7 +1651,7 @@ impl Packet for ChunkData { // TODO: approximate appropriate capacity let mut temp_buf = BytesMut::new(); - for section in self.chunk.sections() { + for section in chunk.sections() { if let Some(section) = section { temp_buf.push_u8(section.bits_per_block()); @@ -1689,7 +1692,7 @@ impl Packet for ChunkData { // Biomes temp_buf.reserve(256 * 4); - self.chunk + chunk .biomes() .iter() .map(|biome| biome.protocol_id()) diff --git a/core/src/world.rs b/core/src/world.rs index 4109f88af..f8322d77a 100644 --- a/core/src/world.rs +++ b/core/src/world.rs @@ -353,6 +353,12 @@ impl ChunkMap { pub fn chunk_at_mut(&self, pos: ChunkPosition) -> Option> { self.0.get(&pos).map(|lock| lock.write()) } + + /// Returns an `Arc>` at the given position. + pub fn chunk_handle_at(&self, pos: ChunkPosition) -> Option>> { + self.0.get(&pos).map(Arc::clone) + } + /// Retrieves the block at the specified /// location. If the chunk in which the block /// exists is not laoded, `None` is returned. diff --git a/server/src/view.rs b/server/src/view.rs index c8b5e4905..8b54c3e3f 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -27,9 +27,11 @@ use feather_core::network::packet::implementation::{ChunkData, DestroyEntities, use feather_core::{Chunk, ChunkPosition, Position}; use fecs::{Entity, IntoQuery, Read, World}; use itertools::Either; +use parking_lot::RwLock; use smallvec::SmallVec; use std::iter; use std::ops::Add; +use std::sync::Arc; /// System which polls for updated positions and /// calls `Game::on_chunk_cross()` accordingly. @@ -211,30 +213,35 @@ fn chunks_within_view_distance( pub struct ChunksToSend(AHashMap>); /// Asynchronously sends a chunk to a player. -fn send_chunk_to_player(game: &mut Game, world: &mut World, player: Entity, chunk: ChunkPosition) { +fn send_chunk_to_player( + game: &mut Game, + world: &mut World, + player: Entity, + chunk_pos: ChunkPosition, +) { // Ensure that the chunk isn't unloaded while the player has it loaded. - chunk_logic::hold_chunk(game, &mut *world.get_mut(player), chunk, player); + chunk_logic::hold_chunk(game, &mut *world.get_mut(player), chunk_pos, player); // If the chunk is already loaded, send it. Otherwise, we need to // queue it for loading. - if let Some(chunk) = game.chunk_map.chunk_at(chunk) { - world.get::(player).send(create_chunk_data(&chunk)); - game.on_chunk_send(world, chunk.position(), player); + if let Some(chunk) = game.chunk_map.chunk_handle_at(chunk_pos) { + world.get::(player).send(create_chunk_data(chunk)); + game.on_chunk_send(world, chunk_pos, player); } else { - let contains = game.chunks_to_send.0.contains_key(&chunk); + let contains = game.chunks_to_send.0.contains_key(&chunk_pos); - let vec = match game.chunks_to_send.0.get_mut(&chunk) { + let vec = match game.chunks_to_send.0.get_mut(&chunk_pos) { Some(vec) => vec, None => { - game.chunks_to_send.0.insert(chunk, smallvec![]); - game.chunks_to_send.0.get_mut(&chunk).unwrap() + game.chunks_to_send.0.insert(chunk_pos, smallvec![]); + game.chunks_to_send.0.get_mut(&chunk_pos).unwrap() } }; vec.push(player); if !contains { // Queue chunk for loading if it isn't already. - chunk_logic::load_chunk(&game.chunk_worker_handle, chunk); + chunk_logic::load_chunk(&game.chunk_worker_handle, chunk_pos); } } } @@ -257,11 +264,11 @@ fn unload_chunk_for_player( } /// System which sends chunks to pending players when a chunk is loaded. -pub fn on_chunk_load_send_to_clients(game: &mut Game, world: &mut World, chunk: ChunkPosition) { - if let Some(players) = game.chunks_to_send.0.get(&chunk) { +pub fn on_chunk_load_send_to_clients(game: &mut Game, world: &mut World, chunk_pos: ChunkPosition) { + if let Some(players) = game.chunks_to_send.0.get(&chunk_pos) { let chunk = game .chunk_map - .chunk_at(chunk) + .chunk_handle_at(chunk_pos) .expect("chunk not loaded, but load event was triggered"); for player in players { if !world.is_alive(*player) { @@ -270,17 +277,15 @@ pub fn on_chunk_load_send_to_clients(game: &mut Game, world: &mut World, chunk: world .get::(*player) - .send(create_chunk_data(&chunk)); - game.on_chunk_send(world, chunk.position(), *player); + .send(create_chunk_data(Arc::clone(&chunk))); + game.on_chunk_send(world, chunk_pos, *player); } } - game.chunks_to_send.0.remove(&chunk); + game.chunks_to_send.0.remove(&chunk_pos); } /// Creates a chunk data packet for the given chunk. -fn create_chunk_data(chunk: &Chunk) -> ChunkData { - ChunkData { - chunk: chunk.clone(), // TODO: optimize - } +fn create_chunk_data(chunk: Arc>) -> ChunkData { + ChunkData { chunk } } From 0bc149e0282e73796cd09f538265631eb1b97181 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 14:22:26 -0600 Subject: [PATCH 103/647] Use jemalloc instead of system allocator --- Cargo.lock | 36 +++++++++++++++++++++++++++++++----- server/Cargo.toml | 8 ++++++-- server/src/game.rs | 11 +++++++---- server/src/lib.rs | 6 ++---- 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 993eda7c7..72a255f2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -737,6 +737,7 @@ dependencies = [ "indexmap", "inventory", "itertools 0.9.0", + "jemallocator", "lazy_static", "lock_api", "log", @@ -776,7 +777,6 @@ version = "0.1.0" [[package]] name = "fecs" version = "0.1.0" -source = "git+https://github.com/feather-rs/fecs?rev=7e4365c6fbf3a89676075659763daa4ecbd492c8#7e4365c6fbf3a89676075659763daa4ecbd492c8" dependencies = [ "fecs-macros", "fxhash", @@ -787,7 +787,6 @@ dependencies = [ [[package]] name = "fecs-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/fecs?rev=7e4365c6fbf3a89676075659763daa4ecbd492c8#7e4365c6fbf3a89676075659763daa4ecbd492c8" dependencies = [ "proc-macro2 1.0.7", "quote 1.0.2", @@ -849,6 +848,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "fs_extra" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674" + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -1253,6 +1258,27 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +[[package]] +name = "jemalloc-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45" +dependencies = [ + "cc", + "fs_extra", + "libc", +] + +[[package]] +name = "jemallocator" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43ae63fcfc45e99ab3d1b29a46782ad679e98436c3169d15a167a1108a724b69" +dependencies = [ + "jemalloc-sys", + "libc", +] + [[package]] name = "js-sys" version = "0.3.35" @@ -1284,7 +1310,7 @@ dependencies = [ [[package]] name = "legion" version = "0.2.1" -source = "git+https://github.com/TomGillen/legion?rev=c5b9628630d4f9fc54b6843b5ce02d0669434a61#c5b9628630d4f9fc54b6843b5ce02d0669434a61" +source = "git+https://github.com/TomGillen/legion?branch=fix-zst-component-ub#b5a3dfd88f201af61fe58ad247e83583dfdde5ec" dependencies = [ "legion-core", "legion-systems", @@ -1293,7 +1319,7 @@ dependencies = [ [[package]] name = "legion-core" version = "0.2.1" -source = "git+https://github.com/TomGillen/legion?rev=c5b9628630d4f9fc54b6843b5ce02d0669434a61#c5b9628630d4f9fc54b6843b5ce02d0669434a61" +source = "git+https://github.com/TomGillen/legion?branch=fix-zst-component-ub#b5a3dfd88f201af61fe58ad247e83583dfdde5ec" dependencies = [ "crossbeam-channel", "derivative 1.0.3", @@ -1310,7 +1336,7 @@ dependencies = [ [[package]] name = "legion-systems" version = "0.2.1" -source = "git+https://github.com/TomGillen/legion?rev=c5b9628630d4f9fc54b6843b5ce02d0669434a61#c5b9628630d4f9fc54b6843b5ce02d0669434a61" +source = "git+https://github.com/TomGillen/legion?branch=fix-zst-component-ub#b5a3dfd88f201af61fe58ad247e83583dfdde5ec" dependencies = [ "bit-set", "crossbeam-channel", diff --git a/server/Cargo.toml b/server/Cargo.toml index 1e8fcd83d..dd81da75c 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -20,7 +20,8 @@ feather-item-block = { path = "../item_block" } feather-codegen = { path = "../codegen" } # Core ECS + systems -fecs = { git = "https://github.com/feather-rs/fecs", rev = "7e4365c6fbf3a89676075659763daa4ecbd492c8" } +# fecs = { git = "https://github.com/feather-rs/fecs", rev = "7e4365c6fbf3a89676075659763daa4ecbd492c8" } +fecs = { path = "../../../dev/fecs" } # Concurrency/threading crossbeam = "0.7" @@ -80,7 +81,6 @@ rand_xorshift = "0.2" num-derive = "0.3" num-traits = "0.2" lazy_static = "1.4" -bumpalo = { version = "3.2", features = ["collections"] } strum = "0.18" simdnoise = "3.1" simdeez = "1.0" @@ -90,6 +90,10 @@ inventory = "0.1" derivative = "2.0" itertools = "0.9" +# Allocators +bumpalo = { version = "3.2", features = ["collections"] } +jemallocator = "0.3" + # Error handling thiserror = "1.0" anyhow = "1.0" diff --git a/server/src/game.rs b/server/src/game.rs index e647999f2..9e2c93f21 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -7,18 +7,21 @@ use crate::chunk_entities::{ on_chunk_cross_update_chunk_entities, on_entity_despawn_update_chunk_entities, on_entity_spawn_update_chunk_entities, ChunkEntities, }; -use crate::chunk_logic::{ChunkHolders, ChunkUnloadQueue, ChunkWorkerHandle}; +use crate::chunk_logic::{ + on_chunk_holder_release_unload_chunk, on_entity_despawn_remove_chunk_holder, ChunkHolders, + ChunkUnloadQueue, ChunkWorkerHandle, +}; use crate::config::Config; use crate::io::{NetworkIoManager, NewClientInfo}; use crate::join::{on_chunk_send_join_player, on_player_join_send_join_game}; use crate::network::Network; use crate::packet_buffer::PacketBuffers; +use crate::player; use crate::player::Player; use crate::view::{ on_chunk_cross_update_chunks, on_chunk_cross_update_entities, on_chunk_load_send_to_clients, on_player_join_trigger_chunk_cross, ChunksToSend, }; -use crate::{chunk_logic, player}; use bumpalo::Bump; use feather_blocks::Block; use feather_core::level::LevelData; @@ -226,7 +229,7 @@ impl Game { /// Note that this is called __before__ the entity is deleted from the world. /// As such, components of the entity can still be accessed. pub fn on_entity_despawn(&mut self, world: &mut World, entity: Entity) { - chunk_logic::on_entity_despawn_remove_chunk_holder(self, world, entity); + on_entity_despawn_remove_chunk_holder(self, world, entity); on_entity_despawn_update_chunk_entities(self, world, entity); on_entity_despawn_broadcast_despawn(self, world, entity); if world.try_get::(entity).is_some() { @@ -276,7 +279,7 @@ impl Game { /// Called when a chunk holder is released. pub fn on_chunk_holder_release(&mut self, chunk: ChunkPosition, _holder: Entity) { - chunk_logic::on_chunk_holder_release_unload_chunk(self, chunk); + on_chunk_holder_release_unload_chunk(self, chunk); } /// Called when an entity crosses into a new chunk. diff --git a/server/src/lib.rs b/server/src/lib.rs index c8d22c7df..0b9f52b59 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -91,8 +91,6 @@ //! chunk packets, inventory, time, nearby entities, etc. `PlayerJoinEvent` //! is used to send this data. -#![feature(alloc_layout_extra)] - #[macro_use] extern crate log; #[macro_use] @@ -111,7 +109,6 @@ extern crate fecs; extern crate nalgebra_glm as glm; use crossbeam::Receiver; -use std::alloc::System; use std::sync::atomic::AtomicU32; use std::sync::Arc; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -127,6 +124,7 @@ use feather_core::level; use feather_core::level::{deserialize_level_file, save_level_file, LevelData, LevelGeneratorType}; use feather_core::world::ChunkMap; use fecs::{Executor, Resources, World}; +use jemallocator::Jemalloc; use rand::Rng; use std::collections::hash_map::DefaultHasher; use std::fs::File; @@ -137,7 +135,7 @@ use std::process::exit; use thread_local::CachedThreadLocal; #[global_allocator] -static ALLOC: System = System; +static ALLOC: Jemalloc = Jemalloc; // pub mod block; mod broadcasters; From b41822ae371d6ef9f8467bcb7f74b65fd1fce2d1 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 15:11:20 -0600 Subject: [PATCH 104/647] Reimplement inventory, held item handling --- server/src/broadcasters/inventory.rs | 57 ++++------ server/src/broadcasters/mod.rs | 6 +- server/src/game.rs | 55 +++++----- server/src/io/mod.rs | 2 + server/src/io/worker.rs | 1 + server/src/lazy.rs | 133 ------------------------ server/src/lib.rs | 11 +- server/src/p_inventory.rs | 3 +- server/src/packet_handlers/inventory.rs | 68 ++++++------ server/src/packet_handlers/mod.rs | 3 +- server/src/packet_handlers/movement.rs | 13 ++- server/src/packet_handlers/placement.rs | 15 ++- server/src/player/mod.rs | 3 +- server/src/systems.rs | 2 + 14 files changed, 116 insertions(+), 256 deletions(-) delete mode 100644 server/src/lazy.rs diff --git a/server/src/broadcasters/inventory.rs b/server/src/broadcasters/inventory.rs index e62ffdb2f..4889605f5 100644 --- a/server/src/broadcasters/inventory.rs +++ b/server/src/broadcasters/inventory.rs @@ -1,26 +1,21 @@ //! Broadcasting of inventory-related events. -use crate::entity::{EntityId, EntitySendEvent}; +use crate::entity::EntityId; +use crate::game::Game; use crate::network::Network; use crate::p_inventory::{EntityInventory, Equipment, InventoryUpdateEvent}; -use crate::state::State; use feather_core::inventory::{SlotIndex, SLOT_HOTBAR_OFFSET}; use feather_core::network::packet::implementation::{EntityEquipment, SetSlot}; -use legion::query::Read; +use fecs::{Entity, World}; use num_traits::ToPrimitive; -use tonks::{PreparedWorld, Query}; /// System for broadcasting equipment updates. -#[event_handler] -fn broadcast_equipment_updates( +pub fn on_inventory_update_broadcast_equipment_update( + game: &mut Game, + world: &mut World, event: &InventoryUpdateEvent, - state: &State, - _query: &mut Query<(Read, Read)>, - world: &mut PreparedWorld, ) { - let inv = world - .get_component::(event.player) - .unwrap(); + let inv = world.get::(event.player); for slot in &event.slots { // Skip this slot if it is not an equipment update. @@ -29,32 +24,27 @@ fn broadcast_equipment_updates( let item = inv.item_at(slot).cloned(); let packet = EntityEquipment { - entity_id: world.get_component::(event.player).unwrap().0, + entity_id: world.get::(event.player).0, slot: equipment.to_i32().unwrap(), item, }; - state.broadcast_entity_update(event.player, packet, Some(event.player)); + game.broadcast_entity_update(world, packet, event.player, Some(event.player)); } } } -/// System which listens to `EntitySendEvent`s and -/// sends entity equipment alongside. -#[event_handler] -fn send_entity_equipment( - event: &EntitySendEvent, - _query: &mut Query<(Read, Read, Read)>, - world: &mut PreparedWorld, -) { - if !world.is_alive(event.to) { +/// System to send an entity's equipment when the +/// entity is sent to a client. +pub fn on_entity_send_send_equipment(world: &mut World, entity: Entity, client: Entity) { + if !world.is_alive(client) || !world.is_alive(entity) { return; } - let network = world.get_component::(event.to).unwrap(); - let inventory = match world.get_component::(event.entity) { + let network = world.get::(client); + let inventory = match world.try_get::(entity) { Some(inv) => inv, - None => return, + None => return, // no equipment to send }; let equipments = [ @@ -75,7 +65,7 @@ fn send_entity_equipment( let equipment_slot = equipment.to_i32().unwrap(); let packet = EntityEquipment { - entity_id: world.get_component::(event.entity).unwrap().0, + entity_id: world.get::(entity).0, slot: equipment_slot, item, }; @@ -85,16 +75,9 @@ fn send_entity_equipment( /// System for sending the Set Slot packet /// when a player's inventory is updated. -#[event_handler] -fn send_set_slot( - event: &InventoryUpdateEvent, - _query: &mut Query<(Read, Read)>, - world: &mut PreparedWorld, -) { - let inv = world - .get_component::(event.player) - .unwrap(); - let network = world.get_component::(event.player).unwrap(); +pub fn on_inventory_update_send_set_slot(world: &mut World, event: &InventoryUpdateEvent) { + let inv = world.get::(event.player); + let network = world.get::(event.player); for slot in &event.slots { let packet = SetSlot { diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index 92a3ee99b..683a9a384 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -12,12 +12,16 @@ // mod chat; mod entity_creation; mod entity_deletion; -// mod inventory; +mod inventory; // mod item_collect; mod keepalive; // mod metadata; mod movement; +pub use self::inventory::{ + on_entity_send_send_equipment, on_inventory_update_broadcast_equipment_update, + on_inventory_update_send_set_slot, +}; pub use entity_creation::on_entity_spawn_send_to_clients; pub use entity_creation::on_player_join_send_existing_entities; pub use entity_deletion::on_entity_despawn_broadcast_despawn; diff --git a/server/src/game.rs b/server/src/game.rs index 9e2c93f21..c9f870e4e 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,7 +1,8 @@ use crate::broadcasters::{ on_entity_client_remove_update_last_known_positions, on_entity_despawn_broadcast_despawn, - on_entity_send_update_last_known_positions, on_entity_spawn_send_to_clients, - on_player_join_send_existing_entities, + on_entity_send_send_equipment, on_entity_send_update_last_known_positions, + on_entity_spawn_send_to_clients, on_inventory_update_broadcast_equipment_update, + on_inventory_update_send_set_slot, on_player_join_send_existing_entities, }; use crate::chunk_entities::{ on_chunk_cross_update_chunk_entities, on_entity_despawn_update_chunk_entities, @@ -12,10 +13,11 @@ use crate::chunk_logic::{ ChunkUnloadQueue, ChunkWorkerHandle, }; use crate::config::Config; -use crate::io::{NetworkIoManager, NewClientInfo}; +use crate::entity::Name; +use crate::io::{NetworkIoManager, NewClientInfo, ServerToWorkerMessage}; use crate::join::{on_chunk_send_join_player, on_player_join_send_join_game}; use crate::network::Network; -use crate::packet_buffer::PacketBuffers; +use crate::p_inventory::InventoryUpdateEvent; use crate::player; use crate::player::Player; use crate::view::{ @@ -28,6 +30,7 @@ use feather_core::level::LevelData; use feather_core::world::ChunkMap; use feather_core::{BlockPosition, ChunkPosition, Packet, Position}; use fecs::{Entity, IntoQuery, Read, World}; +use std::fmt::Display; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; use thread_local::CachedThreadLocal; @@ -40,8 +43,6 @@ use thread_local::CachedThreadLocal; pub struct Game { /// The IO handle. pub io_handle: NetworkIoManager, - /// Packet buffers used to poll for received packets. - pub packet_buffers: Arc, /// The server configuration. pub config: Arc, /// The server tick count, measured in ticks @@ -110,30 +111,19 @@ impl Game { self.bump.get_or_default() } - /* PACKET HANDLING FUNCTIONS */ - /// Returns all packets of type `T` received by `player`. - /// - /// # Panics - /// Panics if the packet buffer for packets of type `T` is not - /// a `MapBuffer` or an `ArrayBuffer`. - pub fn received_for<'a, T>(&'a self, player: Entity) -> impl Iterator + 'a - where - T: Packet, - { - self.packet_buffers.received_for(player) - } + /// Disconnects a player. + pub fn disconnect(&mut self, player: Entity, world: &mut World, reason: impl Display) { + let network = world.get::(player); - /// Returns all packets of type `T` received, along - /// with the players that received them. - /// - /// # Panics - /// Panics if the packet buffer for packets of type `T` is not - /// a `ChannelBuffer`. - pub fn received<'a, T>(&'a self) -> impl Iterator + 'a - where - T: Packet, - { - self.packet_buffers.received() + let name = world.get::(player); + info!("{} disconnected: {}", name.0, reason); + + let _ = network.tx.unbounded_send(ServerToWorkerMessage::Disconnect); + + drop(name); + drop(network); + + self.despawn(player, world); } /* BROADCAST FUNCTIONS */ @@ -246,6 +236,7 @@ impl Game { /// Called when an entity is spawned on a client. pub fn on_entity_send(&self, world: &mut World, entity: Entity, client: Entity) { on_entity_send_update_last_known_positions(world, entity, client); + on_entity_send_send_equipment(world, entity, client); } /// Called when an entity is removed on a client (Destroy Entities packet) @@ -299,6 +290,12 @@ impl Game { pub fn on_chunk_send(&self, world: &mut World, chunk: ChunkPosition, player: Entity) { on_chunk_send_join_player(self, world, chunk, player); } + + /// Called when a player's inventory is updated. + pub fn on_inventory_update(&mut self, world: &mut World, event: InventoryUpdateEvent) { + on_inventory_update_send_set_slot(world, &event); + on_inventory_update_broadcast_equipment_update(self, world, &event); + } } #[system] diff --git a/server/src/io/mod.rs b/server/src/io/mod.rs index 16200c7f6..08b6fbf45 100644 --- a/server/src/io/mod.rs +++ b/server/src/io/mod.rs @@ -20,6 +20,8 @@ mod worker; pub enum ServerToWorkerMessage { /// Requests that a packet be sent to the client. SendPacket(Box), + /// Requests that the client be disconnected. + Disconnect, } #[derive(Debug)] diff --git a/server/src/io/worker.rs b/server/src/io/worker.rs index fca30174f..ae8202118 100644 --- a/server/src/io/worker.rs +++ b/server/src/io/worker.rs @@ -153,6 +153,7 @@ async fn handle_server_to_worker_message( ) -> anyhow::Result<()> { match msg { ServerToWorkerMessage::SendPacket(packet) => worker.framed.send(packet).await?, + ServerToWorkerMessage::Disconnect => anyhow::bail!("server requested disconnect"), } Ok(()) diff --git a/server/src/lazy.rs b/server/src/lazy.rs deleted file mode 100644 index 27fd1f1fb..000000000 --- a/server/src/lazy.rs +++ /dev/null @@ -1,133 +0,0 @@ -use crate::entity::{EntityDeleteEvent, EntityId}; -use crossbeam::queue::SegQueue; -use feather_core::Position; -use legion::entity::Entity; -use legion::storage::{Component, Tag}; -use legion::world::World; -use smallvec::SmallVec; -use tonks::Scheduler; -use uuid::Uuid; - -pub trait LazyFnWithScheduler: FnOnce(&mut World, &mut Scheduler) + Send {} -impl LazyFnWithScheduler for F where F: FnOnce(&mut World, &mut Scheduler) + Send {} - -pub trait LazyFn: FnOnce(&mut World) + Send {} -impl LazyFn for F where F: FnOnce(&mut World) + Send {} - -pub trait LazyEntityFn: FnOnce(&mut World, &mut Scheduler, Entity) + Send {} -impl LazyEntityFn for F where F: FnOnce(&mut World, &mut Scheduler, Entity) + Send {} - -/// Resource which allows lazy creation of entities -/// or execution of functions with world access. -#[derive(Default, Resource)] -pub struct Lazy { - /// Internal queue of actions to perform. - queue: SegQueue, -} - -impl Lazy { - /// Lazily executes a closure with world access. - pub fn exec(&self, f: impl FnOnce(&mut World) + Send + 'static) { - self.exec_with_scheduler(move |world, _| f(world)); - } - - /// Lazily executes a closure with world and scheduler (resource) - /// access. - pub fn exec_with_scheduler(&self, f: impl FnOnce(&mut World, &mut Scheduler) + Send + 'static) { - self.queue.push(Action::Exec(Box::new(f))); - } - - /// Creates an `EntityBuilder` which can be used to lazily - /// create an entity. - pub fn create_entity(&self) -> EntityBuilder { - EntityBuilder { - lazy: self, - fns: smallvec![], - } - } - - /// Deletes an entity, triggering the necessary event as well. - pub fn delete_entity(&self, entity: Entity) { - self.exec_with_scheduler(move |world, scheduler| { - if !world.is_alive(entity) { - return; - } - - let position = world.get_component::(entity).map(|pos| *pos); - let id = *world.get_component::(entity).unwrap(); - let uuid = world - .get_component::(entity) - .map(|u| *u) - .unwrap_or(Uuid::new_v4()); - - scheduler.trigger(EntityDeleteEvent { - entity, - position, - id, - uuid, - }); - - world.delete(entity); - }); - } - - /// Performs all queued actions. - pub fn flush(&self, world: &mut World, scheduler: &mut Scheduler) { - while let Ok(action) = self.queue.pop() { - match action { - Action::Exec(f) => f(world, scheduler), - } - } - } -} - -/// An action which the lazy updater may perform. -enum Action { - Exec(Box), -} - -/// Builder for lazily creating entities. -pub struct EntityBuilder<'a> { - lazy: &'a Lazy, - fns: SmallVec<[Box; 8]>, -} - -impl<'a> EntityBuilder<'a> { - pub fn with_component(mut self, component: C) -> Self { - self.fns.push(Box::new( - move |world: &mut World, _: &mut Scheduler, entity: Entity| { - world.add_component(entity, component); - }, - )); - self - } - - pub fn with_tag(mut self, tag: T) -> Self { - self.fns.push(Box::new( - move |world: &mut World, _: &mut Scheduler, entity: Entity| { - world.add_tag(entity, tag); - }, - )); - self - } - - /// Executes a function with the entity after it is created. - pub fn with_exec( - mut self, - f: impl FnOnce(&mut World, &mut Scheduler, Entity) + Send + 'static, - ) -> Self { - self.fns.push(Box::new(f)); - self - } - - pub fn build(self) { - let fns = self.fns; - self.lazy.exec_with_scheduler(move |world, scheduler| { - let entity = world.insert((), [()].iter().copied())[0]; - - for f in fns { - f(world, scheduler, entity); - } - }) - } -} diff --git a/server/src/lib.rs b/server/src/lib.rs index 0b9f52b59..a91f0ef82 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -146,9 +146,8 @@ pub mod config; pub mod entity; pub mod io; mod join; -// pub mod lazy; pub mod network; -// pub mod p_inventory; // Prefixed to avoid conflict with inventory crate +pub mod p_inventory; // Prefixed to avoid conflict with inventory crate mod packet_handlers; // pub mod physics; pub mod player; @@ -215,7 +214,6 @@ pub fn main() { let game = Game { io_handle, - packet_buffers, config, tick_count: 0, player_count, @@ -229,11 +227,11 @@ pub fn main() { chunk_entities: Default::default(), }; - let (executor, resources) = init_executor(game); + let (executor, resources) = init_executor(game, packet_buffers); let mut world = World::new(); // Channel used by the shutdown handler to notify the server thread. - let (shutdown_tx, shutdown_rx) = crossbeam::unbounded(); + let (shutdown_tx, shutdown_rx) = crossbeam::bounded(1); shutdown::init(shutdown_tx); @@ -300,9 +298,10 @@ fn run_loop( } /// Initializes the executor and resources. -fn init_executor(game: Game) -> (Executor, Resources) { +fn init_executor(game: Game, packet_buffers: Arc) -> (Executor, Resources) { let mut resources = Resources::new(); resources.insert(game); + resources.insert(packet_buffers); let executor = systems::build_executor(); diff --git a/server/src/p_inventory.rs b/server/src/p_inventory.rs index bf23f1aaf..5b9a8fe5c 100644 --- a/server/src/p_inventory.rs +++ b/server/src/p_inventory.rs @@ -3,7 +3,8 @@ use feather_core::inventory::{ SLOT_ARMOR_LEGS, SLOT_HOTBAR_OFFSET, SLOT_OFFHAND, }; use feather_core::ItemStack; -use legion::entity::Entity; +use fecs::Entity; +use num_derive::{FromPrimitive, ToPrimitive}; use smallvec::SmallVec; use std::ops::{Deref, DerefMut}; diff --git a/server/src/packet_handlers/inventory.rs b/server/src/packet_handlers/inventory.rs index 35ba82d66..5da47d2a4 100644 --- a/server/src/packet_handlers/inventory.rs +++ b/server/src/packet_handlers/inventory.rs @@ -1,49 +1,42 @@ //! Handling of inventory update packets. //! This currently includes Creative Inventory Action and Held Item Change. -use crate::entity::item::ItemDropEvent; -use crate::network::PacketQueue; +use crate::game::Game; use crate::p_inventory::{EntityInventory, InventoryUpdateEvent}; -use crate::state::State; -use crate::util::disconnect_player; +use crate::packet_buffer::PacketBuffers; use feather_core::inventory::{HOTBAR_SIZE, SLOT_HOTBAR_OFFSET}; use feather_core::network::packet::implementation::{ CreativeInventoryAction, HeldItemChangeServerbound, }; use feather_core::Gamemode; -use legion::prelude::Read; -use legion::query::Write; -use tonks::{PreparedWorld, Query, Trigger}; +use fecs::World; +use std::sync::Arc; /// System for handling Creative Inventory Action packets. #[system] -fn handle_creative_inventory_action( - state: &State, - queue: &PacketQueue, - _query: &mut Query<(Read, Write)>, - world: &mut PreparedWorld, - trigger_inventory: &mut Trigger, - trigger_drop: &mut Trigger, +pub fn handle_creative_inventory_action( + game: &mut Game, + world: &mut World, + packet_buffers: &Arc, ) { - let packets = queue.received::(); + let packets = packet_buffers.received::(); for (player, packet) in packets { // Creative Inventory Action can only be used in creative // mode. - let gamemode = *world.get_component::(player).unwrap(); + let gamemode = *world.get::(player); if gamemode != Gamemode::Creative { - disconnect_player( - state, + game.disconnect( player, - "Attempted to use Creative Inventory Action while not in creative mode", + world, + "attempted to use Creative Inventory Action outside of creative mode", ); continue; } - let mut inventory = world.get_component_mut::(player).unwrap(); - // Slot -1 means that the user clicked outside the window, // dropping the item. + /* if packet.slot == -1 { match &packet.clicked_item { Some(stack) => { @@ -61,12 +54,19 @@ fn handle_creative_inventory_action( None => (), } } + */ + + let inventory = world.get::(player); + let slot_count = inventory.slot_count() as i16; + drop(inventory); - if packet.slot >= inventory.slot_count() as i16 || packet.slot < -1 { - disconnect_player(state, player, "Slot index out of bounds"); + if packet.slot >= slot_count || packet.slot < -1 { + game.disconnect(player, world, "Slot index out of bounds"); continue; } + let mut inventory = world.get_mut::(player); + match packet.clicked_item.as_ref() { Some(item) => { inventory.set_item_at(packet.slot as usize, item.clone()); @@ -81,28 +81,27 @@ fn handle_creative_inventory_action( slots: smallvec![packet.slot as usize], player, }; - trigger_inventory.trigger(event); + drop(inventory); + game.on_inventory_update(world, event); } } /// System for handling Held Item Change packets. #[system] -fn handle_held_item_change( - state: &State, - queue: &PacketQueue, - _query: &mut Query>, - world: &mut PreparedWorld, - trigger: &mut Trigger, +pub fn handle_held_item_change( + game: &mut Game, + world: &mut World, + packet_buffers: &Arc, ) { - let packets = queue.received::(); + let packets = packet_buffers.received::(); for (player, packet) in packets { if packet.slot as usize >= HOTBAR_SIZE { - disconnect_player(state, player, "Hotbar index out of bounds"); + game.disconnect(player, world, "Hotbar index out of bounds"); continue; } - let mut inventory = world.get_component_mut::(player).unwrap(); + let mut inventory = world.get_mut::(player); inventory.held_item = packet.slot as usize; // Trigger event @@ -110,6 +109,7 @@ fn handle_held_item_change( slots: smallvec![inventory.held_item as usize + SLOT_HOTBAR_OFFSET], player, }; - trigger.trigger(event); + drop(inventory); + game.on_inventory_update(world, event); } } diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index 85778eb9c..c5c0ebf21 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -3,8 +3,9 @@ // mod animation; // mod chat; // mod digging; -// mod inventory; +mod inventory; mod movement; // mod placement; +pub use self::inventory::{handle_creative_inventory_action, handle_held_item_change}; pub use movement::handle_movement_packets; diff --git a/server/src/packet_handlers/movement.rs b/server/src/packet_handlers/movement.rs index 421520143..18581cc74 100644 --- a/server/src/packet_handlers/movement.rs +++ b/server/src/packet_handlers/movement.rs @@ -1,19 +1,22 @@ -use crate::game::Game; use crate::network::Network; +use crate::packet_buffer::PacketBuffers; use feather_core::network::packet::implementation::{ PlayerLook, PlayerPosition, PlayerPositionAndLookServerbound, }; use feather_core::Position; use fecs::{component, IntoQuery, World, Write}; +use std::sync::Arc; /// System to handle player movement updates. #[system] -pub fn handle_movement_packets(game: &Game, world: &mut World) { +pub fn handle_movement_packets(world: &mut World, packet_buffers: &Arc) { >::query() .filter(component::()) .par_entities_for_each_mut(world.inner_mut(), |(player, mut position)| { let mut position: &mut Position = &mut *position; - for position_and_look in game.received_for::(player) { + for position_and_look in + packet_buffers.received_for::(player) + { position.x = position_and_look.x; position.y = position_and_look.feet_y; position.z = position_and_look.z; @@ -22,14 +25,14 @@ pub fn handle_movement_packets(game: &Game, world: &mut World) { position.on_ground = position_and_look.on_ground; } - for position_update in game.received_for::(player) { + for position_update in packet_buffers.received_for::(player) { position.x = position_update.x; position.y = position_update.feet_y; position.z = position_update.z; position.on_ground = position_update.on_ground; } - for look in game.received_for::(player) { + for look in packet_buffers.received_for::(player) { position.pitch = look.pitch; position.yaw = look.yaw; position.on_ground = look.on_ground; diff --git a/server/src/packet_handlers/placement.rs b/server/src/packet_handlers/placement.rs index 7999aa724..38ecf62f8 100644 --- a/server/src/packet_handlers/placement.rs +++ b/server/src/packet_handlers/placement.rs @@ -11,23 +11,22 @@ use feather_core::{Block, Gamemode, ItemStack}; use feather_item_block::ItemToBlock; use legion::query::{Read, Write}; use tonks::{PreparedWorld, Query, Trigger}; +use crate::game::Game; +use fecs::World; /// System for handling Player Block Placement packets /// and updating the world accordingly. #[system] fn handle_player_block_placement( - state: &State, - queue: &PacketQueue, - _query: &mut Query<(Write, Read)>, - world: &mut PreparedWorld, - inventory_update_events: &mut Trigger, + game: &mut Game, + world: &mut World, ) { - let packets = queue.received::(); + let packets = game.received::(); for (player, packet) in packets { // TODO: handle slabs, blocks with directions, etc. - let gamemode = *world.get_component::(player).unwrap(); - let mut inventory = world.get_component_mut::(player).unwrap(); + let gamemode = *world.get::(player); + let mut inventory = world.get::(player).unwrap(); let item = match inventory.item_in_main_hand() { Some(item) => item, diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index 9edb0cca3..284f8a45f 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -6,6 +6,7 @@ use crate::entity; use crate::entity::{CreationPacketCreator, EntityId, Name, PreviousPosition, SpawnPacketCreator}; use crate::io::NewClientInfo; use crate::network::Network; +use crate::p_inventory::EntityInventory; use crate::util::degrees_to_stops; use feather_core::network::packet::implementation::{PlayerInfo, PlayerInfoAction, SpawnPlayer}; use feather_core::{Gamemode, Packet, Position}; @@ -57,7 +58,7 @@ pub fn create(world: &mut World, info: NewClientInfo) -> Entity { .add(entity, CreationPacketCreator(&create_initialization_packet)) .unwrap(); world.add(entity, Gamemode::Creative).unwrap(); // TODO: proper gamemode handling - //world.add(entity, EntityInventory::default()) + world.add(entity, EntityInventory::default()).unwrap(); world.add(entity, Player).unwrap(); entity } diff --git a/server/src/systems.rs b/server/src/systems.rs index 95a2817dc..500c78639 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -8,6 +8,8 @@ pub fn build_executor() -> Executor { .with(network::poll_player_disconnect) .with(network::poll_new_clients) .with(packet_handlers::handle_movement_packets) + .with(packet_handlers::handle_creative_inventory_action) + .with(packet_handlers::handle_held_item_change) .with(chunk_logic::chunk_load) .with(chunk_logic::chunk_unload) .with(chunk_logic::chunk_optimize) From 92c5d466214cc31a15203092e82362e745f30497 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 16:12:08 -0600 Subject: [PATCH 105/647] Reimplement hand animations --- server/src/broadcasters/animation.rs | 24 +++++++++++------------- server/src/broadcasters/mod.rs | 3 ++- server/src/game.rs | 15 +++++++++++++-- server/src/packet_handlers/animation.rs | 13 +++++++------ server/src/packet_handlers/mod.rs | 3 ++- server/src/systems.rs | 1 + 6 files changed, 36 insertions(+), 23 deletions(-) diff --git a/server/src/broadcasters/animation.rs b/server/src/broadcasters/animation.rs index a239e3806..0682e011f 100644 --- a/server/src/broadcasters/animation.rs +++ b/server/src/broadcasters/animation.rs @@ -1,21 +1,19 @@ use crate::entity::EntityId; -use crate::player::PlayerAnimationEvent; -use crate::state::State; +use crate::game::Game; use feather_core::network::packet::implementation::AnimationClientbound; -use legion::query::Read; -use tonks::{PreparedWorld, Query}; +use feather_core::ClientboundAnimation; +use fecs::{Entity, World}; /// Broadcasts animations. -#[event_handler] -fn broadcast_animation( - event: &PlayerAnimationEvent, - state: &State, - _query: &mut Query>, - world: &mut PreparedWorld, +pub fn on_player_animation_broadcast_animation( + game: &mut Game, + world: &World, + player: Entity, + animation: ClientboundAnimation, ) { let packet = AnimationClientbound { - entity_id: world.get_component::(event.player).unwrap().0, - animation: event.animation, + entity_id: world.get::(player).0, + animation, }; - state.broadcast_entity_update(event.player, packet, Some(event.player)); + game.broadcast_entity_update(world, packet, player, Some(player)); } diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index 683a9a384..bfb1ba99c 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -7,7 +7,7 @@ //! packets have been sent. This is done through `EntitySendEvent`. //! * Those which just send a packet to a single player. -// mod animation; +mod animation; // mod block; // mod chat; mod entity_creation; @@ -22,6 +22,7 @@ pub use self::inventory::{ on_entity_send_send_equipment, on_inventory_update_broadcast_equipment_update, on_inventory_update_send_set_slot, }; +pub use animation::on_player_animation_broadcast_animation; pub use entity_creation::on_entity_spawn_send_to_clients; pub use entity_creation::on_player_join_send_existing_entities; pub use entity_deletion::on_entity_despawn_broadcast_despawn; diff --git a/server/src/game.rs b/server/src/game.rs index c9f870e4e..d0bad9284 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -2,7 +2,8 @@ use crate::broadcasters::{ on_entity_client_remove_update_last_known_positions, on_entity_despawn_broadcast_despawn, on_entity_send_send_equipment, on_entity_send_update_last_known_positions, on_entity_spawn_send_to_clients, on_inventory_update_broadcast_equipment_update, - on_inventory_update_send_set_slot, on_player_join_send_existing_entities, + on_inventory_update_send_set_slot, on_player_animation_broadcast_animation, + on_player_join_send_existing_entities, }; use crate::chunk_entities::{ on_chunk_cross_update_chunk_entities, on_entity_despawn_update_chunk_entities, @@ -28,7 +29,7 @@ use bumpalo::Bump; use feather_blocks::Block; use feather_core::level::LevelData; use feather_core::world::ChunkMap; -use feather_core::{BlockPosition, ChunkPosition, Packet, Position}; +use feather_core::{BlockPosition, ChunkPosition, ClientboundAnimation, Packet, Position}; use fecs::{Entity, IntoQuery, Read, World}; use std::fmt::Display; use std::sync::atomic::{AtomicU32, Ordering}; @@ -296,6 +297,16 @@ impl Game { on_inventory_update_send_set_slot(world, &event); on_inventory_update_broadcast_equipment_update(self, world, &event); } + + /// Called when a player causes an animation. + pub fn on_player_animation( + &mut self, + world: &mut World, + player: Entity, + animation: ClientboundAnimation, + ) { + on_player_animation_broadcast_animation(self, world, player, animation); + } } #[system] diff --git a/server/src/packet_handlers/animation.rs b/server/src/packet_handlers/animation.rs index e42a10d3f..c4aafe98c 100644 --- a/server/src/packet_handlers/animation.rs +++ b/server/src/packet_handlers/animation.rs @@ -1,13 +1,14 @@ -use crate::network::PacketQueue; -use crate::player::PlayerAnimationEvent; +use crate::game::Game; +use crate::packet_buffer::PacketBuffers; use feather_core::network::packet::implementation::AnimationServerbound; use feather_core::{ClientboundAnimation, Hand}; -use tonks::Trigger; +use fecs::World; +use std::sync::Arc; /// Handles animation packets. #[system] -fn handle_animation(queue: &PacketQueue, trigger: &mut Trigger) { - queue +pub fn handle_animation(game: &mut Game, world: &mut World, packet_buffers: &Arc) { + packet_buffers .received::() .for_each(|(player, packet)| { let animation = match packet.hand { @@ -15,6 +16,6 @@ fn handle_animation(queue: &PacketQueue, trigger: &mut Trigger ClientboundAnimation::SwingOffhand, }; - trigger.trigger(PlayerAnimationEvent { player, animation }); + game.on_player_animation(world, player, animation); }); } diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index c5c0ebf21..27a419379 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -1,6 +1,6 @@ //! Systems which handle packets. -// mod animation; +mod animation; // mod chat; // mod digging; mod inventory; @@ -8,4 +8,5 @@ mod movement; // mod placement; pub use self::inventory::{handle_creative_inventory_action, handle_held_item_change}; +pub use animation::handle_animation; pub use movement::handle_movement_packets; diff --git a/server/src/systems.rs b/server/src/systems.rs index 500c78639..9b360f261 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -10,6 +10,7 @@ pub fn build_executor() -> Executor { .with(packet_handlers::handle_movement_packets) .with(packet_handlers::handle_creative_inventory_action) .with(packet_handlers::handle_held_item_change) + .with(packet_handlers::handle_animation) .with(chunk_logic::chunk_load) .with(chunk_logic::chunk_unload) .with(chunk_logic::chunk_optimize) From e54f7da71ea0231c8dbf8af1ab86b666660f2ab7 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 16:25:06 -0600 Subject: [PATCH 106/647] Fix entities not being destroyed on client when they exit the view distance --- server/src/network.rs | 7 +++---- server/src/view.rs | 21 +++++++++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/server/src/network.rs b/server/src/network.rs index d3710d06d..fcb99c075 100644 --- a/server/src/network.rs +++ b/server/src/network.rs @@ -58,15 +58,14 @@ pub fn poll_player_disconnect(game: &mut Game, world: &mut World) { while let Ok(msg) = network.rx.try_recv() { match msg { WorkerToServerMessage::NotifyDisconnected { reason } => { - log::debug!("Server observed player disconnect: {}", reason); - to_despawn.push(entity); + to_despawn.push((entity, reason)); } } } }); - to_despawn.into_iter().for_each(|entity| { - game.despawn(entity, world); + to_despawn.into_iter().for_each(|(player, reason)| { + game.disconnect(player, world, reason); }); } diff --git a/server/src/view.rs b/server/src/view.rs index 8b54c3e3f..3ad3253bd 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -132,12 +132,25 @@ pub fn on_chunk_cross_update_entities( let mut to_client_remove_trigger = BumpVec::new_in(game.bump()); to_client_remove_trigger.extend( find_old_chunks(old, new, game.config.server.view_distance) - .flat_map(|chunk| game.chunk_entities.entities_in_chunk(chunk)), + .flat_map(|chunk| game.chunk_entities.entities_in_chunk(chunk)) + .map(|other| (*other, entity)), ); + // Despawn this entity on other visible entities. + find_old_chunks(old, new, game.config.server.view_distance) + .flat_map(|chunk| game.chunk_entities.entities_in_chunk(chunk)) + .filter_map(|entity| world.try_get::(*entity).map(|net| (*entity, net))) + .for_each(|(other, network)| { + let packet = DestroyEntities { + entity_ids: vec![world.get::(entity).0], + }; + network.send(packet); + to_client_remove_trigger.push((entity, other)); + }); + let to_destroy = to_client_remove_trigger .iter() - .map(|entity| world.get::(*entity).0) + .map(|(other, _)| world.get::(*other).0) .collect::>(); if !to_destroy.is_empty() { @@ -155,8 +168,8 @@ pub fn on_chunk_cross_update_entities( } // Trigger on_entity_client_remmove - for other in to_client_remove_trigger { - game.on_entity_client_remove(world, other, entity); + for (other, to) in to_client_remove_trigger { + game.on_entity_client_remove(world, other, to); } } From 0c6ef748dbae8f717ed6524a28f3cdf2d92a5961 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 16:42:06 -0600 Subject: [PATCH 107/647] Load spawn chunks on server start; fix chunks not being unloaded --- server/src/chunk_logic.rs | 3 ++- server/src/lib.rs | 33 +++++++++++++++++++++++++++++---- server/src/view.rs | 6 +++--- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index 040acd0e4..c46beb92b 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -77,7 +77,7 @@ pub fn chunk_load(game: &mut Game, world: &mut World) { /// avoids constant nearby entity queries. #[derive(Default, Clone, Debug)] pub struct ChunkHolders { - inner: MultiMap, + inner: MultiMap, } impl ChunkHolders { @@ -279,6 +279,7 @@ pub fn release_chunk(game: &mut Game, world: &mut World, chunk: ChunkPosition, e vec.swap_remove(index); } } + dbg!(game.chunk_holders.inner.get_vec(&chunk)); game.on_chunk_holder_release(chunk, entity); } diff --git a/server/src/lib.rs b/server/src/lib.rs index a91f0ef82..d106ee861 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -120,10 +120,10 @@ use crate::packet_buffer::PacketBuffers; use crate::worldgen::{ ComposableGenerator, EmptyWorldGenerator, SuperflatWorldGenerator, WorldGenerator, }; -use feather_core::level; use feather_core::level::{deserialize_level_file, save_level_file, LevelData, LevelGeneratorType}; use feather_core::world::ChunkMap; -use fecs::{Executor, Resources, World}; +use feather_core::{level, ChunkPosition}; +use fecs::{EntityBuilder, Executor, Resources, World}; use jemallocator::Jemalloc; use rand::Rng; use std::collections::hash_map::DefaultHasher; @@ -240,8 +240,8 @@ pub fn main() { info!("Generating RSA keypair"); io::init(); - info!("Queuing spawn chunks for loading UNIMPLEMENTED"); - // load_spawn_chunks(&mut world); TODO + info!("Queuing spawn chunks for loading"); + load_spawn_chunks(&mut *resources.get_mut::(), &mut world); info!("Server started"); run_loop(&mut world, &resources, &executor, shutdown_rx); @@ -327,6 +327,31 @@ fn init_chunk_worker(world_dir: &Path, level: &LevelData) -> ChunkWorkerHandle { } } +/// Loads the chunks around the spawn area and creates +/// a chunk hold on those chunks to prevent them from +/// being unloaded. +/// +/// Note that these chunks are loaded asynchronously, +/// and this function will return before loading is complete. +fn load_spawn_chunks(game: &mut Game, world: &mut World) { + let view_distance = i32::from(game.config.server.view_distance); + + // Create an entity for the server and + // add chunk holders using it. + let server_entity = EntityBuilder::new().build().spawn_in(world); + + let offset_x = game.level.spawn_x / 16; + let offset_z = game.level.spawn_z / 16; + for x in -view_distance..=view_distance { + for z in -view_distance..=view_distance { + let chunk = ChunkPosition::new(x + offset_x, z + offset_z); + + chunk_logic::load_chunk(&game.chunk_worker_handle, chunk); + game.chunk_holders.insert_holder(chunk, server_entity); + } + } +} + /// Loads the configuration file, creating a default /// one if it does not exist. fn load_config() -> Config { diff --git a/server/src/view.rs b/server/src/view.rs index 3ad3253bd..52e7ef6ad 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -136,7 +136,7 @@ pub fn on_chunk_cross_update_entities( .map(|other| (*other, entity)), ); - // Despawn this entity on other visible entities. + // Despawn this entity on other visible clients. find_old_chunks(old, new, game.config.server.view_distance) .flat_map(|chunk| game.chunk_entities.entities_in_chunk(chunk)) .filter_map(|entity| world.try_get::(*entity).map(|net| (*entity, net))) @@ -199,8 +199,8 @@ fn find_old_chunks( if let Some(old) = old { Either::Left( chunks_within_view_distance(old, view_distance).filter(move |chunk| { - (chunk.x - new.x).abs() > view_distance as i32 - || (chunk.z - new.z).abs() > view_distance as i32 + (chunk.x - new.x).abs() >= view_distance as i32 + || (chunk.z - new.z).abs() >= view_distance as i32 }), ) } else { From 92743dfc176aa1ec5e4d33cd955c13703edf7f09 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 16 Mar 2020 19:18:10 -0600 Subject: [PATCH 108/647] Reimplement block placement --- Cargo.lock | 2 ++ server/Cargo.toml | 4 +-- server/src/broadcasters/block.rs | 26 +++++++-------- server/src/broadcasters/mod.rs | 3 +- server/src/game.rs | 17 +++++----- server/src/packet_handlers/inventory.rs | 2 +- server/src/packet_handlers/mod.rs | 3 +- server/src/packet_handlers/placement.rs | 43 ++++++++++++++----------- server/src/systems.rs | 1 + 9 files changed, 56 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72a255f2a..0576d303f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -777,6 +777,7 @@ version = "0.1.0" [[package]] name = "fecs" version = "0.1.0" +source = "git+https://github.com/feather-rs/fecs?rev=8060d8bf79db5b2d345cff14de3e8b5fa53897b0#8060d8bf79db5b2d345cff14de3e8b5fa53897b0" dependencies = [ "fecs-macros", "fxhash", @@ -787,6 +788,7 @@ dependencies = [ [[package]] name = "fecs-macros" version = "0.1.0" +source = "git+https://github.com/feather-rs/fecs?rev=8060d8bf79db5b2d345cff14de3e8b5fa53897b0#8060d8bf79db5b2d345cff14de3e8b5fa53897b0" dependencies = [ "proc-macro2 1.0.7", "quote 1.0.2", diff --git a/server/Cargo.toml b/server/Cargo.toml index dd81da75c..eefd7a2f0 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -20,8 +20,8 @@ feather-item-block = { path = "../item_block" } feather-codegen = { path = "../codegen" } # Core ECS + systems -# fecs = { git = "https://github.com/feather-rs/fecs", rev = "7e4365c6fbf3a89676075659763daa4ecbd492c8" } -fecs = { path = "../../../dev/fecs" } +fecs = { git = "https://github.com/feather-rs/fecs", rev = "8060d8bf79db5b2d345cff14de3e8b5fa53897b0" } +# fecs = { path = "../../../dev/fecs" } # Concurrency/threading crossbeam = "0.7" diff --git a/server/src/broadcasters/block.rs b/server/src/broadcasters/block.rs index 4b24f2225..9e03f3526 100644 --- a/server/src/broadcasters/block.rs +++ b/server/src/broadcasters/block.rs @@ -1,24 +1,22 @@ //! Broadcasting of block updates, i.e. when a block is changed to another. -use crate::block::{BlockUpdateCause, BlockUpdateEvent}; -use crate::state::State; +use crate::game::Game; use feather_core::network::packet::implementation::BlockChange; -use feather_core::BlockExt; +use feather_core::{Block, BlockExt, BlockPosition}; +use fecs::World; /// System for broadcasting block update /// events to all clients. -#[event_handler] -fn broadcast_block_update(event: &BlockUpdateEvent, state: &State) { +pub fn on_block_update_broadcast( + game: &mut Game, + world: &mut World, + pos: BlockPosition, + new_block: Block, +) { // Broadcast Block Change packet. - let neq = if let BlockUpdateCause::Player(player) = event.cause { - Some(player) - } else { - None - }; - let packet = BlockChange { - location: event.pos, - block_id: event.new_block.native_state_id() as i32, + location: pos, + block_id: new_block.native_state_id() as i32, }; - state.broadcast_chunk_update(event.pos.into(), packet, neq); + game.broadcast_chunk_update(world, packet, pos.into(), None); } diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index bfb1ba99c..bd59404c8 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -8,7 +8,7 @@ //! * Those which just send a packet to a single player. mod animation; -// mod block; +mod block; // mod chat; mod entity_creation; mod entity_deletion; @@ -23,6 +23,7 @@ pub use self::inventory::{ on_inventory_update_send_set_slot, }; pub use animation::on_player_animation_broadcast_animation; +pub use block::on_block_update_broadcast; pub use entity_creation::on_entity_spawn_send_to_clients; pub use entity_creation::on_player_join_send_existing_entities; pub use entity_deletion::on_entity_despawn_broadcast_despawn; diff --git a/server/src/game.rs b/server/src/game.rs index d0bad9284..8f238ebde 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,9 +1,9 @@ use crate::broadcasters::{ - on_entity_client_remove_update_last_known_positions, on_entity_despawn_broadcast_despawn, - on_entity_send_send_equipment, on_entity_send_update_last_known_positions, - on_entity_spawn_send_to_clients, on_inventory_update_broadcast_equipment_update, - on_inventory_update_send_set_slot, on_player_animation_broadcast_animation, - on_player_join_send_existing_entities, + on_block_update_broadcast, on_entity_client_remove_update_last_known_positions, + on_entity_despawn_broadcast_despawn, on_entity_send_send_equipment, + on_entity_send_update_last_known_positions, on_entity_spawn_send_to_clients, + on_inventory_update_broadcast_equipment_update, on_inventory_update_send_set_slot, + on_player_animation_broadcast_animation, on_player_join_send_existing_entities, }; use crate::chunk_entities::{ on_chunk_cross_update_chunk_entities, on_entity_despawn_update_chunk_entities, @@ -208,11 +208,12 @@ impl Game { /// Called when a block is updated. pub fn on_block_update( &mut self, - _world: &mut World, - _pos: BlockPosition, + world: &mut World, + pos: BlockPosition, _old: Block, - _new: Block, + new: Block, ) { + on_block_update_broadcast(self, world, pos, new); } /// Called when an entity is despawned/removed. diff --git a/server/src/packet_handlers/inventory.rs b/server/src/packet_handlers/inventory.rs index 5da47d2a4..bb0535957 100644 --- a/server/src/packet_handlers/inventory.rs +++ b/server/src/packet_handlers/inventory.rs @@ -60,7 +60,7 @@ pub fn handle_creative_inventory_action( let slot_count = inventory.slot_count() as i16; drop(inventory); - if packet.slot >= slot_count || packet.slot < -1 { + if packet.slot >= slot_count || packet.slot < /* -1 */ 0 { game.disconnect(player, world, "Slot index out of bounds"); continue; } diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index 27a419379..923f0b890 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -5,8 +5,9 @@ mod animation; // mod digging; mod inventory; mod movement; -// mod placement; +mod placement; pub use self::inventory::{handle_creative_inventory_action, handle_held_item_change}; pub use animation::handle_animation; pub use movement::handle_movement_packets; +pub use placement::handle_player_block_placement; diff --git a/server/src/packet_handlers/placement.rs b/server/src/packet_handlers/placement.rs index 38ecf62f8..59b136b52 100644 --- a/server/src/packet_handlers/placement.rs +++ b/server/src/packet_handlers/placement.rs @@ -1,47 +1,47 @@ //! Handling of player block placement packets. -use crate::block::BlockUpdateCause; -use crate::network::PacketQueue; +use crate::game::Game; use crate::p_inventory::{EntityInventory, InventoryUpdateEvent}; -use crate::state::State; -use crate::util::disconnect_player; +use crate::packet_buffer::PacketBuffers; use feather_core::inventory::SLOT_HOTBAR_OFFSET; use feather_core::network::packet::implementation::PlayerBlockPlacement; use feather_core::{Block, Gamemode, ItemStack}; use feather_item_block::ItemToBlock; -use legion::query::{Read, Write}; -use tonks::{PreparedWorld, Query, Trigger}; -use crate::game::Game; use fecs::World; +use std::sync::Arc; /// System for handling Player Block Placement packets /// and updating the world accordingly. #[system] -fn handle_player_block_placement( +pub fn handle_player_block_placement( game: &mut Game, world: &mut World, + packet_buffers: &Arc, ) { - let packets = game.received::(); + let packets = packet_buffers.received::(); for (player, packet) in packets { // TODO: handle slabs, blocks with directions, etc. let gamemode = *world.get::(player); - let mut inventory = world.get::(player).unwrap(); + let inventory = world.get::(player); let item = match inventory.item_in_main_hand() { - Some(item) => item, + Some(item) => *item, None => continue, // No block to place }; + drop(inventory); + let block = match item.ty.to_block() { Some(block) => block, None => continue, // Item is not a block }; - let placed_on = match state.block_at(packet.location) { + let placed_on = match game.block_at(packet.location) { Some(block) => block, None => { - disconnect_player(state, player, "Attempted to place block in unloaded chunk"); + drop(gamemode); + game.disconnect(player, world, "attempted to place block in unloaded chunk"); continue; } }; @@ -54,16 +54,22 @@ fn handle_player_block_placement( _ => packet.location + packet.face.placement_offset(), }; - state.set_block_at(pos, block, BlockUpdateCause::Player(player)); + drop(gamemode); + game.set_block_at(world, pos, block); + + let mut inventory = world.get_mut::(player); // Update player's inventory if in survival if gamemode == Gamemode::Survival { if item.amount == 0 { - disconnect_player( - state, + drop(inventory); + drop(gamemode); + game.disconnect( player, - "Attempted to place block with 0-sized item stack", + world, + "attempted to place block with zero-sized item stack", ); + continue; } let item = ItemStack::new(item.ty, item.amount - 1); @@ -73,7 +79,8 @@ fn handle_player_block_placement( slots: smallvec![SLOT_HOTBAR_OFFSET + inventory.held_item], player, }; - inventory_update_events.trigger(event); + drop(inventory); + game.on_inventory_update(world, event); } } } diff --git a/server/src/systems.rs b/server/src/systems.rs index 9b360f261..1e985d20e 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -11,6 +11,7 @@ pub fn build_executor() -> Executor { .with(packet_handlers::handle_creative_inventory_action) .with(packet_handlers::handle_held_item_change) .with(packet_handlers::handle_animation) + .with(packet_handlers::handle_player_block_placement) .with(chunk_logic::chunk_load) .with(chunk_logic::chunk_unload) .with(chunk_logic::chunk_optimize) From 30178df33f8497521d322b4bb6bf21139b949f48 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Tue, 17 Mar 2020 12:16:47 -0600 Subject: [PATCH 109/647] Implement block digging --- server/src/chunk_logic.rs | 1 - server/src/packet_handlers/digging.rs | 73 ++++++++++----------------- server/src/packet_handlers/mod.rs | 3 +- server/src/systems.rs | 1 + server/src/view.rs | 2 +- 5 files changed, 32 insertions(+), 48 deletions(-) diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index c46beb92b..4aae91c6e 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -279,7 +279,6 @@ pub fn release_chunk(game: &mut Game, world: &mut World, chunk: ChunkPosition, e vec.swap_remove(index); } } - dbg!(game.chunk_holders.inner.get_vec(&chunk)); game.on_chunk_holder_release(chunk, entity); } diff --git a/server/src/packet_handlers/digging.rs b/server/src/packet_handlers/digging.rs index 032c7b1f9..9667a8174 100644 --- a/server/src/packet_handlers/digging.rs +++ b/server/src/packet_handlers/digging.rs @@ -4,53 +4,38 @@ //! for actions mostly unrelated to digging including eating, shooting bows, //! swapping items out to the offhand, and dropping items. -use crate::block::BlockUpdateCause; -use crate::entity::item::ItemDropEvent; -use crate::network::PacketQueue; -use crate::p_inventory::{EntityInventory, InventoryUpdateEvent}; -use crate::state::State; -use crate::util::disconnect_player; -use feather_core::inventory::SLOT_HOTBAR_OFFSET; +use crate::game::Game; +use crate::p_inventory::EntityInventory; +use crate::packet_buffer::PacketBuffers; use feather_core::network::packet::implementation::{PlayerDigging, PlayerDiggingStatus}; -use feather_core::{Block, Gamemode, Item, ItemStack, Position}; -use legion::entity::Entity; -use legion::query::{Read, Write}; -use tonks::{PreparedWorld, Query, Trigger}; +use feather_core::{Block, Gamemode, Item}; +use fecs::{Entity, World}; +use std::sync::Arc; /// System responsible for polling for PlayerDigging /// packets and writing the corresponding events. #[system] -fn handle_player_digging( - state: &State, - queue: &PacketQueue, - _query: &mut Query<(Write, Read, Read)>, - world: &mut PreparedWorld, - inventory_updates: &mut Trigger, - item_drops: &mut Trigger, +pub fn handle_player_digging( + game: &mut Game, + world: &mut World, + packet_buffers: &Arc, ) { use PlayerDiggingStatus::*; - let packets = queue.received::(); + let packets = packet_buffers.received::(); for (player, packet) in packets { - let gamemode = *world.get_component::(player).unwrap(); - let mut inventory = world.get_component_mut::(player).unwrap(); - match packet.status { - StartedDigging | FinishedDigging | CancelledDigging => handle_digging( - packet, - state, - player, - gamemode, - inventory.item_in_main_hand(), - ), - DropItem | DropItemStack => handle_drop_item_stack( + StartedDigging | FinishedDigging | CancelledDigging => { + handle_digging(game, world, player, packet) + } + /*DropItem | DropItemStack => handle_drop_item_stack( packet, player, inventory_updates, item_drops, &mut inventory, - ), + ),*/ /* ConsumeItem => handle_consume_item( packet, @@ -67,13 +52,8 @@ fn handle_player_digging( } } -fn handle_digging( - packet: PlayerDigging, - state: &State, - player: Entity, - gamemode: Gamemode, - item_in_main_hand: Option<&ItemStack>, -) { +fn handle_digging(game: &mut Game, world: &mut World, player: Entity, packet: PlayerDigging) { + let gamemode = *world.get::(player); // Return early if needed match packet.status { PlayerDiggingStatus::StartedDigging => { @@ -85,6 +65,11 @@ fn handle_digging( _ => (), } + let item_in_main_hand = world + .get::(player) + .item_in_main_hand() + .copied(); + // Don't break block if player is holding a sword in creative mode. if gamemode == Gamemode::Creative { if let Some(item_in_main_hand) = item_in_main_hand { @@ -93,22 +78,19 @@ fn handle_digging( | Item::StoneSword | Item::GoldenSword | Item::IronSword - | Item::DiamondSword => return, + | Item::DiamondSword => return, // creative mode: don't break block with swords _ => (), } } } - if !state.set_block_at( - packet.location, - Block::Air, - BlockUpdateCause::Player(player), - ) { - disconnect_player(state, player, "Attempted to break block in unloaded chunk"); + if !game.set_block_at(world, packet.location, Block::Air) { + game.disconnect(player, world, "attempted to break block in unloaded chunk"); return; } } +/* fn handle_drop_item_stack( packet: PlayerDigging, entity: Entity, @@ -167,6 +149,7 @@ fn handle_drop_item_stack( item_drops.trigger(item_drop); } } +*/ /* /// Handles food consumption and shooting arrows. diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index 923f0b890..c245d9018 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -2,12 +2,13 @@ mod animation; // mod chat; -// mod digging; +mod digging; mod inventory; mod movement; mod placement; pub use self::inventory::{handle_creative_inventory_action, handle_held_item_change}; pub use animation::handle_animation; +pub use digging::handle_player_digging; pub use movement::handle_movement_packets; pub use placement::handle_player_block_placement; diff --git a/server/src/systems.rs b/server/src/systems.rs index 1e985d20e..1c3be832b 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -12,6 +12,7 @@ pub fn build_executor() -> Executor { .with(packet_handlers::handle_held_item_change) .with(packet_handlers::handle_animation) .with(packet_handlers::handle_player_block_placement) + .with(packet_handlers::handle_player_digging) .with(chunk_logic::chunk_load) .with(chunk_logic::chunk_unload) .with(chunk_logic::chunk_optimize) diff --git a/server/src/view.rs b/server/src/view.rs index 52e7ef6ad..9958c556a 100644 --- a/server/src/view.rs +++ b/server/src/view.rs @@ -167,7 +167,7 @@ pub fn on_chunk_cross_update_entities( game.on_entity_send(world, entity, client); } - // Trigger on_entity_client_remmove + // Trigger on_entity_client_remove for (other, to) in to_client_remove_trigger { game.on_entity_client_remove(world, other, to); } From 7df51655dcd52ff1ce59381d71232225c5ea11d8 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Tue, 17 Mar 2020 19:00:14 -0600 Subject: [PATCH 110/647] Start on item drops + item entities --- Cargo.lock | 1 + Cargo.toml | 3 - core/Cargo.toml | 1 + core/src/entitymeta.rs | 57 +++++- core/src/lib.rs | 2 +- server/src/broadcasters/metadata.rs | 22 +-- server/src/broadcasters/mod.rs | 3 +- server/src/broadcasters/movement.rs | 3 +- server/src/entity/item.rs | 170 ++++------------- server/src/entity/mod.rs | 2 +- server/src/game.rs | 27 ++- server/src/lib.rs | 3 +- server/src/packet_handlers/inventory.rs | 7 +- server/src/physics/entity.rs | 236 +++++++++++------------- server/src/physics/math.rs | 33 ++-- server/src/physics/mod.rs | 1 + server/src/systems.rs | 1 + 17 files changed, 249 insertions(+), 323 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0576d303f..874c3201e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -641,6 +641,7 @@ version = "0.5.0" dependencies = [ "aes", "anyhow", + "bitflags", "bitvec", "byteorder", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 3422bd56f..1e5dad450 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,3 @@ members = [ "codegen", "generator", ] - -[profile.dev] -opt-level = 1 diff --git a/core/Cargo.toml b/core/Cargo.toml index bc1189a65..1e8e2f39e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -38,6 +38,7 @@ multimap = "0.8" bitvec = "0.17" hash32 = "0.1" hash32-derive = "0.1" +bitflags = "1.2" # Serialization serde = { version = "1.0", features = ["derive"] } diff --git a/core/src/entitymeta.rs b/core/src/entitymeta.rs index ea4f3972a..64bd9a362 100644 --- a/core/src/entitymeta.rs +++ b/core/src/entitymeta.rs @@ -6,12 +6,35 @@ use crate::bytes_ext::{BytesExt, BytesMutExt, TryGetError}; use crate::network::mctypes::{McTypeRead, McTypeWrite}; use crate::world::BlockPosition; use crate::Slot; +use bitflags::bitflags; use bytes::Buf; -use hashbrown::HashMap; use num_traits::FromPrimitive; +use std::collections::BTreeMap; use uuid::Uuid; type OptUuid = Option; +type OptChat = Option; + +// Meta index constants. +pub const META_INDEX_ENTITY_BITMASK: u8 = 0; +pub const META_INDEX_AIR: u8 = 1; +pub const META_INDEX_CUSTOM_NAME: u8 = 2; +pub const META_INDEX_IS_CUSTOM_NAME_VISIBLE: u8 = 3; +pub const META_INDEX_IS_SILENT: u8 = 4; +pub const META_INDEX_NO_GRAVITY: u8 = 5; +pub const META_INDEX_ITEM_SLOT: u8 = 6; + +bitflags! { + pub struct EntityBitMask: u8 { + const ON_FIRE = 0x01; + const CROUCHED = 0x02; + const SPRINTING = 0x08; + const SWIMMING = 0x10; + const INVISIBLE = 0x20; + const GLOWING_EFFECT = 0x40; + const FLYING_WITH_ELYTRA = 0x80; + } +} #[derive(Clone, Debug, PartialEq)] pub enum MetaEntry { @@ -108,19 +131,36 @@ impl ToMetaEntry for BlockPosition { } } +impl ToMetaEntry for OptChat { + fn to_meta_entry(&self) -> MetaEntry { + MetaEntry::OptChat(self.clone()) + } +} + #[derive(Clone, Debug)] pub struct EntityMetadata { - values: HashMap, + values: BTreeMap, } impl EntityMetadata { pub fn new() -> Self { Self { - values: HashMap::new(), + values: BTreeMap::new(), } } - pub fn with(mut self, values: &[(u8, MetaEntry)]) -> Self { + /// Returns an entity metadata with the defaults for an `Entity`. + pub fn entity_base() -> Self { + Self::new() + //.with(META_INDEX_ENTITY_BITMASK, EntityBitMask::empty().bits()) + //.with(META_INDEX_AIR, 0i32) + //.with(META_INDEX_CUSTOM_NAME, OptChat::None) + //.with(META_INDEX_IS_CUSTOM_NAME_VISIBLE, false) + //.with(META_INDEX_IS_SILENT, false) + //.with(META_INDEX_NO_GRAVITY, false) + } + + pub fn with_many(mut self, values: &[(u8, MetaEntry)]) -> Self { for val in values { self.values.insert(val.0, val.1.clone()); } @@ -128,10 +168,15 @@ impl EntityMetadata { self } - pub fn set(&mut self, index: u8, entry: E) { + pub fn set(&mut self, index: u8, entry: impl ToMetaEntry) { self.values.insert(index, entry.to_meta_entry()); } + pub fn with(mut self, index: u8, entry: impl ToMetaEntry) -> Self { + self.set(index, entry); + self + } + pub fn get(&self, index: u8) -> Option { self.values.get(&index).cloned() } @@ -171,7 +216,7 @@ where B: Buf + std::io::Read, { fn try_get_metadata(&mut self) -> anyhow::Result { - let mut values = HashMap::new(); + let mut values = BTreeMap::new(); while self.has_remaining() { let index = self.try_get_u8()?; diff --git a/core/src/lib.rs b/core/src/lib.rs index c0300e6c1..0e103ead3 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -30,7 +30,7 @@ extern crate nalgebra_glm as glm; pub use biomes::Biome; pub use chunk::{BitArray, Chunk, ChunkSection}; -pub use entitymeta::EntityMetadata; +pub use entitymeta::*; pub use feather_blocks::*; pub use feather_items as item; pub use inventory::{ItemStack, Slot}; diff --git a/server/src/broadcasters/metadata.rs b/server/src/broadcasters/metadata.rs index cad4cc27b..d83d37cf3 100644 --- a/server/src/broadcasters/metadata.rs +++ b/server/src/broadcasters/metadata.rs @@ -1,26 +1,20 @@ //! Sending of entity metadata. -use crate::entity::{EntityId, EntitySendEvent}; -use crate::metadata::Metadata; +use crate::entity::EntityId; use crate::network::Network; use feather_core::network::packet::implementation::PacketEntityMetadata; -use legion::query::Read; -use tonks::{PreparedWorld, Query}; +use feather_core::EntityMetadata; +use fecs::{Entity, World}; /// System which sends entity metadata when an entity /// is sent to a player. -#[event_handler] -fn send_entity_metadata( - event: &EntitySendEvent, - _query: &mut Query<(Read, Read, Read)>, - world: &mut PreparedWorld, -) { - if let Some(meta) = world.get_component::(event.entity) { - if let Some(network) = world.get_component::(event.to) { - let entity_id = world.get_component::(event.entity).unwrap().0; +pub fn on_entity_send_send_metadata(world: &World, entity: Entity, client: Entity) { + if let Some(metadata) = world.try_get::(entity) { + if let Some(network) = world.try_get::(client) { + let entity_id = world.get::(entity).0; let packet = PacketEntityMetadata { entity_id, - metadata: meta.to_full_raw_metadata(), + metadata: (&*metadata).clone(), }; network.send(packet); } diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index bd59404c8..1ef69a85a 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -15,7 +15,7 @@ mod entity_deletion; mod inventory; // mod item_collect; mod keepalive; -// mod metadata; +mod metadata; mod movement; pub use self::inventory::{ @@ -28,6 +28,7 @@ pub use entity_creation::on_entity_spawn_send_to_clients; pub use entity_creation::on_player_join_send_existing_entities; pub use entity_deletion::on_entity_despawn_broadcast_despawn; pub use keepalive::broadcast_keepalive; +pub use metadata::on_entity_send_send_metadata; pub use movement::{ broadcast_entity_movement, on_entity_client_remove_update_last_known_positions, on_entity_send_update_last_known_positions, LastKnownPositions, diff --git a/server/src/broadcasters/movement.rs b/server/src/broadcasters/movement.rs index 0eed53b6a..2961da3fd 100644 --- a/server/src/broadcasters/movement.rs +++ b/server/src/broadcasters/movement.rs @@ -16,7 +16,7 @@ use std::ops::Deref; /// Component containing the last sent positions of all entities for a given client. /// This component is used to determine /// the relative movement for an entity. -#[derive(Default)] +#[derive(Default, Debug)] pub struct LastKnownPositions(pub DashMap); /// System to broadcast when an entity moves. @@ -41,6 +41,7 @@ pub fn broadcast_entity_movement(game: &mut Game, world: &mut World) { if let Some(network) = world.try_get::(*player) { let last_known_positions = world.get::(*player); let last_known_positions = last_known_positions.deref(); + if let Some(mut last_known_pos) = last_known_positions.0.get_mut(&entity) { for packet in packets_for_movement_update(entity_id, *last_known_pos.value(), pos) diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index 368bc263d..db59dc727 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -1,23 +1,16 @@ //! Handling of item entities. -use crate::entity::{EntityId, EntityMoveEvent, SpawnPacketCreator, Velocity}; -use crate::lazy::EntityBuilder; -use crate::metadata::Metadata; -use crate::p_inventory::{EntityInventory, InventoryUpdateEvent}; -use crate::physics::{nearby_entities, PhysicsBuilder}; +use crate::entity; +use crate::entity::{EntityId, SpawnPacketCreator, Velocity}; +use crate::game::Game; +use crate::physics::PhysicsBuilder; use crate::player::PLAYER_EYE_HEIGHT; -use crate::state::State; use crate::util::{degrees_to_stops, protocol_velocity}; -use crate::{entity, TickCount, TPS}; use feather_core::inventory::SlotIndex; use feather_core::network::packet::implementation::SpawnObject; -use feather_core::{ItemStack, Packet, Position}; -use legion::entity::Entity; -use legion::query::{Read, Write}; +use feather_core::{EntityMetadata, ItemStack, Packet, Position, META_INDEX_ITEM_SLOT}; +use fecs::{Entity, EntityBuilder, EntityRef, World}; use rand::Rng; -use std::ops::DerefMut; -use std::sync::atomic::{AtomicBool, Ordering}; -use tonks::{EntityAccessor, PreparedWorld, Query, Trigger}; use uuid::Uuid; /// Event triggered when an item is dropped. @@ -35,6 +28,7 @@ pub struct ItemDropEvent { pub player: Entity, } +/* /// Event triggered when an item is collected. #[derive(Debug, Clone)] pub struct ItemCollectEvent { @@ -52,32 +46,26 @@ pub struct CollectableAt(pub u64); /// Component storing if an item stack has been collected and queued for removal. pub struct IsRemoved(AtomicBool); +*/ // Item stack of an item entity is stored in `ItemStack` component /// System for spawning an item entity when /// an item is dropped. -#[event_handler] -pub fn item_spawn( - event: &ItemDropEvent, - state: &State, - _query: &mut Query>, - world: &mut PreparedWorld, - tick: &TickCount, -) { - let mut rng = rand::thread_rng(); - +pub fn on_item_drop_spawn_item_entity(game: &mut Game, world: &mut World, event: &ItemDropEvent) { // Spawn item entity. // Position is player's eye height minus 0.3 let mut pos = { - let player_pos = *world.get_component::(event.player).unwrap() - + glm::vec3(0.0, PLAYER_EYE_HEIGHT, 0.0); + let player_pos = + *world.get::(event.player) + glm::vec3(0.0, PLAYER_EYE_HEIGHT, 0.0); player_pos - glm::vec3(0.0f64, 0.3, 0.0) }; pos.on_ground = false; + let mut rng = game.rng(); + // This velocity calculation was sourced from Glowstone's // work. See https://github.com/GlowstoneMC/Glowstone/blob/dev/src/main/java/net/glowstone/entity/GlowHumanEntity.java // (method drop(ItemStack stack)) for their code. @@ -94,119 +82,27 @@ pub fn item_spawn( vel }; - create(state, pos, event.stack.clone(), tick.0 + TPS) - .with_component(Velocity(velocity)) - .build(); -} + drop(rng); -/// System to add items to entity inventories. -#[event_handler] -pub fn item_collect( - events: &[EntityMoveEvent], - state: &State, - _query: &mut Query<( - Write, - Read, - Write, - Write, - Read, - )>, - world: &mut PreparedWorld, - inventory_updates: &mut Trigger, - item_collects: &mut Trigger, -) { - // TODO: switch to par_iter - events.iter().for_each(|event: &EntityMoveEvent| { - if world - .get_component::(event.entity) - .is_none() - { - return; - } - - let pos = *world.get_component::(event.entity).unwrap(); - // Find nearby items. - let nearby_entities = - nearby_entities(&state.chunk_entities, world, pos, glm::vec3(1.0, 0.5, 1.0)); - - for other in nearby_entities { - if let Some(item_stack) = world.get_component::(other).map(|item| *item) { - // Ensure that this item hasn't already been collected, to avoid duplication. - { - let is_removed = world.get_component::(other).unwrap(); - if is_removed - .0 - .compare_and_swap(false, true, Ordering::Relaxed) - { - continue; - } - } - - let (affected_slots, items_left) = { - let mut inventory = world - .get_component_mut::(event.entity) - .unwrap(); - inventory.collect_item(item_stack) - }; - - inventory_updates.trigger(InventoryUpdateEvent { - slots: affected_slots, - player: event.entity, - }); - - item_collects.trigger(ItemCollectEvent { - item: other, - collector: event.entity, - amount: item_stack.amount - items_left, - }); - - if items_left == 0 { - state.delete_entity(other); - } else { - // Update item stack - let new_stack = ItemStack::new(item_stack.ty, items_left); - *world.get_component_mut::(other).unwrap() = new_stack; - match world - .get_component_mut::(other) - .unwrap() - .deref_mut() - { - Metadata::Item(ref mut meta_item) => meta_item.set_item(Some(new_stack)), - _ => unreachable!(), - } - - world - .get_component::(other) - .unwrap() - .0 - .store(false, Ordering::Relaxed); - } - } - } - }); + let entity = create(game, pos, event.stack) + .with(Velocity(velocity)) + .build() + .spawn_in(world); + game.on_entity_spawn(world, entity); } /// Returns an entity builder to create an item entity /// with the given stack and collectable tick. -pub fn create( - state: &State, - pos: Position, - stack: ItemStack, - collectable_at: u64, -) -> EntityBuilder { - let meta = { - let mut meta_item = crate::metadata::Item::default(); - meta_item.set_item(Some(stack.clone())); - Metadata::Item(meta_item) - }; - - entity::base(state, pos) - .with_component(stack) - .with_component(CollectableAt(collectable_at)) - .with_component(SpawnPacketCreator(&create_spawn_packet)) - .with_component(meta) - .with_component(IsRemoved(AtomicBool::new(false))) - .with_component( +pub fn create(_game: &mut Game, pos: Position, stack: ItemStack) -> EntityBuilder { + let meta = EntityMetadata::entity_base().with(META_INDEX_ITEM_SLOT, Some(stack)); + + entity::base(pos) + .with(stack) + //.with(CollectableAt(collectable_at)) + .with(SpawnPacketCreator(&create_spawn_packet)) + .with(meta) + //.with(IsRemoved(AtomicBool::new(false))) + .with( PhysicsBuilder::new() .bbox(0.25, 0.25, 0.25) .drag(0.98) @@ -215,10 +111,10 @@ pub fn create( ) } -fn create_spawn_packet(accessor: &EntityAccessor, world: &PreparedWorld) -> Box { - let position = *accessor.get_component::(world).unwrap(); - let velocity = *accessor.get_component::(world).unwrap(); - let entity_id = accessor.get_component::(world).unwrap().0; +fn create_spawn_packet(accessor: &EntityRef) -> Box { + let position = *accessor.get::(); + let velocity = *accessor.get::(); + let entity_id = accessor.get::().0; let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocity.0); diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index c059f04fc..2aac08287 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -3,7 +3,7 @@ //! block entities, monsters, etc. Player entities are handled in `crate::player`, //! not here. -// pub mod item; +pub mod item; use feather_core::{Packet, Position}; use fecs::{EntityBuilder, EntityRef, IntoQuery, Read, World, Write}; diff --git a/server/src/game.rs b/server/src/game.rs index 8f238ebde..c3bb4bc4c 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,9 +1,10 @@ use crate::broadcasters::{ on_block_update_broadcast, on_entity_client_remove_update_last_known_positions, on_entity_despawn_broadcast_despawn, on_entity_send_send_equipment, - on_entity_send_update_last_known_positions, on_entity_spawn_send_to_clients, - on_inventory_update_broadcast_equipment_update, on_inventory_update_send_set_slot, - on_player_animation_broadcast_animation, on_player_join_send_existing_entities, + on_entity_send_send_metadata, on_entity_send_update_last_known_positions, + on_entity_spawn_send_to_clients, on_inventory_update_broadcast_equipment_update, + on_inventory_update_send_set_slot, on_player_animation_broadcast_animation, + on_player_join_send_existing_entities, }; use crate::chunk_entities::{ on_chunk_cross_update_chunk_entities, on_entity_despawn_update_chunk_entities, @@ -14,6 +15,7 @@ use crate::chunk_logic::{ ChunkUnloadQueue, ChunkWorkerHandle, }; use crate::config::Config; +use crate::entity::item::{on_item_drop_spawn_item_entity, ItemDropEvent}; use crate::entity::Name; use crate::io::{NetworkIoManager, NewClientInfo, ServerToWorkerMessage}; use crate::join::{on_chunk_send_join_player, on_player_join_send_join_game}; @@ -31,6 +33,9 @@ use feather_core::level::LevelData; use feather_core::world::ChunkMap; use feather_core::{BlockPosition, ChunkPosition, ClientboundAnimation, Packet, Position}; use fecs::{Entity, IntoQuery, Read, World}; +use rand::{Rng, SeedableRng}; +use rand_xorshift::XorShiftRng; +use std::cell::{RefCell, RefMut}; use std::fmt::Display; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; @@ -60,7 +65,7 @@ pub struct Game { /// The chunk map. pub chunk_map: ChunkMap, /// Bump allocator. Reset every tick. - pub bump: CachedThreadLocal, + pub(super) bump: CachedThreadLocal, /// Chunk worker handle used for communication with /// the chunk worker. pub chunk_worker_handle: ChunkWorkerHandle, @@ -69,6 +74,7 @@ pub struct Game { pub chunk_holders: ChunkHolders, pub chunks_to_send: ChunksToSend, pub chunk_entities: ChunkEntities, + pub(super) rng: CachedThreadLocal>, } impl Game { @@ -112,6 +118,13 @@ impl Game { self.bump.get_or_default() } + /// Returns a random number generator. + pub fn rng(&self) -> RefMut { + self.rng + .get_or(|| RefCell::new(XorShiftRng::from_entropy())) + .borrow_mut() + } + /// Disconnects a player. pub fn disconnect(&mut self, player: Entity, world: &mut World, reason: impl Display) { let network = world.get::(player); @@ -239,6 +252,7 @@ impl Game { pub fn on_entity_send(&self, world: &mut World, entity: Entity, client: Entity) { on_entity_send_update_last_known_positions(world, entity, client); on_entity_send_send_equipment(world, entity, client); + on_entity_send_send_metadata(world, entity, client); } /// Called when an entity is removed on a client (Destroy Entities packet) @@ -308,6 +322,11 @@ impl Game { ) { on_player_animation_broadcast_animation(self, world, player, animation); } + + /// Called when an item is dropped by a player. + pub fn on_item_drop(&mut self, world: &mut World, event: ItemDropEvent) { + on_item_drop_spawn_item_entity(self, world, &event); + } } #[system] diff --git a/server/src/lib.rs b/server/src/lib.rs index d106ee861..bddad3f58 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -149,7 +149,7 @@ mod join; pub mod network; pub mod p_inventory; // Prefixed to avoid conflict with inventory crate mod packet_handlers; -// pub mod physics; +pub mod physics; pub mod player; pub mod shutdown; // pub mod time; @@ -225,6 +225,7 @@ pub fn main() { chunk_holders: Default::default(), chunks_to_send: Default::default(), chunk_entities: Default::default(), + rng: CachedThreadLocal::new(), }; let (executor, resources) = init_executor(game, packet_buffers); diff --git a/server/src/packet_handlers/inventory.rs b/server/src/packet_handlers/inventory.rs index bb0535957..d9e4415d1 100644 --- a/server/src/packet_handlers/inventory.rs +++ b/server/src/packet_handlers/inventory.rs @@ -1,6 +1,7 @@ //! Handling of inventory update packets. //! This currently includes Creative Inventory Action and Held Item Change. +use crate::entity::item::ItemDropEvent; use crate::game::Game; use crate::p_inventory::{EntityInventory, InventoryUpdateEvent}; use crate::packet_buffer::PacketBuffers; @@ -36,7 +37,6 @@ pub fn handle_creative_inventory_action( // Slot -1 means that the user clicked outside the window, // dropping the item. - /* if packet.slot == -1 { match &packet.clicked_item { Some(stack) => { @@ -46,7 +46,7 @@ pub fn handle_creative_inventory_action( stack: stack.clone(), player, }; - trigger_drop.trigger(event); + game.on_item_drop(world, event); // No need to update inventory continue; @@ -54,13 +54,12 @@ pub fn handle_creative_inventory_action( None => (), } } - */ let inventory = world.get::(player); let slot_count = inventory.slot_count() as i16; drop(inventory); - if packet.slot >= slot_count || packet.slot < /* -1 */ 0 { + if packet.slot >= slot_count || packet.slot < -1 { game.disconnect(player, world, "Slot index out of bounds"); continue; } diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index 85da1a749..193889328 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -1,16 +1,12 @@ //! Module for performing entity physics, including velocity, drag //! and position updates each tick. -use crate::entity::{EntityMoveEvent, Velocity, VelocityUpdateEvent}; +use crate::entity::Velocity; +use crate::game::Game; use crate::physics::{block_impacted_by_ray, blocks_intersecting_bbox, AABBExt, Physics, Side}; -use crate::state::State; -use crossbeam::queue::SegQueue; use feather_core::Position; use feather_core::{Block, BlockExt}; -use legion::entity::Entity; -use legion::query::{Read, Write}; -use parking_lot::Mutex; -use tonks::{PreparedWorld, Query, Trigger}; +use fecs::{Entity, IntoQuery, Read, World, Write}; /// Event triggered when an entity lands on the ground. #[derive(Debug, Clone)] @@ -22,146 +18,122 @@ pub struct EntityPhysicsLandEvent { /// System for updating all entities' positions and velocities /// each tick. #[system] -fn entity_physics( - state: &State, - query: &mut Query<(Write, Write, Read)>, - world: &mut PreparedWorld, - land_events: &mut Trigger, - move_events: &mut Trigger, - velocity_events: &mut Trigger, -) { - // Using a mutex is fine, since land events are written very rarely - // and thus contention is low. - let land_events = Mutex::new(land_events); - - // For move events, we use a `SegQueue`. (TODO: switch to ripstruct's SegBuffer after audit) - let move_event_queue = SegQueue::new(); - let velocity_event_queue = SegQueue::new(); - +pub fn entity_physics(game: &Game, world: &mut World) { // Go through entities and update their positions according // to their velocities. - query.par_entities_for_each(world, |(entity, (mut position, mut velocity, physics))| { - let mut pending_position = *position + velocity.0; - - // Check for blocks along path between old position and pending position. - // This prevents entities from flying through blocks when their - // velocity is sufficiently high. - let origin = (*position).into(); - let direction = (pending_position - *position).into(); - let distance_squared = pending_position.distance_squared_to(*position); + let query = <(Write, Write, Read)>::query(); + query.par_for_each_mut( + world.inner_mut(), + |(mut position, mut velocity, physics)| { + let mut pending_position = *position + velocity.0; + + // Check for blocks along path between old position and pending position. + // This prevents entities from flying through blocks when their + // velocity is sufficiently high. + let origin = (*position).into(); + let direction = (pending_position - *position).into(); + let distance_squared = pending_position.distance_squared_to(*position); + + if let Some(impacted) = block_impacted_by_ray(game, origin, direction, distance_squared) + { + // Set velocities along correct axis to 0 and then set position + // to just before the bbox would have impacted the block. + let face = impacted.face; + let impact = impacted.pos; + + if face.contains(Side::EAST) || face.contains(Side::WEST) { + velocity.x = 0.0; + pending_position.x = impact.x + physics.bbox.size().x * face.as_vector().x; + } + if face.contains(Side::NORTH) || face.contains(Side::SOUTH) { + velocity.z = 0.0; + pending_position.z = impact.z + physics.bbox.size().z * face.as_vector().z; + } + if face.contains(Side::TOP) || face.contains(Side::BOTTOM) { + velocity.y = 0.0; + pending_position.y = impact.y + physics.bbox.size().y * face.as_vector().y; + } + if face.contains(Side::TOP) { + pending_position.on_ground = true; + } + } - if let Some(impacted) = block_impacted_by_ray(&state, origin, direction, distance_squared) { - // Set velocities along correct axis to 0 and then set position - // to just before the bbox would have impacted the block. - let face = impacted.face; - let impact = impacted.pos; + // Check for blocks around the bbox and apply offset + // to position to stop the bbox from intersecting blocks. + let intersect = + blocks_intersecting_bbox(game, *position, pending_position, &physics.bbox); + intersect.apply_to(&mut pending_position); - if face.contains(Side::EAST) || face.contains(Side::WEST) { + if intersect.x_affected() { velocity.x = 0.0; - pending_position.x = impact.x + physics.bbox.size().x * face.as_vector().x; - } - if face.contains(Side::NORTH) || face.contains(Side::SOUTH) { - velocity.z = 0.0; - pending_position.z = impact.z + physics.bbox.size().z * face.as_vector().z; } - if face.contains(Side::TOP) || face.contains(Side::BOTTOM) { + + if intersect.y_affected() { velocity.y = 0.0; - pending_position.y = impact.y + physics.bbox.size().y * face.as_vector().y; - } - if face.contains(Side::TOP) { - pending_position.on_ground = true; } - } - - // Check for blocks around the bbox and apply offset - // to position to stop the bbox from intersecting blocks. - let intersect = - blocks_intersecting_bbox(&state, *position, pending_position, &physics.bbox); - intersect.apply_to(&mut pending_position); - if intersect.x_affected() { - velocity.x = 0.0; - } - - if intersect.y_affected() { - velocity.y = 0.0; - } - - if intersect.z_affected() { - velocity.z = 0.0; - } + if intersect.z_affected() { + velocity.z = 0.0; + } - // Delete entity if it has gone into unloaded chunks. - let block_at_pos = match state.block_at(pending_position.block()) { - Some(block) => block, - None => { - // Delete entity. - state.exec(move |world| { - world.delete(entity); + // Delete entity if it has gone into unloaded chunks. + let block_at_pos = match game.block_at(pending_position.block()) { + Some(block) => block, + None => { + // TODO: delete entity + return; + } + }; + + // Set on ground status. + pending_position.on_ground = match game.block_at( + position!( + pending_position.x, + pending_position.y - physics.bbox.size().y / 2.0 - 0.01, + pending_position.z + ) + .block(), + ) { + Some(block) => block.is_solid(), + None => false, + }; + /* TODO: land events + if pending_position.on_ground && !position.on_ground { + land_events.lock().trigger(EntityPhysicsLandEvent { + entity, + pos: pending_position, }); - return; } - }; - - // Set on ground status. - pending_position.on_ground = match state.block_at( - position!( - pending_position.x, - pending_position.y - physics.bbox.size().y / 2.0 - 0.01, - pending_position.z - ) - .block(), - ) { - Some(block) => block.is_solid(), - None => false, - }; - if pending_position.on_ground && !position.on_ground { - land_events.lock().trigger(EntityPhysicsLandEvent { - entity, - pos: pending_position, - }); - } + */ - // Apply drag and gravity. + // Apply drag and gravity. - // In water and lava, gravity is four times less, and velocity is multiplied by a special drag force. - let liquid_drag = 0.8; - match block_at_pos { - Block::Water(_) => { - velocity.0 *= liquid_drag; - velocity.0.y += physics.gravity / 4.0; - } - Block::Lava(_) => { - velocity.0 *= liquid_drag - 0.3; - velocity.0.y += physics.gravity / 4.0; - } - _ => { - let slip_multiplier = physics.slip_multiplier; - if pending_position.on_ground { - velocity.0.x *= slip_multiplier; - velocity.0.z *= slip_multiplier; - } else { - velocity.0.y = physics.drag * velocity.0.y + physics.gravity; - velocity.0.x *= physics.drag; - velocity.0.z *= physics.drag; + // In water and lava, gravity is four times less, and velocity is multiplied by a special drag force. + let liquid_drag = 0.8; + match block_at_pos { + Block::Water(_) => { + velocity.0 *= liquid_drag; + velocity.0.y += physics.gravity / 4.0; + } + Block::Lava(_) => { + velocity.0 *= liquid_drag - 0.3; + velocity.0.y += physics.gravity / 4.0; + } + _ => { + let slip_multiplier = physics.slip_multiplier; + if pending_position.on_ground { + velocity.0.x *= slip_multiplier; + velocity.0.z *= slip_multiplier; + } else { + velocity.0.y = physics.drag * velocity.0.y + physics.gravity; + velocity.0.x *= physics.drag; + velocity.0.z *= physics.drag; + } } } - } - - // Set new position. - *position = pending_position; - - // Queue move event + velocity event. - move_event_queue.push(EntityMoveEvent { entity }); - velocity_event_queue.push(VelocityUpdateEvent { entity }); - }); - - // Copy move events to `Trigger` instance. - while let Ok(ev) = move_event_queue.pop() { - move_events.trigger(ev); - } - while let Ok(ev) = velocity_event_queue.pop() { - velocity_events.trigger(ev); - } + // Set new position. + *position = pending_position; + }, + ); } diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index df596f143..1957ad711 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -1,16 +1,16 @@ //! A bunch of math-related functions for use with //! the physics system. -use crate::chunk_entities::ChunkEntities; +use crate::game::Game; use crate::physics::block_bboxes::bbox_for_block; use crate::physics::AABBExt; -use crate::state::State; +use bitflags::bitflags; use feather_blocks::Block; use feather_core::world::{BlockPosition, Position}; use feather_core::{BlockExt, ChunkPosition}; +use fecs::{Entity, World}; use glm::{vec3, DVec3, Vec3}; use heapless::consts::*; -use legion::entity::Entity; use nalgebra::{Isometry3, Point3}; use ncollide3d::bounding_volume::AABB; use ncollide3d::query; @@ -19,7 +19,6 @@ use ncollide3d::shape::{Compound, Cuboid, ShapeHandle}; use smallvec::SmallVec; use std::cmp::Ordering; use std::f64::INFINITY; -use tonks::PreparedWorld; // TODO is a bitflag really the most // idiomatic way to do this? @@ -90,7 +89,7 @@ pub struct RayImpact { /// Traces up to `max_distance` before returning `None` /// if no block was found. pub fn block_impacted_by_ray( - state: &State, + game: &Game, origin: DVec3, ray: DVec3, max_distance_squared: f64, @@ -168,7 +167,7 @@ pub fn block_impacted_by_ray( let mut current_pos = Position::from(origin).block(); while dist_traveled.magnitude_squared() < max_distance_squared { - if let Some(block) = state.block_at(current_pos) { + if let Some(block) = game.block_at(current_pos) { if block.is_solid() { // Calculate world-space position of // impact using `ncollide`. @@ -236,8 +235,8 @@ pub fn block_impacted_by_ray( /// # Panics /// Panics if either coordinate of the radius is negative. pub fn nearby_entities( - chunk_entities: &ChunkEntities, - world: &PreparedWorld, + world: &World, + game: &Game, pos: Position, radius: DVec3, ) -> SmallVec<[Entity; 4]> { @@ -248,12 +247,12 @@ pub fn nearby_entities( let mut result = smallvec![]; for chunk in chunks_within_distance(pos, radius) { - let entities = chunk_entities.entities_in_chunk(chunk); + let entities = game.chunk_entities.entities_in_chunk(chunk); entities .iter() .copied() .filter(|e| { - let epos = world.get_component::(*e); + let epos = world.try_get::(*e); if let Some(epos) = epos { (epos.x - pos.x).abs() <= radius.x && (epos.y - pos.y).abs() <= radius.y @@ -315,7 +314,7 @@ impl BlockIntersect { /// than 1 are not supported. If the bounding box's size /// is more than 1, this function will panic. pub fn blocks_intersecting_bbox( - state: &State, + game: &Game, mut from: Position, mut dest: Position, bbox: &AABB, @@ -348,7 +347,7 @@ pub fn blocks_intersecting_bbox( let mut checked = heapless::FnvIndexSet::new(); for (axis, sign) in &axis { - let compound = adjacent_to_bbox(*axis, *sign, bbox, dest, &state, &mut checked); + let compound = adjacent_to_bbox(*axis, *sign, bbox, dest, &game, &mut checked); blocks.push(compound); } @@ -423,7 +422,7 @@ pub fn adjacent_to_bbox( sign: i32, bbox: &AABB, pos: Position, - state: &State, + game: &Game, checked: &mut heapless::FnvIndexSet, ) -> Compound { assert!(axis <= 2); @@ -483,7 +482,7 @@ pub fn adjacent_to_bbox( continue; } - match state.block_at(block_pos) { + match game.block_at(block_pos) { Some(block) => { if block.is_solid() { checked.insert(block_pos).unwrap(); @@ -612,15 +611,12 @@ pub fn bbox_to_cuboid(bbox: &AABB) -> Cuboid { Cuboid::new(half_lengths) } +/* TODO: update #[cfg(test)] mod tests { use super::*; - use crate::entity::test; - use crate::testframework as t; - use feather_core::world::chunk::Chunk; use feather_core::world::ChunkPosition; use feather_core::Block; - use specs::{Builder, WorldExt}; use std::collections::HashSet; #[test] @@ -831,3 +827,4 @@ mod tests { assert!(checked.contains(&BlockPosition::new(0, 64, 0))); } } +*/ diff --git a/server/src/physics/mod.rs b/server/src/physics/mod.rs index aeec599ef..6b2a962b0 100644 --- a/server/src/physics/mod.rs +++ b/server/src/physics/mod.rs @@ -6,5 +6,6 @@ mod entity; mod math; pub use component::{AABBExt, Physics, PhysicsBuilder}; +pub use entity::entity_physics; pub use entity::EntityPhysicsLandEvent; pub use math::*; diff --git a/server/src/systems.rs b/server/src/systems.rs index 1c3be832b..c522fad76 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -7,6 +7,7 @@ pub fn build_executor() -> Executor { Executor::new() .with(network::poll_player_disconnect) .with(network::poll_new_clients) + .with(physics::entity_physics) .with(packet_handlers::handle_movement_packets) .with(packet_handlers::handle_creative_inventory_action) .with(packet_handlers::handle_held_item_change) From 5c78ddd3957657d8d4b10576cdb93935b2d24eda Mon Sep 17 00:00:00 2001 From: caelunshun Date: Wed, 18 Mar 2020 09:55:08 -0600 Subject: [PATCH 111/647] Fix items not being spawned on client by sending metadata before Spawn Object Ref: #175 --- Cargo.lock | 7 ++--- core/src/network/packet/implementation.rs | 2 +- server/Cargo.toml | 4 +-- server/src/broadcasters/entity_creation.rs | 17 ++++++++++-- server/src/broadcasters/movement.rs | 31 +++++++++++++++++----- server/src/chunk_logic.rs | 2 ++ 6 files changed, 47 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 874c3201e..bacc259a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -778,7 +778,6 @@ version = "0.1.0" [[package]] name = "fecs" version = "0.1.0" -source = "git+https://github.com/feather-rs/fecs?rev=8060d8bf79db5b2d345cff14de3e8b5fa53897b0#8060d8bf79db5b2d345cff14de3e8b5fa53897b0" dependencies = [ "fecs-macros", "fxhash", @@ -789,7 +788,6 @@ dependencies = [ [[package]] name = "fecs-macros" version = "0.1.0" -source = "git+https://github.com/feather-rs/fecs?rev=8060d8bf79db5b2d345cff14de3e8b5fa53897b0#8060d8bf79db5b2d345cff14de3e8b5fa53897b0" dependencies = [ "proc-macro2 1.0.7", "quote 1.0.2", @@ -1313,16 +1311,15 @@ dependencies = [ [[package]] name = "legion" version = "0.2.1" -source = "git+https://github.com/TomGillen/legion?branch=fix-zst-component-ub#b5a3dfd88f201af61fe58ad247e83583dfdde5ec" dependencies = [ "legion-core", "legion-systems", + "tracing", ] [[package]] name = "legion-core" version = "0.2.1" -source = "git+https://github.com/TomGillen/legion?branch=fix-zst-component-ub#b5a3dfd88f201af61fe58ad247e83583dfdde5ec" dependencies = [ "crossbeam-channel", "derivative 1.0.3", @@ -1339,10 +1336,10 @@ dependencies = [ [[package]] name = "legion-systems" version = "0.2.1" -source = "git+https://github.com/TomGillen/legion?branch=fix-zst-component-ub#b5a3dfd88f201af61fe58ad247e83583dfdde5ec" dependencies = [ "bit-set", "crossbeam-channel", + "crossbeam-queue", "derivative 1.0.3", "downcast-rs", "fxhash", diff --git a/core/src/network/packet/implementation.rs b/core/src/network/packet/implementation.rs index b8ec91d8c..5f3beb665 100644 --- a/core/src/network/packet/implementation.rs +++ b/core/src/network/packet/implementation.rs @@ -1742,7 +1742,7 @@ pub struct Particle { // TODO data } -#[derive(Default, AsAny, Packet, Clone)] +#[derive(Default, AsAny, Packet, Clone, Debug)] pub struct JoinGame { pub entity_id: i32, pub gamemode: u8, diff --git a/server/Cargo.toml b/server/Cargo.toml index eefd7a2f0..18d30f737 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -20,8 +20,8 @@ feather-item-block = { path = "../item_block" } feather-codegen = { path = "../codegen" } # Core ECS + systems -fecs = { git = "https://github.com/feather-rs/fecs", rev = "8060d8bf79db5b2d345cff14de3e8b5fa53897b0" } -# fecs = { path = "../../../dev/fecs" } +# fecs = { git = "https://github.com/feather-rs/fecs", rev = "8060d8bf79db5b2d345cff14de3e8b5fa53897b0" } +fecs = { path = "../../../dev/fecs" } # Concurrency/threading crossbeam = "0.7" diff --git a/server/src/broadcasters/entity_creation.rs b/server/src/broadcasters/entity_creation.rs index a8ec2481a..eb1813bf0 100644 --- a/server/src/broadcasters/entity_creation.rs +++ b/server/src/broadcasters/entity_creation.rs @@ -1,8 +1,9 @@ -use crate::entity::{CreationPacketCreator, SpawnPacketCreator}; +use crate::entity::{CreationPacketCreator, EntityId, SpawnPacketCreator}; use crate::game::Game; use crate::network::Network; use crate::BumpVec; -use feather_core::Position; +use feather_core::network::packet::implementation::PacketEntityMetadata; +use feather_core::{EntityMetadata, Position}; use fecs::{Entity, IntoQuery, Read, World}; /// When an entity is created and has a `CreationPacketCreator` and/or `SpawnPacketCreator`, @@ -17,6 +18,18 @@ pub fn on_entity_spawn_send_to_clients(game: &mut Game, world: &mut World, entit let mut to_trigger = BumpVec::new_in(game.bump()); if let Some(creator) = world.try_get::(entity) { + // Send metadata before spawn packet. Not sure why this works, + // but if we don't do this, then the client just despawns + // the entity immediately after sending. + if let Some(meta) = world.try_get::(entity) { + let packet = PacketEntityMetadata { + entity_id: world.get::(entity).0, + metadata: (&*meta).clone(), + }; + game.broadcast_entity_update(world, packet, entity, Some(entity)); + } + + // Now send spawn packet: Spawn Object / Spawn Player / Spawn Mob / whatever. let packet = creator.get(&accessor); game.broadcast_entity_update_boxed(world, packet, entity, Some(entity)); diff --git a/server/src/broadcasters/movement.rs b/server/src/broadcasters/movement.rs index 2961da3fd..2f42138e7 100644 --- a/server/src/broadcasters/movement.rs +++ b/server/src/broadcasters/movement.rs @@ -9,7 +9,7 @@ use feather_core::network::packet::implementation::{ EntityHeadLook, EntityLook, EntityLookAndRelativeMove, EntityRelativeMove, }; use feather_core::{Packet, Position}; -use fecs::{changed, Entity, IntoQuery, Read, World}; +use fecs::{Entity, IntoQuery, Read, World}; use smallvec::SmallVec; use std::ops::Deref; @@ -22,9 +22,9 @@ pub struct LastKnownPositions(pub DashMap); /// System to broadcast when an entity moves. #[system] pub fn broadcast_entity_movement(game: &mut Game, world: &mut World) { - <(Read, Read, Read)>::query() - .filter(changed::()) - .par_entities_for_each(world.inner(), |(entity, (pos, prev_pos, id))| { + <(Read, Read, Read)>::query().par_entities_for_each( + world.inner(), + |(entity, (pos, prev_pos, id))| { let pos: Position = *pos; let prev_pos: Position = prev_pos.0; @@ -37,7 +37,7 @@ pub fn broadcast_entity_movement(game: &mut Game, world: &mut World) { let chunk = pos.chunk(); let players = game.chunk_holders.holders_for(chunk); - for player in players { + for player in players.iter().filter(|player| **player != entity) { if let Some(network) = world.try_get::(*player) { let last_known_positions = world.get::(*player); let last_known_positions = last_known_positions.deref(); @@ -49,17 +49,31 @@ pub fn broadcast_entity_movement(game: &mut Game, world: &mut World) { network.send_boxed(packet); } + trace!("Updated position of {:?} on client {:?}", entity, player); + *last_known_pos.value_mut() = pos; + } else { + trace!( + "Missing last position entry for {:?} on client {:?}", + entity, + player + ); }; } } - }); + }, + ); } pub fn on_entity_send_update_last_known_positions(world: &World, entity: Entity, client: Entity) { if let Some(last_known_positions) = world.try_get::(client) { let pos = *world.get::(entity); last_known_positions.0.insert(entity, pos); + trace!( + "Inserted last position entry for {:?} (player: {:?})", + entity, + client + ); } } @@ -69,6 +83,11 @@ pub fn on_entity_client_remove_update_last_known_positions( client: Entity, ) { if let Some(last_known_positions) = world.try_get::(client) { + trace!( + "Removing last position entry for {:?} (player: {:?})", + entity, + client + ); last_known_positions.0.remove(&entity); } } diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index 4aae91c6e..087c3c4f4 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -261,6 +261,7 @@ pub fn chunk_optimize(game: &mut Game) { pub fn hold_chunk(game: &mut Game, holder: &mut ChunkHolder, chunk: ChunkPosition, entity: Entity) { holder.holds.insert(chunk); game.chunk_holders.inner.insert(chunk, entity); + trace!("Obtained chunk hold on {} for player {:?}", chunk, entity); } /// Releases a hold for a chunk for the given entity. @@ -279,6 +280,7 @@ pub fn release_chunk(game: &mut Game, world: &mut World, chunk: ChunkPosition, e vec.swap_remove(index); } } + trace!("Released chunk hold on {} for player {:?}", chunk, entity); game.on_chunk_holder_release(chunk, entity); } From 296fb4afb84db69a82cd008483243b03469375a8 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Wed, 18 Mar 2020 10:55:49 -0600 Subject: [PATCH 112/647] Don't spam the client with velocity update packets --- server/src/broadcasters/mod.rs | 2 +- server/src/broadcasters/movement.rs | 56 +++++++++++++++-------------- server/src/entity/mod.rs | 33 ++++++++++++++++- server/src/systems.rs | 5 +-- 4 files changed, 66 insertions(+), 30 deletions(-) diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index 1ef69a85a..fe98a0426 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -30,6 +30,6 @@ pub use entity_deletion::on_entity_despawn_broadcast_despawn; pub use keepalive::broadcast_keepalive; pub use metadata::on_entity_send_send_metadata; pub use movement::{ - broadcast_entity_movement, on_entity_client_remove_update_last_known_positions, + broadcast_movement, broadcast_velocity, on_entity_client_remove_update_last_known_positions, on_entity_send_update_last_known_positions, LastKnownPositions, }; diff --git a/server/src/broadcasters/movement.rs b/server/src/broadcasters/movement.rs index 2f42138e7..a011ecfe4 100644 --- a/server/src/broadcasters/movement.rs +++ b/server/src/broadcasters/movement.rs @@ -1,15 +1,15 @@ //! Broadcasting of movement updates. -use crate::entity::{EntityId, PreviousPosition}; +use crate::entity::{EntityId, PreviousPosition, PreviousVelocity, Velocity}; use crate::game::Game; use crate::network::Network; -use crate::util::{calculate_relative_move, degrees_to_stops}; +use crate::util::{calculate_relative_move, degrees_to_stops, protocol_velocity}; use dashmap::DashMap; use feather_core::network::packet::implementation::{ - EntityHeadLook, EntityLook, EntityLookAndRelativeMove, EntityRelativeMove, + EntityHeadLook, EntityLook, EntityLookAndRelativeMove, EntityRelativeMove, EntityVelocity, }; use feather_core::{Packet, Position}; -use fecs::{Entity, IntoQuery, Read, World}; +use fecs::{changed, Entity, IntoQuery, Read, World}; use smallvec::SmallVec; use std::ops::Deref; @@ -21,7 +21,7 @@ pub struct LastKnownPositions(pub DashMap); /// System to broadcast when an entity moves. #[system] -pub fn broadcast_entity_movement(game: &mut Game, world: &mut World) { +pub fn broadcast_movement(game: &mut Game, world: &mut World) { <(Read, Read, Read)>::query().par_entities_for_each( world.inner(), |(entity, (pos, prev_pos, id))| { @@ -92,29 +92,33 @@ pub fn on_entity_client_remove_update_last_known_positions( } } -/* /// Broadcasts an entity's velocity. -#[event_handler] -pub fn broadcast_velocity( - event: &VelocityUpdateEvent, - _query: &mut Query<(Read, Read)>, - world: &mut PreparedWorld, - state: &State, -) { - let entity_id = world.get_component::(event.entity).unwrap().0; - let vel = *world.get_component::(event.entity).unwrap(); - - let (velocity_x, velocity_y, velocity_z) = protocol_velocity(vel.0); - - let packet = EntityVelocity { - entity_id, - velocity_x, - velocity_y, - velocity_z, - }; - state.broadcast_entity_update(event.entity, packet, None); +#[system] +pub fn broadcast_velocity(world: &mut World, game: &mut Game) { + <(Read, Read, Read)>::query() + .filter(changed::()) + .par_entities_for_each(world.inner(), |(entity, (vel, prev_vel, entity_id))| { + let entity_id = entity_id.0; + + if vel.0 == prev_vel.0 { + return; + } + + let (velocity_x, velocity_y, velocity_z) = protocol_velocity(vel.0); + + if velocity_x == 0 && velocity_y == 0 && velocity_z == 0 { + return; + } + + let packet = EntityVelocity { + entity_id, + velocity_x, + velocity_y, + velocity_z, + }; + game.broadcast_entity_update(world, packet, entity, None); + }); } -*/ /// Returns the packet needed to notify a client /// of a position update, from the old position to the new one. diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 2aac08287..526a9d5e7 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -21,6 +21,10 @@ pub static ENTITY_ID_COUNTER: AtomicI32 = AtomicI32::new(0); #[derive(Debug, PartialEq, Clone, Copy)] pub struct Velocity(pub glm::DVec3); +/// The velocity of an entity on the previous tick. +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct PreviousVelocity(pub glm::DVec3); + impl Default for Velocity { fn default() -> Self { Self(glm::vec3(0.0, 0.0, 0.0)) @@ -41,6 +45,26 @@ impl DerefMut for Velocity { } } +impl Default for PreviousVelocity { + fn default() -> Self { + Self(glm::vec3(0.0, 0.0, 0.0)) + } +} + +impl Deref for PreviousVelocity { + type Target = glm::DVec3; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for PreviousVelocity { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + /// The display name of an entity. /// /// Note that unnamed entities do not have this component. @@ -95,13 +119,19 @@ impl CreationPacketCreator { } #[system] -pub fn position_reset(world: &mut World) { +pub fn previous_position_velocity_reset(world: &mut World) { <(Read, Write)>::query().par_for_each_mut( world.inner_mut(), |(pos, mut previous_pos)| { previous_pos.0 = *pos; }, ); + <(Read, Write)>::query().par_for_each_mut( + world.inner_mut(), + |(vel, mut previous_vel)| { + previous_vel.0 = vel.0; + }, + ); } /// Inserts the base components for an entity into an `EntityBuilder`. @@ -118,6 +148,7 @@ pub fn base(position: Position) -> EntityBuilder { .with(position) .with(PreviousPosition(position)) .with(Velocity::default()) + .with(PreviousVelocity::default()) } /// Returns a new entity ID. diff --git a/server/src/systems.rs b/server/src/systems.rs index c522fad76..034d754bd 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -19,8 +19,9 @@ pub fn build_executor() -> Executor { .with(chunk_logic::chunk_optimize) .with(view::check_crossed_chunks) .with(broadcasters::broadcast_keepalive) - .with(broadcasters::broadcast_entity_movement) + .with(broadcasters::broadcast_movement) + .with(broadcasters::broadcast_velocity) .with(game::reset_bump_allocators) .with(game::increment_tick_count) - .with(entity::position_reset) // should be at end + .with(entity::previous_position_velocity_reset) // should be at end } From f1b39966aeeb50fb321b114ba17b0b3d6a28f132 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Wed, 18 Mar 2020 11:54:45 -0600 Subject: [PATCH 113/647] Update fecs to fix item physics not working --- Cargo.lock | 6 +++++- server/Cargo.toml | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bacc259a5..36dc5d29c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -778,6 +778,7 @@ version = "0.1.0" [[package]] name = "fecs" version = "0.1.0" +source = "git+https://github.com/feather-rs/fecs?rev=20d54b0ff8b11fbb76f55fec012e4feb66e20c42#20d54b0ff8b11fbb76f55fec012e4feb66e20c42" dependencies = [ "fecs-macros", "fxhash", @@ -788,6 +789,7 @@ dependencies = [ [[package]] name = "fecs-macros" version = "0.1.0" +source = "git+https://github.com/feather-rs/fecs?rev=20d54b0ff8b11fbb76f55fec012e4feb66e20c42#20d54b0ff8b11fbb76f55fec012e4feb66e20c42" dependencies = [ "proc-macro2 1.0.7", "quote 1.0.2", @@ -1311,15 +1313,16 @@ dependencies = [ [[package]] name = "legion" version = "0.2.1" +source = "git+https://github.com/TomGillen/legion?branch=fix-zst-component-ub#b5a3dfd88f201af61fe58ad247e83583dfdde5ec" dependencies = [ "legion-core", "legion-systems", - "tracing", ] [[package]] name = "legion-core" version = "0.2.1" +source = "git+https://github.com/TomGillen/legion?branch=fix-zst-component-ub#b5a3dfd88f201af61fe58ad247e83583dfdde5ec" dependencies = [ "crossbeam-channel", "derivative 1.0.3", @@ -1336,6 +1339,7 @@ dependencies = [ [[package]] name = "legion-systems" version = "0.2.1" +source = "git+https://github.com/TomGillen/legion?branch=fix-zst-component-ub#b5a3dfd88f201af61fe58ad247e83583dfdde5ec" dependencies = [ "bit-set", "crossbeam-channel", diff --git a/server/Cargo.toml b/server/Cargo.toml index 18d30f737..37ef5ed82 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -20,8 +20,8 @@ feather-item-block = { path = "../item_block" } feather-codegen = { path = "../codegen" } # Core ECS + systems -# fecs = { git = "https://github.com/feather-rs/fecs", rev = "8060d8bf79db5b2d345cff14de3e8b5fa53897b0" } -fecs = { path = "../../../dev/fecs" } +fecs = { git = "https://github.com/feather-rs/fecs", rev = "20d54b0ff8b11fbb76f55fec012e4feb66e20c42" } +# fecs = { path = "../../../dev/fecs" } # Concurrency/threading crossbeam = "0.7" From c02b2f7d1e634fa305119a08b94baa08550fe656 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Thu, 19 Mar 2020 11:26:59 -0600 Subject: [PATCH 114/647] Reimplement item collection --- server/src/broadcasters/item_collect.rs | 20 +--- server/src/broadcasters/mod.rs | 3 +- server/src/entity/item.rs | 143 ++++++++++++++++++++---- server/src/game.rs | 16 ++- server/src/lib.rs | 10 +- server/src/systems.rs | 2 + server/src/time.rs | 25 ++--- 7 files changed, 161 insertions(+), 58 deletions(-) diff --git a/server/src/broadcasters/item_collect.rs b/server/src/broadcasters/item_collect.rs index 77cfbf1cd..e8546c4eb 100644 --- a/server/src/broadcasters/item_collect.rs +++ b/server/src/broadcasters/item_collect.rs @@ -1,24 +1,16 @@ use crate::entity::item::ItemCollectEvent; use crate::entity::EntityId; -use crate::state::State; +use crate::game::Game; use feather_core::network::packet::implementation::CollectItem; -use legion::query::Read; -use tonks::{PreparedWorld, Query}; +use fecs::World; /// Sends `CollectItem` packet when an item is collected. -#[event_handler] -pub fn broadcast_item_collect( - event: &ItemCollectEvent, - state: &State, - _query: &mut Query>, - world: &mut PreparedWorld, -) { +pub fn on_item_collect_broadcast(game: &Game, world: &World, event: &ItemCollectEvent) { let packet = CollectItem { - collected: world.get_component::(event.item).unwrap().0, - collector: world.get_component::(event.item).unwrap().0, + collected: world.get::(event.item).0, + collector: world.get::(event.collector).0, count: event.amount as i32, }; - // TODO: broadcast for item instead - state.broadcast_entity_update(event.collector, packet, None); + game.broadcast_entity_update(world, packet, event.item, None); } diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index fe98a0426..fde2bf0d0 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -13,7 +13,7 @@ mod block; mod entity_creation; mod entity_deletion; mod inventory; -// mod item_collect; +mod item_collect; mod keepalive; mod metadata; mod movement; @@ -27,6 +27,7 @@ pub use block::on_block_update_broadcast; pub use entity_creation::on_entity_spawn_send_to_clients; pub use entity_creation::on_player_join_send_existing_entities; pub use entity_deletion::on_entity_despawn_broadcast_despawn; +pub use item_collect::on_item_collect_broadcast; pub use keepalive::broadcast_keepalive; pub use metadata::on_entity_send_send_metadata; pub use movement::{ diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index db59dc727..8494fce92 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -1,16 +1,19 @@ //! Handling of item entities. -use crate::entity; use crate::entity::{EntityId, SpawnPacketCreator, Velocity}; use crate::game::Game; -use crate::physics::PhysicsBuilder; -use crate::player::PLAYER_EYE_HEIGHT; +use crate::p_inventory::{EntityInventory, InventoryUpdateEvent}; +use crate::physics::{nearby_entities, PhysicsBuilder}; +use crate::player::{Player, PLAYER_EYE_HEIGHT}; use crate::util::{degrees_to_stops, protocol_velocity}; +use crate::{entity, TPS}; use feather_core::inventory::SlotIndex; use feather_core::network::packet::implementation::SpawnObject; use feather_core::{EntityMetadata, ItemStack, Packet, Position, META_INDEX_ITEM_SLOT}; -use fecs::{Entity, EntityBuilder, EntityRef, World}; +use fecs::{changed, component, Entity, EntityBuilder, EntityRef, IntoQuery, Read, World, Write}; +use parking_lot::Mutex; use rand::Rng; +use std::sync::atomic::{AtomicBool, Ordering}; use uuid::Uuid; /// Event triggered when an item is dropped. @@ -28,27 +31,30 @@ pub struct ItemDropEvent { pub player: Entity, } -/* -/// Event triggered when an item is collected. +/// Event triggered when an item is collected into an entity's +/// inventory. +/// +/// Triggered before the item is deleted from the world. #[derive(Debug, Clone)] pub struct ItemCollectEvent { - /// Item entity which was collected. + /// The item which was collected. pub item: Entity, - /// Entity which collected the item. + /// The entity which collected the item. pub collector: Entity, - /// Number of the item which was picked up. + /// Number of items which was collected. pub amount: u8, } -/// Component storing the tick at which an item becomes collectable. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct CollectableAt(pub u64); - -/// Component storing if an item stack has been collected and queued for removal. -pub struct IsRemoved(AtomicBool); -*/ +/// Component which stores the world time at which an item +/// will be collectable. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CollectableAt(u64); -// Item stack of an item entity is stored in `ItemStack` component +/// Component used to store whether an item has been collected/ +/// removed on a given tick. Used by `item_collect` and `item_merge` +/// systems. +#[derive(Debug)] +struct IsRemoved(AtomicBool); /// System for spawning an item entity when /// an item is dropped. @@ -91,17 +97,114 @@ pub fn on_item_drop_spawn_item_entity(game: &mut Game, world: &mut World, event: game.on_entity_spawn(world, entity); } +/// System to add items to player inventories when the player comes near. +#[system] +pub fn item_collect(game: &mut Game, world: &mut World) { + // run every 1/2 second + if game.tick_count % TPS / 2 != 0 { + return; + } + + let items_to_remove = Mutex::new(vec![]); + let inventory_update_events = Mutex::new(vec![]); + let item_collect_events = Mutex::new(vec![]); + + // For each player, check for nearby items and try collecting them + // Safety: we only iterate over entities which are players, + // and we only access item entities inside the loop. As such, + // we will not have multiple mutable references to the same component. + unsafe { + <(Read, Write)>::query() + .filter(component::()) + .filter(changed::()) + .par_entities_for_each_unchecked(world.inner(), |(player, (pos, mut inventory))| { + let inventory: &mut EntityInventory = &mut *inventory; + + let nearby_entities = nearby_entities(world, game, *pos, glm::vec3(1.0, 1.0, 1.0)); + let nearby_items = nearby_entities.iter().filter_map(|entity| { + world + .try_get::(*entity) + .map(|collectable_at| { + if collectable_at.0 <= game.time.world_age() { + Some(*entity) + } else { + None + } + }) + .flatten() + }); + + for item in nearby_items { + debug_assert!(!world.has::(item)); + // try to mark this item is collected + // (this ensures another thread has not collected it + // as well, which makes the mutable access below + // safe) + let is_removed = world.get::(item); + + if is_removed + .0 + .compare_and_swap(false, true, Ordering::Relaxed) + { + // we now have unique access to this item and its components. + let mut stack = world.get_mut_unchecked::(item); + + let (slots, stack_remaining) = inventory.collect_item(*stack); + + let initial_remaining = stack.amount; + + let event = InventoryUpdateEvent { slots, player }; + inventory_update_events.lock().push(event); + + // update stack + if stack_remaining == 0 { + items_to_remove.lock().push(item); + } else { + stack.amount = stack_remaining; + world + .get_mut_unchecked::(item) + .set(META_INDEX_ITEM_SLOT, Some(*stack)); + } + + item_collect_events.lock().push(ItemCollectEvent { + item, + collector: player, + amount: initial_remaining - stack_remaining, + }); + } + } + }); + } + + // Trigger events + deferred entity deletes. + for event in item_collect_events.into_inner() { + game.on_item_collect(world, event); + } + + for item in items_to_remove.into_inner() { + game.despawn(item, world); + } + + for event in inventory_update_events.into_inner() { + game.on_inventory_update(world, event); + } + + // Reset `IsRemoved`. + >::query().for_each(world.inner(), |rem| rem.0.store(true, Ordering::Relaxed)); +} + /// Returns an entity builder to create an item entity /// with the given stack and collectable tick. -pub fn create(_game: &mut Game, pos: Position, stack: ItemStack) -> EntityBuilder { +pub fn create(game: &mut Game, pos: Position, stack: ItemStack) -> EntityBuilder { let meta = EntityMetadata::entity_base().with(META_INDEX_ITEM_SLOT, Some(stack)); + let collectable_at = CollectableAt(game.time.world_age() + TPS); entity::base(pos) .with(stack) - //.with(CollectableAt(collectable_at)) + .with(IsRemoved(AtomicBool::new(false))) + .with(collectable_at) .with(SpawnPacketCreator(&create_spawn_packet)) .with(meta) - //.with(IsRemoved(AtomicBool::new(false))) .with( PhysicsBuilder::new() .bbox(0.25, 0.25, 0.25) diff --git a/server/src/game.rs b/server/src/game.rs index c3bb4bc4c..2e6b574c9 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -3,8 +3,8 @@ use crate::broadcasters::{ on_entity_despawn_broadcast_despawn, on_entity_send_send_equipment, on_entity_send_send_metadata, on_entity_send_update_last_known_positions, on_entity_spawn_send_to_clients, on_inventory_update_broadcast_equipment_update, - on_inventory_update_send_set_slot, on_player_animation_broadcast_animation, - on_player_join_send_existing_entities, + on_inventory_update_send_set_slot, on_item_collect_broadcast, + on_player_animation_broadcast_animation, on_player_join_send_existing_entities, }; use crate::chunk_entities::{ on_chunk_cross_update_chunk_entities, on_entity_despawn_update_chunk_entities, @@ -15,7 +15,7 @@ use crate::chunk_logic::{ ChunkUnloadQueue, ChunkWorkerHandle, }; use crate::config::Config; -use crate::entity::item::{on_item_drop_spawn_item_entity, ItemDropEvent}; +use crate::entity::item::{on_item_drop_spawn_item_entity, ItemCollectEvent, ItemDropEvent}; use crate::entity::Name; use crate::io::{NetworkIoManager, NewClientInfo, ServerToWorkerMessage}; use crate::join::{on_chunk_send_join_player, on_player_join_send_join_game}; @@ -23,6 +23,7 @@ use crate::network::Network; use crate::p_inventory::InventoryUpdateEvent; use crate::player; use crate::player::Player; +use crate::time::{on_player_join_send_time, Time}; use crate::view::{ on_chunk_cross_update_chunks, on_chunk_cross_update_entities, on_chunk_load_send_to_clients, on_player_join_trigger_chunk_cross, ChunksToSend, @@ -75,6 +76,7 @@ pub struct Game { pub chunks_to_send: ChunksToSend, pub chunk_entities: ChunkEntities, pub(super) rng: CachedThreadLocal>, + pub time: Time, } impl Game { @@ -265,7 +267,8 @@ impl Game { self.player_count.fetch_add(1, Ordering::Relaxed); on_player_join_send_join_game(self, world, player); on_player_join_send_existing_entities(world, player); - on_player_join_trigger_chunk_cross(self, world, player) + on_player_join_send_time(self, world, player); + on_player_join_trigger_chunk_cross(self, world, player); } /// Called when a player leaves. @@ -327,6 +330,11 @@ impl Game { pub fn on_item_drop(&mut self, world: &mut World, event: ItemDropEvent) { on_item_drop_spawn_item_entity(self, world, &event); } + + /// Called when an entity collects an item entity. + pub fn on_item_collect(&mut self, world: &mut World, event: ItemCollectEvent) { + on_item_collect_broadcast(self, world, &event); + } } #[system] diff --git a/server/src/lib.rs b/server/src/lib.rs index bddad3f58..57d51300e 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -117,6 +117,7 @@ use crate::chunk_logic::ChunkWorkerHandle; use crate::config::Config; use crate::game::Game; use crate::packet_buffer::PacketBuffers; +use crate::time::Time; use crate::worldgen::{ ComposableGenerator, EmptyWorldGenerator, SuperflatWorldGenerator, WorldGenerator, }; @@ -144,18 +145,18 @@ pub mod chunk_logic; pub mod chunk_worker; pub mod config; pub mod entity; +pub mod game; pub mod io; mod join; pub mod network; pub mod p_inventory; // Prefixed to avoid conflict with inventory crate +pub mod packet_buffer; mod packet_handlers; pub mod physics; pub mod player; pub mod shutdown; -// pub mod time; -pub mod game; -pub mod packet_buffer; mod systems; +mod time; pub mod util; mod view; pub mod worldgen; @@ -212,6 +213,8 @@ pub fn main() { let chunk_worker_handle = init_chunk_worker(world_dir, &level); + let time = Time(level.time as u64); + let game = Game { io_handle, config, @@ -226,6 +229,7 @@ pub fn main() { chunks_to_send: Default::default(), chunk_entities: Default::default(), rng: CachedThreadLocal::new(), + time, }; let (executor, resources) = init_executor(game, packet_buffers); diff --git a/server/src/systems.rs b/server/src/systems.rs index 034d754bd..86f6c3f23 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -14,6 +14,7 @@ pub fn build_executor() -> Executor { .with(packet_handlers::handle_animation) .with(packet_handlers::handle_player_block_placement) .with(packet_handlers::handle_player_digging) + .with(entity::item::item_collect) .with(chunk_logic::chunk_load) .with(chunk_logic::chunk_unload) .with(chunk_logic::chunk_optimize) @@ -23,5 +24,6 @@ pub fn build_executor() -> Executor { .with(broadcasters::broadcast_velocity) .with(game::reset_bump_allocators) .with(game::increment_tick_count) + .with(time::increment_time) .with(entity::previous_position_velocity_reset) // should be at end } diff --git a/server/src/time.rs b/server/src/time.rs index c6111f9de..b0e3e833c 100644 --- a/server/src/time.rs +++ b/server/src/time.rs @@ -1,14 +1,13 @@ //! Handles world time. +use crate::game::Game; use crate::network::Network; -use crate::player::PlayerJoinEvent; use feather_core::packet::TimeUpdate; -use legion::query::Read; +use fecs::{Entity, World}; use std::ops::{Deref, DerefMut}; -use tonks::{PreparedWorld, Query}; /// The current time of the world. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Resource)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct Time(pub u64); impl Deref for Time { @@ -40,24 +39,18 @@ impl Time { /// System for incrementing time each tick. #[system] -pub fn time_increment(time: &mut Time) { - time.0 += 1; +pub fn increment_time(game: &mut Game) { + game.time.0 += 1; } /// Event handler for sending world time to players. -#[event_handler] -pub fn time_send( - event: &PlayerJoinEvent, - time: &Time, - _query: &mut Query>, - world: &mut PreparedWorld, -) { - let network = world.get_component::(event.player).unwrap(); +pub fn on_player_join_send_time(game: &Game, world: &World, player: Entity) { + let network = world.get::(player); // Send time to player. let packet = TimeUpdate { - world_age: time.world_age() as i64, - time_of_day: time.time_of_day() as i64, + world_age: game.time.world_age() as i64, + time_of_day: game.time.time_of_day() as i64, }; network.send(packet); From ba1778f2a106ba675adb90831516e63420f28251 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Thu, 19 Mar 2020 11:45:19 -0600 Subject: [PATCH 115/647] Fix soundness issue in item_collect system --- server/src/entity/item.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index 8494fce92..5cdf7fb2f 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -142,7 +142,7 @@ pub fn item_collect(game: &mut Game, world: &mut World) { // safe) let is_removed = world.get::(item); - if is_removed + if !is_removed .0 .compare_and_swap(false, true, Ordering::Relaxed) { @@ -190,7 +190,7 @@ pub fn item_collect(game: &mut Game, world: &mut World) { } // Reset `IsRemoved`. - >::query().for_each(world.inner(), |rem| rem.0.store(true, Ordering::Relaxed)); + >::query().for_each(world.inner(), |rem| rem.0.store(false, Ordering::Relaxed)); } /// Returns an entity builder to create an item entity From b3d0191d4bac56b5d8c52879641287d425fb1eff Mon Sep 17 00:00:00 2001 From: caelunshun Date: Thu, 19 Mar 2020 18:07:23 -0600 Subject: [PATCH 116/647] Reimplement world persistence --- server/src/chunk_logic.rs | 20 +++++-- server/src/chunk_worker.rs | 5 +- server/src/entity/item.rs | 21 ++++++- server/src/entity/mod.rs | 24 ++++++++ server/src/game.rs | 10 ++++ server/src/lib.rs | 4 +- server/src/save.rs | 117 +++++++++++++++++++++++++++++++++++++ server/src/shutdown.rs | 15 ++++- server/src/systems.rs | 1 + 9 files changed, 205 insertions(+), 12 deletions(-) create mode 100644 server/src/save.rs diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index 087c3c4f4..a065e02ac 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -16,6 +16,7 @@ use feather_core::Chunk; use fecs::{Entity, World}; use hashbrown::HashSet; use multimap::MultiMap; +use parking_lot::RwLock; use std::collections::VecDeque; use std::sync::Arc; @@ -118,7 +119,7 @@ pub struct ChunkUnloadQueue { } /// A chunk to be unloaded. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Copy, Debug, Default)] struct ChunkUnload { /// The position of this chunk. chunk: ChunkPosition, @@ -142,7 +143,7 @@ const CHUNK_UNLOAD_TIME: u64 = TPS * 5; // 5 seconds - TODO make this configurab /// chunks at the edge of their view distance /// to be loaded and unloaded at an alarming rate. #[system] -pub fn chunk_unload(game: &mut Game) { +pub fn chunk_unload(game: &mut Game, world: &mut World) { // Unload chunks which are finished in the queue. // Since chunks are queued in the back and taken out @@ -150,7 +151,7 @@ pub fn chunk_unload(game: &mut Game) { // were queued the longest time ago. Because of this, // we go through the unloads in the front of the queue // to find which chunks to unload. - while let Some(unload) = game.chunk_unload_queue.queue.front() { + while let Some(unload) = game.chunk_unload_queue.queue.front().copied() { if game.tick_count >= unload.time { // Don't unload if new chunk holders have appeared. if game.chunk_holders.chunk_has_holders(unload.chunk) { @@ -159,8 +160,11 @@ pub fn chunk_unload(game: &mut Game) { } // Unload chunk and pop from queue. - game.chunk_map.remove(unload.chunk); - trace!("Unloaded chunk at {}", unload.chunk); + if game.chunk_map.chunk_at(unload.chunk).is_some() { + game.on_chunk_unload(world, unload.chunk); + game.chunk_map.remove(unload.chunk); + trace!("Unloaded chunk at {}", unload.chunk); + } game.chunk_unload_queue.queue.pop_front(); } else { // We're done - all chunks farther up in @@ -299,7 +303,11 @@ pub fn load_chunk(handle: &ChunkWorkerHandle, pos: ChunkPosition) { } /// Asynchronously saves the chunk at the given position. -pub fn save_chunk(handle: &ChunkWorkerHandle, chunk: Arc, entities: Vec) { +pub fn save_chunk( + handle: &ChunkWorkerHandle, + chunk: Arc>, + entities: Vec, +) { handle .sender .send(chunk_worker::Request::SaveChunk(chunk, entities)) diff --git a/server/src/chunk_worker.rs b/server/src/chunk_worker.rs index 73aac51ef..c420eac98 100644 --- a/server/src/chunk_worker.rs +++ b/server/src/chunk_worker.rs @@ -11,6 +11,7 @@ use feather_core::region; use feather_core::region::{RegionHandle, RegionPosition}; use feather_core::{Chunk, ChunkPosition}; use hashbrown::HashMap; +use parking_lot::RwLock; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; @@ -24,7 +25,7 @@ pub enum Reply { #[derive(Clone)] pub enum Request { LoadChunk(ChunkPosition), - SaveChunk(Arc, Vec), + SaveChunk(Arc>, Vec), ShutDown, } @@ -120,7 +121,7 @@ fn run(mut worker: ChunkWorker) { match request { Request::ShutDown => break, Request::SaveChunk(chunk, entities) => { - save_chunk(&mut worker, &chunk, entities); + save_chunk(&mut worker, &*chunk.read(), entities); } Request::LoadChunk(pos) => { if let Some(reply) = load_chunk(&mut worker, pos) { diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index 5cdf7fb2f..075bb4e2d 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -1,15 +1,16 @@ //! Handling of item entities. -use crate::entity::{EntityId, SpawnPacketCreator, Velocity}; +use crate::entity::{ComponentSerializer, EntityId, SpawnPacketCreator, Velocity}; use crate::game::Game; use crate::p_inventory::{EntityInventory, InventoryUpdateEvent}; use crate::physics::{nearby_entities, PhysicsBuilder}; use crate::player::{Player, PLAYER_EYE_HEIGHT}; use crate::util::{degrees_to_stops, protocol_velocity}; use crate::{entity, TPS}; +use feather_core::entity::{BaseEntityData, EntityData, ItemData, ItemEntityData}; use feather_core::inventory::SlotIndex; use feather_core::network::packet::implementation::SpawnObject; -use feather_core::{EntityMetadata, ItemStack, Packet, Position, META_INDEX_ITEM_SLOT}; +use feather_core::{EntityMetadata, ItemStack, Packet, Position, Vec3d, META_INDEX_ITEM_SLOT}; use fecs::{changed, component, Entity, EntityBuilder, EntityRef, IntoQuery, Read, World, Write}; use parking_lot::Mutex; use rand::Rng; @@ -204,6 +205,7 @@ pub fn create(game: &mut Game, pos: Position, stack: ItemStack) -> EntityBuilder .with(IsRemoved(AtomicBool::new(false))) .with(collectable_at) .with(SpawnPacketCreator(&create_spawn_packet)) + .with(ComponentSerializer(&serialize)) .with(meta) .with( PhysicsBuilder::new() @@ -238,3 +240,18 @@ fn create_spawn_packet(accessor: &EntityRef) -> Box { Box::new(packet) } + +fn serialize(game: &Game, accessor: &EntityRef) -> EntityData { + let vel = accessor.get::(); + let item = accessor.get::(); + EntityData::Item(ItemEntityData { + entity: BaseEntityData::new(*accessor.get::(), Vec3d::new(vel.x, vel.y, vel.z)), + age: 0, // todo + pickup_delay: (accessor.get::().0 as i64 - game.tick_count as i64).max(0) + as u8, + item: ItemData { + count: item.amount, + item: item.ty.identifier().to_owned(), + }, + }) +} diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 526a9d5e7..69249f1e8 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -5,6 +5,8 @@ pub mod item; +use crate::game::Game; +use feather_core::entity::EntityData; use feather_core::{Packet, Position}; use fecs::{EntityBuilder, EntityRef, IntoQuery, Read, World, Write}; use std::ops::{Deref, DerefMut}; @@ -118,6 +120,28 @@ impl CreationPacketCreator { } } +pub trait ComponentSerializerFn: + Fn(&Game, &EntityRef) -> EntityData + Send + Sync + 'static +{ +} + +impl ComponentSerializerFn for F where + F: Fn(&Game, &EntityRef) -> EntityData + Send + Sync + 'static +{ +} + +/// Component which stores a function needed to convert an entity's +/// components to the serializable `EntityData`. +pub struct ComponentSerializer(pub &'static dyn ComponentSerializerFn); + +impl ComponentSerializer { + pub fn serialize(&self, game: &Game, accessor: &EntityRef) -> EntityData { + let f = self.0; + + f(game, accessor) + } +} + #[system] pub fn previous_position_velocity_reset(world: &mut World) { <(Read, Write)>::query().par_for_each_mut( diff --git a/server/src/game.rs b/server/src/game.rs index 2e6b574c9..eb70ce6a6 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -23,6 +23,7 @@ use crate::network::Network; use crate::p_inventory::InventoryUpdateEvent; use crate::player; use crate::player::Player; +use crate::save::{on_chunk_load_queue_for_saving, on_chunk_unload_save_chunk, SaveQueue}; use crate::time::{on_player_join_send_time, Time}; use crate::view::{ on_chunk_cross_update_chunks, on_chunk_cross_update_entities, on_chunk_load_send_to_clients, @@ -77,6 +78,7 @@ pub struct Game { pub chunk_entities: ChunkEntities, pub(super) rng: CachedThreadLocal>, pub time: Time, + pub save_queue: SaveQueue, } impl Game { @@ -282,6 +284,14 @@ impl Game { /// Called when a chunk loads successfully. pub fn on_chunk_load(&mut self, world: &mut World, chunk: ChunkPosition) { on_chunk_load_send_to_clients(self, world, chunk); + on_chunk_load_queue_for_saving(self, chunk); + } + + /// Called when a chunk unloads. + /// + /// This is called _before_ the chunk is removed from the chunk map. + pub fn on_chunk_unload(&mut self, world: &mut World, chunk: ChunkPosition) { + on_chunk_unload_save_chunk(self, world, chunk); } /// Called when a chunk fails to load. diff --git a/server/src/lib.rs b/server/src/lib.rs index 57d51300e..d8faaaad8 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -154,6 +154,7 @@ pub mod packet_buffer; mod packet_handlers; pub mod physics; pub mod player; +mod save; pub mod shutdown; mod systems; mod time; @@ -230,6 +231,7 @@ pub fn main() { chunk_entities: Default::default(), rng: CachedThreadLocal::new(), time, + save_queue: Default::default(), }; let (executor, resources) = init_executor(game, packet_buffers); @@ -254,7 +256,7 @@ pub fn main() { info!("Shutting down"); info!("Saving chunks"); - shutdown::save_chunks(&mut world); + shutdown::save_chunks(&*resources.get::(), &mut world); info!("Saving level.dat"); shutdown::save_level(&world); info!("Saving player data"); diff --git a/server/src/save.rs b/server/src/save.rs new file mode 100644 index 000000000..29b5f5c08 --- /dev/null +++ b/server/src/save.rs @@ -0,0 +1,117 @@ +//! Handles saving of chunks and entities + +use crate::entity::ComponentSerializer; +use crate::game::Game; +use crate::{chunk_logic, TICK_TIME, TPS}; +use feather_core::ChunkPosition; +use fecs::World; +use std::collections::VecDeque; + +/// A chunk to save + the tick count at which to do so. +#[derive(Clone, Copy, Debug)] +struct SaveTask { + /// Chunk position to save. + chunk: ChunkPosition, + /// Tick count at which to save this chunk. + at: u64, +} + +/// Queue of chunks to save. +#[derive(Debug, Default)] +pub struct SaveQueue(VecDeque); + +/// On a chunk load, adds the chunk to the save queue. +pub fn on_chunk_load_queue_for_saving(game: &mut Game, chunk: ChunkPosition) { + queue_for_saving(game, chunk); +} + +/// On a chunk unload, saves the chunk first. +pub fn on_chunk_unload_save_chunk(game: &mut Game, world: &World, chunk: ChunkPosition) { + save_chunk_at(game, world, chunk); +} + +fn queue_for_saving(game: &mut Game, chunk: ChunkPosition) { + let tick_to_save_at = + game.tick_count + (game.config.world.save_interval.as_millis() as u64) / TICK_TIME; + + let task = SaveTask { + chunk, + at: tick_to_save_at, + }; + + game.save_queue.0.push_back(task); +} + +/// System which checks for chunks which have been queued for saving +/// and, if it is time, saves them. +#[system] +pub fn chunk_save(game: &mut Game, world: &mut World) { + // no need to run this system every tick + if game.tick_count % TPS != 0 { + return; + } + + loop { + let task = match game.save_queue.0.front().copied() { + Some(task) => task, + None => return, // no save tasks to run + }; + + if game.chunk_map.chunk_at(task.chunk).is_none() { + game.save_queue + .0 + .pop_front() + .expect("we just verified the front task exists"); + continue; + } + + if task.at <= game.tick_count { + // Save the chunk, then pop the task from the queue. + save_chunk_at(game, world, task.chunk); + + game.save_queue + .0 + .pop_front() + .expect("we just verified the front task exists"); + + // Requeue the chunk for saving again. + queue_for_saving(game, task.chunk); + } else { + return; + } + } +} + +pub fn save_chunk_at(game: &Game, world: &World, pos: ChunkPosition) { + let chunk = game + .chunk_map + .chunk_handle_at(pos) + .expect("chunk does not exist"); + + if !chunk.write().check_modified() { + return; + } + + // Serialize the entities in the chunk. + let entities = game + .chunk_entities + .entities_in_chunk(pos) + .into_iter() + .filter_map(|entity| { + if let Some(serializer) = world.try_get::(*entity) { + let accessor = world.entity(*entity).expect("entity does not exist"); + + Some(serializer.serialize(game, &accessor)) + } else { + None + } + }) + .collect(); + + trace!("Queuing chunk at {} for saving", pos); + chunk_logic::save_chunk( + &game.chunk_worker_handle, + game.chunk_map.chunk_handle_at(pos).unwrap(), + entities, + ); +} diff --git a/server/src/shutdown.rs b/server/src/shutdown.rs index 19cec101e..53aa9169d 100644 --- a/server/src/shutdown.rs +++ b/server/src/shutdown.rs @@ -1,4 +1,7 @@ //! Shutdown behavior. +use crate::chunk_worker::Request; +use crate::game::Game; +use crate::save; use crossbeam::Sender; use fecs::World; @@ -9,7 +12,17 @@ pub fn init(tx: Sender<()>) { .unwrap(); } -pub fn save_chunks(_world: &mut World) {} +pub fn save_chunks(game: &Game, world: &World) { + for chunk in game.chunk_map.iter_chunks() { + let pos = chunk.read().position(); + save::save_chunk_at(game, world, pos); + } + + // Wait for chunk worker to shut down + let _ = game.chunk_worker_handle.sender.send(Request::ShutDown); + + while let Ok(_) = game.chunk_worker_handle.receiver.recv() {} +} pub fn save_level(_world: &World) {} diff --git a/server/src/systems.rs b/server/src/systems.rs index 86f6c3f23..4ece69b94 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -22,6 +22,7 @@ pub fn build_executor() -> Executor { .with(broadcasters::broadcast_keepalive) .with(broadcasters::broadcast_movement) .with(broadcasters::broadcast_velocity) + .with(save::chunk_save) .with(game::reset_bump_allocators) .with(game::increment_tick_count) .with(time::increment_time) From fcf9c16e4a01509c6e38e49b4603d5acaa690ea8 Mon Sep 17 00:00:00 2001 From: Jacob Emil Ulvedal Rosborg Date: Fri, 20 Mar 2020 17:38:48 +0100 Subject: [PATCH 117/647] Weather: rain + thunderstorm - no thundering, snowing, and putting out fires (#182) * hold that thought * Weather: rain + thunderstorm, no lightning, snow or putting out fires * better mimic vanilla minecraft * Derive debug * on_weather_change_broadcast_weather --- core/src/network/packet/mod.rs | 10 +++ server/src/game.rs | 17 +++++ server/src/lib.rs | 1 + server/src/systems.rs | 1 + server/src/weather.rs | 133 +++++++++++++++++++++++++++++++++ 5 files changed, 162 insertions(+) create mode 100644 server/src/weather.rs diff --git a/core/src/network/packet/mod.rs b/core/src/network/packet/mod.rs index 6ffecebe3..69820e153 100644 --- a/core/src/network/packet/mod.rs +++ b/core/src/network/packet/mod.rs @@ -459,6 +459,11 @@ lazy_static! { PacketType::SpawnObject, ); + m.insert( + PacketId(0x02, PacketDirection::Clientbound, PacketStage::Play), + PacketType::SpawnGlobalEntity, + ); + m.insert( PacketId(0x03, PacketDirection::Clientbound, PacketStage::Play), PacketType::SpawnMob, @@ -504,6 +509,11 @@ lazy_static! { PacketType::BlockChange, ); + m.insert( + PacketId(0x20, PacketDirection::Clientbound, PacketStage::Play), + PacketType::ChangeGameState, + ); + m.insert( PacketId(0x22, PacketDirection::Clientbound, PacketStage::Play), PacketType::ChunkData, diff --git a/server/src/game.rs b/server/src/game.rs index eb70ce6a6..64789f830 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -29,6 +29,7 @@ use crate::view::{ on_chunk_cross_update_chunks, on_chunk_cross_update_entities, on_chunk_load_send_to_clients, on_player_join_trigger_chunk_cross, ChunksToSend, }; +use crate::weather::{on_weather_change_broadcast_weather, send_weather, Weather, WeatherChangeEvent}; use bumpalo::Bump; use feather_blocks::Block; use feather_core::level::LevelData; @@ -271,6 +272,7 @@ impl Game { on_player_join_send_existing_entities(world, player); on_player_join_send_time(self, world, player); on_player_join_trigger_chunk_cross(self, world, player); + send_weather(world, player, self.weather()); } /// Called when a player leaves. @@ -345,6 +347,21 @@ impl Game { pub fn on_item_collect(&mut self, world: &mut World, event: ItemCollectEvent) { on_item_collect_broadcast(self, world, &event); } + + /// Called when weather changes + pub fn on_weather_change(&mut self, world: &mut World, event: &mut WeatherChangeEvent) { + on_weather_change_broadcast_weather(self, world, event.to); + } + + /// Returns the current state of the weather + pub fn weather(&self) -> Weather { + crate::weather::get_weather(&self) + } + + /// Sets the weather for a given duration + pub fn set_weather(&mut self, weather: Weather, duration: i32) -> Weather { + crate::weather::set_weather(self, weather, duration) + } } #[system] diff --git a/server/src/lib.rs b/server/src/lib.rs index d8faaaad8..1b968e19c 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -160,6 +160,7 @@ mod systems; mod time; pub mod util; mod view; +mod weather; pub mod worldgen; pub type BumpVec<'a, T> = bumpalo::collections::Vec<'a, T>; diff --git a/server/src/systems.rs b/server/src/systems.rs index 4ece69b94..3d13642a4 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -14,6 +14,7 @@ pub fn build_executor() -> Executor { .with(packet_handlers::handle_animation) .with(packet_handlers::handle_player_block_placement) .with(packet_handlers::handle_player_digging) + .with(weather::handle_weather) .with(entity::item::item_collect) .with(chunk_logic::chunk_load) .with(chunk_logic::chunk_unload) diff --git a/server/src/weather.rs b/server/src/weather.rs new file mode 100644 index 000000000..595266a15 --- /dev/null +++ b/server/src/weather.rs @@ -0,0 +1,133 @@ +use crate::{network::Network, Game, World}; +use feather_core::network::packet::implementation::ChangeGameState; +use fecs::Entity; +use rand::Rng; +use std::cmp; + +const TICKS_DAY: i32 = 24_000; +const TICKS_HALF_DAY: i32 = TICKS_DAY / 2; +const TICKS_WEEK: i32 = TICKS_DAY * 7; +const THUNDER_FACTOR: i32 = 10; + +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum Weather { + Clear, + Rain, + Thunder, +} + +#[derive(Debug, Clone, Copy)] +pub struct WeatherChangeEvent { + pub from: Weather, + pub to: Weather, + pub duration: i32, +} + +pub fn clear_weather(game: &mut Game) { + let durration = game + .rng() + .gen_range(TICKS_HALF_DAY, TICKS_WEEK + TICKS_HALF_DAY); + set_weather(game, Weather::Clear, durration); +} + +#[system] +pub fn handle_weather(game: &mut Game, world: &mut World) { + if game.level.clear_weather_time >= 0 { + game.level.clear_weather_time -= 1; + return; + } + + let from = game.weather(); + + game.level.rain_time -= 1; + let mut to = if game.level.rain_time <= 0 { + if game.level.raining { + Weather::Clear + } else { + Weather::Rain + } + } else { + from + }; + + game.level.thunder_time -= 1; + to = if game.level.thunder_time <= 0 { + if game.level.thundering { + Weather::Clear + } else { + Weather::Thunder + } + } else { + to + }; + + if from != to { + let duration = match to { + Weather::Clear => game + .rng() + .gen_range(TICKS_HALF_DAY, TICKS_WEEK + TICKS_HALF_DAY), + _ => game.rng().gen_range(TICKS_HALF_DAY, TICKS_DAY), + }; + let mut event = WeatherChangeEvent { from, to, duration }; + game.on_weather_change(world, &mut event); + if event.to != from { + set_weather(game, event.to, event.duration); + } + } +} + +pub fn get_weather(game: &Game) -> Weather { + if game.level.clear_weather_time > 0 { + Weather::Clear + } else if game.level.thundering { + Weather::Thunder + } else if game.level.raining { + Weather::Rain + } else { + Weather::Clear + } +} + +pub fn set_weather(game: &mut Game, weather: Weather, duration: i32) -> Weather { + let from = get_weather(game); + match weather { + Weather::Rain => { + game.level.raining = true; + game.level.rain_time = duration; + } + Weather::Thunder => { + game.level.thundering = true; + game.level.thunder_time = duration; + } + Weather::Clear => { + game.level.raining = false; + game.level.rain_time = 0; + game.level.thundering = false; + game.level.thunder_time = 0; + game.level.clear_weather_time = duration; + } + }; + from +} + +pub fn send_weather(world: &mut World, player: Entity, to: Weather) { + let network = world.get::(player); + + network.send(create_weather_packet(to)); +} + +pub fn on_weather_change_broadcast_weather(game: &mut Game, world: &mut World, to: Weather) { + game.broadcast_global(world, create_weather_packet(to), None); +} + +fn create_weather_packet(to: Weather) -> ChangeGameState { + let reason = match to { + Weather::Rain | Weather::Thunder => 2, + Weather::Clear => 1, + }; + + ChangeGameState { + reason, + value: 0f32, + } +} From 0020a947010e2cc7f9bf7b57be5fbdd7abe919ea Mon Sep 17 00:00:00 2001 From: Jacob Emil Ulvedal Rosborg Date: Fri, 20 Mar 2020 17:40:28 +0100 Subject: [PATCH 118/647] Heightmaps (#181) - excluding loading * heightmaps * early termination of recalculate_heightmap * usize => u8 * heightmap offset as a const * heightmap offset as a const * silly me * fixed some clippy recomendations * better? * should be all good now =) --- blocks/src/lib.rs | 32 ++++++++ core/src/chunk.rs | 135 +++++++++++++++++++++++++++++++ core/src/save/region/blob.rs | 25 ++---- core/src/save/region/mod.rs | 20 +++++ server/src/worldgen/mod.rs | 2 + server/src/worldgen/superflat.rs | 3 + 6 files changed, 197 insertions(+), 20 deletions(-) diff --git a/blocks/src/lib.rs b/blocks/src/lib.rs index 469232a1c..c2861b38e 100644 --- a/blocks/src/lib.rs +++ b/blocks/src/lib.rs @@ -73,6 +73,12 @@ pub trait BlockExt { /// light will be stopped by this block. fn is_opaque(&self) -> bool; + fn is_air(&self) -> bool; + + fn is_fluid(&self) -> bool; + + fn is_leaves(&self) -> bool; + /// Returns the light level emitted by this block. fn light_emission(&self) -> u8; } @@ -214,6 +220,32 @@ impl BlockExt for Block { } } + fn is_air(&self) -> bool { + match self { + Block::Air | Block::CaveAir | Block::VoidAir => true, + _ => false, + } + } + + fn is_fluid(&self) -> bool { + match self { + Block::Water(_) | Block::Lava(_) => true, + _ => false, + } + } + + fn is_leaves(&self) -> bool { + match self { + Block::AcaciaLeaves(_) + | Block::BirchLeaves(_) + | Block::DarkOakLeaves(_) + | Block::JungleLeaves(_) + | Block::OakLeaves(_) + | Block::SpruceLeaves(_) => true, + _ => false, + } + } + fn light_emission(&self) -> u8 { match self { Block::Beacon diff --git a/core/src/chunk.rs b/core/src/chunk.rs index 39a3581b6..f09907f09 100644 --- a/core/src/chunk.rs +++ b/core/src/chunk.rs @@ -1,5 +1,6 @@ use crate::Biome; use crate::{Block, BlockExt, ChunkPosition}; +use bitflags::bitflags; use multimap::MultiMap; /// The number of bits used for each block @@ -59,6 +60,77 @@ pub struct Chunk { /// Whether this chunk has been modified since the most recent /// call to `check_modified`(). modified: bool, + + heightmaps: [HeightMap; CHUNK_WIDTH * CHUNK_WIDTH], +} + +#[derive(Clone, Copy, Default)] +pub struct HeightMap { + motion_blocking: u8, + motion_blocking_no_leaves: u8, + ocean_floor: u8, + ocean_floor_wg: u8, + world_surface: u8, + world_surface_wg: u8, +} + +impl HeightMap { + /// The highest block that is solid or contains a fluid. + pub fn motion_blocking(self) -> u8 { + self.motion_blocking + } + + pub fn set_motion_blocking(&mut self, motion_blocking: u8) { + self.motion_blocking = motion_blocking; + } + + /// The highest block that is solid or contains a fluid and is not leaves. + pub fn motion_blocking_no_leaves(self) -> u8 { + self.motion_blocking_no_leaves + } + + pub fn set_motion_blocking_no_leaves(&mut self, motion_blocking_no_leaves: u8) { + self.motion_blocking_no_leaves = motion_blocking_no_leaves; + } + + /// The highest block that is solid. + pub fn ocean_floor(self) -> u8 { + self.ocean_floor + } + + pub fn set_ocean_floor(&mut self, ocean_floor: u8) { + self.ocean_floor = ocean_floor; + } + + /// The highest block that is solid for world generation. + pub fn ocean_floor_wg(self) -> u8 { + self.ocean_floor_wg + } + + /// The highest block that is not air. + pub fn world_surface(self) -> u8 { + self.world_surface + } + + pub fn set_world_surface(&mut self, world_surface: u8) { + self.world_surface = world_surface; + } + + /// The highest block is not air for world generation. + pub fn world_surface_wg(self) -> u8 { + self.world_surface_wg + } +} + +bitflags! { + struct HeightMapMask: u8 { + const MOTION_BLOCKING = 0b0000_0001; + const MOTION_BLOCKING_NO_LEAVES = 0b0000_0010; + const OCEAN_FLOOR = 0b0000_0100; + const OCEAN_FLOOR_WG = 0b0000_1000; + const WORLD_SURFACE = 0b0001_0000; + const WORLD_SURFACE_WG = 0b0010_0000; + } } impl Default for Chunk { @@ -77,6 +149,7 @@ impl Default for Chunk { modified: true, sections, biomes: [Biome::Plains; SECTION_WIDTH * SECTION_WIDTH], + heightmaps: [HeightMap::default(); CHUNK_WIDTH * CHUNK_WIDTH], } } } @@ -151,6 +224,68 @@ impl Chunk { } section.set_block_at(x, y % 16, z, block); + + self.update_heightmap(x, y, z, block); + } + + pub fn heightmap(&self, x: usize, z: usize) -> &HeightMap { + Self::check_coords(x, 0, z); + &self.heightmaps[x + z * CHUNK_WIDTH] + } + + pub fn heightmap_mut(&mut self, x: usize, z: usize) -> &mut HeightMap { + Self::check_coords(x, 0, z); + &mut self.heightmaps[x + z * CHUNK_WIDTH] + } + + pub fn heightmaps(&self) -> &[HeightMap] { + &self.heightmaps + } + + fn update_heightmap(&mut self, x: usize, y: usize, z: usize, block: Block) -> HeightMapMask { + let heightmap = self.heightmap_mut(x, z); + let mut mask: HeightMapMask = HeightMapMask::empty(); + if (block.is_solid() || block.is_fluid()) && heightmap.motion_blocking() < y as u8 { + heightmap.set_motion_blocking(y as u8); + mask |= HeightMapMask::MOTION_BLOCKING; + } + + if (block.is_solid() || block.is_fluid()) + && !block.is_leaves() + && heightmap.motion_blocking_no_leaves() < y as u8 + { + heightmap.set_motion_blocking_no_leaves(y as u8); + mask |= HeightMapMask::MOTION_BLOCKING_NO_LEAVES; + } + + if block.is_solid() && heightmap.ocean_floor() < y as u8 { + heightmap.set_ocean_floor(y as u8); + mask |= HeightMapMask::OCEAN_FLOOR; + } + + if !block.is_air() && heightmap.world_surface() < y as u8 { + heightmap.set_world_surface(y as u8); + mask |= HeightMapMask::WORLD_SURFACE; + } + mask + } + + /// Recalculate the heightmap for the chunk + pub fn recalculate_heightmap(&mut self) { + // This function can be optimized, instead of + // fetching heightmap every time, and sections + for x in 0..CHUNK_WIDTH { + for z in 0..CHUNK_WIDTH { + let mut mask: HeightMapMask = HeightMapMask::empty(); + for y in (0..CHUNK_HEIGHT).rev() { + if mask.is_all() { + break; + } + let block = self.block_at(x, y, z); + mask |= self.update_heightmap(x, y, z, block); + } + } + } } pub fn sky_light_at(&self, x: usize, y: usize, z: usize) -> u8 { diff --git a/core/src/save/region/blob.rs b/core/src/save/region/blob.rs index 9bb03b988..2cb79b130 100644 --- a/core/src/save/region/blob.rs +++ b/core/src/save/region/blob.rs @@ -24,26 +24,10 @@ fn level_to_value(level: ChunkLevel) -> Value { map.insert(String::from("InhabitedTime"), Value::Long(0)); // TODO map.insert(String::from("Biomes"), Value::IntArray(level.biomes)); - let mut hmaps = HashMap::new(); - hmaps.insert( - String::from("MOTION_BLOCKING"), - Value::LongArray(vec![0; 32]), - ); // TODO - hmaps.insert( - String::from("MOTION_BLOCKING_NO_LEAVES"), - Value::LongArray(vec![0; 32]), - ); // TODO - hmaps.insert(String::from("OCEAN_FLOOR"), Value::LongArray(vec![0; 32])); // TODO - hmaps.insert( - String::from("OCEAN_FLOOR_WG"), - Value::LongArray(vec![0; 32]), - ); // TODO - hmaps.insert(String::from("WORLD_SURFACE"), Value::LongArray(vec![0; 32])); // TODO - hmaps.insert( - String::from("WORLD_SURFACE_WG"), - Value::LongArray(vec![0; 32]), - ); // TODO - map.insert(String::from("Heightmaps"), Value::Compound(hmaps)); + map.insert( + String::from("Heightmaps"), + Value::LongArray(level.heightmaps), + ); let sections = level.sections.into_iter().map(section_to_value).collect(); map.insert(String::from("Sections"), Value::List(sections)); @@ -158,6 +142,7 @@ mod tests { }], biomes: vec![10], entities: vec![], + heightmaps: vec![], }, }; diff --git a/core/src/save/region/mod.rs b/core/src/save/region/mod.rs index 3c8deecd2..237ffc4a6 100644 --- a/core/src/save/region/mod.rs +++ b/core/src/save/region/mod.rs @@ -30,6 +30,9 @@ const DATA_VERSION: i32 = 1631; /// Length, in bytes, of a sector. const SECTOR_BYTES: usize = 4096; +/// The offset for each heightmap value +const HEIGHTMAP_OFFSET: i64 = 9; + /// Represents the data for a chunk after the "Chunk [x, y]" tag. #[derive(Serialize, Deserialize, Debug)] pub struct ChunkRoot { @@ -53,6 +56,8 @@ pub struct ChunkLevel { biomes: Vec, #[serde(rename = "Entities")] entities: Vec, + #[serde(rename = "Heightmaps")] + heightmaps: Vec, } /// Represents a chunk section in a region file. @@ -200,6 +205,8 @@ impl RegionHandle { // Chunk was not modified, but it thinks it was: disable this chunk.check_modified(); + chunk.recalculate_heightmap(); + Ok((chunk, level.entities.to_vec())) } @@ -344,6 +351,18 @@ fn read_section_into_chunk(section: &LevelSection, chunk: &mut Chunk) -> Result< } fn chunk_to_chunk_root(chunk: &Chunk, entities: Vec) -> ChunkRoot { + let heightmaps: Vec = chunk + .heightmaps() + .iter() + .map(|map| { + (map.motion_blocking() as i64) + + ((map.motion_blocking_no_leaves() as i64) << HEIGHTMAP_OFFSET) + + ((map.ocean_floor() as i64) << (HEIGHTMAP_OFFSET * 2)) + + ((map.ocean_floor_wg() as i64) << (HEIGHTMAP_OFFSET * 3)) + + ((map.world_surface() as i64) << (HEIGHTMAP_OFFSET * 4)) + + ((map.world_surface_wg() as i64) << (HEIGHTMAP_OFFSET * 5)) + }) + .collect(); ChunkRoot { level: ChunkLevel { x_pos: chunk.position().x, @@ -370,6 +389,7 @@ fn chunk_to_chunk_root(chunk: &Chunk, entities: Vec) -> ChunkRoot { .map(|biome| biome.protocol_id()) .collect(), entities, + heightmaps, }, data_version: DATA_VERSION, } diff --git a/server/src/worldgen/mod.rs b/server/src/worldgen/mod.rs index ade3b9dac..da7f05e6b 100644 --- a/server/src/worldgen/mod.rs +++ b/server/src/worldgen/mod.rs @@ -169,6 +169,8 @@ impl WorldGenerator for ComposableGenerator { } } + chunk.recalculate_heightmap(); + // Finishers. for finisher in &self.finishers { finisher.generate_for_chunk( diff --git a/server/src/worldgen/superflat.rs b/server/src/worldgen/superflat.rs index c9039c5af..437f227a9 100644 --- a/server/src/worldgen/superflat.rs +++ b/server/src/worldgen/superflat.rs @@ -34,6 +34,9 @@ impl WorldGenerator for SuperflatWorldGenerator { } y_counter += layer.height; } + + chunk.recalculate_heightmap(); + chunk } } From a82aa383422f0a722909ae2bf40af5a52271dcf6 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Mar 2020 11:02:31 -0600 Subject: [PATCH 119/647] Reimplement chat and player join message --- server/src/block.rs | 27 ------------- server/src/broadcasters/chat.rs | 12 +++--- server/src/broadcasters/mod.rs | 3 +- server/src/chat.rs | 48 +++++++++++++++++++++++ server/src/entity/item.rs | 4 +- server/src/game.rs | 19 ++++++--- server/src/lib.rs | 2 +- server/src/packet_buffer.rs | 2 +- server/src/packet_handlers/chat.rs | 35 +++++++++++++---- server/src/packet_handlers/mod.rs | 3 +- server/src/player/chat.rs | 63 ------------------------------ server/src/player/mod.rs | 2 - server/src/systems.rs | 1 + 13 files changed, 103 insertions(+), 118 deletions(-) delete mode 100644 server/src/block.rs create mode 100644 server/src/chat.rs delete mode 100644 server/src/player/chat.rs diff --git a/server/src/block.rs b/server/src/block.rs deleted file mode 100644 index c6e357d48..000000000 --- a/server/src/block.rs +++ /dev/null @@ -1,27 +0,0 @@ -use feather_core::{Block, BlockPosition}; -use legion::entity::Entity; - -/// Event triggered when a block is updated. -/// -/// This event is triggered *after* the block is updated -/// in the chunk map. -#[derive(Debug, Clone)] -pub struct BlockUpdateEvent { - /// The cause of this block update event. - pub cause: BlockUpdateCause, - /// The location of the block which was updated. - pub pos: BlockPosition, - /// The block which was previously at the position. - pub old_block: Block, - /// The new block at the position. - pub new_block: Block, -} - -/// The possible causes of a block update event. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum BlockUpdateCause { - /// Indicates that a player updated the block. - Player(Entity), - /// Indicates that a falling block updated the block. - FallingBlock, -} diff --git a/server/src/broadcasters/chat.rs b/server/src/broadcasters/chat.rs index 549c46874..f607d64ab 100644 --- a/server/src/broadcasters/chat.rs +++ b/server/src/broadcasters/chat.rs @@ -1,19 +1,19 @@ //! Broadcasting of chat messages -use crate::player::chat::{ChatBroadcastEvent, ChatPosition}; -use crate::state::State; +use crate::chat::{ChatEvent, ChatPosition}; +use crate::game::Game; use feather_core::network::packet::implementation::ChatMessageClientbound; +use fecs::World; /// System that broadcasts chat messages to all players -#[event_handler] -fn broadcast_chat(event: &ChatBroadcastEvent, state: &State) { +pub fn on_chat_broadcast(game: &Game, world: &World, event: &ChatEvent) { let packet = ChatMessageClientbound { - json_data: event.json_data.clone(), + json_data: event.message.clone(), position: match event.position { ChatPosition::Chat => 0, ChatPosition::SystemMessage => 1, ChatPosition::GameInfo => 2, }, }; - state.broadcast_global(packet, None); + game.broadcast_global(world, packet, None); } diff --git a/server/src/broadcasters/mod.rs b/server/src/broadcasters/mod.rs index fde2bf0d0..6023ae7b7 100644 --- a/server/src/broadcasters/mod.rs +++ b/server/src/broadcasters/mod.rs @@ -9,7 +9,7 @@ mod animation; mod block; -// mod chat; +mod chat; mod entity_creation; mod entity_deletion; mod inventory; @@ -24,6 +24,7 @@ pub use self::inventory::{ }; pub use animation::on_player_animation_broadcast_animation; pub use block::on_block_update_broadcast; +pub use chat::on_chat_broadcast; pub use entity_creation::on_entity_spawn_send_to_clients; pub use entity_creation::on_player_join_send_existing_entities; pub use entity_deletion::on_entity_despawn_broadcast_despawn; diff --git a/server/src/chat.rs b/server/src/chat.rs new file mode 100644 index 000000000..4ce892bd6 --- /dev/null +++ b/server/src/chat.rs @@ -0,0 +1,48 @@ +use crate::entity::Name; +use crate::game::Game; +use fecs::{Entity, World}; + +/// Event triggered when a chat message is sent out +#[derive(Debug, Clone)] +pub struct ChatEvent { + /// The JSON-formatted message + pub message: String, + + /// The position of the message + pub position: ChatPosition, +} + +/// Different positions a chat message can be displayed +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ChatPosition { + /// Simple message displayed in the chat box + Chat, + + /// System message displayed in the chat box + SystemMessage, + + /// A text displayed above the hotbar + GameInfo, +} + +pub fn on_player_join_broadcast_join_message(game: &mut Game, world: &mut World, player: Entity) { + let message = { + let name = world.get::(player); + json!({ + "translate": "multiplayer.player.joined", + "color": "yellow", + "with": [ + { "text": name.0 }, + ], + }) + .to_string() + }; + + game.on_chat( + world, + ChatEvent { + message, + position: ChatPosition::Chat, + }, + ); +} diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index 075bb4e2d..d2f70a97b 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -101,8 +101,8 @@ pub fn on_item_drop_spawn_item_entity(game: &mut Game, world: &mut World, event: /// System to add items to player inventories when the player comes near. #[system] pub fn item_collect(game: &mut Game, world: &mut World) { - // run every 1/2 second - if game.tick_count % TPS / 2 != 0 { + // run every 1/4 second + if game.tick_count % TPS / 4 != 0 { return; } diff --git a/server/src/game.rs b/server/src/game.rs index 64789f830..01a255c64 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,11 +1,13 @@ use crate::broadcasters::{ - on_block_update_broadcast, on_entity_client_remove_update_last_known_positions, - on_entity_despawn_broadcast_despawn, on_entity_send_send_equipment, - on_entity_send_send_metadata, on_entity_send_update_last_known_positions, - on_entity_spawn_send_to_clients, on_inventory_update_broadcast_equipment_update, - on_inventory_update_send_set_slot, on_item_collect_broadcast, - on_player_animation_broadcast_animation, on_player_join_send_existing_entities, + on_block_update_broadcast, on_chat_broadcast, + on_entity_client_remove_update_last_known_positions, on_entity_despawn_broadcast_despawn, + on_entity_send_send_equipment, on_entity_send_send_metadata, + on_entity_send_update_last_known_positions, on_entity_spawn_send_to_clients, + on_inventory_update_broadcast_equipment_update, on_inventory_update_send_set_slot, + on_item_collect_broadcast, on_player_animation_broadcast_animation, + on_player_join_send_existing_entities, }; +use crate::chat::{on_player_join_broadcast_join_message, ChatEvent}; use crate::chunk_entities::{ on_chunk_cross_update_chunk_entities, on_entity_despawn_update_chunk_entities, on_entity_spawn_update_chunk_entities, ChunkEntities, @@ -273,6 +275,7 @@ impl Game { on_player_join_send_time(self, world, player); on_player_join_trigger_chunk_cross(self, world, player); send_weather(world, player, self.weather()); + on_player_join_broadcast_join_message(self, world, player); } /// Called when a player leaves. @@ -362,6 +365,10 @@ impl Game { pub fn set_weather(&mut self, weather: Weather, duration: i32) -> Weather { crate::weather::set_weather(self, weather, duration) } + /// Called when a chat message is broadcasted. + pub fn on_chat(&mut self, world: &mut World, event: ChatEvent) { + on_chat_broadcast(self, world, &event); + } } #[system] diff --git a/server/src/lib.rs b/server/src/lib.rs index 1b968e19c..c24f882ad 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -138,8 +138,8 @@ use thread_local::CachedThreadLocal; #[global_allocator] static ALLOC: Jemalloc = Jemalloc; -// pub mod block; mod broadcasters; +mod chat; mod chunk_entities; pub mod chunk_logic; pub mod chunk_worker; diff --git a/server/src/packet_buffer.rs b/server/src/packet_buffer.rs index 9dc0cebf2..e8ce3660a 100644 --- a/server/src/packet_buffer.rs +++ b/server/src/packet_buffer.rs @@ -156,7 +156,7 @@ pub struct ChannelBuffer { impl ChannelBuffer { fn new() -> Self { - let (sender, receiver) = crossbeam::unbounded(); + let (sender, receiver) = crossbeam::bounded(8); Self { sender, receiver } } diff --git a/server/src/packet_handlers/chat.rs b/server/src/packet_handlers/chat.rs index cf64b21c4..1e2ed4ebe 100644 --- a/server/src/packet_handlers/chat.rs +++ b/server/src/packet_handlers/chat.rs @@ -1,16 +1,35 @@ -use crate::network::PacketQueue; -use crate::player::chat::PlayerChatEvent; +use crate::chat::{ChatEvent, ChatPosition}; +use crate::entity::Name; +use crate::game::Game; +use crate::packet_buffer::PacketBuffers; use feather_core::network::packet::implementation::ChatMessageServerbound; -use tonks::Trigger; +use fecs::World; +use std::sync::Arc; -/// Handles animation packets. +/// Handles chat packets. #[system] -fn handle_chat(queue: &PacketQueue, trigger: &mut Trigger) { - queue +pub fn handle_chat(game: &mut Game, world: &mut World, packet_buffers: &Arc) { + packet_buffers .received::() .for_each(|(player, packet)| { - let message = packet.message; + let player_name = world.get::(player); + let message = json!({ + "translate": "chat.type.text", + "with": [ + { "text": &player_name.0 }, + { "text": packet.message } + ] + }); - trigger.trigger(PlayerChatEvent { player, message }); + info!("<{}> {}", player_name.0, message); + drop(player_name); + + game.on_chat( + world, + ChatEvent { + message: message.to_string(), + position: ChatPosition::Chat, + }, + ); }); } diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index c245d9018..9dc251937 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -1,7 +1,7 @@ //! Systems which handle packets. mod animation; -// mod chat; +mod chat; mod digging; mod inventory; mod movement; @@ -9,6 +9,7 @@ mod placement; pub use self::inventory::{handle_creative_inventory_action, handle_held_item_change}; pub use animation::handle_animation; +pub use chat::handle_chat; pub use digging::handle_player_digging; pub use movement::handle_movement_packets; pub use placement::handle_player_block_placement; diff --git a/server/src/player/chat.rs b/server/src/player/chat.rs deleted file mode 100644 index 2e1643633..000000000 --- a/server/src/player/chat.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::entity::Name; -use legion::entity::Entity; -use legion::query::Read; -use tonks::{PreparedWorld, Query, Trigger}; - -/// Event triggered when a player sends a chat message -#[derive(Debug, Clone)] -pub struct PlayerChatEvent { - /// The player that sent the chat message - pub player: Entity, - - /// The raw message that was sent - pub message: String, -} - -/// Event that will result in a chat message being broadcasted -pub struct ChatBroadcastEvent { - // TODO: Use composable chat component here - /// A JSON string representing the Chat component to sent - pub json_data: String, - - /// The position - pub position: ChatPosition, -} - -/// Different positions a chat message can be displayed -pub enum ChatPosition { - /// Simple message displayed in the chat box - Chat, - - /// System message displayed in the chat box - SystemMessage, - - /// A text displayed above the hotbar - GameInfo, -} - -/// System that broadcasts chat messages to all players -#[event_handler] -fn broadcast_chat( - event: &PlayerChatEvent, - world: &mut PreparedWorld, - _query: &mut Query>, - trigger: &mut Trigger, -) { - let player_name = &world.get_component::(event.player).unwrap().0; - - let json_data = json!({ - "translate": "chat.type.text", - "with": [ - {"text": player_name}, - {"text": event.message} - ] - }) - .to_string(); - - trigger.trigger(ChatBroadcastEvent { - json_data, - position: ChatPosition::Chat, - }); - - info!("<{}> {}", player_name, event.message); -} diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index 284f8a45f..5f547849f 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -14,8 +14,6 @@ use fecs::{Entity, EntityRef, World}; use mojang_api::ProfileProperty; use uuid::Uuid; -// pub mod chat; - pub const PLAYER_EYE_HEIGHT: f64 = 1.62; /// Profile properties of a player. diff --git a/server/src/systems.rs b/server/src/systems.rs index 3d13642a4..c923d9dda 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -14,6 +14,7 @@ pub fn build_executor() -> Executor { .with(packet_handlers::handle_animation) .with(packet_handlers::handle_player_block_placement) .with(packet_handlers::handle_player_digging) + .with(packet_handlers::handle_chat) .with(weather::handle_weather) .with(entity::item::item_collect) .with(chunk_logic::chunk_load) From 90cf20e89f4221a3229cf5a8ad58684062d7235d Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Mar 2020 13:13:41 -0600 Subject: [PATCH 120/647] Fix git mess --- server/src/packet_buffer.rs | 2 +- server/src/packet_handlers/chat.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/packet_buffer.rs b/server/src/packet_buffer.rs index e8ce3660a..9dc0cebf2 100644 --- a/server/src/packet_buffer.rs +++ b/server/src/packet_buffer.rs @@ -156,7 +156,7 @@ pub struct ChannelBuffer { impl ChannelBuffer { fn new() -> Self { - let (sender, receiver) = crossbeam::bounded(8); + let (sender, receiver) = crossbeam::unbounded(); Self { sender, receiver } } diff --git a/server/src/packet_handlers/chat.rs b/server/src/packet_handlers/chat.rs index 1e2ed4ebe..64307c735 100644 --- a/server/src/packet_handlers/chat.rs +++ b/server/src/packet_handlers/chat.rs @@ -21,7 +21,7 @@ pub fn handle_chat(game: &mut Game, world: &mut World, packet_buffers: &Arc {}", player_name.0, message); + info!("<{}> {}", player_name.0, packet.message); drop(player_name); game.on_chat( From 16464fc4a5e146e8495c594931d1d63200ff34e7 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Mar 2020 13:18:33 -0600 Subject: [PATCH 121/647] Fix warnings --- server/src/game.rs | 4 +++- server/src/weather.rs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/server/src/game.rs b/server/src/game.rs index 01a255c64..2926bbd85 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -31,7 +31,9 @@ use crate::view::{ on_chunk_cross_update_chunks, on_chunk_cross_update_entities, on_chunk_load_send_to_clients, on_player_join_trigger_chunk_cross, ChunksToSend, }; -use crate::weather::{on_weather_change_broadcast_weather, send_weather, Weather, WeatherChangeEvent}; +use crate::weather::{ + on_weather_change_broadcast_weather, send_weather, Weather, WeatherChangeEvent, +}; use bumpalo::Bump; use feather_blocks::Block; use feather_core::level::LevelData; diff --git a/server/src/weather.rs b/server/src/weather.rs index 595266a15..5f085a7f2 100644 --- a/server/src/weather.rs +++ b/server/src/weather.rs @@ -2,12 +2,11 @@ use crate::{network::Network, Game, World}; use feather_core::network::packet::implementation::ChangeGameState; use fecs::Entity; use rand::Rng; -use std::cmp; const TICKS_DAY: i32 = 24_000; const TICKS_HALF_DAY: i32 = TICKS_DAY / 2; const TICKS_WEEK: i32 = TICKS_DAY * 7; -const THUNDER_FACTOR: i32 = 10; +// const THUNDER_FACTOR: i32 = 10; #[derive(Debug, PartialEq, Clone, Copy)] pub enum Weather { @@ -23,6 +22,7 @@ pub struct WeatherChangeEvent { pub duration: i32, } +#[allow(unused)] pub fn clear_weather(game: &mut Game) { let durration = game .rng() From 435643c9bb502a7705ab7acef25de51ccdbfc33f Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Mar 2020 13:20:51 -0600 Subject: [PATCH 122/647] Fix infinite loop in `block_impacted_by_ray` --- server/src/game.rs | 1 + server/src/physics/math.rs | 21 +++++++++------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/server/src/game.rs b/server/src/game.rs index 2926bbd85..bf012660e 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -367,6 +367,7 @@ impl Game { pub fn set_weather(&mut self, weather: Weather, duration: i32) -> Weather { crate::weather::set_weather(self, weather, duration) } + /// Called when a chat message is broadcasted. pub fn on_chat(&mut self, world: &mut World, event: ChatEvent) { on_chat_broadcast(self, world, &event); diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index 1957ad711..6dc7c0b00 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -175,18 +175,15 @@ pub fn block_impacted_by_ray( let shape = block_shape(&block); let isometry = block_isometry(current_pos); - let impact = match shape.toi_and_normal_with_ray(&isometry, &ray, 1000.0, true) { - Some(toi) => toi, - None => continue, - }; - - let pos = Position::from(origin + impact.toi * direction); - - return Some(RayImpact { - block: current_pos, - pos, - face, - }); + if let Some(impact) = shape.toi_and_normal_with_ray(&isometry, &ray, 1000.0, true) { + let pos = Position::from(origin + impact.toi * direction); + + return Some(RayImpact { + block: current_pos, + pos, + face, + }); + } } } else { // Traveled outside loaded chunks - no blocks found From 5be9a60a78585c80941294d42e4d19cc5a8b3756 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Mar 2020 16:30:33 -0600 Subject: [PATCH 123/647] Reimplement falling blocks --- Cargo.lock | 7 ++ core/src/entitymeta.rs | 3 + core/src/world.rs | 5 + server/Cargo.toml | 1 + server/src/block.rs | 74 ++++++++++++++ server/src/entity/falling_block.rs | 126 ++++++++++++++++++++++++ server/src/entity/mod.rs | 1 + server/src/game.rs | 14 +++ server/src/lib.rs | 3 +- server/src/packet_handlers/inventory.rs | 2 +- server/src/packet_handlers/placement.rs | 3 - server/src/physics/entity.rs | 18 ++-- server/src/save.rs | 2 +- server/src/systems.rs | 1 + server/src/util.rs | 15 ++- 15 files changed, 262 insertions(+), 13 deletions(-) create mode 100644 server/src/block.rs create mode 100644 server/src/entity/falling_block.rs diff --git a/Cargo.lock b/Cargo.lock index 36dc5d29c..6c37aa7aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,6 +95,12 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff" +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + [[package]] name = "as-slice" version = "0.1.2" @@ -715,6 +721,7 @@ dependencies = [ "ahash", "anyhow", "approx 0.3.2", + "arrayvec", "base64 0.12.0", "bitflags", "bitvec", diff --git a/core/src/entitymeta.rs b/core/src/entitymeta.rs index 64bd9a362..5d54e507f 100644 --- a/core/src/entitymeta.rs +++ b/core/src/entitymeta.rs @@ -22,8 +22,11 @@ pub const META_INDEX_CUSTOM_NAME: u8 = 2; pub const META_INDEX_IS_CUSTOM_NAME_VISIBLE: u8 = 3; pub const META_INDEX_IS_SILENT: u8 = 4; pub const META_INDEX_NO_GRAVITY: u8 = 5; + pub const META_INDEX_ITEM_SLOT: u8 = 6; +pub const META_INDEX_FALLING_BLOCK_SPAWN_POSITION: u8 = 7; + bitflags! { pub struct EntityBitMask: u8 { const ON_FIRE = 0x01; diff --git a/core/src/world.rs b/core/src/world.rs index f8322d77a..160f26429 100644 --- a/core/src/world.rs +++ b/core/src/world.rs @@ -251,6 +251,11 @@ impl BlockPosition { pub fn manhattan_distance(self, other: BlockPosition) -> i32 { (self.x - other.x).abs() + (self.y - other.y).abs() + (self.z - other.z).abs() } + + /// Converts this `BlockPosition` to a `Position`. + pub fn position(self) -> Position { + self.into() + } } impl Add for BlockPosition { diff --git a/server/Cargo.toml b/server/Cargo.toml index 37ef5ed82..b533f6997 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -60,6 +60,7 @@ heapless = "0.5" uuid = { version = "0.8", features = ["v4"] } multimap = "0.8" smallvec = "1.2" +arrayvec = "0.5" indexmap = "1.3" dashmap = "3.7" diff --git a/server/src/block.rs b/server/src/block.rs new file mode 100644 index 000000000..eaac02234 --- /dev/null +++ b/server/src/block.rs @@ -0,0 +1,74 @@ +//! Assorted functionality relating to blocks, including: +//! * The block notify system, where a block update "notifies" +//! adjacent blocks of the update. This is used for spawning +//! falling blocks, for example. +//! +//! The block notify system works as follows: when a block +//! is updated, `on_block_update_notify_adjacent` is called, +//! which checks the blocks adjacent to the updated block. +//! For each adjacent block, `notify_entity_for_block` is called +//! which returns an `Option` containing the components +//! to create for the notify entity. For example, `Some(EntityBuilder::new().with(FallingBlockNotify)` +//! could be returned for `Sand` and `Gravel` variants. +//! +//! `on_block_update_notify_adjacent` then creates an entity with those components. +//! The "notify entity," in this case, +//! acts as a sort of event, as other systems can check for these entities +//! and perform actions based on their components. + +use crate::game::Game; +use crate::util; +use feather_blocks::Block; +use feather_core::BlockPosition; +use fecs::{EntityBuilder, World}; + +/// Marker component stating that an entity is a notify entity. +#[derive(Copy, Clone, Debug)] +pub struct BlockNotify; + +/// Component storing the position of a block for a block notify entity. +#[derive(Copy, Clone, Debug)] +pub struct BlockNotifyPosition(pub BlockPosition); + +/// Component storing the type of block notified. +#[derive(Copy, Clone, Debug)] +pub struct BlockNotifyBlock(pub Block); + +/// Marker component for block notify entities created for falling +/// blocks, such as sand and gravel. +#[derive(Copy, Clone, Debug)] +pub struct BlockNotifyFallingBlock; + +/// Returns an `EntityBuilder` to create the block notify entity for +/// the given block type. +fn notify_entity_for_block(block: Block, pos: BlockPosition) -> Option { + let builder = EntityBuilder::new() + .with(BlockNotify) + .with(BlockNotifyPosition(pos)) + .with(BlockNotifyBlock(block)); + + match block { + Block::Sand | Block::Gravel | Block::RedSand => Some(builder.with(BlockNotifyFallingBlock)), + _ => None, + } +} + +/// When a block is updated, spawns notify entities +/// for adjacent blocks. +pub fn on_block_update_notify_adjacent(game: &mut Game, world: &mut World, pos: BlockPosition) { + util::adjacent_blocks(pos) + .into_iter() + .filter_map(|adjacent_pos| { + if let Some(adjacent_block) = game.block_at(adjacent_pos) { + Some((adjacent_block, adjacent_pos)) + } else { + None + } + }) + .filter_map(|(adjacent_block, adjacent_pos)| { + notify_entity_for_block(adjacent_block, adjacent_pos) + }) + .for_each(|builder| { + builder.build().spawn_in(world); + }) +} diff --git a/server/src/entity/falling_block.rs b/server/src/entity/falling_block.rs new file mode 100644 index 000000000..0bdd3e6d7 --- /dev/null +++ b/server/src/entity/falling_block.rs @@ -0,0 +1,126 @@ +//! Implements falling block entities: sand, gravel, etc. + +use crate::block::{BlockNotifyBlock, BlockNotifyFallingBlock, BlockNotifyPosition}; +use crate::entity::{EntityId, SpawnPacketCreator, Velocity}; +use crate::game::Game; +use crate::physics::{EntityPhysicsLandEvent, PhysicsBuilder}; +use crate::util::{degrees_to_stops, protocol_velocity}; +use crate::{entity, BumpVec}; +use feather_blocks::{Block, BlockExt}; +use feather_core::network::packet::implementation::SpawnObject; +use feather_core::{ + BlockPosition, EntityMetadata, Packet, Position, META_INDEX_FALLING_BLOCK_SPAWN_POSITION, +}; +use fecs::{component, EntityBuilder, EntityRef, IntoQuery, Read, World}; +use uuid::Uuid; + +/// Marker component indicating an entity is a falling block. +#[derive(Copy, Clone, Debug)] +pub struct FallingBlock; + +/// Component storing the block type for a falling block. +#[derive(Copy, Clone, Debug)] +pub struct FallingBlockType(pub Block); + +/// System to create a falling block when a block notify +/// entity is spawned with `BlockNotifyFallingBlock`. +#[system] +pub fn spawn_falling_blocks(game: &mut Game, world: &mut World) { + let mut actions = BumpVec::new_in(game.bump()); + + actions.extend( + <(Read, Read)>::query() + .filter(component::()) + .iter_entities(world.inner()) + .map(|(entity, (block, position))| { + let builder = if game.block_at(position.0 - BlockPosition::new(0, 1, 0)) + == Some(Block::Air) + { + Some(create( + position.0.position() + position!(0.5, 0.0, 0.5), + block.0, + position.0, + )) + } else { + None + }; + + (entity, builder, position.0) + }), + ); + + for (entity_to_delete, entity_builder, block_to_clear) in actions { + world.despawn(entity_to_delete); + + if let Some(entity_builder) = entity_builder { + let created_entity = entity_builder.build().spawn_in(world); + game.on_entity_spawn(world, created_entity); + + game.set_block_at(world, block_to_clear, Block::Air); + } + } +} + +/// When a falling block lands on the ground, deletes +/// it and creates a solid block where it landed. +pub fn on_entity_land_remove_falling_block( + game: &mut Game, + world: &mut World, + event: &EntityPhysicsLandEvent, +) { + if let Some(block) = world + .try_get::(event.entity) + .map(|block| block.0) + { + let pos = event.pos.block(); + game.set_block_at(world, pos, block); + + game.despawn(event.entity, world); + } +} + +/// Returns an `EntityBuilder` for a falling block of the given type. +pub fn create(pos: Position, ty: Block, spawn_pos: BlockPosition) -> EntityBuilder { + let meta = + EntityMetadata::entity_base().with(META_INDEX_FALLING_BLOCK_SPAWN_POSITION, spawn_pos); + + entity::base(pos) + .with(FallingBlock) + .with(FallingBlockType(ty)) + .with(SpawnPacketCreator(&create_spawn_packet)) + .with( + PhysicsBuilder::new() + .bbox(0.98, 0.98, 0.98) + .drag(0.98) + .gravity(-0.04) + .build(), + ) + .with(meta) +} + +fn create_spawn_packet(accessor: &EntityRef) -> Box { + let data = i32::from(accessor.get::().0.native_state_id()); + let position = accessor.get::(); + let entity_id = accessor.get::().0; + + let velocity = accessor.get::().0; + + let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocity); + + let packet = SpawnObject { + entity_id, + object_uuid: Uuid::new_v4(), + ty: 70, // Type 70 for falling block + x: position.x, + y: position.y, + z: position.z, + pitch: degrees_to_stops(position.pitch), + yaw: degrees_to_stops(position.yaw), + data, + velocity_x, + velocity_y, + velocity_z, + }; + + Box::new(packet) +} diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 69249f1e8..e5a3aba9b 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -3,6 +3,7 @@ //! block entities, monsters, etc. Player entities are handled in `crate::player`, //! not here. +pub mod falling_block; pub mod item; use crate::game::Game; diff --git a/server/src/game.rs b/server/src/game.rs index bf012660e..3fba6c4e1 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,3 +1,4 @@ +use crate::block::on_block_update_notify_adjacent; use crate::broadcasters::{ on_block_update_broadcast, on_chat_broadcast, on_entity_client_remove_update_last_known_positions, on_entity_despawn_broadcast_despawn, @@ -17,12 +18,14 @@ use crate::chunk_logic::{ ChunkUnloadQueue, ChunkWorkerHandle, }; use crate::config::Config; +use crate::entity::falling_block::on_entity_land_remove_falling_block; use crate::entity::item::{on_item_drop_spawn_item_entity, ItemCollectEvent, ItemDropEvent}; use crate::entity::Name; use crate::io::{NetworkIoManager, NewClientInfo, ServerToWorkerMessage}; use crate::join::{on_chunk_send_join_player, on_player_join_send_join_game}; use crate::network::Network; use crate::p_inventory::InventoryUpdateEvent; +use crate::physics::EntityPhysicsLandEvent; use crate::player; use crate::player::Player; use crate::save::{on_chunk_load_queue_for_saving, on_chunk_unload_save_chunk, SaveQueue}; @@ -235,6 +238,7 @@ impl Game { _old: Block, new: Block, ) { + on_block_update_notify_adjacent(self, world, pos); on_block_update_broadcast(self, world, pos, new); } @@ -252,6 +256,11 @@ impl Game { } /// Called when an entity of any type is spawned/created. + /// + /// This function is only called for "normal" entities, i.e. + /// those which Minecraft normally considers entities. Auxiliary + /// entities used by Feather, such as the block notify entity + /// (see `crate::block`) are not included in this function. pub fn on_entity_spawn(&mut self, world: &mut World, entity: Entity) { on_entity_spawn_update_chunk_entities(self, world, entity); on_entity_spawn_send_to_clients(self, world, entity); @@ -372,6 +381,11 @@ impl Game { pub fn on_chat(&mut self, world: &mut World, event: ChatEvent) { on_chat_broadcast(self, world, &event); } + + /// Called when an entity lands on the ground. + pub fn on_entity_land(&mut self, world: &mut World, event: EntityPhysicsLandEvent) { + on_entity_land_remove_falling_block(self, world, &event); + } } #[system] diff --git a/server/src/lib.rs b/server/src/lib.rs index c24f882ad..123695059 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -138,6 +138,7 @@ use thread_local::CachedThreadLocal; #[global_allocator] static ALLOC: Jemalloc = Jemalloc; +mod block; mod broadcasters; mod chat; mod chunk_entities; @@ -257,7 +258,7 @@ pub fn main() { info!("Shutting down"); info!("Saving chunks"); - shutdown::save_chunks(&*resources.get::(), &mut world); + shutdown::save_chunks(&*resources.get::(), &world); info!("Saving level.dat"); shutdown::save_level(&world); info!("Saving player data"); diff --git a/server/src/packet_handlers/inventory.rs b/server/src/packet_handlers/inventory.rs index d9e4415d1..89d78716a 100644 --- a/server/src/packet_handlers/inventory.rs +++ b/server/src/packet_handlers/inventory.rs @@ -43,7 +43,7 @@ pub fn handle_creative_inventory_action( // Cause item to be dropped let event = ItemDropEvent { slot: None, - stack: stack.clone(), + stack: *stack, player, }; game.on_item_drop(world, event); diff --git a/server/src/packet_handlers/placement.rs b/server/src/packet_handlers/placement.rs index 59b136b52..ff172d158 100644 --- a/server/src/packet_handlers/placement.rs +++ b/server/src/packet_handlers/placement.rs @@ -40,7 +40,6 @@ pub fn handle_player_block_placement( let placed_on = match game.block_at(packet.location) { Some(block) => block, None => { - drop(gamemode); game.disconnect(player, world, "attempted to place block in unloaded chunk"); continue; } @@ -54,7 +53,6 @@ pub fn handle_player_block_placement( _ => packet.location + packet.face.placement_offset(), }; - drop(gamemode); game.set_block_at(world, pos, block); let mut inventory = world.get_mut::(player); @@ -63,7 +61,6 @@ pub fn handle_player_block_placement( if gamemode == Gamemode::Survival { if item.amount == 0 { drop(inventory); - drop(gamemode); game.disconnect( player, world, diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index 193889328..ebbace650 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -7,6 +7,7 @@ use crate::physics::{block_impacted_by_ray, blocks_intersecting_bbox, AABBExt, P use feather_core::Position; use feather_core::{Block, BlockExt}; use fecs::{Entity, IntoQuery, Read, World, Write}; +use parking_lot::Mutex; /// Event triggered when an entity lands on the ground. #[derive(Debug, Clone)] @@ -18,13 +19,15 @@ pub struct EntityPhysicsLandEvent { /// System for updating all entities' positions and velocities /// each tick. #[system] -pub fn entity_physics(game: &Game, world: &mut World) { +pub fn entity_physics(game: &mut Game, world: &mut World) { // Go through entities and update their positions according // to their velocities. + let land_events = Mutex::new(vec![]); + let query = <(Write, Write, Read)>::query(); - query.par_for_each_mut( + query.par_entities_for_each_mut( world.inner_mut(), - |(mut position, mut velocity, physics)| { + |(entity, (mut position, mut velocity, physics))| { let mut pending_position = *position + velocity.0; // Check for blocks along path between old position and pending position. @@ -97,14 +100,12 @@ pub fn entity_physics(game: &Game, world: &mut World) { Some(block) => block.is_solid(), None => false, }; - /* TODO: land events if pending_position.on_ground && !position.on_ground { - land_events.lock().trigger(EntityPhysicsLandEvent { + land_events.lock().push(EntityPhysicsLandEvent { entity, pos: pending_position, }); } - */ // Apply drag and gravity. @@ -136,4 +137,9 @@ pub fn entity_physics(game: &Game, world: &mut World) { *position = pending_position; }, ); + + // Trigger land events. + for event in land_events.into_inner() { + game.on_entity_land(world, event); + } } diff --git a/server/src/save.rs b/server/src/save.rs index 29b5f5c08..3475b637f 100644 --- a/server/src/save.rs +++ b/server/src/save.rs @@ -96,7 +96,7 @@ pub fn save_chunk_at(game: &Game, world: &World, pos: ChunkPosition) { let entities = game .chunk_entities .entities_in_chunk(pos) - .into_iter() + .iter() .filter_map(|entity| { if let Some(serializer) = world.try_get::(*entity) { let accessor = world.entity(*entity).expect("entity does not exist"); diff --git a/server/src/systems.rs b/server/src/systems.rs index c923d9dda..f745e4a03 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -24,6 +24,7 @@ pub fn build_executor() -> Executor { .with(broadcasters::broadcast_keepalive) .with(broadcasters::broadcast_movement) .with(broadcasters::broadcast_velocity) + .with(entity::falling_block::spawn_falling_blocks) .with(save::chunk_save) .with(game::reset_bump_allocators) .with(game::increment_tick_count) diff --git a/server/src/util.rs b/server/src/util.rs index a5b6a0a18..30880917d 100644 --- a/server/src/util.rs +++ b/server/src/util.rs @@ -1,6 +1,7 @@ //! Assorted utility functions. -use feather_core::Position; +use arrayvec::ArrayVec; +use feather_core::{BlockPosition, Position}; use glm::DVec3; /// Calculates the relative move fields @@ -27,3 +28,15 @@ pub fn protocol_velocity(vel: DVec3) -> (i16, i16, i16) { (vel.z * 8000.0) as i16, ) } + +/// Returns the set of block positions adjacent to a given position. +pub fn adjacent_blocks(pos: BlockPosition) -> ArrayVec<[BlockPosition; 6]> { + ArrayVec::from([ + pos + BlockPosition::new(1, 0, 0), + pos + BlockPosition::new(0, 1, 0), + pos + BlockPosition::new(0, 0, 1), + pos + BlockPosition::new(-1, 0, 0), + pos + BlockPosition::new(0, -1, 0), + pos + BlockPosition::new(0, 0, -1), + ]) +} From 079d74bdd9ecdd28a0dc86cf94a4a7f9301d8b92 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Mar 2020 16:58:57 -0600 Subject: [PATCH 124/647] Fix level saving --- server/src/lib.rs | 2 +- server/src/shutdown.rs | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/server/src/lib.rs b/server/src/lib.rs index 123695059..707251f97 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -260,7 +260,7 @@ pub fn main() { info!("Saving chunks"); shutdown::save_chunks(&*resources.get::(), &world); info!("Saving level.dat"); - shutdown::save_level(&world); + shutdown::save_level(&mut *resources.get_mut::()); info!("Saving player data"); shutdown::save_player_data(&world); diff --git a/server/src/shutdown.rs b/server/src/shutdown.rs index 53aa9169d..81a5b0c4b 100644 --- a/server/src/shutdown.rs +++ b/server/src/shutdown.rs @@ -3,7 +3,10 @@ use crate::chunk_worker::Request; use crate::game::Game; use crate::save; use crossbeam::Sender; +use feather_core::level; +use feather_core::level::save_level_file; use fecs::World; +use std::fs::File; pub fn init(tx: Sender<()>) { ctrlc::set_handler(move || { @@ -24,6 +27,18 @@ pub fn save_chunks(game: &Game, world: &World) { while let Ok(_) = game.chunk_worker_handle.receiver.recv() {} } -pub fn save_level(_world: &World) {} +pub fn save_level(game: &mut Game) { + // Sync world time + level time + let time = game.time.world_age() as i64; + game.level.time = time; + + let level_path = format!("{}/{}", game.config.world.name, "level.dat"); + + let root = level::Root { + data: game.level.clone(), + }; + save_level_file(&root, &mut File::create(&level_path).unwrap()) + .expect("Failed to save level file"); +} pub fn save_player_data(_world: &World) {} From 6ed67232be6deec8980224f64f8ba269b6fedec8 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Mar 2020 17:02:11 -0600 Subject: [PATCH 125/647] Fix falling blocks not spawning when a sand block is placed above air --- server/src/block.rs | 2 ++ server/src/game.rs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/server/src/block.rs b/server/src/block.rs index eaac02234..ae34ff983 100644 --- a/server/src/block.rs +++ b/server/src/block.rs @@ -21,6 +21,7 @@ use crate::util; use feather_blocks::Block; use feather_core::BlockPosition; use fecs::{EntityBuilder, World}; +use std::iter; /// Marker component stating that an entity is a notify entity. #[derive(Copy, Clone, Debug)] @@ -58,6 +59,7 @@ fn notify_entity_for_block(block: Block, pos: BlockPosition) -> Option return false, }; + let result = self.chunk_map.set_block_at(pos, block); + self.on_block_update(world, pos, old_block, block); - self.chunk_map.set_block_at(pos, block) + result } /// Despawns an entity. This should be used instead of `World::despawn` From f60d283bfc6160e628e9f82a034697546eb78473 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Mar 2020 19:58:28 -0600 Subject: [PATCH 126/647] Implement loading of items from world save --- core/src/save/entity.rs | 60 ++++++++++++++++++++++++++++----- server/src/chunk_logic.rs | 9 +++-- server/src/chunk_worker.rs | 36 ++++++++++++++++++-- server/src/entity/item.rs | 35 ++++++++++++++++--- server/src/lib.rs | 1 + server/src/load.rs | 69 ++++++++++++++++++++++++++++++++++++++ server/src/save.rs | 2 +- 7 files changed, 193 insertions(+), 19 deletions(-) create mode 100644 server/src/load.rs diff --git a/core/src/save/entity.rs b/core/src/save/entity.rs index 9a926c349..e7d783882 100644 --- a/core/src/save/entity.rs +++ b/core/src/save/entity.rs @@ -1,6 +1,44 @@ use crate::{vec3, Item, Position, Vec3d}; use nbt::Value; use std::collections::HashMap; +use thiserror::Error; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum EntityDataKind { + Item, + Arrow, + Cow, + Pig, + Chicken, + Sheep, + Horse, + Llama, + Mooshroom, + Rabbit, + Squid, + Donkey, + Unknown, +} + +impl<'a> From<&'a EntityData> for EntityDataKind { + fn from(data: &'a EntityData) -> Self { + match data { + EntityData::Arrow(_) => EntityDataKind::Arrow, + EntityData::Item(_) => EntityDataKind::Item, + EntityData::Cow(_) => EntityDataKind::Cow, + EntityData::Pig(_) => EntityDataKind::Pig, + EntityData::Chicken(_) => EntityDataKind::Chicken, + EntityData::Sheep(_) => EntityDataKind::Sheep, + EntityData::Horse(_) => EntityDataKind::Horse, + EntityData::Llama(_) => EntityDataKind::Llama, + EntityData::Mooshroom(_) => EntityDataKind::Mooshroom, + EntityData::Rabbit(_) => EntityDataKind::Rabbit, + EntityData::Squid(_) => EntityDataKind::Squid, + EntityData::Donkey(_) => EntityDataKind::Donkey, + EntityData::Unknown => EntityDataKind::Unknown, + } + } +} #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "id")] @@ -109,6 +147,12 @@ impl BaseEntityData { } } +#[derive(Error, Debug)] +pub enum EntityLoadError { + #[error("missing position/rotation/velocity data")] + MissingData, +} + impl BaseEntityData { /// Creates a `BaseEntityData` from a position and velocity. pub fn new(pos: Position, velocity: Vec3d) -> Self { @@ -119,10 +163,10 @@ impl BaseEntityData { } } - /// Reads the position and rotation fields. If the fields are invalid, None is returned. - pub fn read_position(self: &BaseEntityData) -> Option { + /// Reads the position and rotation fields. If the fields are invalid, an error is returned. + pub fn read_position(self: &BaseEntityData) -> Result { if self.position.len() == 3 && self.rotation.len() == 2 { - Some(Position { + Ok(Position { x: self.position[0], y: self.position[1], z: self.position[2], @@ -131,16 +175,16 @@ impl BaseEntityData { on_ground: true, }) } else { - None + Err(EntityLoadError::MissingData) } } - /// Reads the velocity field. If the field is invalid, None is returned. - pub fn read_velocity(self: &BaseEntityData) -> Option { + /// Reads the velocity field. If the field is invalid, an error is returned. + pub fn read_velocity(self: &BaseEntityData) -> Result { if self.velocity.len() == 3 { - Some(vec3(self.velocity[0], self.velocity[1], self.velocity[2])) + Ok(vec3(self.velocity[0], self.velocity[1], self.velocity[2])) } else { - None + Err(EntityLoadError::MissingData) } } } diff --git a/server/src/chunk_logic.rs b/server/src/chunk_logic.rs index a065e02ac..3b2763095 100644 --- a/server/src/chunk_logic.rs +++ b/server/src/chunk_logic.rs @@ -47,12 +47,15 @@ pub fn chunk_load(game: &mut Game, world: &mut World) { while let Ok(reply) = game.chunk_worker_handle.receiver.try_recv() { if let chunk_worker::Reply::LoadedChunk(pos, result) = reply { match result { - Ok((chunk, _entities)) => { + Ok((chunk, entities)) => { game.chunk_map.insert(chunk); - game.on_chunk_load(world, pos); + entities.into_iter().for_each(|builder| { + let entity = builder.build().spawn_in(world); + game.on_entity_spawn(world, entity); + }); - // TODO: entities + game.on_chunk_load(world, pos); trace!("Loaded chunk at {:?}", pos); } diff --git a/server/src/chunk_worker.rs b/server/src/chunk_worker.rs index c420eac98..c3dad3fc2 100644 --- a/server/src/chunk_worker.rs +++ b/server/src/chunk_worker.rs @@ -4,21 +4,27 @@ //! //! If a chunk cannot be loaded, it is generated on the Rayon thread pool //! instead. +use crate::load::EntityLoader; use crate::worldgen::WorldGenerator; use crossbeam::channel::{Receiver, Sender}; use feather_core::entity::EntityData; use feather_core::region; use feather_core::region::{RegionHandle, RegionPosition}; use feather_core::{Chunk, ChunkPosition}; +use fecs::EntityBuilder; use hashbrown::HashMap; use parking_lot::RwLock; +use smallvec::SmallVec; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; #[allow(clippy::large_enum_variant)] pub enum Reply { - LoadedChunk(ChunkPosition, Result<(Chunk, Vec), Error>), + LoadedChunk( + ChunkPosition, + Result<(Chunk, SmallVec<[EntityBuilder; 4]>), Error>, + ), SavedChunk(ChunkPosition), } @@ -33,6 +39,7 @@ pub enum Request { pub enum Error { ChunkNotExist, LoadError(region::Error), + Other(anyhow::Error), } impl std::fmt::Display for Error { @@ -45,6 +52,10 @@ impl std::fmt::Display for Error { f.write_str("Error loading chunk: ")?; e.fmt(f)?; } + Error::Other(e) => { + f.write_str("Error loading chunk: ")?; + e.fmt(f)?; + } } Ok(()) @@ -82,6 +93,9 @@ struct ChunkWorker { /// World generator for new chunks. world_generator: Arc, + + /// State for loading entities. + entity_loader: EntityLoader, } /// Starts a chunk worker on a new thread. @@ -100,6 +114,7 @@ pub fn start( receiver: request_rx, open_regions: HashMap::new(), world_generator: world_gen, + entity_loader: EntityLoader::new(), }; // Without changing the stack size, @@ -145,6 +160,7 @@ fn load_chunk(worker: &mut ChunkWorker, pos: ChunkPosition) -> Option { &mut file.handle, &Arc::from(worker.sender.clone()), &worker.world_generator, + &worker.entity_loader, ) } @@ -153,11 +169,25 @@ fn load_chunk_from_handle( handle: &mut RegionHandle, sender: &Arc>, generator: &Arc, + entity_loader: &EntityLoader, ) -> Option { let result = handle.load_chunk(pos); match result { - Ok(chunk) => Some(Reply::LoadedChunk(pos, Ok(chunk))), + Ok((chunk, entities)) => { + let entities = entities + .into_iter() + .filter_map(|entity| entity_loader.load(entity)) + .collect::, anyhow::Error>>(); + + Some(Reply::LoadedChunk( + pos, + match entities { + Ok(entities) => Ok((chunk, entities)), + Err(e) => Err(Error::Other(e)), + }, + )) + } Err(e) => match e { region::Error::ChunkNotExist => { schedule_generate_new_chunk(sender, pos, generator); @@ -185,7 +215,7 @@ fn schedule_generate_new_chunk( /// Generates a new chunk synchronously, /// returning a Reply to send to a Sender. fn generate_new_chunk(pos: ChunkPosition, generator: &Arc) -> Reply { - Reply::LoadedChunk(pos, Ok((generator.generate_chunk(pos), vec![]))) + Reply::LoadedChunk(pos, Ok((generator.generate_chunk(pos), smallvec![]))) } /// Saves the chunk at the specified position. diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index d2f70a97b..0c95c3534 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -10,7 +10,9 @@ use crate::{entity, TPS}; use feather_core::entity::{BaseEntityData, EntityData, ItemData, ItemEntityData}; use feather_core::inventory::SlotIndex; use feather_core::network::packet::implementation::SpawnObject; -use feather_core::{EntityMetadata, ItemStack, Packet, Position, Vec3d, META_INDEX_ITEM_SLOT}; +use feather_core::{ + EntityMetadata, Item, ItemStack, Packet, Position, Vec3d, META_INDEX_ITEM_SLOT, +}; use fecs::{changed, component, Entity, EntityBuilder, EntityRef, IntoQuery, Read, World, Write}; use parking_lot::Mutex; use rand::Rng; @@ -57,6 +59,10 @@ pub struct CollectableAt(u64); #[derive(Debug)] struct IsRemoved(AtomicBool); +inventory::submit! { + crate::load::EntityLoaderRegistration::new(feather_core::entity::EntityDataKind::Item, &load) +} + /// System for spawning an item entity when /// an item is dropped. pub fn on_item_drop_spawn_item_entity(game: &mut Game, world: &mut World, event: &ItemDropEvent) { @@ -91,7 +97,7 @@ pub fn on_item_drop_spawn_item_entity(game: &mut Game, world: &mut World, event: drop(rng); - let entity = create(game, pos, event.stack) + let entity = create(pos, event.stack, game.tick_count + TPS) .with(Velocity(velocity)) .build() .spawn_in(world); @@ -196,9 +202,9 @@ pub fn item_collect(game: &mut Game, world: &mut World) { /// Returns an entity builder to create an item entity /// with the given stack and collectable tick. -pub fn create(game: &mut Game, pos: Position, stack: ItemStack) -> EntityBuilder { +pub fn create(pos: Position, stack: ItemStack, collectable_at: u64) -> EntityBuilder { let meta = EntityMetadata::entity_base().with(META_INDEX_ITEM_SLOT, Some(stack)); - let collectable_at = CollectableAt(game.time.world_age() + TPS); + let collectable_at = CollectableAt(collectable_at); entity::base(pos) .with(stack) @@ -255,3 +261,24 @@ fn serialize(game: &Game, accessor: &EntityRef) -> EntityData { }, }) } + +fn load(data: EntityData) -> anyhow::Result { + match data { + EntityData::Item(data) => { + let pos = data.entity.read_position()?; + let vel = data.entity.read_velocity()?; + + let stack = ItemStack::new( + Item::from_identifier(&data.item.item) + .ok_or_else(|| anyhow::anyhow!("invalid item {}", data.item.item))?, + data.item.count, + ); + + let collectable_at = data.pickup_delay; + + Ok(create(pos, stack, collectable_at as u64) + .with(Velocity(glm::vec3(vel.x, vel.y, vel.z)))) + } + _ => panic!("attempted to use item::load to load a non-item"), + } +} diff --git a/server/src/lib.rs b/server/src/lib.rs index 707251f97..66268e7f1 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -149,6 +149,7 @@ pub mod entity; pub mod game; pub mod io; mod join; +mod load; pub mod network; pub mod p_inventory; // Prefixed to avoid conflict with inventory crate pub mod packet_buffer; diff --git a/server/src/load.rs b/server/src/load.rs new file mode 100644 index 000000000..af6a9c82f --- /dev/null +++ b/server/src/load.rs @@ -0,0 +1,69 @@ +//! Implements the loading of entities. + +use ahash::AHashMap; +use feather_core::entity::{EntityData, EntityDataKind}; +use fecs::EntityBuilder; + +pub trait EntityLoaderFn: + Fn(EntityData) -> anyhow::Result + Send + Sync + 'static +{ +} + +impl EntityLoaderFn for F where + F: Fn(EntityData) -> anyhow::Result + Send + Sync + 'static +{ +} + +/// A registration for a function to convert an `EntityData` +/// to an `EntityBuilder` for spawning into the world. The +/// registration must provide the `EntityDataKind` it handles +/// to determine which `EntityData`s to pass to this function. +pub struct EntityLoaderRegistration { + /// The loader function. + pub f: &'static dyn EntityLoaderFn, + /// The kind of `EntityData` which this loader + /// function will accept. + pub kind: EntityDataKind, +} + +impl EntityLoaderRegistration { + pub fn new(kind: EntityDataKind, f: &'static dyn EntityLoaderFn) -> Self { + Self { f, kind } + } +} + +inventory::collect!(EntityLoaderRegistration); + +/// Stores state for loading entities. +pub struct EntityLoader { + /// Map from `EntityDataKind` to functions + /// to load entities of those kinds. + loaders: AHashMap, +} + +impl Default for EntityLoader { + fn default() -> Self { + Self::new() + } +} + +impl EntityLoader { + /// Initializes a new entity loader state. This function allocates. + pub fn new() -> Self { + let loaders = inventory::iter:: + .into_iter() + .map(|registration| (registration.kind, registration.f)) + .collect(); + Self { loaders } + } +} + +impl EntityLoader { + /// Converts an `EntityData` into an `EntityBuilder` + /// ready for spawning in a `World`. + pub fn load(&self, data: EntityData) -> Option> { + self.loaders + .get(&EntityDataKind::from(&data)) + .map(|loader| loader(data)) + } +} diff --git a/server/src/save.rs b/server/src/save.rs index 3475b637f..08f22d06f 100644 --- a/server/src/save.rs +++ b/server/src/save.rs @@ -88,7 +88,7 @@ pub fn save_chunk_at(game: &Game, world: &World, pos: ChunkPosition) { .chunk_handle_at(pos) .expect("chunk does not exist"); - if !chunk.write().check_modified() { + if !chunk.write().check_modified() && game.chunk_entities.entities_in_chunk(pos).is_empty() { return; } From c332f60081d7490ea0cb022bea754d7a32fe20ea Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 20 Mar 2020 23:29:05 -0600 Subject: [PATCH 127/647] Gracefully disconnect players on shutdown --- server/src/lib.rs | 2 ++ server/src/shutdown.rs | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/server/src/lib.rs b/server/src/lib.rs index 66268e7f1..ba2542acc 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -258,6 +258,8 @@ pub fn main() { info!("Shutting down"); + info!("Disconnecting players"); + shutdown::disconnect_players(&world); info!("Saving chunks"); shutdown::save_chunks(&*resources.get::(), &world); info!("Saving level.dat"); diff --git a/server/src/shutdown.rs b/server/src/shutdown.rs index 81a5b0c4b..4d6506394 100644 --- a/server/src/shutdown.rs +++ b/server/src/shutdown.rs @@ -1,11 +1,13 @@ //! Shutdown behavior. use crate::chunk_worker::Request; use crate::game::Game; +use crate::network::Network; use crate::save; use crossbeam::Sender; use feather_core::level; use feather_core::level::save_level_file; -use fecs::World; +use feather_core::network::packet::implementation::DisconnectPlay; +use fecs::{IntoQuery, Read, World}; use std::fs::File; pub fn init(tx: Sender<()>) { @@ -15,6 +17,19 @@ pub fn init(tx: Sender<()>) { .unwrap(); } +pub fn disconnect_players(world: &World) { + >::query().for_each(world.inner(), |network| { + let packet = DisconnectPlay { + reason: json!({ + "text": "Server closed" + }) + .to_string(), + }; + + network.send(packet); + }) +} + pub fn save_chunks(game: &Game, world: &World) { for chunk in game.chunk_map.iter_chunks() { let pos = chunk.read().position(); From cf47894334ade02e3937fcb6c031f2b7c701899c Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 21 Mar 2020 16:01:40 -0600 Subject: [PATCH 128/647] Box chunk heightmaps to prevent `Chunk` from being too large --- core/src/chunk.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/chunk.rs b/core/src/chunk.rs index f09907f09..da1dfd764 100644 --- a/core/src/chunk.rs +++ b/core/src/chunk.rs @@ -61,7 +61,7 @@ pub struct Chunk { /// call to `check_modified`(). modified: bool, - heightmaps: [HeightMap; CHUNK_WIDTH * CHUNK_WIDTH], + heightmaps: Box<[HeightMap]>, } #[derive(Clone, Copy, Default)] @@ -149,7 +149,7 @@ impl Default for Chunk { modified: true, sections, biomes: [Biome::Plains; SECTION_WIDTH * SECTION_WIDTH], - heightmaps: [HeightMap::default(); CHUNK_WIDTH * CHUNK_WIDTH], + heightmaps: vec![HeightMap::default(); CHUNK_WIDTH * CHUNK_WIDTH].into_boxed_slice(), } } } From 0c3c03a7ef73e0539c421553d49f7413f609d3aa Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sat, 21 Mar 2020 16:04:12 -0600 Subject: [PATCH 129/647] Fix entity save tests not compiling --- core/src/save/entity.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/save/entity.rs b/core/src/save/entity.rs index e7d783882..83a824b49 100644 --- a/core/src/save/entity.rs +++ b/core/src/save/entity.rs @@ -147,7 +147,7 @@ impl BaseEntityData { } } -#[derive(Error, Debug)] +#[derive(Error, Debug, PartialEq, Eq)] pub enum EntityLoadError { #[error("missing position/rotation/velocity data")] MissingData, @@ -320,7 +320,7 @@ mod tests { velocity: vec![6.0, 7.0, 8.0], }; let pos = data.read_position(); - assert!(pos.is_none()); + assert!(pos.is_err()); } #[test] @@ -331,7 +331,7 @@ mod tests { velocity: vec![6.0, 7.0, 8.0], }; let pos = data.read_position(); - assert!(pos.is_none()); + assert!(pos.is_err()); } #[test] @@ -356,7 +356,7 @@ mod tests { velocity: vec![6.0, 7.0], }; let vel = data.read_velocity(); - assert!(vel.is_none()); + assert!(vel.is_err()); } #[test] @@ -365,7 +365,7 @@ mod tests { let vel = vec3(0.0, 1.0, 2.0); let data = BaseEntityData::new(pos, vel); - assert_eq!(data.read_position(), Some(pos)); - assert_eq!(data.read_velocity(), Some(vel)); + assert_eq!(data.read_position(), Ok(pos)); + assert_eq!(data.read_velocity(), Ok(vel)); } } From f506e57c4e0736a1815ec450d674c11149984e72 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 22 Mar 2020 14:10:51 -0600 Subject: [PATCH 130/647] Reimplement block lighting with a couple changes Key changes made to the original implementation: * Moved light calculation onto a separate lighting thread * Reduced usage of unsafe * Turned some `collect::>()` into allocation-free iterators --- core/src/world.rs | 7 +- server/src/game.rs | 10 +- server/src/lib.rs | 4 + server/src/lighting.rs | 595 +++++++++++++++++++++++++++++++++++++++++ server/src/shutdown.rs | 12 +- 5 files changed, 625 insertions(+), 3 deletions(-) create mode 100644 server/src/lighting.rs diff --git a/core/src/world.rs b/core/src/world.rs index 160f26429..c200ebc80 100644 --- a/core/src/world.rs +++ b/core/src/world.rs @@ -256,6 +256,11 @@ impl BlockPosition { pub fn position(self) -> Position { self.into() } + + /// Converts into a `ChunkPosition`. + pub fn chunk(self) -> ChunkPosition { + self.into() + } } impl Add for BlockPosition { @@ -339,7 +344,7 @@ pub type ChunkMapInner = HashMap>>; /// of the world in parallel. Mutable access to this /// type is only required for inserting and removing /// chunks. -pub struct ChunkMap(ChunkMapInner); +pub struct ChunkMap(pub ChunkMapInner); impl ChunkMap { /// Creates a new chunk map with no chunks. diff --git a/server/src/game.rs b/server/src/game.rs index a234c0828..cb652de13 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -23,6 +23,10 @@ use crate::entity::item::{on_item_drop_spawn_item_entity, ItemCollectEvent, Item use crate::entity::Name; use crate::io::{NetworkIoManager, NewClientInfo, ServerToWorkerMessage}; use crate::join::{on_chunk_send_join_player, on_player_join_send_join_game}; +use crate::lighting::{ + on_block_update_notify_lighting_worker, on_chunk_load_notify_lighting_worker, + on_chunk_unload_notify_lighting_worker, LightingWorkerHandle, +}; use crate::network::Network; use crate::p_inventory::InventoryUpdateEvent; use crate::physics::EntityPhysicsLandEvent; @@ -87,6 +91,7 @@ pub struct Game { pub(super) rng: CachedThreadLocal>, pub time: Time, pub save_queue: SaveQueue, + pub lighting_worker_handle: LightingWorkerHandle, } impl Game { @@ -237,11 +242,12 @@ impl Game { &mut self, world: &mut World, pos: BlockPosition, - _old: Block, + old: Block, new: Block, ) { on_block_update_notify_adjacent(self, world, pos); on_block_update_broadcast(self, world, pos, new); + on_block_update_notify_lighting_worker(self, pos, old, new); } /// Called when an entity is despawned/removed. @@ -301,6 +307,7 @@ impl Game { /// Called when a chunk loads successfully. pub fn on_chunk_load(&mut self, world: &mut World, chunk: ChunkPosition) { + on_chunk_load_notify_lighting_worker(self, chunk); on_chunk_load_send_to_clients(self, world, chunk); on_chunk_load_queue_for_saving(self, chunk); } @@ -310,6 +317,7 @@ impl Game { /// This is called _before_ the chunk is removed from the chunk map. pub fn on_chunk_unload(&mut self, world: &mut World, chunk: ChunkPosition) { on_chunk_unload_save_chunk(self, world, chunk); + on_chunk_unload_notify_lighting_worker(self, chunk); } /// Called when a chunk fails to load. diff --git a/server/src/lib.rs b/server/src/lib.rs index ba2542acc..87722d7b4 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -149,6 +149,7 @@ pub mod entity; pub mod game; pub mod io; mod join; +mod lighting; mod load; pub mod network; pub mod p_inventory; // Prefixed to avoid conflict with inventory crate @@ -235,6 +236,7 @@ pub fn main() { rng: CachedThreadLocal::new(), time, save_queue: Default::default(), + lighting_worker_handle: lighting::start_worker(), }; let (executor, resources) = init_executor(game, packet_buffers); @@ -260,6 +262,8 @@ pub fn main() { info!("Disconnecting players"); shutdown::disconnect_players(&world); + info!("Shutting down workers"); + shutdown::shut_down_workers(&*resources.get::()); info!("Saving chunks"); shutdown::save_chunks(&*resources.get::(), &world); info!("Saving level.dat"); diff --git a/server/src/lighting.rs b/server/src/lighting.rs new file mode 100644 index 000000000..7aee01088 --- /dev/null +++ b/server/src/lighting.rs @@ -0,0 +1,595 @@ +//! An implementation of lighting, primarily based on 3D flood fill +//! algorithms. +//! +//! # Structure +//! Lighting is done on a separate _lighting worker thread_ which +//! stores its own copy of the chunk map. The server notifies +//! it when chunks are loaded and unloaded, and it can +//! request that it handle a lighting update, either for +//! an entire chunk or for a single block update. Since the lighting +//! worker has clones of the `Arc`s in which chunks are held, any +//! updates it makes to light data are visible to the server thread. +//! +//! # Algorithms: block light +//! For block light calculation, we define four types of block +//! updates for which to perform lighting: +//! +//! * Creation of a light-emitting block. We simply propagate +//! the light update using flood fill. +//! +//! * Removal of a light-emitting block. We first perform flood fill +//! and set any blocks which were previously affected by this block's +//! light to 0. Then, we recalculate lighting for light sources within +//! a range of 30 blocks based on algorithm #1. +//! +//! * Creation of an opaque, non-emitting block. We first set the created +//! block to air temporarily. We then query for nearby lights +//! within a range of 15 (the maximum distance travelled by light) and perform +//! algorithm #2 on them. Finally, we set the created block back to the correct +//! value and perform algorithm #1 on all lights. +//! +//! * Removal of an opaque, non-emitting block. In this case, +//! we set the new air block's light to the highest value of an +//! adjacent block minus 1. We then perform algorithm #1 on this new block. +//! +//! Each algorithm is implemented in a separate function, and `LightingSystem` +//! determines which to use based on the values of the block update event. +//! +//! If we are recalculating light for an entire chunk, e.g. when a chunk is generated, +//! we first zero out light, then find all light sources in the chunk and perform +//! algorithm #1 on them as if they had just been placed. + +use crate::physics::chunks_within_distance; +use ahash::{AHashMap, AHashSet}; +use arrayvec::ArrayVec; +use feather_blocks::{Block, BlockExt}; +use feather_core::world::{chunk_relative_pos, ChunkMap}; +use feather_core::{BlockPosition, Chunk, ChunkPosition}; + +use crate::game::Game; +use parking_lot::{RwLock, RwLockWriteGuard}; +use smallvec::SmallVec; +use std::collections::VecDeque; +use std::marker::PhantomData; +use std::sync::Arc; + +pub fn on_block_update_notify_lighting_worker( + game: &mut Game, + pos: BlockPosition, + old: Block, + new: Block, +) { + game.lighting_worker_handle + .tx + .send(Request::HandleBlockUpdate { pos, old, new }) + .expect("failed to notify lighting worker of block update"); +} + +pub fn on_chunk_load_notify_lighting_worker(game: &mut Game, pos: ChunkPosition) { + let handle = game + .chunk_map + .chunk_handle_at(pos) + .expect("chunk load event triggered, but chunk not in chunk map"); + + game.lighting_worker_handle + .tx + .send(Request::LoadChunk { pos, handle }) + .expect("failed to notify lighting worker of chunk load"); +} + +pub fn on_chunk_unload_notify_lighting_worker(game: &mut Game, pos: ChunkPosition) { + game.lighting_worker_handle + .tx + .send(Request::UnloadChunk { pos }) + .expect("failed to notify lighting worker of chunk unload"); +} + +/// A request sent to the lighting worker. +pub enum Request { + /// Notifies the worker of a new loaded chunk. + LoadChunk { + pos: ChunkPosition, + handle: Arc>, + }, + /// Notifies the worker that a chunk was unloaded. + UnloadChunk { pos: ChunkPosition }, + /// Requests that the lighting worker shuts down. + ShutDown, + /// Requests that the lighting worker handles a block update. + HandleBlockUpdate { + /// The position of the block which was updated. + pos: BlockPosition, + /// The old value of the block. + old: Block, + /// The new value of the block. + new: Block, + }, +} + +/// Handle to the lighting worker. +#[derive(Clone)] +pub struct LightingWorkerHandle { + pub tx: crossbeam::Sender, + pub shutdown_rx: crossbeam::Receiver<()>, +} + +/// Starts the lighting worker, returning a handle to it. +pub fn start_worker() -> LightingWorkerHandle { + let (tx, rx) = crossbeam::bounded(512); + let (shutdown_tx, shutdown_rx) = crossbeam::bounded(1); + + std::thread::spawn(move || run_worker(rx, shutdown_tx)); + + LightingWorkerHandle { tx, shutdown_rx } +} + +/// Cache storing the light sources in each chunk. +#[derive(Debug, Default)] +struct ChunkLights(AHashMap>); + +impl ChunkLights { + /// Returns an iterator over light sources within the given radius + /// of a block position. + pub fn lights_within_radius<'a>( + &'a self, + pos: BlockPosition, + radius: u8, + ) -> impl Iterator + 'a + Clone { + let radius = f64::from(radius); + let chunks = chunks_within_distance(pos.position(), glm::vec3(radius, radius, radius)); + + chunks + .into_iter() + .flat_map(move |chunk| { + self.0 + .get(&chunk) + .map(|vec| vec.as_slice()) + .unwrap_or(&[]) + .iter() + }) + .copied() + } +} + +/// Internal worker state. +struct Worker { + /// Receiver for new requests. + rx: crossbeam::Receiver, + /// The worker's own copy of the chunk map, with `Arc`s + /// being cloned from the server thread's "official" chunk map. + chunk_map: ChunkMap, + /// Caches the light sources in each chunk. + lights: ChunkLights, + /// Whether the worker should shut down. + should_shut_down: bool, +} + +fn run_worker(rx: crossbeam::Receiver, shutdown_tx: crossbeam::Sender<()>) { + let mut worker = Worker { + rx, + chunk_map: Default::default(), + lights: Default::default(), + should_shut_down: false, + }; + + info!("Lighting worker started"); + while let Ok(request) = worker.rx.recv() { + handle_request(&mut worker, request); + + if worker.should_shut_down { + break; + } + } + + info!("Lighting worker shutting down"); + let _ = shutdown_tx.try_send(()); +} + +fn handle_request(worker: &mut Worker, request: Request) { + match request { + Request::ShutDown => worker.should_shut_down = true, + Request::LoadChunk { pos, handle } => load_chunk(worker, pos, handle), + Request::UnloadChunk { pos } => unload_chunk(worker, pos), + Request::HandleBlockUpdate { pos, old, new } => handle_block_update(worker, pos, old, new), + } +} + +fn load_chunk(worker: &mut Worker, pos: ChunkPosition, handle: Arc>) { + worker + .lights + .0 + .insert(pos, lights_in_chunk(&*handle.read()).collect()); + worker.chunk_map.0.insert(pos, handle); +} + +fn lights_in_chunk<'a>(chunk: &'a Chunk) -> impl Iterator + 'a { + (0..16) + .flat_map(|x| (0..256).map(move |y| (x, y))) + .flat_map(|(x, y)| (0..16).map(move |z| (x, y, z))) + .filter_map(move |(x, y, z)| { + let block = chunk.block_at(x, y, z); + + if block.light_emission() > 0 { + Some(BlockPosition::new(x as i32, y as i32, z as i32)) + } else { + None + } + }) +} + +fn unload_chunk(worker: &mut Worker, pos: ChunkPosition) { + worker.lights.0.remove(&pos); + worker.chunk_map.0.remove(&pos); +} + +/// Lighter context, used to cache things during +/// a lighting iteration. +struct Context<'a> { + /// Reference to the current cached chunk. + /// This is used to avoid repetitive hashmap + /// accesses in the chunk map when groups + /// of clustered blocks are queried for. + current_chunk: RwLockWriteGuard<'static, Chunk>, + + chunk_map: *const ChunkMap, + + _phantom: PhantomData<&'a ()>, +} + +impl<'a> Context<'a> { + fn new(chunk_map: &'a ChunkMap, start_chunk: ChunkPosition) -> Option { + Some(Self { + current_chunk: unsafe { std::mem::transmute(chunk_map.chunk_at_mut(start_chunk)?) }, + chunk_map: chunk_map as *const _, + _phantom: PhantomData, + }) + } + + fn chunk_at_mut(&mut self, pos: ChunkPosition) -> Option<&mut Chunk> { + if pos == self.current_chunk.position() { + Some(&mut *self.current_chunk) + } else { + self.current_chunk = unsafe { &*self.chunk_map }.chunk_at_mut(pos)?; + Some(&mut *self.current_chunk) + } + } + + fn block_light_at(&mut self, pos: BlockPosition) -> u8 { + match self.chunk_at_mut(pos.chunk()) { + Some(chunk) => { + let (x, y, z) = chunk_relative_pos(pos); + chunk.block_light_at(x, y, z) + } + None => 0, // TODO: graceful handling of missing chunk information? + } + } + + fn set_block_light_at(&mut self, pos: BlockPosition, value: u8) { + if let Some(chunk) = self.chunk_at_mut(pos.chunk()) { + let (x, y, z) = chunk_relative_pos(pos); + chunk.set_block_light_at(x, y, z, value); + } + } + + fn block_at(&mut self, pos: BlockPosition) -> Block { + match self.chunk_at_mut(pos.chunk()) { + Some(chunk) => { + let (x, y, z) = chunk_relative_pos(pos); + chunk.block_at(x, y, z) + } + None => Block::Air, + } + } + + fn set_block_at(&mut self, pos: BlockPosition, block: Block) { + if let Some(chunk) = self.chunk_at_mut(pos.chunk()) { + let (x, y, z) = chunk_relative_pos(pos); + chunk.set_block_at(x, y, z, block); + } + } +} + +const MAX_TRAVEL_DISTANCE: u8 = 15; + +fn handle_block_update(worker: &mut Worker, pos: BlockPosition, old: Block, new: Block) { + let mut ctx = match Context::new(&worker.chunk_map, pos.chunk()) { + Some(ctx) => ctx, + None => return, // Unloaded chunk + }; + + // Determine which algorithm to use. + if old.light_emission() < new.light_emission() { + ctx.set_block_light_at(pos, new.light_emission()); + emitting_creation(&mut ctx, pos); + } else if new.light_emission() == 0 && old.light_emission() > 0 { + ctx.set_block_light_at(pos, 0); + emitting_removal(&mut ctx, &worker.lights, pos, old); + } else if old.is_opaque() && !new.is_opaque() { + opaque_non_emitting_removal(&mut ctx, pos); + } else { + opaque_non_emitting_creation(&mut ctx, &worker.lights, pos, new); + } + + // Update `ChunkLights`. + if old.light_emission() != new.light_emission() { + if new.light_emission() == 0 { + worker + .lights + .0 + .entry(pos.chunk()) + .or_default() + .retain(|p| *p != pos); + } else if old.light_emission() == 0 { + worker.lights.0.entry(pos.chunk()).or_default().push(pos); + } + } +} + +/// Algorithm #1, as described in the module-level docs. +fn emitting_creation(context: &mut Context, position: BlockPosition) { + let emission = context.block_light_at(position); + // Perform flood fill starting from `position`. + // For each block, set the light value to the maximum light + // value of any adjacent block minus 1. + flood_fill(context, position, emission, |ctx, pos| { + let light = light_value_for_block(ctx, pos); + ctx.set_block_light_at(pos, light); + }); +} + +/// Algorithm #2, as described in the module-level docs. +fn emitting_removal( + context: &mut Context, + chunk_lights: &ChunkLights, + position: BlockPosition, + old_block: Block, +) { + // Perform flood fill and set all blocks affected by the old light to 0 light. + flood_fill(context, position, old_block.light_emission(), |ctx, pos| { + ctx.set_block_light_at(pos, 0); + }); + + // For all lights which could have affected the blocks we just set to 0, + // recalculate lighting using algorithm #1. + let nearby_lights = chunk_lights.lights_within_radius(position, MAX_TRAVEL_DISTANCE * 2); + + for light in nearby_lights { + if light != position { + emitting_creation(context, light); + } + } +} + +/// Algorithm #3, as described in the module-level docs. +fn opaque_non_emitting_creation( + context: &mut Context, + chunk_lights: &ChunkLights, + position: BlockPosition, + new_block: Block, +) { + // Re-calculate all lights that could have affected this block. + // We ensure that all areas are correctly set to dark by first + // faking that the block was never created. + context.set_block_at(position, Block::Air); + + let nearby_lights = chunk_lights.lights_within_radius(position, MAX_TRAVEL_DISTANCE); + + for light in nearby_lights { + let block = context.block_at(light); + emitting_removal(context, chunk_lights, light, block); + } + + // Set block back to correct value. + context.set_block_at(position, new_block); + + let nearby_lights = chunk_lights.lights_within_radius(position, MAX_TRAVEL_DISTANCE); + + // Recalculate nearby lights. + for light in nearby_lights { + emitting_creation(context, light); + } +} + +/// Algorithm #4, as described in the module-level docs. +fn opaque_non_emitting_removal(context: &mut Context, position: BlockPosition) { + let value = light_value_for_block(context, position); + + context.set_block_light_at(position, value); + + // Propagate new light value for this block, as if it were a new light source. + if value > 0 { + emitting_creation(context, position); + } +} + +/// Returns the light value for the block at `position`, +/// equivalent to the maximum light value of an adjacent block +/// minus 1. +fn light_value_for_block(context: &mut Context, position: BlockPosition) -> u8 { + // Find highest light value of 6 adjacent blocks. + let adjacent = adjacent_blocks(position); + + let mut value = adjacent + .into_iter() + .map(|pos| context.block_light_at(pos)) + .max() + .unwrap(); + + if value > 0 { + value -= 1; + } + + value +} + +/// Performs flood fill starting at `start` and travelling up +/// to `max_dist` blocks. +/// +/// For each block iterated over, the provided closure will be invoked. +/// No block will be iterated more than once. +fn flood_fill(context: &mut Context, start: BlockPosition, max_dist: u8, mut f: F) +where + F: FnMut(&mut Context, BlockPosition), +{ + // TODO: bump allocate these data structures. + // Don't iterate over same block more than once + let mut touched = AHashSet::with_capacity_and_hasher(64, ahash::RandomState::new()); + touched.insert(start); + + // We use a queue-based algorithm rather than a recursive + // one. + let mut queue = VecDeque::with_capacity(64); + + queue.push_back(start); + + while let Some(pos) = queue.pop_front() { + let blocks = adjacent_blocks(pos); + + for pos in blocks { + if pos.manhattan_distance(start) > max_dist as i32 { + // Finished + return; + } + + // Skip if we already went over this block + if !touched.insert(pos) { + continue; + } + + let block = context.block_at(pos); + if block.is_opaque() { + continue; // Stop iterating + } + + // Call closure + f(context, pos); + + // Add block to queue + queue.push_back(pos); + } + } +} + +/// Returns the up to six adjacent blocks to a given block position. +fn adjacent_blocks(to: BlockPosition) -> ArrayVec<[BlockPosition; 6]> { + let offsets = [ + (-1, 0, 0), + (1, 0, 0), + (0, -1, 0), + (0, 1, 0), + (0, 0, -1), + (0, 0, 1), + ]; + offsets + .iter() + .map(|(x, y, z)| BlockPosition::new(to.x + *x, to.y + *y, to.z + *z)) + .filter(|pos| pos.y >= 0 && pos.y <= 256) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_context() { + let mut chunk_map = ChunkMap::new(); + + let pos = ChunkPosition::new(0, 0); + chunk_map.insert(Chunk::new(pos)); + let pos2 = ChunkPosition::new(0, 1); + chunk_map.insert(Chunk::new(pos2)); + + let mut ctx = Context::new(&chunk_map, pos).unwrap(); + + assert_eq!(ctx.chunk_at_mut(pos).unwrap().position(), pos); + assert_eq!(ctx.chunk_at_mut(pos2).unwrap().position(), pos2); + assert_eq!(ctx.chunk_at_mut(pos).unwrap().position(), pos); + } + + #[test] + fn test_emitting_creation() { + let chunk_map = chunk_map(); + let mut ctx = Context::new(&chunk_map, ChunkPosition::new(0, 0)).unwrap(); + + let pos = BlockPosition::new(0, 100, 0); + ctx.set_block_at(pos, Block::Glowstone); + ctx.set_block_light_at(pos, Block::Glowstone.light_emission()); + + emitting_creation(&mut ctx, pos); + + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 99, 0)), 14); + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 99, 1)), 13); + } + + #[test] + fn test_opaque_non_emitting_removal() { + let chunk_map = chunk_map(); + let mut ctx = Context::new(&chunk_map, ChunkPosition::new(0, 0)).unwrap(); + + ctx.set_block_light_at(BlockPosition::new(0, 0, 0), 10); + ctx.set_block_light_at(BlockPosition::new(0, 2, 0), 9); + ctx.set_block_light_at(BlockPosition::new(1, 1, 0), 8); + ctx.set_block_light_at(BlockPosition::new(-1, 1, 0), 11); + ctx.set_block_light_at(BlockPosition::new(0, 1, 1), 0); + ctx.set_block_light_at(BlockPosition::new(0, 1, -1), 12); + ctx.set_block_light_at(BlockPosition::new(0, 1, 0), 15); + + opaque_non_emitting_removal(&mut ctx, BlockPosition::new(0, 1, 0)); + + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 1, 0)), 11); + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 1, 1)), 10); + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 1, 2)), 9); + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 1, 3)), 8); + assert_eq!(ctx.block_light_at(BlockPosition::new(0, 1, 4)), 7); + // ... + } + + #[test] + fn test_flood_fill() { + let chunk_map = chunk_map(); + let mut ctx = Context::new(&chunk_map, ChunkPosition::new(0, 0)).unwrap(); + + let mut count = 0; + + flood_fill(&mut ctx, BlockPosition::new(100, 100, 100), 1, |_, _| { + count += 1 + }); + + assert_eq!(count, 6); + } + + #[test] + fn test_chunk_lights() { + let mut chunk_lights = ChunkLights::default(); + chunk_lights.0.insert( + ChunkPosition::new(0, 0), + smallvec![BlockPosition::new(0, 0, 0)], + ); + chunk_lights.0.insert( + ChunkPosition::new(1, 0), + smallvec![BlockPosition::new(16, 0, 0)], + ); + + assert_eq!( + chunk_lights + .lights_within_radius(BlockPosition::new(0, 0, 0), 16) + .collect::>() + .as_slice(), + &[BlockPosition::new(0, 0, 0), BlockPosition::new(16, 0, 0)] + ); + } + + fn chunk_map() -> ChunkMap { + let mut chunk_map = ChunkMap::new(); + + for x in -1..=1 { + for z in -1..=1 { + let pos = ChunkPosition::new(x, z); + chunk_map.insert(Chunk::new(pos)); + } + } + + chunk_map + } +} diff --git a/server/src/shutdown.rs b/server/src/shutdown.rs index 4d6506394..d7c3e1f9f 100644 --- a/server/src/shutdown.rs +++ b/server/src/shutdown.rs @@ -2,7 +2,7 @@ use crate::chunk_worker::Request; use crate::game::Game; use crate::network::Network; -use crate::save; +use crate::{lighting, save}; use crossbeam::Sender; use feather_core::level; use feather_core::level::save_level_file; @@ -57,3 +57,13 @@ pub fn save_level(game: &mut Game) { } pub fn save_player_data(_world: &World) {} + +pub fn shut_down_workers(game: &Game) { + let _ = game + .lighting_worker_handle + .tx + .send(lighting::Request::ShutDown); + + // wait for disconnect + let _ = game.lighting_worker_handle.shutdown_rx.recv(); +} From 65306c25a264b7eda8dbbde6151f0b4803633eda Mon Sep 17 00:00:00 2001 From: Redrield Date: Sun, 22 Mar 2020 16:17:41 -0400 Subject: [PATCH 131/647] Add support for shooting arrows (#185) > Physics for the entity is still a bit weird once it's spawned, however, this code successfully handles spawning the entity when the bow is released. --- server/Cargo.toml | 1 + server/src/entity/arrow.rs | 59 +++++++++++++ server/src/entity/mod.rs | 1 + server/src/packet_handlers/digging.rs | 117 ++++++++++++++----------- server/src/packet_handlers/mod.rs | 2 + server/src/packet_handlers/use_item.rs | 55 ++++++++++++ server/src/physics/math.rs | 30 +++++++ server/src/player/mod.rs | 5 ++ server/src/systems.rs | 1 + 9 files changed, 221 insertions(+), 50 deletions(-) create mode 100644 server/src/entity/arrow.rs create mode 100644 server/src/packet_handlers/use_item.rs diff --git a/server/Cargo.toml b/server/Cargo.toml index b533f6997..c0f4574ab 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -76,6 +76,7 @@ hematite-nbt = "0.4" # RNGs rand = "0.7" +rand_distr = "0.2.2" rand_xorshift = "0.2" # Other diff --git a/server/src/entity/arrow.rs b/server/src/entity/arrow.rs new file mode 100644 index 000000000..814b50ad4 --- /dev/null +++ b/server/src/entity/arrow.rs @@ -0,0 +1,59 @@ +use crate::entity; +use crate::entity::{ComponentSerializer, EntityId, SpawnPacketCreator, Velocity}; +use crate::game::Game; +use crate::physics::PhysicsBuilder; +use crate::util::{degrees_to_stops, protocol_velocity}; +use feather_core::entity::{ArrowEntityData, BaseEntityData, EntityData}; +use feather_core::network::packet::implementation::SpawnObject; +use feather_core::{Packet, Position, Vec3d}; +use fecs::{EntityRef, World, EntityBuilder}; +use uuid::Uuid; + +pub fn create(position: Position, velocity: glm::DVec3) -> EntityBuilder { + entity::base(position) + .with(Velocity(velocity)) + .with(SpawnPacketCreator(&create_spawn_packet)) + .with(ComponentSerializer(&serialize)) + .with( + PhysicsBuilder::new() + .bbox(0.5, 0.5, 0.5) + .gravity(-0.05) + .slip_multiplier(0.0) + .drag(0.99) + .build(), + ) +} + +fn create_spawn_packet(accessor: &EntityRef) -> Box { + let position = *accessor.get::(); + let velocity = *accessor.get::(); + let entity_id = accessor.get::().0; + + let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocity.0); + + let packet = SpawnObject { + entity_id, + object_uuid: Uuid::new_v4(), + ty: 60, // Type 60 for arrow projectile + x: position.x, + y: position.y, + z: position.z, + pitch: degrees_to_stops(position.pitch), + yaw: degrees_to_stops(position.yaw), + data: entity_id + 1, + velocity_x, + velocity_y, + velocity_z, + }; + + Box::new(packet) +} + +fn serialize(_game: &Game, accessor: &EntityRef) -> EntityData { + let vel = accessor.get::(); + + EntityData::Arrow(ArrowEntityData { + entity: BaseEntityData::new(*accessor.get::(), Vec3d::new(vel.x, vel.y, vel.z)), + critical: 0, // TODO + }) +} diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index e5a3aba9b..97f2ba185 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -4,6 +4,7 @@ //! not here. pub mod falling_block; +pub mod arrow; pub mod item; use crate::game::Game; diff --git a/server/src/packet_handlers/digging.rs b/server/src/packet_handlers/digging.rs index 9667a8174..cf83e2105 100644 --- a/server/src/packet_handlers/digging.rs +++ b/server/src/packet_handlers/digging.rs @@ -4,11 +4,15 @@ //! for actions mostly unrelated to digging including eating, shooting bows, //! swapping items out to the offhand, and dropping items. +use crate::entity::arrow; use crate::game::Game; use crate::p_inventory::EntityInventory; use crate::packet_buffer::PacketBuffers; +use crate::physics::{charge_from_ticks_held, compute_projectile_velocity}; +use crate::player::{ItemTimedUse, PLAYER_EYE_HEIGHT}; +use feather_core::inventory::{SlotIndex, SLOT_HOTBAR_OFFSET, SLOT_OFFHAND}; use feather_core::network::packet::implementation::{PlayerDigging, PlayerDiggingStatus}; -use feather_core::{Block, Gamemode, Item}; +use feather_core::{Block, Gamemode, Item, ItemStack, Position}; use fecs::{Entity, World}; use std::sync::Arc; @@ -36,17 +40,8 @@ pub fn handle_player_digging( item_drops, &mut inventory, ),*/ - /* - ConsumeItem => handle_consume_item( - packet, - players.get(player).unwrap(), - player, - inventories.get_mut(player).unwrap(), - &mut inventory_updates, - positions.get(player).unwrap().current, - &mut shoot_arrow_events, - ), - */ + ConsumeItem => handle_consume_item(game, world, player, packet), + status => warn!("Unhandled Player Digging status {:?}", status), } } @@ -151,46 +146,28 @@ fn handle_drop_item_stack( } */ -/* /// Handles food consumption and shooting arrows. -fn handle_consume_item( - packet: &PlayerDigging, - player: &PlayerComponent, - entity: Entity, - inventory: &mut InventoryComponent, - inventory_updates: &mut EventChannel, - position: Position, - shoot_arrow_events: &mut EventChannel, -) { +fn handle_consume_item(game: &mut Game, world: &mut World, player: Entity, packet: PlayerDigging) { assert_eq!(packet.status, PlayerDiggingStatus::ConsumeItem); // TODO: Fallback to off-hand if main-hand is not a consumable + let inventory = world.get::(player); let used_item = inventory.item_in_main_hand(); if let Some(item) = used_item { if item.ty == Item::Bow { - handle_shoot_bow( - player, - entity, - inventory, - inventory_updates, - position, - shoot_arrow_events, - ); + drop(inventory); + handle_shoot_bow(game, world, player); } // TODO: Food, potions } } -fn handle_shoot_bow( - player: &PlayerComponent, - entity: Entity, - inventory: &mut InventoryComponent, - inventory_updates: &mut EventChannel, - position: Position, - shoot_arrow_events: &mut EventChannel, -) { +fn handle_shoot_bow(game: &mut Game, world: &mut World, player: Entity) { + let inventory = world.get::(player); let arrow_to_consume: Option<(SlotIndex, ItemStack)> = find_arrow(&inventory); + // Unnecessary until more gamemodes are supported + /* if player.gamemode == Gamemode::Survival || player.gamemode == Gamemode::Adventure { // If no arrow was found, don't shoot let arrow_to_consume = arrow_to_consume.clone(); @@ -210,33 +187,74 @@ fn handle_shoot_bow( player: entity, }); } + */ + + drop(inventory); // Inventory no longer used. - let arrow_type: Item = match arrow_to_consume { + let _arrow_type: Item = match arrow_to_consume { None => Item::Arrow, // Default to generic arrow in creative mode with none in inventory Some((_, arrow_stack)) => arrow_stack.ty, }; - shoot_arrow_events.single_write(ShootArrowEvent { - shooter: Some(entity), - position, - arrow_type, - critical: false, // TODO: Determine critical based on how long bow was pulled back - }); + let timed_use = world.try_get::(player); + + // Spam clicking can lead to a scenario where this system is called before the UseItem system adds the component + // In that case just return. + if timed_use.is_none() { + return; + } + + let timed_use = timed_use.unwrap(); + + let mut time_held = game.tick_count - timed_use.tick_start; + + if time_held > 20 { + time_held = 20; + } + + let charge_force = charge_from_ticks_held(time_held as u32); + trace!("Held for {} ticks. Force of {}", time_held, charge_force); + + let init_position = *world.get::(player) + glm::vec3(0.0, PLAYER_EYE_HEIGHT, 0.0); + + let direction = init_position.direction(); + + let arrow_velocity = compute_projectile_velocity( + glm::vec3(direction.x, direction.y, direction.z), + charge_force as f64, + 0.0, + &mut *game.rng(), + ); + trace!( + "Computed exit velocity: {}. Velocity is norm {}", + arrow_velocity, + arrow_velocity.norm() + ); + + drop(timed_use); + + world.remove::(player).unwrap(); + + trace!("Spawning arrow entity."); + let entity = arrow::create(init_position, arrow_velocity) + .build() + .spawn_in(world); + game.on_entity_spawn(world, entity); } -fn find_arrow(inventory: &InventoryComponent) -> Option<(SlotIndex, ItemStack)> { +fn find_arrow(inventory: &EntityInventory) -> Option<(SlotIndex, ItemStack)> { // Order of priority is: off-hand, hotbar (0 to 8), rest of inventory if let Some(offhand) = inventory.item_at(SLOT_OFFHAND) { if is_arrow_item(offhand.ty) { - return Some((SLOT_OFFHAND, offhand.clone())); + return Some((SLOT_OFFHAND, *offhand)); } } for hotbar_slot in 0..9 { if let Some(hotbar_stack) = inventory.item_at(SLOT_HOTBAR_OFFSET + hotbar_slot) { if is_arrow_item(hotbar_stack.ty) { - return Some((SLOT_HOTBAR_OFFSET + hotbar_slot, hotbar_stack.clone())); + return Some((SLOT_HOTBAR_OFFSET + hotbar_slot, *hotbar_stack)); } } } @@ -244,7 +262,7 @@ fn find_arrow(inventory: &InventoryComponent) -> Option<(SlotIndex, ItemStack)> for inv_slot in 9..=35 { if let Some(inv_stack) = inventory.item_at(inv_slot) { if is_arrow_item(inv_stack.ty) { - return Some((inv_slot, inv_stack.clone())); + return Some((inv_slot, *inv_stack)); } } } @@ -257,4 +275,3 @@ fn is_arrow_item(item: Item) -> bool { _ => false, } } -*/ diff --git a/server/src/packet_handlers/mod.rs b/server/src/packet_handlers/mod.rs index 9dc251937..88bcbd1e4 100644 --- a/server/src/packet_handlers/mod.rs +++ b/server/src/packet_handlers/mod.rs @@ -6,6 +6,7 @@ mod digging; mod inventory; mod movement; mod placement; +mod use_item; pub use self::inventory::{handle_creative_inventory_action, handle_held_item_change}; pub use animation::handle_animation; @@ -13,3 +14,4 @@ pub use chat::handle_chat; pub use digging::handle_player_digging; pub use movement::handle_movement_packets; pub use placement::handle_player_block_placement; +pub use use_item::handle_player_use_item; diff --git a/server/src/packet_handlers/use_item.rs b/server/src/packet_handlers/use_item.rs new file mode 100644 index 000000000..afbf807b7 --- /dev/null +++ b/server/src/packet_handlers/use_item.rs @@ -0,0 +1,55 @@ +use crate::entity::Name; +use crate::game::Game; +use crate::p_inventory::EntityInventory; +use crate::packet_buffer::PacketBuffers; +use crate::player::ItemTimedUse; +use feather_core::network::packet::implementation::UseItem; +use feather_core::{Hand, Item}; +use fecs::{Entity, World}; +use std::sync::Arc; + +#[system] +pub fn handle_player_use_item( + game: &mut Game, + world: &mut World, + packet_buffers: &Arc, +) { + let packets = packet_buffers.received::(); + + for (player, packet) in packets { + handle_use_item(game, world, player, packet); + } +} + +fn handle_use_item(game: &mut Game, world: &mut World, player: Entity, packet: UseItem) { + let hand = match packet.hand { + 0 => Hand::Main, + _ => Hand::Off, + }; + + if hand != Hand::Main { + return; + } + + let item_in_main_hand = world + .get::(player) + .item_in_main_hand() + .copied(); + + if let Some(item_in_main_hand) = item_in_main_hand { + if item_in_main_hand.ty != Item::Bow { + //TODO: Handle other used items + return; + } + world + .add( + player, + ItemTimedUse { + tick_start: game.tick_count, + }, + ) + .unwrap(); + let player_name = world.get::(player); + trace!("Added ItemTimedUse to player {}.", player_name.0); + } +} diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index 6dc7c0b00..fcc082139 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -16,6 +16,8 @@ use ncollide3d::bounding_volume::AABB; use ncollide3d::query; use ncollide3d::query::{Ray, RayCast}; use ncollide3d::shape::{Compound, Cuboid, ShapeHandle}; +use rand::Rng; +use rand_distr::{Distribution, StandardNormal}; use smallvec::SmallVec; use std::cmp::Ordering; use std::f64::INFINITY; @@ -226,6 +228,34 @@ pub fn block_impacted_by_ray( None } +pub fn charge_from_ticks_held(ticks: u32) -> f32 { + let ticks = ticks as f32; + + let mut unbounded_force = (ticks * (ticks + 40.0)) / 400.0; + + if unbounded_force > 3.0 { + unbounded_force = 3.0 + } + + unbounded_force +} + +pub fn compute_projectile_velocity( + direction: DVec3, + charge: f64, + inaccuracy: f64, + rng: &mut impl Rng, +) -> DVec3 { + let gaussian = vec3( + StandardNormal.sample(rng), + StandardNormal.sample(rng), + StandardNormal.sample(rng), + ); + let inaccuracy = vec3(inaccuracy, inaccuracy, inaccuracy).component_mul(&gaussian) * 0.0075; + + (direction + inaccuracy) * charge +} + /// Returns all entities within the given distance of the given /// position. /// diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index 5f547849f..f09e422e4 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -24,6 +24,11 @@ pub struct ProfileProperties(pub Vec); #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Player; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ItemTimedUse { + pub tick_start: u64, +} + /// Creates a new player from the given `NewClientInfo`. /// /// This function also triggers the `PlayerJoinEvent` for this player. diff --git a/server/src/systems.rs b/server/src/systems.rs index f745e4a03..098e30611 100644 --- a/server/src/systems.rs +++ b/server/src/systems.rs @@ -13,6 +13,7 @@ pub fn build_executor() -> Executor { .with(packet_handlers::handle_held_item_change) .with(packet_handlers::handle_animation) .with(packet_handlers::handle_player_block_placement) + .with(packet_handlers::handle_player_use_item) .with(packet_handlers::handle_player_digging) .with(packet_handlers::handle_chat) .with(weather::handle_weather) From 19c15f862ce5dca22476b0463c148b31902b923d Mon Sep 17 00:00:00 2001 From: caelunshun Date: Sun, 22 Mar 2020 14:28:25 -0600 Subject: [PATCH 132/647] Run rustfmt + fix warning --- Cargo.lock | 1 + server/src/entity/arrow.rs | 2 +- server/src/entity/mod.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c37aa7aa..660a0e19d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -759,6 +759,7 @@ dependencies = [ "num-traits 0.2.11", "parking_lot", "rand", + "rand_distr", "rand_xorshift", "rayon", "rsa", diff --git a/server/src/entity/arrow.rs b/server/src/entity/arrow.rs index 814b50ad4..56b01329d 100644 --- a/server/src/entity/arrow.rs +++ b/server/src/entity/arrow.rs @@ -6,7 +6,7 @@ use crate::util::{degrees_to_stops, protocol_velocity}; use feather_core::entity::{ArrowEntityData, BaseEntityData, EntityData}; use feather_core::network::packet::implementation::SpawnObject; use feather_core::{Packet, Position, Vec3d}; -use fecs::{EntityRef, World, EntityBuilder}; +use fecs::{EntityBuilder, EntityRef}; use uuid::Uuid; pub fn create(position: Position, velocity: glm::DVec3) -> EntityBuilder { diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 97f2ba185..b1c596f8a 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -3,8 +3,8 @@ //! block entities, monsters, etc. Player entities are handled in `crate::player`, //! not here. -pub mod falling_block; pub mod arrow; +pub mod falling_block; pub mod item; use crate::game::Game; From 1e72f287b533a7d5be1e5684f5dea30eb55b54c4 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 23 Mar 2020 10:08:13 -0600 Subject: [PATCH 133/647] Make unsafe lifetime transmute explicit --- server/src/lighting.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/src/lighting.rs b/server/src/lighting.rs index 7aee01088..36cc6ab90 100644 --- a/server/src/lighting.rs +++ b/server/src/lighting.rs @@ -239,7 +239,11 @@ struct Context<'a> { impl<'a> Context<'a> { fn new(chunk_map: &'a ChunkMap, start_chunk: ChunkPosition) -> Option { Some(Self { - current_chunk: unsafe { std::mem::transmute(chunk_map.chunk_at_mut(start_chunk)?) }, + current_chunk: unsafe { + std::mem::transmute::, RwLockWriteGuard<'static, Chunk>>( + chunk_map.chunk_at_mut(start_chunk)?, + ) + }, chunk_map: chunk_map as *const _, _phantom: PhantomData, }) From 4b5e8471929459d2c359f6c5653179386cfbb80f Mon Sep 17 00:00:00 2001 From: Caelum van Ispelen Date: Mon, 23 Mar 2020 11:15:26 -0600 Subject: [PATCH 134/647] Create stubs for most 1.13.2 mobs (#187) * Refactored the entity modules: * Item, arrow, and falling block now under server/src/entity/object * New module entity::mob * Implemented stubs for 1.13.2 mobs. Currently, this adds no functionality to the server, but it opens up the opportunity to implement more entities in the future. --- server/src/entity/mob.rs | 134 ++++++++++++++++++ server/src/entity/mob/boss.rs | 2 + server/src/entity/mob/boss/ender_dragon.rs | 8 ++ server/src/entity/mob/boss/wither.rs | 8 ++ server/src/entity/mob/defensive.rs | 1 + server/src/entity/mob/defensive/pufferfish.rs | 8 ++ server/src/entity/mob/hostile.rs | 22 +++ server/src/entity/mob/hostile/blaze.rs | 8 ++ server/src/entity/mob/hostile/creeper.rs | 8 ++ server/src/entity/mob/hostile/drowned.rs | 8 ++ .../src/entity/mob/hostile/elder_guardian.rs | 8 ++ server/src/entity/mob/hostile/endermite.rs | 8 ++ server/src/entity/mob/hostile/evoker.rs | 8 ++ server/src/entity/mob/hostile/ghast.rs | 8 ++ server/src/entity/mob/hostile/guardian.rs | 8 ++ server/src/entity/mob/hostile/husk.rs | 8 ++ server/src/entity/mob/hostile/magma_cube.rs | 8 ++ server/src/entity/mob/hostile/phantom.rs | 8 ++ server/src/entity/mob/hostile/shulker.rs | 8 ++ server/src/entity/mob/hostile/silverfish.rs | 8 ++ server/src/entity/mob/hostile/skeleton.rs | 8 ++ server/src/entity/mob/hostile/slime.rs | 8 ++ server/src/entity/mob/hostile/stray.rs | 8 ++ server/src/entity/mob/hostile/vex.rs | 8 ++ server/src/entity/mob/hostile/vindicator.rs | 8 ++ server/src/entity/mob/hostile/witch.rs | 8 ++ .../src/entity/mob/hostile/wither_skeleton.rs | 8 ++ server/src/entity/mob/hostile/zombie.rs | 8 ++ .../src/entity/mob/hostile/zombie_villager.rs | 8 ++ server/src/entity/mob/neutral.rs | 9 ++ server/src/entity/mob/neutral/cave_spider.rs | 8 ++ server/src/entity/mob/neutral/dolphin.rs | 8 ++ server/src/entity/mob/neutral/enderman.rs | 8 ++ server/src/entity/mob/neutral/iron_golem.rs | 8 ++ server/src/entity/mob/neutral/llama.rs | 8 ++ server/src/entity/mob/neutral/polar_bear.rs | 8 ++ server/src/entity/mob/neutral/spider.rs | 8 ++ server/src/entity/mob/neutral/wolf.rs | 8 ++ .../src/entity/mob/neutral/zombie_pigman.rs | 8 ++ server/src/entity/mob/passive.rs | 23 +++ server/src/entity/mob/passive/bat.rs | 8 ++ server/src/entity/mob/passive/cat.rs | 8 ++ server/src/entity/mob/passive/chicken.rs | 8 ++ server/src/entity/mob/passive/cod.rs | 8 ++ server/src/entity/mob/passive/cow.rs | 8 ++ server/src/entity/mob/passive/donkey.rs | 8 ++ server/src/entity/mob/passive/horse.rs | 8 ++ server/src/entity/mob/passive/mooshroom.rs | 8 ++ server/src/entity/mob/passive/mule.rs | 8 ++ server/src/entity/mob/passive/ocelot.rs | 8 ++ server/src/entity/mob/passive/parrot.rs | 8 ++ server/src/entity/mob/passive/pig.rs | 8 ++ server/src/entity/mob/passive/rabbit.rs | 8 ++ server/src/entity/mob/passive/salmon.rs | 8 ++ server/src/entity/mob/passive/sheep.rs | 8 ++ .../src/entity/mob/passive/skeleton_horse.rs | 8 ++ server/src/entity/mob/passive/snow_golem.rs | 8 ++ server/src/entity/mob/passive/squid.rs | 8 ++ .../src/entity/mob/passive/tropical_fish.rs | 8 ++ server/src/entity/mob/passive/turtle.rs | 8 ++ server/src/entity/mob/passive/villager.rs | 8 ++ server/src/entity/mod.rs | 19 ++- server/src/entity/object.rs | 3 + server/src/entity/{ => object}/arrow.rs | 5 +- .../src/entity/{ => object}/falling_block.rs | 13 +- server/src/entity/{ => object}/item.rs | 10 +- server/src/packet_handlers/digging.rs | 4 +- 67 files changed, 660 insertions(+), 25 deletions(-) create mode 100644 server/src/entity/mob.rs create mode 100644 server/src/entity/mob/boss.rs create mode 100644 server/src/entity/mob/boss/ender_dragon.rs create mode 100644 server/src/entity/mob/boss/wither.rs create mode 100644 server/src/entity/mob/defensive.rs create mode 100644 server/src/entity/mob/defensive/pufferfish.rs create mode 100644 server/src/entity/mob/hostile.rs create mode 100644 server/src/entity/mob/hostile/blaze.rs create mode 100644 server/src/entity/mob/hostile/creeper.rs create mode 100644 server/src/entity/mob/hostile/drowned.rs create mode 100644 server/src/entity/mob/hostile/elder_guardian.rs create mode 100644 server/src/entity/mob/hostile/endermite.rs create mode 100644 server/src/entity/mob/hostile/evoker.rs create mode 100644 server/src/entity/mob/hostile/ghast.rs create mode 100644 server/src/entity/mob/hostile/guardian.rs create mode 100644 server/src/entity/mob/hostile/husk.rs create mode 100644 server/src/entity/mob/hostile/magma_cube.rs create mode 100644 server/src/entity/mob/hostile/phantom.rs create mode 100644 server/src/entity/mob/hostile/shulker.rs create mode 100644 server/src/entity/mob/hostile/silverfish.rs create mode 100644 server/src/entity/mob/hostile/skeleton.rs create mode 100644 server/src/entity/mob/hostile/slime.rs create mode 100644 server/src/entity/mob/hostile/stray.rs create mode 100644 server/src/entity/mob/hostile/vex.rs create mode 100644 server/src/entity/mob/hostile/vindicator.rs create mode 100644 server/src/entity/mob/hostile/witch.rs create mode 100644 server/src/entity/mob/hostile/wither_skeleton.rs create mode 100644 server/src/entity/mob/hostile/zombie.rs create mode 100644 server/src/entity/mob/hostile/zombie_villager.rs create mode 100644 server/src/entity/mob/neutral.rs create mode 100644 server/src/entity/mob/neutral/cave_spider.rs create mode 100644 server/src/entity/mob/neutral/dolphin.rs create mode 100644 server/src/entity/mob/neutral/enderman.rs create mode 100644 server/src/entity/mob/neutral/iron_golem.rs create mode 100644 server/src/entity/mob/neutral/llama.rs create mode 100644 server/src/entity/mob/neutral/polar_bear.rs create mode 100644 server/src/entity/mob/neutral/spider.rs create mode 100644 server/src/entity/mob/neutral/wolf.rs create mode 100644 server/src/entity/mob/neutral/zombie_pigman.rs create mode 100644 server/src/entity/mob/passive.rs create mode 100644 server/src/entity/mob/passive/bat.rs create mode 100644 server/src/entity/mob/passive/cat.rs create mode 100644 server/src/entity/mob/passive/chicken.rs create mode 100644 server/src/entity/mob/passive/cod.rs create mode 100644 server/src/entity/mob/passive/cow.rs create mode 100644 server/src/entity/mob/passive/donkey.rs create mode 100644 server/src/entity/mob/passive/horse.rs create mode 100644 server/src/entity/mob/passive/mooshroom.rs create mode 100644 server/src/entity/mob/passive/mule.rs create mode 100644 server/src/entity/mob/passive/ocelot.rs create mode 100644 server/src/entity/mob/passive/parrot.rs create mode 100644 server/src/entity/mob/passive/pig.rs create mode 100644 server/src/entity/mob/passive/rabbit.rs create mode 100644 server/src/entity/mob/passive/salmon.rs create mode 100644 server/src/entity/mob/passive/sheep.rs create mode 100644 server/src/entity/mob/passive/skeleton_horse.rs create mode 100644 server/src/entity/mob/passive/snow_golem.rs create mode 100644 server/src/entity/mob/passive/squid.rs create mode 100644 server/src/entity/mob/passive/tropical_fish.rs create mode 100644 server/src/entity/mob/passive/turtle.rs create mode 100644 server/src/entity/mob/passive/villager.rs create mode 100644 server/src/entity/object.rs rename server/src/entity/{ => object}/arrow.rs (92%) rename server/src/entity/{ => object}/falling_block.rs (92%) rename server/src/entity/{ => object}/item.rs (97%) diff --git a/server/src/entity/mob.rs b/server/src/entity/mob.rs new file mode 100644 index 000000000..a0e2a6a2b --- /dev/null +++ b/server/src/entity/mob.rs @@ -0,0 +1,134 @@ +//! Components and functionality shared across all mobs. + +mod boss; +mod defensive; +mod hostile; +mod neutral; +mod passive; + +use crate::entity; +use crate::entity::{EntityId, SpawnPacketCreator, Velocity}; +use crate::util::{degrees_to_stops, protocol_velocity}; +pub use boss::*; +pub use defensive::*; +use feather_core::network::packet::implementation::SpawnMob; +use feather_core::{EntityMetadata, Packet, Position}; +use fecs::{EntityBuilder, EntityRef}; +pub use hostile::*; +pub use neutral::*; +pub use passive::*; +use uuid::Uuid; + +/// Enumeration of mob types. Note that this enum should not be +/// used in queries to identify mobs of a given type. +/// +/// This is _not_ a component. It is only used for utility +/// functions such as `mob::spawn_packet_creator`. +/// +/// https://wiki.vg/Entity_metadata#Mobs +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(i32)] +pub enum MobKind { + Bat = 3, + Blaze = 4, + CaveSpider = 6, + Chicken = 7, + Cod = 8, + Cow = 9, + Creeper = 10, + Donkey = 11, + Dolphin = 12, + Drowned = 14, + ElderGuardian = 15, + EnderDragon = 16, + Enderman = 18, + Endermite = 19, + EvocationIllager = 21, + Ghast = 26, + Giant = 27, + Guardian = 28, + Horse = 29, + Husk = 30, + IllusionIllager = 31, + Llama = 36, + MagmaCube = 38, + Mule = 46, + MushroomCow = 47, + Ocelot = 48, + Parrot = 50, + Pig = 51, + Pufferfish = 52, + PigZombie = 53, + PolarBear = 54, + Rabbit = 56, + Salmon = 57, + Sheep = 58, + Shulker = 59, + Silverfish = 61, + Skeleton = 62, + SkeletonHorse = 63, + Slime = 64, + SnowGolem = 66, + Spider = 69, + Squid = 70, + Stray = 71, + TropicalFish = 72, + Turtle = 73, + Vex = 78, + Villager = 79, + IronGolem = 80, + VindicationIllager = 81, + Witch = 82, + Wither = 83, + WitherSkeleton = 84, + Wolf = 86, + Zombie = 87, + ZombieHorse = 88, + ZombieVillager = 89, + Phantom = 90, +} + +/// Returns the base components for a mob with the given +/// kind. +pub fn base(kind: MobKind) -> EntityBuilder { + entity::base().with(spawn_packet_creator(kind)) +} + +/// Returns a `SpawnPacketCreator` for a mob with the given kind. +pub fn spawn_packet_creator(kind: MobKind) -> SpawnPacketCreator { + let f = Box::new(move |accessor: &EntityRef| { + let entity_id = accessor.get::().0; + let uuid = accessor + .try_get::() + .map(|r| *r) + .unwrap_or_else(Uuid::new_v4); + + let position = *accessor.get::(); + let velocity = *accessor.get::(); + let meta = accessor + .try_get::() + .map(|meta| (&*meta).clone()) + .unwrap_or_else(EntityMetadata::entity_base); + + let (velocity_x, velocity_y, velocity_z) = protocol_velocity(velocity.0); + + let res: Box = Box::new(SpawnMob { + entity_id, + entity_uuid: uuid, + ty: kind as i32, + x: position.x, + y: position.y, + z: position.z, + yaw: degrees_to_stops(position.yaw), + pitch: degrees_to_stops(position.pitch), + head_pitch: 0, // todo + velocity_x, + velocity_y, + velocity_z, + meta, + }); + res + }); + + SpawnPacketCreator(Box::leak(f)) +} diff --git a/server/src/entity/mob/boss.rs b/server/src/entity/mob/boss.rs new file mode 100644 index 000000000..3adebbbe3 --- /dev/null +++ b/server/src/entity/mob/boss.rs @@ -0,0 +1,2 @@ +pub mod ender_dragon; +pub mod wither; diff --git a/server/src/entity/mob/boss/ender_dragon.rs b/server/src/entity/mob/boss/ender_dragon.rs new file mode 100644 index 000000000..8ae6992e8 --- /dev/null +++ b/server/src/entity/mob/boss/ender_dragon.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct EnderDragon; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::EnderDragon).with(EnderDragon) +} diff --git a/server/src/entity/mob/boss/wither.rs b/server/src/entity/mob/boss/wither.rs new file mode 100644 index 000000000..a670392ae --- /dev/null +++ b/server/src/entity/mob/boss/wither.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Wither; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Wither).with(Wither) +} diff --git a/server/src/entity/mob/defensive.rs b/server/src/entity/mob/defensive.rs new file mode 100644 index 000000000..66354d1fa --- /dev/null +++ b/server/src/entity/mob/defensive.rs @@ -0,0 +1 @@ +pub mod pufferfish; diff --git a/server/src/entity/mob/defensive/pufferfish.rs b/server/src/entity/mob/defensive/pufferfish.rs new file mode 100644 index 000000000..c91605ad8 --- /dev/null +++ b/server/src/entity/mob/defensive/pufferfish.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Pufferfish; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Pufferfish).with(Pufferfish) +} diff --git a/server/src/entity/mob/hostile.rs b/server/src/entity/mob/hostile.rs new file mode 100644 index 000000000..bf4aa054f --- /dev/null +++ b/server/src/entity/mob/hostile.rs @@ -0,0 +1,22 @@ +pub mod blaze; +pub mod creeper; +pub mod drowned; +pub mod elder_guardian; +pub mod endermite; +pub mod evoker; +pub mod ghast; +pub mod guardian; +pub mod husk; +pub mod magma_cube; +pub mod phantom; +pub mod shulker; +pub mod silverfish; +pub mod skeleton; +pub mod slime; +pub mod stray; +pub mod vex; +pub mod vindicator; +pub mod witch; +pub mod wither_skeleton; +pub mod zombie; +pub mod zombie_villager; diff --git a/server/src/entity/mob/hostile/blaze.rs b/server/src/entity/mob/hostile/blaze.rs new file mode 100644 index 000000000..48a47ed45 --- /dev/null +++ b/server/src/entity/mob/hostile/blaze.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Blaze; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Blaze).with(Blaze) +} diff --git a/server/src/entity/mob/hostile/creeper.rs b/server/src/entity/mob/hostile/creeper.rs new file mode 100644 index 000000000..3d8caa6ab --- /dev/null +++ b/server/src/entity/mob/hostile/creeper.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Creeper; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Creeper).with(Creeper) +} diff --git a/server/src/entity/mob/hostile/drowned.rs b/server/src/entity/mob/hostile/drowned.rs new file mode 100644 index 000000000..3faf2b28a --- /dev/null +++ b/server/src/entity/mob/hostile/drowned.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Drowned; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Drowned).with(Drowned) +} diff --git a/server/src/entity/mob/hostile/elder_guardian.rs b/server/src/entity/mob/hostile/elder_guardian.rs new file mode 100644 index 000000000..4ca356db0 --- /dev/null +++ b/server/src/entity/mob/hostile/elder_guardian.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct ElderGuardian; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::ElderGuardian).with(ElderGuardian) +} diff --git a/server/src/entity/mob/hostile/endermite.rs b/server/src/entity/mob/hostile/endermite.rs new file mode 100644 index 000000000..809e96392 --- /dev/null +++ b/server/src/entity/mob/hostile/endermite.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Endermite; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Endermite).with(Endermite) +} diff --git a/server/src/entity/mob/hostile/evoker.rs b/server/src/entity/mob/hostile/evoker.rs new file mode 100644 index 000000000..31b8548b9 --- /dev/null +++ b/server/src/entity/mob/hostile/evoker.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Evoker; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::EvocationIllager).with(Evoker) +} diff --git a/server/src/entity/mob/hostile/ghast.rs b/server/src/entity/mob/hostile/ghast.rs new file mode 100644 index 000000000..d934c2213 --- /dev/null +++ b/server/src/entity/mob/hostile/ghast.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Ghast; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Ghast).with(Ghast) +} diff --git a/server/src/entity/mob/hostile/guardian.rs b/server/src/entity/mob/hostile/guardian.rs new file mode 100644 index 000000000..b1fd1dbb6 --- /dev/null +++ b/server/src/entity/mob/hostile/guardian.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Guardian; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Guardian).with(Guardian) +} diff --git a/server/src/entity/mob/hostile/husk.rs b/server/src/entity/mob/hostile/husk.rs new file mode 100644 index 000000000..d1c5bd3eb --- /dev/null +++ b/server/src/entity/mob/hostile/husk.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Husk; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Husk).with(Husk) +} diff --git a/server/src/entity/mob/hostile/magma_cube.rs b/server/src/entity/mob/hostile/magma_cube.rs new file mode 100644 index 000000000..36b40b8d8 --- /dev/null +++ b/server/src/entity/mob/hostile/magma_cube.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct MagmaCube; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::MagmaCube).with(MagmaCube) +} diff --git a/server/src/entity/mob/hostile/phantom.rs b/server/src/entity/mob/hostile/phantom.rs new file mode 100644 index 000000000..85372e935 --- /dev/null +++ b/server/src/entity/mob/hostile/phantom.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Phantom; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Phantom).with(Phantom) +} diff --git a/server/src/entity/mob/hostile/shulker.rs b/server/src/entity/mob/hostile/shulker.rs new file mode 100644 index 000000000..74cc1a7c9 --- /dev/null +++ b/server/src/entity/mob/hostile/shulker.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Shulker; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Shulker).with(Shulker) +} diff --git a/server/src/entity/mob/hostile/silverfish.rs b/server/src/entity/mob/hostile/silverfish.rs new file mode 100644 index 000000000..b4b84f9d5 --- /dev/null +++ b/server/src/entity/mob/hostile/silverfish.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Silverfish; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Silverfish).with(Silverfish) +} diff --git a/server/src/entity/mob/hostile/skeleton.rs b/server/src/entity/mob/hostile/skeleton.rs new file mode 100644 index 000000000..1319302d3 --- /dev/null +++ b/server/src/entity/mob/hostile/skeleton.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Skeleton; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Skeleton).with(Skeleton) +} diff --git a/server/src/entity/mob/hostile/slime.rs b/server/src/entity/mob/hostile/slime.rs new file mode 100644 index 000000000..fc3721f7a --- /dev/null +++ b/server/src/entity/mob/hostile/slime.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Slime; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Slime).with(Slime) +} diff --git a/server/src/entity/mob/hostile/stray.rs b/server/src/entity/mob/hostile/stray.rs new file mode 100644 index 000000000..cba167eca --- /dev/null +++ b/server/src/entity/mob/hostile/stray.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Stray; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Stray).with(Stray) +} diff --git a/server/src/entity/mob/hostile/vex.rs b/server/src/entity/mob/hostile/vex.rs new file mode 100644 index 000000000..448090ebf --- /dev/null +++ b/server/src/entity/mob/hostile/vex.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Vex; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Vex).with(Vex) +} diff --git a/server/src/entity/mob/hostile/vindicator.rs b/server/src/entity/mob/hostile/vindicator.rs new file mode 100644 index 000000000..dc0439b14 --- /dev/null +++ b/server/src/entity/mob/hostile/vindicator.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Vindicator; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::VindicationIllager).with(Vindicator) +} diff --git a/server/src/entity/mob/hostile/witch.rs b/server/src/entity/mob/hostile/witch.rs new file mode 100644 index 000000000..524b95737 --- /dev/null +++ b/server/src/entity/mob/hostile/witch.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Witch; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Witch).with(Witch) +} diff --git a/server/src/entity/mob/hostile/wither_skeleton.rs b/server/src/entity/mob/hostile/wither_skeleton.rs new file mode 100644 index 000000000..cc266b33d --- /dev/null +++ b/server/src/entity/mob/hostile/wither_skeleton.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct WitherSkeleton; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::WitherSkeleton).with(WitherSkeleton) +} diff --git a/server/src/entity/mob/hostile/zombie.rs b/server/src/entity/mob/hostile/zombie.rs new file mode 100644 index 000000000..733d87a54 --- /dev/null +++ b/server/src/entity/mob/hostile/zombie.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Zombie; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Zombie).with(Zombie) +} diff --git a/server/src/entity/mob/hostile/zombie_villager.rs b/server/src/entity/mob/hostile/zombie_villager.rs new file mode 100644 index 000000000..820c0e0db --- /dev/null +++ b/server/src/entity/mob/hostile/zombie_villager.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct ZombieVillager; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::ZombieVillager).with(ZombieVillager) +} diff --git a/server/src/entity/mob/neutral.rs b/server/src/entity/mob/neutral.rs new file mode 100644 index 000000000..f2caabe4b --- /dev/null +++ b/server/src/entity/mob/neutral.rs @@ -0,0 +1,9 @@ +pub mod cave_spider; +pub mod dolphin; +pub mod enderman; +pub mod iron_golem; +pub mod llama; +pub mod polar_bear; +pub mod spider; +pub mod wolf; +pub mod zombie_pigman; diff --git a/server/src/entity/mob/neutral/cave_spider.rs b/server/src/entity/mob/neutral/cave_spider.rs new file mode 100644 index 000000000..593dfffa3 --- /dev/null +++ b/server/src/entity/mob/neutral/cave_spider.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct CaveSpider; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::CaveSpider).with(CaveSpider) +} diff --git a/server/src/entity/mob/neutral/dolphin.rs b/server/src/entity/mob/neutral/dolphin.rs new file mode 100644 index 000000000..ed6147e50 --- /dev/null +++ b/server/src/entity/mob/neutral/dolphin.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Dolphin; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Dolphin).with(Dolphin) +} diff --git a/server/src/entity/mob/neutral/enderman.rs b/server/src/entity/mob/neutral/enderman.rs new file mode 100644 index 000000000..fca2fe516 --- /dev/null +++ b/server/src/entity/mob/neutral/enderman.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Enderman; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Enderman).with(Enderman) +} diff --git a/server/src/entity/mob/neutral/iron_golem.rs b/server/src/entity/mob/neutral/iron_golem.rs new file mode 100644 index 000000000..d72ba739e --- /dev/null +++ b/server/src/entity/mob/neutral/iron_golem.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct IronGolem; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::IronGolem).with(IronGolem) +} diff --git a/server/src/entity/mob/neutral/llama.rs b/server/src/entity/mob/neutral/llama.rs new file mode 100644 index 000000000..705530e31 --- /dev/null +++ b/server/src/entity/mob/neutral/llama.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Llama; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Llama).with(Llama) +} diff --git a/server/src/entity/mob/neutral/polar_bear.rs b/server/src/entity/mob/neutral/polar_bear.rs new file mode 100644 index 000000000..d6d697c4c --- /dev/null +++ b/server/src/entity/mob/neutral/polar_bear.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct PolarBear; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::PolarBear).with(PolarBear) +} diff --git a/server/src/entity/mob/neutral/spider.rs b/server/src/entity/mob/neutral/spider.rs new file mode 100644 index 000000000..6f0256ea4 --- /dev/null +++ b/server/src/entity/mob/neutral/spider.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Spider; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Spider).with(Spider) +} diff --git a/server/src/entity/mob/neutral/wolf.rs b/server/src/entity/mob/neutral/wolf.rs new file mode 100644 index 000000000..b9249d370 --- /dev/null +++ b/server/src/entity/mob/neutral/wolf.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Wolf; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Wolf).with(Wolf) +} diff --git a/server/src/entity/mob/neutral/zombie_pigman.rs b/server/src/entity/mob/neutral/zombie_pigman.rs new file mode 100644 index 000000000..81c356a09 --- /dev/null +++ b/server/src/entity/mob/neutral/zombie_pigman.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct ZombiePigman; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::PigZombie).with(ZombiePigman) +} diff --git a/server/src/entity/mob/passive.rs b/server/src/entity/mob/passive.rs new file mode 100644 index 000000000..adc6aaf72 --- /dev/null +++ b/server/src/entity/mob/passive.rs @@ -0,0 +1,23 @@ +pub mod bat; +pub mod cat; +pub mod chicken; +pub mod cod; +pub mod cow; +pub mod donkey; +pub mod horse; +pub mod mooshroom; +pub mod mule; +pub mod ocelot; +pub mod parrot; +pub mod pig; +pub mod rabbit; +pub mod salmon; +pub mod sheep; +pub mod skeleton_horse; +pub mod snow_golem; +pub mod squid; +pub mod tropical_fish; +pub mod turtle; +pub mod villager; + +// Base components for all passive mobs. diff --git a/server/src/entity/mob/passive/bat.rs b/server/src/entity/mob/passive/bat.rs new file mode 100644 index 000000000..e4f6db095 --- /dev/null +++ b/server/src/entity/mob/passive/bat.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Bat; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Bat).with(Bat) +} diff --git a/server/src/entity/mob/passive/cat.rs b/server/src/entity/mob/passive/cat.rs new file mode 100644 index 000000000..a89c993e4 --- /dev/null +++ b/server/src/entity/mob/passive/cat.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Cat; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Ocelot).with(Cat) +} diff --git a/server/src/entity/mob/passive/chicken.rs b/server/src/entity/mob/passive/chicken.rs new file mode 100644 index 000000000..b103df4ca --- /dev/null +++ b/server/src/entity/mob/passive/chicken.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Chicken; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Chicken).with(Chicken) +} diff --git a/server/src/entity/mob/passive/cod.rs b/server/src/entity/mob/passive/cod.rs new file mode 100644 index 000000000..df16a8e1d --- /dev/null +++ b/server/src/entity/mob/passive/cod.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Cod; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Cod).with(Cod) +} diff --git a/server/src/entity/mob/passive/cow.rs b/server/src/entity/mob/passive/cow.rs new file mode 100644 index 000000000..630e2d24f --- /dev/null +++ b/server/src/entity/mob/passive/cow.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Cow; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Cow).with(Cow) +} diff --git a/server/src/entity/mob/passive/donkey.rs b/server/src/entity/mob/passive/donkey.rs new file mode 100644 index 000000000..3fb69d4ab --- /dev/null +++ b/server/src/entity/mob/passive/donkey.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Donkey; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Donkey).with(Donkey) +} diff --git a/server/src/entity/mob/passive/horse.rs b/server/src/entity/mob/passive/horse.rs new file mode 100644 index 000000000..a816a50db --- /dev/null +++ b/server/src/entity/mob/passive/horse.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Horse; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Horse).with(Horse) +} diff --git a/server/src/entity/mob/passive/mooshroom.rs b/server/src/entity/mob/passive/mooshroom.rs new file mode 100644 index 000000000..db32df63b --- /dev/null +++ b/server/src/entity/mob/passive/mooshroom.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Mooshroom; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::MushroomCow).with(Mooshroom) +} diff --git a/server/src/entity/mob/passive/mule.rs b/server/src/entity/mob/passive/mule.rs new file mode 100644 index 000000000..7efee3e8c --- /dev/null +++ b/server/src/entity/mob/passive/mule.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Mule; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Mule).with(Mule) +} diff --git a/server/src/entity/mob/passive/ocelot.rs b/server/src/entity/mob/passive/ocelot.rs new file mode 100644 index 000000000..64adf8a13 --- /dev/null +++ b/server/src/entity/mob/passive/ocelot.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Ocelot; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Ocelot).with(Ocelot) +} diff --git a/server/src/entity/mob/passive/parrot.rs b/server/src/entity/mob/passive/parrot.rs new file mode 100644 index 000000000..30af86e74 --- /dev/null +++ b/server/src/entity/mob/passive/parrot.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Parrot; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Parrot).with(Parrot) +} diff --git a/server/src/entity/mob/passive/pig.rs b/server/src/entity/mob/passive/pig.rs new file mode 100644 index 000000000..0d5f5ffe5 --- /dev/null +++ b/server/src/entity/mob/passive/pig.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Pig; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Pig).with(Pig) +} diff --git a/server/src/entity/mob/passive/rabbit.rs b/server/src/entity/mob/passive/rabbit.rs new file mode 100644 index 000000000..0c957a262 --- /dev/null +++ b/server/src/entity/mob/passive/rabbit.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Rabbit; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Rabbit).with(Rabbit) +} diff --git a/server/src/entity/mob/passive/salmon.rs b/server/src/entity/mob/passive/salmon.rs new file mode 100644 index 000000000..eb76b3b51 --- /dev/null +++ b/server/src/entity/mob/passive/salmon.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Salmon; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Salmon).with(Salmon) +} diff --git a/server/src/entity/mob/passive/sheep.rs b/server/src/entity/mob/passive/sheep.rs new file mode 100644 index 000000000..bfe9bb4b8 --- /dev/null +++ b/server/src/entity/mob/passive/sheep.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Sheep; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Sheep).with(Sheep) +} diff --git a/server/src/entity/mob/passive/skeleton_horse.rs b/server/src/entity/mob/passive/skeleton_horse.rs new file mode 100644 index 000000000..3e9abdcf0 --- /dev/null +++ b/server/src/entity/mob/passive/skeleton_horse.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct SkeletonHorse; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Horse).with(SkeletonHorse) +} diff --git a/server/src/entity/mob/passive/snow_golem.rs b/server/src/entity/mob/passive/snow_golem.rs new file mode 100644 index 000000000..a073b84cd --- /dev/null +++ b/server/src/entity/mob/passive/snow_golem.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct SnowGolem; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::SnowGolem).with(SnowGolem) +} diff --git a/server/src/entity/mob/passive/squid.rs b/server/src/entity/mob/passive/squid.rs new file mode 100644 index 000000000..1f4193a3a --- /dev/null +++ b/server/src/entity/mob/passive/squid.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Squid; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Squid).with(Squid) +} diff --git a/server/src/entity/mob/passive/tropical_fish.rs b/server/src/entity/mob/passive/tropical_fish.rs new file mode 100644 index 000000000..e92fe52f6 --- /dev/null +++ b/server/src/entity/mob/passive/tropical_fish.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct TropicalFish; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::TropicalFish).with(TropicalFish) +} diff --git a/server/src/entity/mob/passive/turtle.rs b/server/src/entity/mob/passive/turtle.rs new file mode 100644 index 000000000..2ee70278e --- /dev/null +++ b/server/src/entity/mob/passive/turtle.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Turtle; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Turtle).with(Turtle) +} diff --git a/server/src/entity/mob/passive/villager.rs b/server/src/entity/mob/passive/villager.rs new file mode 100644 index 000000000..9121092bf --- /dev/null +++ b/server/src/entity/mob/passive/villager.rs @@ -0,0 +1,8 @@ +use crate::entity::{mob, MobKind}; +use fecs::EntityBuilder; + +pub struct Villager; + +pub fn create() -> EntityBuilder { + mob::base(MobKind::Villager).with(Villager) +} diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index b1c596f8a..70801095e 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -3,9 +3,11 @@ //! block entities, monsters, etc. Player entities are handled in `crate::player`, //! not here. -pub mod arrow; -pub mod falling_block; -pub mod item; +mod mob; +mod object; + +pub use mob::*; +pub use object::*; use crate::game::Game; use feather_core::entity::EntityData; @@ -163,18 +165,15 @@ pub fn previous_position_velocity_reset(world: &mut World) { /// Inserts the base components for an entity into an `EntityBuilder`. /// /// This currently includes: -/// * Velocity (0) -/// * Entity ID -/// * Position and previous position -/// * Triggers `EntityCreateEvent` -pub fn base(position: Position) -> EntityBuilder { +/// * Velocity (0) and PreviousVelocity +/// * Entity ID for the protocol +pub fn base() -> EntityBuilder { let id = new_id(); EntityBuilder::new() .with(EntityId(id)) - .with(position) - .with(PreviousPosition(position)) .with(Velocity::default()) .with(PreviousVelocity::default()) + .with(PreviousPosition(position!(0.0, 0.0, 0.0))) } /// Returns a new entity ID. diff --git a/server/src/entity/object.rs b/server/src/entity/object.rs new file mode 100644 index 000000000..5da4adcdb --- /dev/null +++ b/server/src/entity/object.rs @@ -0,0 +1,3 @@ +pub mod arrow; +pub mod falling_block; +pub mod item; diff --git a/server/src/entity/arrow.rs b/server/src/entity/object/arrow.rs similarity index 92% rename from server/src/entity/arrow.rs rename to server/src/entity/object/arrow.rs index 56b01329d..1e4d79ff6 100644 --- a/server/src/entity/arrow.rs +++ b/server/src/entity/object/arrow.rs @@ -9,9 +9,8 @@ use feather_core::{Packet, Position, Vec3d}; use fecs::{EntityBuilder, EntityRef}; use uuid::Uuid; -pub fn create(position: Position, velocity: glm::DVec3) -> EntityBuilder { - entity::base(position) - .with(Velocity(velocity)) +pub fn create() -> EntityBuilder { + entity::base() .with(SpawnPacketCreator(&create_spawn_packet)) .with(ComponentSerializer(&serialize)) .with( diff --git a/server/src/entity/falling_block.rs b/server/src/entity/object/falling_block.rs similarity index 92% rename from server/src/entity/falling_block.rs rename to server/src/entity/object/falling_block.rs index 0bdd3e6d7..1524ca73a 100644 --- a/server/src/entity/falling_block.rs +++ b/server/src/entity/object/falling_block.rs @@ -36,11 +36,10 @@ pub fn spawn_falling_blocks(game: &mut Game, world: &mut World) { let builder = if game.block_at(position.0 - BlockPosition::new(0, 1, 0)) == Some(Block::Air) { - Some(create( - position.0.position() + position!(0.5, 0.0, 0.5), - block.0, - position.0, - )) + Some( + create(block.0, position.0) + .with(position.0.position() + position!(0.5, 0.0, 0.5)), + ) } else { None }; @@ -80,11 +79,11 @@ pub fn on_entity_land_remove_falling_block( } /// Returns an `EntityBuilder` for a falling block of the given type. -pub fn create(pos: Position, ty: Block, spawn_pos: BlockPosition) -> EntityBuilder { +pub fn create(ty: Block, spawn_pos: BlockPosition) -> EntityBuilder { let meta = EntityMetadata::entity_base().with(META_INDEX_FALLING_BLOCK_SPAWN_POSITION, spawn_pos); - entity::base(pos) + entity::base() .with(FallingBlock) .with(FallingBlockType(ty)) .with(SpawnPacketCreator(&create_spawn_packet)) diff --git a/server/src/entity/item.rs b/server/src/entity/object/item.rs similarity index 97% rename from server/src/entity/item.rs rename to server/src/entity/object/item.rs index 0c95c3534..393b9de00 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/object/item.rs @@ -97,7 +97,8 @@ pub fn on_item_drop_spawn_item_entity(game: &mut Game, world: &mut World, event: drop(rng); - let entity = create(pos, event.stack, game.tick_count + TPS) + let entity = create(event.stack, game.tick_count + TPS) + .with(pos) .with(Velocity(velocity)) .build() .spawn_in(world); @@ -202,11 +203,11 @@ pub fn item_collect(game: &mut Game, world: &mut World) { /// Returns an entity builder to create an item entity /// with the given stack and collectable tick. -pub fn create(pos: Position, stack: ItemStack, collectable_at: u64) -> EntityBuilder { +pub fn create(stack: ItemStack, collectable_at: u64) -> EntityBuilder { let meta = EntityMetadata::entity_base().with(META_INDEX_ITEM_SLOT, Some(stack)); let collectable_at = CollectableAt(collectable_at); - entity::base(pos) + entity::base() .with(stack) .with(IsRemoved(AtomicBool::new(false))) .with(collectable_at) @@ -276,7 +277,8 @@ fn load(data: EntityData) -> anyhow::Result { let collectable_at = data.pickup_delay; - Ok(create(pos, stack, collectable_at as u64) + Ok(create(stack, collectable_at as u64) + .with(pos) .with(Velocity(glm::vec3(vel.x, vel.y, vel.z)))) } _ => panic!("attempted to use item::load to load a non-item"), diff --git a/server/src/packet_handlers/digging.rs b/server/src/packet_handlers/digging.rs index cf83e2105..68779904e 100644 --- a/server/src/packet_handlers/digging.rs +++ b/server/src/packet_handlers/digging.rs @@ -236,7 +236,9 @@ fn handle_shoot_bow(game: &mut Game, world: &mut World, player: Entity) { world.remove::(player).unwrap(); trace!("Spawning arrow entity."); - let entity = arrow::create(init_position, arrow_velocity) + let entity = arrow::create() + .with(init_position) + .with(arrow_velocity) .build() .spawn_in(world); game.on_entity_spawn(world, entity); From ab6f2609c707fe241440c2f7becb5cd907b911af Mon Sep 17 00:00:00 2001 From: caelunshun Date: Mon, 23 Mar 2020 13:00:11 -0600 Subject: [PATCH 135/647] Don't interpret commands as chat messages --- server/src/packet_handlers/chat.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/src/packet_handlers/chat.rs b/server/src/packet_handlers/chat.rs index 64307c735..7872d010c 100644 --- a/server/src/packet_handlers/chat.rs +++ b/server/src/packet_handlers/chat.rs @@ -12,6 +12,11 @@ pub fn handle_chat(game: &mut Game, world: &mut World, packet_buffers: &Arc() .for_each(|(player, packet)| { + if packet.message.starts_with('/') { + debug!("Skipping command {}", packet.message); + return; + } + let player_name = world.get::(player); let message = json!({ "translate": "chat.type.text", From 108aa173c1a112ea927c295a3603f6157ebf9553 Mon Sep 17 00:00:00 2001 From: Jacob Emil Ulvedal Rosborg Date: Mon, 23 Mar 2020 20:48:04 +0100 Subject: [PATCH 136/647] Text API for proper text component support (#186) --- Cargo.lock | 23 + core/Cargo.toml | 2 + core/src/lib.rs | 1 + core/src/text.rs | 1119 ++++++++++++++++++++++++++++ server/src/chat.rs | 15 +- server/src/packet_handlers/chat.rs | 15 +- 6 files changed, 1158 insertions(+), 17 deletions(-) create mode 100644 core/src/text.rs diff --git a/Cargo.lock b/Cargo.lock index 660a0e19d..820afdd1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -669,6 +669,8 @@ dependencies = [ "parking_lot", "rayon", "serde", + "serde_json", + "serde_with", "smallvec", "strum 0.18.0", "strum_macros 0.18.0", @@ -2327,6 +2329,27 @@ dependencies = [ "url", ] +[[package]] +name = "serde_with" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d3d595d64120bbbc70b7f6d5ae63298b62a3d9f373ec2f56acf5365ca8a444" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4070d2c9b9d258465ad1d82aabb985b84cd9a3afa94da25ece5a9938ba5f1606" +dependencies = [ + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", +] + [[package]] name = "sha1" version = "0.6.0" diff --git a/core/Cargo.toml b/core/Cargo.toml index 1e8e2f39e..0f9b13ddc 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -43,6 +43,8 @@ bitflags = "1.2" # Serialization serde = { version = "1.0", features = ["derive"] } hematite-nbt = "0.4" +serde_json = "1.0" +serde_with = "1.4" # Concurrency parking_lot = "0.10" diff --git a/core/src/lib.rs b/core/src/lib.rs index 0e103ead3..7390f40b6 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -25,6 +25,7 @@ pub mod inventory; mod math_types; pub mod network; mod save; +pub mod text; extern crate nalgebra_glm as glm; diff --git a/core/src/text.rs b/core/src/text.rs new file mode 100644 index 000000000..d77b72105 --- /dev/null +++ b/core/src/text.rs @@ -0,0 +1,1119 @@ +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::borrow::Cow; +use uuid::Uuid; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Color { + DarkRed, + Red, + Gold, + Yellow, + DarkGreen, + Green, + Aqua, + DarkAqua, + DarkBlue, + Blue, + LightPurple, + DarkPurple, + White, + Gray, + DarkGray, + Black, +} + +impl From for Text { + fn from(color: Color) -> Self { + Text::empty().color(color) + } +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Style { + Bold, + Italic, + Underlined, + Strikethrough, + Obfuscated, +} + +impl From