diff --git a/examples/combat.rs b/examples/combat.rs index bd8dd2a..b8c9ba8 100644 --- a/examples/combat.rs +++ b/examples/combat.rs @@ -83,7 +83,7 @@ impl Config for Game { } fn init(&self, server: &mut Server) { - let (_, world) = server.worlds.create(DimensionId::default(), ()); + let (_, world) = server.worlds.insert(DimensionId::default(), ()); world.meta.set_flat(true); let min_y = server.shared.dimension(DimensionId::default()).min_y; @@ -92,7 +92,7 @@ impl Config for Game { let size = 2; for chunk_z in -size - 2..size + 2 { for chunk_x in -size - 2..size + 2 { - let chunk = world.chunks.create([chunk_x, chunk_z], ()); + let chunk = world.chunks.insert([chunk_x, chunk_z], ()); let r = -size..size; if r.contains(&chunk_x) && r.contains(&chunk_z) { for z in 0..16 { @@ -131,7 +131,7 @@ impl Config for Game { return false; } - let (player_id, player) = match server.entities.create_with_uuid( + let (player_id, player) = match server.entities.insert_with_uuid( EntityKind::Player, client.uuid(), EntityState::default(), @@ -177,7 +177,7 @@ impl Config for Game { if client.is_disconnected() { self.player_count.fetch_sub(1, Ordering::SeqCst); - server.entities.delete(client.state.player); + server.entities.remove(client.state.player); world.meta.player_list_mut().remove(client.uuid()); return false; } diff --git a/examples/conway.rs b/examples/conway.rs index dffeb8f..8cc0f43 100644 --- a/examples/conway.rs +++ b/examples/conway.rs @@ -95,12 +95,12 @@ impl Config for Game { } fn init(&self, server: &mut Server) { - let world = server.worlds.create(DimensionId::default(), ()).1; + let world = server.worlds.insert(DimensionId::default(), ()).1; world.meta.set_flat(true); for chunk_z in -2..Integer::div_ceil(&(SIZE_X as i32), &16) + 2 { for chunk_x in -2..Integer::div_ceil(&(SIZE_Z as i32), &16) + 2 { - world.chunks.create((chunk_x as i32, chunk_z as i32), ()); + world.chunks.insert((chunk_x as i32, chunk_z as i32), ()); } } } @@ -129,7 +129,7 @@ impl Config for Game { match server .entities - .create_with_uuid(EntityKind::Player, client.uuid(), ()) + .insert_with_uuid(EntityKind::Player, client.uuid(), ()) { Some((id, _)) => client.state = id, None => { @@ -156,7 +156,7 @@ impl Config for Game { if client.is_disconnected() { self.player_count.fetch_sub(1, Ordering::SeqCst); - server.entities.delete(client.state); + server.entities.remove(client.state); world.meta.player_list_mut().remove(client.uuid()); return false; } diff --git a/examples/cow_sphere.rs b/examples/cow_sphere.rs index 69e5489..8549979 100644 --- a/examples/cow_sphere.rs +++ b/examples/cow_sphere.rs @@ -73,20 +73,20 @@ impl Config for Game { } fn init(&self, server: &mut Server) { - let (world_id, world) = server.worlds.create(DimensionId::default(), ()); + let (world_id, world) = server.worlds.insert(DimensionId::default(), ()); world.meta.set_flat(true); let size = 5; for z in -size..size { for x in -size..size { - world.chunks.create([x, z], ()); + world.chunks.insert([x, z], ()); } } world.chunks.set_block_state(SPAWN_POS, BlockState::BEDROCK); server.state.cows.extend((0..200).map(|_| { - let (id, e) = server.entities.create(EntityKind::Cow, ()); + let (id, e) = server.entities.insert(EntityKind::Cow, ()); e.set_world(world_id); id })); @@ -110,7 +110,7 @@ impl Config for Game { match server .entities - .create_with_uuid(EntityKind::Player, client.uuid(), ()) + .insert_with_uuid(EntityKind::Player, client.uuid(), ()) { Some((id, _)) => client.state = id, None => { @@ -144,7 +144,7 @@ impl Config for Game { if client.is_disconnected() { self.player_count.fetch_sub(1, Ordering::SeqCst); world.meta.player_list_mut().remove(client.uuid()); - server.entities.delete(client.state); + server.entities.remove(client.state); return false; } diff --git a/examples/raycast.rs b/examples/raycast.rs index 021579f..27e6033 100644 --- a/examples/raycast.rs +++ b/examples/raycast.rs @@ -72,13 +72,13 @@ impl Config for Game { } fn init(&self, server: &mut Server) { - let (world_id, world) = server.worlds.create(DimensionId::default(), ()); + let (world_id, world) = server.worlds.insert(DimensionId::default(), ()); world.meta.set_flat(true); let size = 5; for z in -size..size { for x in -size..size { - world.chunks.create([x, z], ()); + world.chunks.insert([x, z], ()); } } @@ -88,7 +88,7 @@ impl Config for Game { for i in 0..SHEEP_COUNT { let offset = (i as f64 - (SHEEP_COUNT - 1) as f64 / 2.0) * 1.25; - let (_, sheep) = server.entities.create(EntityKind::Sheep, false); + let (_, sheep) = server.entities.insert(EntityKind::Sheep, false); sheep.set_world(world_id); sheep.set_position([offset + 0.5, SPAWN_POS.y as f64 + 1.0, 0.0]); sheep.set_yaw(180.0); @@ -114,7 +114,7 @@ impl Config for Game { match server .entities - .create_with_uuid(EntityKind::Player, client.uuid(), false) + .insert_with_uuid(EntityKind::Player, client.uuid(), false) { Some((id, _)) => client.state = id, None => { @@ -154,7 +154,7 @@ impl Config for Game { if client.is_disconnected() { self.player_count.fetch_sub(1, Ordering::SeqCst); world.meta.player_list_mut().remove(client.uuid()); - server.entities.delete(client.state); + server.entities.remove(client.state); return false; } diff --git a/examples/terrain.rs b/examples/terrain.rs index f689005..38f39b4 100644 --- a/examples/terrain.rs +++ b/examples/terrain.rs @@ -82,7 +82,7 @@ impl Config for Game { } fn init(&self, server: &mut Server) { - let (_, world) = server.worlds.create(DimensionId::default(), ()); + let (_, world) = server.worlds.insert(DimensionId::default(), ()); world.meta.set_flat(true); } @@ -106,7 +106,7 @@ impl Config for Game { match server .entities - .create_with_uuid(EntityKind::Player, client.uuid(), ()) + .insert_with_uuid(EntityKind::Player, client.uuid(), ()) { Some((id, _)) => client.state = id, None => { @@ -134,7 +134,7 @@ impl Config for Game { if client.is_disconnected() { self.player_count.fetch_sub(1, Ordering::SeqCst); world.meta.player_list_mut().remove(client.uuid()); - server.entities.delete(client.state); + server.entities.remove(client.state); return false; } @@ -149,7 +149,7 @@ impl Config for Game { for pos in chunks_in_view_distance(ChunkPos::at(p.x, p.z), dist) { chunks_to_unload.remove(&pos); if world.chunks.get(pos).is_none() { - world.chunks.create(pos, ()); + world.chunks.insert(pos, ()); } } @@ -157,7 +157,7 @@ impl Config for Game { }); for pos in chunks_to_unload { - world.chunks.delete(pos); + world.chunks.remove(pos); } world.chunks.par_iter_mut().for_each(|(pos, chunk)| { diff --git a/src/chunk.rs b/src/chunk.rs index 6021a63..a6f4903 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -41,7 +41,7 @@ impl Chunks { } /// Creates an empty chunk at the provided position and returns a mutable - /// refernce to it. + /// reference to it. /// /// If a chunk at the position already exists, then the old chunk /// is overwritten. @@ -50,9 +50,9 @@ impl Chunks { /// adjacent to it must also be loaded. It is also important that clients /// are not spawned within unloaded chunks via /// [`spawn`](crate::client::Client::spawn). - pub fn create(&mut self, pos: impl Into, data: C::ChunkState) -> &mut Chunk { + pub fn insert(&mut self, pos: impl Into, state: C::ChunkState) -> &mut Chunk { let section_count = (self.server.dimension(self.dimension).height / 16) as u32; - let chunk = Chunk::new(section_count, self.server.current_tick(), data); + let chunk = Chunk::new(section_count, self.server.current_tick(), state); match self.chunks.entry(pos.into()) { Entry::Occupied(mut oe) => { @@ -67,12 +67,12 @@ impl Chunks { /// /// If a chunk exists at the position, then it is deleted and `true` is /// returned. Otherwise, `false` is returned. - pub fn delete(&mut self, pos: impl Into) -> bool { - self.chunks.remove(&pos.into()).is_some() + pub fn remove(&mut self, pos: impl Into) -> Option { + self.chunks.remove(&pos.into()).map(|c| c.state) } /// Returns the number of loaded chunks. - pub fn count(&self) -> usize { + pub fn len(&self) -> usize { self.chunks.len() } @@ -97,13 +97,17 @@ impl Chunks { /// Returns an immutable iterator over all chunks in the world in an /// unspecified order. - pub fn iter(&self) -> impl FusedIterator)> + Clone + '_ { + pub fn iter( + &self, + ) -> impl ExactSizeIterator)> + FusedIterator + Clone + '_ { self.chunks.iter().map(|(&pos, chunk)| (pos, chunk)) } /// Returns a mutable iterator over all chunks in the world in an /// unspecified order. - pub fn iter_mut(&mut self) -> impl FusedIterator)> + '_ { + pub fn iter_mut( + &mut self, + ) -> impl ExactSizeIterator)> + FusedIterator + '_ { self.chunks.iter_mut().map(|(&pos, chunk)| (pos, chunk)) } diff --git a/src/client.rs b/src/client.rs index aaa5bfc..1c684fe 100644 --- a/src/client.rs +++ b/src/client.rs @@ -37,7 +37,7 @@ use crate::protocol_inner::packets::s2c::play::{ }; use crate::protocol_inner::{BoundedInt, ByteAngle, Nbt, RawBytes, VarInt}; use crate::server::{C2sPacketChannels, NewClientData, S2cPlayMessage, SharedServer}; -use crate::slotmap::{Key, SlotMap}; +use crate::slab_versioned::{Key, VersionedSlab}; use crate::text::Text; use crate::util::{chunks_in_view_distance, is_chunk_in_view_distance}; use crate::world::{WorldId, Worlds}; @@ -52,16 +52,18 @@ mod event; /// are not automatically deleted. It is your responsibility to delete them once /// they disconnect. This can be checked with [`Client::is_disconnected`]. pub struct Clients { - sm: SlotMap>, + slab: VersionedSlab>, } impl Clients { pub(crate) fn new() -> Self { - Self { sm: SlotMap::new() } + Self { + slab: VersionedSlab::new(), + } } pub(crate) fn insert(&mut self, client: Client) -> (ClientId, &mut Client) { - let (k, client) = self.sm.insert(client); + let (k, client) = self.slab.insert(client); (ClientId(k), client) } @@ -69,8 +71,8 @@ impl Clients { /// /// If the given client ID is valid, `true` is returned and the client is /// deleted. Otherwise, `false` is returned and the function has no effect. - pub fn delete(&mut self, client: ClientId) -> bool { - self.sm.remove(client.0).is_some() + pub fn remove(&mut self, client: ClientId) -> Option { + self.slab.remove(client.0).map(|c| c.state) } /// Deletes all clients from the server for @@ -78,43 +80,47 @@ impl Clients { /// /// All clients are visited in an unspecified order. pub fn retain(&mut self, mut f: impl FnMut(ClientId, &mut Client) -> bool) { - self.sm.retain(|k, v| f(ClientId(k), v)) + self.slab.retain(|k, v| f(ClientId(k), v)) } /// Returns the number of clients on the server. This includes clients /// which may be disconnected. - pub fn count(&self) -> usize { - self.sm.len() + pub fn len(&self) -> usize { + self.slab.len() } /// Returns a shared reference to the client with the given ID. If /// the ID is invalid, then `None` is returned. pub fn get(&self, client: ClientId) -> Option<&Client> { - self.sm.get(client.0) + self.slab.get(client.0) } /// Returns an exclusive reference to the client with the given ID. If the /// ID is invalid, then `None` is returned. pub fn get_mut(&mut self, client: ClientId) -> Option<&mut Client> { - self.sm.get_mut(client.0) + self.slab.get_mut(client.0) } /// Returns an immutable iterator over all clients on the server in an /// unspecified order. - pub fn iter(&self) -> impl FusedIterator)> + Clone + '_ { - self.sm.iter().map(|(k, v)| (ClientId(k), v)) + pub fn iter( + &self, + ) -> impl ExactSizeIterator)> + FusedIterator + Clone + '_ { + self.slab.iter().map(|(k, v)| (ClientId(k), v)) } /// Returns a mutable iterator over all clients on the server in an /// unspecified order. - pub fn iter_mut(&mut self) -> impl FusedIterator)> + '_ { - self.sm.iter_mut().map(|(k, v)| (ClientId(k), v)) + pub fn iter_mut( + &mut self, + ) -> impl ExactSizeIterator)> + FusedIterator + '_ { + self.slab.iter_mut().map(|(k, v)| (ClientId(k), v)) } /// Returns a parallel immutable iterator over all clients on the server in /// an unspecified order. pub fn par_iter(&self) -> impl ParallelIterator)> + Clone + '_ { - self.sm.par_iter().map(|(k, v)| (ClientId(k), v)) + self.slab.par_iter().map(|(k, v)| (ClientId(k), v)) } /// Returns a parallel mutable iterator over all clients on the server in an @@ -122,7 +128,7 @@ impl Clients { pub fn par_iter_mut( &mut self, ) -> impl ParallelIterator)> + '_ { - self.sm.par_iter_mut().map(|(k, v)| (ClientId(k), v)) + self.slab.par_iter_mut().map(|(k, v)| (ClientId(k), v)) } } diff --git a/src/entity.rs b/src/entity.rs index d43e279..af1fa97 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -16,7 +16,7 @@ use crate::protocol_inner::packets::s2c::play::{ EntitySpawn, EntityTrackerUpdate, ExperienceOrbSpawn, PlayerSpawn, S2cPlayPacket, }; use crate::protocol_inner::{ByteAngle, RawBytes, VarInt}; -use crate::slotmap::{Key, SlotMap}; +use crate::slab_versioned::{Key, VersionedSlab}; use crate::util::aabb_from_bottom_and_size; use crate::world::WorldId; use crate::STANDARD_TPS; @@ -37,7 +37,7 @@ include!(concat!(env!("OUT_DIR"), "/entity_event.rs")); /// [`Player`]: crate::entity::types::Player /// [`PlayerList`]: crate::player_list::PlayerList pub struct Entities { - sm: SlotMap>, + slab: VersionedSlab>, uuid_to_entity: HashMap, network_id_to_entity: HashMap, } @@ -45,7 +45,7 @@ pub struct Entities { impl Entities { pub(crate) fn new() -> Self { Self { - sm: SlotMap::new(), + slab: VersionedSlab::new(), uuid_to_entity: HashMap::new(), network_id_to_entity: HashMap::new(), } @@ -53,8 +53,12 @@ impl Entities { /// Spawns a new entity with a random UUID. A reference to the entity along /// with its ID is returned. - pub fn create(&mut self, kind: EntityKind, data: C::EntityState) -> (EntityId, &mut Entity) { - self.create_with_uuid(kind, Uuid::from_bytes(rand::random()), data) + pub fn insert( + &mut self, + kind: EntityKind, + state: C::EntityState, + ) -> (EntityId, &mut Entity) { + self.insert_with_uuid(kind, Uuid::from_bytes(rand::random()), state) .expect("UUID collision") } @@ -63,7 +67,7 @@ impl Entities { /// /// The provided UUID must not conflict with an existing entity UUID. If it /// does, `None` is returned and the entity is not spawned. - pub fn create_with_uuid( + pub fn insert_with_uuid( &mut self, kind: EntityKind, uuid: Uuid, @@ -72,7 +76,7 @@ impl Entities { match self.uuid_to_entity.entry(uuid) { Entry::Occupied(_) => None, Entry::Vacant(ve) => { - let (k, e) = self.sm.insert(Entity { + let (k, e) = self.slab.insert(Entity { state: data, variants: TrackedData::new(kind), events: Vec::new(), @@ -101,8 +105,8 @@ impl Entities { /// /// If the given entity ID is valid, `true` is returned and the entity is /// deleted. Otherwise, `false` is returned and the function has no effect. - pub fn delete(&mut self, entity: EntityId) -> bool { - if let Some(e) = self.sm.remove(entity.0) { + pub fn remove(&mut self, entity: EntityId) -> Option { + self.slab.remove(entity.0).map(|e| { self.uuid_to_entity .remove(&e.uuid) .expect("UUID should have been in UUID map"); @@ -111,17 +115,15 @@ impl Entities { .remove(&entity.0.version()) .expect("network ID should have been in the network ID map"); - true - } else { - false - } + e.state + }) } /// Removes all entities from the server for which `f` returns `true`. /// /// All entities are visited in an unspecified order. pub fn retain(&mut self, mut f: impl FnMut(EntityId, &mut Entity) -> bool) { - self.sm.retain(|k, v| { + self.slab.retain(|k, v| { if f(EntityId(k), v) { true } else { @@ -139,8 +141,8 @@ impl Entities { } /// Returns the number of entities in this container. - pub fn count(&self) -> usize { - self.sm.len() + pub fn len(&self) -> usize { + self.slab.len() } /// Gets the [`EntityId`] of the entity with the given UUID in an efficient @@ -155,14 +157,14 @@ impl Entities { /// /// If the ID is invalid, `None` is returned. pub fn get(&self, entity: EntityId) -> Option<&Entity> { - self.sm.get(entity.0) + self.slab.get(entity.0) } /// Gets an exclusive reference to the entity with the given [`EntityId`]. /// /// If the ID is invalid, `None` is returned. pub fn get_mut(&mut self, entity: EntityId) -> Option<&mut Entity> { - self.sm.get_mut(entity.0) + self.slab.get_mut(entity.0) } pub(crate) fn get_with_network_id(&self, network_id: i32) -> Option { @@ -173,20 +175,24 @@ impl Entities { /// Returns an immutable iterator over all entities on the server in an /// unspecified order. - pub fn iter(&self) -> impl FusedIterator)> + Clone + '_ { - self.sm.iter().map(|(k, v)| (EntityId(k), v)) + pub fn iter( + &self, + ) -> impl ExactSizeIterator)> + FusedIterator + Clone + '_ { + self.slab.iter().map(|(k, v)| (EntityId(k), v)) } /// Returns a mutable iterator over all entities on the server in an /// unspecified order. - pub fn iter_mut(&mut self) -> impl FusedIterator)> + '_ { - self.sm.iter_mut().map(|(k, v)| (EntityId(k), v)) + pub fn iter_mut( + &mut self, + ) -> impl ExactSizeIterator)> + FusedIterator + '_ { + self.slab.iter_mut().map(|(k, v)| (EntityId(k), v)) } /// Returns a parallel immutable iterator over all entities on the server in /// an unspecified order. pub fn par_iter(&self) -> impl ParallelIterator)> + Clone + '_ { - self.sm.par_iter().map(|(k, v)| (EntityId(k), v)) + self.slab.par_iter().map(|(k, v)| (EntityId(k), v)) } /// Returns a parallel mutable iterator over all clients on the server in an @@ -194,7 +200,7 @@ impl Entities { pub fn par_iter_mut( &mut self, ) -> impl ParallelIterator)> + '_ { - self.sm.par_iter_mut().map(|(k, v)| (EntityId(k), v)) + self.slab.par_iter_mut().map(|(k, v)| (EntityId(k), v)) } pub(crate) fn update(&mut self) { diff --git a/src/lib.rs b/src/lib.rs index a5612d2..e36fec0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,6 +119,17 @@ clippy::comparison_chain )] +/// Used on [`Config`](config::Config) to allow for async methods in traits. +/// +/// For more information see the [async_trait] crate. +/// +/// [async_trait]: https://docs.rs/async-trait/latest/async_trait/ +pub use async_trait::async_trait; +#[doc(inline)] +pub use server::start_server; +#[doc(inline)] +pub use {nbt, uuid, vek}; + pub mod biome; pub mod block; mod block_pos; @@ -135,7 +146,8 @@ pub mod player_textures; #[allow(dead_code)] mod protocol_inner; pub mod server; -mod slotmap; +mod slab; +mod slab_versioned; pub mod spatial_index; pub mod text; pub mod util; @@ -147,17 +159,6 @@ pub mod protocol { pub use crate::protocol_inner::*; } -/// Used on [`Config`](config::Config) to allow for async methods in traits. -/// -/// For more information see the [async_trait] crate. -/// -/// [async_trait]: https://docs.rs/async-trait/latest/async_trait/ -pub use async_trait::async_trait; -#[doc(inline)] -pub use server::start_server; -#[doc(inline)] -pub use {nbt, uuid, vek}; - /// The Minecraft protocol version this library currently targets. pub const PROTOCOL_VERSION: i32 = 759; /// The name of the Minecraft version this library currently targets, e.g. diff --git a/src/slab.rs b/src/slab.rs new file mode 100644 index 0000000..c1a44c3 --- /dev/null +++ b/src/slab.rs @@ -0,0 +1,340 @@ +use std::iter::FusedIterator; +use std::{iter, mem, slice}; + +use rayon::iter::plumbing::UnindexedConsumer; +use rayon::prelude::*; + +#[derive(Clone, Debug)] +pub struct Slab { + entries: Vec>, + next_free_head: usize, + len: usize, +} + +impl Default for Slab { + fn default() -> Self { + Self::new() + } +} + +#[derive(Clone, Debug)] +enum Entry { + Occupied(T), + Vacant { next_free: usize }, +} + +impl Slab { + pub const fn new() -> Self { + Self { + entries: Vec::new(), + next_free_head: 0, + len: 0, + } + } + + pub fn get(&self, key: usize) -> Option<&T> { + match self.entries.get(key)? { + Entry::Occupied(value) => Some(value), + Entry::Vacant { .. } => None, + } + } + + pub fn get_mut(&mut self, key: usize) -> Option<&mut T> { + match self.entries.get_mut(key)? { + Entry::Occupied(value) => Some(value), + Entry::Vacant { .. } => None, + } + } + + pub fn len(&self) -> usize { + self.len + } + + pub fn insert(&mut self, value: T) -> (usize, &mut T) { + self.insert_with(|_| value) + } + + pub fn insert_with(&mut self, f: impl FnOnce(usize) -> T) -> (usize, &mut T) { + self.len += 1; + + if self.next_free_head == self.entries.len() { + let key = self.next_free_head; + + self.next_free_head += 1; + + self.entries.push(Entry::Occupied(f(key))); + + match self.entries.last_mut() { + Some(Entry::Occupied(value)) => (key, value), + _ => unreachable!(), + } + } else { + let entry = &mut self.entries[self.next_free_head]; + + let next_free = match entry { + Entry::Occupied(_) => unreachable!("corrupt free list"), + Entry::Vacant { next_free } => *next_free, + }; + + let key = self.next_free_head; + + *entry = Entry::Occupied(f(key)); + + self.next_free_head = next_free; + + match entry { + Entry::Occupied(value) => (key, value), + Entry::Vacant { .. } => unreachable!(), + } + } + } + + pub fn remove(&mut self, key: usize) -> Option { + let entry = self.entries.get_mut(key)?; + match entry { + Entry::Occupied(_) => { + let old_entry = mem::replace( + entry, + Entry::Vacant { + next_free: self.next_free_head, + }, + ); + + self.next_free_head = key; + self.len -= 1; + + match old_entry { + Entry::Occupied(value) => Some(value), + Entry::Vacant { .. } => unreachable!(), + } + } + Entry::Vacant { .. } => None, + } + } + + pub fn retain(&mut self, mut f: impl FnMut(usize, &mut T) -> bool) { + for (key, entry) in self.entries.iter_mut().enumerate() { + if let Entry::Occupied(value) = entry { + if !f(key, value) { + *entry = Entry::Vacant { + next_free: self.next_free_head, + }; + + self.next_free_head = key; + self.len -= 1; + } + } + } + } + + pub fn clear(&mut self) { + self.entries.clear(); + self.next_free_head = 0; + self.len = 0; + } + + pub fn iter(&self) -> Iter { + Iter { + entries: self.entries.iter().enumerate(), + len: self.len, + } + } + + pub fn iter_mut(&mut self) -> IterMut { + IterMut { + entries: self.entries.iter_mut().enumerate(), + len: self.len, + } + } +} + +impl<'a, T> IntoIterator for &'a Slab { + type IntoIter = Iter<'a, T>; + type Item = (usize, &'a T); + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, T> IntoIterator for &'a mut Slab { + type IntoIter = IterMut<'a, T>; + type Item = (usize, &'a mut T); + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +impl<'a, T: Send + Sync> IntoParallelIterator for &'a Slab { + type Item = (usize, &'a T); + type Iter = ParIter<'a, T>; + + fn into_par_iter(self) -> Self::Iter { + ParIter { slab: self } + } +} + +impl<'a, T: Send + Sync> IntoParallelIterator for &'a mut Slab { + type Item = (usize, &'a mut T); + type Iter = ParIterMut<'a, T>; + + fn into_par_iter(self) -> Self::Iter { + ParIterMut { slab: self } + } +} + +pub struct Iter<'a, T> { + entries: iter::Enumerate>>, + len: usize, +} + +pub struct IterMut<'a, T> { + entries: iter::Enumerate>>, + len: usize, +} + +pub struct ParIter<'a, T> { + slab: &'a Slab, +} + +pub struct ParIterMut<'a, T> { + slab: &'a mut Slab, +} + +impl<'a, T> Clone for Iter<'a, T> { + fn clone(&self) -> Self { + Self { + entries: self.entries.clone(), + len: self.len, + } + } +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = (usize, &'a T); + + fn next(&mut self) -> Option { + for (key, entry) in &mut self.entries { + if let Entry::Occupied(value) = entry { + self.len -= 1; + return Some((key, value)); + } + } + + debug_assert_eq!(self.len, 0); + None + } + + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +impl DoubleEndedIterator for Iter<'_, T> { + fn next_back(&mut self) -> Option { + while let Some((key, entry)) = self.entries.next_back() { + if let Entry::Occupied(value) = entry { + self.len -= 1; + return Some((key, value)); + } + } + + debug_assert_eq!(self.len, 0); + None + } +} + +impl ExactSizeIterator for Iter<'_, T> { + fn len(&self) -> usize { + self.len + } +} + +impl FusedIterator for Iter<'_, T> {} + +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = (usize, &'a mut T); + + fn next(&mut self) -> Option { + for (key, entry) in &mut self.entries { + if let Entry::Occupied(value) = entry { + self.len -= 1; + return Some((key, value)); + } + } + + debug_assert_eq!(self.len, 0); + None + } + + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +impl DoubleEndedIterator for IterMut<'_, T> { + fn next_back(&mut self) -> Option { + while let Some((key, entry)) = self.entries.next_back() { + if let Entry::Occupied(value) = entry { + self.len -= 1; + return Some((key, value)); + } + } + + debug_assert_eq!(self.len, 0); + None + } +} + +impl ExactSizeIterator for IterMut<'_, T> { + fn len(&self) -> usize { + self.len + } +} + +impl FusedIterator for IterMut<'_, T> {} + +impl Clone for ParIter<'_, T> { + fn clone(&self) -> Self { + Self { slab: &self.slab } + } +} + +impl<'a, T: Send + Sync> ParallelIterator for ParIter<'a, T> { + type Item = (usize, &'a T); + + fn drive_unindexed(self, consumer: C) -> C::Result + where + C: UnindexedConsumer, + { + self.slab + .entries + .par_iter() + .enumerate() + .filter_map(|(key, value)| match value { + Entry::Occupied(value) => Some((key, value)), + Entry::Vacant { .. } => None, + }) + .drive_unindexed(consumer) + } +} + +impl<'a, T: Send + Sync> ParallelIterator for ParIterMut<'a, T> { + type Item = (usize, &'a mut T); + + fn drive_unindexed(self, consumer: C) -> C::Result + where + C: UnindexedConsumer, + { + self.slab + .entries + .par_iter_mut() + .enumerate() + .filter_map(|(key, value)| match value { + Entry::Occupied(value) => Some((key, value)), + Entry::Vacant { .. } => None, + }) + .drive_unindexed(consumer) + } +} diff --git a/src/slab_versioned.rs b/src/slab_versioned.rs new file mode 100644 index 0000000..e4ecced --- /dev/null +++ b/src/slab_versioned.rs @@ -0,0 +1,197 @@ +use std::iter::FusedIterator; +use std::num::NonZeroU32; + +use rayon::iter::{IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator}; + +use crate::slab::Slab; + +#[derive(Clone, Debug)] +pub struct VersionedSlab { + slab: Slab>, + version: NonZeroU32, +} + +#[derive(Clone, Debug)] +struct Slot { + value: T, + version: NonZeroU32, +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct Key { + pub index: u32, + pub version: NonZeroU32, +} + +impl Key { + pub const NULL: Self = Self { + index: u32::MAX, + version: match NonZeroU32::new(u32::MAX) { + Some(n) => n, + None => unreachable!(), + }, + }; + + pub fn new(index: u32, version: NonZeroU32) -> Self { + Self { index, version } + } +} + +impl Default for Key { + fn default() -> Self { + Self::NULL + } +} + +impl Key { + pub fn index(self) -> u32 { + self.index + } + + pub fn version(self) -> NonZeroU32 { + self.version + } +} + +const ONE: NonZeroU32 = match NonZeroU32::new(1) { + Some(n) => n, + None => unreachable!(), +}; + +impl VersionedSlab { + pub const fn new() -> Self { + Self { + slab: Slab::new(), + version: ONE, + } + } + + pub fn get(&self, key: Key) -> Option<&T> { + let slot = self.slab.get(key.index as usize)?; + (slot.version == key.version).then(|| &slot.value) + } + + pub fn get_mut(&mut self, key: Key) -> Option<&mut T> { + let slot = self.slab.get_mut(key.index as usize)?; + (slot.version == key.version).then(|| &mut slot.value) + } + + pub fn len(&self) -> usize { + self.slab.len() + } + + pub fn insert(&mut self, value: T) -> (Key, &mut T) { + self.insert_with(|_| value) + } + + pub fn insert_with(&mut self, f: impl FnOnce(Key) -> T) -> (Key, &mut T) { + let version = self.version; + self.version = NonZeroU32::new(version.get().wrapping_add(1)).unwrap_or_else(|| { + log::warn!("slab version overflow"); + ONE + }); + + let (index, slot) = self.slab.insert_with(|index| { + assert!( + index < u32::MAX as usize, + "too many values in versioned slab" + ); + Slot { + value: f(Key::new(index as u32, version)), + version, + } + }); + + (Key::new(index as u32, version), &mut slot.value) + } + + pub fn remove(&mut self, key: Key) -> Option { + self.get(key)?; + Some(self.slab.remove(key.index as usize).unwrap().value) + } + + pub fn retain(&mut self, mut f: impl FnMut(Key, &mut T) -> bool) { + self.slab + .retain(|idx, slot| f(Key::new(idx as u32, slot.version), &mut slot.value)) + } + + #[allow(unused)] + pub fn clear(&mut self) { + self.slab.clear(); + } + + pub fn iter(&self) -> impl ExactSizeIterator + FusedIterator + Clone + '_ { + self.slab + .iter() + .map(|(idx, slot)| (Key::new(idx as u32, slot.version), &slot.value)) + } + + pub fn iter_mut( + &mut self, + ) -> impl ExactSizeIterator + FusedIterator + '_ { + self.slab + .iter_mut() + .map(|(idx, slot)| (Key::new(idx as u32, slot.version), &mut slot.value)) + } + + pub fn par_iter(&self) -> impl ParallelIterator + Clone + '_ + where + T: Send + Sync, + { + self.slab + .par_iter() + .map(|(idx, slot)| (Key::new(idx as u32, slot.version), &slot.value)) + } + + pub fn par_iter_mut(&mut self) -> impl ParallelIterator + '_ + where + T: Send + Sync, + { + self.slab + .par_iter_mut() + .map(|(idx, slot)| (Key::new(idx as u32, slot.version), &mut slot.value)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn insert_remove() { + let mut slab = VersionedSlab::new(); + + let k0 = slab.insert(10).0; + let k1 = slab.insert(20).0; + let k2 = slab.insert(30).0; + assert!(k0 != k1 && k1 != k2 && k0 != k2); + + assert_eq!(slab.remove(k1), Some(20)); + assert_eq!(slab.get(k1), None); + assert_eq!(slab.get(k2), Some(&30)); + let k3 = slab.insert(40).0; + assert_eq!(slab.get(k0), Some(&10)); + assert_eq!(slab.get_mut(k3), Some(&mut 40)); + assert_eq!(slab.remove(k0), Some(10)); + + slab.clear(); + assert_eq!(slab.len(), 0); + } + + #[test] + fn retain() { + let mut sm = VersionedSlab::new(); + + let k0 = sm.insert(10).0; + let k1 = sm.insert(20).0; + let k2 = sm.insert(30).0; + + sm.retain(|k, _| k == k1); + + assert_eq!(sm.get(k1), Some(&20)); + assert_eq!(sm.len(), 1); + + assert_eq!(sm.get(k0), None); + assert_eq!(sm.get(k2), None); + } +} diff --git a/src/slotmap.rs b/src/slotmap.rs deleted file mode 100644 index 9fd92a1..0000000 --- a/src/slotmap.rs +++ /dev/null @@ -1,295 +0,0 @@ -use std::iter::FusedIterator; -use std::mem; -use std::num::NonZeroU32; - -use rayon::iter::{ - IndexedParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator, -}; - -#[derive(Clone, Debug)] -pub struct SlotMap { - slots: Vec>, - /// Top of the free stack. - next_free_head: u32, - /// The number of occupied slots. - count: u32, - /// Version counter. - version: NonZeroU32, -} - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct Key { - index: u32, - version: NonZeroU32, -} - -impl Key { - pub const NULL: Self = Self { - index: u32::MAX, - version: match NonZeroU32::new(u32::MAX) { - Some(n) => n, - None => unreachable!(), - }, - }; -} - -impl Default for Key { - fn default() -> Self { - Self::NULL - } -} - -impl Key { - pub fn new(index: u32, version: NonZeroU32) -> Self { - Self { index, version } - } - - fn new_unique(index: u32, version: &mut NonZeroU32) -> Self { - *version = NonZeroU32::new(version.get().wrapping_add(1)).unwrap_or_else(|| { - log::warn!("slotmap version overflow"); - ONE - }); - - Self { - index, - version: *version, - } - } - - pub fn index(self) -> u32 { - self.index - } - - pub fn version(self) -> NonZeroU32 { - self.version - } -} - -const ONE: NonZeroU32 = match NonZeroU32::new(1) { - Some(n) => n, - None => unreachable!(), -}; - -#[derive(Clone, Debug)] -enum Slot { - Occupied { value: T, version: NonZeroU32 }, - Free { next_free: u32 }, -} - -impl SlotMap { - pub fn new() -> Self { - Self { - slots: Vec::new(), - next_free_head: 0, - count: 0, - version: ONE, - } - } - - pub fn len(&self) -> usize { - self.count as usize - } - - pub fn insert(&mut self, val: T) -> (Key, &mut T) { - self.insert_with(|_| val) - } - - pub fn insert_with(&mut self, f: impl FnOnce(Key) -> T) -> (Key, &mut T) { - assert!(self.count < u32::MAX, "SlotMap: too many items inserted"); - - if self.next_free_head == self.slots.len() as u32 { - self.count += 1; - self.next_free_head += 1; - - let key = Key::new_unique(self.next_free_head - 1, &mut self.version); - - self.slots.push(Slot::Occupied { - value: f(key), - version: key.version(), - }); - - let value = match self.slots.last_mut() { - Some(Slot::Occupied { value, .. }) => value, - _ => unreachable!(), - }; - (key, value) - } else { - let slot = &mut self.slots[self.next_free_head as usize]; - - let next_free = match slot { - Slot::Occupied { .. } => unreachable!("corrupt free list"), - Slot::Free { next_free } => *next_free, - }; - - let key = Key::new_unique(self.next_free_head, &mut self.version); - - *slot = Slot::Occupied { - value: f(key), - version: key.version(), - }; - - let value = match slot { - Slot::Occupied { value, .. } => value, - Slot::Free { .. } => unreachable!(), - }; - - self.next_free_head = next_free; - self.count += 1; - - (key, value) - } - } - - pub fn remove(&mut self, key: Key) -> Option { - let slot = self.slots.get_mut(key.index as usize)?; - match slot { - Slot::Occupied { version, .. } if *version == key.version() => { - let old_slot = mem::replace( - slot, - Slot::Free { - next_free: self.next_free_head, - }, - ); - - self.next_free_head = key.index; - self.count -= 1; - - match old_slot { - Slot::Occupied { value, .. } => Some(value), - Slot::Free { .. } => unreachable!(), - } - } - _ => None, - } - } - - pub fn get(&self, key: Key) -> Option<&T> { - match self.slots.get(key.index as usize)? { - Slot::Occupied { value, version } if *version == key.version() => Some(value), - _ => None, - } - } - - pub fn get_mut(&mut self, key: Key) -> Option<&mut T> { - match self.slots.get_mut(key.index as usize)? { - Slot::Occupied { value, version } if *version == key.version() => Some(value), - _ => None, - } - } - - #[allow(unused)] - pub fn clear(&mut self) { - self.slots.clear(); - self.next_free_head = 0; - self.count = 0; - } - - pub fn retain(&mut self, mut f: impl FnMut(Key, &mut T) -> bool) { - for (i, mut slot) in self.slots.iter_mut().enumerate() { - if let Slot::Occupied { value, version } = &mut slot { - let key = Key::new(i as u32, *version); - - if !f(key, value) { - *slot = Slot::Free { - next_free: self.next_free_head, - }; - - self.next_free_head = key.index; - self.count -= 1; - } - } - } - } - - pub fn iter(&self) -> impl FusedIterator + Clone + '_ { - self.slots - .iter() - .enumerate() - .filter_map(|(i, slot)| match &slot { - Slot::Occupied { value, version } => Some((Key::new(i as u32, *version), value)), - Slot::Free { .. } => None, - }) - } - - pub fn iter_mut(&mut self) -> impl FusedIterator + '_ { - self.slots - .iter_mut() - .enumerate() - .filter_map(|(i, slot)| match slot { - Slot::Occupied { value, version } => Some((Key::new(i as u32, *version), value)), - Slot::Free { .. } => None, - }) - } -} - -impl SlotMap { - pub fn par_iter(&self) -> impl ParallelIterator + Clone + '_ { - self.slots - .par_iter() - .enumerate() - .filter_map(|(i, slot)| match &slot { - Slot::Occupied { value, version } => Some((Key::new(i as u32, *version), value)), - Slot::Free { .. } => None, - }) - } -} - -impl SlotMap { - pub fn par_iter_mut(&mut self) -> impl ParallelIterator + '_ { - self.slots - .par_iter_mut() - .enumerate() - .filter_map(|(i, slot)| match slot { - Slot::Occupied { value, version } => Some((Key::new(i as u32, *version), value)), - Slot::Free { .. } => None, - }) - } -} - -impl Default for SlotMap { - fn default() -> Self { - Self::new() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn insert_remove() { - let mut sm = SlotMap::new(); - - let k0 = sm.insert(10).0; - let k1 = sm.insert(20).0; - let k2 = sm.insert(30).0; - - assert_eq!(sm.remove(k1), Some(20)); - assert_eq!(sm.get(k1), None); - assert_eq!(sm.get(k2), Some(&30)); - let k3 = sm.insert(40).0; - assert_eq!(sm.get(k0), Some(&10)); - assert_eq!(sm.get_mut(k3), Some(&mut 40)); - assert_eq!(sm.remove(k0), Some(10)); - - sm.clear(); - assert_eq!(sm.len(), 0); - } - - #[test] - fn retain() { - let mut sm = SlotMap::new(); - - let k0 = sm.insert(10).0; - let k1 = sm.insert(20).0; - let k2 = sm.insert(30).0; - - sm.retain(|k, _| k == k1); - - assert_eq!(sm.get(k1), Some(&20)); - assert_eq!(sm.len(), 1); - - assert_eq!(sm.get(k0), None); - assert_eq!(sm.get(k2), None); - } -} diff --git a/src/world.rs b/src/world.rs index d4878b9..5a0bdb9 100644 --- a/src/world.rs +++ b/src/world.rs @@ -9,12 +9,12 @@ use crate::config::Config; use crate::dimension::DimensionId; use crate::player_list::PlayerList; use crate::server::SharedServer; -use crate::slotmap::{Key, SlotMap}; +use crate::slab_versioned::{Key, VersionedSlab}; use crate::spatial_index::SpatialIndex; /// A container for all [`World`]s on a [`Server`](crate::server::Server). pub struct Worlds { - sm: SlotMap>, + slab: VersionedSlab>, server: SharedServer, } @@ -38,16 +38,16 @@ impl WorldId { impl Worlds { pub(crate) fn new(server: SharedServer) -> Self { Self { - sm: SlotMap::new(), + slab: VersionedSlab::new(), server, } } /// Creates a new world on the server with the provided dimension. A /// reference to the world along with its ID is returned. - pub fn create(&mut self, dim: DimensionId, data: C::WorldState) -> (WorldId, &mut World) { - let (id, world) = self.sm.insert(World { - state: data, + pub fn insert(&mut self, dim: DimensionId, state: C::WorldState) -> (WorldId, &mut World) { + let (id, world) = self.slab.insert(World { + state, spatial_index: SpatialIndex::new(), chunks: Chunks::new(self.server.clone(), dim), meta: WorldMeta { @@ -65,8 +65,8 @@ impl Worlds { /// Note that entities located in the world are not deleted themselves. /// Additionally, any clients that are still in the deleted world at the end /// of the tick are disconnected. - pub fn delete(&mut self, world: WorldId) -> bool { - self.sm.remove(world.0).is_some() + pub fn remove(&mut self, world: WorldId) -> bool { + self.slab.remove(world.0).is_some() } /// Deletes all worlds from the server (as if by [`Self::delete`]) for which @@ -74,48 +74,52 @@ impl Worlds { /// /// All worlds are visited in an unspecified order. pub fn retain(&mut self, mut f: impl FnMut(WorldId, &mut World) -> bool) { - self.sm.retain(|k, v| f(WorldId(k), v)) + self.slab.retain(|k, v| f(WorldId(k), v)) } /// Returns the number of worlds on the server. - pub fn count(&self) -> usize { - self.sm.len() + pub fn len(&self) -> usize { + self.slab.len() } /// Returns a shared reference to the world with the given ID. If /// the ID is invalid, then `None` is returned. pub fn get(&self, world: WorldId) -> Option<&World> { - self.sm.get(world.0) + self.slab.get(world.0) } /// Returns an exclusive reference to the world with the given ID. If the /// ID is invalid, then `None` is returned. pub fn get_mut(&mut self, world: WorldId) -> Option<&mut World> { - self.sm.get_mut(world.0) + self.slab.get_mut(world.0) } /// Returns an immutable iterator over all worlds on the server in an /// unspecified order. - pub fn iter(&self) -> impl FusedIterator)> + Clone + '_ { - self.sm.iter().map(|(k, v)| (WorldId(k), v)) + pub fn iter( + &self, + ) -> impl ExactSizeIterator)> + FusedIterator + Clone + '_ { + self.slab.iter().map(|(k, v)| (WorldId(k), v)) } /// Returns a mutable iterator over all worlds on the server in an - /// unspecified ordder. - pub fn iter_mut(&mut self) -> impl FusedIterator)> + '_ { - self.sm.iter_mut().map(|(k, v)| (WorldId(k), v)) + /// unspecified order. + pub fn iter_mut( + &mut self, + ) -> impl ExactSizeIterator)> + FusedIterator + '_ { + self.slab.iter_mut().map(|(k, v)| (WorldId(k), v)) } /// Returns a parallel immutable iterator over all worlds on the server in /// an unspecified order. pub fn par_iter(&self) -> impl ParallelIterator)> + Clone + '_ { - self.sm.par_iter().map(|(k, v)| (WorldId(k), v)) + self.slab.par_iter().map(|(k, v)| (WorldId(k), v)) } /// Returns a parallel mutable iterator over all worlds on the server in an /// unspecified order. pub fn par_iter_mut(&mut self) -> impl ParallelIterator)> + '_ { - self.sm.par_iter_mut().map(|(k, v)| (WorldId(k), v)) + self.slab.par_iter_mut().map(|(k, v)| (WorldId(k), v)) } }