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 01/21] 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 02/21] 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 03/21] 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 04/21] 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 05/21] 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 06/21] 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 07/21] 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 08/21] 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 09/21] 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 10/21] 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 11/21] 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 12/21] 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 13/21] 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 14/21] 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 15/21] 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 16/21] 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 17/21] 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 18/21] 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 19/21] 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 20/21] 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 21/21] 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 ); }