mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 22:41:30 +11:00
Extract slotmap logic into separate modules
This commit is contained in:
parent
cd110c3cbb
commit
1838c290a0
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)| {
|
||||
|
|
20
src/chunk.rs
20
src/chunk.rs
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
25
src/lib.rs
25
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.
|
||||
|
|
340
src/slab.rs
Normal file
340
src/slab.rs
Normal 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
197
src/slab_versioned.rs
Normal 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);
|
||||
}
|
||||
}
|
295
src/slotmap.rs
295
src/slotmap.rs
|
@ -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);
|
||||
}
|
||||
}
|
44
src/world.rs
44
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<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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue