diff --git a/mclib/main/src/lib.rs b/mclib/main/src/lib.rs index 2699576..10af99a 100644 --- a/mclib/main/src/lib.rs +++ b/mclib/main/src/lib.rs @@ -5,6 +5,7 @@ pub use mclib_protocol::types::base::MCType; pub mod packets { pub mod server { pub use mclib_protocol::packets::server::handshake::{Handshake, HandshakeNextState}; + pub use mclib_protocol::packets::server::login_acknowledged::LoginAcknowledged; pub use mclib_protocol::packets::server::login_start::LoginStart; pub use mclib_protocol::packets::server::ping::PingRequest; pub use mclib_protocol::packets::server::status_request::StatusRequest; @@ -26,7 +27,7 @@ pub mod types { pub use mclib_protocol::types::varint::MCVarInt; } pub mod nbt { - pub use mclib_protocol::nbt::tags::base::NBTTag; + pub use mclib_protocol::nbt::tags::base::{IntoNBTTag, NBTTag}; pub use mclib_protocol::nbt::tags::byte::TagByte; pub use mclib_protocol::nbt::tags::byte_array::TagByteArray; pub use mclib_protocol::nbt::tags::compound::TagCompound; @@ -37,4 +38,5 @@ pub mod nbt { pub use mclib_protocol::nbt::tags::long::TagLong; pub use mclib_protocol::nbt::tags::short::TagShort; pub use mclib_protocol::nbt::tags::string::TagString; + pub use mclib_protocol::nbt::NBT; } diff --git a/mclib/protocol/src/lib.rs b/mclib/protocol/src/lib.rs index 4af705b..1d6a03b 100644 --- a/mclib/protocol/src/lib.rs +++ b/mclib/protocol/src/lib.rs @@ -1,3 +1,4 @@ pub mod nbt; pub mod packets; pub mod types; +pub mod utils; diff --git a/mclib/protocol/src/nbt.rs b/mclib/protocol/src/nbt.rs index 47e639b..62249e8 100644 --- a/mclib/protocol/src/nbt.rs +++ b/mclib/protocol/src/nbt.rs @@ -1,8 +1,19 @@ -use crate::nbt::tags::base::NBTTag; +use crate::nbt::tags::base::{unpack_by_ty_id, NBTTag}; +use crate::utils::TcpUtils; +use std::io::Read; pub mod tags; -pub struct NBT(Option, Box); +#[derive(Debug)] +pub struct NBT(pub Option, pub Box); + +impl Clone for NBT { + // TODO + fn clone(&self) -> Self { + let bin = self.pack(); + Self::unpack(&mut std::io::Cursor::new(bin), self.0.is_some()) + } +} impl NBT { pub fn pack(&self) -> Vec { @@ -16,6 +27,22 @@ impl NBT { result.extend(self.1.pack()); result } + + pub fn unpack(src: &mut dyn Read, read_name: bool) -> Self { + let ty_id = src.read_byte(); + let name = if read_name { + let name_length = i16::from_be_bytes([src.read_byte(), src.read_byte()]); + let mut name = Vec::new(); + for _ in 0..name_length { + name.push(src.read_byte()); + } + Some(String::from_utf8(name).unwrap()) + } else { + None + }; + let inner = unpack_by_ty_id(ty_id, src); + Self(name, inner) + } } #[cfg(test)] @@ -129,8 +156,12 @@ mod tests { let mut decoder = flate2::read::GzDecoder::new(std::io::Cursor::new(result)); let mut result = Vec::new(); decoder.read_to_end(&mut result).unwrap(); - let p = nbt.pack(); - assert_eq!(p[..p.len() - 1], result[..p.len() - 1]); + assert_eq!(nbt.pack(), result); + + assert_eq!( + NBT::unpack(&mut std::io::Cursor::new(result.clone()), true).pack(), + result + ) } #[test] diff --git a/mclib/protocol/src/nbt/tags/base.rs b/mclib/protocol/src/nbt/tags/base.rs index 4bda1cb..6763d90 100644 --- a/mclib/protocol/src/nbt/tags/base.rs +++ b/mclib/protocol/src/nbt/tags/base.rs @@ -1,10 +1,48 @@ +use crate::nbt::tags::byte::TagByte; +use crate::nbt::tags::byte_array::TagByteArray; +use crate::nbt::tags::compound::TagCompound; +use crate::nbt::tags::double::TagDouble; +use crate::nbt::tags::float::TagFloat; +use crate::nbt::tags::int::TagInt; +use crate::nbt::tags::list::TagList; +use crate::nbt::tags::long::TagLong; +use crate::nbt::tags::short::TagShort; +use crate::nbt::tags::string::TagString; use std::fmt::Debug; +use std::io::Read; pub trait NBTTag: Debug { fn ty_id(&self) -> u8; fn pack(&self) -> Vec; + fn unpack(src: &mut dyn Read) -> Self + where + Self: Sized; } pub trait IntoNBTTag { fn to_nbt(self) -> Box; } + +pub fn unpack_by_ty_id(ty_id: u8, src: &mut dyn Read) -> Box { + match ty_id { + 1 => Box::new(TagByte::unpack(src)), + 2 => Box::new(TagShort::unpack(src)), + 3 => Box::new(TagInt::unpack(src)), + 4 => Box::new(TagLong::unpack(src)), + 5 => Box::new(TagFloat::unpack(src)), + 6 => Box::new(TagDouble::unpack(src)), + 7 => Box::new(TagByteArray::unpack(src)), + 8 => Box::new(TagString::unpack(src)), + 9 => Box::new(TagList::unpack(src)), + 10 => Box::new(TagCompound::unpack(src)), + 11 => { + todo!() + } + 12 => { + todo!() + } + _ => { + todo!() + } + } +} diff --git a/mclib/protocol/src/nbt/tags/byte.rs b/mclib/protocol/src/nbt/tags/byte.rs index 3d3ac00..96a36c7 100644 --- a/mclib/protocol/src/nbt/tags/byte.rs +++ b/mclib/protocol/src/nbt/tags/byte.rs @@ -1,4 +1,6 @@ use crate::nbt::tags::base::{IntoNBTTag, NBTTag}; +use crate::utils::TcpUtils; +use std::io::Read; #[derive(Debug)] pub struct TagByte(i8); @@ -17,4 +19,8 @@ impl NBTTag for TagByte { fn pack(&self) -> Vec { vec![self.0 as u8] } + + fn unpack(src: &mut dyn Read) -> Self { + Self(src.read_byte() as i8) + } } diff --git a/mclib/protocol/src/nbt/tags/byte_array.rs b/mclib/protocol/src/nbt/tags/byte_array.rs index 107f236..1820001 100644 --- a/mclib/protocol/src/nbt/tags/byte_array.rs +++ b/mclib/protocol/src/nbt/tags/byte_array.rs @@ -1,4 +1,6 @@ use crate::nbt::tags::base::{IntoNBTTag, NBTTag}; +use crate::utils::TcpUtils; +use std::io::Read; #[derive(Debug)] pub struct TagByteArray(Vec); @@ -16,10 +18,24 @@ impl NBTTag for TagByteArray { fn pack(&self) -> Vec { let mut result = Vec::new(); - result.extend((1000i32).to_be_bytes()); + result.extend((self.0.len() as i32).to_be_bytes()); for b in &self.0 { result.push(*b as u8); } result } + + fn unpack(src: &mut dyn Read) -> Self { + let length = i32::from_be_bytes([ + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + ]); + let mut result = Vec::new(); + for _ in 0..length { + result.push(src.read_byte() as i8); + } + Self(result) + } } diff --git a/mclib/protocol/src/nbt/tags/compound.rs b/mclib/protocol/src/nbt/tags/compound.rs index 25b08bd..0505661 100644 --- a/mclib/protocol/src/nbt/tags/compound.rs +++ b/mclib/protocol/src/nbt/tags/compound.rs @@ -1,4 +1,6 @@ -use crate::nbt::tags::base::{IntoNBTTag, NBTTag}; +use crate::nbt::tags::base::{unpack_by_ty_id, IntoNBTTag, NBTTag}; +use crate::utils::TcpUtils; +use std::io::Read; #[derive(Debug)] pub struct TagCompound(Vec<(String, Box)>); @@ -30,4 +32,24 @@ impl NBTTag for TagCompound { result.push(0x00); result } + + fn unpack(src: &mut dyn Read) -> Self { + let mut result = Vec::<(String, Box)>::new(); + let read_name = |src: &mut dyn Read| { + let name_len = u16::from_be_bytes([src.read_byte(), src.read_byte()]); + let mut name_bytes = Vec::new(); + for _ in 0..name_len { + name_bytes.push(src.read_byte()); + } + String::from_utf8(name_bytes).unwrap() + }; + loop { + let ty_id = src.read_byte(); + if ty_id == 0 { + break; + } + result.push((read_name(src), unpack_by_ty_id(ty_id, src))); + } + Self(result) + } } diff --git a/mclib/protocol/src/nbt/tags/double.rs b/mclib/protocol/src/nbt/tags/double.rs index f22480f..255b151 100644 --- a/mclib/protocol/src/nbt/tags/double.rs +++ b/mclib/protocol/src/nbt/tags/double.rs @@ -1,4 +1,6 @@ use crate::nbt::tags::base::{IntoNBTTag, NBTTag}; +use crate::utils::TcpUtils; +use std::io::Read; #[derive(Debug)] pub struct TagDouble(f64); @@ -17,4 +19,17 @@ impl NBTTag for TagDouble { fn pack(&self) -> Vec { self.0.to_be_bytes().to_vec() } + + fn unpack(src: &mut dyn Read) -> Self { + Self(f64::from_be_bytes([ + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + ])) + } } diff --git a/mclib/protocol/src/nbt/tags/float.rs b/mclib/protocol/src/nbt/tags/float.rs index 2729eb0..d576aa8 100644 --- a/mclib/protocol/src/nbt/tags/float.rs +++ b/mclib/protocol/src/nbt/tags/float.rs @@ -1,4 +1,6 @@ use crate::nbt::tags::base::{IntoNBTTag, NBTTag}; +use crate::utils::TcpUtils; +use std::io::Read; #[derive(Debug)] pub struct TagFloat(f32); @@ -17,4 +19,13 @@ impl NBTTag for TagFloat { fn pack(&self) -> Vec { self.0.to_be_bytes().to_vec() } + + fn unpack(src: &mut dyn Read) -> Self { + Self(f32::from_be_bytes([ + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + ])) + } } diff --git a/mclib/protocol/src/nbt/tags/int.rs b/mclib/protocol/src/nbt/tags/int.rs index 27c0b21..7492cb6 100644 --- a/mclib/protocol/src/nbt/tags/int.rs +++ b/mclib/protocol/src/nbt/tags/int.rs @@ -1,4 +1,6 @@ use crate::nbt::tags::base::{IntoNBTTag, NBTTag}; +use crate::utils::TcpUtils; +use std::io::Read; #[derive(Debug)] pub struct TagInt(i32); @@ -17,4 +19,13 @@ impl NBTTag for TagInt { fn pack(&self) -> Vec { self.0.to_be_bytes().to_vec() } + + fn unpack(src: &mut dyn Read) -> Self { + Self(i32::from_be_bytes([ + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + ])) + } } diff --git a/mclib/protocol/src/nbt/tags/list.rs b/mclib/protocol/src/nbt/tags/list.rs index 0381531..941fe58 100644 --- a/mclib/protocol/src/nbt/tags/list.rs +++ b/mclib/protocol/src/nbt/tags/list.rs @@ -1,4 +1,6 @@ -use crate::nbt::tags::base::{IntoNBTTag, NBTTag}; +use crate::nbt::tags::base::{unpack_by_ty_id, IntoNBTTag, NBTTag}; +use crate::utils::TcpUtils; +use std::io::Read; #[derive(Debug)] pub struct TagList(Vec>); @@ -29,4 +31,19 @@ impl NBTTag for TagList { result.extend(elements); result } + + fn unpack(src: &mut dyn Read) -> Self { + let mut result = Vec::>::new(); + let ty_id = src.read_byte(); + let length = i32::from_be_bytes([ + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + ]); + for _ in 0..length { + result.push(unpack_by_ty_id(ty_id, src)); + } + Self(result) + } } diff --git a/mclib/protocol/src/nbt/tags/long.rs b/mclib/protocol/src/nbt/tags/long.rs index 9d9b8ff..fd1ba3a 100644 --- a/mclib/protocol/src/nbt/tags/long.rs +++ b/mclib/protocol/src/nbt/tags/long.rs @@ -1,4 +1,6 @@ use crate::nbt::tags::base::{IntoNBTTag, NBTTag}; +use crate::utils::TcpUtils; +use std::io::Read; #[derive(Debug)] pub struct TagLong(i64); @@ -17,4 +19,17 @@ impl NBTTag for TagLong { fn pack(&self) -> Vec { self.0.to_be_bytes().to_vec() } + + fn unpack(src: &mut dyn Read) -> Self { + Self(i64::from_be_bytes([ + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + ])) + } } diff --git a/mclib/protocol/src/nbt/tags/short.rs b/mclib/protocol/src/nbt/tags/short.rs index e7b0f18..c692702 100644 --- a/mclib/protocol/src/nbt/tags/short.rs +++ b/mclib/protocol/src/nbt/tags/short.rs @@ -1,4 +1,6 @@ use crate::nbt::tags::base::{IntoNBTTag, NBTTag}; +use crate::utils::TcpUtils; +use std::io::Read; #[derive(Debug)] pub struct TagShort(i16); @@ -17,4 +19,8 @@ impl NBTTag for TagShort { fn pack(&self) -> Vec { self.0.to_be_bytes().to_vec() } + + fn unpack(src: &mut dyn Read) -> Self { + Self(i16::from_be_bytes([src.read_byte(), src.read_byte()])) + } } diff --git a/mclib/protocol/src/nbt/tags/string.rs b/mclib/protocol/src/nbt/tags/string.rs index 50434b1..b4b6035 100644 --- a/mclib/protocol/src/nbt/tags/string.rs +++ b/mclib/protocol/src/nbt/tags/string.rs @@ -1,4 +1,6 @@ use crate::nbt::tags::base::{IntoNBTTag, NBTTag}; +use crate::utils::TcpUtils; +use std::io::Read; #[derive(Debug)] pub struct TagString(Vec); @@ -26,4 +28,13 @@ impl NBTTag for TagString { result.extend(&self.0); result } + + fn unpack(src: &mut dyn Read) -> Self { + let length = u16::from_be_bytes([src.read_byte(), src.read_byte()]); + let mut result = Vec::new(); + for _ in 0..length { + result.push(src.read_byte()); + } + Self(result) + } } diff --git a/mclib/protocol/src/packets/client.rs b/mclib/protocol/src/packets/client.rs index 43daf2a..5838101 100644 --- a/mclib/protocol/src/packets/client.rs +++ b/mclib/protocol/src/packets/client.rs @@ -1,2 +1,3 @@ pub mod login_success; +pub mod registry_data; pub mod status_response; diff --git a/mclib/protocol/src/packets/client/registry_data.rs b/mclib/protocol/src/packets/client/registry_data.rs new file mode 100644 index 0000000..a7b4fee --- /dev/null +++ b/mclib/protocol/src/packets/client/registry_data.rs @@ -0,0 +1,11 @@ +use crate::packets::base::MCPacket; +use crate::types::base::MCType; +use crate::types::nbt::MCNBT; +use crate::types::varint::MCVarInt; +use mclib_macros::MCPacket; + +#[derive(MCPacket, Debug, Clone)] +#[packet(packet_id = 0x05)] +pub struct RegistryData { + registry_codec: MCNBT, +} diff --git a/mclib/protocol/src/packets/server.rs b/mclib/protocol/src/packets/server.rs index f736459..a45c24d 100644 --- a/mclib/protocol/src/packets/server.rs +++ b/mclib/protocol/src/packets/server.rs @@ -1,4 +1,5 @@ pub mod handshake; +pub mod login_acknowledged; pub mod login_start; pub mod ping; pub mod status_request; diff --git a/mclib/protocol/src/packets/server/login_acknowledged.rs b/mclib/protocol/src/packets/server/login_acknowledged.rs new file mode 100644 index 0000000..4711d5f --- /dev/null +++ b/mclib/protocol/src/packets/server/login_acknowledged.rs @@ -0,0 +1,8 @@ +use crate::packets::base::MCPacket; +use crate::types::base::MCType; +use crate::types::varint::MCVarInt; +use mclib_macros::MCPacket; + +#[derive(MCPacket, Debug, Clone)] +#[packet(packet_id = 0x03)] +pub struct LoginAcknowledged {} diff --git a/mclib/protocol/src/types.rs b/mclib/protocol/src/types.rs index 17ede94..d78a9b9 100644 --- a/mclib/protocol/src/types.rs +++ b/mclib/protocol/src/types.rs @@ -2,6 +2,7 @@ pub mod base; pub mod boolean; pub mod byte; pub mod long; +pub mod nbt; pub mod option; pub mod string; pub mod ubyte; diff --git a/mclib/protocol/src/types/base.rs b/mclib/protocol/src/types/base.rs index 9dc1ef1..edc7cb8 100644 --- a/mclib/protocol/src/types/base.rs +++ b/mclib/protocol/src/types/base.rs @@ -4,10 +4,4 @@ use std::io::Read; pub trait MCType: Clone + Debug { fn pack(&self) -> Vec; fn unpack(src: &mut dyn Read) -> Self; - - fn read_byte(src: &mut dyn Read) -> u8 { - let mut buf = [0; 1]; - src.read_exact(&mut buf).unwrap(); - buf[0] - } } diff --git a/mclib/protocol/src/types/boolean.rs b/mclib/protocol/src/types/boolean.rs index d073846..a5c7c01 100644 --- a/mclib/protocol/src/types/boolean.rs +++ b/mclib/protocol/src/types/boolean.rs @@ -1,4 +1,5 @@ use crate::types::base::MCType; +use crate::utils::TcpUtils; use std::io::Read; const MC_BOOLEAN_TRUE: u8 = 0x01; @@ -30,7 +31,7 @@ impl MCType for MCBoolean { } fn unpack(src: &mut dyn Read) -> Self { - Self(Self::read_byte(src) == MC_BOOLEAN_TRUE) + Self(src.read_byte() == MC_BOOLEAN_TRUE) } } diff --git a/mclib/protocol/src/types/byte.rs b/mclib/protocol/src/types/byte.rs index a49f3e5..9b9f681 100644 --- a/mclib/protocol/src/types/byte.rs +++ b/mclib/protocol/src/types/byte.rs @@ -1,4 +1,5 @@ use crate::types::base::MCType; +use crate::utils::TcpUtils; use std::io::Read; #[derive(Debug, Clone)] @@ -28,7 +29,7 @@ impl MCType for MCByte { } fn unpack(src: &mut dyn Read) -> Self { - Self(Self::read_byte(src) as i8) + Self(src.read_byte() as i8) } } diff --git a/mclib/protocol/src/types/long.rs b/mclib/protocol/src/types/long.rs index dcbc1f9..d34ae2c 100644 --- a/mclib/protocol/src/types/long.rs +++ b/mclib/protocol/src/types/long.rs @@ -1,4 +1,5 @@ use crate::types::base::MCType; +use crate::utils::TcpUtils; use std::io::Read; #[derive(Debug, Clone)] @@ -29,14 +30,14 @@ impl MCType for MCLong { fn unpack(src: &mut dyn Read) -> Self { Self(i64::from_be_bytes([ - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), ])) } } diff --git a/mclib/protocol/src/types/nbt.rs b/mclib/protocol/src/types/nbt.rs new file mode 100644 index 0000000..3978200 --- /dev/null +++ b/mclib/protocol/src/types/nbt.rs @@ -0,0 +1,16 @@ +use crate::nbt::NBT; +use crate::types::base::MCType; +use std::io::Read; + +#[derive(Debug, Clone)] +pub struct MCNBT(NBT); + +impl MCType for MCNBT { + fn pack(&self) -> Vec { + self.0.pack() + } + + fn unpack(src: &mut dyn Read) -> Self { + Self(NBT::unpack(src, false)) + } +} diff --git a/mclib/protocol/src/types/string.rs b/mclib/protocol/src/types/string.rs index 1c2ca19..f09ecc3 100644 --- a/mclib/protocol/src/types/string.rs +++ b/mclib/protocol/src/types/string.rs @@ -1,5 +1,6 @@ use crate::types::base::MCType; use crate::types::varint::MCVarInt; +use crate::utils::TcpUtils; use std::io::Read; // TODO max length? @@ -43,7 +44,7 @@ impl MCType for MCString { let mut string_buffer = Vec::new(); for _ in 0..string_length.into() { - string_buffer.push(Self::read_byte(src)); + string_buffer.push(src.read_byte()); } let string_result = String::from_utf8(string_buffer).unwrap_or_default(); // TODO add error handling diff --git a/mclib/protocol/src/types/ubyte.rs b/mclib/protocol/src/types/ubyte.rs index 73024fe..02849b5 100644 --- a/mclib/protocol/src/types/ubyte.rs +++ b/mclib/protocol/src/types/ubyte.rs @@ -1,4 +1,5 @@ use crate::types::base::MCType; +use crate::utils::TcpUtils; use std::io::Read; #[derive(Debug, Clone)] @@ -28,7 +29,7 @@ impl MCType for MCUByte { } fn unpack(src: &mut dyn Read) -> Self { - Self(Self::read_byte(src)) + Self(src.read_byte()) } } diff --git a/mclib/protocol/src/types/ushort.rs b/mclib/protocol/src/types/ushort.rs index 4e6be84..5bb4d49 100644 --- a/mclib/protocol/src/types/ushort.rs +++ b/mclib/protocol/src/types/ushort.rs @@ -1,4 +1,5 @@ use crate::types::base::MCType; +use crate::utils::TcpUtils; use std::io::Read; #[derive(Debug, Clone)] @@ -28,10 +29,7 @@ impl MCType for MCUShort { } fn unpack(src: &mut dyn Read) -> Self { - Self(u16::from_be_bytes([ - Self::read_byte(src), - Self::read_byte(src), - ])) + Self(u16::from_be_bytes([src.read_byte(), src.read_byte()])) } } diff --git a/mclib/protocol/src/types/uuid.rs b/mclib/protocol/src/types/uuid.rs index 862bb05..9d83b93 100644 --- a/mclib/protocol/src/types/uuid.rs +++ b/mclib/protocol/src/types/uuid.rs @@ -1,4 +1,5 @@ use crate::types::base::MCType; +use crate::utils::TcpUtils; use std::io::Read; use uuid::Uuid; @@ -30,22 +31,22 @@ impl MCType for MCUuid { fn unpack(src: &mut dyn Read) -> Self { Self(Uuid::from_u128(u128::from_be_bytes([ - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), - Self::read_byte(src), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), + src.read_byte(), ]))) } } diff --git a/mclib/protocol/src/types/varint.rs b/mclib/protocol/src/types/varint.rs index 89b45a5..7f2773b 100644 --- a/mclib/protocol/src/types/varint.rs +++ b/mclib/protocol/src/types/varint.rs @@ -1,4 +1,5 @@ use crate::types::base::MCType; +use crate::utils::TcpUtils; use std::io::Read; const SEGMENT_BITS: u8 = 0b0111_1111; @@ -49,7 +50,7 @@ impl MCType for MCVarInt { let mut value = 0i32; for i in 0..5 { - let current_byte = Self::read_byte(src); + let current_byte = src.read_byte(); value |= ((current_byte & SEGMENT_BITS) as i32) << (i * 7); if (current_byte & CONTINUE_BIT) == 0 { diff --git a/mclib/protocol/src/utils.rs b/mclib/protocol/src/utils.rs new file mode 100644 index 0000000..478a2d6 --- /dev/null +++ b/mclib/protocol/src/utils.rs @@ -0,0 +1,13 @@ +use std::io::Read; + +pub trait TcpUtils { + fn read_byte(&mut self) -> u8; +} + +impl TcpUtils for dyn Read + '_ { + fn read_byte(&mut self) -> u8 { + let mut buf = [0; 1]; + self.read_exact(&mut buf).unwrap(); + buf[0] + } +} diff --git a/src/main.rs b/src/main.rs index 320fd72..726657c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use tracing::Level; use tracing_subscriber::EnvFilter; mod conf; +mod registry; mod server; fn main() { diff --git a/src/registry.rs b/src/registry.rs new file mode 100644 index 0000000..1567441 --- /dev/null +++ b/src/registry.rs @@ -0,0 +1,109 @@ +use crate::registry::entry::RegistryEntry; +use mclib::nbt::NBT; +use mclib::nbt::{IntoNBTTag, NBTTag}; + +pub mod biome; +pub mod chat; +pub mod damage; +pub mod dimension; +pub mod entry; +pub mod trim_material; +pub mod trim_pattern; + +pub struct RegistryData { + trim_patterns: Vec>, + trim_materials: Vec>, + chat_types: Vec>, + dimension_types: Vec>, + damage_types: Vec>, + biomes: Vec>, +} + +impl From for NBT { + fn from(value: RegistryData) -> Self { + let mut trim_patterns = Vec::>::new(); + for trim_pattern in value.trim_patterns { + trim_patterns.push(trim_pattern.to_nbt()) + } + + let mut trim_materials = Vec::>::new(); + for trim_material in value.trim_materials { + trim_materials.push(trim_material.to_nbt()) + } + + let mut chat_types = Vec::>::new(); + for chat_type in value.chat_types { + chat_types.push(chat_type.to_nbt()) + } + + let mut dimension_types = Vec::new(); + for dimension_type in value.dimension_types { + dimension_types.push(dimension_type.to_nbt()); + } + + let mut damage_types = Vec::new(); + for damage_type in value.damage_types { + damage_types.push(damage_type.to_nbt()); + } + + let mut biomes = Vec::>::new(); + for biome in value.biomes { + biomes.push(biome.to_nbt()) + } + + NBT( + Some("REGISTRY".to_string()), + vec![ + ( + "minecraft:trim_pattern", + vec![ + ("type", "minecraft:trim_pattern".to_nbt()), + ("value", trim_patterns.to_nbt()), + ] + .to_nbt(), + ), + ( + "minecraft:trim_material", + vec![ + ("type", "minecraft:trim_material".to_nbt()), + ("value", trim_materials.to_nbt()), + ] + .to_nbt(), + ), + ( + "minecraft:chat_type", + vec![ + ("type", "minecraft:chat_type".to_nbt()), + ("value", chat_types.to_nbt()), + ] + .to_nbt(), + ), + ( + "minecraft:dimension_type", + vec![ + ("type", "minecraft:dimension_type".to_nbt()), + ("value", dimension_types.to_nbt()), + ] + .to_nbt(), + ), + ( + "minecraft:damage_type", + vec![ + ("type", "minecraft:damage_type".to_nbt()), + ("value", damage_types.to_nbt()), + ] + .to_nbt(), + ), + ( + "minecraft:worldgen/biome", + vec![ + ("type", "minecraft:worldgen/biome".to_nbt()), + ("value", biomes.to_nbt()), + ] + .to_nbt(), + ), + ] + .to_nbt(), + ) + } +} diff --git a/src/registry/biome.rs b/src/registry/biome.rs new file mode 100644 index 0000000..5706993 --- /dev/null +++ b/src/registry/biome.rs @@ -0,0 +1,58 @@ +use mclib::nbt::{IntoNBTTag, NBTTag}; + +/// Biome special effects. +pub struct BiomeEffects { + /// The color of the fog effect when looking past the view distance. + pub fog_color: i32, + /// The tint color of the water blocks. + pub water_color: i32, + /// The color of the fog effect when looking past the view distance when underwater. + pub water_fog_color: i32, + /// The color of the sky. + pub sky_color: i32, + // TODO optional values +} + +impl IntoNBTTag for BiomeEffects { + fn to_nbt(self) -> Box { + vec![ + ("fog_color", self.fog_color.to_nbt()), + ("water_color", self.water_color.to_nbt()), + ("water_fog_color", self.water_fog_color.to_nbt()), + ("sky_color", self.sky_color.to_nbt()), + ] + .to_nbt() + } +} + +/// The `minecraft:worldgen/biome` registry. +/// It defines several aesthetic characteristics of the biomes present in the game. +pub struct Biome { + /// Determines whether or not the biome has precipitation + has_precipitation: bool, + /// The temperature factor of the biome. + /// Affects foliage and grass color if they are not explicitly set. + temperature: f32, + /// Modifier that affects the resulting temperature. + temperature_modifier: Option, + /// The downfall factor of the biome. + /// Affects foliage and grass color if they are not explicitly set. + downfall: f32, + /// Biome special effects. + effects: BiomeEffects, +} + +impl IntoNBTTag for Biome { + fn to_nbt(self) -> Box { + let mut result = vec![ + ("has_precipitation", (self.has_precipitation as i8).to_nbt()), + ("temperature", self.temperature.to_nbt()), + ("downfall", self.downfall.to_nbt()), + ("effects", self.effects.to_nbt()), + ]; + if let Some(temperature_modifier) = self.temperature_modifier { + result.push(("temperature_modifier", temperature_modifier.to_nbt())); + } + result.to_nbt() + } +} diff --git a/src/registry/chat.rs b/src/registry/chat.rs new file mode 100644 index 0000000..7714429 --- /dev/null +++ b/src/registry/chat.rs @@ -0,0 +1,46 @@ +use mclib::nbt::{IntoNBTTag, NBTTag}; + +pub struct Decoration { + /// The translation key representing the chat format. It can also be a formatting string directly. + /// + /// Example: `chat.type.text`, which translates to `<%s> %s`. + pub translation_key: String, + // /// Optional styling to be applied on the final message. + // /// Not present in the narration decoration. + // pub style: Option<_> + /// Placeholders used when formatting the string given by the translation_key field. + /// + /// Can be either: + /// * `sender`, for the name of the player sending the message. + /// * `target`, for the name of the player receiving the message, which may be empty. + /// * `content`, for the actual message. + pub parameters: String, +} + +impl IntoNBTTag for Decoration { + fn to_nbt(self) -> Box { + vec![ + ("translation_key", self.translation_key.to_nbt()), + ("parameters", self.parameters.to_nbt()), + ] + .to_nbt() + } +} + +/// The `minecraft:chat_type` registry. It defines the different types of in-game chat and how they're formatted. +pub struct Chat { + /// The chat decoration. + pub chat: Decoration, + /// The narration decoration. + pub narration: Decoration, +} + +impl IntoNBTTag for Chat { + fn to_nbt(self) -> Box { + vec![ + ("chat", self.chat.to_nbt()), + ("narration", self.narration.to_nbt()), + ] + .to_nbt() + } +} diff --git a/src/registry/damage.rs b/src/registry/damage.rs new file mode 100644 index 0000000..3cacf4b --- /dev/null +++ b/src/registry/damage.rs @@ -0,0 +1,46 @@ +use mclib::nbt::{IntoNBTTag, NBTTag}; + +pub struct DamageType { + /// Id of the death message. The full message is displayed as `death.attack.`. + /// + /// Example: "onFire". + pub message_id: String, + /// Whether the damage taken scales with the difficulty. + /// + /// Can be either: + /// * `never` + /// * `when_caused_by_living_non_player` + /// * `always` + pub scaling: String, + /// The amount of exhaustion caused when suffering this type of damage. + /// Default values are either 0.0 or 0.1. + pub exhaustion: f32, + // /// Effect played when the player suffers this damage, including the sound that is played. + // /// + // /// Can be either: + // /// * `hurt` + // /// * `thorns` + // /// * `drowning` + // /// * `burning` + // /// * `poking` + // /// * `freezing` + // pub effects: Option, + // /// Defines how the death message is constructed. + // /// + // /// Can be either: + // /// * `default`, for the message to be built normally. + // /// * `fall_variants`, for the most significant fall damage to be considered. + // /// * `intentional_game_design`, for MCPE-28723 to be considered as an argument when translating the message. + // pub death_message_type: Option, +} + +impl IntoNBTTag for DamageType { + fn to_nbt(self) -> Box { + vec![ + ("message_id", self.message_id.to_nbt()), + ("scaling", self.scaling.to_nbt()), + ("exhaustion", self.exhaustion.to_nbt()), + ] + .to_nbt() + } +} diff --git a/src/registry/dimension.rs b/src/registry/dimension.rs new file mode 100644 index 0000000..f44a963 --- /dev/null +++ b/src/registry/dimension.rs @@ -0,0 +1,109 @@ +use mclib::nbt::{IntoNBTTag, NBTTag}; + +/// The minecraft:dimension_type registry. It defines the types of dimension that can be attributed to a world, along with all their characteristics. +/// Dimension type entries are referenced in the Login (play) and Respawn packets. +pub struct DimensionType { + /// If set, the time of the day fixed to the specified value. + /// Allowed values vary between 0 and 24000. + pub fixed_time: Option, + /// Whether the dimension has skylight access or not. + pub has_skylight: bool, + /// Whether the dimension has a bedrock ceiling or not. When true, causes lava to spread faster. + pub has_ceiling: bool, + /// Whether the dimensions behaves like the nether (water evaporates and sponges dry) or not. + /// Also causes lava to spread thinner. + pub ultrawarm: bool, + /// When false, compasses spin randomly. When true, nether portals can spawn zombified piglins. + pub natural: bool, + /// The multiplier applied to coordinates when traveling to the dimension. + pub coordinate_scale: f64, + /// Whether players can use a bed to sleep. + pub bed_works: bool, + /// Whether players can charge and use respawn anchors. + pub respawn_anchor_works: bool, + /// The minimum Y level. + /// Allowed values vary between -2032 and 2031, and must also be a multiple of 16. + /// + /// min_y + height cannot exceed 2032. + pub min_y: i32, + /// The maximum height. + /// Allowed values vary between 16 and 4064, and must also be a multiple of 16. + /// + /// min_y + height cannot exceed 2032. + pub height: i32, + /// The maximum height to which chorus fruits and nether portals can bring players within this dimension. (Must be lower than height) + /// Allowed values vary between 0 and 4064, and must also be a multiple of 16. + /// + /// logical_height cannot exceed the height. + pub logical_height: i32, + /// A resource location defining what block tag to use for infiniburn. + /// + /// "#" or minecraft resource "#minecraft:...". + pub infiniburn: String, + /// Defines special dimensional effects, which includes: + /// * Cloud level: Height at which clouds appear, if at all. + /// * Sky type: Whether it's the normal sky with sun and moon; the low-visibility, foggy sky of the nether; or the static sky of the end. + /// * Forced light map: Whether a bright light map is forced, siimilar to the night vision effect. + /// * Constant ambient light: Whether blocks have shade on their faces. + /// + /// Can be either: + /// * `minecraft:overworld`, for clouds at 192, normal sky type, normal light map and normal ambient light. + /// * `minecraft:the_nether`, for no clouds, nether sky type, normal light map and constant ambient light. + /// * `minecraft:the_end`, for no clouds, end sky type, forced light map and normal ambient light. + pub effects: String, + /// How much light the dimension has. Used as interpolation factor when calculating the brightness generated from sky light. + /// The default values are 0.0 and 0.1, 0.1 for the nether and 0.0 for the other dimensions. + pub ambient_light: f32, + /// Whether piglins shake and transform to zombified piglins. + pub piglin_safe: bool, + /// Whether players with the Bad Omen effect can cause a raid. + pub has_raids: bool, + /// During a monster spawn attempt, this is the maximum allowed light level for it to succeed. + /// It can be either a fixed value, or one of several types of distributions. + pub monster_spawn_light_level: i32, // TODO + /// Maximum allowed block light level monster spawn attempts to happen. + /// + /// Allowed values vary between 0 and 15. + /// The default values are 0 and 15, 15 for the nether (where monsters can spawn anywhere) + /// and 0 for other dimensions (where monsters can only spawn naturally in complete darkness). + pub monster_spawn_block_light_limit: i32, +} + +impl IntoNBTTag for DimensionType { + fn to_nbt(self) -> Box { + let mut result = vec![ + ("has_skylight", (self.has_skylight as i8).to_nbt()), + ("has_ceiling", (self.has_ceiling as i8).to_nbt()), + ("ultrawarm", (self.ultrawarm as i8).to_nbt()), + ("natural", (self.natural as i8).to_nbt()), + ("coordinate_scale", self.coordinate_scale.to_nbt()), + ("bed_works", (self.bed_works as i8).to_nbt()), + ( + "respawn_anchor_works", + (self.respawn_anchor_works as i8).to_nbt(), + ), + ("min_y", self.min_y.to_nbt()), + ("height", self.height.to_nbt()), + ("logical_height", self.logical_height.to_nbt()), + ("infiniburn", self.infiniburn.to_nbt()), + ("effects", self.effects.to_nbt()), + ("ambient_light", self.ambient_light.to_nbt()), + ("piglin_safe", (self.piglin_safe as i8).to_nbt()), + ("has_raids", (self.has_raids as i8).to_nbt()), + ( + "monster_spawn_light_level", + self.monster_spawn_light_level.to_nbt(), + ), + ( + "monster_spawn_block_light_limit", + self.monster_spawn_block_light_limit.to_nbt(), + ), + ]; + + if let Some(fixed_time) = self.fixed_time { + result.push(("fixed_time", fixed_time.to_nbt())); + } + + result.to_nbt() + } +} diff --git a/src/registry/entry.rs b/src/registry/entry.rs new file mode 100644 index 0000000..1fbfafb --- /dev/null +++ b/src/registry/entry.rs @@ -0,0 +1,19 @@ +use mclib::nbt::{IntoNBTTag, NBTTag}; + +/// The Registry Entry Compound Tag specifies a single entry of a Registry. +pub struct RegistryEntry { + name: String, + id: i32, + element: T, +} + +impl IntoNBTTag for RegistryEntry { + fn to_nbt(self) -> Box { + vec![ + ("name", self.name.to_nbt()), + ("id", self.id.to_nbt()), + ("element", self.element.to_nbt()), + ] + .to_nbt() + } +} diff --git a/src/registry/trim_material.rs b/src/registry/trim_material.rs new file mode 100644 index 0000000..b336a9b --- /dev/null +++ b/src/registry/trim_material.rs @@ -0,0 +1,37 @@ +use mclib::nbt::{IntoNBTTag, NBTTag}; + +/// The minecraft:trim_material registry. It defines various visual properties of trim materials in armors. +pub struct ArmorTrimMaterial { + /// The trim color model to be rendered on top of the armor. + /// The Notchian client uses the corresponding asset located at trims/color_palettes. + /// + /// Example: `minecraft:amethyst`. + pub asset_name: String, + /// The ingredient used. + /// This has the visual effect of showing the trimmed armor model on the Smithing Table when the correct item is placed. + /// + /// Example: `minecraft:copper_ingot`. + pub ingredient: String, + /// Color index of the trim on the armor item when in the inventory. + /// Default values vary between 0.1 and 1.0. + pub item_model_index: f32, + // TODO + // /// Asset for different types of armor materials, which overrides the value specified in the asset_name field. + // /// The Notchian client uses this to give a darker color shade when a trim material is applied to armor of the same material, such as iron applied to iron armor. + // pub override_armor_materials: Option<_> + /// The name of the trim material to be displayed on the armor tool-tip. + /// Any styling used in this component is also applied to the trim pattern description. + pub description: String, +} + +impl IntoNBTTag for ArmorTrimMaterial { + fn to_nbt(self) -> Box { + vec![ + ("asset_name", self.asset_name.to_nbt()), + ("ingredient", self.ingredient.to_nbt()), + ("item_model_index", self.item_model_index.to_nbt()), + ("description", self.description.to_nbt()), + ] + .to_nbt() + } +} diff --git a/src/registry/trim_pattern.rs b/src/registry/trim_pattern.rs new file mode 100644 index 0000000..f98f4cb --- /dev/null +++ b/src/registry/trim_pattern.rs @@ -0,0 +1,31 @@ +use mclib::nbt::{IntoNBTTag, NBTTag}; + +/// The minecraft:trim_pattern registry. It defines various visual properties of trim patterns in armors. +pub struct ArmorTrimPattern { + /// The trim pattern model to be rendered on top of the armor. + /// The Notchian client uses the corresponding asset located at trims/models/armor. + /// + /// Example: `minecraft:coast`. + pub asset_id: String, + /// The template item used for this trim. + /// This has the visual effect of showing the trimmed armor model on the Smithing Table when the correct item is placed. + /// + /// Example: `minecraft:coast_armor_trim_smithing_template`. + pub template_item: String, + /// The name of the trim pattern to be displayed on the armor tool-tip. + pub description: String, + /// Whether this trim is a decal. + pub decal: bool, +} + +impl IntoNBTTag for ArmorTrimPattern { + fn to_nbt(self) -> Box { + vec![ + ("asset_id", self.asset_id.to_nbt()), + ("template_item", self.template_item.to_nbt()), + ("description", self.description.to_nbt()), + ("decal", (self.decal as i8).to_nbt()), + ] + .to_nbt() + } +} diff --git a/src/server/thread/tcp_listener.rs b/src/server/thread/tcp_listener.rs index 7ecebdf..b553fbd 100644 --- a/src/server/thread/tcp_listener.rs +++ b/src/server/thread/tcp_listener.rs @@ -4,7 +4,7 @@ use crate::server::net::tcp::{NativeRead, TCPRead}; use crate::server::thread::tcp_writer::TCPWriterAPI; use mclib::packets::client::{LoginSuccess, StatusResponse}; use mclib::packets::server::{ - Handshake, HandshakeNextState, LoginStart, PingRequest, StatusRequest, + Handshake, HandshakeNextState, LoginAcknowledged, LoginStart, PingRequest, StatusRequest, }; use mclib::types::MCVarInt; use mclib::MCPacket; @@ -70,11 +70,19 @@ impl TCPListenerThread { body: login_success.pack(), }) } + 0x01 => { + let _login_acknowledged = packet.parse_packet::(); + return; + } _ => break, } } } + pub fn handle_configuration(&mut self) { + todo!() + } + pub fn execute(&mut self) { match self.handle_handshake() { HandshakeNextState::Status => { @@ -82,6 +90,7 @@ impl TCPListenerThread { } HandshakeNextState::Login => { self.handle_login(); + self.handle_configuration(); } HandshakeNextState::Unknown => { error!("Unknown next state for handshake");