Extract slotmap logic into separate modules

This commit is contained in:
Ryan 2022-08-08 18:42:39 -07:00
parent cd110c3cbb
commit 1838c290a0
13 changed files with 662 additions and 399 deletions

View file

@ -83,7 +83,7 @@ impl Config for Game {
}
fn init(&self, server: &mut Server<Self>) {
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;
}

View file

@ -95,12 +95,12 @@ impl Config for Game {
}
fn init(&self, server: &mut Server<Self>) {
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;
}

View file

@ -73,20 +73,20 @@ impl Config for Game {
}
fn init(&self, server: &mut Server<Self>) {
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;
}

View file

@ -72,13 +72,13 @@ impl Config for Game {
}
fn init(&self, server: &mut Server<Self>) {
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;
}

View file

@ -82,7 +82,7 @@ impl Config for Game {
}
fn init(&self, server: &mut Server<Self>) {
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)| {

View file

@ -41,7 +41,7 @@ impl<C: Config> Chunks<C> {
}
/// 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<C: Config> Chunks<C> {
/// 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<ChunkPos>, data: C::ChunkState) -> &mut Chunk<C> {
pub fn insert(&mut self, pos: impl Into<ChunkPos>, state: C::ChunkState) -> &mut Chunk<C> {
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<C: Config> Chunks<C> {
///
/// 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<ChunkPos>) -> bool {
self.chunks.remove(&pos.into()).is_some()
pub fn remove(&mut self, pos: impl Into<ChunkPos>) -> Option<C::ChunkState> {
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<C: Config> Chunks<C> {
/// Returns an immutable iterator over all chunks in the world in an
/// unspecified order.
pub fn iter(&self) -> impl FusedIterator<Item = (ChunkPos, &Chunk<C>)> + Clone + '_ {
pub fn iter(
&self,
) -> impl ExactSizeIterator<Item = (ChunkPos, &Chunk<C>)> + 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<Item = (ChunkPos, &mut Chunk<C>)> + '_ {
pub fn iter_mut(
&mut self,
) -> impl ExactSizeIterator<Item = (ChunkPos, &mut Chunk<C>)> + FusedIterator + '_ {
self.chunks.iter_mut().map(|(&pos, chunk)| (pos, chunk))
}

View file

@ -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<C: Config> {
sm: SlotMap<Client<C>>,
slab: VersionedSlab<Client<C>>,
}
impl<C: Config> Clients<C> {
pub(crate) fn new() -> Self {
Self { sm: SlotMap::new() }
Self {
slab: VersionedSlab::new(),
}
}
pub(crate) fn insert(&mut self, client: Client<C>) -> (ClientId, &mut Client<C>) {
let (k, client) = self.sm.insert(client);
let (k, client) = self.slab.insert(client);
(ClientId(k), client)
}
@ -69,8 +71,8 @@ impl<C: Config> Clients<C> {
///
/// 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<C::ClientState> {
self.slab.remove(client.0).map(|c| c.state)
}
/// Deletes all clients from the server for
@ -78,43 +80,47 @@ impl<C: Config> Clients<C> {
///
/// All clients are visited in an unspecified order.
pub fn retain(&mut self, mut f: impl FnMut(ClientId, &mut Client<C>) -> 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<C>> {
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<C>> {
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<Item = (ClientId, &Client<C>)> + Clone + '_ {
self.sm.iter().map(|(k, v)| (ClientId(k), v))
pub fn iter(
&self,
) -> impl ExactSizeIterator<Item = (ClientId, &Client<C>)> + 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<Item = (ClientId, &mut Client<C>)> + '_ {
self.sm.iter_mut().map(|(k, v)| (ClientId(k), v))
pub fn iter_mut(
&mut self,
) -> impl ExactSizeIterator<Item = (ClientId, &mut Client<C>)> + 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<Item = (ClientId, &Client<C>)> + 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<C: Config> Clients<C> {
pub fn par_iter_mut(
&mut self,
) -> impl ParallelIterator<Item = (ClientId, &mut Client<C>)> + '_ {
self.sm.par_iter_mut().map(|(k, v)| (ClientId(k), v))
self.slab.par_iter_mut().map(|(k, v)| (ClientId(k), v))
}
}

View file

@ -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<C: Config> {
sm: SlotMap<Entity<C>>,
slab: VersionedSlab<Entity<C>>,
uuid_to_entity: HashMap<Uuid, EntityId>,
network_id_to_entity: HashMap<NonZeroU32, u32>,
}
@ -45,7 +45,7 @@ pub struct Entities<C: Config> {
impl<C: Config> Entities<C> {
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<C: Config> Entities<C> {
/// 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<C>) {
self.create_with_uuid(kind, Uuid::from_bytes(rand::random()), data)
pub fn insert(
&mut self,
kind: EntityKind,
state: C::EntityState,
) -> (EntityId, &mut Entity<C>) {
self.insert_with_uuid(kind, Uuid::from_bytes(rand::random()), state)
.expect("UUID collision")
}
@ -63,7 +67,7 @@ impl<C: Config> Entities<C> {
///
/// 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<C: Config> Entities<C> {
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<C: Config> Entities<C> {
///
/// 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<C::EntityState> {
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<C: Config> Entities<C> {
.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<C>) -> bool) {
self.sm.retain(|k, v| {
self.slab.retain(|k, v| {
if f(EntityId(k), v) {
true
} else {
@ -139,8 +141,8 @@ impl<C: Config> Entities<C> {
}
/// 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<C: Config> Entities<C> {
///
/// If the ID is invalid, `None` is returned.
pub fn get(&self, entity: EntityId) -> Option<&Entity<C>> {
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<C>> {
self.sm.get_mut(entity.0)
self.slab.get_mut(entity.0)
}
pub(crate) fn get_with_network_id(&self, network_id: i32) -> Option<EntityId> {
@ -173,20 +175,24 @@ impl<C: Config> Entities<C> {
/// Returns an immutable iterator over all entities on the server in an
/// unspecified order.
pub fn iter(&self) -> impl FusedIterator<Item = (EntityId, &Entity<C>)> + Clone + '_ {
self.sm.iter().map(|(k, v)| (EntityId(k), v))
pub fn iter(
&self,
) -> impl ExactSizeIterator<Item = (EntityId, &Entity<C>)> + 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<Item = (EntityId, &mut Entity<C>)> + '_ {
self.sm.iter_mut().map(|(k, v)| (EntityId(k), v))
pub fn iter_mut(
&mut self,
) -> impl ExactSizeIterator<Item = (EntityId, &mut Entity<C>)> + 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<Item = (EntityId, &Entity<C>)> + 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<C: Config> Entities<C> {
pub fn par_iter_mut(
&mut self,
) -> impl ParallelIterator<Item = (EntityId, &mut Entity<C>)> + '_ {
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) {

View file

@ -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.

340
src/slab.rs Normal file
View file

@ -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<T> {
entries: Vec<Entry<T>>,
next_free_head: usize,
len: usize,
}
impl<T> Default for Slab<T> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
enum Entry<T> {
Occupied(T),
Vacant { next_free: usize },
}
impl<T> Slab<T> {
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<T> {
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<T> {
Iter {
entries: self.entries.iter().enumerate(),
len: self.len,
}
}
pub fn iter_mut(&mut self) -> IterMut<T> {
IterMut {
entries: self.entries.iter_mut().enumerate(),
len: self.len,
}
}
}
impl<'a, T> IntoIterator for &'a Slab<T> {
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<T> {
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<T> {
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<T> {
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<slice::Iter<'a, Entry<T>>>,
len: usize,
}
pub struct IterMut<'a, T> {
entries: iter::Enumerate<slice::IterMut<'a, Entry<T>>>,
len: usize,
}
pub struct ParIter<'a, T> {
slab: &'a Slab<T>,
}
pub struct ParIterMut<'a, T> {
slab: &'a mut Slab<T>,
}
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<Self::Item> {
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<usize>) {
(self.len, Some(self.len))
}
}
impl<T> DoubleEndedIterator for Iter<'_, T> {
fn next_back(&mut self) -> Option<Self::Item> {
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<T> ExactSizeIterator for Iter<'_, T> {
fn len(&self) -> usize {
self.len
}
}
impl<T> FusedIterator for Iter<'_, T> {}
impl<'a, T> Iterator for IterMut<'a, T> {
type Item = (usize, &'a mut T);
fn next(&mut self) -> Option<Self::Item> {
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<usize>) {
(self.len, Some(self.len))
}
}
impl<T> DoubleEndedIterator for IterMut<'_, T> {
fn next_back(&mut self) -> Option<Self::Item> {
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<T> ExactSizeIterator for IterMut<'_, T> {
fn len(&self) -> usize {
self.len
}
}
impl<T> FusedIterator for IterMut<'_, T> {}
impl<T> 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<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
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<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
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)
}
}

197
src/slab_versioned.rs Normal file
View file

@ -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<T> {
slab: Slab<Slot<T>>,
version: NonZeroU32,
}
#[derive(Clone, Debug)]
struct Slot<T> {
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<T> VersionedSlab<T> {
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<T> {
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<Item = (Key, &T)> + 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<Item = (Key, &mut T)> + 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<Item = (Key, &T)> + 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<Item = (Key, &mut T)> + '_
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);
}
}

View file

@ -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<T> {
slots: Vec<Slot<T>>,
/// 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<T> {
Occupied { value: T, version: NonZeroU32 },
Free { next_free: u32 },
}
impl<T> SlotMap<T> {
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<T> {
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<Item = (Key, &T)> + 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<Item = (Key, &mut T)> + '_ {
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<T: Sync> SlotMap<T> {
pub fn par_iter(&self) -> impl ParallelIterator<Item = (Key, &T)> + 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<T: Send + Sync> SlotMap<T> {
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (Key, &mut T)> + '_ {
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<T> Default for SlotMap<T> {
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);
}
}

View file

@ -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<C: Config> {
sm: SlotMap<World<C>>,
slab: VersionedSlab<World<C>>,
server: SharedServer<C>,
}
@ -38,16 +38,16 @@ impl WorldId {
impl<C: Config> Worlds<C> {
pub(crate) fn new(server: SharedServer<C>) -> 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<C>) {
let (id, world) = self.sm.insert(World {
state: data,
pub fn insert(&mut self, dim: DimensionId, state: C::WorldState) -> (WorldId, &mut World<C>) {
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<C: Config> Worlds<C> {
/// 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<C: Config> Worlds<C> {
///
/// All worlds are visited in an unspecified order.
pub fn retain(&mut self, mut f: impl FnMut(WorldId, &mut World<C>) -> 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<C>> {
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<C>> {
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<Item = (WorldId, &World<C>)> + Clone + '_ {
self.sm.iter().map(|(k, v)| (WorldId(k), v))
pub fn iter(
&self,
) -> impl ExactSizeIterator<Item = (WorldId, &World<C>)> + 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<Item = (WorldId, &mut World<C>)> + '_ {
self.sm.iter_mut().map(|(k, v)| (WorldId(k), v))
/// unspecified order.
pub fn iter_mut(
&mut self,
) -> impl ExactSizeIterator<Item = (WorldId, &mut World<C>)> + 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<Item = (WorldId, &World<C>)> + 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<Item = (WorldId, &mut World<C>)> + '_ {
self.sm.par_iter_mut().map(|(k, v)| (WorldId(k), v))
self.slab.par_iter_mut().map(|(k, v)| (WorldId(k), v))
}
}