Reorganize modules

This commit is contained in:
Ryan 2022-06-09 20:26:21 -07:00
parent 34d831f5fd
commit efc2873908
15 changed files with 1729 additions and 1720 deletions

118
src/biome.rs Normal file
View file

@ -0,0 +1,118 @@
use crate::{ident, Ident};
/// Identifies a particular [`Biome`].
///
/// Biome IDs are always valid and are cheap to copy and store.
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct BiomeId(pub(crate) u16);
impl BiomeId {
pub fn to_index(self) -> usize {
self.0 as usize
}
}
/// Contains the configuration for a biome.
#[derive(Clone, Debug)]
pub struct Biome {
/// The unique name for this biome. The name can be
/// seen in the F3 debug menu.
pub name: Ident,
pub precipitation: BiomePrecipitation,
pub sky_color: u32,
pub water_fog_color: u32,
pub fog_color: u32,
pub water_color: u32,
pub foliage_color: Option<u32>,
pub grass_color_modifier: BiomeGrassColorModifier,
pub music: Option<BiomeMusic>,
pub ambient_sound: Option<Ident>,
pub additions_sound: Option<BiomeAdditionsSound>,
pub mood_sound: Option<BiomeMoodSound>,
pub particle: Option<BiomeParticle>,
// TODO: The following fields should be added if they can affect the appearance of the biome to
// clients.
// * depth: f32
// * temperature: f32
// * scale: f32
// * downfall: f32
// * category
// * temperature_modifier
// * grass_color (misleading name?)
}
impl Default for Biome {
fn default() -> Self {
Self {
name: ident!("plains"),
precipitation: BiomePrecipitation::Rain,
sky_color: 7907327,
water_fog_color: 329011,
fog_color: 12638463,
water_color: 4159204,
foliage_color: None,
grass_color_modifier: BiomeGrassColorModifier::None,
music: None,
ambient_sound: None,
additions_sound: None,
mood_sound: None,
particle: None,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum BiomePrecipitation {
Rain,
Snow,
None,
}
impl Default for BiomePrecipitation {
fn default() -> Self {
Self::Rain
}
}
/// Minecraft handles grass colors for swamps and dark oak forests in a special
/// way.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum BiomeGrassColorModifier {
Swamp,
DarkForest,
None,
}
impl Default for BiomeGrassColorModifier {
fn default() -> Self {
Self::None
}
}
#[derive(Clone, Debug)]
pub struct BiomeMusic {
pub replace_current_music: bool,
pub sound: Ident,
pub min_delay: i32,
pub max_delay: i32,
}
#[derive(Clone, Debug)]
pub struct BiomeAdditionsSound {
pub sound: Ident,
pub tick_chance: f64,
}
#[derive(Clone, Debug)]
pub struct BiomeMoodSound {
pub sound: Ident,
pub tick_delay: i32,
pub offset: f64,
pub block_search_extent: i32,
}
#[derive(Clone, Debug)]
pub struct BiomeParticle {
pub probability: f32,
pub typ: Ident,
}

View file

@ -11,9 +11,9 @@ impl ByteAngle {
ByteAngle((f.rem_euclid(360.0) / 360.0 * 256.0).round() as u8)
}
pub fn to_degrees(self) -> f32 {
self.0 as f32 / 256.0 * 360.0
}
// pub fn to_degrees(self) -> f32 {
// self.0 as f32 / 256.0 * 360.0
// }
}
impl Encode for ByteAngle {

View file

@ -8,11 +8,10 @@ use std::ops::Deref;
use bitvec::vec::BitVec;
use num::Integer;
use rayon::iter::{IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator};
use vek::Vec2;
use crate::block::BlockState;
use crate::packets::play::{
BlockChange, ChunkDataAndUpdateLight, ChunkDataHeightmaps, ClientPlayPacket, MultiBlockChange,
use crate::packets::play::s2c::{
BlockChange, ChunkDataAndUpdateLight, ChunkDataHeightmaps, MultiBlockChange, S2cPlayPacket,
};
use crate::protocol::{Encode, Nbt};
use crate::var_int::VarInt;
@ -269,7 +268,7 @@ pub(crate) enum BlockChangePacket {
Multi(MultiBlockChange),
}
impl From<BlockChangePacket> for ClientPlayPacket {
impl From<BlockChangePacket> for S2cPlayPacket {
fn from(p: BlockChangePacket) -> Self {
match p {
BlockChangePacket::Single(p) => p.into(),

View file

@ -7,24 +7,24 @@ use rayon::iter::ParallelIterator;
use uuid::Uuid;
use vek::Vec3;
use crate::biome::{Biome, BiomeGrassColorModifier, BiomePrecipitation};
use crate::block_pos::BlockPos;
use crate::byte_angle::ByteAngle;
use crate::config::{
Biome, BiomeGrassColorModifier, BiomePrecipitation, Dimension, DimensionEffects, DimensionId,
};
use crate::dimension::{Dimension, DimensionEffects};
use crate::entity::{velocity_to_packet_units, EntityType};
pub use crate::packets::play::GameMode;
use crate::packets::play::{
use crate::packets::play::c2s::C2sPlayPacket;
pub use crate::packets::play::s2c::GameMode;
use crate::packets::play::s2c::{
Biome as BiomeRegistryBiome, BiomeAdditionsSound, BiomeEffects, BiomeMoodSound, BiomeMusic,
BiomeParticle, BiomeParticleOptions, BiomeProperty, BiomeRegistry, ChangeGameState,
ChangeGameStateReason, ClientPlayPacket, DestroyEntities, DimensionCodec, DimensionType,
DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect, EntityHeadLook, EntityPosition,
ChangeGameStateReason, DestroyEntities, DimensionCodec, DimensionType, DimensionTypeRegistry,
DimensionTypeRegistryEntry, Disconnect, EntityHeadLook, EntityPosition,
EntityPositionAndRotation, EntityRotation, EntityTeleport, EntityVelocity, JoinGame,
KeepAliveClientbound, PlayerPositionAndLook, PlayerPositionAndLookFlags, ServerPlayPacket,
KeepAliveClientbound, PlayerPositionAndLook, PlayerPositionAndLookFlags, S2cPlayPacket,
SpawnPosition, UnloadChunk, UpdateViewDistance, UpdateViewPosition,
};
use crate::protocol::{BoundedInt, Nbt};
use crate::server::ServerPacketChannels;
use crate::server::C2sPacketChannels;
use crate::slotmap::{Key, SlotMap};
use crate::util::{chunks_in_view_distance, is_chunk_in_view_distance};
use crate::var_int::VarInt;
@ -113,8 +113,8 @@ pub struct ClientId(Key);
/// Represents a client connected to the server after logging in.
pub struct Client {
/// Setting this to `None` disconnects the client.
send: Option<Sender<ClientPlayPacket>>,
recv: Receiver<ServerPlayPacket>,
send: Option<Sender<S2cPlayPacket>>,
recv: Receiver<C2sPlayPacket>,
/// The tick this client was created.
created_tick: Ticks,
username: String,
@ -170,7 +170,7 @@ impl<'a> Deref for ClientMut<'a> {
impl Client {
pub(crate) fn new(
packet_channels: ServerPacketChannels,
packet_channels: C2sPacketChannels,
username: String,
uuid: Uuid,
server: &Server,
@ -301,7 +301,7 @@ impl<'a> ClientMut<'a> {
/// Attempts to enqueue a play packet to be sent to this client. The client
/// is disconnected if the clientbound packet buffer is full.
pub(crate) fn send_packet(&mut self, packet: impl Into<ClientPlayPacket>) {
pub(crate) fn send_packet(&mut self, packet: impl Into<S2cPlayPacket>) {
send_packet(&mut self.0.send, packet);
}
@ -630,7 +630,7 @@ impl<'a> ClientMut<'a> {
self.0.old_position = self.0.new_position;
}
fn handle_serverbound_packet(&mut self, pkt: ServerPlayPacket) {
fn handle_serverbound_packet(&mut self, pkt: C2sPlayPacket) {
let client = &mut self.0;
fn handle_movement_packet(
@ -658,7 +658,7 @@ impl<'a> ClientMut<'a> {
}
match pkt {
ServerPlayPacket::TeleportConfirm(p) => {
C2sPlayPacket::TeleportConfirm(p) => {
if client.pending_teleports == 0 {
self.disconnect("Unexpected teleport confirmation");
return;
@ -677,11 +677,11 @@ impl<'a> ClientMut<'a> {
));
}
}
ServerPlayPacket::QueryBlockNbt(_) => {}
ServerPlayPacket::SetDifficulty(_) => {}
ServerPlayPacket::ChatMessageServerbound(_) => {}
ServerPlayPacket::ClientStatus(_) => {}
ServerPlayPacket::ClientSettings(p) => {
C2sPlayPacket::QueryBlockNbt(_) => {}
C2sPlayPacket::SetDifficulty(_) => {}
C2sPlayPacket::ChatMessageServerbound(_) => {}
C2sPlayPacket::ClientStatus(_) => {}
C2sPlayPacket::ClientSettings(p) => {
let old = client.settings.replace(Settings {
locale: p.locale.0,
view_distance: p.view_distance.0,
@ -694,16 +694,16 @@ impl<'a> ClientMut<'a> {
client.events.push(Event::SettingsChanged(old));
}
ServerPlayPacket::TabCompleteServerbound(_) => {}
ServerPlayPacket::ClickWindowButton(_) => {}
ServerPlayPacket::ClickWindow(_) => {}
ServerPlayPacket::CloseWindow(_) => {}
ServerPlayPacket::PluginMessageServerbound(_) => {}
ServerPlayPacket::EditBook(_) => {}
ServerPlayPacket::QueryEntityNbt(_) => {}
ServerPlayPacket::InteractEntity(_) => {}
ServerPlayPacket::GenerateStructure(_) => {}
ServerPlayPacket::KeepAliveServerbound(p) => {
C2sPlayPacket::TabCompleteServerbound(_) => {}
C2sPlayPacket::ClickWindowButton(_) => {}
C2sPlayPacket::ClickWindow(_) => {}
C2sPlayPacket::CloseWindow(_) => {}
C2sPlayPacket::PluginMessageServerbound(_) => {}
C2sPlayPacket::EditBook(_) => {}
C2sPlayPacket::QueryEntityNbt(_) => {}
C2sPlayPacket::InteractEntity(_) => {}
C2sPlayPacket::GenerateStructure(_) => {}
C2sPlayPacket::KeepAliveServerbound(p) => {
let last_keepalive_id = client.last_keepalive_id;
if client.got_keepalive {
self.disconnect("Unexpected keepalive");
@ -716,51 +716,51 @@ impl<'a> ClientMut<'a> {
client.got_keepalive = true;
}
}
ServerPlayPacket::LockDifficulty(_) => {}
ServerPlayPacket::PlayerPosition(p) => {
C2sPlayPacket::LockDifficulty(_) => {}
C2sPlayPacket::PlayerPosition(p) => {
handle_movement_packet(client, p.position, client.yaw, client.pitch, p.on_ground)
}
ServerPlayPacket::PlayerPositionAndRotation(p) => {
C2sPlayPacket::PlayerPositionAndRotation(p) => {
handle_movement_packet(client, p.position, p.yaw, p.pitch, p.on_ground)
}
ServerPlayPacket::PlayerRotation(p) => {
C2sPlayPacket::PlayerRotation(p) => {
handle_movement_packet(client, client.new_position, p.yaw, p.pitch, p.on_ground)
}
ServerPlayPacket::PlayerMovement(p) => handle_movement_packet(
C2sPlayPacket::PlayerMovement(p) => handle_movement_packet(
client,
client.new_position,
client.yaw,
client.pitch,
p.on_ground,
),
ServerPlayPacket::VehicleMoveServerbound(_) => {}
ServerPlayPacket::SteerBoat(_) => {}
ServerPlayPacket::PickItem(_) => {}
ServerPlayPacket::CraftRecipeRequest(_) => {}
ServerPlayPacket::PlayerAbilitiesServerbound(_) => {}
ServerPlayPacket::PlayerDigging(_) => {}
ServerPlayPacket::EntityAction(_) => {}
ServerPlayPacket::SteerVehicle(_) => {}
ServerPlayPacket::Pong(_) => {}
ServerPlayPacket::SetRecipeBookState(_) => {}
ServerPlayPacket::SetDisplayedRecipe(_) => {}
ServerPlayPacket::NameItem(_) => {}
ServerPlayPacket::ResourcePackStatus(_) => {}
ServerPlayPacket::AdvancementTab(_) => {}
ServerPlayPacket::SelectTrade(_) => {}
ServerPlayPacket::SetBeaconEffect(_) => {}
ServerPlayPacket::HeldItemChangeServerbound(_) => {}
ServerPlayPacket::UpdateCommandBlock(_) => {}
ServerPlayPacket::UpdateCommandBlockMinecart(_) => {}
ServerPlayPacket::CreativeInventoryAction(_) => {}
ServerPlayPacket::UpdateJigsawBlock(_) => {}
ServerPlayPacket::UpdateStructureBlock(_) => {}
ServerPlayPacket::UpdateSign(_) => {}
ServerPlayPacket::PlayerArmSwing(_) => {}
ServerPlayPacket::Spectate(_) => {}
ServerPlayPacket::PlayerBlockPlacement(_) => {}
ServerPlayPacket::UseItem(_) => {}
C2sPlayPacket::VehicleMoveServerbound(_) => {}
C2sPlayPacket::SteerBoat(_) => {}
C2sPlayPacket::PickItem(_) => {}
C2sPlayPacket::CraftRecipeRequest(_) => {}
C2sPlayPacket::PlayerAbilitiesServerbound(_) => {}
C2sPlayPacket::PlayerDigging(_) => {}
C2sPlayPacket::EntityAction(_) => {}
C2sPlayPacket::SteerVehicle(_) => {}
C2sPlayPacket::Pong(_) => {}
C2sPlayPacket::SetRecipeBookState(_) => {}
C2sPlayPacket::SetDisplayedRecipe(_) => {}
C2sPlayPacket::NameItem(_) => {}
C2sPlayPacket::ResourcePackStatus(_) => {}
C2sPlayPacket::AdvancementTab(_) => {}
C2sPlayPacket::SelectTrade(_) => {}
C2sPlayPacket::SetBeaconEffect(_) => {}
C2sPlayPacket::HeldItemChangeServerbound(_) => {}
C2sPlayPacket::UpdateCommandBlock(_) => {}
C2sPlayPacket::UpdateCommandBlockMinecart(_) => {}
C2sPlayPacket::CreativeInventoryAction(_) => {}
C2sPlayPacket::UpdateJigsawBlock(_) => {}
C2sPlayPacket::UpdateStructureBlock(_) => {}
C2sPlayPacket::UpdateSign(_) => {}
C2sPlayPacket::PlayerArmSwing(_) => {}
C2sPlayPacket::Spectate(_) => {}
C2sPlayPacket::PlayerBlockPlacement(_) => {}
C2sPlayPacket::UseItem(_) => {}
}
}
}
@ -803,9 +803,9 @@ pub struct Settings {
pub allow_server_listings: bool,
}
pub use crate::packets::play::{ChatMode, DisplayedSkinParts, MainHand};
pub use crate::packets::play::c2s::{ChatMode, DisplayedSkinParts, MainHand};
fn send_packet(send_opt: &mut Option<Sender<ClientPlayPacket>>, pkt: impl Into<ClientPlayPacket>) {
fn send_packet(send_opt: &mut Option<Sender<S2cPlayPacket>>, pkt: impl Into<S2cPlayPacket>) {
if let Some(send) = send_opt {
match send.try_send(pkt.into()) {
Err(TrySendError::Full(_)) => {

View file

@ -6,7 +6,7 @@ use async_trait::async_trait;
use tokio::runtime::Handle as TokioHandle;
use crate::client::ClientMut;
use crate::{ident, Identifier, NewClientData, Server, Text, Ticks, WorldId, WorldsMut};
use crate::{Biome, Dimension, NewClientData, Server, Text, Ticks, WorldId, WorldsMut};
/// A trait containing callbacks which are invoked by the running Minecraft
/// server.
@ -223,203 +223,3 @@ pub enum ServerListPing<'a> {
/// Ignores the query and disconnects from the client.
Ignore,
}
/// A handle to a particular [`Dimension`] on the server.
///
/// Dimension IDs must only be used on servers from which they originate.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct DimensionId(pub(crate) u16);
impl DimensionId {
pub fn to_index(self) -> usize {
self.0 as usize
}
}
/// The default dimension ID corresponds to the first element in the `Vec`
/// returned by [`Config::dimensions`].
impl Default for DimensionId {
fn default() -> Self {
Self(0)
}
}
/// Contains the configuration for a dimension type.
///
/// In Minecraft, "dimension" and "dimension type" are two different concepts.
/// For instance, the Overworld and Nether are dimensions, each with
/// their own dimension type. A dimension in this library is analogous to a
/// [`World`](crate::World) while [`Dimension`] represents a
/// dimension type.
#[derive(Clone, Debug)]
pub struct Dimension {
/// When false, compases will spin randomly.
pub natural: bool,
/// Must be between 0.0 and 1.0.
pub ambient_light: f32,
/// Must be between 0 and 24000.
pub fixed_time: Option<u16>,
/// Determines what skybox/fog effects to use.
pub effects: DimensionEffects,
/// The minimum height in which blocks can exist in this dimension.
///
/// `min_y` must meet the following conditions:
/// * `min_y % 16 == 0`
/// * `-2032 <= min_y <= 2016`
pub min_y: i32,
/// The total height in which blocks can exist in this dimension.
///
/// `height` must meet the following conditions:
/// * `height % 16 == 0`
/// * `0 <= height <= 4064`
/// * `min_y + height <= 2032`
pub height: i32,
// TODO: The following fields should be added if they can affect the
// appearance of the dimension to clients.
// * infiniburn
// * respawn_anchor_works
// * has_skylight
// * bed_works
// * has_raids
// * logical_height
// * coordinate_scale
// * ultrawarm
// * has_ceiling
}
impl Default for Dimension {
fn default() -> Self {
Self {
natural: true,
ambient_light: 1.0,
fixed_time: None,
effects: DimensionEffects::Overworld,
min_y: -64,
height: 384,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DimensionEffects {
Overworld,
TheNether,
TheEnd,
}
/// Identifies a particular [`Biome`].
///
/// Biome IDs are always valid and are cheap to copy and store.
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct BiomeId(pub(crate) u16);
impl BiomeId {
pub fn to_index(self) -> usize {
self.0 as usize
}
}
/// Contains the configuration for a biome.
#[derive(Clone, Debug)]
pub struct Biome {
/// The unique name for this biome. The name can be
/// seen in the F3 debug menu.
pub name: Identifier,
pub precipitation: BiomePrecipitation,
pub sky_color: u32,
pub water_fog_color: u32,
pub fog_color: u32,
pub water_color: u32,
pub foliage_color: Option<u32>,
pub grass_color_modifier: BiomeGrassColorModifier,
pub music: Option<BiomeMusic>,
pub ambient_sound: Option<Identifier>,
pub additions_sound: Option<BiomeAdditionsSound>,
pub mood_sound: Option<BiomeMoodSound>,
pub particle: Option<BiomeParticle>,
// TODO: The following fields should be added if they can affect the appearance of the biome to
// clients.
// * depth: f32
// * temperature: f32
// * scale: f32
// * downfall: f32
// * category
// * temperature_modifier
// * grass_color (misleading name?)
}
impl Default for Biome {
fn default() -> Self {
Self {
name: ident!("plains"),
precipitation: BiomePrecipitation::Rain,
sky_color: 7907327,
water_fog_color: 329011,
fog_color: 12638463,
water_color: 4159204,
foliage_color: None,
grass_color_modifier: BiomeGrassColorModifier::None,
music: None,
ambient_sound: None,
additions_sound: None,
mood_sound: None,
particle: None,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum BiomePrecipitation {
Rain,
Snow,
None,
}
impl Default for BiomePrecipitation {
fn default() -> Self {
Self::Rain
}
}
/// Minecraft handles grass colors for swamps and dark oak forests in a special
/// way.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum BiomeGrassColorModifier {
Swamp,
DarkForest,
None,
}
impl Default for BiomeGrassColorModifier {
fn default() -> Self {
Self::None
}
}
#[derive(Clone, Debug)]
pub struct BiomeMusic {
pub replace_current_music: bool,
pub sound: Identifier,
pub min_delay: i32,
pub max_delay: i32,
}
#[derive(Clone, Debug)]
pub struct BiomeAdditionsSound {
pub sound: Identifier,
pub tick_chance: f64,
}
#[derive(Clone, Debug)]
pub struct BiomeMoodSound {
pub sound: Identifier,
pub tick_delay: i32,
pub offset: f64,
pub block_search_extent: i32,
}
#[derive(Clone, Debug)]
pub struct BiomeParticle {
pub probability: f32,
pub typ: Identifier,
}

82
src/dimension.rs Normal file
View file

@ -0,0 +1,82 @@
/// A handle to a particular [`Dimension`] on the server.
///
/// Dimension IDs must only be used on servers from which they originate.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct DimensionId(pub(crate) u16);
impl DimensionId {
pub fn to_index(self) -> usize {
self.0 as usize
}
}
/// The default dimension ID corresponds to the first element in the `Vec`
/// returned by [`Config::dimensions`].
impl Default for DimensionId {
fn default() -> Self {
Self(0)
}
}
/// Contains the configuration for a dimension type.
///
/// In Minecraft, "dimension" and "dimension type" are two different concepts.
/// For instance, the Overworld and Nether are dimensions, each with
/// their own dimension type. A dimension in this library is analogous to a
/// [`World`](crate::World) while [`Dimension`] represents a
/// dimension type.
#[derive(Clone, Debug)]
pub struct Dimension {
/// When false, compases will spin randomly.
pub natural: bool,
/// Must be between 0.0 and 1.0.
pub ambient_light: f32,
/// Must be between 0 and 24000.
pub fixed_time: Option<u16>,
/// Determines what skybox/fog effects to use.
pub effects: DimensionEffects,
/// The minimum height in which blocks can exist in this dimension.
///
/// `min_y` must meet the following conditions:
/// * `min_y % 16 == 0`
/// * `-2032 <= min_y <= 2016`
pub min_y: i32,
/// The total height in which blocks can exist in this dimension.
///
/// `height` must meet the following conditions:
/// * `height % 16 == 0`
/// * `0 <= height <= 4064`
/// * `min_y + height <= 2032`
pub height: i32,
// TODO: The following fields should be added if they can affect the
// appearance of the dimension to clients.
// * infiniburn
// * respawn_anchor_works
// * has_skylight
// * bed_works
// * has_raids
// * logical_height
// * coordinate_scale
// * ultrawarm
// * has_ceiling
}
impl Default for Dimension {
fn default() -> Self {
Self {
natural: true,
ambient_light: 1.0,
fixed_time: None,
effects: DimensionEffects::Overworld,
min_y: -64,
height: 384,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DimensionEffects {
Overworld,
TheNether,
TheEnd,
}

View file

@ -12,8 +12,8 @@ use uuid::Uuid;
use vek::{Aabb, Vec3};
use crate::byte_angle::ByteAngle;
use crate::packets::play::{
ClientPlayPacket, EntityMetadata, SpawnEntity, SpawnExperienceOrb, SpawnLivingEntity,
use crate::packets::play::s2c::{
EntityMetadata, S2cPlayPacket, SpawnEntity, SpawnExperienceOrb, SpawnLivingEntity,
SpawnPainting, SpawnPlayer,
};
use crate::protocol::RawBytes;
@ -580,7 +580,7 @@ pub(crate) enum EntitySpawnPacket {
SpawnPlayer(SpawnPlayer),
}
impl From<EntitySpawnPacket> for ClientPlayPacket {
impl From<EntitySpawnPacket> for S2cPlayPacket {
fn from(pkt: EntitySpawnPacket) -> Self {
match pkt {
EntitySpawnPacket::SpawnEntity(pkt) => pkt.into(),

View file

@ -17,7 +17,7 @@ use crate::protocol::{encode_string_bounded, BoundedString, Decode, Encode};
///
/// The entire identifier must match the regex `([a-z0-9_-]+:)?[a-z0-9_\/.-]+`.
#[derive(Clone, Eq)]
pub struct Identifier {
pub struct Ident {
ident: Cow<'static, AsciiStr>,
/// The index of the ':' character in the string.
/// If there is no namespace then it is `usize::MAX`.
@ -33,12 +33,12 @@ pub struct ParseError {
src: Cow<'static, str>,
}
impl Identifier {
impl Ident {
/// Parses a new identifier from a string.
///
/// The string must match the regex `([a-z0-9_-]+:)?[a-z0-9_\/.-]+`.
/// If not, an error is returned.
pub fn new(str: impl Into<Cow<'static, str>>) -> Result<Identifier, ParseError> {
pub fn new(str: impl Into<Cow<'static, str>>) -> Result<Ident, ParseError> {
#![allow(bindings_with_variant_name)]
let cow = match str.into() {
@ -125,55 +125,55 @@ impl ParseError {
}
}
impl std::fmt::Debug for Identifier {
impl std::fmt::Debug for Ident {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Identifier").field(&self.as_str()).finish()
}
}
impl FromStr for Identifier {
impl FromStr for Ident {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Identifier::new(s.to_string())
Ident::new(s.to_string())
}
}
impl From<Identifier> for String {
fn from(id: Identifier) -> Self {
impl From<Ident> for String {
fn from(id: Ident) -> Self {
id.ident.into_owned().into()
}
}
impl From<Identifier> for Cow<'static, str> {
fn from(id: Identifier) -> Self {
impl From<Ident> for Cow<'static, str> {
fn from(id: Ident) -> Self {
ascii_cow_to_str_cow(id.ident)
}
}
impl AsRef<str> for Identifier {
impl AsRef<str> for Ident {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl TryFrom<String> for Identifier {
impl TryFrom<String> for Ident {
type Error = ParseError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Identifier::new(value)
Ident::new(value)
}
}
impl TryFrom<&'static str> for Identifier {
impl TryFrom<&'static str> for Ident {
type Error = ParseError;
fn try_from(value: &'static str) -> Result<Self, Self::Error> {
Identifier::new(value)
Ident::new(value)
}
}
impl std::fmt::Display for Identifier {
impl std::fmt::Display for Ident {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
@ -181,40 +181,40 @@ impl std::fmt::Display for Identifier {
/// Equality for identifiers respects the fact that "minecraft:apple" and
/// "apple" have the same meaning.
impl PartialEq for Identifier {
impl PartialEq for Ident {
fn eq(&self, other: &Self) -> bool {
self.namespace().unwrap_or("minecraft") == other.namespace().unwrap_or("minecraft")
&& self.name() == other.name()
}
}
impl std::hash::Hash for Identifier {
impl std::hash::Hash for Ident {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.namespace().unwrap_or("minecraft").hash(state);
self.name().hash(state);
}
}
impl Encode for Identifier {
impl Encode for Ident {
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
encode_string_bounded(self.as_str(), 0, 32767, w)
}
}
impl Decode for Identifier {
impl Decode for Ident {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
let string = BoundedString::<0, 32767>::decode(r)?.0;
Ok(Identifier::new(string)?)
Ok(Ident::new(string)?)
}
}
impl Serialize for Identifier {
impl Serialize for Ident {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.as_str().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Identifier {
impl<'de> Deserialize<'de> for Ident {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_str(IdentifierVisitor)
}
@ -224,18 +224,18 @@ impl<'de> Deserialize<'de> for Identifier {
struct IdentifierVisitor;
impl<'de> Visitor<'de> for IdentifierVisitor {
type Value = Identifier;
type Value = Ident;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "a valid Minecraft identifier")
}
fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
Identifier::from_str(s).map_err(E::custom)
Ident::from_str(s).map_err(E::custom)
}
fn visit_string<E: serde::de::Error>(self, s: String) -> Result<Self::Value, E> {
Identifier::new(s).map_err(E::custom)
Ident::new(s).map_err(E::custom)
}
}
@ -249,8 +249,8 @@ macro_rules! ident {
let errmsg = "invalid identifier in `ident` macro";
#[allow(clippy::redundant_closure_call)]
(|args: ::std::fmt::Arguments| match args.as_str() {
Some(s) => $crate::Identifier::new(s).expect(errmsg),
None => $crate::Identifier::new(args.to_string()).expect(errmsg),
Some(s) => $crate::Ident::new(s).expect(errmsg),
None => $crate::Ident::new(args.to_string()).expect(errmsg),
})(format_args!($($arg)*))
}}
}

View file

@ -7,6 +7,7 @@
// missing_docs
)]
pub mod biome;
pub mod block;
mod block_pos;
mod byte_angle;
@ -14,8 +15,9 @@ pub mod chunk;
pub mod client;
mod codec;
pub mod config;
pub mod dimension;
pub mod entity;
pub mod identifier;
pub mod ident;
mod packets;
mod protocol;
pub mod server;
@ -27,12 +29,14 @@ mod var_long;
pub mod world;
pub use async_trait::async_trait;
pub use biome::{Biome, BiomeId};
pub use block_pos::BlockPos;
pub use chunk::{Chunk, ChunkPos, Chunks, ChunksMut};
pub use client::{Client, ClientMut, Clients, ClientsMut};
pub use config::{Biome, BiomeId, Config, Dimension, DimensionId};
pub use config::Config;
pub use dimension::{Dimension, DimensionId};
pub use entity::{Entities, EntitiesMut, Entity, EntityId, EntityType};
pub use identifier::Identifier;
pub use ident::Ident;
pub use server::{start_server, NewClientData, Server, ShutdownResult};
pub use text::{Text, TextFormat};
pub use uuid::Uuid;

View file

@ -17,11 +17,10 @@ use vek::Vec3;
use crate::block_pos::BlockPos;
use crate::byte_angle::ByteAngle;
use crate::identifier::Identifier;
use crate::protocol::{BoundedArray, BoundedInt, BoundedString, Decode, Encode, Nbt, RawBytes};
use crate::var_int::VarInt;
use crate::var_long::VarLong;
use crate::Text;
use crate::{Ident, Text};
/// Trait for types that can be written to the Minecraft protocol as a complete
/// packet.
@ -353,9 +352,8 @@ pub mod handshake {
/// Packets and types used during the status state.
pub mod status {
use super::*;
// ==== Clientbound ====
pub mod s2c {
use super::super::*;
def_struct! {
Response 0x00 {
@ -369,8 +367,10 @@ pub mod status {
payload: u64
}
}
}
// ==== Serverbound ====
pub mod c2s {
use super::super::*;
def_struct! {
Request 0x00 {}
@ -382,12 +382,12 @@ pub mod status {
}
}
}
}
/// Packets and types used during the play state.
pub mod login {
use super::*;
// ==== Clientbound ====
pub mod s2c {
use super::super::*;
def_struct! {
Disconnect 0x00 {
@ -417,8 +417,10 @@ pub mod login {
threshold: VarInt
}
}
}
// ==== Serverbound ====
pub mod c2s {
use super::super::*;
def_struct! {
LoginStart 0x00 {
@ -433,12 +435,12 @@ pub mod login {
}
}
}
}
/// Packets and types used during the play state.
pub mod play {
use super::*;
// ==== Clientbound ====
pub mod s2c {
use super::super::*;
def_struct! {
SpawnEntity 0x00 {
@ -506,7 +508,7 @@ pub mod play {
def_struct! {
SculkVibrationSignal 0x05 {
source_position: BlockPos,
destination_identifier: Identifier, // TODO: destination codec type?
destination_identifier: Ident, // TODO: destination codec type?
destination: BlockPos, // TODO: this type varies depending on destination_identifier
arrival_ticks: VarInt,
}
@ -807,12 +809,12 @@ pub mod play {
/// The previous gamemode for the purpose of the F3+F4 gamemode switcher. (TODO: verify)
/// Is `-1` if there was no previous gamemode.
previous_gamemode: GameMode,
dimension_names: Vec<Identifier>,
dimension_names: Vec<Ident>,
dimension_codec: Nbt<DimensionCodec>,
/// The specification of the dimension being spawned into.
dimension: Nbt<DimensionType>,
/// The identifier of the dimension being spawned into.
dimension_name: Identifier,
dimension_name: Ident,
/// Hash of the world's seed used for client biome noise.
hashed_seed: i64,
/// No longer used by the client.
@ -841,13 +843,13 @@ pub mod play {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DimensionTypeRegistry {
#[serde(rename = "type")]
pub typ: Identifier,
pub typ: Ident,
pub value: Vec<DimensionTypeRegistryEntry>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DimensionTypeRegistryEntry {
pub name: Identifier,
pub name: Ident,
pub id: i32,
pub element: DimensionType,
}
@ -862,7 +864,7 @@ pub mod play {
pub respawn_anchor_works: bool,
pub has_skylight: bool,
pub bed_works: bool,
pub effects: Identifier,
pub effects: Ident,
pub has_raids: bool,
pub min_y: i32,
pub height: i32,
@ -875,13 +877,13 @@ pub mod play {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BiomeRegistry {
#[serde(rename = "type")]
pub typ: Identifier,
pub typ: Ident,
pub value: Vec<Biome>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Biome {
pub name: Identifier,
pub name: Ident,
pub id: i32,
pub element: BiomeProperty,
}
@ -909,7 +911,7 @@ pub mod play {
pub grass_color: Option<i32>,
pub grass_color_modifier: Option<String>,
pub music: Option<BiomeMusic>,
pub ambient_sound: Option<Identifier>,
pub ambient_sound: Option<Ident>,
pub additions_sound: Option<BiomeAdditionsSound>,
pub mood_sound: Option<BiomeMoodSound>,
}
@ -917,20 +919,20 @@ pub mod play {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BiomeMusic {
pub replace_current_music: bool,
pub sound: Identifier,
pub sound: Ident,
pub max_delay: i32,
pub min_delay: i32,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BiomeAdditionsSound {
pub sound: Identifier,
pub sound: Ident,
pub tick_chance: f64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BiomeMoodSound {
pub sound: Identifier,
pub sound: Ident,
pub tick_delay: i32,
pub offset: f64,
pub block_search_extent: i32,
@ -945,7 +947,7 @@ pub mod play {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BiomeParticleOptions {
#[serde(rename = "type")]
pub typ: Identifier,
pub typ: Ident,
}
def_enum! {
@ -1094,34 +1096,34 @@ pub mod play {
}
}
macro_rules! def_client_play_packet_enum {
macro_rules! def_s2c_play_packet_enum {
{
$($packet:ident),* $(,)?
} => {
/// An enum of all clientbound play packets.
/// An enum of all s2c play packets.
#[derive(Clone, Debug)]
pub enum ClientPlayPacket {
pub enum S2cPlayPacket {
$($packet($packet)),*
}
impl private::Sealed for ClientPlayPacket {}
impl private::Sealed for S2cPlayPacket {}
$(
impl From<$packet> for ClientPlayPacket {
fn from(p: $packet) -> ClientPlayPacket {
ClientPlayPacket::$packet(p)
impl From<$packet> for S2cPlayPacket {
fn from(p: $packet) -> S2cPlayPacket {
S2cPlayPacket::$packet(p)
}
}
)*
impl EncodePacket for ClientPlayPacket {
impl EncodePacket for S2cPlayPacket {
fn encode_packet(&self, w: &mut impl Write) -> anyhow::Result<()> {
match self {
$(
Self::$packet(p) => {
VarInt($packet::PACKET_ID)
.encode(w)
.context(concat!("failed to write play packet ID for `", stringify!($packet), "`"))?;
.context(concat!("failed to write s2c play packet ID for `", stringify!($packet), "`"))?;
p.encode(w)
}
)*
@ -1131,7 +1133,7 @@ pub mod play {
#[cfg(test)]
#[test]
fn test_client_play_packet_order() {
fn test_s2c_play_packet_order() {
let ids = [
$(
(stringify!($packet), $packet::PACKET_ID),
@ -1139,13 +1141,13 @@ pub mod play {
];
if let Some(w) = ids.windows(2).find(|w| w[0].1 >= w[1].1) {
panic!("the {} and {} variants of the client play packet enum are not properly sorted by their packet ID", w[0].0, w[1].0);
panic!("the {} and {} variants of the s2c play packet enum are not properly sorted by their packet ID", w[0].0, w[1].0);
}
}
}
}
def_client_play_packet_enum! {
def_s2c_play_packet_enum! {
SpawnEntity,
SpawnExperienceOrb,
SpawnLivingEntity,
@ -1182,8 +1184,10 @@ pub mod play {
EntityTeleport,
TimeUpdate,
}
}
// ==== Serverbound ====
pub mod c2s {
use super::super::*;
def_struct! {
TeleportConfirm 0x00 {
@ -1302,7 +1306,7 @@ pub mod play {
def_struct! {
PluginMessageServerbound 0x0a {
channel: Identifier,
channel: Ident,
data: RawBytes,
}
}
@ -1434,7 +1438,7 @@ pub mod play {
def_struct! {
CraftRecipeRequest 0x18 {
window_id: i8,
recipe: Identifier,
recipe: Ident,
make_all: bool,
}
}
@ -1545,7 +1549,7 @@ pub mod play {
def_struct! {
SetDisplayedRecipe 0x1f {
recipe_id: Identifier,
recipe_id: Ident,
}
}
@ -1566,7 +1570,7 @@ pub mod play {
def_enum! {
AdvancementTab 0x22: VarInt {
OpenedTab: Identifier = 0,
OpenedTab: Ident = 0,
ClosedScreen = 1,
}
}
@ -1634,9 +1638,9 @@ pub mod play {
def_struct! {
UpdateJigsawBlock 0x29 {
location: BlockPos,
name: Identifier,
target: Identifier,
pool: Identifier,
name: Ident,
target: Ident,
pool: Ident,
final_state: String,
joint_type: String,
}
@ -1737,29 +1741,29 @@ pub mod play {
}
}
macro_rules! def_server_play_packet_enum {
macro_rules! def_c2s_play_packet_enum {
{
$($packet:ident),* $(,)?
} => {
/// An enum of all serverbound play packets.
/// An enum of all client-to-server play packets.
#[derive(Clone, Debug)]
pub enum ServerPlayPacket {
pub enum C2sPlayPacket {
$($packet($packet)),*
}
impl private::Sealed for ServerPlayPacket {}
impl private::Sealed for C2sPlayPacket {}
impl DecodePacket for ServerPlayPacket {
fn decode_packet(r: &mut impl Read) -> anyhow::Result<ServerPlayPacket> {
let packet_id = VarInt::decode(r).context("failed to read play packet ID")?.0;
impl DecodePacket for C2sPlayPacket {
fn decode_packet(r: &mut impl Read) -> anyhow::Result<C2sPlayPacket> {
let packet_id = VarInt::decode(r).context("failed to read c2s play packet ID")?.0;
match packet_id {
$(
$packet::PACKET_ID => {
let pkt = $packet::decode(r)?;
Ok(ServerPlayPacket::$packet(pkt))
Ok(C2sPlayPacket::$packet(pkt))
}
)*
id => bail!("unknown play packet ID {:#04x}", id)
id => bail!("unknown c2s play packet ID {:#04x}", id)
}
}
}
@ -1767,7 +1771,7 @@ pub mod play {
#[cfg(test)]
#[test]
fn test_server_play_packet_order() {
fn test_c2s_play_packet_order() {
let ids = [
$(
(stringify!($packet), $packet::PACKET_ID),
@ -1775,13 +1779,13 @@ pub mod play {
];
if let Some(w) = ids.windows(2).find(|w| w[0].1 >= w[1].1) {
panic!("the {} and {} variants of the server play packet enum are not properly sorted by their packet ID", w[0].0, w[1].0);
panic!("the {} and {} variants of the c2s play packet enum are not properly sorted by their packet ID", w[0].0, w[1].0);
}
}
}
}
def_server_play_packet_enum! {
def_c2s_play_packet_enum! {
TeleportConfirm,
QueryBlockNbt,
SetDifficulty,
@ -1832,6 +1836,7 @@ pub mod play {
UseItem,
}
}
}
#[cfg(test)]
pub mod test {

View file

@ -27,18 +27,23 @@ use tokio::sync::{oneshot, Semaphore};
use uuid::Uuid;
use crate::codec::{Decoder, Encoder};
use crate::config::{Biome, BiomeId, Config, Dimension, DimensionId, ServerListPing};
use crate::config::{Config, ServerListPing};
use crate::packets::handshake::{Handshake, HandshakeNextState};
use crate::packets::login::{
self, EncryptionRequest, EncryptionResponse, LoginStart, LoginSuccess, SetCompression,
};
use crate::packets::play::{ClientPlayPacket, ServerPlayPacket};
use crate::packets::status::{Ping, Pong, Request, Response};
use crate::packets::login;
use crate::packets::login::c2s::{EncryptionResponse, LoginStart};
use crate::packets::login::s2c::{EncryptionRequest, LoginSuccess, SetCompression};
use crate::packets::play::c2s::C2sPlayPacket;
use crate::packets::play::s2c::S2cPlayPacket;
use crate::packets::status::c2s::{Ping, Request};
use crate::packets::status::s2c::{Pong, Response};
use crate::protocol::{BoundedArray, BoundedString};
use crate::util::valid_username;
use crate::var_int::VarInt;
use crate::world::Worlds;
use crate::{Client, ClientMut, Ticks, WorldsMut, PROTOCOL_VERSION, VERSION_NAME};
use crate::{
Biome, BiomeId, Client, ClientMut, Dimension, DimensionId, Ticks, WorldsMut, PROTOCOL_VERSION,
VERSION_NAME,
};
/// A handle to a running Minecraft server containing state which is accessible
/// outside the update loop. Servers are internally refcounted and can be shared
@ -89,7 +94,7 @@ pub struct NewClientData {
struct NewClientMessage {
ncd: NewClientData,
reply: oneshot::Sender<ClientPacketChannels>,
reply: oneshot::Sender<S2cPacketChannels>,
}
/// The result type returned from [`ServerConfig::start`] after the server is
@ -97,8 +102,8 @@ struct NewClientMessage {
pub type ShutdownResult = Result<(), ShutdownError>;
pub type ShutdownError = Box<dyn Error + Send + Sync + 'static>;
pub(crate) type ClientPacketChannels = (Sender<ServerPlayPacket>, Receiver<ClientPlayPacket>);
pub(crate) type ServerPacketChannels = (Sender<ClientPlayPacket>, Receiver<ServerPlayPacket>);
pub(crate) type S2cPacketChannels = (Sender<C2sPlayPacket>, Receiver<S2cPlayPacket>);
pub(crate) type C2sPacketChannels = (Sender<S2cPlayPacket>, Receiver<C2sPlayPacket>);
impl Server {
pub fn config(&self) -> &(impl Config + ?Sized) {
@ -368,12 +373,7 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult {
});
world.clients.par_iter_mut().for_each(|(_, mut client)| {
client.update(
&server,
&world.entities,
&world.chunks,
&world.meta,
);
client.update(&server, &world.entities, &world.chunks, &world.meta);
});
world.entities.update();
@ -396,8 +396,8 @@ fn join_player(server: &Server, mut worlds: WorldsMut, msg: NewClientMessage) {
let (clientbound_tx, clientbound_rx) = flume::bounded(server.0.outgoing_packet_capacity);
let (serverbound_tx, serverbound_rx) = flume::bounded(server.0.incoming_packet_capacity);
let client_packet_channels: ClientPacketChannels = (serverbound_tx, clientbound_rx);
let server_packet_channels: ServerPacketChannels = (clientbound_tx, serverbound_rx);
let client_packet_channels: S2cPacketChannels = (serverbound_tx, clientbound_rx);
let server_packet_channels: C2sPacketChannels = (clientbound_tx, serverbound_rx);
let _ = msg.reply.send(client_packet_channels);
@ -674,7 +674,7 @@ async fn handle_login(
if let Err(reason) = server.0.cfg.login(server, &npd).await {
log::info!("Disconnect at login: \"{reason}\"");
c.0.write_packet(&login::Disconnect { reason }).await?;
c.0.write_packet(&login::s2c::Disconnect { reason }).await?;
return Ok(None);
}

View file

@ -9,7 +9,7 @@ use serde::de::Visitor;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::protocol::{BoundedString, Decode, Encode};
use crate::Identifier;
use crate::Ident;
/// Represents formatted text in Minecraft's JSON text format.
///
@ -308,14 +308,14 @@ enum ClickEvent {
enum HoverEvent {
ShowText(Box<Text>),
ShowItem {
id: Identifier,
id: Ident,
count: Option<i32>,
// TODO: tag
},
ShowEntity {
name: Box<Text>,
#[serde(rename = "type")]
typ: Identifier,
typ: Ident,
// TODO: id (hyphenated entity UUID as a string)
},
}

View file

@ -54,7 +54,8 @@ where
aabb
}
/// Takes a normalized direction vector and returns a (yaw, pitch) tuple in degrees.
/// Takes a normalized direction vector and returns a (yaw, pitch) tuple in
/// degrees.
///
/// This function is the inverse of [`from_yaw_and_pitch`].
pub fn to_yaw_and_pitch(d: Vec3<f64>) -> (f32, f32) {
@ -65,7 +66,8 @@ pub fn to_yaw_and_pitch(d: Vec3<f64>) -> (f32, f32) {
(yaw, pitch)
}
/// Takes yaw and pitch angles (in degrees) and returns a normalized direction vector.
/// Takes yaw and pitch angles (in degrees) and returns a normalized direction
/// vector.
///
/// This function is the inverse of [`to_yaw_and_pitch`].
pub fn from_yaw_and_pitch(yaw: f32, pitch: f32) -> Vec3<f64> {

View file

@ -3,9 +3,8 @@ use std::ops::Deref;
use rayon::iter::ParallelIterator;
use crate::config::DimensionId;
use crate::slotmap::{Key, SlotMap};
use crate::{Chunks, ChunksMut, Clients, ClientsMut, Entities, EntitiesMut, Server};
use crate::{Chunks, ChunksMut, Clients, ClientsMut, DimensionId, Entities, EntitiesMut, Server};
pub struct Worlds {
sm: SlotMap<World>,