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:
parent
33cf5237a8
commit
7eccda00ff
32
Cargo.lock
generated
32
Cargo.lock
generated
@ -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",
|
||||
]
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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();
|
||||
"states" => loop {
|
||||
let prop_type = read_byte(reader);
|
||||
if prop_type == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
match byte {
|
||||
8 => {
|
||||
let b = &mut vec![0; read_varint(reader) as usize];
|
||||
reader.read_exact(b).unwrap();
|
||||
}
|
||||
3 => {
|
||||
read_varint(reader);
|
||||
}
|
||||
let prop_key = read_nbt_string(reader);
|
||||
|
||||
let prop_val = match prop_type {
|
||||
1 => {
|
||||
read_byte(reader);
|
||||
}
|
||||
_ => panic!("{}", byte),
|
||||
}
|
||||
byte = read_byte(reader);
|
||||
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);
|
||||
}
|
||||
|
||||
if !block_name.is_empty() {
|
||||
block_data
|
||||
.entry(name)
|
||||
.and_modify(|(v, _)| *v += 1)
|
||||
.or_insert((1, current_id));
|
||||
.entry(block_name)
|
||||
.or_default()
|
||||
.push((current_id, properties));
|
||||
}
|
||||
|
||||
current_id += 1;
|
||||
}
|
||||
|
||||
block_data
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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>,
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
397
pumpkin-protocol/src/bedrock/client/set_actor_data.rs
Normal file
397
pumpkin-protocol/src/bedrock/client/set_actor_data.rs
Normal 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;
|
||||
}
|
||||
@ -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>,
|
||||
|
||||
@ -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)?;
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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 }
|
||||
}
|
||||
|
||||
@ -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],
|
||||
}
|
||||
|
||||
|
||||
@ -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],
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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]>,
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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 }
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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]>,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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>>,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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>,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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]>,
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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)>,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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],
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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>,
|
||||
}
|
||||
|
||||
|
||||
@ -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>],
|
||||
}
|
||||
|
||||
|
||||
@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -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],
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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],
|
||||
}
|
||||
|
||||
|
||||
@ -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>,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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]>,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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>),
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user