From 806ffa4f426ac10f1ab1395cf47e274d25f32729 Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 29 Jun 2022 11:09:00 -0700 Subject: [PATCH] Remove &mut wrappers The mutable reference wappers were causing more problems than they were solving. We will document the dangers of mem::swap later. --- examples/cow_sphere.rs | 22 +-- examples/terrain.rs | 20 +-- src/chunk.rs | 158 +++++++----------- src/client.rs | 368 +++++++++++++++++------------------------ src/config.rs | 14 +- src/entity.rs | 339 ++++++++++++++++--------------------- src/lib.rs | 12 +- src/player_list.rs | 222 +++++++++++-------------- src/server.rs | 41 ++--- src/spatial_index.rs | 25 +-- src/world.rs | 188 +++++---------------- 11 files changed, 545 insertions(+), 864 deletions(-) diff --git a/examples/cow_sphere.rs b/examples/cow_sphere.rs index 5ab1ea6..700ecd2 100644 --- a/examples/cow_sphere.rs +++ b/examples/cow_sphere.rs @@ -9,8 +9,8 @@ use valence::config::{Config, ServerListPing}; use valence::text::Color; use valence::util::to_yaw_and_pitch; use valence::{ - async_trait, ClientMut, DimensionId, EntityId, EntityType, Server, ShutdownResult, Text, - TextFormat, WorldId, WorldsMut, + async_trait, DimensionId, EntityId, EntityType, Server, ShutdownResult, Text, + TextFormat, WorldId, Worlds, Client, }; use vek::{Mat3, Vec3}; @@ -57,8 +57,8 @@ impl Config for Game { fn join( &self, _server: &Server, - _client: ClientMut, - worlds: WorldsMut, + _client: &mut Client, + worlds: &mut Worlds, ) -> Result { if let Ok(_) = self .player_count @@ -72,8 +72,8 @@ impl Config for Game { } } - fn init(&self, _server: &Server, mut worlds: WorldsMut) { - let mut world = worlds.create(DimensionId::default()).1; + fn init(&self, _server: &Server, worlds: &mut Worlds) { + let world = worlds.create(DimensionId::default()).1; world.meta.set_flat(true); let size = 5; @@ -84,16 +84,16 @@ impl Config for Game { } self.cows.lock().unwrap().extend((0..200).map(|_| { - let (id, mut e) = world.entities.create(); + let (id, e) = world.entities.create(); e.set_type(EntityType::Cow); id })); } - fn update(&self, server: &Server, mut worlds: WorldsMut) { - let mut world = worlds.iter_mut().next().unwrap().1; + fn update(&self, server: &Server, worlds: &mut Worlds) { + let world = worlds.iter_mut().next().unwrap().1; - world.clients.retain(|_, mut client| { + world.clients.retain(|_, client| { if client.created_tick() == server.current_tick() { client.set_game_mode(GameMode::Creative); client.teleport([0.0, 200.0, 0.0], 0.0, 0.0); @@ -136,7 +136,7 @@ impl Config for Game { .unwrap_or_default(); for (cow_id, p) in cows.iter().cloned().zip(fibonacci_spiral(cow_count)) { - let mut cow = world.entities.get_mut(cow_id).expect("missing cow"); + let cow = world.entities.get_mut(cow_id).expect("missing cow"); let rotated = p * rot; let transformed = rotated * radius + [0.0, 100.0, 0.0]; diff --git a/examples/terrain.rs b/examples/terrain.rs index f62bbde..89c87ea 100644 --- a/examples/terrain.rs +++ b/examples/terrain.rs @@ -11,8 +11,8 @@ use valence::config::{Config, ServerListPing}; use valence::text::Color; use valence::util::chunks_in_view_distance; use valence::{ - async_trait, ChunkPos, ClientMut, DimensionId, Server, ShutdownResult, Text, TextFormat, - WorldId, WorldsMut, + async_trait, ChunkPos, Client, DimensionId, Server, ShutdownResult, Text, TextFormat, WorldId, + Worlds, }; use vek::Lerp; @@ -69,8 +69,8 @@ impl Config for Game { fn join( &self, _server: &Server, - _client: ClientMut, - worlds: WorldsMut, + _client: &mut Client, + worlds: &mut Worlds, ) -> Result { if let Ok(_) = self .player_count @@ -84,17 +84,17 @@ impl Config for Game { } } - fn init(&self, _server: &Server, mut worlds: WorldsMut) { - let (_, mut world) = worlds.create(DimensionId::default()); + fn init(&self, _server: &Server, worlds: &mut Worlds) { + let (_, world) = worlds.create(DimensionId::default()); world.meta.set_flat(true); } - fn update(&self, server: &Server, mut worlds: WorldsMut) { - let mut world = worlds.iter_mut().next().unwrap().1; + fn update(&self, server: &Server, worlds: &mut Worlds) { + let world = worlds.iter_mut().next().unwrap().1; let mut chunks_to_unload = HashSet::<_>::from_iter(world.chunks.iter().map(|t| t.0)); - world.clients.retain(|_, mut client| { + world.clients.retain(|_, client| { if client.is_disconnected() { self.player_count.fetch_sub(1, Ordering::SeqCst); return false; @@ -132,7 +132,7 @@ impl Config for Game { world.chunks.delete(pos); } - world.chunks.par_iter_mut().for_each(|(pos, mut chunk)| { + world.chunks.par_iter_mut().for_each(|(pos, chunk)| { if chunk.created_tick() == server.current_tick() { for z in 0..16 { for x in 0..16 { diff --git a/src/chunk.rs b/src/chunk.rs index 2f51576..9ac3c20 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -3,7 +3,6 @@ use std::collections::HashMap; use std::io::Write; use std::iter::FusedIterator; -use std::ops::Deref; use bitvec::vec::BitVec; use num::Integer; @@ -33,6 +32,16 @@ impl Chunks { } } + pub fn create(&mut self, pos: impl Into) -> bool { + let section_count = (self.server.dimension(self.dimension).height / 16) as u32; + let chunk = Chunk::new(section_count, self.server.current_tick()); + self.chunks.insert(pos.into(), chunk).is_none() + } + + pub fn delete(&mut self, pos: ChunkPos) -> bool { + self.chunks.remove(&pos).is_some() + } + pub fn count(&self) -> usize { self.chunks.len() } @@ -41,6 +50,10 @@ impl Chunks { self.chunks.get(&pos.into()) } + pub fn get_mut(&mut self, pos: impl Into) -> Option<&mut Chunk> { + self.chunks.get_mut(&pos.into()) + } + pub fn clear(&mut self) { self.chunks.clear(); } @@ -49,10 +62,18 @@ impl Chunks { self.chunks.iter().map(|(&pos, chunk)| (pos, chunk)) } + pub fn iter_mut(&mut self) -> impl FusedIterator + '_ { + self.chunks.iter_mut().map(|(&pos, chunk)| (pos, chunk)) + } + pub fn par_iter(&self) -> impl ParallelIterator + Clone + '_ { self.chunks.par_iter().map(|(&pos, chunk)| (pos, chunk)) } + pub fn par_iter_mut(&mut self) -> impl ParallelIterator + '_ { + self.chunks.par_iter_mut().map(|(&pos, chunk)| (pos, chunk)) + } + pub fn get_block_state(&self, pos: impl Into) -> Option { let pos = pos.into(); let chunk_pos = ChunkPos::from(pos); @@ -73,55 +94,17 @@ impl Chunks { None } } -} - -impl<'a> ChunksMut<'a> { - pub(crate) fn new(chunks: &'a mut Chunks) -> Self { - Self(chunks) - } - - pub fn reborrow(&mut self) -> ChunksMut { - ChunksMut(self.0) - } - - pub fn create(&mut self, pos: impl Into) -> bool { - let section_count = (self.server.dimension(self.dimension).height / 16) as u32; - let chunk = Chunk::new(section_count, self.server.current_tick()); - self.0.chunks.insert(pos.into(), chunk).is_none() - } - - pub fn delete(&mut self, pos: ChunkPos) -> bool { - self.0.chunks.remove(&pos).is_some() - } - - pub fn get_mut(&mut self, pos: impl Into) -> Option { - self.0.chunks.get_mut(&pos.into()).map(ChunkMut) - } - - pub fn iter_mut(&mut self) -> impl FusedIterator + '_ { - self.0 - .chunks - .iter_mut() - .map(|(&pos, chunk)| (pos, ChunkMut(chunk))) - } - - pub fn par_iter_mut(&mut self) -> impl ParallelIterator + '_ { - self.0 - .chunks - .par_iter_mut() - .map(|(&pos, chunk)| (pos, ChunkMut(chunk))) - } pub fn set_block_state(&mut self, pos: impl Into, block: BlockState) -> bool { let pos = pos.into(); let chunk_pos = ChunkPos::from(pos); - if let Some(chunk) = self.0.chunks.get_mut(&chunk_pos) { - let min_y = self.0.server.dimension(self.0.dimension).min_y; + if let Some(chunk) = self.chunks.get_mut(&chunk_pos) { + let min_y = self.server.dimension(self.dimension).min_y; if let Some(y) = pos.y.checked_sub(min_y).and_then(|y| y.try_into().ok()) { if y < chunk.height() { - ChunkMut(chunk).set_block_state( + chunk.set_block_state( pos.x.rem_euclid(16) as usize, y, pos.z.rem_euclid(16) as usize, @@ -136,16 +119,6 @@ impl<'a> ChunksMut<'a> { } } -pub struct ChunksMut<'a>(&'a mut Chunks); - -impl<'a> Deref for ChunksMut<'a> { - type Target = Chunks; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - pub struct Chunk { sections: Box<[ChunkSection]>, // TODO block_entities: HashMap, @@ -169,7 +142,7 @@ impl Chunk { created_tick: current_tick, }; - ChunkMut(&mut chunk).apply_modifications(); + chunk.apply_modifications(); chunk } @@ -191,6 +164,27 @@ impl Chunk { } } + pub fn set_block_state(&mut self, x: usize, y: usize, z: usize, block: BlockState) { + assert!( + x < 16 && y < self.height() && z < 16, + "the chunk block coordinates must be within bounds" + ); + + let sect = &mut self.sections[y / 16]; + let idx = x + z * 16 + y % 16 * 16 * 16; + + if block.to_raw() != sect.blocks[idx] & BLOCK_STATE_MASK { + if sect.blocks[idx] & !BLOCK_STATE_MASK == 0 { + sect.modified_count += 1; + } + sect.blocks[idx] = block.to_raw() | !BLOCK_STATE_MASK; + + // TODO: if the block type was modified and the old block type + // could be a block entity, then the block entity at this + // position must be cleared. + } + } + pub fn get_biome(&self, x: usize, y: usize, z: usize) -> BiomeId { if x < 4 && y < self.height() / 4 && z < 4 { self.sections[y / 4].biomes[x + z * 4 + y % 4 * 4 * 4] @@ -199,6 +193,15 @@ impl Chunk { } } + pub fn set_biome(&mut self, x: usize, y: usize, z: usize, b: BiomeId) { + assert!( + x < 4 && y < self.height() / 4 && z < 4, + "the chunk biome coordinates must be within bounds" + ); + + self.sections[y / 4].biomes[x + z * 4 + y % 4 * 4 * 4] = b; + } + /// Gets the chunk data packet for this chunk with the given position. This /// does not include unapplied changes. pub(crate) fn chunk_data_packet(&self, pos: ChunkPos) -> ChunkDataAndUpdateLight { @@ -283,53 +286,11 @@ impl Chunk { } } } -} - -pub struct ChunkMut<'a>(&'a mut Chunk); - -impl<'a> Deref for ChunkMut<'a> { - type Target = Chunk; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - -impl<'a> ChunkMut<'a> { - pub fn set_block_state(&mut self, x: usize, y: usize, z: usize, block: BlockState) { - assert!( - x < 16 && y < self.height() && z < 16, - "the chunk block coordinates must be within bounds" - ); - - let sect = &mut self.0.sections[y / 16]; - let idx = x + z * 16 + y % 16 * 16 * 16; - - if block.to_raw() != sect.blocks[idx] & BLOCK_STATE_MASK { - if sect.blocks[idx] & !BLOCK_STATE_MASK == 0 { - sect.modified_count += 1; - } - sect.blocks[idx] = block.to_raw() | !BLOCK_STATE_MASK; - - // TODO: if the block type was modified and the old block type - // could be a block entity, then the block entity at this - // position must be cleared. - } - } - - pub fn set_biome(&mut self, x: usize, y: usize, z: usize, b: BiomeId) { - assert!( - x < 4 && y < self.height() / 4 && z < 4, - "the chunk biome coordinates must be within bounds" - ); - - self.0.sections[y / 4].biomes[x + z * 4 + y % 4 * 4 * 4] = b; - } pub(crate) fn apply_modifications(&mut self) { let mut any_modified = false; - for sect in self.0.sections.iter_mut() { + for sect in self.sections.iter_mut() { if sect.modified_count > 0 { sect.modified_count = 0; any_modified = true; @@ -370,7 +331,7 @@ impl<'a> ChunkMut<'a> { } if any_modified { - build_heightmap(&self.0.sections, &mut self.0.heightmap); + build_heightmap(&self.sections, &mut self.heightmap); } } } @@ -530,7 +491,6 @@ mod tests { #[test] fn set_get() { let mut chunk = Chunk::new(16, 0); - let mut chunk = ChunkMut(&mut chunk); chunk.set_block_state(1, 2, 3, BlockState::CAKE); assert_eq!(chunk.get_block_state(1, 2, 3), BlockState::CAKE); @@ -543,7 +503,6 @@ mod tests { #[should_panic] fn block_state_oob() { let mut chunk = Chunk::new(16, 0); - let mut chunk = ChunkMut(&mut chunk); chunk.set_block_state(16, 0, 0, BlockState::CAKE); } @@ -552,7 +511,6 @@ mod tests { #[should_panic] fn biome_oob() { let mut chunk = Chunk::new(16, 0); - let mut chunk = ChunkMut(&mut chunk); chunk.set_biome(4, 0, 0, BiomeId(0)); } diff --git a/src/client.rs b/src/client.rs index 1fcf512..4e50c8d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2,7 +2,6 @@ mod event; use std::collections::HashSet; use std::iter::FusedIterator; -use std::ops::Deref; pub use event::*; use flume::{Receiver, Sender, TrySendError}; @@ -40,21 +39,24 @@ pub struct Clients { sm: SlotMap, } -pub struct ClientsMut<'a>(&'a mut Clients); - -impl<'a> Deref for ClientsMut<'a> { - type Target = Clients; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - impl Clients { pub(crate) fn new() -> Self { Self { sm: SlotMap::new() } } + pub(crate) fn create(&mut self, client: Client) -> (ClientId, &mut Client) { + let (id, client) = self.sm.insert(client); + (ClientId(id), client) + } + + pub fn delete(&mut self, client: ClientId) -> bool { + self.sm.remove(client.0).is_some() + } + + pub fn retain(&mut self, mut f: impl FnMut(ClientId, &mut Client) -> bool) { + self.sm.retain(|k, v| f(ClientId(k), v)) + } + pub fn count(&self) -> usize { self.sm.len() } @@ -63,55 +65,27 @@ impl Clients { self.sm.get(client.0) } + pub fn get_mut(&mut self, client: ClientId) -> Option<&mut Client> { + self.sm.get_mut(client.0) + } + pub fn iter(&self) -> impl FusedIterator + Clone + '_ { self.sm.iter().map(|(k, v)| (ClientId(k), v)) } + pub fn iter_mut(&mut self) -> impl FusedIterator + '_ { + self.sm.iter_mut().map(|(k, v)| (ClientId(k), v)) + } + pub fn par_iter(&self) -> impl ParallelIterator + Clone + '_ { self.sm.par_iter().map(|(k, v)| (ClientId(k), v)) } -} -impl<'a> ClientsMut<'a> { - pub(crate) fn new(c: &'a mut Clients) -> Self { - Self(c) - } - - pub fn reborrow(&mut self) -> ClientsMut { - ClientsMut(self.0) - } - - pub(crate) fn create(&mut self, client: Client) -> (ClientId, ClientMut) { - let (id, client) = self.0.sm.insert(client); - (ClientId(id), ClientMut(client)) - } - - pub fn delete(&mut self, client: ClientId) -> bool { - self.0.sm.remove(client.0).is_some() - } - - pub fn retain(&mut self, mut f: impl FnMut(ClientId, ClientMut) -> bool) { - self.0.sm.retain(|k, v| f(ClientId(k), ClientMut(v))) - } - - pub fn get_mut(&mut self, client: ClientId) -> Option { - self.0.sm.get_mut(client.0).map(ClientMut) - } - - pub fn iter_mut(&mut self) -> impl FusedIterator + '_ { - self.0 - .sm - .iter_mut() - .map(|(k, v)| (ClientId(k), ClientMut(v))) - } - - pub fn par_iter_mut(&mut self) -> impl ParallelIterator + '_ { - self.0 - .sm - .par_iter_mut() - .map(|(k, v)| (ClientId(k), ClientMut(v))) + pub fn par_iter_mut(&mut self) -> impl ParallelIterator + '_ { + self.sm.par_iter_mut().map(|(k, v)| (ClientId(k), v)) } } + pub struct ClientId(Key); /// Represents a client connected to the server after logging in. @@ -165,16 +139,6 @@ pub struct Client { // player_meta: Player, } -pub struct ClientMut<'a>(&'a mut Client); - -impl<'a> Deref for ClientMut<'a> { - type Target = Client; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - impl Client { pub(crate) fn new( packet_channels: C2sPacketChannels, @@ -236,6 +200,27 @@ impl Client { self.new_position } + pub fn teleport(&mut self, pos: impl Into>, yaw: f32, pitch: f32) { + self.new_position = pos.into(); + + self.yaw = yaw; + self.pitch = pitch; + + if !self.teleported_this_tick { + self.teleported_this_tick = true; + + self.pending_teleports = match self.pending_teleports.checked_add(1) { + Some(n) => n, + None => { + self.disconnect("Too many pending teleports"); + return; + } + }; + + self.teleport_id_counter = self.teleport_id_counter.wrapping_add(1); + } + } + pub fn yaw(&self) -> f32 { self.yaw } @@ -250,6 +235,17 @@ impl Client { self.spawn_position } + /// Sets the spawn position. The client will see regular compasses point at + /// the provided position. + pub fn set_spawn_position(&mut self, pos: impl Into, yaw_degrees: f32) { + let pos = pos.into(); + if pos != self.spawn_position || yaw_degrees != self.spawn_position_yaw { + self.spawn_position = pos; + self.spawn_position_yaw = yaw_degrees; + self.modified_spawn_position = true; + } + } + /// Gets the last death location. The client will see recovery compasses /// point at the returned position. If the client's current dimension /// differs from the returned dimension or the location is `None` then the @@ -258,10 +254,25 @@ impl Client { self.death_location } + /// Sets the last death location. The client will see recovery compasses + /// point at the provided position. If the client's current dimension + /// differs from the provided dimension or the location is `None` then the + /// compass will spin randomly. + /// + /// Changes to the last death location take effect when the client + /// (re)spawns. + pub fn set_death_location(&mut self, location: Option<(DimensionId, BlockPos)>) { + self.death_location = location; + } + pub fn game_mode(&self) -> GameMode { self.new_game_mode } + pub fn set_game_mode(&mut self, new_game_mode: GameMode) { + self.new_game_mode = new_game_mode; + } + pub fn on_ground(&self) -> bool { self.on_ground } @@ -274,6 +285,10 @@ impl Client { &self.events } + pub fn events_mut(&mut self) -> &mut Vec { + &mut self.events + } + /// The current view distance of this client measured in chunks. pub fn view_distance(&self) -> u8 { self.settings @@ -286,110 +301,47 @@ impl Client { self.new_max_view_distance } + /// The new view distance is clamped to `2..=32`. + pub fn set_max_view_distance(&mut self, dist: u8) { + self.new_max_view_distance = dist.clamp(2, 32); + } + pub fn settings(&self) -> Option<&Settings> { self.settings.as_ref() } -} -impl<'a> ClientMut<'a> { - pub(crate) fn new(client: &'a mut Client) -> Self { - Self(client) - } + pub fn disconnect(&mut self, reason: impl Into) { + if self.send.is_some() { + let txt = reason.into(); + log::info!("disconnecting client '{}': \"{txt}\"", self.username); - pub fn reborrow(&mut self) -> ClientMut { - ClientMut(self.0) - } + self.send_packet(Disconnect { reason: txt }); - pub fn events_mut(&mut self) -> &mut Vec { - &mut self.0.events - } - - pub fn teleport(&mut self, pos: impl Into>, yaw: f32, pitch: f32) { - self.0.new_position = pos.into(); - - self.0.yaw = yaw; - self.0.pitch = pitch; - - if !self.teleported_this_tick { - self.0.teleported_this_tick = true; - - self.0.pending_teleports = match self.0.pending_teleports.checked_add(1) { - Some(n) => n, - None => { - self.disconnect("Too many pending teleports"); - return; - } - }; - - self.0.teleport_id_counter = self.0.teleport_id_counter.wrapping_add(1); + self.send = None; } } - pub fn set_game_mode(&mut self, new_game_mode: GameMode) { - self.0.new_game_mode = new_game_mode; - } - - /// Sets the spawn position. The client will see regular compasses point at - /// the provided position. - pub fn set_spawn_position(&mut self, pos: impl Into, yaw_degrees: f32) { - let pos = pos.into(); - if pos != self.0.spawn_position || yaw_degrees != self.0.spawn_position_yaw { - self.0.spawn_position = pos; - self.0.spawn_position_yaw = yaw_degrees; - self.0.modified_spawn_position = true; + pub fn disconnect_no_reason(&mut self) { + if self.send.is_some() { + log::info!("disconnecting client '{}' (no reason)", self.username); + self.send = None; } } - /// Sets the last death location. The client will see recovery compasses - /// point at the provided position. If the client's current dimension - /// differs from the provided dimension or the location is `None` then the - /// compass will spin randomly. - /// - /// Changes to the last death location take effect when the client - /// (re)spawns. - pub fn set_death_location(&mut self, location: Option<(DimensionId, BlockPos)>) { - self.0.death_location = location; - } - /// Attempts to enqueue a play packet to be sent to this client. The client /// is disconnected if the clientbound packet buffer is full. pub(crate) fn send_packet(&mut self, packet: impl Into) { - send_packet(&mut self.0.send, packet); - } - - pub fn disconnect(&mut self, reason: impl Into) { - if self.0.send.is_some() { - let txt = reason.into(); - log::info!("disconnecting client '{}': \"{txt}\"", self.0.username); - - self.send_packet(Disconnect { reason: txt }); - - self.0.send = None; - } - } - - pub fn disconnect_no_reason(&mut self) { - if self.0.send.is_some() { - log::info!("disconnecting client '{}' (no reason)", self.0.username); - self.0.send = None; - } - } - - /// The new view distance is clamped to `2..=32`. - pub fn set_max_view_distance(&mut self, dist: u8) { - self.0.new_max_view_distance = dist.clamp(2, 32); + send_packet(&mut self.send, packet); } pub(crate) fn handle_serverbound_packets(&mut self, entities: &Entities) { - self.0.events.clear(); + self.events.clear(); for _ in 0..self.recv.len() { self.handle_serverbound_packet(entities, self.recv.try_recv().unwrap()); } } fn handle_serverbound_packet(&mut self, entities: &Entities, pkt: C2sPlayPacket) { - let client = &mut self.0; - fn handle_movement_packet( client: &mut Client, _vehicle: bool, @@ -419,18 +371,18 @@ impl<'a> ClientMut<'a> { match pkt { C2sPlayPacket::TeleportConfirm(p) => { - if client.pending_teleports == 0 { + if self.pending_teleports == 0 { self.disconnect("Unexpected teleport confirmation"); return; } let got = p.teleport_id.0 as u32; - let expected = client + let expected = self .teleport_id_counter - .wrapping_sub(client.pending_teleports); + .wrapping_sub(self.pending_teleports); if got == expected { - client.pending_teleports -= 1; + self.pending_teleports -= 1; } else { self.disconnect(format!( "Unexpected teleport ID (expected {expected}, got {got})" @@ -444,7 +396,7 @@ impl<'a> ClientMut<'a> { C2sPlayPacket::ChatPreview(_) => {} C2sPlayPacket::ClientStatus(_) => {} C2sPlayPacket::ClientSettings(p) => { - let old = client.settings.replace(Settings { + let old = self.settings.replace(Settings { locale: p.locale.0, view_distance: p.view_distance.0, chat_mode: p.chat_mode, @@ -454,7 +406,7 @@ impl<'a> ClientMut<'a> { allow_server_listings: p.allow_server_listings, }); - client.events.push(Event::SettingsChanged(old)); + self.events.push(Event::SettingsChanged(old)); } C2sPlayPacket::TabComplete(_) => {} C2sPlayPacket::ClickWindowButton(_) => {} @@ -468,7 +420,7 @@ impl<'a> ClientMut<'a> { // that the distance is <=4 blocks. use crate::packets::play::c2s::InteractType; - client.events.push(Event::InteractWithEntity { + self.events.push(Event::InteractWithEntity { id, sneaking: p.sneaking, typ: match p.typ { @@ -483,8 +435,8 @@ impl<'a> ClientMut<'a> { } C2sPlayPacket::GenerateStructure(_) => {} C2sPlayPacket::KeepAlive(p) => { - let last_keepalive_id = client.last_keepalive_id; - if client.got_keepalive { + let last_keepalive_id = self.last_keepalive_id; + if self.got_keepalive { self.disconnect("Unexpected keepalive"); } else if p.id != last_keepalive_id { self.disconnect(format!( @@ -492,42 +444,32 @@ impl<'a> ClientMut<'a> { last_keepalive_id, p.id )); } else { - client.got_keepalive = true; + self.got_keepalive = true; } } C2sPlayPacket::LockDifficulty(_) => {} - C2sPlayPacket::PlayerPosition(p) => handle_movement_packet( - client, - false, - p.position, - client.yaw, - client.pitch, - p.on_ground, - ), - C2sPlayPacket::PlayerPositionAndRotation(p) => { - handle_movement_packet(client, false, p.position, p.yaw, p.pitch, p.on_ground) + C2sPlayPacket::PlayerPosition(p) => { + handle_movement_packet(self, false, p.position, self.yaw, self.pitch, p.on_ground) + } + C2sPlayPacket::PlayerPositionAndRotation(p) => { + handle_movement_packet(self, false, p.position, p.yaw, p.pitch, p.on_ground) + } + C2sPlayPacket::PlayerRotation(p) => { + handle_movement_packet(self, false, self.new_position, p.yaw, p.pitch, p.on_ground) } - C2sPlayPacket::PlayerRotation(p) => handle_movement_packet( - client, - false, - client.new_position, - p.yaw, - p.pitch, - p.on_ground, - ), C2sPlayPacket::PlayerMovement(p) => handle_movement_packet( - client, + self, false, - client.new_position, - client.yaw, - client.pitch, + self.new_position, + self.yaw, + self.pitch, p.on_ground, ), C2sPlayPacket::VehicleMove(p) => { - handle_movement_packet(client, true, p.position, p.yaw, p.pitch, client.on_ground); + handle_movement_packet(self, true, p.position, p.yaw, p.pitch, self.on_ground); } C2sPlayPacket::SteerBoat(p) => { - client.events.push(Event::SteerBoat { + self.events.push(Event::SteerBoat { left_paddle_turning: p.left_paddle_turning, right_paddle_turning: p.right_paddle_turning, }); @@ -540,10 +482,10 @@ impl<'a> ClientMut<'a> { // TODO: verify that the broken block is allowed to be broken? if p.sequence.0 != 0 { - client.dug_blocks.push(p.sequence.0); + self.dug_blocks.push(p.sequence.0); } - client.events.push(match p.status { + self.events.push(match p.status { DiggingStatus::StartedDigging => Event::Digging(Digging { status: event::DiggingStatus::Start, position: p.location, @@ -599,7 +541,7 @@ impl<'a> ClientMut<'a> { ) { // Mark the client as disconnected when appropriate. if self.recv.is_disconnected() || self.send.as_ref().map_or(true, |s| s.is_disconnected()) { - self.0.send = None; + self.send = None; return; } @@ -641,11 +583,11 @@ impl<'a> ClientMut<'a> { self.teleport(self.position(), self.yaw(), self.pitch()); } else { - if self.0.old_game_mode != self.0.new_game_mode { - self.0.old_game_mode = self.0.new_game_mode; + if self.old_game_mode != self.new_game_mode { + self.old_game_mode = self.new_game_mode; self.send_packet(ChangeGameState { reason: ChangeGameStateReason::ChangeGameMode, - value: self.0.new_game_mode as i32 as f32, + value: self.new_game_mode as i32 as f32, }); } @@ -653,8 +595,8 @@ impl<'a> ClientMut<'a> { } // Update the players spawn position (compass position) - if self.0.modified_spawn_position { - self.0.modified_spawn_position = false; + if self.modified_spawn_position { + self.modified_spawn_position = false; self.send_packet(SpawnPosition { location: self.spawn_position, @@ -663,22 +605,22 @@ impl<'a> ClientMut<'a> { } // Update view distance fog on the client if necessary. - if self.0.old_max_view_distance != self.0.new_max_view_distance { - self.0.old_max_view_distance = self.0.new_max_view_distance; - if self.0.created_tick != current_tick { + if self.old_max_view_distance != self.new_max_view_distance { + self.old_max_view_distance = self.new_max_view_distance; + if self.created_tick != current_tick { self.send_packet(UpdateViewDistance { - view_distance: BoundedInt(VarInt(self.0.new_max_view_distance as i32)), + view_distance: BoundedInt(VarInt(self.new_max_view_distance as i32)), }) } } // Check if it's time to send another keepalive. if current_tick % (server.tick_rate() * 8) == 0 { - if self.0.got_keepalive { + if self.got_keepalive { let id = rand::random(); self.send_packet(KeepAlive { id }); - self.0.last_keepalive_id = id; - self.0.got_keepalive = false; + self.last_keepalive_id = id; + self.got_keepalive = false; } else { self.disconnect("Timed out (no keepalive response)"); } @@ -691,8 +633,8 @@ impl<'a> ClientMut<'a> { // Send the update view position packet if the client changes the chunk section // they're in. { - let old_section = self.0.old_position.map(|n| (n / 16.0).floor() as i32); - let new_section = self.0.new_position.map(|n| (n / 16.0).floor() as i32); + let old_section = self.old_position.map(|n| (n / 16.0).floor() as i32); + let new_section = self.new_position.map(|n| (n / 16.0).floor() as i32); if old_section != new_section { self.send_packet(UpdateViewPosition { @@ -706,7 +648,7 @@ impl<'a> ClientMut<'a> { // Update existing chunks and unload those outside the view distance. Chunks // that have been overwritten also need to be unloaded. - self.0.loaded_chunks.retain(|&pos| { + self.loaded_chunks.retain(|&pos| { // The cache stops chunk data packets from needing to be sent when a player // moves to an adjacent chunk and back to the original. let cache = 2; @@ -716,14 +658,14 @@ impl<'a> ClientMut<'a> { && chunk.created_tick() != current_tick { chunk.block_change_packets(pos, dimension.min_y, |pkt| { - send_packet(&mut self.0.send, pkt) + send_packet(&mut self.send, pkt) }); return true; } } send_packet( - &mut self.0.send, + &mut self.send, UnloadChunk { chunk_x: pos.x, chunk_z: pos.z, @@ -735,7 +677,7 @@ impl<'a> ClientMut<'a> { // Load new chunks within the view distance for pos in chunks_in_view_distance(center, view_dist) { if let Some(chunk) = chunks.get(pos) { - if self.0.loaded_chunks.insert(pos) { + if self.loaded_chunks.insert(pos) { self.send_packet(chunk.chunk_data_packet(pos)); chunk.block_change_packets(pos, dimension.min_y, |pkt| self.send_packet(pkt)); } @@ -743,9 +685,9 @@ impl<'a> ClientMut<'a> { } // Acknowledge broken blocks. - for seq in self.0.dug_blocks.drain(..) { + for seq in self.dug_blocks.drain(..) { send_packet( - &mut self.0.send, + &mut self.send, AcknowledgeBlockChanges { sequence: VarInt(seq), }, @@ -754,8 +696,8 @@ impl<'a> ClientMut<'a> { // This is done after the chunks are loaded so that the "downloading terrain" // screen is closed at the appropriate time. - if self.0.teleported_this_tick { - self.0.teleported_this_tick = false; + if self.teleported_this_tick { + self.teleported_this_tick = false; self.send_packet(PlayerPositionAndLook { position: self.new_position, @@ -771,14 +713,14 @@ impl<'a> ClientMut<'a> { // Update all entities that are visible and unload entities that are no // longer visible. - self.0.loaded_entities.retain(|&id| { + self.loaded_entities.retain(|&id| { if let Some(entity) = entities.get(id) { debug_assert!(entity.typ() != EntityType::Marker); - if self.0.new_position.distance(entity.position()) <= view_dist as f64 * 16.0 + if self.new_position.distance(entity.position()) <= view_dist as f64 * 16.0 && !entity.flags().type_modified() { if let Some(meta) = entity.updated_metadata_packet(id) { - send_packet(&mut self.0.send, meta); + send_packet(&mut self.send, meta); } let position_delta = entity.position() - entity.old_position(); @@ -790,7 +732,7 @@ impl<'a> ClientMut<'a> { && flags.yaw_or_pitch_modified() { send_packet( - &mut self.0.send, + &mut self.send, EntityPositionAndRotation { entity_id: VarInt(id.to_network_id()), delta: (position_delta * 4096.0).as_(), @@ -802,7 +744,7 @@ impl<'a> ClientMut<'a> { } else { if entity.position() != entity.old_position() && !needs_teleport { send_packet( - &mut self.0.send, + &mut self.send, EntityPosition { entity_id: VarInt(id.to_network_id()), delta: (position_delta * 4096.0).as_(), @@ -813,7 +755,7 @@ impl<'a> ClientMut<'a> { if flags.yaw_or_pitch_modified() { send_packet( - &mut self.0.send, + &mut self.send, EntityRotation { entity_id: VarInt(id.to_network_id()), yaw: ByteAngle::from_degrees(entity.yaw()), @@ -826,7 +768,7 @@ impl<'a> ClientMut<'a> { if needs_teleport { send_packet( - &mut self.0.send, + &mut self.send, EntityTeleport { entity_id: VarInt(id.to_network_id()), position: entity.position(), @@ -839,7 +781,7 @@ impl<'a> ClientMut<'a> { if flags.velocity_modified() { send_packet( - &mut self.0.send, + &mut self.send, EntityVelocity { entity_id: VarInt(id.to_network_id()), velocity: velocity_to_packet_units(entity.velocity()), @@ -849,7 +791,7 @@ impl<'a> ClientMut<'a> { if flags.head_yaw_modified() { send_packet( - &mut self.0.send, + &mut self.send, EntityHeadLook { entity_id: VarInt(id.to_network_id()), head_yaw: ByteAngle::from_degrees(entity.head_yaw()), @@ -876,7 +818,7 @@ impl<'a> ClientMut<'a> { spatial_index.query::<_, _, ()>( |bb| bb.projected_point(pos).distance(pos) <= view_dist as f64 * 16.0, |id, _| { - if self.0.loaded_entities.insert(id) { + if self.loaded_entities.insert(id) { let entity = entities.get(id).unwrap(); if entity.typ() != EntityType::Marker { self.send_packet( @@ -894,13 +836,7 @@ impl<'a> ClientMut<'a> { }, ); - self.0.old_position = self.0.new_position; - } -} - -impl Drop for Client { - fn drop(&mut self) { - log::trace!("Dropping client '{}'", self.username); + self.old_position = self.new_position; } } diff --git a/src/config.rs b/src/config.rs index 1fce007..4acf897 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,8 +5,7 @@ use std::panic::{RefUnwindSafe, UnwindSafe}; use async_trait::async_trait; use tokio::runtime::Handle as TokioHandle; -use crate::client::ClientMut; -use crate::{Biome, Dimension, NewClientData, Server, Text, Ticks, WorldId, WorldsMut}; +use crate::{Biome, Client, Dimension, NewClientData, Server, Text, Ticks, WorldId, Worlds}; /// A trait containing callbacks which are invoked by the running Minecraft /// server. @@ -182,7 +181,12 @@ pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe { /// If the returned [`WorldId`] is invalid, then the client is disconnected. /// /// This method is called from within a tokio runtime. - fn join(&self, server: &Server, client: ClientMut, worlds: WorldsMut) -> Result; + fn join( + &self, + server: &Server, + client: &mut Client, + worlds: &mut Worlds, + ) -> Result; /// Called after the server is created, but prior to accepting connections /// and entering the update loop. @@ -191,7 +195,7 @@ pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe { /// no connections to the server will be made until this function returns. /// /// This method is called from within a tokio runtime. - fn init(&self, server: &Server, worlds: WorldsMut) {} + fn init(&self, server: &Server, worlds: &mut Worlds) {} /// Called once at the beginning of every server update (also known as /// a "tick"). @@ -203,7 +207,7 @@ pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe { /// /// # Default Implementation /// The default implementation does nothing. - fn update(&self, server: &Server, worlds: WorldsMut); + fn update(&self, server: &Server, worlds: &mut Worlds); } /// The result of the [`server_list_ping`](Handler::server_list_ping) callback. diff --git a/src/entity.rs b/src/entity.rs index 460dea8..c73c3bd 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -5,10 +5,10 @@ use std::collections::hash_map::Entry; use std::collections::HashMap; use std::iter::FusedIterator; use std::num::NonZeroU32; -use std::ops::Deref; use bitfield_struct::bitfield; use rayon::iter::ParallelIterator; +pub use types::{EntityMeta, EntityType}; use uuid::Uuid; use vek::{Aabb, Vec3}; @@ -27,16 +27,6 @@ pub struct Entities { network_id_to_entity: HashMap, } -pub struct EntitiesMut<'a>(&'a mut Entities); - -impl<'a> Deref for EntitiesMut<'a> { - type Target = Entities; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - impl Entities { pub(crate) fn new() -> Self { Self { @@ -46,8 +36,80 @@ impl Entities { } } + /// Spawns a new entity with the default data. The new entity's [`EntityId`] + /// is returned. + /// + /// To actually see the new entity, set its position to somewhere nearby and + /// [set its type](EntityData::set_type) to something visible. + pub fn create(&mut self) -> (EntityId, &mut Entity) { + self.create_with_uuid(Uuid::from_bytes(rand::random())) + .expect("UUID collision") + } + + /// Like [`create`](Entities::create), but requires specifying the new + /// entity's UUID. + /// + /// The provided UUID must not conflict with an existing entity UUID in this + /// world. If it does, `None` is returned and the entity is not spawned. + pub fn create_with_uuid(&mut self, uuid: Uuid) -> Option<(EntityId, &mut Entity)> { + match self.uuid_to_entity.entry(uuid) { + Entry::Occupied(_) => None, + Entry::Vacant(ve) => { + let (k, e) = self.sm.insert(Entity { + flags: EntityFlags(0), + meta: EntityMeta::new(EntityType::Marker), + new_position: Vec3::default(), + old_position: Vec3::default(), + yaw: 0.0, + pitch: 0.0, + head_yaw: 0.0, + velocity: Vec3::default(), + uuid, + }); + + ve.insert(EntityId(k)); + + Some((EntityId(k), e)) + } + } + } + + pub fn delete(&mut self, entity: EntityId) -> bool { + if let Some(e) = self.sm.remove(entity.0) { + self.uuid_to_entity + .remove(&e.uuid) + .expect("UUID should have been in UUID map"); + + self.network_id_to_entity + .remove(&entity.0.version()) + .expect("network ID should have been in the network ID map"); + + true + } else { + false + } + } + + pub fn retain(&mut self, mut f: impl FnMut(EntityId, &mut Entity) -> bool) { + self.sm.retain(|k, v| { + if f(EntityId(k), v) { + true + } else { + self.uuid_to_entity + .remove(&v.uuid) + .expect("UUID should have been in UUID map"); + + self.network_id_to_entity + .remove(&k.version()) + .expect("network ID should have been in the network ID map"); + + false + } + }); + } + /// Returns the number of live entities. - pub fn len(&self) -> usize { + pub fn count(&self) -> usize { self.sm.len() } @@ -64,6 +126,10 @@ impl Entities { self.sm.get(entity.0) } + pub fn get_mut(&mut self, entity: EntityId) -> Option<&mut Entity> { + self.sm.get_mut(entity.0) + } + pub(crate) fn get_with_network_id(&self, network_id: i32) -> Option { let version = NonZeroU32::new(network_id as u32)?; let index = *self.network_id_to_entity.get(&version)?; @@ -74,122 +140,26 @@ impl Entities { self.sm.iter().map(|(k, v)| (EntityId(k), v)) } + pub fn iter_mut(&mut self) -> impl FusedIterator + '_ { + self.sm.iter_mut().map(|(k, v)| (EntityId(k), v)) + } + pub fn par_iter(&self) -> impl ParallelIterator + Clone + '_ { self.sm.par_iter().map(|(k, v)| (EntityId(k), v)) } -} -impl<'a> EntitiesMut<'a> { - pub(crate) fn new(entities: &'a mut Entities) -> Self { - Self(entities) - } - - pub fn reborrow(&mut self) -> EntitiesMut { - EntitiesMut(self.0) - } - - /// Spawns a new entity with the default data. The new entity's [`EntityId`] - /// is returned. - /// - /// To actually see the new entity, set its position to somewhere nearby and - /// [set its type](EntityData::set_type) to something visible. - pub fn create(&mut self) -> (EntityId, EntityMut) { - self.create_with_uuid(Uuid::from_bytes(rand::random())) - .expect("UUID collision") - } - - /// Like [`create`](Entities::create), but requires specifying the new - /// entity's UUID. - /// - /// The provided UUID must not conflict with an existing entity UUID in this - /// world. If it does, `None` is returned and the entity is not spawned. - pub fn create_with_uuid(&mut self, uuid: Uuid) -> Option<(EntityId, EntityMut)> { - match self.0.uuid_to_entity.entry(uuid) { - Entry::Occupied(_) => None, - Entry::Vacant(ve) => { - let (k, e) = self.0.sm.insert(Entity { - flags: EntityFlags(0), - meta: EntityMeta::new(EntityType::Marker), - new_position: Vec3::default(), - old_position: Vec3::default(), - yaw: 0.0, - pitch: 0.0, - head_yaw: 0.0, - velocity: Vec3::default(), - uuid, - }); - - ve.insert(EntityId(k)); - - Some((EntityId(k), EntityMut(e))) - } - } - } - - pub fn delete(&mut self, entity: EntityId) -> bool { - if let Some(e) = self.0.sm.remove(entity.0) { - self.0 - .uuid_to_entity - .remove(&e.uuid) - .expect("UUID should have been in UUID map"); - - self.0 - .network_id_to_entity - .remove(&entity.0.version()) - .expect("network ID should have been in the network ID map"); - - true - } else { - false - } - } - - pub fn retain(&mut self, mut f: impl FnMut(EntityId, EntityMut) -> bool) { - self.0.sm.retain(|k, v| { - if f(EntityId(k), EntityMut(v)) { - true - } else { - self.0 - .uuid_to_entity - .remove(&v.uuid) - .expect("UUID should have been in UUID map"); - - self.0 - .network_id_to_entity - .remove(&k.version()) - .expect("network ID should have been in the network ID map"); - - false - } - }); - } - - pub fn get_mut(&mut self, entity: EntityId) -> Option { - self.0.sm.get_mut(entity.0).map(EntityMut) - } - - pub fn iter_mut(&mut self) -> impl FusedIterator + '_ { - self.0 - .sm - .iter_mut() - .map(|(k, v)| (EntityId(k), EntityMut(v))) - } - - pub fn par_iter_mut(&mut self) -> impl ParallelIterator + '_ { - self.0 - .sm - .par_iter_mut() - .map(|(k, v)| (EntityId(k), EntityMut(v))) + pub fn par_iter_mut(&mut self) -> impl ParallelIterator + '_ { + self.sm.par_iter_mut().map(|(k, v)| (EntityId(k), v)) } pub(crate) fn update(&mut self) { for (_, e) in self.iter_mut() { - e.0.old_position = e.new_position; - e.0.meta.clear_modifications(); + e.old_position = e.new_position; + e.meta.clear_modifications(); - let on_ground = e.0.flags.on_ground(); - e.0.flags = EntityFlags(0); - e.0.flags.set_on_ground(on_ground); + let on_ground = e.flags.on_ground(); + e.flags = EntityFlags(0); + e.flags.set_on_ground(on_ground); } } } @@ -215,16 +185,6 @@ pub struct Entity { uuid: Uuid, } -pub struct EntityMut<'a>(&'a mut Entity); - -impl<'a> Deref for EntityMut<'a> { - type Target = Entity; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - /// Contains a bit for certain fields in [`Entity`] to track if they have been /// modified. #[bitfield(u8)] @@ -249,16 +209,35 @@ impl Entity { &self.meta } + /// Returns a mutable reference to this entity's [`EntityMeta`]. + pub fn meta_mut(&mut self) -> &mut EntityMeta { + &mut self.meta + } + /// Returns the [`EntityType`] of this entity. pub fn typ(&self) -> EntityType { self.meta.typ() } + /// Changes the [`EntityType`] of this entity to the provided type. + /// + /// All metadata of this entity is reset to the default values. + pub fn set_type(&mut self, typ: EntityType) { + self.meta = EntityMeta::new(typ); + // All metadata is lost so we must mark it as modified unconditionally. + self.flags.set_type_modified(true); + } + /// Returns the position of this entity in the world it inhabits. pub fn position(&self) -> Vec3 { self.new_position } + /// Sets the position of this entity in the world it inhabits. + pub fn set_position(&mut self, pos: impl Into>) { + self.new_position = pos.into(); + } + /// Returns the position of this entity as it existed at the end of the /// previous tick. pub fn old_position(&self) -> Vec3 { @@ -270,25 +249,62 @@ impl Entity { self.yaw } + /// Sets the yaw of this entity (in degrees). + pub fn set_yaw(&mut self, yaw: f32) { + if self.yaw != yaw { + self.yaw = yaw; + self.flags.set_yaw_or_pitch_modified(true); + } + } + /// Gets the pitch of this entity (in degrees). pub fn pitch(&self) -> f32 { self.pitch } + /// Sets the pitch of this entity (in degrees). + pub fn set_pitch(&mut self, pitch: f32) { + if self.pitch != pitch { + self.pitch = pitch; + self.flags.set_yaw_or_pitch_modified(true); + } + } + /// Gets the head yaw of this entity (in degrees). pub fn head_yaw(&self) -> f32 { self.head_yaw } + /// Sets the head yaw of this entity (in degrees). + pub fn set_head_yaw(&mut self, head_yaw: f32) { + if self.head_yaw != head_yaw { + self.head_yaw = head_yaw; + self.flags.set_head_yaw_modified(true); + } + } + /// Gets the velocity of this entity in meters per second. pub fn velocity(&self) -> Vec3 { self.velocity } + pub fn set_velocity(&mut self, velocity: impl Into>) { + let new_vel = velocity.into(); + + if self.velocity != new_vel { + self.velocity = new_vel; + self.flags.set_velocity_modified(true); + } + } + pub fn on_ground(&self) -> bool { self.flags.on_ground() } + pub fn set_on_ground(&mut self, on_ground: bool) { + self.flags.set_on_ground(on_ground); + } + /// Gets the metadata packet to send to clients after this entity has been /// spawned. /// @@ -490,69 +506,6 @@ pub(crate) fn velocity_to_packet_units(vel: Vec3) -> Vec3 { (vel * 400.0).as_() } -impl<'a> EntityMut<'a> { - // TODO: exposing &mut EntityMeta is unsound? - /// Returns a mutable reference to this entity's [`EntityMeta`]. - /// - /// **NOTE:** Never call [`std::mem::swap`] on the returned reference or any - /// part of `EntityMeta` as this would break invariants within the - /// library. - pub fn meta_mut(&mut self) -> &mut EntityMeta { - &mut self.0.meta - } - - /// Changes the [`EntityType`] of this entity to the provided type. - /// - /// All metadata of this entity is reset to the default values. - pub fn set_type(&mut self, typ: EntityType) { - self.0.meta = EntityMeta::new(typ); - // All metadata is lost so we must mark it as modified unconditionally. - self.0.flags.set_type_modified(true); - } - - /// Sets the position of this entity in the world it inhabits. - pub fn set_position(&mut self, pos: impl Into>) { - self.0.new_position = pos.into(); - } - - /// Sets the yaw of this entity (in degrees). - pub fn set_yaw(&mut self, yaw: f32) { - if self.0.yaw != yaw { - self.0.yaw = yaw; - self.0.flags.set_yaw_or_pitch_modified(true); - } - } - - /// Sets the pitch of this entity (in degrees). - pub fn set_pitch(&mut self, pitch: f32) { - if self.0.pitch != pitch { - self.0.pitch = pitch; - self.0.flags.set_yaw_or_pitch_modified(true); - } - } - - /// Sets the head yaw of this entity (in degrees). - pub fn set_head_yaw(&mut self, head_yaw: f32) { - if self.0.head_yaw != head_yaw { - self.0.head_yaw = head_yaw; - self.0.flags.set_head_yaw_modified(true); - } - } - - pub fn set_velocity(&mut self, velocity: impl Into>) { - let new_vel = velocity.into(); - - if self.0.velocity != new_vel { - self.0.velocity = new_vel; - self.0.flags.set_velocity_modified(true); - } - } - - pub fn set_on_ground(&mut self, on_ground: bool) { - self.0.flags.set_on_ground(on_ground); - } -} - pub(crate) enum EntitySpawnPacket { SpawnEntity(SpawnEntity), SpawnExperienceOrb(SpawnExperienceOrb), @@ -568,5 +521,3 @@ impl From for S2cPlayPacket { } } } - -pub use types::{EntityMeta, EntityType}; diff --git a/src/lib.rs b/src/lib.rs index 2d019e6..f6bca66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,7 @@ pub mod entity; pub mod ident; mod packets; mod player_list; +pub mod player_textures; mod protocol; pub mod server; mod slotmap; @@ -31,24 +32,23 @@ pub mod util; mod var_int; mod var_long; pub mod world; -pub mod player_textures; pub use async_trait::async_trait; pub use biome::{Biome, BiomeId}; pub use block::BlockState; pub use block_pos::BlockPos; -pub use chunk::{Chunk, Chunks, ChunksMut}; +pub use chunk::{Chunk, Chunks}; pub use chunk_pos::ChunkPos; -pub use client::{Client, ClientMut, Clients, ClientsMut}; +pub use client::{Client, Clients}; pub use config::Config; pub use dimension::{Dimension, DimensionId}; -pub use entity::{Entities, EntitiesMut, Entity, EntityId, EntityType}; +pub use entity::{Entities, Entity, EntityId, EntityType}; pub use ident::Ident; pub use server::{start_server, NewClientData, Server, ShutdownResult}; -pub use spatial_index::{SpatialIndex, SpatialIndexMut}; +pub use spatial_index::SpatialIndex; pub use text::{Text, TextFormat}; pub use uuid::Uuid; -pub use world::{WorldId, WorldMeta, WorldMetaMut, WorldMut, WorldRef, Worlds, WorldsMut}; +pub use world::{WorldId, WorldMeta, Worlds}; pub use {nbt, uuid, vek}; /// The Minecraft protocol version that this library targets. diff --git a/src/player_list.rs b/src/player_list.rs index d876fb5..8df0bd7 100644 --- a/src/player_list.rs +++ b/src/player_list.rs @@ -1,6 +1,5 @@ use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; -use std::ops::Deref; use bitfield_struct::bitfield; use uuid::Uuid; @@ -33,18 +32,91 @@ impl PlayerList { } } + pub fn insert( + &mut self, + uuid: Uuid, + username: impl Into, + textures: Option, + game_mode: GameMode, + ping: i32, + display_name: impl Into>, + ) { + match self.entries.entry(uuid) { + Entry::Occupied(mut oe) => { + let e = oe.get_mut(); + let username = username.into(); + + if e.username() != username || e.textures != textures { + self.removed.insert(*oe.key()); + + oe.insert(PlayerListEntry { + username, + textures, + game_mode, + ping, + display_name: display_name.into(), + flags: EntryFlags::new().with_created_this_tick(true), + }); + } else { + e.set_game_mode(game_mode); + e.set_ping(ping); + e.set_display_name(display_name); + } + } + Entry::Vacant(ve) => { + ve.insert(PlayerListEntry { + username: username.into(), + textures, + game_mode, + ping, + display_name: display_name.into(), + flags: EntryFlags::new().with_created_this_tick(true), + }); + } + } + } + + pub fn remove(&mut self, uuid: Uuid) -> bool { + if self.entries.remove(&uuid).is_some() { + self.removed.insert(uuid); + true + } else { + false + } + } + pub fn header(&self) -> &Text { &self.header } + pub fn set_header(&mut self, header: impl Into) { + let header = header.into(); + if self.header != header { + self.header = header; + self.modified_header_or_footer = true; + } + } + pub fn footer(&self) -> &Text { &self.footer } + pub fn set_footer(&mut self, footer: impl Into) { + let footer = footer.into(); + if self.footer != footer { + self.footer = footer; + self.modified_header_or_footer = true; + } + } + pub fn entries(&self) -> impl Iterator + '_ { self.entries.iter().map(|(k, v)| (*k, v)) } + pub fn entries_mut(&mut self) -> impl Iterator + '_ { + self.entries.iter_mut().map(|(k, v)| (*k, v)) + } + pub(crate) fn initial_packets(&self, mut packet: impl FnMut(S2cPlayPacket)) { let add_player: Vec<_> = self .entries @@ -158,105 +230,13 @@ impl PlayerList { ); } } -} - -pub struct PlayerListMut<'a>(pub(crate) &'a mut PlayerList); - -impl<'a> Deref for PlayerListMut<'a> { - type Target = PlayerList; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a> PlayerListMut<'a> { - pub fn reborrow(&mut self) -> PlayerListMut { - PlayerListMut(self.0) - } - - pub fn insert( - &mut self, - uuid: Uuid, - username: impl Into, - textures: Option, - game_mode: GameMode, - ping: i32, - display_name: impl Into>, - ) { - match self.0.entries.entry(uuid) { - Entry::Occupied(mut oe) => { - let mut e = PlayerListEntryMut(oe.get_mut()); - let username = username.into(); - - if e.username() != username || e.textures != textures { - self.0.removed.insert(*oe.key()); - - oe.insert(PlayerListEntry { - username, - textures, - game_mode, - ping, - display_name: display_name.into(), - flags: EntryFlags::new().with_created_this_tick(true), - }); - } else { - e.set_game_mode(game_mode); - e.set_ping(ping); - e.set_display_name(display_name); - } - } - Entry::Vacant(ve) => { - ve.insert(PlayerListEntry { - username: username.into(), - textures, - game_mode, - ping, - display_name: display_name.into(), - flags: EntryFlags::new().with_created_this_tick(true), - }); - } - } - } - - pub fn remove(&mut self, uuid: Uuid) -> bool { - if self.0.entries.remove(&uuid).is_some() { - self.0.removed.insert(uuid); - true - } else { - false - } - } - - pub fn set_header(&mut self, header: impl Into) { - let header = header.into(); - if self.0.header != header { - self.0.header = header; - self.0.modified_header_or_footer = true; - } - } - - pub fn set_footer(&mut self, footer: impl Into) { - let footer = footer.into(); - if self.0.footer != footer { - self.0.footer = footer; - self.0.modified_header_or_footer = true; - } - } - - pub fn entries_mut(&mut self) -> impl Iterator + '_ { - self.0 - .entries - .iter_mut() - .map(|(k, v)| (*k, PlayerListEntryMut(v))) - } pub(crate) fn update(&mut self) { - for e in self.0.entries.values_mut() { + for e in self.entries.values_mut() { e.flags = EntryFlags(0); } - self.0.removed.clear(); - self.0.modified_header_or_footer = false; + self.removed.clear(); + self.modified_header_or_footer = false; } } @@ -282,49 +262,33 @@ impl PlayerListEntry { self.game_mode } + pub fn set_game_mode(&mut self, game_mode: GameMode) { + if self.game_mode != game_mode { + self.game_mode = game_mode; + self.flags.set_modified_game_mode(true); + } + } + pub fn ping(&self) -> i32 { self.ping } + pub fn set_ping(&mut self, ping: i32) { + if self.ping != ping { + self.ping = ping; + self.flags.set_modified_ping(true); + } + } + pub fn display_name(&self) -> Option<&Text> { self.display_name.as_ref() } -} - -pub struct PlayerListEntryMut<'a>(&'a mut PlayerListEntry); - -impl<'a> Deref for PlayerListEntryMut<'a> { - type Target = PlayerListEntry; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a> PlayerListEntryMut<'a> { - pub fn reborrow(&mut self) -> PlayerListEntryMut { - PlayerListEntryMut(self.0) - } - - pub fn set_game_mode(&mut self, game_mode: GameMode) { - if self.0.game_mode != game_mode { - self.0.game_mode = game_mode; - self.0.flags.set_modified_game_mode(true); - } - } - - pub fn set_ping(&mut self, ping: i32) { - if self.0.ping != ping { - self.0.ping = ping; - self.0.flags.set_modified_ping(true); - } - } pub fn set_display_name(&mut self, display_name: impl Into>) { let display_name = display_name.into(); - if self.0.display_name != display_name { - self.0.display_name = display_name; - self.0.flags.set_modified_display_name(true); + if self.display_name != display_name { + self.display_name = display_name; + self.flags.set_modified_display_name(true); } } } diff --git a/src/server.rs b/src/server.rs index 4160400..02b8392 100644 --- a/src/server.rs +++ b/src/server.rs @@ -42,8 +42,7 @@ use crate::util::valid_username; use crate::var_int::VarInt; use crate::world::Worlds; use crate::{ - Biome, BiomeId, Client, ClientMut, Dimension, DimensionId, Ticks, WorldsMut, PROTOCOL_VERSION, - VERSION_NAME, + Biome, BiomeId, Client, Dimension, DimensionId, Ticks, PROTOCOL_VERSION, VERSION_NAME, }; /// A handle to a running Minecraft server containing state which is accessible @@ -215,13 +214,12 @@ pub fn start_server(config: impl Config) -> ShutdownResult { let _guard = server.tokio_handle().enter(); let mut worlds = Worlds::new(server.clone()); - let mut worlds_mut = WorldsMut::new(&mut worlds); - server.config().init(&server, worlds_mut.reborrow()); + server.config().init(&server, &mut worlds); tokio::spawn(do_accept_loop(server.clone())); - do_update_loop(server, worlds_mut) + do_update_loop(server, &mut worlds) } fn setup_server(cfg: impl Config) -> anyhow::Result { @@ -350,7 +348,7 @@ fn setup_server(cfg: impl Config) -> anyhow::Result { Ok(Server(Arc::new(server))) } -fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult { +fn do_update_loop(server: Server, worlds: &mut Worlds) -> ShutdownResult { let mut tick_start = Instant::now(); loop { @@ -359,20 +357,20 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult { } while let Ok(msg) = server.0.new_clients_rx.try_recv() { - join_player(&server, worlds.reborrow(), msg); + join_player(&server, worlds, msg); } // Get serverbound packets first so they are not dealt with a tick late. - worlds.par_iter_mut().for_each(|(_, mut world)| { - world.clients.par_iter_mut().for_each(|(_, mut client)| { + worlds.par_iter_mut().for_each(|(_, world)| { + world.clients.par_iter_mut().for_each(|(_, client)| { client.handle_serverbound_packets(&world.entities); }); }); - server.config().update(&server, worlds.reborrow()); + server.config().update(&server, worlds); - worlds.par_iter_mut().for_each(|(_, mut world)| { - world.chunks.par_iter_mut().for_each(|(_, mut chunk)| { + worlds.par_iter_mut().for_each(|(_, world)| { + world.chunks.par_iter_mut().for_each(|(_, chunk)| { if chunk.created_tick() == server.current_tick() { // Chunks created this tick can have their changes applied immediately because // they have not been observed by clients yet. Clients will not have to be sent @@ -381,9 +379,9 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult { } }); - world.spatial_index.update(world.entities.reborrow()); + world.spatial_index.update(&world.entities); - world.clients.par_iter_mut().for_each(|(_, mut client)| { + world.clients.par_iter_mut().for_each(|(_, client)| { client.update( &server, &world.entities, @@ -395,7 +393,7 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult { world.entities.update(); - world.chunks.par_iter_mut().for_each(|(_, mut chunk)| { + world.chunks.par_iter_mut().for_each(|(_, chunk)| { chunk.apply_modifications(); }); @@ -411,7 +409,7 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult { } } -fn join_player(server: &Server, mut worlds: WorldsMut, msg: NewClientMessage) { +fn join_player(server: &Server, worlds: &mut Worlds, msg: NewClientMessage) { let (clientbound_tx, clientbound_rx) = flume::bounded(server.0.outgoing_packet_capacity); let (serverbound_tx, serverbound_rx) = flume::bounded(server.0.incoming_packet_capacity); @@ -421,15 +419,10 @@ fn join_player(server: &Server, mut worlds: WorldsMut, msg: NewClientMessage) { let _ = msg.reply.send(s2c_packet_channels); let mut client = Client::new(c2s_packet_channels, server, msg.ncd); - let mut client_mut = ClientMut::new(&mut client); - match server - .0 - .cfg - .join(server, client_mut.reborrow(), worlds.reborrow()) - { + match server.0.cfg.join(server, &mut client, worlds) { Ok(world_id) => { - if let Some(mut world) = worlds.get_mut(world_id) { + if let Some(world) = worlds.get_mut(world_id) { if world.entities.get_with_uuid(client.uuid()).is_none() { world.clients.create(client); } else { @@ -448,7 +441,7 @@ fn join_player(server: &Server, mut worlds: WorldsMut, msg: NewClientMessage) { ); } } - Err(errmsg) => client_mut.disconnect(errmsg), + Err(errmsg) => client.disconnect(errmsg), } } diff --git a/src/spatial_index.rs b/src/spatial_index.rs index af9414c..c157b77 100644 --- a/src/spatial_index.rs +++ b/src/spatial_index.rs @@ -1,25 +1,13 @@ -use std::ops::Deref; - use vek::{Aabb, Vec3}; use crate::bvh::Bvh; pub use crate::bvh::TraverseStep; -use crate::{EntitiesMut, EntityId}; +use crate::{Entities, EntityId}; pub struct SpatialIndex { bvh: Bvh, } -pub struct SpatialIndexMut<'a>(&'a mut SpatialIndex); - -impl<'a> Deref for SpatialIndexMut<'a> { - type Target = SpatialIndex; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - impl SpatialIndex { pub(crate) fn new() -> Self { Self { bvh: Bvh::new() } @@ -107,16 +95,9 @@ impl SpatialIndex { }, ) } -} -impl<'a> SpatialIndexMut<'a> { - pub(crate) fn new(si: &'a mut SpatialIndex) -> Self { - Self(si) - } - - pub(crate) fn update(&mut self, entities: EntitiesMut) { - self.0 - .bvh + pub(crate) fn update(&mut self, entities: &Entities) { + self.bvh .build(entities.iter().map(|(id, e)| (id, e.hitbox()))) } } diff --git a/src/world.rs b/src/world.rs index 15e22f5..5cd4a73 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,30 +1,16 @@ use std::iter::FusedIterator; -use std::ops::Deref; use rayon::iter::ParallelIterator; -use crate::player_list::{PlayerList, PlayerListMut}; +use crate::player_list::PlayerList; use crate::slotmap::{Key, SlotMap}; -use crate::{ - Chunks, ChunksMut, Clients, ClientsMut, DimensionId, Entities, EntitiesMut, Server, - SpatialIndex, SpatialIndexMut, -}; +use crate::{Chunks, Clients, DimensionId, Entities, Server, SpatialIndex}; pub struct Worlds { sm: SlotMap, server: Server, } -pub struct WorldsMut<'a>(&'a mut Worlds); - -impl<'a> Deref for WorldsMut<'a> { - type Target = Worlds; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct WorldId(Key); @@ -36,30 +22,8 @@ impl Worlds { } } - pub fn count(&self) -> usize { - self.sm.len() - } - - pub fn get(&self, world: WorldId) -> Option { - self.sm.get(world.0).map(WorldRef::new) - } - - pub fn iter(&self) -> impl FusedIterator + Clone + '_ { - self.sm.iter().map(|(k, v)| (WorldId(k), WorldRef::new(v))) - } -} - -impl<'a> WorldsMut<'a> { - pub(crate) fn new(worlds: &'a mut Worlds) -> Self { - Self(worlds) - } - - pub fn reborrow(&mut self) -> WorldsMut { - WorldsMut(self.0) - } - - pub fn create(&mut self, dim: DimensionId) -> (WorldId, WorldMut) { - let (id, world) = self.0.sm.insert(World { + pub fn create(&mut self, dim: DimensionId) -> (WorldId, &mut World) { + let (id, world) = self.sm.insert(World { clients: Clients::new(), entities: Entities::new(), spatial_index: SpatialIndex::new(), @@ -71,7 +35,7 @@ impl<'a> WorldsMut<'a> { }, }); - (WorldId(id), WorldMut::new(world)) + (WorldId(id), world) } /// Deletes a world from the server. Any [`WorldId`] referring to the @@ -80,106 +44,48 @@ impl<'a> WorldsMut<'a> { /// Note that any entities with positions inside the deleted world will not /// be deleted themselves. pub fn delete(&mut self, world: WorldId) -> bool { - self.0.sm.remove(world.0).is_some() + self.sm.remove(world.0).is_some() } - pub fn retain(&mut self, mut f: impl FnMut(WorldId, WorldMut) -> bool) { - self.0.sm.retain(|k, v| f(WorldId(k), WorldMut::new(v))) + pub fn retain(&mut self, mut f: impl FnMut(WorldId, &mut World) -> bool) { + self.sm.retain(|k, v| f(WorldId(k), v)) } - pub fn get_mut(&mut self, world: WorldId) -> Option { - self.0.sm.get_mut(world.0).map(WorldMut::new) + pub fn count(&self) -> usize { + self.sm.len() } - pub fn iter_mut(&mut self) -> impl FusedIterator + '_ { - self.0 - .sm - .iter_mut() - .map(|(k, v)| (WorldId(k), WorldMut::new(v))) + pub fn get(&self, world: WorldId) -> Option<&World> { + self.sm.get(world.0) } - pub fn par_iter(&self) -> impl ParallelIterator + Clone + '_ { - self.0 - .sm - .par_iter() - .map(|(k, v)| (WorldId(k), WorldRef::new(v))) + pub fn get_mut(&mut self, world: WorldId) -> Option<&mut World> { + self.sm.get_mut(world.0) } - pub fn par_iter_mut(&mut self) -> impl ParallelIterator + '_ { - self.0 - .sm - .par_iter_mut() - .map(|(k, v)| (WorldId(k), WorldMut::new(v))) + pub fn iter(&self) -> impl FusedIterator + Clone + '_ { + self.sm.iter().map(|(k, v)| (WorldId(k), v)) + } + + pub fn iter_mut(&mut self) -> impl FusedIterator + '_ { + self.sm.iter_mut().map(|(k, v)| (WorldId(k), v)) + } + + pub fn par_iter(&self) -> impl ParallelIterator + Clone + '_ { + self.sm.par_iter().map(|(k, v)| (WorldId(k), v)) + } + + pub fn par_iter_mut(&mut self) -> impl ParallelIterator + '_ { + self.sm.par_iter_mut().map(|(k, v)| (WorldId(k), v)) } } -/// A world on the server is a space for chunks, entities, and clients to -/// inhabit. -/// -/// Worlds maintain a collection of chunks and entities that are a part of it. -/// For a chunk or entity to appear, it must be inserted into the world. Chunks -/// and entities can be in multiple worlds at the same time. -/// -/// Deleted chunks and entities are automatically removed from worlds at the end -/// of each tick. -pub(crate) struct World { - clients: Clients, - entities: Entities, - spatial_index: SpatialIndex, - chunks: Chunks, - meta: WorldMeta, -} - -/// A bag of immutable references to the components of a world. -pub struct WorldRef<'a> { - pub clients: &'a Clients, - pub entities: &'a Entities, - pub spatial_index: &'a SpatialIndex, - pub chunks: &'a Chunks, - pub meta: &'a WorldMeta, -} - -impl<'a> WorldRef<'a> { - pub(crate) fn new(w: &'a World) -> Self { - Self { - clients: &w.clients, - entities: &w.entities, - spatial_index: &w.spatial_index, - chunks: &w.chunks, - meta: &w.meta, - } - } -} - -/// A bag of mutable references to the components of a world. -pub struct WorldMut<'a> { - pub clients: ClientsMut<'a>, - pub entities: EntitiesMut<'a>, - pub spatial_index: SpatialIndexMut<'a>, - pub chunks: ChunksMut<'a>, - pub meta: WorldMetaMut<'a>, -} - -impl<'a> WorldMut<'a> { - pub(crate) fn new(w: &'a mut World) -> Self { - WorldMut { - clients: ClientsMut::new(&mut w.clients), - entities: EntitiesMut::new(&mut w.entities), - spatial_index: SpatialIndexMut::new(&mut w.spatial_index), - chunks: ChunksMut::new(&mut w.chunks), - meta: WorldMetaMut(&mut w.meta), - } - } - - pub fn immutable(&'a self) -> WorldRef<'a> { - WorldRef { - clients: &self.clients, - entities: &self.entities, - spatial_index: &self.spatial_index, - chunks: &self.chunks, - meta: &self.meta, - } - } +pub struct World { + pub clients: Clients, + pub entities: Entities, + pub spatial_index: SpatialIndex, + pub chunks: Chunks, + pub meta: WorldMeta, } pub struct WorldMeta { @@ -198,31 +104,19 @@ impl WorldMeta { self.is_flat } + pub fn set_flat(&mut self, flat: bool) { + self.is_flat = flat; + } + pub fn player_list(&self) -> &PlayerList { &self.player_list } -} -pub struct WorldMetaMut<'a>(&'a mut WorldMeta); - -impl<'a> Deref for WorldMetaMut<'a> { - type Target = WorldMeta; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a> WorldMetaMut<'a> { - pub fn set_flat(&mut self, flat: bool) { - self.0.is_flat = flat; - } - - pub fn player_list_mut(&mut self) -> PlayerListMut { - PlayerListMut(&mut self.0.player_list) + pub fn player_list_mut(&mut self) -> &mut PlayerList { + &mut self.player_list } pub(crate) fn update(&mut self) { - self.player_list_mut().update(); + self.player_list.update(); } }