From 33c5f4124a58be152003893934eefa4b09ba5641 Mon Sep 17 00:00:00 2001 From: kralverde <80051564+kralverde@users.noreply.github.com> Date: Tue, 10 Dec 2024 02:31:33 -0500 Subject: [PATCH 01/42] check closed status before sending packets (#390) --- pumpkin/src/client/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 1c8d1057..47a7a03b 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -216,6 +216,10 @@ impl Client { pub async fn send_packet(&self, packet: &P) { //log::debug!("Sending packet with id {} to {}", P::PACKET_ID, self.id); // assert!(!self.closed); + if self.closed.load(std::sync::atomic::Ordering::Relaxed) { + return; + } + let mut enc = self.enc.lock().await; if let Err(error) = enc.append_packet(packet) { if error.kickable() { @@ -226,7 +230,7 @@ impl Client { let mut writer = self.connection_writer.lock().await; if let Err(error) = writer.write_all(&enc.take()).await { - log::debug!("{}", error.to_string()); + log::debug!("Unable to write to connection: {}", error.to_string()); } /* From bab92b39ca175439d8c83f5af75d4686737f6c7d Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Tue, 10 Dec 2024 16:17:53 +0100 Subject: [PATCH 02/42] Refactor VarInt and VarLong - Less duplicate code - Cleaner Code :D --- pumpkin-protocol/src/bytebuf/mod.rs | 92 +++++++------------------- pumpkin-protocol/src/packet_decoder.rs | 6 +- pumpkin-protocol/src/packet_encoder.rs | 41 +++--------- pumpkin-protocol/src/var_int.rs | 28 ++++---- pumpkin-protocol/src/var_long.rs | 17 ++--- pumpkin/src/server/connection_cache.rs | 4 +- 6 files changed, 56 insertions(+), 132 deletions(-) diff --git a/pumpkin-protocol/src/bytebuf/mod.rs b/pumpkin-protocol/src/bytebuf/mod.rs index 3b46f2f9..accceb4b 100644 --- a/pumpkin-protocol/src/bytebuf/mod.rs +++ b/pumpkin-protocol/src/bytebuf/mod.rs @@ -1,15 +1,13 @@ -use crate::{BitSet, FixedBitSet, VarInt, VarLongType}; -use bytes::{Buf, BufMut, BytesMut}; use core::str; +use crate::{BitSet, FixedBitSet, VarInt, VarLong}; +use bytes::{Buf, BufMut, BytesMut}; + mod deserializer; pub use deserializer::DeserializerError; pub mod packet_id; mod serializer; -const SEGMENT_BITS: u8 = 0x7F; -const CONTINUE_BIT: u8 = 0x80; - #[derive(Debug)] pub struct ByteBuffer { buffer: BytesMut, @@ -26,49 +24,31 @@ impl ByteBuffer { } pub fn get_var_int(&mut self) -> Result { - let mut value: i32 = 0; - let mut position: i32 = 0; - - loop { - let read = self.get_u8()?; - - value |= ((read & SEGMENT_BITS) as i32) << position; - - if read & CONTINUE_BIT == 0 { - break; - } - - position += 7; - - if position >= 32 { - return Err(DeserializerError::Message("VarInt is too big".to_string())); - } + match VarInt::decode(&mut self.buffer) { + Ok(var_int) => Ok(var_int), + Err(error) => match error { + crate::VarIntDecodeError::Incomplete => Err(DeserializerError::Message( + "VarInt is Incomplete".to_string(), + )), + crate::VarIntDecodeError::TooLarge => { + Err(DeserializerError::Message("VarInt is too big".to_string())) + } + }, } - - Ok(VarInt(value)) } - pub fn get_var_long(&mut self) -> Result { - let mut value: i64 = 0; - let mut position: i64 = 0; - - loop { - let read = self.get_u8()?; - - value |= ((read & SEGMENT_BITS) as i64) << position; - - if read & CONTINUE_BIT == 0 { - break; - } - - position += 7; - - if position >= 64 { - return Err(DeserializerError::Message("VarLong is too big".to_string())); - } + pub fn get_var_long(&mut self) -> Result { + match VarLong::decode(&mut self.buffer) { + Ok(var_long) => Ok(var_long), + Err(error) => match error { + crate::VarLongDecodeError::Incomplete => Err(DeserializerError::Message( + "VarLong is Incomplete".to_string(), + )), + crate::VarLongDecodeError::TooLarge => { + Err(DeserializerError::Message("VarLong is too big".to_string())) + } + }, } - - Ok(value) } pub fn get_string(&mut self) -> Result { @@ -144,18 +124,7 @@ impl ByteBuffer { } pub fn put_var_int(&mut self, value: &VarInt) { - let mut val = value.0; - for _ in 0..5 { - let mut b: u8 = val as u8 & 0b01111111; - val >>= 7; - if val != 0 { - b |= 0b10000000; - } - self.buffer.put_u8(b); - if val == 0 { - break; - } - } + value.encode(&mut self.buffer); } pub fn put_bit_set(&mut self, set: &BitSet) { @@ -209,17 +178,6 @@ impl ByteBuffer { self.put_list(v, |p, &v| p.put_var_int(&v.into())) } - /* pub fn get_nbt(&mut self) -> Option { - match crab_nbt::NbtTag::deserialize(self.buf()) { - Ok(v) => Some(v), - Err(err) => None, - } - } - - pub fn put_nbt(&mut self, nbt: N) { - self.buffer.put_slice(&nbt.serialize()); - } - */ pub fn buf(&mut self) -> &mut BytesMut { &mut self.buffer } diff --git a/pumpkin-protocol/src/packet_decoder.rs b/pumpkin-protocol/src/packet_decoder.rs index 2a8986ca..257fb0f8 100644 --- a/pumpkin-protocol/src/packet_decoder.rs +++ b/pumpkin-protocol/src/packet_decoder.rs @@ -209,7 +209,6 @@ mod tests { use cfb8::cipher::AsyncStreamCipher; use cfb8::Encryptor as Cfb8Encryptor; use libdeflater::{CompressionLvl, Compressor}; - use std::io::Cursor; /// Helper function to compress data using libdeflater's Zlib compressor fn compress_zlib(data: &[u8]) -> Vec { @@ -265,10 +264,7 @@ mod tests { let packet_len_varint = VarInt(packet_len); let mut packet_length_encoded = Vec::new(); { - let mut cursor = Cursor::new(&mut packet_length_encoded); - packet_len_varint - .encode(&mut cursor) - .expect("VarInt encoding failed"); + packet_len_varint.encode(&mut packet_length_encoded); } // Create a new buffer for the entire packet diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index 6d162a19..dec7e0ac 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -1,5 +1,3 @@ -use std::io::Write; - use aes::cipher::{generic_array::GenericArray, BlockEncryptMut, BlockSizeUser, KeyIvInit}; use bytes::{BufMut, BytesMut}; use pumpkin_config::compression::CompressionInfo; @@ -39,17 +37,12 @@ impl Default for PacketEncoder { impl PacketEncoder { pub fn append_packet(&mut self, packet: &P) -> Result<(), PacketEncodeError> { let start_len = self.buf.len(); - let mut writer = (&mut self.buf).writer(); let mut packet_buf = ByteBuffer::empty(); - VarInt(P::PACKET_ID) - .encode(&mut writer) - .map_err(|_| PacketEncodeError::EncodeID)?; + VarInt(P::PACKET_ID).encode(&mut self.buf); packet.write(&mut packet_buf); - writer - .write(packet_buf.buf()) - .map_err(|_| PacketEncodeError::EncodeFailedWrite)?; + self.buf.put(packet_buf.buf()); let data_len = self.buf.len() - start_len; @@ -87,14 +80,8 @@ impl PacketEncoder { self.buf.truncate(start_len); - let mut writer = (&mut self.buf).writer(); - - VarInt(packet_len as i32) - .encode(&mut writer) - .map_err(|_| PacketEncodeError::EncodeLength)?; - VarInt(data_len as i32) - .encode(&mut writer) - .map_err(|_| PacketEncodeError::EncodeData)?; + VarInt(packet_len as i32).encode(&mut self.buf); + VarInt(data_len as i32).encode(&mut self.buf); self.buf.extend_from_slice(&self.compress_buf); } else { let data_len_size = 1; @@ -114,13 +101,9 @@ impl PacketEncoder { let mut front = &mut self.buf[start_len..]; - VarInt(packet_len as i32) - .encode(&mut front) - .map_err(|_| PacketEncodeError::EncodeLength)?; + VarInt(packet_len as i32).encode(&mut front); // Zero for no compression on this packet. - VarInt(0) - .encode(front) - .map_err(|_| PacketEncodeError::EncodeData)?; + VarInt(0).encode(&mut front); } return Ok(()); @@ -138,10 +121,8 @@ impl PacketEncoder { self.buf .copy_within(start_len..start_len + data_len, start_len + packet_len_size); - let front = &mut self.buf[start_len..]; - VarInt(packet_len as i32) - .encode(front) - .map_err(|_| PacketEncodeError::EncodeID)?; + let mut front = &mut self.buf[start_len..]; + VarInt(packet_len as i32).encode(&mut front); Ok(()) } @@ -188,18 +169,12 @@ impl PacketEncoder { #[derive(Error, Debug)] pub enum PacketEncodeError { - #[error("failed to encode packet ID")] - EncodeID, - #[error("failed to encode packet Length")] - EncodeLength, #[error("failed to encode packet data")] EncodeData, #[error("failed to write encoded packet")] EncodeFailedWrite, #[error("packet exceeds maximum length")] TooLong, - #[error("invalid compression level")] - InvalidCompressionLevel, #[error("compression failed")] CompressionFailed, } diff --git a/pumpkin-protocol/src/var_int.rs b/pumpkin-protocol/src/var_int.rs index 61f596ca..caf7a2cc 100644 --- a/pumpkin-protocol/src/var_int.rs +++ b/pumpkin-protocol/src/var_int.rs @@ -1,6 +1,4 @@ -use std::io::{self, Write}; - -use bytes::Buf; +use bytes::{Buf, BufMut}; use thiserror::Error; use crate::VarIntType; @@ -22,7 +20,7 @@ impl VarInt { } } - pub fn decode_partial(r: &mut &[u8]) -> Result { + pub fn decode_partial(r: &mut impl Buf) -> Result { let mut val = 0; for i in 0..Self::MAX_SIZE { if !r.has_remaining() { @@ -34,25 +32,25 @@ impl VarInt { return Ok(val); } } - Err(VarIntDecodeError::TooLarge) } - pub fn encode(&self, mut w: impl Write) -> Result<(), io::Error> { - let mut x = self.0 as u64; - loop { - let byte = (x & 0x7F) as u8; - x >>= 7; - if x == 0 { - w.write_all(&[byte])?; + pub fn encode(&self, w: &mut impl BufMut) { + let mut val = self.0; + for _ in 0..Self::MAX_SIZE { + let mut b: u8 = val as u8 & 0b01111111; + val >>= 7; + if val != 0 { + b |= 0b10000000; + } + w.put_u8(b); + if val == 0 { break; } - w.write_all(&[byte | 0x80])?; } - Ok(()) } - pub fn decode(r: &mut &[u8]) -> Result { + pub fn decode(r: &mut impl Buf) -> Result { let mut val = 0; for i in 0..Self::MAX_SIZE { if !r.has_remaining() { diff --git a/pumpkin-protocol/src/var_long.rs b/pumpkin-protocol/src/var_long.rs index 91a3bbc2..1f4b384d 100644 --- a/pumpkin-protocol/src/var_long.rs +++ b/pumpkin-protocol/src/var_long.rs @@ -1,6 +1,4 @@ -use std::io::{self, Write}; - -use bytes::Buf; +use bytes::{Buf, BufMut}; use thiserror::Error; use crate::VarLongType; @@ -22,21 +20,20 @@ impl VarLong { } } - pub fn encode(&self, mut w: impl Write) -> Result<(), io::Error> { - let mut x = self.0 as u64; - loop { + pub fn encode(&self, w: &mut impl BufMut) { + let mut x = self.0; + for _ in 0..Self::MAX_SIZE { let byte = (x & 0x7F) as u8; x >>= 7; if x == 0 { - w.write_all(&[byte])?; + w.put_slice(&[byte]); break; } - w.write_all(&[byte | 0x80])?; + w.put_slice(&[byte | 0x80]); } - Ok(()) } - pub fn decode(r: &mut &[u8]) -> Result { + pub fn decode(r: &mut impl Buf) -> Result { let mut val = 0; for i in 0..Self::MAX_SIZE { if !r.has_remaining() { diff --git a/pumpkin/src/server/connection_cache.rs b/pumpkin/src/server/connection_cache.rs index a8fa05d6..8e6fce8f 100644 --- a/pumpkin/src/server/connection_cache.rs +++ b/pumpkin/src/server/connection_cache.rs @@ -60,8 +60,8 @@ impl CachedBranding { } fn build_brand() -> Vec { let brand = "Pumpkin"; - let mut buf = vec![]; - let _ = VarInt(brand.len() as i32).encode(&mut buf); + let mut buf = Vec::new(); + VarInt(brand.len() as i32).encode(&mut buf); buf.extend_from_slice(brand.as_bytes()); buf } From bd8bac1ff5c1a3751894a1bf9aa85d5b7630a863 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Wed, 11 Dec 2024 00:29:23 +0100 Subject: [PATCH 03/42] Less heap allocations --- pumpkin-core/src/text/mod.rs | 8 ++--- .../src/client/play/c_boss_event.rs | 4 +-- pumpkin-protocol/src/packet_decoder.rs | 14 ++++----- pumpkin-protocol/src/packet_encoder.rs | 25 ++++++++-------- .../src/server/config/s_cookie_response.rs | 4 +-- .../src/server/config/s_plugin_message.rs | 4 +-- .../src/server/login/s_cookie_response.rs | 4 +-- .../src/server/login/s_encryption_response.rs | 8 ++--- .../src/server/play/s_cookie_response.rs | 4 +-- pumpkin-protocol/src/var_int.rs | 29 ++++--------------- pumpkin-protocol/src/var_long.rs | 5 ++-- pumpkin-world/src/chunk/anvil.rs | 2 +- pumpkin-world/src/chunk/mod.rs | 6 ++-- pumpkin/src/client/client_packet.rs | 5 ++-- 14 files changed, 50 insertions(+), 72 deletions(-) diff --git a/pumpkin-core/src/text/mod.rs b/pumpkin-core/src/text/mod.rs index 2284f60f..32299fb1 100644 --- a/pumpkin-core/src/text/mod.rs +++ b/pumpkin-core/src/text/mod.rs @@ -100,7 +100,7 @@ impl serde::Serialize for TextComponent<'_> { where S: serde::Serializer, { - serializer.serialize_bytes(self.encode().as_slice()) + serializer.serialize_bytes(&self.encode()) } } @@ -181,7 +181,7 @@ impl<'a> TextComponent<'a> { self } - pub fn encode(&self) -> Vec { + pub fn encode(&self) -> bytes::BytesMut { // TODO: Somehow fix this ugly mess #[derive(serde::Serialize)] #[serde(rename_all = "camelCase")] @@ -215,9 +215,7 @@ impl<'a> TextComponent<'a> { // dbg!(pumpkin_nbt::serializer::to_bytes_unnamed(&astruct).unwrap().to_vec()); // TODO - pumpkin_nbt::serializer::to_bytes_unnamed(&astruct) - .unwrap() - .to_vec() + pumpkin_nbt::serializer::to_bytes_unnamed(&astruct).unwrap() } } diff --git a/pumpkin-protocol/src/client/play/c_boss_event.rs b/pumpkin-protocol/src/client/play/c_boss_event.rs index 34452654..0100c154 100644 --- a/pumpkin-protocol/src/client/play/c_boss_event.rs +++ b/pumpkin-protocol/src/client/play/c_boss_event.rs @@ -28,7 +28,7 @@ impl ClientPacket for CBossEvent<'_> { flags, } => { bytebuf.put_var_int(&VarInt::from(0u8)); - bytebuf.put_slice(title.encode().as_slice()); + bytebuf.put_slice(&title.encode()); bytebuf.put_f32(*health); bytebuf.put_var_int(color); bytebuf.put_var_int(division); @@ -43,7 +43,7 @@ impl ClientPacket for CBossEvent<'_> { } BosseventAction::UpdateTile(title) => { bytebuf.put_var_int(&VarInt::from(3u8)); - bytebuf.put_slice(title.encode().as_slice()); + bytebuf.put_slice(&title.encode()); } BosseventAction::UpdateStyle { color, dividers } => { bytebuf.put_var_int(&VarInt::from(4u8)); diff --git a/pumpkin-protocol/src/packet_decoder.rs b/pumpkin-protocol/src/packet_decoder.rs index 257fb0f8..c0a3f513 100644 --- a/pumpkin-protocol/src/packet_decoder.rs +++ b/pumpkin-protocol/src/packet_decoder.rs @@ -36,11 +36,12 @@ impl PacketDecoder { pub fn decode(&mut self) -> Result, PacketDecodeError> { let mut r = &self.buf[..]; - let packet_len = match VarInt::decode_partial(&mut r) { + let packet_len = match VarInt::decode(&mut r) { Ok(len) => len, Err(VarIntDecodeError::Incomplete) => return Ok(None), Err(VarIntDecodeError::TooLarge) => Err(PacketDecodeError::MalformedLength)?, }; + let packet_len = packet_len.0; if !(0..=MAX_PACKET_SIZE).contains(&packet_len) { Err(PacketDecodeError::OutOfBounds)? @@ -222,11 +223,9 @@ mod tests { } /// Helper function to encrypt data using AES-128 CFB-8 mode - fn encrypt_aes128(data: &[u8], key: &[u8; 16], iv: &[u8; 16]) -> Vec { + fn encrypt_aes128(mut data: &mut [u8], key: &[u8; 16], iv: &[u8; 16]) { let encryptor = Cfb8Encryptor::::new_from_slices(key, iv).expect("Invalid key/iv"); - let mut encrypted = data.to_vec(); - encryptor.encrypt(&mut encrypted); - encrypted + encryptor.encrypt(&mut data); } /// Helper function to build a packet with optional compression and encryption @@ -274,7 +273,8 @@ mod tests { // Encrypt if key and iv are provided if let (Some(k), Some(v)) = (key, iv) { - encrypt_aes128(&packet, k, v) + encrypt_aes128(&mut packet, k, v); + packet } else { packet } @@ -416,7 +416,7 @@ mod tests { packet_buffer.put_var_int(&packet_len_varint); packet_buffer.put_slice(buffer.buf()); - let packet_bytes = packet_buffer.buf().to_vec(); + let packet_bytes = packet_buffer.buf(); // Initialize the decoder with compression enabled let mut decoder = PacketDecoder::default(); diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index dec7e0ac..c9158b3a 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -37,11 +37,11 @@ impl Default for PacketEncoder { impl PacketEncoder { pub fn append_packet(&mut self, packet: &P) -> Result<(), PacketEncodeError> { let start_len = self.buf.len(); - - let mut packet_buf = ByteBuffer::empty(); + // Write the Packet ID first VarInt(P::PACKET_ID).encode(&mut self.buf); + let mut packet_buf = ByteBuffer::empty(); + // Now write the packet into an empty buffer packet.write(&mut packet_buf); - self.buf.put(packet_buf.buf()); let data_len = self.buf.len() - start_len; @@ -229,11 +229,9 @@ mod tests { } /// Helper function to decrypt data using AES-128 CFB-8 mode - fn decrypt_aes128(encrypted_data: &[u8], key: &[u8; 16], iv: &[u8; 16]) -> Vec { + fn decrypt_aes128(mut encrypted_data: &mut [u8], key: &[u8; 16], iv: &[u8; 16]) { let decryptor = Cfb8Decryptor::::new_from_slices(key, iv).expect("Invalid key/iv"); - let mut decrypted = encrypted_data.to_vec(); - decryptor.decrypt(&mut decrypted); - decrypted + decryptor.decrypt(&mut encrypted_data); } /// Helper function to build a packet with optional compression and encryption @@ -356,13 +354,13 @@ mod tests { let key = [0x00u8; 16]; // Example key // Build the packet with encryption enabled (no compression) - let packet_bytes = build_packet_with_encoder(&packet, None, Some(&key)); + let mut packet_bytes = build_packet_with_encoder(&packet, None, Some(&key)); // Decrypt the packet - let decrypted_packet = decrypt_aes128(&packet_bytes, &key, &key); + decrypt_aes128(&mut packet_bytes, &key, &key); // Decode the packet manually to verify correctness - let mut buffer = &decrypted_packet[..]; + let mut buffer = &packet_bytes[..]; // Read packet length VarInt let packet_length = decode_varint(&mut buffer).expect("Failed to decode packet length"); @@ -399,13 +397,14 @@ mod tests { let key = [0x01u8; 16]; // Example key // Build the packet with both compression and encryption enabled - let packet_bytes = build_packet_with_encoder(&packet, Some(compression_info), Some(&key)); + let mut packet_bytes = + build_packet_with_encoder(&packet, Some(compression_info), Some(&key)); // Decrypt the packet - let decrypted_packet = decrypt_aes128(&packet_bytes, &key, &key); + decrypt_aes128(&mut packet_bytes, &key, &key); // Decode the packet manually to verify correctness - let mut buffer = &decrypted_packet[..]; + let mut buffer = &packet_bytes[..]; // Read packet length VarInt let packet_length = decode_varint(&mut buffer).expect("Failed to decode packet length"); diff --git a/pumpkin-protocol/src/server/config/s_cookie_response.rs b/pumpkin-protocol/src/server/config/s_cookie_response.rs index 3f818535..69440254 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 @@ pub struct SCookieResponse { pub key: Identifier, pub has_payload: bool, pub payload_length: Option, - pub payload: Option>, // 5120, + pub payload: Option, // 5120, } const MAX_PAYLOAD_SIZE: i32 = 5120; @@ -39,7 +39,7 @@ impl ServerPacket for SCookieResponse { )); } - let payload = bytebuf.copy_to_bytes(length as usize)?.to_vec(); + let payload = bytebuf.copy_to_bytes(length as usize)?; Ok(Self { key, diff --git a/pumpkin-protocol/src/server/config/s_plugin_message.rs b/pumpkin-protocol/src/server/config/s_plugin_message.rs index ea20a35c..d5770139 100644 --- a/pumpkin-protocol/src/server/config/s_plugin_message.rs +++ b/pumpkin-protocol/src/server/config/s_plugin_message.rs @@ -8,14 +8,14 @@ use crate::{ #[server_packet("config:custom_payload")] pub struct SPluginMessage { pub channel: Identifier, - pub data: Vec, + pub data: bytes::BytesMut, } impl ServerPacket for SPluginMessage { fn read(bytebuf: &mut ByteBuffer) -> Result { Ok(Self { channel: bytebuf.get_string()?, - data: bytebuf.get_slice().to_vec(), + data: bytebuf.get_slice(), }) } } diff --git a/pumpkin-protocol/src/server/login/s_cookie_response.rs b/pumpkin-protocol/src/server/login/s_cookie_response.rs index a5fcf06e..8f70bfb5 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 @@ pub struct SCookieResponse { pub key: Identifier, pub has_payload: bool, pub payload_length: Option, - pub payload: Option>, // 5120, + pub payload: Option, // 5120, } const MAX_PAYLOAD_SIZE: i32 = 5120; @@ -38,7 +38,7 @@ impl ServerPacket for SCookieResponse { )); } - let payload = bytebuf.copy_to_bytes(length as usize)?.to_vec(); + let payload = bytebuf.copy_to_bytes(length as usize)?; Ok(Self { key, diff --git a/pumpkin-protocol/src/server/login/s_encryption_response.rs b/pumpkin-protocol/src/server/login/s_encryption_response.rs index 06c22462..64be02b4 100644 --- a/pumpkin-protocol/src/server/login/s_encryption_response.rs +++ b/pumpkin-protocol/src/server/login/s_encryption_response.rs @@ -8,9 +8,9 @@ use crate::{ #[server_packet("login:key")] pub struct SEncryptionResponse { pub shared_secret_length: VarInt, - pub shared_secret: Vec, + pub shared_secret: bytes::Bytes, pub verify_token_length: VarInt, - pub verify_token: Vec, + pub verify_token: bytes::Bytes, } impl ServerPacket for SEncryptionResponse { @@ -21,9 +21,9 @@ impl ServerPacket for SEncryptionResponse { let verify_token = bytebuf.copy_to_bytes(shared_secret_length.0 as usize)?; Ok(Self { shared_secret_length, - shared_secret: shared_secret.to_vec(), + shared_secret, verify_token_length, - verify_token: verify_token.to_vec(), + verify_token, }) } } diff --git a/pumpkin-protocol/src/server/play/s_cookie_response.rs b/pumpkin-protocol/src/server/play/s_cookie_response.rs index 97311b31..b7ad9b70 100644 --- a/pumpkin-protocol/src/server/play/s_cookie_response.rs +++ b/pumpkin-protocol/src/server/play/s_cookie_response.rs @@ -10,7 +10,7 @@ pub struct SCookieResponse { pub key: Identifier, pub has_payload: bool, pub payload_length: Option, - pub payload: Option>, // 5120, + pub payload: Option, // 5120, } const MAX_PAYLOAD_SIZE: i32 = 5120; @@ -38,7 +38,7 @@ impl ServerPacket for SCookieResponse { )); } - let payload = bytebuf.copy_to_bytes(length as usize)?.to_vec(); + let payload = bytebuf.copy_to_bytes(length as usize)?; Ok(Self { key, diff --git a/pumpkin-protocol/src/var_int.rs b/pumpkin-protocol/src/var_int.rs index caf7a2cc..7c461d1f 100644 --- a/pumpkin-protocol/src/var_int.rs +++ b/pumpkin-protocol/src/var_int.rs @@ -7,8 +7,7 @@ use crate::VarIntType; pub struct VarInt(pub VarIntType); impl VarInt { - /// The maximum number of bytes a `VarInt` could occupy when read from and - /// written to the Minecraft protocol. + /// The maximum number of bytes a `VarInt` pub const MAX_SIZE: usize = 5; /// Returns the exact number of bytes this varint will write when @@ -20,30 +19,12 @@ impl VarInt { } } - pub fn decode_partial(r: &mut impl Buf) -> Result { - let mut val = 0; - for i in 0..Self::MAX_SIZE { - if !r.has_remaining() { - return Err(VarIntDecodeError::Incomplete); - } - let byte = r.get_u8(); - val |= (i32::from(byte) & 0b01111111) << (i * 7); - if byte & 0b10000000 == 0 { - return Ok(val); - } - } - Err(VarIntDecodeError::TooLarge) - } - pub fn encode(&self, w: &mut impl BufMut) { let mut val = self.0; for _ in 0..Self::MAX_SIZE { - let mut b: u8 = val as u8 & 0b01111111; + let b: u8 = val as u8 & 0b01111111; val >>= 7; - if val != 0 { - b |= 0b10000000; - } - w.put_u8(b); + w.put_u8(if val == 0 { b } else { b | 0b10000000 }); if val == 0 { break; } @@ -57,8 +38,8 @@ impl VarInt { return Err(VarIntDecodeError::Incomplete); } let byte = r.get_u8(); - val |= (i32::from(byte) & 0b01111111) << (i * 7); - if byte & 0b10000000 == 0 { + val |= (i32::from(byte) & 0x7F) << (i * 7); + if byte & 0x80 == 0 { return Ok(VarInt(val)); } } diff --git a/pumpkin-protocol/src/var_long.rs b/pumpkin-protocol/src/var_long.rs index 1f4b384d..3f33ca77 100644 --- a/pumpkin-protocol/src/var_long.rs +++ b/pumpkin-protocol/src/var_long.rs @@ -7,11 +7,10 @@ use crate::VarLongType; pub struct VarLong(pub VarLongType); impl VarLong { - /// The maximum number of bytes a `VarInt` could occupy when read from and - /// written to the Minecraft protocol. + /// The maximum number of bytes a `VarLong` pub const MAX_SIZE: usize = 10; - /// Returns the exact number of bytes this varint will write when + /// 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 { diff --git a/pumpkin-world/src/chunk/anvil.rs b/pumpkin-world/src/chunk/anvil.rs index 6d13e960..c97142ac 100644 --- a/pumpkin-world/src/chunk/anvil.rs +++ b/pumpkin-world/src/chunk/anvil.rs @@ -157,7 +157,7 @@ impl ChunkReader for AnvilChunkReader { .decompress_data(chunk_data) .map_err(ChunkReadingError::Compression)?; - ChunkData::from_bytes(decompressed_chunk, *at).map_err(ChunkReadingError::ParsingError) + ChunkData::from_bytes(&decompressed_chunk, *at).map_err(ChunkReadingError::ParsingError) } } diff --git a/pumpkin-world/src/chunk/mod.rs b/pumpkin-world/src/chunk/mod.rs index 93edbbfa..a04e9f5b 100644 --- a/pumpkin-world/src/chunk/mod.rs +++ b/pumpkin-world/src/chunk/mod.rs @@ -232,15 +232,15 @@ impl Index for ChunkBlocks { } impl ChunkData { - pub fn from_bytes(chunk_data: Vec, at: Vector2) -> Result { - if fastnbt::from_bytes::(&chunk_data) + pub fn from_bytes(chunk_data: &[u8], at: Vector2) -> Result { + if fastnbt::from_bytes::(chunk_data) .map_err(|_| ChunkParsingError::FailedReadStatus)? != ChunkStatus::Full { return Err(ChunkParsingError::ChunkNotGenerated); } - let chunk_data = fastnbt::from_bytes::(chunk_data.as_slice()) + let chunk_data = fastnbt::from_bytes::(chunk_data) .map_err(|e| ChunkParsingError::ErrorDeserializingChunk(e.to_string()))?; // this needs to be boxed, otherwise it will cause a stack-overflow diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index a95c4b40..ac21d99f 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -8,6 +8,7 @@ use crate::{ }, server::{Server, CURRENT_MC_VERSION}, }; +use core::str; use num_traits::FromPrimitive; use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_core::text::TextComponent; @@ -412,8 +413,8 @@ impl Client { || plugin_message.channel.starts_with("MC|Brand") { log::debug!("got a client brand"); - match String::from_utf8(plugin_message.data) { - Ok(brand) => *self.brand.lock().await = Some(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, } } From 365d83f1233195139ead2a4fb8d8fc0e14d160c5 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Wed, 11 Dec 2024 08:09:50 +0100 Subject: [PATCH 04/42] Fix: clippy Normal clippy was not showing these lints, i had to run `cargo clippy --release --all-targets --all-features --no-default-features` --- pumpkin-protocol/src/packet_decoder.rs | 6 +++--- pumpkin-protocol/src/packet_encoder.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pumpkin-protocol/src/packet_decoder.rs b/pumpkin-protocol/src/packet_decoder.rs index c0a3f513..3af766db 100644 --- a/pumpkin-protocol/src/packet_decoder.rs +++ b/pumpkin-protocol/src/packet_decoder.rs @@ -223,9 +223,9 @@ mod tests { } /// Helper function to encrypt data using AES-128 CFB-8 mode - fn encrypt_aes128(mut data: &mut [u8], key: &[u8; 16], iv: &[u8; 16]) { + fn encrypt_aes128(data: &mut [u8], key: &[u8; 16], iv: &[u8; 16]) { let encryptor = Cfb8Encryptor::::new_from_slices(key, iv).expect("Invalid key/iv"); - encryptor.encrypt(&mut data); + encryptor.encrypt(data); } /// Helper function to build a packet with optional compression and encryption @@ -423,7 +423,7 @@ mod tests { decoder.set_compression(true); // Feed the invalid compressed packet to the decoder - decoder.queue_slice(&packet_bytes); + decoder.queue_slice(packet_bytes); // Attempt to decode and expect a decompression error let result = decoder.decode(); diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index c9158b3a..14ae6a36 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -229,9 +229,9 @@ mod tests { } /// Helper function to decrypt data using AES-128 CFB-8 mode - fn decrypt_aes128(mut encrypted_data: &mut [u8], key: &[u8; 16], iv: &[u8; 16]) { + fn decrypt_aes128(encrypted_data: &mut [u8], key: &[u8; 16], iv: &[u8; 16]) { let decryptor = Cfb8Decryptor::::new_from_slices(key, iv).expect("Invalid key/iv"); - decryptor.decrypt(&mut encrypted_data); + decryptor.decrypt(encrypted_data); } /// Helper function to build a packet with optional compression and encryption From 874b116cac2ade00913f258ba81af9c76339cb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=A8=E3=81=B4=E3=80=82=EF=BC=88=E3=81=8A=E3=83=BC?= =?UTF-8?q?=E3=81=91?= Date: Wed, 11 Dec 2024 22:53:46 +0900 Subject: [PATCH 05/42] fix mut raw_args in commands (#394) --- pumpkin/src/command/dispatcher.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pumpkin/src/command/dispatcher.rs b/pumpkin/src/command/dispatcher.rs index 6b25bb25..4980c69e 100644 --- a/pumpkin/src/command/dispatcher.rs +++ b/pumpkin/src/command/dispatcher.rs @@ -145,13 +145,13 @@ impl<'a> CommandDispatcher<'a> { let key = parts .next() .ok_or(GeneralCommandIssue("Empty Command".to_string()))?; - let mut raw_args: Vec<&str> = parts.rev().collect(); + let raw_args: Vec<&str> = parts.rev().collect(); let tree = self.get_tree(key)?; // 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).await? { + if Self::try_is_fitting_path(src, server, &path, tree, &mut raw_args.clone()).await? { return Ok(()); } } From 2ee2cc06dde0efacc0e2665ecccc9b45949ffa12 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Wed, 11 Dec 2024 16:15:31 +0100 Subject: [PATCH 06/42] fix some typos --- CONTRIBUTING.md | 2 ++ pumpkin-config/src/compression.rs | 2 +- pumpkin-core/src/random/mod.rs | 2 +- pumpkin-core/src/text/hover.rs | 4 +-- pumpkin-core/src/text/mod.rs | 9 ++--- pumpkin-protocol/src/bytebuf/serializer.rs | 4 +-- .../src/client/play/c_commands.rs | 4 +-- .../src/client/play/c_teleport_entity.rs | 6 ++-- pumpkin-protocol/src/lib.rs | 2 +- pumpkin-protocol/src/packet_decoder.rs | 8 ++--- pumpkin-protocol/src/packet_encoder.rs | 19 +++------- pumpkin-protocol/src/query.rs | 20 +++++------ pumpkin-registry/src/lib.rs | 5 ++- pumpkin-world/src/chunk/mod.rs | 12 +++---- pumpkin-world/src/world_gen/noise/router.rs | 4 +-- pumpkin-world/src/world_gen/proto_chunk.rs | 6 ++-- pumpkin/src/client/container.rs | 36 +++++++------------ pumpkin/src/client/mod.rs | 5 +-- pumpkin/src/client/player_packet.rs | 2 +- pumpkin/src/command/args/arg_entity.rs | 2 +- pumpkin/src/command/args/arg_position_2d.rs | 4 +-- ...stition_block.rs => arg_position_block.rs} | 0 pumpkin/src/command/args/mod.rs | 4 +-- pumpkin/src/command/commands/cmd_bossbar.rs | 2 +- pumpkin/src/command/commands/cmd_fill.rs | 2 +- pumpkin/src/command/commands/cmd_setblock.rs | 2 +- pumpkin/src/command/dispatcher.rs | 2 +- pumpkin/src/entity/mod.rs | 4 +-- pumpkin/src/entity/player.rs | 4 +-- pumpkin/src/main.rs | 1 + pumpkin/src/proxy/velocity.rs | 4 +-- pumpkin/src/query.rs | 26 +++++++------- pumpkin/src/server/mod.rs | 16 ++++----- pumpkin/src/world/custom_bossbar.rs | 4 +-- pumpkin/src/world/mod.rs | 10 +++--- 35 files changed, 102 insertions(+), 137 deletions(-) rename pumpkin/src/command/args/{arg_postition_block.rs => arg_position_block.rs} (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06bed628..3599738c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,6 +29,8 @@ There are several ways you can contribute to Pumpkin: The Documentation of Pumpkin can be found at +**Tip: [typos](https://github.com/crate-ci/typos) is a great Project to detect and automatically fix typos + ### Coding Guidelines - **Working with Tokio and Rayon:** diff --git a/pumpkin-config/src/compression.rs b/pumpkin-config/src/compression.rs index 2f3835f0..7a7b22c8 100644 --- a/pumpkin-config/src/compression.rs +++ b/pumpkin-config/src/compression.rs @@ -21,7 +21,7 @@ impl Default for CompressionConfig { #[derive(Deserialize, Serialize, Clone)] #[serde(default)] -/// We have this in a Seperate struct so we can use it outside of the Config +/// We have this in a Separate struct so we can use it outside of the Config pub struct CompressionInfo { /// The compression threshold used when compression is enabled pub threshold: u32, diff --git a/pumpkin-core/src/random/mod.rs b/pumpkin-core/src/random/mod.rs index 546e082e..8e147cb1 100644 --- a/pumpkin-core/src/random/mod.rs +++ b/pumpkin-core/src/random/mod.rs @@ -254,7 +254,7 @@ mod tests { -1992287231i32, ), ("求同存异", 847053876), - // This might look wierd because hebrew is text is right to left + // This might look weird because hebrew is text is right to left ("אבְּרֵאשִׁ֖ית בָּרָ֣א אֱלֹהִ֑ים אֵ֥ת הַשָּׁמַ֖יִם וְאֵ֥ת הָאָֽרֶץ:", 1372570871), ("संस्कृत-", 1748614838), ("minecraft:offset", -920384768i32), diff --git a/pumpkin-core/src/text/hover.rs b/pumpkin-core/src/text/hover.rs index 867a4a3b..35182e8e 100644 --- a/pumpkin-core/src/text/hover.rs +++ b/pumpkin-core/src/text/hover.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use serde::{Deserialize, Serialize}; -use super::Text; +use super::TextComponent; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] #[serde(tag = "action", content = "contents", rename_all = "snake_case")] @@ -28,6 +28,6 @@ pub enum HoverEvent<'a> { kind: Option>, /// Optional custom name for the entity #[serde(default, skip_serializing_if = "Option::is_none")] - name: Option>, + name: Option>>, }, } diff --git a/pumpkin-core/src/text/mod.rs b/pumpkin-core/src/text/mod.rs index 32299fb1..f94e3956 100644 --- a/pumpkin-core/src/text/mod.rs +++ b/pumpkin-core/src/text/mod.rs @@ -14,11 +14,7 @@ pub mod color; pub mod hover; pub mod style; -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -#[serde(transparent)] -pub struct Text<'a>(pub Box>); - -// Represents a Text component +/// Represents a Text component #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Hash)] #[serde(rename_all = "camelCase")] pub struct TextComponent<'a> { @@ -29,6 +25,7 @@ pub struct TextComponent<'a> { /// Also has `ClickEvent #[serde(flatten)] pub style: Style<'a>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] /// Extra text components pub extra: Vec>, } @@ -228,7 +225,7 @@ pub enum TextContent<'a> { Translate { translate: Cow<'a, str>, #[serde(default, skip_serializing_if = "Vec::is_empty")] - with: Vec>, + with: Vec>, }, /// Displays the name of one or more entities found by a selector. EntityNames { diff --git a/pumpkin-protocol/src/bytebuf/serializer.rs b/pumpkin-protocol/src/bytebuf/serializer.rs index cb2f7866..d683bb03 100644 --- a/pumpkin-protocol/src/bytebuf/serializer.rs +++ b/pumpkin-protocol/src/bytebuf/serializer.rs @@ -55,7 +55,7 @@ impl ser::Error for SerializerError { // Enums are written as a varint of the index // 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 seperate field) +// iterable itself is written (list sizes should be a separate field) impl ser::Serializer for &mut Serializer { type Ok = (); type Error = SerializerError; @@ -137,7 +137,7 @@ impl ser::Serializer for &mut Serializer { Ok(()) } fn serialize_seq(self, _len: Option) -> Result { - // here is where all arrays/list getting written, usally we prefix the length of every length with an var int. The problem is + // here is where all arrays/list getting written, usually we prefix the length of every length with an var int. The problem is // that byte arrays also getting thrown in here, and we don't want to prefix them Ok(self) } diff --git a/pumpkin-protocol/src/client/play/c_commands.rs b/pumpkin-protocol/src/client/play/c_commands.rs index 32dc71df..bfd88556 100644 --- a/pumpkin-protocol/src/client/play/c_commands.rs +++ b/pumpkin-protocol/src/client/play/c_commands.rs @@ -69,10 +69,10 @@ impl ProtoNode<'_> { name: _, is_executable, parser: _, - override_suggestion_type: override_suggestion_tpye, + override_suggestion_type, } => { let mut n = 2; - if override_suggestion_tpye.is_some() { + if override_suggestion_type.is_some() { n |= Self::FLAG_HAS_SUGGESTION_TYPE } if is_executable { diff --git a/pumpkin-protocol/src/client/play/c_teleport_entity.rs b/pumpkin-protocol/src/client/play/c_teleport_entity.rs index 4327ed65..b5bc2151 100644 --- a/pumpkin-protocol/src/client/play/c_teleport_entity.rs +++ b/pumpkin-protocol/src/client/play/c_teleport_entity.rs @@ -4,7 +4,7 @@ use pumpkin_macros::client_packet; use crate::{ClientPacket, PositionFlag, VarInt}; #[client_packet("play:teleport_entity")] -pub struct CTeleportEntitiy<'a> { +pub struct CTeleportEntity<'a> { entity_id: VarInt, position: Vector3, delta: Vector3, @@ -14,7 +14,7 @@ pub struct CTeleportEntitiy<'a> { on_ground: bool, } -impl<'a> CTeleportEntitiy<'a> { +impl<'a> CTeleportEntity<'a> { pub fn new( entity_id: VarInt, position: Vector3, @@ -36,7 +36,7 @@ impl<'a> CTeleportEntitiy<'a> { } } -impl ClientPacket for CTeleportEntitiy<'_> { +impl ClientPacket for CTeleportEntity<'_> { fn write(&self, bytebuf: &mut crate::bytebuf::ByteBuffer) { bytebuf.put_var_int(&self.entity_id); bytebuf.put_f64(self.position.x); diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index e4e20b22..f15928e9 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -22,7 +22,7 @@ pub const CURRENT_MC_PROTOCOL: u32 = 769; pub const MAX_PACKET_SIZE: i32 = 2097152; -/// usally uses a namespace like "minecraft:thing" +/// usually uses a namespace like "minecraft:thing" pub type Identifier = String; pub type VarIntType = i32; pub type VarLongType = i64; diff --git a/pumpkin-protocol/src/packet_decoder.rs b/pumpkin-protocol/src/packet_decoder.rs index 3af766db..9472c28e 100644 --- a/pumpkin-protocol/src/packet_decoder.rs +++ b/pumpkin-protocol/src/packet_decoder.rs @@ -9,7 +9,7 @@ type Cipher = cfb8::Decryptor; // Decoder: Client -> Server // Supports ZLib decoding/decompression -// Supports Aes128 Encyption +// Supports Aes128 Encryption pub struct PacketDecoder { buf: BytesMut, decompress_buf: BytesMut, @@ -135,7 +135,7 @@ impl PacketDecoder { } } - /// Sets ZLib Deompression + /// Sets ZLib Decompression pub fn set_compression(&mut self, compression: bool) { self.compression = compression; } @@ -183,10 +183,6 @@ impl PacketDecoder { pub enum PacketDecodeError { #[error("failed to decode packet ID")] DecodeID, - #[error("failed to write into decoder: {0}")] - FailedWrite(String), - #[error("failed to flush decoder")] - FailedFinish, #[error("packet exceeds maximum length")] TooLong, #[error("packet length is out of bounds")] diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index 14ae6a36..0df2b157 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -11,7 +11,7 @@ type Cipher = cfb8::Encryptor; // Encoder: Server -> Client // Supports ZLib endecoding/compression -// Supports Aes128 Encyption +// Supports Aes128 Encryption pub struct PacketEncoder { buf: BytesMut, compress_buf: Vec, @@ -65,7 +65,7 @@ impl PacketEncoder { let compressed_size = self .compressor .zlib_compress(data_to_compress, &mut self.compress_buf) - .map_err(|_| PacketEncodeError::CompressionFailed)?; + .map_err(|e| PacketEncodeError::CompressionFailed(e.to_string()))?; // Resize compress_buf to actual compressed size self.compress_buf.resize(compressed_size, 0); @@ -169,21 +169,10 @@ impl PacketEncoder { #[derive(Error, Debug)] pub enum PacketEncodeError { - #[error("failed to encode packet data")] - EncodeData, - #[error("failed to write encoded packet")] - EncodeFailedWrite, #[error("packet exceeds maximum length")] TooLong, - #[error("compression failed")] - CompressionFailed, -} - -impl PacketEncodeError { - pub fn kickable(&self) -> bool { - // We no longer have a connection, so dont try to kick the player, just close - !matches!(self, Self::EncodeData | Self::EncodeFailedWrite) - } + #[error("compression failed {0}")] + CompressionFailed(String), } #[cfg(test)] diff --git a/pumpkin-protocol/src/query.rs b/pumpkin-protocol/src/query.rs index 8943d91e..9cd0444c 100644 --- a/pumpkin-protocol/src/query.rs +++ b/pumpkin-protocol/src/query.rs @@ -24,7 +24,7 @@ impl RawQueryPacket { match reader.read_u16().await.map_err(|_| ())? { // Magic should always equal 65277 - // Since it denotes the protocl being used + // Since it denotes the protocol being used // Should not attempt to decode packets with other magic values 65277 => Ok(Self { packet_type: PacketType::from_u8(reader.read_u8().await.map_err(|_| ())?) @@ -52,7 +52,7 @@ impl SHandshake { #[derive(PartialEq, Debug)] pub struct SStatusRequest { pub session_id: i32, - pub challange_token: i32, + pub challenge_token: i32, // Full status request and basic status request are pretty much similar // So might as just use the same struct pub is_full_request: bool, @@ -62,7 +62,7 @@ impl SStatusRequest { pub async fn decode(packet: &mut RawQueryPacket) -> Result { Ok(Self { session_id: packet.reader.read_i32().await.map_err(|_| ())?, - challange_token: packet.reader.read_i32().await.map_err(|_| ())?, + challenge_token: packet.reader.read_i32().await.map_err(|_| ())?, is_full_request: { let mut buf = [0; 4]; @@ -74,7 +74,7 @@ impl SStatusRequest { Ok(0) => false, Ok(4) => true, _ => { - // Just ingnore malformed packets or errors + // Just ignore malformed packets or errors return Err(()); } } @@ -88,7 +88,7 @@ pub struct CHandshake { // For simplicity use a number type // Should be encoded as string here // Will be converted in encoding - pub challange_token: i32, + pub challenge_token: i32, } impl CHandshake { @@ -99,10 +99,10 @@ impl CHandshake { buf.write_u8(9).await.unwrap(); // Session ID buf.write_i32(self.session_id).await.unwrap(); - // Challange token + // Challenge token // Use CString to add null terminator and ensure no null bytes in the middle of data // Unwrap here since there should be no errors with nulls in the middle of data - let token = CString::new(self.challange_token.to_string()).unwrap(); + let token = CString::new(self.challenge_token.to_string()).unwrap(); buf.extend_from_slice(token.as_bytes_with_nul()); buf @@ -249,7 +249,7 @@ async fn test_handshake_response() { let packet = CHandshake { session_id: 1, - challange_token: 9513307, + challenge_token: 9513307, }; assert_eq!(bytes, packet.encode().await) @@ -265,7 +265,7 @@ async fn test_basic_stat_request() { let actual_packet = SStatusRequest { session_id: 1, - challange_token: 9513307, + challenge_token: 9513307, is_full_request: false, }; @@ -304,7 +304,7 @@ async fn test_full_stat_request() { let actual_packet = SStatusRequest { session_id: 1, - challange_token: 9513307, + challenge_token: 9513307, is_full_request: true, }; diff --git a/pumpkin-registry/src/lib.rs b/pumpkin-registry/src/lib.rs index 8be13d6f..6960a183 100644 --- a/pumpkin-registry/src/lib.rs +++ b/pumpkin-registry/src/lib.rs @@ -146,11 +146,10 @@ impl Registry { .wolf_variant .iter() .map(|s| { - // I present to you, A ugly hack which is done because Mojang developers decited to put is_ instead of just on 3 wolf varients while all others have just the biome, this causes the client to not find the biome and disconnect - let varient = s.1.clone(); + let variant = s.1.clone(); RegistryEntry { entry_id: s.0, - data: pumpkin_nbt::serializer::to_bytes_unnamed(&varient).unwrap(), + data: pumpkin_nbt::serializer::to_bytes_unnamed(&variant).unwrap(), } }) .collect(); diff --git a/pumpkin-world/src/chunk/mod.rs b/pumpkin-world/src/chunk/mod.rs index a04e9f5b..fcbb53f1 100644 --- a/pumpkin-world/src/chunk/mod.rs +++ b/pumpkin-world/src/chunk/mod.rs @@ -273,22 +273,22 @@ impl ChunkData { Some(d) => d, }; - // How many bits each block has in one of the pallete u64s + // How many bits each block has in one of the palette u64s let block_bit_size = { let size = 64 - (palette.len() as i64 - 1).leading_zeros(); max(4, size) }; - // How many blocks there are in one of the palletes u64s - let blocks_in_pallete = 64 / block_bit_size; + // How many blocks there are in one of the palettes u64s + let blocks_in_palette = 64 / block_bit_size; let mask = (1 << block_bit_size) - 1; 'block_loop: for block in block_data.iter() { - for i in 0..blocks_in_pallete { + for i in 0..blocks_in_palette { let index = (block >> (i * block_bit_size)) & mask; let block = &palette[index as usize]; // TODO allow indexing blocks directly so we can just use block_index and save some time? - // this is fine because we initalized the heightmap of `blocks` + // this is fine because we initialized the heightmap of `blocks` // from the cached value in the world file blocks.set_block_no_heightmap_update( ChunkRelativeBlockCoordinates { @@ -301,7 +301,7 @@ impl ChunkData { block_index += 1; - // if `SUBCHUNK_VOLUME `is not divisible by `blocks_in_pallete` the block_data + // if `SUBCHUNK_VOLUME `is not divisible by `blocks_in_palette` the block_data // can sometimes spill into other subchunks. We avoid that by aborting early if (block_index % SUBCHUNK_VOLUME) == 0 { break 'block_loop; diff --git a/pumpkin-world/src/world_gen/noise/router.rs b/pumpkin-world/src/world_gen/noise/router.rs index 782600fd..ddf605c3 100644 --- a/pumpkin-world/src/world_gen/noise/router.rs +++ b/pumpkin-world/src/world_gen/noise/router.rs @@ -271,7 +271,7 @@ impl BaseRouter { .clone() .min(ConstantFunction::new(5f64).mul(CAVES_ENTRANCES_OVERWORLD.clone())); - let mapped_cave_entraces_overworld = RangeFunction::< + let mapped_cave_entrances_overworld = RangeFunction::< NoEnvironment, SharedComponentReference, SharedComponentReference, @@ -286,7 +286,7 @@ impl BaseRouter { let blended_cave_entrances_overworld = apply_blend_density(apply_surface_slides( amplified, - mapped_cave_entraces_overworld.into(), + mapped_cave_entrances_overworld.into(), )) .min(CAVES_NOODLE_OVERWORLD.clone()); diff --git a/pumpkin-world/src/world_gen/proto_chunk.rs b/pumpkin-world/src/world_gen/proto_chunk.rs index 366df514..aceefc18 100644 --- a/pumpkin-world/src/world_gen/proto_chunk.rs +++ b/pumpkin-world/src/world_gen/proto_chunk.rs @@ -120,7 +120,7 @@ impl ProtoChunk { let horizontal_cell_block_count = self.sampler.horizontal_cell_block_count(); let vertical_cell_block_count = self.sampler.vertical_cell_block_count(); - let horizonal_cells = CHUNK_DIM / horizontal_cell_block_count; + let horizontal_cells = CHUNK_DIM / horizontal_cell_block_count; let min_y = self.sampler.min_y(); let minimum_cell_y = min_y / vertical_cell_block_count as i8; @@ -133,10 +133,10 @@ impl ProtoChunk { // - All unsafe functions are encapsulated and no mutable references are leaked unsafe { self.sampler.sample_start_density(); - for cell_x in 0..horizonal_cells { + for cell_x in 0..horizontal_cells { self.sampler.sample_end_density(cell_x); - for cell_z in 0..horizonal_cells { + for cell_z in 0..horizontal_cells { for cell_y in (0..cell_height).rev() { self.sampler.on_sampled_cell_corners(cell_y, cell_z); for local_y in (0..vertical_cell_block_count).rev() { diff --git a/pumpkin/src/client/container.rs b/pumpkin/src/client/container.rs index 2ebbb4a1..e8902638 100644 --- a/pumpkin/src/client/container.rs +++ b/pumpkin/src/client/container.rs @@ -314,15 +314,9 @@ impl Player { let find_condition = |(slot_number, slot): (usize, &mut Option)| { // TODO: Check for max item count here match slot { - Some(item) => { - if item.item_id == item_in_pressed_slot.item_id - && item.item_count != 64 - { - Some(slot_number) - } else { - None - } - } + Some(item) => (item.item_id == item_in_pressed_slot.item_id + && item.item_count != 64) + .then_some(slot_number), None => Some(slot_number), } }; @@ -496,11 +490,7 @@ impl Player { None } else { let entity_id = player.entity_id(); - if player_ids.contains(&entity_id) { - Some(player.clone()) - } else { - None - } + player_ids.contains(&entity_id).then(|| player.clone()) } }) .collect_vec(); @@ -560,13 +550,11 @@ impl Player { let slots = inventory.slots_with_hotbar_first(); let matching_slots = slots.filter_map(|slot| { - if let Some(item_slot) = slot.as_ref() { - if item_slot.item_id == item.id && item_slot.item_count < max_stack { + if let Some(item_slot) = slot.as_mut() { + (item_slot.item_id == item.id && item_slot.item_count < max_stack).then(|| { let item_count = item_slot.item_count; - Some((slot, item_count)) - } else { - None - } + (item_slot, item_count) + }) } else { None } @@ -579,15 +567,15 @@ impl Player { let amount_to_add = max_stack - item_count; if let Some(amount_left) = amount.checked_sub(u32::from(amount_to_add)) { amount = amount_left; - *slot = Some(ItemStack { + *slot = ItemStack { item_id: item.id, item_count: item.components.max_stack_size, - }); + }; } else { - *slot = Some(ItemStack { + *slot = ItemStack { item_id: item.id, item_count: max_stack - (amount_to_add - amount as u8), - }); + }; return; } } diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 47a7a03b..6c260447 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -222,9 +222,7 @@ impl Client { let mut enc = self.enc.lock().await; if let Err(error) = enc.append_packet(packet) { - if error.kickable() { - self.kick(&error.to_string()).await; - } + self.kick(&error.to_string()).await; return; } @@ -445,7 +443,6 @@ impl Client { "Failed to handle client packet id {} in Login State", packet.id.0 ); - return Ok(()); } }; Ok(()) diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 1dd55bf0..9d74dceb 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -722,7 +722,7 @@ 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 posistion + //TODO: Make this check for every entity in that position if !bounding_box.intersects(&block_bounding_box) { world .set_block_state(world_pos, block.default_state_id) diff --git a/pumpkin/src/command/args/arg_entity.rs b/pumpkin/src/command/args/arg_entity.rs index 996d1e60..dce52240 100644 --- a/pumpkin/src/command/args/arg_entity.rs +++ b/pumpkin/src/command/args/arg_entity.rs @@ -14,7 +14,7 @@ use crate::server::Server; use super::super::args::ArgumentConsumer; use super::{Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser}; -/// todo: implement for entitites that aren't players +/// todo: implement for entities that aren't players /// /// For selecting a single entity, eg. using @s, a player name or entity uuid. /// diff --git a/pumpkin/src/command/args/arg_position_2d.rs b/pumpkin/src/command/args/arg_position_2d.rs index b855348e..9dc45073 100644 --- a/pumpkin/src/command/args/arg_position_2d.rs +++ b/pumpkin/src/command/args/arg_position_2d.rs @@ -39,7 +39,7 @@ impl ArgumentConsumer for Position2DArgumentConsumer { ) -> Option> { let pos = MaybeRelativePosition2D::try_new(args.pop()?, args.pop()?)?; - let vec2 = pos.try_to_abolute(src.position())?; + let vec2 = pos.try_to_absolute(src.position())?; Some(Arg::Pos2D(vec2)) } @@ -64,7 +64,7 @@ impl MaybeRelativePosition2D { Some(Self(x.try_into().ok()?, z.try_into().ok()?)) } - fn try_to_abolute(self, origin: Option>) -> Option> { + fn try_to_absolute(self, origin: Option>) -> Option> { Some(Vector2::new( self.0.into_absolute(origin.map(|o| o.x))?, self.1.into_absolute(origin.map(|o| o.z))?, diff --git a/pumpkin/src/command/args/arg_postition_block.rs b/pumpkin/src/command/args/arg_position_block.rs similarity index 100% rename from pumpkin/src/command/args/arg_postition_block.rs rename to pumpkin/src/command/args/arg_position_block.rs diff --git a/pumpkin/src/command/args/mod.rs b/pumpkin/src/command/args/mod.rs index 3274dd8d..f27d850a 100644 --- a/pumpkin/src/command/args/mod.rs +++ b/pumpkin/src/command/args/mod.rs @@ -32,7 +32,7 @@ pub(crate) mod arg_message; pub(crate) mod arg_players; pub(crate) mod arg_position_2d; pub(crate) mod arg_position_3d; -pub(crate) mod arg_postition_block; +pub(crate) mod arg_position_block; pub(crate) mod arg_resource_location; pub(crate) mod arg_rotation; pub(crate) mod arg_simple; @@ -50,7 +50,7 @@ pub(crate) trait ArgumentConsumer: Sync + GetClientSideArgParser { /// Used for tab completion (but only if argument suggestion type is "minecraft:ask_server"!). /// - /// NOTE: This is called after this consumer's [`ArgumentConsumer::consume`] method returnd None, so if args is used here, make sure [`ArgumentConsumer::consume`] never returns None after mutating args. + /// NOTE: This is called after this consumer's [`ArgumentConsumer::consume`] method returned None, so if args is used here, make sure [`ArgumentConsumer::consume`] never returns None after mutating args. async fn suggest<'a>( &self, sender: &CommandSender<'a>, diff --git a/pumpkin/src/command/commands/cmd_bossbar.rs b/pumpkin/src/command/commands/cmd_bossbar.rs index eb9d3c8a..9caaea21 100644 --- a/pumpkin/src/command/commands/cmd_bossbar.rs +++ b/pumpkin/src/command/commands/cmd_bossbar.rs @@ -463,7 +463,7 @@ impl CommandExecutor for BossbarSetExecuter { .bossbars .lock() .await - .update_visibilty(server, namespace.to_string(), visibility) + .update_visibility(server, namespace.to_string(), visibility) .await { Ok(()) => {} diff --git a/pumpkin/src/command/commands/cmd_fill.rs b/pumpkin/src/command/commands/cmd_fill.rs index 031ba114..1a01fb15 100644 --- a/pumpkin/src/command/commands/cmd_fill.rs +++ b/pumpkin/src/command/commands/cmd_fill.rs @@ -1,5 +1,5 @@ use crate::command::args::arg_block::BlockArgumentConsumer; -use crate::command::args::arg_postition_block::BlockPosArgumentConsumer; +use crate::command::args::arg_position_block::BlockPosArgumentConsumer; use crate::command::args::{ConsumedArgs, FindArg}; use crate::command::tree::CommandTree; use crate::command::tree_builder::{argument, literal, require}; diff --git a/pumpkin/src/command/commands/cmd_setblock.rs b/pumpkin/src/command/commands/cmd_setblock.rs index 4bd3b264..0dd6ec9f 100644 --- a/pumpkin/src/command/commands/cmd_setblock.rs +++ b/pumpkin/src/command/commands/cmd_setblock.rs @@ -3,7 +3,7 @@ use pumpkin_core::text::color::NamedColor; use pumpkin_core::text::TextComponent; use crate::command::args::arg_block::BlockArgumentConsumer; -use crate::command::args::arg_postition_block::BlockPosArgumentConsumer; +use crate::command::args::arg_position_block::BlockPosArgumentConsumer; use crate::command::args::{ConsumedArgs, FindArg}; use crate::command::tree::CommandTree; use crate::command::tree_builder::{argument, literal, require}; diff --git a/pumpkin/src/command/dispatcher.rs b/pumpkin/src/command/dispatcher.rs index 4980c69e..9448c505 100644 --- a/pumpkin/src/command/dispatcher.rs +++ b/pumpkin/src/command/dispatcher.rs @@ -70,7 +70,7 @@ impl<'a> CommandDispatcher<'a> { } Err(pumpkin_error) => { pumpkin_error.log(); - sender.send_message(TextComponent::text("Unknown internal error occured while running command. Please see server log").color(Color::Named(NamedColor::Red))).await; + sender.send_message(TextComponent::text("Unknown internal error occurred while running command. Please see server log").color(Color::Named(NamedColor::Red))).await; } } } diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 9be5e8aa..92544ccb 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -11,7 +11,7 @@ use pumpkin_core::math::{ }; use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, EntityId}; use pumpkin_protocol::{ - client::play::{CSetEntityMetadata, CTeleportEntitiy, Metadata}, + client::play::{CSetEntityMetadata, CTeleportEntity, Metadata}, VarInt, }; @@ -134,7 +134,7 @@ impl Entity { pub async fn teleport(&self, position: Vector3, yaw: f32, pitch: f32) { self.world - .broadcast_packet_all(&CTeleportEntitiy::new( + .broadcast_packet_all(&CTeleportEntity::new( self.entity_id.into(), position, Vector3::new(0.0, 0.0, 0.0), diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 84a46545..030b484a 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -86,7 +86,7 @@ pub struct Player { /// The item currently being held by the player. pub carried_item: AtomicCell>, - /// send `send_abilties_update` when changed + /// send `send_abilities_update` when changed /// The player's abilities and special powers. /// /// This field represents the various abilities that the player possesses, such as flight, invulnerability, and other special effects. @@ -390,7 +390,7 @@ impl Player { } /// Updates the current abilities the Player has - pub async fn send_abilties_update(&self) { + pub async fn send_abilities_update(&self) { let mut b = 0i8; let abilities = &self.abilities.lock().await; diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 755658f2..d33cd9e1 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -2,6 +2,7 @@ #![deny(clippy::pedantic)] // #![warn(clippy::restriction)] #![deny(clippy::cargo)] +#![deny(clippy::if_then_some_else_none)] // REMOVE SOME WHEN RELEASE #![expect(clippy::cargo_common_metadata)] #![expect(clippy::multiple_crate_versions)] diff --git a/pumpkin/src/proxy/velocity.rs b/pumpkin/src/proxy/velocity.rs index cf4bcfc1..95f9aa07 100644 --- a/pumpkin/src/proxy/velocity.rs +++ b/pumpkin/src/proxy/velocity.rs @@ -34,7 +34,7 @@ pub enum VelocityError { #[error("Failed to read address")] FailedReadAddress, #[error("Failed to parse address")] - FailedParseAddres, + FailedParseAddress, #[error("Failed to read game profile name")] FailedReadProfileName, #[error("Failed to read game profile UUID")] @@ -129,7 +129,7 @@ pub fn receive_velocity_plugin_response( let socket_addr: SocketAddr = SocketAddr::new( addr.parse::() - .map_err(|_| VelocityError::FailedParseAddres)?, + .map_err(|_| VelocityError::FailedParseAddress)?, port, ); let profile = read_game_profile(&mut buf)?; diff --git a/pumpkin/src/query.rs b/pumpkin/src/query.rs index 39375ac6..0968c58c 100644 --- a/pumpkin/src/query.rs +++ b/pumpkin/src/query.rs @@ -27,16 +27,16 @@ pub async fn start_query_handler(server: Arc, bound_addr: SocketAddr) { .expect("Unable to bind to address"), ); - // Challange tokens are bound to the IP address and port - let valid_challange_tokens = Arc::new(RwLock::new(HashMap::new())); - let valid_challange_tokens_clone = valid_challange_tokens.clone(); - // All challange tokens ever created are expired every 30 seconds + // Challenge tokens are bound to the IP address and port + let valid_challenge_tokens = Arc::new(RwLock::new(HashMap::new())); + let valid_challenge_tokens_clone = valid_challenge_tokens.clone(); + // All challenge tokens ever created are expired every 30 seconds tokio::spawn(async move { let mut interval = time::interval(Duration::from_secs(30)); loop { interval.tick().await; - valid_challange_tokens_clone.write().await.clear(); + valid_challenge_tokens_clone.write().await.clear(); } }); @@ -49,7 +49,7 @@ pub async fn start_query_handler(server: Arc, bound_addr: SocketAddr) { loop { let socket = socket.clone(); - let valid_challange_tokens = valid_challange_tokens.clone(); + let valid_challenge_tokens = valid_challenge_tokens.clone(); let server = server.clone(); let mut buf = vec![0; 1024]; let (_, addr) = socket.recv_from(&mut buf).await.unwrap(); @@ -57,7 +57,7 @@ pub async fn start_query_handler(server: Arc, bound_addr: SocketAddr) { tokio::spawn(async move { if let Err(err) = handle_packet( buf, - valid_challange_tokens, + valid_challenge_tokens, server, socket, addr, @@ -87,10 +87,10 @@ async fn handle_packet( match raw_packet.packet_type { PacketType::Handshake => { if let Ok(packet) = SHandshake::decode(&mut raw_packet).await { - let challange_token = rand::thread_rng().gen_range(1..=i32::MAX); + let challenge_token = rand::thread_rng().gen_range(1..=i32::MAX); let response = CHandshake { session_id: packet.session_id, - challange_token, + challenge_token, }; // Ignore all errors since we don't want the query handler to crash @@ -99,7 +99,7 @@ async fn handle_packet( .send_to(response.encode().await.as_slice(), addr) .await; - clients.write().await.insert(challange_token, addr); + clients.write().await.insert(challenge_token, addr); } } PacketType::Status => { @@ -107,7 +107,7 @@ async fn handle_packet( if clients .read() .await - .get(&packet.challange_token) + .get(&packet.challenge_token) .is_some_and(|token_bound_ip: &SocketAddr| token_bound_ip == &addr) { if packet.is_full_request { @@ -150,7 +150,7 @@ async fn handle_packet( .send_to(response.encode().await.as_slice(), addr) .await; } else { - let resposne = CBasicStatus { + let response = CBasicStatus { session_id: packet.session_id, motd: CString::new(BASIC_CONFIG.motd.as_str())?, map: CString::new("world")?, @@ -161,7 +161,7 @@ async fn handle_packet( }; let _ = socket - .send_to(resposne.encode().await.as_slice(), addr) + .send_to(response.encode().await.as_slice(), addr) .await; } } diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index b01d7d0c..36ae0dde 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -73,16 +73,12 @@ impl Server { pub fn new() -> Self { // TODO: only create when needed - let auth_client = if BASIC_CONFIG.online_mode { - Some( - reqwest::Client::builder() - .timeout(Duration::from_millis(5000)) - .build() - .expect("Failed to to make reqwest client"), - ) - } else { - None - }; + let auth_client = BASIC_CONFIG.online_mode.then(|| { + reqwest::Client::builder() + .timeout(Duration::from_millis(5000)) + .build() + .expect("Failed to to make reqwest client") + }); // First register default command, after that plugins can put in their own let command_dispatcher = default_dispatcher(); diff --git a/pumpkin/src/world/custom_bossbar.rs b/pumpkin/src/world/custom_bossbar.rs index 3f7542e6..f6d02679 100644 --- a/pumpkin/src/world/custom_bossbar.rs +++ b/pumpkin/src/world/custom_bossbar.rs @@ -189,7 +189,7 @@ impl CustomBossbars { )) } - pub async fn update_visibilty( + pub async fn update_visibility( &mut self, server: &Server, resource_location: String, @@ -365,7 +365,7 @@ impl CustomBossbars { ) -> Result<(), BossbarUpdateError> { let bossbar = self.custom_bossbars.get_mut(&resource_location); if let Some(bossbar) = bossbar { - // Get differnce between old and new player list and remove bossbars from old players + // Get difference between old and new player list and remove bossbars from old players let removed_players: Vec = bossbar .player .iter() diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index b36f51ce..f86a8c8c 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -150,16 +150,16 @@ impl World { &self, sound_id: u16, category: SoundCategory, - posistion: &Vector3, + position: &Vector3, ) { let seed = thread_rng().gen::(); self.broadcast_packet_all(&CSoundEffect::new( VarInt(i32::from(sound_id)), None, category, - posistion.x, - posistion.y, - posistion.z, + position.x, + position.y, + position.z, 1.0, 1.0, seed, @@ -442,7 +442,7 @@ impl World { .await; log::debug!("Sending player abilities to {}", player.gameprofile.name); - player.send_abilties_update().await; + player.send_abilities_update().await; player.send_permission_lvl_update().await; From ff6f9ca2989b8ffe4c83abbe3290db113b3eec8e Mon Sep 17 00:00:00 2001 From: Kris <37947442+OfficialKris@users.noreply.github.com> Date: Thu, 12 Dec 2024 07:46:37 -0800 Subject: [PATCH 07/42] Added chest and furnace (#384) --- pumpkin-inventory/Cargo.toml | 1 + pumpkin-inventory/src/open_container.rs | 51 +++++++++++++++- pumpkin/src/block/block_manager.rs | 13 ++++ pumpkin/src/block/blocks/chest.rs | 70 ++++++++++++++++++++++ pumpkin/src/block/blocks/crafting_table.rs | 19 ++++-- pumpkin/src/block/blocks/furnace.rs | 59 ++++++++++++++++++ pumpkin/src/block/blocks/mod.rs | 2 + pumpkin/src/block/mod.rs | 5 ++ pumpkin/src/block/pumpkin_block.rs | 2 + pumpkin/src/client/player_packet.rs | 10 ++++ pumpkin/src/entity/player.rs | 8 ++- pumpkin/src/world/mod.rs | 10 ++++ 12 files changed, 243 insertions(+), 7 deletions(-) create mode 100644 pumpkin/src/block/blocks/chest.rs create mode 100644 pumpkin/src/block/blocks/furnace.rs diff --git a/pumpkin-inventory/Cargo.toml b/pumpkin-inventory/Cargo.toml index 28beef94..ac680e6d 100644 --- a/pumpkin-inventory/Cargo.toml +++ b/pumpkin-inventory/Cargo.toml @@ -8,6 +8,7 @@ edition.workspace = true pumpkin-world = { path = "../pumpkin-world" } pumpkin-registry = {path = "../pumpkin-registry"} pumpkin-macros = { path = "../pumpkin-macros" } +pumpkin-core = { path = "../pumpkin-core" } log.workspace = true rayon.workspace = true diff --git a/pumpkin-inventory/src/open_container.rs b/pumpkin-inventory/src/open_container.rs index fdabf912..ec68578f 100644 --- a/pumpkin-inventory/src/open_container.rs +++ b/pumpkin-inventory/src/open_container.rs @@ -1,11 +1,16 @@ use crate::crafting::check_if_matches_crafting; use crate::{Container, WindowType}; +use pumpkin_core::math::position::WorldPosition; +use pumpkin_world::block::block_registry::Block; use pumpkin_world::item::ItemStack; use std::sync::Arc; use tokio::sync::Mutex; pub struct OpenContainer { + // TODO: unique id should be here players: Vec, container: Arc>>, + location: Option, + block: Option, } impl OpenContainer { @@ -36,16 +41,30 @@ impl OpenContainer { } } - pub fn new_empty_container(player_id: i32) -> Self { + pub fn new_empty_container( + player_id: i32, + location: Option, + block: Option, + ) -> Self { Self { players: vec![player_id], container: Arc::new(Mutex::new(Box::new(C::default()))), + location, + block, } } pub fn all_player_ids(&self) -> Vec { self.players.clone() } + + pub fn get_location(&self) -> Option { + self.location + } + + pub fn get_block(&self) -> Option { + self.block.clone() + } } #[derive(Default)] pub struct Chest([Option; 27]); @@ -139,3 +158,33 @@ impl Container for CraftingTable { }) } } + +#[derive(Default)] +pub struct Furnace { + cook: Option, + fuel: Option, + output: Option, +} + +impl Container for Furnace { + fn window_type(&self) -> &'static WindowType { + &WindowType::Furnace + } + + fn window_name(&self) -> &'static str { + "Furnace" + } + fn all_slots(&mut self) -> Vec<&mut Option> { + let mut slots = vec![&mut self.cook]; + slots.push(&mut self.fuel); + slots.push(&mut self.output); + slots + } + + fn all_slots_ref(&self) -> Vec> { + let mut slots = vec![self.cook.as_ref()]; + slots.push(self.fuel.as_ref()); + slots.push(self.output.as_ref()); + slots + } +} diff --git a/pumpkin/src/block/block_manager.rs b/pumpkin/src/block/block_manager.rs index 50ab938e..01b3c08a 100644 --- a/pumpkin/src/block/block_manager.rs +++ b/pumpkin/src/block/block_manager.rs @@ -81,6 +81,19 @@ impl BlockManager { } } + pub async fn on_close( + &self, + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, + ) { + let pumpkin_block = self.get_pumpkin_block(block); + if let Some(pumpkin_block) = pumpkin_block { + pumpkin_block.on_close(player, location, server).await; + } + } + #[must_use] pub fn get_pumpkin_block(&self, block: &Block) -> Option<&Arc> { self.blocks diff --git a/pumpkin/src/block/blocks/chest.rs b/pumpkin/src/block/blocks/chest.rs new file mode 100644 index 00000000..1600bb09 --- /dev/null +++ b/pumpkin/src/block/blocks/chest.rs @@ -0,0 +1,70 @@ +use async_trait::async_trait; +use pumpkin_core::math::position::WorldPosition; +use pumpkin_inventory::{Chest, OpenContainer, WindowType}; +use pumpkin_macros::{pumpkin_block, sound}; +use pumpkin_world::{block::block_registry::get_block, item::item_registry::Item}; + +use crate::{ + block::{block_manager::BlockActionResult, pumpkin_block::PumpkinBlock}, + entity::player::Player, + server::Server, +}; + +#[pumpkin_block("minecraft:chest")] +pub struct ChestBlock; + +#[async_trait] +impl PumpkinBlock for ChestBlock { + async fn on_use<'a>(&self, player: &Player, _location: WorldPosition, server: &Server) { + self.open_chest_block(player, _location, server).await; + player + .world() + .play_block_sound(sound!("block.chest.open"), _location) + .await; + } + + async fn on_use_with_item<'a>( + &self, + player: &Player, + _location: WorldPosition, + _item: &Item, + server: &Server, + ) -> BlockActionResult { + self.open_chest_block(player, _location, server).await; + BlockActionResult::Consume + } + + async fn on_close<'a>(&self, player: &Player, _location: WorldPosition, _server: &Server) { + player + .world() + .play_block_sound(sound!("block.chest.close"), _location) + .await; + } +} + +impl ChestBlock { + pub async fn open_chest_block( + &self, + player: &Player, + location: WorldPosition, + server: &Server, + ) { + let entity_id = player.entity_id(); + // TODO: This should be a unique identifier for the each block container + player.open_container.store(Some(2)); + { + let mut open_containers = server.open_containers.write().await; + if let Some(chest) = open_containers.get_mut(&2) { + chest.add_player(entity_id); + } else { + let open_container = OpenContainer::new_empty_container::( + entity_id, + Some(location), + get_block("minecraft:chest").cloned(), + ); + open_containers.insert(2, open_container); + } + } + player.open_container(server, WindowType::Generic9x3).await; + } +} diff --git a/pumpkin/src/block/blocks/crafting_table.rs b/pumpkin/src/block/blocks/crafting_table.rs index 22e224fc..962d5662 100644 --- a/pumpkin/src/block/blocks/crafting_table.rs +++ b/pumpkin/src/block/blocks/crafting_table.rs @@ -6,7 +6,7 @@ use async_trait::async_trait; use pumpkin_core::math::position::WorldPosition; use pumpkin_inventory::{CraftingTable, OpenContainer, WindowType}; use pumpkin_macros::pumpkin_block; -use pumpkin_world::item::item_registry::Item; +use pumpkin_world::{block::block_registry::get_block, item::item_registry::Item}; #[pumpkin_block("minecraft:crafting_table")] pub struct CraftingTableBlock; @@ -14,7 +14,7 @@ pub struct CraftingTableBlock; #[async_trait] impl PumpkinBlock for CraftingTableBlock { async fn on_use<'a>(&self, player: &Player, _location: WorldPosition, server: &Server) { - self.open_crafting_screen(player, server).await; + self.open_crafting_screen(player, _location, server).await; } async fn on_use_with_item<'a>( @@ -24,13 +24,18 @@ impl PumpkinBlock for CraftingTableBlock { _item: &Item, server: &Server, ) -> BlockActionResult { - self.open_crafting_screen(player, server).await; + self.open_crafting_screen(player, _location, server).await; BlockActionResult::Consume } } impl CraftingTableBlock { - pub async fn open_crafting_screen(&self, player: &Player, server: &Server) { + pub async fn open_crafting_screen( + &self, + player: &Player, + location: WorldPosition, + server: &Server, + ) { //TODO: Adjust /craft command to real crafting table let entity_id = player.entity_id(); player.open_container.store(Some(1)); @@ -39,7 +44,11 @@ impl CraftingTableBlock { if let Some(ender_chest) = open_containers.get_mut(&1) { ender_chest.add_player(entity_id); } else { - let open_container = OpenContainer::new_empty_container::(entity_id); + let open_container = OpenContainer::new_empty_container::( + entity_id, + Some(location), + get_block("minecraft:crafting_table").cloned(), + ); open_containers.insert(1, open_container); } } diff --git a/pumpkin/src/block/blocks/furnace.rs b/pumpkin/src/block/blocks/furnace.rs new file mode 100644 index 00000000..8fc47949 --- /dev/null +++ b/pumpkin/src/block/blocks/furnace.rs @@ -0,0 +1,59 @@ +use crate::block::block_manager::BlockActionResult; +use crate::entity::player::Player; +use async_trait::async_trait; +use pumpkin_core::math::position::WorldPosition; +use pumpkin_inventory::Furnace; +use pumpkin_inventory::{OpenContainer, WindowType}; +use pumpkin_macros::pumpkin_block; +use pumpkin_world::block::block_registry::get_block; +use pumpkin_world::item::item_registry::Item; + +use crate::{block::pumpkin_block::PumpkinBlock, server::Server}; + +#[pumpkin_block("minecraft:furnace")] +pub struct FurnaceBlock; + +#[async_trait] +impl PumpkinBlock for FurnaceBlock { + async fn on_use<'a>(&self, player: &Player, _location: WorldPosition, server: &Server) { + self.open_furnace_screen(player, _location, server).await; + } + + async fn on_use_with_item<'a>( + &self, + player: &Player, + _location: WorldPosition, + _item: &Item, + server: &Server, + ) -> BlockActionResult { + self.open_furnace_screen(player, _location, server).await; + BlockActionResult::Consume + } +} + +impl FurnaceBlock { + pub async fn open_furnace_screen( + &self, + player: &Player, + location: WorldPosition, + server: &Server, + ) { + //TODO: Adjust /craft command to real crafting table + let entity_id = player.entity_id(); + player.open_container.store(Some(3)); + { + let mut open_containers = server.open_containers.write().await; + if let Some(ender_chest) = open_containers.get_mut(&3) { + ender_chest.add_player(entity_id); + } else { + let open_container = OpenContainer::new_empty_container::( + entity_id, + Some(location), + get_block("minecraft:furnace").cloned(), + ); + open_containers.insert(3, open_container); + } + } + player.open_container(server, WindowType::Furnace).await; + } +} diff --git a/pumpkin/src/block/blocks/mod.rs b/pumpkin/src/block/blocks/mod.rs index 8e7fde73..4bdfa819 100644 --- a/pumpkin/src/block/blocks/mod.rs +++ b/pumpkin/src/block/blocks/mod.rs @@ -1,2 +1,4 @@ +pub(crate) mod chest; pub(crate) mod crafting_table; +pub(crate) mod furnace; pub(crate) mod jukebox; diff --git a/pumpkin/src/block/mod.rs b/pumpkin/src/block/mod.rs index 10d1bc3f..b0779033 100644 --- a/pumpkin/src/block/mod.rs +++ b/pumpkin/src/block/mod.rs @@ -1,3 +1,6 @@ +use blocks::chest::ChestBlock; +use blocks::furnace::FurnaceBlock; + use crate::block::block_manager::BlockManager; use crate::block::blocks::crafting_table::CraftingTableBlock; use crate::block::blocks::jukebox::JukeboxBlock; @@ -13,6 +16,8 @@ pub fn default_block_manager() -> Arc { manager.register(JukeboxBlock); manager.register(CraftingTableBlock); + manager.register(FurnaceBlock); + manager.register(ChestBlock); Arc::new(manager) } diff --git a/pumpkin/src/block/pumpkin_block.rs b/pumpkin/src/block/pumpkin_block.rs index 78a3bd05..1849ea5a 100644 --- a/pumpkin/src/block/pumpkin_block.rs +++ b/pumpkin/src/block/pumpkin_block.rs @@ -29,4 +29,6 @@ pub trait PumpkinBlock: Send + Sync { async fn on_placed<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} async fn on_broken<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} + + async fn on_close<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 9d74dceb..cc54520f 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -803,6 +803,16 @@ impl Player { if let Some(id) = open_container { let mut open_containers = server.open_containers.write().await; if let Some(container) = open_containers.get_mut(&id) { + // If container contains both a location and a type, run the on_close block_manager handler + if let Some(pos) = container.get_location() { + if let Some(block) = container.get_block() { + server + .block_manager + .on_close(&block, self, pos, server) //block, self, location, server) + .await; + } + } + // Remove the player from the container container.remove_player(self.entity_id()); } self.open_container.store(None); diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 030b484a..2fbfa002 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -23,7 +23,9 @@ use pumpkin_core::{ use pumpkin_entity::{entity_type::EntityType, EntityId}; use pumpkin_inventory::player::PlayerInventory; use pumpkin_macros::sound; -use pumpkin_protocol::server::play::{SCookieResponse as SPCookieResponse, SPlayPingRequest}; +use pumpkin_protocol::server::play::{ + SCloseContainer, SCookieResponse as SPCookieResponse, SPlayPingRequest, +}; use pumpkin_protocol::{ bytebuf::packet_id::Packet, client::play::{ @@ -734,6 +736,10 @@ impl Player { SPCookieResponse::PACKET_ID => { self.handle_cookie_response(SPCookieResponse::read(bytebuf)?); } + SCloseContainer::PACKET_ID => { + self.handle_close_container(server, SCloseContainer::read(bytebuf)?) + .await; + } _ => { log::warn!("Failed to handle player packet id {}", packet.id.0); // TODO: We give an error if all play packets are implemented diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index f86a8c8c..867303bb 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -167,6 +167,16 @@ impl World { .await; } + pub async fn play_block_sound(&self, sound_id: u16, position: WorldPosition) { + let new_vec = Vector3::new( + f64::from(position.0.x) + 0.5, + f64::from(position.0.y) + 0.5, + f64::from(position.0.z) + 0.5, + ); + self.play_sound(sound_id, SoundCategory::Blocks, &new_vec) + .await; + } + pub async fn play_record(&self, record_id: i32, position: WorldPosition) { self.broadcast_packet_all(&CLevelEvent::new(1010, position, record_id, false)) .await; From 43c3f03bdb9674792043339c148fa4b963765e0d Mon Sep 17 00:00:00 2001 From: Kris <37947442+OfficialKris@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:30:58 -0800 Subject: [PATCH 08/42] Added Initial Time Packet on Login (#382) * Added initial time packet on login --- pumpkin/src/entity/player.rs | 13 +++++++++++++ pumpkin/src/world/mod.rs | 3 +++ 2 files changed, 16 insertions(+) diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 2fbfa002..5e14a0c1 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -23,6 +23,7 @@ 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, }; @@ -438,6 +439,18 @@ impl Player { self.permission_lvl } + /// Sends the world time to just the player. + pub async fn send_time(&self, world: &World) { + let l_world = world.level_time.lock().await; + self.client + .send_packet(&CUpdateTime::new( + l_world.world_age, + l_world.time_of_day, + true, + )) + .await; + } + /// Yaw and Pitch in degrees /// Rarly used, For example when waking up player from bed or first time spawn. Otherwise entity teleport is used /// Player should respond with the `SConfirmTeleport` packet diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 867303bb..282dab5f 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -411,6 +411,9 @@ impl World { .init_client(&player.client) .await; + // Sends initial time + player.send_time(self).await; + // Spawn in initial chunks player_chunker::player_join(self, player.clone()).await; From f587cb6748bae28443f19f9ecc80d1d24d0c3a1e Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Thu, 12 Dec 2024 23:40:52 +0100 Subject: [PATCH 09/42] remove iter_tools we only used them for collect_vec. I think its worth the "effort" to write Vec type annotations ourself and have one big crate with way too may features we don't need less --- Cargo.toml | 2 -- pumpkin-core/Cargo.toml | 2 -- pumpkin-inventory/Cargo.toml | 2 -- pumpkin-inventory/src/crafting.rs | 5 ++- pumpkin-inventory/src/drag_handler.rs | 5 ++- pumpkin-macros/Cargo.toml | 1 - pumpkin-protocol/Cargo.toml | 1 - .../src/client/play/c_chunk_data.rs | 7 ++-- pumpkin-registry/Cargo.toml | 6 ---- pumpkin-registry/src/recipe/recipe_formats.rs | 4 +-- pumpkin-world/Cargo.toml | 7 +--- pumpkin-world/src/chunk/anvil.rs | 5 ++- .../src/cylindrical_chunk_iterator.rs | 3 +- .../src/world_gen/noise/density/spline.rs | 21 ++++++----- pumpkin-world/src/world_gen/noise/perlin.rs | 36 ++++++------------- pumpkin-world/src/world_gen/proto_chunk.rs | 5 ++- pumpkin/Cargo.toml | 6 ++-- pumpkin/src/client/container.rs | 11 +++--- pumpkin/src/command/commands/cmd_list.rs | 17 ++++++--- pumpkin/src/world/mod.rs | 9 ++--- 20 files changed, 56 insertions(+), 99 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dd3e6ba0..0c92812e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,5 +61,3 @@ uuid = { version = "1.11.0", features = ["serde", "v3", "v4"] } derive_more = { version = "1.0.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" - -itertools = "0.13.0" diff --git a/pumpkin-core/Cargo.toml b/pumpkin-core/Cargo.toml index c56368a9..31fd66e5 100644 --- a/pumpkin-core/Cargo.toml +++ b/pumpkin-core/Cargo.toml @@ -13,5 +13,3 @@ num-derive.workspace = true colored = "2" md5 = "0.7.0" - -enum_dispatch = "0.3.13" diff --git a/pumpkin-inventory/Cargo.toml b/pumpkin-inventory/Cargo.toml index ac680e6d..34fb022f 100644 --- a/pumpkin-inventory/Cargo.toml +++ b/pumpkin-inventory/Cargo.toml @@ -12,8 +12,6 @@ pumpkin-core = { path = "../pumpkin-core" } log.workspace = true rayon.workspace = true -itertools.workspace = true -crossbeam.workspace = true tokio.workspace = true thiserror.workspace = true diff --git a/pumpkin-inventory/src/crafting.rs b/pumpkin-inventory/src/crafting.rs index 61e2a359..643ffbd3 100644 --- a/pumpkin-inventory/src/crafting.rs +++ b/pumpkin-inventory/src/crafting.rs @@ -1,4 +1,3 @@ -use itertools::Itertools; use pumpkin_registry::{ flatten_3x3, get_tag_values, IngredientSlot, IngredientType, RecipeResult, TagCategory, RECIPES, }; @@ -76,13 +75,13 @@ fn shapeless_crafting_match( input: [[Option; 3]; 3], pattern: &[[[Option; 3]; 3]], ) -> bool { - let mut pattern = pattern + let mut pattern: Vec = pattern .iter() .flatten() .flatten() .flatten() .cloned() - .collect_vec(); + .collect(); for item in input.into_iter().flatten().flatten() { if let Some(index) = pattern.iter().enumerate().find_map(|(i, recipe_item)| { if ingredient_slot_check(recipe_item, item) { diff --git a/pumpkin-inventory/src/drag_handler.rs b/pumpkin-inventory/src/drag_handler.rs index 70336c56..fefcd605 100644 --- a/pumpkin-inventory/src/drag_handler.rs +++ b/pumpkin-inventory/src/drag_handler.rs @@ -1,6 +1,5 @@ use crate::container_click::MouseDragType; use crate::{Container, InventoryError}; -use itertools::Itertools; use num_traits::Euclid; use pumpkin_world::item::ItemStack; use std::collections::HashMap; @@ -73,10 +72,10 @@ impl DragHandler { Err(InventoryError::MultiplePlayersDragging)? } let mut slots = container.all_slots(); - let slots_cloned = slots + let slots_cloned: Vec> = slots .iter() .map(|stack| stack.map(|item| item.to_owned())) - .collect_vec(); + .collect(); let Some(carried_item) = maybe_carried_item else { return Ok(()); }; diff --git a/pumpkin-macros/Cargo.toml b/pumpkin-macros/Cargo.toml index 233af3bd..9f242888 100644 --- a/pumpkin-macros/Cargo.toml +++ b/pumpkin-macros/Cargo.toml @@ -9,7 +9,6 @@ proc-macro = true [dependencies] serde.workspace = true serde_json.workspace = true -itertools.workspace = true proc-macro2 = "1.0" quote = "1.0" diff --git a/pumpkin-protocol/Cargo.toml b/pumpkin-protocol/Cargo.toml index b2a4c780..3e417ecb 100644 --- a/pumpkin-protocol/Cargo.toml +++ b/pumpkin-protocol/Cargo.toml @@ -13,7 +13,6 @@ pumpkin-core = { path = "../pumpkin-core" } uuid.workspace = true serde.workspace = true thiserror.workspace = true -itertools.workspace = true log.workspace = true tokio.workspace = true num-traits.workspace = true diff --git a/pumpkin-protocol/src/client/play/c_chunk_data.rs b/pumpkin-protocol/src/client/play/c_chunk_data.rs index cd98d1cf..919dcc2f 100644 --- a/pumpkin-protocol/src/client/play/c_chunk_data.rs +++ b/pumpkin-protocol/src/client/play/c_chunk_data.rs @@ -1,5 +1,4 @@ use crate::{bytebuf::ByteBuffer, BitSet, ClientPacket, VarInt}; -use itertools::Itertools; use pumpkin_macros::client_packet; use pumpkin_world::{chunk::ChunkData, DIRECT_PALETTE_BITS}; @@ -26,7 +25,7 @@ impl ClientPacket for CChunkData<'_> { data_buf.put_i16(block_count); //// Block states - let palette = chunk.iter().dedup().collect_vec(); + let palette = chunk; // TODO: make dynamic block_size work // TODO: make direct block_size work enum PaletteType { @@ -55,7 +54,7 @@ impl ClientPacket for CChunkData<'_> { palette.iter().for_each(|id| { // Palette - data_buf.put_var_int(&VarInt(**id as i32)); + data_buf.put_var_int(&VarInt(*id as i32)); }); // Data array length let data_array_len = chunk.len().div_ceil(64 / block_size as usize); @@ -67,7 +66,7 @@ impl ClientPacket for CChunkData<'_> { for block in block_clump.iter().rev() { let index = palette .iter() - .position(|b| *b == block) + .position(|b| b == block) .expect("Its just got added, ofc it should be there"); out_long = out_long << block_size | (index as i64); } diff --git a/pumpkin-registry/Cargo.toml b/pumpkin-registry/Cargo.toml index 3d55b6bb..a90646d7 100644 --- a/pumpkin-registry/Cargo.toml +++ b/pumpkin-registry/Cargo.toml @@ -13,9 +13,3 @@ indexmap = { version = "2.7.0", features = ["serde"] } serde.workspace = true serde_json.workspace = true -rayon.workspace = true - -num-traits.workspace = true -num-derive.workspace = true - -itertools.workspace = true diff --git a/pumpkin-registry/src/recipe/recipe_formats.rs b/pumpkin-registry/src/recipe/recipe_formats.rs index 7c26376a..5726c282 100644 --- a/pumpkin-registry/src/recipe/recipe_formats.rs +++ b/pumpkin-registry/src/recipe/recipe_formats.rs @@ -2,8 +2,6 @@ use super::super::recipe::RecipeType; use super::read::{ ingredients::IngredientSlot, CraftingType, RecipeKeys, RecipeResult, RecipeTrait, }; -use itertools::Itertools; - pub struct ShapedCrafting { keys: RecipeKeys, pattern: [[Option; 3]; 3], @@ -85,7 +83,7 @@ impl RecipeTrait for ShapelessCrafting { [v1, v2, v3] }) - .collect_vec() + .collect() } fn result(self) -> RecipeResult { diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index 18745f73..fcbc14a5 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -11,24 +11,20 @@ pumpkin-macros = { path = "../pumpkin-macros" } tokio.workspace = true rayon.workspace = true derive_more.workspace = true -itertools.workspace = true thiserror.workspace = true serde.workspace = true serde_json.workspace = true log.workspace = true -parking_lot.workspace = true num-traits.workspace = true num-derive.workspace = true -futures = "0.3" dashmap = "6.1.0" -# Compression +# Compression flate2 = "1.0" lz4 = "1.28.0" enum_dispatch = "0.3.13" -derive-getters = "0.5.0" fastnbt = { git = "https://github.com/owengage/fastnbt.git" } @@ -39,7 +35,6 @@ rand = "0.8.5" [dev-dependencies] criterion = { version = "0.5.1", features = ["html_reports"] } - [[bench]] name = "chunk_noise" harness = false diff --git a/pumpkin-world/src/chunk/anvil.rs b/pumpkin-world/src/chunk/anvil.rs index c97142ac..86389be6 100644 --- a/pumpkin-world/src/chunk/anvil.rs +++ b/pumpkin-world/src/chunk/anvil.rs @@ -4,7 +4,6 @@ use std::{ }; use flate2::bufread::{GzDecoder, ZlibDecoder}; -use itertools::Itertools; use crate::level::SaveFile; @@ -143,7 +142,7 @@ impl ChunkReader for AnvilChunkReader { }; // TODO: check checksum to make sure chunk is not corrupted - let header = file_buf.drain(0..5).collect_vec(); + let header: Vec = file_buf.drain(0..5).collect(); let compression = Compression::from_byte(header[4]).ok_or( ChunkReadingError::Compression(CompressionError::UnknownCompression), @@ -152,7 +151,7 @@ impl ChunkReader for AnvilChunkReader { let size = u32::from_be_bytes(header[..4].try_into().unwrap()); // size includes the compression scheme byte, so we need to subtract 1 - let chunk_data = file_buf.drain(0..size as usize - 1).collect_vec(); + let chunk_data = file_buf.drain(0..size as usize - 1).collect(); let decompressed_chunk = compression .decompress_data(chunk_data) .map_err(ChunkReadingError::Compression)?; diff --git a/pumpkin-world/src/cylindrical_chunk_iterator.rs b/pumpkin-world/src/cylindrical_chunk_iterator.rs index 67d1f498..7e388261 100644 --- a/pumpkin-world/src/cylindrical_chunk_iterator.rs +++ b/pumpkin-world/src/cylindrical_chunk_iterator.rs @@ -1,4 +1,3 @@ -use itertools::Itertools; use pumpkin_core::math::vector2::Vector2; #[derive(Debug, Clone, Copy, PartialEq)] @@ -76,7 +75,7 @@ impl Cylindrical { all_chunks .into_iter() .filter(|chunk| self.is_within_distance(chunk.x, chunk.z)) - .collect_vec() + .collect() } } diff --git a/pumpkin-world/src/world_gen/noise/density/spline.rs b/pumpkin-world/src/world_gen/noise/density/spline.rs index 9f469818..cb2aa6ee 100644 --- a/pumpkin-world/src/world_gen/noise/density/spline.rs +++ b/pumpkin-world/src/world_gen/noise/density/spline.rs @@ -1,7 +1,6 @@ use std::{marker::PhantomData, sync::Arc}; use enum_dispatch::enum_dispatch; -use itertools::Itertools; use crate::world_gen::noise::lerp; @@ -302,13 +301,13 @@ impl> MutableSpline = converted_points .into_iter() .map(|point| match point { SplinePoint::Immutable(point) => point, _ => unreachable!(), }) - .collect_vec(); + .collect(); SplineRef::Immutable( ImmutableSpline { function: shared, @@ -387,7 +386,7 @@ impl> MutableSplineImpl< let converted_points = points .into_iter() .map(|point| point.convert(converter)) - .collect_vec(); + .collect(); Self::create_new_spline(converted_base, converted_points) } @@ -398,7 +397,7 @@ impl> MutableSplineImpl< .points .iter() .map(|point| point.clone_to_new_point()) - .collect_vec(); + .collect(); Self::create_new_spline(cloned_function, cloned_points) } @@ -462,18 +461,18 @@ impl ImmutableSplineRef { converter: &mut dyn ConverterImpl, ) -> Option> { let converted_base = self.0.function.maybe_convert(converter); - let maybe_converted_points = self + let maybe_converted_points: Vec>> = self .0 .points .iter() .map(|point| point.maybe_convert(converter)) - .collect_vec(); + .collect(); if converted_base.is_none() && maybe_converted_points.iter().all(|point| point.is_none()) { None } else { let converted_base = converted_base.unwrap_or_else(|| self.0.function.clone().into()); - let converted_points = maybe_converted_points + let converted_points: Vec> = maybe_converted_points .into_iter() .enumerate() .map(|(index, point)| { @@ -483,7 +482,7 @@ impl ImmutableSplineRef { self.0.points[index].clone().into() } }) - .collect_vec(); + .collect(); Some(match converted_base { ComponentReferenceImplementation::Shared(shared) => { @@ -491,13 +490,13 @@ impl ImmutableSplineRef { .iter() .all(|point| matches!(point, SplinePoint::Immutable(_))) { - let immutable_points = converted_points + let immutable_points: Vec = converted_points .into_iter() .map(|point| match point { SplinePoint::Immutable(point) => point, _ => unreachable!(), }) - .collect_vec(); + .collect(); SplineRef::Immutable( ImmutableSpline { function: shared, diff --git a/pumpkin-world/src/world_gen/noise/perlin.rs b/pumpkin-world/src/world_gen/noise/perlin.rs index 4faf760f..09563485 100644 --- a/pumpkin-world/src/world_gen/noise/perlin.rs +++ b/pumpkin-world/src/world_gen/noise/perlin.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use itertools::{izip, Itertools}; use num_traits::Pow; use pumpkin_core::random::RandomGenerator; @@ -226,19 +225,6 @@ impl OctavePerlinNoiseSampler { random.skip(262); } } - - #[cfg(debug_assertions)] - { - use itertools::Itertools; - use num_traits::Zero; - - if let Ok(length1) = samplers.iter().filter(|x| x.is_some()).try_len() { - if let Ok(length2) = amplitudes.iter().filter(|x| !x.is_zero()).try_len() { - assert_eq!(length1, length2); - } - } - assert!(j >= i as i32 - 1); - } } else { let splitter = random.next_splitter(); for k in 0..i { @@ -256,20 +242,20 @@ impl OctavePerlinNoiseSampler { let mut lacunarity = 2f64.pow((-j) as f64); let max_value = Self::get_total_amplitude(2f64, persistence, amplitudes); - let persistences = (0..amplitudes.len()) + let persistences: Vec = (0..amplitudes.len()) .map(|_| { let result = persistence; persistence /= 2f64; result }) - .collect_vec(); - let lacunarities = (0..amplitudes.len()) + .collect(); + let lacunarities: Vec = (0..amplitudes.len()) .map(|_| { let result = lacunarity; lacunarity *= 2f64; result }) - .collect_vec(); + .collect(); Self { octave_samplers: samplers.into(), @@ -284,13 +270,13 @@ impl OctavePerlinNoiseSampler { pub fn sample(&self, x: f64, y: f64, z: f64) -> f64 { let mut d = 0f64; - for (sampler, amplitude, persistence, lacunarity) in izip!( - &self.octave_samplers, - &self.amplitudes, - &self.persistences, - &self.lacunarities - ) { - if let Some(sampler) = sampler { + let num_octaves = self.octave_samplers.len(); + for i in 0..num_octaves { + if let Some(sampler) = &self.octave_samplers[i] { + let lacunarity = self.lacunarities[i]; + let amplitude = self.amplitudes[i]; + let persistence = self.persistences[i]; + let g = sampler.sample_no_fade( Self::maintain_precision(x * lacunarity), Self::maintain_precision(y * lacunarity), diff --git a/pumpkin-world/src/world_gen/proto_chunk.rs b/pumpkin-world/src/world_gen/proto_chunk.rs index aceefc18..6f36cab6 100644 --- a/pumpkin-world/src/world_gen/proto_chunk.rs +++ b/pumpkin-world/src/world_gen/proto_chunk.rs @@ -216,7 +216,6 @@ impl ProtoChunk { mod test { use std::{fs, path::Path}; - use itertools::Itertools; use pumpkin_core::math::vector2::Vector2; use crate::read_data_from_file; @@ -235,7 +234,7 @@ mod test { .flat_block_map .into_iter() .map(|state| state.state_id) - .collect_vec() + .collect::>() ); } @@ -252,7 +251,7 @@ mod test { .flat_block_map .into_iter() .map(|state| state.state_id) - .collect_vec() + .collect::>() ); } } diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index 38bd7190..a3f4a14e 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -21,7 +21,6 @@ pumpkin-protocol = { path = "../pumpkin-protocol" } pumpkin-registry = { path = "../pumpkin-registry" } pumpkin-macros = { path = "../pumpkin-macros" } -itertools.workspace = true log.workspace = true crossbeam.workspace = true uuid.workspace = true @@ -31,7 +30,6 @@ thiserror.workspace = true num-traits.workspace = true num-derive.workspace = true -parking_lot.workspace = true # config serde.workspace = true @@ -56,14 +54,14 @@ reqwest = { version = "0.12.9", default-features = false, features = [ ] } sha1 = "0.10.6" -digest = "=0.11.0-pre.9" # velocity en hmac = "0.12.1" sha2 = "0.10.8" -# icon loading base64 = "0.22.1" + +# icon loading png = "0.17.15" # logging diff --git a/pumpkin/src/client/container.rs b/pumpkin/src/client/container.rs index e8902638..11a5c854 100644 --- a/pumpkin/src/client/container.rs +++ b/pumpkin/src/client/container.rs @@ -1,6 +1,5 @@ use crate::entity::player::Player; use crate::server::Server; -use itertools::Itertools; use pumpkin_core::text::TextComponent; use pumpkin_core::GameMode; use pumpkin_inventory::container_click::{ @@ -59,11 +58,11 @@ impl Player { let container = OptionallyCombinedContainer::new(&mut inventory, container); - let slots = container + let slots: Vec = container .all_slots_ref() .into_iter() .map(Slot::from) - .collect_vec(); + .collect(); let carried_item = self .carried_item @@ -462,7 +461,7 @@ impl Player { } async fn get_current_players_in_container(&self, server: &Server) -> Vec> { - let player_ids = { + let player_ids: Vec = { let open_containers = server.open_containers.read().await; open_containers .get(&self.open_container.load().unwrap()) @@ -470,7 +469,7 @@ impl Player { .all_player_ids() .into_iter() .filter(|player_id| *player_id != self.entity_id()) - .collect_vec() + .collect() }; let player_token = self.gameprofile.id; @@ -493,7 +492,7 @@ impl Player { player_ids.contains(&entity_id).then(|| player.clone()) } }) - .collect_vec(); + .collect(); players } diff --git a/pumpkin/src/command/commands/cmd_list.rs b/pumpkin/src/command/commands/cmd_list.rs index fe1b4e0c..5a81f8f9 100644 --- a/pumpkin/src/command/commands/cmd_list.rs +++ b/pumpkin/src/command/commands/cmd_list.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use async_trait::async_trait; -use itertools::Itertools; use pumpkin_config::BASIC_CONFIG; use pumpkin_core::text::TextComponent; @@ -35,10 +34,7 @@ impl CommandExecutor for ListExecutor { "There are {} of a max of {} players online: {}", players.len(), BASIC_CONFIG.max_players, - players - .iter() - .map(|player| &player.gameprofile.name) - .join(", ") + get_player_names(players) ) }; @@ -48,6 +44,17 @@ impl CommandExecutor for ListExecutor { } } +fn get_player_names(players: Vec>) -> String { + let mut names = String::new(); + for player in players { + if !names.is_empty() { + names.push_str(", "); + } + names.push_str(&player.gameprofile.name); + } + names +} + pub fn init_command_tree<'a>() -> CommandTree<'a> { CommandTree::new(NAMES, DESCRIPTION).execute(&ListExecutor) } diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 282dab5f..0f9b5bb9 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -9,7 +9,6 @@ use crate::{ error::PumpkinError, server::Server, }; -use itertools::Itertools; use level_time::LevelTime; use pumpkin_config::BasicConfiguration; use pumpkin_core::math::vector2::Vector2; @@ -224,11 +223,7 @@ impl World { server: &Server, ) { let command_dispatcher = &server.command_dispatcher; - let dimensions = &server - .dimensions - .iter() - .map(DimensionType::name) - .collect_vec(); + let dimensions: Vec<&str> = server.dimensions.iter().map(DimensionType::name).collect(); // This code follows the vanilla packet order let entity_id = player.entity_id(); @@ -245,7 +240,7 @@ impl World { .send_packet(&CLogin::new( entity_id, base_config.hardcore, - dimensions, + &dimensions, base_config.max_players.into(), base_config.view_distance.into(), // TODO: view distance base_config.simulation_distance.into(), // TODO: sim view dinstance From dde83d0dc5f281377e2c3538555631e21a1c1fb8 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Fri, 13 Dec 2024 00:29:48 +0100 Subject: [PATCH 10/42] put VarInt and VarLong Serialize and Deserialize together --- pumpkin-protocol/src/bytebuf/deserializer.rs | 6 +- pumpkin-protocol/src/bytebuf/packet_id.rs | 130 +------------------ pumpkin-protocol/src/lib.rs | 14 +- pumpkin-protocol/src/var_int.rs | 65 +++++++++- pumpkin-protocol/src/var_long.rs | 65 +++++++++- 5 files changed, 143 insertions(+), 137 deletions(-) diff --git a/pumpkin-protocol/src/bytebuf/deserializer.rs b/pumpkin-protocol/src/bytebuf/deserializer.rs index 829eb515..433c705a 100644 --- a/pumpkin-protocol/src/bytebuf/deserializer.rs +++ b/pumpkin-protocol/src/bytebuf/deserializer.rs @@ -127,16 +127,14 @@ impl<'de> de::Deserializer<'de> for Deserializer<'_> { where V: de::Visitor<'de>, { - let string = self.inner.get_string()?; - visitor.visit_str(&string) + visitor.visit_str(&self.inner.get_string()?) } fn deserialize_string(self, visitor: V) -> Result where V: de::Visitor<'de>, { - let string = self.inner.get_string()?; - visitor.visit_str(&string) + visitor.visit_str(&self.inner.get_string()?) } fn deserialize_bytes(self, _visitor: V) -> Result diff --git a/pumpkin-protocol/src/bytebuf/packet_id.rs b/pumpkin-protocol/src/bytebuf/packet_id.rs index 9f66e67d..913889db 100644 --- a/pumpkin-protocol/src/bytebuf/packet_id.rs +++ b/pumpkin-protocol/src/bytebuf/packet_id.rs @@ -1,135 +1,9 @@ -use bytes::BufMut; -use serde::{ - de::{self, DeserializeOwned, SeqAccess, Visitor}, - Deserialize, Deserializer, Serialize, Serializer, -}; +use serde::{de::DeserializeOwned, Serialize}; -use crate::{BitSet, ClientPacket, ServerPacket, VarInt, VarIntType, VarLong}; +use crate::{ClientPacket, ServerPacket, VarIntType}; use super::{deserializer, serializer, ByteBuffer, DeserializerError}; -impl Serialize for BitSet<'_> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // TODO: make this right - (&self.0, self.1).serialize(serializer) - } -} - -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 { - 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(de::Error::custom("VarInt was too large")) - } - } - - deserializer.deserialize_seq(VarIntVisitor) - } -} - -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 { - 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) - } -} - pub trait Packet { const PACKET_ID: VarIntType; } diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index f15928e9..0143abe4 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -1,6 +1,6 @@ use bytebuf::{packet_id::Packet, ByteBuffer, DeserializerError}; use pumpkin_core::text::{style::Style, TextComponent}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, Serializer}; pub mod bytebuf; pub mod client; @@ -24,12 +24,20 @@ pub const MAX_PACKET_SIZE: i32 = 2097152; /// usually 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, diff --git a/pumpkin-protocol/src/var_int.rs b/pumpkin-protocol/src/var_int.rs index 7c461d1f..90c3dde7 100644 --- a/pumpkin-protocol/src/var_int.rs +++ b/pumpkin-protocol/src/var_int.rs @@ -1,8 +1,15 @@ use bytes::{Buf, BufMut}; +use serde::{ + de::{SeqAccess, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; use thiserror::Error; -use crate::VarIntType; +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); @@ -84,3 +91,59 @@ pub enum VarIntDecodeError { #[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 { + 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 index 3f33ca77..cbb800c5 100644 --- a/pumpkin-protocol/src/var_long.rs +++ b/pumpkin-protocol/src/var_long.rs @@ -1,8 +1,15 @@ use bytes::{Buf, BufMut}; +use serde::{ + de::{self, SeqAccess, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; use thiserror::Error; -use crate::VarLongType; +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); @@ -85,3 +92,59 @@ pub enum VarLongDecodeError { #[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 { + 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 1e2bfad4f939ee8156ee646c671d58a71c596d34 Mon Sep 17 00:00:00 2001 From: kralverde <80051564+kralverde@users.noreply.github.com> Date: Fri, 13 Dec 2024 08:10:07 -0500 Subject: [PATCH 11/42] Parse seeds same as java (#391) * parse seeds same as java * use time with random seed --- pumpkin-core/src/random/mod.rs | 29 +++++++++++++++++-- pumpkin-world/src/level.rs | 2 +- .../src/world_gen/implementation/test.rs | 2 +- pumpkin-world/src/world_gen/seed.rs | 19 +++++++----- 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/pumpkin-core/src/random/mod.rs b/pumpkin-core/src/random/mod.rs index 8e147cb1..d52b58fd 100644 --- a/pumpkin-core/src/random/mod.rs +++ b/pumpkin-core/src/random/mod.rs @@ -1,3 +1,8 @@ +use std::{ + sync::atomic::{AtomicU64, Ordering}, + time, +}; + use legacy_rand::{LegacyRand, LegacySplitter}; use xoroshiro128::{Xoroshiro, XoroshiroSplitter}; @@ -5,6 +10,26 @@ mod gaussian; pub mod legacy_rand; pub mod xoroshiro128; +static SEED_UNIQUIFIER: AtomicU64 = AtomicU64::new(8682522807148012u64); + +pub fn get_seed() -> u64 { + let seed = SEED_UNIQUIFIER + .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |val| { + Some(val.wrapping_mul(1181783497276652981u64)) + }) + // We always return Some, so there will always be an Ok result + .unwrap(); + + let nanos = time::SystemTime::now() + .duration_since(time::SystemTime::UNIX_EPOCH) + .unwrap() + .as_nanos(); + + let nano_upper = (nanos >> 8) as u64; + let nano_lower = nanos as u64; + seed ^ nano_upper ^ nano_lower +} + pub enum RandomGenerator { Xoroshiro(Xoroshiro), Legacy(LegacyRand), @@ -186,7 +211,7 @@ pub trait RandomDeriverImpl { fn split_pos(&self, x: i32, y: i32, z: i32) -> impl RandomImpl; } -fn hash_block_pos(x: i32, y: i32, z: i32) -> i64 { +pub fn hash_block_pos(x: i32, y: i32, z: i32) -> i64 { let l = (x.wrapping_mul(3129871) as i64) ^ ((z as i64).wrapping_mul(116129781i64)) ^ (y as i64); let l = l .wrapping_mul(l) @@ -195,7 +220,7 @@ fn hash_block_pos(x: i32, y: i32, z: i32) -> i64 { l >> 16 } -fn java_string_hash(string: &str) -> i32 { +pub fn java_string_hash(string: &str) -> i32 { // All byte values of latin1 align with // the values of U+0000 - U+00FF making this code // equivalent to both java hash implementations diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index b8628082..2a6a9571 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -56,7 +56,7 @@ impl Level { "World region folder does not exist, despite there being a root folder." ); // TODO: read seed from level.dat - let seed = Seed(0); + let seed = get_or_create_seed(); let world_gen = get_world_gen(seed).into(); // TODO Read Seed from config. Self { diff --git a/pumpkin-world/src/world_gen/implementation/test.rs b/pumpkin-world/src/world_gen/implementation/test.rs index 6b360c45..c7bf6089 100644 --- a/pumpkin-world/src/world_gen/implementation/test.rs +++ b/pumpkin-world/src/world_gen/implementation/test.rs @@ -115,7 +115,7 @@ impl TerrainGenerator for TestTerrainGenerator { let entry = self.chunks.entry(*at); match entry { Entry::Vacant(entry) => { - let mut proto_chunk = ProtoChunk::new(*at, self.seed.0 as u64); + let mut proto_chunk = ProtoChunk::new(*at, self.seed.0); //let inst = std::time::Instant::now(); //println!("Populating chunk: {:?}", at); proto_chunk.populate_noise(); diff --git a/pumpkin-world/src/world_gen/seed.rs b/pumpkin-world/src/world_gen/seed.rs index 137a6ecd..1d9aa9cb 100644 --- a/pumpkin-world/src/world_gen/seed.rs +++ b/pumpkin-world/src/world_gen/seed.rs @@ -1,15 +1,20 @@ -use std::hash::{DefaultHasher, Hash, Hasher}; +use pumpkin_core::random::{get_seed, java_string_hash, legacy_rand::LegacyRand, RandomImpl}; #[derive(Clone, Copy)] -pub struct Seed(pub i64); +pub struct Seed(pub u64); impl From<&str> for Seed { fn from(value: &str) -> Self { - // TODO replace with a deterministic hasher (the same as vanilla?) - let mut hasher = DefaultHasher::new(); - value.hash(&mut hasher); + let trimmed = value.trim(); + let value = if !trimmed.is_empty() { + let i64_value = trimmed + .parse::() + .unwrap_or_else(|_| java_string_hash(trimmed) as i64); + Some(i64_value as u64) + } else { + None + }; - // TODO use cast_signed once the feature is stabilized. - Self(hasher.finish() as i64) + Seed(value.unwrap_or_else(|| LegacyRand::from_seed(get_seed()).next_i64() as u64)) } } From cbf9e63329d886102d23e9920fb9c1808533df5c Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Fri, 13 Dec 2024 15:42:04 +0100 Subject: [PATCH 12/42] Remove ByteBuffer struct Replaced trough trait's which implement bytes::Buf and bytes::BufMut. Closes https://github.com/Snowiiii/Pumpkin/issues/396 --- pumpkin-protocol/src/bytebuf/deserializer.rs | 49 +- pumpkin-protocol/src/bytebuf/mod.rs | 515 +++++++++--------- pumpkin-protocol/src/bytebuf/packet_id.rs | 11 +- pumpkin-protocol/src/bytebuf/serializer.rs | 17 +- .../src/client/config/c_known_packs.rs | 5 +- .../src/client/config/c_registry_data.rs | 6 +- .../src/client/login/c_login_success.rs | 5 +- .../src/client/play/c_boss_event.rs | 5 +- .../src/client/play/c_chunk_data.rs | 11 +- .../src/client/play/c_command_suggestions.rs | 5 +- .../src/client/play/c_commands.rs | 25 +- .../src/client/play/c_entity_sound_effect.rs | 5 +- .../src/client/play/c_player_info_update.rs | 5 +- .../src/client/play/c_player_position.rs | 5 +- .../src/client/play/c_sound_effect.rs | 5 +- .../src/client/play/c_teleport_entity.rs | 5 +- .../src/client/play/c_update_objectives.rs | 5 +- pumpkin-protocol/src/lib.rs | 13 +- pumpkin-protocol/src/packet_decoder.rs | 57 +- pumpkin-protocol/src/packet_encoder.rs | 40 +- .../src/server/config/s_cookie_response.rs | 17 +- .../src/server/config/s_plugin_message.rs | 11 +- pumpkin-protocol/src/server/handshake/mod.rs | 13 +- .../src/server/login/s_cookie_response.rs | 17 +- .../src/server/login/s_encryption_response.rs | 13 +- .../src/server/login/s_login_start.rs | 9 +- .../src/server/login/s_plugin_response.rs | 12 +- .../src/server/play/s_chat_message.rs | 16 +- .../src/server/play/s_cookie_response.rs | 17 +- .../src/server/play/s_interact.rs | 31 +- .../src/server/play/s_player_command.rs | 14 +- pumpkin/src/client/mod.rs | 15 +- pumpkin/src/error.rs | 4 +- pumpkin/src/proxy/velocity.rs | 20 +- pumpkin/src/world/mod.rs | 5 +- 35 files changed, 510 insertions(+), 498 deletions(-) diff --git a/pumpkin-protocol/src/bytebuf/deserializer.rs b/pumpkin-protocol/src/bytebuf/deserializer.rs index 433c705a..3b999e96 100644 --- a/pumpkin-protocol/src/bytebuf/deserializer.rs +++ b/pumpkin-protocol/src/bytebuf/deserializer.rs @@ -1,34 +1,27 @@ use std::fmt::Display; +use super::{ByteBuf, ReadingError}; +use bytes::Bytes; use serde::de::{self, DeserializeSeed, SeqAccess}; -use thiserror::Error; - -use super::ByteBuffer; pub struct Deserializer<'a> { - inner: &'a mut ByteBuffer, -} - -#[derive(Debug, Error)] -pub enum DeserializerError { - #[error("serializer error {0}")] - Message(String), + inner: &'a mut Bytes, } -impl de::Error for DeserializerError { +impl de::Error for ReadingError { fn custom(msg: T) -> Self { Self::Message(msg.to_string()) } } impl<'a> Deserializer<'a> { - pub fn new(buf: &'a mut ByteBuffer) -> Self { + pub fn new(buf: &'a mut Bytes) -> Self { Self { inner: buf } } } impl<'de> de::Deserializer<'de> for Deserializer<'_> { - type Error = DeserializerError; + type Error = ReadingError; fn deserialize_any(self, _visitor: V) -> Result where @@ -43,77 +36,77 @@ impl<'de> de::Deserializer<'de> for Deserializer<'_> { where V: de::Visitor<'de>, { - visitor.visit_bool(self.inner.get_bool()?) + visitor.visit_bool(self.inner.try_get_bool()?) } fn deserialize_i8(self, visitor: V) -> Result where V: de::Visitor<'de>, { - visitor.visit_i8(self.inner.get_i8()?) + visitor.visit_i8(self.inner.try_get_i8()?) } fn deserialize_i16(self, visitor: V) -> Result where V: de::Visitor<'de>, { - visitor.visit_i16(self.inner.get_i16()?) + visitor.visit_i16(self.inner.try_get_i16()?) } fn deserialize_i32(self, visitor: V) -> Result where V: de::Visitor<'de>, { - visitor.visit_i32(self.inner.get_i32()?) + visitor.visit_i32(self.inner.try_get_i32()?) } fn deserialize_i64(self, visitor: V) -> Result where V: de::Visitor<'de>, { - visitor.visit_i64(self.inner.get_i64()?) + visitor.visit_i64(self.inner.try_get_i64()?) } fn deserialize_u8(self, visitor: V) -> Result where V: de::Visitor<'de>, { - visitor.visit_u8(self.inner.get_u8()?) + visitor.visit_u8(self.inner.try_get_u8()?) } fn deserialize_u16(self, visitor: V) -> Result where V: de::Visitor<'de>, { - visitor.visit_u16(self.inner.get_u16()?) + visitor.visit_u16(self.inner.try_get_u16()?) } fn deserialize_u32(self, visitor: V) -> Result where V: de::Visitor<'de>, { - visitor.visit_u32(self.inner.get_u32()?) + visitor.visit_u32(self.inner.try_get_u32()?) } fn deserialize_u64(self, visitor: V) -> Result where V: de::Visitor<'de>, { - visitor.visit_u64(self.inner.get_u64()?) + visitor.visit_u64(self.inner.try_get_u64()?) } fn deserialize_f32(self, visitor: V) -> Result where V: de::Visitor<'de>, { - visitor.visit_f32(self.inner.get_f32()?) + visitor.visit_f32(self.inner.try_get_f32()?) } fn deserialize_f64(self, visitor: V) -> Result where V: de::Visitor<'de>, { - visitor.visit_f64(self.inner.get_f64()?) + visitor.visit_f64(self.inner.try_get_f64()?) } fn deserialize_char(self, _visitor: V) -> Result @@ -127,14 +120,14 @@ impl<'de> de::Deserializer<'de> for Deserializer<'_> { where V: de::Visitor<'de>, { - visitor.visit_str(&self.inner.get_string()?) + visitor.visit_str(&self.inner.try_get_string()?) } fn deserialize_string(self, visitor: V) -> Result where V: de::Visitor<'de>, { - visitor.visit_str(&self.inner.get_string()?) + visitor.visit_str(&self.inner.try_get_string()?) } fn deserialize_bytes(self, _visitor: V) -> Result @@ -196,7 +189,7 @@ impl<'de> de::Deserializer<'de> for Deserializer<'_> { } impl<'de, 'a, 'b: 'a> SeqAccess<'de> for Access<'a, 'b> { - type Error = DeserializerError; + type Error = ReadingError; fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> where @@ -229,7 +222,7 @@ impl<'de> de::Deserializer<'de> for Deserializer<'_> { } impl<'de, 'a, 'b: 'a> SeqAccess<'de> for Access<'a, 'b> { - type Error = DeserializerError; + type Error = ReadingError; fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> where diff --git a/pumpkin-protocol/src/bytebuf/mod.rs b/pumpkin-protocol/src/bytebuf/mod.rs index accceb4b..94e57f52 100644 --- a/pumpkin-protocol/src/bytebuf/mod.rs +++ b/pumpkin-protocol/src/bytebuf/mod.rs @@ -1,376 +1,359 @@ use core::str; use crate::{BitSet, FixedBitSet, VarInt, VarLong}; -use bytes::{Buf, BufMut, BytesMut}; +use bytes::{Buf, BufMut}; mod deserializer; -pub use deserializer::DeserializerError; +use thiserror::Error; pub mod packet_id; mod serializer; -#[derive(Debug)] -pub struct ByteBuffer { - buffer: BytesMut, +use std::mem::size_of; + +#[derive(Debug, Error)] +pub enum ReadingError { + /// End-of-File + #[error("EOF, Tried to read {0} but No bytes left to consume")] + EOF(String), + #[error("{0} is Incomplete")] + Incomplete(String), + #[error("{0} is too Large")] + TooLarge(String), + #[error("{0}")] + Message(String), } -impl ByteBuffer { - pub fn empty() -> Self { - Self { - buffer: BytesMut::new(), - } - } - pub fn new(buffer: BytesMut) -> Self { - Self { buffer } - } +pub trait ByteBuf: Buf { + fn try_get_bool(&mut self) -> Result; - pub fn get_var_int(&mut self) -> Result { - match VarInt::decode(&mut self.buffer) { - Ok(var_int) => Ok(var_int), - Err(error) => match error { - crate::VarIntDecodeError::Incomplete => Err(DeserializerError::Message( - "VarInt is Incomplete".to_string(), - )), - crate::VarIntDecodeError::TooLarge => { - Err(DeserializerError::Message("VarInt is too big".to_string())) - } - }, - } - } + fn try_get_u8(&mut self) -> Result; - pub fn get_var_long(&mut self) -> Result { - match VarLong::decode(&mut self.buffer) { - Ok(var_long) => Ok(var_long), - Err(error) => match error { - crate::VarLongDecodeError::Incomplete => Err(DeserializerError::Message( - "VarLong is Incomplete".to_string(), - )), - crate::VarLongDecodeError::TooLarge => { - Err(DeserializerError::Message("VarLong is too big".to_string())) - } - }, - } - } + fn try_get_i8(&mut self) -> Result; - pub fn get_string(&mut self) -> Result { - self.get_string_len(i16::MAX as i32) - } + fn try_get_u16(&mut self) -> Result; - pub fn get_string_len(&mut self, max_size: i32) -> Result { - let size = self.get_var_int()?.0; - if size > max_size { - return Err(DeserializerError::Message( - "String length is bigger than max size".to_string(), - )); - } + fn try_get_i16(&mut self) -> Result; - let data = self.copy_to_bytes(size as usize)?; - if data.len() as i32 > max_size { - return Err(DeserializerError::Message( - "String is bigger than max size".to_string(), - )); - } - match str::from_utf8(&data) { - Ok(string_result) => Ok(string_result.to_string()), - Err(e) => Err(DeserializerError::Message(e.to_string())), - } - } + fn try_get_u32(&mut self) -> Result; - pub fn get_bool(&mut self) -> Result { - Ok(self.get_u8()? != 0) - } + fn try_get_i32(&mut self) -> Result; - pub fn get_uuid(&mut self) -> Result { - let mut bytes = [0u8; 16]; - self.copy_to_slice(&mut bytes)?; - Ok(uuid::Uuid::from_slice(&bytes).expect("Failed to parse UUID")) - } + fn try_get_u64(&mut self) -> Result; - pub fn get_fixed_bitset(&mut self, bits: usize) -> Result { - self.copy_to_bytes(bits.div_ceil(8)) - } + fn try_get_i64(&mut self) -> Result; - pub fn put_bool(&mut self, v: bool) { - if v { - self.buffer.put_u8(1); - } else { - self.buffer.put_u8(0); - } - } + fn try_get_f32(&mut self) -> Result; - pub fn put_uuid(&mut self, v: &uuid::Uuid) { - // thats the vanilla way - let pair = v.as_u64_pair(); - self.put_u64(pair.0); - self.put_u64(pair.1); - } + fn try_get_f64(&mut self) -> Result; - pub fn put_string(&mut self, val: &str) { - self.put_string_len(val, i16::MAX as i32); - } + fn try_copy_to_bytes(&mut self, len: usize) -> Result; - pub fn put_string_len(&mut self, val: &str, max_size: i32) { - if val.len() as i32 > max_size { - // Should be panic?, I mean its our fault - panic!("String is too big"); - } - self.put_var_int(&val.len().into()); - self.buffer.put(val.as_bytes()); - } + fn try_copy_to_slice(&mut self, dst: &mut [u8]) -> Result<(), ReadingError>; - pub fn put_string_array(&mut self, array: &[String]) { - for string in array { - self.put_string(string) - } - } + fn try_get_var_int(&mut self) -> Result; - pub fn put_var_int(&mut self, value: &VarInt) { - value.encode(&mut self.buffer); - } + fn try_get_var_long(&mut self) -> Result; - pub fn put_bit_set(&mut self, set: &BitSet) { - self.put_var_int(&set.0); - for b in set.1 { - self.put_i64(*b); - } - } + fn try_get_string(&mut self) -> Result; + + fn try_get_string_len(&mut self, max_size: u32) -> Result; /// Reads a boolean. If true, the closure is called, and the returned value is /// wrapped in Some. Otherwise, this returns None. - pub fn get_option( + fn try_get_option( &mut self, - val: impl FnOnce(&mut Self) -> Result, - ) -> Result, DeserializerError> { - if self.get_bool()? { - Ok(Some(val(self)?)) - } else { - Ok(None) - } - } - /// Writes `true` if the option is Some, or `false` if None. If the option is - /// some, then it also calls the `write` closure. - pub fn put_option(&mut self, val: &Option, write: impl FnOnce(&mut Self, &T)) { - self.put_bool(val.is_some()); - if let Some(v) = val { - write(self, v) - } - } + val: impl FnOnce(&mut Self) -> Result, + ) -> Result, ReadingError>; - pub fn get_list( + fn get_list( &mut self, - val: impl Fn(&mut Self) -> Result, - ) -> Result, DeserializerError> { - let len = self.get_var_int()?.0 as usize; - let mut list = Vec::with_capacity(len); - for _ in 0..len { - list.push(val(self)?); - } - Ok(list) - } - /// Writes a list to the buffer. - pub fn put_list(&mut self, list: &[T], write: impl Fn(&mut Self, &T)) { - self.put_var_int(&list.len().into()); - for v in list { - write(self, v); - } + val: impl Fn(&mut Self) -> Result, + ) -> Result, ReadingError>; + + fn try_get_uuid(&mut self) -> Result; + + fn try_get_fixed_bitset(&mut self, bits: usize) -> Result; +} + +impl ByteBuf for T { + fn try_get_bool(&mut self) -> Result { + Ok(self.try_get_u8()? != 0) } - pub fn put_varint_arr(&mut self, v: &[i32]) { - self.put_list(v, |p, &v| p.put_var_int(&v.into())) + fn try_get_u8(&mut self) -> Result { + if size_of::() <= self.remaining() { + Ok(self.get_u8()) + } else { + Err(ReadingError::EOF("u8".to_string())) + } } - pub fn buf(&mut self) -> &mut BytesMut { - &mut self.buffer + fn try_get_i8(&mut self) -> Result { + if size_of::() <= self.remaining() { + Ok(self.get_i8()) + } else { + Err(ReadingError::EOF("i8".to_string())) + } } - // Trait equivalents - pub fn get_u8(&mut self) -> Result { - if self.buffer.has_remaining() { - Ok(self.buffer.get_u8()) + fn try_get_u16(&mut self) -> Result { + if size_of::() <= self.remaining() { + Ok(self.get_u16()) } else { - Err(DeserializerError::Message( - "No bytes left to consume".to_string(), - )) + Err(ReadingError::EOF("u16".to_string())) } } - pub fn get_i8(&mut self) -> Result { - if self.buffer.has_remaining() { - Ok(self.buffer.get_i8()) + fn try_get_i16(&mut self) -> Result { + if size_of::() <= self.remaining() { + Ok(self.get_i16()) } else { - Err(DeserializerError::Message( - "No bytes left to consume".to_string(), - )) + Err(ReadingError::EOF("i16".to_string())) } } - pub fn get_u16(&mut self) -> Result { - if self.buffer.remaining() >= 2 { - Ok(self.buffer.get_u16()) + fn try_get_u32(&mut self) -> Result { + if size_of::() <= self.remaining() { + Ok(self.get_u32()) } else { - Err(DeserializerError::Message( - "Less than 2 bytes left to consume".to_string(), - )) + Err(ReadingError::EOF("u32".to_string())) } } - pub fn get_i16(&mut self) -> Result { - if self.buffer.remaining() >= 2 { - Ok(self.buffer.get_i16()) + fn try_get_i32(&mut self) -> Result { + if size_of::() <= self.remaining() { + Ok(self.get_i32()) } else { - Err(DeserializerError::Message( - "Less than 2 bytes left to consume".to_string(), - )) + Err(ReadingError::EOF("i32".to_string())) } } - pub fn get_u32(&mut self) -> Result { - if self.buffer.remaining() >= 4 { - Ok(self.buffer.get_u32()) + fn try_get_u64(&mut self) -> Result { + if size_of::() <= self.remaining() { + Ok(self.get_u64()) } else { - Err(DeserializerError::Message( - "Less than 4 bytes left to consume".to_string(), - )) + Err(ReadingError::EOF("u64".to_string())) } } - pub fn get_i32(&mut self) -> Result { - if self.buffer.remaining() >= 4 { - Ok(self.buffer.get_i32()) + fn try_get_i64(&mut self) -> Result { + if size_of::() <= self.remaining() { + Ok(self.get_i64()) } else { - Err(DeserializerError::Message( - "Less than 4 bytes left to consume".to_string(), - )) + Err(ReadingError::EOF("i64".to_string())) } } - pub fn get_u64(&mut self) -> Result { - if self.buffer.remaining() >= 8 { - Ok(self.buffer.get_u64()) + fn try_get_f32(&mut self) -> Result { + if size_of::() <= self.remaining() { + Ok(self.get_f32()) } else { - Err(DeserializerError::Message( - "Less than 8 bytes left to consume".to_string(), - )) + Err(ReadingError::EOF("f32".to_string())) } } - pub fn get_i64(&mut self) -> Result { - if self.buffer.remaining() >= 8 { - Ok(self.buffer.get_i64()) + fn try_get_f64(&mut self) -> Result { + if size_of::() <= self.remaining() { + Ok(self.get_f64()) } else { - Err(DeserializerError::Message( - "Less than 8 bytes left to consume".to_string(), - )) + Err(ReadingError::EOF("f64".to_string())) } } - pub fn get_f32(&mut self) -> Result { - if self.buffer.remaining() >= 4 { - Ok(self.buffer.get_f32()) + fn try_copy_to_bytes(&mut self, len: usize) -> Result { + if self.remaining() >= len { + Ok(self.copy_to_bytes(len)) } else { - Err(DeserializerError::Message( - "Less than 4 bytes left to consume".to_string(), - )) + Err(ReadingError::Message("Unable to copy bytes".to_string())) } } - pub fn get_f64(&mut self) -> Result { - if self.buffer.remaining() >= 8 { - Ok(self.buffer.get_f64()) + fn try_copy_to_slice(&mut self, dst: &mut [u8]) -> Result<(), ReadingError> { + if self.remaining() >= dst.len() { + self.copy_to_slice(dst); + Ok(()) } else { - Err(DeserializerError::Message( - "Less than 8 bytes left to consume".to_string(), - )) + Err(ReadingError::Message("Unable to copy slice".to_string())) } } - // TODO: SerializerError? - pub fn put_u8(&mut self, n: u8) { - self.buffer.put_u8(n) + fn try_get_var_int(&mut self) -> Result { + 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())) + } + }, + } + } + fn try_get_var_long(&mut self) -> Result { + 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())) + } + }, + } } - pub fn put_i8(&mut self, n: i8) { - self.buffer.put_i8(n) + fn try_get_string(&mut self) -> Result { + self.try_get_string_len(i16::MAX as u32) } - pub fn put_u16(&mut self, n: u16) { - self.buffer.put_u16(n) + fn try_get_string_len(&mut self, max_size: u32) -> Result { + let size = self.try_get_var_int()?.0; + if size as u32 > 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 { + return Err(ReadingError::TooLarge("string".to_string())); + } + match str::from_utf8(&data) { + Ok(string_result) => Ok(string_result.to_string()), + Err(e) => Err(ReadingError::Message(e.to_string())), + } } - pub fn put_i16(&mut self, n: i16) { - self.buffer.put_i16(n) + fn try_get_option( + &mut self, + val: impl FnOnce(&mut Self) -> Result, + ) -> Result, ReadingError> { + if self.try_get_bool()? { + Ok(Some(val(self)?)) + } else { + Ok(None) + } } - pub fn put_u32(&mut self, n: u32) { - self.buffer.put_u32(n) + fn get_list( + &mut self, + val: impl Fn(&mut Self) -> Result, + ) -> Result, ReadingError> { + let len = self.try_get_var_int()?.0 as usize; + let mut list = Vec::with_capacity(len); + for _ in 0..len { + list.push(val(self)?); + } + Ok(list) } - pub fn put_i32(&mut self, n: i32) { - self.buffer.put_i32(n) + fn try_get_uuid(&mut self) -> Result { + let mut bytes = [0u8; 16]; + self.try_copy_to_slice(&mut bytes)?; + Ok(uuid::Uuid::from_slice(&bytes).expect("Failed to parse UUID")) } - pub fn put_u64(&mut self, n: u64) { - self.buffer.put_u64(n) + fn try_get_fixed_bitset(&mut self, bits: usize) -> Result { + self.try_copy_to_bytes(bits.div_ceil(8)) } +} + +pub trait ByteBufMut { + fn put_bool(&mut self, v: bool); + + fn put_uuid(&mut self, v: &uuid::Uuid); - pub fn put_i64(&mut self, n: i64) { - self.buffer.put_i64(n) + fn put_string(&mut self, val: &str); + + fn put_string_len(&mut self, val: &str, max_size: u32); + + fn put_string_array(&mut self, array: &[String]); + + fn put_bit_set(&mut self, set: &BitSet); + + /// Writes `true` if the option is Some, or `false` if None. If the option is + /// some, then it also calls the `write` closure. + fn put_option(&mut self, val: &Option, write: impl FnOnce(&mut Self, &G)); + + fn put_list(&mut self, list: &[G], write: impl Fn(&mut Self, &G)); + + fn put_var_int(&mut self, value: &VarInt); + + fn put_varint_arr(&mut self, v: &[i32]); +} + +impl ByteBufMut for T { + fn put_bool(&mut self, v: bool) { + if v { + self.put_u8(1); + } else { + self.put_u8(0); + } } - pub fn put_f32(&mut self, n: f32) { - self.buffer.put_f32(n) + fn put_uuid(&mut self, v: &uuid::Uuid) { + // thats the vanilla way + let pair = v.as_u64_pair(); + self.put_u64(pair.0); + self.put_u64(pair.1); } - pub fn put_f64(&mut self, n: f64) { - self.buffer.put_f64(n) + fn put_string(&mut self, val: &str) { + self.put_string_len(val, i16::MAX as u32); } - pub fn copy_to_bytes(&mut self, len: usize) -> Result { - if self.buffer.len() >= len { - Ok(self.buffer.copy_to_bytes(len)) - } else { - Err(DeserializerError::Message( - "Unable to copy bytes".to_string(), - )) + fn put_string_len(&mut self, val: &str, max_size: u32) { + if val.len() as u32 > max_size { + // Should be panic?, I mean its our fault + panic!("String is too big"); } + self.put_var_int(&val.len().into()); + self.put(val.as_bytes()); } - pub fn copy_to_slice(&mut self, dst: &mut [u8]) -> Result<(), DeserializerError> { - if self.buffer.remaining() >= dst.len() { - self.buffer.copy_to_slice(dst); - Ok(()) - } else { - Err(DeserializerError::Message( - "Unable to copy slice".to_string(), - )) + fn put_string_array(&mut self, array: &[String]) { + for string in array { + self.put_string(string) } } - pub fn put_slice(&mut self, src: &[u8]) { - self.buffer.put_slice(src) + fn put_var_int(&mut self, value: &VarInt) { + value.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); + } } - pub fn put(&mut self, src: T) - where - Self: Sized, - { - self.buffer.put(src) + fn put_option(&mut self, val: &Option, write: impl FnOnce(&mut Self, &G)) { + self.put_bool(val.is_some()); + if let Some(v) = val { + write(self, v) + } } - pub fn reserve(&mut self, additional: usize) { - self.buffer.reserve(additional) + fn put_list(&mut self, list: &[G], write: impl Fn(&mut Self, &G)) { + self.put_var_int(&list.len().into()); + for v in list { + write(self, v); + } } - pub fn get_slice(&mut self) -> BytesMut { - self.buffer.split() + fn put_varint_arr(&mut self, v: &[i32]) { + self.put_list(v, |p, &v| p.put_var_int(&v.into())) } } #[cfg(test)] mod test { + use bytes::{Bytes, BytesMut}; use serde::{Deserialize, Serialize}; use crate::{ - bytebuf::{deserializer, serializer, ByteBuffer}, + bytebuf::{deserializer, serializer}, VarInt, }; @@ -381,12 +364,14 @@ mod test { bar: i32, } let foo = Foo { bar: 69 }; - let mut serializer = serializer::Serializer::new(ByteBuffer::empty()); + let mut serializer = serializer::Serializer::new(BytesMut::new()); foo.serialize(&mut serializer).unwrap(); - let mut serialized: ByteBuffer = serializer.into(); - let deserialized: Foo = - Foo::deserialize(deserializer::Deserializer::new(&mut serialized)).unwrap(); + let serialized: BytesMut = serializer.into(); + let deserialized: Foo = Foo::deserialize(deserializer::Deserializer::new( + &mut Bytes::from(serialized), + )) + .unwrap(); assert_eq!(foo, deserialized); } @@ -398,12 +383,14 @@ mod test { bar: VarInt, } let foo = Foo { bar: 69.into() }; - let mut serializer = serializer::Serializer::new(ByteBuffer::empty()); + let mut serializer = serializer::Serializer::new(BytesMut::new()); foo.serialize(&mut serializer).unwrap(); - let mut serialized: ByteBuffer = serializer.into(); - let deserialized: Foo = - Foo::deserialize(deserializer::Deserializer::new(&mut serialized)).unwrap(); + let serialized: BytesMut = serializer.into(); + let deserialized: Foo = Foo::deserialize(deserializer::Deserializer::new( + &mut Bytes::from(serialized), + )) + .unwrap(); assert_eq!(foo, deserialized); } diff --git a/pumpkin-protocol/src/bytebuf/packet_id.rs b/pumpkin-protocol/src/bytebuf/packet_id.rs index 913889db..95c8a18e 100644 --- a/pumpkin-protocol/src/bytebuf/packet_id.rs +++ b/pumpkin-protocol/src/bytebuf/packet_id.rs @@ -1,8 +1,9 @@ +use bytes::{BufMut, Bytes, BytesMut}; use serde::{de::DeserializeOwned, Serialize}; use crate::{ClientPacket, ServerPacket, VarIntType}; -use super::{deserializer, serializer, ByteBuffer, DeserializerError}; +use super::{deserializer, serializer, ReadingError}; pub trait Packet { const PACKET_ID: VarIntType; @@ -12,14 +13,14 @@ impl

ClientPacket for P where P: Packet + Serialize, { - fn write(&self, bytebuf: &mut ByteBuffer) { - let mut serializer = serializer::Serializer::new(ByteBuffer::empty()); + fn write(&self, bytebuf: &mut BytesMut) { + let mut serializer = serializer::Serializer::new(BytesMut::new()); 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.buf()); + bytebuf.put(serializer.output); } } @@ -27,7 +28,7 @@ impl

ServerPacket for P where P: Packet + DeserializeOwned, { - fn read(bytebuf: &mut ByteBuffer) -> Result { + fn read(bytebuf: &mut Bytes) -> 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 d683bb03..cace3c6a 100644 --- a/pumpkin-protocol/src/bytebuf/serializer.rs +++ b/pumpkin-protocol/src/bytebuf/serializer.rs @@ -1,37 +1,38 @@ use std::fmt::Display; +use bytes::{BufMut, BytesMut}; use serde::{ ser::{self}, Serialize, }; use thiserror::Error; -use super::ByteBuffer; +use super::ByteBufMut; pub struct Serializer { - pub output: ByteBuffer, + pub output: BytesMut, } impl Serializer { - pub fn new(buf: ByteBuffer) -> Self { + pub fn new(buf: BytesMut) -> Self { Self { output: buf } } } -impl From for ByteBuffer { +impl From for BytesMut { fn from(val: Serializer) -> Self { val.output } } -impl AsRef for Serializer { - fn as_ref(&self) -> &ByteBuffer { +impl AsRef for Serializer { + fn as_ref(&self) -> &BytesMut { &self.output } } -impl AsMut for Serializer { - fn as_mut(&mut self) -> &mut ByteBuffer { +impl AsMut for Serializer { + fn as_mut(&mut self) -> &mut BytesMut { &mut self.output } } diff --git a/pumpkin-protocol/src/client/config/c_known_packs.rs b/pumpkin-protocol/src/client/config/c_known_packs.rs index 80b37580..467143e2 100644 --- a/pumpkin-protocol/src/client/config/c_known_packs.rs +++ b/pumpkin-protocol/src/client/config/c_known_packs.rs @@ -1,6 +1,7 @@ +use bytes::BytesMut; use pumpkin_macros::client_packet; -use crate::{bytebuf::ByteBuffer, ClientPacket, KnownPack}; +use crate::{bytebuf::ByteBufMut, ClientPacket, KnownPack}; #[client_packet("config:select_known_packs")] pub struct CKnownPacks<'a> { @@ -14,7 +15,7 @@ impl<'a> CKnownPacks<'a> { } impl ClientPacket for CKnownPacks<'_> { - fn write(&self, bytebuf: &mut ByteBuffer) { + fn write(&self, bytebuf: &mut BytesMut) { 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 d346ca45..001769d5 100644 --- a/pumpkin-protocol/src/client/config/c_registry_data.rs +++ b/pumpkin-protocol/src/client/config/c_registry_data.rs @@ -1,7 +1,7 @@ -use bytes::BytesMut; +use bytes::{BufMut, BytesMut}; use pumpkin_macros::client_packet; -use crate::{bytebuf::ByteBuffer, ClientPacket}; +use crate::{bytebuf::ByteBufMut, ClientPacket}; #[client_packet("config:registry_data")] pub struct CRegistryData<'a> { @@ -24,7 +24,7 @@ pub struct RegistryEntry<'a> { } impl ClientPacket for CRegistryData<'_> { - fn write(&self, bytebuf: &mut ByteBuffer) { + fn write(&self, bytebuf: &mut BytesMut) { bytebuf.put_string(self.registry_id); bytebuf.put_list::(self.entries, |p, v| { p.put_string(v.entry_id); diff --git a/pumpkin-protocol/src/client/login/c_login_success.rs b/pumpkin-protocol/src/client/login/c_login_success.rs index 462fcce5..8c6f311a 100644 --- a/pumpkin-protocol/src/client/login/c_login_success.rs +++ b/pumpkin-protocol/src/client/login/c_login_success.rs @@ -1,6 +1,7 @@ +use bytes::BytesMut; use pumpkin_macros::client_packet; -use crate::{bytebuf::ByteBuffer, ClientPacket, Property}; +use crate::{bytebuf::ByteBufMut, ClientPacket, Property}; #[client_packet("login:login_finished")] pub struct CLoginSuccess<'a> { @@ -20,7 +21,7 @@ impl<'a> CLoginSuccess<'a> { } impl ClientPacket for CLoginSuccess<'_> { - fn write(&self, bytebuf: &mut ByteBuffer) { + fn write(&self, bytebuf: &mut BytesMut) { 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 0100c154..449f18e1 100644 --- a/pumpkin-protocol/src/client/play/c_boss_event.rs +++ b/pumpkin-protocol/src/client/play/c_boss_event.rs @@ -1,6 +1,7 @@ -use crate::bytebuf::ByteBuffer; +use crate::bytebuf::ByteBufMut; use crate::client::play::bossevent_action::BosseventAction; use crate::{ClientPacket, VarInt}; +use bytes::{BufMut, BytesMut}; use pumpkin_macros::client_packet; #[client_packet("play:boss_event")] @@ -16,7 +17,7 @@ impl<'a> CBossEvent<'a> { } impl ClientPacket for CBossEvent<'_> { - fn write(&self, bytebuf: &mut ByteBuffer) { + fn write(&self, bytebuf: &mut BytesMut) { 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 919dcc2f..f655c00c 100644 --- a/pumpkin-protocol/src/client/play/c_chunk_data.rs +++ b/pumpkin-protocol/src/client/play/c_chunk_data.rs @@ -1,5 +1,6 @@ -use crate::{bytebuf::ByteBuffer, BitSet, ClientPacket, VarInt}; +use crate::{bytebuf::ByteBufMut, BitSet, ClientPacket, VarInt}; +use bytes::{BufMut, BytesMut}; use pumpkin_macros::client_packet; use pumpkin_world::{chunk::ChunkData, DIRECT_PALETTE_BITS}; @@ -7,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 crate::bytebuf::ByteBuffer) { + fn write(&self, buf: &mut BytesMut) { // Chunk X buf.put_i32(self.0.position.x); // Chunk Z @@ -18,7 +19,7 @@ impl ClientPacket for CChunkData<'_> { // Heightmaps buf.put_slice(&heightmap_nbt); - let mut data_buf = ByteBuffer::empty(); + let mut data_buf = BytesMut::new(); self.0.blocks.iter_subchunks().for_each(|chunk| { let block_count = chunk.len() as i16; // Block count @@ -102,9 +103,9 @@ impl ClientPacket for CChunkData<'_> { }); // Size - buf.put_var_int(&VarInt(data_buf.buf().len() as i32)); + buf.put_var_int(&VarInt(data_buf.len() as i32)); // Data - buf.put_slice(data_buf.buf()); + buf.put_slice(&data_buf); // TODO: block entities buf.put_var_int(&VarInt(0)); diff --git a/pumpkin-protocol/src/client/play/c_command_suggestions.rs b/pumpkin-protocol/src/client/play/c_command_suggestions.rs index fae9b04f..d3592ce3 100644 --- a/pumpkin-protocol/src/client/play/c_command_suggestions.rs +++ b/pumpkin-protocol/src/client/play/c_command_suggestions.rs @@ -1,7 +1,8 @@ +use bytes::{BufMut, BytesMut}; use pumpkin_core::text::TextComponent; use pumpkin_macros::client_packet; -use crate::{ClientPacket, VarInt}; +use crate::{bytebuf::ByteBufMut, ClientPacket, VarInt}; #[client_packet("play:command_suggestions")] pub struct CCommandSuggestions<'a> { @@ -28,7 +29,7 @@ impl<'a> CCommandSuggestions<'a> { } impl ClientPacket for CCommandSuggestions<'_> { - fn write(&self, bytebuf: &mut crate::bytebuf::ByteBuffer) { + fn write(&self, bytebuf: &mut BytesMut) { 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 bfd88556..d3b54d6f 100644 --- a/pumpkin-protocol/src/client/play/c_commands.rs +++ b/pumpkin-protocol/src/client/play/c_commands.rs @@ -1,6 +1,7 @@ +use bytes::{BufMut, BytesMut}; use pumpkin_macros::client_packet; -use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt}; +use crate::{bytebuf::ByteBufMut, ClientPacket, VarInt}; #[client_packet("play:commands")] pub struct CCommands<'a> { @@ -18,7 +19,7 @@ impl<'a> CCommands<'a> { } impl ClientPacket for CCommands<'_> { - fn write(&self, bytebuf: &mut ByteBuffer) { + fn write(&self, bytebuf: &mut BytesMut) { bytebuf.put_list(&self.nodes, |bytebuf, node: &ProtoNode| { node.write_to(bytebuf) }); @@ -51,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 ByteBuffer) { + pub fn write_to(&self, bytebuf: &mut BytesMut) { // flags let flags = match self.node_type { ProtoNodeType::Root => 0, @@ -187,7 +188,7 @@ impl ProtoCmdArgParser<'_> { pub const SCORE_HOLDER_FLAG_ALLOW_MULTIPLE: u8 = 1; - pub fn write_to_buffer(&self, bytebuf: &mut ByteBuffer) { + pub fn write_to_buffer(&self, bytebuf: &mut BytesMut) { match self { Self::Bool => bytebuf.put_var_int(&0.into()), Self::Float { min, max } => Self::write_number_arg(&1.into(), *min, *max, bytebuf), @@ -269,7 +270,7 @@ impl ProtoCmdArgParser<'_> { id: &VarInt, min: Option, max: Option, - bytebuf: &mut ByteBuffer, + bytebuf: &mut BytesMut, ) { let mut flags: u8 = 0; if min.is_some() { @@ -290,13 +291,13 @@ impl ProtoCmdArgParser<'_> { } } - fn write_with_flags(id: &VarInt, flags: u8, bytebuf: &mut ByteBuffer) { + fn write_with_flags(id: &VarInt, flags: u8, bytebuf: &mut BytesMut) { bytebuf.put_var_int(id); bytebuf.put_u8(flags); } - fn write_with_identifier(id: &VarInt, extra_identifier: &str, bytebuf: &mut ByteBuffer) { + fn write_with_identifier(id: &VarInt, extra_identifier: &str, bytebuf: &mut BytesMut) { bytebuf.put_var_int(id); bytebuf.put_string(extra_identifier); @@ -312,29 +313,29 @@ pub enum StringProtoArgBehavior { } trait NumberCmdArg { - fn write(self, bytebuf: &mut ByteBuffer); + fn write(self, bytebuf: &mut BytesMut); } impl NumberCmdArg for f32 { - fn write(self, bytebuf: &mut ByteBuffer) { + fn write(self, bytebuf: &mut BytesMut) { bytebuf.put_f32(self); } } impl NumberCmdArg for f64 { - fn write(self, bytebuf: &mut ByteBuffer) { + fn write(self, bytebuf: &mut BytesMut) { bytebuf.put_f64(self); } } impl NumberCmdArg for i32 { - fn write(self, bytebuf: &mut ByteBuffer) { + fn write(self, bytebuf: &mut BytesMut) { bytebuf.put_i32(self); } } impl NumberCmdArg for i64 { - fn write(self, bytebuf: &mut ByteBuffer) { + fn write(self, bytebuf: &mut BytesMut) { bytebuf.put_i64(self); } } 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 62afd46d..37848de8 100644 --- a/pumpkin-protocol/src/client/play/c_entity_sound_effect.rs +++ b/pumpkin-protocol/src/client/play/c_entity_sound_effect.rs @@ -1,6 +1,7 @@ +use bytes::{BufMut, BytesMut}; use pumpkin_macros::client_packet; -use crate::{ClientPacket, IDOrSoundEvent, SoundCategory, SoundEvent, VarInt}; +use crate::{bytebuf::ByteBufMut, ClientPacket, IDOrSoundEvent, SoundCategory, SoundEvent, VarInt}; #[client_packet("play:sound_entity")] pub struct CEntitySoundEffect { @@ -38,7 +39,7 @@ impl CEntitySoundEffect { } impl ClientPacket for CEntitySoundEffect { - fn write(&self, bytebuf: &mut crate::bytebuf::ByteBuffer) { + fn write(&self, bytebuf: &mut BytesMut) { bytebuf.put_var_int(&self.sound_event.id); if self.sound_event.id.0 == 0 { if let Some(test) = &self.sound_event.sound_event { 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 09fbff94..5626b387 100644 --- a/pumpkin-protocol/src/client/play/c_player_info_update.rs +++ b/pumpkin-protocol/src/client/play/c_player_info_update.rs @@ -1,6 +1,7 @@ +use bytes::{BufMut, BytesMut}; use pumpkin_macros::client_packet; -use crate::{bytebuf::ByteBuffer, ClientPacket, Property}; +use crate::{bytebuf::ByteBufMut, ClientPacket, Property}; use super::PlayerAction; @@ -22,7 +23,7 @@ impl<'a> CPlayerInfoUpdate<'a> { } impl ClientPacket for CPlayerInfoUpdate<'_> { - fn write(&self, bytebuf: &mut ByteBuffer) { + fn write(&self, bytebuf: &mut BytesMut) { 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 0a99a9e0..96a6bc59 100644 --- a/pumpkin-protocol/src/client/play/c_player_position.rs +++ b/pumpkin-protocol/src/client/play/c_player_position.rs @@ -1,7 +1,8 @@ +use bytes::{BufMut, BytesMut}; use pumpkin_core::math::vector3::Vector3; use pumpkin_macros::client_packet; -use crate::{ClientPacket, PositionFlag, VarInt}; +use crate::{bytebuf::ByteBufMut, ClientPacket, PositionFlag, VarInt}; #[client_packet("play:player_position")] pub struct CPlayerPosition<'a> { @@ -34,7 +35,7 @@ impl<'a> CPlayerPosition<'a> { } impl ClientPacket for CPlayerPosition<'_> { - fn write(&self, bytebuf: &mut crate::bytebuf::ByteBuffer) { + fn write(&self, bytebuf: &mut BytesMut) { 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_sound_effect.rs b/pumpkin-protocol/src/client/play/c_sound_effect.rs index 14e92ec9..d2f7f344 100644 --- a/pumpkin-protocol/src/client/play/c_sound_effect.rs +++ b/pumpkin-protocol/src/client/play/c_sound_effect.rs @@ -1,6 +1,7 @@ +use bytes::{BufMut, BytesMut}; use pumpkin_macros::client_packet; -use crate::{ClientPacket, IDOrSoundEvent, SoundCategory, SoundEvent, VarInt}; +use crate::{bytebuf::ByteBufMut, ClientPacket, IDOrSoundEvent, SoundCategory, SoundEvent, VarInt}; #[client_packet("play:sound")] pub struct CSoundEffect { @@ -44,7 +45,7 @@ impl CSoundEffect { } impl ClientPacket for CSoundEffect { - fn write(&self, bytebuf: &mut crate::bytebuf::ByteBuffer) { + fn write(&self, bytebuf: &mut BytesMut) { bytebuf.put_var_int(&self.sound_event.id); if self.sound_event.id.0 == 0 { if let Some(test) = &self.sound_event.sound_event { diff --git a/pumpkin-protocol/src/client/play/c_teleport_entity.rs b/pumpkin-protocol/src/client/play/c_teleport_entity.rs index b5bc2151..02029373 100644 --- a/pumpkin-protocol/src/client/play/c_teleport_entity.rs +++ b/pumpkin-protocol/src/client/play/c_teleport_entity.rs @@ -1,7 +1,8 @@ +use bytes::{BufMut, BytesMut}; use pumpkin_core::math::vector3::Vector3; use pumpkin_macros::client_packet; -use crate::{ClientPacket, PositionFlag, VarInt}; +use crate::{bytebuf::ByteBufMut, ClientPacket, PositionFlag, VarInt}; #[client_packet("play:teleport_entity")] pub struct CTeleportEntity<'a> { @@ -37,7 +38,7 @@ impl<'a> CTeleportEntity<'a> { } impl ClientPacket for CTeleportEntity<'_> { - fn write(&self, bytebuf: &mut crate::bytebuf::ByteBuffer) { + fn write(&self, bytebuf: &mut BytesMut) { 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 e5ae2e2d..12fcefc3 100644 --- a/pumpkin-protocol/src/client/play/c_update_objectives.rs +++ b/pumpkin-protocol/src/client/play/c_update_objectives.rs @@ -1,7 +1,8 @@ +use bytes::{BufMut, BytesMut}; use pumpkin_core::text::TextComponent; use pumpkin_macros::client_packet; -use crate::{ClientPacket, NumberFormat, VarInt}; +use crate::{bytebuf::ByteBufMut, ClientPacket, NumberFormat, VarInt}; #[client_packet("play:set_objective")] pub struct CUpdateObjectives<'a> { @@ -31,7 +32,7 @@ impl<'a> CUpdateObjectives<'a> { } impl ClientPacket for CUpdateObjectives<'_> { - fn write(&self, bytebuf: &mut crate::bytebuf::ByteBuffer) { + fn write(&self, bytebuf: &mut BytesMut) { 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 0143abe4..336e86f0 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -1,4 +1,5 @@ -use bytebuf::{packet_id::Packet, ByteBuffer, DeserializerError}; +use bytebuf::{packet_id::Packet, ReadingError}; +use bytes::{Bytes, BytesMut}; use pumpkin_core::text::{style::Style, TextComponent}; use serde::{Deserialize, Serialize, Serializer}; @@ -22,8 +23,10 @@ pub const CURRENT_MC_PROTOCOL: u32 = 769; pub const MAX_PACKET_SIZE: i32 = 2097152; -/// usually uses a namespace like "minecraft:thing" +/// 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]); @@ -90,15 +93,15 @@ pub struct SoundEvent { pub struct RawPacket { pub id: VarInt, - pub bytebuf: ByteBuffer, + pub bytebuf: Bytes, } pub trait ClientPacket: Packet { - fn write(&self, bytebuf: &mut ByteBuffer); + fn write(&self, bytebuf: &mut BytesMut); } pub trait ServerPacket: Packet + Sized { - fn read(bytebuf: &mut ByteBuffer) -> Result; + fn read(bytebuf: &mut Bytes) -> Result; } #[derive(Serialize)] diff --git a/pumpkin-protocol/src/packet_decoder.rs b/pumpkin-protocol/src/packet_decoder.rs index 9472c28e..f40db1bb 100644 --- a/pumpkin-protocol/src/packet_decoder.rs +++ b/pumpkin-protocol/src/packet_decoder.rs @@ -1,9 +1,9 @@ use aes::cipher::{generic_array::GenericArray, BlockDecryptMut, BlockSizeUser, KeyIvInit}; -use bytes::{Buf, BytesMut}; +use bytes::{Buf, Bytes, BytesMut}; use libdeflater::{DecompressionError, Decompressor}; use thiserror::Error; -use crate::{bytebuf::ByteBuffer, RawPacket, VarInt, VarIntDecodeError, MAX_PACKET_SIZE}; +use crate::{RawPacket, VarInt, VarIntDecodeError, MAX_PACKET_SIZE}; type Cipher = cfb8::Decryptor; @@ -113,7 +113,7 @@ impl PacketDecoder { data.advance(data.len() - r.len()); Ok(Some(RawPacket { id: packet_id, - bytebuf: ByteBuffer::new(data), + bytebuf: Bytes::from(data), })) } @@ -201,8 +201,11 @@ impl From for PacketDecodeError { #[cfg(test)] mod tests { + use crate::bytebuf::ByteBufMut; + use super::*; use aes::Aes128; + use bytes::BufMut; use cfb8::cipher::AsyncStreamCipher; use cfb8::Encryptor as Cfb8Encryptor; use libdeflater::{CompressionLvl, Compressor}; @@ -232,18 +235,18 @@ mod tests { key: Option<&[u8; 16]>, iv: Option<&[u8; 16]>, ) -> Vec { - let mut buffer = ByteBuffer::empty(); + let mut buffer = BytesMut::new(); if compress { // Create a buffer that includes packet_id_varint and payload - let mut data_to_compress = ByteBuffer::empty(); + let mut data_to_compress = BytesMut::new(); let packet_id_varint = VarInt(packet_id); data_to_compress.put_var_int(&packet_id_varint); data_to_compress.put_slice(payload); // Compress the combined data - let compressed_payload = compress_zlib(data_to_compress.buf()); - let data_len = data_to_compress.buf().len() as i32; // 1 + payload.len() + let compressed_payload = compress_zlib(&data_to_compress); + let data_len = data_to_compress.len() as i32; // 1 + payload.len() let data_len_varint = VarInt(data_len); buffer.put_var_int(&data_len_varint); buffer.put_slice(&compressed_payload); @@ -255,7 +258,7 @@ mod tests { } // Calculate packet length: length of buffer - let packet_len = buffer.buf().len() as i32; + let packet_len = buffer.len() as i32; let packet_len_varint = VarInt(packet_len); let mut packet_length_encoded = Vec::new(); { @@ -265,7 +268,7 @@ mod tests { // Create a new buffer for the entire packet let mut packet = Vec::new(); packet.extend_from_slice(&packet_length_encoded); - packet.extend_from_slice(buffer.buf()); + packet.extend_from_slice(&buffer); // Encrypt if key and iv are provided if let (Some(k), Some(v)) = (key, iv) { @@ -297,9 +300,9 @@ mod tests { let result = decoder.decode().expect("Decoding failed"); assert!(result.is_some()); - let mut raw_packet = result.unwrap(); + let raw_packet = result.unwrap(); assert_eq!(raw_packet.id.0, packet_id); - assert_eq!(raw_packet.bytebuf.buf().as_ref(), payload); + assert_eq!(raw_packet.bytebuf.as_ref(), payload); } /// Test decoding with compression @@ -323,9 +326,9 @@ mod tests { let result = decoder.decode().expect("Decoding failed"); assert!(result.is_some()); - let mut raw_packet = result.unwrap(); + let raw_packet = result.unwrap(); assert_eq!(raw_packet.id.0, packet_id); - assert_eq!(raw_packet.bytebuf.buf().as_ref(), payload); + assert_eq!(raw_packet.bytebuf.as_ref(), payload); } /// Test decoding with encryption @@ -354,9 +357,9 @@ mod tests { let result = decoder.decode().expect("Decoding failed"); assert!(result.is_some()); - let mut raw_packet = result.unwrap(); + let raw_packet = result.unwrap(); assert_eq!(raw_packet.id.0, packet_id); - assert_eq!(raw_packet.bytebuf.buf().as_ref(), payload); + assert_eq!(raw_packet.bytebuf.as_ref(), payload); } /// Test decoding with both compression and encryption @@ -385,9 +388,9 @@ mod tests { let result = decoder.decode().expect("Decoding failed"); assert!(result.is_some()); - let mut raw_packet = result.unwrap(); + let raw_packet = result.unwrap(); assert_eq!(raw_packet.id.0, packet_id); - assert_eq!(raw_packet.bytebuf.buf().as_ref(), payload); + assert_eq!(raw_packet.bytebuf.as_ref(), payload); } /// Test decoding with invalid compressed data @@ -398,28 +401,28 @@ mod tests { let invalid_compressed_data = vec![0xFF, 0xFF, 0xFF]; // Invalid Zlib data // Build the packet with compression enabled but invalid compressed data - let mut buffer = ByteBuffer::empty(); + let mut buffer = BytesMut::new(); let data_len_varint = VarInt(data_len); buffer.put_var_int(&data_len_varint); buffer.put_slice(&invalid_compressed_data); // Calculate packet length: VarInt(data_len) + invalid compressed data - let packet_len = buffer.buf().len() as i32; + let packet_len = buffer.len() as i32; let packet_len_varint = VarInt(packet_len); // Create a new buffer for the entire packet - let mut packet_buffer = ByteBuffer::empty(); + let mut packet_buffer = BytesMut::new(); packet_buffer.put_var_int(&packet_len_varint); - packet_buffer.put_slice(buffer.buf()); + packet_buffer.put_slice(&buffer); - let packet_bytes = packet_buffer.buf(); + let packet_bytes = packet_buffer; // Initialize the decoder with compression enabled let mut decoder = PacketDecoder::default(); decoder.set_compression(true); // Feed the invalid compressed packet to the decoder - decoder.queue_slice(packet_bytes); + decoder.queue_slice(&packet_bytes); // Attempt to decode and expect a decompression error let result = decoder.decode(); @@ -450,9 +453,9 @@ mod tests { let result = decoder.decode().expect("Decoding failed"); assert!(result.is_some()); - let mut raw_packet = result.unwrap(); + let raw_packet = result.unwrap(); assert_eq!(raw_packet.id.0, packet_id); - assert_eq!(raw_packet.bytebuf.buf().as_ref(), payload); + assert_eq!(raw_packet.bytebuf.as_ref(), payload); } /// Test decoding with maximum length packet @@ -484,13 +487,13 @@ mod tests { "Decoder returned None when it should have decoded a packet" ); - let mut raw_packet = result.unwrap(); + let raw_packet = result.unwrap(); assert_eq!( raw_packet.id.0, packet_id, "Decoded packet_id does not match" ); assert_eq!( - raw_packet.bytebuf.buf().as_ref(), + raw_packet.bytebuf.as_ref(), &payload[..], "Decoded payload does not match" ); diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index 0df2b157..3ce1f3b4 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::{bytebuf::ByteBuffer, ClientPacket, VarInt, MAX_PACKET_SIZE}; +use crate::{ClientPacket, VarInt, MAX_PACKET_SIZE}; type Cipher = cfb8::Encryptor; @@ -39,10 +39,8 @@ impl PacketEncoder { let start_len = self.buf.len(); // Write the Packet ID first VarInt(P::PACKET_ID).encode(&mut self.buf); - let mut packet_buf = ByteBuffer::empty(); // Now write the packet into an empty buffer - packet.write(&mut packet_buf); - self.buf.put(packet_buf.buf()); + packet.write(&mut self.buf); let data_len = self.buf.len() - start_len; @@ -274,10 +272,10 @@ mod tests { // Remaining buffer is the payload // We need to obtain the expected payload - let mut expected_payload = ByteBuffer::empty(); + let mut expected_payload = BytesMut::new(); packet.write(&mut expected_payload); - assert_eq!(buffer, expected_payload.buf()); + assert_eq!(buffer, expected_payload); } /// Test encoding with compression @@ -308,10 +306,10 @@ mod tests { // Read data length VarInt (uncompressed data length) let data_length = decode_varint(&mut buffer).expect("Failed to decode data length"); - let mut expected_payload = ByteBuffer::empty(); + let mut expected_payload = BytesMut::new(); packet.write(&mut expected_payload); let uncompressed_data_length = - VarInt(CStatusResponse::PACKET_ID).written_size() + expected_payload.buf().len(); + VarInt(CStatusResponse::PACKET_ID).written_size() + expected_payload.len(); assert_eq!(data_length as usize, uncompressed_data_length); // Remaining buffer is the compressed data @@ -330,7 +328,7 @@ mod tests { assert_eq!(decoded_packet_id, CStatusResponse::PACKET_ID); // Remaining buffer is the payload - assert_eq!(decompressed_buffer, expected_payload.buf()); + assert_eq!(decompressed_buffer, expected_payload); } /// Test encoding with encryption @@ -364,10 +362,10 @@ mod tests { assert_eq!(decoded_packet_id, CStatusResponse::PACKET_ID); // Remaining buffer is the payload - let mut expected_payload = ByteBuffer::empty(); + let mut expected_payload = BytesMut::new(); packet.write(&mut expected_payload); - assert_eq!(buffer, expected_payload.buf()); + assert_eq!(buffer, expected_payload); } /// Test encoding with both compression and encryption @@ -405,10 +403,10 @@ mod tests { // Read data length VarInt (uncompressed data length) let data_length = decode_varint(&mut buffer).expect("Failed to decode data length"); - let mut expected_payload = ByteBuffer::empty(); + let mut expected_payload = BytesMut::new(); packet.write(&mut expected_payload); let uncompressed_data_length = - VarInt(CStatusResponse::PACKET_ID).written_size() + expected_payload.buf().len(); + VarInt(CStatusResponse::PACKET_ID).written_size() + expected_payload.len(); assert_eq!(data_length as usize, uncompressed_data_length); // Remaining buffer is the compressed data @@ -427,7 +425,7 @@ mod tests { assert_eq!(decoded_packet_id, CStatusResponse::PACKET_ID); // Remaining buffer is the payload - assert_eq!(decompressed_buffer, expected_payload.buf()); + assert_eq!(decompressed_buffer, expected_payload); } /// Test encoding with zero-length payload @@ -455,15 +453,15 @@ mod tests { assert_eq!(decoded_packet_id, CStatusResponse::PACKET_ID); // Remaining buffer is the payload (empty) - let mut expected_payload = ByteBuffer::empty(); + let mut expected_payload = BytesMut::new(); packet.write(&mut expected_payload); assert_eq!( buffer.len(), - expected_payload.buf().len(), + expected_payload.len(), "Payload length mismatch" ); - assert_eq!(buffer, expected_payload.buf()); + assert_eq!(buffer, expected_payload); } /// Test encoding with maximum length payload @@ -500,10 +498,10 @@ mod tests { assert_eq!(decoded_packet_id, CStatusResponse::PACKET_ID); // Remaining buffer is the payload - let mut expected_payload = ByteBuffer::empty(); + let mut expected_payload = BytesMut::new(); packet.write(&mut expected_payload); - assert_eq!(buffer, expected_payload.buf()); + assert_eq!(buffer, expected_payload); } /// Test encoding a packet that exceeds MAX_PACKET_SIZE @@ -557,9 +555,9 @@ mod tests { assert_eq!(decoded_packet_id, CStatusResponse::PACKET_ID); // Remaining buffer is the payload - let mut expected_payload = ByteBuffer::empty(); + let mut expected_payload = BytesMut::new(); packet.write(&mut expected_payload); - assert_eq!(buffer, expected_payload.buf()); + assert_eq!(buffer, expected_payload); } } diff --git a/pumpkin-protocol/src/server/config/s_cookie_response.rs b/pumpkin-protocol/src/server/config/s_cookie_response.rs index 69440254..ec3f4da0 100644 --- a/pumpkin-protocol/src/server/config/s_cookie_response.rs +++ b/pumpkin-protocol/src/server/config/s_cookie_response.rs @@ -1,8 +1,11 @@ +use bytes::Bytes; use pumpkin_macros::server_packet; use serde::de; -use crate::bytebuf::{ByteBuffer, DeserializerError}; -use crate::{Identifier, ServerPacket, VarInt}; +use crate::{ + bytebuf::{ByteBuf, ReadingError}, + Identifier, ServerPacket, VarInt, +}; #[server_packet("config:cookie_response")] /// Response to a Cookie Request (configuration) from the server. @@ -17,9 +20,9 @@ pub struct SCookieResponse { const MAX_PAYLOAD_SIZE: i32 = 5120; impl ServerPacket for SCookieResponse { - fn read(bytebuf: &mut ByteBuffer) -> Result { - let key = bytebuf.get_string()?; - let has_payload = bytebuf.get_bool()?; + fn read(bytebuf: &mut Bytes) -> Result { + let key = bytebuf.try_get_string()?; + let has_payload = bytebuf.try_get_bool()?; if !has_payload { return Ok(Self { @@ -30,7 +33,7 @@ impl ServerPacket for SCookieResponse { }); } - let payload_length = bytebuf.get_var_int()?; + let payload_length = bytebuf.try_get_var_int()?; let length = payload_length.0; if length > MAX_PAYLOAD_SIZE { @@ -39,7 +42,7 @@ impl ServerPacket for SCookieResponse { )); } - let payload = bytebuf.copy_to_bytes(length as usize)?; + let payload = bytebuf.try_copy_to_bytes(length as usize)?; Ok(Self { key, diff --git a/pumpkin-protocol/src/server/config/s_plugin_message.rs b/pumpkin-protocol/src/server/config/s_plugin_message.rs index d5770139..25918761 100644 --- a/pumpkin-protocol/src/server/config/s_plugin_message.rs +++ b/pumpkin-protocol/src/server/config/s_plugin_message.rs @@ -1,21 +1,22 @@ +use bytes::Bytes; use pumpkin_macros::server_packet; use crate::{ - bytebuf::{ByteBuffer, DeserializerError}, + bytebuf::{ByteBuf, ReadingError}, Identifier, ServerPacket, }; #[server_packet("config:custom_payload")] pub struct SPluginMessage { pub channel: Identifier, - pub data: bytes::BytesMut, + pub data: bytes::Bytes, } impl ServerPacket for SPluginMessage { - fn read(bytebuf: &mut ByteBuffer) -> Result { + fn read(bytebuf: &mut Bytes) -> Result { Ok(Self { - channel: bytebuf.get_string()?, - data: bytebuf.get_slice(), + channel: bytebuf.try_get_string()?, + data: bytebuf.split_to(bytebuf.len()), }) } } diff --git a/pumpkin-protocol/src/server/handshake/mod.rs b/pumpkin-protocol/src/server/handshake/mod.rs index a1d74352..0e8a15ae 100644 --- a/pumpkin-protocol/src/server/handshake/mod.rs +++ b/pumpkin-protocol/src/server/handshake/mod.rs @@ -1,7 +1,8 @@ +use bytes::Bytes; use pumpkin_macros::server_packet; use crate::{ - bytebuf::{ByteBuffer, DeserializerError}, + bytebuf::{ByteBuf, ReadingError}, ConnectionState, ServerPacket, VarInt, }; @@ -14,12 +15,12 @@ pub struct SHandShake { } impl ServerPacket for SHandShake { - fn read(bytebuf: &mut ByteBuffer) -> Result { + fn read(bytebuf: &mut Bytes) -> Result { Ok(Self { - protocol_version: bytebuf.get_var_int()?, - server_address: bytebuf.get_string_len(255)?, - server_port: bytebuf.get_u16()?, - next_state: bytebuf.get_var_int()?.into(), + protocol_version: bytebuf.try_get_var_int()?, + server_address: bytebuf.try_get_string_len(255)?, + server_port: bytebuf.try_get_u16()?, + next_state: bytebuf.try_get_var_int()?.into(), }) } } diff --git a/pumpkin-protocol/src/server/login/s_cookie_response.rs b/pumpkin-protocol/src/server/login/s_cookie_response.rs index 8f70bfb5..833ae788 100644 --- a/pumpkin-protocol/src/server/login/s_cookie_response.rs +++ b/pumpkin-protocol/src/server/login/s_cookie_response.rs @@ -1,5 +1,8 @@ -use crate::bytebuf::{ByteBuffer, DeserializerError}; -use crate::{Identifier, ServerPacket, VarInt}; +use crate::{ + bytebuf::{ByteBuf, ReadingError}, + Identifier, ServerPacket, VarInt, +}; +use bytes::Bytes; use pumpkin_macros::server_packet; use serde::de; @@ -16,9 +19,9 @@ pub struct SCookieResponse { const MAX_PAYLOAD_SIZE: i32 = 5120; impl ServerPacket for SCookieResponse { - fn read(bytebuf: &mut ByteBuffer) -> Result { - let key = bytebuf.get_string()?; - let has_payload = bytebuf.get_bool()?; + fn read(bytebuf: &mut Bytes) -> Result { + let key = bytebuf.try_get_string()?; + let has_payload = bytebuf.try_get_bool()?; if !has_payload { return Ok(Self { @@ -29,7 +32,7 @@ impl ServerPacket for SCookieResponse { }); } - let payload_length = bytebuf.get_var_int()?; + let payload_length = bytebuf.try_get_var_int()?; let length = payload_length.0; if length > MAX_PAYLOAD_SIZE { @@ -38,7 +41,7 @@ impl ServerPacket for SCookieResponse { )); } - let payload = bytebuf.copy_to_bytes(length as usize)?; + let payload = bytebuf.try_copy_to_bytes(length as usize)?; Ok(Self { key, diff --git a/pumpkin-protocol/src/server/login/s_encryption_response.rs b/pumpkin-protocol/src/server/login/s_encryption_response.rs index 64be02b4..300ada9c 100644 --- a/pumpkin-protocol/src/server/login/s_encryption_response.rs +++ b/pumpkin-protocol/src/server/login/s_encryption_response.rs @@ -1,7 +1,8 @@ +use bytes::Bytes; use pumpkin_macros::server_packet; use crate::{ - bytebuf::{ByteBuffer, DeserializerError}, + bytebuf::{ByteBuf, ReadingError}, ServerPacket, VarInt, }; @@ -14,11 +15,11 @@ pub struct SEncryptionResponse { } impl ServerPacket for SEncryptionResponse { - fn read(bytebuf: &mut ByteBuffer) -> Result { - let shared_secret_length = bytebuf.get_var_int()?; - let shared_secret = bytebuf.copy_to_bytes(shared_secret_length.0 as usize)?; - let verify_token_length = bytebuf.get_var_int()?; - let verify_token = bytebuf.copy_to_bytes(shared_secret_length.0 as usize)?; + fn read(bytebuf: &mut Bytes) -> 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()?; + let verify_token = bytebuf.try_copy_to_bytes(shared_secret_length.0 as usize)?; Ok(Self { shared_secret_length, shared_secret, diff --git a/pumpkin-protocol/src/server/login/s_login_start.rs b/pumpkin-protocol/src/server/login/s_login_start.rs index 7a4a724d..44347dc7 100644 --- a/pumpkin-protocol/src/server/login/s_login_start.rs +++ b/pumpkin-protocol/src/server/login/s_login_start.rs @@ -1,7 +1,8 @@ +use bytes::Bytes; use pumpkin_macros::server_packet; use crate::{ - bytebuf::{ByteBuffer, DeserializerError}, + bytebuf::{ByteBuf, ReadingError}, ServerPacket, }; @@ -12,10 +13,10 @@ pub struct SLoginStart { } impl ServerPacket for SLoginStart { - fn read(bytebuf: &mut ByteBuffer) -> Result { + fn read(bytebuf: &mut Bytes) -> Result { Ok(Self { - name: bytebuf.get_string_len(16)?, - uuid: bytebuf.get_uuid()?, + 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 24656471..d96b796e 100644 --- a/pumpkin-protocol/src/server/login/s_plugin_response.rs +++ b/pumpkin-protocol/src/server/login/s_plugin_response.rs @@ -1,21 +1,21 @@ use crate::{ - bytebuf::{ByteBuffer, DeserializerError}, + bytebuf::{ByteBuf, ReadingError}, ServerPacket, VarInt, }; -use bytes::BytesMut; +use bytes::Bytes; use pumpkin_macros::server_packet; #[server_packet("login:custom_query_answer")] pub struct SLoginPluginResponse { pub message_id: VarInt, - pub data: Option, + pub data: Option, } impl ServerPacket for SLoginPluginResponse { - fn read(bytebuf: &mut ByteBuffer) -> Result { + fn read(bytebuf: &mut Bytes) -> Result { Ok(Self { - message_id: bytebuf.get_var_int()?, - data: bytebuf.get_option(|v| Ok(v.get_slice()))?, + message_id: bytebuf.try_get_var_int()?, + data: bytebuf.try_get_option(|v| Ok(v.split_to(v.len())))?, }) } } diff --git a/pumpkin-protocol/src/server/play/s_chat_message.rs b/pumpkin-protocol/src/server/play/s_chat_message.rs index a8e61d74..9fd65df3 100644 --- a/pumpkin-protocol/src/server/play/s_chat_message.rs +++ b/pumpkin-protocol/src/server/play/s_chat_message.rs @@ -2,7 +2,7 @@ use bytes::Bytes; use pumpkin_macros::server_packet; use crate::{ - bytebuf::{ByteBuffer, DeserializerError}, + bytebuf::{ByteBuf, ReadingError}, FixedBitSet, ServerPacket, VarInt, }; @@ -19,14 +19,14 @@ pub struct SChatMessage { // TODO impl ServerPacket for SChatMessage { - fn read(bytebuf: &mut ByteBuffer) -> Result { + fn read(bytebuf: &mut Bytes) -> Result { Ok(Self { - message: bytebuf.get_string()?, - timestamp: bytebuf.get_i64()?, - salt: bytebuf.get_i64()?, - signature: bytebuf.get_option(|v| v.copy_to_bytes(256))?, - message_count: bytebuf.get_var_int()?, - acknowledged: bytebuf.get_fixed_bitset(20)?, + message: bytebuf.try_get_string()?, + timestamp: bytebuf.try_get_i64()?, + salt: bytebuf.try_get_i64()?, + signature: bytebuf.try_get_option(|v| v.try_copy_to_bytes(256))?, + message_count: bytebuf.try_get_var_int()?, + acknowledged: bytebuf.try_get_fixed_bitset(20)?, }) } } diff --git a/pumpkin-protocol/src/server/play/s_cookie_response.rs b/pumpkin-protocol/src/server/play/s_cookie_response.rs index b7ad9b70..f3127fb4 100644 --- a/pumpkin-protocol/src/server/play/s_cookie_response.rs +++ b/pumpkin-protocol/src/server/play/s_cookie_response.rs @@ -1,5 +1,8 @@ -use crate::bytebuf::{ByteBuffer, DeserializerError}; -use crate::{Identifier, ServerPacket, VarInt}; +use crate::{ + bytebuf::{ByteBuf, ReadingError}, + Identifier, ServerPacket, VarInt, +}; +use bytes::Bytes; use pumpkin_macros::server_packet; use serde::de; @@ -16,9 +19,9 @@ pub struct SCookieResponse { const MAX_PAYLOAD_SIZE: i32 = 5120; impl ServerPacket for SCookieResponse { - fn read(bytebuf: &mut ByteBuffer) -> Result { - let key = bytebuf.get_string()?; - let has_payload = bytebuf.get_bool()?; + fn read(bytebuf: &mut Bytes) -> Result { + let key = bytebuf.try_get_string()?; + let has_payload = bytebuf.try_get_bool()?; if !has_payload { return Ok(Self { @@ -29,7 +32,7 @@ impl ServerPacket for SCookieResponse { }); } - let payload_length = bytebuf.get_var_int()?; + let payload_length = bytebuf.try_get_var_int()?; let length = payload_length.0; if length > MAX_PAYLOAD_SIZE { @@ -38,7 +41,7 @@ impl ServerPacket for SCookieResponse { )); } - let payload = bytebuf.copy_to_bytes(length as usize)?; + let payload = bytebuf.try_copy_to_bytes(length as usize)?; Ok(Self { key, diff --git a/pumpkin-protocol/src/server/play/s_interact.rs b/pumpkin-protocol/src/server/play/s_interact.rs index f283f048..c7371502 100644 --- a/pumpkin-protocol/src/server/play/s_interact.rs +++ b/pumpkin-protocol/src/server/play/s_interact.rs @@ -1,9 +1,13 @@ +use bytes::Bytes; use num_derive::FromPrimitive; use num_traits::FromPrimitive; use pumpkin_core::math::vector3::Vector3; use pumpkin_macros::server_packet; -use crate::{bytebuf::DeserializerError, ServerPacket, VarInt}; +use crate::{ + bytebuf::{ByteBuf, ReadingError}, + ServerPacket, VarInt, +}; #[server_packet("play:interact")] pub struct SInteract { @@ -16,27 +20,24 @@ pub struct SInteract { // Great job Mojang ;D impl ServerPacket for SInteract { - fn read( - bytebuf: &mut crate::bytebuf::ByteBuffer, - ) -> Result { - let entity_id = bytebuf.get_var_int()?; - let typ = bytebuf.get_var_int()?; - let action = ActionType::from_i32(typ.0).ok_or(DeserializerError::Message( - "invalid action type".to_string(), - ))?; + fn read(bytebuf: &mut Bytes) -> Result { + let entity_id = bytebuf.try_get_var_int()?; + let typ = bytebuf.try_get_var_int()?; + let action = ActionType::from_i32(typ.0) + .ok_or(ReadingError::Message("invalid action type".to_string()))?; let target_position: Option> = match action { ActionType::Interact => None, ActionType::Attack => None, ActionType::InteractAt => Some(Vector3::new( - bytebuf.get_f32()?, - bytebuf.get_f32()?, - bytebuf.get_f32()?, + bytebuf.try_get_f32()?, + bytebuf.try_get_f32()?, + bytebuf.try_get_f32()?, )), }; let hand = match action { - ActionType::Interact => Some(bytebuf.get_var_int()?), + ActionType::Interact => Some(bytebuf.try_get_var_int()?), ActionType::Attack => None, - ActionType::InteractAt => Some(bytebuf.get_var_int()?), + ActionType::InteractAt => Some(bytebuf.try_get_var_int()?), }; Ok(Self { @@ -44,7 +45,7 @@ impl ServerPacket for SInteract { typ, target_position, hand, - sneaking: bytebuf.get_bool()?, + sneaking: bytebuf.try_get_bool()?, }) } } diff --git a/pumpkin-protocol/src/server/play/s_player_command.rs b/pumpkin-protocol/src/server/play/s_player_command.rs index aeafcf15..8b2c7055 100644 --- a/pumpkin-protocol/src/server/play/s_player_command.rs +++ b/pumpkin-protocol/src/server/play/s_player_command.rs @@ -1,7 +1,11 @@ +use bytes::Bytes; use num_derive::FromPrimitive; use pumpkin_macros::server_packet; -use crate::{bytebuf::DeserializerError, ServerPacket, VarInt}; +use crate::{ + bytebuf::{ByteBuf, ReadingError}, + ServerPacket, VarInt, +}; #[server_packet("play:player_command")] pub struct SPlayerCommand { @@ -23,11 +27,11 @@ pub enum Action { } impl ServerPacket for SPlayerCommand { - fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Result { + fn read(bytebuf: &mut Bytes) -> Result { Ok(Self { - entity_id: bytebuf.get_var_int()?, - action: bytebuf.get_var_int()?, - jump_boost: bytebuf.get_var_int()?, + entity_id: bytebuf.try_get_var_int()?, + action: bytebuf.try_get_var_int()?, + jump_boost: bytebuf.try_get_var_int()?, }) } } diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 6c260447..9fd6fed0 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -17,7 +17,7 @@ use crossbeam::atomic::AtomicCell; use pumpkin_config::compression::CompressionInfo; use pumpkin_core::text::TextComponent; use pumpkin_protocol::{ - bytebuf::{packet_id::Packet, DeserializerError}, + bytebuf::{packet_id::Packet, ReadingError}, client::{config::CConfigDisconnect, login::CLoginDisconnect, play::CPlayDisconnect}, packet_decoder::PacketDecoder, packet_encoder::{PacketEncodeError, PacketEncoder}, @@ -343,7 +343,7 @@ impl Client { &self, server: &Arc, packet: &mut RawPacket, - ) -> Result<(), DeserializerError> { + ) -> Result<(), ReadingError> { match self.connection_state.load() { pumpkin_protocol::ConnectionState::HandShake => { self.handle_handshake_packet(packet).await @@ -366,10 +366,7 @@ impl Client { } } - async fn handle_handshake_packet( - &self, - packet: &mut RawPacket, - ) -> Result<(), DeserializerError> { + async fn handle_handshake_packet(&self, packet: &mut RawPacket) -> Result<(), ReadingError> { log::debug!("Handling handshake group"); let bytebuf = &mut packet.bytebuf; match packet.id.0 { @@ -390,7 +387,7 @@ impl Client { &self, server: &Arc, packet: &mut RawPacket, - ) -> Result<(), DeserializerError> { + ) -> Result<(), ReadingError> { log::debug!("Handling status group"); let bytebuf = &mut packet.bytebuf; match packet.id.0 { @@ -416,7 +413,7 @@ impl Client { &self, server: &Arc, packet: &mut RawPacket, - ) -> Result<(), DeserializerError> { + ) -> Result<(), ReadingError> { log::debug!("Handling login group for id"); let bytebuf = &mut packet.bytebuf; match packet.id.0 { @@ -452,7 +449,7 @@ impl Client { &self, server: &Arc, packet: &mut RawPacket, - ) -> Result<(), DeserializerError> { + ) -> Result<(), ReadingError> { log::debug!("Handling config group"); let bytebuf = &mut packet.bytebuf; match packet.id.0 { diff --git a/pumpkin/src/error.rs b/pumpkin/src/error.rs index d83e9a3e..10cd79ca 100644 --- a/pumpkin/src/error.rs +++ b/pumpkin/src/error.rs @@ -1,6 +1,6 @@ use log::log; use pumpkin_inventory::InventoryError; -use pumpkin_protocol::bytebuf::DeserializerError; +use pumpkin_protocol::bytebuf::ReadingError; use std::fmt::Display; pub trait PumpkinError: Send + std::error::Error + Display { @@ -52,7 +52,7 @@ impl PumpkinError for InventoryError { } } -impl PumpkinError for DeserializerError { +impl PumpkinError for ReadingError { fn is_kick(&self) -> bool { true } diff --git a/pumpkin/src/proxy/velocity.rs b/pumpkin/src/proxy/velocity.rs index 95f9aa07..68f7d4e9 100644 --- a/pumpkin/src/proxy/velocity.rs +++ b/pumpkin/src/proxy/velocity.rs @@ -7,7 +7,7 @@ use bytes::{BufMut, BytesMut}; use hmac::{Hmac, Mac}; use pumpkin_config::proxy::VelocityConfig; use pumpkin_protocol::{ - bytebuf::ByteBuffer, client::login::CLoginPluginRequest, server::login::SLoginPluginResponse, + bytebuf::ByteBuf, client::login::CLoginPluginRequest, server::login::SLoginPluginResponse, Property, }; use rand::Rng; @@ -68,19 +68,19 @@ pub fn check_integrity(data: (&[u8], &[u8]), secret: &str) -> bool { mac.verify_slice(signature).is_ok() } -fn read_game_profile(buf: &mut ByteBuffer) -> Result { +fn read_game_profile(buf: &mut BytesMut) -> Result { let id = buf - .get_uuid() + .try_get_uuid() .map_err(|_| VelocityError::FailedReadProfileUUID)?; let name = buf - .get_string() + .try_get_string() .map_err(|_| VelocityError::FailedReadProfileName)?; let properties = buf .get_list(|data| { - let name = data.get_string()?; - let value = data.get_string()?; - let signature = data.get_option(pumpkin_protocol::bytebuf::ByteBuffer::get_string)?; + let name = data.try_get_string()?; + let value = data.try_get_string()?; + let signature = data.try_get_option(ByteBuf::try_get_string)?; Ok(Property { name, @@ -109,12 +109,12 @@ pub fn receive_velocity_plugin_response( if !check_integrity((signature, data_without_signature), &config.secret) { return Err(VelocityError::FailedVerifyIntegrity); } - let mut buf = ByteBuffer::new(BytesMut::new()); + let mut buf = BytesMut::new(); buf.put_slice(data_without_signature); // check velocity version let version = buf - .get_var_int() + .try_get_var_int() .map_err(|_| VelocityError::FailedReadForwardVersion)?; let version = version.0 as u8; if version > MAX_SUPPORTED_FORWARDING_VERSION { @@ -124,7 +124,7 @@ pub fn receive_velocity_plugin_response( )); } let addr = buf - .get_string() + .try_get_string() .map_err(|_| VelocityError::FailedReadAddress)?; let socket_addr: SocketAddr = SocketAddr::new( diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 0f9b5bb9..35d7383e 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -575,10 +575,9 @@ impl World { let packet = CChunkData(&chunk_data); #[cfg(debug_assertions)] if chunk_data.position == (0, 0).into() { - use pumpkin_protocol::bytebuf::ByteBuffer; - let mut test = ByteBuffer::empty(); + let mut test = bytes::BytesMut::new(); packet.write(&mut test); - let len = test.buf().len(); + let len = test.len(); log::debug!( "Chunk packet size: {}B {}KB {}MB", len, From 9a2e68a053fc98073d2804a7dc5b92b78636ec7b Mon Sep 17 00:00:00 2001 From: Lucas11 <63660708+lucas11222@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:30:23 +0100 Subject: [PATCH 13/42] add Github link in /pumpkin command (#372) * Add the text and the open link * Update cmd_pumpkin.rs * Fixed all the warnings and clippy and fmt * fixed more * trying to fix confics * Fixed a bug * another one * Update .gitignore * Update cmd_pumpkin.rs * Added docs * Formated * Update cmd_pumpkin.rs * im gonna test * Change color bcs aqua is ugly * Update cmd_pumpkin.rs * Update cmd_pumpkin.rs * Update cmd_pumpkin.rs --- .gitignore | 1 - pumpkin/src/command/commands/cmd_pumpkin.rs | 26 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9016d354..4b08876e 100644 --- a/.gitignore +++ b/.gitignore @@ -74,7 +74,6 @@ gradle-app.setting *.zip *.tar.gz *.rar - # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* diff --git a/pumpkin/src/command/commands/cmd_pumpkin.rs b/pumpkin/src/command/commands/cmd_pumpkin.rs index 2b2f41a2..3dad98ec 100644 --- a/pumpkin/src/command/commands/cmd_pumpkin.rs +++ b/pumpkin/src/command/commands/cmd_pumpkin.rs @@ -59,6 +59,32 @@ impl CommandExecutor for PumpkinExecutor { "Click to Copy Minecraft Version", ))) .color_named(NamedColor::Gold), + ) + .add_child(TextComponent::text(" ")) + // https://snowiiii.github.io/Pumpkin/ + .add_child( + TextComponent::text("Github Repository") + .click_event(ClickEvent::OpenUrl(Cow::from( + "https://github.com/Snowiiii/Pumpkin", + ))) + .hover_event(HoverEvent::ShowText(Cow::from( + "Click to open repository.", + ))) + .color_named(NamedColor::Blue) + .bold() + .underlined(), + ) + // Added docs. and a space for spacing + .add_child(TextComponent::text(" ")) + .add_child( + TextComponent::text("Docs") + .click_event(ClickEvent::OpenUrl(Cow::from( + "https://snowiiii.github.io/Pumpkin/", + ))) + .hover_event(HoverEvent::ShowText(Cow::from("Click to open docs."))) + .color_named(NamedColor::Blue) + .bold() + .underlined(), ), ) .await; From f323e78ef540fda123e3cec1177f935e56f08d9b Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Tue, 17 Dec 2024 00:06:49 +0100 Subject: [PATCH 14/42] Fix: https://github.com/Snowiiii/Pumpkin/issues/270 --- extractor/gradle.properties | 6 +- pumpkin-inventory/Cargo.toml | 3 +- pumpkin-inventory/src/container_click.rs | 20 ++-- pumpkin-protocol/src/packet_decoder.rs | 5 - pumpkin-protocol/src/packet_encoder.rs | 93 ++++++++++++++----- .../src/server/play/s_click_container.rs | 29 +++++- pumpkin-protocol/src/var_int.rs | 4 +- pumpkin/src/client/container.rs | 10 +- 8 files changed, 114 insertions(+), 56 deletions(-) diff --git a/extractor/gradle.properties b/extractor/gradle.properties index f028ef93..0a546d28 100644 --- a/extractor/gradle.properties +++ b/extractor/gradle.properties @@ -4,13 +4,11 @@ org.gradle.parallel=true # Fabric Properties # check these on https://modmuss50.me/fabric.html minecraft_version=1.21.4 -yarn_mappings=1.21.4+build.1 +yarn_mappings=1.21.4+build.2 loader_version=0.16.9 kotlin_loader_version=1.12.3+kotlin.2.0.21 # Mod Properties mod_version=1.0-SNAPSHOT maven_group=de.snowii archives_base_name=extractor -# Dependencies -# check this on https://modmuss50.me/fabric.html -fabric_version=0.110.5+1.21.4 +fabric_version=0.112.1+1.21.4 diff --git a/pumpkin-inventory/Cargo.toml b/pumpkin-inventory/Cargo.toml index 34fb022f..79ff82f6 100644 --- a/pumpkin-inventory/Cargo.toml +++ b/pumpkin-inventory/Cargo.toml @@ -4,7 +4,8 @@ version.workspace = true edition.workspace = true [dependencies] -# For items +pumpkin-protocol = { path = "../pumpkin-protocol" } + pumpkin-world = { path = "../pumpkin-world" } pumpkin-registry = {path = "../pumpkin-registry"} pumpkin-macros = { path = "../pumpkin-macros" } diff --git a/pumpkin-inventory/src/container_click.rs b/pumpkin-inventory/src/container_click.rs index 5d1039a9..d93ba3d3 100644 --- a/pumpkin-inventory/src/container_click.rs +++ b/pumpkin-inventory/src/container_click.rs @@ -1,4 +1,5 @@ use crate::InventoryError; +use pumpkin_protocol::server::play::SlotActionType; use pumpkin_world::item::ItemStack; pub struct Click { @@ -7,23 +8,22 @@ pub struct Click { } impl Click { - pub fn new(mode: u8, button: i8, slot: i16) -> Result { + pub fn new(mode: SlotActionType, button: i8, slot: i16) -> Result { match mode { - 0 => Self::new_normal_click(button, slot), + SlotActionType::Pickup => Self::new_normal_click(button, slot), // Both buttons do the same here, so we omit it - 1 => Self::new_shift_click(slot), - 2 => Self::new_key_click(button, slot), - 3 => Ok(Self { + SlotActionType::QuickMove => Self::new_shift_click(slot), + SlotActionType::Swap => Self::new_key_click(button, slot), + SlotActionType::Clone => Ok(Self { click_type: ClickType::CreativePickItem, slot: Slot::Normal(slot.try_into().or(Err(InventoryError::InvalidSlot))?), }), - 4 => Self::new_drop_item(button), - 5 => Self::new_drag_item(button, slot), - 6 => Ok(Self { + SlotActionType::Throw => Self::new_drop_item(button), + SlotActionType::QuickCraft => Self::new_drag_item(button, slot), + SlotActionType::PickupAll => Ok(Self { click_type: ClickType::DoubleClick, slot: Slot::Normal(slot.try_into().or(Err(InventoryError::InvalidSlot))?), }), - _ => Err(InventoryError::InvalidPacket), } } @@ -31,7 +31,7 @@ impl Click { let slot = match slot { -999 => Slot::OutsideInventory, _ => { - let slot = slot.try_into().or(Err(InventoryError::InvalidSlot))?; + let slot = slot.try_into().unwrap_or(0); Slot::Normal(slot) } }; diff --git a/pumpkin-protocol/src/packet_decoder.rs b/pumpkin-protocol/src/packet_decoder.rs index f40db1bb..badd34b7 100644 --- a/pumpkin-protocol/src/packet_decoder.rs +++ b/pumpkin-protocol/src/packet_decoder.rs @@ -120,17 +120,12 @@ impl PacketDecoder { pub fn set_encryption(&mut self, key: Option<&[u8; 16]>) { if let Some(key) = key { assert!(self.cipher.is_none(), "encryption is already enabled"); - let mut cipher = Cipher::new_from_slices(key, key).expect("invalid key"); - // Don't forget to decrypt the data we already have. - Self::decrypt_bytes(&mut cipher, &mut self.buf); - self.cipher = Some(cipher); } else { assert!(self.cipher.is_some(), "encryption is already disabled"); - self.cipher = None; } } diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index 3ce1f3b4..4b0bb198 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -9,13 +9,13 @@ use crate::{ClientPacket, VarInt, MAX_PACKET_SIZE}; type Cipher = cfb8::Encryptor; -// Encoder: Server -> Client -// Supports ZLib endecoding/compression -// Supports Aes128 Encryption +/// Encoder: Server -> Client +/// Supports ZLib endecoding/compression +/// Supports Aes128 Encryption pub struct PacketEncoder { buf: BytesMut, compress_buf: Vec, - compression: Option, + compression_threshold: Option, cipher: Option, compressor: Compressor, // Reuse the compressor for all packets } @@ -27,25 +27,58 @@ impl Default for PacketEncoder { Self { buf: BytesMut::with_capacity(1024), compress_buf: Vec::with_capacity(1024), - compression: None, + compression_threshold: None, cipher: None, - compressor: Compressor::new(CompressionLvl::fastest()), // init compressor with no compression level + compressor: Compressor::new(CompressionLvl::fastest()), // init compressor with fastest compression level } } } impl PacketEncoder { + /// Appends a Clientbound `ClientPacket` to the internal buffer and applies compression when needed. + /// + /// If compression is enabled and the packet size exceeds the threshold, the packet is compressed. + /// The packet is prefixed with its length and, if compressed, the uncompressed data length. + /// The packet format is as follows: + /// + /// **Uncompressed:** + /// ``` + /// +-----------------------+ + /// | Packet Length (VarInt)| + /// +-----------------------+ + /// | Packet ID (VarInt) | + /// +-----------------------+ + /// | Data (Byte Array) | + /// +-----------------------+ + /// ``` + /// + /// **Compressed:** + /// ``` + /// +-----------------------+ + /// | Packet Length (VarInt) | + /// +-----------------------+ + /// | Data Length (VarInt) | + /// +-----------------------+ + /// | Packet ID (VarInt) | + /// +-----------------------+ + /// | Data (Byte Array) | + /// +-----------------------+ + /// + /// * `Packet Length`: The total length of the packet *excluding* the `Packet Length` field itself. + /// * `Data Length`: (Only present in compressed packets) The length of the uncompressed `Packet ID` and `Data`. + /// * `Packet ID`: The ID of the packet. + /// * `Data`: The packet's data. + /// ``` pub fn append_packet(&mut self, packet: &P) -> Result<(), PacketEncodeError> { let start_len = self.buf.len(); // Write the Packet ID first VarInt(P::PACKET_ID).encode(&mut self.buf); // Now write the packet into an empty buffer packet.write(&mut self.buf); - let data_len = self.buf.len() - start_len; - if let Some(compression) = &self.compression { - if data_len > compression.threshold as usize { + if let Some(compression_threshold) = self.compression_threshold { + if data_len > compression_threshold as usize { // Get the data to compress let data_to_compress = &self.buf[start_len..]; @@ -73,7 +106,7 @@ impl PacketEncoder { let packet_len = data_len_size + compressed_size; if packet_len >= MAX_PACKET_SIZE as usize { - return Err(PacketEncodeError::TooLong); + return Err(PacketEncodeError::TooLong(packet_len)); } self.buf.truncate(start_len); @@ -86,7 +119,7 @@ impl PacketEncoder { let packet_len = data_len_size + data_len; if packet_len >= MAX_PACKET_SIZE as usize { - Err(PacketEncodeError::TooLong)? + Err(PacketEncodeError::TooLong(packet_len))? } let packet_len_size = VarInt(packet_len as i32).written_size(); @@ -110,7 +143,7 @@ impl PacketEncoder { let packet_len = data_len; if packet_len >= MAX_PACKET_SIZE as usize { - Err(PacketEncodeError::TooLong)? + Err(PacketEncodeError::TooLong(packet_len))? } let packet_len_size = VarInt(packet_len as i32).written_size(); @@ -124,6 +157,7 @@ impl PacketEncoder { Ok(()) } + /// Enable encryption for taking all packets buffer ` pub fn set_encryption(&mut self, key: Option<&[u8; 16]>) { if let Some(key) = key { assert!(self.cipher.is_none(), "encryption is already enabled"); @@ -136,23 +170,37 @@ impl PacketEncoder { } } - /// Enables ZLib Compression + /// Enables or disables Zlib compression with the given options. + /// + /// If `compression` is `Some`, compression is enabled with the provided + /// options. If `compression` is `None`, compression is disabled. pub fn set_compression(&mut self, compression: Option) { - self.compression = compression; - // Reset the compressor with the new compression level - if let Some(compression) = &self.compression { + if let Some(compression) = &compression { + self.compression_threshold = Some(compression.threshold); let compression_level = compression.level as i32; - let level = match CompressionLvl::new(compression_level) { Ok(level) => level, - Err(_) => return, + Err(error) => { + log::error!("Invalid compression level {:?}", error); + return; + } }; - self.compressor = Compressor::new(level); + } else { + self.compression_threshold = None; } } + /// Encrypts the data in the internal buffer and returns it as a `BytesMut`. + /// + /// If a cipher is set, the data is encrypted in-place using block cipher encryption. + /// The buffer is processed in chunks of the cipher's block size. If the buffer's + /// length is not a multiple of the block size, the last partial block is *not* encrypted. + /// It's important to ensure that the data being encrypted is padded appropriately + /// beforehand if necessary. + /// + /// If no cipher is set, the buffer is returned as is. pub fn take(&mut self) -> BytesMut { if let Some(cipher) = &mut self.cipher { for chunk in self.buf.chunks_mut(Cipher::block_size()) { @@ -165,11 +213,12 @@ impl PacketEncoder { } } +/// Errors that can occur during packet encoding. #[derive(Error, Debug)] pub enum PacketEncodeError { - #[error("packet exceeds maximum length")] - TooLong, - #[error("compression failed {0}")] + #[error("Packet exceeds maximum length: {0}")] + TooLong(usize), + #[error("Compression failed {0}")] CompressionFailed(String), } diff --git a/pumpkin-protocol/src/server/play/s_click_container.rs b/pumpkin-protocol/src/server/play/s_click_container.rs index 8de3f8a7..b81e366c 100644 --- a/pumpkin-protocol/src/server/play/s_click_container.rs +++ b/pumpkin-protocol/src/server/play/s_click_container.rs @@ -1,17 +1,18 @@ use crate::slot::Slot; use crate::VarInt; +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; use pumpkin_macros::server_packet; use serde::de::SeqAccess; use serde::{de, Deserialize}; -#[derive(Debug)] #[server_packet("play:container_click")] pub struct SClickContainer { pub window_id: VarInt, pub state_id: VarInt, pub slot: i16, pub button: i8, - pub mode: VarInt, + pub mode: SlotActionType, pub length_of_array: VarInt, pub array_of_changed_slots: Vec<(i16, Slot)>, pub carried_item: Slot, @@ -73,7 +74,8 @@ impl<'de> Deserialize<'de> for SClickContainer { state_id, slot, button, - mode, + mode: SlotActionType::from_i32(mode.0) + .expect("Invalid Slot action, TODO better error handling ;D"), length_of_array, array_of_changed_slots, carried_item, @@ -84,3 +86,24 @@ impl<'de> Deserialize<'de> for SClickContainer { deserializer.deserialize_seq(Visitor) } } + +#[derive(Deserialize, FromPrimitive)] +pub enum SlotActionType { + /// Performs a normal slot click. This can pickup or place items in the slot, possibly merging the cursor stack into the slot, or swapping the slot stack with the cursor stack if they can't be merged. + Pickup, + /// Performs a shift-click. This usually quickly moves items between the player's inventory and the open screen handler. + QuickMove, + /// Exchanges items between a slot and a hotbar slot. This is usually triggered by the player pressing a 1-9 number key while hovering over a slot. + /// When the action type is swap, the click data is the hotbar slot to swap with (0-8). + Swap, + /// Clones the item in the slot. Usually triggered by middle clicking an item in creative mode. + Clone, + /// Throws the item out of the inventory. This is usually triggered by the player pressing Q while hovering over a slot, or clicking outside the window. + /// When the action type is throw, the click data determines whether to throw a whole stack (1) or a single item from that stack (0). + Throw, + /// Drags items between multiple slots. This is usually triggered by the player clicking and dragging between slots. + /// This action happens in 3 stages. Stage 0 signals that the drag has begun, and stage 2 signals that the drag has ended. In between multiple stage 1s signal which slots were dragged on. + QuickCraft, + /// Replenishes the cursor stack with items from the screen handler. This is usually triggered by the player double clicking + PickupAll, +} diff --git a/pumpkin-protocol/src/var_int.rs b/pumpkin-protocol/src/var_int.rs index 90c3dde7..608287fd 100644 --- a/pumpkin-protocol/src/var_int.rs +++ b/pumpkin-protocol/src/var_int.rs @@ -14,7 +14,7 @@ pub type VarIntType = i32; pub struct VarInt(pub VarIntType); impl VarInt { - /// The maximum number of bytes a `VarInt` + /// The maximum number of bytes a `VarInt` can occupy. pub const MAX_SIZE: usize = 5; /// Returns the exact number of bytes this varint will write when @@ -86,7 +86,7 @@ impl From for i32 { #[derive(Copy, Clone, PartialEq, Eq, Debug, Error)] pub enum VarIntDecodeError { - #[error("incomplete VarInt decode")] + #[error("Incomplete VarInt decode")] Incomplete, #[error("VarInt is too large")] TooLarge, diff --git a/pumpkin/src/client/container.rs b/pumpkin/src/client/container.rs index 11a5c854..f171c116 100644 --- a/pumpkin/src/client/container.rs +++ b/pumpkin/src/client/container.rs @@ -134,15 +134,7 @@ impl Player { return Err(InventoryError::ClosedContainerInteract(self.entity_id())); } - let click = Click::new( - packet - .mode - .0 - .try_into() - .expect("Mode can only be between 0-6"), - packet.button, - packet.slot, - )?; + let click = Click::new(packet.mode, packet.button, packet.slot)?; let (crafted_item, crafted_item_slot) = { let mut inventory = self.inventory().lock().await; let combined = From c285915d004b5be10251ee4e6ef19c99252a1d41 Mon Sep 17 00:00:00 2001 From: kralverde <80051564+kralverde@users.noreply.github.com> Date: Mon, 16 Dec 2024 18:12:59 -0500 Subject: [PATCH 15/42] properly check respawn and config updates (#398) --- pumpkin/src/client/client_packet.rs | 6 +++ pumpkin/src/client/player_packet.rs | 74 ++++++++++++++++++++++------- pumpkin/src/entity/player.rs | 11 +++-- pumpkin/src/world/mod.rs | 5 +- pumpkin/src/world/player_chunker.rs | 47 ++++-------------- 5 files changed, 83 insertions(+), 60 deletions(-) diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index ac21d99f..a6e41944 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -388,6 +388,12 @@ impl Client { 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()), diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index cc54520f..b5dfd447 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -435,28 +435,68 @@ impl Player { ) */ } - pub async fn handle_client_information(&self, client_information: SClientInformationPlay) { + pub async fn handle_client_information( + self: &Arc, + client_information: SClientInformationPlay, + ) { if let (Some(main_hand), Some(chat_mode)) = ( Hand::from_i32(client_information.main_hand.into()), ChatMode::from_i32(client_information.chat_mode.into()), ) { - let mut config = self.config.lock().await; - let update = - config.main_hand != main_hand || config.skin_parts != client_information.skin_parts; - - *config = PlayerConfig { - locale: client_information.locale, - // A Negative view distance would be impossible and make no sense right ?, Mojang: Lets make is signed :D - view_distance: 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, + if client_information.view_distance <= 0 { + self.kick(TextComponent::text( + "Cannot have zero or negative view distance!", + )) + .await; + return; + } + + let (update_skin, update_watched) = { + let mut config = self.config.lock().await; + let update_skin = config.main_hand != main_hand + || config.skin_parts != client_information.skin_parts; + + let old_view_distance = config.view_distance; + + let update_watched = if old_view_distance == client_information.view_distance as u8 + { + false + } else { + log::debug!( + "Player {} ({}) updated render distance: {} -> {}.", + self.gameprofile.name, + self.client.id, + old_view_distance, + client_information.view_distance + ); + + true + }; + + *config = PlayerConfig { + locale: client_information.locale, + // A Negative view distance would be impossible and make no sense right ?, Mojang: Lets make is signed :D + view_distance: 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, + }; + (update_skin, update_watched) }; - drop(config); - if update { + + if update_watched { + player_chunker::update_position(self).await; + } + + if update_skin { + log::debug!( + "Player {} ({}) updated their skin.", + self.gameprofile.name, + self.client.id, + ); self.update_client_information().await; } } else { diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 5e14a0c1..cc8af323 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -130,7 +130,7 @@ impl Player { ) -> Self { let gameprofile = client.gameprofile.lock().await.clone().map_or_else( || { - log::error!("No gameprofile?. Impossible"); + log::error!("Client {} has no game profile!", client.id); GameProfile { id: uuid::Uuid::new_v4(), name: String::new(), @@ -141,7 +141,6 @@ impl Player { |profile| profile, ); let config = client.config.lock().await.clone().unwrap_or_default(); - let view_distance = config.view_distance; let bounding_box_size = BoundingBoxSize { width: 0.6, height: 1.8, @@ -172,7 +171,13 @@ impl Player { teleport_id_count: AtomicI32::new(0), abilities: Mutex::new(Abilities::default()), gamemode: AtomicCell::new(gamemode), - watched_section: AtomicCell::new(Cylindrical::new(Vector2::new(0, 0), view_distance)), + // We want this to be an impossible watched section so that `player_chunker::update_position` + // will mark chunks as watched for a new join rather than a respawn + // (We left shift by one so we can search around that chunk) + watched_section: AtomicCell::new(Cylindrical::new( + Vector2::new(i32::MAX >> 1, i32::MAX >> 1), + 0, + )), wait_for_keep_alive: AtomicBool::new(false), keep_alive_id: AtomicI64::new(0), last_keep_alive_time: AtomicCell::new(std::time::Instant::now()), diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 35d7383e..54c5d105 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -410,7 +410,7 @@ impl World { player.send_time(self).await; // Spawn in initial chunks - player_chunker::player_join(self, player.clone()).await; + player_chunker::player_join(&player).await; // if let Some(bossbars) = self..lock().await.get_player_bars(&player.gameprofile.id) { // for bossbar in bossbars { @@ -512,7 +512,7 @@ impl World { ) .await; - player_chunker::player_join(self, player.clone()).await; + player_chunker::player_join(player).await; self.broadcast_packet_all(&entity_metadata_packet).await; // update commands @@ -565,7 +565,6 @@ impl World { rel_x * rel_x + rel_z * rel_z }); - player.world().mark_chunks_as_watched(&chunks); let mut receiver = self.receive_chunks(chunks); let level = self.level.clone(); diff --git a/pumpkin/src/world/player_chunker.rs b/pumpkin/src/world/player_chunker.rs index 73f5c324..a7c6f8a5 100644 --- a/pumpkin/src/world/player_chunker.rs +++ b/pumpkin/src/world/player_chunker.rs @@ -10,8 +10,6 @@ use pumpkin_world::cylindrical_chunk_iterator::Cylindrical; use crate::entity::player::Player; -use super::World; - pub async fn get_view_distance(player: &Player) -> u8 { player .config @@ -21,18 +19,9 @@ pub async fn get_view_distance(player: &Player) -> u8 { .clamp(2, BASIC_CONFIG.view_distance) } -pub async fn player_join(world: &World, player: Arc) { - let new_watched = chunk_section_from_pos(&player.living_entity.entity.block_pos.load()); - - let mut cylindrical = player.watched_section.load(); - cylindrical.center = new_watched.into(); - player.watched_section.store(cylindrical); - +pub async fn player_join(player: &Arc) { let chunk_pos = player.living_entity.entity.chunk_pos.load(); - assert_eq!(new_watched.x, chunk_pos.x); - assert_eq!(new_watched.z, chunk_pos.z); - log::debug!("Sending center chunk to {}", player.gameprofile.name); player .client @@ -41,20 +30,15 @@ pub async fn player_join(world: &World, player: Arc) { chunk_z: chunk_pos.z.into(), }) .await; - let view_distance = get_view_distance(&player).await; + let view_distance = get_view_distance(player).await; log::debug!( "Player {} ({}) joined with view distance: {}", player.gameprofile.name, - player.gameprofile.name, + player.client.id, view_distance ); - let new_cylindrical = Cylindrical::new(chunk_pos, view_distance); - let loading_chunks = new_cylindrical.all_chunks_within(); - - if !loading_chunks.is_empty() { - world.spawn_world_chunks(player, loading_chunks, chunk_pos); - } + update_position(player).await; } pub async fn update_position(player: &Arc) { @@ -74,8 +58,6 @@ pub async fn update_position(player: &Arc) { let new_cylindrical = Cylindrical::new(new_chunk_center, view_distance); if old_cylindrical != new_cylindrical { - player.watched_section.store(new_cylindrical); - player .client .send_packet(&CCenterChunk { @@ -97,14 +79,15 @@ pub async fn update_position(player: &Arc) { }, ); - if !unloading_chunks.is_empty() { - //let inst = std::time::Instant::now(); + // 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); + player.watched_section.store(new_cylindrical); - //log::debug!("Unloading chunks took {:?} (1)", inst.elapsed()); - let chunks_to_clean = entity.world.mark_chunks_as_not_watched(&unloading_chunks); + if !chunks_to_clean.is_empty() { entity.world.clean_chunks(&chunks_to_clean); - //log::debug!("Unloading chunks took {:?} (2)", inst.elapsed()); // This can take a little if we are sending a bunch of packets, queue it up :p let client = player.client.clone(); tokio::spawn(async move { @@ -118,22 +101,12 @@ pub async fn update_position(player: &Arc) { .await; } }); - //log::debug!("Unloading chunks took {:?} (3)", inst.elapsed()); } if !loading_chunks.is_empty() { - //let inst = std::time::Instant::now(); - - // loading_chunks.sort_by(|a, b| { - // let distance_a_squared = a.sub(a).length_squared(); - // let distance_b_squared = b.sub(a).length_squared(); - // distance_a_squared.cmp(&distance_b_squared) - // }); - entity .world .spawn_world_chunks(player.clone(), loading_chunks, new_chunk_center); - //log::debug!("Loading chunks took {:?}", inst.elapsed()); } } } From f7fa7bfa467e5c0b09b7bbd526297b4a2a7ca088 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Tue, 17 Dec 2024 00:27:21 +0100 Subject: [PATCH 16/42] fix: clippy test failing -_- --- pumpkin-protocol/src/packet_encoder.rs | 30 +++++++++++--------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index 4b0bb198..a329ee08 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -42,33 +42,29 @@ impl PacketEncoder { /// The packet format is as follows: /// /// **Uncompressed:** - /// ``` - /// +-----------------------+ + /// |-----------------------| /// | Packet Length (VarInt)| - /// +-----------------------+ + /// |-----------------------| /// | Packet ID (VarInt) | - /// +-----------------------+ + /// |-----------------------| /// | Data (Byte Array) | - /// +-----------------------+ - /// ``` + /// |-----------------------| /// /// **Compressed:** - /// ``` - /// +-----------------------+ + /// |------------------------| /// | Packet Length (VarInt) | - /// +-----------------------+ + /// |------------------------| /// | Data Length (VarInt) | - /// +-----------------------+ + /// |------------------------| /// | Packet ID (VarInt) | - /// +-----------------------+ + /// |------------------------| /// | Data (Byte Array) | - /// +-----------------------+ + /// |------------------------| /// - /// * `Packet Length`: The total length of the packet *excluding* the `Packet Length` field itself. - /// * `Data Length`: (Only present in compressed packets) The length of the uncompressed `Packet ID` and `Data`. - /// * `Packet ID`: The ID of the packet. - /// * `Data`: The packet's data. - /// ``` + /// - `Packet Length`: The total length of the packet *excluding* the `Packet Length` field itself. + /// - `Data Length`: (Only present in compressed packets) The length of the uncompressed `Packet ID` and `Data`. + /// - `Packet ID`: The ID of the packet. + /// - `Data`: The packet's data. pub fn append_packet(&mut self, packet: &P) -> Result<(), PacketEncodeError> { let start_len = self.buf.len(); // Write the Packet ID first From 133ef45c1901a5da00e0473b05fd05509df1e479 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Tue, 17 Dec 2024 00:56:13 +0100 Subject: [PATCH 17/42] extractor: update to Kotlin 2.1 --- extractor/build.gradle.kts | 4 ++-- extractor/gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extractor/build.gradle.kts b/extractor/build.gradle.kts index 0fccb5cc..944a30d3 100644 --- a/extractor/build.gradle.kts +++ b/extractor/build.gradle.kts @@ -2,8 +2,8 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "2.0.21" - id("fabric-loom") version "1.8.9" + kotlin("jvm") version "2.1.0" + id("fabric-loom") version "1.9-SNAPSHOT" id("maven-publish") } diff --git a/extractor/gradle.properties b/extractor/gradle.properties index 0a546d28..5b4def5d 100644 --- a/extractor/gradle.properties +++ b/extractor/gradle.properties @@ -6,7 +6,7 @@ org.gradle.parallel=true minecraft_version=1.21.4 yarn_mappings=1.21.4+build.2 loader_version=0.16.9 -kotlin_loader_version=1.12.3+kotlin.2.0.21 +kotlin_loader_version=1.13.0+kotlin.2.1.0 # Mod Properties mod_version=1.0-SNAPSHOT maven_group=de.snowii From cc0f30848ea597ef702558cb1e5fc57ed2a20372 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Thu, 19 Dec 2024 01:25:19 +0100 Subject: [PATCH 18/42] More clippy lints Also made some vars NonZero --- .github/workflows/rust.yml | 4 +-- pumpkin-config/src/lib.rs | 18 ++++++---- pumpkin-protocol/src/lib.rs | 4 ++- pumpkin-protocol/src/var_int.rs | 10 +++--- pumpkin-protocol/src/var_long.rs | 10 +++--- .../src/cylindrical_chunk_iterator.rs | 20 ++++++----- pumpkin/src/block/block_manager.rs | 3 +- pumpkin/src/client/client_packet.rs | 16 +++++---- pumpkin/src/client/mod.rs | 5 +-- pumpkin/src/client/player_packet.rs | 35 ++++++++++--------- pumpkin/src/command/args/arg_block.rs | 14 ++++---- pumpkin/src/command/args/arg_bounded_num.rs | 4 +-- pumpkin/src/command/args/arg_command.rs | 9 +++-- pumpkin/src/command/args/arg_entities.rs | 2 +- pumpkin/src/command/args/arg_entity.rs | 2 +- pumpkin/src/command/args/arg_gamemode.rs | 2 +- pumpkin/src/command/args/arg_item.rs | 14 ++++---- pumpkin/src/command/args/arg_message.rs | 2 +- pumpkin/src/command/args/arg_players.rs | 8 ++--- pumpkin/src/command/args/arg_position_2d.rs | 2 +- pumpkin/src/command/args/arg_position_3d.rs | 2 +- .../src/command/args/arg_position_block.rs | 2 +- pumpkin/src/command/args/arg_rotation.rs | 2 +- pumpkin/src/command/dispatcher.rs | 4 +-- pumpkin/src/entity/player.rs | 3 +- pumpkin/src/main.rs | 19 ++++++++-- pumpkin/src/server/connection_cache.rs | 7 ++-- pumpkin/src/world/bossbar.rs | 2 +- pumpkin/src/world/custom_bossbar.rs | 2 +- pumpkin/src/world/mod.rs | 6 ++-- pumpkin/src/world/player_chunker.rs | 14 ++++---- 31 files changed, 141 insertions(+), 106 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ccfd6ea8..193c05eb 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -32,7 +32,7 @@ jobs: - uses: actions/checkout@v4 - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - uses: Swatinem/rust-cache@v2 - - run: cargo clippy --all-targets --all-features --no-default-features + - run: cargo clippy --all-targets --all-features build_and_test: name: Build project and test runs-on: ${{ matrix.os }} @@ -77,4 +77,4 @@ jobs: - uses: actions/checkout@v4 - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - uses: Swatinem/rust-cache@v2 - - run: cargo clippy --release --all-targets --all-features --no-default-features + - run: cargo clippy --release --all-targets --all-features diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index a69c5349..6e2035c0 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -7,6 +7,7 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::{ fs, net::{Ipv4Addr, SocketAddr}, + num::NonZeroU8, path::Path, sync::LazyLock, }; @@ -71,9 +72,9 @@ pub struct BasicConfiguration { /// The maximum number of players allowed on the server. Specifying `0` disables the limit. pub max_players: u32, /// The maximum view distance for players. - pub view_distance: u8, + pub view_distance: NonZeroU8, /// The maximum simulated view distance. - pub simulation_distance: u8, + pub simulation_distance: NonZeroU8, /// The default game difficulty. pub default_difficulty: Difficulty, /// Whether the Nether dimension is enabled. @@ -103,8 +104,8 @@ impl Default for BasicConfiguration { server_address: SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 25565), seed: "".to_string(), max_players: 100000, - view_distance: 10, - simulation_distance: 10, + view_distance: NonZeroU8::new(10).unwrap(), + simulation_distance: NonZeroU8::new(10).unwrap(), default_difficulty: Difficulty::Normal, allow_nether: true, hardcore: false, @@ -176,9 +177,14 @@ impl LoadConfiguration for BasicConfiguration { } fn validate(&self) { - assert!(self.view_distance >= 2, "View distance must be at least 2"); assert!( - self.view_distance <= 32, + self.view_distance + .ge(unsafe { &NonZeroU8::new_unchecked(2) }), + "View distance must be at least 2" + ); + assert!( + self.view_distance + .le(unsafe { &NonZeroU8::new_unchecked(32) }), "View distance must be less than 32" ); if self.online_mode { diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index 336e86f0..f5b5db46 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -1,3 +1,5 @@ +use std::num::NonZeroU16; + use bytebuf::{packet_id::Packet, ReadingError}; use bytes::{Bytes, BytesMut}; use pumpkin_core::text::{style::Style, TextComponent}; @@ -19,7 +21,7 @@ pub use var_long::*; /// To current Minecraft protocol /// Don't forget to change this when porting -pub const CURRENT_MC_PROTOCOL: u32 = 769; +pub const CURRENT_MC_PROTOCOL: NonZeroU16 = unsafe { NonZeroU16::new_unchecked(769) }; pub const MAX_PACKET_SIZE: i32 = 2097152; diff --git a/pumpkin-protocol/src/var_int.rs b/pumpkin-protocol/src/var_int.rs index 608287fd..1401230a 100644 --- a/pumpkin-protocol/src/var_int.rs +++ b/pumpkin-protocol/src/var_int.rs @@ -1,3 +1,5 @@ +use std::num::NonZeroUsize; + use bytes::{Buf, BufMut}; use serde::{ de::{SeqAccess, Visitor}, @@ -15,7 +17,7 @@ pub struct VarInt(pub VarIntType); impl VarInt { /// The maximum number of bytes a `VarInt` can occupy. - pub const MAX_SIZE: usize = 5; + 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. @@ -28,7 +30,7 @@ impl VarInt { pub fn encode(&self, w: &mut impl BufMut) { let mut val = self.0; - for _ in 0..Self::MAX_SIZE { + 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 }); @@ -40,7 +42,7 @@ impl VarInt { pub fn decode(r: &mut impl Buf) -> Result { let mut val = 0; - for i in 0..Self::MAX_SIZE { + for i in 0..Self::MAX_SIZE.get() { if !r.has_remaining() { return Err(VarIntDecodeError::Incomplete); } @@ -130,7 +132,7 @@ impl<'de> Deserialize<'de> for VarInt { A: SeqAccess<'de>, { let mut val = 0; - for i in 0..VarInt::MAX_SIZE { + 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 { diff --git a/pumpkin-protocol/src/var_long.rs b/pumpkin-protocol/src/var_long.rs index cbb800c5..72f5c11e 100644 --- a/pumpkin-protocol/src/var_long.rs +++ b/pumpkin-protocol/src/var_long.rs @@ -1,3 +1,5 @@ +use std::num::NonZeroUsize; + use bytes::{Buf, BufMut}; use serde::{ de::{self, SeqAccess, Visitor}, @@ -15,7 +17,7 @@ pub struct VarLong(pub VarLongType); impl VarLong { /// The maximum number of bytes a `VarLong` - pub const MAX_SIZE: usize = 10; + 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. @@ -28,7 +30,7 @@ impl VarLong { pub fn encode(&self, w: &mut impl BufMut) { let mut x = self.0; - for _ in 0..Self::MAX_SIZE { + for _ in 0..Self::MAX_SIZE.get() { let byte = (x & 0x7F) as u8; x >>= 7; if x == 0 { @@ -41,7 +43,7 @@ impl VarLong { pub fn decode(r: &mut impl Buf) -> Result { let mut val = 0; - for i in 0..Self::MAX_SIZE { + for i in 0..Self::MAX_SIZE.get() { if !r.has_remaining() { return Err(VarLongDecodeError::Incomplete); } @@ -131,7 +133,7 @@ impl<'de> Deserialize<'de> for VarLong { A: SeqAccess<'de>, { let mut val = 0; - for i in 0..VarLong::MAX_SIZE { + 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 { diff --git a/pumpkin-world/src/cylindrical_chunk_iterator.rs b/pumpkin-world/src/cylindrical_chunk_iterator.rs index 7e388261..d8b65fe3 100644 --- a/pumpkin-world/src/cylindrical_chunk_iterator.rs +++ b/pumpkin-world/src/cylindrical_chunk_iterator.rs @@ -1,13 +1,15 @@ +use std::num::NonZeroU8; + use pumpkin_core::math::vector2::Vector2; #[derive(Debug, Clone, Copy, PartialEq)] pub struct Cylindrical { pub center: Vector2, - pub view_distance: u8, + pub view_distance: NonZeroU8, } impl Cylindrical { - pub fn new(center: Vector2, view_distance: u8) -> Self { + pub fn new(center: Vector2, view_distance: NonZeroU8) -> Self { Self { center, view_distance, @@ -36,19 +38,19 @@ impl Cylindrical { } fn left(&self) -> i32 { - self.center.x - self.view_distance as i32 - 1 + self.center.x - self.view_distance.get() as i32 - 1 } fn bottom(&self) -> i32 { - self.center.z - self.view_distance as i32 - 1 + self.center.z - self.view_distance.get() as i32 - 1 } fn right(&self) -> i32 { - self.center.x + self.view_distance as i32 + 1 + self.center.x + self.view_distance.get() as i32 + 1 } fn top(&self) -> i32 { - self.center.z + self.view_distance as i32 + 1 + self.center.z + self.view_distance.get() as i32 + 1 } fn is_within_distance(&self, x: i32, z: i32) -> bool { @@ -59,7 +61,7 @@ impl Cylindrical { let min_leg = rel_x.min(rel_z) as i64; let hyp_sqr = max_leg * max_leg + min_leg * min_leg; - hyp_sqr < (self.view_distance as i64 * self.view_distance as i64) + hyp_sqr < (self.view_distance.get() as i64 * self.view_distance.get() as i64) } /// Returns an iterator of all chunks within this cylinder @@ -82,12 +84,14 @@ impl Cylindrical { #[cfg(test)] mod test { + use std::num::NonZeroU8; + use super::Cylindrical; use pumpkin_core::math::vector2::Vector2; #[test] fn test_bounds() { - let cylinder = Cylindrical::new(Vector2::new(0, 0), 10); + let cylinder = Cylindrical::new(Vector2::new(0, 0), unsafe { NonZeroU8::new_unchecked(1) }); for chunk in cylinder.all_chunks_within() { assert!(chunk.x >= cylinder.left() && chunk.x <= cylinder.right()); assert!(chunk.z >= cylinder.bottom() && chunk.z <= cylinder.top()); diff --git a/pumpkin/src/block/block_manager.rs b/pumpkin/src/block/block_manager.rs index 01b3c08a..92395bb8 100644 --- a/pumpkin/src/block/block_manager.rs +++ b/pumpkin/src/block/block_manager.rs @@ -21,8 +21,7 @@ pub struct BlockManager { impl BlockManager { pub fn register(&mut self, block: T) { - self.blocks - .insert(block.name().to_string(), Arc::new(block)); + self.blocks.insert(block.name(), Arc::new(block)); } pub async fn on_use( diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index a6e41944..dfe913e1 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -29,7 +29,10 @@ use pumpkin_protocol::{ }, ConnectionState, KnownPack, VarInt, CURRENT_MC_PROTOCOL, }; -use std::sync::LazyLock; +use std::{ + num::{NonZeroI32, NonZeroU8}, + sync::LazyLock, +}; use uuid::Uuid; static LINKS: LazyLock> = LazyLock::new(|| { @@ -106,7 +109,7 @@ impl Client { self.connection_state.store(handshake.next_state); if self.connection_state.load() != ConnectionState::Status { let protocol = version; - match protocol.cmp(&(CURRENT_MC_PROTOCOL as i32)) { + 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; } @@ -132,10 +135,7 @@ impl Client { } fn is_valid_player_name(name: &str) -> bool { - name.len() <= 16 - && name - .chars() - .all(|c| c > 32_u8 as char && c < 127_u8 as char) + 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) { @@ -400,7 +400,9 @@ impl Client { ) { *self.config.lock().await = Some(PlayerConfig { locale: client_information.locale, - view_distance: client_information.view_distance as u8, + 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, diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 9fd6fed0..1e3ce5e5 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -1,6 +1,7 @@ use std::{ collections::VecDeque, net::SocketAddr, + num::NonZeroU8, sync::{ atomic::{AtomicBool, AtomicI32}, Arc, @@ -53,7 +54,7 @@ pub struct PlayerConfig { /// The player's preferred language. pub locale: String, // 16 /// The maximum distance at which chunks are rendered. - pub view_distance: u8, + pub view_distance: NonZeroU8, /// The player's chat mode settings pub chat_mode: ChatMode, /// Whether chat colors are enabled. @@ -72,7 +73,7 @@ impl Default for PlayerConfig { fn default() -> Self { Self { locale: "en_us".to_string(), - view_distance: 2, + view_distance: unsafe { NonZeroU8::new_unchecked(10) }, chat_mode: ChatMode::Enabled, chat_colors: true, skin_parts: 0, diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index b5dfd447..920de697 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -1,3 +1,4 @@ +use std::num::NonZeroU8; use std::sync::Arc; use super::PlayerConfig; @@ -436,7 +437,7 @@ impl Player { } pub async fn handle_client_information( - self: &Arc, + self: &Arc, client_information: SClientInformationPlay, ) { if let (Some(main_hand), Some(chat_mode)) = ( @@ -458,25 +459,27 @@ impl Player { let old_view_distance = config.view_distance; - let update_watched = if old_view_distance == client_information.view_distance as u8 - { - false - } else { - log::debug!( - "Player {} ({}) updated render distance: {} -> {}.", - self.gameprofile.name, - self.client.id, - old_view_distance, - client_information.view_distance - ); - - true - }; + let update_watched = + if old_view_distance.get() == client_information.view_distance as u8 { + false + } else { + log::debug!( + "Player {} ({}) updated render distance: {} -> {}.", + self.gameprofile.name, + self.client.id, + old_view_distance, + client_information.view_distance + ); + + true + }; *config = PlayerConfig { locale: client_information.locale, // A Negative view distance would be impossible and make no sense right ?, Mojang: Lets make is signed :D - view_distance: client_information.view_distance as u8, + 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, diff --git a/pumpkin/src/command/args/arg_block.rs b/pumpkin/src/command/args/arg_block.rs index 41e3ebd3..fe11228f 100644 --- a/pumpkin/src/command/args/arg_block.rs +++ b/pumpkin/src/command/args/arg_block.rs @@ -65,12 +65,14 @@ impl<'a> FindArg<'a> for BlockArgumentConsumer { fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { match args.get(name) { - Some(Arg::Block(name)) => match block_registry::get_block(name) { - Some(block) => Ok(block), - None => Err(CommandError::GeneralCommandIssue(format!( - "Block {name} does not exist." - ))), - }, + Some(Arg::Block(name)) => block_registry::get_block(name).map_or_else( + || { + Err(CommandError::GeneralCommandIssue(format!( + "Block {name} does not exist." + ))) + }, + Result::Ok, + ), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), } } diff --git a/pumpkin/src/command/args/arg_bounded_num.rs b/pumpkin/src/command/args/arg_bounded_num.rs index 4a9f718b..41410367 100644 --- a/pumpkin/src/command/args/arg_bounded_num.rs +++ b/pumpkin/src/command/args/arg_bounded_num.rs @@ -22,7 +22,7 @@ pub(crate) struct BoundedNumArgumentConsumer { #[async_trait] impl ArgumentConsumer for BoundedNumArgumentConsumer where - BoundedNumArgumentConsumer: GetClientSideArgParser, + Self: GetClientSideArgParser, { async fn consume<'a>( &self, @@ -236,7 +236,7 @@ impl GetClientSideArgParser for BoundedNumArgumentConsumer { impl DefaultNameArgConsumer for BoundedNumArgumentConsumer where - BoundedNumArgumentConsumer: ArgumentConsumer, + Self: ArgumentConsumer, { fn default_name(&self) -> &'static str { // setting a single default name for all BoundedNumArgumentConsumer variants is probably a bad idea since it would lead to confusion diff --git a/pumpkin/src/command/args/arg_command.rs b/pumpkin/src/command/args/arg_command.rs index ef933977..93955987 100644 --- a/pumpkin/src/command/args/arg_command.rs +++ b/pumpkin/src/command/args/arg_command.rs @@ -38,10 +38,9 @@ impl ArgumentConsumer for CommandTreeArgumentConsumer { let s = args.pop()?; let dispatcher = &server.command_dispatcher; - return match dispatcher.get_tree(s) { - Ok(tree) => Some(Arg::CommandTree(tree)), - Err(_) => None, - }; + return dispatcher + .get_tree(s) + .map_or_else(|_| None, |tree| Some(Arg::CommandTree(tree))); } async fn suggest<'a>( @@ -71,7 +70,7 @@ impl DefaultNameArgConsumer for CommandTreeArgumentConsumer { } fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &CommandTreeArgumentConsumer + &Self } } diff --git a/pumpkin/src/command/args/arg_entities.rs b/pumpkin/src/command/args/arg_entities.rs index 9f077597..102a177b 100644 --- a/pumpkin/src/command/args/arg_entities.rs +++ b/pumpkin/src/command/args/arg_entities.rs @@ -62,7 +62,7 @@ impl DefaultNameArgConsumer for EntitiesArgumentConsumer { } fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &EntitiesArgumentConsumer + &Self } } diff --git a/pumpkin/src/command/args/arg_entity.rs b/pumpkin/src/command/args/arg_entity.rs index dce52240..37e4dac0 100644 --- a/pumpkin/src/command/args/arg_entity.rs +++ b/pumpkin/src/command/args/arg_entity.rs @@ -84,7 +84,7 @@ impl DefaultNameArgConsumer for EntityArgumentConsumer { } fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &EntityArgumentConsumer + &Self } } diff --git a/pumpkin/src/command/args/arg_gamemode.rs b/pumpkin/src/command/args/arg_gamemode.rs index 469a92f9..d520b25c 100644 --- a/pumpkin/src/command/args/arg_gamemode.rs +++ b/pumpkin/src/command/args/arg_gamemode.rs @@ -65,7 +65,7 @@ impl DefaultNameArgConsumer for GamemodeArgumentConsumer { } fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &GamemodeArgumentConsumer + &Self } } diff --git a/pumpkin/src/command/args/arg_item.rs b/pumpkin/src/command/args/arg_item.rs index 3deeb2fe..481feb7d 100644 --- a/pumpkin/src/command/args/arg_item.rs +++ b/pumpkin/src/command/args/arg_item.rs @@ -63,12 +63,14 @@ impl<'a> FindArg<'a> for ItemArgumentConsumer { fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { match args.get(name) { - Some(Arg::Item(name)) => match item_registry::get_item(name) { - Some(item) => Ok((name, item)), - None => Err(CommandError::GeneralCommandIssue(format!( - "Item {name} does not exist." - ))), - }, + Some(Arg::Item(name)) => item_registry::get_item(name).map_or_else( + || { + Err(CommandError::GeneralCommandIssue(format!( + "Item {name} does not exist." + ))) + }, + |item| Ok((*name, item)), + ), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), } } diff --git a/pumpkin/src/command/args/arg_message.rs b/pumpkin/src/command/args/arg_message.rs index e2815de2..00af2d87 100644 --- a/pumpkin/src/command/args/arg_message.rs +++ b/pumpkin/src/command/args/arg_message.rs @@ -60,7 +60,7 @@ impl DefaultNameArgConsumer for MsgArgConsumer { } fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &MsgArgConsumer + &Self } } diff --git a/pumpkin/src/command/args/arg_players.rs b/pumpkin/src/command/args/arg_players.rs index 7c06f8e4..0e8876a7 100644 --- a/pumpkin/src/command/args/arg_players.rs +++ b/pumpkin/src/command/args/arg_players.rs @@ -53,11 +53,7 @@ impl ArgumentConsumer for PlayersArgumentConsumer { _ => None, }, "@r" => { - if let Some(p) = server.get_random_player().await { - Some(vec![p.clone()]) - } else { - Some(vec![]) - } + (server.get_random_player().await).map_or_else(|| Some(vec![]), |p| Some(vec![p])) } "@a" | "@e" => Some(server.get_all_players().await), name => server.get_player_by_name(name).await.map(|p| vec![p]), @@ -82,7 +78,7 @@ impl DefaultNameArgConsumer for PlayersArgumentConsumer { } fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &PlayersArgumentConsumer + &Self } } diff --git a/pumpkin/src/command/args/arg_position_2d.rs b/pumpkin/src/command/args/arg_position_2d.rs index 9dc45073..107ea432 100644 --- a/pumpkin/src/command/args/arg_position_2d.rs +++ b/pumpkin/src/command/args/arg_position_2d.rs @@ -78,7 +78,7 @@ impl DefaultNameArgConsumer for Position2DArgumentConsumer { } fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &Position2DArgumentConsumer + &Self } } diff --git a/pumpkin/src/command/args/arg_position_3d.rs b/pumpkin/src/command/args/arg_position_3d.rs index 4940b4db..1e58ad81 100644 --- a/pumpkin/src/command/args/arg_position_3d.rs +++ b/pumpkin/src/command/args/arg_position_3d.rs @@ -81,7 +81,7 @@ impl DefaultNameArgConsumer for Position3DArgumentConsumer { } fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &Position3DArgumentConsumer + &Self } } diff --git a/pumpkin/src/command/args/arg_position_block.rs b/pumpkin/src/command/args/arg_position_block.rs index b8ae25db..861eec12 100644 --- a/pumpkin/src/command/args/arg_position_block.rs +++ b/pumpkin/src/command/args/arg_position_block.rs @@ -82,7 +82,7 @@ impl DefaultNameArgConsumer for BlockPosArgumentConsumer { } fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &BlockPosArgumentConsumer + &Self } } diff --git a/pumpkin/src/command/args/arg_rotation.rs b/pumpkin/src/command/args/arg_rotation.rs index 4efdbde0..6e53f2f9 100644 --- a/pumpkin/src/command/args/arg_rotation.rs +++ b/pumpkin/src/command/args/arg_rotation.rs @@ -66,7 +66,7 @@ impl DefaultNameArgConsumer for RotationArgumentConsumer { } fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &RotationArgumentConsumer + &Self } } diff --git a/pumpkin/src/command/dispatcher.rs b/pumpkin/src/command/dispatcher.rs index 9448c505..e63a67ac 100644 --- a/pumpkin/src/command/dispatcher.rs +++ b/pumpkin/src/command/dispatcher.rs @@ -32,11 +32,11 @@ impl CommandError { pub fn into_string_or_pumpkin_error(self, cmd: &str) -> Result> { match self { InvalidConsumption(s) => { - println!("Error while parsing command \"{cmd}\": {s:?} was consumed, but couldn't be parsed"); + log::error!("Error while parsing command \"{cmd}\": {s:?} was consumed, but couldn't be parsed"); Ok("Internal Error (See logs for details)".into()) } InvalidRequirement => { - println!("Error while parsing command \"{cmd}\": a requirement that was expected was not met."); + log::error!("Error while parsing command \"{cmd}\": a requirement that was expected was not met."); Ok("Internal Error (See logs for details)".into()) } GeneralCommandIssue(s) => Ok(s), diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index cc8af323..31ef1131 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -1,4 +1,5 @@ use std::{ + num::NonZeroU8, sync::{ atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU32, AtomicU8}, Arc, @@ -176,7 +177,7 @@ impl Player { // (We left shift by one so we can search around that chunk) watched_section: AtomicCell::new(Cylindrical::new( Vector2::new(i32::MAX >> 1, i32::MAX >> 1), - 0, + unsafe { NonZeroU8::new_unchecked(1) }, )), wait_for_keep_alive: AtomicBool::new(false), keep_alive_id: AtomicI64::new(0), diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index d33cd9e1..c2547e78 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -2,7 +2,22 @@ #![deny(clippy::pedantic)] // #![warn(clippy::restriction)] #![deny(clippy::cargo)] +// to keep consistency #![deny(clippy::if_then_some_else_none)] +#![deny(clippy::empty_enum_variants_with_brackets)] +#![deny(clippy::empty_structs_with_brackets)] +#![deny(clippy::separated_literal_suffix)] +#![deny(clippy::semicolon_outside_block)] +#![deny(clippy::non_zero_suggestions)] +#![deny(clippy::string_lit_chars_any)] +#![deny(clippy::use_self)] +#![deny(clippy::useless_let_if_seq)] +#![deny(clippy::branches_sharing_code)] +#![deny(clippy::equatable_if_let)] +#![deny(clippy::option_if_let_else)] +// use log crate +#![deny(clippy::print_stdout)] +#![deny(clippy::print_stderr)] // REMOVE SOME WHEN RELEASE #![expect(clippy::cargo_common_metadata)] #![expect(clippy::multiple_crate_versions)] @@ -182,8 +197,8 @@ async fn main() { let server = server.clone(); tokio::spawn(async move { ticker.run(&server).await; - }); - } + }) + }; let mut master_client_id: u16 = 0; loop { diff --git a/pumpkin/src/server/connection_cache.rs b/pumpkin/src/server/connection_cache.rs index 8e6fce8f..4171ea13 100644 --- a/pumpkin/src/server/connection_cache.rs +++ b/pumpkin/src/server/connection_cache.rs @@ -2,6 +2,7 @@ use core::error; use std::{ fs::File, io::{Cursor, Read}, + num::NonZeroU32, path::Path, }; @@ -105,7 +106,7 @@ impl CachedStatus { } pub fn build_response(config: &BasicConfiguration) -> StatusResponse { - let icon = if config.use_favicon { + let favicon = if config.use_favicon { let icon_path = &config.favicon_path; log::debug!("Loading server favicon from '{}'", icon_path); match load_icon_from_file(icon_path).or_else(|err| { @@ -142,7 +143,7 @@ impl CachedStatus { StatusResponse { version: Some(Version { name: CURRENT_MC_VERSION.into(), - protocol: CURRENT_MC_PROTOCOL, + protocol: NonZeroU32::from(CURRENT_MC_PROTOCOL).get(), }), players: Some(Players { max: config.max_players, @@ -150,7 +151,7 @@ impl CachedStatus { sample: vec![], }), description: config.motd.clone(), - favicon: icon, + favicon, enforce_secure_chat: false, } } diff --git a/pumpkin/src/world/bossbar.rs b/pumpkin/src/world/bossbar.rs index fdc06243..dc9bb74d 100644 --- a/pumpkin/src/world/bossbar.rs +++ b/pumpkin/src/world/bossbar.rs @@ -43,7 +43,7 @@ pub struct Bossbar { impl Bossbar { #[must_use] - pub fn new(title: String) -> Bossbar { + pub fn new(title: String) -> Self { let uuid = Uuid::new_v4(); Self { diff --git a/pumpkin/src/world/custom_bossbar.rs b/pumpkin/src/world/custom_bossbar.rs index f6d02679..99a7a0b8 100644 --- a/pumpkin/src/world/custom_bossbar.rs +++ b/pumpkin/src/world/custom_bossbar.rs @@ -53,7 +53,7 @@ impl Default for CustomBossbars { impl CustomBossbars { #[must_use] - pub fn new() -> CustomBossbars { + pub fn new() -> Self { Self { custom_bossbars: HashMap::new(), } diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 54c5d105..dcfab4b9 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -242,8 +242,8 @@ impl World { base_config.hardcore, &dimensions, base_config.max_players.into(), - base_config.view_distance.into(), // TODO: view distance - base_config.simulation_distance.into(), // TODO: sim view dinstance + base_config.view_distance.get().into(), // TODO: view distance + base_config.simulation_distance.get().into(), // TODO: sim view dinstance false, true, false, @@ -324,7 +324,7 @@ impl World { .client .send_packet(&CPlayerInfoUpdate::new(0x01 | 0x08, &entries)) .await; - } + }; let gameprofile = &player.gameprofile; diff --git a/pumpkin/src/world/player_chunker.rs b/pumpkin/src/world/player_chunker.rs index a7c6f8a5..c0ba854e 100644 --- a/pumpkin/src/world/player_chunker.rs +++ b/pumpkin/src/world/player_chunker.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{num::NonZeroU8, sync::Arc}; use pumpkin_config::BASIC_CONFIG; use pumpkin_core::{ @@ -10,13 +10,11 @@ use pumpkin_world::cylindrical_chunk_iterator::Cylindrical; use crate::entity::player::Player; -pub async fn get_view_distance(player: &Player) -> u8 { - player - .config - .lock() - .await - .view_distance - .clamp(2, BASIC_CONFIG.view_distance) +pub async fn get_view_distance(player: &Player) -> NonZeroU8 { + player.config.lock().await.view_distance.clamp( + unsafe { NonZeroU8::new_unchecked(2) }, + BASIC_CONFIG.view_distance, + ) } pub async fn player_join(player: &Arc) { 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 19/42] 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 20/42] 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 21/42] 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 22/42] 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 23/42] 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 24/42] 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 25/42] 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 26/42] 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 27/42] 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 28/42] 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 29/42] 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 30/42] 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 31/42] 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 32/42] 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. From febbfd89e6c647f11612721c3f8a1b4cc7fad9cc Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Thu, 26 Dec 2024 00:04:43 +0100 Subject: [PATCH 33/42] Remove `target-cpu=native` from Docker This can cause illegal instructions when using the public docker image build on the CI --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 760bc635..7fc54425 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ 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" +ENV RUSTFLAGS="-C target-feature=-crt-static" RUN apk add --no-cache musl-dev WORKDIR /pumpkin From d833582c7bd2b4dcbdc43222a2520105584401de Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Thu, 26 Dec 2024 14:09:18 +0100 Subject: [PATCH 34/42] Check byte size on custom payloads --- pumpkin-protocol/src/bytebuf/mod.rs | 23 +++++++++++++++++++ .../src/server/config/s_cookie_response.rs | 11 ++------- .../src/server/config/s_plugin_message.rs | 3 ++- .../src/server/login/s_cookie_response.rs | 11 ++------- .../src/server/login/s_plugin_response.rs | 5 +++- .../src/server/play/s_cookie_response.rs | 11 ++------- 6 files changed, 35 insertions(+), 29 deletions(-) diff --git a/pumpkin-protocol/src/bytebuf/mod.rs b/pumpkin-protocol/src/bytebuf/mod.rs index b1ee75ae..edfa15c7 100644 --- a/pumpkin-protocol/src/bytebuf/mod.rs +++ b/pumpkin-protocol/src/bytebuf/mod.rs @@ -54,6 +54,12 @@ pub trait ByteBuf: Buf { fn try_copy_to_bytes(&mut self, len: usize) -> Result; + fn try_copy_to_bytes_len( + &mut self, + len: usize, + max_length: usize, + ) -> Result; + fn try_copy_to_slice(&mut self, dst: &mut [u8]) -> Result<(), ReadingError>; fn try_get_var_int(&mut self) -> Result; @@ -176,6 +182,23 @@ impl ByteBuf for T { } } + fn try_copy_to_bytes_len( + &mut self, + len: usize, + max_size: usize, + ) -> Result { + if len > max_size { + return Err(ReadingError::Message( + "Tried to copy bytes but length exceeds maximum length".to_string(), + )); + } + if self.remaining() >= len { + Ok(self.copy_to_bytes(len)) + } else { + Err(ReadingError::Message("Unable to copy bytes".to_string())) + } + } + fn try_copy_to_slice(&mut self, dst: &mut [u8]) -> Result<(), ReadingError> { if self.remaining() >= dst.len() { self.copy_to_slice(dst); diff --git a/pumpkin-protocol/src/server/config/s_cookie_response.rs b/pumpkin-protocol/src/server/config/s_cookie_response.rs index b9e3a0a9..9f683a5c 100644 --- a/pumpkin-protocol/src/server/config/s_cookie_response.rs +++ b/pumpkin-protocol/src/server/config/s_cookie_response.rs @@ -1,6 +1,5 @@ use bytes::Buf; use pumpkin_macros::server_packet; -use serde::de; use crate::{ bytebuf::{ByteBuf, ReadingError}, @@ -18,7 +17,7 @@ pub struct SConfigCookieResponse { pub payload: Option, // 5120, } -const MAX_PAYLOAD_SIZE: i32 = 5120; +const MAX_COOKIE_LENGTH: usize = 5120; impl ServerPacket for SConfigCookieResponse { fn read(bytebuf: &mut impl Buf) -> Result { @@ -37,13 +36,7 @@ impl ServerPacket for SConfigCookieResponse { let payload_length = bytebuf.try_get_var_int()?; let length = payload_length.0; - if length > MAX_PAYLOAD_SIZE { - return Err(de::Error::custom( - "Payload exceeds the maximum allowed size (5120 bytes)", - )); - } - - let payload = bytebuf.try_copy_to_bytes(length as usize)?; + let payload = bytebuf.try_copy_to_bytes_len(length as usize, MAX_COOKIE_LENGTH)?; Ok(Self { key, diff --git a/pumpkin-protocol/src/server/config/s_plugin_message.rs b/pumpkin-protocol/src/server/config/s_plugin_message.rs index d9e0abae..e5313443 100644 --- a/pumpkin-protocol/src/server/config/s_plugin_message.rs +++ b/pumpkin-protocol/src/server/config/s_plugin_message.rs @@ -6,6 +6,7 @@ use crate::{ codec::identifier::Identifier, ServerPacket, }; +const MAX_PAYLOAD_SIZE: usize = 1048576; #[server_packet("config:custom_payload")] pub struct SPluginMessage { @@ -17,7 +18,7 @@ impl ServerPacket for SPluginMessage { fn read(bytebuf: &mut impl Buf) -> Result { Ok(Self { channel: bytebuf.try_get_identifer()?, - data: bytebuf.try_copy_to_bytes(bytebuf.remaining())?, + data: bytebuf.try_copy_to_bytes_len(bytebuf.remaining(), MAX_PAYLOAD_SIZE)?, }) } } diff --git a/pumpkin-protocol/src/server/login/s_cookie_response.rs b/pumpkin-protocol/src/server/login/s_cookie_response.rs index 5e498a1b..6a6d7383 100644 --- a/pumpkin-protocol/src/server/login/s_cookie_response.rs +++ b/pumpkin-protocol/src/server/login/s_cookie_response.rs @@ -5,7 +5,6 @@ use crate::{ }; use bytes::Buf; use pumpkin_macros::server_packet; -use serde::de; #[server_packet("login:cookie_response")] /// Response to a Cookie Request (login) from the server. @@ -17,7 +16,7 @@ pub struct SLoginCookieResponse { pub payload: Option, // 5120, } -const MAX_PAYLOAD_SIZE: i32 = 5120; +const MAX_COOKIE_LENGTH: usize = 5120; impl ServerPacket for SLoginCookieResponse { fn read(bytebuf: &mut impl Buf) -> Result { @@ -36,13 +35,7 @@ impl ServerPacket for SLoginCookieResponse { let payload_length = bytebuf.try_get_var_int()?; let length = payload_length.0; - if length > MAX_PAYLOAD_SIZE { - return Err(de::Error::custom( - "Payload exceeds the maximum allowed size (5120 bytes)", - )); - } - - let payload = bytebuf.try_copy_to_bytes(length as usize)?; + let payload = bytebuf.try_copy_to_bytes_len(length as usize, MAX_COOKIE_LENGTH)?; Ok(Self { key, diff --git a/pumpkin-protocol/src/server/login/s_plugin_response.rs b/pumpkin-protocol/src/server/login/s_plugin_response.rs index dd92a1bb..bffc72b9 100644 --- a/pumpkin-protocol/src/server/login/s_plugin_response.rs +++ b/pumpkin-protocol/src/server/login/s_plugin_response.rs @@ -5,6 +5,8 @@ use crate::{ use bytes::{Buf, Bytes}; use pumpkin_macros::server_packet; +const MAX_PAYLOAD_SIZE: usize = 1048576; + #[server_packet("login:custom_query_answer")] pub struct SLoginPluginResponse { pub message_id: VarInt, @@ -15,7 +17,8 @@ impl ServerPacket for SLoginPluginResponse { fn read(bytebuf: &mut impl Buf) -> Result { Ok(Self { message_id: bytebuf.try_get_var_int()?, - data: bytebuf.try_get_option(|v| v.try_copy_to_bytes(v.remaining()))?, + data: bytebuf + .try_get_option(|v| v.try_copy_to_bytes_len(v.remaining(), MAX_PAYLOAD_SIZE))?, }) } } diff --git a/pumpkin-protocol/src/server/play/s_cookie_response.rs b/pumpkin-protocol/src/server/play/s_cookie_response.rs index 7779d8ac..e8e5d4bb 100644 --- a/pumpkin-protocol/src/server/play/s_cookie_response.rs +++ b/pumpkin-protocol/src/server/play/s_cookie_response.rs @@ -5,7 +5,6 @@ use crate::{ }; use bytes::Buf; use pumpkin_macros::server_packet; -use serde::de; #[server_packet("play:cookie_response")] /// Response to a Cookie Request (play) from the server. @@ -17,7 +16,7 @@ pub struct SCookieResponse { pub payload: Option, // 5120, } -const MAX_PAYLOAD_SIZE: i32 = 5120; +const MAX_COOKIE_LENGTH: usize = 5120; impl ServerPacket for SCookieResponse { fn read(bytebuf: &mut impl Buf) -> Result { @@ -36,13 +35,7 @@ impl ServerPacket for SCookieResponse { let payload_length = bytebuf.try_get_var_int()?; let length = payload_length.0; - if length > MAX_PAYLOAD_SIZE { - return Err(de::Error::custom( - "Payload exceeds the maximum allowed size (5120 bytes)", - )); - } - - let payload = bytebuf.try_copy_to_bytes(length as usize)?; + let payload = bytebuf.try_copy_to_bytes_len(length as usize, MAX_COOKIE_LENGTH)?; Ok(Self { key, From a0339728c7058a40fe55840a235d517f64405313 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Thu, 26 Dec 2024 20:59:25 +0100 Subject: [PATCH 35/42] pumpkin-protocol: remove pumpkin-config --- pumpkin-protocol/Cargo.toml | 1 - pumpkin-protocol/src/lib.rs | 15 ++++ pumpkin-protocol/src/packet_decoder.rs | 17 ++-- pumpkin-protocol/src/packet_encoder.rs | 111 +++++++++++++------------ pumpkin/src/net/mod.rs | 11 ++- 5 files changed, 89 insertions(+), 66 deletions(-) diff --git a/pumpkin-protocol/Cargo.toml b/pumpkin-protocol/Cargo.toml index 674b290e..c167df95 100644 --- a/pumpkin-protocol/Cargo.toml +++ b/pumpkin-protocol/Cargo.toml @@ -12,7 +12,6 @@ query = [] [dependencies] pumpkin-nbt = { path = "../pumpkin-nbt" } -pumpkin-config = { path = "../pumpkin-config" } pumpkin-macros = { path = "../pumpkin-macros" } pumpkin-world = { path = "../pumpkin-world" } pumpkin-core = { path = "../pumpkin-core" } diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index 8ef37f7a..fec62014 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -25,6 +25,21 @@ pub const MAX_PACKET_SIZE: i32 = 2097152; pub type FixedBitSet = bytes::Bytes; +/// Represents a compression threshold. +/// +/// The threshold determines the minimum size of data that should be compressed. +/// Data smaller than the threshold will not be compressed. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CompressionThreshold(pub u32); + +/// Represents a compression level. +/// +/// The level controls the amount of compression applied to the data. +/// Higher levels generally result in higher compression ratios but also +/// increase CPU usage. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CompressionLevel(pub u32); + #[derive(Debug, PartialEq, Clone, Copy)] pub enum ConnectionState { HandShake, diff --git a/pumpkin-protocol/src/packet_decoder.rs b/pumpkin-protocol/src/packet_decoder.rs index 5059a2c5..89ec4f42 100644 --- a/pumpkin-protocol/src/packet_decoder.rs +++ b/pumpkin-protocol/src/packet_decoder.rs @@ -17,8 +17,7 @@ pub struct PacketDecoder { buf: BytesMut, decompress_buf: BytesMut, cipher: Option, - compression: bool, - decompressor: Decompressor, + decompressor: Option, } // Manual implementation of Default trait for PacketDecoder @@ -29,8 +28,7 @@ impl Default for PacketDecoder { buf: BytesMut::new(), decompress_buf: BytesMut::new(), cipher: None, - compression: false, - decompressor: Decompressor::new(), + decompressor: None, } } } @@ -58,7 +56,7 @@ impl PacketDecoder { let packet_len_len = VarInt(packet_len).written_size(); let mut data; - if self.compression { + if let Some(decompressor) = &mut self.decompressor { r = &r[..packet_len as usize]; let data_len = VarInt::decode(&mut r) @@ -77,8 +75,7 @@ impl PacketDecoder { self.decompress_buf.resize(data_len as usize, 0); // Perform decompression using libdeflater - let decompressed_size = self - .decompressor + let decompressed_size = decompressor .zlib_decompress(r, &mut self.decompress_buf) .map_err(PacketDecodeError::from)?; @@ -135,7 +132,11 @@ impl PacketDecoder { /// Sets ZLib Decompression pub fn set_compression(&mut self, compression: bool) { - self.compression = compression; + if compression { + self.decompressor = Some(Decompressor::new()); + } else { + self.decompressor = None + } } fn decrypt_bytes(cipher: &mut Cipher, bytes: &mut [u8]) { diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index 1636d56e..006e1a99 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -1,11 +1,12 @@ use aes::cipher::{generic_array::GenericArray, BlockEncryptMut, BlockSizeUser, KeyIvInit}; use bytes::{BufMut, BytesMut}; -use pumpkin_config::compression::CompressionInfo; use thiserror::Error; use libdeflater::{CompressionLvl, Compressor}; -use crate::{codec::Codec, ClientPacket, VarInt, MAX_PACKET_SIZE}; +use crate::{ + codec::Codec, ClientPacket, CompressionLevel, CompressionThreshold, VarInt, MAX_PACKET_SIZE, +}; type Cipher = cfb8::Encryptor; @@ -16,8 +17,8 @@ pub struct PacketEncoder { buf: BytesMut, compress_buf: Vec, cipher: Option, - compression_threshold: Option, - compressor: Compressor, // Reuse the compressor for all packets + // compression and compression threshold + compression: Option<(Compressor, CompressionThreshold)>, } // Manual implementation of Default trait for PacketEncoder @@ -28,8 +29,7 @@ impl Default for PacketEncoder { buf: BytesMut::with_capacity(1024), compress_buf: Vec::with_capacity(1024), cipher: None, - compression_threshold: None, - compressor: Compressor::new(CompressionLvl::fastest()), // init compressor with fastest compression level + compression: None, // init compressor with fastest compression level } } } @@ -73,8 +73,8 @@ impl PacketEncoder { packet.write(&mut self.buf); let data_len = self.buf.len() - start_len; - if let Some(compression_threshold) = self.compression_threshold { - if data_len > compression_threshold as usize { + if let Some((compressor, compression_threshold)) = &mut self.compression { + if data_len > compression_threshold.0 as usize { // Get the data to compress let data_to_compress = &self.buf[start_len..]; @@ -82,15 +82,13 @@ impl PacketEncoder { self.compress_buf.clear(); // Compute the maximum size of compressed data - let max_compressed_size = - self.compressor.zlib_compress_bound(data_to_compress.len()); + let max_compressed_size = compressor.zlib_compress_bound(data_to_compress.len()); // Ensure compress_buf has enough capacity self.compress_buf.resize(max_compressed_size, 0); // Compress the data - let compressed_size = self - .compressor + let compressed_size = compressor .zlib_compress(data_to_compress, &mut self.compress_buf) .map_err(|e| PacketEncodeError::CompressionFailed(e.to_string()))?; @@ -166,26 +164,30 @@ impl PacketEncoder { } } - /// Enables or disables Zlib compression with the given options. + /// Enables or disables Zlib compression. /// - /// If `compression` is `Some`, compression is enabled with the provided - /// options. If `compression` is `None`, compression is disabled. - pub fn set_compression(&mut self, compression: Option) { - // Reset the compressor with the new compression level - if let Some(compression) = &compression { - self.compression_threshold = Some(compression.threshold); - let compression_level = compression.level as i32; - let level = match CompressionLvl::new(compression_level) { - Ok(level) => level, - Err(error) => { - log::error!("Invalid compression level {:?}", error); - return; - } - }; - self.compressor = Compressor::new(level); - } else { - self.compression_threshold = None; + /// If `compression` is `Some`, compression is enabled with the given `threshold` + /// for triggering compression and the specified `level`. If `compression` is + /// `None`, compression is disabled. + /// + /// # Errors + /// + /// Returns an `CompressionLevelError` if an invalid compression level is provided. + pub fn set_compression( + &mut self, + compression: Option<(CompressionThreshold, CompressionLevel)>, + ) -> Result<(), CompressionLevelError> { + match compression { + Some((threshold, level)) => { + let level = + CompressionLvl::new(level.0 as i32).map_err(|_| CompressionLevelError)?; + self.compression = Some((Compressor::new(level), threshold)); + } + None => { + self.compression = None; + } } + Ok(()) } /// Encrypts the data in the internal buffer and returns it as a `BytesMut`. @@ -209,6 +211,10 @@ impl PacketEncoder { } } +#[derive(Error, Debug)] +#[error("Invalid compression Level")] +pub struct CompressionLevelError; + /// Errors that can occur during packet encoding. #[derive(Error, Debug)] pub enum PacketEncodeError { @@ -268,15 +274,15 @@ mod tests { /// Helper function to build a packet with optional compression and encryption fn build_packet_with_encoder( packet: &T, - compression_info: Option, + compression_info: Option<(CompressionThreshold, CompressionLevel)>, key: Option<&[u8; 16]>, ) -> BytesMut { let mut encoder = PacketEncoder::default(); if let Some(compression) = compression_info { - encoder.set_compression(Some(compression)); + encoder.set_compression(Some(compression)).unwrap(); } else { - encoder.set_compression(None); + encoder.set_compression(None).unwrap(); } if let Some(key) = key { @@ -328,14 +334,12 @@ mod tests { // Create a CStatusResponse packet let packet = CStatusResponse::new("{\"description\": \"A Minecraft Server\"}"); - // Compression threshold is set to 0 to force compression - let compression_info = CompressionInfo { - threshold: 0, - level: 6, // Standard compression level - }; - // Build the packet with compression enabled - let packet_bytes = build_packet_with_encoder(&packet, Some(compression_info), None); + let packet_bytes = build_packet_with_encoder( + &packet, + Some((CompressionThreshold(0), CompressionLevel(6))), + None, + ); // Decode the packet manually to verify correctness let mut buffer = &packet_bytes[..]; @@ -418,18 +422,16 @@ mod tests { // Create a CStatusResponse packet let packet = CStatusResponse::new("{\"description\": \"A Minecraft Server\"}"); - // Compression threshold is set to 0 to force compression - let compression_info = CompressionInfo { - threshold: 0, - level: 6, // Standard compression level - }; - // Encryption key and IV (IV is the same as key in this case) let key = [0x01u8; 16]; // Example key // Build the packet with both compression and encryption enabled - let mut packet_bytes = - build_packet_with_encoder(&packet, Some(compression_info), Some(&key)); + // Compression threshold is set to 0 to force compression + let mut packet_bytes = build_packet_with_encoder( + &packet, + Some((CompressionThreshold(0), CompressionLevel(6))), + Some(&key), + ); // Decrypt the packet decrypt_aes128(&mut packet_bytes, &key, &key); @@ -567,14 +569,13 @@ mod tests { // Create a CStatusResponse packet with small payload let packet = CStatusResponse::new("Hi"); - // Compression threshold is set to a value higher than payload length - let compression_info = CompressionInfo { - threshold: 10, - level: 6, // Standard compression level - }; - // Build the packet with compression enabled - let packet_bytes = build_packet_with_encoder(&packet, Some(compression_info), None); + // Compression threshold is set to a value higher than payload length + let packet_bytes = build_packet_with_encoder( + &packet, + Some((CompressionThreshold(10), CompressionLevel(6))), + None, + ); // Decode the packet manually to verify that it was not compressed let mut buffer = &packet_bytes[..]; diff --git a/pumpkin/src/net/mod.rs b/pumpkin/src/net/mod.rs index 0e5f9e9c..b43cb1cb 100644 --- a/pumpkin/src/net/mod.rs +++ b/pumpkin/src/net/mod.rs @@ -33,7 +33,8 @@ use pumpkin_protocol::{ }, status::{SStatusPingRequest, SStatusRequest}, }, - ClientPacket, ConnectionState, Property, RawPacket, ServerPacket, + ClientPacket, CompressionLevel, CompressionThreshold, ConnectionState, Property, RawPacket, + ServerPacket, }; use serde::Deserialize; use sha1::Digest; @@ -224,7 +225,13 @@ impl Client { /// * `compression`: An optional `CompressionInfo` struct containing the compression threshold and compression level. pub async fn set_compression(&self, compression: Option) { self.dec.lock().await.set_compression(compression.is_some()); - self.enc.lock().await.set_compression(compression); + self.enc + .lock() + .await + .set_compression( + compression.map(|s| (CompressionThreshold(s.threshold), CompressionLevel(s.level))), + ) + .unwrap_or_else(|_| log::warn!("invalid compression level")); } /// Sends a clientbound packet to the connected client. From 29ed502aeaa892ae06497f8dbf941b28bc715b6b Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Thu, 26 Dec 2024 22:10:12 +0100 Subject: [PATCH 36/42] extractor: extract entities --- assets/entities.json | 1 + extractor/gradle.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- extractor/gradlew | 5 +- extractor/gradlew.bat | 2 +- .../kotlin/de/snowii/extractor/Extractor.kt | 2 +- .../snowii/extractor/extractors/Entities.kt | 42 ++++ .../de/snowii/extractor/extractors/Tests.kt | 217 +++++++++--------- 8 files changed, 155 insertions(+), 118 deletions(-) create mode 100644 assets/entities.json create mode 100644 extractor/src/main/kotlin/de/snowii/extractor/extractors/Entities.kt diff --git a/assets/entities.json b/assets/entities.json new file mode 100644 index 00000000..ac4995f5 --- /dev/null +++ b/assets/entities.json @@ -0,0 +1 @@ +{"acacia_boat":{"id":0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"acacia_chest_boat":{"id":1,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"allay":{"id":2,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.35,0.6]},"area_effect_cloud":{"id":3,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[6.0,0.5]},"armadillo":{"id":4,"max_health":12.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.7,0.65]},"armor_stand":{"id":5,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.5,1.975]},"arrow":{"id":6,"attackable":false,"summonable":true,"fire_immune":false,"dimension":[0.5,0.5]},"axolotl":{"id":7,"max_health":14.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.75,0.42]},"bamboo_chest_raft":{"id":8,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"bamboo_raft":{"id":9,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"bat":{"id":10,"max_health":6.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.5,0.9]},"bee":{"id":11,"max_health":10.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.7,0.6]},"birch_boat":{"id":12,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"birch_chest_boat":{"id":13,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"blaze":{"id":14,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[0.6,1.8]},"block_display":{"id":15,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.0,0.0]},"bogged":{"id":16,"max_health":16.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.99]},"breeze":{"id":17,"max_health":30.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.77]},"breeze_wind_charge":{"id":18,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.3125,0.3125]},"camel":{"id":19,"max_health":32.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.7,2.375]},"cat":{"id":20,"max_health":10.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,0.7]},"cave_spider":{"id":21,"max_health":12.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.7,0.5]},"cherry_boat":{"id":22,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"cherry_chest_boat":{"id":23,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"chest_minecart":{"id":24,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.98,0.7]},"chicken":{"id":25,"max_health":4.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.4,0.7]},"cod":{"id":26,"max_health":3.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.5,0.3]},"command_block_minecart":{"id":27,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.98,0.7]},"cow":{"id":28,"max_health":10.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.9,1.4]},"creaking":{"id":29,"max_health":1.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.9,2.7]},"creeper":{"id":30,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.7]},"dark_oak_boat":{"id":31,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"dark_oak_chest_boat":{"id":32,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"dolphin":{"id":33,"max_health":10.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.9,0.6]},"donkey":{"id":34,"max_health":53.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.3964844,1.5]},"dragon_fireball":{"id":35,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.0,1.0]},"drowned":{"id":36,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.95]},"egg":{"id":37,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.25,0.25]},"elder_guardian":{"id":38,"max_health":80.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.9975,1.9975]},"enderman":{"id":39,"max_health":40.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,2.9]},"endermite":{"id":40,"max_health":8.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.4,0.3]},"ender_dragon":{"id":41,"max_health":200.0,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[16.0,8.0]},"ender_pearl":{"id":42,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.25,0.25]},"end_crystal":{"id":43,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[2.0,2.0]},"evoker":{"id":44,"max_health":24.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.95]},"evoker_fangs":{"id":45,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.5,0.8]},"experience_bottle":{"id":46,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.25,0.25]},"experience_orb":{"id":47,"attackable":false,"summonable":true,"fire_immune":false,"dimension":[0.5,0.5]},"eye_of_ender":{"id":48,"attackable":false,"summonable":true,"fire_immune":false,"dimension":[0.25,0.25]},"falling_block":{"id":49,"attackable":false,"summonable":true,"fire_immune":false,"dimension":[0.98,0.98]},"fireball":{"id":50,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.0,1.0]},"firework_rocket":{"id":51,"attackable":false,"summonable":true,"fire_immune":false,"dimension":[0.25,0.25]},"fox":{"id":52,"max_health":10.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,0.7]},"frog":{"id":53,"max_health":10.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.5,0.5]},"furnace_minecart":{"id":54,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.98,0.7]},"ghast":{"id":55,"max_health":10.0,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[4.0,4.0]},"giant":{"id":56,"max_health":100.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[3.6,12.0]},"glow_item_frame":{"id":57,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.5,0.5]},"glow_squid":{"id":58,"max_health":10.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.8,0.8]},"goat":{"id":59,"max_health":10.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.9,1.3]},"guardian":{"id":60,"max_health":30.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.85,0.85]},"hoglin":{"id":61,"max_health":40.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.3964844,1.4]},"hopper_minecart":{"id":62,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.98,0.7]},"horse":{"id":63,"max_health":53.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.3964844,1.6]},"husk":{"id":64,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.95]},"illusioner":{"id":65,"max_health":32.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.95]},"interaction":{"id":66,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.0,0.0]},"iron_golem":{"id":67,"max_health":100.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.4,2.7]},"item":{"id":68,"attackable":false,"summonable":true,"fire_immune":false,"dimension":[0.25,0.25]},"item_display":{"id":69,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.0,0.0]},"item_frame":{"id":70,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.5,0.5]},"jungle_boat":{"id":71,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"jungle_chest_boat":{"id":72,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"leash_knot":{"id":73,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.375,0.5]},"lightning_bolt":{"id":74,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.0,0.0]},"llama":{"id":75,"max_health":53.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.9,1.87]},"llama_spit":{"id":76,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.25,0.25]},"magma_cube":{"id":77,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[0.52,0.52]},"mangrove_boat":{"id":78,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"mangrove_chest_boat":{"id":79,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"marker":{"id":80,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.0,0.0]},"minecart":{"id":81,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.98,0.7]},"mooshroom":{"id":82,"max_health":10.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.9,1.4]},"mule":{"id":83,"max_health":53.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.3964844,1.6]},"oak_boat":{"id":84,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"oak_chest_boat":{"id":85,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"ocelot":{"id":86,"max_health":10.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,0.7]},"ominous_item_spawner":{"id":87,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.25,0.25]},"painting":{"id":88,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.5,0.5]},"pale_oak_boat":{"id":89,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"pale_oak_chest_boat":{"id":90,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"panda":{"id":91,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.3,1.25]},"parrot":{"id":92,"max_health":6.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.5,0.9]},"phantom":{"id":93,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.9,0.5]},"pig":{"id":94,"max_health":10.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.9,0.9]},"piglin":{"id":95,"max_health":16.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.95]},"piglin_brute":{"id":96,"max_health":50.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.95]},"pillager":{"id":97,"max_health":24.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.95]},"polar_bear":{"id":98,"max_health":30.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.4,1.4]},"potion":{"id":99,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.25,0.25]},"pufferfish":{"id":100,"max_health":3.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.7,0.7]},"rabbit":{"id":101,"max_health":3.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.4,0.5]},"ravager":{"id":102,"max_health":100.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.95,2.2]},"salmon":{"id":103,"max_health":3.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.7,0.4]},"sheep":{"id":104,"max_health":8.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.9,1.3]},"shulker":{"id":105,"max_health":30.0,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[1.0,1.0]},"shulker_bullet":{"id":106,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.3125,0.3125]},"silverfish":{"id":107,"max_health":8.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.4,0.3]},"skeleton":{"id":108,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.99]},"skeleton_horse":{"id":109,"max_health":15.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.3964844,1.6]},"slime":{"id":110,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.52,0.52]},"small_fireball":{"id":111,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.3125,0.3125]},"sniffer":{"id":112,"max_health":14.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.9,1.75]},"snowball":{"id":113,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.25,0.25]},"snow_golem":{"id":114,"max_health":4.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.7,1.9]},"spawner_minecart":{"id":115,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.98,0.7]},"spectral_arrow":{"id":116,"attackable":false,"summonable":true,"fire_immune":false,"dimension":[0.5,0.5]},"spider":{"id":117,"max_health":16.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.4,0.9]},"spruce_boat":{"id":118,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"spruce_chest_boat":{"id":119,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.375,0.5625]},"squid":{"id":120,"max_health":10.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.8,0.8]},"stray":{"id":121,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.99]},"strider":{"id":122,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[0.9,1.7]},"tadpole":{"id":123,"max_health":6.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.4,0.3]},"text_display":{"id":124,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.0,0.0]},"tnt":{"id":125,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[0.98,0.98]},"tnt_minecart":{"id":126,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.98,0.7]},"trader_llama":{"id":127,"max_health":53.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.9,1.87]},"trident":{"id":128,"attackable":false,"summonable":true,"fire_immune":false,"dimension":[0.5,0.5]},"tropical_fish":{"id":129,"max_health":3.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.5,0.4]},"turtle":{"id":130,"max_health":30.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.2,0.4]},"vex":{"id":131,"max_health":14.0,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[0.4,0.8]},"villager":{"id":132,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.95]},"vindicator":{"id":133,"max_health":24.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.95]},"wandering_trader":{"id":134,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.95]},"warden":{"id":135,"max_health":500.0,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[0.9,2.9]},"wind_charge":{"id":136,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.3125,0.3125]},"witch":{"id":137,"max_health":26.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.95]},"wither":{"id":138,"max_health":300.0,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[0.9,3.5]},"wither_skeleton":{"id":139,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[0.7,2.4]},"wither_skull":{"id":140,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.3125,0.3125]},"wolf":{"id":141,"max_health":8.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,0.85]},"zoglin":{"id":142,"max_health":40.0,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[1.3964844,1.4]},"zombie":{"id":143,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.95]},"zombie_horse":{"id":144,"max_health":15.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[1.3964844,1.6]},"zombie_villager":{"id":145,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":false,"dimension":[0.6,1.95]},"zombified_piglin":{"id":146,"max_health":20.0,"attackable":true,"summonable":true,"fire_immune":true,"dimension":[0.6,1.95]},"fishing_bobber":{"id":148,"attackable":true,"summonable":false,"fire_immune":false,"dimension":[0.25,0.25]}} \ No newline at end of file diff --git a/extractor/gradle.properties b/extractor/gradle.properties index 5b4def5d..880c9772 100644 --- a/extractor/gradle.properties +++ b/extractor/gradle.properties @@ -11,4 +11,4 @@ kotlin_loader_version=1.13.0+kotlin.2.1.0 mod_version=1.0-SNAPSHOT maven_group=de.snowii archives_base_name=extractor -fabric_version=0.112.1+1.21.4 +fabric_version=0.113.0+1.21.4 diff --git a/extractor/gradle/wrapper/gradle-wrapper.properties b/extractor/gradle/wrapper/gradle-wrapper.properties index e2847c82..cea7a793 100644 --- a/extractor/gradle/wrapper/gradle-wrapper.properties +++ b/extractor/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/extractor/gradlew b/extractor/gradlew index f5feea6d..057afac5 100755 --- a/extractor/gradlew +++ b/extractor/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -203,7 +202,7 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +DEFAULT_JVM_OPTS='-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, diff --git a/extractor/gradlew.bat b/extractor/gradlew.bat index 9b42019c..6a90cee9 100644 --- a/extractor/gradlew.bat +++ b/extractor/gradlew.bat @@ -36,7 +36,7 @@ set APP_HOME=%DIRNAME% for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" +set DEFAULT_JVM_OPTS=-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/extractor/src/main/kotlin/de/snowii/extractor/Extractor.kt b/extractor/src/main/kotlin/de/snowii/extractor/Extractor.kt index d16e89b6..2f47fe63 100644 --- a/extractor/src/main/kotlin/de/snowii/extractor/Extractor.kt +++ b/extractor/src/main/kotlin/de/snowii/extractor/Extractor.kt @@ -1,6 +1,5 @@ package de.snowii.extractor -import com.google.gson.Gson import com.google.gson.GsonBuilder import com.google.gson.JsonElement import de.snowii.extractor.extractors.* @@ -31,6 +30,7 @@ class Extractor : ModInitializer { Packets(), Screens(), Tags(), + Entities(), Items(), Blocks(), Tests(), diff --git a/extractor/src/main/kotlin/de/snowii/extractor/extractors/Entities.kt b/extractor/src/main/kotlin/de/snowii/extractor/extractors/Entities.kt new file mode 100644 index 00000000..47504711 --- /dev/null +++ b/extractor/src/main/kotlin/de/snowii/extractor/extractors/Entities.kt @@ -0,0 +1,42 @@ +package de.snowii.extractor.extractors + +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import de.snowii.extractor.Extractor +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.SpawnReason +import net.minecraft.registry.Registries +import net.minecraft.server.MinecraftServer + +class Entities : Extractor.Extractor { + override fun fileName(): String { + return "entities.json" + } + + override fun extract(server: MinecraftServer): JsonElement { + val entitiesJson = JsonObject() + for (entityType in Registries.ENTITY_TYPE) { + val entity = entityType.create(server.overworld!!, SpawnReason.NATURAL) ?: continue + val entityJson = JsonObject() + entityJson.addProperty("id", Registries.ENTITY_TYPE.getRawId(entityType)) + if (entity is LivingEntity) { + entityJson.addProperty("max_health", entity.maxHealth) + + } + entityJson.addProperty("attackable", entity.isAttackable) + entityJson.addProperty("summonable", entityType.isSummonable) + entityJson.addProperty("fire_immune", entityType.isFireImmune) + val dimension = JsonArray() + dimension.add(entityType.dimensions.width) + dimension.add(entityType.dimensions.height) + entityJson.add("dimension", dimension) + + entitiesJson.add( + Registries.ENTITY_TYPE.getId(entityType).path, entityJson + ) + } + + return entitiesJson + } +} diff --git a/extractor/src/main/kotlin/de/snowii/extractor/extractors/Tests.kt b/extractor/src/main/kotlin/de/snowii/extractor/extractors/Tests.kt index ac81ec76..e75a1797 100644 --- a/extractor/src/main/kotlin/de/snowii/extractor/extractors/Tests.kt +++ b/extractor/src/main/kotlin/de/snowii/extractor/extractors/Tests.kt @@ -1,8 +1,5 @@ - package de.snowii.extractor.extractors -import com.google.gson.Gson -import com.google.gson.GsonBuilder import com.google.gson.JsonArray import com.google.gson.JsonElement import de.snowii.extractor.Extractor @@ -11,129 +8,127 @@ import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.registry.BuiltinRegistries import net.minecraft.registry.RegistryKeys -import net.minecraft.registry.RegistryWrapper -import net.minecraft.registry.RegistryWrapper.WrapperLookup -import net.minecraft.registry.entry.RegistryEntry.Reference import net.minecraft.server.MinecraftServer -import net.minecraft.util.math.noise.DoublePerlinNoiseSampler.NoiseParameters import net.minecraft.util.math.ChunkPos -import net.minecraft.world.gen.chunk.AquiferSampler -import net.minecraft.world.gen.chunk.Blender -import net.minecraft.world.gen.chunk.ChunkGeneratorSettings -import net.minecraft.world.gen.chunk.ChunkNoiseSampler -import net.minecraft.world.gen.chunk.GenerationShapeConfig -import net.minecraft.world.gen.densityfunction.DensityFunction.NoisePos; -import net.minecraft.world.gen.densityfunction.DensityFunction.EachApplier; +import net.minecraft.world.gen.chunk.* +import net.minecraft.world.gen.densityfunction.DensityFunction.EachApplier +import net.minecraft.world.gen.densityfunction.DensityFunction.NoisePos import net.minecraft.world.gen.densityfunction.DensityFunctionTypes import net.minecraft.world.gen.noise.NoiseConfig - -import java.lang.reflect.Method -import java.util.Arrays -import kotlin.reflect.full.createType -import kotlin.reflect.full.declaredFunctions -import kotlin.reflect.jvm.javaMethod import kotlin.reflect.KFunction +import kotlin.reflect.full.declaredFunctions class Tests : Extractor.Extractor { override fun fileName(): String = "chunk.json" - private fun createFluidLevelSampler(settings: ChunkGeneratorSettings): AquiferSampler.FluidLevelSampler { - val fluidLevel = AquiferSampler.FluidLevel(-54, Blocks.LAVA.getDefaultState()); - val i = settings.seaLevel(); - val fluidLevel2 = AquiferSampler.FluidLevel(i, settings.defaultFluid()); - return AquiferSampler.FluidLevelSampler {_, y, _ -> if (y < Math.min(-54, i)) fluidLevel else fluidLevel2}; - } - - private fun get_index(config: GenerationShapeConfig, x: Int, y: Int, z: Int): Int { - if (x < 0 || y < 0 || z < 0) { - System.err.println("Bad local pos"); - System.exit(1); - } - return config.height() * 16 * x + 16 * y + z - } - - // This is basically just what NoiseChunkGenerator is doing - private fun populate_noise(start_x: Int, start_z: Int, sampler: ChunkNoiseSampler, config: GenerationShapeConfig, settings: ChunkGeneratorSettings): IntArray? { - val result = IntArray(16 * 16 * config.height()) - - for (method: KFunction<*> in sampler::class.declaredFunctions) { - if (method.name.equals("sampleBlockState")) { - sampler.sampleStartDensity() - val k = config.horizontalCellBlockCount() - val l = config.verticalCellBlockCount() - - val m = 16 / k - val n = 16 / k + private fun createFluidLevelSampler(settings: ChunkGeneratorSettings): AquiferSampler.FluidLevelSampler { + val fluidLevel = AquiferSampler.FluidLevel(-54, Blocks.LAVA.defaultState) + val i = settings.seaLevel() + val fluidLevel2 = AquiferSampler.FluidLevel(i, settings.defaultFluid()) + return AquiferSampler.FluidLevelSampler { _, y, _ -> if (y < Math.min(-54, i)) fluidLevel else fluidLevel2 } + } - val cellHeight = config.height() / l - val minimumCellY = config.minimumY() / l + private fun get_index(config: GenerationShapeConfig, x: Int, y: Int, z: Int): Int { + if (x < 0 || y < 0 || z < 0) { + System.err.println("Bad local pos") + System.exit(1) + } + return config.height() * 16 * x + 16 * y + z + } - for (o in 0.. in sampler::class.declaredFunctions) { + if (method.name.equals("sampleBlockState")) { + sampler.sampleStartDensity() + val k = config.horizontalCellBlockCount() + val l = config.verticalCellBlockCount() + + val m = 16 / k + val n = 16 / k + + val cellHeight = config.height() / l + val minimumCellY = config.minimumY() / l + + for (o in 0.. + val seed = 0L + val chunk_pos = ChunkPos(7, 4) + + val lookup = BuiltinRegistries.createWrapperLookup() + val wrapper = lookup.getOrThrow(RegistryKeys.CHUNK_GENERATOR_SETTINGS) + val noise_params = lookup.getOrThrow(RegistryKeys.NOISE_PARAMETERS) + + val ref = wrapper.getOrThrow(ChunkGeneratorSettings.OVERWORLD) + val settings = ref.value() + val config = NoiseConfig.create(settings, noise_params, seed) + + // Overworld shape config + val shape = GenerationShapeConfig(-64, 384, 1, 2) + val test_sampler = + ChunkNoiseSampler( + 16 / shape.horizontalCellBlockCount(), config, chunk_pos.startX, chunk_pos.startZ, + shape, object : DensityFunctionTypes.Beardifying { + override fun maxValue(): Double = 0.0 + override fun minValue(): Double = 0.0 + override fun sample(pos: NoisePos): Double = 0.0 + override fun fill(densities: DoubleArray, applier: EachApplier) { + densities.fill(0.0) + } + }, settings, createFluidLevelSampler(settings), Blender.getNoBlending() + ) + + val data = populate_noise(chunk_pos.startX, chunk_pos.startZ, test_sampler, shape, settings) + data?.forEach { state -> topLevelJson.add(state) } From 25f48342366ef2782675283e85eb12781c192223 Mon Sep 17 00:00:00 2001 From: urisinger <60300761+urisinger@users.noreply.github.com> Date: Fri, 27 Dec 2024 00:15:13 +0200 Subject: [PATCH 37/42] Remove unnecessary lifetimes in CommandDispatcher and Command (#419) * make command dispatcher use owned strings and not take any lifetimes * remove get_argument_consumer * fix clippy * fix elided lifetimes --- .../src/client/play/c_command_suggestions.rs | 6 +- pumpkin/src/command/args/arg_block.rs | 14 +-- pumpkin/src/command/args/arg_bool.rs | 6 +- pumpkin/src/command/args/arg_bossbar_color.rs | 16 +-- pumpkin/src/command/args/arg_bossbar_style.rs | 16 +-- pumpkin/src/command/args/arg_bounded_num.rs | 12 +- pumpkin/src/command/args/arg_command.rs | 20 ++-- pumpkin/src/command/args/arg_entities.rs | 14 +-- pumpkin/src/command/args/arg_entity.rs | 14 +-- pumpkin/src/command/args/arg_gamemode.rs | 14 +-- pumpkin/src/command/args/arg_item.rs | 14 +-- pumpkin/src/command/args/arg_message.rs | 14 +-- pumpkin/src/command/args/arg_players.rs | 14 +-- pumpkin/src/command/args/arg_position_2d.rs | 14 +-- pumpkin/src/command/args/arg_position_3d.rs | 14 +-- .../src/command/args/arg_position_block.rs | 14 +-- .../src/command/args/arg_resource_location.rs | 14 +-- pumpkin/src/command/args/arg_rotation.rs | 14 +-- pumpkin/src/command/args/arg_simple.rs | 6 +- pumpkin/src/command/args/mod.rs | 19 ++- pumpkin/src/command/client_cmd_suggestions.rs | 9 +- pumpkin/src/command/commands/cmd_bossbar.rs | 89 +++++++------- pumpkin/src/command/commands/cmd_clear.rs | 6 +- pumpkin/src/command/commands/cmd_fill.rs | 28 ++--- pumpkin/src/command/commands/cmd_gamemode.rs | 10 +- pumpkin/src/command/commands/cmd_give.rs | 24 ++-- pumpkin/src/command/commands/cmd_help.rs | 23 ++-- pumpkin/src/command/commands/cmd_kick.rs | 4 +- pumpkin/src/command/commands/cmd_kill.rs | 6 +- pumpkin/src/command/commands/cmd_list.rs | 4 +- pumpkin/src/command/commands/cmd_pumpkin.rs | 4 +- pumpkin/src/command/commands/cmd_say.rs | 6 +- pumpkin/src/command/commands/cmd_seed.rs | 6 +- pumpkin/src/command/commands/cmd_setblock.rs | 22 ++-- pumpkin/src/command/commands/cmd_stop.rs | 4 +- pumpkin/src/command/commands/cmd_teleport.rs | 30 ++--- pumpkin/src/command/commands/cmd_time.rs | 28 ++--- pumpkin/src/command/commands/cmd_transfer.rs | 30 ++--- .../src/command/commands/cmd_worldborder.rs | 111 +++++++++--------- pumpkin/src/command/dispatcher.rs | 46 ++++---- pumpkin/src/command/mod.rs | 2 +- pumpkin/src/command/tree.rs | 42 +++---- pumpkin/src/command/tree_builder.rs | 90 ++++++++------ pumpkin/src/command/tree_format.rs | 10 +- pumpkin/src/server/mod.rs | 2 +- 45 files changed, 429 insertions(+), 476 deletions(-) diff --git a/pumpkin-protocol/src/client/play/c_command_suggestions.rs b/pumpkin-protocol/src/client/play/c_command_suggestions.rs index 25ae6683..c137b9ad 100644 --- a/pumpkin-protocol/src/client/play/c_command_suggestions.rs +++ b/pumpkin-protocol/src/client/play/c_command_suggestions.rs @@ -35,7 +35,7 @@ impl ClientPacket for CCommandSuggestions<'_> { bytebuf.put_var_int(&self.length); bytebuf.put_list(&self.matches, |bytebuf, suggestion| { - bytebuf.put_string(suggestion.suggestion); + bytebuf.put_string(&suggestion.suggestion); bytebuf.put_bool(suggestion.tooltip.is_some()); if let Some(tooltip) = &suggestion.tooltip { bytebuf.put_slice(&tooltip.encode()); @@ -46,12 +46,12 @@ impl ClientPacket for CCommandSuggestions<'_> { #[derive(PartialEq, Eq, Hash, Debug)] pub struct CommandSuggestion<'a> { - pub suggestion: &'a str, + pub suggestion: String, pub tooltip: Option>, } impl<'a> CommandSuggestion<'a> { - pub fn new(suggestion: &'a str, tooltip: Option>) -> Self { + pub fn new(suggestion: String, tooltip: Option>) -> Self { Self { suggestion, tooltip, diff --git a/pumpkin/src/command/args/arg_block.rs b/pumpkin/src/command/args/arg_block.rs index fe11228f..77962552 100644 --- a/pumpkin/src/command/args/arg_block.rs +++ b/pumpkin/src/command/args/arg_block.rs @@ -31,7 +31,7 @@ impl GetClientSideArgParser for BlockArgumentConsumer { #[async_trait] impl ArgumentConsumer for BlockArgumentConsumer { async fn consume<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -41,7 +41,7 @@ impl ArgumentConsumer for BlockArgumentConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -51,19 +51,15 @@ impl ArgumentConsumer for BlockArgumentConsumer { } impl DefaultNameArgConsumer for BlockArgumentConsumer { - fn default_name(&self) -> &'static str { - "block" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - self + fn default_name(&self) -> String { + "block".to_string() } } impl<'a> FindArg<'a> for BlockArgumentConsumer { type Data = &'a Block; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::Block(name)) => block_registry::get_block(name).map_or_else( || { diff --git a/pumpkin/src/command/args/arg_bool.rs b/pumpkin/src/command/args/arg_bool.rs index fc47f590..39b69c89 100644 --- a/pumpkin/src/command/args/arg_bool.rs +++ b/pumpkin/src/command/args/arg_bool.rs @@ -23,7 +23,7 @@ impl GetClientSideArgParser for BoolArgConsumer { #[async_trait] impl ArgumentConsumer for BoolArgConsumer { async fn consume<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -38,7 +38,7 @@ impl ArgumentConsumer for BoolArgConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -50,7 +50,7 @@ impl ArgumentConsumer for BoolArgConsumer { impl<'a> FindArg<'a> for BoolArgConsumer { type Data = bool; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::Bool(data)) => Ok(*data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_bossbar_color.rs b/pumpkin/src/command/args/arg_bossbar_color.rs index 94df5a4c..664264ea 100644 --- a/pumpkin/src/command/args/arg_bossbar_color.rs +++ b/pumpkin/src/command/args/arg_bossbar_color.rs @@ -27,7 +27,7 @@ impl GetClientSideArgParser for BossbarColorArgumentConsumer { #[async_trait] impl ArgumentConsumer for BossbarColorArgumentConsumer { async fn consume<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -49,7 +49,7 @@ impl ArgumentConsumer for BossbarColorArgumentConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -57,26 +57,22 @@ impl ArgumentConsumer for BossbarColorArgumentConsumer { let colors = ["blue", "green", "pink", "purple", "red", "white", "yellow"]; let suggestions: Vec = colors .iter() - .map(|color| CommandSuggestion::new(color, None)) + .map(|color| CommandSuggestion::new((*color).to_string(), None)) .collect(); Ok(Some(suggestions)) } } impl DefaultNameArgConsumer for BossbarColorArgumentConsumer { - fn default_name(&self) -> &'static str { - "color" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - self + fn default_name(&self) -> String { + "color".to_string() } } impl<'a> FindArg<'a> for BossbarColorArgumentConsumer { type Data = &'a BossbarColor; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::BossbarColor(data)) => Ok(data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_bossbar_style.rs b/pumpkin/src/command/args/arg_bossbar_style.rs index 6111688b..6d3b2d44 100644 --- a/pumpkin/src/command/args/arg_bossbar_style.rs +++ b/pumpkin/src/command/args/arg_bossbar_style.rs @@ -27,7 +27,7 @@ impl GetClientSideArgParser for BossbarStyleArgumentConsumer { #[async_trait] impl ArgumentConsumer for BossbarStyleArgumentConsumer { async fn consume<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -47,7 +47,7 @@ impl ArgumentConsumer for BossbarStyleArgumentConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -61,26 +61,22 @@ impl ArgumentConsumer for BossbarStyleArgumentConsumer { ]; let suggestions: Vec = styles .iter() - .map(|style| CommandSuggestion::new(style, None)) + .map(|style| CommandSuggestion::new((*style).to_string(), None)) .collect(); Ok(Some(suggestions)) } } impl DefaultNameArgConsumer for BossbarStyleArgumentConsumer { - fn default_name(&self) -> &'static str { - "style" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - self + fn default_name(&self) -> String { + "style".to_string() } } impl<'a> FindArg<'a> for BossbarStyleArgumentConsumer { type Data = &'a BossbarDivisions; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::BossbarStyle(data)) => Ok(data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_bounded_num.rs b/pumpkin/src/command/args/arg_bounded_num.rs index 41410367..0cd20af2 100644 --- a/pumpkin/src/command/args/arg_bounded_num.rs +++ b/pumpkin/src/command/args/arg_bounded_num.rs @@ -25,7 +25,7 @@ where Self: GetClientSideArgParser, { async fn consume<'a>( - &self, + &'a self, _src: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -48,7 +48,7 @@ where } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -238,12 +238,8 @@ impl DefaultNameArgConsumer for BoundedNumArgumentConsumer where Self: ArgumentConsumer, { - fn default_name(&self) -> &'static str { + fn default_name(&self) -> String { // setting a single default name for all BoundedNumArgumentConsumer variants is probably a bad idea since it would lead to confusion - self.name.expect("Only use *_default variants of methods with a BoundedNumArgumentConsumer that has a name.") - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - self + self.name.expect("Only use *_default variants of methods with a BoundedNumArgumentConsumer that has a name.").to_string() } } diff --git a/pumpkin/src/command/args/arg_command.rs b/pumpkin/src/command/args/arg_command.rs index 9055fde1..a8ea8234 100644 --- a/pumpkin/src/command/args/arg_command.rs +++ b/pumpkin/src/command/args/arg_command.rs @@ -30,7 +30,7 @@ impl GetClientSideArgParser for CommandTreeArgumentConsumer { #[async_trait] impl ArgumentConsumer for CommandTreeArgumentConsumer { async fn consume<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, server: &'a Server, args: &mut RawArgs<'a>, @@ -40,11 +40,11 @@ impl ArgumentConsumer for CommandTreeArgumentConsumer { 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.clone()))) } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, server: &'a Server, input: &'a str, @@ -58,26 +58,22 @@ impl ArgumentConsumer for CommandTreeArgumentConsumer { .commands .keys() .filter(|suggestion| suggestion.starts_with(input)) - .map(|suggestion| CommandSuggestion::new(suggestion, None)) + .map(|suggestion| CommandSuggestion::new(suggestion.to_string(), None)) .collect(); Ok(Some(suggestions)) } } impl DefaultNameArgConsumer for CommandTreeArgumentConsumer { - fn default_name(&self) -> &'static str { - "cmd" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &Self + fn default_name(&self) -> String { + "cmd".to_string() } } impl<'a> FindArg<'a> for CommandTreeArgumentConsumer { - type Data = &'a CommandTree<'a>; + type Data = &'a CommandTree; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::CommandTree(tree)) => Ok(tree), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_entities.rs b/pumpkin/src/command/args/arg_entities.rs index 102a177b..0c202ac7 100644 --- a/pumpkin/src/command/args/arg_entities.rs +++ b/pumpkin/src/command/args/arg_entities.rs @@ -34,7 +34,7 @@ impl GetClientSideArgParser for EntitiesArgumentConsumer { #[async_trait] impl ArgumentConsumer for EntitiesArgumentConsumer { async fn consume<'a>( - &self, + &'a self, src: &CommandSender<'a>, server: &'a Server, args: &mut RawArgs<'a>, @@ -47,7 +47,7 @@ impl ArgumentConsumer for EntitiesArgumentConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -57,19 +57,15 @@ impl ArgumentConsumer for EntitiesArgumentConsumer { } impl DefaultNameArgConsumer for EntitiesArgumentConsumer { - fn default_name(&self) -> &'static str { - "targets" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &Self + fn default_name(&self) -> String { + "targets".to_string() } } impl<'a> FindArg<'a> for EntitiesArgumentConsumer { type Data = &'a [Arc]; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::Entities(data)) => Ok(data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_entity.rs b/pumpkin/src/command/args/arg_entity.rs index 37e4dac0..bbbed190 100644 --- a/pumpkin/src/command/args/arg_entity.rs +++ b/pumpkin/src/command/args/arg_entity.rs @@ -37,7 +37,7 @@ impl GetClientSideArgParser for EntityArgumentConsumer { #[async_trait] impl ArgumentConsumer for EntityArgumentConsumer { async fn consume<'a>( - &self, + &'a self, src: &CommandSender<'a>, server: &'a Server, args: &mut RawArgs<'a>, @@ -69,7 +69,7 @@ impl ArgumentConsumer for EntityArgumentConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -79,19 +79,15 @@ impl ArgumentConsumer for EntityArgumentConsumer { } impl DefaultNameArgConsumer for EntityArgumentConsumer { - fn default_name(&self) -> &'static str { - "target" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &Self + fn default_name(&self) -> String { + "target".to_string() } } impl<'a> FindArg<'a> for EntityArgumentConsumer { type Data = Arc; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::Entity(data)) => Ok(data.clone()), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_gamemode.rs b/pumpkin/src/command/args/arg_gamemode.rs index d520b25c..fee1264d 100644 --- a/pumpkin/src/command/args/arg_gamemode.rs +++ b/pumpkin/src/command/args/arg_gamemode.rs @@ -29,7 +29,7 @@ impl GetClientSideArgParser for GamemodeArgumentConsumer { #[async_trait] impl ArgumentConsumer for GamemodeArgumentConsumer { async fn consume<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -50,7 +50,7 @@ impl ArgumentConsumer for GamemodeArgumentConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -60,19 +60,15 @@ impl ArgumentConsumer for GamemodeArgumentConsumer { } impl DefaultNameArgConsumer for GamemodeArgumentConsumer { - fn default_name(&self) -> &'static str { - "gamemode" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &Self + fn default_name(&self) -> String { + "gamemode".to_string() } } impl<'a> FindArg<'a> for GamemodeArgumentConsumer { type Data = GameMode; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::GameMode(data)) => Ok(*data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_item.rs b/pumpkin/src/command/args/arg_item.rs index 481feb7d..bf4254b9 100644 --- a/pumpkin/src/command/args/arg_item.rs +++ b/pumpkin/src/command/args/arg_item.rs @@ -29,7 +29,7 @@ impl GetClientSideArgParser for ItemArgumentConsumer { #[async_trait] impl ArgumentConsumer for ItemArgumentConsumer { async fn consume<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -39,7 +39,7 @@ impl ArgumentConsumer for ItemArgumentConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -49,19 +49,15 @@ impl ArgumentConsumer for ItemArgumentConsumer { } impl DefaultNameArgConsumer for ItemArgumentConsumer { - fn default_name(&self) -> &'static str { - "item" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - self + fn default_name(&self) -> String { + "item".to_string() } } impl<'a> FindArg<'a> for ItemArgumentConsumer { type Data = (&'a str, &'a Item); - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::Item(name)) => item_registry::get_item(name).map_or_else( || { diff --git a/pumpkin/src/command/args/arg_message.rs b/pumpkin/src/command/args/arg_message.rs index 00af2d87..8d4081d6 100644 --- a/pumpkin/src/command/args/arg_message.rs +++ b/pumpkin/src/command/args/arg_message.rs @@ -29,7 +29,7 @@ impl GetClientSideArgParser for MsgArgConsumer { #[async_trait] impl ArgumentConsumer for MsgArgConsumer { async fn consume<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -45,7 +45,7 @@ impl ArgumentConsumer for MsgArgConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -55,19 +55,15 @@ impl ArgumentConsumer for MsgArgConsumer { } impl DefaultNameArgConsumer for MsgArgConsumer { - fn default_name(&self) -> &'static str { - "msg" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &Self + fn default_name(&self) -> String { + "msg".to_string() } } impl<'a> FindArg<'a> for MsgArgConsumer { type Data = &'a str; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::Msg(data)) => Ok(data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_players.rs b/pumpkin/src/command/args/arg_players.rs index 0e8876a7..c7fdc70a 100644 --- a/pumpkin/src/command/args/arg_players.rs +++ b/pumpkin/src/command/args/arg_players.rs @@ -33,7 +33,7 @@ impl GetClientSideArgParser for PlayersArgumentConsumer { #[async_trait] impl ArgumentConsumer for PlayersArgumentConsumer { async fn consume<'a>( - &self, + &'a self, src: &CommandSender<'a>, server: &'a Server, args: &mut RawArgs<'a>, @@ -63,7 +63,7 @@ impl ArgumentConsumer for PlayersArgumentConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -73,19 +73,15 @@ impl ArgumentConsumer for PlayersArgumentConsumer { } impl DefaultNameArgConsumer for PlayersArgumentConsumer { - fn default_name(&self) -> &'static str { - "player" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &Self + fn default_name(&self) -> String { + "player".to_string() } } impl<'a> FindArg<'a> for PlayersArgumentConsumer { type Data = &'a [Arc]; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::Players(data)) => Ok(data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_position_2d.rs b/pumpkin/src/command/args/arg_position_2d.rs index 107ea432..9a9c5b56 100644 --- a/pumpkin/src/command/args/arg_position_2d.rs +++ b/pumpkin/src/command/args/arg_position_2d.rs @@ -32,7 +32,7 @@ impl GetClientSideArgParser for Position2DArgumentConsumer { #[async_trait] impl ArgumentConsumer for Position2DArgumentConsumer { async fn consume<'a>( - &self, + &'a self, src: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -45,7 +45,7 @@ impl ArgumentConsumer for Position2DArgumentConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -73,19 +73,15 @@ impl MaybeRelativePosition2D { } impl DefaultNameArgConsumer for Position2DArgumentConsumer { - fn default_name(&self) -> &'static str { - "pos2d" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &Self + fn default_name(&self) -> String { + "pos2d".to_string() } } impl<'a> FindArg<'a> for Position2DArgumentConsumer { type Data = Vector2; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::Pos2D(data)) => Ok(*data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_position_3d.rs b/pumpkin/src/command/args/arg_position_3d.rs index 1e58ad81..8df9761c 100644 --- a/pumpkin/src/command/args/arg_position_3d.rs +++ b/pumpkin/src/command/args/arg_position_3d.rs @@ -29,7 +29,7 @@ impl GetClientSideArgParser for Position3DArgumentConsumer { #[async_trait] impl ArgumentConsumer for Position3DArgumentConsumer { async fn consume<'a>( - &self, + &'a self, src: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -42,7 +42,7 @@ impl ArgumentConsumer for Position3DArgumentConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -76,19 +76,15 @@ impl MaybeRelativePosition3D { } impl DefaultNameArgConsumer for Position3DArgumentConsumer { - fn default_name(&self) -> &'static str { - "pos" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &Self + fn default_name(&self) -> String { + "pos".to_string() } } impl<'a> FindArg<'a> for Position3DArgumentConsumer { type Data = Vector3; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::Pos3D(data)) => Ok(*data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_position_block.rs b/pumpkin/src/command/args/arg_position_block.rs index 861eec12..4f5b3a40 100644 --- a/pumpkin/src/command/args/arg_position_block.rs +++ b/pumpkin/src/command/args/arg_position_block.rs @@ -30,7 +30,7 @@ impl GetClientSideArgParser for BlockPosArgumentConsumer { #[async_trait] impl ArgumentConsumer for BlockPosArgumentConsumer { async fn consume<'a>( - &self, + &'a self, src: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -43,7 +43,7 @@ impl ArgumentConsumer for BlockPosArgumentConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -77,19 +77,15 @@ impl MaybeRelativeBlockPos { } impl DefaultNameArgConsumer for BlockPosArgumentConsumer { - fn default_name(&self) -> &'static str { - "block_pos" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &Self + fn default_name(&self) -> String { + "block_pos".to_string() } } impl<'a> FindArg<'a> for BlockPosArgumentConsumer { type Data = WorldPosition; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::BlockPos(data)) => Ok(*data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_resource_location.rs b/pumpkin/src/command/args/arg_resource_location.rs index 02781593..c156a57f 100644 --- a/pumpkin/src/command/args/arg_resource_location.rs +++ b/pumpkin/src/command/args/arg_resource_location.rs @@ -27,7 +27,7 @@ impl GetClientSideArgParser for ResourceLocationArgumentConsumer { #[async_trait] impl ArgumentConsumer for ResourceLocationArgumentConsumer { async fn consume<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -36,7 +36,7 @@ impl ArgumentConsumer for ResourceLocationArgumentConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -60,19 +60,15 @@ impl ArgumentConsumer for ResourceLocationArgumentConsumer { } impl DefaultNameArgConsumer for ResourceLocationArgumentConsumer { - fn default_name(&self) -> &'static str { - "id" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - self + fn default_name(&self) -> String { + "id".to_string() } } impl<'a> FindArg<'a> for ResourceLocationArgumentConsumer { type Data = &'a str; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::ResourceLocation(data)) => Ok(data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_rotation.rs b/pumpkin/src/command/args/arg_rotation.rs index 6e53f2f9..91b3363b 100644 --- a/pumpkin/src/command/args/arg_rotation.rs +++ b/pumpkin/src/command/args/arg_rotation.rs @@ -27,7 +27,7 @@ impl GetClientSideArgParser for RotationArgumentConsumer { #[async_trait] impl ArgumentConsumer for RotationArgumentConsumer { async fn consume<'a>( - &self, + &'a self, _src: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -51,7 +51,7 @@ impl ArgumentConsumer for RotationArgumentConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -61,19 +61,15 @@ impl ArgumentConsumer for RotationArgumentConsumer { } impl DefaultNameArgConsumer for RotationArgumentConsumer { - fn default_name(&self) -> &'static str { - "rotation" - } - - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { - &Self + fn default_name(&self) -> String { + "rotation".to_string() } } impl<'a> FindArg<'a> for RotationArgumentConsumer { type Data = (f32, f32); - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::Rotation(yaw, pitch)) => Ok((*yaw, *pitch)), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/arg_simple.rs b/pumpkin/src/command/args/arg_simple.rs index 1412d63e..67207fb8 100644 --- a/pumpkin/src/command/args/arg_simple.rs +++ b/pumpkin/src/command/args/arg_simple.rs @@ -30,7 +30,7 @@ impl GetClientSideArgParser for SimpleArgConsumer { #[async_trait] impl ArgumentConsumer for SimpleArgConsumer { async fn consume<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, @@ -39,7 +39,7 @@ impl ArgumentConsumer for SimpleArgConsumer { } async fn suggest<'a>( - &self, + &'a self, _sender: &CommandSender<'a>, _server: &'a Server, _input: &'a str, @@ -51,7 +51,7 @@ impl ArgumentConsumer for SimpleArgConsumer { impl<'a> FindArg<'a> for SimpleArgConsumer { type Data = &'a str; - fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::Simple(data)) => Ok(data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/mod.rs b/pumpkin/src/command/args/mod.rs index 8342386b..f1fa8f85 100644 --- a/pumpkin/src/command/args/mod.rs +++ b/pumpkin/src/command/args/mod.rs @@ -42,21 +42,21 @@ mod coordinate; #[async_trait] pub(crate) trait ArgumentConsumer: Sync + GetClientSideArgParser { async fn consume<'a>( - &self, + &'a self, sender: &CommandSender<'a>, server: &'a Server, args: &mut RawArgs<'a>, - ) -> Option>; + ) -> Option; /// Used for tab completion (but only if argument suggestion type is "minecraft:ask_server"!). /// /// NOTE: This is called after this consumer's [`ArgumentConsumer::consume`] method returned None, so if args is used here, make sure [`ArgumentConsumer::consume`] never returns None after mutating args. async fn suggest<'a>( - &self, + &'a self, sender: &CommandSender<'a>, server: &'a Server, input: &'a str, - ) -> Result>>, CommandError>; + ) -> Result>, CommandError>; } pub(crate) trait GetClientSideArgParser { @@ -67,10 +67,7 @@ pub(crate) trait GetClientSideArgParser { } pub(crate) trait DefaultNameArgConsumer: ArgumentConsumer { - fn default_name(&self) -> &'static str; - - /// needed because trait upcasting is not stable - fn get_argument_consumer(&self) -> &dyn ArgumentConsumer; + fn default_name(&self) -> String; } #[derive(Clone)] @@ -83,7 +80,7 @@ pub(crate) enum Arg<'a> { Pos2D(Vector2), Rotation(f32, f32), GameMode(GameMode), - CommandTree(CommandTree<'a>), + CommandTree(CommandTree), Item(&'a str), ResourceLocation(&'a str), Block(&'a str), @@ -112,7 +109,7 @@ impl GetCloned for HashMap { pub(crate) trait FindArg<'a> { type Data; - fn find_arg(args: &'a ConsumedArgs, name: &'a str) -> Result; + fn find_arg(args: &'a ConsumedArgs, name: &str) -> Result; } pub(crate) trait FindArgDefaultName<'a, T> { @@ -121,7 +118,7 @@ pub(crate) trait FindArgDefaultName<'a, T> { impl<'a, T, C: FindArg<'a, Data = T> + DefaultNameArgConsumer> FindArgDefaultName<'a, T> for C { fn find_arg_default_name(&self, args: &'a ConsumedArgs) -> Result { - C::find_arg(args, self.default_name()) + C::find_arg(args, &self.default_name()) } } diff --git a/pumpkin/src/command/client_cmd_suggestions.rs b/pumpkin/src/command/client_cmd_suggestions.rs index 7c8c7b2f..707dfcf5 100644 --- a/pumpkin/src/command/client_cmd_suggestions.rs +++ b/pumpkin/src/command/client_cmd_suggestions.rs @@ -10,10 +10,7 @@ use super::{ tree::{Node, NodeType}, }; -pub async fn send_c_commands_packet<'a>( - player: &Arc, - dispatcher: &RwLock>, -) { +pub async fn send_c_commands_packet(player: &Arc, dispatcher: &RwLock) { let cmd_src = super::CommandSender::Player(player.clone()); let mut first_level = Vec::new(); @@ -74,7 +71,7 @@ impl<'a> ProtoNodeBuilder<'a> { fn nodes_to_proto_node_builders<'a>( cmd_src: &super::CommandSender, - nodes: &[Node<'a>], + nodes: &'a [Node], children: &[usize], ) -> (bool, Vec>) { let mut child_nodes = Vec::new(); @@ -82,7 +79,7 @@ fn nodes_to_proto_node_builders<'a>( for i in children { let node = &nodes[*i]; - match node.node_type { + match &node.node_type { NodeType::Argument { name, consumer } => { let (node_is_executable, node_children) = nodes_to_proto_node_builders(cmd_src, nodes, &node.children); diff --git a/pumpkin/src/command/commands/cmd_bossbar.rs b/pumpkin/src/command/commands/cmd_bossbar.rs index 9caaea21..25e449a7 100644 --- a/pumpkin/src/command/commands/cmd_bossbar.rs +++ b/pumpkin/src/command/commands/cmd_bossbar.rs @@ -28,10 +28,12 @@ const ARG_NAME: &str = "name"; const ARG_VISIBLE: &str = "visible"; -const AUTOCOMPLETE_CONSUMER: ResourceLocationArgumentConsumer = - ResourceLocationArgumentConsumer::new(true); -const NON_AUTOCOMPLETE_CONSUMER: ResourceLocationArgumentConsumer = - ResourceLocationArgumentConsumer::new(false); +const fn autocomplete_consumer() -> ResourceLocationArgumentConsumer { + ResourceLocationArgumentConsumer::new(true) +} +const fn non_autocomplete_consumer() -> ResourceLocationArgumentConsumer { + ResourceLocationArgumentConsumer::new(false) +} enum CommandValueGet { Max, @@ -61,7 +63,7 @@ impl CommandExecutor for BossbarAddExecuter { server: &Server, args: &ConsumedArgs<'a>, ) -> Result<(), CommandError> { - let namespace = NON_AUTOCOMPLETE_CONSUMER.find_arg_default_name(args)?; + let namespace = non_autocomplete_consumer().find_arg_default_name(args)?; let Some(Arg::Simple(name)) = args.get(ARG_NAME) else { return Err(InvalidConsumption(Some(ARG_NAME.into()))); }; @@ -96,7 +98,7 @@ impl CommandExecutor for BossbarGetExecuter { server: &Server, args: &ConsumedArgs<'a>, ) -> Result<(), CommandError> { - let namespace = AUTOCOMPLETE_CONSUMER.find_arg_default_name(args)?; + let namespace = autocomplete_consumer().find_arg_default_name(args)?; let Some(bossbar) = server.bossbars.lock().await.get_bossbar(namespace) else { send_error_message( @@ -197,7 +199,7 @@ impl CommandExecutor for BossbarRemoveExecuter { server: &Server, args: &ConsumedArgs<'a>, ) -> Result<(), CommandError> { - let namespace = AUTOCOMPLETE_CONSUMER.find_arg_default_name(args)?; + let namespace = autocomplete_consumer().find_arg_default_name(args)?; if !server.bossbars.lock().await.has_bossbar(namespace) { send_error_message( @@ -237,7 +239,7 @@ impl CommandExecutor for BossbarSetExecuter { server: &Server, args: &ConsumedArgs<'a>, ) -> Result<(), CommandError> { - let namespace = AUTOCOMPLETE_CONSUMER.find_arg_default_name(args)?; + let namespace = autocomplete_consumer().find_arg_default_name(args)?; let Some(bossbar) = server.bossbars.lock().await.get_bossbar(namespace) else { handle_bossbar_error( @@ -275,10 +277,10 @@ impl CommandExecutor for BossbarSetExecuter { Ok(()) } CommandValueSet::Max => { - let Ok(max_value) = MAX_VALUE_CONSUMER.find_arg_default_name(args)? else { + let Ok(max_value) = max_value_consumer().find_arg_default_name(args)? else { send_error_message( sender, - format!("{} is out of bounds.", MAX_VALUE_CONSUMER.default_name()), + format!("{} is out of bounds.", max_value_consumer().default_name()), ) .await; return Ok(()); @@ -423,10 +425,10 @@ impl CommandExecutor for BossbarSetExecuter { Ok(()) } CommandValueSet::Value => { - let Ok(value) = VALUE_CONSUMER.find_arg_default_name(args)? else { + let Ok(value) = value_consumer().find_arg_default_name(args)? else { send_error_message( sender, - format!("{} is out of bounds.", VALUE_CONSUMER.default_name()), + format!("{} is out of bounds.", value_consumer().default_name()), ) .await; return Ok(()); @@ -488,85 +490,86 @@ impl CommandExecutor for BossbarSetExecuter { } } -static MAX_VALUE_CONSUMER: BoundedNumArgumentConsumer = - BoundedNumArgumentConsumer::new().min(0).name("max"); +fn max_value_consumer() -> BoundedNumArgumentConsumer { + BoundedNumArgumentConsumer::new().min(0).name("max") +} -static VALUE_CONSUMER: BoundedNumArgumentConsumer = - BoundedNumArgumentConsumer::new().min(0).name("value"); +fn value_consumer() -> BoundedNumArgumentConsumer { + BoundedNumArgumentConsumer::new().min(0).name("value") +} -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) .with_child( literal("add").with_child( - argument_default_name(&NON_AUTOCOMPLETE_CONSUMER).with_child( - argument(ARG_NAME, &SimpleArgConsumer).execute(&BossbarAddExecuter), - ), + argument_default_name(non_autocomplete_consumer()) + .with_child(argument(ARG_NAME, SimpleArgConsumer).execute(BossbarAddExecuter)), ), ) .with_child( literal("get").with_child( - argument_default_name(&AUTOCOMPLETE_CONSUMER) - .with_child(literal("max").execute(&BossbarGetExecuter(CommandValueGet::Max))) + argument_default_name(autocomplete_consumer()) + .with_child(literal("max").execute(BossbarGetExecuter(CommandValueGet::Max))) .with_child( - literal("players").execute(&BossbarGetExecuter(CommandValueGet::Players)), + literal("players").execute(BossbarGetExecuter(CommandValueGet::Players)), ) .with_child( - literal("value").execute(&BossbarGetExecuter(CommandValueGet::Value)), + literal("value").execute(BossbarGetExecuter(CommandValueGet::Value)), ) .with_child( - literal("visible").execute(&BossbarGetExecuter(CommandValueGet::Visible)), + literal("visible").execute(BossbarGetExecuter(CommandValueGet::Visible)), ), ), ) - .with_child(literal("list").execute(&BossbarListExecuter)) + .with_child(literal("list").execute(BossbarListExecuter)) .with_child(literal("remove").with_child( - argument_default_name(&AUTOCOMPLETE_CONSUMER).execute(&BossbarRemoveExecuter), + argument_default_name(autocomplete_consumer()).execute(BossbarRemoveExecuter), )) .with_child( literal("set").with_child( - argument_default_name(&AUTOCOMPLETE_CONSUMER) + argument_default_name(autocomplete_consumer()) .with_child( literal("color").with_child( - argument_default_name(&BossbarColorArgumentConsumer) - .execute(&BossbarSetExecuter(CommandValueSet::Color)), + argument_default_name(BossbarColorArgumentConsumer) + .execute(BossbarSetExecuter(CommandValueSet::Color)), ), ) .with_child( literal("max").with_child( - argument_default_name(&MAX_VALUE_CONSUMER) - .execute(&BossbarSetExecuter(CommandValueSet::Max)), + argument_default_name(max_value_consumer()) + .execute(BossbarSetExecuter(CommandValueSet::Max)), ), ) .with_child( literal("name").with_child( - argument(ARG_NAME, &SimpleArgConsumer) - .execute(&BossbarSetExecuter(CommandValueSet::Name)), + argument(ARG_NAME, SimpleArgConsumer) + .execute(BossbarSetExecuter(CommandValueSet::Name)), ), ) .with_child( literal("players") .with_child( - argument_default_name(&PlayersArgumentConsumer) - .execute(&BossbarSetExecuter(CommandValueSet::Players(true))), + argument_default_name(PlayersArgumentConsumer) + .execute(BossbarSetExecuter(CommandValueSet::Players(true))), ) - .execute(&BossbarSetExecuter(CommandValueSet::Players(false))), + .execute(BossbarSetExecuter(CommandValueSet::Players(false))), ) .with_child( literal("style").with_child( - argument_default_name(&BossbarStyleArgumentConsumer) - .execute(&BossbarSetExecuter(CommandValueSet::Style)), + argument_default_name(BossbarStyleArgumentConsumer) + .execute(BossbarSetExecuter(CommandValueSet::Style)), ), ) .with_child( literal("value").with_child( - argument_default_name(&VALUE_CONSUMER) - .execute(&BossbarSetExecuter(CommandValueSet::Value)), + argument_default_name(value_consumer()) + .execute(BossbarSetExecuter(CommandValueSet::Value)), ), ) .with_child( literal("visible").with_child( - argument(ARG_VISIBLE, &BoolArgConsumer) - .execute(&BossbarSetExecuter(CommandValueSet::Visible)), + argument(ARG_VISIBLE, BoolArgConsumer) + .execute(BossbarSetExecuter(CommandValueSet::Visible)), ), ), ), diff --git a/pumpkin/src/command/commands/cmd_clear.rs b/pumpkin/src/command/commands/cmd_clear.rs index 07379587..b948d0df 100644 --- a/pumpkin/src/command/commands/cmd_clear.rs +++ b/pumpkin/src/command/commands/cmd_clear.rs @@ -107,8 +107,8 @@ impl CommandExecutor for ClearSelfExecutor { } #[allow(clippy::redundant_closure_for_method_calls)] // causes lifetime issues -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .with_child(argument(ARG_TARGET, &EntitiesArgumentConsumer).execute(&ClearExecutor)) - .with_child(require(&|sender| sender.is_player()).execute(&ClearSelfExecutor)) + .with_child(argument(ARG_TARGET, EntitiesArgumentConsumer).execute(ClearExecutor)) + .with_child(require(|sender| sender.is_player()).execute(ClearSelfExecutor)) } diff --git a/pumpkin/src/command/commands/cmd_fill.rs b/pumpkin/src/command/commands/cmd_fill.rs index 1a01fb15..140e8729 100644 --- a/pumpkin/src/command/commands/cmd_fill.rs +++ b/pumpkin/src/command/commands/cmd_fill.rs @@ -154,23 +154,21 @@ impl CommandExecutor for SetblockExecutor { } } -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).with_child( - require(&|sender| { - sender.has_permission_lvl(PermissionLvl::Two) && sender.world().is_some() - }) - .with_child( - argument(ARG_FROM, &BlockPosArgumentConsumer).with_child( - argument(ARG_TO, &BlockPosArgumentConsumer).with_child( - argument(ARG_BLOCK, &BlockArgumentConsumer) - .with_child(literal("destroy").execute(&SetblockExecutor(Mode::Destroy))) - .with_child(literal("hollow").execute(&SetblockExecutor(Mode::Hollow))) - .with_child(literal("keep").execute(&SetblockExecutor(Mode::Keep))) - .with_child(literal("outline").execute(&SetblockExecutor(Mode::Outline))) - .with_child(literal("replace").execute(&SetblockExecutor(Mode::Replace))) - .execute(&SetblockExecutor(Mode::Replace)), + require(|sender| sender.has_permission_lvl(PermissionLvl::Two) && sender.world().is_some()) + .with_child( + argument(ARG_FROM, BlockPosArgumentConsumer).with_child( + argument(ARG_TO, BlockPosArgumentConsumer).with_child( + argument(ARG_BLOCK, BlockArgumentConsumer) + .with_child(literal("destroy").execute(SetblockExecutor(Mode::Destroy))) + .with_child(literal("hollow").execute(SetblockExecutor(Mode::Hollow))) + .with_child(literal("keep").execute(SetblockExecutor(Mode::Keep))) + .with_child(literal("outline").execute(SetblockExecutor(Mode::Outline))) + .with_child(literal("replace").execute(SetblockExecutor(Mode::Replace))) + .execute(SetblockExecutor(Mode::Replace)), + ), ), ), - ), ) } diff --git a/pumpkin/src/command/commands/cmd_gamemode.rs b/pumpkin/src/command/commands/cmd_gamemode.rs index faab9537..2b627862 100644 --- a/pumpkin/src/command/commands/cmd_gamemode.rs +++ b/pumpkin/src/command/commands/cmd_gamemode.rs @@ -107,13 +107,13 @@ impl CommandExecutor for GamemodeTargetPlayer { } #[allow(clippy::redundant_closure_for_method_calls)] -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).with_child( - require(&|sender| sender.has_permission_lvl(PermissionLvl::Two)).with_child( - argument(ARG_GAMEMODE, &GamemodeArgumentConsumer) - .with_child(require(&|sender| sender.is_player()).execute(&GamemodeTargetSelf)) + require(|sender| sender.has_permission_lvl(PermissionLvl::Two)).with_child( + argument(ARG_GAMEMODE, GamemodeArgumentConsumer) + .with_child(require(|sender| sender.is_player()).execute(GamemodeTargetSelf)) .with_child( - argument(ARG_TARGET, &PlayersArgumentConsumer).execute(&GamemodeTargetPlayer), + argument(ARG_TARGET, PlayersArgumentConsumer).execute(GamemodeTargetPlayer), ), ), ) diff --git a/pumpkin/src/command/commands/cmd_give.rs b/pumpkin/src/command/commands/cmd_give.rs index 36edb640..cdc3334b 100644 --- a/pumpkin/src/command/commands/cmd_give.rs +++ b/pumpkin/src/command/commands/cmd_give.rs @@ -17,10 +17,12 @@ const DESCRIPTION: &str = "Give items to player(s)."; const ARG_ITEM: &str = "item"; -static ITEM_COUNT_CONSUMER: BoundedNumArgumentConsumer = BoundedNumArgumentConsumer::new() - .name("count") - .min(0) - .max(6400); +fn item_count_consumer() -> BoundedNumArgumentConsumer { + BoundedNumArgumentConsumer::new() + .name("count") + .min(0) + .max(6400) +} struct GiveExecutor; @@ -36,7 +38,7 @@ impl CommandExecutor for GiveExecutor { let (item_name, item) = ItemArgumentConsumer::find_arg(args, ARG_ITEM)?; - let item_count = match ITEM_COUNT_CONSUMER.find_arg_default_name(args) { + let item_count = match item_count_consumer().find_arg_default_name(args) { Err(_) => 1, Ok(Ok(count)) => count, Ok(Err(())) => { @@ -72,13 +74,13 @@ impl CommandExecutor for GiveExecutor { } } -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).with_child( - require(&|sender| sender.has_permission_lvl(PermissionLvl::Two)).with_child( - argument_default_name(&PlayersArgumentConsumer).with_child( - argument(ARG_ITEM, &ItemArgumentConsumer) - .execute(&GiveExecutor) - .with_child(argument_default_name(&ITEM_COUNT_CONSUMER).execute(&GiveExecutor)), + require(|sender| sender.has_permission_lvl(PermissionLvl::Two)).with_child( + argument_default_name(PlayersArgumentConsumer).with_child( + argument(ARG_ITEM, ItemArgumentConsumer) + .execute(GiveExecutor) + .with_child(argument_default_name(item_count_consumer()).execute(GiveExecutor)), ), ), ) diff --git a/pumpkin/src/command/commands/cmd_help.rs b/pumpkin/src/command/commands/cmd_help.rs index e382febf..c0ab3eb7 100644 --- a/pumpkin/src/command/commands/cmd_help.rs +++ b/pumpkin/src/command/commands/cmd_help.rs @@ -22,8 +22,9 @@ const ARG_COMMAND: &str = "command"; const COMMANDS_PER_PAGE: i32 = 7; -static PAGE_NUMBER_CONSUMER: BoundedNumArgumentConsumer = - BoundedNumArgumentConsumer::new().name("page").min(1); +fn page_number_consumer() -> BoundedNumArgumentConsumer { + BoundedNumArgumentConsumer::new().name("page").min(1) +} struct CommandHelpExecutor; @@ -41,7 +42,7 @@ impl CommandExecutor for CommandHelpExecutor { let command_names = tree.names.join(", /"); let usage = format!("{tree}"); - let description = tree.description; + let description = &tree.description; let header_text = format!(" Help - /{} ", tree.names[0]); @@ -107,7 +108,7 @@ impl CommandExecutor for BaseHelpExecutor { server: &Server, args: &ConsumedArgs<'a>, ) -> Result<(), CommandError> { - let page_number = match PAGE_NUMBER_CONSUMER.find_arg_default_name(args) { + let page_number = match page_number_consumer().find_arg_default_name(args) { Err(_) => 1, Ok(Ok(number)) => number, Ok(Err(())) => { @@ -131,7 +132,7 @@ impl CommandExecutor for BaseHelpExecutor { }) .collect(); - commands.sort_by(|a, b| a.names[0].cmp(b.names[0])); + commands.sort_by(|a, b| a.names[0].cmp(&b.names[0])); let total_pages = (commands.len().to_i32().unwrap() + COMMANDS_PER_PAGE - 1) / COMMANDS_PER_PAGE; @@ -183,7 +184,7 @@ impl CommandExecutor for BaseHelpExecutor { .color_named(NamedColor::Gold) .add_child(TextComponent::text(" - ").color_named(NamedColor::Yellow)) .add_child( - TextComponent::text_string(tree.description.to_owned() + "\n") + TextComponent::text_string(tree.description.clone() + "\n") .color_named(NamedColor::White), ) .add_child(TextComponent::text(" Usage: ").color_named(NamedColor::Yellow)) @@ -220,11 +221,9 @@ impl CommandExecutor for BaseHelpExecutor { } } -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .with_child( - argument(ARG_COMMAND, &CommandTreeArgumentConsumer).execute(&CommandHelpExecutor), - ) - .with_child(argument_default_name(&PAGE_NUMBER_CONSUMER).execute(&BaseHelpExecutor)) - .execute(&BaseHelpExecutor) + .with_child(argument(ARG_COMMAND, CommandTreeArgumentConsumer).execute(CommandHelpExecutor)) + .with_child(argument_default_name(page_number_consumer()).execute(BaseHelpExecutor)) + .execute(BaseHelpExecutor) } diff --git a/pumpkin/src/command/commands/cmd_kick.rs b/pumpkin/src/command/commands/cmd_kick.rs index d0a8eec9..8d77bf3d 100644 --- a/pumpkin/src/command/commands/cmd_kick.rs +++ b/pumpkin/src/command/commands/cmd_kick.rs @@ -49,7 +49,7 @@ impl CommandExecutor for KickExecutor { } } -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .with_child(argument(ARG_TARGET, &PlayersArgumentConsumer).execute(&KickExecutor)) + .with_child(argument(ARG_TARGET, PlayersArgumentConsumer).execute(KickExecutor)) } diff --git a/pumpkin/src/command/commands/cmd_kill.rs b/pumpkin/src/command/commands/cmd_kill.rs index d763054b..2b36b0c4 100644 --- a/pumpkin/src/command/commands/cmd_kill.rs +++ b/pumpkin/src/command/commands/cmd_kill.rs @@ -65,8 +65,8 @@ impl CommandExecutor for KillSelfExecutor { } #[allow(clippy::redundant_closure_for_method_calls)] // causes lifetime issues -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .with_child(argument(ARG_TARGET, &EntitiesArgumentConsumer).execute(&KillExecutor)) - .with_child(require(&|sender| sender.is_player()).execute(&KillSelfExecutor)) + .with_child(argument(ARG_TARGET, EntitiesArgumentConsumer).execute(KillExecutor)) + .with_child(require(|sender| sender.is_player()).execute(KillSelfExecutor)) } diff --git a/pumpkin/src/command/commands/cmd_list.rs b/pumpkin/src/command/commands/cmd_list.rs index 5a81f8f9..bbcc3641 100644 --- a/pumpkin/src/command/commands/cmd_list.rs +++ b/pumpkin/src/command/commands/cmd_list.rs @@ -55,6 +55,6 @@ fn get_player_names(players: Vec>) -> String { names } -pub fn init_command_tree<'a>() -> CommandTree<'a> { - CommandTree::new(NAMES, DESCRIPTION).execute(&ListExecutor) +pub fn init_command_tree() -> CommandTree { + CommandTree::new(NAMES, DESCRIPTION).execute(ListExecutor) } diff --git a/pumpkin/src/command/commands/cmd_pumpkin.rs b/pumpkin/src/command/commands/cmd_pumpkin.rs index 3dad98ec..a76a1be9 100644 --- a/pumpkin/src/command/commands/cmd_pumpkin.rs +++ b/pumpkin/src/command/commands/cmd_pumpkin.rs @@ -92,6 +92,6 @@ impl CommandExecutor for PumpkinExecutor { } } -pub fn init_command_tree<'a>() -> CommandTree<'a> { - CommandTree::new(NAMES, DESCRIPTION).execute(&PumpkinExecutor) +pub fn init_command_tree() -> CommandTree { + CommandTree::new(NAMES, DESCRIPTION).execute(PumpkinExecutor) } diff --git a/pumpkin/src/command/commands/cmd_say.rs b/pumpkin/src/command/commands/cmd_say.rs index 66b74338..fad4b64b 100644 --- a/pumpkin/src/command/commands/cmd_say.rs +++ b/pumpkin/src/command/commands/cmd_say.rs @@ -43,9 +43,9 @@ impl CommandExecutor for SayExecutor { } } -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).with_child( - require(&|sender| sender.has_permission_lvl(PermissionLvl::Two)) - .with_child(argument(ARG_MESSAGE, &MsgArgConsumer).execute(&SayExecutor)), + require(|sender| sender.has_permission_lvl(PermissionLvl::Two)) + .with_child(argument(ARG_MESSAGE, MsgArgConsumer).execute(SayExecutor)), ) } diff --git a/pumpkin/src/command/commands/cmd_seed.rs b/pumpkin/src/command/commands/cmd_seed.rs index fb93acb0..c4ffc375 100644 --- a/pumpkin/src/command/commands/cmd_seed.rs +++ b/pumpkin/src/command/commands/cmd_seed.rs @@ -54,10 +54,10 @@ impl CommandExecutor for PumpkinExecutor { } } -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .with_child(require(&|sender| { + .with_child(require(|sender| { sender.has_permission_lvl(PermissionLvl::Two) })) - .execute(&PumpkinExecutor) + .execute(PumpkinExecutor) } diff --git a/pumpkin/src/command/commands/cmd_setblock.rs b/pumpkin/src/command/commands/cmd_setblock.rs index 0dd6ec9f..8b7cf973 100644 --- a/pumpkin/src/command/commands/cmd_setblock.rs +++ b/pumpkin/src/command/commands/cmd_setblock.rs @@ -79,19 +79,17 @@ impl CommandExecutor for SetblockExecutor { } } -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).with_child( - require(&|sender| { - sender.has_permission_lvl(PermissionLvl::Two) && sender.world().is_some() - }) - .with_child( - argument(ARG_BLOCK_POS, &BlockPosArgumentConsumer).with_child( - argument(ARG_BLOCK, &BlockArgumentConsumer) - .with_child(literal("replace").execute(&SetblockExecutor(Mode::Replace))) - .with_child(literal("destroy").execute(&SetblockExecutor(Mode::Destroy))) - .with_child(literal("keep").execute(&SetblockExecutor(Mode::Keep))) - .execute(&SetblockExecutor(Mode::Replace)), + require(|sender| sender.has_permission_lvl(PermissionLvl::Two) && sender.world().is_some()) + .with_child( + argument(ARG_BLOCK_POS, BlockPosArgumentConsumer).with_child( + argument(ARG_BLOCK, BlockArgumentConsumer) + .with_child(literal("replace").execute(SetblockExecutor(Mode::Replace))) + .with_child(literal("destroy").execute(SetblockExecutor(Mode::Destroy))) + .with_child(literal("keep").execute(SetblockExecutor(Mode::Keep))) + .execute(SetblockExecutor(Mode::Replace)), + ), ), - ), ) } diff --git a/pumpkin/src/command/commands/cmd_stop.rs b/pumpkin/src/command/commands/cmd_stop.rs index de7803b1..9e3ada0b 100644 --- a/pumpkin/src/command/commands/cmd_stop.rs +++ b/pumpkin/src/command/commands/cmd_stop.rs @@ -37,8 +37,8 @@ impl CommandExecutor for StopExecutor { } } -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).with_child( - require(&|sender| sender.has_permission_lvl(PermissionLvl::Four)).execute(&StopExecutor), + require(|sender| sender.has_permission_lvl(PermissionLvl::Four)).execute(StopExecutor), ) } diff --git a/pumpkin/src/command/commands/cmd_teleport.rs b/pumpkin/src/command/commands/cmd_teleport.rs index 3bc4cf08..d9468147 100644 --- a/pumpkin/src/command/commands/cmd_teleport.rs +++ b/pumpkin/src/command/commands/cmd_teleport.rs @@ -237,41 +237,41 @@ impl CommandExecutor for TpSelfToPosExecutor { } } -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).with_child( - require(&|sender| sender.has_permission_lvl(PermissionLvl::Two)) + require(|sender| sender.has_permission_lvl(PermissionLvl::Two)) .with_child( - argument(ARG_LOCATION, &Position3DArgumentConsumer).execute(&TpSelfToPosExecutor), + argument(ARG_LOCATION, Position3DArgumentConsumer).execute(TpSelfToPosExecutor), ) .with_child( - argument(ARG_DESTINATION, &EntityArgumentConsumer).execute(&TpSelfToEntityExecutor), + argument(ARG_DESTINATION, EntityArgumentConsumer).execute(TpSelfToEntityExecutor), ) .with_child( - argument(ARG_TARGETS, &EntitiesArgumentConsumer) + argument(ARG_TARGETS, EntitiesArgumentConsumer) .with_child( - argument(ARG_LOCATION, &Position3DArgumentConsumer) - .execute(&TpEntitiesToPosExecutor) + argument(ARG_LOCATION, Position3DArgumentConsumer) + .execute(TpEntitiesToPosExecutor) .with_child( - argument(ARG_ROTATION, &RotationArgumentConsumer) - .execute(&TpEntitiesToPosWithRotationExecutor), + argument(ARG_ROTATION, RotationArgumentConsumer) + .execute(TpEntitiesToPosWithRotationExecutor), ) .with_child( literal("facing") .with_child( literal("entity").with_child( - argument(ARG_FACING_ENTITY, &EntityArgumentConsumer) - .execute(&TpEntitiesToPosFacingEntityExecutor), + argument(ARG_FACING_ENTITY, EntityArgumentConsumer) + .execute(TpEntitiesToPosFacingEntityExecutor), ), ) .with_child( - argument(ARG_FACING_LOCATION, &Position3DArgumentConsumer) - .execute(&TpEntitiesToPosFacingPosExecutor), + argument(ARG_FACING_LOCATION, Position3DArgumentConsumer) + .execute(TpEntitiesToPosFacingPosExecutor), ), ), ) .with_child( - argument(ARG_DESTINATION, &EntityArgumentConsumer) - .execute(&TpEntitiesToEntityExecutor), + argument(ARG_DESTINATION, EntityArgumentConsumer) + .execute(TpEntitiesToEntityExecutor), ), ), ) diff --git a/pumpkin/src/command/commands/cmd_time.rs b/pumpkin/src/command/commands/cmd_time.rs index ad289401..669a3294 100644 --- a/pumpkin/src/command/commands/cmd_time.rs +++ b/pumpkin/src/command/commands/cmd_time.rs @@ -18,10 +18,12 @@ const NAMES: [&str; 1] = ["time"]; const DESCRIPTION: &str = "Query the world time."; // TODO: This should be either higher or not bounded -static ARG_NUMBER: BoundedNumArgumentConsumer = BoundedNumArgumentConsumer::new() - .name("time") - .min(0) - .max(24000); +fn arg_number() -> BoundedNumArgumentConsumer { + BoundedNumArgumentConsumer::new() + .name("time") + .min(0) + .max(24000) +} #[derive(Clone, Copy)] enum Mode { @@ -83,7 +85,7 @@ impl CommandExecutor for TimeChangeExecutor { server: &crate::server::Server, args: &ConsumedArgs<'a>, ) -> Result<(), CommandError> { - let time_count = match ARG_NUMBER.find_arg_default_name(args) { + let time_count = match arg_number().find_arg_default_name(args) { Err(_) => 1, Ok(Ok(count)) => count, Ok(Err(())) => { @@ -124,22 +126,20 @@ impl CommandExecutor for TimeChangeExecutor { } } -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).with_child( - require(&|sender| sender.has_permission_lvl(PermissionLvl::Two)) + require(|sender| sender.has_permission_lvl(PermissionLvl::Two)) .with_child(literal("add").with_child( - argument_default_name(&ARG_NUMBER).execute(&TimeChangeExecutor(Mode::Add)), + argument_default_name(arg_number()).execute(TimeChangeExecutor(Mode::Add)), )) .with_child( literal("query") - .with_child(literal("daytime").execute(&TimeQueryExecutor(QueryMode::DayTime))) - .with_child( - literal("gametime").execute(&TimeQueryExecutor(QueryMode::GameTime)), - ) - .with_child(literal("day").execute(&TimeQueryExecutor(QueryMode::Day))), + .with_child(literal("daytime").execute(TimeQueryExecutor(QueryMode::DayTime))) + .with_child(literal("gametime").execute(TimeQueryExecutor(QueryMode::GameTime))) + .with_child(literal("day").execute(TimeQueryExecutor(QueryMode::Day))), ) .with_child(literal("set").with_child( - argument_default_name(&ARG_NUMBER).execute(&TimeChangeExecutor(Mode::Set)), + argument_default_name(arg_number()).execute(TimeChangeExecutor(Mode::Set)), )), ) } diff --git a/pumpkin/src/command/commands/cmd_transfer.rs b/pumpkin/src/command/commands/cmd_transfer.rs index 7660b808..e35b0329 100644 --- a/pumpkin/src/command/commands/cmd_transfer.rs +++ b/pumpkin/src/command/commands/cmd_transfer.rs @@ -23,10 +23,12 @@ const ARG_HOSTNAME: &str = "hostname"; const ARG_PLAYERS: &str = "players"; -static PORT_CONSUMER: BoundedNumArgumentConsumer = BoundedNumArgumentConsumer::new() - .name("port") - .min(1) - .max(65535); +fn port_consumer() -> BoundedNumArgumentConsumer { + BoundedNumArgumentConsumer::new() + .name("port") + .min(1) + .max(65535) +} struct TransferTargetSelf; @@ -42,7 +44,7 @@ impl CommandExecutor for TransferTargetSelf { return Err(InvalidConsumption(Some(ARG_HOSTNAME.into()))); }; - let port = match PORT_CONSUMER.find_arg_default_name(args) { + let port = match port_consumer().find_arg_default_name(args) { Err(_) => 25565, Ok(Ok(count)) => count, Ok(Err(())) => { @@ -84,7 +86,7 @@ impl CommandExecutor for TransferTargetPlayer { return Err(InvalidConsumption(Some(ARG_HOSTNAME.into()))); }; - let port = match PORT_CONSUMER.find_arg_default_name(args) { + let port = match port_consumer().find_arg_default_name(args) { Err(_) => 25565, Ok(Ok(count)) => count, Ok(Err(())) => { @@ -117,19 +119,19 @@ impl CommandExecutor for TransferTargetPlayer { } #[allow(clippy::redundant_closure_for_method_calls)] -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).with_child( - require(&|sender| sender.has_permission_lvl(PermissionLvl::Three)).with_child( - argument(ARG_HOSTNAME, &SimpleArgConsumer) - .with_child(require(&|sender| sender.is_player()).execute(&TransferTargetSelf)) + require(|sender| sender.has_permission_lvl(PermissionLvl::Three)).with_child( + argument(ARG_HOSTNAME, SimpleArgConsumer) + .with_child(require(|sender| sender.is_player()).execute(TransferTargetSelf)) .with_child( - argument_default_name(&PORT_CONSUMER) + argument_default_name(port_consumer()) .with_child( - require(&|sender| sender.is_player()).execute(&TransferTargetSelf), + require(|sender| sender.is_player()).execute(TransferTargetSelf), ) .with_child( - argument(ARG_PLAYERS, &PlayersArgumentConsumer) - .execute(&TransferTargetPlayer), + argument(ARG_PLAYERS, PlayersArgumentConsumer) + .execute(TransferTargetPlayer), ), ), ), diff --git a/pumpkin/src/command/commands/cmd_worldborder.rs b/pumpkin/src/command/commands/cmd_worldborder.rs index fd9ae1a2..b52ac6b5 100644 --- a/pumpkin/src/command/commands/cmd_worldborder.rs +++ b/pumpkin/src/command/commands/cmd_worldborder.rs @@ -25,6 +25,28 @@ const NAMES: [&str; 1] = ["worldborder"]; const DESCRIPTION: &str = "Worldborder command."; +fn distance_consumer() -> BoundedNumArgumentConsumer { + BoundedNumArgumentConsumer::new().min(0.0).name("distance") +} + +fn time_consumer() -> BoundedNumArgumentConsumer { + BoundedNumArgumentConsumer::new().min(0).name("time") +} + +fn damage_per_block_consumer() -> BoundedNumArgumentConsumer { + BoundedNumArgumentConsumer::new() + .min(0.0) + .name("damage_per_block") +} + +fn damage_buffer_consumer() -> BoundedNumArgumentConsumer { + BoundedNumArgumentConsumer::new().min(0.0).name("buffer") +} + +fn warning_distance_consumer() -> BoundedNumArgumentConsumer { + BoundedNumArgumentConsumer::new().min(0).name("distance") +} + struct WorldborderGetExecutor; #[async_trait] @@ -67,12 +89,12 @@ impl CommandExecutor for WorldborderSetExecutor { .expect("There should always be atleast one world"); let mut border = world.worldborder.lock().await; - let Ok(distance) = DISTANCE_CONSUMER.find_arg_default_name(args)? else { + let Ok(distance) = distance_consumer().find_arg_default_name(args)? else { sender .send_message( TextComponent::text_string(format!( "{} is out of bounds.", - DISTANCE_CONSUMER.default_name() + distance_consumer().default_name() )) .color(Color::Named(NamedColor::Red)), ) @@ -116,24 +138,24 @@ impl CommandExecutor for WorldborderSetTimeExecutor { .expect("There should always be atleast one world"); let mut border = world.worldborder.lock().await; - let Ok(distance) = DISTANCE_CONSUMER.find_arg_default_name(args)? else { + let Ok(distance) = distance_consumer().find_arg_default_name(args)? else { sender .send_message( TextComponent::text_string(format!( "{} is out of bounds.", - DISTANCE_CONSUMER.default_name() + distance_consumer().default_name() )) .color(Color::Named(NamedColor::Red)), ) .await; return Ok(()); }; - let Ok(time) = TIME_CONSUMER.find_arg_default_name(args)? else { + let Ok(time) = time_consumer().find_arg_default_name(args)? else { sender .send_message( TextComponent::text_string(format!( "{} is out of bounds.", - TIME_CONSUMER.default_name() + time_consumer().default_name() )) .color(Color::Named(NamedColor::Red)), ) @@ -188,12 +210,12 @@ impl CommandExecutor for WorldborderAddExecutor { .expect("There should always be atleast one world"); let mut border = world.worldborder.lock().await; - let Ok(distance) = DISTANCE_CONSUMER.find_arg_default_name(args)? else { + let Ok(distance) = distance_consumer().find_arg_default_name(args)? else { sender .send_message( TextComponent::text_string(format!( "{} is out of bounds.", - DISTANCE_CONSUMER.default_name() + distance_consumer().default_name() )) .color(Color::Named(NamedColor::Red)), ) @@ -239,24 +261,24 @@ impl CommandExecutor for WorldborderAddTimeExecutor { .expect("There should always be atleast one world"); let mut border = world.worldborder.lock().await; - let Ok(distance) = DISTANCE_CONSUMER.find_arg_default_name(args)? else { + let Ok(distance) = distance_consumer().find_arg_default_name(args)? else { sender .send_message( TextComponent::text_string(format!( "{} is out of bounds.", - DISTANCE_CONSUMER.default_name() + distance_consumer().default_name() )) .color(Color::Named(NamedColor::Red)), ) .await; return Ok(()); }; - let Ok(time) = TIME_CONSUMER.find_arg_default_name(args)? else { + let Ok(time) = time_consumer().find_arg_default_name(args)? else { sender .send_message( TextComponent::text_string(format!( "{} is out of bounds.", - TIME_CONSUMER.default_name() + time_consumer().default_name() )) .color(Color::Named(NamedColor::Red)), ) @@ -341,12 +363,12 @@ impl CommandExecutor for WorldborderDamageAmountExecutor { .expect("There should always be atleast one world"); let mut border = world.worldborder.lock().await; - let Ok(damage_per_block) = DAMAGE_PER_BLOCK_CONSUMER.find_arg_default_name(args)? else { + let Ok(damage_per_block) = damage_per_block_consumer().find_arg_default_name(args)? else { sender .send_message( TextComponent::text_string(format!( "{} is out of bounds.", - DAMAGE_PER_BLOCK_CONSUMER.default_name() + damage_per_block_consumer().default_name() )) .color(Color::Named(NamedColor::Red)), ) @@ -392,12 +414,12 @@ impl CommandExecutor for WorldborderDamageBufferExecutor { .expect("There should always be atleast one world"); let mut border = world.worldborder.lock().await; - let Ok(buffer) = DAMAGE_BUFFER_CONSUMER.find_arg_default_name(args)? else { + let Ok(buffer) = damage_buffer_consumer().find_arg_default_name(args)? else { sender .send_message( TextComponent::text_string(format!( "{} is out of bounds.", - DAMAGE_BUFFER_CONSUMER.default_name() + damage_buffer_consumer().default_name() )) .color(Color::Named(NamedColor::Red)), ) @@ -443,12 +465,12 @@ impl CommandExecutor for WorldborderWarningDistanceExecutor { .expect("There should always be atleast one world"); let mut border = world.worldborder.lock().await; - let Ok(distance) = WARNING_DISTANCE_CONSUMER.find_arg_default_name(args)? else { + let Ok(distance) = warning_distance_consumer().find_arg_default_name(args)? else { sender .send_message( TextComponent::text_string(format!( "{} is out of bounds.", - WARNING_DISTANCE_CONSUMER.default_name() + warning_distance_consumer().default_name() )) .color(Color::Named(NamedColor::Red)), ) @@ -494,12 +516,12 @@ impl CommandExecutor for WorldborderWarningTimeExecutor { .expect("There should always be atleast one world"); let mut border = world.worldborder.lock().await; - let Ok(time) = TIME_CONSUMER.find_arg_default_name(args)? else { + let Ok(time) = time_consumer().find_arg_default_name(args)? else { sender .send_message( TextComponent::text_string(format!( "{} is out of bounds.", - TIME_CONSUMER.default_name() + time_consumer().default_name() )) .color(Color::Named(NamedColor::Red)), ) @@ -529,59 +551,42 @@ impl CommandExecutor for WorldborderWarningTimeExecutor { } } -static DISTANCE_CONSUMER: BoundedNumArgumentConsumer = - BoundedNumArgumentConsumer::new().min(0.0).name("distance"); - -static TIME_CONSUMER: BoundedNumArgumentConsumer = - BoundedNumArgumentConsumer::new().min(0).name("time"); - -static DAMAGE_PER_BLOCK_CONSUMER: BoundedNumArgumentConsumer = - BoundedNumArgumentConsumer::new() - .min(0.0) - .name("damage_per_block"); - -static DAMAGE_BUFFER_CONSUMER: BoundedNumArgumentConsumer = - BoundedNumArgumentConsumer::new().min(0.0).name("buffer"); - -static WARNING_DISTANCE_CONSUMER: BoundedNumArgumentConsumer = - BoundedNumArgumentConsumer::new().min(0).name("distance"); - -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) .with_child( literal("add").with_child( - argument_default_name(&DISTANCE_CONSUMER) - .execute(&WorldborderAddExecutor) + argument_default_name(distance_consumer()) + .execute(WorldborderAddExecutor) .with_child( - argument_default_name(&TIME_CONSUMER).execute(&WorldborderAddTimeExecutor), + argument_default_name(time_consumer()).execute(WorldborderAddTimeExecutor), ), ), ) .with_child(literal("center").with_child( - argument_default_name(&Position2DArgumentConsumer).execute(&WorldborderCenterExecutor), + argument_default_name(Position2DArgumentConsumer).execute(WorldborderCenterExecutor), )) .with_child( literal("damage") .with_child( literal("amount").with_child( - argument_default_name(&DAMAGE_PER_BLOCK_CONSUMER) - .execute(&WorldborderDamageAmountExecutor), + argument_default_name(damage_per_block_consumer()) + .execute(WorldborderDamageAmountExecutor), ), ) .with_child( literal("buffer").with_child( - argument_default_name(&DAMAGE_BUFFER_CONSUMER) - .execute(&WorldborderDamageBufferExecutor), + argument_default_name(damage_buffer_consumer()) + .execute(WorldborderDamageBufferExecutor), ), ), ) - .with_child(literal("get").execute(&WorldborderGetExecutor)) + .with_child(literal("get").execute(WorldborderGetExecutor)) .with_child( literal("set").with_child( - argument_default_name(&DISTANCE_CONSUMER) - .execute(&WorldborderSetExecutor) + argument_default_name(distance_consumer()) + .execute(WorldborderSetExecutor) .with_child( - argument_default_name(&TIME_CONSUMER).execute(&WorldborderSetTimeExecutor), + argument_default_name(time_consumer()).execute(WorldborderSetTimeExecutor), ), ), ) @@ -589,12 +594,12 @@ pub fn init_command_tree<'a>() -> CommandTree<'a> { literal("warning") .with_child( literal("distance").with_child( - argument_default_name(&WARNING_DISTANCE_CONSUMER) - .execute(&WorldborderWarningDistanceExecutor), + argument_default_name(warning_distance_consumer()) + .execute(WorldborderWarningDistanceExecutor), ), ) .with_child(literal("time").with_child( - argument_default_name(&TIME_CONSUMER).execute(&WorldborderWarningTimeExecutor), + argument_default_name(time_consumer()).execute(WorldborderWarningTimeExecutor), )), ) } diff --git a/pumpkin/src/command/dispatcher.rs b/pumpkin/src/command/dispatcher.rs index b2dc27ae..4d1bb943 100644 --- a/pumpkin/src/command/dispatcher.rs +++ b/pumpkin/src/command/dispatcher.rs @@ -46,13 +46,13 @@ impl CommandError { } #[derive(Default)] -pub struct CommandDispatcher<'a> { - pub(crate) commands: HashMap<&'a str, Command<'a>>, +pub struct CommandDispatcher { + pub(crate) commands: HashMap, } /// Stores registered [`CommandTree`]s and dispatches commands to them. -impl<'a> CommandDispatcher<'a> { - pub async fn handle_command( +impl CommandDispatcher { + pub async fn handle_command<'a>( &'a self, sender: &mut CommandSender<'a>, server: &'a Server, @@ -81,7 +81,7 @@ impl<'a> CommandDispatcher<'a> { /// # todo /// - make this less ugly /// - do not query suggestions for the same consumer multiple times just because they are on different paths through the tree - pub(crate) async fn find_suggestions( + pub(crate) async fn find_suggestions<'a>( &'a self, src: &mut CommandSender<'a>, server: &'a Server, @@ -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)) => { @@ -129,12 +129,12 @@ impl<'a> CommandDispatcher<'a> { } let mut suggestions = Vec::from_iter(suggestions); - suggestions.sort_by(|a, b| a.suggestion.cmp(b.suggestion)); + suggestions.sort_by(|a, b| a.suggestion.cmp(&b.suggestion)); suggestions } /// Execute a command using its corresponding [`CommandTree`]. - pub(crate) async fn dispatch( + pub(crate) async fn dispatch<'a>( &'a self, src: &mut CommandSender<'a>, server: &'a Server, @@ -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,14 +160,14 @@ impl<'a> CommandDispatcher<'a> { ))) } - pub(crate) fn get_tree(&self, key: &str) -> Result, CommandError> { + pub(crate) fn get_tree(&self, key: &str) -> Result<&CommandTree, CommandError> { let command = self .commands .get(key) .ok_or(GeneralCommandIssue("Command not found".to_string()))?; match command { - Command::Tree(tree) => Ok(tree.clone()), + Command::Tree(tree) => Ok(tree), Command::Alias(target) => { 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"); @@ -175,22 +175,22 @@ impl<'a> CommandDispatcher<'a> { "Internal Error (See logs for details)".into(), )); }; - Ok(tree.clone()) + Ok(tree) } } } - async fn try_is_fitting_path( + async fn try_is_fitting_path<'a>( src: &mut CommandSender<'a>, server: &'a Server, path: &[usize], - tree: &CommandTree<'a>, + tree: &'a CommandTree, raw_args: &mut RawArgs<'a>, ) -> Result { let mut parsed_args: ConsumedArgs = HashMap::new(); for node in path.iter().map(|&i| &tree.nodes[i]) { - match node.node_type { + match &node.node_type { NodeType::ExecuteLeaf { executor } => { return if raw_args.is_empty() { executor.execute(src, server, &parsed_args).await?; @@ -223,18 +223,18 @@ impl<'a> CommandDispatcher<'a> { Ok(false) } - async fn try_find_suggestions_on_path( + async fn try_find_suggestions_on_path<'a>( src: &mut CommandSender<'a>, server: &'a Server, path: &[usize], - tree: &CommandTree<'a>, + tree: &'a CommandTree, raw_args: &mut RawArgs<'a>, input: &'a str, ) -> Result>>, CommandError> { let mut parsed_args: ConsumedArgs = HashMap::new(); for node in path.iter().map(|&i| &tree.nodes[i]) { - match node.node_type { + match &node.node_type { NodeType::ExecuteLeaf { .. } => { return Ok(None); } @@ -270,16 +270,18 @@ impl<'a> CommandDispatcher<'a> { } /// Register a command with the dispatcher. - pub(crate) fn register(&mut self, tree: CommandTree<'a>) { + pub(crate) fn register(&mut self, tree: CommandTree) { let mut names = tree.names.iter(); let primary_name = names.next().expect("at least one name must be provided"); - for &name in names { - self.commands.insert(name, Command::Alias(primary_name)); + for name in names { + self.commands + .insert(name.to_string(), Command::Alias(primary_name.to_string())); } - self.commands.insert(primary_name, Command::Tree(tree)); + self.commands + .insert(primary_name.to_string(), Command::Tree(tree)); } } diff --git a/pumpkin/src/command/mod.rs b/pumpkin/src/command/mod.rs index 9ab33038..11eea197 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>() -> CommandDispatcher<'a> { +pub fn default_dispatcher() -> CommandDispatcher { let mut dispatcher = CommandDispatcher::default(); dispatcher.register(cmd_pumpkin::init_command_tree()); diff --git a/pumpkin/src/command/tree.rs b/pumpkin/src/command/tree.rs index 9ea18e35..8f54f949 100644 --- a/pumpkin/src/command/tree.rs +++ b/pumpkin/src/command/tree.rs @@ -1,34 +1,34 @@ use super::{args::ArgumentConsumer, CommandExecutor}; use crate::command::CommandSender; -use std::{collections::VecDeque, fmt::Debug}; +use std::{collections::VecDeque, fmt::Debug, sync::Arc}; /// see [`crate::commands::tree_builder::argument`] pub type RawArgs<'a> = Vec<&'a str>; #[derive(Debug, Clone)] -pub struct Node<'a> { +pub struct Node { pub(crate) children: Vec, - pub(crate) node_type: NodeType<'a>, + pub(crate) node_type: NodeType, } #[derive(Clone)] -pub enum NodeType<'a> { +pub enum NodeType { ExecuteLeaf { - executor: &'a dyn CommandExecutor, + executor: Arc, }, Literal { - string: &'a str, + string: String, }, Argument { - name: &'a str, - consumer: &'a dyn ArgumentConsumer, + name: String, + consumer: Arc, }, Require { - predicate: &'a (dyn Fn(&CommandSender) -> bool + Sync), + predicate: Arc bool + Send + Sync>, }, } -impl Debug for NodeType<'_> { +impl Debug for NodeType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::ExecuteLeaf { .. } => f @@ -46,28 +46,28 @@ impl Debug for NodeType<'_> { } } -pub enum Command<'a> { - Tree(CommandTree<'a>), - Alias(&'a str), +pub enum Command { + Tree(CommandTree), + Alias(String), } #[derive(Debug, Clone)] -pub struct CommandTree<'a> { - pub(crate) nodes: Vec>, +pub struct CommandTree { + pub(crate) nodes: Vec, pub(crate) children: Vec, - pub(crate) names: Vec<&'a str>, - pub(crate) description: &'a str, + pub(crate) names: Vec, + pub(crate) description: String, } -impl<'a> CommandTree<'a> { +impl CommandTree { /// iterate over all possible paths that end in a [`NodeType::ExecuteLeaf`] - pub(crate) fn iter_paths(&'a self) -> impl Iterator> + 'a { + pub(crate) fn iter_paths(&self) -> impl Iterator> + use<'_> { let mut todo = VecDeque::<(usize, usize)>::new(); // add root's children todo.extend(self.children.iter().map(|&i| (0, i))); - TraverseAllPathsIter::<'a> { + TraverseAllPathsIter { tree: self, path: Vec::::new(), todo, @@ -76,7 +76,7 @@ impl<'a> CommandTree<'a> { } struct TraverseAllPathsIter<'a> { - tree: &'a CommandTree<'a>, + tree: &'a CommandTree, path: Vec, /// (depth, i) todo: VecDeque<(usize, usize)>, diff --git a/pumpkin/src/command/tree_builder.rs b/pumpkin/src/command/tree_builder.rs index e750ea0b..92f65d9e 100644 --- a/pumpkin/src/command/tree_builder.rs +++ b/pumpkin/src/command/tree_builder.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use crate::command::args::ArgumentConsumer; use crate::command::tree::{CommandTree, Node, NodeType}; use crate::command::CommandSender; @@ -5,9 +7,9 @@ use crate::command::CommandSender; use super::args::DefaultNameArgConsumer; use super::CommandExecutor; -impl<'a> CommandTree<'a> { +impl CommandTree { /// Add a child [Node] to the root of this [`CommandTree`]. - pub fn with_child(mut self, child: impl NodeBuilder<'a>) -> Self { + pub fn with_child(mut self, child: impl NodeBuilder) -> Self { let node = child.build(&mut self); self.children.push(self.nodes.len()); self.nodes.push(node); @@ -15,23 +17,17 @@ impl<'a> CommandTree<'a> { } /// provide at least one name - pub fn new( - names: [&'a str; NAME_COUNT], - description: &'a str, + pub fn new( + names: impl IntoIterator>, + description: impl Into, ) -> Self { - assert!(NAME_COUNT > 0); - - let mut names_vec = Vec::with_capacity(NAME_COUNT); - - for name in names { - names_vec.push(name); - } + let names_vec = names.into_iter().map(Into::into).collect(); Self { nodes: Vec::new(), children: Vec::new(), names: names_vec, - description, + description: description.into(), } } @@ -42,9 +38,11 @@ impl<'a> CommandTree<'a> { /// desired type. /// /// Also see [`NonLeafNodeBuilder::execute`]. - pub fn execute(mut self, executor: &'a dyn CommandExecutor) -> Self { + pub fn execute(mut self, executor: impl CommandExecutor + 'static + Send) -> Self { let node = Node { - node_type: NodeType::ExecuteLeaf { executor }, + node_type: NodeType::ExecuteLeaf { + executor: Arc::new(executor), + }, children: Vec::new(), }; @@ -55,16 +53,16 @@ impl<'a> CommandTree<'a> { } } -pub trait NodeBuilder<'a> { - fn build(self, tree: &mut CommandTree<'a>) -> Node<'a>; +pub trait NodeBuilder { + fn build(self, tree: &mut CommandTree) -> Node; } -struct LeafNodeBuilder<'a> { - node_type: NodeType<'a>, +struct LeafNodeBuilder { + node_type: NodeType, } -impl<'a> NodeBuilder<'a> for LeafNodeBuilder<'a> { - fn build(self, _tree: &mut CommandTree<'a>) -> Node<'a> { +impl NodeBuilder for LeafNodeBuilder { + fn build(self, _tree: &mut CommandTree) -> Node { Node { children: Vec::new(), node_type: self.node_type, @@ -72,14 +70,14 @@ impl<'a> NodeBuilder<'a> for LeafNodeBuilder<'a> { } } -pub struct NonLeafNodeBuilder<'a> { - node_type: NodeType<'a>, - child_nodes: Vec>, - leaf_nodes: Vec>, +pub struct NonLeafNodeBuilder { + node_type: NodeType, + child_nodes: Vec, + leaf_nodes: Vec, } -impl<'a> NodeBuilder<'a> for NonLeafNodeBuilder<'a> { - fn build(self, tree: &mut CommandTree<'a>) -> Node<'a> { +impl NodeBuilder for NonLeafNodeBuilder { + fn build(self, tree: &mut CommandTree) -> Node { let mut child_indices = Vec::new(); for node_builder in self.child_nodes { @@ -101,7 +99,7 @@ impl<'a> NodeBuilder<'a> for NonLeafNodeBuilder<'a> { } } -impl<'a> NonLeafNodeBuilder<'a> { +impl NonLeafNodeBuilder { /// Add a child [Node] to this one. pub fn with_child(mut self, child: Self) -> Self { self.child_nodes.push(child); @@ -115,9 +113,11 @@ impl<'a> NonLeafNodeBuilder<'a> { /// desired type. /// /// Also see [`CommandTree::execute`]. - pub fn execute(mut self, executor: &'a dyn CommandExecutor) -> Self { + pub fn execute(mut self, executor: impl CommandExecutor + 'static + Send) -> Self { self.leaf_nodes.push(LeafNodeBuilder { - node_type: NodeType::ExecuteLeaf { executor }, + node_type: NodeType::ExecuteLeaf { + executor: Arc::new(executor), + }, }); self @@ -125,9 +125,11 @@ impl<'a> NonLeafNodeBuilder<'a> { } /// Matches a sting literal. -pub const fn literal(string: &str) -> NonLeafNodeBuilder { +pub fn literal(string: impl Into) -> NonLeafNodeBuilder { NonLeafNodeBuilder { - node_type: NodeType::Literal { string }, + node_type: NodeType::Literal { + string: string.into(), + }, child_nodes: Vec::new(), leaf_nodes: Vec::new(), } @@ -141,20 +143,28 @@ pub const fn literal(string: &str) -> NonLeafNodeBuilder { /// [`NonLeafNodeBuilder::execute`] nodes in a [`ConsumedArgs`] instance. It must remove consumed arg(s) /// from [`RawArgs`] and return them. It must return None if [`RawArgs`] are invalid. [`RawArgs`] is /// reversed, so [`Vec::pop`] can be used to obtain args in ltr order. -pub fn argument<'a>(name: &'a str, consumer: &'a dyn ArgumentConsumer) -> NonLeafNodeBuilder<'a> { +pub fn argument( + name: impl Into, + consumer: impl ArgumentConsumer + 'static + Send, +) -> NonLeafNodeBuilder { NonLeafNodeBuilder { - node_type: NodeType::Argument { name, consumer }, + node_type: NodeType::Argument { + name: name.into(), + consumer: Arc::new(consumer), + }, child_nodes: Vec::new(), leaf_nodes: Vec::new(), } } /// same as [`crate::command::tree_builder::argument`], but uses default arg name of consumer -pub fn argument_default_name(consumer: &dyn DefaultNameArgConsumer) -> NonLeafNodeBuilder<'_> { +pub fn argument_default_name( + consumer: impl DefaultNameArgConsumer + 'static + Send, +) -> NonLeafNodeBuilder { NonLeafNodeBuilder { node_type: NodeType::Argument { name: consumer.default_name(), - consumer: consumer.get_argument_consumer(), + consumer: Arc::new(consumer), }, child_nodes: Vec::new(), leaf_nodes: Vec::new(), @@ -163,9 +173,13 @@ pub fn argument_default_name(consumer: &dyn DefaultNameArgConsumer) -> NonLeafNo /// ```predicate``` should return ```false``` if requirement for reaching following [Node]s is not /// met. -pub fn require(predicate: &(dyn Fn(&CommandSender) -> bool + Sync)) -> NonLeafNodeBuilder { +pub fn require( + predicate: impl Fn(&CommandSender) -> bool + Send + Sync + 'static, +) -> NonLeafNodeBuilder { NonLeafNodeBuilder { - node_type: NodeType::Require { predicate }, + node_type: NodeType::Require { + predicate: Arc::new(predicate), + }, child_nodes: Vec::new(), leaf_nodes: Vec::new(), } diff --git a/pumpkin/src/command/tree_format.rs b/pumpkin/src/command/tree_format.rs index 406d95f2..597aece0 100644 --- a/pumpkin/src/command/tree_format.rs +++ b/pumpkin/src/command/tree_format.rs @@ -7,7 +7,7 @@ trait IsVisible { fn is_visible(&self) -> bool; } -impl IsVisible for Node<'_> { +impl IsVisible for Node { fn is_visible(&self) -> bool { matches!( self.node_type, @@ -16,9 +16,9 @@ impl IsVisible for Node<'_> { } } -impl Display for Node<'_> { +impl Display for Node { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self.node_type { + match &self.node_type { NodeType::Literal { string } => { f.write_str(string)?; } @@ -50,10 +50,10 @@ fn flatten_require_nodes(nodes: &[Node], children: &[usize]) -> Vec { new_children } -impl Display for CommandTree<'_> { +impl Display for CommandTree { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_char('/')?; - f.write_str(self.names[0])?; + f.write_str(&self.names[0])?; let mut todo = VecDeque::<&[usize]>::with_capacity(self.children.len()); todo.push_back(&self.children); diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index cb5c57b1..2ae5804f 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: RwLock>, + pub command_dispatcher: RwLock, /// Saves and calls blocks blocks pub block_manager: Arc, /// Manages multiple worlds within the server. From 7161491005f4308eff7cce646df910c3c9cc5993 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Fri, 27 Dec 2024 00:27:24 +0100 Subject: [PATCH 38/42] add some simple test to our nbt crate --- pumpkin-core/src/text/click.rs | 2 + pumpkin-entity/src/entity_type.rs | 2 +- pumpkin-nbt/src/deserializer.rs | 12 +++--- pumpkin-nbt/src/lib.rs | 62 +++++++++++++++++++++++++++++++ pumpkin-protocol/Cargo.toml | 2 +- pumpkin/Cargo.toml | 2 +- 6 files changed, 73 insertions(+), 9 deletions(-) diff --git a/pumpkin-core/src/text/click.rs b/pumpkin-core/src/text/click.rs index 0e917e15..caa78f61 100644 --- a/pumpkin-core/src/text/click.rs +++ b/pumpkin-core/src/text/click.rs @@ -8,6 +8,8 @@ use serde::{Deserialize, Serialize}; pub enum ClickEvent<'a> { /// Opens a URL OpenUrl(Cow<'a, str>), + /// Opens a File + OpenFile(Cow<'a, str>), /// Works in signs, but only on the root text component RunCommand(Cow<'a, str>), /// Replaces the contents of the chat box with the text, not necessarily a diff --git a/pumpkin-entity/src/entity_type.rs b/pumpkin-entity/src/entity_type.rs index 282ac129..e065722f 100644 --- a/pumpkin-entity/src/entity_type.rs +++ b/pumpkin-entity/src/entity_type.rs @@ -1,4 +1,4 @@ -// TODO +// TODO make this dynamic #[derive(Clone)] #[repr(i32)] pub enum EntityType { diff --git a/pumpkin-nbt/src/deserializer.rs b/pumpkin-nbt/src/deserializer.rs index ae017b82..80e3b96f 100644 --- a/pumpkin-nbt/src/deserializer.rs +++ b/pumpkin-nbt/src/deserializer.rs @@ -23,7 +23,7 @@ impl<'de, T: Buf> Deserializer<'de, T> { } } -/// Deserializes struct using Serde Deserializer from unnamed (network) NBT +/// Deserializes struct using Serde Deserializer from normal NBT pub fn from_bytes<'a, T>(s: &'a mut impl Buf) -> Result where T: Deserialize<'a>, @@ -32,20 +32,20 @@ where T::deserialize(&mut deserializer) } -pub fn from_cursor<'a, T>(cursor: &'a mut Cursor<&[u8]>) -> Result +/// Deserializes struct using Serde Deserializer from normal NBT +pub fn from_bytes_unnamed<'a, T>(s: &'a mut impl Buf) -> Result where T: Deserialize<'a>, { - let mut deserializer = Deserializer::new(cursor, true); + let mut deserializer = Deserializer::new(s, false); T::deserialize(&mut deserializer) } -/// Deserializes struct using Serde Deserializer from normal NBT -pub fn from_bytes_unnamed<'a, T>(s: &'a mut impl Buf) -> Result +pub fn from_cursor<'a, T>(cursor: &'a mut Cursor<&[u8]>) -> Result where T: Deserialize<'a>, { - let mut deserializer = Deserializer::new(s, false); + let mut deserializer = Deserializer::new(cursor, true); T::deserialize(&mut deserializer) } diff --git a/pumpkin-nbt/src/lib.rs b/pumpkin-nbt/src/lib.rs index b2be0d65..acee723f 100644 --- a/pumpkin-nbt/src/lib.rs +++ b/pumpkin-nbt/src/lib.rs @@ -200,3 +200,65 @@ macro_rules! impl_array { impl_array!(IntArray, "int"); impl_array!(LongArray, "long"); impl_array!(BytesArray, "byte"); + +#[cfg(test)] +mod test { + use serde::{Deserialize, Serialize}; + + use crate::BytesArray; + use crate::IntArray; + use crate::LongArray; + use crate::{ + deserializer::from_bytes_unnamed, + serializer::to_bytes_unnamed, + }; + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Test { + byte: i8, + short: i16, + int: i32, + long: i64, + float: f32, + string: String, + } + + #[test] + fn test_simple_ser_de_unamed() { + let test = Test { + byte: 123, + short: 1342, + int: 4313, + long: 34, + float: 1.00, + string: "Hello test".to_string(), + }; + let mut bytes = to_bytes_unnamed(&test).unwrap(); + let recreated_struct: Test = from_bytes_unnamed(&mut bytes).unwrap(); + + assert_eq!(test, recreated_struct); + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct TestArray { + #[serde(with = "BytesArray")] + byte_array: Vec, + #[serde(with = "IntArray")] + int_array: Vec, + #[serde(with = "LongArray")] + long_array: Vec, + } + + #[test] + fn test_simple_ser_de_array() { + let test = TestArray { + byte_array: vec![0, 3, 2], + int_array: vec![13, 1321, 2], + long_array: vec![1, 0, 200301, 1], + }; + let mut bytes = to_bytes_unnamed(&test).unwrap(); + let recreated_struct: TestArray = from_bytes_unnamed(&mut bytes).unwrap(); + + assert_eq!(test, recreated_struct); + } +} diff --git a/pumpkin-protocol/Cargo.toml b/pumpkin-protocol/Cargo.toml index c167df95..f04d7731 100644 --- a/pumpkin-protocol/Cargo.toml +++ b/pumpkin-protocol/Cargo.toml @@ -30,4 +30,4 @@ aes = "0.8.4" cfb8 = "0.8.1" # decryption -libdeflater = "1.22.0" \ No newline at end of file +libdeflater = "1.23.0" \ No newline at end of file diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index a3f4a14e..87b03020 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -46,7 +46,7 @@ rsa = "0.9.7" rsa-der = "0.3.0" # authentication -reqwest = { version = "0.12.9", default-features = false, features = [ +reqwest = { version = "0.12.10", default-features = false, features = [ "http2", "json", "macos-system-configuration", From af54cb428969a2861f239bdd1f60969758d15f5b Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Fri, 27 Dec 2024 13:15:45 +0100 Subject: [PATCH 39/42] fix: clippy fmt --- pumpkin-nbt/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pumpkin-nbt/src/lib.rs b/pumpkin-nbt/src/lib.rs index acee723f..5ed48da9 100644 --- a/pumpkin-nbt/src/lib.rs +++ b/pumpkin-nbt/src/lib.rs @@ -208,10 +208,7 @@ mod test { use crate::BytesArray; use crate::IntArray; use crate::LongArray; - use crate::{ - deserializer::from_bytes_unnamed, - serializer::to_bytes_unnamed, - }; + use crate::{deserializer::from_bytes_unnamed, serializer::to_bytes_unnamed}; #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Test { From 37372986b029985e839dedcc615932187dd9d3d8 Mon Sep 17 00:00:00 2001 From: LieOnLion Date: Fri, 27 Dec 2024 12:18:43 +0000 Subject: [PATCH 40/42] Fixed Pumpkin website links (#423) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7e4df0ef..f7113930 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ -[Pumpkin](https://snowiiii.github.io/Pumpkin/) is a Minecraft server built entirely in Rust, offering a fast, efficient, +[Pumpkin](https://snowiiii.github.io/Pumpkin-Website/) is a Minecraft server built entirely in Rust, offering a fast, efficient, and customizable experience. It prioritizes performance and player enjoyment while adhering to the core mechanics of the game. ![image](https://github.com/user-attachments/assets/7e2e865e-b150-4675-a2d5-b52f9900378e) @@ -87,7 +87,7 @@ Check out our [Github Project](https://github.com/users/Snowiiii/projects/12/vie ## How to run -See our [Quick Start](https://snowiiii.github.io/Pumpkin/about/quick-start.html) Guide to get Pumpkin running +See our [Quick Start](https://snowiiii.github.io/Pumpkin-Website/about/quick-start.html) Guide to get Pumpkin running ## Contributions @@ -95,7 +95,7 @@ Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) ## Docs -The Documentation of Pumpkin can be found at https://snowiiii.github.io/Pumpkin/ +The Documentation of Pumpkin can be found at https://snowiiii.github.io/Pumpkin-Website/ ## Communication From 98d140664639cffc1b48167adb2dff3d24c12624 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Fri, 27 Dec 2024 16:04:40 +0100 Subject: [PATCH 41/42] Implement session.lock Lock session.lock so other processes know that we currently read and write to the level. This is vanilla behavior. Test: Tested to start the a new server while on was locking the file, and got `Failed to lock level: AlreadyLocked("session.lock")` as expected --- pumpkin-world/Cargo.toml | 2 ++ pumpkin-world/src/level.rs | 9 +++++++++ pumpkin-world/src/lib.rs | 1 + pumpkin-world/src/lock/anvil.rs | 32 ++++++++++++++++++++++++++++++++ pumpkin-world/src/lock/mod.rs | 18 ++++++++++++++++++ 5 files changed, 62 insertions(+) create mode 100644 pumpkin-world/src/lock/anvil.rs create mode 100644 pumpkin-world/src/lock/mod.rs diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index 495ee3ff..1c7eacd8 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -25,6 +25,8 @@ dashmap = "6.1.0" flate2 = "1.0" lz4 = "1.28.0" +file-guard = "0.2.0" + enum_dispatch = "0.3.13" fastnbt = { git = "https://github.com/owengage/fastnbt.git" } diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 930363ec..ede2d616 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -14,6 +14,7 @@ use crate::{ anvil::AnvilChunkReader, ChunkData, ChunkParsingError, ChunkReader, ChunkReadingError, }, generation::{get_world_gen, Seed, WorldGenerator}, + lock::{anvil::AnvilLevelLocker, LevelLocker}, world_info::{anvil::AnvilLevelInfo, LevelData, WorldInfoReader, WorldInfoWriter}, }; @@ -35,6 +36,9 @@ pub struct Level { chunk_watchers: Arc, usize>>, chunk_reader: Arc, world_gen: Arc, + // Gets unlocked when dropped + // TODO: Make this a trait + _locker: Arc, } #[derive(Clone)] @@ -55,6 +59,10 @@ impl Level { region_folder, }; + // if we fail to lock, lets crash ???. maybe not the best soultion when we have a large server with many worlds and one is locked. + // So TODO + let locker = AnvilLevelLocker::look(&level_folder).expect("Failed to lock level"); + // TODO: Load info correctly based on world format type let level_info = AnvilLevelInfo .read_world_info(&level_folder) @@ -71,6 +79,7 @@ impl Level { loaded_chunks: Arc::new(DashMap::new()), chunk_watchers: Arc::new(DashMap::new()), level_info, + _locker: Arc::new(locker), } } diff --git a/pumpkin-world/src/lib.rs b/pumpkin-world/src/lib.rs index 8e26b106..ec23b151 100644 --- a/pumpkin-world/src/lib.rs +++ b/pumpkin-world/src/lib.rs @@ -16,6 +16,7 @@ pub mod dimension; mod generation; pub mod item; pub mod level; +mod lock; pub mod world_info; pub const WORLD_HEIGHT: usize = 384; pub const WORLD_LOWEST_Y: i16 = -64; diff --git a/pumpkin-world/src/lock/anvil.rs b/pumpkin-world/src/lock/anvil.rs new file mode 100644 index 00000000..7864cf71 --- /dev/null +++ b/pumpkin-world/src/lock/anvil.rs @@ -0,0 +1,32 @@ +use file_guard::{FileGuard, Lock}; + +use super::{LevelLocker, LockError}; + +use std::{fs::File, io::Write, sync::Arc}; + +pub struct AnvilLevelLocker { + _lock: Option>>, +} + +const SESSION_LOCK_FILE_NAME: &str = "session.lock"; + +const SNOWMAN: &[u8] = "☃".as_bytes(); + +impl LevelLocker for AnvilLevelLocker { + fn look(folder: &crate::level::LevelFolder) -> Result { + let file_path = folder.root_folder.join(SESSION_LOCK_FILE_NAME); + let mut file = File::options() + .create(true) + .truncate(false) + .write(true) + .open(file_path) + .unwrap(); + // im not joking, mojang writes a snowman into the lock file + file.write_all(SNOWMAN) + .map_err(|_| LockError::FailedWrite)?; + let file_arc = Arc::new(file); + let lock = file_guard::try_lock(file_arc, Lock::Exclusive, 0, 1) + .map_err(|_| LockError::AlreadyLocked(SESSION_LOCK_FILE_NAME.to_string()))?; + Ok(Self { _lock: Some(lock) }) + } +} diff --git a/pumpkin-world/src/lock/mod.rs b/pumpkin-world/src/lock/mod.rs new file mode 100644 index 00000000..bf4b262f --- /dev/null +++ b/pumpkin-world/src/lock/mod.rs @@ -0,0 +1,18 @@ +use thiserror::Error; + +use crate::level::LevelFolder; + +pub mod anvil; + +// Gets unlocked when dropped +pub trait LevelLocker: Send + Sync { + fn look(folder: &LevelFolder) -> Result; +} + +#[derive(Error, Debug)] +pub enum LockError { + #[error("Oh no, Level is already locked by {0}")] + AlreadyLocked(String), + #[error("Failed to write into lock file")] + FailedWrite, +} From c521ef8bb0be0c6c8cb1887c087a16c6730ca2fd Mon Sep 17 00:00:00 2001 From: Kris <37947442+OfficialKris@users.noreply.github.com> Date: Fri, 27 Dec 2024 10:24:23 -0500 Subject: [PATCH 42/42] Correct Interactive Block Behavior (#388) * Added chest and furnace * Corrected interactive block functionality * Fixed merge * Added standard functions and player remove on destroy * Added unique block standard functions * Fix clippy * Fix deadlock in standard function --- pumpkin-core/src/math/position.rs | 2 +- pumpkin-inventory/src/lib.rs | 7 ++ pumpkin-inventory/src/open_container.rs | 22 +++++ pumpkin/src/block/block_manager.rs | 18 +++- pumpkin/src/block/blocks/chest.rs | 65 +++++++----- pumpkin/src/block/blocks/crafting_table.rs | 75 +++++++++----- pumpkin/src/block/blocks/furnace.rs | 55 ++++++----- pumpkin/src/block/blocks/jukebox.rs | 18 +++- pumpkin/src/block/blocks/mod.rs | 110 +++++++++++++++++++++ pumpkin/src/block/pumpkin_block.rs | 40 +++++++- pumpkin/src/net/container.rs | 1 + pumpkin/src/net/packet/play.rs | 16 +-- pumpkin/src/server/mod.rs | 57 +++++++++++ 13 files changed, 400 insertions(+), 86 deletions(-) diff --git a/pumpkin-core/src/math/position.rs b/pumpkin-core/src/math/position.rs index 83989064..a3b82cbe 100644 --- a/pumpkin-core/src/math/position.rs +++ b/pumpkin-core/src/math/position.rs @@ -5,7 +5,7 @@ use crate::math::vector2::Vector2; use num_traits::Euclid; use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq)] /// Aka Block Position pub struct WorldPosition(pub Vector3); diff --git a/pumpkin-inventory/src/lib.rs b/pumpkin-inventory/src/lib.rs index bf56dca6..f1273b05 100644 --- a/pumpkin-inventory/src/lib.rs +++ b/pumpkin-inventory/src/lib.rs @@ -94,6 +94,13 @@ pub trait Container: Sync + Send { fn all_slots_ref(&self) -> Vec>; + fn clear_all_slots(&mut self) { + let all_slots = self.all_slots(); + for stack in all_slots { + *stack = None; + } + } + fn all_combinable_slots(&self) -> Vec> { self.all_slots_ref() } diff --git a/pumpkin-inventory/src/open_container.rs b/pumpkin-inventory/src/open_container.rs index ec68578f..3025ed92 100644 --- a/pumpkin-inventory/src/open_container.rs +++ b/pumpkin-inventory/src/open_container.rs @@ -5,8 +5,10 @@ use pumpkin_world::block::block_registry::Block; use pumpkin_world::item::ItemStack; use std::sync::Arc; use tokio::sync::Mutex; + pub struct OpenContainer { // TODO: unique id should be here + // TODO: should this be uuid? players: Vec, container: Arc>>, location: Option, @@ -54,6 +56,22 @@ impl OpenContainer { } } + pub fn is_location(&self, try_position: WorldPosition) -> bool { + if let Some(location) = self.location { + location == try_position + } else { + false + } + } + + pub async fn clear_all_slots(&self) { + self.container.lock().await.clear_all_slots(); + } + + pub fn clear_all_players(&mut self) { + self.players = vec![]; + } + pub fn all_player_ids(&self) -> Vec { self.players.clone() } @@ -62,6 +80,10 @@ impl OpenContainer { self.location } + pub async fn set_location(&mut self, location: Option) { + self.location = location; + } + pub fn get_block(&self) -> Option { self.block.clone() } diff --git a/pumpkin/src/block/block_manager.rs b/pumpkin/src/block/block_manager.rs index 92395bb8..0fb26ede 100644 --- a/pumpkin/src/block/block_manager.rs +++ b/pumpkin/src/block/block_manager.rs @@ -2,6 +2,7 @@ use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock}; use crate::entity::player::Player; use crate::server::Server; use pumpkin_core::math::position::WorldPosition; +use pumpkin_inventory::OpenContainer; use pumpkin_world::block::block_registry::Block; use pumpkin_world::item::item_registry::Item; use std::collections::HashMap; @@ -33,7 +34,7 @@ impl BlockManager { ) { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block.on_use(player, location, server).await; + pumpkin_block.on_use(block, player, location, server).await; } } @@ -48,7 +49,7 @@ impl BlockManager { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block - .on_use_with_item(player, location, item, server) + .on_use_with_item(block, player, location, item, server) .await; } BlockActionResult::Continue @@ -63,7 +64,9 @@ impl BlockManager { ) { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block.on_placed(player, location, server).await; + pumpkin_block + .on_placed(block, player, location, server) + .await; } } @@ -76,7 +79,9 @@ impl BlockManager { ) { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block.on_broken(player, location, server).await; + pumpkin_block + .on_broken(block, player, location, server) + .await; } } @@ -86,10 +91,13 @@ impl BlockManager { player: &Player, location: WorldPosition, server: &Server, + container: &mut OpenContainer, ) { let pumpkin_block = self.get_pumpkin_block(block); if let Some(pumpkin_block) = pumpkin_block { - pumpkin_block.on_close(player, location, server).await; + pumpkin_block + .on_close(block, player, location, server, container) + .await; } } diff --git a/pumpkin/src/block/blocks/chest.rs b/pumpkin/src/block/blocks/chest.rs index 1600bb09..313d9a5b 100644 --- a/pumpkin/src/block/blocks/chest.rs +++ b/pumpkin/src/block/blocks/chest.rs @@ -2,7 +2,7 @@ use async_trait::async_trait; use pumpkin_core::math::position::WorldPosition; use pumpkin_inventory::{Chest, OpenContainer, WindowType}; use pumpkin_macros::{pumpkin_block, sound}; -use pumpkin_world::{block::block_registry::get_block, item::item_registry::Item}; +use pumpkin_world::{block::block_registry::Block, item::item_registry::Item}; use crate::{ block::{block_manager::BlockActionResult, pumpkin_block::PumpkinBlock}, @@ -15,8 +15,15 @@ pub struct ChestBlock; #[async_trait] impl PumpkinBlock for ChestBlock { - async fn on_use<'a>(&self, player: &Player, _location: WorldPosition, server: &Server) { - self.open_chest_block(player, _location, server).await; + async fn on_use<'a>( + &self, + block: &Block, + player: &Player, + _location: WorldPosition, + server: &Server, + ) { + self.open_chest_block(block, player, _location, server) + .await; player .world() .play_block_sound(sound!("block.chest.open"), _location) @@ -25,46 +32,60 @@ impl PumpkinBlock for ChestBlock { async fn on_use_with_item<'a>( &self, + block: &Block, player: &Player, _location: WorldPosition, _item: &Item, server: &Server, ) -> BlockActionResult { - self.open_chest_block(player, _location, server).await; + self.open_chest_block(block, player, _location, server) + .await; BlockActionResult::Consume } - async fn on_close<'a>(&self, player: &Player, _location: WorldPosition, _server: &Server) { + async fn on_broken<'a>( + &self, + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, + ) { + super::standard_on_broken_with_container(block, player, location, server).await; + } + + async fn on_close<'a>( + &self, + _block: &Block, + player: &Player, + _location: WorldPosition, + _server: &Server, + _container: &OpenContainer, + ) { player .world() .play_block_sound(sound!("block.chest.close"), _location) .await; + // TODO: send entity updates close } } impl ChestBlock { pub async fn open_chest_block( &self, + block: &Block, player: &Player, location: WorldPosition, server: &Server, ) { - let entity_id = player.entity_id(); - // TODO: This should be a unique identifier for the each block container - player.open_container.store(Some(2)); - { - let mut open_containers = server.open_containers.write().await; - if let Some(chest) = open_containers.get_mut(&2) { - chest.add_player(entity_id); - } else { - let open_container = OpenContainer::new_empty_container::( - entity_id, - Some(location), - get_block("minecraft:chest").cloned(), - ); - open_containers.insert(2, open_container); - } - } - player.open_container(server, WindowType::Generic9x3).await; + // TODO: shouldn't Chest and window type be constrained together to avoid errors? + super::standard_open_container::( + block, + player, + location, + server, + WindowType::Generic9x3, + ) + .await; + // TODO: send entity updates open } } diff --git a/pumpkin/src/block/blocks/crafting_table.rs b/pumpkin/src/block/blocks/crafting_table.rs index 962d5662..01ad4179 100644 --- a/pumpkin/src/block/blocks/crafting_table.rs +++ b/pumpkin/src/block/blocks/crafting_table.rs @@ -6,54 +6,83 @@ use async_trait::async_trait; use pumpkin_core::math::position::WorldPosition; use pumpkin_inventory::{CraftingTable, OpenContainer, WindowType}; use pumpkin_macros::pumpkin_block; -use pumpkin_world::{block::block_registry::get_block, item::item_registry::Item}; +use pumpkin_world::{block::block_registry::Block, item::item_registry::Item}; #[pumpkin_block("minecraft:crafting_table")] pub struct CraftingTableBlock; #[async_trait] impl PumpkinBlock for CraftingTableBlock { - async fn on_use<'a>(&self, player: &Player, _location: WorldPosition, server: &Server) { - self.open_crafting_screen(player, _location, server).await; + async fn on_use<'a>( + &self, + block: &Block, + player: &Player, + _location: WorldPosition, + server: &Server, + ) { + self.open_crafting_screen(block, player, _location, server) + .await; } async fn on_use_with_item<'a>( &self, + block: &Block, player: &Player, _location: WorldPosition, _item: &Item, server: &Server, ) -> BlockActionResult { - self.open_crafting_screen(player, _location, server).await; + self.open_crafting_screen(block, player, _location, server) + .await; BlockActionResult::Consume } -} -impl CraftingTableBlock { - pub async fn open_crafting_screen( + async fn on_broken<'a>( &self, + block: &Block, player: &Player, location: WorldPosition, server: &Server, ) { - //TODO: Adjust /craft command to real crafting table + super::standard_on_broken_with_container(block, player, location, server).await; + } + + async fn on_close<'a>( + &self, + _block: &Block, + player: &Player, + _location: WorldPosition, + _server: &Server, + container: &OpenContainer, + ) { let entity_id = player.entity_id(); - player.open_container.store(Some(1)); - { - let mut open_containers = server.open_containers.write().await; - if let Some(ender_chest) = open_containers.get_mut(&1) { - ender_chest.add_player(entity_id); - } else { - let open_container = OpenContainer::new_empty_container::( - entity_id, - Some(location), - get_block("minecraft:crafting_table").cloned(), - ); - open_containers.insert(1, open_container); + for player_id in container.all_player_ids() { + if entity_id == player_id { + container.clear_all_slots().await; } } - player - .open_container(server, WindowType::CraftingTable) - .await; + + // TODO: items should be re-added to player inventory or dropped dependending on if they are in movement. + // TODO: unique containers should be implemented as a separate stack internally (optimizes large player servers for example) + // TODO: ephemeral containers (crafting tables) might need to be separate data structure than stored (ender chest) + } +} + +impl CraftingTableBlock { + pub async fn open_crafting_screen( + &self, + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, + ) { + super::standard_open_container_unique::( + block, + player, + location, + server, + WindowType::CraftingTable, + ) + .await; } } diff --git a/pumpkin/src/block/blocks/furnace.rs b/pumpkin/src/block/blocks/furnace.rs index 8fc47949..1ecdf995 100644 --- a/pumpkin/src/block/blocks/furnace.rs +++ b/pumpkin/src/block/blocks/furnace.rs @@ -3,9 +3,9 @@ use crate::entity::player::Player; use async_trait::async_trait; use pumpkin_core::math::position::WorldPosition; use pumpkin_inventory::Furnace; -use pumpkin_inventory::{OpenContainer, WindowType}; +use pumpkin_inventory::WindowType; use pumpkin_macros::pumpkin_block; -use pumpkin_world::block::block_registry::get_block; +use pumpkin_world::block::block_registry::Block; use pumpkin_world::item::item_registry::Item; use crate::{block::pumpkin_block::PumpkinBlock, server::Server}; @@ -15,45 +15,56 @@ pub struct FurnaceBlock; #[async_trait] impl PumpkinBlock for FurnaceBlock { - async fn on_use<'a>(&self, player: &Player, _location: WorldPosition, server: &Server) { - self.open_furnace_screen(player, _location, server).await; + async fn on_use<'a>( + &self, + block: &Block, + player: &Player, + _location: WorldPosition, + server: &Server, + ) { + self.open_furnace_screen(block, player, _location, server) + .await; } async fn on_use_with_item<'a>( &self, + block: &Block, player: &Player, _location: WorldPosition, _item: &Item, server: &Server, ) -> BlockActionResult { - self.open_furnace_screen(player, _location, server).await; + self.open_furnace_screen(block, player, _location, server) + .await; BlockActionResult::Consume } + + async fn on_broken<'a>( + &self, + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, + ) { + super::standard_on_broken_with_container(block, player, location, server).await; + } } impl FurnaceBlock { pub async fn open_furnace_screen( &self, + block: &Block, player: &Player, location: WorldPosition, server: &Server, ) { - //TODO: Adjust /craft command to real crafting table - let entity_id = player.entity_id(); - player.open_container.store(Some(3)); - { - let mut open_containers = server.open_containers.write().await; - if let Some(ender_chest) = open_containers.get_mut(&3) { - ender_chest.add_player(entity_id); - } else { - let open_container = OpenContainer::new_empty_container::( - entity_id, - Some(location), - get_block("minecraft:furnace").cloned(), - ); - open_containers.insert(3, open_container); - } - } - player.open_container(server, WindowType::Furnace).await; + super::standard_open_container::( + block, + player, + location, + server, + WindowType::Furnace, + ) + .await; } } diff --git a/pumpkin/src/block/blocks/jukebox.rs b/pumpkin/src/block/blocks/jukebox.rs index 3c52395b..4836c4be 100644 --- a/pumpkin/src/block/blocks/jukebox.rs +++ b/pumpkin/src/block/blocks/jukebox.rs @@ -6,6 +6,7 @@ use async_trait::async_trait; use pumpkin_core::math::position::WorldPosition; use pumpkin_macros::pumpkin_block; use pumpkin_registry::SYNCED_REGISTRIES; +use pumpkin_world::block::block_registry::Block; use pumpkin_world::item::item_registry::Item; #[pumpkin_block("minecraft:jukebox")] @@ -13,7 +14,13 @@ pub struct JukeboxBlock; #[async_trait] impl PumpkinBlock for JukeboxBlock { - async fn on_use<'a>(&self, player: &Player, location: WorldPosition, _server: &Server) { + async fn on_use<'a>( + &self, + _block: &Block, + player: &Player, + location: WorldPosition, + _server: &Server, + ) { // For now just stop the music at this position let world = &player.living_entity.entity.world; @@ -22,6 +29,7 @@ impl PumpkinBlock for JukeboxBlock { async fn on_use_with_item<'a>( &self, + _block: &Block, player: &Player, location: WorldPosition, item: &Item, @@ -49,7 +57,13 @@ impl PumpkinBlock for JukeboxBlock { BlockActionResult::Consume } - async fn on_broken<'a>(&self, player: &Player, location: WorldPosition, _server: &Server) { + async fn on_broken<'a>( + &self, + _block: &Block, + player: &Player, + location: WorldPosition, + _server: &Server, + ) { // For now just stop the music at this position let world = &player.living_entity.entity.world; diff --git a/pumpkin/src/block/blocks/mod.rs b/pumpkin/src/block/blocks/mod.rs index 4bdfa819..b0b12802 100644 --- a/pumpkin/src/block/blocks/mod.rs +++ b/pumpkin/src/block/blocks/mod.rs @@ -1,4 +1,114 @@ +use pumpkin_core::math::position::WorldPosition; +use pumpkin_inventory::{Container, OpenContainer, WindowType}; +use pumpkin_world::block::block_registry::Block; + +use crate::{entity::player::Player, server::Server}; + pub(crate) mod chest; pub(crate) mod crafting_table; pub(crate) mod furnace; pub(crate) mod jukebox; + +/// The standard destroy with container removes the player forcibly from the container, +/// drops items to the floor, and back to the player's inventory if the item stack is in movement. +pub async fn standard_on_broken_with_container( + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, +) { + // TODO: drop all items and back to players inventory if in motion + if let Some(all_container_ids) = server.get_all_container_ids(location, block.clone()).await { + let mut open_containers = server.open_containers.write().await; + for individual_id in all_container_ids { + if let Some(container) = open_containers.get_mut(&u64::from(individual_id)) { + container.clear_all_slots().await; + player.open_container.store(None); + close_all_in_container(player, container).await; + container.clear_all_players(); + } + } + } +} + +/// The standard open container creates a new container if a container of the same block +/// type does not exist at the selected block location. If a container of the same type exists, the player +/// is added to the currently connected players to that container. +pub async fn standard_open_container( + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, + window_type: WindowType, +) { + let entity_id = player.entity_id(); + // If container exists, add player to container, otherwise create new container + if let Some(container_id) = server.get_container_id(location, block.clone()).await { + let mut open_containers = server.open_containers.write().await; + log::debug!("Using previous standard container ID: {}", container_id); + if let Some(container) = open_containers.get_mut(&u64::from(container_id)) { + container.add_player(entity_id); + player.open_container.store(Some(container_id.into())); + } + } else { + let mut open_containers = server.open_containers.write().await; + let new_id = server.new_container_id(); + log::debug!("Creating new standard container ID: {}", new_id); + let open_container = + OpenContainer::new_empty_container::(entity_id, Some(location), Some(block.clone())); + open_containers.insert(new_id.into(), open_container); + player.open_container.store(Some(new_id.into())); + } + player.open_container(server, window_type).await; +} + +pub async fn standard_open_container_unique( + block: &Block, + player: &Player, + location: WorldPosition, + server: &Server, + window_type: WindowType, +) { + let entity_id = player.entity_id(); + let mut open_containers = server.open_containers.write().await; + let mut id_to_use = -1; + + // TODO: we can do better than brute force + for (id, container) in open_containers.iter() { + if let Some(a_block) = container.get_block() { + if a_block.id == block.id && container.all_player_ids().is_empty() { + id_to_use = *id as i64; + } + } + } + + if id_to_use == -1 { + let new_id = server.new_container_id(); + log::debug!("Creating new unqiue container ID: {}", new_id); + let open_container = + OpenContainer::new_empty_container::(entity_id, Some(location), Some(block.clone())); + + open_containers.insert(new_id.into(), open_container); + + player.open_container.store(Some(new_id.into())); + } else { + log::debug!("Using previous unqiue container ID: {}", id_to_use); + if let Some(unique_container) = open_containers.get_mut(&(id_to_use as u64)) { + unique_container.set_location(Some(location)).await; + unique_container.add_player(entity_id); + player + .open_container + .store(Some(id_to_use.try_into().unwrap())); + } + } + drop(open_containers); + player.open_container(server, window_type).await; +} + +pub async fn close_all_in_container(player: &Player, container: &OpenContainer) { + for id in container.all_player_ids() { + if let Some(remote_player) = player.world().get_player_by_entityid(id).await { + remote_player.close_container().await; + } + } +} diff --git a/pumpkin/src/block/pumpkin_block.rs b/pumpkin/src/block/pumpkin_block.rs index 1849ea5a..c0a65c31 100644 --- a/pumpkin/src/block/pumpkin_block.rs +++ b/pumpkin/src/block/pumpkin_block.rs @@ -3,6 +3,8 @@ use crate::entity::player::Player; use crate::server::Server; use async_trait::async_trait; use pumpkin_core::math::position::WorldPosition; +use pumpkin_inventory::OpenContainer; +use pumpkin_world::block::block_registry::Block; use pumpkin_world::item::item_registry::Item; pub trait BlockMetadata { @@ -15,9 +17,17 @@ pub trait BlockMetadata { #[async_trait] pub trait PumpkinBlock: Send + Sync { - async fn on_use<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} + async fn on_use<'a>( + &self, + _block: &Block, + _player: &Player, + _location: WorldPosition, + _server: &Server, + ) { + } async fn on_use_with_item<'a>( &self, + _block: &Block, _player: &Player, _location: WorldPosition, _item: &Item, @@ -26,9 +36,31 @@ pub trait PumpkinBlock: Send + Sync { BlockActionResult::Continue } - async fn on_placed<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} + async fn on_placed<'a>( + &self, + _block: &Block, + _player: &Player, + _location: WorldPosition, + _server: &Server, + ) { + } - async fn on_broken<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} + async fn on_broken<'a>( + &self, + _block: &Block, + _player: &Player, + _location: WorldPosition, + _server: &Server, + ) { + } - async fn on_close<'a>(&self, _player: &Player, _location: WorldPosition, _server: &Server) {} + async fn on_close<'a>( + &self, + _block: &Block, + _player: &Player, + _location: WorldPosition, + _server: &Server, + _container: &OpenContainer, + ) { + } } diff --git a/pumpkin/src/net/container.rs b/pumpkin/src/net/container.rs index 9e06a46d..c157b5e2 100644 --- a/pumpkin/src/net/container.rs +++ b/pumpkin/src/net/container.rs @@ -81,6 +81,7 @@ impl Player { } /// The official Minecraft client is weird, and will always just close *any* window that is opened when this gets sent + // TODO: is this just bc ids are not synced? pub async fn close_container(&self) { let mut inventory = self.inventory().lock().await; inventory.total_opened_containers += 1; diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index ec01ee04..baf90465 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -18,7 +18,7 @@ use pumpkin_core::{ text::TextComponent, GameMode, }; -use pumpkin_inventory::{InventoryError, WindowType}; +use pumpkin_inventory::InventoryError; use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::server::play::SCookieResponse as SPCookieResponse; use pumpkin_protocol::{ @@ -838,11 +838,13 @@ impl Player { // TODO: // This function will in the future be used to keep track of if the client is in a valid state. // But this is not possible yet - pub async fn handle_close_container(&self, server: &Server, packet: SCloseContainer) { - let Some(_window_type) = WindowType::from_i32(packet.window_id.0) else { - self.kick(TextComponent::text("Invalid window ID")).await; - return; - }; + pub async fn handle_close_container(&self, server: &Server, _packet: SCloseContainer) { + // TODO: This should check if player sent this packet before + // let Some(_window_type) = WindowType::from_i32(packet.window_id.0) else { + // log::info!("Closed ID: {}", packet.window_id.0); + // self.kick(TextComponent::text("Invalid window ID")).await; + // return; + // }; // window_id 0 represents both 9x1 Generic AND inventory here let mut inventory = self.inventory().lock().await; @@ -856,7 +858,7 @@ impl Player { if let Some(block) = container.get_block() { server .block_manager - .on_close(&block, self, pos, server) //block, self, location, server) + .on_close(&block, self, pos, server, container) //block, self, location, server) .await; } } diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 2ae5804f..96090ae8 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -1,6 +1,7 @@ use connection_cache::{CachedBranding, CachedStatus}; use key_store::KeyStore; use pumpkin_config::BASIC_CONFIG; +use pumpkin_core::math::position::WorldPosition; use pumpkin_core::math::vector2::Vector2; use pumpkin_core::GameMode; use pumpkin_entity::EntityId; @@ -9,9 +10,11 @@ use pumpkin_inventory::{Container, OpenContainer}; use pumpkin_protocol::client::login::CEncryptionRequest; use pumpkin_protocol::{client::config::CPluginMessage, ClientPacket}; use pumpkin_registry::{DimensionType, Registry}; +use pumpkin_world::block::block_registry::Block; use pumpkin_world::dimension::Dimension; use rand::prelude::SliceRandom; use std::collections::HashMap; +use std::sync::atomic::AtomicU32; use std::{ sync::{ atomic::{AtomicI32, Ordering}, @@ -57,10 +60,13 @@ pub struct Server { /// Caches game registries for efficient access. pub cached_registry: Vec, /// Tracks open containers used for item interactions. + // TODO: should have per player open_containers pub open_containers: RwLock>, pub drag_handler: DragHandler, /// Assigns unique IDs to entities. entity_id: AtomicI32, + /// Assigns unique IDs to containers. + container_id: AtomicU32, /// Manages authentication with a authentication server, if enabled. pub auth_client: Option, /// The server's custom bossbars @@ -102,6 +108,7 @@ impl Server { drag_handler: DragHandler::new(), // 0 is invalid entity_id: 2.into(), + container_id: 0.into(), worlds: vec![Arc::new(world)], dimensions: vec![ DimensionType::Overworld, @@ -192,6 +199,51 @@ impl Server { .cloned() } + /// Returns the first id with a matching location and block type. If this is used with unique + /// blocks, the output will return a random result. + pub async fn get_container_id(&self, location: WorldPosition, block: Block) -> Option { + let open_containers = self.open_containers.read().await; + // TODO: do better than brute force + for (id, container) in open_containers.iter() { + if container.is_location(location) { + if let Some(container_block) = container.get_block() { + if container_block.id == block.id { + log::debug!("Found container id: {}", id); + return Some(*id as u32); + } + } + } + } + + drop(open_containers); + + None + } + + pub async fn get_all_container_ids( + &self, + location: WorldPosition, + block: Block, + ) -> Option> { + let open_containers = self.open_containers.read().await; + let mut matching_container_ids: Vec = vec![]; + // TODO: do better than brute force + for (id, container) in open_containers.iter() { + if container.is_location(location) { + if let Some(container_block) = container.get_block() { + if container_block.id == block.id { + log::debug!("Found matching container id: {}", id); + matching_container_ids.push(*id as u32); + } + } + } + } + + drop(open_containers); + + Some(matching_container_ids) + } + /// Broadcasts a packet to all players in all worlds. /// /// This function sends the specified packet to every connected player in every world managed by the server. @@ -303,6 +355,11 @@ impl Server { self.entity_id.fetch_add(1, Ordering::SeqCst) } + /// Generates a new container id + pub fn new_container_id(&self) -> u32 { + self.container_id.fetch_add(1, Ordering::SeqCst) + } + pub fn get_branding(&self) -> CPluginMessage<'_> { self.server_branding.get_branding() }