diff --git a/.gitignore b/.gitignore index ea8c4bf7f..78e0b6bbd 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target +configuration.toml +features.toml diff --git a/configuration.toml b/configuration.toml deleted file mode 100644 index aa0b0edc3..000000000 --- a/configuration.toml +++ /dev/null @@ -1,14 +0,0 @@ -server_address = "127.0.0.1" -server_port = 25565 -seed = "" -max_plyers = 32 -view_distances = 10 -simulation_distance = 10 -resource_pack = "" -resource_pack_sha1 = "" -default_difficulty = "Normal" -allow_nether = true -hardcore = false -online_mode = true -spawn_protection = 16 -motd = "A Blazing fast Pumpkin Server!" diff --git a/features.toml b/features.toml deleted file mode 100644 index 65150ccd9..000000000 --- a/features.toml +++ /dev/null @@ -1,2 +0,0 @@ -liquid_physics = true -encryption = true diff --git a/pumpkin/src/protocol/bytebuf/buffer.rs b/pumpkin/src/protocol/bytebuf/buffer.rs index bfd3708c7..1b8d1d27b 100644 --- a/pumpkin/src/protocol/bytebuf/buffer.rs +++ b/pumpkin/src/protocol/bytebuf/buffer.rs @@ -1,4 +1,4 @@ -use crate::protocol::{nbt::NBT, VarInt, VarLong}; +use crate::protocol::{nbt::nbt::NBT, VarInt, VarLong}; use super::{Endian, CONTINUE_BIT, SEGMENT_BITS}; use byteorder::{BigEndian, ByteOrder, LittleEndian}; @@ -618,6 +618,29 @@ impl ByteBuffer { read_number!(self, read_f64, 8) } + pub fn read_list(&mut self, val: impl Fn(&mut ByteBuffer) -> Result) -> Result> { + let len = self.read_var_int()?.try_into().unwrap(); + let mut list = Vec::with_capacity(len); + for _ in 0..len { + list.push(val(self)?); + } + Ok(list) + } + /// Writes a list to the buffer. + pub fn write_list(&mut self, list: &[T], write: impl Fn(&mut ByteBuffer, &T)) { + self.write_var_int(list.len().try_into().unwrap()); + for v in list { + write(self, v); + } + } + + pub fn read_varint_arr(&mut self) -> Result> { + self.read_list(|buf| buf.read_var_int()) + } + pub fn write_varint_arr(&mut self, v: &[i32]) { + self.write_list(v, |p, &v| p.write_var_int(v)) + } + pub fn read_nbt(&mut self) -> Result { match NBT::deserialize_buf(self) { Ok(v) => Ok(v), diff --git a/pumpkin/src/protocol/client/config/mod.rs b/pumpkin/src/protocol/client/config/mod.rs index 24689f9e3..f273a221b 100644 --- a/pumpkin/src/protocol/client/config/mod.rs +++ b/pumpkin/src/protocol/client/config/mod.rs @@ -1,26 +1,4 @@ -use crate::protocol::{nbt::NBT, ClientPacket, VarInt}; - -pub struct CRegistryData { - registry_id: String, - entry_count: VarInt, - entries: NBT, -} - -struct Entry { - entry_id: String, - has_data: bool, - data: NBT, -} - -impl ClientPacket for CRegistryData { - const PACKET_ID: VarInt = 0x07; - - fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) { - bytebuf.write_string(&self.registry_id); - bytebuf.write_var_int(self.entry_count); - // bytebuf.write_array(self.entries); - } -} +use crate::protocol::{registry, ClientPacket, VarInt}; pub struct CCookieRequest { // TODO @@ -69,3 +47,69 @@ impl ClientPacket for CFinishConfig { fn write(&self, _bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {} } + +pub struct CKnownPacks { + count: VarInt, + known_packs: Vec, +} + +impl CKnownPacks { + pub fn new(count: VarInt, known_packs: Vec) -> Self { + Self { count, known_packs } + } +} + +pub struct KnownPack { + pub namespace: String, + pub id: String, + pub version: String, +} + +impl ClientPacket for CKnownPacks { + const PACKET_ID: VarInt = 0x0E; + + fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) { + bytebuf.write_var_int(self.count); + bytebuf.write_list::(&self.known_packs, |p, v| { + p.write_string(&v.namespace); + p.write_string(&v.id); + p.write_string(&v.version); + }); + } +} + +pub struct CRegistryData { + registry_id: String, + entry_count: VarInt, + entries: Vec, +} + +impl CRegistryData { + pub fn new(registry_id: String, entry_count: VarInt, entries: Vec) -> Self { + Self { + registry_id, + entry_count, + entries, + } + } +} + +pub struct Entry { + pub entry_id: String, + pub has_data: bool, + // data provided by registry::write_codec +} + +impl ClientPacket for CRegistryData { + const PACKET_ID: VarInt = 0x07; + + fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) { + bytebuf.write_string(&self.registry_id); + bytebuf.write_var_int(self.entry_count); + bytebuf.write_list::(&self.entries, |p, v| { + p.write_string(&v.entry_id); + p.write_bool(v.has_data); + registry::write_codec(p, -64, 320); + }); + } +} diff --git a/pumpkin/src/protocol/client/play/mod.rs b/pumpkin/src/protocol/client/play/mod.rs index b96e34ae8..47835ce94 100644 --- a/pumpkin/src/protocol/client/play/mod.rs +++ b/pumpkin/src/protocol/client/play/mod.rs @@ -1,4 +1,3 @@ - use crate::{ entity::player::GameMode, protocol::{ClientPacket, VarInt}, diff --git a/pumpkin/src/protocol/nbt/deserialize.rs b/pumpkin/src/protocol/nbt/deserialize.rs index 601a82986..32538537f 100644 --- a/pumpkin/src/protocol/nbt/deserialize.rs +++ b/pumpkin/src/protocol/nbt/deserialize.rs @@ -1,29 +1,52 @@ -use std::{collections::HashMap, error::Error, fmt, string::FromUtf8Error}; +use flate2::read::{GzDecoder, ZlibDecoder}; +use std::{collections::HashMap, error::Error, fmt, io, io::Read, string::FromUtf8Error}; use crate::protocol::bytebuf::buffer::ByteBuffer; -use super::{Tag, NBT}; - -#[derive(Debug)] -pub enum ParseError { - InvalidType(u8), - InvalidString(FromUtf8Error), -} +use super::{nbt::ParseError, Tag, NBT}; impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::InvalidType(ty) => write!(f, "invalid tag type: {}", ty), - Self::InvalidString(e) => write!(f, "invalid string: {}", e), + Self::InvalidType(ty) => write!(f, "invalid tag type: {ty}"), + Self::InvalidString(e) => write!(f, "invalid string: {e}"), + Self::IO(e) => write!(f, "io error: {e}"), } } } +impl From for ParseError { + fn from(e: FromUtf8Error) -> ParseError { + ParseError::InvalidString(e) + } +} +impl From for ParseError { + fn from(e: io::Error) -> ParseError { + ParseError::IO(e) + } +} impl Error for ParseError {} impl NBT { + pub fn deserialize_file(buf: Vec) -> Result { + if buf.len() >= 2 && buf[0] == 0x1f && buf[1] == 0x8b { + // This means its gzipped + let mut d: GzDecoder<&[u8]> = GzDecoder::new(buf.as_ref()); + let mut buf = vec![]; + d.read_to_end(&mut buf)?; + Self::deserialize(buf) + } else { + // It could be zlib compressed or not compressed + let mut d: ZlibDecoder<&[u8]> = ZlibDecoder::new(buf.as_ref()); + let mut decompressed = vec![]; + match d.read_to_end(&mut decompressed) { + Ok(_) => Self::deserialize(decompressed), + Err(_) => Self::deserialize(buf), + } + } + } /// Deserializes the given byte array as nbt data. - pub fn deserialize(buf: Vec) -> Result { + pub fn deserialize(mut buf: Vec) -> Result { Self::deserialize_buf(&mut ByteBuffer::from_vec(buf)) } /// Deserializes the given buffer as nbt data. This will continue reading @@ -32,10 +55,14 @@ impl NBT { /// buffer will be in an undefined state (it will still be safe, but there are /// no guarantees as too how far ahead the buffer will have been advanced). pub fn deserialize_buf(buf: &mut ByteBuffer) -> Result { - let ty = buf.read_u8().unwrap(); - let len = buf.read_u16().unwrap(); - let name = String::from_utf8(buf.read_bytes(len as usize).unwrap()).unwrap(); - Ok(NBT::new(&name, Tag::deserialize(ty, buf)?)) + let ty = buf.read_u8()?; + if ty == 0 { + Ok(NBT::empty()) + } else { + let len = buf.read_u16()?; + let name = String::from_utf8(buf.read_bytes(len as usize)?)?; + Ok(NBT::new(&name, Tag::deserialize(ty, buf)?)) + } } } @@ -43,26 +70,26 @@ impl Tag { fn deserialize(ty: u8, buf: &mut ByteBuffer) -> Result { match ty { 0 => Ok(Self::End), - 1 => Ok(Self::Byte(buf.read_i8().unwrap())), - 2 => Ok(Self::Short(buf.read_i16().unwrap())), - 3 => Ok(Self::Int(buf.read_i32().unwrap())), - 4 => Ok(Self::Long(buf.read_i64().unwrap())), - 5 => Ok(Self::Float(buf.read_f32().unwrap())), - 6 => Ok(Self::Double(buf.read_f64().unwrap())), + 1 => Ok(Self::Byte(buf.read_i8()?)), + 2 => Ok(Self::Short(buf.read_i16()?)), + 3 => Ok(Self::Int(buf.read_i32()?)), + 4 => Ok(Self::Long(buf.read_i64()?)), + 5 => Ok(Self::Float(buf.read_f32()?)), + 6 => Ok(Self::Double(buf.read_f64()?)), 7 => { - let len = buf.read_i32().unwrap(); - Ok(Self::ByteArr(buf.read_bytes(len as usize).unwrap())) + let len = buf.read_i32()?; + Ok(Self::ByteArr(buf.read_bytes(len as usize)?)) } 8 => { - let len = buf.read_u16().unwrap(); - match String::from_utf8(buf.read_bytes(len as usize).unwrap()) { + let len = buf.read_u16()?; + match String::from_utf8(buf.read_bytes(len as usize)?) { Ok(v) => Ok(Self::String(v)), Err(e) => Err(ParseError::InvalidString(e)), } } 9 => { - let inner_ty = buf.read_u8().unwrap(); - let len = buf.read_i32().unwrap(); + let inner_ty = buf.read_u8()?; + let len = buf.read_i32()?; let mut inner = Vec::with_capacity(len as usize); for _ in 0..len { inner.push(Tag::deserialize(inner_ty, buf)?); @@ -72,30 +99,30 @@ impl Tag { 10 => { let mut inner = HashMap::new(); loop { - let ty = buf.read_u8().unwrap(); + let ty = buf.read_u8()?; if ty == Self::End.ty() { break; } - let len = buf.read_u16().unwrap(); - let name = String::from_utf8(buf.read_bytes(len as usize).unwrap()).unwrap(); + let len = buf.read_u16()?; + let name = String::from_utf8(buf.read_bytes(len as usize)?).unwrap(); let tag = Tag::deserialize(ty, buf)?; inner.insert(name, tag); } - Ok(Self::Compound(inner)) + Ok(inner.into()) } 11 => { - let len = buf.read_i32().unwrap(); + let len = buf.read_i32()?; let mut inner = Vec::with_capacity(len as usize); for _ in 0..len { - inner.push(buf.read_i32().unwrap()); + inner.push(buf.read_i32()?); } Ok(Self::IntArray(inner)) } 12 => { - let len = buf.read_i32().unwrap(); + let len = buf.read_i32()?; let mut inner = Vec::with_capacity(len as usize); for _ in 0..len { - inner.push(buf.read_i64().unwrap()); + inner.push(buf.read_i64()?); } Ok(Self::LongArray(inner)) } diff --git a/pumpkin/src/protocol/nbt/error.rs b/pumpkin/src/protocol/nbt/error.rs new file mode 100644 index 000000000..4963758a7 --- /dev/null +++ b/pumpkin/src/protocol/nbt/error.rs @@ -0,0 +1,56 @@ +use super::Tag; +use serde::{de, ser}; +use std::{fmt, fmt::Display, num::TryFromIntError}; + +#[derive(Debug, Clone, PartialEq)] +pub enum Error { + Message(String), + Eof, + + TryFromInt(TryFromIntError), + ListType(Tag, Tag), + MapKey(Tag), + CannotSerializeNone, + Enum, +} + +pub type Result = std::result::Result; + +impl ser::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl de::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Message(msg) => write!(f, "{msg}"), + Error::Eof => write!(f, "unexpected end of input"), + Error::TryFromInt(e) => write!(f, "invalid integer: {e}"), + Error::ListType(expected, got) => { + write!(f, "expected type in list: {expected:?}, got: {got:?}") + } + Error::MapKey(got) => { + write!(f, "expected a string for map key, got: {got:?}") + } + Error::CannotSerializeNone => { + write!( + f, + "cannot serialize `None` or `()` (use `#[serde(skip_serializing_if = \"Option::is_none\")]`)" + ) + } + Error::Enum => { + write!(f, "enums are not supported") + } + } + } +} + +impl std::error::Error for Error {} diff --git a/pumpkin/src/protocol/nbt/mod.rs b/pumpkin/src/protocol/nbt/mod.rs index 4a07e910b..0f47c3129 100644 --- a/pumpkin/src/protocol/nbt/mod.rs +++ b/pumpkin/src/protocol/nbt/mod.rs @@ -1,115 +1,445 @@ use std::collections::HashMap; +use error::Error; +use error::Result; +use nbt::Tag; +use nbt::NBT; +use serde::{ser, Serialize}; + mod deserialize; +mod error; +pub mod nbt; mod serialize; -#[derive(Debug, Clone, PartialEq)] -pub struct NBT { +pub fn to_nbt(name: &str, value: &T) -> anyhow::Result +where + T: Serialize, +{ + Ok(NBT::new(name, to_tag(value)?)) +} + +pub fn to_tag(value: &T) -> anyhow::Result +where + T: Serialize, +{ + let mut serializer = Serializer { tag: Tag::End }; + value.serialize(&mut serializer)?; + Ok(serializer.tag) +} + +pub struct Serializer { tag: Tag, - name: String, } -/// This is a single tag. It does not contain a name, but has the actual data -/// for any of the nbt tags. -#[derive(Debug, Clone, PartialEq)] -pub enum Tag { - End, - Byte(i8), - Short(i16), - Int(i32), - Long(i64), - Float(f32), - Double(f64), - ByteArr(Vec), - String(String), - List(Vec), // All elements must be the same type, and un-named. - Compound(HashMap), // Types can be any kind, and are named. Order is not defined. - IntArray(Vec), - LongArray(Vec), +pub struct SeqSerializer<'a> { + ser: &'a mut Serializer, + items: Vec, +} +pub struct MapSerializer<'a> { + ser: &'a mut Serializer, + key: Option, + items: HashMap, } -impl NBT { - /// Creates a new nbt tag. The tag value can be anything. - /// - /// # Panics - /// This will panic if the tag is a list, and the values within that list - /// contain multiple types. This is a limitation with the nbt data format: - /// lists can only contain one type of data. - pub fn new(name: &str, tag: Tag) -> Self { - if let Tag::List(inner) = &tag { - if let Some(v) = inner.get(0) { - let ty = v.ty(); - for v in inner { - if v.ty() != ty { - panic!("the given list contains multiple types: {:?}", inner); - } - } - } - } - NBT { - tag, - name: name.into(), - } +impl<'a> ser::Serializer for &'a mut Serializer { + // The output type produced by this `Serializer` during successful + // serialization. Most serializers that produce text or binary output should + // set `Ok = ()` and serialize into an `io::Write` or buffer contained + // within the `Serializer` instance, as happens here. Serializers that build + // in-memory data structures may be simplified by using `Ok` to propagate + // the data structure around. + type Ok = (); + type Error = Error; + + // Associated types for keeping track of additional state while serializing + // compound data structures like sequences and maps. In this case no + // additional state is required beyond what is already stored in the + // Serializer struct. + type SerializeSeq = SeqSerializer<'a>; + type SerializeTuple = SeqSerializer<'a>; + type SerializeTupleStruct = SeqSerializer<'a>; + type SerializeTupleVariant = SeqSerializer<'a>; + type SerializeMap = MapSerializer<'a>; + type SerializeStruct = MapSerializer<'a>; + type SerializeStructVariant = MapSerializer<'a>; + + // Here we go with the simple methods. The following 12 methods receive one + // of the primitive types of the data model and map it to JSON by appending + // into the output string. + fn serialize_bool(self, v: bool) -> Result<()> { + self.tag = Tag::Byte(v as i8); + Ok(()) } - /// Creates an empty nbt tag. - pub fn empty(name: &str) -> Self { - NBT { - tag: Tag::End, - name: name.into(), - } + // JSON does not distinguish between different sizes of integers, so all + // signed integers will be serialized the same and all unsigned integers + // will be serialized the same. Other formats, especially compact binary + // formats, may need independent logic for the different sizes. + fn serialize_i8(self, v: i8) -> Result<()> { + self.tag = Tag::Byte(v); + Ok(()) + } + fn serialize_i16(self, v: i16) -> Result<()> { + self.tag = Tag::Short(v); + Ok(()) + } + fn serialize_i32(self, v: i32) -> Result<()> { + self.tag = Tag::Int(v); + Ok(()) + } + fn serialize_i64(self, v: i64) -> Result<()> { + self.tag = Tag::Long(v); + Ok(()) + } + + fn serialize_u8(self, v: u8) -> Result<()> { + self.serialize_i8(v as i8) + } + fn serialize_u16(self, v: u16) -> Result<()> { + self.serialize_i16(v as i16) + } + fn serialize_u32(self, v: u32) -> Result<()> { + self.serialize_i32(v as i32) + } + fn serialize_u64(self, v: u64) -> Result<()> { + self.serialize_i64(v as i64) + } + + fn serialize_f32(self, v: f32) -> Result<()> { + self.tag = Tag::Float(v); + Ok(()) + } + fn serialize_f64(self, v: f64) -> Result<()> { + self.tag = Tag::Double(v); + Ok(()) + } + + fn serialize_char(self, v: char) -> Result<()> { + self.serialize_str(&v.to_string()) + } + fn serialize_str(self, v: &str) -> Result<()> { + self.tag = Tag::String(v.into()); + Ok(()) + } + + fn serialize_bytes(self, v: &[u8]) -> Result<()> { + self.tag = Tag::ByteArr(v.into()); + Ok(()) + } + + // There isn't really a `None` in NBT. There is `Tag::End`, which could be + // searched and removed if this was in a struct or map, which would essentially + // skip this value if it's `None`. However, I don't care enough, so I'm just + // going to produce an error. + fn serialize_none(self) -> Result<()> { + Err(Error::CannotSerializeNone) + } + fn serialize_some(self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + value.serialize(self) + } + + fn serialize_unit(self) -> Result<()> { + Err(Error::CannotSerializeNone) + } + + // Unit struct means a named value containing no data. Again, since there is + // no data, map this to JSON as `null`. There is no need to serialize the + // name in most formats. + fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { + self.serialize_unit() + } + + // As is done here, serializers are encouraged to treat newtype structs as + // insignificant wrappers around the data they contain. + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + value.serialize(self) + } + + // Now we get to the serialization of compound types. + // + // The start of the sequence, each value, and the end are three separate + // method calls. This one is responsible only for serializing the start, + // which in JSON is `[`. + // + // The length of the sequence may or may not be known ahead of time. This + // doesn't make a difference in JSON because the length is not represented + // explicitly in the serialized form. Some serializers may only be able to + // support sequences for which the length is known up front. + fn serialize_seq(self, _len: Option) -> Result { + Ok(SeqSerializer { + ser: self, + items: vec![], + }) + } + + // Tuples look just like sequences in JSON. Some formats may be able to + // represent tuples more efficiently by omitting the length, since tuple + // means that the corresponding `Deserialize implementation will know the + // length without needing to look at the serialized data. + fn serialize_tuple(self, len: usize) -> Result { + self.serialize_seq(Some(len)) + } + + // Tuple structs look just like sequences in JSON. + fn serialize_tuple_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + self.serialize_seq(Some(len)) + } + + // Maps are represented in JSON as `{ K: V, K: V, ... }`. + fn serialize_map(self, _len: Option) -> Result { + Ok(MapSerializer { + ser: self, + key: None, + items: HashMap::new(), + }) } - /// Appends the given element to the list. This will panic if self is not a - /// list, or if tag does not match the type of the existing elements. - pub fn list_add(&mut self, tag: Tag) { - if let Tag::List(inner) = &mut self.tag { - if let Some(v) = inner.get(0) { - if tag.ty() != v.ty() { - panic!( - "cannot add different types to list. current: {:?}, new: {:?}", - inner, tag - ); - } else { - inner.push(tag); - } - } else { - // No elements yet, so we add this no matter what type it is. - inner.push(tag); + // Structs look just like maps in JSON. In particular, JSON requires that we + // serialize the field names of the struct. Other formats may be able to + // omit the field names when serializing structs because the corresponding + // Deserialize implementation is required to know what the keys are without + // looking at the serialized data. + fn serialize_struct(self, _name: &'static str, len: usize) -> Result { + self.serialize_map(Some(len)) + } + + // We don't support enums (they don't really make sense) + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + ) -> Result<()> { + Err(Error::Enum) + } + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result<()> + where + T: ?Sized + Serialize, + { + Err(Error::Enum) + } + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(Error::Enum) + } + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(Error::Enum) + } +} + +// The following 7 impls deal with the serialization of compound types like +// sequences and maps. Serialization of such types is begun by a Serializer +// method and followed by zero or more calls to serialize individual elements of +// the compound type and one call to end the compound type. +// +// This impl is SerializeSeq so these methods are called after `serialize_seq` +// is called on the Serializer. +impl<'a> ser::SerializeSeq for SeqSerializer<'a> { + type Ok = (); + type Error = Error; + + // Serialize a single element of the sequence. + fn serialize_element(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + value.serialize(&mut *self.ser)?; + let tag = std::mem::replace(&mut self.ser.tag, Tag::End); + if let Some(first) = self.items.first() { + let expected_ty = first.ty(); + let actual_ty = tag.ty(); + if expected_ty != actual_ty { + return Err(Error::ListType(first.clone(), tag)); } - } else { - panic!("called list_add on non-list type: {:?}", self); } + self.items.push(tag); + Ok(()) } - /// Appends the given element to the compound. This will panic if self is not - /// a compound tag. - pub fn compound_add(&mut self, name: String, value: Tag) { - if let Tag::Compound(inner) = &mut self.tag { - inner.insert(name, value); - } else { - panic!("called compound_add on non-compound type: {:?}", self); - } + fn end(self) -> Result<()> { + self.ser.tag = Tag::List(self.items); + Ok(()) } +} - /// If this is a compound tag, this returns the inner data of the tag. - /// Otherwise, this panics. - pub fn compound(&self) -> &HashMap { - if let Tag::Compound(inner) = &self.tag { - &inner - } else { - panic!("called compound on non-compound type: {:?}", self); - } +// Same thing but for tuples. +impl<'a> ser::SerializeTuple for SeqSerializer<'a> { + type Ok = (); + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + ::serialize_element(self, value) + } + + fn end(self) -> Result<()> { + ::end(self) } } -impl Tag { - /// A simpler way to construct compound tags inline. - pub fn compound(value: &[(&str, Tag)]) -> Self { - let mut inner = HashMap::new(); - for (name, tag) in value { - inner.insert(name.to_string(), tag.clone()); - } - Self::Compound(inner) +// Same thing but for tuple structs. +impl<'a> ser::SerializeTupleStruct for SeqSerializer<'a> { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + ::serialize_element(self, value) + } + + fn end(self) -> Result<()> { + ::end(self) + } +} + +// Tuple variants are a little different. Refer back to the +// `serialize_tuple_variant` method above: +// +// self.output += "{"; +// variant.serialize(&mut *self)?; +// self.output += ":["; +// +// So the `end` method in this impl is responsible for closing both the `]` and +// the `}`. +impl<'a> ser::SerializeTupleVariant for SeqSerializer<'a> { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + ::serialize_element(self, value) + } + + fn end(self) -> Result<()> { + ::end(self) + } +} + +// Some `Serialize` types are not able to hold a key and value in memory at the +// same time so `SerializeMap` implementations are required to support +// `serialize_key` and `serialize_value` individually. +// +// There is a third optional method on the `SerializeMap` trait. The +// `serialize_entry` method allows serializers to optimize for the case where +// key and value are both available simultaneously. In JSON it doesn't make a +// difference so the default behavior for `serialize_entry` is fine. +impl<'a> ser::SerializeMap for MapSerializer<'a> { + type Ok = (); + type Error = Error; + + // The Serde data model allows map keys to be any serializable type. JSON + // only allows string keys so the implementation below will produce invalid + // JSON if the key serializes as something other than a string. + // + // A real JSON serializer would need to validate that map keys are strings. + // This can be done by using a different Serializer to serialize the key + // (instead of `&mut **self`) and having that other serializer only + // implement `serialize_str` and return an error on any other data type. + fn serialize_key(&mut self, key: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + key.serialize(&mut *self.ser)?; + let tag = std::mem::replace(&mut self.ser.tag, Tag::End); + self.key = match tag { + Tag::String(key) => Some(key), + other => return Err(Error::MapKey(other)), + }; + Ok(()) + } + + // It doesn't make a difference whether the colon is printed at the end of + // `serialize_key` or at the beginning of `serialize_value`. In this case + // the code is a bit simpler having it here. + fn serialize_value(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + value.serialize(&mut *self.ser)?; + let tag = std::mem::replace(&mut self.ser.tag, Tag::End); + self.items.insert(self.key.take().unwrap(), tag); + Ok(()) + } + + fn end(self) -> Result<()> { + self.ser.tag = self.items.into(); + Ok(()) + } +} + +// Structs are like maps in which the keys are constrained to be compile-time +// constant strings. +impl<'a> ser::SerializeStruct for MapSerializer<'a> { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + value.serialize(&mut *self.ser)?; + let tag = std::mem::replace(&mut self.ser.tag, Tag::End); + self.items.insert(key.into(), tag); + Ok(()) + } + + fn end(self) -> Result<()> { + self.ser.tag = self.items.into(); + Ok(()) + } +} + +// Similar to `SerializeTupleVariant`, here the `end` method is responsible for +// closing both of the curly braces opened by `serialize_struct_variant`. +impl<'a> ser::SerializeStructVariant for MapSerializer<'a> { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + value.serialize(&mut *self.ser)?; + let tag = std::mem::replace(&mut self.ser.tag, Tag::End); + self.items.insert(key.into(), tag); + Ok(()) + } + + fn end(self) -> Result<()> { + self.ser.tag = self.items.into(); + Ok(()) } } diff --git a/pumpkin/src/protocol/nbt/nbt.rs b/pumpkin/src/protocol/nbt/nbt.rs new file mode 100644 index 000000000..7a134c970 --- /dev/null +++ b/pumpkin/src/protocol/nbt/nbt.rs @@ -0,0 +1,283 @@ +use std::{collections::HashMap, fmt, io, ops::Index, string::FromUtf8Error}; + +#[derive(Debug)] +pub enum ParseError { + InvalidType(u8), + InvalidString(FromUtf8Error), + IO(io::Error), +} + +#[derive(Debug, Clone, PartialEq)] +pub struct WrongTag(Tag); + +impl fmt::Display for WrongTag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "wrong tag: {:?}", self.0) + } +} + +impl std::error::Error for WrongTag {} + +/// This is an nbt tag. It has a name, and any amount of data. This can be used +/// to store item data, entity data, level data, and more. +#[derive(Debug, Clone, PartialEq)] +pub struct NBT { + pub tag: Tag, + pub name: String, +} + +impl Default for NBT { + fn default() -> Self { + NBT::new("", Tag::new_compound(&[])) + } +} + +/// This is a single tag. It does not contain a name, but has the actual data +/// for any of the nbt tags. +#[derive(Debug, Clone, PartialEq)] +pub enum Tag { + End, + Byte(i8), + Short(i16), + Int(i32), + Long(i64), + Float(f32), + Double(f64), + ByteArr(Vec), + String(String), + List(Vec), // All elements must be the same type, and un-named. + Compound(Compound), // Types can be any kind, and are named. Order is not defined. + IntArray(Vec), + LongArray(Vec), +} + +/// An NBT Compound tag. This is essentially a map, with some extra helper +/// functions. +#[derive(Debug, Clone, PartialEq)] +pub struct Compound { + pub inner: HashMap, +} + +impl Compound { + pub fn new() -> Self { + Compound { + inner: HashMap::new(), + } + } + pub fn insert(&mut self, key: impl Into, value: impl Into) { + self.inner.insert(key.into(), value.into()); + } + pub fn get_or_create_compound(&mut self, key: impl Into) -> &mut Compound { + self.inner + .entry(key.into()) + .or_insert_with(|| Tag::Compound(Compound::new())) + .compound_mut() + .unwrap() + } + + pub fn contains_key(&self, key: impl AsRef) -> bool { + self.inner.contains_key(key.as_ref()) + } + + pub fn iter(&self) -> std::collections::hash_map::Iter { + self.inner.iter() + } + pub fn iter_mut(&mut self) -> std::collections::hash_map::IterMut { + self.inner.iter_mut() + } +} +impl IntoIterator for Compound { + type Item = (String, Tag); + type IntoIter = std::collections::hash_map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.inner.into_iter() + } +} +impl<'a> IntoIterator for &'a Compound { + type Item = (&'a String, &'a Tag); + type IntoIter = std::collections::hash_map::Iter<'a, String, Tag>; + + fn into_iter(self) -> Self::IntoIter { + self.inner.iter() + } +} +impl<'a> IntoIterator for &'a mut Compound { + type Item = (&'a String, &'a mut Tag); + type IntoIter = std::collections::hash_map::IterMut<'a, String, Tag>; + + fn into_iter(self) -> Self::IntoIter { + self.inner.iter_mut() + } +} +impl From> for Compound { + fn from(v: HashMap) -> Self { + Compound { inner: v } + } +} + +impl Index<&str> for Compound { + type Output = Tag; + fn index(&self, index: &str) -> &Tag { + &self.inner[index] + } +} + +impl From for Tag { + fn from(v: bool) -> Self { + Tag::Byte(v as i8) + } +} +impl From<&str> for Tag { + fn from(s: &str) -> Self { + Tag::String(s.into()) + } +} +impl From for Tag { + fn from(s: String) -> Self { + Tag::String(s) + } +} +impl From> for Tag { + fn from(v: HashMap) -> Self { + Tag::Compound(Compound::from(v)) + } +} + +impl From> for Tag +where + Tag: From, +{ + fn from(list: Vec) -> Self { + Tag::List(list.into_iter().map(|it| it.into()).collect()) + } +} + +impl NBT { + /// Creates a new nbt tag. The tag value can be anything. + /// + /// # Panics + /// This will panic if the tag is a list, and the values within that list + /// contain multiple types. This is a limitation with the nbt data format: + /// lists can only contain one type of data. + pub fn new(name: &str, tag: Tag) -> Self { + if let Tag::List(inner) = &tag { + if let Some(v) = inner.get(0) { + let ty = v.ty(); + for v in inner { + if v.ty() != ty { + panic!("the given list contains multiple types: {inner:?}"); + } + } + } + } + NBT { + tag, + name: name.into(), + } + } + + /// Creates an empty nbt tag. + pub const fn empty() -> Self { + NBT { + tag: Tag::End, + name: String::new(), + } + } + + /// Appends the given element to the list. This will panic if self is not a + /// list, or if tag does not match the type of the existing elements. + pub fn list_add(&mut self, tag: Tag) { + if let Tag::List(inner) = &mut self.tag { + if let Some(v) = inner.get(0) { + if tag.ty() != v.ty() { + panic!("cannot add different types to list. current: {inner:?}, new: {tag:?}"); + } else { + inner.push(tag); + } + } else { + // No elements yet, so we add this no matter what type it is. + inner.push(tag); + } + } else { + panic!("called list_add on non-list type: {self:?}"); + } + } + + /// Appends the given element to the compound. This will panic if self is not + /// a compound tag. + pub fn compound_add(&mut self, name: String, value: Tag) { + if let Tag::Compound(inner) = &mut self.tag { + inner.insert(name, value); + } else { + panic!("called compound_add on non-compound type: {self:?}"); + } + } + + /// If this is a compound tag, this returns the inner data of the tag. + /// Otherwise, this panics. + pub fn compound(&self) -> Option<&Compound> { + if let Tag::Compound(inner) = &self.tag { + Some(inner) + } else { + None + } + } + /// If this is a compound tag, this returns the inner data of the tag. + /// Otherwise, this panics. + pub fn compound_mut(&mut self) -> Option<&mut Compound> { + if let Tag::Compound(inner) = &mut self.tag { + Some(inner) + } else { + None + } + } + + pub fn tag(&self) -> &Tag { + &self.tag + } + pub fn into_tag(self) -> Tag { + self.tag + } +} + +macro_rules! getter { + ( $(: $conv:tt)? $name:ident -> $variant:ident ( $ty:ty ) ) => { + pub fn $name(&self) -> Result<$ty, WrongTag> { + match self { + Self::$variant(v) => Ok($($conv)? v), + _ => Err(WrongTag(self.clone())), + } + } + }; +} + +impl Tag { + /// A simpler way to construct compound tags inline. + pub fn new_compound(value: &[(&str, Tag)]) -> Self { + let mut inner = HashMap::new(); + for (name, tag) in value { + inner.insert(name.to_string(), tag.clone()); + } + inner.into() + } + + getter!(:*byte -> Byte(i8)); + getter!(:*short -> Short(i16)); + getter!(:*int -> Int(i32)); + getter!(:*long -> Long(i64)); + getter!(:*float -> Float(f32)); + getter!(:*double -> Double(f64)); + getter!(string -> String(&str)); + getter!(byte_arr -> ByteArr(&[u8])); + getter!(list -> List(&Vec)); + getter!(compound -> Compound(&Compound)); + getter!(long_arr -> LongArray(&Vec)); + + pub fn compound_mut(&mut self) -> Result<&mut Compound, WrongTag> { + match self { + Self::Compound(v) => Ok(v), + _ => Err(WrongTag(self.clone())), + } + } +} diff --git a/pumpkin/src/protocol/nbt/serialize.rs b/pumpkin/src/protocol/nbt/serialize.rs index b55d44f2f..068b654bc 100644 --- a/pumpkin/src/protocol/nbt/serialize.rs +++ b/pumpkin/src/protocol/nbt/serialize.rs @@ -3,12 +3,18 @@ use crate::protocol::bytebuf::buffer::ByteBuffer; use super::{Tag, NBT}; impl NBT { - pub fn serialize(&self) -> Vec { - let mut out = ByteBuffer::new(); + pub fn serialize_buf(&self, out: &mut ByteBuffer) { out.write_u8(self.tag.ty()); + if matches!(self.tag, Tag::End) { + return; + } out.write_u16(self.name.len() as u16); out.write_bytes(self.name.as_bytes()); - out.write_bytes(&self.tag.serialize()); + self.tag.serialize(out); + } + pub fn serialize(&self) -> Vec { + let mut out = ByteBuffer::new(); + self.serialize_buf(&mut out); out.into_vec() } } @@ -33,8 +39,8 @@ impl Tag { } } - fn serialize(&self) -> Vec { - let mut out = ByteBuffer::new(); + /// Serializes the data of the tag. Does not add type byte. + fn serialize(&self, out: &mut ByteBuffer) { match self { Self::End => (), Self::Byte(v) => out.write_i8(*v), @@ -55,11 +61,11 @@ impl Tag { out.write_u8(v.get(0).unwrap_or(&Self::End).ty()); out.write_i32(v.len() as i32); for tag in v { - out.write_bytes(&tag.serialize()); + tag.serialize(out); } } Self::Compound(v) => { - for (name, tag) in v { + for (name, tag) in &v.inner { // Each element in the HashMap is essentially a NBT, but we store it in a // separated form, so we have a manual implementation of serialize() here. out.write_u8(tag.ty()); @@ -69,7 +75,7 @@ impl Tag { } out.write_u16(name.len() as u16); out.write_bytes(name.as_bytes()); - out.write_bytes(&tag.serialize()); + tag.serialize(out); } out.write_u8(Self::End.ty()); } @@ -86,6 +92,5 @@ impl Tag { } } } - out.into_vec() } } diff --git a/pumpkin/src/protocol/registry/biomes.rs b/pumpkin/src/protocol/registry/biomes.rs new file mode 100644 index 000000000..da1581023 --- /dev/null +++ b/pumpkin/src/protocol/registry/biomes.rs @@ -0,0 +1,73 @@ +use super::CodecItem; +use serde::Serialize; + +#[derive(Debug, Clone, Serialize)] +pub struct Biome { + category: String, + depth: f32, + downfall: f32, + effects: BiomeEffects, + precipitation: String, + scale: f32, + temperature: f32, + has_precipitation: bool, +} +#[derive(Debug, Clone, Serialize)] +struct BiomeEffects { + sky_color: i32, + fog_color: i32, + water_fog_color: i32, + water_color: i32, + #[serde(skip_serializing_if = "Option::is_none")] + foliage_color: Option, + #[serde(skip_serializing_if = "Option::is_none")] + grass_color: Option, + #[serde(skip_serializing_if = "Option::is_none")] + mood_sound: Option, // 1.18.2+ +} +#[derive(Debug, Clone, Serialize)] +struct MoodSound { + block_search_extent: i32, + offset: f64, + sound: String, + tick_delay: i32, +} + +pub(super) fn all() -> Vec> { + let biome = Biome { + precipitation: "rain".into(), + depth: 1.0, + temperature: 1.0, + scale: 1.0, + downfall: 1.0, + category: "none".into(), + has_precipitation: true, + effects: BiomeEffects { + sky_color: 0x78a7ff, + fog_color: 0xc0d8ff, + water_fog_color: 0x050533, + water_color: 0x3f76e4, + foliage_color: None, + grass_color: None, + mood_sound: Some(MoodSound { + block_search_extent: 8, + offset: 2.0, + sound: "minecraft:ambient.cave".into(), + tick_delay: 6000, + }), + // sky_color: 0xff00ff, + // water_color: 0xff00ff, + // fog_color: 0xff00ff, + // water_fog_color: 0xff00ff, + // grass_color: 0xff00ff, + // foliage_color: 0x00ffe5, + // grass_color: 0xff5900, + }, + }; + + vec![CodecItem { + name: "minecraft:plains".into(), + id: 0, + element: biome, + }] +} diff --git a/pumpkin/src/protocol/registry/chat_type.rs b/pumpkin/src/protocol/registry/chat_type.rs new file mode 100644 index 000000000..3dbf3a434 --- /dev/null +++ b/pumpkin/src/protocol/registry/chat_type.rs @@ -0,0 +1,30 @@ +use super::CodecItem; +use serde::Serialize; + +#[derive(Debug, Clone, Serialize)] +pub struct ChatType { + chat: ChatParams, + narration: ChatParams, +} +#[derive(Debug, Clone, Serialize)] +struct ChatParams { + parameters: Vec, + translation_key: String, +} + +pub(super) fn all() -> Vec> { + vec![CodecItem { + name: "minecraft:chat".into(), + id: 0, + element: ChatType { + chat: ChatParams { + parameters: vec!["sender".into(), "content".into()], + translation_key: "chat.type.text".into(), + }, + narration: ChatParams { + parameters: vec!["sender".into(), "content".into()], + translation_key: "chat.type.text.narrate".into(), + }, + }, + }] +} diff --git a/pumpkin/src/protocol/registry/damage_type.rs b/pumpkin/src/protocol/registry/damage_type.rs new file mode 100644 index 000000000..35e6d6431 --- /dev/null +++ b/pumpkin/src/protocol/registry/damage_type.rs @@ -0,0 +1,58 @@ +use super::CodecItem; +use serde::Serialize; + +#[derive(Debug, Clone, Serialize)] +pub struct DamageType { + exhaustion: f32, + message_id: String, + scaling: String, + #[serde(skip_serializing_if = "Option::is_none")] + effects: Option, +} + +const NAMES: &[&str] = &[ + "in_fire", + "lightning_bolt", + "on_fire", + "lava", + "hot_floor", + "in_wall", + "cramming", + "drown", + "starve", + "cactus", + "fall", + "fly_into_wall", + "out_of_world", + "generic", + "magic", + "wither", + "dragon_breath", + "dry_out", + "sweet_berry_bush", + "freeze", + "stalagmite", + // 1.20+ + "outside_border", + "generic_kill", +]; + +pub(super) fn all() -> Vec> { + let mut items: Vec<_> = NAMES + .iter() + .map(|name| CodecItem { + name: (*name).into(), + id: 0, + element: DamageType { + exhaustion: 0.1, + message_id: "inFire".into(), + scaling: "when_caused_by_living_non_player".into(), + effects: None, + }, + }) + .collect(); + + items[1].element.effects = Some("burning".into()); + + items +} diff --git a/pumpkin/src/protocol/registry/dimensions.rs b/pumpkin/src/protocol/registry/dimensions.rs new file mode 100644 index 000000000..db4cf2208 --- /dev/null +++ b/pumpkin/src/protocol/registry/dimensions.rs @@ -0,0 +1,49 @@ +use serde::Serialize; + +#[derive(Debug, Clone, Serialize)] +pub struct Dimension { + ambient_light: f32, + bed_works: bool, + coordinate_scale: f32, + effects: String, + has_ceiling: bool, + has_raids: bool, + has_skylight: bool, + height: i32, // 1.17+ + infiniburn: String, + logical_height: i32, + min_y: i32, // 1.17+ + natural: bool, + piglin_safe: bool, + fixed_time: i64, + respawn_anchor_works: bool, + ultrawarm: bool, + + // 1.19+ + monster_spawn_light_level: i32, + monster_spawn_block_light_limit: i32, +} + +pub fn overworld(world_min_y: i32, world_height: u32) -> Dimension { + Dimension { + piglin_safe: false, + natural: true, + ambient_light: 0.0, + fixed_time: 6000, + infiniburn: "#minecraft:infiniburn_overworld".into(), + respawn_anchor_works: false, + has_skylight: true, + bed_works: true, + effects: "minecraft:overworld".into(), + has_raids: false, + logical_height: 128, + coordinate_scale: 1.0, + ultrawarm: false, + has_ceiling: false, + min_y: world_min_y, + height: (world_height as i32 + 15) / 16 * 16, + + monster_spawn_light_level: 7, + monster_spawn_block_light_limit: 7, + } +} diff --git a/pumpkin/src/protocol/registry/mod.rs b/pumpkin/src/protocol/registry/mod.rs index 8b1378917..e21b4f21e 100644 --- a/pumpkin/src/protocol/registry/mod.rs +++ b/pumpkin/src/protocol/registry/mod.rs @@ -1 +1,75 @@ +use serde::Serialize; +use super::{bytebuf::buffer::ByteBuffer, nbt}; + +mod biomes; +mod chat_type; +mod damage_type; +mod dimensions; + +#[derive(Debug, Clone, Serialize)] +struct LoginInfo { + #[serde(rename = "minecraft:dimension_type")] + dimensions: Codec, + #[serde(rename = "minecraft:worldgen/biome")] + biomes: Codec, + #[serde(rename = "minecraft:chat_type")] + chat: Codec, + #[serde(rename = "minecraft:damage_type")] + damage: Codec, +} + +#[derive(Debug, Clone, Serialize)] +struct Codec { + #[serde(rename = "type")] + ty: String, + value: Vec>, +} +#[derive(Debug, Clone, Serialize)] +struct CodecItem { + name: String, + id: i32, + element: T, +} + +pub fn write_single_dimension(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) +where + std::io::Cursor: std::io::Write, +{ + let dimension = dimensions::overworld(world_min_y, world_height); + out.write_bytes(&nbt::to_nbt("", &dimension).unwrap().serialize()); +} + +pub fn write_codec(out: &mut ByteBuffer, world_min_y: i32, world_height: u32) { + let dimension = dimensions::overworld(world_min_y, world_height); + + let info = LoginInfo { + dimensions: Codec { + ty: "minecraft:dimension_type".into(), + value: vec![CodecItem { + name: "minecraft:overworld".into(), + id: 0, + element: dimension, + }], + }, + biomes: Codec { + ty: "minecraft:worldgen/biome".into(), + value: biomes::all(), + }, + chat: Codec { + ty: "minecraft:chat_type".into(), + value: chat_type::all(), + }, + damage: Codec { + ty: "minecraft:damage_type".into(), + value: damage_type::all(), + }, + }; + + // Dimension codec + out.write_bytes(&nbt::to_nbt("", &info).unwrap().serialize()); + // Current dimension type (key in dimension codec) + out.write_string("minecraft:overworld"); + // Current world + out.write_string("minecraft:overworld"); +} diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index b721ca5ec..d0eaab6bd 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -15,7 +15,13 @@ use crate::{ player::{GameMode, Player}, Entity, EntityId, }, - protocol::{client::play::CLogin, Players, Sample, StatusResponse, VarInt, Version}, + protocol::{ + client::{ + config::{CKnownPacks, CRegistryData, Entry, KnownPack}, + play::CLogin, + }, + Players, Sample, StatusResponse, VarInt, Version, + }, world::World, }; @@ -88,6 +94,23 @@ impl Server { entity_id: self.new_entity_id(), }, }; + // known data packs + client.send_packet(CKnownPacks::new( + 1, + vec![KnownPack { + namespace: "minecraft".to_string(), + id: "core".to_string(), + version: "1.21".to_string(), + }], + )); + client.send_packet(CRegistryData::new( + "0".into(), + 1, + vec![Entry { + entry_id: "minecraft:dimension_type".into(), + has_data: true, + }], + )); client.send_packet(CLogin::new( player.entity_id(),