From 55e8cd696ebad8f45cff389a4e808719cf9f0671 Mon Sep 17 00:00:00 2001 From: kralverde <80051564+kralverde@users.noreply.github.com> Date: Sat, 21 Dec 2024 06:54:23 -0500 Subject: [PATCH 01/14] Remove optimizations for dev builds (#404) --- Cargo.toml | 2 -- pumpkin-world/src/level.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0c92812e..7122a354 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,6 @@ members = [ version = "0.1.0" edition = "2021" -[profile.dev.package."*"] -opt-level = 3 [profile.dev] opt-level = 1 diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 2a6a9571..e0b8ac12 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -72,7 +72,7 @@ impl Level { } } else { let seed = get_or_create_seed(); - let world_gen = get_world_gen(seed).into(); // TODO Read Seed from config. + let world_gen = get_world_gen(seed).into(); Self { seed, world_gen, From 0633197b628aeb861d386e32971b10b1d5c43ed1 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Sat, 21 Dec 2024 17:03:52 +0100 Subject: [PATCH 02/14] pumpkin-protocol: added codecs --- pumpkin-protocol/src/bytebuf/deserializer.rs | 24 +-- pumpkin-protocol/src/bytebuf/mod.rs | 91 ++++++----- pumpkin-protocol/src/bytebuf/packet_id.rs | 14 +- pumpkin-protocol/src/bytebuf/serializer.rs | 44 ++--- .../src/client/config/c_cookie_request.rs | 2 +- .../src/client/config/c_known_packs.rs | 4 +- .../src/client/config/c_registry_data.rs | 23 ++- .../src/client/config/c_store_cookie.rs | 2 +- .../src/client/login/c_cookie_request.rs | 3 +- .../src/client/login/c_login_success.rs | 4 +- .../src/client/play/c_boss_event.rs | 4 +- .../src/client/play/c_chunk_data.rs | 12 +- .../src/client/play/c_command_suggestions.rs | 4 +- .../src/client/play/c_commands.rs | 24 +-- .../src/client/play/c_cookie_request.rs | 3 +- .../src/client/play/c_entity_sound_effect.rs | 6 +- .../client/play/c_initialize_world_border.rs | 2 +- pumpkin-protocol/src/client/play/c_login.rs | 14 +- .../src/client/play/c_player_chat_message.rs | 10 +- .../src/client/play/c_player_info_update.rs | 4 +- .../src/client/play/c_player_position.rs | 4 +- pumpkin-protocol/src/client/play/c_respawn.rs | 14 +- .../src/client/play/c_set_border_lerp_size.rs | 2 +- .../src/client/play/c_sound_effect.rs | 6 +- .../src/client/play/c_store_cookie.rs | 2 +- .../src/client/play/c_teleport_entity.rs | 4 +- .../src/client/play/c_update_objectives.rs | 4 +- pumpkin-protocol/src/lib.rs | 34 +--- pumpkin-protocol/src/packet_decoder.rs | 9 +- pumpkin-protocol/src/packet_encoder.rs | 7 +- .../src/server/config/s_cookie_response.rs | 9 +- .../src/server/config/s_plugin_message.rs | 11 +- pumpkin-protocol/src/server/handshake/mod.rs | 4 +- .../src/server/login/s_cookie_response.rs | 9 +- .../src/server/login/s_encryption_response.rs | 4 +- .../src/server/login/s_login_start.rs | 4 +- .../src/server/login/s_plugin_response.rs | 6 +- .../src/server/play/s_chat_message.rs | 4 +- .../src/server/play/s_cookie_response.rs | 9 +- .../src/server/play/s_interact.rs | 4 +- .../src/server/play/s_player_command.rs | 4 +- pumpkin-protocol/src/var_int.rs | 151 ----------------- pumpkin-protocol/src/var_long.rs | 152 ------------------ pumpkin-registry/src/banner_pattern.rs | 3 +- pumpkin-registry/src/biome.rs | 2 +- pumpkin-registry/src/lib.rs | 72 ++++----- pumpkin-registry/src/paint.rs | 3 +- pumpkin-registry/src/trim_pattern.rs | 3 +- pumpkin-world/src/item/item_registry.rs | 11 +- pumpkin/src/client/client_packet.rs | 17 +- pumpkin/src/client/combat.rs | 3 +- pumpkin/src/client/container.rs | 2 +- pumpkin/src/client/mod.rs | 14 +- pumpkin/src/client/player_packet.rs | 4 +- pumpkin/src/command/commands/cmd_transfer.rs | 2 +- pumpkin/src/entity/mod.rs | 2 +- pumpkin/src/entity/player.rs | 11 +- pumpkin/src/server/connection_cache.rs | 3 +- pumpkin/src/server/mod.rs | 2 - pumpkin/src/world/mod.rs | 10 +- pumpkin/src/world/scoreboard.rs | 3 +- 61 files changed, 307 insertions(+), 611 deletions(-) delete mode 100644 pumpkin-protocol/src/var_int.rs delete mode 100644 pumpkin-protocol/src/var_long.rs diff --git a/pumpkin-protocol/src/bytebuf/deserializer.rs b/pumpkin-protocol/src/bytebuf/deserializer.rs index 3b999e96..c7f574c3 100644 --- a/pumpkin-protocol/src/bytebuf/deserializer.rs +++ b/pumpkin-protocol/src/bytebuf/deserializer.rs @@ -1,11 +1,11 @@ use std::fmt::Display; use super::{ByteBuf, ReadingError}; -use bytes::Bytes; +use bytes::Buf; use serde::de::{self, DeserializeSeed, SeqAccess}; -pub struct Deserializer<'a> { - inner: &'a mut Bytes, +pub struct Deserializer<'a, B: Buf> { + inner: &'a mut B, } impl de::Error for ReadingError { @@ -14,13 +14,13 @@ impl de::Error for ReadingError { } } -impl<'a> Deserializer<'a> { - pub fn new(buf: &'a mut Bytes) -> Self { +impl<'a, B: Buf> Deserializer<'a, B> { + pub fn new(buf: &'a mut B) -> Self { Self { inner: buf } } } -impl<'de> de::Deserializer<'de> for Deserializer<'_> { +impl<'de, B: Buf> de::Deserializer<'de> for Deserializer<'_, B> { type Error = ReadingError; fn deserialize_any(self, _visitor: V) -> Result @@ -184,11 +184,11 @@ impl<'de> de::Deserializer<'de> for Deserializer<'_> { where V: de::Visitor<'de>, { - struct Access<'a, 'b> { - deserializer: &'a mut Deserializer<'b>, + struct Access<'a, 'b, B: Buf> { + deserializer: &'a mut Deserializer<'b, B>, } - impl<'de, 'a, 'b: 'a> SeqAccess<'de> for Access<'a, 'b> { + impl<'de, 'a, 'b: 'a, B: Buf> SeqAccess<'de> for Access<'a, 'b, B> { type Error = ReadingError; fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> @@ -216,12 +216,12 @@ impl<'de> de::Deserializer<'de> for Deserializer<'_> { where V: de::Visitor<'de>, { - struct Access<'a, 'b> { - deserializer: &'a mut Deserializer<'b>, + struct Access<'a, 'b, B: Buf> { + deserializer: &'a mut Deserializer<'b, B>, len: usize, } - impl<'de, 'a, 'b: 'a> SeqAccess<'de> for Access<'a, 'b> { + impl<'de, 'a, 'b: 'a, B: Buf> SeqAccess<'de> for Access<'a, 'b, B> { type Error = ReadingError; fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> diff --git a/pumpkin-protocol/src/bytebuf/mod.rs b/pumpkin-protocol/src/bytebuf/mod.rs index 94e57f52..b1ee75ae 100644 --- a/pumpkin-protocol/src/bytebuf/mod.rs +++ b/pumpkin-protocol/src/bytebuf/mod.rs @@ -1,6 +1,12 @@ use core::str; -use crate::{BitSet, FixedBitSet, VarInt, VarLong}; +use crate::{ + codec::{ + bit_set::BitSet, identifier::Identifier, var_int::VarInt, var_long::VarLong, Codec, + DecodeError, + }, + FixedBitSet, +}; use bytes::{Buf, BufMut}; mod deserializer; @@ -54,9 +60,11 @@ pub trait ByteBuf: Buf { fn try_get_var_long(&mut self) -> Result; + fn try_get_identifer(&mut self) -> Result; + fn try_get_string(&mut self) -> Result; - fn try_get_string_len(&mut self, max_size: u32) -> Result; + fn try_get_string_len(&mut self, max_size: usize) -> Result; /// Reads a boolean. If true, the closure is called, and the returned value is /// wrapped in Some. Otherwise, this returns None. @@ -181,12 +189,8 @@ impl ByteBuf for T { match VarInt::decode(self) { Ok(var_int) => Ok(var_int), Err(error) => match error { - crate::VarIntDecodeError::Incomplete => { - Err(ReadingError::Incomplete("varint".to_string())) - } - crate::VarIntDecodeError::TooLarge => { - Err(ReadingError::TooLarge("varint".to_string())) - } + DecodeError::Incomplete => Err(ReadingError::Incomplete("varint".to_string())), + DecodeError::TooLarge => Err(ReadingError::TooLarge("varint".to_string())), }, } } @@ -194,28 +198,24 @@ impl ByteBuf for T { match VarLong::decode(self) { Ok(var_long) => Ok(var_long), Err(error) => match error { - crate::VarLongDecodeError::Incomplete => { - Err(ReadingError::Incomplete("varint".to_string())) - } - crate::VarLongDecodeError::TooLarge => { - Err(ReadingError::TooLarge("varlong".to_string())) - } + DecodeError::Incomplete => Err(ReadingError::Incomplete("varint".to_string())), + DecodeError::TooLarge => Err(ReadingError::TooLarge("varlong".to_string())), }, } } fn try_get_string(&mut self) -> Result { - self.try_get_string_len(i16::MAX as u32) + self.try_get_string_len(i16::MAX as usize) } - fn try_get_string_len(&mut self, max_size: u32) -> Result { + fn try_get_string_len(&mut self, max_size: usize) -> Result { let size = self.try_get_var_int()?.0; - if size as u32 > max_size { + if size as usize > max_size { return Err(ReadingError::TooLarge("string".to_string())); } let data = self.try_copy_to_bytes(size as usize)?; - if data.len() as u32 > max_size { + if data.len() > max_size { return Err(ReadingError::TooLarge("string".to_string())); } match str::from_utf8(&data) { @@ -256,6 +256,16 @@ impl ByteBuf for T { fn try_get_fixed_bitset(&mut self, bits: usize) -> Result { self.try_copy_to_bytes(bits.div_ceil(8)) } + + fn try_get_identifer(&mut self) -> Result { + match Identifier::decode(self) { + Ok(identifer) => Ok(identifer), + Err(error) => match error { + DecodeError::Incomplete => Err(ReadingError::Incomplete("identifer".to_string())), + DecodeError::TooLarge => Err(ReadingError::TooLarge("identifer".to_string())), + }, + } + } } pub trait ByteBufMut { @@ -265,7 +275,7 @@ pub trait ByteBufMut { fn put_string(&mut self, val: &str); - fn put_string_len(&mut self, val: &str, max_size: u32); + fn put_string_len(&mut self, val: &str, max_size: usize); fn put_string_array(&mut self, array: &[String]); @@ -277,6 +287,8 @@ pub trait ByteBufMut { fn put_list(&mut self, list: &[G], write: impl Fn(&mut Self, &G)); + fn put_identifier(&mut self, val: &Identifier); + fn put_var_int(&mut self, value: &VarInt); fn put_varint_arr(&mut self, v: &[i32]); @@ -299,11 +311,11 @@ impl ByteBufMut for T { } fn put_string(&mut self, val: &str) { - self.put_string_len(val, i16::MAX as u32); + self.put_string_len(val, i16::MAX as usize); } - fn put_string_len(&mut self, val: &str, max_size: u32) { - if val.len() as u32 > max_size { + fn put_string_len(&mut self, val: &str, max_size: usize) { + if val.len() > max_size { // Should be panic?, I mean its our fault panic!("String is too big"); } @@ -317,15 +329,12 @@ impl ByteBufMut for T { } } - fn put_var_int(&mut self, value: &VarInt) { - value.encode(self); + fn put_var_int(&mut self, var_int: &VarInt) { + var_int.encode(self); } - fn put_bit_set(&mut self, set: &BitSet) { - self.put_var_int(&set.0); - for b in set.1 { - self.put_i64(*b); - } + fn put_bit_set(&mut self, bit_set: &BitSet) { + bit_set.encode(self); } fn put_option(&mut self, val: &Option, write: impl FnOnce(&mut Self, &G)) { @@ -345,6 +354,10 @@ impl ByteBufMut for T { fn put_varint_arr(&mut self, v: &[i32]) { self.put_list(v, |p, &v| p.put_var_int(&v.into())) } + + fn put_identifier(&mut self, val: &Identifier) { + val.encode(self); + } } #[cfg(test)] @@ -364,14 +377,12 @@ mod test { bar: i32, } let foo = Foo { bar: 69 }; - let mut serializer = serializer::Serializer::new(BytesMut::new()); + let mut bytes = BytesMut::new(); + let mut serializer = serializer::Serializer::new(&mut bytes); foo.serialize(&mut serializer).unwrap(); - let serialized: BytesMut = serializer.into(); - let deserialized: Foo = Foo::deserialize(deserializer::Deserializer::new( - &mut Bytes::from(serialized), - )) - .unwrap(); + let deserialized: Foo = + Foo::deserialize(deserializer::Deserializer::new(&mut Bytes::from(bytes))).unwrap(); assert_eq!(foo, deserialized); } @@ -383,14 +394,12 @@ mod test { bar: VarInt, } let foo = Foo { bar: 69.into() }; - let mut serializer = serializer::Serializer::new(BytesMut::new()); + let mut bytes = BytesMut::new(); + let mut serializer = serializer::Serializer::new(&mut bytes); foo.serialize(&mut serializer).unwrap(); - let serialized: BytesMut = serializer.into(); - let deserialized: Foo = Foo::deserialize(deserializer::Deserializer::new( - &mut Bytes::from(serialized), - )) - .unwrap(); + let deserialized: Foo = + Foo::deserialize(deserializer::Deserializer::new(&mut Bytes::from(bytes))).unwrap(); assert_eq!(foo, deserialized); } diff --git a/pumpkin-protocol/src/bytebuf/packet_id.rs b/pumpkin-protocol/src/bytebuf/packet_id.rs index 95c8a18e..6534033e 100644 --- a/pumpkin-protocol/src/bytebuf/packet_id.rs +++ b/pumpkin-protocol/src/bytebuf/packet_id.rs @@ -1,7 +1,7 @@ -use bytes::{BufMut, Bytes, BytesMut}; +use bytes::{Buf, BufMut}; use serde::{de::DeserializeOwned, Serialize}; -use crate::{ClientPacket, ServerPacket, VarIntType}; +use crate::{codec::var_int::VarIntType, ClientPacket, ServerPacket}; use super::{deserializer, serializer, ReadingError}; @@ -13,14 +13,10 @@ impl

ClientPacket for P where P: Packet + Serialize, { - fn write(&self, bytebuf: &mut BytesMut) { - let mut serializer = serializer::Serializer::new(BytesMut::new()); + fn write(&self, bytebuf: &mut impl BufMut) { + let mut serializer = serializer::Serializer::new(bytebuf); self.serialize(&mut serializer) .expect("Could not serialize packet"); - // We write the packet in an empty bytebuffer and then put it into our current one. - // In the future we may do packet batching thats the reason i don't let every packet create a new bytebuffer and use - // an existing instead - bytebuf.put(serializer.output); } } @@ -28,7 +24,7 @@ impl

ServerPacket for P where P: Packet + DeserializeOwned, { - fn read(bytebuf: &mut Bytes) -> Result { + fn read(bytebuf: &mut impl Buf) -> Result { let deserializer = deserializer::Deserializer::new(bytebuf); P::deserialize(deserializer) } diff --git a/pumpkin-protocol/src/bytebuf/serializer.rs b/pumpkin-protocol/src/bytebuf/serializer.rs index cace3c6a..5682aa7b 100644 --- a/pumpkin-protocol/src/bytebuf/serializer.rs +++ b/pumpkin-protocol/src/bytebuf/serializer.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use bytes::{BufMut, BytesMut}; +use bytes::BufMut; use serde::{ ser::{self}, Serialize, @@ -9,34 +9,16 @@ use thiserror::Error; use super::ByteBufMut; -pub struct Serializer { - pub output: BytesMut, +pub struct Serializer { + pub output: B, } -impl Serializer { - pub fn new(buf: BytesMut) -> Self { +impl Serializer { + pub fn new(buf: B) -> Self { Self { output: buf } } } -impl From for BytesMut { - fn from(val: Serializer) -> Self { - val.output - } -} - -impl AsRef for Serializer { - fn as_ref(&self) -> &BytesMut { - &self.output - } -} - -impl AsMut for Serializer { - fn as_mut(&mut self) -> &mut BytesMut { - &mut self.output - } -} - #[derive(Debug, Error)] pub enum SerializerError { #[error("serializer error {0}")] @@ -57,7 +39,7 @@ impl ser::Error for SerializerError { // Structs are ignored // Iterables' values are written in order, but NO information (e.g. size) about the // iterable itself is written (list sizes should be a separate field) -impl ser::Serializer for &mut Serializer { +impl ser::Serializer for &mut Serializer { type Ok = (); type Error = SerializerError; @@ -227,7 +209,7 @@ impl ser::Serializer for &mut Serializer { } } -impl ser::SerializeSeq for &mut Serializer { +impl ser::SerializeSeq for &mut Serializer { // Must match the `Ok` type of the serializer. type Ok = (); // Must match the `Error` type of the serializer. @@ -247,7 +229,7 @@ impl ser::SerializeSeq for &mut Serializer { } } -impl ser::SerializeTuple for &mut Serializer { +impl ser::SerializeTuple for &mut Serializer { type Ok = (); type Error = SerializerError; @@ -264,7 +246,7 @@ impl ser::SerializeTuple for &mut Serializer { } // Same thing but for tuple structs. -impl ser::SerializeTupleStruct for &mut Serializer { +impl ser::SerializeTupleStruct for &mut Serializer { type Ok = (); type Error = SerializerError; @@ -289,7 +271,7 @@ impl ser::SerializeTupleStruct for &mut Serializer { // // So the `end` method in this impl is responsible for closing both the `]` and // the `}`. -impl ser::SerializeTupleVariant for &mut Serializer { +impl ser::SerializeTupleVariant for &mut Serializer { type Ok = (); type Error = SerializerError; @@ -313,7 +295,7 @@ impl ser::SerializeTupleVariant for &mut Serializer { // `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 ser::SerializeMap for &mut Serializer { +impl ser::SerializeMap for &mut Serializer { type Ok = (); type Error = SerializerError; @@ -349,7 +331,7 @@ impl ser::SerializeMap for &mut Serializer { // Structs are like maps in which the keys are constrained to be compile-time // constant strings. -impl ser::SerializeStruct for &mut Serializer { +impl ser::SerializeStruct for &mut Serializer { type Ok = (); type Error = SerializerError; @@ -372,7 +354,7 @@ impl ser::SerializeStruct for &mut Serializer { // Similar to `SerializeTupleVariant`, here the `end` method is responsible for // closing both of the curly braces opened by `serialize_struct_variant`. -impl ser::SerializeStructVariant for &mut Serializer { +impl ser::SerializeStructVariant for &mut Serializer { type Ok = (); type Error = SerializerError; diff --git a/pumpkin-protocol/src/client/config/c_cookie_request.rs b/pumpkin-protocol/src/client/config/c_cookie_request.rs index 08bdcedb..71fb9aca 100644 --- a/pumpkin-protocol/src/client/config/c_cookie_request.rs +++ b/pumpkin-protocol/src/client/config/c_cookie_request.rs @@ -1,6 +1,6 @@ use pumpkin_macros::client_packet; -use crate::Identifier; +use crate::codec::identifier::Identifier; #[derive(serde::Serialize)] #[client_packet("config:cookie_request")] diff --git a/pumpkin-protocol/src/client/config/c_known_packs.rs b/pumpkin-protocol/src/client/config/c_known_packs.rs index 467143e2..7622b93e 100644 --- a/pumpkin-protocol/src/client/config/c_known_packs.rs +++ b/pumpkin-protocol/src/client/config/c_known_packs.rs @@ -1,4 +1,4 @@ -use bytes::BytesMut; +use bytes::BufMut; use pumpkin_macros::client_packet; use crate::{bytebuf::ByteBufMut, ClientPacket, KnownPack}; @@ -15,7 +15,7 @@ impl<'a> CKnownPacks<'a> { } impl ClientPacket for CKnownPacks<'_> { - fn write(&self, bytebuf: &mut BytesMut) { + fn write(&self, bytebuf: &mut impl BufMut) { bytebuf.put_list::(self.known_packs, |p, v| { p.put_string(v.namespace); p.put_string(v.id); diff --git a/pumpkin-protocol/src/client/config/c_registry_data.rs b/pumpkin-protocol/src/client/config/c_registry_data.rs index 001769d5..5a04e682 100644 --- a/pumpkin-protocol/src/client/config/c_registry_data.rs +++ b/pumpkin-protocol/src/client/config/c_registry_data.rs @@ -1,16 +1,16 @@ use bytes::{BufMut, BytesMut}; use pumpkin_macros::client_packet; -use crate::{bytebuf::ByteBufMut, ClientPacket}; +use crate::{bytebuf::ByteBufMut, codec::identifier::Identifier, ClientPacket}; #[client_packet("config:registry_data")] pub struct CRegistryData<'a> { - registry_id: &'a str, - entries: &'a [RegistryEntry<'a>], + registry_id: &'a Identifier, + entries: &'a [RegistryEntry], } impl<'a> CRegistryData<'a> { - pub fn new(registry_id: &'a str, entries: &'a [RegistryEntry]) -> Self { + pub fn new(registry_id: &'a Identifier, entries: &'a [RegistryEntry]) -> Self { Self { registry_id, entries, @@ -18,18 +18,17 @@ impl<'a> CRegistryData<'a> { } } -pub struct RegistryEntry<'a> { - pub entry_id: &'a str, - pub data: BytesMut, +pub struct RegistryEntry { + pub entry_id: Identifier, + pub data: Option, } impl ClientPacket for CRegistryData<'_> { - fn write(&self, bytebuf: &mut BytesMut) { - bytebuf.put_string(self.registry_id); + fn write(&self, bytebuf: &mut impl BufMut) { + bytebuf.put_identifier(self.registry_id); bytebuf.put_list::(self.entries, |p, v| { - p.put_string(v.entry_id); - p.put_bool(!v.data.is_empty()); - p.put_slice(&v.data); + p.put_identifier(&v.entry_id); + p.put_option(&v.data, |p, v| p.put_slice(v)); }); } } diff --git a/pumpkin-protocol/src/client/config/c_store_cookie.rs b/pumpkin-protocol/src/client/config/c_store_cookie.rs index 511bdd0d..0334df2d 100644 --- a/pumpkin-protocol/src/client/config/c_store_cookie.rs +++ b/pumpkin-protocol/src/client/config/c_store_cookie.rs @@ -1,4 +1,4 @@ -use crate::{Identifier, VarInt}; +use crate::{codec::identifier::Identifier, VarInt}; use pumpkin_macros::client_packet; #[derive(serde::Serialize)] diff --git a/pumpkin-protocol/src/client/login/c_cookie_request.rs b/pumpkin-protocol/src/client/login/c_cookie_request.rs index df592625..e1749fac 100644 --- a/pumpkin-protocol/src/client/login/c_cookie_request.rs +++ b/pumpkin-protocol/src/client/login/c_cookie_request.rs @@ -1,7 +1,8 @@ -use crate::Identifier; use pumpkin_macros::client_packet; use serde::Serialize; +use crate::codec::identifier::Identifier; + #[derive(Serialize)] #[client_packet("login:cookie_request")] /// Requests a cookie that was previously stored. diff --git a/pumpkin-protocol/src/client/login/c_login_success.rs b/pumpkin-protocol/src/client/login/c_login_success.rs index 8c6f311a..290ca4c2 100644 --- a/pumpkin-protocol/src/client/login/c_login_success.rs +++ b/pumpkin-protocol/src/client/login/c_login_success.rs @@ -1,4 +1,4 @@ -use bytes::BytesMut; +use bytes::BufMut; use pumpkin_macros::client_packet; use crate::{bytebuf::ByteBufMut, ClientPacket, Property}; @@ -21,7 +21,7 @@ impl<'a> CLoginSuccess<'a> { } impl ClientPacket for CLoginSuccess<'_> { - fn write(&self, bytebuf: &mut BytesMut) { + fn write(&self, bytebuf: &mut impl BufMut) { bytebuf.put_uuid(self.uuid); bytebuf.put_string(self.username); bytebuf.put_list::(self.properties, |p, v| { diff --git a/pumpkin-protocol/src/client/play/c_boss_event.rs b/pumpkin-protocol/src/client/play/c_boss_event.rs index 449f18e1..e5b2292d 100644 --- a/pumpkin-protocol/src/client/play/c_boss_event.rs +++ b/pumpkin-protocol/src/client/play/c_boss_event.rs @@ -1,7 +1,7 @@ use crate::bytebuf::ByteBufMut; use crate::client::play::bossevent_action::BosseventAction; use crate::{ClientPacket, VarInt}; -use bytes::{BufMut, BytesMut}; +use bytes::BufMut; use pumpkin_macros::client_packet; #[client_packet("play:boss_event")] @@ -17,7 +17,7 @@ impl<'a> CBossEvent<'a> { } impl ClientPacket for CBossEvent<'_> { - fn write(&self, bytebuf: &mut BytesMut) { + fn write(&self, bytebuf: &mut impl BufMut) { bytebuf.put_uuid(&self.uuid); let action = &self.action; match action { diff --git a/pumpkin-protocol/src/client/play/c_chunk_data.rs b/pumpkin-protocol/src/client/play/c_chunk_data.rs index f655c00c..1356508f 100644 --- a/pumpkin-protocol/src/client/play/c_chunk_data.rs +++ b/pumpkin-protocol/src/client/play/c_chunk_data.rs @@ -1,4 +1,4 @@ -use crate::{bytebuf::ByteBufMut, BitSet, ClientPacket, VarInt}; +use crate::{bytebuf::ByteBufMut, codec::bit_set::BitSet, ClientPacket, VarInt}; use bytes::{BufMut, BytesMut}; use pumpkin_macros::client_packet; @@ -8,7 +8,7 @@ use pumpkin_world::{chunk::ChunkData, DIRECT_PALETTE_BITS}; pub struct CChunkData<'a>(pub &'a ChunkData); impl ClientPacket for CChunkData<'_> { - fn write(&self, buf: &mut BytesMut) { + fn write(&self, buf: &mut impl BufMut) { // Chunk X buf.put_i32(self.0.position.x); // Chunk Z @@ -113,13 +113,13 @@ impl ClientPacket for CChunkData<'_> { // Sky Light Mask // All of the chunks, this is not optimal and uses way more data than needed but will be // overhauled with full lighting system. - buf.put_bit_set(&BitSet(VarInt(1), &[0b01111111111111111111111110])); + buf.put_bit_set(&BitSet(VarInt(1), vec![0b01111111111111111111111110])); // Block Light Mask - buf.put_bit_set(&BitSet(VarInt(1), &[0])); + buf.put_bit_set(&BitSet(VarInt(1), vec![0])); // Empty Sky Light Mask - buf.put_bit_set(&BitSet(VarInt(1), &[0b0])); + buf.put_bit_set(&BitSet(VarInt(1), vec![0b0])); // Empty Block Light Mask - buf.put_bit_set(&BitSet(VarInt(1), &[0])); + buf.put_bit_set(&BitSet(VarInt(1), vec![0])); buf.put_var_int(&VarInt(self.0.blocks.subchunks_len() as i32)); self.0.blocks.iter_subchunks().for_each(|chunk| { diff --git a/pumpkin-protocol/src/client/play/c_command_suggestions.rs b/pumpkin-protocol/src/client/play/c_command_suggestions.rs index d3592ce3..25ae6683 100644 --- a/pumpkin-protocol/src/client/play/c_command_suggestions.rs +++ b/pumpkin-protocol/src/client/play/c_command_suggestions.rs @@ -1,4 +1,4 @@ -use bytes::{BufMut, BytesMut}; +use bytes::BufMut; use pumpkin_core::text::TextComponent; use pumpkin_macros::client_packet; @@ -29,7 +29,7 @@ impl<'a> CCommandSuggestions<'a> { } impl ClientPacket for CCommandSuggestions<'_> { - fn write(&self, bytebuf: &mut BytesMut) { + fn write(&self, bytebuf: &mut impl BufMut) { bytebuf.put_var_int(&self.id); bytebuf.put_var_int(&self.start); bytebuf.put_var_int(&self.length); diff --git a/pumpkin-protocol/src/client/play/c_commands.rs b/pumpkin-protocol/src/client/play/c_commands.rs index d3b54d6f..264a5133 100644 --- a/pumpkin-protocol/src/client/play/c_commands.rs +++ b/pumpkin-protocol/src/client/play/c_commands.rs @@ -1,4 +1,4 @@ -use bytes::{BufMut, BytesMut}; +use bytes::BufMut; use pumpkin_macros::client_packet; use crate::{bytebuf::ByteBufMut, ClientPacket, VarInt}; @@ -19,7 +19,7 @@ impl<'a> CCommands<'a> { } impl ClientPacket for CCommands<'_> { - fn write(&self, bytebuf: &mut BytesMut) { + fn write(&self, bytebuf: &mut impl BufMut) { bytebuf.put_list(&self.nodes, |bytebuf, node: &ProtoNode| { node.write_to(bytebuf) }); @@ -52,7 +52,7 @@ impl ProtoNode<'_> { const FLAG_HAS_REDIRECT: u8 = 8; const FLAG_HAS_SUGGESTION_TYPE: u8 = 16; - pub fn write_to(&self, bytebuf: &mut BytesMut) { + pub fn write_to(&self, bytebuf: &mut impl BufMut) { // flags let flags = match self.node_type { ProtoNodeType::Root => 0, @@ -188,7 +188,7 @@ impl ProtoCmdArgParser<'_> { pub const SCORE_HOLDER_FLAG_ALLOW_MULTIPLE: u8 = 1; - pub fn write_to_buffer(&self, bytebuf: &mut BytesMut) { + pub fn write_to_buffer(&self, bytebuf: &mut impl BufMut) { match self { Self::Bool => bytebuf.put_var_int(&0.into()), Self::Float { min, max } => Self::write_number_arg(&1.into(), *min, *max, bytebuf), @@ -270,7 +270,7 @@ impl ProtoCmdArgParser<'_> { id: &VarInt, min: Option, max: Option, - bytebuf: &mut BytesMut, + bytebuf: &mut impl BufMut, ) { let mut flags: u8 = 0; if min.is_some() { @@ -291,13 +291,13 @@ impl ProtoCmdArgParser<'_> { } } - fn write_with_flags(id: &VarInt, flags: u8, bytebuf: &mut BytesMut) { + fn write_with_flags(id: &VarInt, flags: u8, bytebuf: &mut impl BufMut) { bytebuf.put_var_int(id); bytebuf.put_u8(flags); } - fn write_with_identifier(id: &VarInt, extra_identifier: &str, bytebuf: &mut BytesMut) { + fn write_with_identifier(id: &VarInt, extra_identifier: &str, bytebuf: &mut impl BufMut) { bytebuf.put_var_int(id); bytebuf.put_string(extra_identifier); @@ -313,29 +313,29 @@ pub enum StringProtoArgBehavior { } trait NumberCmdArg { - fn write(self, bytebuf: &mut BytesMut); + fn write(self, bytebuf: &mut impl BufMut); } impl NumberCmdArg for f32 { - fn write(self, bytebuf: &mut BytesMut) { + fn write(self, bytebuf: &mut impl BufMut) { bytebuf.put_f32(self); } } impl NumberCmdArg for f64 { - fn write(self, bytebuf: &mut BytesMut) { + fn write(self, bytebuf: &mut impl BufMut) { bytebuf.put_f64(self); } } impl NumberCmdArg for i32 { - fn write(self, bytebuf: &mut BytesMut) { + fn write(self, bytebuf: &mut impl BufMut) { bytebuf.put_i32(self); } } impl NumberCmdArg for i64 { - fn write(self, bytebuf: &mut BytesMut) { + fn write(self, bytebuf: &mut impl BufMut) { bytebuf.put_i64(self); } } diff --git a/pumpkin-protocol/src/client/play/c_cookie_request.rs b/pumpkin-protocol/src/client/play/c_cookie_request.rs index 58d49494..9b569cc0 100644 --- a/pumpkin-protocol/src/client/play/c_cookie_request.rs +++ b/pumpkin-protocol/src/client/play/c_cookie_request.rs @@ -1,7 +1,8 @@ -use crate::Identifier; use pumpkin_macros::client_packet; use serde::Serialize; +use crate::codec::identifier::Identifier; + #[derive(Serialize)] #[client_packet("play:cookie_request")] /// Requests a cookie that was previously stored. diff --git a/pumpkin-protocol/src/client/play/c_entity_sound_effect.rs b/pumpkin-protocol/src/client/play/c_entity_sound_effect.rs index 37848de8..27de764f 100644 --- a/pumpkin-protocol/src/client/play/c_entity_sound_effect.rs +++ b/pumpkin-protocol/src/client/play/c_entity_sound_effect.rs @@ -1,4 +1,4 @@ -use bytes::{BufMut, BytesMut}; +use bytes::BufMut; use pumpkin_macros::client_packet; use crate::{bytebuf::ByteBufMut, ClientPacket, IDOrSoundEvent, SoundCategory, SoundEvent, VarInt}; @@ -39,11 +39,11 @@ impl CEntitySoundEffect { } impl ClientPacket for CEntitySoundEffect { - fn write(&self, bytebuf: &mut BytesMut) { + fn write(&self, bytebuf: &mut impl BufMut) { bytebuf.put_var_int(&self.sound_event.id); if self.sound_event.id.0 == 0 { if let Some(test) = &self.sound_event.sound_event { - bytebuf.put_string(&test.sound_name); + bytebuf.put_identifier(&test.sound_name); bytebuf.put_option(&test.range, |p, v| { p.put_f32(*v); diff --git a/pumpkin-protocol/src/client/play/c_initialize_world_border.rs b/pumpkin-protocol/src/client/play/c_initialize_world_border.rs index f4d6663a..f2ab72f6 100644 --- a/pumpkin-protocol/src/client/play/c_initialize_world_border.rs +++ b/pumpkin-protocol/src/client/play/c_initialize_world_border.rs @@ -1,7 +1,7 @@ use pumpkin_macros::client_packet; use serde::Serialize; -use crate::{VarInt, VarLong}; +use crate::{codec::var_long::VarLong, VarInt}; #[derive(Serialize)] #[client_packet("play:initialize_border")] diff --git a/pumpkin-protocol/src/client/play/c_login.rs b/pumpkin-protocol/src/client/play/c_login.rs index 12a594f8..ec6e23bb 100644 --- a/pumpkin-protocol/src/client/play/c_login.rs +++ b/pumpkin-protocol/src/client/play/c_login.rs @@ -3,7 +3,7 @@ use pumpkin_core::math::position::WorldPosition; use pumpkin_macros::client_packet; use serde::Serialize; -use crate::VarInt; +use crate::{codec::identifier::Identifier, VarInt}; #[derive(Serialize)] #[client_packet("play:login")] @@ -11,7 +11,7 @@ pub struct CLogin<'a> { entity_id: i32, is_hardcore: bool, dimension_count: VarInt, - dimension_names: &'a [&'a str], + dimension_names: &'a [Identifier], max_players: VarInt, view_distance: VarInt, simulated_distance: VarInt, @@ -20,13 +20,13 @@ pub struct CLogin<'a> { limited_crafting: bool, // Spawn Info dimension_type: VarInt, - dimension_name: &'a str, + dimension_name: Identifier, hashed_seed: i64, game_mode: u8, previous_gamemode: i8, debug: bool, is_flat: bool, - death_dimension_name: Option<(&'a str, WorldPosition)>, + death_dimension_name: Option<(Identifier, WorldPosition)>, portal_cooldown: VarInt, sealevel: VarInt, enforce_secure_chat: bool, @@ -37,7 +37,7 @@ impl<'a> CLogin<'a> { pub fn new( entity_id: i32, is_hardcore: bool, - dimension_names: &'a [&'a str], + dimension_names: &'a [Identifier], max_players: VarInt, view_distance: VarInt, simulated_distance: VarInt, @@ -45,13 +45,13 @@ impl<'a> CLogin<'a> { enabled_respawn_screen: bool, limited_crafting: bool, dimension_type: VarInt, - dimension_name: &'a str, + dimension_name: Identifier, hashed_seed: i64, game_mode: u8, previous_gamemode: i8, debug: bool, is_flat: bool, - death_dimension_name: Option<(&'a str, WorldPosition)>, + death_dimension_name: Option<(Identifier, WorldPosition)>, portal_cooldown: VarInt, sealevel: VarInt, enforce_secure_chat: bool, diff --git a/pumpkin-protocol/src/client/play/c_player_chat_message.rs b/pumpkin-protocol/src/client/play/c_player_chat_message.rs index b774a2f2..0db6c3df 100644 --- a/pumpkin-protocol/src/client/play/c_player_chat_message.rs +++ b/pumpkin-protocol/src/client/play/c_player_chat_message.rs @@ -3,7 +3,7 @@ use pumpkin_core::text::TextComponent; use pumpkin_macros::client_packet; use serde::Serialize; -use crate::{BitSet, VarInt}; +use crate::{codec::bit_set::BitSet, VarInt}; #[derive(Serialize)] #[client_packet("play:player_chat")] @@ -18,7 +18,7 @@ pub struct CPlayerChatMessage<'a> { previous_messages_count: VarInt, previous_messages: &'a [PreviousMessage<'a>], // max 20 unsigned_content: Option>, - filter_type: FilterType<'a>, + filter_type: FilterType, chat_type: VarInt, sender_name: TextComponent<'a>, target_name: Option>, @@ -35,7 +35,7 @@ impl<'a> CPlayerChatMessage<'a> { salt: i64, previous_messages: &'a [PreviousMessage<'a>], unsigned_content: Option>, - filter_type: FilterType<'a>, + filter_type: FilterType, chat_type: VarInt, sender_name: TextComponent<'a>, target_name: Option>, @@ -66,11 +66,11 @@ pub struct PreviousMessage<'a> { #[derive(Serialize)] #[repr(i32)] -pub enum FilterType<'a> { +pub enum FilterType { /// Message is not filtered at all PassThrough = 0, /// Message is fully filtered FullyFiltered = 1, /// Only some characters in the message are filtered - PartiallyFiltered(BitSet<'a>) = 2, + PartiallyFiltered(BitSet) = 2, } diff --git a/pumpkin-protocol/src/client/play/c_player_info_update.rs b/pumpkin-protocol/src/client/play/c_player_info_update.rs index 5626b387..72d912cb 100644 --- a/pumpkin-protocol/src/client/play/c_player_info_update.rs +++ b/pumpkin-protocol/src/client/play/c_player_info_update.rs @@ -1,4 +1,4 @@ -use bytes::{BufMut, BytesMut}; +use bytes::BufMut; use pumpkin_macros::client_packet; use crate::{bytebuf::ByteBufMut, ClientPacket, Property}; @@ -23,7 +23,7 @@ impl<'a> CPlayerInfoUpdate<'a> { } impl ClientPacket for CPlayerInfoUpdate<'_> { - fn write(&self, bytebuf: &mut BytesMut) { + fn write(&self, bytebuf: &mut impl BufMut) { bytebuf.put_i8(self.actions); bytebuf.put_list::(self.players, |p, v| { p.put_uuid(&v.uuid); diff --git a/pumpkin-protocol/src/client/play/c_player_position.rs b/pumpkin-protocol/src/client/play/c_player_position.rs index 96a6bc59..287c1b2f 100644 --- a/pumpkin-protocol/src/client/play/c_player_position.rs +++ b/pumpkin-protocol/src/client/play/c_player_position.rs @@ -1,4 +1,4 @@ -use bytes::{BufMut, BytesMut}; +use bytes::BufMut; use pumpkin_core::math::vector3::Vector3; use pumpkin_macros::client_packet; @@ -35,7 +35,7 @@ impl<'a> CPlayerPosition<'a> { } impl ClientPacket for CPlayerPosition<'_> { - fn write(&self, bytebuf: &mut BytesMut) { + fn write(&self, bytebuf: &mut impl BufMut) { bytebuf.put_var_int(&self.teleport_id); bytebuf.put_f64(self.position.x); bytebuf.put_f64(self.position.y); diff --git a/pumpkin-protocol/src/client/play/c_respawn.rs b/pumpkin-protocol/src/client/play/c_respawn.rs index b892d53e..9d68b059 100644 --- a/pumpkin-protocol/src/client/play/c_respawn.rs +++ b/pumpkin-protocol/src/client/play/c_respawn.rs @@ -2,35 +2,35 @@ use pumpkin_core::math::position::WorldPosition; use pumpkin_macros::client_packet; use serde::Serialize; -use crate::VarInt; +use crate::{codec::identifier::Identifier, VarInt}; #[derive(Serialize)] #[client_packet("play:respawn")] -pub struct CRespawn<'a> { +pub struct CRespawn { dimension_type: VarInt, - dimension_name: &'a str, + dimension_name: Identifier, hashed_seed: i64, game_mode: u8, previous_gamemode: i8, debug: bool, is_flat: bool, - death_dimension_name: Option<(&'a str, WorldPosition)>, + death_dimension_name: Option<(Identifier, WorldPosition)>, portal_cooldown: VarInt, sealevel: VarInt, data_kept: u8, } -impl<'a> CRespawn<'a> { +impl CRespawn { #[expect(clippy::too_many_arguments)] pub fn new( dimension_type: VarInt, - dimension_name: &'a str, + dimension_name: Identifier, hashed_seed: i64, game_mode: u8, previous_gamemode: i8, debug: bool, is_flat: bool, - death_dimension_name: Option<(&'a str, WorldPosition)>, + death_dimension_name: Option<(Identifier, WorldPosition)>, portal_cooldown: VarInt, sealevel: VarInt, data_kept: u8, diff --git a/pumpkin-protocol/src/client/play/c_set_border_lerp_size.rs b/pumpkin-protocol/src/client/play/c_set_border_lerp_size.rs index 0d15ab9b..5f8861a7 100644 --- a/pumpkin-protocol/src/client/play/c_set_border_lerp_size.rs +++ b/pumpkin-protocol/src/client/play/c_set_border_lerp_size.rs @@ -1,7 +1,7 @@ use pumpkin_macros::client_packet; use serde::Serialize; -use crate::VarLong; +use crate::codec::var_long::VarLong; #[derive(Serialize)] #[client_packet("play:set_border_lerp_size")] diff --git a/pumpkin-protocol/src/client/play/c_sound_effect.rs b/pumpkin-protocol/src/client/play/c_sound_effect.rs index d2f7f344..d34af971 100644 --- a/pumpkin-protocol/src/client/play/c_sound_effect.rs +++ b/pumpkin-protocol/src/client/play/c_sound_effect.rs @@ -1,4 +1,4 @@ -use bytes::{BufMut, BytesMut}; +use bytes::BufMut; use pumpkin_macros::client_packet; use crate::{bytebuf::ByteBufMut, ClientPacket, IDOrSoundEvent, SoundCategory, SoundEvent, VarInt}; @@ -45,11 +45,11 @@ impl CSoundEffect { } impl ClientPacket for CSoundEffect { - fn write(&self, bytebuf: &mut BytesMut) { + fn write(&self, bytebuf: &mut impl BufMut) { bytebuf.put_var_int(&self.sound_event.id); if self.sound_event.id.0 == 0 { if let Some(test) = &self.sound_event.sound_event { - bytebuf.put_string(&test.sound_name); + bytebuf.put_identifier(&test.sound_name); bytebuf.put_option(&test.range, |p, v| { p.put_f32(*v); diff --git a/pumpkin-protocol/src/client/play/c_store_cookie.rs b/pumpkin-protocol/src/client/play/c_store_cookie.rs index b9fc02ce..da715f22 100644 --- a/pumpkin-protocol/src/client/play/c_store_cookie.rs +++ b/pumpkin-protocol/src/client/play/c_store_cookie.rs @@ -1,4 +1,4 @@ -use crate::{Identifier, VarInt}; +use crate::{codec::identifier::Identifier, VarInt}; use pumpkin_macros::client_packet; use serde::Serialize; diff --git a/pumpkin-protocol/src/client/play/c_teleport_entity.rs b/pumpkin-protocol/src/client/play/c_teleport_entity.rs index 02029373..2ec00492 100644 --- a/pumpkin-protocol/src/client/play/c_teleport_entity.rs +++ b/pumpkin-protocol/src/client/play/c_teleport_entity.rs @@ -1,4 +1,4 @@ -use bytes::{BufMut, BytesMut}; +use bytes::BufMut; use pumpkin_core::math::vector3::Vector3; use pumpkin_macros::client_packet; @@ -38,7 +38,7 @@ impl<'a> CTeleportEntity<'a> { } impl ClientPacket for CTeleportEntity<'_> { - fn write(&self, bytebuf: &mut BytesMut) { + fn write(&self, bytebuf: &mut impl BufMut) { bytebuf.put_var_int(&self.entity_id); bytebuf.put_f64(self.position.x); bytebuf.put_f64(self.position.y); diff --git a/pumpkin-protocol/src/client/play/c_update_objectives.rs b/pumpkin-protocol/src/client/play/c_update_objectives.rs index 12fcefc3..41c45ff5 100644 --- a/pumpkin-protocol/src/client/play/c_update_objectives.rs +++ b/pumpkin-protocol/src/client/play/c_update_objectives.rs @@ -1,4 +1,4 @@ -use bytes::{BufMut, BytesMut}; +use bytes::BufMut; use pumpkin_core::text::TextComponent; use pumpkin_macros::client_packet; @@ -32,7 +32,7 @@ impl<'a> CUpdateObjectives<'a> { } impl ClientPacket for CUpdateObjectives<'_> { - fn write(&self, bytebuf: &mut BytesMut) { + fn write(&self, bytebuf: &mut impl BufMut) { bytebuf.put_string(self.objective_name); bytebuf.put_u8(self.mode); if self.mode == 0 || self.mode == 2 { diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index f5b5db46..85a5311e 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -1,48 +1,28 @@ use std::num::NonZeroU16; use bytebuf::{packet_id::Packet, ReadingError}; -use bytes::{Bytes, BytesMut}; +use bytes::{Buf, BufMut, Bytes}; +use codec::{identifier::Identifier, var_int::VarInt}; use pumpkin_core::text::{style::Style, TextComponent}; -use serde::{Deserialize, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; pub mod bytebuf; pub mod client; +pub mod codec; pub mod packet_decoder; pub mod packet_encoder; pub mod query; pub mod server; pub mod slot; -mod var_int; -pub use var_int::*; - -mod var_long; -pub use var_long::*; - /// To current Minecraft protocol /// Don't forget to change this when porting pub const CURRENT_MC_PROTOCOL: NonZeroU16 = unsafe { NonZeroU16::new_unchecked(769) }; pub const MAX_PACKET_SIZE: i32 = 2097152; -/// usally uses a namespace like "minecraft:thing" -pub type Identifier = String; -pub type VarIntType = i32; -pub type VarLongType = i64; pub type FixedBitSet = bytes::Bytes; -pub struct BitSet<'a>(pub VarInt, pub &'a [i64]); - -impl Serialize for BitSet<'_> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // TODO: make this right - (&self.0, self.1).serialize(serializer) - } -} - #[derive(Debug, PartialEq, Clone, Copy)] pub enum ConnectionState { HandShake, @@ -89,7 +69,7 @@ pub struct IDOrSoundEvent { #[derive(Serialize)] pub struct SoundEvent { - pub sound_name: String, + pub sound_name: Identifier, pub range: Option, } @@ -99,11 +79,11 @@ pub struct RawPacket { } pub trait ClientPacket: Packet { - fn write(&self, bytebuf: &mut BytesMut); + fn write(&self, bytebuf: &mut impl BufMut); } pub trait ServerPacket: Packet + Sized { - fn read(bytebuf: &mut Bytes) -> Result; + fn read(bytebuf: &mut impl Buf) -> Result; } #[derive(Serialize)] diff --git a/pumpkin-protocol/src/packet_decoder.rs b/pumpkin-protocol/src/packet_decoder.rs index badd34b7..8c267f94 100644 --- a/pumpkin-protocol/src/packet_decoder.rs +++ b/pumpkin-protocol/src/packet_decoder.rs @@ -3,7 +3,10 @@ use bytes::{Buf, Bytes, BytesMut}; use libdeflater::{DecompressionError, Decompressor}; use thiserror::Error; -use crate::{RawPacket, VarInt, VarIntDecodeError, MAX_PACKET_SIZE}; +use crate::{ + codec::{Codec, DecodeError}, + RawPacket, VarInt, MAX_PACKET_SIZE, +}; type Cipher = cfb8::Decryptor; @@ -38,8 +41,8 @@ impl PacketDecoder { let packet_len = match VarInt::decode(&mut r) { Ok(len) => len, - Err(VarIntDecodeError::Incomplete) => return Ok(None), - Err(VarIntDecodeError::TooLarge) => Err(PacketDecodeError::MalformedLength)?, + Err(DecodeError::Incomplete) => return Ok(None), + Err(DecodeError::TooLarge) => Err(PacketDecodeError::MalformedLength)?, }; let packet_len = packet_len.0; diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index a329ee08..d8bea1b7 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -5,7 +5,7 @@ use thiserror::Error; use libdeflater::{CompressionLvl, Compressor}; -use crate::{ClientPacket, VarInt, MAX_PACKET_SIZE}; +use crate::{codec::Codec, ClientPacket, VarInt, MAX_PACKET_SIZE}; type Cipher = cfb8::Encryptor; @@ -221,9 +221,8 @@ pub enum PacketEncodeError { #[cfg(test)] mod tests { use super::*; - use crate::bytebuf::packet_id::Packet; use crate::client::status::CStatusResponse; - use crate::VarIntDecodeError; + use crate::{bytebuf::packet_id::Packet, codec::DecodeError}; use aes::Aes128; use cfb8::cipher::AsyncStreamCipher; use cfb8::Decryptor as Cfb8Decryptor; @@ -247,7 +246,7 @@ mod tests { } /// Helper function to decode a VarInt from bytes - fn decode_varint(buffer: &mut &[u8]) -> Result { + fn decode_varint(buffer: &mut &[u8]) -> Result { VarInt::decode(buffer).map(|varint| varint.0) } diff --git a/pumpkin-protocol/src/server/config/s_cookie_response.rs b/pumpkin-protocol/src/server/config/s_cookie_response.rs index ec3f4da0..0a2fc03d 100644 --- a/pumpkin-protocol/src/server/config/s_cookie_response.rs +++ b/pumpkin-protocol/src/server/config/s_cookie_response.rs @@ -1,10 +1,11 @@ -use bytes::Bytes; +use bytes::Buf; use pumpkin_macros::server_packet; use serde::de; use crate::{ bytebuf::{ByteBuf, ReadingError}, - Identifier, ServerPacket, VarInt, + codec::identifier::Identifier, + ServerPacket, VarInt, }; #[server_packet("config:cookie_response")] @@ -20,8 +21,8 @@ pub struct SCookieResponse { const MAX_PAYLOAD_SIZE: i32 = 5120; impl ServerPacket for SCookieResponse { - fn read(bytebuf: &mut Bytes) -> Result { - let key = bytebuf.try_get_string()?; + fn read(bytebuf: &mut impl Buf) -> Result { + let key = bytebuf.try_get_identifer()?; let has_payload = bytebuf.try_get_bool()?; if !has_payload { diff --git a/pumpkin-protocol/src/server/config/s_plugin_message.rs b/pumpkin-protocol/src/server/config/s_plugin_message.rs index 25918761..d9e0abae 100644 --- a/pumpkin-protocol/src/server/config/s_plugin_message.rs +++ b/pumpkin-protocol/src/server/config/s_plugin_message.rs @@ -1,9 +1,10 @@ -use bytes::Bytes; +use bytes::Buf; use pumpkin_macros::server_packet; use crate::{ bytebuf::{ByteBuf, ReadingError}, - Identifier, ServerPacket, + codec::identifier::Identifier, + ServerPacket, }; #[server_packet("config:custom_payload")] @@ -13,10 +14,10 @@ pub struct SPluginMessage { } impl ServerPacket for SPluginMessage { - fn read(bytebuf: &mut Bytes) -> Result { + fn read(bytebuf: &mut impl Buf) -> Result { Ok(Self { - channel: bytebuf.try_get_string()?, - data: bytebuf.split_to(bytebuf.len()), + channel: bytebuf.try_get_identifer()?, + data: bytebuf.try_copy_to_bytes(bytebuf.remaining())?, }) } } diff --git a/pumpkin-protocol/src/server/handshake/mod.rs b/pumpkin-protocol/src/server/handshake/mod.rs index 0e8a15ae..9436e569 100644 --- a/pumpkin-protocol/src/server/handshake/mod.rs +++ b/pumpkin-protocol/src/server/handshake/mod.rs @@ -1,4 +1,4 @@ -use bytes::Bytes; +use bytes::Buf; use pumpkin_macros::server_packet; use crate::{ @@ -15,7 +15,7 @@ pub struct SHandShake { } impl ServerPacket for SHandShake { - fn read(bytebuf: &mut Bytes) -> Result { + fn read(bytebuf: &mut impl Buf) -> Result { Ok(Self { protocol_version: bytebuf.try_get_var_int()?, server_address: bytebuf.try_get_string_len(255)?, diff --git a/pumpkin-protocol/src/server/login/s_cookie_response.rs b/pumpkin-protocol/src/server/login/s_cookie_response.rs index 833ae788..82ed37f0 100644 --- a/pumpkin-protocol/src/server/login/s_cookie_response.rs +++ b/pumpkin-protocol/src/server/login/s_cookie_response.rs @@ -1,8 +1,9 @@ use crate::{ bytebuf::{ByteBuf, ReadingError}, - Identifier, ServerPacket, VarInt, + codec::identifier::Identifier, + ServerPacket, VarInt, }; -use bytes::Bytes; +use bytes::Buf; use pumpkin_macros::server_packet; use serde::de; @@ -19,8 +20,8 @@ pub struct SCookieResponse { const MAX_PAYLOAD_SIZE: i32 = 5120; impl ServerPacket for SCookieResponse { - fn read(bytebuf: &mut Bytes) -> Result { - let key = bytebuf.try_get_string()?; + fn read(bytebuf: &mut impl Buf) -> Result { + let key = bytebuf.try_get_identifer()?; let has_payload = bytebuf.try_get_bool()?; if !has_payload { diff --git a/pumpkin-protocol/src/server/login/s_encryption_response.rs b/pumpkin-protocol/src/server/login/s_encryption_response.rs index 300ada9c..f094231d 100644 --- a/pumpkin-protocol/src/server/login/s_encryption_response.rs +++ b/pumpkin-protocol/src/server/login/s_encryption_response.rs @@ -1,4 +1,4 @@ -use bytes::Bytes; +use bytes::Buf; use pumpkin_macros::server_packet; use crate::{ @@ -15,7 +15,7 @@ pub struct SEncryptionResponse { } impl ServerPacket for SEncryptionResponse { - fn read(bytebuf: &mut Bytes) -> Result { + fn read(bytebuf: &mut impl Buf) -> Result { let shared_secret_length = bytebuf.try_get_var_int()?; let shared_secret = bytebuf.try_copy_to_bytes(shared_secret_length.0 as usize)?; let verify_token_length = bytebuf.try_get_var_int()?; diff --git a/pumpkin-protocol/src/server/login/s_login_start.rs b/pumpkin-protocol/src/server/login/s_login_start.rs index 44347dc7..88bda187 100644 --- a/pumpkin-protocol/src/server/login/s_login_start.rs +++ b/pumpkin-protocol/src/server/login/s_login_start.rs @@ -1,4 +1,4 @@ -use bytes::Bytes; +use bytes::Buf; use pumpkin_macros::server_packet; use crate::{ @@ -13,7 +13,7 @@ pub struct SLoginStart { } impl ServerPacket for SLoginStart { - fn read(bytebuf: &mut Bytes) -> Result { + fn read(bytebuf: &mut impl Buf) -> Result { Ok(Self { name: bytebuf.try_get_string_len(16)?, uuid: bytebuf.try_get_uuid()?, diff --git a/pumpkin-protocol/src/server/login/s_plugin_response.rs b/pumpkin-protocol/src/server/login/s_plugin_response.rs index d96b796e..dd92a1bb 100644 --- a/pumpkin-protocol/src/server/login/s_plugin_response.rs +++ b/pumpkin-protocol/src/server/login/s_plugin_response.rs @@ -2,7 +2,7 @@ use crate::{ bytebuf::{ByteBuf, ReadingError}, ServerPacket, VarInt, }; -use bytes::Bytes; +use bytes::{Buf, Bytes}; use pumpkin_macros::server_packet; #[server_packet("login:custom_query_answer")] @@ -12,10 +12,10 @@ pub struct SLoginPluginResponse { } impl ServerPacket for SLoginPluginResponse { - fn read(bytebuf: &mut Bytes) -> Result { + fn read(bytebuf: &mut impl Buf) -> Result { Ok(Self { message_id: bytebuf.try_get_var_int()?, - data: bytebuf.try_get_option(|v| Ok(v.split_to(v.len())))?, + data: bytebuf.try_get_option(|v| v.try_copy_to_bytes(v.remaining()))?, }) } } diff --git a/pumpkin-protocol/src/server/play/s_chat_message.rs b/pumpkin-protocol/src/server/play/s_chat_message.rs index 9fd65df3..c0049af2 100644 --- a/pumpkin-protocol/src/server/play/s_chat_message.rs +++ b/pumpkin-protocol/src/server/play/s_chat_message.rs @@ -1,4 +1,4 @@ -use bytes::Bytes; +use bytes::{Buf, Bytes}; use pumpkin_macros::server_packet; use crate::{ @@ -19,7 +19,7 @@ pub struct SChatMessage { // TODO impl ServerPacket for SChatMessage { - fn read(bytebuf: &mut Bytes) -> Result { + fn read(bytebuf: &mut impl Buf) -> Result { Ok(Self { message: bytebuf.try_get_string()?, timestamp: bytebuf.try_get_i64()?, diff --git a/pumpkin-protocol/src/server/play/s_cookie_response.rs b/pumpkin-protocol/src/server/play/s_cookie_response.rs index f3127fb4..7779d8ac 100644 --- a/pumpkin-protocol/src/server/play/s_cookie_response.rs +++ b/pumpkin-protocol/src/server/play/s_cookie_response.rs @@ -1,8 +1,9 @@ use crate::{ bytebuf::{ByteBuf, ReadingError}, - Identifier, ServerPacket, VarInt, + codec::identifier::Identifier, + ServerPacket, VarInt, }; -use bytes::Bytes; +use bytes::Buf; use pumpkin_macros::server_packet; use serde::de; @@ -19,8 +20,8 @@ pub struct SCookieResponse { const MAX_PAYLOAD_SIZE: i32 = 5120; impl ServerPacket for SCookieResponse { - fn read(bytebuf: &mut Bytes) -> Result { - let key = bytebuf.try_get_string()?; + fn read(bytebuf: &mut impl Buf) -> Result { + let key = bytebuf.try_get_identifer()?; let has_payload = bytebuf.try_get_bool()?; if !has_payload { diff --git a/pumpkin-protocol/src/server/play/s_interact.rs b/pumpkin-protocol/src/server/play/s_interact.rs index c7371502..a6a3844d 100644 --- a/pumpkin-protocol/src/server/play/s_interact.rs +++ b/pumpkin-protocol/src/server/play/s_interact.rs @@ -1,4 +1,4 @@ -use bytes::Bytes; +use bytes::Buf; use num_derive::FromPrimitive; use num_traits::FromPrimitive; use pumpkin_core::math::vector3::Vector3; @@ -20,7 +20,7 @@ pub struct SInteract { // Great job Mojang ;D impl ServerPacket for SInteract { - fn read(bytebuf: &mut Bytes) -> Result { + fn read(bytebuf: &mut impl Buf) -> Result { let entity_id = bytebuf.try_get_var_int()?; let typ = bytebuf.try_get_var_int()?; let action = ActionType::from_i32(typ.0) diff --git a/pumpkin-protocol/src/server/play/s_player_command.rs b/pumpkin-protocol/src/server/play/s_player_command.rs index 8b2c7055..6a9b65a4 100644 --- a/pumpkin-protocol/src/server/play/s_player_command.rs +++ b/pumpkin-protocol/src/server/play/s_player_command.rs @@ -1,4 +1,4 @@ -use bytes::Bytes; +use bytes::Buf; use num_derive::FromPrimitive; use pumpkin_macros::server_packet; @@ -27,7 +27,7 @@ pub enum Action { } impl ServerPacket for SPlayerCommand { - fn read(bytebuf: &mut Bytes) -> Result { + fn read(bytebuf: &mut impl Buf) -> Result { Ok(Self { entity_id: bytebuf.try_get_var_int()?, action: bytebuf.try_get_var_int()?, diff --git a/pumpkin-protocol/src/var_int.rs b/pumpkin-protocol/src/var_int.rs deleted file mode 100644 index 1401230a..00000000 --- a/pumpkin-protocol/src/var_int.rs +++ /dev/null @@ -1,151 +0,0 @@ -use std::num::NonZeroUsize; - -use bytes::{Buf, BufMut}; -use serde::{ - de::{SeqAccess, Visitor}, - Deserialize, Deserializer, Serialize, Serializer, -}; -use thiserror::Error; - -pub type VarIntType = i32; - -/** - * A variable-length integer type used by the Minecraft network protocol. - */ -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct VarInt(pub VarIntType); - -impl VarInt { - /// The maximum number of bytes a `VarInt` can occupy. - pub const MAX_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(5) }; - - /// Returns the exact number of bytes this varint will write when - /// [`Encode::encode`] is called, assuming no error occurs. - pub const fn written_size(self) -> usize { - match self.0 { - 0 => 1, - n => (31 - n.leading_zeros() as usize) / 7 + 1, - } - } - - pub fn encode(&self, w: &mut impl BufMut) { - let mut val = self.0; - for _ in 0..Self::MAX_SIZE.get() { - let b: u8 = val as u8 & 0b01111111; - val >>= 7; - w.put_u8(if val == 0 { b } else { b | 0b10000000 }); - if val == 0 { - break; - } - } - } - - pub fn decode(r: &mut impl Buf) -> Result { - let mut val = 0; - for i in 0..Self::MAX_SIZE.get() { - if !r.has_remaining() { - return Err(VarIntDecodeError::Incomplete); - } - let byte = r.get_u8(); - val |= (i32::from(byte) & 0x7F) << (i * 7); - if byte & 0x80 == 0 { - return Ok(VarInt(val)); - } - } - Err(VarIntDecodeError::TooLarge) - } -} - -impl From for VarInt { - fn from(value: i32) -> Self { - VarInt(value) - } -} - -impl From for VarInt { - fn from(value: u32) -> Self { - VarInt(value as i32) - } -} - -impl From for VarInt { - fn from(value: u8) -> Self { - VarInt(value as i32) - } -} - -impl From for VarInt { - fn from(value: usize) -> Self { - VarInt(value as i32) - } -} - -impl From for i32 { - fn from(value: VarInt) -> Self { - value.0 - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Error)] -pub enum VarIntDecodeError { - #[error("Incomplete VarInt decode")] - Incomplete, - #[error("VarInt is too large")] - TooLarge, -} - -impl Serialize for VarInt { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut value = self.0 as u32; - let mut buf = Vec::new(); - - while value > 0x7F { - buf.put_u8(value as u8 | 0x80); - value >>= 7; - } - - buf.put_u8(value as u8); - - serializer.serialize_bytes(&buf) - } -} - -impl<'de> Deserialize<'de> for VarInt { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct VarIntVisitor; - - impl<'de> Visitor<'de> for VarIntVisitor { - type Value = VarInt; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a valid VarInt encoded in a byte sequence") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - let mut val = 0; - for i in 0..VarInt::MAX_SIZE.get() { - if let Some(byte) = seq.next_element::()? { - val |= (i32::from(byte) & 0b01111111) << (i * 7); - if byte & 0b10000000 == 0 { - return Ok(VarInt(val)); - } - } else { - break; - } - } - Err(serde::de::Error::custom("VarInt was too large")) - } - } - - deserializer.deserialize_seq(VarIntVisitor) - } -} diff --git a/pumpkin-protocol/src/var_long.rs b/pumpkin-protocol/src/var_long.rs deleted file mode 100644 index 72f5c11e..00000000 --- a/pumpkin-protocol/src/var_long.rs +++ /dev/null @@ -1,152 +0,0 @@ -use std::num::NonZeroUsize; - -use bytes::{Buf, BufMut}; -use serde::{ - de::{self, SeqAccess, Visitor}, - Deserialize, Deserializer, Serialize, Serializer, -}; -use thiserror::Error; - -pub type VarLongType = i64; - -/** - * A variable-length long type used by the Minecraft network protocol. - */ -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct VarLong(pub VarLongType); - -impl VarLong { - /// The maximum number of bytes a `VarLong` - pub const MAX_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(10) }; - - /// Returns the exact number of bytes this varlong will write when - /// [`Encode::encode`] is called, assuming no error occurs. - pub const fn written_size(self) -> usize { - match self.0 { - 0 => 1, - n => (31 - n.leading_zeros() as usize) / 7 + 1, - } - } - - pub fn encode(&self, w: &mut impl BufMut) { - let mut x = self.0; - for _ in 0..Self::MAX_SIZE.get() { - let byte = (x & 0x7F) as u8; - x >>= 7; - if x == 0 { - w.put_slice(&[byte]); - break; - } - w.put_slice(&[byte | 0x80]); - } - } - - pub fn decode(r: &mut impl Buf) -> Result { - let mut val = 0; - for i in 0..Self::MAX_SIZE.get() { - if !r.has_remaining() { - return Err(VarLongDecodeError::Incomplete); - } - let byte = r.get_u8(); - val |= (i64::from(byte) & 0b01111111) << (i * 7); - if byte & 0b10000000 == 0 { - return Ok(VarLong(val)); - } - } - Err(VarLongDecodeError::TooLarge) - } -} - -impl From for VarLong { - fn from(value: i64) -> Self { - VarLong(value) - } -} - -impl From for VarLong { - fn from(value: u32) -> Self { - VarLong(value as i64) - } -} - -impl From for VarLong { - fn from(value: u8) -> Self { - VarLong(value as i64) - } -} - -impl From for VarLong { - fn from(value: usize) -> Self { - VarLong(value as i64) - } -} - -impl From for i64 { - fn from(value: VarLong) -> Self { - value.0 - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Error)] -pub enum VarLongDecodeError { - #[error("incomplete VarLong decode")] - Incomplete, - #[error("VarLong is too large")] - TooLarge, -} - -impl Serialize for VarLong { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut value = self.0 as u64; - let mut buf = Vec::new(); - - while value > 0x7F { - buf.put_u8(value as u8 | 0x80); - value >>= 7; - } - - buf.put_u8(value as u8); - - serializer.serialize_bytes(&buf) - } -} - -impl<'de> Deserialize<'de> for VarLong { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct VarLongVisitor; - - impl<'de> Visitor<'de> for VarLongVisitor { - type Value = VarLong; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a valid VarInt encoded in a byte sequence") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - let mut val = 0; - for i in 0..VarLong::MAX_SIZE.get() { - if let Some(byte) = seq.next_element::()? { - val |= (i64::from(byte) & 0b01111111) << (i * 7); - if byte & 0b10000000 == 0 { - return Ok(VarLong(val)); - } - } else { - break; - } - } - Err(de::Error::custom("VarInt was too large")) - } - } - - deserializer.deserialize_seq(VarLongVisitor) - } -} diff --git a/pumpkin-registry/src/banner_pattern.rs b/pumpkin-registry/src/banner_pattern.rs index bac63fda..da631c33 100644 --- a/pumpkin-registry/src/banner_pattern.rs +++ b/pumpkin-registry/src/banner_pattern.rs @@ -1,7 +1,8 @@ +use pumpkin_protocol::codec::identifier::Identifier; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BannerPattern { - asset_id: String, + asset_id: Identifier, translation_key: String, } diff --git a/pumpkin-registry/src/biome.rs b/pumpkin-registry/src/biome.rs index cbe03b41..e2d52253 100644 --- a/pumpkin-registry/src/biome.rs +++ b/pumpkin-registry/src/biome.rs @@ -1,4 +1,4 @@ -use pumpkin_protocol::VarInt; +use pumpkin_protocol::codec::var_int::VarInt; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/pumpkin-registry/src/lib.rs b/pumpkin-registry/src/lib.rs index 6960a183..80786b17 100644 --- a/pumpkin-registry/src/lib.rs +++ b/pumpkin-registry/src/lib.rs @@ -10,7 +10,7 @@ use indexmap::IndexMap; use instrument::Instrument; use jukebox_song::JukeboxSong; use paint::Painting; -use pumpkin_protocol::client::config::RegistryEntry; +use pumpkin_protocol::{client::config::RegistryEntry, codec::identifier::Identifier}; pub use recipe::{ flatten_3x3, IngredientSlot, IngredientType, Recipe, RecipeResult, RecipeType, RECIPES, }; @@ -41,8 +41,8 @@ pub static SYNCED_REGISTRIES: LazyLock = LazyLock::new(|| { }); pub struct Registry { - pub registry_id: String, - pub registry_entries: Vec>, + pub registry_id: Identifier, + pub registry_entries: Vec, } #[derive(Serialize, Deserialize)] @@ -78,12 +78,12 @@ struct DataPool { } impl DimensionType { - pub fn name(&self) -> &str { + pub fn name(&self) -> Identifier { match self { - Self::Overworld => "minecraft:overworld", - Self::OverworldCaves => "minecraft:overworld_caves", - Self::TheEnd => "minecraft:the_end", - Self::TheNether => "minecraft:the_nether", + Self::Overworld => Identifier::vanilla("overworld"), + Self::OverworldCaves => Identifier::vanilla("overworld_caves"), + Self::TheEnd => Identifier::vanilla("the_end"), + Self::TheNether => Identifier::vanilla("the_nether"), } } } @@ -94,12 +94,12 @@ impl Registry { .biome .iter() .map(|s| RegistryEntry { - entry_id: s.0, - data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(), + entry_id: Identifier::vanilla(s.0), + data: Some(pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap()), }) .collect(); let biome = Registry { - registry_id: "minecraft:worldgen/biome".to_string(), + registry_id: Identifier::vanilla("worldgen/biome"), registry_entries, }; @@ -107,12 +107,12 @@ impl Registry { .chat_type .iter() .map(|s| RegistryEntry { - entry_id: s.0, - data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(), + entry_id: Identifier::vanilla(s.0), + data: Some(pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap()), }) .collect(); let chat_type = Registry { - registry_id: "minecraft:chat_type".to_string(), + registry_id: Identifier::vanilla("chat_type"), registry_entries, }; @@ -120,7 +120,7 @@ impl Registry { // .trim_pattern // .iter() // .map(|s| RegistryEntry { - // entry_id: s.0, + // entry_id: Identifier::vanilla(s.0), // data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(), // }) // .collect(); @@ -133,7 +133,7 @@ impl Registry { // .trim_material // .iter() // .map(|s| RegistryEntry { - // entry_id: s.0, + // entry_id: Identifier::vanilla(s.0), // data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(), // }) // .collect(); @@ -148,13 +148,13 @@ impl Registry { .map(|s| { let variant = s.1.clone(); RegistryEntry { - entry_id: s.0, - data: pumpkin_nbt::serializer::to_bytes_unnamed(&variant).unwrap(), + entry_id: Identifier::vanilla(s.0), + data: Some(pumpkin_nbt::serializer::to_bytes_unnamed(&variant).unwrap()), } }) .collect(); let wolf_variant = Registry { - registry_id: "minecraft:wolf_variant".to_string(), + registry_id: Identifier::vanilla("wolf_variant"), registry_entries, }; @@ -162,12 +162,12 @@ impl Registry { .painting_variant .iter() .map(|s| RegistryEntry { - entry_id: s.0, - data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(), + entry_id: Identifier::vanilla(s.0), + data: Some(pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap()), }) .collect(); let painting_variant = Registry { - registry_id: "minecraft:painting_variant".to_string(), + registry_id: Identifier::vanilla("painting_variant"), registry_entries, }; @@ -175,12 +175,12 @@ impl Registry { .dimension_type .iter() .map(|s| RegistryEntry { - entry_id: s.0, - data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(), + entry_id: Identifier::vanilla(s.0), + data: Some(pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap()), }) .collect(); let dimension_type = Registry { - registry_id: "minecraft:dimension_type".to_string(), + registry_id: Identifier::vanilla("dimension_type"), registry_entries, }; @@ -188,12 +188,12 @@ impl Registry { .damage_type .iter() .map(|s| RegistryEntry { - entry_id: s.0, - data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(), + entry_id: Identifier::vanilla(s.0), + data: Some(pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap()), }) .collect(); let damage_type = Registry { - registry_id: "minecraft:damage_type".to_string(), + registry_id: Identifier::vanilla("damage_type"), registry_entries, }; @@ -201,12 +201,12 @@ impl Registry { .banner_pattern .iter() .map(|s| RegistryEntry { - entry_id: s.0, - data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(), + entry_id: Identifier::vanilla(s.0), + data: Some(pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap()), }) .collect(); let banner_pattern = Registry { - registry_id: "minecraft:banner_pattern".to_string(), + registry_id: Identifier::vanilla("banner_pattern"), registry_entries, }; @@ -215,7 +215,7 @@ impl Registry { // .enchantment // .iter() // .map(|s| RegistryEntry { - // entry_id: s.0, + // entry_id: Identifier::vanilla(s.0), // data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(), // }) // .collect(); @@ -228,12 +228,12 @@ impl Registry { .jukebox_song .iter() .map(|s| RegistryEntry { - entry_id: s.0, - data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(), + entry_id: Identifier::vanilla(s.0), + data: Some(pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap()), }) .collect(); let jukebox_song = Registry { - registry_id: "minecraft:jukebox_song".to_string(), + registry_id: Identifier::vanilla("jukebox_song"), registry_entries, }; @@ -241,7 +241,7 @@ impl Registry { // .instrument // .iter() // .map(|s| RegistryEntry { - // entry_id: s.0, + // entry_id: Identifier::vanilla(s.0), // data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(), // }) // .collect(); diff --git a/pumpkin-registry/src/paint.rs b/pumpkin-registry/src/paint.rs index 9c383f6f..1217798e 100644 --- a/pumpkin-registry/src/paint.rs +++ b/pumpkin-registry/src/paint.rs @@ -1,8 +1,9 @@ +use pumpkin_protocol::codec::identifier::Identifier; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Painting { - asset_id: String, + asset_id: Identifier, // #[serde(skip_serializing_if = "Option::is_none")] // title: Option>, // #[serde(skip_serializing_if = "Option::is_none")] diff --git a/pumpkin-registry/src/trim_pattern.rs b/pumpkin-registry/src/trim_pattern.rs index 48746df1..62f07daa 100644 --- a/pumpkin-registry/src/trim_pattern.rs +++ b/pumpkin-registry/src/trim_pattern.rs @@ -1,8 +1,9 @@ +use pumpkin_protocol::codec::identifier::Identifier; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TrimPattern { - asset_id: String, + asset_id: Identifier, template_item: String, // description: TextComponent<'static>, decal: bool, diff --git a/pumpkin-world/src/item/item_registry.rs b/pumpkin-world/src/item/item_registry.rs index e446e362..e393acc1 100644 --- a/pumpkin-world/src/item/item_registry.rs +++ b/pumpkin-world/src/item/item_registry.rs @@ -57,6 +57,15 @@ pub struct Modifier { pub type_val: String, pub id: String, pub amount: f64, - pub operation: String, + pub operation: Operation, + // TODO: Make this an enum pub slot: String, } + +#[derive(Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum Operation { + AddValue, + AddMultipliedBase, + AddMultipliedTotal, +} diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index dfe913e1..76e0ce68 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -12,9 +12,12 @@ use core::str; use num_traits::FromPrimitive; use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_core::text::TextComponent; -use pumpkin_protocol::client::config::{CServerLinks, Label, Link, LinkType}; use pumpkin_protocol::server::config::SCookieResponse as SCCookieResponse; use pumpkin_protocol::server::login::SCookieResponse as SLCookieResponse; +use pumpkin_protocol::{ + client::config::{CServerLinks, Label, Link, LinkType}, + codec::var_int::VarInt, +}; use pumpkin_protocol::{ client::{ config::{CConfigAddResourcePack, CFinishConfig, CKnownPacks, CRegistryData}, @@ -27,7 +30,7 @@ use pumpkin_protocol::{ login::{SEncryptionResponse, SLoginPluginResponse, SLoginStart}, status::SStatusPingRequest, }, - ConnectionState, KnownPack, VarInt, CURRENT_MC_PROTOCOL, + ConnectionState, KnownPack, CURRENT_MC_PROTOCOL, }; use std::{ num::{NonZeroI32, NonZeroU8}, @@ -318,7 +321,7 @@ impl Client { // TODO: allow plugins to access this log::debug!( "Received cookie_response[login]: key: \"{}\", has_payload: \"{}\", payload_length: \"{}\"", - packet.key, + packet.key.to_string(), packet.has_payload, packet.payload_length.unwrap_or(VarInt::from(0)).0 ); @@ -417,8 +420,10 @@ impl Client { pub async fn handle_plugin_message(&self, plugin_message: SPluginMessage) { log::debug!("Handling plugin message"); - if plugin_message.channel.starts_with("minecraft:brand") - || plugin_message.channel.starts_with("MC|Brand") + if plugin_message + .channel + .to_string() + .starts_with("minecraft:brand") { log::debug!("got a client brand"); match str::from_utf8(&plugin_message.data) { @@ -432,7 +437,7 @@ impl Client { // TODO: allow plugins to access this log::debug!( "Received cookie_response[config]: key: \"{}\", has_payload: \"{}\", payload_length: \"{}\"", - packet.key, + packet.key.to_string(), packet.has_payload, packet.payload_length.unwrap_or(VarInt::from(0)).0 ); diff --git a/pumpkin/src/client/combat.rs b/pumpkin/src/client/combat.rs index 8b5b0ed1..12cb0a90 100644 --- a/pumpkin/src/client/combat.rs +++ b/pumpkin/src/client/combat.rs @@ -4,7 +4,8 @@ use pumpkin_core::math::vector3::Vector3; use pumpkin_macros::{particle, sound}; use pumpkin_protocol::{ client::play::{CEntityVelocity, CParticle}, - SoundCategory, VarInt, + codec::var_int::VarInt, + SoundCategory, }; use pumpkin_world::item::ItemStack; diff --git a/pumpkin/src/client/container.rs b/pumpkin/src/client/container.rs index f171c116..42a4b23c 100644 --- a/pumpkin/src/client/container.rs +++ b/pumpkin/src/client/container.rs @@ -12,9 +12,9 @@ use pumpkin_inventory::{Container, WindowType}; use pumpkin_protocol::client::play::{ CCloseContainer, COpenScreen, CSetContainerContent, CSetContainerProperty, CSetContainerSlot, }; +use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::server::play::SClickContainer; use pumpkin_protocol::slot::Slot; -use pumpkin_protocol::VarInt; use pumpkin_world::item::item_registry::Item; use pumpkin_world::item::ItemStack; use std::sync::Arc; diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 1e3ce5e5..977dd6f6 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -296,8 +296,8 @@ impl Client { /// /// # Arguments /// - /// * `server`: A reference to the `Arc` instance. - pub async fn process_packets(&self, server: &Arc) { + /// * `server`: A reference to the `Server` instance. + pub async fn process_packets(&self, server: &Server) { let mut packet_queue = self.client_packets_queue.lock().await; while let Some(mut packet) = packet_queue.pop_front() { if self.closed.load(std::sync::atomic::Ordering::Relaxed) { @@ -330,7 +330,7 @@ impl Client { /// /// # Arguments /// - /// * `server`: A reference to the `Arc` instance. + /// * `server`: A reference to the `Server` instance. /// * `packet`: A mutable reference to the `RawPacket` to be processed. /// /// # Returns @@ -342,7 +342,7 @@ impl Client { /// Returns a `DeserializerError` if an error occurs during packet deserialization. pub async fn handle_packet( &self, - server: &Arc, + server: &Server, packet: &mut RawPacket, ) -> Result<(), ReadingError> { match self.connection_state.load() { @@ -386,7 +386,7 @@ impl Client { async fn handle_status_packet( &self, - server: &Arc, + server: &Server, packet: &mut RawPacket, ) -> Result<(), ReadingError> { log::debug!("Handling status group"); @@ -412,7 +412,7 @@ impl Client { async fn handle_login_packet( &self, - server: &Arc, + server: &Server, packet: &mut RawPacket, ) -> Result<(), ReadingError> { log::debug!("Handling login group for id"); @@ -448,7 +448,7 @@ impl Client { async fn handle_config_packet( &self, - server: &Arc, + server: &Server, packet: &mut RawPacket, ) -> Result<(), ReadingError> { log::debug!("Handling config group"); diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 920de697..a7b2b809 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -19,11 +19,11 @@ use pumpkin_core::{ GameMode, }; use pumpkin_inventory::{InventoryError, WindowType}; +use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::server::play::SCookieResponse as SPCookieResponse; use pumpkin_protocol::{ client::play::CCommandSuggestions, server::play::{SCloseContainer, SCommandSuggestion, SKeepAlive, SSetPlayerGround, SUseItem}, - VarInt, }; use pumpkin_protocol::{ client::play::{ @@ -896,7 +896,7 @@ impl Player { // TODO: allow plugins to access this log::debug!( "Received cookie_response[play]: key: \"{}\", has_payload: \"{}\", payload_length: \"{}\"", - packet.key, + packet.key.to_string(), packet.has_payload, packet.payload_length.unwrap_or(VarInt::from(0)).0 ); diff --git a/pumpkin/src/command/commands/cmd_transfer.rs b/pumpkin/src/command/commands/cmd_transfer.rs index 1af9ae3c..7660b808 100644 --- a/pumpkin/src/command/commands/cmd_transfer.rs +++ b/pumpkin/src/command/commands/cmd_transfer.rs @@ -2,7 +2,7 @@ use async_trait::async_trait; use pumpkin_core::text::color::{Color, NamedColor}; use pumpkin_core::text::TextComponent; use pumpkin_protocol::client::play::CTransfer; -use pumpkin_protocol::VarInt; +use pumpkin_protocol::codec::var_int::VarInt; use crate::command::args::arg_bounded_num::BoundedNumArgumentConsumer; use crate::command::args::arg_players::PlayersArgumentConsumer; diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 92544ccb..649144a7 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -12,7 +12,7 @@ use pumpkin_core::math::{ use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, EntityId}; use pumpkin_protocol::{ client::play::{CSetEntityMetadata, CTeleportEntity, Metadata}, - VarInt, + codec::var_int::VarInt, }; use crate::world::World; diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 31ef1131..9f0a8e07 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -24,7 +24,6 @@ use pumpkin_core::{ use pumpkin_entity::{entity_type::EntityType, EntityId}; use pumpkin_inventory::player::PlayerInventory; use pumpkin_macros::sound; -use pumpkin_protocol::client::play::CUpdateTime; use pumpkin_protocol::server::play::{ SCloseContainer, SCookieResponse as SPCookieResponse, SPlayPingRequest, }; @@ -41,15 +40,19 @@ use pumpkin_protocol::{ SPlayerCommand, SPlayerInput, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUseItem, SUseItemOn, }, - RawPacket, ServerPacket, SoundCategory, VarInt, + RawPacket, ServerPacket, SoundCategory, }; +use pumpkin_protocol::{client::play::CUpdateTime, codec::var_int::VarInt}; use pumpkin_protocol::{ client::play::{CSetEntityMetadata, Metadata}, server::play::{SClickContainer, SKeepAlive}, }; use pumpkin_world::{ cylindrical_chunk_iterator::Cylindrical, - item::{item_registry::get_item_by_id, ItemStack}, + item::{ + item_registry::{get_item_by_id, Operation}, + ItemStack, + }, }; use tokio::sync::{Mutex, Notify}; @@ -257,7 +260,7 @@ impl Player { // TODO: this should be cached in memory if let Some(modifiers) = &item.components.attribute_modifiers { for item_mod in &modifiers.modifiers { - if item_mod.operation == "add_value" { + if item_mod.operation == Operation::AddValue { if item_mod.id == "minecraft:base_attack_damage" { add_damage = item_mod.amount; } diff --git a/pumpkin/src/server/connection_cache.rs b/pumpkin/src/server/connection_cache.rs index 4171ea13..618fdac3 100644 --- a/pumpkin/src/server/connection_cache.rs +++ b/pumpkin/src/server/connection_cache.rs @@ -10,7 +10,8 @@ use base64::{engine::general_purpose, Engine as _}; use pumpkin_config::{BasicConfiguration, BASIC_CONFIG}; use pumpkin_protocol::{ client::{config::CPluginMessage, status::CStatusResponse}, - Players, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL, + codec::{var_int::VarInt, Codec}, + Players, StatusResponse, Version, CURRENT_MC_PROTOCOL, }; use super::CURRENT_MC_VERSION; diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 36ae0dde..33dd0c27 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -71,8 +71,6 @@ impl Server { #[allow(clippy::new_without_default)] #[must_use] pub fn new() -> Self { - // TODO: only create when needed - let auth_client = BASIC_CONFIG.online_mode.then(|| { reqwest::Client::builder() .timeout(Duration::from_millis(5000)) diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index dcfab4b9..9380f1bb 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -15,7 +15,10 @@ use pumpkin_core::math::vector2::Vector2; use pumpkin_core::math::{position::WorldPosition, vector3::Vector3}; use pumpkin_core::text::{color::NamedColor, TextComponent}; use pumpkin_entity::{entity_type::EntityType, EntityId}; -use pumpkin_protocol::client::play::CLevelEvent; +use pumpkin_protocol::{ + client::play::CLevelEvent, + codec::{identifier::Identifier, var_int::VarInt}, +}; use pumpkin_protocol::{ client::play::{CBlockUpdate, CRespawn, CSoundEffect, CWorldEvent}, SoundCategory, @@ -25,7 +28,7 @@ use pumpkin_protocol::{ CChunkData, CGameEvent, CLogin, CPlayerInfoUpdate, CRemoveEntities, CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, GameEvent, Metadata, PlayerAction, }, - ClientPacket, VarInt, + ClientPacket, }; use pumpkin_registry::DimensionType; use pumpkin_world::chunk::ChunkData; @@ -223,7 +226,8 @@ impl World { server: &Server, ) { let command_dispatcher = &server.command_dispatcher; - let dimensions: Vec<&str> = server.dimensions.iter().map(DimensionType::name).collect(); + let dimensions: Vec = + server.dimensions.iter().map(DimensionType::name).collect(); // This code follows the vanilla packet order let entity_id = player.entity_id(); diff --git a/pumpkin/src/world/scoreboard.rs b/pumpkin/src/world/scoreboard.rs index 61279eed..16a3e329 100644 --- a/pumpkin/src/world/scoreboard.rs +++ b/pumpkin/src/world/scoreboard.rs @@ -3,7 +3,8 @@ use std::collections::HashMap; use pumpkin_core::text::TextComponent; use pumpkin_protocol::{ client::play::{CDisplayObjective, CUpdateObjectives, CUpdateScore, RenderType}, - NumberFormat, VarInt, + codec::var_int::VarInt, + NumberFormat, }; use super::World; From fd667901cdeb16ace017bc5a584df4b14f083274 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Sat, 21 Dec 2024 17:09:27 +0100 Subject: [PATCH 03/14] add codecs folder to git --- pumpkin-protocol/src/codec/bit_set.rs | 53 ++++++++ pumpkin-protocol/src/codec/identifier.rs | 101 +++++++++++++++ pumpkin-protocol/src/codec/mod.rs | 27 ++++ pumpkin-protocol/src/codec/var_int.rs | 157 ++++++++++++++++++++++ pumpkin-protocol/src/codec/var_long.rs | 158 +++++++++++++++++++++++ 5 files changed, 496 insertions(+) create mode 100644 pumpkin-protocol/src/codec/bit_set.rs create mode 100644 pumpkin-protocol/src/codec/identifier.rs create mode 100644 pumpkin-protocol/src/codec/mod.rs create mode 100644 pumpkin-protocol/src/codec/var_int.rs create mode 100644 pumpkin-protocol/src/codec/var_long.rs diff --git a/pumpkin-protocol/src/codec/bit_set.rs b/pumpkin-protocol/src/codec/bit_set.rs new file mode 100644 index 00000000..27aa329d --- /dev/null +++ b/pumpkin-protocol/src/codec/bit_set.rs @@ -0,0 +1,53 @@ +use std::num::NonZeroUsize; + +use bytes::{Buf, BufMut}; +use serde::{Serialize, Serializer}; + +use crate::bytebuf::ByteBuf; +use crate::bytebuf::ByteBufMut; + +use super::{var_int::VarInt, Codec, DecodeError}; + +pub struct BitSet(pub VarInt, pub Vec); + +impl Codec for BitSet { + /// The maximum size of the BitSet is `remaining / 8`. + const MAX_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(usize::MAX) }; + + fn written_size(&self) -> usize { + todo!() + } + + fn encode(&self, write: &mut impl BufMut) { + write.put_var_int(&self.0); + for b in &self.1 { + write.put_i64(*b); + } + } + + fn decode(read: &mut impl Buf) -> Result { + // read length + let length = read + .try_get_var_int() + .map_err(|_| DecodeError::Incomplete)?; + // vanilla uses remaining / 8 + if length.0 as usize >= read.remaining() / 8 { + return Err(DecodeError::TooLarge); + } + let mut array: Vec = Vec::with_capacity(size_of::() * length.0 as usize); + for _ in 0..length.0 { + let long = read.try_get_i64().map_err(|_| DecodeError::Incomplete)?; + array.push(long); + } + Ok(BitSet(length, array)) + } +} + +impl Serialize for BitSet { + fn serialize(&self, _serializer: S) -> Result + where + S: Serializer, + { + todo!() + } +} diff --git a/pumpkin-protocol/src/codec/identifier.rs b/pumpkin-protocol/src/codec/identifier.rs new file mode 100644 index 00000000..6be5675c --- /dev/null +++ b/pumpkin-protocol/src/codec/identifier.rs @@ -0,0 +1,101 @@ +use std::num::NonZeroUsize; + +use bytes::{Buf, BufMut}; +use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; + +use crate::bytebuf::{ByteBuf, ByteBufMut}; + +use super::{Codec, DecodeError}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Identifier { + pub namespace: String, + pub path: String, +} + +impl Identifier { + pub fn vanilla(path: &str) -> Self { + Self { + namespace: "minecraft".to_string(), + path: path.to_string(), + } + } +} +impl Codec for Identifier { + /// The maximum number of bytes a `Identifer` is the same as for a normal String. + const MAX_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(i16::MAX as usize) }; + + fn written_size(&self) -> usize { + todo!() + } + + fn encode(&self, write: &mut impl BufMut) { + write.put_string_len(&self.to_string(), Self::MAX_SIZE.get()); + } + + fn decode(read: &mut impl Buf) -> Result { + let identifer = read + .try_get_string_len(Self::MAX_SIZE.get()) + .map_err(|_| DecodeError::Incomplete)?; + match identifer.split_once(":") { + Some((namespace, path)) => Ok(Identifier { + namespace: namespace.to_string(), + path: path.to_string(), + }), + None => Err(DecodeError::Incomplete), + } + } +} + +impl Serialize for Identifier { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Identifier { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct IdentifierVisitor; + + impl Visitor<'_> for IdentifierVisitor { + type Value = Identifier; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a valid Identifier (namespace:path)") + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + self.visit_str(&v) + } + + fn visit_str(self, identifer: &str) -> Result + where + E: serde::de::Error, + { + match identifer.split_once(":") { + Some((namespace, path)) => Ok(Identifier { + namespace: namespace.to_string(), + path: path.to_string(), + }), + None => Err(serde::de::Error::custom("Identifier can't be split")), + } + } + } + deserializer.deserialize_str(IdentifierVisitor) + } +} + +impl std::fmt::Display for Identifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{}", self.namespace, self.path) + } +} diff --git a/pumpkin-protocol/src/codec/mod.rs b/pumpkin-protocol/src/codec/mod.rs new file mode 100644 index 00000000..3af01e21 --- /dev/null +++ b/pumpkin-protocol/src/codec/mod.rs @@ -0,0 +1,27 @@ +use std::num::NonZeroUsize; + +use bytes::{Buf, BufMut}; +use thiserror::Error; + +pub mod bit_set; +pub mod identifier; +pub mod var_int; +pub mod var_long; + +pub trait Codec { + const MAX_SIZE: NonZeroUsize; + + fn written_size(&self) -> usize; + + fn encode(&self, write: &mut impl BufMut); + + fn decode(read: &mut impl Buf) -> Result; +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Error)] +pub enum DecodeError { + #[error("Incomplete VarInt decode")] + Incomplete, + #[error("VarInt is too large")] + TooLarge, +} diff --git a/pumpkin-protocol/src/codec/var_int.rs b/pumpkin-protocol/src/codec/var_int.rs new file mode 100644 index 00000000..4f502687 --- /dev/null +++ b/pumpkin-protocol/src/codec/var_int.rs @@ -0,0 +1,157 @@ +use std::{num::NonZeroUsize, ops::Deref}; + +use super::{Codec, DecodeError}; +use bytes::{Buf, BufMut}; +use serde::{ + de::{SeqAccess, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; + +pub type VarIntType = i32; + +/** + * A variable-length integer type used by the Minecraft network protocol. + */ +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VarInt(pub VarIntType); + +impl Codec for VarInt { + /// The maximum number of bytes a `VarInt` can occupy. + const MAX_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(5) }; + + /// Returns the exact number of bytes this varint will write when + /// [`Encode::encode`] is called, assuming no error occurs. + fn written_size(&self) -> usize { + match self.0 { + 0 => 1, + n => (31 - n.leading_zeros() as usize) / 7 + 1, + } + } + + fn encode(&self, write: &mut impl BufMut) { + let mut val = self.0; + for _ in 0..Self::MAX_SIZE.get() { + let b: u8 = val as u8 & 0b01111111; + val >>= 7; + write.put_u8(if val == 0 { b } else { b | 0b10000000 }); + if val == 0 { + break; + } + } + } + + fn decode(read: &mut impl Buf) -> Result { + let mut val = 0; + for i in 0..Self::MAX_SIZE.get() { + if !read.has_remaining() { + return Err(DecodeError::Incomplete); + } + let byte = read.get_u8(); + val |= (i32::from(byte) & 0x7F) << (i * 7); + if byte & 0x80 == 0 { + return Ok(VarInt(val)); + } + } + Err(DecodeError::TooLarge) + } +} + +impl From for VarInt { + fn from(value: i32) -> Self { + VarInt(value) + } +} + +impl From for VarInt { + fn from(value: u32) -> Self { + VarInt(value as i32) + } +} + +impl From for VarInt { + fn from(value: u8) -> Self { + VarInt(value as i32) + } +} + +impl From for VarInt { + fn from(value: usize) -> Self { + VarInt(value as i32) + } +} + +impl From for i32 { + fn from(value: VarInt) -> Self { + value.0 + } +} + +impl AsRef for VarInt { + fn as_ref(&self) -> &i32 { + &self.0 + } +} + +impl Deref for VarInt { + type Target = i32; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Serialize for VarInt { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut value = self.0 as u32; + let mut buf = Vec::new(); + + while value > 0x7F { + buf.put_u8(value as u8 | 0x80); + value >>= 7; + } + + buf.put_u8(value as u8); + + serializer.serialize_bytes(&buf) + } +} + +impl<'de> Deserialize<'de> for VarInt { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct VarIntVisitor; + + impl<'de> Visitor<'de> for VarIntVisitor { + type Value = VarInt; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a valid VarInt encoded in a byte sequence") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut val = 0; + for i in 0..VarInt::MAX_SIZE.get() { + if let Some(byte) = seq.next_element::()? { + val |= (i32::from(byte) & 0b01111111) << (i * 7); + if byte & 0b10000000 == 0 { + return Ok(VarInt(val)); + } + } else { + break; + } + } + Err(serde::de::Error::custom("VarInt was too large")) + } + } + + deserializer.deserialize_seq(VarIntVisitor) + } +} diff --git a/pumpkin-protocol/src/codec/var_long.rs b/pumpkin-protocol/src/codec/var_long.rs new file mode 100644 index 00000000..ceb2c1c9 --- /dev/null +++ b/pumpkin-protocol/src/codec/var_long.rs @@ -0,0 +1,158 @@ +use std::{num::NonZeroUsize, ops::Deref}; + +use super::{Codec, DecodeError}; +use bytes::{Buf, BufMut}; +use serde::{ + de::{self, SeqAccess, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; + +pub type VarLongType = i64; + +/** + * A variable-length long type used by the Minecraft network protocol. + */ +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VarLong(pub VarLongType); + +impl Codec for VarLong { + /// The maximum number of bytes a `VarLong` can occupy. + const MAX_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(10) }; + + /// Returns the exact number of bytes this varlong will write when + /// [`Encode::encode`] is called, assuming no error occurs. + fn written_size(&self) -> usize { + match self.0 { + 0 => 1, + n => (31 - n.leading_zeros() as usize) / 7 + 1, + } + } + + fn encode(&self, write: &mut impl BufMut) { + let mut x = self.0; + for _ in 0..Self::MAX_SIZE.get() { + let byte = (x & 0x7F) as u8; + x >>= 7; + if x == 0 { + write.put_slice(&[byte]); + break; + } + write.put_slice(&[byte | 0x80]); + } + } + + fn decode(read: &mut impl Buf) -> Result { + let mut val = 0; + for i in 0..Self::MAX_SIZE.get() { + if !read.has_remaining() { + return Err(DecodeError::Incomplete); + } + let byte = read.get_u8(); + val |= (i64::from(byte) & 0b01111111) << (i * 7); + if byte & 0b10000000 == 0 { + return Ok(VarLong(val)); + } + } + Err(DecodeError::TooLarge) + } +} + +impl From for VarLong { + fn from(value: i64) -> Self { + VarLong(value) + } +} + +impl From for VarLong { + fn from(value: u32) -> Self { + VarLong(value as i64) + } +} + +impl From for VarLong { + fn from(value: u8) -> Self { + VarLong(value as i64) + } +} + +impl From for VarLong { + fn from(value: usize) -> Self { + VarLong(value as i64) + } +} + +impl From for i64 { + fn from(value: VarLong) -> Self { + value.0 + } +} + +impl AsRef for VarLong { + fn as_ref(&self) -> &i64 { + &self.0 + } +} + +impl Deref for VarLong { + type Target = i64; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Serialize for VarLong { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut value = self.0 as u64; + let mut buf = Vec::new(); + + while value > 0x7F { + buf.put_u8(value as u8 | 0x80); + value >>= 7; + } + + buf.put_u8(value as u8); + + serializer.serialize_bytes(&buf) + } +} + +impl<'de> Deserialize<'de> for VarLong { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct VarLongVisitor; + + impl<'de> Visitor<'de> for VarLongVisitor { + type Value = VarLong; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a valid VarInt encoded in a byte sequence") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut val = 0; + for i in 0..VarLong::MAX_SIZE.get() { + if let Some(byte) = seq.next_element::()? { + val |= (i64::from(byte) & 0b01111111) << (i * 7); + if byte & 0b10000000 == 0 { + return Ok(VarLong(val)); + } + } else { + break; + } + } + Err(de::Error::custom("VarInt was too large")) + } + } + + deserializer.deserialize_seq(VarLongVisitor) + } +} From 3dae300f818af81d44eb64f9d688f110ce846fa3 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Tue, 24 Dec 2024 00:56:19 +0100 Subject: [PATCH 04/14] Some refactors --- pumpkin-protocol/Cargo.toml | 7 + .../client/play/c_set_container_content.rs | 2 +- .../src/client/play/c_set_container_slot.rs | 2 +- pumpkin-protocol/src/codec/mod.rs | 1 + pumpkin-protocol/src/{ => codec}/slot.rs | 0 pumpkin-protocol/src/lib.rs | 4 +- pumpkin-protocol/src/packet_decoder.rs | 4 +- pumpkin-protocol/src/packet_encoder.rs | 4 +- .../src/server/config/s_cookie_response.rs | 4 +- .../src/server/login/s_cookie_response.rs | 4 +- .../src/server/play/s_click_container.rs | 2 +- .../src/server/play/s_set_creative_slot.rs | 2 +- .../aquifer_sampler.rs | 2 +- .../{world_gen => generation}/blender/mod.rs | 0 .../{world_gen => generation}/chunk_noise.rs | 6 +- .../generation_shapes.rs | 0 .../{world_gen => generation}/generator.rs | 2 +- .../generic_generator.rs | 0 .../{world_gen => generation}/height_limit.rs | 0 .../implementation/mod.rs | 0 .../implementation/overworld/biome/mod.rs | 0 .../implementation/overworld/biome/plains.rs | 2 +- .../implementation/overworld/mod.rs | 0 .../implementation/superflat.rs | 2 +- .../implementation/test.rs | 2 +- .../src/{world_gen => generation}/mod.rs | 0 .../{world_gen => generation}/noise/config.rs | 4 +- .../noise/density/basic.rs | 6 +- .../noise/density/blend.rs | 2 +- .../noise/density/component_functions.rs | 2 +- .../noise/density/end.rs | 2 +- .../noise/density/math.rs | 0 .../noise/density/mod.rs | 8 +- .../noise/density/noise.rs | 4 +- .../noise/density/offset.rs | 0 .../noise/density/spline.rs | 4 +- .../noise/density/terrain_helpers.rs | 6 +- .../noise/density/unary.rs | 0 .../noise/density/weird.rs | 0 .../{world_gen => generation}/noise/mod.rs | 0 .../{world_gen => generation}/noise/perlin.rs | 4 +- .../{world_gen => generation}/noise/router.rs | 6 +- .../noise/simplex.rs | 4 +- .../{world_gen => generation}/ore_sampler.rs | 0 .../{world_gen => generation}/positions.rs | 0 .../{world_gen => generation}/proto_chunk.rs | 2 +- .../src/{world_gen => generation}/seed.rs | 0 pumpkin-world/src/level.rs | 2 +- pumpkin-world/src/lib.rs | 6 +- pumpkin/src/entity/player.rs | 5 +- pumpkin/src/main.rs | 9 +- pumpkin/src/{client => net}/authentication.rs | 26 +-- pumpkin/src/{client => net}/combat.rs | 0 pumpkin/src/{client => net}/container.rs | 2 +- pumpkin/src/{ => net}/lan_broadcast.rs | 0 pumpkin/src/{client => net}/mod.rs | 56 +++-- pumpkin/src/net/packet/config.rs | 98 +++++++++ pumpkin/src/net/packet/handshake.rs | 29 +++ .../client_packet.rs => net/packet/login.rs} | 201 ++++-------------- pumpkin/src/net/packet/mod.rs | 9 + .../player_packet.rs => net/packet/play.rs} | 2 +- pumpkin/src/net/packet/status.rs | 18 ++ pumpkin/src/{ => net}/proxy/bungeecord.rs | 34 +-- pumpkin/src/{ => net}/proxy/mod.rs | 0 pumpkin/src/{ => net}/proxy/velocity.rs | 2 +- pumpkin/src/{ => net}/query.rs | 0 pumpkin/src/{ => net}/rcon/mod.rs | 1 - pumpkin/src/{ => net}/rcon/packet.rs | 0 pumpkin/src/server/connection_cache.rs | 2 + pumpkin/src/server/key_store.rs | 3 +- pumpkin/src/server/mod.rs | 4 +- pumpkin/src/world/worldborder.rs | 2 +- 72 files changed, 333 insertions(+), 284 deletions(-) rename pumpkin-protocol/src/{ => codec}/slot.rs (100%) rename pumpkin-world/src/{world_gen => generation}/aquifer_sampler.rs (99%) rename pumpkin-world/src/{world_gen => generation}/blender/mod.rs (100%) rename pumpkin-world/src/{world_gen => generation}/chunk_noise.rs (99%) rename pumpkin-world/src/{world_gen => generation}/generation_shapes.rs (100%) rename pumpkin-world/src/{world_gen => generation}/generator.rs (97%) rename pumpkin-world/src/{world_gen => generation}/generic_generator.rs (100%) rename pumpkin-world/src/{world_gen => generation}/height_limit.rs (100%) rename pumpkin-world/src/{world_gen => generation}/implementation/mod.rs (100%) rename pumpkin-world/src/{world_gen => generation}/implementation/overworld/biome/mod.rs (100%) rename pumpkin-world/src/{world_gen => generation}/implementation/overworld/biome/plains.rs (99%) rename pumpkin-world/src/{world_gen => generation}/implementation/overworld/mod.rs (100%) rename pumpkin-world/src/{world_gen => generation}/implementation/superflat.rs (98%) rename pumpkin-world/src/{world_gen => generation}/implementation/test.rs (99%) rename pumpkin-world/src/{world_gen => generation}/mod.rs (100%) rename pumpkin-world/src/{world_gen => generation}/noise/config.rs (99%) rename pumpkin-world/src/{world_gen => generation}/noise/density/basic.rs (99%) rename pumpkin-world/src/{world_gen => generation}/noise/density/blend.rs (99%) rename pumpkin-world/src/{world_gen => generation}/noise/density/component_functions.rs (99%) rename pumpkin-world/src/{world_gen => generation}/noise/density/end.rs (97%) rename pumpkin-world/src/{world_gen => generation}/noise/density/math.rs (100%) rename pumpkin-world/src/{world_gen => generation}/noise/density/mod.rs (99%) rename pumpkin-world/src/{world_gen => generation}/noise/density/noise.rs (99%) rename pumpkin-world/src/{world_gen => generation}/noise/density/offset.rs (100%) rename pumpkin-world/src/{world_gen => generation}/noise/density/spline.rs (99%) rename pumpkin-world/src/{world_gen => generation}/noise/density/terrain_helpers.rs (99%) rename pumpkin-world/src/{world_gen => generation}/noise/density/unary.rs (100%) rename pumpkin-world/src/{world_gen => generation}/noise/density/weird.rs (100%) rename pumpkin-world/src/{world_gen => generation}/noise/mod.rs (100%) rename pumpkin-world/src/{world_gen => generation}/noise/perlin.rs (99%) rename pumpkin-world/src/{world_gen => generation}/noise/router.rs (99%) rename pumpkin-world/src/{world_gen => generation}/noise/simplex.rs (99%) rename pumpkin-world/src/{world_gen => generation}/ore_sampler.rs (100%) rename pumpkin-world/src/{world_gen => generation}/positions.rs (100%) rename pumpkin-world/src/{world_gen => generation}/proto_chunk.rs (99%) rename pumpkin-world/src/{world_gen => generation}/seed.rs (100%) rename pumpkin/src/{client => net}/authentication.rs (85%) rename pumpkin/src/{client => net}/combat.rs (100%) rename pumpkin/src/{client => net}/container.rs (99%) rename pumpkin/src/{ => net}/lan_broadcast.rs (100%) rename pumpkin/src/{client => net}/mod.rs (94%) create mode 100644 pumpkin/src/net/packet/config.rs create mode 100644 pumpkin/src/net/packet/handshake.rs rename pumpkin/src/{client/client_packet.rs => net/packet/login.rs} (63%) create mode 100644 pumpkin/src/net/packet/mod.rs rename pumpkin/src/{client/player_packet.rs => net/packet/play.rs} (99%) create mode 100644 pumpkin/src/net/packet/status.rs rename pumpkin/src/{ => net}/proxy/bungeecord.rs (59%) rename pumpkin/src/{ => net}/proxy/mod.rs (100%) rename pumpkin/src/{ => net}/proxy/velocity.rs (98%) rename pumpkin/src/{ => net}/query.rs (100%) rename pumpkin/src/{ => net}/rcon/mod.rs (98%) rename pumpkin/src/{ => net}/rcon/packet.rs (100%) diff --git a/pumpkin-protocol/Cargo.toml b/pumpkin-protocol/Cargo.toml index 3e417ecb..674b290e 100644 --- a/pumpkin-protocol/Cargo.toml +++ b/pumpkin-protocol/Cargo.toml @@ -3,6 +3,13 @@ name = "pumpkin-protocol" version.workspace = true edition.workspace = true +[features] +default = ["packets", "query"] +packets = ["serverbound", "clientbound"] +serverbound = [] +clientbound = [] +query = [] + [dependencies] pumpkin-nbt = { path = "../pumpkin-nbt" } pumpkin-config = { path = "../pumpkin-config" } diff --git a/pumpkin-protocol/src/client/play/c_set_container_content.rs b/pumpkin-protocol/src/client/play/c_set_container_content.rs index 38e6b39a..275739f2 100644 --- a/pumpkin-protocol/src/client/play/c_set_container_content.rs +++ b/pumpkin-protocol/src/client/play/c_set_container_content.rs @@ -1,4 +1,4 @@ -use crate::slot::Slot; +use crate::codec::slot::Slot; use crate::VarInt; use pumpkin_macros::client_packet; diff --git a/pumpkin-protocol/src/client/play/c_set_container_slot.rs b/pumpkin-protocol/src/client/play/c_set_container_slot.rs index 19e6b484..e632da4e 100644 --- a/pumpkin-protocol/src/client/play/c_set_container_slot.rs +++ b/pumpkin-protocol/src/client/play/c_set_container_slot.rs @@ -1,4 +1,4 @@ -use crate::slot::Slot; +use crate::codec::slot::Slot; use crate::VarInt; use pumpkin_macros::client_packet; diff --git a/pumpkin-protocol/src/codec/mod.rs b/pumpkin-protocol/src/codec/mod.rs index 3af01e21..57af2e19 100644 --- a/pumpkin-protocol/src/codec/mod.rs +++ b/pumpkin-protocol/src/codec/mod.rs @@ -5,6 +5,7 @@ use thiserror::Error; pub mod bit_set; pub mod identifier; +pub mod slot; pub mod var_int; pub mod var_long; diff --git a/pumpkin-protocol/src/slot.rs b/pumpkin-protocol/src/codec/slot.rs similarity index 100% rename from pumpkin-protocol/src/slot.rs rename to pumpkin-protocol/src/codec/slot.rs diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index 85a5311e..c6156b94 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -7,13 +7,15 @@ use pumpkin_core::text::{style::Style, TextComponent}; use serde::{Deserialize, Serialize}; pub mod bytebuf; +#[cfg(feature = "clientbound")] pub mod client; pub mod codec; pub mod packet_decoder; pub mod packet_encoder; +#[cfg(feature = "query")] pub mod query; +#[cfg(feature = "serverbound")] pub mod server; -pub mod slot; /// To current Minecraft protocol /// Don't forget to change this when porting diff --git a/pumpkin-protocol/src/packet_decoder.rs b/pumpkin-protocol/src/packet_decoder.rs index 8c267f94..5059a2c5 100644 --- a/pumpkin-protocol/src/packet_decoder.rs +++ b/pumpkin-protocol/src/packet_decoder.rs @@ -16,8 +16,8 @@ type Cipher = cfb8::Decryptor; pub struct PacketDecoder { buf: BytesMut, decompress_buf: BytesMut, - compression: bool, cipher: Option, + compression: bool, decompressor: Decompressor, } @@ -28,8 +28,8 @@ impl Default for PacketDecoder { Self { buf: BytesMut::new(), decompress_buf: BytesMut::new(), - compression: false, cipher: None, + compression: false, decompressor: Decompressor::new(), } } diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index d8bea1b7..1636d56e 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -15,8 +15,8 @@ type Cipher = cfb8::Encryptor; pub struct PacketEncoder { buf: BytesMut, compress_buf: Vec, - compression_threshold: Option, cipher: Option, + compression_threshold: Option, compressor: Compressor, // Reuse the compressor for all packets } @@ -27,8 +27,8 @@ impl Default for PacketEncoder { Self { buf: BytesMut::with_capacity(1024), compress_buf: Vec::with_capacity(1024), - compression_threshold: None, cipher: None, + compression_threshold: None, compressor: Compressor::new(CompressionLvl::fastest()), // init compressor with fastest compression level } } diff --git a/pumpkin-protocol/src/server/config/s_cookie_response.rs b/pumpkin-protocol/src/server/config/s_cookie_response.rs index 0a2fc03d..b9e3a0a9 100644 --- a/pumpkin-protocol/src/server/config/s_cookie_response.rs +++ b/pumpkin-protocol/src/server/config/s_cookie_response.rs @@ -11,7 +11,7 @@ use crate::{ #[server_packet("config:cookie_response")] /// Response to a Cookie Request (configuration) from the server. /// The Notchian (vanilla) server only accepts responses of up to 5 kiB in size. -pub struct SCookieResponse { +pub struct SConfigCookieResponse { pub key: Identifier, pub has_payload: bool, pub payload_length: Option, @@ -20,7 +20,7 @@ pub struct SCookieResponse { const MAX_PAYLOAD_SIZE: i32 = 5120; -impl ServerPacket for SCookieResponse { +impl ServerPacket for SConfigCookieResponse { fn read(bytebuf: &mut impl Buf) -> Result { let key = bytebuf.try_get_identifer()?; let has_payload = bytebuf.try_get_bool()?; diff --git a/pumpkin-protocol/src/server/login/s_cookie_response.rs b/pumpkin-protocol/src/server/login/s_cookie_response.rs index 82ed37f0..5e498a1b 100644 --- a/pumpkin-protocol/src/server/login/s_cookie_response.rs +++ b/pumpkin-protocol/src/server/login/s_cookie_response.rs @@ -10,7 +10,7 @@ use serde::de; #[server_packet("login:cookie_response")] /// Response to a Cookie Request (login) from the server. /// The Notchian server only accepts responses of up to 5 kiB in size. -pub struct SCookieResponse { +pub struct SLoginCookieResponse { pub key: Identifier, pub has_payload: bool, pub payload_length: Option, @@ -19,7 +19,7 @@ pub struct SCookieResponse { const MAX_PAYLOAD_SIZE: i32 = 5120; -impl ServerPacket for SCookieResponse { +impl ServerPacket for SLoginCookieResponse { fn read(bytebuf: &mut impl Buf) -> Result { let key = bytebuf.try_get_identifer()?; let has_payload = bytebuf.try_get_bool()?; diff --git a/pumpkin-protocol/src/server/play/s_click_container.rs b/pumpkin-protocol/src/server/play/s_click_container.rs index b81e366c..6dcf4abd 100644 --- a/pumpkin-protocol/src/server/play/s_click_container.rs +++ b/pumpkin-protocol/src/server/play/s_click_container.rs @@ -1,4 +1,4 @@ -use crate::slot::Slot; +use crate::codec::slot::Slot; use crate::VarInt; use num_derive::FromPrimitive; use num_traits::FromPrimitive; diff --git a/pumpkin-protocol/src/server/play/s_set_creative_slot.rs b/pumpkin-protocol/src/server/play/s_set_creative_slot.rs index 0dff80b4..59835a43 100644 --- a/pumpkin-protocol/src/server/play/s_set_creative_slot.rs +++ b/pumpkin-protocol/src/server/play/s_set_creative_slot.rs @@ -1,6 +1,6 @@ use pumpkin_macros::server_packet; -use crate::slot::Slot; +use crate::codec::slot::Slot; #[derive(serde::Deserialize, Debug)] #[server_packet("play:set_creative_mode_slot")] diff --git a/pumpkin-world/src/world_gen/aquifer_sampler.rs b/pumpkin-world/src/generation/aquifer_sampler.rs similarity index 99% rename from pumpkin-world/src/world_gen/aquifer_sampler.rs rename to pumpkin-world/src/generation/aquifer_sampler.rs index ebe4d6ac..8dfb4646 100644 --- a/pumpkin-world/src/world_gen/aquifer_sampler.rs +++ b/pumpkin-world/src/generation/aquifer_sampler.rs @@ -661,7 +661,7 @@ mod test { use crate::{ block::BlockState, - world_gen::{ + generation::{ chunk_noise::{ BlockStateSampler, ChunkNoiseDensityFunctions, ChunkNoiseGenerator, ChunkNoiseState, LAVA_BLOCK, WATER_BLOCK, diff --git a/pumpkin-world/src/world_gen/blender/mod.rs b/pumpkin-world/src/generation/blender/mod.rs similarity index 100% rename from pumpkin-world/src/world_gen/blender/mod.rs rename to pumpkin-world/src/generation/blender/mod.rs diff --git a/pumpkin-world/src/world_gen/chunk_noise.rs b/pumpkin-world/src/generation/chunk_noise.rs similarity index 99% rename from pumpkin-world/src/world_gen/chunk_noise.rs rename to pumpkin-world/src/generation/chunk_noise.rs index 5ff2bb27..cab1abf0 100644 --- a/pumpkin-world/src/world_gen/chunk_noise.rs +++ b/pumpkin-world/src/generation/chunk_noise.rs @@ -6,11 +6,11 @@ use pumpkin_macros::block_state; use crate::{ block::BlockState, - match_ref_implementations, - world_gen::{ + generation::{ noise::{density::basic::WrapperType, lerp3}, section_coords, }, + match_ref_implementations, }; use super::{ @@ -1379,7 +1379,7 @@ impl ChunkNoiseGenerator { mod test { use pumpkin_core::math::vector2::Vector2; - use crate::world_gen::{ + use crate::generation::{ aquifer_sampler::{FluidLevel, FluidLevelSampler}, generation_shapes::GenerationShape, noise::{config::NoiseConfig, router::OVERWORLD_NOISE_ROUTER}, diff --git a/pumpkin-world/src/world_gen/generation_shapes.rs b/pumpkin-world/src/generation/generation_shapes.rs similarity index 100% rename from pumpkin-world/src/world_gen/generation_shapes.rs rename to pumpkin-world/src/generation/generation_shapes.rs diff --git a/pumpkin-world/src/world_gen/generator.rs b/pumpkin-world/src/generation/generator.rs similarity index 97% rename from pumpkin-world/src/world_gen/generator.rs rename to pumpkin-world/src/generation/generator.rs index 8a0120c7..a570c8c3 100644 --- a/pumpkin-world/src/world_gen/generator.rs +++ b/pumpkin-world/src/generation/generator.rs @@ -6,7 +6,7 @@ use crate::biome::Biome; use crate::block::block_state::BlockState; use crate::chunk::{ChunkBlocks, ChunkData}; use crate::coordinates::{BlockCoordinates, ChunkRelativeBlockCoordinates, XZBlockCoordinates}; -use crate::world_gen::Seed; +use crate::generation::Seed; pub trait GeneratorInit { fn new(seed: Seed) -> Self; diff --git a/pumpkin-world/src/world_gen/generic_generator.rs b/pumpkin-world/src/generation/generic_generator.rs similarity index 100% rename from pumpkin-world/src/world_gen/generic_generator.rs rename to pumpkin-world/src/generation/generic_generator.rs diff --git a/pumpkin-world/src/world_gen/height_limit.rs b/pumpkin-world/src/generation/height_limit.rs similarity index 100% rename from pumpkin-world/src/world_gen/height_limit.rs rename to pumpkin-world/src/generation/height_limit.rs diff --git a/pumpkin-world/src/world_gen/implementation/mod.rs b/pumpkin-world/src/generation/implementation/mod.rs similarity index 100% rename from pumpkin-world/src/world_gen/implementation/mod.rs rename to pumpkin-world/src/generation/implementation/mod.rs diff --git a/pumpkin-world/src/world_gen/implementation/overworld/biome/mod.rs b/pumpkin-world/src/generation/implementation/overworld/biome/mod.rs similarity index 100% rename from pumpkin-world/src/world_gen/implementation/overworld/biome/mod.rs rename to pumpkin-world/src/generation/implementation/overworld/biome/mod.rs diff --git a/pumpkin-world/src/world_gen/implementation/overworld/biome/plains.rs b/pumpkin-world/src/generation/implementation/overworld/biome/plains.rs similarity index 99% rename from pumpkin-world/src/world_gen/implementation/overworld/biome/plains.rs rename to pumpkin-world/src/generation/implementation/overworld/biome/plains.rs index dde27c9c..775d3e5f 100644 --- a/pumpkin-world/src/world_gen/implementation/overworld/biome/plains.rs +++ b/pumpkin-world/src/generation/implementation/overworld/biome/plains.rs @@ -7,7 +7,7 @@ use crate::{ biome::Biome, chunk::ChunkBlocks, coordinates::{BlockCoordinates, ChunkRelativeBlockCoordinates, XZBlockCoordinates}, - world_gen::{ + generation::{ generator::{BiomeGenerator, GeneratorInit, PerlinTerrainGenerator}, generic_generator::GenericGenerator, Seed, diff --git a/pumpkin-world/src/world_gen/implementation/overworld/mod.rs b/pumpkin-world/src/generation/implementation/overworld/mod.rs similarity index 100% rename from pumpkin-world/src/world_gen/implementation/overworld/mod.rs rename to pumpkin-world/src/generation/implementation/overworld/mod.rs diff --git a/pumpkin-world/src/world_gen/implementation/superflat.rs b/pumpkin-world/src/generation/implementation/superflat.rs similarity index 98% rename from pumpkin-world/src/world_gen/implementation/superflat.rs rename to pumpkin-world/src/generation/implementation/superflat.rs index 2ec9fdad..4e12eba7 100644 --- a/pumpkin-world/src/world_gen/implementation/superflat.rs +++ b/pumpkin-world/src/generation/implementation/superflat.rs @@ -4,7 +4,7 @@ use crate::{ biome::Biome, block::block_state::BlockState, coordinates::XZBlockCoordinates, - world_gen::{ + generation::{ generator::{BiomeGenerator, GeneratorInit, TerrainGenerator}, generic_generator::GenericGenerator, Seed, diff --git a/pumpkin-world/src/world_gen/implementation/test.rs b/pumpkin-world/src/generation/implementation/test.rs similarity index 99% rename from pumpkin-world/src/world_gen/implementation/test.rs rename to pumpkin-world/src/generation/implementation/test.rs index c7bf6089..3a1f08f1 100644 --- a/pumpkin-world/src/world_gen/implementation/test.rs +++ b/pumpkin-world/src/generation/implementation/test.rs @@ -14,7 +14,7 @@ use crate::{ coordinates::{ ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates, XZBlockCoordinates, }, - world_gen::{ + generation::{ generator::{BiomeGenerator, GeneratorInit, TerrainGenerator}, proto_chunk::ProtoChunk, Seed, WorldGenerator, diff --git a/pumpkin-world/src/world_gen/mod.rs b/pumpkin-world/src/generation/mod.rs similarity index 100% rename from pumpkin-world/src/world_gen/mod.rs rename to pumpkin-world/src/generation/mod.rs diff --git a/pumpkin-world/src/world_gen/noise/config.rs b/pumpkin-world/src/generation/noise/config.rs similarity index 99% rename from pumpkin-world/src/world_gen/noise/config.rs rename to pumpkin-world/src/generation/noise/config.rs index 4ce194ee..a1e16a0f 100644 --- a/pumpkin-world/src/world_gen/noise/config.rs +++ b/pumpkin-world/src/generation/noise/config.rs @@ -151,8 +151,7 @@ mod test { }; use crate::{ - read_data_from_file, - world_gen::noise::{ + generation::noise::{ config::NoiseConfig, density::{ built_in_density_function::{ @@ -165,6 +164,7 @@ mod test { }, router::OVERWORLD_NOISE_ROUTER, }, + read_data_from_file, }; use super::LegacyChunkNoiseVisitor; diff --git a/pumpkin-world/src/world_gen/noise/density/basic.rs b/pumpkin-world/src/generation/noise/density/basic.rs similarity index 99% rename from pumpkin-world/src/world_gen/noise/density/basic.rs rename to pumpkin-world/src/generation/noise/density/basic.rs index cb118a4a..a96be636 100644 --- a/pumpkin-world/src/world_gen/noise/density/basic.rs +++ b/pumpkin-world/src/generation/noise/density/basic.rs @@ -1,6 +1,6 @@ use std::{hash::Hash, marker::PhantomData}; -use crate::{match_ref_implementations, world_gen::noise::clamped_map}; +use crate::{generation::noise::clamped_map, match_ref_implementations}; use super::{ component_functions::{ @@ -419,10 +419,10 @@ mod test { use std::{fs, path::Path}; use crate::{ - read_data_from_file, - world_gen::noise::density::{ + generation::noise::density::{ component_functions::ImmutableComponentFunctionImpl, NoisePos, UnblendedNoisePos, }, + read_data_from_file, }; use super::YClampedFunction; diff --git a/pumpkin-world/src/world_gen/noise/density/blend.rs b/pumpkin-world/src/generation/noise/density/blend.rs similarity index 99% rename from pumpkin-world/src/world_gen/noise/density/blend.rs rename to pumpkin-world/src/generation/noise/density/blend.rs index 309fa345..bf6bbb70 100644 --- a/pumpkin-world/src/world_gen/noise/density/blend.rs +++ b/pumpkin-world/src/generation/noise/density/blend.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use crate::world_gen::blender::BlenderImpl; +use crate::generation::blender::BlenderImpl; use super::{ component_functions::{ diff --git a/pumpkin-world/src/world_gen/noise/density/component_functions.rs b/pumpkin-world/src/generation/noise/density/component_functions.rs similarity index 99% rename from pumpkin-world/src/world_gen/noise/density/component_functions.rs rename to pumpkin-world/src/generation/noise/density/component_functions.rs index 6c47665e..036c4eba 100644 --- a/pumpkin-world/src/world_gen/noise/density/component_functions.rs +++ b/pumpkin-world/src/generation/noise/density/component_functions.rs @@ -953,7 +953,7 @@ mod test { use pumpkin_core::random::{legacy_rand::LegacyRand, RandomDeriver, RandomImpl}; - use crate::world_gen::noise::{ + use crate::generation::noise::{ built_in_noise_params, density::{ noise::{InternalNoise, NoiseFunction}, diff --git a/pumpkin-world/src/world_gen/noise/density/end.rs b/pumpkin-world/src/generation/noise/density/end.rs similarity index 97% rename from pumpkin-world/src/world_gen/noise/density/end.rs rename to pumpkin-world/src/generation/noise/density/end.rs index 39ee37bc..c1c3db33 100644 --- a/pumpkin-world/src/world_gen/noise/density/end.rs +++ b/pumpkin-world/src/generation/noise/density/end.rs @@ -1,6 +1,6 @@ use pumpkin_core::random::{legacy_rand::LegacyRand, RandomImpl}; -use crate::world_gen::noise::simplex::SimplexNoiseSampler; +use crate::generation::noise::simplex::SimplexNoiseSampler; use super::{ component_functions::{ diff --git a/pumpkin-world/src/world_gen/noise/density/math.rs b/pumpkin-world/src/generation/noise/density/math.rs similarity index 100% rename from pumpkin-world/src/world_gen/noise/density/math.rs rename to pumpkin-world/src/generation/noise/density/math.rs diff --git a/pumpkin-world/src/world_gen/noise/density/mod.rs b/pumpkin-world/src/generation/noise/density/mod.rs similarity index 99% rename from pumpkin-world/src/world_gen/noise/density/mod.rs rename to pumpkin-world/src/generation/noise/density/mod.rs index ecec8350..e789b3bd 100644 --- a/pumpkin-world/src/world_gen/noise/density/mod.rs +++ b/pumpkin-world/src/generation/noise/density/mod.rs @@ -10,7 +10,7 @@ use component_functions::{ use enum_dispatch::enum_dispatch; use noise::{InternalNoise, NoiseFunction}; -use crate::world_gen::{blender::Blender, chunk_noise::ChunkNoisePos}; +use crate::generation::{blender::Blender, chunk_noise::ChunkNoisePos}; use super::perlin::DoublePerlinNoiseParameters; @@ -76,8 +76,8 @@ pub trait NoisePosImpl { pub mod built_in_density_function { use std::sync::{Arc, LazyLock}; - use crate::world_gen::noise::built_in_noise_params::{self}; - use crate::world_gen::positions::{MAX_COLUMN_HEIGHT, MIN_HEIGHT}; + use crate::generation::noise::built_in_noise_params::{self}; + use crate::generation::positions::{MAX_COLUMN_HEIGHT, MIN_HEIGHT}; use pumpkin_core::math::floor_div; @@ -879,7 +879,7 @@ mod test { legacy_rand::LegacyRand, RandomDeriver, RandomDeriverImpl, RandomImpl, }; - use crate::world_gen::noise::{ + use crate::generation::noise::{ built_in_noise_params, density::{built_in_density_function::*, NoisePos, UnblendedNoisePos}, perlin::DoublePerlinNoiseSampler, diff --git a/pumpkin-world/src/world_gen/noise/density/noise.rs b/pumpkin-world/src/generation/noise/density/noise.rs similarity index 99% rename from pumpkin-world/src/world_gen/noise/density/noise.rs rename to pumpkin-world/src/generation/noise/density/noise.rs index 353273b4..1fe13937 100644 --- a/pumpkin-world/src/world_gen/noise/density/noise.rs +++ b/pumpkin-world/src/generation/noise/density/noise.rs @@ -3,11 +3,11 @@ use std::{marker::PhantomData, sync::Arc}; use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomGenerator, RandomImpl}; use crate::{ - match_ref_implementations, - world_gen::noise::{ + generation::noise::{ clamped_lerp, perlin::{DoublePerlinNoiseParameters, DoublePerlinNoiseSampler, OctavePerlinNoiseSampler}, }, + match_ref_implementations, }; use super::{ diff --git a/pumpkin-world/src/world_gen/noise/density/offset.rs b/pumpkin-world/src/generation/noise/density/offset.rs similarity index 100% rename from pumpkin-world/src/world_gen/noise/density/offset.rs rename to pumpkin-world/src/generation/noise/density/offset.rs diff --git a/pumpkin-world/src/world_gen/noise/density/spline.rs b/pumpkin-world/src/generation/noise/density/spline.rs similarity index 99% rename from pumpkin-world/src/world_gen/noise/density/spline.rs rename to pumpkin-world/src/generation/noise/density/spline.rs index cb2aa6ee..142079a9 100644 --- a/pumpkin-world/src/world_gen/noise/density/spline.rs +++ b/pumpkin-world/src/generation/noise/density/spline.rs @@ -2,7 +2,7 @@ use std::{marker::PhantomData, sync::Arc}; use enum_dispatch::enum_dispatch; -use crate::world_gen::noise::lerp; +use crate::generation::noise::lerp; use super::{ component_functions::{ @@ -744,7 +744,7 @@ mod test { use pumpkin_core::random::{legacy_rand::LegacyRand, RandomDeriver, RandomImpl}; - use crate::world_gen::noise::density::{ + use crate::generation::noise::density::{ built_in_density_function::CONTINENTS_OVERWORLD, component_functions::{ComponentReference, NoEnvironment, SharedComponentReference}, test::{FakeEnvironment, OwnedConverter, TestConverter}, diff --git a/pumpkin-world/src/world_gen/noise/density/terrain_helpers.rs b/pumpkin-world/src/generation/noise/density/terrain_helpers.rs similarity index 99% rename from pumpkin-world/src/world_gen/noise/density/terrain_helpers.rs rename to pumpkin-world/src/generation/noise/density/terrain_helpers.rs index b19df8c6..cc8ad704 100644 --- a/pumpkin-world/src/world_gen/noise/density/terrain_helpers.rs +++ b/pumpkin-world/src/generation/noise/density/terrain_helpers.rs @@ -1,7 +1,7 @@ // From da java -use crate::world_gen::noise::density::peaks_valleys_noise; -use crate::world_gen::noise::lerp; +use crate::generation::noise::density::peaks_valleys_noise; +use crate::generation::noise::lerp; use super::component_functions::SharedComponentReference; use super::spline::{FloatAmplifier, ImmutableSpline, ImmutableSplineRef, SplineBuilder}; @@ -523,7 +523,7 @@ pub fn create_jaggedness_spline( mod test { use pumpkin_core::random::{legacy_rand::LegacyRand, RandomDeriver, RandomImpl}; - use crate::world_gen::noise::density::{ + use crate::generation::noise::density::{ built_in_density_function::{ CONTINENTS_OVERWORLD, EROSION_OVERWORLD, RIDGES_FOLDED_OVERWORLD, RIDGES_OVERWORLD, }, diff --git a/pumpkin-world/src/world_gen/noise/density/unary.rs b/pumpkin-world/src/generation/noise/density/unary.rs similarity index 100% rename from pumpkin-world/src/world_gen/noise/density/unary.rs rename to pumpkin-world/src/generation/noise/density/unary.rs diff --git a/pumpkin-world/src/world_gen/noise/density/weird.rs b/pumpkin-world/src/generation/noise/density/weird.rs similarity index 100% rename from pumpkin-world/src/world_gen/noise/density/weird.rs rename to pumpkin-world/src/generation/noise/density/weird.rs diff --git a/pumpkin-world/src/world_gen/noise/mod.rs b/pumpkin-world/src/generation/noise/mod.rs similarity index 100% rename from pumpkin-world/src/world_gen/noise/mod.rs rename to pumpkin-world/src/generation/noise/mod.rs diff --git a/pumpkin-world/src/world_gen/noise/perlin.rs b/pumpkin-world/src/generation/noise/perlin.rs similarity index 99% rename from pumpkin-world/src/world_gen/noise/perlin.rs rename to pumpkin-world/src/generation/noise/perlin.rs index 09563485..4e8de285 100644 --- a/pumpkin-world/src/world_gen/noise/perlin.rs +++ b/pumpkin-world/src/generation/noise/perlin.rs @@ -381,7 +381,7 @@ mod double_perlin_noise_sampler_test { legacy_rand::LegacyRand, xoroshiro128::Xoroshiro, RandomGenerator, RandomImpl, }; - use crate::world_gen::noise::perlin::{DoublePerlinNoiseParameters, DoublePerlinNoiseSampler}; + use crate::generation::noise::perlin::{DoublePerlinNoiseParameters, DoublePerlinNoiseSampler}; #[test] fn sample_legacy() { @@ -760,7 +760,7 @@ mod perlin_noise_sampler_test { random::{xoroshiro128::Xoroshiro, RandomDeriverImpl, RandomGenerator, RandomImpl}, }; - use crate::{read_data_from_file, world_gen::noise::perlin::PerlinNoiseSampler}; + use crate::{generation::noise::perlin::PerlinNoiseSampler, read_data_from_file}; use super::OctavePerlinNoiseSampler; diff --git a/pumpkin-world/src/world_gen/noise/router.rs b/pumpkin-world/src/generation/noise/router.rs similarity index 99% rename from pumpkin-world/src/world_gen/noise/router.rs rename to pumpkin-world/src/generation/noise/router.rs index ddf605c3..cb8093c1 100644 --- a/pumpkin-world/src/world_gen/noise/router.rs +++ b/pumpkin-world/src/generation/noise/router.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, LazyLock}; -use crate::world_gen::{ +use crate::generation::{ noise::density::{ apply_blend_density, basic::RangeFunction, @@ -476,8 +476,7 @@ mod test { }; use crate::{ - read_data_from_file, - world_gen::noise::{ + generation::noise::{ config::LegacyChunkNoiseVisitor, density::{ built_in_density_function::{EROSION_OVERWORLD, SLOPED_CHEESE_OVERWORLD}, @@ -491,6 +490,7 @@ mod test { perlin::DoublePerlinNoiseSampler, router::OVERWORLD_NOISE_ROUTER, }, + read_data_from_file, }; use super::{apply_surface_slides, create_caves}; diff --git a/pumpkin-world/src/world_gen/noise/simplex.rs b/pumpkin-world/src/generation/noise/simplex.rs similarity index 99% rename from pumpkin-world/src/world_gen/noise/simplex.rs rename to pumpkin-world/src/generation/noise/simplex.rs index ff9afd6e..c69128c3 100644 --- a/pumpkin-world/src/world_gen/noise/simplex.rs +++ b/pumpkin-world/src/generation/noise/simplex.rs @@ -276,7 +276,7 @@ impl OctaveSimplexNoiseSampler { mod octave_simplex_noise_sampler_test { use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomImpl}; - use crate::world_gen::noise::simplex::OctaveSimplexNoiseSampler; + use crate::generation::noise::simplex::OctaveSimplexNoiseSampler; #[test] fn test_new() { @@ -413,7 +413,7 @@ mod simplex_noise_sampler_test { use pumpkin_core::random::{xoroshiro128::Xoroshiro, RandomImpl}; - use crate::world_gen::noise::simplex::SimplexNoiseSampler; + use crate::generation::noise::simplex::SimplexNoiseSampler; #[test] fn test_create() { diff --git a/pumpkin-world/src/world_gen/ore_sampler.rs b/pumpkin-world/src/generation/ore_sampler.rs similarity index 100% rename from pumpkin-world/src/world_gen/ore_sampler.rs rename to pumpkin-world/src/generation/ore_sampler.rs diff --git a/pumpkin-world/src/world_gen/positions.rs b/pumpkin-world/src/generation/positions.rs similarity index 100% rename from pumpkin-world/src/world_gen/positions.rs rename to pumpkin-world/src/generation/positions.rs diff --git a/pumpkin-world/src/world_gen/proto_chunk.rs b/pumpkin-world/src/generation/proto_chunk.rs similarity index 99% rename from pumpkin-world/src/world_gen/proto_chunk.rs rename to pumpkin-world/src/generation/proto_chunk.rs index 6f36cab6..056a702f 100644 --- a/pumpkin-world/src/world_gen/proto_chunk.rs +++ b/pumpkin-world/src/generation/proto_chunk.rs @@ -2,7 +2,7 @@ use pumpkin_core::math::{vector2::Vector2, vector3::Vector3}; use crate::{ block::BlockState, - world_gen::{ + generation::{ chunk_noise::CHUNK_DIM, generation_shapes::GenerationShape, noise::{config::NoiseConfig, router::OVERWORLD_NOISE_ROUTER}, diff --git a/pumpkin-world/src/world_gen/seed.rs b/pumpkin-world/src/generation/seed.rs similarity index 100% rename from pumpkin-world/src/world_gen/seed.rs rename to pumpkin-world/src/generation/seed.rs diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index e0b8ac12..70dcdc7a 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -14,7 +14,7 @@ use crate::{ chunk::{ anvil::AnvilChunkReader, ChunkData, ChunkParsingError, ChunkReader, ChunkReadingError, }, - world_gen::{get_world_gen, Seed, WorldGenerator}, + generation::{get_world_gen, Seed, WorldGenerator}, }; /// The `Level` module provides functionality for working with chunks within or outside a Minecraft world. diff --git a/pumpkin-world/src/lib.rs b/pumpkin-world/src/lib.rs index d1d6284c..3735dbb5 100644 --- a/pumpkin-world/src/lib.rs +++ b/pumpkin-world/src/lib.rs @@ -1,11 +1,11 @@ -use pumpkin_core::math::vector2::Vector2; -use world_gen::{ +use generation::{ aquifer_sampler::{FluidLevel, FluidLevelSampler}, chunk_noise::{ChunkNoiseGenerator, LAVA_BLOCK, WATER_BLOCK}, generation_shapes::GenerationShape, noise::{config::NoiseConfig, router::OVERWORLD_NOISE_ROUTER}, proto_chunk::{ProtoChunk, StandardChunkFluidLevelSampler}, }; +use pumpkin_core::math::vector2::Vector2; pub mod biome; pub mod block; @@ -13,9 +13,9 @@ pub mod chunk; pub mod coordinates; pub mod cylindrical_chunk_iterator; pub mod dimension; +mod generation; pub mod item; pub mod level; -mod world_gen; pub const WORLD_HEIGHT: usize = 384; pub const WORLD_LOWEST_Y: i16 = -64; diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 9f0a8e07..c9e51dd1 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -57,10 +57,9 @@ use pumpkin_world::{ use tokio::sync::{Mutex, Notify}; use super::Entity; -use crate::error::PumpkinError; +use crate::{error::PumpkinError, net::GameProfile}; use crate::{ - client::{ - authentication::GameProfile, + net::{ combat::{self, player_attack_sound, AttackType}, Client, PlayerConfig, }, diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index c2547e78..232f30a1 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -35,7 +35,7 @@ compile_error!("Compiling for WASI targets is not supported!"); use log::LevelFilter; -use client::Client; +use net::{lan_broadcast, query, rcon::RCONServer, Client}; use server::{ticker::Ticker, Server}; use std::io::{self}; use tokio::io::{AsyncBufReadExt, BufReader}; @@ -50,19 +50,14 @@ use crate::server::CURRENT_MC_VERSION; use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_core::text::{color::NamedColor, TextComponent}; use pumpkin_protocol::CURRENT_MC_PROTOCOL; -use rcon::RCONServer; use std::time::Instant; // Setup some tokens to allow us to identify which event is for which socket. pub mod block; -pub mod client; pub mod command; pub mod entity; pub mod error; -pub mod lan_broadcast; -pub mod proxy; -pub mod query; -pub mod rcon; +pub mod net; pub mod server; pub mod world; diff --git a/pumpkin/src/client/authentication.rs b/pumpkin/src/net/authentication.rs similarity index 85% rename from pumpkin/src/client/authentication.rs rename to pumpkin/src/net/authentication.rs index f99a62cb..4136a74a 100644 --- a/pumpkin/src/client/authentication.rs +++ b/pumpkin/src/net/authentication.rs @@ -2,15 +2,14 @@ use std::{collections::HashMap, net::IpAddr}; use base64::{engine::general_purpose, Engine}; use pumpkin_config::{auth::TextureConfig, ADVANCED_CONFIG}; -use pumpkin_core::ProfileAction; use pumpkin_protocol::Property; use reqwest::{StatusCode, Url}; use serde::Deserialize; -use sha1::Digest; -use sha2::Sha256; use thiserror::Error; use uuid::Uuid; +use super::GameProfile; + #[derive(Deserialize, Clone, Debug)] #[expect(dead_code)] #[serde(rename_all = "camelCase")] @@ -29,14 +28,8 @@ pub struct Texture { metadata: Option>, } -#[derive(Deserialize, Clone, Debug)] -pub struct GameProfile { - pub id: Uuid, - pub name: String, - pub properties: Vec, - #[serde(rename = "profileActions")] - pub profile_actions: Option>, -} +const MOJANG_AUTHENTICATION_URL: &str = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}"; +const MOJANG_PREVENT_PROXY_AUTHENTICATION_URL: &str = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}"; /// Sends a GET request to Mojang's authentication servers to verify a client's Minecraft account. /// @@ -50,20 +43,19 @@ pub struct GameProfile { /// 2. Mojang's servers verify the client's credentials and add the player to the their Servers /// 3. Now our server will send a Request to the Session servers and check if the Player has joined the Session Server . /// -/// See +/// See pub async fn authenticate( username: &str, server_hash: &str, ip: &IpAddr, auth_client: &reqwest::Client, ) -> Result { - assert!(ADVANCED_CONFIG.authentication.enabled); let address = if ADVANCED_CONFIG.authentication.prevent_proxy_connections { let auth_url = ADVANCED_CONFIG .authentication .prevent_proxy_connection_auth_url .as_deref() - .unwrap_or("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}&ip={ip}"); + .unwrap_or(MOJANG_PREVENT_PROXY_AUTHENTICATION_URL); auth_url .replace("{username}", username) @@ -74,7 +66,7 @@ pub async fn authenticate( .authentication .auth_url .as_deref() - .unwrap_or("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}"); + .unwrap_or(MOJANG_AUTHENTICATION_URL); auth_url .replace("{username}", username) @@ -129,10 +121,6 @@ pub fn is_texture_url_valid(url: &Url, config: &TextureConfig) -> Result<(), Tex Ok(()) } -pub fn offline_uuid(username: &str) -> Result { - Uuid::from_slice(&Sha256::digest(username)[..16]) -} - #[derive(Error, Debug)] pub enum AuthError { #[error("Missing auth client")] diff --git a/pumpkin/src/client/combat.rs b/pumpkin/src/net/combat.rs similarity index 100% rename from pumpkin/src/client/combat.rs rename to pumpkin/src/net/combat.rs diff --git a/pumpkin/src/client/container.rs b/pumpkin/src/net/container.rs similarity index 99% rename from pumpkin/src/client/container.rs rename to pumpkin/src/net/container.rs index 42a4b23c..9e06a46d 100644 --- a/pumpkin/src/client/container.rs +++ b/pumpkin/src/net/container.rs @@ -12,9 +12,9 @@ use pumpkin_inventory::{Container, WindowType}; use pumpkin_protocol::client::play::{ CCloseContainer, COpenScreen, CSetContainerContent, CSetContainerProperty, CSetContainerSlot, }; +use pumpkin_protocol::codec::slot::Slot; use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::server::play::SClickContainer; -use pumpkin_protocol::slot::Slot; use pumpkin_world::item::item_registry::Item; use pumpkin_world::item::ItemStack; use std::sync::Arc; diff --git a/pumpkin/src/lan_broadcast.rs b/pumpkin/src/net/lan_broadcast.rs similarity index 100% rename from pumpkin/src/lan_broadcast.rs rename to pumpkin/src/net/lan_broadcast.rs diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/net/mod.rs similarity index 94% rename from pumpkin/src/client/mod.rs rename to pumpkin/src/net/mod.rs index 977dd6f6..0e5f9e9c 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/net/mod.rs @@ -13,34 +13,57 @@ use crate::{ server::Server, }; -use authentication::GameProfile; use crossbeam::atomic::AtomicCell; use pumpkin_config::compression::CompressionInfo; -use pumpkin_core::text::TextComponent; +use pumpkin_core::{text::TextComponent, ProfileAction}; use pumpkin_protocol::{ bytebuf::{packet_id::Packet, ReadingError}, client::{config::CConfigDisconnect, login::CLoginDisconnect, play::CPlayDisconnect}, packet_decoder::PacketDecoder, packet_encoder::{PacketEncodeError, PacketEncoder}, server::{ - config::{SAcknowledgeFinishConfig, SClientInformationConfig, SKnownPacks, SPluginMessage}, + config::{ + SAcknowledgeFinishConfig, SClientInformationConfig, SConfigCookieResponse, SKnownPacks, + SPluginMessage, + }, handshake::SHandShake, - login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, + login::{ + SEncryptionResponse, SLoginAcknowledged, SLoginCookieResponse, SLoginPluginResponse, + SLoginStart, + }, status::{SStatusPingRequest, SStatusRequest}, }, - ClientPacket, ConnectionState, RawPacket, ServerPacket, + ClientPacket, ConnectionState, Property, RawPacket, ServerPacket, }; +use serde::Deserialize; +use sha1::Digest; +use sha2::Sha256; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::sync::Mutex; -use pumpkin_protocol::server::config::SCookieResponse as SCCookieResponse; -use pumpkin_protocol::server::login::SCookieResponse as SLCookieResponse; use thiserror::Error; -pub mod authentication; -mod client_packet; +use uuid::Uuid; +mod authentication; pub mod combat; mod container; -pub mod player_packet; +pub mod lan_broadcast; +mod packet; +mod proxy; +pub mod query; +pub mod rcon; + +#[derive(Deserialize, Clone, Debug)] +pub struct GameProfile { + pub id: Uuid, + pub name: String, + pub properties: Vec, + #[serde(rename = "profileActions")] + pub profile_actions: Option>, +} + +pub fn offline_uuid(username: &str) -> Result { + Uuid::from_slice(&Sha256::digest(username)[..16]) +} /// Represents a player's configuration settings. /// @@ -102,8 +125,6 @@ pub struct Client { pub server_address: Mutex, /// The current connection state of the client (e.g., Handshaking, Status, Play). pub connection_state: AtomicCell, - /// Whether encryption is enabled for the connection. - pub encryption: AtomicBool, /// Indicates if the client connection is closed. pub closed: AtomicBool, /// The underlying TCP connection to the client. @@ -138,7 +159,6 @@ impl Client { connection_writer: Arc::new(Mutex::new(connection_writer)), enc: Arc::new(Mutex::new(PacketEncoder::default())), dec: Arc::new(Mutex::new(PacketDecoder::default())), - encryption: AtomicBool::new(false), closed: AtomicBool::new(false), client_packets_queue: Arc::new(Mutex::new(VecDeque::new())), make_player: AtomicBool::new(false), @@ -182,8 +202,6 @@ impl Client { shared_secret: Option<&[u8]>, // decrypted ) -> Result<(), EncryptionError> { if let Some(shared_secret) = shared_secret { - self.encryption - .store(true, std::sync::atomic::Ordering::Relaxed); let crypt_key: [u8; 16] = shared_secret .try_into() .map_err(|_| EncryptionError::SharedWrongLength)?; @@ -433,8 +451,8 @@ impl Client { SLoginAcknowledged::PACKET_ID => { self.handle_login_acknowledged(server).await; } - SLCookieResponse::PACKET_ID => { - self.handle_login_cookie_response(SLCookieResponse::read(bytebuf)?); + SLoginCookieResponse::PACKET_ID => { + self.handle_login_cookie_response(SLoginCookieResponse::read(bytebuf)?); } _ => { log::error!( @@ -469,8 +487,8 @@ impl Client { self.handle_known_packs(server, SKnownPacks::read(bytebuf)?) .await; } - SCCookieResponse::PACKET_ID => { - self.handle_config_cookie_response(SCCookieResponse::read(bytebuf)?); + SConfigCookieResponse::PACKET_ID => { + self.handle_config_cookie_response(SConfigCookieResponse::read(bytebuf)?); } _ => { log::error!( diff --git a/pumpkin/src/net/packet/config.rs b/pumpkin/src/net/packet/config.rs new file mode 100644 index 00000000..ed1d8769 --- /dev/null +++ b/pumpkin/src/net/packet/config.rs @@ -0,0 +1,98 @@ +use std::num::NonZeroU8; + +use crate::{ + entity::player::{ChatMode, Hand}, + net::{Client, PlayerConfig}, + server::Server, +}; +use core::str; +use num_traits::FromPrimitive; +use pumpkin_protocol::{ + client::config::{CFinishConfig, CRegistryData}, + codec::var_int::VarInt, + server::config::{ + SClientInformationConfig, SConfigCookieResponse, SKnownPacks, SPluginMessage, + }, + ConnectionState, +}; + +impl Client { + pub async fn handle_client_information_config( + &self, + client_information: SClientInformationConfig, + ) { + log::debug!("Handling client settings"); + if client_information.view_distance <= 0 { + self.kick("Cannot have zero or negative view distance!") + .await; + return; + } + + if let (Some(main_hand), Some(chat_mode)) = ( + Hand::from_i32(client_information.main_hand.into()), + ChatMode::from_i32(client_information.chat_mode.into()), + ) { + *self.config.lock().await = Some(PlayerConfig { + locale: client_information.locale, + view_distance: unsafe { + NonZeroU8::new_unchecked(client_information.view_distance as u8) + }, + chat_mode, + chat_colors: client_information.chat_colors, + skin_parts: client_information.skin_parts, + main_hand, + text_filtering: client_information.text_filtering, + server_listing: client_information.server_listing, + }); + } else { + self.kick("Invalid hand or chat type").await; + } + } + + pub async fn handle_plugin_message(&self, plugin_message: SPluginMessage) { + log::debug!("Handling plugin message"); + if plugin_message + .channel + .to_string() + .starts_with("minecraft:brand") + { + log::debug!("got a client brand"); + match str::from_utf8(&plugin_message.data) { + Ok(brand) => *self.brand.lock().await = Some(brand.to_string()), + Err(e) => self.kick(&e.to_string()).await, + } + } + } + + pub fn handle_config_cookie_response(&self, packet: SConfigCookieResponse) { + // TODO: allow plugins to access this + log::debug!( + "Received cookie_response[config]: key: \"{}\", has_payload: \"{}\", payload_length: \"{}\"", + packet.key.to_string(), + packet.has_payload, + packet.payload_length.unwrap_or(VarInt::from(0)).0 + ); + } + + pub async fn handle_known_packs(&self, server: &Server, _config_acknowledged: SKnownPacks) { + log::debug!("Handling known packs"); + for registry in &server.cached_registry { + self.send_packet(&CRegistryData::new( + ®istry.registry_id, + ®istry.registry_entries, + )) + .await; + } + + // We are done with configuring + log::debug!("finished config"); + self.send_packet(&CFinishConfig::new()).await; + } + + pub fn handle_config_acknowledged(&self) { + log::debug!("Handling config acknowledge"); + self.connection_state.store(ConnectionState::Play); + self.make_player + .store(true, std::sync::atomic::Ordering::Relaxed); + } +} diff --git a/pumpkin/src/net/packet/handshake.rs b/pumpkin/src/net/packet/handshake.rs new file mode 100644 index 00000000..58705210 --- /dev/null +++ b/pumpkin/src/net/packet/handshake.rs @@ -0,0 +1,29 @@ +use std::num::NonZeroI32; + +use pumpkin_protocol::{server::handshake::SHandShake, ConnectionState, CURRENT_MC_PROTOCOL}; + +use crate::{net::Client, server::CURRENT_MC_VERSION}; + +impl Client { + pub async fn handle_handshake(&self, handshake: SHandShake) { + let version = handshake.protocol_version.0; + self.protocol_version + .store(version, std::sync::atomic::Ordering::Relaxed); + *self.server_address.lock().await = handshake.server_address; + + log::debug!("Handshake: next state {:?}", &handshake.next_state); + self.connection_state.store(handshake.next_state); + if self.connection_state.load() != ConnectionState::Status { + let protocol = version; + match protocol.cmp(&NonZeroI32::from(CURRENT_MC_PROTOCOL).get()) { + std::cmp::Ordering::Less => { + self.kick(&format!("Client outdated ({protocol}), Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}")).await; + } + std::cmp::Ordering::Equal => {} + std::cmp::Ordering::Greater => { + self.kick(&format!("Server outdated, Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}")).await; + } + } + } + } +} diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/net/packet/login.rs similarity index 63% rename from pumpkin/src/client/client_packet.rs rename to pumpkin/src/net/packet/login.rs index 76e0ce68..db61b865 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/net/packet/login.rs @@ -1,43 +1,30 @@ -use super::{authentication::AuthError, Client, PlayerConfig}; -use crate::{ - client::authentication::{self, offline_uuid, validate_textures, GameProfile}, - entity::player::{ChatMode, Hand}, - proxy::{ - bungeecord, - velocity::{self, velocity_login}, - }, - server::{Server, CURRENT_MC_VERSION}, -}; -use core::str; -use num_traits::FromPrimitive; +use std::sync::LazyLock; + use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_core::text::TextComponent; -use pumpkin_protocol::server::config::SCookieResponse as SCCookieResponse; -use pumpkin_protocol::server::login::SCookieResponse as SLCookieResponse; -use pumpkin_protocol::{ - client::config::{CServerLinks, Label, Link, LinkType}, - codec::var_int::VarInt, -}; use pumpkin_protocol::{ client::{ - config::{CConfigAddResourcePack, CFinishConfig, CKnownPacks, CRegistryData}, + config::{CConfigAddResourcePack, CKnownPacks}, login::{CLoginSuccess, CSetCompression}, - status::CPingResponse, - }, - server::{ - config::{SClientInformationConfig, SKnownPacks, SPluginMessage}, - handshake::SHandShake, - login::{SEncryptionResponse, SLoginPluginResponse, SLoginStart}, - status::SStatusPingRequest, + play::{CServerLinks, Label, Link, LinkType}, }, - ConnectionState, KnownPack, CURRENT_MC_PROTOCOL, -}; -use std::{ - num::{NonZeroI32, NonZeroU8}, - sync::LazyLock, + codec::var_int::VarInt, + server::login::{SEncryptionResponse, SLoginCookieResponse, SLoginPluginResponse, SLoginStart}, + ConnectionState, KnownPack, }; use uuid::Uuid; +use crate::{ + net::{ + authentication::{self, AuthError}, + offline_uuid, + packet::is_valid_player_name, + proxy::{bungeecord, velocity}, + Client, GameProfile, + }, + server::Server, +}; + static LINKS: LazyLock> = LazyLock::new(|| { let mut links: Vec = Vec::new(); @@ -98,49 +85,7 @@ static LINKS: LazyLock> = LazyLock::new(|| { links }); -/// Processes incoming Packets from the Client to the Server -/// Implements the `Client` Packets -/// NEVER TRUST THE CLIENT. HANDLE EVERY ERROR, UNWRAP/EXPECT impl Client { - pub async fn handle_handshake(&self, handshake: SHandShake) { - let version = handshake.protocol_version.0; - self.protocol_version - .store(version, std::sync::atomic::Ordering::Relaxed); - *self.server_address.lock().await = handshake.server_address; - - log::debug!("Handshake: next state {:?}", &handshake.next_state); - self.connection_state.store(handshake.next_state); - if self.connection_state.load() != ConnectionState::Status { - let protocol = version; - match protocol.cmp(&NonZeroI32::from(CURRENT_MC_PROTOCOL).get()) { - std::cmp::Ordering::Less => { - self.kick(&format!("Client outdated ({protocol}), Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}")).await; - } - std::cmp::Ordering::Equal => {} - std::cmp::Ordering::Greater => { - self.kick(&format!("Server outdated, Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}")).await; - } - } - } - } - - pub async fn handle_status_request(&self, server: &Server) { - log::debug!("Handling status request"); - let status = server.get_status(); - self.send_packet(&status.lock().await.get_status()).await; - } - - pub async fn handle_ping_request(&self, ping_request: SStatusPingRequest) { - log::debug!("Handling ping request"); - self.send_packet(&CPingResponse::new(ping_request.payload)) - .await; - self.close(); - } - - fn is_valid_player_name(name: &str) -> bool { - name.len() <= 16 && name.chars().all(|c| c > 32u8 as char && c < 127u8 as char) - } - pub async fn handle_login_start(&self, server: &Server, login_start: SLoginStart) { log::debug!("login start"); @@ -154,7 +99,7 @@ impl Client { return; } - if !Self::is_valid_player_name(&login_start.name) { + if !is_valid_player_name(&login_start.name) { self.kick("Invalid characters in username").await; return; } @@ -164,9 +109,15 @@ impl Client { let proxy = &ADVANCED_CONFIG.proxy; if proxy.enabled { if proxy.velocity.enabled { - velocity_login(self).await; + velocity::velocity_login(self).await; } else if proxy.bungeecord.enabled { - match bungeecord::bungeecord_login(self, login_start.name).await { + match bungeecord::bungeecord_login( + &self.address, + &self.server_address.lock().await, + login_start.name, + ) + .await + { Ok((_ip, profile)) => { // self.address.lock() = ip; self.finish_login(&profile).await; @@ -309,24 +260,26 @@ impl Client { } // validate textures for property in &profile.properties { - validate_textures(property, &ADVANCED_CONFIG.authentication.textures) - .map_err(AuthError::TextureError)?; + authentication::validate_textures( + property, + &ADVANCED_CONFIG.authentication.textures, + ) + .map_err(AuthError::TextureError)?; } return Ok(profile); } Err(AuthError::MissingAuthClient) } - pub fn handle_login_cookie_response(&self, packet: SLCookieResponse) { + pub fn handle_login_cookie_response(&self, packet: SLoginCookieResponse) { // TODO: allow plugins to access this log::debug!( - "Received cookie_response[login]: key: \"{}\", has_payload: \"{}\", payload_length: \"{}\"", - packet.key.to_string(), - packet.has_payload, - packet.payload_length.unwrap_or(VarInt::from(0)).0 - ); + "Received cookie_response[login]: key: \"{}\", has_payload: \"{}\", payload_length: \"{}\"", + packet.key.to_string(), + packet.has_payload, + packet.payload_length.unwrap_or(VarInt::from(0)).0 + ); } - pub async fn handle_plugin_response(&self, plugin_response: SLoginPluginResponse) { log::debug!("Handling plugin"); let velocity_config = &ADVANCED_CONFIG.proxy.velocity; @@ -386,82 +339,4 @@ impl Client { .await; log::debug!("login acknowledged"); } - pub async fn handle_client_information_config( - &self, - client_information: SClientInformationConfig, - ) { - log::debug!("Handling client settings"); - if client_information.view_distance <= 0 { - self.kick("Cannot have zero or negative view distance!") - .await; - return; - } - - if let (Some(main_hand), Some(chat_mode)) = ( - Hand::from_i32(client_information.main_hand.into()), - ChatMode::from_i32(client_information.chat_mode.into()), - ) { - *self.config.lock().await = Some(PlayerConfig { - locale: client_information.locale, - view_distance: unsafe { - NonZeroU8::new_unchecked(client_information.view_distance as u8) - }, - chat_mode, - chat_colors: client_information.chat_colors, - skin_parts: client_information.skin_parts, - main_hand, - text_filtering: client_information.text_filtering, - server_listing: client_information.server_listing, - }); - } else { - self.kick("Invalid hand or chat type").await; - } - } - - pub async fn handle_plugin_message(&self, plugin_message: SPluginMessage) { - log::debug!("Handling plugin message"); - if plugin_message - .channel - .to_string() - .starts_with("minecraft:brand") - { - log::debug!("got a client brand"); - match str::from_utf8(&plugin_message.data) { - Ok(brand) => *self.brand.lock().await = Some(brand.to_string()), - Err(e) => self.kick(&e.to_string()).await, - } - } - } - - pub fn handle_config_cookie_response(&self, packet: SCCookieResponse) { - // TODO: allow plugins to access this - log::debug!( - "Received cookie_response[config]: key: \"{}\", has_payload: \"{}\", payload_length: \"{}\"", - packet.key.to_string(), - packet.has_payload, - packet.payload_length.unwrap_or(VarInt::from(0)).0 - ); - } - - pub async fn handle_known_packs(&self, server: &Server, _config_acknowledged: SKnownPacks) { - log::debug!("Handling known packs"); - for registry in &server.cached_registry { - self.send_packet(&CRegistryData::new( - ®istry.registry_id, - ®istry.registry_entries, - )) - .await; - } - - // We are done with configuring - log::debug!("finished config"); - self.send_packet(&CFinishConfig::new()).await; - } - - pub fn handle_config_acknowledged(&self) { - log::debug!("Handling config acknowledge"); - self.connection_state.store(ConnectionState::Play); - self.make_player - .store(true, std::sync::atomic::Ordering::Relaxed); - } } diff --git a/pumpkin/src/net/packet/mod.rs b/pumpkin/src/net/packet/mod.rs new file mode 100644 index 00000000..0dbc697d --- /dev/null +++ b/pumpkin/src/net/packet/mod.rs @@ -0,0 +1,9 @@ +mod config; +mod handshake; +mod login; +mod play; +mod status; + +fn is_valid_player_name(name: &str) -> bool { + name.len() <= 16 && name.chars().all(|c| c > 32u8 as char && c < 127u8 as char) +} diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/net/packet/play.rs similarity index 99% rename from pumpkin/src/client/player_packet.rs rename to pumpkin/src/net/packet/play.rs index a7b2b809..e3062678 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/net/packet/play.rs @@ -1,8 +1,8 @@ use std::num::NonZeroU8; use std::sync::Arc; -use super::PlayerConfig; use crate::block::block_manager::BlockActionResult; +use crate::net::PlayerConfig; use crate::{ command::CommandSender, entity::player::{ChatMode, Hand, Player}, diff --git a/pumpkin/src/net/packet/status.rs b/pumpkin/src/net/packet/status.rs new file mode 100644 index 00000000..0e9790c2 --- /dev/null +++ b/pumpkin/src/net/packet/status.rs @@ -0,0 +1,18 @@ +use pumpkin_protocol::{client::status::CPingResponse, server::status::SStatusPingRequest}; + +use crate::{net::Client, server::Server}; + +impl Client { + pub async fn handle_status_request(&self, server: &Server) { + log::debug!("Handling status request"); + let status = server.get_status(); + self.send_packet(&status.lock().await.get_status()).await; + } + + pub async fn handle_ping_request(&self, ping_request: SStatusPingRequest) { + log::debug!("Handling ping request"); + self.send_packet(&CPingResponse::new(ping_request.payload)) + .await; + self.close(); + } +} diff --git a/pumpkin/src/proxy/bungeecord.rs b/pumpkin/src/net/proxy/bungeecord.rs similarity index 59% rename from pumpkin/src/proxy/bungeecord.rs rename to pumpkin/src/net/proxy/bungeecord.rs index c8c9d1ce..dc197671 100644 --- a/pumpkin/src/proxy/bungeecord.rs +++ b/pumpkin/src/net/proxy/bungeecord.rs @@ -1,12 +1,10 @@ -use std::net::IpAddr; +use std::{net::IpAddr, net::SocketAddr}; use pumpkin_protocol::Property; use thiserror::Error; +use tokio::sync::Mutex; -use crate::{ - client::authentication::{offline_uuid, GameProfile}, - Client, -}; +use crate::net::{offline_uuid, GameProfile}; #[derive(Error, Debug)] pub enum BungeeCordError { @@ -20,11 +18,23 @@ pub enum BungeeCordError { FailedMakeOfflineUUID, } +/// Attempts to login a player via `BungeeCord`. +/// +/// This function should be called when receiving the `SLoginStart` packet. +/// It utilizes the `server_address` received in the `SHandShake` packet, +/// which may contain optional data about the client: +/// +/// 1. IP address (if `ip_forward` is enabled on the `BungeeCord` server) +/// 2. UUID (if `ip_forward` is enabled on the `BungeeCord` server) +/// 3. Game profile properties (if `ip_forward` and `online_mode` are enabled on the `BungeeCord` server) +/// +/// If any of the optional data is missing, the function will attempt to +/// determine the player's information locally. pub async fn bungeecord_login( - client: &Client, - username: String, + client_address: &Mutex, + server_address: &str, + name: String, ) -> Result<(IpAddr, GameProfile), BungeeCordError> { - let server_address = client.server_address.lock().await; let data = server_address.split('\0').take(4).collect::>(); // Ip of player, only given if ip_forward on bungee is true @@ -32,15 +42,13 @@ pub async fn bungeecord_login( Some(ip) => ip .parse() .map_err(|_| BungeeCordError::FailedParseAddress)?, - None => client.address.lock().await.ip(), + None => client_address.lock().await.ip(), }; // Uuid of player, only given if ip_forward on bungee is true let id = match data.get(2) { Some(uuid) => uuid.parse().map_err(|_| BungeeCordError::FailedParseUUID)?, - None => { - offline_uuid(username.as_str()).map_err(|_| BungeeCordError::FailedMakeOfflineUUID)? - } + None => offline_uuid(name.as_str()).map_err(|_| BungeeCordError::FailedMakeOfflineUUID)?, }; // Read properties and get textures @@ -57,7 +65,7 @@ pub async fn bungeecord_login( ip, GameProfile { id, - name: username, + name, properties, profile_actions: None, }, diff --git a/pumpkin/src/proxy/mod.rs b/pumpkin/src/net/proxy/mod.rs similarity index 100% rename from pumpkin/src/proxy/mod.rs rename to pumpkin/src/net/proxy/mod.rs diff --git a/pumpkin/src/proxy/velocity.rs b/pumpkin/src/net/proxy/velocity.rs similarity index 98% rename from pumpkin/src/proxy/velocity.rs rename to pumpkin/src/net/proxy/velocity.rs index 68f7d4e9..461c9e0f 100644 --- a/pumpkin/src/proxy/velocity.rs +++ b/pumpkin/src/net/proxy/velocity.rs @@ -14,7 +14,7 @@ use rand::Rng; use sha2::Sha256; use thiserror::Error; -use crate::client::{authentication::GameProfile, Client}; +use crate::net::{Client, GameProfile}; type HmacSha256 = Hmac; diff --git a/pumpkin/src/query.rs b/pumpkin/src/net/query.rs similarity index 100% rename from pumpkin/src/query.rs rename to pumpkin/src/net/query.rs diff --git a/pumpkin/src/rcon/mod.rs b/pumpkin/src/net/rcon/mod.rs similarity index 98% rename from pumpkin/src/rcon/mod.rs rename to pumpkin/src/net/rcon/mod.rs index 20bc12b0..96adbc96 100644 --- a/pumpkin/src/rcon/mod.rs +++ b/pumpkin/src/net/rcon/mod.rs @@ -13,7 +13,6 @@ pub struct RCONServer; impl RCONServer { pub async fn new(config: &RCONConfig, server: Arc) -> Result { - assert!(config.enabled, "RCON is not enabled"); let listener = tokio::net::TcpListener::bind(config.address).await.unwrap(); let password = Arc::new(config.password.clone()); diff --git a/pumpkin/src/rcon/packet.rs b/pumpkin/src/net/rcon/packet.rs similarity index 100% rename from pumpkin/src/rcon/packet.rs rename to pumpkin/src/net/rcon/packet.rs diff --git a/pumpkin/src/server/connection_cache.rs b/pumpkin/src/server/connection_cache.rs index 618fdac3..ccc8e9dd 100644 --- a/pumpkin/src/server/connection_cache.rs +++ b/pumpkin/src/server/connection_cache.rs @@ -26,6 +26,7 @@ fn load_icon_from_file>(path: P) -> Result Result> { + assert!(!png_data.is_empty(), "PNG data is empty"); let icon = png::Decoder::new(Cursor::new(&png_data)); let reader = icon.read_info()?; let info = reader.info(); @@ -70,6 +71,7 @@ impl CachedBranding { } impl CachedStatus { + #[must_use] pub fn new() -> Self { let status_response = Self::build_response(&BASIC_CONFIG); let status_response_json = serde_json::to_string(&status_response) diff --git a/pumpkin/src/server/key_store.rs b/pumpkin/src/server/key_store.rs index daf7f734..84d2c0d8 100644 --- a/pumpkin/src/server/key_store.rs +++ b/pumpkin/src/server/key_store.rs @@ -5,7 +5,7 @@ use rsa::{traits::PublicKeyParts as _, Pkcs1v15Encrypt, RsaPrivateKey}; use sha1::Sha1; use sha2::Digest; -use crate::client::EncryptionError; +use crate::net::EncryptionError; pub struct KeyStore { pub private_key: RsaPrivateKey, @@ -13,6 +13,7 @@ pub struct KeyStore { } impl KeyStore { + #[must_use] pub fn new() -> Self { log::debug!("Creating encryption keys..."); let private_key = Self::generate_private_key(); diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 33dd0c27..d8f0fc83 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -23,12 +23,12 @@ use tokio::sync::{Mutex, RwLock}; use crate::block::block_manager::BlockManager; use crate::block::default_block_manager; -use crate::client::EncryptionError; +use crate::net::EncryptionError; use crate::world::custom_bossbar::CustomBossbars; use crate::{ - client::Client, command::{default_dispatcher, dispatcher::CommandDispatcher}, entity::player::Player, + net::Client, world::World, }; diff --git a/pumpkin/src/world/worldborder.rs b/pumpkin/src/world/worldborder.rs index 6402a3f9..3426d6c7 100644 --- a/pumpkin/src/world/worldborder.rs +++ b/pumpkin/src/world/worldborder.rs @@ -3,7 +3,7 @@ use pumpkin_protocol::client::play::{ CSetBorderWarningDelay, CSetBorderWarningDistance, }; -use crate::client::Client; +use crate::net::Client; use super::World; From 1d96701cc09a9c6a63fed1e7b2094d4fb8beb592 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Tue, 24 Dec 2024 12:20:10 +0100 Subject: [PATCH 05/14] fix: Received unknown packet id 130 this was mostly because of poor naming, While there are two CServerLinks packets, I accidentally send the play CServerLinks in the login state which has a different packet id --- .../src/client/config/c_server_links.rs | 79 +------------------ .../src/client/login/c_cookie_request.rs | 4 +- .../src/client/play/c_cookie_request.rs | 4 +- .../src/client/play/c_server_links.rs | 79 +------------------ pumpkin-protocol/src/lib.rs | 72 ++++++++++++++++- pumpkin/src/net/packet/login.rs | 12 +-- 6 files changed, 90 insertions(+), 160 deletions(-) diff --git a/pumpkin-protocol/src/client/config/c_server_links.rs b/pumpkin-protocol/src/client/config/c_server_links.rs index b4150d8e..b14547ca 100644 --- a/pumpkin-protocol/src/client/config/c_server_links.rs +++ b/pumpkin-protocol/src/client/config/c_server_links.rs @@ -1,87 +1,16 @@ -use crate::VarInt; -use pumpkin_core::text::TextComponent; +use crate::{Link, VarInt}; use pumpkin_macros::client_packet; -use serde::{Serialize, Serializer}; +use serde::Serialize; #[derive(Serialize)] #[client_packet("config:server_links")] -pub struct CServerLinks<'a> { +pub struct CConfigServerLinks<'a> { links_count: &'a VarInt, links: &'a [Link<'a>], } -impl<'a> CServerLinks<'a> { +impl<'a> CConfigServerLinks<'a> { pub fn new(links_count: &'a VarInt, links: &'a [Link<'a>]) -> Self { Self { links_count, links } } } - -pub enum Label<'a> { - BuiltIn(LinkType), - TextComponent(TextComponent<'a>), -} - -impl Serialize for Label<'_> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - Label::BuiltIn(link_type) => link_type.serialize(serializer), - Label::TextComponent(component) => component.serialize(serializer), - } - } -} - -#[derive(Serialize)] -pub struct Link<'a> { - pub is_built_in: bool, - pub label: Label<'a>, - pub url: &'a String, -} - -impl<'a> Link<'a> { - pub fn new(label: Label<'a>, url: &'a String) -> Self { - Self { - is_built_in: match label { - Label::BuiltIn(_) => true, - Label::TextComponent(_) => false, - }, - label, - url, - } - } -} - -pub enum LinkType { - BugReport, - CommunityGuidelines, - Support, - Status, - Feedback, - Community, - Website, - Forums, - News, - Announcements, -} - -impl Serialize for LinkType { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - LinkType::BugReport => VarInt(0).serialize(serializer), - LinkType::CommunityGuidelines => VarInt(1).serialize(serializer), - LinkType::Support => VarInt(2).serialize(serializer), - LinkType::Status => VarInt(3).serialize(serializer), - LinkType::Feedback => VarInt(4).serialize(serializer), - LinkType::Community => VarInt(5).serialize(serializer), - LinkType::Website => VarInt(6).serialize(serializer), - LinkType::Forums => VarInt(7).serialize(serializer), - LinkType::News => VarInt(8).serialize(serializer), - LinkType::Announcements => VarInt(9).serialize(serializer), - } - } -} diff --git a/pumpkin-protocol/src/client/login/c_cookie_request.rs b/pumpkin-protocol/src/client/login/c_cookie_request.rs index e1749fac..174e8569 100644 --- a/pumpkin-protocol/src/client/login/c_cookie_request.rs +++ b/pumpkin-protocol/src/client/login/c_cookie_request.rs @@ -6,11 +6,11 @@ use crate::codec::identifier::Identifier; #[derive(Serialize)] #[client_packet("login:cookie_request")] /// Requests a cookie that was previously stored. -pub struct CCookieRequest<'a> { +pub struct CLoginCookieRequest<'a> { key: &'a Identifier, } -impl<'a> CCookieRequest<'a> { +impl<'a> CLoginCookieRequest<'a> { pub fn new(key: &'a Identifier) -> Self { Self { key } } diff --git a/pumpkin-protocol/src/client/play/c_cookie_request.rs b/pumpkin-protocol/src/client/play/c_cookie_request.rs index 9b569cc0..fb9bc806 100644 --- a/pumpkin-protocol/src/client/play/c_cookie_request.rs +++ b/pumpkin-protocol/src/client/play/c_cookie_request.rs @@ -6,11 +6,11 @@ use crate::codec::identifier::Identifier; #[derive(Serialize)] #[client_packet("play:cookie_request")] /// Requests a cookie that was previously stored. -pub struct CCookieRequest<'a> { +pub struct CPlayCookieRequest<'a> { key: &'a Identifier, } -impl<'a> CCookieRequest<'a> { +impl<'a> CPlayCookieRequest<'a> { pub fn new(key: &'a Identifier) -> Self { Self { key } } diff --git a/pumpkin-protocol/src/client/play/c_server_links.rs b/pumpkin-protocol/src/client/play/c_server_links.rs index 7619a0c6..a2cb76fa 100644 --- a/pumpkin-protocol/src/client/play/c_server_links.rs +++ b/pumpkin-protocol/src/client/play/c_server_links.rs @@ -1,87 +1,16 @@ -use crate::VarInt; -use pumpkin_core::text::TextComponent; +use crate::{Link, VarInt}; use pumpkin_macros::client_packet; -use serde::{Serialize, Serializer}; +use serde::Serialize; #[derive(Serialize)] #[client_packet("play:server_links")] -pub struct CServerLinks<'a> { +pub struct CPlayServerLinks<'a> { links_count: &'a VarInt, links: &'a [Link<'a>], } -impl<'a> CServerLinks<'a> { +impl<'a> CPlayServerLinks<'a> { pub fn new(links_count: &'a VarInt, links: &'a [Link<'a>]) -> Self { Self { links_count, links } } } - -pub enum Label<'a> { - BuiltIn(LinkType), - TextComponent(TextComponent<'a>), -} - -impl Serialize for Label<'_> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - Label::BuiltIn(link_type) => link_type.serialize(serializer), - Label::TextComponent(component) => component.serialize(serializer), - } - } -} - -#[derive(Serialize)] -pub struct Link<'a> { - pub is_built_in: bool, - pub label: Label<'a>, - pub url: &'a String, -} - -impl<'a> Link<'a> { - pub fn new(label: Label<'a>, url: &'a String) -> Self { - Self { - is_built_in: match label { - Label::BuiltIn(_) => true, - Label::TextComponent(_) => false, - }, - label, - url, - } - } -} - -pub enum LinkType { - BugReport, - CommunityGuidelines, - Support, - Status, - Feedback, - Community, - Website, - Forums, - News, - Announcements, -} - -impl Serialize for LinkType { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - LinkType::BugReport => VarInt(0).serialize(serializer), - LinkType::CommunityGuidelines => VarInt(1).serialize(serializer), - LinkType::Support => VarInt(2).serialize(serializer), - LinkType::Status => VarInt(3).serialize(serializer), - LinkType::Feedback => VarInt(4).serialize(serializer), - LinkType::Community => VarInt(5).serialize(serializer), - LinkType::Website => VarInt(6).serialize(serializer), - LinkType::Forums => VarInt(7).serialize(serializer), - LinkType::News => VarInt(8).serialize(serializer), - LinkType::Announcements => VarInt(9).serialize(serializer), - } - } -} diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index c6156b94..8ef37f7a 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -4,7 +4,7 @@ use bytebuf::{packet_id::Packet, ReadingError}; use bytes::{Buf, BufMut, Bytes}; use codec::{identifier::Identifier, var_int::VarInt}; use pumpkin_core::text::{style::Style, TextComponent}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, Serializer}; pub mod bytebuf; #[cfg(feature = "clientbound")] @@ -186,3 +186,73 @@ impl PositionFlag { flags.iter().fold(0, |acc, flag| acc | flag.get_mask()) } } + +pub enum Label<'a> { + BuiltIn(LinkType), + TextComponent(TextComponent<'a>), +} + +impl Serialize for Label<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Label::BuiltIn(link_type) => link_type.serialize(serializer), + Label::TextComponent(component) => component.serialize(serializer), + } + } +} + +#[derive(Serialize)] +pub struct Link<'a> { + pub is_built_in: bool, + pub label: Label<'a>, + pub url: &'a String, +} + +impl<'a> Link<'a> { + pub fn new(label: Label<'a>, url: &'a String) -> Self { + Self { + is_built_in: match label { + Label::BuiltIn(_) => true, + Label::TextComponent(_) => false, + }, + label, + url, + } + } +} + +pub enum LinkType { + BugReport, + CommunityGuidelines, + Support, + Status, + Feedback, + Community, + Website, + Forums, + News, + Announcements, +} + +impl Serialize for LinkType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + LinkType::BugReport => VarInt(0).serialize(serializer), + LinkType::CommunityGuidelines => VarInt(1).serialize(serializer), + LinkType::Support => VarInt(2).serialize(serializer), + LinkType::Status => VarInt(3).serialize(serializer), + LinkType::Feedback => VarInt(4).serialize(serializer), + LinkType::Community => VarInt(5).serialize(serializer), + LinkType::Website => VarInt(6).serialize(serializer), + LinkType::Forums => VarInt(7).serialize(serializer), + LinkType::News => VarInt(8).serialize(serializer), + LinkType::Announcements => VarInt(9).serialize(serializer), + } + } +} diff --git a/pumpkin/src/net/packet/login.rs b/pumpkin/src/net/packet/login.rs index db61b865..585413d6 100644 --- a/pumpkin/src/net/packet/login.rs +++ b/pumpkin/src/net/packet/login.rs @@ -4,13 +4,12 @@ use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_core::text::TextComponent; use pumpkin_protocol::{ client::{ - config::{CConfigAddResourcePack, CKnownPacks}, + config::{CConfigAddResourcePack, CConfigServerLinks, CKnownPacks}, login::{CLoginSuccess, CSetCompression}, - play::{CServerLinks, Label, Link, LinkType}, }, codec::var_int::VarInt, server::login::{SEncryptionResponse, SLoginCookieResponse, SLoginPluginResponse, SLoginStart}, - ConnectionState, KnownPack, + ConnectionState, KnownPack, Label, Link, LinkType, }; use uuid::Uuid; @@ -326,8 +325,11 @@ impl Client { } if ADVANCED_CONFIG.server_links.enabled { - self.send_packet(&CServerLinks::new(&VarInt(LINKS.len() as i32), &LINKS)) - .await; + self.send_packet(&CConfigServerLinks::new( + &VarInt(LINKS.len() as i32), + &LINKS, + )) + .await; } // known data packs From 53949103cd3b9ceac5629de5988ff98a34af3570 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Tue, 24 Dec 2024 12:40:05 +0100 Subject: [PATCH 06/14] fix: https://github.com/Snowiiii/Pumpkin/issues/406 --- pumpkin/src/entity/player.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index c9e51dd1..bc275f8b 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -562,8 +562,6 @@ impl Player { "Setting the same gamemode as already is" ); self.gamemode.store(gamemode); - // The client is using the same method for setting abilities when receiving the CGameEvent ChangeGameMode packet. - // So we can just update the abilities without sending them. { // use another scope so we instantly unlock abilities let mut abilities = self.abilities.lock().await; @@ -587,6 +585,7 @@ impl Player { } } } + self.send_abilities_update().await; self.living_entity .entity .world @@ -796,7 +795,7 @@ impl Default for Abilities { flying: false, allow_flying: false, creative: false, - fly_speed: 0.4, + fly_speed: 0.05, walk_speed_fov: 0.1, } } From cc83e090f93cf9dbf462539625195dc05688f40d Mon Sep 17 00:00:00 2001 From: we sell insurance <72574589+neeleshpoli@users.noreply.github.com> Date: Tue, 24 Dec 2024 08:25:33 -0600 Subject: [PATCH 07/14] Implement level.dat reading and load the seed for use in world generation (#402) * Implement level.dat reading and use the seed in the world for chunk generation * Add tests * Seperate world info loading from chunk reading * Fix cargo fmt --------- Co-authored-by: Alexander Medvedev --- pumpkin-world/src/level.rs | 22 +++-- pumpkin-world/src/lib.rs | 2 +- pumpkin-world/src/world_info/anvil.rs | 93 ++++++++++++++++++++ pumpkin-world/src/world_info/mod.rs | 34 +++++++ pumpkin-world/test-files/sample-1/level.dat | Bin 0 -> 1530 bytes 5 files changed, 144 insertions(+), 7 deletions(-) create mode 100644 pumpkin-world/src/world_info/anvil.rs create mode 100644 pumpkin-world/src/world_info/mod.rs create mode 100644 pumpkin-world/test-files/sample-1/level.dat diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 70dcdc7a..4c06f5a2 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -15,6 +15,7 @@ use crate::{ anvil::AnvilChunkReader, ChunkData, ChunkParsingError, ChunkReader, ChunkReadingError, }, generation::{get_world_gen, Seed, WorldGenerator}, + world_info::{anvil::AnvilInfoReader, WorldInfo, WorldInfoReader}, }; /// The `Level` module provides functionality for working with chunks within or outside a Minecraft world. @@ -28,6 +29,7 @@ use crate::{ /// For more details on world generation, refer to the `WorldGenerator` module. pub struct Level { pub seed: Seed, + pub world_info: Option, save_file: Option, loaded_chunks: Arc, Arc>>>, chunk_watchers: Arc, usize>>, @@ -55,20 +57,27 @@ impl Level { region_folder.exists(), "World region folder does not exist, despite there being a root folder." ); - // TODO: read seed from level.dat - let seed = get_or_create_seed(); + let save_file = SaveFile { + root_folder, + region_folder, + }; + + // TODO: Load info correctly based on world format type + let world_info_reader = AnvilInfoReader::new(); + let info = world_info_reader + .read_world_info(&save_file) + .expect("Unable to get world info!"); // TODO: Improve error handling + let seed = Seed(info.seed as u64); let world_gen = get_world_gen(seed).into(); // TODO Read Seed from config. Self { seed, world_gen, - save_file: Some(SaveFile { - root_folder, - region_folder, - }), + save_file: Some(save_file), chunk_reader: Arc::new(AnvilChunkReader::new()), loaded_chunks: Arc::new(DashMap::new()), chunk_watchers: Arc::new(DashMap::new()), + world_info: Some(info), } } else { let seed = get_or_create_seed(); @@ -80,6 +89,7 @@ impl Level { chunk_reader: Arc::new(AnvilChunkReader::new()), loaded_chunks: Arc::new(DashMap::new()), chunk_watchers: Arc::new(DashMap::new()), + world_info: None, } } } diff --git a/pumpkin-world/src/lib.rs b/pumpkin-world/src/lib.rs index 3735dbb5..8e26b106 100644 --- a/pumpkin-world/src/lib.rs +++ b/pumpkin-world/src/lib.rs @@ -16,7 +16,7 @@ pub mod dimension; mod generation; pub mod item; pub mod level; - +pub mod world_info; pub const WORLD_HEIGHT: usize = 384; pub const WORLD_LOWEST_Y: i16 = -64; pub const WORLD_MAX_Y: i16 = WORLD_HEIGHT as i16 - WORLD_LOWEST_Y.abs(); diff --git a/pumpkin-world/src/world_info/anvil.rs b/pumpkin-world/src/world_info/anvil.rs new file mode 100644 index 00000000..1132d072 --- /dev/null +++ b/pumpkin-world/src/world_info/anvil.rs @@ -0,0 +1,93 @@ +use std::{fs::OpenOptions, io::Read}; + +use flate2::read::GzDecoder; +use serde::Deserialize; + +use crate::level::SaveFile; + +use super::{WorldInfo, WorldInfoError, WorldInfoReader}; + +pub struct AnvilInfoReader {} + +impl AnvilInfoReader { + pub fn new() -> Self { + Self {} + } +} + +impl WorldInfoReader for AnvilInfoReader { + fn read_world_info(&self, save_file: &SaveFile) -> Result { + let path = save_file.root_folder.join("level.dat"); + + let mut world_info_file = OpenOptions::new().read(true).open(path)?; + + let mut buffer = Vec::new(); + world_info_file.read_to_end(&mut buffer)?; + + let mut decoder = GzDecoder::new(&buffer[..]); + let mut decompressed_data = Vec::new(); + decoder.read_to_end(&mut decompressed_data)?; + + let info = fastnbt::from_bytes::(&decompressed_data) + .map_err(|e| WorldInfoError::DeserializationError(e.to_string()))?; + + Ok(WorldInfo { + seed: info.data.world_gen_settings.seed, + }) + } +} + +impl Default for AnvilInfoReader { + fn default() -> Self { + Self::new() + } +} + +#[derive(Deserialize)] +pub struct LevelDat { + // No idea why its formatted like this + #[serde(rename = "Data")] + pub data: WorldData, +} + +#[derive(Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct WorldData { + pub world_gen_settings: WorldGenSettings, + // TODO: Implement the rest of the fields + // Fields below this comment are being deserialized, but are not being used + pub spawn_x: i32, + pub spawn_y: i32, + pub spawn_z: i32, +} + +#[derive(Deserialize)] +pub struct WorldGenSettings { + pub seed: i64, +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use crate::{ + level::SaveFile, + world_info::{anvil::AnvilInfoReader, WorldInfo, WorldInfoReader}, + }; + + #[test] + fn test_level_dat_reading() { + let world_info = AnvilInfoReader::new(); + let root_folder = PathBuf::from("test-files").join("sample-1"); + let save_file = SaveFile { + root_folder: root_folder.clone(), + region_folder: root_folder, + }; + let expected = WorldInfo { + seed: -79717552349559436, + }; + let info = world_info.read_world_info(&save_file).unwrap(); + + assert_eq!(info, expected); + } +} diff --git a/pumpkin-world/src/world_info/mod.rs b/pumpkin-world/src/world_info/mod.rs new file mode 100644 index 00000000..a1184635 --- /dev/null +++ b/pumpkin-world/src/world_info/mod.rs @@ -0,0 +1,34 @@ +use thiserror::Error; + +use crate::level::SaveFile; + +pub mod anvil; + +pub(crate) trait WorldInfoReader { + fn read_world_info(&self, save_file: &SaveFile) -> Result; +} + +#[derive(Debug, PartialEq)] +pub struct WorldInfo { + pub seed: i64, + // TODO: Implement all fields +} + +#[derive(Error, Debug)] +pub enum WorldInfoError { + #[error("Io error: {0}")] + IoError(std::io::ErrorKind), + #[error("Info not found!")] + InfoNotFound, + #[error("Deserialization error: {0}")] + DeserializationError(String), +} + +impl From for WorldInfoError { + fn from(value: std::io::Error) -> Self { + match value.kind() { + std::io::ErrorKind::NotFound => Self::InfoNotFound, + value => Self::IoError(value), + } + } +} diff --git a/pumpkin-world/test-files/sample-1/level.dat b/pumpkin-world/test-files/sample-1/level.dat new file mode 100644 index 0000000000000000000000000000000000000000..1a527907bf8616dee934fd9e7c1cdce5087c30e7 GIT binary patch literal 1530 zcmVwogCsOVZ<@@Rej*sCGado)?tL*sYI{=QfzRK;?vKAdA^0ni+7Z>J z71ALvLcE_o{PyY(k1WtIlvfQIMN}|NX2?SNv;o3^`j%urkIZqw4W}V~l6f;^Ej7EK zN^sHJ3uSPrU##cGl|EH0kP65&nH_-ATna+I{=G`(eNcF?r>wh*GcLDi3~HLp_%Xc_ z&gC?z9MA~PhB4TGXUJlf5beSnDIoV$CN=kHSM)+>Ss~RU7(jS}uSxkzs*sgP$pPbj zJETgM{Gbmk34!eoFiTBRkIO*_R9yfy{R~Ryc|G;qq!H~w3nRCwe)axm`}<{@+`ga| zdc@#NQ5&HRrwNF18qVTga(T(k=6MFfGnedx`sQ|h^U?kKsOEG^@g-uzOYCifbhW>JkrRQjueS*Bh=cU@k$Q_ zC~BbHMT8Rt+N+To!w(9Ji>{INhzkf5?V9IUfwll!hYBefOM;1)HB!yKDhtDoG&b8MQ6!PxJ3-sMu!p^?zoq` zn{@X-MnB2jSOOCbFo%_Km_Za7e0=jC;{W(#W6>>)uI-g2Vo3wAzyExH^4EubW97ke zFV(xvI!0n;u|-jXs$_Ow&<-|t#c_LfKm`v&O6Iyega;khjiemgF07JDlk027DXvtR zHjiS3$V5&b))g>qPd`T%iM3Bp-p@=$0Q z2~LZnE-HGeR{7BqlTA7dd9QC8LsYr^{PPL!o6x05snq+_Q*^(`WG0GZkC$~SBZ;`o zQu4Md*yf=`JbZ~lm8olP&t!`lB}1n#RyMMWjflR@<`0Sz7e^p^rccT{j~_qWLHCK^ zc7o6lMIJk?jQLrvZx~_r-^QV|U^kVFn^?JFalMMjL=iBKl5+|!*5a<#%6@k45sK zBN|fV<1vqJMfc@Z(;|KhT4Mxy6r_?rKwyx_x=y3QA&H#%QSv^6_w2vhPKfrE5tUQm*r|&W=G+Q1m*hiQL9+t=~ ziswHgTyqi9cRUF@&_0D9&6A{-0=04Fc!Q2+n{ literal 0 HcmV?d00001 From fcf85df518ec64df851ae1c3bc7bc88248a26809 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Tue, 24 Dec 2024 17:48:59 +0100 Subject: [PATCH 08/14] Save level.dat --- pumpkin-core/src/lib.rs | 2 +- pumpkin-world/Cargo.toml | 1 + pumpkin-world/src/chunk/anvil.rs | 8 +- pumpkin-world/src/chunk/mod.rs | 4 +- pumpkin-world/src/level.rs | 119 +++++++++---------- pumpkin-world/src/world_info/anvil.rs | 125 ++++++++++---------- pumpkin-world/src/world_info/mod.rs | 106 ++++++++++++++++- pumpkin-world/test-files/sample-1/level.dat | Bin 1530 -> 0 bytes pumpkin/src/command/commands/cmd_stop.rs | 2 +- pumpkin/src/entity/player.rs | 8 +- pumpkin/src/server/mod.rs | 6 + pumpkin/src/world/mod.rs | 24 +--- pumpkin/src/world/player_chunker.rs | 12 +- 13 files changed, 248 insertions(+), 169 deletions(-) delete mode 100644 pumpkin-world/test-files/sample-1/level.dat diff --git a/pumpkin-core/src/lib.rs b/pumpkin-core/src/lib.rs index 09f36f9b..8ffd25b2 100644 --- a/pumpkin-core/src/lib.rs +++ b/pumpkin-core/src/lib.rs @@ -7,7 +7,7 @@ pub use gamemode::GameMode; use serde::{Deserialize, Serialize}; -#[derive(PartialEq, Serialize, Deserialize)] +#[derive(PartialEq, Serialize, Deserialize, Clone)] pub enum Difficulty { Peaceful, Easy, diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index fcbc14a5..495ee3ff 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -4,6 +4,7 @@ version.workspace = true edition.workspace = true [dependencies] +pumpkin-nbt = { path = "../pumpkin-nbt" } pumpkin-core = { path = "../pumpkin-core" } pumpkin-config = { path = "../pumpkin-config" } pumpkin-macros = { path = "../pumpkin-macros" } diff --git a/pumpkin-world/src/chunk/anvil.rs b/pumpkin-world/src/chunk/anvil.rs index 86389be6..2391537f 100644 --- a/pumpkin-world/src/chunk/anvil.rs +++ b/pumpkin-world/src/chunk/anvil.rs @@ -5,7 +5,7 @@ use std::{ use flate2::bufread::{GzDecoder, ZlibDecoder}; -use crate::level::SaveFile; +use crate::level::LevelFolder; use super::{ChunkData, ChunkReader, ChunkReadingError, CompressionError}; @@ -87,7 +87,7 @@ impl Compression { impl ChunkReader for AnvilChunkReader { fn read_chunk( &self, - save_file: &SaveFile, + save_file: &LevelFolder, at: &pumpkin_core::math::vector2::Vector2, ) -> Result { let region = (at.x >> 5, at.z >> 5); @@ -168,14 +168,14 @@ mod tests { use crate::{ chunk::{anvil::AnvilChunkReader, ChunkReader, ChunkReadingError}, - level::SaveFile, + level::LevelFolder, }; #[test] fn not_existing() { let region_path = PathBuf::from("not_existing"); let result = AnvilChunkReader::new().read_chunk( - &SaveFile { + &LevelFolder { root_folder: PathBuf::from(""), region_folder: region_path, }, diff --git a/pumpkin-world/src/chunk/mod.rs b/pumpkin-world/src/chunk/mod.rs index fcbb53f1..8473e030 100644 --- a/pumpkin-world/src/chunk/mod.rs +++ b/pumpkin-world/src/chunk/mod.rs @@ -9,7 +9,7 @@ use thiserror::Error; use crate::{ block::BlockState, coordinates::{ChunkRelativeBlockCoordinates, Height}, - level::SaveFile, + level::LevelFolder, WORLD_HEIGHT, }; @@ -22,7 +22,7 @@ const CHUNK_VOLUME: usize = CHUNK_AREA * WORLD_HEIGHT; pub trait ChunkReader: Sync + Send { fn read_chunk( &self, - save_file: &SaveFile, + save_file: &LevelFolder, at: &Vector2, ) -> Result; } diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 4c06f5a2..930363ec 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -2,7 +2,6 @@ use std::{path::PathBuf, sync::Arc}; use dashmap::{DashMap, Entry}; use num_traits::Zero; -use pumpkin_config::BASIC_CONFIG; use pumpkin_core::math::vector2::Vector2; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use tokio::{ @@ -15,7 +14,7 @@ use crate::{ anvil::AnvilChunkReader, ChunkData, ChunkParsingError, ChunkReader, ChunkReadingError, }, generation::{get_world_gen, Seed, WorldGenerator}, - world_info::{anvil::AnvilInfoReader, WorldInfo, WorldInfoReader}, + world_info::{anvil::AnvilLevelInfo, LevelData, WorldInfoReader, WorldInfoWriter}, }; /// The `Level` module provides functionality for working with chunks within or outside a Minecraft world. @@ -29,8 +28,9 @@ use crate::{ /// For more details on world generation, refer to the `WorldGenerator` module. pub struct Level { pub seed: Seed, - pub world_info: Option, - save_file: Option, + pub level_info: LevelData, + world_info_writer: Arc, + level_folder: LevelFolder, loaded_chunks: Arc, Arc>>>, chunk_watchers: Arc, usize>>, chunk_reader: Arc, @@ -38,60 +38,53 @@ pub struct Level { } #[derive(Clone)] -pub struct SaveFile { +pub struct LevelFolder { pub root_folder: PathBuf, pub region_folder: PathBuf, } -fn get_or_create_seed() -> Seed { - // TODO: if there is a seed in the config (!= 0) use it. Otherwise make a random one - Seed::from(BASIC_CONFIG.seed.as_str()) -} - impl Level { pub fn from_root_folder(root_folder: PathBuf) -> Self { // If we are using an already existing world we want to read the seed from the level.dat, If not we want to check if there is a seed in the config, if not lets create a random one - if root_folder.exists() { - let region_folder = root_folder.join("region"); - assert!( - region_folder.exists(), - "World region folder does not exist, despite there being a root folder." - ); - let save_file = SaveFile { - root_folder, - region_folder, - }; + let region_folder = root_folder.join("region"); + if !region_folder.exists() { + std::fs::create_dir_all(®ion_folder).expect("Failed to create Region folder"); + } + let level_folder = LevelFolder { + root_folder, + region_folder, + }; - // TODO: Load info correctly based on world format type - let world_info_reader = AnvilInfoReader::new(); - let info = world_info_reader - .read_world_info(&save_file) - .expect("Unable to get world info!"); // TODO: Improve error handling - let seed = Seed(info.seed as u64); - let world_gen = get_world_gen(seed).into(); // TODO Read Seed from config. + // TODO: Load info correctly based on world format type + let level_info = AnvilLevelInfo + .read_world_info(&level_folder) + .unwrap_or_default(); // TODO: Improve error handling + let seed = Seed(level_info.world_gen_settings.seed as u64); + let world_gen = get_world_gen(seed).into(); - Self { - seed, - world_gen, - save_file: Some(save_file), - chunk_reader: Arc::new(AnvilChunkReader::new()), - loaded_chunks: Arc::new(DashMap::new()), - chunk_watchers: Arc::new(DashMap::new()), - world_info: Some(info), - } - } else { - let seed = get_or_create_seed(); - let world_gen = get_world_gen(seed).into(); - Self { - seed, - world_gen, - save_file: None, - chunk_reader: Arc::new(AnvilChunkReader::new()), - loaded_chunks: Arc::new(DashMap::new()), - chunk_watchers: Arc::new(DashMap::new()), - world_info: None, - } + Self { + seed, + world_gen, + world_info_writer: Arc::new(AnvilLevelInfo), + level_folder, + chunk_reader: Arc::new(AnvilChunkReader::new()), + loaded_chunks: Arc::new(DashMap::new()), + chunk_watchers: Arc::new(DashMap::new()), + level_info, + } + } + + pub async fn save(&self) { + log::info!("Saving level..."); + // lets first save all chunks + for chunk in self.loaded_chunks.iter() { + let chunk = chunk.read().await; + self.clean_chunk(&chunk.position); } + // then lets save the world info + self.world_info_writer + .write_world_info(self.level_info.clone(), &self.level_folder) + .expect("Failed to save world info"); } pub fn get_block() {} @@ -205,10 +198,10 @@ impl Level { fn load_chunk_from_save( chunk_reader: Arc, - save_file: SaveFile, + save_file: &LevelFolder, chunk_pos: Vector2, ) -> Result>>, ChunkReadingError> { - match chunk_reader.read_chunk(&save_file, &chunk_pos) { + match chunk_reader.read_chunk(save_file, &chunk_pos) { Ok(data) => Ok(Some(Arc::new(RwLock::new(data)))), Err( ChunkReadingError::ChunkNotExist @@ -233,7 +226,7 @@ impl Level { let channel = channel.clone(); let loaded_chunks = self.loaded_chunks.clone(); let chunk_reader = self.chunk_reader.clone(); - let save_file = self.save_file.clone(); + let level_info = self.level_folder.clone(); let world_gen = self.world_gen.clone(); let chunk_pos = *at; @@ -241,20 +234,18 @@ impl Level { .get(&chunk_pos) .map(|entry| entry.value().clone()) .unwrap_or_else(|| { - let loaded_chunk = save_file - .and_then(|save_file| { - match Self::load_chunk_from_save(chunk_reader, save_file, chunk_pos) { - Ok(chunk) => chunk, - Err(err) => { - log::error!( - "Failed to read chunk (regenerating) {:?}: {:?}", - chunk_pos, - err - ); - None - } + let loaded_chunk = + match Self::load_chunk_from_save(chunk_reader, &level_info, chunk_pos) { + Ok(chunk) => chunk, + Err(err) => { + log::error!( + "Failed to read chunk (regenerating) {:?}: {:?}", + chunk_pos, + err + ); + None } - }) + } .unwrap_or_else(|| { Arc::new(RwLock::new(world_gen.generate_chunk(chunk_pos))) }); diff --git a/pumpkin-world/src/world_info/anvil.rs b/pumpkin-world/src/world_info/anvil.rs index 1132d072..080d61e1 100644 --- a/pumpkin-world/src/world_info/anvil.rs +++ b/pumpkin-world/src/world_info/anvil.rs @@ -1,29 +1,30 @@ -use std::{fs::OpenOptions, io::Read}; +use std::{ + fs::OpenOptions, + io::{Read, Write}, + time::{SystemTime, UNIX_EPOCH}, +}; -use flate2::read::GzDecoder; -use serde::Deserialize; +use flate2::{read::GzDecoder, write::GzEncoder, Compression}; +use serde::{Deserialize, Serialize}; -use crate::level::SaveFile; +use crate::level::LevelFolder; -use super::{WorldInfo, WorldInfoError, WorldInfoReader}; +use super::{LevelData, WorldInfoError, WorldInfoReader, WorldInfoWriter}; -pub struct AnvilInfoReader {} +const LEVEL_DAT_FILE_NAME: &str = "level.dat"; -impl AnvilInfoReader { - pub fn new() -> Self { - Self {} - } -} +pub struct AnvilLevelInfo; -impl WorldInfoReader for AnvilInfoReader { - fn read_world_info(&self, save_file: &SaveFile) -> Result { - let path = save_file.root_folder.join("level.dat"); +impl WorldInfoReader for AnvilLevelInfo { + fn read_world_info(&self, level_folder: &LevelFolder) -> Result { + let path = level_folder.root_folder.join(LEVEL_DAT_FILE_NAME); let mut world_info_file = OpenOptions::new().read(true).open(path)?; let mut buffer = Vec::new(); world_info_file.read_to_end(&mut buffer)?; + // try to decompress using GZip let mut decoder = GzDecoder::new(&buffer[..]); let mut decompressed_data = Vec::new(); decoder.read_to_end(&mut decompressed_data)?; @@ -31,63 +32,57 @@ impl WorldInfoReader for AnvilInfoReader { let info = fastnbt::from_bytes::(&decompressed_data) .map_err(|e| WorldInfoError::DeserializationError(e.to_string()))?; - Ok(WorldInfo { - seed: info.data.world_gen_settings.seed, - }) + // todo check version + + Ok(info.data) } } -impl Default for AnvilInfoReader { - fn default() -> Self { - Self::new() +impl WorldInfoWriter for AnvilLevelInfo { + fn write_world_info( + &self, + info: LevelData, + level_folder: &LevelFolder, + ) -> Result<(), WorldInfoError> { + let start = SystemTime::now(); + let since_the_epoch = start + .duration_since(UNIX_EPOCH) + .expect("Time went backwards"); + let level = LevelDat { + data: LevelData { + allow_commands: info.allow_commands, + data_version: info.data_version, + difficulty: info.difficulty, + world_gen_settings: info.world_gen_settings, + last_played: since_the_epoch.as_millis() as i64, + level_name: info.level_name, + spawn_x: info.spawn_x, + spawn_y: info.spawn_y, + spawn_z: info.spawn_z, + nbt_version: info.nbt_version, + version: info.version, + }, + }; + // convert it into nbt + let nbt = pumpkin_nbt::serializer::to_bytes_unnamed(&level).unwrap(); + // now compress using GZip, TODO: im not sure about the to_vec, but writer is not implemented for BytesMut, see https://github.com/tokio-rs/bytes/pull/478 + let mut encoder = GzEncoder::new(nbt.to_vec(), Compression::best()); + let compressed_data = Vec::new(); + encoder.write_all(&compressed_data)?; + + // open file + let path = level_folder.root_folder.join(LEVEL_DAT_FILE_NAME); + let mut world_info_file = OpenOptions::new().write(true).open(path)?; + // write compressed data into file + world_info_file.write_all(&compressed_data).unwrap(); + + Ok(()) } } -#[derive(Deserialize)] +#[derive(Serialize, Deserialize)] pub struct LevelDat { - // No idea why its formatted like this + // This tag contains all the level data. #[serde(rename = "Data")] - pub data: WorldData, -} - -#[derive(Deserialize)] -#[serde(rename_all = "PascalCase")] -pub struct WorldData { - pub world_gen_settings: WorldGenSettings, - // TODO: Implement the rest of the fields - // Fields below this comment are being deserialized, but are not being used - pub spawn_x: i32, - pub spawn_y: i32, - pub spawn_z: i32, -} - -#[derive(Deserialize)] -pub struct WorldGenSettings { - pub seed: i64, -} - -#[cfg(test)] -mod tests { - use std::path::PathBuf; - - use crate::{ - level::SaveFile, - world_info::{anvil::AnvilInfoReader, WorldInfo, WorldInfoReader}, - }; - - #[test] - fn test_level_dat_reading() { - let world_info = AnvilInfoReader::new(); - let root_folder = PathBuf::from("test-files").join("sample-1"); - let save_file = SaveFile { - root_folder: root_folder.clone(), - region_folder: root_folder, - }; - let expected = WorldInfo { - seed: -79717552349559436, - }; - let info = world_info.read_world_info(&save_file).unwrap(); - - assert_eq!(info, expected); - } + pub data: LevelData, } diff --git a/pumpkin-world/src/world_info/mod.rs b/pumpkin-world/src/world_info/mod.rs index a1184635..d06be497 100644 --- a/pumpkin-world/src/world_info/mod.rs +++ b/pumpkin-world/src/world_info/mod.rs @@ -1,17 +1,113 @@ +use pumpkin_config::BASIC_CONFIG; +use pumpkin_core::Difficulty; +use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::level::SaveFile; +use crate::{generation::Seed, level::LevelFolder}; pub mod anvil; pub(crate) trait WorldInfoReader { - fn read_world_info(&self, save_file: &SaveFile) -> Result; + fn read_world_info(&self, level_folder: &LevelFolder) -> Result; } -#[derive(Debug, PartialEq)] -pub struct WorldInfo { +pub(crate) trait WorldInfoWriter: Sync + Send { + fn write_world_info( + &self, + info: LevelData, + level_folder: &LevelFolder, + ) -> Result<(), WorldInfoError>; +} + +#[derive(Serialize, Deserialize, Clone)] +#[serde(rename_all = "PascalCase")] +pub struct LevelData { + // true if cheats are enabled. + pub allow_commands: bool, + // An integer displaying the data version. + pub data_version: i32, + // The current difficulty setting. + pub difficulty: Difficulty, + // the generation settings for each dimension. + pub world_gen_settings: WorldGenSettings, + // The Unix time in milliseconds when the level was last loaded. + pub last_played: i64, + // The name of the level. + pub level_name: String, + // The X coordinate of the world spawn. + pub spawn_x: i32, + // The Y coordinate of the world spawn. + pub spawn_y: i32, + // The Z coordinate of the world spawn. + pub spawn_z: i32, + #[serde(rename = "version")] + // The NBT version of the level + pub nbt_version: i32, + #[serde(rename = "Version")] + pub version: WorldVersion, + // TODO: Implement the rest of the fields +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct WorldGenSettings { + // the numerical seed of the world pub seed: i64, - // TODO: Implement all fields +} + +fn get_or_create_seed() -> Seed { + // TODO: if there is a seed in the config (!= 0) use it. Otherwise make a random one + Seed::from(BASIC_CONFIG.seed.as_str()) +} + +impl Default for WorldGenSettings { + fn default() -> Self { + Self { + seed: get_or_create_seed().0 as i64, + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +#[serde(rename_all = "PascalCase")] +pub struct WorldVersion { + // The version name as a string, e.g. "15w32b". + pub name: String, + // An integer displaying the data version. + pub id: i32, + // Whether the version is a snapshot or not. + pub snapshot: bool, + // Developing series. In 1.18 experimental snapshots, it was set to "ccpreview". In others, set to "main". + pub series: String, +} + +impl Default for WorldVersion { + fn default() -> Self { + Self { + name: "1.24.4".to_string(), + id: -1, + snapshot: false, + series: "main".to_string(), + } + } +} + +impl Default for LevelData { + fn default() -> Self { + Self { + allow_commands: true, + // TODO + data_version: -1, + difficulty: Difficulty::Normal, + world_gen_settings: Default::default(), + last_played: -1, + level_name: "world".to_string(), + spawn_x: 0, + spawn_y: 200, + spawn_z: 0, + nbt_version: -1, + version: Default::default(), + } + } } #[derive(Error, Debug)] diff --git a/pumpkin-world/test-files/sample-1/level.dat b/pumpkin-world/test-files/sample-1/level.dat deleted file mode 100644 index 1a527907bf8616dee934fd9e7c1cdce5087c30e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1530 zcmVwogCsOVZ<@@Rej*sCGado)?tL*sYI{=QfzRK;?vKAdA^0ni+7Z>J z71ALvLcE_o{PyY(k1WtIlvfQIMN}|NX2?SNv;o3^`j%urkIZqw4W}V~l6f;^Ej7EK zN^sHJ3uSPrU##cGl|EH0kP65&nH_-ATna+I{=G`(eNcF?r>wh*GcLDi3~HLp_%Xc_ z&gC?z9MA~PhB4TGXUJlf5beSnDIoV$CN=kHSM)+>Ss~RU7(jS}uSxkzs*sgP$pPbj zJETgM{Gbmk34!eoFiTBRkIO*_R9yfy{R~Ryc|G;qq!H~w3nRCwe)axm`}<{@+`ga| zdc@#NQ5&HRrwNF18qVTga(T(k=6MFfGnedx`sQ|h^U?kKsOEG^@g-uzOYCifbhW>JkrRQjueS*Bh=cU@k$Q_ zC~BbHMT8Rt+N+To!w(9Ji>{INhzkf5?V9IUfwll!hYBefOM;1)HB!yKDhtDoG&b8MQ6!PxJ3-sMu!p^?zoq` zn{@X-MnB2jSOOCbFo%_Km_Za7e0=jC;{W(#W6>>)uI-g2Vo3wAzyExH^4EubW97ke zFV(xvI!0n;u|-jXs$_Ow&<-|t#c_LfKm`v&O6Iyega;khjiemgF07JDlk027DXvtR zHjiS3$V5&b))g>qPd`T%iM3Bp-p@=$0Q z2~LZnE-HGeR{7BqlTA7dd9QC8LsYr^{PPL!o6x05snq+_Q*^(`WG0GZkC$~SBZ;`o zQu4Md*yf=`JbZ~lm8olP&t!`lB}1n#RyMMWjflR@<`0Sz7e^p^rccT{j~_qWLHCK^ zc7o6lMIJk?jQLrvZx~_r-^QV|U^kVFn^?JFalMMjL=iBKl5+|!*5a<#%6@k45sK zBN|fV<1vqJMfc@Z(;|KhT4Mxy6r_?rKwyx_x=y3QA&H#%QSv^6_w2vhPKfrE5tUQm*r|&W=G+Q1m*hiQL9+t=~ ziswHgTyqi9cRUF@&_0D9&6A{-0=04Fc!Q2+n{ diff --git a/pumpkin/src/command/commands/cmd_stop.rs b/pumpkin/src/command/commands/cmd_stop.rs index 0f1bc7af..de7803b1 100644 --- a/pumpkin/src/command/commands/cmd_stop.rs +++ b/pumpkin/src/command/commands/cmd_stop.rs @@ -32,7 +32,7 @@ impl CommandExecutor for StopExecutor { for player in server.get_all_players().await { player.kick(kick_message.clone()).await; } - + server.save().await; std::process::exit(0) } } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index bc275f8b..dbc84e74 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -220,18 +220,18 @@ impl Player { ); // Decrement value of watched chunks - let chunks_to_clean = world.mark_chunks_as_not_watched(&radial_chunks); + let chunks_to_clean = world.level.mark_chunks_as_not_watched(&radial_chunks); // Remove chunks with no watchers from the cache - world.clean_chunks(&chunks_to_clean); + world.level.clean_chunks(&chunks_to_clean); // Remove left over entries from all possiblily loaded chunks - world.clean_memory(&radial_chunks); + world.level.clean_memory(&radial_chunks); log::debug!( "Removed player id {} ({}) ({} chunks remain cached)", self.gameprofile.name, self.client.id, - self.world().get_cached_chunk_len() + self.world().level.loaded_chunk_count() ); //self.world().level.list_cached(); diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index d8f0fc83..c672eb2c 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -174,6 +174,12 @@ impl Server { self.server_listing.lock().await.remove_player(); } + pub async fn save(&self) { + for world in &self.worlds { + world.save().await; + } + } + pub async fn try_get_container( &self, player_id: EntityId, diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 9380f1bb..02c13b10 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -118,6 +118,10 @@ impl World { } } + pub async fn save(&self) { + self.level.save().await; + } + /// Broadcasts a packet to all connected players within the world. /// /// Sends the specified packet to every player currently logged in to the world. @@ -523,26 +527,6 @@ impl World { player.set_health(20.0, 20, 20.0).await; } - pub fn mark_chunks_as_not_watched(&self, chunks: &[Vector2]) -> Vec> { - self.level.mark_chunks_as_not_watched(chunks) - } - - pub fn mark_chunks_as_watched(&self, chunks: &[Vector2]) { - self.level.mark_chunks_as_newly_watched(chunks); - } - - pub fn clean_chunks(&self, chunks: &[Vector2]) { - self.level.clean_chunks(chunks); - } - - pub fn clean_memory(&self, chunks_to_check: &[Vector2]) { - self.level.clean_memory(chunks_to_check); - } - - pub fn get_cached_chunk_len(&self) -> usize { - self.level.loaded_chunk_count() - } - /// IMPORTANT: Chunks have to be non-empty fn spawn_world_chunks( &self, diff --git a/pumpkin/src/world/player_chunker.rs b/pumpkin/src/world/player_chunker.rs index c0ba854e..98e1157d 100644 --- a/pumpkin/src/world/player_chunker.rs +++ b/pumpkin/src/world/player_chunker.rs @@ -79,12 +79,18 @@ pub async fn update_position(player: &Arc) { // Make sure the watched section and the chunk watcher updates are async atomic. We want to // ensure what we unload when the player disconnects is correct - entity.world.mark_chunks_as_watched(&loading_chunks); - let chunks_to_clean = entity.world.mark_chunks_as_not_watched(&unloading_chunks); + entity + .world + .level + .mark_chunks_as_newly_watched(&loading_chunks); + let chunks_to_clean = entity + .world + .level + .mark_chunks_as_not_watched(&unloading_chunks); player.watched_section.store(new_cylindrical); if !chunks_to_clean.is_empty() { - entity.world.clean_chunks(&chunks_to_clean); + entity.world.level.clean_chunks(&chunks_to_clean); // This can take a little if we are sending a bunch of packets, queue it up :p let client = player.client.clone(); From 5e60dba9d687f8d353a97d41e987bd131f4a9f39 Mon Sep 17 00:00:00 2001 From: kralverde <80051564+kralverde@users.noreply.github.com> Date: Tue, 24 Dec 2024 12:05:04 -0500 Subject: [PATCH 09/14] fix seed command output (#403) --- pumpkin/src/command/commands/cmd_seed.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pumpkin/src/command/commands/cmd_seed.rs b/pumpkin/src/command/commands/cmd_seed.rs index 18b4c360..fb93acb0 100644 --- a/pumpkin/src/command/commands/cmd_seed.rs +++ b/pumpkin/src/command/commands/cmd_seed.rs @@ -24,11 +24,9 @@ impl CommandExecutor for PumpkinExecutor { _args: &ConsumedArgs<'a>, ) -> Result<(), CommandError> { let seed = match sender { - CommandSender::Player(player) => { - player.living_entity.entity.world.level.seed.0.to_string() - } + CommandSender::Player(player) => player.living_entity.entity.world.level.seed.0, _ => match server.worlds.first() { - Some(world) => world.level.seed.0.to_string(), + Some(world) => world.level.seed.0, None => { return Err(CommandError::GeneralCommandIssue( "Unable to get Seed".to_string(), @@ -36,6 +34,7 @@ impl CommandExecutor for PumpkinExecutor { } }, }; + let seed = (seed as i64).to_string(); sender .send_message( From f5c20fb5d9540487fefc4e71077e4889b77593ae Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Tue, 24 Dec 2024 20:02:27 +0100 Subject: [PATCH 10/14] Create configs in a folder (#407) * create configs in a folder * use `env::current_dir()` --- pumpkin-config/src/lib.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index 6e2035c0..5e82ae33 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -5,7 +5,7 @@ use query::QueryConfig; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::{ - fs, + env, fs, net::{Ipv4Addr, SocketAddr}, num::NonZeroU8, path::Path, @@ -36,6 +36,8 @@ mod server_links; use proxy::ProxyConfig; use resource_pack::ResourcePackConfig; +const CONFIG_ROOT_FOLDER: &str = "config/"; + pub static ADVANCED_CONFIG: LazyLock = LazyLock::new(AdvancedConfiguration::load); @@ -126,26 +128,32 @@ trait LoadConfiguration { where Self: Sized + Default + Serialize + DeserializeOwned, { - let path = Self::get_path(); + let exe_dir = env::current_dir().unwrap(); + let config_dir = exe_dir.join(CONFIG_ROOT_FOLDER); + if !config_dir.exists() { + log::debug!("creating new config root folder"); + fs::create_dir(&config_dir).expect("Failed to create Config root folder"); + } + let path = config_dir.join(Self::get_path()); let config = if path.exists() { - let file_content = fs::read_to_string(path) - .unwrap_or_else(|_| panic!("Couldn't read configuration file at {:?}", path)); + let file_content = fs::read_to_string(&path) + .unwrap_or_else(|_| panic!("Couldn't read configuration file at {:?}", &path)); toml::from_str(&file_content).unwrap_or_else(|err| { panic!( "Couldn't parse config at {:?}. Reason: {}. This is is proberbly caused by an Config update, Just delete the old Config and start Pumpkin again", - path, + &path, err.message() ) }) } else { let content = Self::default(); - if let Err(err) = fs::write(path, toml::to_string(&content).unwrap()) { + if let Err(err) = fs::write(&path, toml::to_string(&content).unwrap()) { warn!( "Couldn't write default config to {:?}. Reason: {}. This is is proberbly caused by an Config update, Just delete the old Config and start Pumpkin again", - path, err + &path, err ); } From 2ae6b08c4752bbfd2f31ee35e735b982cd66d6ca Mon Sep 17 00:00:00 2001 From: Commandcracker <49335821+Commandcracker@users.noreply.github.com> Date: Wed, 25 Dec 2024 13:32:21 +0100 Subject: [PATCH 11/14] Upgrade Alpine to 3.21 (see CVE-2024-9143) (#409) --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 57db48cf..760bc635 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1-alpine3.20 AS builder +FROM rust:1-alpine3.21 AS builder ARG GIT_VERSION=Docker ENV GIT_VERSION=$GIT_VERSION ENV RUSTFLAGS="-C target-feature=-crt-static -C target-cpu=native" @@ -16,7 +16,7 @@ RUN --mount=type=cache,sharing=private,target=/pumpkin/target \ # strip debug symbols from binary RUN strip pumpkin.release -FROM alpine:3.20 +FROM alpine:3.21 # Identifying information for registries like ghcr.io LABEL org.opencontainers.image.source=https://github.com/Snowiiii/Pumpkin From 991ac37eaeb30d6c66a653c43fc4571141b924e3 Mon Sep 17 00:00:00 2001 From: DataModel <183248792+DataM0del@users.noreply.github.com> Date: Wed, 25 Dec 2024 07:59:44 -0500 Subject: [PATCH 12/14] fix(pumpkin-inventory/src/drag_handler.rs): divide by 0 (#393) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(pumpkin-inventory/src/drag_handler.rs): divide by 0 Please work Also, commit made on mobile using Built-in GitHub editor 🔥 * fix(pumpkin-inventory/src/drag_handler.rs): syntax errors --- pumpkin-inventory/src/drag_handler.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pumpkin-inventory/src/drag_handler.rs b/pumpkin-inventory/src/drag_handler.rs index fefcd605..4a6ea438 100644 --- a/pumpkin-inventory/src/drag_handler.rs +++ b/pumpkin-inventory/src/drag_handler.rs @@ -120,8 +120,12 @@ impl DragHandler { let changing_slots = drag.possibly_changing_slots(&slots_cloned, carried_item.item_id); let amount_of_slots = changing_slots.clone().count(); - let (amount_per_slot, remainder) = - (carried_item.item_count as usize).div_rem_euclid(&amount_of_slots); + let (amount_per_slot, remainder) = if amount_of_slots == 0 { + // TODO: please work lol + (1, 0) + } else { + (carried_item.item_count as usize).div_rem_euclid(&amount_of_slots) + }; let mut item_in_each_slot = *carried_item; item_in_each_slot.item_count = amount_per_slot as u8; changing_slots.for_each(|slot| *slots[slot] = Some(item_in_each_slot)); From 0e9b1af76004cccbfce5cb8fd5c40c3d830a01e9 Mon Sep 17 00:00:00 2001 From: kralverde <80051564+kralverde@users.noreply.github.com> Date: Wed, 25 Dec 2024 08:43:26 -0500 Subject: [PATCH 13/14] make command dispatcher writable (#405) --- pumpkin/src/command/args/arg_command.rs | 10 ++++---- pumpkin/src/command/args/mod.rs | 2 +- pumpkin/src/command/client_cmd_suggestions.rs | 8 ++++--- pumpkin/src/command/commands/cmd_help.rs | 4 ++-- pumpkin/src/command/dispatcher.rs | 24 ++++++++++++++----- pumpkin/src/command/mod.rs | 4 ++-- pumpkin/src/command/tree.rs | 5 ++-- pumpkin/src/main.rs | 2 +- pumpkin/src/net/packet/play.rs | 8 +++---- pumpkin/src/net/rcon/mod.rs | 2 +- pumpkin/src/server/mod.rs | 4 ++-- pumpkin/src/world/mod.rs | 4 +--- 12 files changed, 44 insertions(+), 33 deletions(-) diff --git a/pumpkin/src/command/args/arg_command.rs b/pumpkin/src/command/args/arg_command.rs index 93955987..9055fde1 100644 --- a/pumpkin/src/command/args/arg_command.rs +++ b/pumpkin/src/command/args/arg_command.rs @@ -37,10 +37,10 @@ impl ArgumentConsumer for CommandTreeArgumentConsumer { ) -> Option> { let s = args.pop()?; - let dispatcher = &server.command_dispatcher; - return dispatcher + let dispatcher = server.command_dispatcher.read().await; + dispatcher .get_tree(s) - .map_or_else(|_| None, |tree| Some(Arg::CommandTree(tree))); + .map_or_else(|_| None, |tree| Some(Arg::CommandTree(tree))) } async fn suggest<'a>( @@ -53,8 +53,8 @@ impl ArgumentConsumer for CommandTreeArgumentConsumer { return Ok(None); }; - let suggestions = server - .command_dispatcher + let dispatcher = server.command_dispatcher.read().await; + let suggestions = dispatcher .commands .keys() .filter(|suggestion| suggestion.starts_with(input)) diff --git a/pumpkin/src/command/args/mod.rs b/pumpkin/src/command/args/mod.rs index f27d850a..8342386b 100644 --- a/pumpkin/src/command/args/mod.rs +++ b/pumpkin/src/command/args/mod.rs @@ -83,7 +83,7 @@ pub(crate) enum Arg<'a> { Pos2D(Vector2), Rotation(f32, f32), GameMode(GameMode), - CommandTree(&'a CommandTree<'a>), + CommandTree(CommandTree<'a>), Item(&'a str), ResourceLocation(&'a str), Block(&'a str), diff --git a/pumpkin/src/command/client_cmd_suggestions.rs b/pumpkin/src/command/client_cmd_suggestions.rs index a1bec5da..7c8c7b2f 100644 --- a/pumpkin/src/command/client_cmd_suggestions.rs +++ b/pumpkin/src/command/client_cmd_suggestions.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use pumpkin_protocol::client::play::{CCommands, ProtoNode, ProtoNodeType}; +use tokio::sync::RwLock; use crate::entity::player::Player; @@ -11,11 +12,12 @@ use super::{ pub async fn send_c_commands_packet<'a>( player: &Arc, - dispatcher: &'a CommandDispatcher<'a>, + dispatcher: &RwLock>, ) { let cmd_src = super::CommandSender::Player(player.clone()); let mut first_level = Vec::new(); + let dispatcher = dispatcher.read().await; for key in dispatcher.commands.keys() { let Ok(tree) = dispatcher.get_tree(key) else { continue; @@ -72,8 +74,8 @@ impl<'a> ProtoNodeBuilder<'a> { fn nodes_to_proto_node_builders<'a>( cmd_src: &super::CommandSender, - nodes: &'a [Node<'a>], - children: &'a [usize], + nodes: &[Node<'a>], + children: &[usize], ) -> (bool, Vec>) { let mut child_nodes = Vec::new(); let mut is_executable = false; diff --git a/pumpkin/src/command/commands/cmd_help.rs b/pumpkin/src/command/commands/cmd_help.rs index 8c714dcb..e382febf 100644 --- a/pumpkin/src/command/commands/cmd_help.rs +++ b/pumpkin/src/command/commands/cmd_help.rs @@ -121,8 +121,8 @@ impl CommandExecutor for BaseHelpExecutor { } }; - let mut commands: Vec<&CommandTree> = server - .command_dispatcher + let dispatcher = server.command_dispatcher.read().await; + let mut commands: Vec<&CommandTree> = dispatcher .commands .values() .filter_map(|cmd| match cmd { diff --git a/pumpkin/src/command/dispatcher.rs b/pumpkin/src/command/dispatcher.rs index e63a67ac..b2dc27ae 100644 --- a/pumpkin/src/command/dispatcher.rs +++ b/pumpkin/src/command/dispatcher.rs @@ -102,7 +102,7 @@ impl<'a> CommandDispatcher<'a> { // try paths and collect the nodes that fail // todo: make this more fine-grained for path in tree.iter_paths() { - match Self::try_find_suggestions_on_path(src, server, &path, tree, &mut raw_args, cmd) + match Self::try_find_suggestions_on_path(src, server, &path, &tree, &mut raw_args, cmd) .await { Err(InvalidConsumption(s)) => { @@ -151,7 +151,7 @@ impl<'a> CommandDispatcher<'a> { // try paths until fitting path is found for path in tree.iter_paths() { - if Self::try_is_fitting_path(src, server, &path, tree, &mut raw_args.clone()).await? { + if Self::try_is_fitting_path(src, server, &path, &tree, &mut raw_args.clone()).await? { return Ok(()); } } @@ -160,22 +160,22 @@ impl<'a> CommandDispatcher<'a> { ))) } - pub(crate) fn get_tree(&'a self, key: &str) -> Result<&'a CommandTree<'a>, CommandError> { + pub(crate) fn get_tree(&self, key: &str) -> Result, CommandError> { let command = self .commands .get(key) .ok_or(GeneralCommandIssue("Command not found".to_string()))?; match command { - Command::Tree(tree) => Ok(tree), + Command::Tree(tree) => Ok(tree.clone()), Command::Alias(target) => { - let Some(Command::Tree(tree)) = &self.commands.get(target) else { + let Some(Command::Tree(tree)) = self.commands.get(target) else { log::error!("Error while parsing command alias \"{key}\": pointing to \"{target}\" which is not a valid tree"); return Err(GeneralCommandIssue( "Internal Error (See logs for details)".into(), )); }; - Ok(tree) + Ok(tree.clone()) } } } @@ -282,3 +282,15 @@ impl<'a> CommandDispatcher<'a> { self.commands.insert(primary_name, Command::Tree(tree)); } } + +#[cfg(test)] +mod test { + use crate::command::{default_dispatcher, tree::CommandTree}; + + #[test] + fn test_dynamic_command() { + let mut dispatcher = default_dispatcher(); + let tree = CommandTree::new(["test"], "test_desc"); + dispatcher.register(tree); + } +} diff --git a/pumpkin/src/command/mod.rs b/pumpkin/src/command/mod.rs index c3f1b67d..9ab33038 100644 --- a/pumpkin/src/command/mod.rs +++ b/pumpkin/src/command/mod.rs @@ -107,7 +107,7 @@ impl<'a> CommandSender<'a> { } #[must_use] -pub fn default_dispatcher<'a>() -> Arc> { +pub fn default_dispatcher<'a>() -> CommandDispatcher<'a> { let mut dispatcher = CommandDispatcher::default(); dispatcher.register(cmd_pumpkin::init_command_tree()); @@ -129,7 +129,7 @@ pub fn default_dispatcher<'a>() -> Arc> { dispatcher.register(cmd_transfer::init_command_tree()); dispatcher.register(cmd_fill::init_command_tree()); - Arc::new(dispatcher) + dispatcher } #[async_trait] diff --git a/pumpkin/src/command/tree.rs b/pumpkin/src/command/tree.rs index 06d9c0e5..9ea18e35 100644 --- a/pumpkin/src/command/tree.rs +++ b/pumpkin/src/command/tree.rs @@ -5,12 +5,13 @@ use std::{collections::VecDeque, fmt::Debug}; /// see [`crate::commands::tree_builder::argument`] pub type RawArgs<'a> = Vec<&'a str>; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Node<'a> { pub(crate) children: Vec, pub(crate) node_type: NodeType<'a>, } +#[derive(Clone)] pub enum NodeType<'a> { ExecuteLeaf { executor: &'a dyn CommandExecutor, @@ -50,7 +51,7 @@ pub enum Command<'a> { Alias(&'a str), } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CommandTree<'a> { pub(crate) nodes: Vec>, pub(crate) children: Vec, diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 232f30a1..5009e96f 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -306,7 +306,7 @@ fn setup_console(server: Arc) { .expect("Failed to read console line"); if !out.is_empty() { - let dispatcher = server.command_dispatcher.clone(); + let dispatcher = server.command_dispatcher.read().await; dispatcher .handle_command(&mut command::CommandSender::Console, &server, &out) .await; diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index e3062678..b674f4df 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -286,7 +286,7 @@ impl Player { server: &Arc, command: SChatCommand, ) { - let dispatcher = server.command_dispatcher.clone(); + let dispatcher = server.command_dispatcher.read().await; dispatcher .handle_command( &mut CommandSender::Player(self.clone()), @@ -877,10 +877,8 @@ impl Player { return; }; - let suggestions = server - .command_dispatcher - .find_suggestions(&mut src, server, cmd) - .await; + let dispatcher = server.command_dispatcher.read().await; + let suggestions = dispatcher.find_suggestions(&mut src, server, cmd).await; let response = CCommandSuggestions::new( packet.id, diff --git a/pumpkin/src/net/rcon/mod.rs b/pumpkin/src/net/rcon/mod.rs index 96adbc96..1f36d827 100644 --- a/pumpkin/src/net/rcon/mod.rs +++ b/pumpkin/src/net/rcon/mod.rs @@ -104,7 +104,7 @@ impl RCONClient { ServerboundPacket::ExecCommand => { if self.logged_in { let output = tokio::sync::Mutex::new(Vec::new()); - let dispatcher = server.command_dispatcher.clone(); + let dispatcher = server.command_dispatcher.read().await; dispatcher .handle_command( &mut crate::command::CommandSender::Rcon(&output), diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index c672eb2c..cb5c57b1 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -47,7 +47,7 @@ pub struct Server { /// Saves server branding information. server_branding: CachedBranding, /// Saves and Dispatches commands to appropriate handlers. - pub command_dispatcher: Arc>, + pub command_dispatcher: RwLock>, /// Saves and calls blocks blocks pub block_manager: Arc, /// Manages multiple worlds within the server. @@ -79,7 +79,7 @@ impl Server { }); // First register default command, after that plugins can put in their own - let command_dispatcher = default_dispatcher(); + let command_dispatcher = RwLock::new(default_dispatcher()); let world = World::load( Dimension::OverWorld.into_level( diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 02c13b10..52d6b8c3 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -229,7 +229,6 @@ impl World { player: Arc, server: &Server, ) { - let command_dispatcher = &server.command_dispatcher; let dimensions: Vec = server.dimensions.iter().map(DimensionType::name).collect(); @@ -270,8 +269,7 @@ impl World { .await; // permissions, i. e. the commands a player may use player.send_permission_lvl_update().await; - client_cmd_suggestions::send_c_commands_packet(&player, command_dispatcher).await; - + client_cmd_suggestions::send_c_commands_packet(&player, &server.command_dispatcher).await; // teleport let mut position = Vector3::new(10.0, 120.0, 10.0); let yaw = 10.0; From e8deff519ec75aefdc18eda4321841da13e9aa16 Mon Sep 17 00:00:00 2001 From: Tiiita <96084154+Tiiita@users.noreply.github.com> Date: Wed, 25 Dec 2024 16:53:39 +0100 Subject: [PATCH 14/14] Add nearby players collision check when trying to place blocks (#293) * Try to update boundingbox when changin pos * Remove unused imports * Add nearby player check when placing blocks * Add seach for nearby players when placing block * Reformat * Fix conflict * fix: conflicts and fix clippy --------- Co-authored-by: Alexander Medvedev --- pumpkin/src/net/packet/play.rs | 11 ++++-- pumpkin/src/world/mod.rs | 62 ++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index b674f4df..ec01ee04 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -764,9 +764,14 @@ impl Player { } let block_bounding_box = BoundingBox::from_block(&world_pos); - let bounding_box = entity.bounding_box.load(); - //TODO: Make this check for every entity in that position - if !bounding_box.intersects(&block_bounding_box) { + let mut intersects = false; + for player in world.get_nearby_players(entity.pos.load(), 20).await { + let bounding_box = player.1.living_entity.entity.bounding_box.load(); + if bounding_box.intersects(&block_bounding_box) { + intersects = true; + } + } + if !intersects { world .set_block_state(world_pos, block.default_state_id) .await; diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 52d6b8c3..92239719 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -630,6 +630,68 @@ impl World { return self.current_players.lock().await.get(&id).cloned(); } + /// Gets a list of players who's location equals the given position in the world. + /// + /// It iterates through the players in the world and checks their location. If the player's location matches the + /// given position it will add this to a Vec which it later returns. If no + /// player was found in that position it will just return an empty Vec. + /// + /// # Arguments + /// + /// * `position`: The position the function will check. + pub async fn get_players_by_pos( + &self, + position: WorldPosition, + ) -> HashMap> { + self.current_players + .lock() + .await + .iter() + .filter_map(|(uuid, player)| { + let player_block_pos = player.living_entity.entity.block_pos.load().0; + (position.0.x == player_block_pos.x + && position.0.y == player_block_pos.y + && position.0.z == player_block_pos.z) + .then(|| (*uuid, Arc::clone(player))) + }) + .collect::>>() + } + + /// Gets the nearby players around a given world position + /// It "creates" a sphere and checks if whether players are inside + /// and returns a hashmap where the uuid is the key and the player + /// object the value. + /// + /// # Arguments + /// * `pos`: The middlepoint of the sphere + /// * `radius`: The radius of the sphere. The higher the radius + /// the more area will be checked, in every direction. + pub async fn get_nearby_players( + &self, + pos: Vector3, + radius: u16, + ) -> HashMap> { + let radius_squared = (f64::from(radius)).powi(2); + + let mut found_players = HashMap::new(); + for player in self.current_players.lock().await.iter() { + let player_pos = player.1.living_entity.entity.pos.load(); + + let diff = Vector3::new( + player_pos.x - pos.x, + player_pos.y - pos.y, + player_pos.z - pos.z, + ); + + let distance_squared = diff.x.powi(2) + diff.y.powi(2) + diff.z.powi(2); + if distance_squared <= radius_squared { + found_players.insert(*player.0, player.1.clone()); + } + } + + found_players + } + /// Adds a player to the world and broadcasts a join message if enabled. /// /// This function takes a player's UUID and an `Arc` reference.