Update rust docs

This commit is contained in:
Ryan 2022-09-02 00:06:45 -07:00
parent 5d8f7a49da
commit f7a35f356e
14 changed files with 174 additions and 158 deletions

View file

@ -37,7 +37,7 @@ sha2 = "0.10"
thiserror = "1" thiserror = "1"
url = { version = "2.2.2", features = ["serde"] } url = { version = "2.2.2", features = ["serde"] }
uuid = "1" uuid = "1"
valence_nbt = { path = "valence_nbt" } serde_nbt = { path = "serde_nbt" }
vek = "0.15" vek = "0.15"
[dependencies.tokio] [dependencies.tokio]
@ -68,4 +68,4 @@ num = "0.4"
protocol = [] protocol = []
[workspace] [workspace]
members = ["valence_nbt", "packet_inspector"] members = ["serde_nbt", "packet_inspector"]

View file

@ -23,7 +23,7 @@ use crate::protocol::packets::s2c::play::{
use crate::protocol::{Encode, NbtBridge, VarInt, VarLong}; use crate::protocol::{Encode, NbtBridge, VarInt, VarLong};
use crate::server::SharedServer; use crate::server::SharedServer;
/// A container for all [`Chunks`]s in a [`World`](crate::world::World). /// A container for all [`Chunk`]s in a [`World`](crate::world::World).
pub struct Chunks<C: Config> { pub struct Chunks<C: Config> {
chunks: HashMap<ChunkPos, Chunk<C>>, chunks: HashMap<ChunkPos, Chunk<C>>,
shared: SharedServer<C>, shared: SharedServer<C>,
@ -65,8 +65,8 @@ impl<C: Config> Chunks<C> {
/// Removes a chunk at the provided position. /// Removes a chunk at the provided position.
/// ///
/// If a chunk exists at the position, then it is deleted and `true` is /// If a chunk exists at the position, then it is deleted and its
/// returned. Otherwise, `false` is returned. /// `ChunkState` is returned. Otherwise, `None` is returned.
pub fn remove(&mut self, pos: impl Into<ChunkPos>) -> Option<C::ChunkState> { pub fn remove(&mut self, pos: impl Into<ChunkPos>) -> Option<C::ChunkState> {
self.chunks.remove(&pos.into()).map(|c| c.state) self.chunks.remove(&pos.into()).map(|c| c.state)
} }
@ -90,6 +90,9 @@ impl<C: Config> Chunks<C> {
self.chunks.get_mut(&pos.into()) self.chunks.get_mut(&pos.into())
} }
/// Removes all chunks for which `f` returns `true`.
///
/// All chunks are visited in an unspecified order.
pub fn retain(&mut self, mut f: impl FnMut(ChunkPos, &mut Chunk<C>) -> bool) { pub fn retain(&mut self, mut f: impl FnMut(ChunkPos, &mut Chunk<C>) -> bool) {
self.chunks.retain(|&pos, chunk| f(pos, chunk)) self.chunks.retain(|&pos, chunk| f(pos, chunk))
} }
@ -99,8 +102,8 @@ impl<C: Config> Chunks<C> {
self.chunks.clear(); self.chunks.clear();
} }
/// Returns an immutable iterator over all chunks in the world in an /// Returns an iterator over all chunks in the world in an unspecified
/// unspecified order. /// order.
pub fn iter( pub fn iter(
&self, &self,
) -> impl ExactSizeIterator<Item = (ChunkPos, &Chunk<C>)> + FusedIterator + Clone + '_ { ) -> impl ExactSizeIterator<Item = (ChunkPos, &Chunk<C>)> + FusedIterator + Clone + '_ {
@ -115,7 +118,7 @@ impl<C: Config> Chunks<C> {
self.chunks.iter_mut().map(|(&pos, chunk)| (pos, chunk)) self.chunks.iter_mut().map(|(&pos, chunk)| (pos, chunk))
} }
/// Returns a parallel immutable iterator over all chunks in the world in an /// Returns a parallel iterator over all chunks in the world in an
/// unspecified order. /// unspecified order.
pub fn par_iter(&self) -> impl ParallelIterator<Item = (ChunkPos, &Chunk<C>)> + Clone + '_ { pub fn par_iter(&self) -> impl ParallelIterator<Item = (ChunkPos, &Chunk<C>)> + Clone + '_ {
self.chunks.par_iter().map(|(&pos, chunk)| (pos, chunk)) self.chunks.par_iter().map(|(&pos, chunk)| (pos, chunk))
@ -131,9 +134,8 @@ impl<C: Config> Chunks<C> {
/// ///
/// If the position is not inside of a chunk, then `None` is returned. /// If the position is not inside of a chunk, then `None` is returned.
/// ///
/// Note: if you need to get a large number of blocks, it may be more /// Note: if you need to get a large number of blocks, it is more efficient
/// efficient to read from the chunks directly with /// to read from the chunks directly with [`Chunk::get_block_state`].
/// [`Chunk::get_block_state`].
pub fn get_block_state(&self, pos: impl Into<BlockPos>) -> Option<BlockState> { pub fn get_block_state(&self, pos: impl Into<BlockPos>) -> Option<BlockState> {
let pos = pos.into(); let pos = pos.into();
let chunk_pos = ChunkPos::from(pos); let chunk_pos = ChunkPos::from(pos);
@ -243,28 +245,41 @@ impl<C: Config> Chunk<C> {
chunk chunk
} }
/// Returns `true` if this chunk was created during the current tick.
pub fn created_this_tick(&self) -> bool { pub fn created_this_tick(&self) -> bool {
self.created_this_tick self.created_this_tick
} }
/// Returns the height of this chunk in blocks.
pub fn height(&self) -> usize { pub fn height(&self) -> usize {
self.sections.len() * 16 self.sections.len() * 16
} }
/// Gets the block state at the provided offsets in the chunk.
///
/// # Panics
///
/// Panics if the offsets are outside the bounds of the chunk.
pub fn get_block_state(&self, x: usize, y: usize, z: usize) -> BlockState { pub fn get_block_state(&self, x: usize, y: usize, z: usize) -> BlockState {
if x < 16 && y < self.height() && z < 16 { assert!(
x < 16 && y < self.height() && z < 16,
"chunk block offsets must be within bounds"
);
BlockState::from_raw_unchecked( BlockState::from_raw_unchecked(
self.sections[y / 16].blocks[x + z * 16 + y % 16 * 16 * 16] & BLOCK_STATE_MASK, self.sections[y / 16].blocks[x + z * 16 + y % 16 * 16 * 16] & BLOCK_STATE_MASK,
) )
} else {
BlockState::AIR
}
} }
/// Sets the block state at the provided offsets in the chunk.
///
/// # Panics
///
/// Panics if the offsets are outside the bounds of the chunk.
pub fn set_block_state(&mut self, x: usize, y: usize, z: usize, block: BlockState) { pub fn set_block_state(&mut self, x: usize, y: usize, z: usize, block: BlockState) {
assert!( assert!(
x < 16 && y < self.height() && z < 16, x < 16 && y < self.height() && z < 16,
"the chunk block coordinates must be within bounds" "chunk block offsets must be within bounds"
); );
let sect = &mut self.sections[y / 16]; let sect = &mut self.sections[y / 16];
@ -282,18 +297,35 @@ impl<C: Config> Chunk<C> {
} }
} }
/// Gets the biome at the provided biome offsets in the chunk.
///
/// Note: the arguments are **not** block positions. Biomes are 4x4x4
/// segments of a chunk, so `x` and `z` are in `0..=4`.
///
/// # Panics
///
/// Panics if the offsets are outside the bounds of the chunk.
pub fn get_biome(&self, x: usize, y: usize, z: usize) -> BiomeId { pub fn get_biome(&self, x: usize, y: usize, z: usize) -> BiomeId {
if x < 4 && y < self.height() / 4 && z < 4 { assert!(
x < 4 && y < self.height() / 4 && z < 4,
"chunk biome offsets must be within bounds"
);
self.sections[y / 4].biomes[x + z * 4 + y % 4 * 4 * 4] self.sections[y / 4].biomes[x + z * 4 + y % 4 * 4 * 4]
} else {
BiomeId::default()
}
} }
/// Sets the biome at the provided biome offsets in the chunk.
///
/// Note: the arguments are **not** block positions. Biomes are 4x4x4
/// segments of a chunk, so `x` and `z` are in `0..=4`.
///
/// # Panics
///
/// Panics if the offsets are outside the bounds of the chunk.
pub fn set_biome(&mut self, x: usize, y: usize, z: usize, b: BiomeId) { pub fn set_biome(&mut self, x: usize, y: usize, z: usize, b: BiomeId) {
assert!( assert!(
x < 4 && y < self.height() / 4 && z < 4, x < 4 && y < self.height() / 4 && z < 4,
"the chunk biome coordinates must be within bounds" "chunk biome offsets must be within bounds"
); );
self.sections[y / 4].biomes[x + z * 4 + y % 4 * 4 * 4] = b; self.sections[y / 4].biomes[x + z * 4 + y % 4 * 4 * 4] = b;

View file

@ -70,22 +70,22 @@ impl<C: Config> Clients<C> {
/// Removes a client from the server. /// Removes a client from the server.
/// ///
/// If the given client ID is valid, `true` is returned and the client is /// If the given client ID is valid, the client's `ClientState` is returned
/// deleted. Otherwise, `false` is returned and the function has no effect. /// and the client is deleted. Otherwise, `None` is returned and the
/// function has no effect.
pub fn remove(&mut self, client: ClientId) -> Option<C::ClientState> { pub fn remove(&mut self, client: ClientId) -> Option<C::ClientState> {
self.slab.remove(client.0).map(|c| c.state) self.slab.remove(client.0).map(|c| c.state)
} }
/// Deletes all clients from the server for /// Deletes all clients from the server for which `f` returns `true`.
/// which `f` returns `true`.
/// ///
/// All clients are visited in an unspecified order. /// All clients are visited in an unspecified order.
pub fn retain(&mut self, mut f: impl FnMut(ClientId, &mut Client<C>) -> bool) { pub fn retain(&mut self, mut f: impl FnMut(ClientId, &mut Client<C>) -> bool) {
self.slab.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 /// Returns the number of clients on the server. This includes clients for
/// which may be disconnected. /// which [`Client::is_disconnected`] returns true.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.slab.len() self.slab.len()
} }
@ -102,8 +102,8 @@ impl<C: Config> Clients<C> {
self.slab.get_mut(client.0) self.slab.get_mut(client.0)
} }
/// Returns an immutable iterator over all clients on the server in an /// Returns an iterator over all clients on the server in an unspecified
/// unspecified order. /// order.
pub fn iter( pub fn iter(
&self, &self,
) -> impl ExactSizeIterator<Item = (ClientId, &Client<C>)> + FusedIterator + Clone + '_ { ) -> impl ExactSizeIterator<Item = (ClientId, &Client<C>)> + FusedIterator + Clone + '_ {
@ -118,8 +118,8 @@ impl<C: Config> Clients<C> {
self.slab.iter_mut().map(|(k, v)| (ClientId(k), v)) self.slab.iter_mut().map(|(k, v)| (ClientId(k), v))
} }
/// Returns a parallel immutable iterator over all clients on the server in /// Returns a parallel iterator over all clients on the server in an
/// an unspecified order. /// unspecified order.
pub fn par_iter(&self) -> impl ParallelIterator<Item = (ClientId, &Client<C>)> + Clone + '_ { pub fn par_iter(&self) -> impl ParallelIterator<Item = (ClientId, &Client<C>)> + Clone + '_ {
self.slab.par_iter().map(|(k, v)| (ClientId(k), v)) self.slab.par_iter().map(|(k, v)| (ClientId(k), v))
} }
@ -170,7 +170,9 @@ impl ClientId {
/// simply a subtype of the entity base class backed by a remote connection. /// simply a subtype of the entity base class backed by a remote connection.
/// ///
/// In Valence however, clients and players are decoupled. This separation /// In Valence however, clients and players are decoupled. This separation
/// allows for greater flexibility and parallelism. /// allows for greater flexibility and enables parallelism.
///
/// [`Entity`]: crate::entity::Entity
pub struct Client<C: Config> { pub struct Client<C: Config> {
/// Custom state. /// Custom state.
pub state: C::ClientState, pub state: C::ClientState,
@ -301,7 +303,7 @@ impl<C: Config> Client<C> {
self.uuid self.uuid
} }
/// Gets the username of this client, which is always valid. /// Gets the username of this client.
pub fn username(&self) -> &str { pub fn username(&self) -> &str {
&self.username &self.username
} }
@ -317,18 +319,22 @@ impl<C: Config> Client<C> {
self.world self.world
} }
/// Gets the player list this client sees.
pub fn player_list(&self) -> Option<&PlayerListId> { pub fn player_list(&self) -> Option<&PlayerListId> {
self.new_player_list.as_ref() self.new_player_list.as_ref()
} }
pub fn set_player_list(&mut self, id: Option<PlayerListId>) -> Option<PlayerListId> { /// Sets the player list this client sees.
mem::replace(&mut self.new_player_list, id) ///
/// The previous player list ID is returned.
pub fn set_player_list(&mut self, id: impl Into<Option<PlayerListId>>) -> Option<PlayerListId> {
mem::replace(&mut self.new_player_list, id.into())
} }
/// Sets if this client sees the world as superflat. Superflat worlds have /// Sets if this client sees the world as superflat. Superflat worlds have
/// a horizon line lower than normal worlds. /// a horizon line lower than normal worlds.
/// ///
/// The player must be spawned for changes to take effect. /// The player must be (re)spawned for changes to take effect.
pub fn set_flat(&mut self, flat: bool) { pub fn set_flat(&mut self, flat: bool) {
self.bits.set_flat(flat); self.bits.set_flat(flat);
} }
@ -349,7 +355,8 @@ impl<C: Config> Client<C> {
self.bits.set_spawn(true); self.bits.set_spawn(true);
} }
/// Sends a system message to the player which is visible in the chat. /// Sends a system message to the player which is visible in the chat. The
/// message is only visible to this client.
pub fn send_message(&mut self, msg: impl Into<Text>) { pub fn send_message(&mut self, msg: impl Into<Text>) {
// We buffer messages because weird things happen if we send them before the // We buffer messages because weird things happen if we send them before the
// login packet. // login packet.
@ -512,6 +519,8 @@ impl<C: Config> Client<C> {
self.send.is_none() self.send.is_none()
} }
/// Returns an iterator over all pending client events in the order they
/// will be removed from the queue.
pub fn events( pub fn events(
&self, &self,
) -> impl DoubleEndedIterator<Item = &ClientEvent> + ExactSizeIterator + FusedIterator + Clone + '_ ) -> impl DoubleEndedIterator<Item = &ClientEvent> + ExactSizeIterator + FusedIterator + Clone + '_
@ -519,7 +528,7 @@ impl<C: Config> Client<C> {
self.events.iter() self.events.iter()
} }
/// Removes an [`Event`] from the event queue. /// Removes a [`ClientEvent`] from the event queue.
/// ///
/// If there are no remaining events, `None` is returned. /// If there are no remaining events, `None` is returned.
/// ///
@ -529,6 +538,7 @@ impl<C: Config> Client<C> {
self.events.pop_front() self.events.pop_front()
} }
/// Pushes an entity event to the queue.
pub fn push_entity_event(&mut self, event: EntityEvent) { pub fn push_entity_event(&mut self, event: EntityEvent) {
self.entity_events.push(event); self.entity_events.push(event);
} }

View file

@ -15,12 +15,13 @@ use crate::{Ticks, STANDARD_TPS};
/// A trait for the configuration of a server. /// A trait for the configuration of a server.
/// ///
/// This trait uses the [async_trait] attribute macro. It is exported at the /// This trait uses the [async_trait] attribute macro. It is exported at the
/// root of this crate. /// root of this crate. async_trait will be removed once async fns in traits
/// are stabilized.
/// ///
/// [async_trait]: https://docs.rs/async-trait/latest/async_trait/ /// [async_trait]: https://docs.rs/async-trait/latest/async_trait/
#[async_trait] #[async_trait]
#[allow(unused_variables)] #[allow(unused_variables)]
pub trait Config: 'static + Sized + Send + Sync + UnwindSafe + RefUnwindSafe { pub trait Config: Sized + Send + Sync + UnwindSafe + RefUnwindSafe + 'static {
/// Custom state to store with the [`Server`]. /// Custom state to store with the [`Server`].
type ServerState: Send + Sync; type ServerState: Send + Sync;
/// Custom state to store with every [`Client`](crate::client::Client). /// Custom state to store with every [`Client`](crate::client::Client).
@ -93,22 +94,22 @@ pub trait Config: 'static + Sized + Send + Sync + UnwindSafe + RefUnwindSafe {
/// Called once at startup to get the capacity of the buffer used to /// Called once at startup to get the capacity of the buffer used to
/// hold incoming packets. /// hold incoming packets.
/// ///
/// A larger capacity reduces the chance of packet loss but increases /// A larger capacity reduces the chance that a client needs to be
/// potential memory usage. /// disconnected due to a full buffer, but increases potential memory usage.
/// ///
/// # Default Implementation /// # Default Implementation
/// ///
/// An unspecified value is returned that should be adequate in most /// An unspecified value is returned that should be adequate in most
/// situations. /// situations.
fn incoming_packet_capacity(&self) -> usize { fn incoming_packet_capacity(&self) -> usize {
32 64
} }
/// Called once at startup to get the capacity of the buffer used to /// Called once at startup to get the capacity of the buffer used to
/// hold outgoing packets. /// hold outgoing packets.
/// ///
/// A larger capacity reduces the chance of packet loss due to a full buffer /// A larger capacity reduces the chance that a client needs to be
/// but increases potential memory usage. /// disconnected due to a full buffer, but increases potential memory usage.
/// ///
/// # Default Implementation /// # Default Implementation
/// ///

View file

@ -1,4 +1,4 @@
//! Dynamic actors in a world. //! Entities in a world.
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::collections::HashMap; use std::collections::HashMap;
@ -26,7 +26,7 @@ pub mod types;
include!(concat!(env!("OUT_DIR"), "/entity_event.rs")); include!(concat!(env!("OUT_DIR"), "/entity_event.rs"));
/// A container for all [`Entity`]s on a [`Server`](crate::server::Server). /// A container for all [`Entity`]s on a server.
/// ///
/// # Spawning Player Entities /// # Spawning Player Entities
/// ///
@ -34,7 +34,7 @@ include!(concat!(env!("OUT_DIR"), "/entity_event.rs"));
/// entity to be visible to clients, the player's UUID must be added to the /// entity to be visible to clients, the player's UUID must be added to the
/// [`PlayerList`] _before_ being loaded by the client. /// [`PlayerList`] _before_ being loaded by the client.
/// ///
/// [`Player`]: crate::entity::types::Player /// [`Player`]: crate::entity::data::Player
/// [`PlayerList`]: crate::player_list::PlayerList /// [`PlayerList`]: crate::player_list::PlayerList
pub struct Entities<C: Config> { pub struct Entities<C: Config> {
slab: VersionedSlab<Entity<C>>, slab: VersionedSlab<Entity<C>>,
@ -62,7 +62,7 @@ impl<C: Config> Entities<C> {
.expect("UUID collision") .expect("UUID collision")
} }
/// Like [`Self::create`], but requires specifying the new /// Like [`Self::insert`], but requires specifying the new
/// entity's UUID. /// entity's UUID.
/// ///
/// The provided UUID must not conflict with an existing entity UUID. If it /// The provided UUID must not conflict with an existing entity UUID. If it
@ -103,8 +103,9 @@ impl<C: Config> Entities<C> {
/// Removes an entity from the server. /// Removes an entity from the server.
/// ///
/// If the given entity ID is valid, `true` is returned and the entity is /// If the given entity ID is valid, the entity's `EntityState` is returned
/// deleted. Otherwise, `false` is returned and the function has no effect. /// and the entity is deleted. Otherwise, `None` is returned and the
/// function has no effect.
pub fn remove(&mut self, entity: EntityId) -> Option<C::EntityState> { pub fn remove(&mut self, entity: EntityId) -> Option<C::EntityState> {
self.slab.remove(entity.0).map(|e| { self.slab.remove(entity.0).map(|e| {
self.uuid_to_entity self.uuid_to_entity
@ -173,8 +174,8 @@ impl<C: Config> Entities<C> {
Some(EntityId(Key::new(index, version))) Some(EntityId(Key::new(index, version)))
} }
/// Returns an immutable iterator over all entities on the server in an /// Returns an iterator over all entities on the server in an unspecified
/// unspecified order. /// order.
pub fn iter( pub fn iter(
&self, &self,
) -> impl ExactSizeIterator<Item = (EntityId, &Entity<C>)> + FusedIterator + Clone + '_ { ) -> impl ExactSizeIterator<Item = (EntityId, &Entity<C>)> + FusedIterator + Clone + '_ {
@ -189,8 +190,8 @@ impl<C: Config> Entities<C> {
self.slab.iter_mut().map(|(k, v)| (EntityId(k), v)) self.slab.iter_mut().map(|(k, v)| (EntityId(k), v))
} }
/// Returns a parallel immutable iterator over all entities on the server in /// Returns a parallel iterator over all entities on the server in an
/// an unspecified order. /// unspecified order.
pub fn par_iter(&self) -> impl ParallelIterator<Item = (EntityId, &Entity<C>)> + Clone + '_ { pub fn par_iter(&self) -> impl ParallelIterator<Item = (EntityId, &Entity<C>)> + Clone + '_ {
self.slab.par_iter().map(|(k, v)| (EntityId(k), v)) self.slab.par_iter().map(|(k, v)| (EntityId(k), v))
} }
@ -239,7 +240,7 @@ impl EntityId {
/// Represents an entity on the server. /// Represents an entity on the server.
/// ///
/// In essence, an entity is anything in a world that isn't a block or client. /// An entity is mostly anything in a world that isn't a block or client.
/// Entities include paintings, falling blocks, zombies, fireballs, and more. /// Entities include paintings, falling blocks, zombies, fireballs, and more.
/// ///
/// Every entity has common state which is accessible directly from /// Every entity has common state which is accessible directly from
@ -277,10 +278,12 @@ impl<C: Config> Entity<C> {
self.bits self.bits
} }
/// Returns a shared reference to this entity's tracked data.
pub fn data(&self) -> &TrackedData { pub fn data(&self) -> &TrackedData {
&self.variants &self.variants
} }
/// Returns an exclusive reference to this entity's tracked data.
pub fn data_mut(&mut self) -> &mut TrackedData { pub fn data_mut(&mut self) -> &mut TrackedData {
&mut self.variants &mut self.variants
} }
@ -290,6 +293,7 @@ impl<C: Config> Entity<C> {
self.variants.kind() self.variants.kind()
} }
/// Triggers an entity event for this entity.
pub fn push_event(&mut self, event: EntityEvent) { pub fn push_event(&mut self, event: EntityEvent) {
self.events.push(event); self.events.push(event);
} }
@ -411,7 +415,7 @@ impl<C: Config> Entity<C> {
/// The hitbox of an entity is determined by its position, entity type, and /// The hitbox of an entity is determined by its position, entity type, and
/// other state specific to that type. /// other state specific to that type.
/// ///
/// [interact event]: crate::client::EntityEvent::InteractWithEntity /// [interact event]: crate::client::ClientEvent::InteractWithEntity
pub fn hitbox(&self) -> Aabb<f64> { pub fn hitbox(&self) -> Aabb<f64> {
let dims = match &self.variants { let dims = match &self.variants {
TrackedData::Allay(_) => [0.6, 0.35, 0.6], TrackedData::Allay(_) => [0.6, 0.35, 0.6],

View file

@ -11,13 +11,14 @@ use thiserror::Error;
use crate::protocol::{encode_string_bounded, BoundedString, Decode, Encode}; use crate::protocol::{encode_string_bounded, BoundedString, Decode, Encode};
/// An identifier is a string split into a "namespace" part and a "name" part. /// An identifier is a string split into a "namespace" part and a "path" part.
/// For instance `minecraft:apple` and `apple` are both valid identifiers. /// For instance `minecraft:apple` and `apple` are both valid identifiers.
/// ///
/// If the namespace part is left off (the part before and including the colon) /// If the namespace part is left off (the part before and including the colon)
/// the namespace is considered to be "minecraft" for the purposes of equality. /// the namespace is considered to be "minecraft" for the purposes of equality.
/// ///
/// The identifier must match the regex `^([a-z0-9_-]+:)?[a-z0-9_\/.-]+$`. /// A string must match the regex `^([a-z0-9_-]+:)?[a-z0-9_\/.-]+$` to be a
/// valid identifier.
#[derive(Clone, Eq)] #[derive(Clone, Eq)]
pub struct Ident { pub struct Ident {
ident: Cow<'static, AsciiStr>, ident: Cow<'static, AsciiStr>,
@ -90,6 +91,7 @@ impl Ident {
} }
/// Returns the namespace part of this namespaced identifier. /// Returns the namespace part of this namespaced identifier.
///
/// If this identifier was constructed from a string without a namespace, /// If this identifier was constructed from a string without a namespace,
/// then `None` is returned. /// then `None` is returned.
pub fn namespace(&self) -> Option<&str> { pub fn namespace(&self) -> Option<&str> {
@ -100,8 +102,8 @@ impl Ident {
} }
} }
/// Returns the name part of this namespaced identifier. /// Returns the path part of this namespaced identifier.
pub fn name(&self) -> &str { pub fn path(&self) -> &str {
if self.colon_idx == usize::MAX { if self.colon_idx == usize::MAX {
self.ident.as_str() self.ident.as_str()
} else { } else {
@ -188,14 +190,14 @@ impl std::fmt::Display for Ident {
impl PartialEq for Ident { impl PartialEq for Ident {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.namespace().unwrap_or("minecraft") == other.namespace().unwrap_or("minecraft") self.namespace().unwrap_or("minecraft") == other.namespace().unwrap_or("minecraft")
&& self.name() == other.name() && self.path() == other.path()
} }
} }
impl std::hash::Hash for Ident { impl std::hash::Hash for Ident {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.namespace().unwrap_or("minecraft").hash(state); self.namespace().unwrap_or("minecraft").hash(state);
self.name().hash(state); self.path().hash(state);
} }
} }
@ -261,7 +263,7 @@ impl<'de> Visitor<'de> for IdentifierVisitor {
/// let apple = ident!("{namespace}:apple"); /// let apple = ident!("{namespace}:apple");
/// ///
/// assert_eq!(apple.namespace(), Some("my_namespace")); /// assert_eq!(apple.namespace(), Some("my_namespace"));
/// assert_eq!(apple.name(), "apple"); /// assert_eq!(apple.path(), "apple");
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! ident { macro_rules! ident {

View file

@ -1,21 +1,20 @@
//! A Rust framework for building Minecraft servers. //! A Rust framework for building Minecraft servers.
//! //!
//! Valence is a Rust library which provides the necessary abstractions over
//! Minecraft's protocol to build servers. Very few assumptions about the
//! desired server are made, which allows for greater flexibility in its design.
//!
//! At a high level, a Valence [`Server`] is a collection of [`Clients`], //! At a high level, a Valence [`Server`] is a collection of [`Clients`],
//! [`Entities`], and [`Worlds`]. When a client connects to the server, they are //! [`Entities`], and [`Worlds`]. When a client connects to the server they are
//! added to the server's [`Clients`]. After connecting, clients are assigned to //! added to the collection of `Clients`. After connecting, clients should
//! a [`World`] where they are able to interact with the entities and //! be assigned to a [`World`] where they can interact with the entities
//! [`Chunks`] that are a part of it. //! and [`Chunks`] that are a part of it.
//! //!
//! The Valence documentation assumes some familiarity with Minecraft and its //! The Valence documentation assumes some familiarity with Minecraft and its
//! mechanics. See the [Minecraft Wiki] for general information and [wiki.vg] //! mechanics. See the [Minecraft Wiki] for general information and [wiki.vg]
//! for protocol documentation. //! for protocol documentation.
//! //!
//! For more information, see the repository [README].
//!
//! [Minecraft Wiki]: https://minecraft.fandom.com/wiki/Minecraft_Wiki //! [Minecraft Wiki]: https://minecraft.fandom.com/wiki/Minecraft_Wiki
//! [wiki.vg]: https://wiki.vg/Main_Page //! [wiki.vg]: https://wiki.vg/Main_Page
//! [README]: https://github.com/rj00a/valence
//! //!
//! # Logging //! # Logging
//! //!
@ -35,58 +34,16 @@
//! **You must not call [`mem::swap`] on these references (or any other //! **You must not call [`mem::swap`] on these references (or any other
//! function that would move their location in memory).** Doing so breaks //! function that would move their location in memory).** Doing so breaks
//! invariants within the library and the resulting behavior is safe but //! invariants within the library and the resulting behavior is safe but
//! unspecified. These types should be considered [pinned](std::pin). //! unspecified. You can think of these types as being [pinned](std::pin).
//! //!
//! Preventing this illegal behavior using Rust's type system was considered too //! Preventing this illegal behavior using Rust's type system was considered too
//! cumbersome, so a note has been left here instead. //! cumbersome, so this note has been left here instead.
//! //!
//! [`mem::swap`]: std::mem::swap //! [`mem::swap`]: std::mem::swap
//! //!
//! # Examples //! # Examples
//! //!
//! The following is a minimal server implementation. You should be able to //! See the [examples] directory in the source repository.
//! connect to the server at `localhost`.
//!
//! ```
//! use valence::config::Config;
//! use valence::server::{Server, ShutdownResult};
//!
//! pub fn main() -> ShutdownResult {
//! valence::start_server(Game, ())
//! }
//!
//! struct Game;
//!
//! impl Config for Game {
//! type ServerState = ();
//! type ClientState = ();
//! type EntityState = ();
//! type WorldState = ();
//! type ChunkState = ();
//!
//! fn max_connections(&self) -> usize {
//! 256
//! }
//!
//! fn update(&self, server: &mut Server<Self>) {
//! server.clients.retain(|_, client| {
//! if client.created_tick() == server.shared.current_tick() {
//! println!("{} joined!", client.username());
//! }
//!
//! if client.is_disconnected() {
//! println!("{} left!", client.username());
//! false
//! } else {
//! true
//! }
//! });
//! # server.shared.shutdown::<_, std::convert::Infallible>(Ok(()));
//! }
//! }
//! ```
//!
//! For more complete examples, see the [examples] in the source repository.
//! //!
//! [examples]: https://github.com/rj00a/valence/tree/main/examples //! [examples]: https://github.com/rj00a/valence/tree/main/examples
//! //!
@ -122,7 +79,7 @@ pub use async_trait::async_trait;
#[doc(inline)] #[doc(inline)]
pub use server::start_server; pub use server::start_server;
#[doc(inline)] #[doc(inline)]
pub use {uuid, valence_nbt as nbt, vek}; pub use {serde_nbt as nbt, uuid, vek};
pub mod biome; pub mod biome;
pub mod block; pub mod block;

View file

@ -17,10 +17,19 @@ use crate::protocol::VarInt;
use crate::slab_rc::{Key, SlabRc}; use crate::slab_rc::{Key, SlabRc};
use crate::text::Text; use crate::text::Text;
/// A container for all [`PlayerList`]s on a server.
pub struct PlayerLists<C: Config> { pub struct PlayerLists<C: Config> {
slab: SlabRc<PlayerList<C>>, slab: SlabRc<PlayerList<C>>,
} }
/// An identifier for a [`PlayerList`] on the server.
///
/// Player list IDs are refcounted. Once all IDs referring to the same player
/// list are dropped, the player list is automatically deleted.
///
/// The [`Ord`] instance on this type is correct but otherwise unspecified. This
/// is useful for storing IDs in containers such as
/// [`BTreeMap`](std::collections::BTreeMap).
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct PlayerListId(Key); pub struct PlayerListId(Key);
@ -31,6 +40,7 @@ impl<C: Config> PlayerLists<C> {
} }
} }
///
pub fn insert(&mut self, state: C::PlayerListState) -> (PlayerListId, &mut PlayerList<C>) { pub fn insert(&mut self, state: C::PlayerListState) -> (PlayerListId, &mut PlayerList<C>) {
let (key, pl) = self.slab.insert(PlayerList { let (key, pl) = self.slab.insert(PlayerList {
state, state,
@ -199,8 +209,7 @@ impl<C: Config> PlayerList<C> {
self.entries.iter().map(|(k, v)| (*k, v)) self.entries.iter().map(|(k, v)| (*k, v))
} }
/// Returns an iterator which allows modifications over all entries. The /// Returns a mutable iterator over all entries in an unspecified order.
/// entries are visited in an unspecified order.
pub fn entries_mut(&mut self) -> impl Iterator<Item = (Uuid, &mut PlayerListEntry)> + '_ { pub fn entries_mut(&mut self) -> impl Iterator<Item = (Uuid, &mut PlayerListEntry)> + '_ {
self.entries.iter_mut().map(|(k, v)| (*k, v)) self.entries.iter_mut().map(|(k, v)| (*k, v))
} }

View file

@ -1,6 +1,6 @@
//! Provides low-level access to the Minecraft protocol. //! Provides low-level access to the Minecraft protocol.
//! //!
//! Hopefully you will not need to use this module. //! You should avoid this module if possible.
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::mem; use std::mem;

View file

@ -49,9 +49,6 @@ pub trait DecodePacket: Sized + fmt::Debug {
/// ///
/// The fields of the struct are encoded and decoded in the order they are /// The fields of the struct are encoded and decoded in the order they are
/// defined. /// defined.
///
/// If a packet ID is provided after the struct name, then this struct will
/// implement [`EncodePacket`] and [`DecodePacket`].
macro_rules! def_struct { macro_rules! def_struct {
( (
$(#[$struct_attrs:meta])* $(#[$struct_attrs:meta])*
@ -110,9 +107,6 @@ macro_rules! def_struct {
/// ///
/// The enum tag is encoded and decoded first, followed by the appropriate /// The enum tag is encoded and decoded first, followed by the appropriate
/// variant. /// variant.
///
/// If a packet ID is provided after the struct name, then this struct will
/// implement [`EncodePacket`] and [`DecodePacket`].
macro_rules! def_enum { macro_rules! def_enum {
( (
$(#[$enum_attrs:meta])* $(#[$enum_attrs:meta])*
@ -195,6 +189,7 @@ macro_rules! if_typ_is_empty_pat {
}; };
} }
/// Defines a bitfield struct which implements [`Encode`] and [`Decode`].
macro_rules! def_bitfield { macro_rules! def_bitfield {
( (
$(#[$struct_attrs:meta])* $(#[$struct_attrs:meta])*
@ -273,6 +268,10 @@ macro_rules! def_bitfield {
} }
} }
/// Defines an enum of packets.
///
/// An impl for [`EncodePacket`] and [`DecodePacket`] is defined for each
/// supplied packet.
macro_rules! def_packet_group { macro_rules! def_packet_group {
( (
$(#[$attrs:meta])* $(#[$attrs:meta])*

View file

@ -36,9 +36,7 @@ use crate::player_list::PlayerLists;
use crate::player_textures::SignedPlayerTextures; use crate::player_textures::SignedPlayerTextures;
use crate::protocol::codec::{Decoder, Encoder}; use crate::protocol::codec::{Decoder, Encoder};
use crate::protocol::packets::c2s::handshake::{Handshake, HandshakeNextState}; use crate::protocol::packets::c2s::handshake::{Handshake, HandshakeNextState};
use crate::protocol::packets::c2s::login::{ use crate::protocol::packets::c2s::login::{EncryptionResponse, LoginStart, VerifyTokenOrMsgSig};
EncryptionResponse, LoginStart, VerifyTokenOrMsgSig,
};
use crate::protocol::packets::c2s::play::C2sPlayPacket; use crate::protocol::packets::c2s::play::C2sPlayPacket;
use crate::protocol::packets::c2s::status::{QueryPing, QueryRequest}; use crate::protocol::packets::c2s::status::{QueryPing, QueryRequest};
use crate::protocol::packets::s2c::login::{ use crate::protocol::packets::s2c::login::{
@ -59,13 +57,13 @@ pub struct Server<C: Config> {
pub state: C::ServerState, pub state: C::ServerState,
/// A handle to this server's [`SharedServer`]. /// A handle to this server's [`SharedServer`].
pub shared: SharedServer<C>, pub shared: SharedServer<C>,
/// All of the clients in the server. /// All of the clients on the server.
pub clients: Clients<C>, pub clients: Clients<C>,
/// All of entities in the server. /// All of entities on the server.
pub entities: Entities<C>, pub entities: Entities<C>,
/// All of the worlds in the server. /// All of the worlds on the server.
pub worlds: Worlds<C>, pub worlds: Worlds<C>,
/// All of the player lists in the server. /// All of the player lists on the server.
pub player_lists: PlayerLists<C>, pub player_lists: PlayerLists<C>,
} }
@ -221,6 +219,10 @@ impl<C: Config> SharedServer<C> {
} }
/// Obtains a [`Biome`] by using its corresponding [`BiomeId`]. /// Obtains a [`Biome`] by using its corresponding [`BiomeId`].
///
/// It is safe but unspecified behavior to call this function using a
/// [`BiomeId`] not originating from the configuration used to construct
/// the server.
pub fn biome(&self, id: BiomeId) -> &Biome { pub fn biome(&self, id: BiomeId) -> &Biome {
self.0.biomes.get(id.0 as usize).expect("invalid biome ID") self.0.biomes.get(id.0 as usize).expect("invalid biome ID")
} }

View file

@ -16,7 +16,7 @@ use crate::world::WorldId;
/// ///
/// The spatial index is only updated at the end of each tick. Any modification /// The spatial index is only updated at the end of each tick. Any modification
/// to an entity that would change its hitbox is not reflected in the spatial /// to an entity that would change its hitbox is not reflected in the spatial
/// index until the end of the tick. /// index until the next tick.
/// ///
/// [hitboxes]: crate::entity::Entity::hitbox /// [hitboxes]: crate::entity::Entity::hitbox
pub struct SpatialIndex { pub struct SpatialIndex {
@ -28,9 +28,9 @@ impl SpatialIndex {
Self { bvh: Bvh::new() } Self { bvh: Bvh::new() }
} }
/// This is for tests only! Not part of the public API.
#[doc(hidden)] #[doc(hidden)]
#[deprecated = "This is for documentation tests only!"] pub fn test_new() -> Self {
pub fn example_new() -> Self {
dbg!("Don't call me from outside tests!"); dbg!("Don't call me from outside tests!");
Self::new() Self::new()
} }
@ -50,8 +50,7 @@ impl SpatialIndex {
/// Visit all entities intersecting a 10x10x10 cube centered at the origin. /// Visit all entities intersecting a 10x10x10 cube centered at the origin.
/// ///
/// ``` /// ```
/// # #[allow(deprecated)] /// # let si = valence::spatial_index::SpatialIndex::test_new();
/// # let si = valence::spatial_index::SpatialIndex::example_new();
/// use valence::vek::*; /// use valence::vek::*;
/// ///
/// let cube = Aabb { /// let cube = Aabb {
@ -104,9 +103,9 @@ impl SpatialIndex {
/// Casts a ray defined by `origin` and `direction` through entity hitboxes /// Casts a ray defined by `origin` and `direction` through entity hitboxes
/// and returns the closest intersection for which `f` returns `true`. /// and returns the closest intersection for which `f` returns `true`.
/// ///
/// `f` is a predicate which can be used to filter intersections. For /// `f` is a predicate used to filter intersections. For instance, if a ray
/// instance, if a ray is shot from a player's eye position, you probably /// is shot from a player's eye position, you probably don't want the
/// don't want the ray to intersect with the player's own hitbox. /// ray to intersect with the player's own hitbox.
/// ///
/// If no intersections are found or if `f` never returns `true` then `None` /// If no intersections are found or if `f` never returns `true` then `None`
/// is returned. Additionally, the given ray direction must be /// is returned. Additionally, the given ray direction must be
@ -115,8 +114,7 @@ impl SpatialIndex {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # #[allow(deprecated)] /// # let si = valence::spatial_index::SpatialIndex::test_new();
/// # let si = valence::spatial_index::SpatialIndex::example_new();
/// use valence::vek::*; /// use valence::vek::*;
/// ///
/// let origin = Vec3::new(0.0, 0.0, 0.0); /// let origin = Vec3::new(0.0, 0.0, 0.0);

View file

@ -17,7 +17,7 @@ use crate::protocol::{BoundedString, Decode, Encode};
/// ///
/// For more information, see the relevant [Minecraft Wiki article]. /// For more information, see the relevant [Minecraft Wiki article].
/// ///
/// Note that the current `Deserialize` implementation on this type recognizes /// Note that the current [`Deserialize`] implementation on this type recognizes
/// only a subset of the full JSON chat component format. /// only a subset of the full JSON chat component format.
/// ///
/// [Minecraft Wiki article]: https://minecraft.fandom.com/wiki/Raw_JSON_text_format /// [Minecraft Wiki article]: https://minecraft.fandom.com/wiki/Raw_JSON_text_format

View file

@ -57,15 +57,17 @@ impl<C: Config> Worlds<C> {
/// Deletes a world from the server. /// Deletes a world from the server.
/// ///
/// Note that entities located in the world are not deleted themselves. /// Note that any entities located in the world are not deleted.
/// Additionally, any clients that are still in the deleted world at the end /// Additionally, clients that are still in the deleted world at the end
/// of the tick are disconnected. /// of the tick are disconnected.
///
/// Returns `true` if the world was deleted. Otherwise, `false` is returned
/// and the function has no effect.
pub fn remove(&mut self, world: WorldId) -> bool { pub fn remove(&mut self, world: WorldId) -> bool {
self.slab.remove(world.0).is_some() self.slab.remove(world.0).is_some()
} }
/// Deletes all worlds from the server (as if by [`Self::delete`]) for which /// Removes all worlds from the server for which `f` returns `true`.
/// `f` returns `true`.
/// ///
/// All worlds are visited in an unspecified order. /// All worlds are visited in an unspecified order.
pub fn retain(&mut self, mut f: impl FnMut(WorldId, &mut World<C>) -> bool) { pub fn retain(&mut self, mut f: impl FnMut(WorldId, &mut World<C>) -> bool) {
@ -89,8 +91,8 @@ impl<C: Config> Worlds<C> {
self.slab.get_mut(world.0) self.slab.get_mut(world.0)
} }
/// Returns an immutable iterator over all worlds on the server in an /// Returns an iterator over all worlds on the server in an unspecified
/// unspecified order. /// order.
pub fn iter( pub fn iter(
&self, &self,
) -> impl ExactSizeIterator<Item = (WorldId, &World<C>)> + FusedIterator + Clone + '_ { ) -> impl ExactSizeIterator<Item = (WorldId, &World<C>)> + FusedIterator + Clone + '_ {
@ -105,8 +107,8 @@ impl<C: Config> Worlds<C> {
self.slab.iter_mut().map(|(k, v)| (WorldId(k), v)) self.slab.iter_mut().map(|(k, v)| (WorldId(k), v))
} }
/// Returns a parallel immutable iterator over all worlds on the server in /// Returns a parallel iterator over all worlds on the server in an
/// an unspecified order. /// unspecified order.
pub fn par_iter(&self) -> impl ParallelIterator<Item = (WorldId, &World<C>)> + Clone + '_ { pub fn par_iter(&self) -> impl ParallelIterator<Item = (WorldId, &World<C>)> + Clone + '_ {
self.slab.par_iter().map(|(k, v)| (WorldId(k), v)) self.slab.par_iter().map(|(k, v)| (WorldId(k), v))
} }
@ -130,7 +132,7 @@ pub struct World<C: Config> {
pub meta: WorldMeta, pub meta: WorldMeta,
} }
/// Contains miscellaneous world state. /// Contains miscellaneous data about the world.
pub struct WorldMeta { pub struct WorldMeta {
dimension: DimensionId, dimension: DimensionId,
} }