fix: bedrock movement

also added a bunch of docs, and fixed bedrock air bubbles constantly shown, and also fix bedrock block states
This commit is contained in:
Alexander Medvedev 2026-01-24 22:05:50 +01:00
parent 33cf5237a8
commit 7eccda00ff
82 changed files with 1337 additions and 160 deletions

32
Cargo.lock generated
View File

@ -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",
]

View File

@ -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"

View File

@ -681,17 +681,6 @@ pub(crate) fn build() -> TokenStream {
let mut existing_item_ids: std::collections::HashSet<u16> = 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<i32> = 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<i32> = 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<R: Read>(reader: &mut R) -> BTreeMap<String, (u32, u32)> {
let mut block_data: BTreeMap<String, (u32, u32)> = BTreeMap::new();
#[expect(clippy::type_complexity)]
fn get_be_data_from_nbt<R: Read>(
reader: &mut R,
) -> BTreeMap<String, Vec<(u32, BTreeMap<String, String>)>> {
let mut block_data: BTreeMap<String, Vec<(u32, BTreeMap<String, String>)>> = 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<u8> {
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
}

View File

@ -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,
}

View File

@ -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<Flammable>,
/// Defines the items dropped when this block is destroyed.
pub loot_table: Option<LootTable>,
/// Defines the amount of XP dropped when the block is mined (e.g., Coal or Diamond).
pub experience: Option<Experience>,
}

View File

@ -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;

View File

@ -11,9 +11,16 @@ pub struct CMovePlayer {
pub position: Vector3<f32>,
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;
}

View File

@ -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<u32, MetadataValue>);
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<W: 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<W: 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<u32, i32>,
pub float_properties: std::collections::HashMap<u32, f32>,
}
impl PacketWrite for PropertySyncData {
fn write<W: 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;
}

View File

@ -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<f32>,

View File

@ -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<W: Write>(&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<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
VarInt(self.0.0.x).write(writer)?;
VarUInt(self.0.0.y as u32).write(writer)?;

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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 }
}

View File

@ -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],
}

View File

@ -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],
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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]>,
}

View File

@ -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,
}

View File

@ -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 }
}
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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;

View File

@ -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);

View File

@ -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,
}

View File

@ -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 }

View File

@ -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,
}

View File

@ -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]>,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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<Vector3<f64>>,
}

View File

@ -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,
}

View File

@ -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>,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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]>,
}

View File

@ -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<f64>,
/// The current velocity (delta) of the entity, used by the client
/// for smooth interpolation.
pub delta: Vector3<f64>,
/// 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,
}

View File

@ -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<SoundEvent>,
/// 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,

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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<f64>,
/// 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<Vector3<f64>>,
/// 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<SoundEvent>,
/// The size of the block particles pool, used for debris visuals.
pub block_particles_pool_size: VarInt,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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<i32>,
/// 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)>,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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<f64>,
/// The maximum distance from the center that particles can spawn.
pub offset: Vector3<f32>,
/// 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],
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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<Box<[u8]>>, // always 256
/// The RSA signature (256 bytes) verifying the message's authenticity.
pub message_signature: Option<Box<[u8]>>,
/// 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<TextComponent>,
/// 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<TextComponent>,
}

View File

@ -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>],
}

View File

@ -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<f64>,
/// The intended velocity of the player after teleporting.
pub delta: Vector3<f64>,
/// The horizontal rotation (0-360 degrees).
pub yaw: f32,
/// The vertical rotation (-90 to 90 degrees).
pub pitch: f32,
pub releatives: Vec<PositionFlag>,
/// A set of flags determining which of the above fields are relative (~).
pub relatives: Vec<PositionFlag>,
}
impl CPlayerPosition {
@ -26,7 +38,7 @@ impl CPlayerPosition {
delta: Vector3<f64>,
yaw: f32,
pitch: f32,
releatives: Vec<PositionFlag>,
relatives: Vec<PositionFlag>,
) -> 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(),
})
}
}

View File

@ -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],
}

View File

@ -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,
}

View File

@ -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],
}

View File

@ -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<TextComponent>,
/// 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<NumberFormat>,
}

View File

@ -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,
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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,
}

View File

@ -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<Box<[u8]>>, // 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<Box<[u8]>>,
}
impl ServerPacket for SConfigCookieResponse {
fn read(read: impl Read) -> Result<Self, ReadingError> {
let mut read = read;

View File

@ -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]>,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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 {

View File

@ -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;

View File

@ -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,
}

View File

@ -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<tokio::sync::Mutex<Vec<String>>>),
/// 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<Player>),
/// 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<dyn BlockEntity>, Arc<World>),
}

View File

@ -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);

View File

@ -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<Player>, packet: SInteraction) {

View File

@ -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

View File

@ -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;