From 7eccda00ffa6e782dfe4e5c72828535bb1ab7d88 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Sat, 24 Jan 2026 22:05:50 +0100 Subject: [PATCH] fix: bedrock movement also added a bunch of docs, and fixed bedrock air bubbles constantly shown, and also fix bedrock block states --- Cargo.lock | 32 +- Cargo.toml | 4 +- pumpkin-data/build/block.rs | 205 +++++---- pumpkin-data/src/block_state.rs | 18 +- pumpkin-data/src/blocks.rs | 19 + pumpkin-protocol/src/bedrock/client/mod.rs | 1 + .../src/bedrock/client/move_player.rs | 11 +- .../src/bedrock/client/set_actor_data.rs | 397 ++++++++++++++++++ .../src/bedrock/client/start_game.rs | 6 +- .../src/codec/bedrock_block_pos.rs | 10 +- .../src/java/client/login/cookie_request.rs | 6 +- .../java/client/login/encryption_request.rs | 14 +- .../src/java/client/login/login_disconnect.rs | 6 +- .../src/java/client/login/login_success.rs | 10 +- .../src/java/client/login/plugin_request.rs | 9 + .../src/java/client/login/set_compression.rs | 8 + .../src/java/client/play/acknowledge_block.rs | 9 + .../src/java/client/play/actionbar.rs | 6 + .../java/client/play/block_destroy_stage.rs | 9 + .../src/java/client/play/block_entity_data.rs | 7 + .../src/java/client/play/block_event.rs | 9 + .../src/java/client/play/block_update.rs | 12 +- .../src/java/client/play/boss_event.rs | 6 + .../src/java/client/play/center_chunk.rs | 10 +- .../src/java/client/play/change_difficulty.rs | 13 + .../src/java/client/play/chunk_batch_end.rs | 6 + .../src/java/client/play/chunk_batch_start.rs | 6 + .../src/java/client/play/chunk_data.rs | 5 + .../src/java/client/play/clear_title.rs | 9 + .../src/java/client/play/close_container.rs | 10 +- .../src/java/client/play/combat_death.rs | 6 + .../java/client/play/command_suggestions.rs | 13 + .../src/java/client/play/commands.rs | 9 + .../src/java/client/play/cookie_request.rs | 7 +- .../src/java/client/play/damage_event.rs | 15 + .../src/java/client/play/disconnect.rs | 7 + .../client/play/disguised_chat_message.rs | 10 + .../src/java/client/play/display_objective.rs | 8 + .../src/java/client/play/entity_animation.rs | 8 +- .../src/java/client/play/entity_metadata.rs | 9 +- .../java/client/play/entity_position_sync.rs | 17 +- .../java/client/play/entity_sound_effect.rs | 22 +- .../src/java/client/play/entity_status.rs | 9 + .../src/java/client/play/entity_velocity.rs | 6 + .../src/java/client/play/explode.rs | 16 + .../src/java/client/play/game_event.rs | 7 + .../src/java/client/play/head_rot.rs | 8 + .../src/java/client/play/hurt_animation.rs | 8 + .../client/play/initialize_world_border.rs | 15 + .../src/java/client/play/keep_alive.rs | 8 + .../src/java/client/play/level_event.rs | 13 + .../src/java/client/play/login.rs | 21 +- .../java/client/play/multi_block_update.rs | 9 + .../src/java/client/play/open_screen.rs | 12 + .../src/java/client/play/open_sign_editor.rs | 8 + .../src/java/client/play/particle.rs | 18 +- .../src/java/client/play/ping_response.rs | 3 + .../src/java/client/play/player_abilities.rs | 14 + .../java/client/play/player_chat_message.rs | 30 +- .../java/client/play/player_info_update.rs | 20 + .../src/java/client/play/player_position.rs | 22 +- .../src/java/client/play/player_remove.rs | 8 + .../java/client/play/player_spawn_position.rs | 9 + .../src/java/client/play/remove_entities.rs | 5 + .../src/java/client/play/update_score.rs | 12 + .../src/java/client/play/worldevent.rs | 13 + .../src/java/client/status/ping_response.rs | 10 +- .../src/java/client/status/status_response.rs | 10 +- .../config/acknowledge_finish_config.rs | 2 + .../java/server/config/client_information.rs | 13 +- .../src/java/server/config/cookie_response.rs | 24 +- .../src/java/server/config/plugin_message.rs | 9 + .../server/config/resource_pack_response.rs | 6 + .../src/java/server/handshake/mod.rs | 10 +- .../src/java/server/status/ping_request.rs | 4 + .../src/java/server/status/status_request.rs | 4 + pumpkin-util/src/version.rs | 19 + pumpkin/src/command/mod.rs | 21 + pumpkin/src/entity/player.rs | 2 + pumpkin/src/net/bedrock/play.rs | 23 +- pumpkin/src/net/lan_broadcast.rs | 18 +- pumpkin/src/world/mod.rs | 24 ++ 82 files changed, 1337 insertions(+), 160 deletions(-) create mode 100644 pumpkin-protocol/src/bedrock/client/set_actor_data.rs diff --git a/Cargo.lock b/Cargo.lock index 56d353f29..c0706d615 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -681,9 +681,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.0-rc.12" +version = "0.2.0-rc.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6dcdb44f2c3ee25689ca12a4c19e664fd09f97aeae0bc5043b2dbab6389e308" +checksum = "c7722afd27468475c9b6063dc03a57ef2ca833816981619f8ebe64d38d207eef" dependencies = [ "hybrid-array", ] @@ -767,13 +767,13 @@ dependencies = [ [[package]] name = "digest" -version = "0.11.0-rc.7" +version = "0.11.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca14c221bd9052fd2da7c34a2eeb5ae54732db28be47c35937be71793d675422" +checksum = "2fc1408b7a9f59a7b933faff3e9e7fc15a05a524effd3b3d1601156944c8077f" dependencies = [ "block-buffer 0.11.0", "const-oid 0.10.2", - "crypto-common 0.2.0-rc.12", + "crypto-common 0.2.0-rc.13", "subtle", ] @@ -1208,7 +1208,7 @@ version = "0.13.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1c597ac7d6cc8143e30e83ef70915e7f883b18d8bec2e2b2bce47f5bbb06d57" dependencies = [ - "digest 0.11.0-rc.7", + "digest 0.11.0-rc.8", ] [[package]] @@ -2133,7 +2133,7 @@ dependencies = [ "serde", "serde_json", "sha1", - "sha2 0.11.0-rc.3", + "sha2 0.11.0-rc.4", "signature 2.2.0", "simplelog", "tempfile", @@ -2300,7 +2300,7 @@ dependencies = [ "serde", "serde_json", "serde_json5", - "sha2 0.11.0-rc.3", + "sha2 0.11.0-rc.4", "slotmap", "temp-dir", "thiserror", @@ -2451,7 +2451,7 @@ dependencies = [ "const-oid 0.10.2", "crypto-bigint 0.7.0-rc.21", "crypto-primes", - "digest 0.11.0-rc.7", + "digest 0.11.0-rc.8", "pkcs1", "pkcs8 0.11.0-rc.9", "rand_core 0.10.0-rc-5", @@ -2656,13 +2656,13 @@ dependencies = [ [[package]] name = "sha1" -version = "0.11.0-rc.3" +version = "0.11.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa1ae819b9870cadc959a052363de870944a1646932d274a4e270f64bf79e5ef" +checksum = "9c777f0a122a53fddb0beb6e706771197000b8eb5c9f42b5b850f450ef48c788" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.11.0-rc.7", + "digest 0.11.0-rc.8", ] [[package]] @@ -2678,13 +2678,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.11.0-rc.3" +version = "0.11.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d43dc0354d88b791216bb5c1bfbb60c0814460cc653ae0ebd71f286d0bd927" +checksum = "7535f94fa3339fe9e5e9be6260a909e62af97f6e14b32345ccf79b92b8b81233" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.11.0-rc.7", + "digest 0.11.0-rc.8", ] [[package]] @@ -2728,7 +2728,7 @@ version = "3.0.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c04b70a14ee5f15e2e0c785a5fdb2e9a51138dfe13ba3cf8eab037a9e60b1879" dependencies = [ - "digest 0.11.0-rc.7", + "digest 0.11.0-rc.8", "rand_core 0.10.0-rc-5", ] diff --git a/Cargo.toml b/Cargo.toml index a952db3cf..fa7fac69d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,8 +99,8 @@ rustc-hash = "2.1.1" rustyline = "17.0.2" ruzstd = "0.8.2" serde_json5 = "0.2.1" -sha1 = "=0.11.0-rc.3" -sha2 = "=0.11.0-rc.3" +sha1 = "=0.11.0-rc.4" +sha2 = "=0.11.0-rc.4" signature = "2.2.0" simplelog = "0.12.2" slotmap = "1.1" diff --git a/pumpkin-data/build/block.rs b/pumpkin-data/build/block.rs index b095463c6..21b704713 100644 --- a/pumpkin-data/build/block.rs +++ b/pumpkin-data/build/block.rs @@ -681,17 +681,6 @@ pub(crate) fn build() -> TokenStream { let mut existing_item_ids: std::collections::HashSet = std::collections::HashSet::new(); for block in blocks_assets.blocks { - for state in &block.states { - if state.has_random_ticks() { - let state_id = LitInt::new(&state.id.to_string(), Span::call_site()); - random_tick_states.push(state_id); - } - if state.is_air() { - let state_id = LitInt::new(&state.id.to_string(), Span::call_site()); - air_states.push(state_id); - } - } - let mut property_collection = HashSet::new(); let mut property_mapping = Vec::new(); @@ -731,16 +720,6 @@ pub(crate) fn build() -> TokenStream { }); } - if !property_collection.is_empty() { - let mut property_collection_vec: Vec = property_collection.into_iter().collect(); - property_collection_vec.sort_unstable(); - - property_collection_map - .entry(property_collection_vec) - .or_insert_with(|| PropertyCollectionData::from_mappings(property_mapping)) - .add_block(block.name.clone(), block.id); - } - let const_ident = format_ident!("{}", const_block_name_from_block_name(&block.name)); let name_str = &block.name; let id_lit = LitInt::new(&block.id.to_string(), Span::call_site()); @@ -770,22 +749,78 @@ pub(crate) fn build() -> TokenStream { name => name, }; - let (state_count, id) = *be_blocks.get(be_name).unwrap_or(&(0, 1)); + let be_state_list = be_blocks.get(be_name); for (i, state) in block.states.iter().enumerate() { - if state_count != 0 { - let bedrock_val = if state_count > i as u32 { - id as u16 + i as u16 - } else { - id as u16 - }; - block_state_to_bedrock.push((state.id, bedrock_val)); + if state.has_random_ticks() { + let state_id = LitInt::new(&state.id.to_string(), Span::call_site()); + random_tick_states.push(state_id); + } + if state.is_air() { + let state_id = LitInt::new(&state.id.to_string(), Span::call_site()); + air_states.push(state_id); } + let mut matched_be_id = 1; + + if let Some(be_variants) = be_state_list { + let mut temp_index = i as u16; + let mut java_props_for_this_state = BTreeMap::new(); + + for mapping in property_mapping.iter().rev() { + match &mapping.property_type { + PropertyType::Bool => { + let val = temp_index % 2; + temp_index /= 2; + java_props_for_this_state.insert( + mapping.original_name.clone(), + if val == 0 { "true" } else { "false" }.to_string(), + ); + } + PropertyType::Enum { name } => { + let enum_info = property_enums.get(name).unwrap(); + let count = enum_info.values.len() as u16; + let val_idx = temp_index % count; + temp_index /= count; + + let raw_val = &enum_info.values[val_idx as usize]; + let val_str = if raw_val.starts_with('L') { + raw_val.strip_prefix('L').unwrap().to_string() + } else { + raw_val.clone() + }; + java_props_for_this_state + .insert(mapping.original_name.clone(), val_str); + } + } + } + + matched_be_id = be_variants + .iter() + .find(|(_, be_props)| { + java_props_for_this_state + .iter() + .all(|(k, v)| be_props.get(k).map(|be_v| be_v == v).unwrap_or(false)) + }) + .map(|(id, _)| *id) + .unwrap_or_else(|| be_variants.first().map(|(id, _)| *id).unwrap_or(1)); + } + + block_state_to_bedrock.push((state.id, matched_be_id)); raw_id_from_state_id_array.push((state.id, id_lit.clone())); state_from_state_id_array.push((const_ident.clone(), i, state.id)); } + if !property_collection.is_empty() { + let mut property_collection_vec: Vec = property_collection.into_iter().collect(); + property_collection_vec.sort_unstable(); + + property_collection_map + .entry(property_collection_vec) + .or_insert_with(|| PropertyCollectionData::from_mappings(property_mapping)) + .add_block(block.name.clone(), block.id); + } + if existing_item_ids.insert(item_id) { block_from_item_id_arms.push(quote! { #item_id => Some(&Self::#const_ident), @@ -1130,69 +1165,97 @@ pub(crate) fn build() -> TokenStream { } } -fn get_be_data_from_nbt(reader: &mut R) -> BTreeMap { - let mut block_data: BTreeMap = BTreeMap::new(); +#[expect(clippy::type_complexity)] +fn get_be_data_from_nbt( + reader: &mut R, +) -> BTreeMap)>> { + let mut block_data: BTreeMap)>> = BTreeMap::new(); let mut current_id = 0; - while read_byte(reader) == 10 { + let read_nbt_string = |reader: &mut R| -> String { let len = read_varint(reader); let mut buf = vec![0; len as usize]; reader.read_exact(&mut buf).unwrap(); + String::from_utf8(buf).unwrap() + }; - let mut name = String::new(); - let mut byte = read_byte(reader); + let read_byte_safe = |reader: &mut R| -> Option { + let mut buf = [0; 1]; + if reader.read_exact(&mut buf).is_ok() { + Some(buf[0]) + } else { + None + } + }; - while byte != 0 { - let mut name_buf = vec![0; read_varint(reader) as usize]; - reader.read_exact(&mut name_buf).unwrap(); - let cp_name = String::from_utf8(name_buf).unwrap(); + while let Some(tag_id) = read_byte_safe(reader) { + if tag_id != 10 { + break; + } // Tag_Compound (10) required - match cp_name.as_str() { + // Read Root Name (usually empty string in palette) + let _root_name = read_nbt_string(reader); + + let mut block_name = String::new(); + let mut properties = BTreeMap::new(); + + loop { + let field_type = read_byte(reader); + if field_type == 0 { + break; + } // Tag_End (0) + + let field_name = read_nbt_string(reader); + + match field_name.as_str() { "name" => { - let mut name_buf = vec![0; read_varint(reader) as usize]; - reader.read_exact(&mut name_buf).unwrap(); - name = String::from_utf8(name_buf) - .unwrap() + let raw_name = read_nbt_string(reader); + block_name = raw_name .strip_prefix("minecraft:") - .unwrap() + .unwrap_or(&raw_name) .to_string(); } - "states" => { - let mut byte = read_byte(reader); - while byte != 0 { - let b = &mut vec![0; read_varint(reader) as usize]; - reader.read_exact(b).unwrap(); - - match byte { - 8 => { - let b = &mut vec![0; read_varint(reader) as usize]; - reader.read_exact(b).unwrap(); - } - 3 => { - read_varint(reader); - } - 1 => { - read_byte(reader); - } - _ => panic!("{}", byte), - } - byte = read_byte(reader); + "states" => loop { + let prop_type = read_byte(reader); + if prop_type == 0 { + break; } - } + + let prop_key = read_nbt_string(reader); + + let prop_val = match prop_type { + 1 => { + let val = read_byte(reader); + if val == 1 { + "true".to_string() + } else { + "false".to_string() + } + } + 3 => read_varint(reader).to_string(), + 8 => read_nbt_string(reader), + _ => panic!("Unknown property type {} for key {}", prop_type, prop_key), + }; + + properties.insert(prop_key, prop_val); + }, "version" => { read_varint(reader); } - _ => panic!(), + _ => panic!("Unexpected root field: {}", field_name), } - byte = read_byte(reader); } - block_data - .entry(name) - .and_modify(|(v, _)| *v += 1) - .or_insert((1, current_id)); + if !block_name.is_empty() { + block_data + .entry(block_name) + .or_default() + .push((current_id, properties)); + } + current_id += 1; } + block_data } diff --git a/pumpkin-data/src/block_state.rs b/pumpkin-data/src/block_state.rs index 47f089030..2e150118a 100644 --- a/pumpkin-data/src/block_state.rs +++ b/pumpkin-data/src/block_state.rs @@ -3,19 +3,35 @@ use pumpkin_util::math::vector3::Vector3; use crate::block_properties::{COLLISION_SHAPES, Instrument}; use crate::{Block, BlockDirection, CollisionShape}; +/// Represents a specific state of a block, including its properties and physical behaviors. +/// +/// A single `Block` (like a Hopper) can have multiple `BlockState`s (e.g., pointing North, +/// South, or being powered). This struct is optimized for high-speed lookups during +/// physics and lighting calculations. #[derive(Debug)] pub struct BlockState { + /// The global palette ID used for network serialization and chunk storage. pub id: u16, + /// Bit-flags representing boolean or enum properties (e.g., `waterlogged`, `lit`, `facing`). pub state_flags: u16, + /// Cached flags for each of the 6 sides to speed up ambient occlusion and face culling. pub side_flags: u8, + /// The note block instrument produced when this block is placed underneath one. pub instrument: Instrument, + /// The light level emitted by this block, ranging from 0 to 15. pub luminance: u8, + /// Defines how the block reacts to being pushed or pulled by a piston. pub piston_behavior: PistonBehavior, + /// Overrides the base block hardness for this specific state if necessary. pub hardness: f32, + /// Indices into a global voxel-shape registry for physical entity collisions. pub collision_shapes: &'static [u16], + /// Indices into a global voxel-shape registry for the selection highlight box. pub outline_shapes: &'static [u16], + /// How much light is subtracted as it passes through this block (0 for transparent, 15 for opaque). pub opacity: u8, - /// u16::MAX is used as None + /// The ID of the block entity associated with this state. + /// Set to `u16::MAX` if the block does not hold NBT data. pub block_entity_type: u16, } diff --git a/pumpkin-data/src/blocks.rs b/pumpkin-data/src/blocks.rs index b7c45e525..bbeb3a515 100644 --- a/pumpkin-data/src/blocks.rs +++ b/pumpkin-data/src/blocks.rs @@ -9,21 +9,40 @@ use pumpkin_util::{ }; use std::hash::{Hash, Hasher}; +/// Represents the static definition of a Minecraft block type. +/// +/// This struct contains the base properties shared by all instances of a block +/// Data-driven attributes like `hardness` and `blast_resistance` are defined here, +/// while specific orientations or variations are stored in the associated `BlockState`. #[derive(Debug)] pub struct Block { + /// The numeric ID used for internal registry mapping. pub id: u16, + /// The unique namespaced ID (e.g., "diamond_ore"). pub name: &'static str, + /// The key used for client-side localization (e.g., "block.minecraft.diamond_ore"). pub translation_key: &'static str, + /// How hard the block is to break. A value of -1.0 indicates an unbreakable block (e.g., Bedrock). pub hardness: f32, + /// The block's resistance to explosions. pub blast_resistance: f32, + /// The friction coefficient. Default is 0.6; Ice is 0.98. pub slipperiness: f32, + /// How much this block affects the speed of an entity walking on it (e.g., Soul Sand). pub velocity_multiplier: f32, + /// How much this block affects an entity's jump height (e.g., Honey Blocks). pub jump_velocity_multiplier: f32, + /// The ID of the item form of this block, used for inventory and drops. pub item_id: u16, + /// The initial state of the block when placed without extra data. pub default_state: &'static BlockState, + /// A list of all possible valid states (properties like rotation, waterlogged, etc.) for this block. pub states: &'static [BlockState], + /// Fire behavior settings. If `None`, the block is not flammable. pub flammable: Option, + /// Defines the items dropped when this block is destroyed. pub loot_table: Option, + /// Defines the amount of XP dropped when the block is mined (e.g., Coal or Diamond). pub experience: Option, } diff --git a/pumpkin-protocol/src/bedrock/client/mod.rs b/pumpkin-protocol/src/bedrock/client/mod.rs index 6f6547f3c..fa89aa6e7 100644 --- a/pumpkin-protocol/src/bedrock/client/mod.rs +++ b/pumpkin-protocol/src/bedrock/client/mod.rs @@ -15,6 +15,7 @@ pub mod player_hotbar; pub mod raknet; pub mod resource_pack_stack; pub mod resource_packs_info; +pub mod set_actor_data; pub mod set_actor_motion; pub mod set_player_gamemode; pub mod set_time; diff --git a/pumpkin-protocol/src/bedrock/client/move_player.rs b/pumpkin-protocol/src/bedrock/client/move_player.rs index 5ffa432f1..e2b29552a 100644 --- a/pumpkin-protocol/src/bedrock/client/move_player.rs +++ b/pumpkin-protocol/src/bedrock/client/move_player.rs @@ -11,9 +11,16 @@ pub struct CMovePlayer { pub position: Vector3, pub pitch: f32, pub yaw: f32, - pub y_head_rotation: f32, - pub position_mode: u8, + pub head_yaw: f32, + pub mode: u8, pub on_ground: bool, pub riding_runtime_id: VarULong, pub tick: VarULong, } + +impl CMovePlayer { + pub const MODE_NORMAL: u8 = 0; + pub const MODE_RESET: u8 = 1; + pub const MODE_TELEPORT: u8 = 2; + pub const MODE_PITCH: u8 = 3; +} diff --git a/pumpkin-protocol/src/bedrock/client/set_actor_data.rs b/pumpkin-protocol/src/bedrock/client/set_actor_data.rs new file mode 100644 index 000000000..f4ef75570 --- /dev/null +++ b/pumpkin-protocol/src/bedrock/client/set_actor_data.rs @@ -0,0 +1,397 @@ +use std::{collections::HashMap, io::Write}; + +use crate::{ + codec::{var_int::VarInt, var_long::VarLong, var_uint::VarUInt, var_ulong::VarULong}, + serial::PacketWrite, +}; +use pumpkin_macros::packet; +use std::io::Error; + +#[derive(PacketWrite)] +#[packet(39)] // ProtocolInfo::SET_ACTOR_DATA_PACKET is 39 +pub struct CSetActorData { + /// The unique runtime ID of the entity being updated + pub actor_runtime_id: VarULong, + /// A map of entity metadata properties (e.g., flags, name tags, scale) + pub metadata: EntityMetadata, + /// Dynamic properties synced between client and server + pub synced_properties: PropertySyncData, + /// The server tick at which this update occurred + pub tick: VarULong, +} + +#[derive(Default)] +pub struct EntityMetadata(pub HashMap); + +impl EntityMetadata { + pub fn set(&mut self, key: u32, value: MetadataValue) { + self.0.insert(key, value); + } + + pub fn set_flag(&mut self, flag_index: u32) { + let (key, index) = if flag_index < 64 { + (entity_data_key::FLAGS, flag_index) + } else { + (entity_data_key::FLAGS_TWO, flag_index - 64) + }; + + let current_value = match self.0.get(&key) { + Some(MetadataValue::Long(v)) => *v, + _ => 0, + }; + + self.0 + .insert(key, MetadataValue::Long(current_value | (1 << index))); + } +} + +impl PacketWrite for EntityMetadata { + fn write(&self, writer: &mut W) -> Result<(), Error> { + VarUInt(self.0.len() as u32).write(writer)?; + + for (key, value) in &self.0 { + VarUInt(*key).write(writer)?; + VarUInt(value.type_id()).write(writer)?; + value.write(writer)?; + } + Ok(()) + } +} + +pub enum MetadataValue { + Byte(i8), + Short(i16), + Int(i32), + Float(f32), + String(String), + CompoundTag, + BlockPos, + Long(i64), + Vec3, +} + +impl MetadataValue { + pub fn type_id(&self) -> u32 { + match self { + Self::Byte(_) => 0, + Self::Short(_) => 1, + Self::Int(_) => 2, + Self::Float(_) => 3, + Self::String(_) => 4, + Self::CompoundTag => 5, + Self::BlockPos => 6, + Self::Long(_) => 7, + Self::Vec3 => 8, + } + } + + pub fn write(&self, writer: &mut W) -> Result<(), Error> { + match self { + Self::Byte(v) => v.write(writer), + Self::Short(v) => v.write(writer), + // Type 2: Signed VarInt + Self::Int(v) => VarInt(*v).write(writer), + // Type 3: LE Float (4 bytes) + Self::Float(v) => writer.write_all(&v.to_le_bytes()), + // Type 4: VarInt length + UTF8 String + Self::String(v) => v.write(writer), + Self::CompoundTag => todo!(), + Self::BlockPos => todo!(), + Self::Long(v) => VarLong(*v).write(writer), + Self::Vec3 => todo!(), + } + } +} + +pub struct PropertySyncData { + pub int_properties: std::collections::HashMap, + pub float_properties: std::collections::HashMap, +} + +impl PacketWrite for PropertySyncData { + fn write(&self, writer: &mut W) -> Result<(), Error> { + // Int Properties + VarUInt(self.int_properties.len() as u32).write(writer)?; + for (key, value) in &self.int_properties { + VarUInt(*key).write(writer)?; + VarInt(*value).write(writer)?; // Signed VarInt + } + + // Float Properties + VarUInt(self.float_properties.len() as u32).write(writer)?; + for (key, value) in &self.float_properties { + VarUInt(*key).write(writer)?; + writer.write_all(&value.to_le_bytes())?; // LE Float + } + Ok(()) + } +} +pub mod entity_data_key { + pub const FLAGS: u32 = 0; + pub const STRUCTURAL_INTEGRITY: u32 = 1; + pub const VARIANT: u32 = 2; + pub const COLOR_INDEX: u32 = 3; + pub const NAME: u32 = 4; + pub const OWNER: u32 = 5; + pub const TARGET: u32 = 6; + pub const AIR_SUPPLY: u32 = 7; + pub const EFFECT_COLOR: u32 = 8; + pub const EFFECT_AMBIENCE: u32 = 9; + pub const JUMP_DURATION: u32 = 10; + pub const HURT: u32 = 11; + pub const HURT_DIRECTION: u32 = 12; + pub const ROW_TIME_LEFT: u32 = 13; + pub const ROW_TIME_RIGHT: u32 = 14; + pub const VALUE: u32 = 15; + pub const DISPLAY_TILE_RUNTIME_ID: u32 = 16; + pub const DISPLAY_OFFSET: u32 = 17; + pub const CUSTOM_DISPLAY: u32 = 18; + pub const SWELL: u32 = 19; + pub const OLD_SWELL: u32 = 20; + pub const SWELL_DIRECTION: u32 = 21; + pub const CHARGE_AMOUNT: u32 = 22; + pub const CARRY_BLOCK_RUNTIME_ID: u32 = 23; + pub const CLIENT_EVENT: u32 = 24; + pub const USING_ITEM: u32 = 25; + pub const PLAYER_FLAGS: u32 = 26; + pub const PLAYER_INDEX: u32 = 27; + pub const BED_POSITION: u32 = 28; + pub const POWER_X: u32 = 29; + pub const POWER_Y: u32 = 30; + pub const POWER_Z: u32 = 31; + pub const AUX_POWER: u32 = 32; + pub const FISH_X: u32 = 33; + pub const FISH_Z: u32 = 34; + pub const FISH_ANGLE: u32 = 35; + pub const AUX_VALUE_DATA: u32 = 36; + pub const LEASH_HOLDER: u32 = 37; + pub const SCALE: u32 = 38; + pub const HAS_NPC: u32 = 39; + pub const NPC_DATA: u32 = 40; + pub const ACTIONS: u32 = 41; + pub const AIR_SUPPLY_MAX: u32 = 42; + pub const MARK_VARIANT: u32 = 43; + pub const CONTAINER_TYPE: u32 = 44; + pub const CONTAINER_SIZE: u32 = 45; + pub const CONTAINER_STRENGTH_MODIFIER: u32 = 46; + pub const BLOCK_TARGET: u32 = 47; + pub const INVULNERABLE_TICKS: u32 = 48; + pub const TARGET_A: u32 = 49; + pub const TARGET_B: u32 = 50; + pub const TARGET_C: u32 = 51; + pub const AERIAL_ATTACK: u32 = 52; + pub const WIDTH: u32 = 53; + pub const HEIGHT: u32 = 54; + pub const FUSE_TIME: u32 = 55; + pub const SEAT_OFFSET: u32 = 56; + pub const SEAT_LOCK_PASSENGER_ROTATION: u32 = 57; + pub const SEAT_LOCK_PASSENGER_ROTATION_DEGREES: u32 = 58; + pub const SEAT_ROTATION_OFFSET: u32 = 59; + pub const SEAT_ROTATION_OFFSET_DEGREES: u32 = 60; + pub const DATA_RADIUS: u32 = 61; + pub const DATA_WAITING: u32 = 62; + pub const DATA_PARTICLE: u32 = 63; + pub const PEEK_ID: u32 = 64; + pub const ATTACH_FACE: u32 = 65; + pub const ATTACHED: u32 = 66; + pub const ATTACHED_POSITION: u32 = 67; + pub const TRADE_TARGET: u32 = 68; + pub const CAREER: u32 = 69; + pub const HAS_COMMAND_BLOCK: u32 = 70; + pub const COMMAND_NAME: u32 = 71; + pub const LAST_COMMAND_OUTPUT: u32 = 72; + pub const TRACK_COMMAND_OUTPUT: u32 = 73; + pub const CONTROLLING_SEAT_INDEX: u32 = 74; + pub const STRENGTH: u32 = 75; + pub const STRENGTH_MAX: u32 = 76; + pub const DATA_SPELL_CASTING_COLOR: u32 = 77; + pub const DATA_LIFETIME_TICKS: u32 = 78; + pub const POSE_INDEX: u32 = 79; + pub const DATA_TICK_OFFSET: u32 = 80; + pub const ALWAYS_SHOW_NAME_TAG: u32 = 81; + pub const COLOR_TWO_INDEX: u32 = 82; + pub const NAME_AUTHOR: u32 = 83; + pub const SCORE: u32 = 84; + pub const BALLOON_ANCHOR: u32 = 85; + pub const PUFFED_STATE: u32 = 86; + pub const BUBBLE_TIME: u32 = 87; + pub const AGENT: u32 = 88; + pub const SITTING_AMOUNT: u32 = 89; + pub const SITTING_AMOUNT_PREVIOUS: u32 = 90; + pub const EATING_COUNTER: u32 = 91; + pub const FLAGS_TWO: u32 = 92; + pub const LAYING_AMOUNT: u32 = 93; + pub const LAYING_AMOUNT_PREVIOUS: u32 = 94; + pub const DATA_DURATION: u32 = 95; + pub const DATA_SPAWN_TIME: u32 = 96; + pub const DATA_CHANGE_RATE: u32 = 97; + pub const DATA_CHANGE_ON_PICKUP: u32 = 98; + pub const DATA_PICKUP_COUNT: u32 = 99; + pub const INTERACT_TEXT: u32 = 100; + pub const TRADE_TIER: u32 = 101; + pub const MAX_TRADE_TIER: u32 = 102; + pub const TRADE_EXPERIENCE: u32 = 103; + pub const SKIN_ID: u32 = 104; + pub const SPAWNING_FRAMES: u32 = 105; + pub const COMMAND_BLOCK_TICK_DELAY: u32 = 106; + pub const COMMAND_BLOCK_EXECUTE_ON_FIRST_TICK: u32 = 107; + pub const AMBIENT_SOUND_INTERVAL: u32 = 108; + pub const AMBIENT_SOUND_INTERVAL_RANGE: u32 = 109; + pub const AMBIENT_SOUND_EVENT_NAME: u32 = 110; + pub const FALL_DAMAGE_MULTIPLIER: u32 = 111; + pub const NAME_RAW_TEXT: u32 = 112; + pub const CAN_RIDE_TARGET: u32 = 113; + pub const LOW_TIER_CURED_TRADE_DISCOUNT: u32 = 114; + pub const HIGH_TIER_CURED_TRADE_DISCOUNT: u32 = 115; + pub const NEARBY_CURED_TRADE_DISCOUNT: u32 = 116; + pub const NEARBY_CURED_DISCOUNT_TIME_STAMP: u32 = 117; + pub const HIT_BOX: u32 = 118; + pub const IS_BUOYANT: u32 = 119; + pub const FREEZING_EFFECT_STRENGTH: u32 = 120; + pub const BUOYANCY_DATA: u32 = 121; + pub const GOAT_HORN_COUNT: u32 = 122; + pub const BASE_RUNTIME_ID: u32 = 123; + pub const MOVEMENT_SOUND_DISTANCE_OFFSET: u32 = 124; + pub const HEARTBEAT_INTERVAL_TICKS: u32 = 125; + pub const HEARTBEAT_SOUND_EVENT: u32 = 126; + pub const PLAYER_LAST_DEATH_POSITION: u32 = 127; + pub const PLAYER_LAST_DEATH_DIMENSION: u32 = 128; + pub const PLAYER_HAS_DIED: u32 = 129; + pub const COLLISION_BOX: u32 = 130; + pub const VISIBLE_MOB_EFFECTS: u32 = 131; + pub const FILTERED_NAME: u32 = 132; + pub const ENTER_BED_POSITION: u32 = 133; + pub const SEAT_THIRD_PERSON_CAMERA_RADIUS: u32 = 134; + pub const SEAT_CAMERA_RELAX_DISTANCE_SMOOTHING: u32 = 135; +} + +pub mod entity_data_flag { + pub const ON_FIRE: u32 = 0; + pub const SNEAKING: u32 = 1; + pub const RIDING: u32 = 2; + pub const SPRINTING: u32 = 3; + pub const USING_ITEM: u32 = 4; + pub const INVISIBLE: u32 = 5; + pub const TEMPTED: u32 = 6; + pub const IN_LOVE: u32 = 7; + pub const SADDLED: u32 = 8; + pub const POWERED: u32 = 9; + pub const IGNITED: u32 = 10; + pub const BABY: u32 = 11; + pub const CONVERTING: u32 = 12; + pub const CRITICAL: u32 = 13; + pub const SHOW_NAME: u32 = 14; + pub const ALWAYS_SHOW_NAME: u32 = 15; + pub const NO_AI: u32 = 16; + pub const SILENT: u32 = 17; + pub const WALL_CLIMBING: u32 = 18; + pub const CLIMB: u32 = 19; + pub const SWIM: u32 = 20; + pub const FLY: u32 = 21; + pub const WALK: u32 = 22; + pub const RESTING: u32 = 23; + pub const SITTING: u32 = 24; + pub const ANGRY: u32 = 25; + pub const INTERESTED: u32 = 26; + pub const CHARGED: u32 = 27; + pub const TAMED: u32 = 28; + pub const ORPHANED: u32 = 29; + pub const LEASHED: u32 = 30; + pub const SHEARED: u32 = 31; + pub const GLIDING: u32 = 32; + pub const ELDER: u32 = 33; + pub const MOVING: u32 = 34; + pub const BREATHING: u32 = 35; + pub const CHESTED: u32 = 36; + pub const STACKABLE: u32 = 37; + pub const SHOW_BOTTOM: u32 = 38; + pub const STANDING: u32 = 39; + pub const SHAKING: u32 = 40; + pub const IDLING: u32 = 41; + pub const CASTING: u32 = 42; + pub const CHARGING: u32 = 43; + pub const KEYBOARD_CONTROLLED: u32 = 44; + pub const POWER_JUMP: u32 = 45; + pub const DASH: u32 = 46; + pub const LINGERING: u32 = 47; + pub const HAS_COLLISION: u32 = 48; + pub const HAS_GRAVITY: u32 = 49; + pub const FIRE_IMMUNE: u32 = 50; + pub const DANCING: u32 = 51; + pub const ENCHANTED: u32 = 52; + pub const RETURN_TRIDENT: u32 = 53; + pub const CONTAINER_PRIVATE: u32 = 54; + pub const TRANSFORMING: u32 = 55; + pub const DAMAGE_NEARBY_MOBS: u32 = 56; + pub const SWIMMING: u32 = 57; + pub const BRIBED: u32 = 58; + pub const PREGNANT: u32 = 59; + pub const LAYING_EGG: u32 = 60; + pub const PASSENGER_CAN_PICK: u32 = 61; + pub const TRANSITION_SITTING: u32 = 62; + pub const EATING: u32 = 63; + pub const LAYING_DOWN: u32 = 64; + pub const SNEEZING: u32 = 65; + pub const TRUSTING: u32 = 66; + pub const ROLLING: u32 = 67; + pub const SCARED: u32 = 68; + pub const IN_SCAFFOLDING: u32 = 69; + pub const OVER_SCAFFOLDING: u32 = 70; + pub const DESCEND_THROUGH_BLOCK: u32 = 71; + pub const BLOCKING: u32 = 72; + pub const TRANSITION_BLOCKING: u32 = 73; + pub const BLOCKED_USING_SHIELD: u32 = 74; + pub const BLOCKED_USING_DAMAGED_SHIELD: u32 = 75; + pub const SLEEPING: u32 = 76; + pub const WANTS_TO_WAKE: u32 = 77; + pub const TRADE_INTEREST: u32 = 78; + pub const DOOR_BREAKER: u32 = 79; + pub const BREAKING_OBSTRUCTION: u32 = 80; + pub const DOOR_OPENER: u32 = 81; + pub const CAPTAIN: u32 = 82; + pub const STUNNED: u32 = 83; + pub const ROARING: u32 = 84; + pub const DELAYED_ATTACK: u32 = 85; + pub const AVOIDING_MOBS: u32 = 86; + pub const AVOIDING_BLOCK: u32 = 87; + pub const FACING_TARGET_TO_RANGE_ATTACK: u32 = 88; + pub const HIDDEN_WHEN_INVISIBLE: u32 = 89; + pub const IN_UI: u32 = 90; + pub const STALKING: u32 = 91; + pub const EMOTING: u32 = 92; + pub const CELEBRATING: u32 = 93; + pub const ADMIRING: u32 = 94; + pub const CELEBRATING_SPECIAL: u32 = 95; + pub const OUT_OF_CONTROL: u32 = 96; + pub const RAM_ATTACK: u32 = 97; + pub const PLAYING_DEAD: u32 = 98; + pub const IN_ASCENDING_BLOCK: u32 = 99; + pub const OVER_DESCENDING_BLOCK: u32 = 100; + pub const CROAKING: u32 = 101; + pub const DIGEST_MOB: u32 = 102; + pub const JUMP_GOAL: u32 = 103; + pub const EMERGING: u32 = 104; + pub const SNIFFING: u32 = 105; + pub const DIGGING: u32 = 106; + pub const SONIC_BOOM: u32 = 107; + pub const HAS_DASH_TIMEOUT: u32 = 108; + pub const PUSH_TOWARDS_CLOSEST_SPACE: u32 = 109; + pub const SCENTING: u32 = 110; + pub const RISING: u32 = 111; + pub const FEELING_HAPPY: u32 = 112; + pub const SEARCHING: u32 = 113; + pub const CRAWLING: u32 = 114; + pub const TIMER_FLAG_1: u32 = 115; + pub const TIMER_FLAG_2: u32 = 116; + pub const TIMER_FLAG_3: u32 = 117; + pub const BODY_ROTATION_BLOCKED: u32 = 118; + pub const RENDER_WHEN_INVISIBLE: u32 = 119; + pub const BODY_ROTATION_AXIS_ALIGNED: u32 = 120; + pub const COLLIDABLE: u32 = 121; + pub const WASD_AIR_CONTROLLED: u32 = 122; + pub const DOES_SERVER_AUTH_ONLY_DISMOUNT: u32 = 123; + pub const BODY_ROTATION_ALWAYS_FOLLOWS_HEAD: u32 = 124; + pub const CAN_USE_VERTICAL_MOVEMENT_ACTION: u32 = 125; + pub const ROTATION_LOCKED_TO_VEHICLE: u32 = 126; + pub const COUNT: u32 = 127; +} diff --git a/pumpkin-protocol/src/bedrock/client/start_game.rs b/pumpkin-protocol/src/bedrock/client/start_game.rs index c68827772..83164b8d5 100644 --- a/pumpkin-protocol/src/bedrock/client/start_game.rs +++ b/pumpkin-protocol/src/bedrock/client/start_game.rs @@ -15,8 +15,12 @@ use uuid::Uuid; #[derive(PacketWrite)] #[packet(11)] pub struct CStartGame { - // https://mojang.github.io/bedrock-protocol-docs/html/StartGamePacket.html + // The unique ID is a value that remains consistent across + // different sessions of the same world, but most servers simply fill the runtime ID of the entity out for + // this field. pub entity_id: VarLong, + // The runtime ID is unique for each world session, and + // entities are generally identified in packets using this runtime ID. pub runtime_entity_id: VarULong, pub player_gamemode: GameMode, pub position: Vector3, diff --git a/pumpkin-protocol/src/codec/bedrock_block_pos.rs b/pumpkin-protocol/src/codec/bedrock_block_pos.rs index dbee597bd..00243aa98 100644 --- a/pumpkin-protocol/src/codec/bedrock_block_pos.rs +++ b/pumpkin-protocol/src/codec/bedrock_block_pos.rs @@ -7,10 +7,14 @@ use crate::{ serial::PacketWrite, }; -/// Bedrocks Writes and Reads BlockPos types in Packets differently +/// A wrapper for `BlockPos` that handles Bedrock-specific network serialization. +/// +/// Bedrock Edition encodes coordinates differently than Java Edition, using +/// VarInt's to save bandwidth. pub struct NetworkPos(pub BlockPos); impl NetworkPos { + /// Writes coordinates where all axes (X, Y, Z) are treated as signed `VarInt`. pub fn write_signed(&self, writer: &mut W) -> Result<(), Error> { VarInt(self.0.0.x).write(writer)?; VarInt(self.0.0.y).write(writer)?; @@ -19,6 +23,10 @@ impl NetworkPos { } impl PacketWrite for NetworkPos { + /// The default Bedrock network encoding for block positions. + /// + /// Note: X and Z are signed (`VarInt`), but Y is unsigned (`VarUInt`). + /// This matches the standard Bedrock block action and block update packets fn write(&self, writer: &mut W) -> Result<(), Error> { VarInt(self.0.0.x).write(writer)?; VarUInt(self.0.0.y as u32).write(writer)?; diff --git a/pumpkin-protocol/src/java/client/login/cookie_request.rs b/pumpkin-protocol/src/java/client/login/cookie_request.rs index b7d82d3a2..92ee6bfc1 100644 --- a/pumpkin-protocol/src/java/client/login/cookie_request.rs +++ b/pumpkin-protocol/src/java/client/login/cookie_request.rs @@ -3,10 +3,14 @@ use pumpkin_macros::packet; use pumpkin_util::resource_location::ResourceLocation; use serde::Serialize; +/// Sent by the server to retrieve a previously stored cookie from the client. +/// +/// This occurs during the login phase, allowing the server to identify +/// returning players or retrieve session data stored during a previous visit. #[derive(Serialize)] #[packet(LOGIN_COOKIE_REQUEST)] -/// Requests a cookie that was previously stored. pub struct CLoginCookieRequest<'a> { + /// The unique identifier of the cookie being requested. pub key: &'a ResourceLocation, } diff --git a/pumpkin-protocol/src/java/client/login/encryption_request.rs b/pumpkin-protocol/src/java/client/login/encryption_request.rs index 4705b03d0..d81f21139 100644 --- a/pumpkin-protocol/src/java/client/login/encryption_request.rs +++ b/pumpkin-protocol/src/java/client/login/encryption_request.rs @@ -2,12 +2,24 @@ use pumpkin_data::packet::clientbound::LOGIN_HELLO; use pumpkin_macros::packet; use serde::{Deserialize, Serialize}; +/// Sent by the server to initiate the encryption handshake. +/// +/// This packet provides the client with the server's public key and a +/// verification token, allowing the client to generate a shared secret +/// for secure communication. #[derive(Serialize, Deserialize)] #[packet(LOGIN_HELLO)] pub struct CEncryptionRequest<'a> { - pub server_id: &'a str, // 20 + /// The server's ID string. In modern Minecraft, this is usually + /// an empty string unless the server is using legacy authentication. + pub server_id: &'a str, + /// The server's DER-encoded RSA public key. pub public_key: &'a [u8], + /// A random bitstring used to verify that the client can correctly + /// encrypt data with the server's public key. pub verify_token: &'a [u8], + /// Indicates whether the server is in "online mode" and requires + /// Mojang authentication. pub should_authenticate: bool, } diff --git a/pumpkin-protocol/src/java/client/login/login_disconnect.rs b/pumpkin-protocol/src/java/client/login/login_disconnect.rs index cee3b9b8d..317b6c483 100644 --- a/pumpkin-protocol/src/java/client/login/login_disconnect.rs +++ b/pumpkin-protocol/src/java/client/login/login_disconnect.rs @@ -2,14 +2,18 @@ use pumpkin_data::packet::clientbound::LOGIN_LOGIN_DISCONNECT; use pumpkin_macros::packet; use serde::{Deserialize, Serialize}; +/// Sent by the server to reject a login attempt or kick a player during the login phase +/// +/// This is used for reasons such as the server being full, the player being banned, +/// or version mismatches. After this packet is sent, the connection is closed. #[derive(Serialize, Deserialize)] #[packet(LOGIN_LOGIN_DISCONNECT)] pub struct CLoginDisconnect { + /// A JSON-encoded chat component explaining why the player was disconnected. pub json_reason: String, } impl CLoginDisconnect { - // input json! pub fn new(json_reason: String) -> Self { Self { json_reason } } diff --git a/pumpkin-protocol/src/java/client/login/login_success.rs b/pumpkin-protocol/src/java/client/login/login_success.rs index 8a0d968ea..75e58d431 100644 --- a/pumpkin-protocol/src/java/client/login/login_success.rs +++ b/pumpkin-protocol/src/java/client/login/login_success.rs @@ -4,11 +4,19 @@ use serde::Serialize; use crate::Property; +/// Sent by the server to signal a successful login and transition to the configuration phase +/// +/// This packet provides the client with its official UUID and username as +/// recognized by the server, along with any associated skin or cape properties. #[derive(Serialize)] #[packet(LOGIN_LOGIN_FINISHED)] pub struct CLoginSuccess<'a> { + /// The unique identifier assigned to the player. pub uuid: &'a uuid::Uuid, - pub username: &'a str, // 16 + /// The player's verified username. + pub username: &'a str, + /// A list of properties for the player's profile, such as skin data and signatures. + /// This is typically retrieved from the Mojang authentication servers. pub properties: &'a [Property], } diff --git a/pumpkin-protocol/src/java/client/login/plugin_request.rs b/pumpkin-protocol/src/java/client/login/plugin_request.rs index c58480f9f..a63fa7baf 100644 --- a/pumpkin-protocol/src/java/client/login/plugin_request.rs +++ b/pumpkin-protocol/src/java/client/login/plugin_request.rs @@ -4,11 +4,20 @@ use serde::Serialize; use crate::{VarInt, ser::network_serialize_no_prefix}; +/// Sent by the server to initiate a custom plugin messaging exchange during login. +/// +/// This is used by server software (like proxies or anti-cheats) to request +/// information from a client-side mod before the player officially joins. #[derive(Serialize)] #[packet(LOGIN_CUSTOM_QUERY)] pub struct CLoginPluginRequest<'a> { + /// A unique ID for this request. The client must include this same ID + /// in its response so the server can match them up. pub message_id: VarInt, + /// The name of the custom channel (e.g., "velocity:main"). pub channel: &'a str, + /// The raw payload data. Unlike standard plugin messages, this data + /// is often serialized without a length prefix at the end of the packet. #[serde(serialize_with = "network_serialize_no_prefix")] pub data: &'a [u8], } diff --git a/pumpkin-protocol/src/java/client/login/set_compression.rs b/pumpkin-protocol/src/java/client/login/set_compression.rs index 2dbb858d1..d5fa3d585 100644 --- a/pumpkin-protocol/src/java/client/login/set_compression.rs +++ b/pumpkin-protocol/src/java/client/login/set_compression.rs @@ -4,9 +4,17 @@ use serde::{Deserialize, Serialize}; use crate::VarInt; +/// Sent by the server to enable network compression for all subsequent packets. +/// +/// Once this packet is sent, both the server and the client must compress any +/// packet with a size equal to or greater than the specified threshold. #[derive(Serialize, Deserialize)] #[packet(LOGIN_LOGIN_COMPRESSION)] pub struct CSetCompression { + /// The packet size threshold (in bytes) at which compression is applied. + /// + /// Packets smaller than this are sent uncompressed. A negative threshold + /// typically disables compression. pub threshold: VarInt, } diff --git a/pumpkin-protocol/src/java/client/play/acknowledge_block.rs b/pumpkin-protocol/src/java/client/play/acknowledge_block.rs index 5ed08af71..85c54a387 100644 --- a/pumpkin-protocol/src/java/client/play/acknowledge_block.rs +++ b/pumpkin-protocol/src/java/client/play/acknowledge_block.rs @@ -4,9 +4,18 @@ use serde::{Deserialize, Serialize}; use crate::VarInt; +/// Sent by the server to acknowledge a sequence of block changes initiated by the client. +/// +/// This packet is critical for preventing "ghost blocks" and synchronization issues. +/// It tells the client that the server has processed all actions up to a specific point. #[derive(Serialize, Deserialize)] #[packet(PLAY_BLOCK_CHANGED_ACK)] pub struct CAcknowledgeBlockChange { + /// The ID of the last sequence processed by the server. + /// + /// The client increments this ID every time it starts a sequence of actions + /// (like breaking or placing a block), and the server must mirror it back + /// to confirm processing is complete. pub sequence_id: VarInt, } diff --git a/pumpkin-protocol/src/java/client/play/actionbar.rs b/pumpkin-protocol/src/java/client/play/actionbar.rs index 1934cc832..5153e76dc 100644 --- a/pumpkin-protocol/src/java/client/play/actionbar.rs +++ b/pumpkin-protocol/src/java/client/play/actionbar.rs @@ -4,9 +4,15 @@ use pumpkin_util::text::TextComponent; use pumpkin_macros::packet; use serde::Serialize; +/// Updates the text displayed above the player's hotbar (the Action Bar). +/// +/// Unlike chat messages, Action Bar text is transient and generally used for +/// non-critical status information like "Now entering: Wilderness" or +/// mana/stamina counters. #[derive(Serialize)] #[packet(PLAY_SET_ACTION_BAR_TEXT)] pub struct CActionBar<'a> { + /// The text component to be displayed. pub action_bar: &'a TextComponent, } diff --git a/pumpkin-protocol/src/java/client/play/block_destroy_stage.rs b/pumpkin-protocol/src/java/client/play/block_destroy_stage.rs index 7c0811a3d..44198f14a 100644 --- a/pumpkin-protocol/src/java/client/play/block_destroy_stage.rs +++ b/pumpkin-protocol/src/java/client/play/block_destroy_stage.rs @@ -6,11 +6,20 @@ use serde::Serialize; use crate::VarInt; +/// Updates the visual "breaking" progress of a block for all clients. +/// +/// This packet controls the overlay of cracks that appear on a block when +/// it is being mined. It is often used to show other players' mining progress. #[derive(Serialize)] #[packet(PLAY_BLOCK_DESTRUCTION)] pub struct CSetBlockDestroyStage { + /// A unique ID for this destruction instance. Usually the miner's Entity ID. + /// If multiple entities mine the same block, they must use different IDs. pub entity_id: VarInt, + /// The coordinates of the block being destroyed. pub location: BlockPos, + /// The destruction stage, typically a value from 0 to 9. + /// Any value outside 0-9 (like -1) will remove the destruction overlay. pub destroy_stage: i8, } diff --git a/pumpkin-protocol/src/java/client/play/block_entity_data.rs b/pumpkin-protocol/src/java/client/play/block_entity_data.rs index eb25c9a53..dec2b7c2c 100644 --- a/pumpkin-protocol/src/java/client/play/block_entity_data.rs +++ b/pumpkin-protocol/src/java/client/play/block_entity_data.rs @@ -5,11 +5,18 @@ use serde::Serialize; use crate::{VarInt, ser::network_serialize_no_prefix}; +/// Updates the NBT data of a block entity (e.g., signs, chests, or banners). +/// +/// This packet is sent by the server when a block entity's state changes +/// (like text on a sign) or when the block entity is loaded into the client's view. #[derive(Serialize)] #[packet(PLAY_BLOCK_ENTITY_DATA)] pub struct CBlockEntityData { + /// The world coordinates of the block entity. pub location: BlockPos, + /// The type of block entity being updated (e.g., Mob Spawner, Command Block). pub r#type: VarInt, + /// The raw NBT payload containing the block's specific data. #[serde(serialize_with = "network_serialize_no_prefix")] pub nbt_data: Box<[u8]>, } diff --git a/pumpkin-protocol/src/java/client/play/block_event.rs b/pumpkin-protocol/src/java/client/play/block_event.rs index 7e1e3d3f7..df69a4b47 100644 --- a/pumpkin-protocol/src/java/client/play/block_event.rs +++ b/pumpkin-protocol/src/java/client/play/block_event.rs @@ -6,12 +6,21 @@ use serde::Serialize; use crate::VarInt; +/// Triggers a physical block animation or sound effect. +/// +/// This is used for simple block interactions that don't necessarily change +/// NBT data, such as chests opening/closing, pistons extending, or note blocks playing. #[derive(Serialize)] #[packet(PLAY_BLOCK_EVENT)] pub struct CBlockEvent { + /// The coordinates where the event occurs. pub location: BlockPos, + /// The ID of the action to perform. Meaning varies by block type. pub action_id: u8, + /// A parameter for the action (e.g., note pitch or instrument). pub action_parameter: u8, + /// The block type ID (e.g., `minecraft:chest`). + /// Note: This is the block ID, not the state ID. pub block_type: VarInt, } diff --git a/pumpkin-protocol/src/java/client/play/block_update.rs b/pumpkin-protocol/src/java/client/play/block_update.rs index ce9f7986c..b8ddf2278 100644 --- a/pumpkin-protocol/src/java/client/play/block_update.rs +++ b/pumpkin-protocol/src/java/client/play/block_update.rs @@ -6,15 +6,21 @@ use serde::Serialize; use crate::VarInt; +/// Updates a single block state at a specific location in the world. +/// +/// This is the most common way to sync world changes to the client, such as +/// when a player places a block, a fluid flows, or a redstone component toggles. #[derive(Serialize)] #[packet(PLAY_BLOCK_UPDATE)] pub struct CBlockUpdate { + /// The world coordinates of the block being updated. pub location: BlockPos, - pub block_id: VarInt, + /// The new block state ID. + pub state_id: VarInt, } impl CBlockUpdate { - pub fn new(location: BlockPos, block_id: VarInt) -> Self { - Self { location, block_id } + pub fn new(location: BlockPos, state_id: VarInt) -> Self { + Self { location, state_id } } } diff --git a/pumpkin-protocol/src/java/client/play/boss_event.rs b/pumpkin-protocol/src/java/client/play/boss_event.rs index 81c462d96..cb2de4572 100644 --- a/pumpkin-protocol/src/java/client/play/boss_event.rs +++ b/pumpkin-protocol/src/java/client/play/boss_event.rs @@ -7,9 +7,15 @@ use pumpkin_data::packet::clientbound::PLAY_BOSS_EVENT; use pumpkin_macros::packet; use pumpkin_util::version::MinecraftVersion; +/// Updates the "Boss Bar" displayed at the top of the player's screen. +/// +/// This packet is used to manage health bars for entities like the Ender Dragon +/// or Wither, as well as custom progress bars for server events or raids. #[packet(PLAY_BOSS_EVENT)] pub struct CBossEvent<'a> { + /// A unique identifier for this specific boss bar instance. pub uuid: &'a uuid::Uuid, + /// The action to perform (Add, Remove, Update Health, etc.). pub action: BosseventAction, } diff --git a/pumpkin-protocol/src/java/client/play/center_chunk.rs b/pumpkin-protocol/src/java/client/play/center_chunk.rs index ee5eca3ac..6aea0ad34 100644 --- a/pumpkin-protocol/src/java/client/play/center_chunk.rs +++ b/pumpkin-protocol/src/java/client/play/center_chunk.rs @@ -1,11 +1,19 @@ use pumpkin_data::packet::clientbound::PLAY_SET_CHUNK_CACHE_CENTER; use pumpkin_macros::packet; +use serde::Serialize; use crate::VarInt; -#[derive(serde::Serialize)] +/// Updates the center of the player's loaded chunk radius (the "view center"). +/// +/// This packet tells the client which chunk coordinate it should use as the +/// focal point for loading and unloading chunks. It is typically sent when +/// a player moves across a chunk boundary. +#[derive(Serialize)] #[packet(PLAY_SET_CHUNK_CACHE_CENTER)] pub struct CCenterChunk { + /// The X coordinate of the center chunk. pub chunk_x: VarInt, + /// The Z coordinate of the center chunk. pub chunk_z: VarInt, } diff --git a/pumpkin-protocol/src/java/client/play/change_difficulty.rs b/pumpkin-protocol/src/java/client/play/change_difficulty.rs index ec0d43b2d..593a6a81a 100644 --- a/pumpkin-protocol/src/java/client/play/change_difficulty.rs +++ b/pumpkin-protocol/src/java/client/play/change_difficulty.rs @@ -2,10 +2,23 @@ use pumpkin_data::packet::clientbound::PLAY_CHANGE_DIFFICULTY; use pumpkin_macros::packet; use serde::Serialize; +/// Notifies the client of a change in the world's difficulty level or lock status. +/// +/// This updates the client's internal state, which affects certain UI elements +/// and client-side behavior (though actual game logic like mob damage is +/// primarily handled by the server). #[derive(Serialize)] #[packet(PLAY_CHANGE_DIFFICULTY)] pub struct CChangeDifficulty { + /// The current difficulty level of the world. + /// + /// * **0**: Peaceful + /// * **1**: Easy + /// * **2**: Normal + /// * **3**: Hard pub difficulty: u8, + /// Whether the difficulty is locked. If true, the client's difficulty + /// toggle in the options menu will be disabled. pub locked: bool, } diff --git a/pumpkin-protocol/src/java/client/play/chunk_batch_end.rs b/pumpkin-protocol/src/java/client/play/chunk_batch_end.rs index acb9ac72a..8ed6d449e 100644 --- a/pumpkin-protocol/src/java/client/play/chunk_batch_end.rs +++ b/pumpkin-protocol/src/java/client/play/chunk_batch_end.rs @@ -4,9 +4,15 @@ use serde::Serialize; use crate::codec::var_int::VarInt; +/// Notifies the client that the server has finished sending a batch of chunks. +/// +/// Introduced in modern protocol versions to optimize world loading, this packet +/// allows the client to acknowledge the receipt of a group of chunks, helping +/// the server regulate the flow of data and prevent network congestion. #[derive(Serialize)] #[packet(PLAY_CHUNK_BATCH_FINISHED)] pub struct CChunkBatchEnd { + /// The number of chunks sent in the batch that just finished. pub batch_size: VarInt, } diff --git a/pumpkin-protocol/src/java/client/play/chunk_batch_start.rs b/pumpkin-protocol/src/java/client/play/chunk_batch_start.rs index 679558e69..20a4f3e8e 100644 --- a/pumpkin-protocol/src/java/client/play/chunk_batch_start.rs +++ b/pumpkin-protocol/src/java/client/play/chunk_batch_start.rs @@ -2,6 +2,12 @@ use pumpkin_data::packet::clientbound::PLAY_CHUNK_BATCH_START; use pumpkin_macros::packet; use serde::Serialize; +/// Signals the beginning of a new batch of chunk data packets. +/// +/// This packet initiates a synchronized chunk loading sequence. In modern +/// protocol versions, the server must wrap chunk data transmissions between +/// a `Start` and `End` packet to manage client-side backpressure and +/// network throughput. #[derive(Serialize)] #[packet(PLAY_CHUNK_BATCH_START)] pub struct CChunkBatchStart; diff --git a/pumpkin-protocol/src/java/client/play/chunk_data.rs b/pumpkin-protocol/src/java/client/play/chunk_data.rs index 1b5625239..d5e4276f2 100644 --- a/pumpkin-protocol/src/java/client/play/chunk_data.rs +++ b/pumpkin-protocol/src/java/client/play/chunk_data.rs @@ -10,6 +10,11 @@ use pumpkin_world::chunk::format::LightContainer; use pumpkin_world::chunk::{ChunkData, palette::NetworkPalette}; use std::io::Write; +/// Sent by the server to provide the client with the full data for a chunk. +/// +/// This includes heightmaps, the actual block and biome data (organized into sections), +/// block entities (like signs or chests), and the light level information for both +/// sky and block light. #[packet(PLAY_LEVEL_CHUNK_WITH_LIGHT)] pub struct CChunkData<'a>(pub &'a ChunkData); diff --git a/pumpkin-protocol/src/java/client/play/clear_title.rs b/pumpkin-protocol/src/java/client/play/clear_title.rs index c1960252c..e7ccbefee 100644 --- a/pumpkin-protocol/src/java/client/play/clear_title.rs +++ b/pumpkin-protocol/src/java/client/play/clear_title.rs @@ -2,9 +2,18 @@ use pumpkin_data::packet::clientbound::PLAY_CLEAR_TITLES; use pumpkin_macros::packet; use serde::Serialize; +/// Removes any currently displayed Title or Subtitle from the player's screen. +/// +/// This packet is used to immediately hide titles that are currently in their +/// "stay" or "fade-out" phases. #[derive(Serialize)] #[packet(PLAY_CLEAR_TITLES)] pub struct CClearTitle { + /// If true, the client also resets the title timings (fade-in, stay, fade-out) + /// to their default values (10, 70, 20 ticks). + /// + /// Set this to false if you want to clear the text but keep custom timings + /// for the next title you send. pub reset: bool, } diff --git a/pumpkin-protocol/src/java/client/play/close_container.rs b/pumpkin-protocol/src/java/client/play/close_container.rs index 5a5d987fc..1b43bf681 100644 --- a/pumpkin-protocol/src/java/client/play/close_container.rs +++ b/pumpkin-protocol/src/java/client/play/close_container.rs @@ -4,12 +4,20 @@ use serde::Serialize; use crate::VarInt; +/// Notifies the client that a container (inventory, chest, etc.) has been closed. +/// +/// This is used by the server to force the player's UI to shut, for example, +/// if the player moves too far away from a chest or if an NPC's trade window +/// is invalidated. #[derive(Serialize)] #[packet(PLAY_CONTAINER_CLOSE)] pub struct CCloseContainer { + /// The ID of the container window to close. + /// + /// A value of 0 usually refers to the player's own inventory, while higher + /// values refer to active windows opened via previous packets. pub sync_id: VarInt, } - impl CCloseContainer { pub const fn new(window_id: VarInt) -> Self { Self { sync_id: window_id } diff --git a/pumpkin-protocol/src/java/client/play/combat_death.rs b/pumpkin-protocol/src/java/client/play/combat_death.rs index 9cad619f9..6c158f25a 100644 --- a/pumpkin-protocol/src/java/client/play/combat_death.rs +++ b/pumpkin-protocol/src/java/client/play/combat_death.rs @@ -5,10 +5,16 @@ use serde::Serialize; use crate::VarInt; +/// Notifies the client that a player has died. +/// +/// This packet is responsible for triggering the death screen on the client +/// and displaying the death message in the chat for the deceased player. #[derive(Serialize)] #[packet(PLAY_PLAYER_COMBAT_KILL)] pub struct CCombatDeath<'a> { + /// The Entity ID of the player who died. pub player_id: VarInt, + /// The death message to be displayed (e.g., "Player was pricked to death by a Cactus"). pub message: &'a TextComponent, } diff --git a/pumpkin-protocol/src/java/client/play/command_suggestions.rs b/pumpkin-protocol/src/java/client/play/command_suggestions.rs index a0a9b5969..df4e2f925 100644 --- a/pumpkin-protocol/src/java/client/play/command_suggestions.rs +++ b/pumpkin-protocol/src/java/client/play/command_suggestions.rs @@ -5,12 +5,25 @@ use serde::Serialize; use crate::VarInt; +/// Sent by the server to provide a list of "tab-completion" suggestions. +/// +/// This packet responds to a client's request for help with commands, +/// appearing as a scrollable list of options while the player is typing +/// in the chat bar. #[derive(Serialize)] #[packet(PLAY_COMMAND_SUGGESTIONS)] pub struct CCommandSuggestions { + /// The unique ID of the request this response is for. + /// This must match the ID sent by the client in the request packet. pub id: VarInt, + /// The starting character index in the chat bar where the completion + /// should be inserted. pub start: VarInt, + /// The number of characters in the original text to replace with + /// the suggestion. pub length: VarInt, + /// The list of possible completions, which can include tooltips + /// for extra context. pub matches: Box<[CommandSuggestion]>, } diff --git a/pumpkin-protocol/src/java/client/play/commands.rs b/pumpkin-protocol/src/java/client/play/commands.rs index b0ed1be20..649764c52 100644 --- a/pumpkin-protocol/src/java/client/play/commands.rs +++ b/pumpkin-protocol/src/java/client/play/commands.rs @@ -6,9 +6,18 @@ use pumpkin_util::version::MinecraftVersion; use crate::{ClientPacket, VarInt, WritingError, ser::NetworkWriteExt}; +/// Sends the entire command tree to the client for client-side parsing and tab-completion. +/// +/// Minecraft uses the "Brigadier" command system. This packet informs the client +/// which commands exist, their arguments, and how they branch, allowing the +/// client to highlight syntax errors in red before the command is even sent. #[packet(PLAY_COMMANDS)] pub struct CCommands<'a> { + /// A flat list of all nodes in the command graph. + /// Nodes reference each other by their index in this array. pub nodes: Box<[ProtoNode<'a>]>, + /// The index of the "root" node in the `nodes` array. + /// This is the entry point for all commands (the '/' symbol). pub root_node_index: VarInt, } diff --git a/pumpkin-protocol/src/java/client/play/cookie_request.rs b/pumpkin-protocol/src/java/client/play/cookie_request.rs index fc063dc8b..97befada8 100644 --- a/pumpkin-protocol/src/java/client/play/cookie_request.rs +++ b/pumpkin-protocol/src/java/client/play/cookie_request.rs @@ -3,10 +3,15 @@ use pumpkin_macros::packet; use pumpkin_util::resource_location::ResourceLocation; use serde::Serialize; +/// Sent by the server to request a "cookie" (stored data) from the client. +/// +/// Introduced in modern Minecraft versions, cookies allow servers to store +/// small amounts of persistent data on the client side that can be retrieved +/// even across different server instances or sub-servers in a network. #[derive(Serialize)] #[packet(PLAY_COOKIE_REQUEST)] -/// Requests a cookie that was previously stored. pub struct CPlayCookieRequest<'a> { + /// The unique identifier (namespace:path) of the cookie to retrieve. pub key: &'a ResourceLocation, } diff --git a/pumpkin-protocol/src/java/client/play/damage_event.rs b/pumpkin-protocol/src/java/client/play/damage_event.rs index c3a932853..8d1596d7d 100644 --- a/pumpkin-protocol/src/java/client/play/damage_event.rs +++ b/pumpkin-protocol/src/java/client/play/damage_event.rs @@ -5,13 +5,28 @@ use serde::Serialize; use crate::VarInt; +/// Notifies the client that an entity has taken damage. +/// +/// This packet is used to trigger damage animations (like the red tint on mobs), +/// directional knockback visuals, and sound effects. It provides the client +/// with specific details about the damage source to ensure the visual feedback +/// matches the cause. #[derive(Serialize)] #[packet(PLAY_DAMAGE_EVENT)] pub struct CDamageEvent { + /// The Entity ID of the entity taking damage. pub entity_id: VarInt, + /// The ID of the damage type (references the `minecraft:damage_type` registry). + /// Examples: `magic`, `fall`, `on_fire`, or `arrow`. pub source_type_id: VarInt, + /// The Entity ID of the actual cause of the damage (e.g., the player who shot the arrow). + /// Set to 0 if there is no specific entity cause. pub source_cause_id: VarInt, + /// The Entity ID of the direct damager (e.g., the arrow entity itself). + /// Set to 0 if this is the same as the cause or if not applicable. pub source_direct_id: VarInt, + /// The coordinates of the damage source. Used by the client to calculate + /// the direction of the "damage tilt" camera effect. pub source_position: Option>, } diff --git a/pumpkin-protocol/src/java/client/play/disconnect.rs b/pumpkin-protocol/src/java/client/play/disconnect.rs index 13929151a..5ada704bc 100644 --- a/pumpkin-protocol/src/java/client/play/disconnect.rs +++ b/pumpkin-protocol/src/java/client/play/disconnect.rs @@ -4,9 +4,16 @@ use pumpkin_util::text::TextComponent; use pumpkin_macros::packet; use serde::Serialize; +/// Forces the client to disconnect from the server while in the "Play" state. +/// +/// This packet displays the provided reason to the player on a dedicated +/// disconnection screen. It is used for kicks, server shutdowns, or when +/// a player is banned. #[derive(Serialize)] #[packet(PLAY_DISCONNECT)] pub struct CPlayDisconnect<'a> { + /// The message shown to the player explaining why they were disconnected. + /// This supports full JSON formatting (colors, bold, links, etc.). pub reason: &'a TextComponent, } diff --git a/pumpkin-protocol/src/java/client/play/disguised_chat_message.rs b/pumpkin-protocol/src/java/client/play/disguised_chat_message.rs index f775c7908..c35252c90 100644 --- a/pumpkin-protocol/src/java/client/play/disguised_chat_message.rs +++ b/pumpkin-protocol/src/java/client/play/disguised_chat_message.rs @@ -6,12 +6,22 @@ use serde::Serialize; use crate::VarInt; +/// Sends a chat message that is not cryptographically signed by a player. +/// +/// Introduced to support server-side "disguised" identities (like /say or NPC chat), +/// this packet bypasses the player-to-player chat signing requirements while +/// still allowing the client to format the message using the standard chat registry. #[derive(Serialize)] #[packet(PLAY_DISGUISED_CHAT)] pub struct CDisguisedChatMessage<'a> { + /// The raw content of the message. pub message: &'a TextComponent, + /// An index into the `minecraft:chat_type` registry. + /// This determines the decoration (e.g., "<%s> %s" or "[%s -> %s] %s"). pub chat_type: VarInt, + /// The name shown as the "sender" of the message. pub sender_name: &'a TextComponent, + /// The optional name shown as the "target" (used for private messages/whispers). pub target_name: Option<&'a TextComponent>, } diff --git a/pumpkin-protocol/src/java/client/play/display_objective.rs b/pumpkin-protocol/src/java/client/play/display_objective.rs index 9ce45a4c7..53ec6d3f9 100644 --- a/pumpkin-protocol/src/java/client/play/display_objective.rs +++ b/pumpkin-protocol/src/java/client/play/display_objective.rs @@ -6,10 +6,18 @@ use serde::Serialize; use crate::VarInt; +/// Instructs the client to display a specific scoreboard objective in a given slot. +/// +/// This packet is the final step in showing a scoreboard to a player. After +/// an objective is created and populated with scores, this packet "maps" +/// that objective to a visual location like the sidebar or the player list. #[derive(Serialize)] #[packet(PLAY_SET_DISPLAY_OBJECTIVE)] pub struct CDisplayObjective { + /// The display slot/position for the objective. pub position: VarInt, + /// The unique internal name of the objective to be displayed. + /// To hide an objective in a specific slot, send an empty string. pub score_name: String, } diff --git a/pumpkin-protocol/src/java/client/play/entity_animation.rs b/pumpkin-protocol/src/java/client/play/entity_animation.rs index 791493632..d1c786abc 100644 --- a/pumpkin-protocol/src/java/client/play/entity_animation.rs +++ b/pumpkin-protocol/src/java/client/play/entity_animation.rs @@ -4,11 +4,17 @@ use serde::Serialize; use crate::VarInt; +/// Triggers a specific animation for an entity that is visible to the client. +/// +/// This is primarily used for player-driven animations like swinging an arm +/// or showing damage, but it can apply to other entities as well. #[derive(Serialize)] #[packet(PLAY_ANIMATE)] pub struct CEntityAnimation { + /// The Entity ID of the entity performing the animation. pub entity_id: VarInt, - /// See `Animation` + /// The ID of the animation to play. + /// See the table below for standard values. pub animation: u8, } diff --git a/pumpkin-protocol/src/java/client/play/entity_metadata.rs b/pumpkin-protocol/src/java/client/play/entity_metadata.rs index 24bbbc3df..2174cfc9e 100644 --- a/pumpkin-protocol/src/java/client/play/entity_metadata.rs +++ b/pumpkin-protocol/src/java/client/play/entity_metadata.rs @@ -4,11 +4,18 @@ use serde::Serialize; use crate::{VarInt, ser::network_serialize_no_prefix}; +/// Updates the "Data Tracker" values for an entity. +/// +/// Entity Metadata (or DataWatchers) controls persistent visual states that +/// don't require a full packet to update, such as whether an entity is on fire, +/// crouching, glowing, or the custom name displayed above its head. #[derive(Serialize)] #[packet(PLAY_SET_ENTITY_DATA)] pub struct CSetEntityMetadata { + /// The Entity ID of the entity whose metadata is being updated. pub entity_id: VarInt, - // TODO: We should migrate the serialization of this into this file + /// A serialized collection of metadata entries. + /// Ends with a terminal byte (0xFF). #[serde(serialize_with = "network_serialize_no_prefix")] pub metadata: Box<[u8]>, } diff --git a/pumpkin-protocol/src/java/client/play/entity_position_sync.rs b/pumpkin-protocol/src/java/client/play/entity_position_sync.rs index 45c1a72aa..be32a6275 100644 --- a/pumpkin-protocol/src/java/client/play/entity_position_sync.rs +++ b/pumpkin-protocol/src/java/client/play/entity_position_sync.rs @@ -5,16 +5,29 @@ use serde::Serialize; use crate::VarInt; -/// Synchronize entity position and rotation to the client. -/// The entity must not be the player itself, nor its vehicles. +/// Updates the exact position, rotation, and velocity of an entity. +/// +/// This packet is used for server-side authority over entity movement. +/// In the latest protocol versions, this replaces several older "Relative Move" +/// packets to provide more precise synchronization and reduce "rubber-banding." +/// +/// Note: This packet must NOT be used for the player receiving the packet or +/// any entity the player is currently riding. #[packet(PLAY_ENTITY_POSITION_SYNC)] #[derive(Serialize)] pub struct CEntityPositionSync { + /// The Entity ID of the entity being moved. pub entity_id: VarInt, + /// The absolute position of the entity in the world. pub position: Vector3, + /// The current velocity (delta) of the entity, used by the client + /// for smooth interpolation. pub delta: Vector3, + /// The absolute yaw (horizontal rotation) in degrees. pub yaw: f32, + /// The absolute pitch (vertical rotation) in degrees. pub pitch: f32, + /// Whether the entity is currently touching the ground. pub on_ground: bool, } diff --git a/pumpkin-protocol/src/java/client/play/entity_sound_effect.rs b/pumpkin-protocol/src/java/client/play/entity_sound_effect.rs index c4dc15b83..dcf9ddb30 100644 --- a/pumpkin-protocol/src/java/client/play/entity_sound_effect.rs +++ b/pumpkin-protocol/src/java/client/play/entity_sound_effect.rs @@ -1,18 +1,32 @@ use pumpkin_data::{packet::clientbound::PLAY_SOUND_ENTITY, sound::SoundCategory}; use pumpkin_macros::packet; -use serde::Deserialize; +use serde::Serialize; use crate::{IdOr, SoundEvent, VarInt}; -#[derive(Deserialize)] +/// Plays a sound effect that originates from a specific entity. +/// +/// Unlike global sounds, this sound will follow the entity as it moves +/// through the world. The client handles the panning and attenuation +/// (volume drop-off) based on the distance between the player and the entity. +#[derive(Serialize)] #[packet(PLAY_SOUND_ENTITY)] pub struct CEntitySoundEffect { + /// The sound to play. Can be a hardcoded ID or a custom SoundEvent + /// (Resource Location). pub sound_event: IdOr, + /// The category of the sound (e.g., Master, Music, Weather, Players). + /// Used by the client to apply volume sliders from settings. pub sound_category: VarInt, + /// The Entity ID that the sound is "attached" to. pub entity_id: VarInt, + /// The loudness of the sound (usually 1.0). pub volume: f32, + /// The playback speed/pitch (0.5 to 2.0). pub pitch: f32, - pub seed: f64, + /// A random seed used for sound variations (like different pitch shifts + /// for the same sound). + pub seed: i64, } impl CEntitySoundEffect { @@ -22,7 +36,7 @@ impl CEntitySoundEffect { entity_id: VarInt, volume: f32, pitch: f32, - seed: f64, + seed: i64, ) -> Self { Self { sound_event, diff --git a/pumpkin-protocol/src/java/client/play/entity_status.rs b/pumpkin-protocol/src/java/client/play/entity_status.rs index bfe171854..7f0f890cb 100644 --- a/pumpkin-protocol/src/java/client/play/entity_status.rs +++ b/pumpkin-protocol/src/java/client/play/entity_status.rs @@ -2,10 +2,19 @@ use pumpkin_data::packet::clientbound::PLAY_ENTITY_EVENT; use pumpkin_macros::packet; use serde::{Deserialize, Serialize}; +/// Sends a status update for a specific entity. +/// +/// This packet is a "catch-all" for various entity triggers that don't +/// warrant a complex packet of their own. It primarily handles visual +/// and logical state triggers, such as tool breaking, totem usage, +/// or sheep shearing. #[derive(Serialize, Deserialize)] #[packet(PLAY_ENTITY_EVENT)] pub struct CEntityStatus { + /// The Entity ID of the entity affected by the status change. pub entity_id: i32, + /// The ID of the status/event to trigger. + /// See the table below for common entity statuses. pub entity_status: i8, } diff --git a/pumpkin-protocol/src/java/client/play/entity_velocity.rs b/pumpkin-protocol/src/java/client/play/entity_velocity.rs index efb89de04..f9c553f74 100644 --- a/pumpkin-protocol/src/java/client/play/entity_velocity.rs +++ b/pumpkin-protocol/src/java/client/play/entity_velocity.rs @@ -5,10 +5,16 @@ use serde::Serialize; use crate::{VarInt, codec::velocity::Velocity}; +/// Updates the velocity of an entity. +/// +/// This packet informs the client of a sudden change in an entity's movement, +/// such as knockback from an attack, explosions, or being launched by a piston. #[derive(Serialize)] #[packet(PLAY_SET_ENTITY_MOTION)] pub struct CEntityVelocity { + /// The Entity ID of the entity whose velocity is being set pub entity_id: VarInt, + /// The velocity vector pub velocity: Velocity, } diff --git a/pumpkin-protocol/src/java/client/play/explode.rs b/pumpkin-protocol/src/java/client/play/explode.rs index 75181437b..d88c9ad58 100644 --- a/pumpkin-protocol/src/java/client/play/explode.rs +++ b/pumpkin-protocol/src/java/client/play/explode.rs @@ -5,15 +5,31 @@ use serde::Serialize; use crate::{IdOr, SoundEvent, codec::var_int::VarInt}; +/// Notifies the client that an explosion has occurred. +/// +/// This is a high-level packet that handles the visual, auditory, and physical +/// effects of an explosion in a single call. It triggers the explosion particles, +/// plays the sound at the source, and applies knockback to the player. #[derive(Serialize)] #[packet(PLAY_EXPLODE)] pub struct CExplosion { + /// The center coordinates of the explosion. pub center: Vector3, + /// The strength/radius of the explosion. + /// Higher values increase the visual size of the particle effect. pub radius: f32, + /// The number of blocks affected/destroyed. + /// Note: The actual block list is typically handled via a separate packet or + /// following byte array in older versions, but this field specifies the count. pub block_count: i32, + /// The impulse/knockback applied to the player receiving this packet. + /// If None, no velocity change is applied. pub knockback: Option>, + /// The ID of the particle to use for the explosion (e.g., `minecraft:explosion`). pub particle: VarInt, + /// The sound to play (e.g., `minecraft:entity.generic.explode`). pub sound: IdOr, + /// The size of the block particles pool, used for debris visuals. pub block_particles_pool_size: VarInt, } diff --git a/pumpkin-protocol/src/java/client/play/game_event.rs b/pumpkin-protocol/src/java/client/play/game_event.rs index 87e687eff..f419af9c6 100644 --- a/pumpkin-protocol/src/java/client/play/game_event.rs +++ b/pumpkin-protocol/src/java/client/play/game_event.rs @@ -2,10 +2,17 @@ use pumpkin_data::packet::clientbound::PLAY_GAME_EVENT; use pumpkin_macros::packet; use serde::{Deserialize, Serialize}; +/// Updates the game state or triggers specific environmental changes. +/// +/// This packet is the primary way the server communicates global or +/// context-specific transitions, such as changing the weather, +/// altering the player's gamemode, or displaying the credits. #[derive(Serialize, Deserialize)] #[packet(PLAY_GAME_EVENT)] pub struct CGameEvent { + /// The ID of the event type. pub event: u8, + /// A value associated with the event (usage depends on the event ID). pub value: f32, } diff --git a/pumpkin-protocol/src/java/client/play/head_rot.rs b/pumpkin-protocol/src/java/client/play/head_rot.rs index 049b694bf..1a154f864 100644 --- a/pumpkin-protocol/src/java/client/play/head_rot.rs +++ b/pumpkin-protocol/src/java/client/play/head_rot.rs @@ -4,10 +4,18 @@ use serde::{Deserialize, Serialize}; use crate::VarInt; +/// Rotates an entity's head to a specific yaw. +/// +/// In Minecraft, an entity's "body yaw" and "head yaw" are separate. +/// While standard movement packets update the body, this packet is +/// required to make an entity (like a player or a mob) look in a +/// specific direction without necessarily turning its entire body. #[derive(Serialize, Deserialize)] #[packet(PLAY_ROTATE_HEAD)] pub struct CHeadRot { + /// The Entity ID of the entity whose head is rotating. pub entity_id: VarInt, + /// The new head yaw, in steps of 1/256 of a full turn (0-255). pub head_yaw: u8, } diff --git a/pumpkin-protocol/src/java/client/play/hurt_animation.rs b/pumpkin-protocol/src/java/client/play/hurt_animation.rs index ed53fe986..d6914bd2e 100644 --- a/pumpkin-protocol/src/java/client/play/hurt_animation.rs +++ b/pumpkin-protocol/src/java/client/play/hurt_animation.rs @@ -4,10 +4,18 @@ use serde::{Deserialize, Serialize}; use crate::VarInt; +/// Triggers the "hurt" visual effect on an entity. +/// +/// This packet causes the entity to turn red and perform a directional +/// camera shake or model tilt. It is typically sent immediately after +/// an entity's health is reduced. #[derive(Serialize, Deserialize)] #[packet(PLAY_HURT_ANIMATION)] pub struct CHurtAnimation { + /// The Entity ID of the entity that was hurt. pub entity_id: VarInt, + /// The yaw (direction) from which the damage originated. + /// This determines the direction the entity's model tilts. pub yaw: f32, } diff --git a/pumpkin-protocol/src/java/client/play/initialize_world_border.rs b/pumpkin-protocol/src/java/client/play/initialize_world_border.rs index bcd0ce6e5..2c7c8d31f 100644 --- a/pumpkin-protocol/src/java/client/play/initialize_world_border.rs +++ b/pumpkin-protocol/src/java/client/play/initialize_world_border.rs @@ -4,16 +4,31 @@ use serde::Serialize; use crate::{VarInt, codec::var_long::VarLong}; +/// Fully initializes the world border for the client. +/// +/// This packet is sent when a player joins the world or changes dimensions. +/// It synchronizes the current position, size, and all warning parameters +/// to ensure the client-side visual barrier matches the server's authority. #[derive(Serialize)] #[packet(PLAY_INITIALIZE_BORDER)] pub struct CInitializeWorldBorder { + /// The X coordinate of the center of the world border. pub x: f64, + /// The Z coordinate of the center of the world border. pub z: f64, + /// The diameter the border is moving from. pub old_diameter: f64, + /// The diameter the border is moving toward. pub new_diameter: f64, + /// The time (in milliseconds) it will take to reach `new_diameter`. pub speed: VarLong, + /// The maximum distance a player can be teleported by a portal + /// before the border prevents the teleport. pub portal_teleport_boundary: VarInt, + /// Distance in blocks from the border where the screen starts to tint red. pub warning_blocks: VarInt, + /// Time in seconds that a player must be on a collision course with + /// the border before the warning tint appears. pub warning_time: VarInt, } diff --git a/pumpkin-protocol/src/java/client/play/keep_alive.rs b/pumpkin-protocol/src/java/client/play/keep_alive.rs index ed70231a3..ae9702914 100644 --- a/pumpkin-protocol/src/java/client/play/keep_alive.rs +++ b/pumpkin-protocol/src/java/client/play/keep_alive.rs @@ -2,9 +2,17 @@ use pumpkin_data::packet::clientbound::PLAY_KEEP_ALIVE; use pumpkin_macros::packet; use serde::{Deserialize, Serialize}; +/// Maintains the connection and measures latency (ping) between client and server. +/// +/// The server sends this packet at regular intervals (typically every 15–20 seconds). +/// The client must respond with the exact same ID. If the server does not receive +/// a response within a timeout period (usually 30 seconds), it will disconnect +/// the player with a "Timed Out" message. #[derive(Serialize, Deserialize)] #[packet(PLAY_KEEP_ALIVE)] pub struct CKeepAlive { + /// A unique random identifier for this specific keep-alive request. + /// Used to match the server's request with the client's response. pub keep_alive_id: i64, } diff --git a/pumpkin-protocol/src/java/client/play/level_event.rs b/pumpkin-protocol/src/java/client/play/level_event.rs index d0a6e5976..9f72fee13 100644 --- a/pumpkin-protocol/src/java/client/play/level_event.rs +++ b/pumpkin-protocol/src/java/client/play/level_event.rs @@ -3,12 +3,25 @@ use pumpkin_macros::packet; use pumpkin_util::math::position::BlockPos; use serde::{Deserialize, Serialize}; +/// Triggers a specific sound or particle effect at a world location. +/// +/// This packet handles a wide variety of "world-level" events, such as +/// block breaking particles, firework explosions, or ambient sounds +/// like doors opening and portals humming. #[derive(Serialize, Deserialize)] #[packet(PLAY_LEVEL_EVENT)] pub struct CLevelEvent { + /// The ID of the event to trigger. + /// Event IDs are generally divided into Sound Events (1000s) and + /// Particle/Visual Events (2000s). pub event: i32, + /// The world coordinates where the event occurs. pub location: BlockPos, + /// Event-specific data (e.g., the block ID for break particles + /// or the direction of a smoke puff). pub data: i32, + /// If true, the sound is played at a constant volume regardless of + /// the player's distance from the `location`. pub disable_relative_volume: bool, } diff --git a/pumpkin-protocol/src/java/client/play/login.rs b/pumpkin-protocol/src/java/client/play/login.rs index 480c74ae6..27220dc59 100644 --- a/pumpkin-protocol/src/java/client/play/login.rs +++ b/pumpkin-protocol/src/java/client/play/login.rs @@ -6,30 +6,49 @@ use serde::Serialize; use crate::VarInt; +/// The "Join Game" packet that transitions the client from the Configuration state +/// to the Play state. +/// +/// This is one of the largest and most important packets in the protocol. It +/// initializes the player's world view, dimension settings, and local game +/// rules. Once received, the client begins rendering the world. #[derive(Serialize)] #[packet(PLAY_LOGIN)] pub struct CLogin<'a> { + /// The unique ID assigned to the player for the current session. pub entity_id: i32, pub is_hardcore: bool, + /// A list of all dimensions present on the server (e.g., overworld, nether, end). pub dimension_names: &'a [ResourceLocation], pub max_players: VarInt, + /// The number of chunks the client will render in each direction. pub view_distance: VarInt, + /// The distance at which entities and world ticks are processed. pub simulated_distance: VarInt, + /// If true, hides coordinates and other info from the F3 screen. pub reduced_debug_info: bool, pub enabled_respawn_screen: bool, pub limited_crafting: bool, // Spawn info + /// The registry ID for the current dimension's properties (lighting, sky color). pub dimension_type: VarInt, + /// The specific resource location of the current dimension. pub dimension_name: ResourceLocation, - /// First 8 bytes of the SHA-256 hash of the world's seed. Used client side for biome noise + /// Used by the client to seed local biome noise and decoration algorithms. pub hashed_seed: i64, pub game_mode: u8, + /// The previous gamemode (used for the F3+F4 toggle UI). -1 if none. pub previous_gamemode: i8, + /// If true, the world is a debug world (all blocks shown in a grid). pub debug: bool, + /// If true, the world is a flat world (affects the horizon rendering). pub is_flat: bool, + /// The location where the player last died (used for the recovery compass). pub death_dimension_name: Option<(ResourceLocation, BlockPos)>, pub portal_cooldown: VarInt, + /// The height of the ocean level (usually 63). pub sealevel: VarInt, + /// If true, the client will warn the player if they send unsigned chat messages. pub enforce_secure_chat: bool, } diff --git a/pumpkin-protocol/src/java/client/play/multi_block_update.rs b/pumpkin-protocol/src/java/client/play/multi_block_update.rs index 6616c2d24..716c7bc91 100644 --- a/pumpkin-protocol/src/java/client/play/multi_block_update.rs +++ b/pumpkin-protocol/src/java/client/play/multi_block_update.rs @@ -9,9 +9,18 @@ use serde::{Serialize, ser::SerializeTuple}; use crate::codec::{var_int::VarInt, var_long::VarLong}; +/// Updates multiple blocks within a single 16x16x16 chunk section. +/// +/// This packet is much more efficient than sending multiple individual +/// `CBlockUpdate` packets when many changes occur in the same area +/// (e.g., explosions, structure generation, or large-scale terraforming). #[packet(PLAY_SECTION_BLOCKS_UPDATE)] pub struct CMultiBlockUpdate { + /// The coordinates of the chunk section being updated. + /// Calculated as (block_coord >> 4). pub chunk_section: Vector3, + /// A list of relative positions and their new block state IDs. + /// The i16 encodes the relative position within the section. pub positions_to_state_ids: Vec<(i16, i32)>, } diff --git a/pumpkin-protocol/src/java/client/play/open_screen.rs b/pumpkin-protocol/src/java/client/play/open_screen.rs index 4fead3210..752a42f16 100644 --- a/pumpkin-protocol/src/java/client/play/open_screen.rs +++ b/pumpkin-protocol/src/java/client/play/open_screen.rs @@ -6,11 +6,23 @@ use serde::Serialize; use crate::VarInt; +/// Instructs the client to open a specific type of GUI (inventory, chest, etc.). +/// +/// This packet is sent when a player interacts with a block (like a chest) +/// or when a command/plugin forces an interface to open. It establishes a +/// `sync_id` which must be used in all subsequent "Set Slot" or "Click Slot" +/// packets to ensure the server and client are talking about the same window. #[derive(Serialize)] #[packet(PLAY_OPEN_SCREEN)] pub struct COpenScreen<'a> { + /// A unique identifier for the current window session. + /// Typically increments by 1 for every new window opened. pub sync_id: VarInt, + /// The ID of the window type to open (e.g., Generic 9x3, Crafting Table). + /// See the table below for standard IDs. pub window_type: VarInt, + /// The title displayed at the top of the GUI. + /// Supports full JSON formatting (colors, bold, etc.). pub window_title: &'a TextComponent, } diff --git a/pumpkin-protocol/src/java/client/play/open_sign_editor.rs b/pumpkin-protocol/src/java/client/play/open_sign_editor.rs index 72809c124..6e3617a59 100644 --- a/pumpkin-protocol/src/java/client/play/open_sign_editor.rs +++ b/pumpkin-protocol/src/java/client/play/open_sign_editor.rs @@ -3,10 +3,18 @@ use pumpkin_macros::packet; use pumpkin_util::math::position::BlockPos; use serde::Serialize; +/// Opens the sign text input screen for the client. +/// +/// This packet is sent by the server to force the client to show the +/// sign editing interface. This usually happens immediately after a +/// player places a sign or interacts with an existing one (if allowed). #[derive(Serialize)] #[packet(PLAY_OPEN_SIGN_EDITOR)] pub struct COpenSignEditor { + /// The world coordinates of the sign block to be edited. pub location: BlockPos, + /// Whether the editor should open the front or the back of the sign. + /// Introduced in the 1.20 "Trails & Tales" update for double-sided signs. pub is_front_text: bool, } diff --git a/pumpkin-protocol/src/java/client/play/particle.rs b/pumpkin-protocol/src/java/client/play/particle.rs index bb33289ba..85295d002 100644 --- a/pumpkin-protocol/src/java/client/play/particle.rs +++ b/pumpkin-protocol/src/java/client/play/particle.rs @@ -5,17 +5,33 @@ use serde::Serialize; use crate::{VarInt, ser::network_serialize_no_prefix}; +/// Spawns a cluster of particles at a specific location. +/// +/// This is the most versatile visual packet in the protocol. It allows for +/// precise control over particle density, spread, and speed. It can also +/// carry extra data for complex particles like redstone dust (color) or +/// block/item breaking (textures). #[derive(Serialize)] #[packet(PLAY_LEVEL_PARTICLES)] pub struct CParticle<'a> { + /// If true, the particle renders even if the client's "Particles" + /// setting is set to "Minimal". pub force_spawn: bool, - /// If true, particle distance increases from 256 to 65536. + /// If true, the distance at which particles are visible is significantly + /// increased (from 256 to 65536 blocks). Often used for massive events. pub important: bool, + /// The absolute center position of the particle cluster. pub position: Vector3, + /// The maximum distance from the center that particles can spawn. pub offset: Vector3, + /// The velocity or "spread" speed of the particles. pub max_speed: f32, + /// The total number of particles to spawn in this cluster. pub particle_count: i32, + /// The ID of the particle type (e.g., `minecraft:flame`). pub particle_id: VarInt, + /// Extra data required by specific particles (e.g., block states for + /// `block` particles or RGB values for `dust`). #[serde(serialize_with = "network_serialize_no_prefix")] pub data: &'a [u8], } diff --git a/pumpkin-protocol/src/java/client/play/ping_response.rs b/pumpkin-protocol/src/java/client/play/ping_response.rs index e4fbfc498..a26c87552 100644 --- a/pumpkin-protocol/src/java/client/play/ping_response.rs +++ b/pumpkin-protocol/src/java/client/play/ping_response.rs @@ -2,9 +2,12 @@ use pumpkin_data::packet::clientbound::PLAY_PONG_RESPONSE; use pumpkin_macros::packet; use serde::{Deserialize, Serialize}; +/// Responds to a client-initiated ping request to synchronize game state. #[derive(Serialize, Deserialize)] #[packet(PLAY_PONG_RESPONSE)] pub struct CPingResponse { + /// The unique identifier sent by the client in the initial Ping packet. + /// The server must return this exact value. pub payload: i64, } diff --git a/pumpkin-protocol/src/java/client/play/player_abilities.rs b/pumpkin-protocol/src/java/client/play/player_abilities.rs index 92a6b6889..8e47185de 100644 --- a/pumpkin-protocol/src/java/client/play/player_abilities.rs +++ b/pumpkin-protocol/src/java/client/play/player_abilities.rs @@ -2,11 +2,25 @@ use pumpkin_data::packet::clientbound::PLAY_PLAYER_ABILITIES; use pumpkin_macros::packet; use serde::Serialize; +/// Updates the player's movement and interaction abilities. +/// +/// This packet informs the client about the player's state (flying, invulnerable) +/// and sets the movement speeds. While the client applies these visuals, +/// the server must still validate these states to prevent cheating. #[derive(Serialize)] #[packet(PLAY_PLAYER_ABILITIES)] pub struct CPlayerAbilities { + /// A bitmask of player states. + /// Bit 0 (0x01): Invulnerable (Creative mode) + /// Bit 1 (0x02): Flying + /// Bit 2 (0x04): Allow Flying + /// Bit 3 (0x08): Creative Mode (Instant Break) pub flags: i8, + /// The multiplier for flying speed. + /// Default is 0.05. pub flying_speed: f32, + /// The field of view modifier (Walking speed multiplier). + /// Default is 0.1. pub field_of_view: f32, } diff --git a/pumpkin-protocol/src/java/client/play/player_chat_message.rs b/pumpkin-protocol/src/java/client/play/player_chat_message.rs index d569c620d..177cd0a06 100644 --- a/pumpkin-protocol/src/java/client/play/player_chat_message.rs +++ b/pumpkin-protocol/src/java/client/play/player_chat_message.rs @@ -10,23 +10,43 @@ use crate::{ ser::NetworkWriteExt, }; +/// Sends a cryptographically signed player chat message to the client. +/// +/// This packet is the backbone of the modern secure chat system. It includes +/// tracking indices, digital signatures, and context for reporting. #[packet(PLAY_PLAYER_CHAT)] pub struct CPlayerChatMessage { - /// An index that increases for every message sent TO the client + /// Incremental index for messages sent TO this specific client. + /// Starts at 0 on login; client disconnects if the sequence is broken. pub global_index: VarInt, + /// The UUID of the player who sent the message. pub sender: uuid::Uuid, - /// An index that increases for every message sent BY the client + /// Incremental index for messages sent BY the sender player. + /// Used by the client to verify the order of the sender's history. pub index: VarInt, - pub message_signature: Option>, // always 256 + /// The RSA signature (256 bytes) verifying the message's authenticity. + pub message_signature: Option>, + /// The raw plain-text content of the message. pub message: String, + /// Epoch timestamp (milliseconds) when the message was sent. pub timestamp: i64, + /// A random 64-bit value used to ensure signature uniqueness. pub salt: i64, - pub previous_messages: Box<[PreviousMessage]>, // max 20 + /// Last 20 message signatures seen by the sender, providing context + /// for chat reporting and ensuring no messages were omitted. + pub previous_messages: Box<[PreviousMessage]>, + /// Optional formatted version of the message (e.g., if the server + /// added colors or links that aren't in the signed raw text). pub unsigned_content: Option, + /// Indicates if the message should be hidden or partially masked + /// by the client's profanity filter. pub filter_type: FilterType, - /// This should not be zero, (index + 1) + /// ID of the chat type registry entry (e.g., "chat", "say_command"). + /// Usually `(index + 1)`. pub chat_type: VarInt, + /// The display name of the sender. pub sender_name: TextComponent, + /// The display name of the target (used in private messages). pub target_name: Option, } diff --git a/pumpkin-protocol/src/java/client/play/player_info_update.rs b/pumpkin-protocol/src/java/client/play/player_info_update.rs index e8323f638..12d1eeb61 100644 --- a/pumpkin-protocol/src/java/client/play/player_info_update.rs +++ b/pumpkin-protocol/src/java/client/play/player_info_update.rs @@ -10,22 +10,42 @@ use crate::{ClientPacket, Property, WritingError, ser::NetworkWriteExt}; use super::PlayerAction; bitflags! { + /// Defines which fields are present in the Player Info Update packet. + /// + /// This bitmask allows the server to update multiple aspects of a player's + /// presence in the Tab List (and global state) in a single packet. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct PlayerInfoFlags: u8 { + /// Adds the player to the client's internal player list (Registry). const ADD_PLAYER = 0x01; + /// Initializes the chat signature session for secure chat. const INITIALIZE_CHAT = 0x02; + /// Changes the player's displayed gamemode in the Tab list. const UPDATE_GAME_MODE = 0x04; + /// Determines if the player is visible in the Tab list. const UPDATE_LISTED = 0x08; + /// Updates the ping/latency bars. const UPDATE_LATENCY = 0x10; + /// Changes the name shown in the Tab list (supports formatting). const UPDATE_DISPLAY_NAME = 0x20; + /// Sets the sorting order in the Tab list (Latest 2026 feature). const UPDATE_LIST_PRIORITY = 0x40; + /// Toggles the visibility of the player's hat layer. const UPDATE_HAT = 0x80; } } +/// Updates one or more players' information on the client. +/// +/// This packet replaces the legacy "Player Info" packet with a more efficient +/// bitmask-driven approach. Instead of sending full data every time, the +/// server only sends the fields specified in the `actions` bitmask. #[packet(PLAY_PLAYER_INFO_UPDATE)] pub struct CPlayerInfoUpdate<'a> { + /// The bitmask (PlayerInfoFlags) determining which data follows. pub actions: u8, + /// The list of players being updated. Each player entry contains + /// data fields in the order they appear in the bitmask. pub players: &'a [Player<'a>], } diff --git a/pumpkin-protocol/src/java/client/play/player_position.rs b/pumpkin-protocol/src/java/client/play/player_position.rs index 2ca493d6c..2e10713b6 100644 --- a/pumpkin-protocol/src/java/client/play/player_position.rs +++ b/pumpkin-protocol/src/java/client/play/player_position.rs @@ -9,14 +9,26 @@ use crate::{ ser::NetworkWriteExt, }; +/// Updates the player's position and rotation on the client. +/// +/// Commonly known as the "Teleport Packet," this is sent by the server to +/// force a change in the player's location. The client must respond with a +/// `Teleport Confirm` packet matching the `teleport_id`. #[packet(PLAY_PLAYER_POSITION)] pub struct CPlayerPosition { + /// A unique ID for this teleport. The client must echo this back + /// to confirm the teleport was processed. pub teleport_id: VarInt, + /// The absolute or relative target position. pub position: Vector3, + /// The intended velocity of the player after teleporting. pub delta: Vector3, + /// The horizontal rotation (0-360 degrees). pub yaw: f32, + /// The vertical rotation (-90 to 90 degrees). pub pitch: f32, - pub releatives: Vec, + /// A set of flags determining which of the above fields are relative (~). + pub relatives: Vec, } impl CPlayerPosition { @@ -26,7 +38,7 @@ impl CPlayerPosition { delta: Vector3, yaw: f32, pitch: f32, - releatives: Vec, + relatives: Vec, ) -> Self { Self { teleport_id, @@ -34,7 +46,7 @@ impl CPlayerPosition { delta, yaw, pitch, - releatives, + relatives, } } } @@ -58,7 +70,7 @@ impl ClientPacket for CPlayerPosition { write.write_f32_be(self.yaw)?; write.write_f32_be(self.pitch)?; // not sure about that - write.write_i32_be(PositionFlag::get_bitfield(self.releatives.as_slice())) + write.write_i32_be(PositionFlag::get_bitfield(self.relatives.as_slice())) } } @@ -71,7 +83,7 @@ impl ServerPacket for CPlayerPosition { delta: Vector3::new(0.0, 0.0, 0.0), yaw: 0.0, pitch: 0.0, - releatives: Vec::new(), + relatives: Vec::new(), }) } } diff --git a/pumpkin-protocol/src/java/client/play/player_remove.rs b/pumpkin-protocol/src/java/client/play/player_remove.rs index decd1386e..0d5919e77 100644 --- a/pumpkin-protocol/src/java/client/play/player_remove.rs +++ b/pumpkin-protocol/src/java/client/play/player_remove.rs @@ -2,9 +2,17 @@ use pumpkin_data::packet::clientbound::PLAY_PLAYER_INFO_REMOVE; use pumpkin_macros::packet; use serde::{Serialize, ser::SerializeSeq}; +/// Sent by the server to remove one or more players from the client's player list (tab list). +/// +/// This packet is typically used when a player leaves the server or becomes invisible +/// to the recipient (e.g., moving out of tracking range). #[derive(Serialize)] #[packet(PLAY_PLAYER_INFO_REMOVE)] pub struct CRemovePlayerInfo<'a> { + /// A list of UUIDs corresponding to the players that should be removed. + /// + /// The field uses a custom serializer to format the UUIDs according to the + /// Minecraft protocol's VarInt-prefixed array format. #[serde(serialize_with = "serialize_slice_uuids")] pub players: &'a [uuid::Uuid], } diff --git a/pumpkin-protocol/src/java/client/play/player_spawn_position.rs b/pumpkin-protocol/src/java/client/play/player_spawn_position.rs index 29cbb1b0b..c3c45e4c7 100644 --- a/pumpkin-protocol/src/java/client/play/player_spawn_position.rs +++ b/pumpkin-protocol/src/java/client/play/player_spawn_position.rs @@ -4,12 +4,21 @@ use pumpkin_data::packet::clientbound::PLAY_SET_DEFAULT_SPAWN_POSITION; use pumpkin_macros::packet; use pumpkin_util::math::position::BlockPos; +/// Sent by the server to set the client's default spawn point and compass target. +/// +/// This packet updates where the player will respawn upon death (if no bed or anchor is set) +/// and dictates the coordinates that a compass will point toward. #[derive(Serialize)] #[packet(PLAY_SET_DEFAULT_SPAWN_POSITION)] pub struct CPlayerSpawnPosition { + /// The namespaced ID of the dimension (e.g., "minecraft:overworld"). + /// Required for the client to determine if the spawn point is in their current world. pub dimension_name: String, + /// The X, Y, and Z coordinates of the spawn location. pub location: BlockPos, + /// The horizontal rotation (0-360 degrees) the player's camera should face upon respawning. pub yaw: f32, + /// The vertical rotation (-90 to 90 degrees) the player's camera should face upon respawning. pub pitch: f32, } diff --git a/pumpkin-protocol/src/java/client/play/remove_entities.rs b/pumpkin-protocol/src/java/client/play/remove_entities.rs index 91aad0578..a238080aa 100644 --- a/pumpkin-protocol/src/java/client/play/remove_entities.rs +++ b/pumpkin-protocol/src/java/client/play/remove_entities.rs @@ -4,9 +4,14 @@ use serde::Serialize; use crate::VarInt; +/// Sent by the server to instruct the client to remove (despawn) one or more entities. +/// +/// This is typically sent when an entity leaves the player's tracking range, +/// is killed, or is otherwise removed from the world. #[derive(Serialize)] #[packet(PLAY_REMOVE_ENTITIES)] pub struct CRemoveEntities<'a> { + /// A list of entity IDs to be removed. pub entity_ids: &'a [VarInt], } diff --git a/pumpkin-protocol/src/java/client/play/update_score.rs b/pumpkin-protocol/src/java/client/play/update_score.rs index 7c808637c..a6d84636f 100644 --- a/pumpkin-protocol/src/java/client/play/update_score.rs +++ b/pumpkin-protocol/src/java/client/play/update_score.rs @@ -6,13 +6,25 @@ use serde::Serialize; use crate::{NumberFormat, VarInt}; +/// Sent by the server to create or update a score for an entity on a specific objective. +/// +/// This packet is the primary way to manage scoreboard data. In the latest protocol, +/// it also supports optional custom formatting for how the numeric score is displayed. #[derive(Serialize)] #[packet(PLAY_SET_SCORE)] pub struct CUpdateScore { + /// The name of the entity whose score is being updated (e.g., a player's username + /// or a non-player entry like "Kills"). pub entity_name: String, + /// The internal name of the objective this score belongs to. pub objective_name: String, + /// The actual integer value of the score. pub value: VarInt, + /// An optional custom name for the entity to be displayed in the scoreboard. + /// If `None`, the `entity_name` is used by default. pub display_name: Option, + /// Optional formatting for the number (e.g., blank, fixed text, or styled). + /// This allows for scores to appear as something other than raw numbers. pub number_format: Option, } diff --git a/pumpkin-protocol/src/java/client/play/worldevent.rs b/pumpkin-protocol/src/java/client/play/worldevent.rs index 164eccd9d..63ac51ff3 100644 --- a/pumpkin-protocol/src/java/client/play/worldevent.rs +++ b/pumpkin-protocol/src/java/client/play/worldevent.rs @@ -4,12 +4,25 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_macros::packet; use serde::Serialize; +/// Sent by the server to trigger a specific sound or particle effect at a world location. +/// +/// This is used for a wide variety of effects, from breaking blocks and firework +/// explosions to splashing water or record playing. #[derive(Serialize)] #[packet(PLAY_LEVEL_EVENT)] pub struct CWorldEvent { + /// The ID of the event to trigger (e.g., 1000 for a bow shoot, 2001 for block break). + /// Refer to the latest protocol registry for the full list of sound/particle IDs. pub event: i32, + /// The world coordinates where the effect should originate. pub location: BlockPos, + /// Additional metadata associated with the event. + /// + /// For example, if breaking a block, this contains the block ID. + /// For firework particles, it may contain the color or type. pub data: i32, + /// If true, the sound will be played at a constant volume regardless of the + /// player's distance from the `location`. pub disable_relative_volume: bool, } diff --git a/pumpkin-protocol/src/java/client/status/ping_response.rs b/pumpkin-protocol/src/java/client/status/ping_response.rs index 2522f510d..ad141016a 100644 --- a/pumpkin-protocol/src/java/client/status/ping_response.rs +++ b/pumpkin-protocol/src/java/client/status/ping_response.rs @@ -2,10 +2,18 @@ use pumpkin_data::packet::clientbound::STATUS_PONG_RESPONSE; use pumpkin_macros::packet; use serde::{Deserialize, Serialize}; +/// Sent by the server to complete a latency check initiated by a `SStatusPingRequest`. +/// +/// This is the final packet in the Server List Ping (SLP) sequence. It allows the +/// client to calculate the round-trip time (ping) to the server. #[derive(Serialize, Deserialize)] #[packet(STATUS_PONG_RESPONSE)] pub struct CPingResponse { - pub payload: i64, // must respond with the same as in `SPingRequest` + /// The exact 64-bit integer received from the client's ping request. + /// + /// The client uses this value to ensure the response matches the specific + /// request it sent and to measure elapsed time. + pub payload: i64, } impl CPingResponse { diff --git a/pumpkin-protocol/src/java/client/status/status_response.rs b/pumpkin-protocol/src/java/client/status/status_response.rs index 3ebdd2fd0..fe124bc80 100644 --- a/pumpkin-protocol/src/java/client/status/status_response.rs +++ b/pumpkin-protocol/src/java/client/status/status_response.rs @@ -2,10 +2,18 @@ use pumpkin_data::packet::clientbound::STATUS_STATUS_RESPONSE; use pumpkin_macros::packet; use serde::{Deserialize, Serialize}; +/// Sent by the server in response to a `SStatusRequest`. +/// +/// This packet provides the client with the information required to display the +/// server in the multiplayer menu, including the MOTD, player count, and icon #[derive(Serialize, Deserialize)] #[packet(STATUS_STATUS_RESPONSE)] pub struct CStatusResponse { - pub json_response: String, // 32767 + /// A JSON-encoded string containing the server's status data. + /// + /// The maximum length of this string is 32,767 characters. It typically + /// includes fields for `version`, `players`, `description` (MOTD), and `favicon` + pub json_response: String, } impl CStatusResponse { pub fn new(json_response: String) -> Self { diff --git a/pumpkin-protocol/src/java/server/config/acknowledge_finish_config.rs b/pumpkin-protocol/src/java/server/config/acknowledge_finish_config.rs index e4c9c945a..55a6139ec 100644 --- a/pumpkin-protocol/src/java/server/config/acknowledge_finish_config.rs +++ b/pumpkin-protocol/src/java/server/config/acknowledge_finish_config.rs @@ -2,6 +2,8 @@ use pumpkin_data::packet::serverbound::CONFIG_FINISH_CONFIGURATION; use pumpkin_macros::packet; use serde::Serialize; +/// This packet signals to the server that the client is ready to transition +/// from the `Configuration` state to the `Play` state. #[derive(Serialize)] #[packet(CONFIG_FINISH_CONFIGURATION)] pub struct SAcknowledgeFinishConfig; diff --git a/pumpkin-protocol/src/java/server/config/client_information.rs b/pumpkin-protocol/src/java/server/config/client_information.rs index 441852e44..707398145 100644 --- a/pumpkin-protocol/src/java/server/config/client_information.rs +++ b/pumpkin-protocol/src/java/server/config/client_information.rs @@ -4,15 +4,24 @@ use serde::Serialize; use crate::VarInt; +/// Sent by the client to inform the server about its local settings #[derive(serde::Deserialize, Serialize)] #[packet(CONFIG_CLIENT_INFORMATION)] pub struct SClientInformationConfig { - pub locale: String, // 16 + /// The language code used by the client (e.g., "en_us") + pub locale: String, + /// The maximum number of chunks the client renders pub view_distance: i8, - pub chat_mode: VarInt, // VarInt + /// Visibility of chat messages (0: Enabled, 1: Commands Only, 2: Hidden) + pub chat_mode: VarInt, + /// Whether the client wants chat colors/formatting rendered pub chat_colors: bool, + /// Bitmask representing displayed skin parts (e.g., cape, jacket, sleeves) pub skin_parts: u8, + /// The player's dominant hand (0: Left, 1: Right) pub main_hand: VarInt, + /// Whether the client wants text filtering (e.g., for profanity) enabled pub text_filtering: bool, + /// Whether the player should appear in the server's online player list pub server_listing: bool, } diff --git a/pumpkin-protocol/src/java/server/config/cookie_response.rs b/pumpkin-protocol/src/java/server/config/cookie_response.rs index 95b7f0241..dbec272d8 100644 --- a/pumpkin-protocol/src/java/server/config/cookie_response.rs +++ b/pumpkin-protocol/src/java/server/config/cookie_response.rs @@ -6,17 +6,23 @@ use pumpkin_util::resource_location::ResourceLocation; use crate::{ReadingError, ServerPacket, ser::NetworkReadExt}; -#[packet(CONFIG_COOKIE_RESPONSE)] -/// Response to a `CCookieRequest` (configuration) from the server. -/// The Notchian (vanilla) server only accepts responses of up to 5 KiB in size. -pub struct SConfigCookieResponse { - pub key: ResourceLocation, - pub has_payload: bool, - pub payload: Option>, // 5120, -} - +/// The maximum allowed size for a cookie payload (5 KiB). const MAX_COOKIE_LENGTH: usize = 5120; +/// Response to a `CCookieRequest` from the server during the configuration phase +/// +/// Cookies allow servers to store small amounts of data on the client side, +/// which can be retrieved later (e.g., for session tracking or preferences) +#[packet(CONFIG_COOKIE_RESPONSE)] +pub struct SConfigCookieResponse { + /// The unique identifier for the cookie being returned + pub key: ResourceLocation, + /// Indicates whether a payload is attached to this response + pub has_payload: bool, + /// The actual data stored in the cookie. Limited to 5120 bytes + pub payload: Option>, +} + impl ServerPacket for SConfigCookieResponse { fn read(read: impl Read) -> Result { let mut read = read; diff --git a/pumpkin-protocol/src/java/server/config/plugin_message.rs b/pumpkin-protocol/src/java/server/config/plugin_message.rs index 0ba44a738..57830b101 100644 --- a/pumpkin-protocol/src/java/server/config/plugin_message.rs +++ b/pumpkin-protocol/src/java/server/config/plugin_message.rs @@ -5,11 +5,20 @@ use pumpkin_macros::packet; use pumpkin_util::resource_location::ResourceLocation; use crate::{ReadingError, ServerPacket, ser::NetworkReadExt}; + +/// The maximum allowed size for a plugin message payload (1 MiB). const MAX_PAYLOAD_SIZE: usize = 1048576; +/// A packet used for custom communication between the client and server. +/// +/// This allows mods, plugins, or proxy +/// software to send proprietary data over the standard Minecraft protocol. #[packet(CONFIG_CUSTOM_PAYLOAD)] pub struct SPluginMessage { + /// The name of the channel used to distinguish different types of messages. + /// Example: `minecraft:brand` or `velocity:main`. pub channel: ResourceLocation, + /// The payload sent by the client. pub data: Box<[u8]>, } diff --git a/pumpkin-protocol/src/java/server/config/resource_pack_response.rs b/pumpkin-protocol/src/java/server/config/resource_pack_response.rs index f067c6070..d5766da03 100644 --- a/pumpkin-protocol/src/java/server/config/resource_pack_response.rs +++ b/pumpkin-protocol/src/java/server/config/resource_pack_response.rs @@ -16,11 +16,17 @@ pub enum ResourcePackResponseResult { Unknown(i32), } +/// Sent by the client to inform the server of the status of a requested resource pack. +/// +/// This allows the server to know if the player is using the required textures +/// or if the download failed. #[derive(serde::Deserialize, Serialize)] #[packet(CONFIG_RESOURCE_PACK)] pub struct SConfigResourcePack { + /// The unique identifier of the resource pack this response refers to. #[serde(with = "uuid::serde::compact")] pub uuid: uuid::Uuid, + /// The status code of the operation, mapped to [ResourcePackResponseResult]. result: VarInt, } diff --git a/pumpkin-protocol/src/java/server/handshake/mod.rs b/pumpkin-protocol/src/java/server/handshake/mod.rs index 2bda71d1e..867f385e4 100644 --- a/pumpkin-protocol/src/java/server/handshake/mod.rs +++ b/pumpkin-protocol/src/java/server/handshake/mod.rs @@ -8,11 +8,19 @@ use pumpkin_data::packet::serverbound::HANDSHAKE_INTENTION; use pumpkin_macros::packet; use pumpkin_util::version::MinecraftVersion; +/// The very first packet sent by the client to initiate a connection +/// +/// It determines whether the client wants to check the server status (SLP) +/// or actually login to play. #[packet(HANDSHAKE_INTENTION)] pub struct SHandShake { + /// The protocol version of the client (e.g., 767 for 1.21). pub protocol_version: VarInt, - pub server_address: String, // 255 + /// The hostname or IP used by the client to connect + pub server_address: String, + /// The port number used by the client to connect pub server_port: u16, + /// The state the client wants to transition to (1 for Status, 2 for Login) pub next_state: ConnectionState, } diff --git a/pumpkin-protocol/src/java/server/status/ping_request.rs b/pumpkin-protocol/src/java/server/status/ping_request.rs index 090dde1ca..9725a8c4a 100644 --- a/pumpkin-protocol/src/java/server/status/ping_request.rs +++ b/pumpkin-protocol/src/java/server/status/ping_request.rs @@ -2,6 +2,10 @@ use pumpkin_data::packet::serverbound::STATUS_PING_REQUEST; use pumpkin_macros::packet; use serde::Serialize; +/// Sent by the client to measure the round-trip time (latency) to the server. +/// +/// This is the second part of the Server List Ping (SLP) process +/// The server should respond with `CPingResponse`. #[derive(serde::Deserialize, Serialize)] #[packet(STATUS_PING_REQUEST)] pub struct SStatusPingRequest { diff --git a/pumpkin-protocol/src/java/server/status/status_request.rs b/pumpkin-protocol/src/java/server/status/status_request.rs index c68a43b77..384960f29 100644 --- a/pumpkin-protocol/src/java/server/status/status_request.rs +++ b/pumpkin-protocol/src/java/server/status/status_request.rs @@ -2,6 +2,10 @@ use pumpkin_data::packet::serverbound::STATUS_STATUS_REQUEST; use pumpkin_macros::packet; use serde::Serialize; +/// Sent by the client to request the server's current status information. +/// +/// This is the first packet sent during the "Status" state. +/// The server should respond with `CStatusResponse`. #[derive(Serialize)] #[packet(STATUS_STATUS_REQUEST)] pub struct SStatusRequest; diff --git a/pumpkin-util/src/version.rs b/pumpkin-util/src/version.rs index 7c030262d..4c697d69e 100644 --- a/pumpkin-util/src/version.rs +++ b/pumpkin-util/src/version.rs @@ -1,55 +1,74 @@ +/// Represents a specific version of the Minecraft Java Edition protocol. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[allow(non_camel_case_types)] pub enum MinecraftVersion { + /// 1.7.2: The Update That Changed The World V_1_7_2, V_1_7_6, + /// 1.8: The Bountiful Update V_1_8, + /// 1.9: The Combat Update V_1_9, V_1_9_1, V_1_9_2, V_1_9_3, + /// 1.10: The Frostburn Update V_1_10, + /// 1.11: The Exploration Update V_1_11, V_1_11_1, + /// 1.12: The World of Color Update V_1_12, V_1_12_1, V_1_12_2, + /// 1.13: Update Aquatic V_1_13, V_1_13_1, V_1_13_2, + /// 1.14: Village & Pillage V_1_14, V_1_14_1, V_1_14_2, V_1_14_3, V_1_14_4, + /// 1.15: Buzzy Bees V_1_15, V_1_15_1, V_1_15_2, + /// 1.16: Nether Update V_1_16, V_1_16_1, V_1_16_2, V_1_16_3, V_1_16_4, + /// 1.17: Caves & Cliffs: Part I V_1_17, V_1_17_1, + /// 1.18: Caves & Cliffs: Part II V_1_18, V_1_18_2, + /// 1.19: The Wild Update V_1_19, V_1_19_1, V_1_19_3, V_1_19_4, + /// 1.20: Trails & Tales V_1_20, V_1_20_2, V_1_20_3, + /// 1.20.5: Armored Paws V_1_20_5, + /// 1.21: Tricky Trials V_1_21, V_1_21_2, V_1_21_4, + /// 1.21.5: Bundles of Bravery V_1_21_5, V_1_21_6, V_1_21_7, V_1_21_9, V_1_21_11, + /// Fallback for unrecognized protocol versions. Unknown, } diff --git a/pumpkin/src/command/mod.rs b/pumpkin/src/command/mod.rs index 22dea8d98..e08107452 100644 --- a/pumpkin/src/command/mod.rs +++ b/pumpkin/src/command/mod.rs @@ -22,10 +22,31 @@ pub mod commands; pub mod dispatcher; pub mod tree; +/// Represents the source of a command execution. +/// +/// Different senders have different permissions, output targets, and +/// positions in the world. This enum abstracts those differences for the +/// command dispatcher. pub enum CommandSender { + /// A remote console connection via the RCON protocol. + /// + /// Stores an asynchronous buffer to capture command output + /// so it can be sent back over the network to the RCON client. Rcon(Arc>>), + /// The local server terminal/console. + /// + /// This sender typically has absolute permissions (bypass) and + /// outputs directly to the server logs. Console, + /// A player currently connected to the server. + /// + /// Contains a reference to the [Player] struct to access their + /// location, permissions, and session. Player(Arc), + /// A Command Block or Command Block Minecart. + /// + /// Contains the block entity responsible for the command and the + /// world context it exists in for coordinate-relative execution (e.g., `~ ~ ~`). CommandBlock(Arc, Arc), } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index e7857e0a3..79bb893d6 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -1227,6 +1227,8 @@ impl Player { ability_value |= 1 << (ability as u32); } }; + dbg!(abilities.allow_flying); + dbg!(abilities.flying); // Base Permissions set_ability(Ability::MayFly, abilities.allow_flying); diff --git a/pumpkin/src/net/bedrock/play.rs b/pumpkin/src/net/bedrock/play.rs index eb078f61e..30bfad9c0 100644 --- a/pumpkin/src/net/bedrock/play.rs +++ b/pumpkin/src/net/bedrock/play.rs @@ -6,10 +6,7 @@ use std::{ use pumpkin_macros::send_cancellable; use pumpkin_protocol::{ bedrock::{ - client::{ - chunk_radius_update::CChunkRadiusUpdate, container_open::CContainerOpen, - set_actor_motion::CSetActorMotion, - }, + client::{chunk_radius_update::CChunkRadiusUpdate, container_open::CContainerOpen}, server::{ command_request::SCommandRequest, container_close::SContainerClose, @@ -19,15 +16,10 @@ use pumpkin_protocol::{ text::SText, }, }, - codec::{ - bedrock_block_pos::NetworkPos, var_int::VarInt, var_long::VarLong, var_ulong::VarULong, - }, + codec::{bedrock_block_pos::NetworkPos, var_int::VarInt, var_long::VarLong}, java::client::play::CSystemChatMessage, }; -use pumpkin_util::{ - math::{position::BlockPos, vector3::Vector3}, - text::TextComponent, -}; +use pumpkin_util::{math::position::BlockPos, text::TextComponent}; use crate::{ command::CommandSender, @@ -115,15 +107,6 @@ impl BedrockClient { } else if input_data.get(InputData::StopSneaking) { entity.set_sneaking(false).await; } - - if !player.abilities.lock().await.flying { - self.send_game_packet(&CSetActorMotion { - target_runtime_id: VarULong(entity.entity_id as _), - motion: packet.pos_delta + Vector3::new(0.0, -0.08, 0.0), - tick: packet.client_tick, - }) - .await; - } } pub async fn handle_interaction(&self, _player: &Arc, packet: SInteraction) { diff --git a/pumpkin/src/net/lan_broadcast.rs b/pumpkin/src/net/lan_broadcast.rs index 222c3fee6..88f15c461 100644 --- a/pumpkin/src/net/lan_broadcast.rs +++ b/pumpkin/src/net/lan_broadcast.rs @@ -7,8 +7,10 @@ use tokio::{select, time}; use crate::{SHOULD_STOP, STOP_INTERRUPT}; -// https://www.wikiwand.com/en/articles/Multicast_address - +/// The standard Minecraft multicast address used for LAN discovery +/// +/// Bedrock and Java editions use this specific multicast group to "shout" +/// server presence to clients on the same local network const BROADCAST_ADDRESS: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(224, 0, 2, 60)), 4445); @@ -18,6 +20,7 @@ pub struct LANBroadcast { } impl LANBroadcast { + /// Creates a new LAN broadcast instance from the provided configuration #[must_use] pub fn new(config: &LANBroadcastConfig, basic_config: &BasicConfiguration) -> Self { let port = config.port.unwrap_or(0); @@ -36,6 +39,17 @@ impl LANBroadcast { Self { port, motd } } + /// Starts the UDP broadcast loop. This should be spawned in a separate task + /// + /// The loop sends a packet every 1.5 seconds containing the MOTD and the + /// port the actual game server is listening on. + /// + /// # Arguments + /// * `bound_addr` - The address where the actual Minecraft server is running + /// The port from this address is what clients will use to connect + /// + /// # Panics + /// Panics if the UDP socket cannot be bound or if broadcast permissions are denied pub async fn start(self, bound_addr: SocketAddr) { let socket = UdpSocket::bind(format!("0.0.0.0:{}", self.port)) .await diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index a854deb8d..08e490d66 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -56,6 +56,10 @@ use pumpkin_data::{ use pumpkin_data::{BlockDirection, BlockState}; use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_nbt::{compound::NbtCompound, to_bytes_unnamed}; +use pumpkin_protocol::bedrock::client::set_actor_data::{ + CSetActorData, EntityMetadata, MetadataValue, PropertySyncData, entity_data_flag, + entity_data_key, +}; use pumpkin_protocol::bedrock::client::start_game::CStartGame; use pumpkin_protocol::bedrock::frame_set::FrameSet; use pumpkin_protocol::java::client::play::CPlayerSpawnPosition; @@ -1316,6 +1320,26 @@ impl World { let mut abilities = player.abilities.lock().await; abilities.set_for_gamemode(player.gamemode.load()); }; + let mut metadata = EntityMetadata::default(); + + metadata.set(entity_data_key::WIDTH, MetadataValue::Float(0.6)); + metadata.set(entity_data_key::HEIGHT, MetadataValue::Float(1.8)); + + // This is super important, oterwise the client will float by default + metadata.set_flag(entity_data_flag::HAS_GRAVITY); + + // Prevents the client from showing air buddles on hud even when not in water + metadata.set_flag(entity_data_flag::BREATHING); + let actor_data = CSetActorData { + actor_runtime_id: VarULong(runtime_id), + metadata, + synced_properties: PropertySyncData { + int_properties: HashMap::new(), + float_properties: HashMap::new(), + }, + tick: VarULong(0), + }; + client.send_game_packet(&actor_data).await; player.send_abilities_update().await;