perf: ditch serde because its slower

i rather trade performance and compile time over convenience
This commit is contained in:
Alexander Medvedev
2026-07-19 18:07:37 +02:00
parent 712bb96d3e
commit e0117ab559
175 changed files with 3995 additions and 3093 deletions

101
Cargo.lock generated
View File

@@ -120,7 +120,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -213,9 +213,9 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]]
name = "bitflags"
version = "2.13.0"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]]
name = "block-buffer"
@@ -485,7 +485,7 @@ version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -1047,7 +1047,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -1131,7 +1131,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -1147,7 +1147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -1300,7 +1300,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -2159,7 +2159,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -2186,7 +2186,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -2360,7 +2360,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -2403,7 +2403,7 @@ dependencies = [
"phf_shared",
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -2432,7 +2432,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -2520,7 +2520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -2600,7 +2600,7 @@ dependencies = [
"itertools 0.14.0",
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -2632,7 +2632,7 @@ checksum = "5a7ac85c0bb3fb351f10d531230aaa5e366b46d7c4e5328e5f02801d6dac1165"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -2701,7 +2701,7 @@ dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn",
"syn 3.0.0",
]
[[package]]
@@ -2759,7 +2759,7 @@ dependencies = [
"proc-macro-error2",
"pumpkin-data",
"quote",
"syn",
"syn 3.0.0",
]
[[package]]
@@ -2835,7 +2835,7 @@ dependencies = [
"serde",
"serde_json",
"sha2 0.11.0",
"syn",
"syn 3.0.0",
"thiserror 2.0.18",
"tokio",
"ureq",
@@ -3085,7 +3085,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -3230,7 +3230,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -3265,7 +3265,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -3398,7 +3398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -3455,6 +3455,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fac314a64dc9a36e61a9eb4261a5e9bbfbc922b27e518af97bc32b926cf967"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "sync_wrapper"
version = "1.0.2"
@@ -3469,7 +3480,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -3507,7 +3518,7 @@ dependencies = [
"fastrand",
"once_cell",
"rustix",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -3545,7 +3556,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -3556,7 +3567,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -3622,9 +3633,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.52.3"
version = "1.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
checksum = "d988bcd52dbe076d3d46903332f58c912b87a2c49b1428419a5845154762ffee"
dependencies = [
"bytes",
"libc",
@@ -3645,7 +3656,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -3835,7 +3846,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -3998,9 +4009,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.23.4"
version = "1.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53"
checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
dependencies = [
"getrandom 0.4.3",
"js-sys",
@@ -4078,7 +4089,7 @@ dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
"wasm-bindgen-shared",
]
@@ -4262,7 +4273,7 @@ dependencies = [
"anyhow",
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
"wasmtime-internal-component-util",
"wasmtime-internal-wit-bindgen",
"wit-parser 0.251.0",
@@ -4370,7 +4381,7 @@ checksum = "e747f4a074699ba1b4e4d841fb263f9b7df5bd1555181c4752bf5990d21ba676"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -4490,7 +4501,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -4552,7 +4563,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -4563,7 +4574,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -4826,7 +4837,7 @@ dependencies = [
"heck",
"indexmap",
"prettyplease",
"syn",
"syn 2.0.118",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
@@ -4842,7 +4853,7 @@ dependencies = [
"prettyplease",
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
"wit-bindgen-core",
"wit-bindgen-rust",
]
@@ -4946,7 +4957,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
"synstructure",
]
@@ -4967,7 +4978,7 @@ checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]
@@ -4987,7 +4998,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
"synstructure",
]
@@ -5027,7 +5038,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.118",
]
[[package]]

View File

@@ -98,8 +98,8 @@ strip = false
[workspace.dependencies]
serde_json5 = "0.2.1"
tokio = { version = "1.52", default-features = false }
syn = { version = "2.0", default-features = false, features = ["printing"] }
tokio = { version = "1.53", default-features = false }
syn = { version = "3.0", default-features = false, features = ["full", "parsing", "printing", "proc-macro", "clone-impls", "derive"] }
thiserror = { version = "2.0", default-features = false }
@@ -110,7 +110,7 @@ futures = { version = "0.3", default-features = false, features = ["executor"] }
rayon = { version = "1.12", default-features = false }
crossbeam = { version = "0.8", default-features = false, features = ["std"] }
uuid = { version = "1.23", default-features = false, features = ["serde", "v3", "v4", "std"] }
uuid = { version = "1.24", default-features = false, features = ["serde", "v3", "v4", "std"] }
serde = { version = "1.0", default-features = false, features = ["derive", "std"] }
serde_repr = { version = "0.1", default-features = false }
serde_json = { version = "1.0", default-features = false, features = ["std"] }
@@ -120,7 +120,7 @@ proc-macro-error2 = { version = "2", default-features = false }
aes = { version = "0.9", default-features = false }
async-compression = { version = "0.4.42", default-features = false }
base64 = { version = "0.22.1", default-features = false, features = ["std"] }
bitflags = { version = "2.13.0", default-features = false, features = ["std"] }
bitflags = { version = "2.13.1", default-features = false, features = ["std"] }
cesu8 = { version = "1.1", default-features = false }
cfb8 = { version = "0.9", default-features = false }
ctr = { version = "0.10.1", default-features = false }

View File

@@ -17,9 +17,9 @@ pumpkin-data = { workspace = true, features = ["packet", "item_id_remap", "entit
pumpkin-macros.workspace = true
pumpkin-world.workspace = true
pumpkin-util.workspace = true
serde = { workspace = true, features = ["derive"] }
uuid.workspace = true
serde.workspace = true
thiserror.workspace = true
tokio = { workspace = true, features = ["net"] }
bytes.workspace = true

View File

@@ -1,8 +1,6 @@
use std::io::Read;
use std::io::Write;
use serde::{Serialize, Serializer};
use crate::ReadingError;
use crate::WritingError;
use crate::ser::NetworkReadExt;
@@ -34,11 +32,3 @@ impl BitSet {
Ok(Self(array.into_boxed_slice()))
}
}
impl Serialize for BitSet {
fn serialize<S: Serializer>(&self, _serializer: S) -> Result<S::Ok, S::Error> {
Err(serde::ser::Error::custom(
"BitSet serialization not implemented",
))
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
use crate::VarInt;
use crate::codec::data_component::{deserialize, serialize};
use crate::ser::{NetworkReadExt, ReadingError, WritingError, deserializer, serializer};
use crate::ser::{NetworkReadExt, NetworkWriteExt, ReadingError, WritingError};
use pumpkin_data::data_component::DataComponent;
use pumpkin_data::data_component_impl::{CustomNameImpl, DataComponentImpl};
use pumpkin_data::item::Item;
@@ -9,13 +9,8 @@ use pumpkin_data::item_stack::ItemStack;
use pumpkin_nbt::tag::NbtTag;
use pumpkin_util::text::TextComponent;
use pumpkin_util::version::JavaMinecraftVersion;
use serde::ser::SerializeStruct;
use serde::{
Deserialize, Serialize, Serializer,
de::{self, DeserializeSeed, SeqAccess},
};
use std::borrow::Cow;
use std::io::{Cursor, Read};
use std::io::Cursor;
#[derive(Clone)]
pub struct ItemStackSerializer<'a>(pub Cow<'a, ItemStack>);
@@ -35,67 +30,52 @@ fn item_component_counts(stack: &ItemStack) -> (u8, u8) {
(to_add, to_remove)
}
fn serialize_any_item_stack_with_id<S: Serializer>(
fn serialize_any_item_stack_with_id(
stack: &ItemStack,
item_id: u16,
is_template: bool,
serializer: S,
) -> Result<S::Ok, S::Error> {
write: &mut impl NetworkWriteExt,
) -> Result<(), WritingError> {
if stack.is_empty() {
VarInt(0).serialize(serializer)
write.put_var_int(&VarInt(0))
} else {
let (to_add, to_remove) = item_component_counts(stack);
let mut seq = serializer.serialize_struct("", 0)?;
if is_template {
seq.serialize_field("", &VarInt::from(item_id))?;
seq.serialize_field("", &VarInt::from(stack.item_count))?;
write.put_var_int(&VarInt::from(item_id))?;
write.put_var_int(&VarInt::from(stack.item_count))?;
} else {
seq.serialize_field("", &VarInt::from(stack.item_count))?;
seq.serialize_field("", &VarInt::from(item_id))?;
write.put_var_int(&VarInt::from(stack.item_count))?;
write.put_var_int(&VarInt::from(item_id))?;
}
seq.serialize_field::<VarInt>("", &VarInt::from(to_add))?;
seq.serialize_field::<VarInt>("", &VarInt::from(to_remove))?;
write.put_var_int(&VarInt::from(to_add))?;
write.put_var_int(&VarInt::from(to_remove))?;
for (id, data) in &stack.patch {
if let Some(data) = data {
seq.serialize_field::<VarInt>("", &VarInt::from(id.to_id()))?;
serialize(*id, data.as_ref(), &mut seq)?;
write.put_var_int(&VarInt::from(id.to_id()))?;
serialize(*id, data.as_ref(), write)?;
}
}
for (id, data) in &stack.patch {
if data.is_none() {
seq.serialize_field::<VarInt>("", &VarInt::from(id.to_id()))?;
write.put_var_int(&VarInt::from(id.to_id()))?;
}
}
seq.end()
Ok(())
}
}
fn serialize_item_stack_with_id<S: Serializer>(
fn serialize_item_stack_with_id(
stack: &ItemStack,
item_id: u16,
serializer: S,
) -> Result<S::Ok, S::Error> {
serialize_any_item_stack_with_id(stack, item_id, false, serializer)
write: &mut impl NetworkWriteExt,
) -> Result<(), WritingError> {
serialize_any_item_stack_with_id(stack, item_id, false, write)
}
struct ComponentAccess<R: Read> {
deserializer: deserializer::Deserializer<R>,
}
impl<'de, R: Read> SeqAccess<'de> for ComponentAccess<R> {
type Error = ReadingError;
fn next_element_seed<T: DeserializeSeed<'de>>(
&mut self,
seed: T,
) -> Result<Option<T::Value>, Self::Error> {
seed.deserialize(&mut self.deserializer).map(Some)
}
}
fn read_component_id(read: &mut impl Read) -> Result<DataComponent, ReadingError> {
fn read_component_id(read: &mut impl NetworkReadExt) -> Result<DataComponent, ReadingError> {
let id_val = read.get_var_int()?.0;
let id_u8 = id_val
.try_into()
@@ -122,7 +102,7 @@ fn decode_custom_name(component_data: &[u8]) -> Result<Box<dyn DataComponentImpl
}
fn read_length_prefixed_component(
read: &mut impl Read,
read: &mut impl NetworkReadExt,
) -> Result<(DataComponent, Box<dyn DataComponentImpl>), ReadingError> {
let id = read_component_id(read)?;
let byte_len = read.get_var_int()?.0;
@@ -134,114 +114,82 @@ fn read_length_prefixed_component(
let component_impl = if id == DataComponent::CustomName {
decode_custom_name(component_data.as_ref())?
} else {
let cursor = Cursor::new(component_data);
let mut access = ComponentAccess {
deserializer: deserializer::Deserializer::new(cursor),
};
deserialize(id, &mut access)?
let mut cursor = Cursor::new(component_data);
deserialize(id, &mut cursor)?
};
Ok((id, component_impl))
}
impl<'de> Deserialize<'de> for ItemStackSerializer<'static> {
fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = ItemStackSerializer<'static>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a valid Slot encoded in a byte sequence")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
const MAX_COMPONENTS: i32 = 256;
let item_count = seq
.next_element::<VarInt>()?
.ok_or_else(|| de::Error::custom("Failed to decode VarInt"))?;
if item_count.0 == 0 {
return Ok(ItemStackSerializer(Cow::Borrowed(ItemStack::EMPTY)));
}
let item_id = seq
.next_element::<VarInt>()?
.ok_or_else(|| de::Error::custom("No item id VarInt!"))?;
let num_to_add = seq.next_element::<VarInt>()?.map_or(0, |v| v.0);
let num_to_remove = seq.next_element::<VarInt>()?.map_or(0, |v| v.0);
if num_to_add < 0 || num_to_remove < 0 {
return Err(de::Error::custom("Negative component count"));
}
let total_components = num_to_add
.checked_add(num_to_remove)
.ok_or_else(|| de::Error::custom("Component count overflow"))?;
if total_components > MAX_COMPONENTS {
return Err(de::Error::custom("Too many components in ItemStack patch"));
}
let mut patch = Vec::with_capacity((num_to_add + num_to_remove) as usize);
for _ in 0..num_to_add {
let id_val = seq
.next_element::<VarInt>()?
.ok_or_else(|| de::Error::custom("Missing component ID"))?
.0;
let id = DataComponent::try_from_id(id_val as u8).ok_or_else(|| {
de::Error::custom(format!("Unknown component ID: {id_val}"))
})?;
// Minecraft protocol sends a byte length for the component data here
let _byte_len = seq
.next_element::<VarInt>()?
.ok_or_else(|| de::Error::custom("No data len VarInt!"))?;
let component_impl = deserialize(id, &mut seq)?;
patch.push((id, Some(component_impl)));
}
for _ in 0..num_to_remove {
let id_val = seq
.next_element::<VarInt>()?
.ok_or_else(|| de::Error::custom("Missing remove component ID"))?
.0;
let id = DataComponent::try_from_id(id_val as u8)
.ok_or_else(|| de::Error::custom("Unknown component ID"))?;
patch.push((id, None));
}
let item_id_u16: u16 = item_id
.0
.try_into()
.map_err(|_| de::Error::custom("Invalid item id!"))?;
Ok(ItemStackSerializer(Cow::Owned(
ItemStack::new_with_component(
item_count.0 as u8,
Item::from_id(item_id_u16).unwrap_or(&Item::AIR),
patch,
),
)))
}
}
deserializer.deserialize_seq(Visitor)
}
}
impl Serialize for ItemStackSerializer<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serialize_item_stack_with_id(self.0.as_ref(), self.0.item.id, serializer)
}
}
impl ItemStackSerializer<'_> {
pub fn read(
read: &mut impl NetworkReadExt,
) -> Result<ItemStackSerializer<'static>, ReadingError> {
const MAX_COMPONENTS: i32 = 256;
let item_count = read.get_var_int()?;
if item_count.0 == 0 {
return Ok(ItemStackSerializer(Cow::Borrowed(ItemStack::EMPTY)));
}
let item_id = read.get_var_int()?;
let num_to_add = read.get_var_int()?.0;
let num_to_remove = read.get_var_int()?.0;
if num_to_add < 0 || num_to_remove < 0 {
return Err(ReadingError::Message("Negative component count".into()));
}
let total_components = num_to_add
.checked_add(num_to_remove)
.ok_or_else(|| ReadingError::Message("Component count overflow".into()))?;
if total_components > MAX_COMPONENTS {
return Err(ReadingError::Message(
"Too many components in ItemStack patch".into(),
));
}
let mut patch = Vec::with_capacity((num_to_add + num_to_remove) as usize);
for _ in 0..num_to_add {
let id_val = read.get_var_int()?.0;
let id = DataComponent::try_from_id(id_val as u8)
.ok_or_else(|| ReadingError::Message(format!("Unknown component ID: {id_val}")))?;
let _byte_len = read.get_var_int()?;
let component_impl = deserialize(id, read)?;
patch.push((id, Some(component_impl)));
}
for _ in 0..num_to_remove {
let id_val = read.get_var_int()?.0;
let id = DataComponent::try_from_id(id_val as u8)
.ok_or_else(|| ReadingError::Message("Unknown component ID".into()))?;
patch.push((id, None));
}
let item_id_u16: u16 = item_id
.0
.try_into()
.map_err(|_| ReadingError::Message("Invalid item id!".into()))?;
Ok(ItemStackSerializer(Cow::Owned(
ItemStack::new_with_component(
item_count.0 as u8,
Item::from_id(item_id_u16).unwrap_or(&Item::AIR),
patch,
),
)))
}
pub fn write(&self, write: &mut impl NetworkWriteExt) -> Result<(), WritingError> {
serialize_item_stack_with_id(self.0.as_ref(), self.0.item.id, write)
}
pub fn read_length_prefixed_optional(
mut read: impl Read,
read: &mut impl NetworkReadExt,
) -> Result<ItemStackSerializer<'static>, ReadingError> {
const MAX_COMPONENTS: i32 = 256;
@@ -275,12 +223,12 @@ impl ItemStackSerializer<'_> {
let mut patch = Vec::with_capacity(total_components as usize);
for _ in 0..num_to_add {
let (id, component_impl) = read_length_prefixed_component(&mut read)?;
let (id, component_impl) = read_length_prefixed_component(read)?;
patch.push((id, Some(component_impl)));
}
for _ in 0..num_to_remove {
patch.push((read_component_id(&mut read)?, None));
patch.push((read_component_id(read)?, None));
}
let item_id_u16 = item_id
@@ -299,12 +247,11 @@ impl ItemStackSerializer<'_> {
pub fn write_with_version(
&self,
write: impl std::io::Write,
write: &mut impl NetworkWriteExt,
version: &JavaMinecraftVersion,
) -> Result<(), WritingError> {
let remapped_item_id = remap_item_id_for_version(self.0.item.id, *version);
let mut network_serializer = serializer::Serializer::new(write);
serialize_item_stack_with_id(self.0.as_ref(), remapped_item_id, &mut network_serializer)
serialize_item_stack_with_id(self.0.as_ref(), remapped_item_id, write)
}
#[must_use]
@@ -346,6 +293,49 @@ pub struct ItemComponentHash {
pub removed: Vec<VarInt>,
}
impl ItemComponentHash {
pub fn read(read: &mut impl NetworkReadExt) -> Result<Self, ReadingError> {
const MAX_COMPONENTS: i32 = 256;
let mut added = Vec::new();
let mut removed = Vec::new();
let added_length = read.get_var_int()?;
if added_length.0 < 0 || added_length.0 > MAX_COMPONENTS {
return Err(ReadingError::Message("added_length out of bounds".into()));
}
for _ in 0..added_length.0 {
let component_id = read.get_var_int()?;
let component_value = read.get_i32()?;
added.push((component_id, component_value));
}
let removed_length = read.get_var_int()?;
if removed_length.0 < 0 || removed_length.0 > MAX_COMPONENTS {
return Err(ReadingError::Message("removed_length out of bounds".into()));
}
for _ in 0..removed_length.0 {
let component_id = read.get_var_int()?;
removed.push(component_id);
}
Ok(Self { added, removed })
}
pub fn write(&self, write: &mut impl NetworkWriteExt) -> Result<(), WritingError> {
write.put_var_int(&VarInt::from(self.added.len() as i32))?;
for (id, val) in &self.added {
write.put_var_int(id)?;
write.put_i32(*val)?;
}
write.put_var_int(&VarInt::from(self.removed.len() as i32))?;
for id in &self.removed {
write.put_var_int(id)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct ItemStackHash {
item_id: VarInt,
@@ -353,7 +343,39 @@ pub struct ItemStackHash {
components: ItemComponentHash,
}
#[derive(Debug, Clone)]
pub struct OptionalItemStackHash(pub Option<ItemStackHash>);
impl OptionalItemStackHash {
pub fn read(read: &mut impl NetworkReadExt) -> Result<Self, ReadingError> {
let is_some = read.get_bool()?;
if is_some {
let item_id = read.get_var_int()?;
let count = read.get_var_int()?;
let components = ItemComponentHash::read(read)?;
Ok(Self(Some(ItemStackHash {
item_id,
count,
components,
})))
} else {
Ok(Self(None))
}
}
pub fn write(&self, write: &mut impl NetworkWriteExt) -> Result<(), WritingError> {
if let Some(hash) = &self.0 {
write.put_bool(true)?;
write.put_var_int(&hash.item_id)?;
write.put_var_int(&hash.count)?;
hash.components.write(write)?;
} else {
write.put_bool(false)?;
}
Ok(())
}
#[must_use]
pub fn hash_equals(&self, other: &ItemStack) -> bool {
if let Some(hash) = &self.0 {
@@ -404,121 +426,26 @@ impl OptionalItemStackHash {
}
}
#[derive(Debug, Clone)]
pub struct OptionalItemStackHash(pub Option<ItemStackHash>);
impl<'de> Deserialize<'de> for OptionalItemStackHash {
fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = OptionalItemStackHash;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a valid Slot encoded in a byte sequence")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let is_some = seq
.next_element::<bool>()?
.ok_or(de::Error::custom("No is some bool!"))?;
if is_some {
let item_id = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No item id VarInt!"))?;
let count = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No item count VarInt!"))?;
let hashed_components = seq
.next_element::<ItemComponentHash>()?
.ok_or(de::Error::custom("No item component hash!"))?;
let item_stack_hash = ItemStackHash {
item_id,
count,
components: hashed_components,
};
Ok(OptionalItemStackHash(Some(item_stack_hash)))
} else {
Ok(OptionalItemStackHash(None))
}
}
}
deserializer.deserialize_seq(Visitor)
}
}
impl<'de> Deserialize<'de> for ItemComponentHash {
fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = ItemComponentHash;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a valid Slot encoded in a byte sequence")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
const MAX_COMPONENTS: i32 = 256;
let mut added = Vec::new();
let mut removed = Vec::new();
let added_length = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No added length VarInt!"))?;
if added_length.0 < 0 || added_length.0 > MAX_COMPONENTS {
return Err(de::Error::custom("added_length out of bounds"));
}
for _ in 0..added_length.0 {
let component_id = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No component id VarInt!"))?;
let component_value = seq
.next_element::<i32>()?
.ok_or(de::Error::custom("No component value i32!"))?;
added.push((component_id, component_value));
}
let removed_length = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No removed length VarInt!"))?;
if removed_length.0 < 0 || removed_length.0 > MAX_COMPONENTS {
return Err(de::Error::custom("removed_length out of bounds"));
}
for _ in 0..removed_length.0 {
let component_id = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No component id VarInt!"))?;
removed.push(component_id);
}
Ok(ItemComponentHash { added, removed })
}
}
deserializer.deserialize_seq(Visitor)
}
}
pub struct ItemStackTemplateSerializer<'a>(pub Cow<'a, ItemStack>);
impl ItemStackTemplateSerializer<'_> {
pub fn write_with_version(
&self,
write: impl std::io::Write,
write: &mut impl NetworkWriteExt,
version: &JavaMinecraftVersion,
) -> Result<(), WritingError> {
let remapped_item_id = remap_item_id_for_version(self.0.item.id, *version);
let mut network_serializer = serializer::Serializer::new(write);
serialize_any_item_stack_with_id(
self.0.as_ref(),
remapped_item_id,
*version >= JavaMinecraftVersion::V_26_1,
&mut network_serializer,
write,
)
}
pub fn write(&self, write: &mut impl NetworkWriteExt) -> Result<(), WritingError> {
serialize_any_item_stack_with_id(self.0.as_ref(), self.0.item.id, true, write)
}
}
impl From<ItemStack> for ItemStackTemplateSerializer<'_> {
@@ -526,12 +453,3 @@ impl From<ItemStack> for ItemStackTemplateSerializer<'_> {
ItemStackTemplateSerializer(Cow::Owned(item))
}
}
impl Serialize for ItemStackTemplateSerializer<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serialize_any_item_stack_with_id(self.0.as_ref(), self.0.item.id, true, serializer)
}
}

View File

@@ -1,51 +1,65 @@
use serde::{Serialize, Serializer};
pub struct Le64(pub i64);
impl Serialize for Le64 {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_bytes(&self.0.to_le_bytes())
impl Le64 {
pub fn write(
&self,
write: &mut impl crate::ser::NetworkWriteExt,
) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.0.to_le_bytes())
}
}
pub struct Le32(pub i32);
impl Serialize for Le32 {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_bytes(&self.0.to_le_bytes())
impl Le32 {
pub fn write(
&self,
write: &mut impl crate::ser::NetworkWriteExt,
) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.0.to_le_bytes())
}
}
pub struct Le16(pub i16);
impl Serialize for Le16 {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_bytes(&self.0.to_le_bytes())
impl Le16 {
pub fn write(
&self,
write: &mut impl crate::ser::NetworkWriteExt,
) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.0.to_le_bytes())
}
}
// Unsigned
pub struct LeU64(pub u64);
impl Serialize for LeU64 {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_bytes(&self.0.to_le_bytes())
impl LeU64 {
pub fn write(
&self,
write: &mut impl crate::ser::NetworkWriteExt,
) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.0.to_le_bytes())
}
}
pub struct LeU32(pub u32);
impl Serialize for LeU32 {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_bytes(&self.0.to_le_bytes())
impl LeU32 {
pub fn write(
&self,
write: &mut impl crate::ser::NetworkWriteExt,
) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.0.to_le_bytes())
}
}
pub struct LeU16(pub u16);
impl Serialize for LeU16 {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_bytes(&self.0.to_le_bytes())
impl LeU16 {
pub fn write(
&self,
write: &mut impl crate::ser::NetworkWriteExt,
) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.0.to_le_bytes())
}
}

View File

@@ -1,16 +1,9 @@
use std::fmt::Formatter;
use pumpkin_util::math::vector3::Vector3;
use crate::{
VarInt,
ser::{NetworkWriteExt, ReadingError, WritingError},
};
use serde::{
Deserializer, Serialize,
de::{self, Visitor},
ser::Serializer,
};
#[derive(Clone, Copy)]
pub struct LpVector3d(pub Vector3<f64>);
@@ -153,42 +146,6 @@ fn from_long(quantized: i64, scale: f64) -> f64 {
(normalized / 0.5) * scale
}
impl Serialize for LpVector3d {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut buf = Vec::new();
self.write(&mut buf)
.map_err(|e| serde::ser::Error::custom(e.to_string()))?;
serializer.serialize_bytes(&buf)
}
}
impl<'de> de::Deserialize<'de> for LpVector3d {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct VelocityVisitor;
impl Visitor<'_> for VelocityVisitor {
type Value = LpVector3d;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("a byte array representing bit-packed velocity")
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
let mut cursor = std::io::Cursor::new(v);
LpVector3d::read(&mut cursor).map_err(de::Error::custom)
}
}
deserializer.deserialize_bytes(VelocityVisitor)
}
}
#[cfg(test)]
mod tests {
use super::{LpVector3d, encode_legacy_velocity_component};

View File

@@ -1,14 +1,11 @@
use serde::{Serialize, Serializer};
use crate::codec::var_int::VarInt;
#[derive(Debug, Clone, Copy)]
pub struct OptionalInt(pub Option<i32>);
impl Serialize for OptionalInt {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
// Vanilla logic: empty is 0, otherwise value + 1
impl OptionalInt {
pub fn write(
&self,
write: &mut impl crate::ser::NetworkWriteExt,
) -> Result<(), crate::ser::WritingError> {
let val = self.0.map_or(0, |id| id + 1);
VarInt(val).serialize(serializer)
write.write_var_int(&crate::codec::var_int::VarInt(val))
}
}

View File

@@ -1,10 +1,9 @@
use pumpkin_data::recipes::RecipeCategoryTypes;
use serde::Serialize;
use pumpkin_data::item::Item;
use pumpkin_data::tag::Taggable;
#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug)]
pub enum OwnedRecipeIngredient {
Simple(String),
Tagged(String),
@@ -30,14 +29,14 @@ impl OwnedRecipeIngredient {
}
}
#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug)]
pub struct OwnedRecipeResult {
pub item_id: String,
pub count: u8,
// TODO: Add components/enchantments if needed for the display result
}
#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug)]
pub enum OwnedCraftingRecipe {
Shaped {
category: RecipeCategoryTypes,
@@ -55,7 +54,7 @@ pub enum OwnedCraftingRecipe {
},
}
#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug)]
pub struct OwnedCookingRecipe {
pub recipe_id: String,
pub category: RecipeCategoryTypes,
@@ -66,7 +65,7 @@ pub struct OwnedCookingRecipe {
pub result: OwnedRecipeResult,
}
#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug)]
pub enum OwnedCookingRecipeType {
Blasting(OwnedCookingRecipe),
Smelting(OwnedCookingRecipe),
@@ -74,7 +73,7 @@ pub enum OwnedCookingRecipeType {
CampfireCooking(OwnedCookingRecipe),
}
#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug)]
pub enum DynamicRecipe {
Crafting(OwnedCraftingRecipe),
Cooking(OwnedCookingRecipeType),

View File

@@ -4,11 +4,6 @@ use std::{
ops::Deref,
};
use bytes::BufMut;
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{SeqAccess, Visitor},
};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use crate::{
@@ -153,53 +148,6 @@ impl Deref for VarInt {
}
}
impl Serialize for VarInt {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut value = self.0 as u32;
let mut buf = Vec::new();
while value > 0x7F {
buf.put_u8(value as u8 | 0x80);
value >>= 7;
}
buf.put_u8(value as u8);
serializer.serialize_bytes(&buf)
}
}
impl<'de> Deserialize<'de> for VarInt {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct VarIntVisitor;
impl<'de> Visitor<'de> for VarIntVisitor {
type Value = VarInt;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a valid VarInt encoded in a byte sequence")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut val = 0;
for i in 0..VarInt::MAX_SIZE.get() {
if let Some(byte) = seq.next_element::<u8>()? {
val |= (i32::from(byte) & 0b0111_1111) << (i * 7);
if byte & 0b1000_0000 == 0 {
return Ok(VarInt(val));
}
} else {
break;
}
}
Err(serde::de::Error::custom("VarInt was too large"))
}
}
deserializer.deserialize_seq(VarIntVisitor)
}
}
impl PacketWrite for VarInt {
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
let mut val = ((self.0 << 1) ^ (self.0 >> 31)) as u32;

View File

@@ -4,11 +4,6 @@ use std::{
ops::Deref,
};
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{self, SeqAccess, Visitor},
};
use crate::{
WritingError,
ser::{NetworkReadExt, NetworkWriteExt, ReadingError},
@@ -92,53 +87,6 @@ impl Deref for VarLong {
}
}
impl Serialize for VarLong {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut value = self.0 as u64;
let mut buf = Vec::new();
while value > 0x7F {
buf.push(value as u8 | 0x80);
value >>= 7;
}
buf.push(value as u8);
serializer.serialize_bytes(&buf)
}
}
impl<'de> Deserialize<'de> for VarLong {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct VarLongVisitor;
impl<'de> Visitor<'de> for VarLongVisitor {
type Value = VarLong;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a valid VarInt encoded in a byte sequence")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut val = 0;
for i in 0..VarLong::MAX_SIZE.get() {
if let Some(byte) = seq.next_element::<u8>()? {
val |= (i64::from(byte) & 0b0111_1111) << (i * 7);
if byte & 0b1000_0000 == 0 {
return Ok(VarLong(val));
}
} else {
break;
}
}
Err(de::Error::custom("VarInt was too large"))
}
}
deserializer.deserialize_seq(VarLongVisitor)
}
}
impl PacketRead for VarLong {
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
let mut val: u64 = 0;

View File

@@ -3,11 +3,6 @@ use std::{
num::NonZeroUsize,
};
use bytes::BufMut;
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{SeqAccess, Visitor},
};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use crate::{
@@ -136,53 +131,6 @@ gen_from!(u64);
gen_from!(isize);
gen_from!(usize);
impl Serialize for VarUInt {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut value = self.0;
let mut buf = Vec::with_capacity(5);
while value > 0x7F {
buf.put_u8(value as u8 | 0x80);
value >>= 7;
}
buf.put_u8(value as u8);
serializer.serialize_bytes(&buf)
}
}
impl<'de> Deserialize<'de> for VarUInt {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct VarIntVisitor;
impl<'de> Visitor<'de> for VarIntVisitor {
type Value = VarUInt;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a valid VarInt encoded in a byte sequence")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut val = 0;
for i in 0..VarUInt::MAX_SIZE.get() {
if let Some(byte) = seq.next_element::<u8>()? {
val |= (u32::from(byte) & 0b0111_1111) << (i * 7);
if byte & 0b1000_0000 == 0 {
return Ok(VarUInt(val));
}
} else {
break;
}
}
Err(serde::de::Error::custom("VarInt was too large"))
}
}
deserializer.deserialize_seq(VarIntVisitor)
}
}
impl PacketWrite for VarUInt {
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
let mut val = self.0;

View File

@@ -4,11 +4,6 @@ use std::{
ops::Deref,
};
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{self, SeqAccess, Visitor},
};
use crate::{
WritingError,
ser::{NetworkReadExt, NetworkWriteExt, ReadingError},
@@ -115,53 +110,6 @@ impl Deref for VarULong {
}
}
impl Serialize for VarULong {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut value = self.0;
let mut buf = Vec::new();
while value > 0x7F {
buf.push(value as u8 | 0x80);
value >>= 7;
}
buf.push(value as u8);
serializer.serialize_bytes(&buf)
}
}
impl<'de> Deserialize<'de> for VarULong {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct VarLongVisitor;
impl<'de> Visitor<'de> for VarLongVisitor {
type Value = VarULong;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a valid VarInt encoded in a byte sequence")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut val = 0;
for i in 0..VarULong::MAX_SIZE.get() {
if let Some(byte) = seq.next_element::<u8>()? {
val |= (u64::from(byte) & 0b0111_1111) << (i * 7);
if byte & 0b1000_0000 == 0 {
return Ok(VarULong(val));
}
} else {
break;
}
}
Err(de::Error::custom("VarInt was too large"))
}
}
deserializer.deserialize_seq(VarLongVisitor)
}
}
impl PacketWrite for VarULong {
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
let mut x = self.0;

View File

@@ -1,14 +1,15 @@
use pumpkin_util::text::TextComponent;
use pumpkin_macros::java_packet;
use serde::Serialize;
use pumpkin_data::packet::clientbound::CONFIG_RESOURCE_PACK_PUSH;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(CONFIG_RESOURCE_PACK_PUSH)]
pub struct CConfigAddResourcePack<'a> {
#[serde(with = "uuid::serde::compact")]
pub uuid: &'a uuid::Uuid,
pub url: &'a str,
pub hash: &'a str, // max 40
@@ -34,3 +35,23 @@ impl<'a> CConfigAddResourcePack<'a> {
}
}
}
impl ClientPacket for CConfigAddResourcePack<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_uuid(self.uuid)?;
write.write_string(self.url)?;
write.write_string(self.hash)?;
write.write_bool(self.forced)?;
if let Some(_prompt) = &self.prompt_message {
write.write_bool(true)?;
// TODO
} else {
write.write_bool(false)?;
}
Ok(())
}
}

View File

@@ -1,8 +1,9 @@
use pumpkin_data::packet::clientbound::CONFIG_CLEAR_DIALOG;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(CONFIG_CLEAR_DIALOG)]
pub struct CConfigClearDialog;
@@ -18,3 +19,13 @@ impl Default for CConfigClearDialog {
Self::new()
}
}
impl ClientPacket for CConfigClearDialog {
fn write_packet_data(
&self,
_write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
Ok(())
}
}

View File

@@ -1,8 +1,10 @@
use pumpkin_data::packet::clientbound::CONFIG_DISCONNECT;
use pumpkin_macros::java_packet;
use serde::Deserialize;
#[derive(serde::Serialize, Deserialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(CONFIG_DISCONNECT)]
pub struct CConfigDisconnect<'a> {
pub reason: &'a str,
@@ -14,3 +16,14 @@ impl<'a> CConfigDisconnect<'a> {
Self { reason }
}
}
impl ClientPacket for CConfigDisconnect<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_string(self.reason)?;
Ok(())
}
}

View File

@@ -2,7 +2,10 @@ use pumpkin_data::packet::clientbound::CONFIG_COOKIE_REQUEST;
use pumpkin_macros::java_packet;
use pumpkin_util::resource_location::ResourceLocation;
#[derive(serde::Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(CONFIG_COOKIE_REQUEST)]
/// Requests a cookie that was previously stored.
pub struct CCookieRequest<'a> {
@@ -15,3 +18,14 @@ impl<'a> CCookieRequest<'a> {
Self { key }
}
}
impl ClientPacket for CCookieRequest<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_string(self.key)?;
Ok(())
}
}

View File

@@ -1,7 +1,18 @@
use pumpkin_data::packet::clientbound::CONFIG_FINISH_CONFIGURATION;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(CONFIG_FINISH_CONFIGURATION)]
pub struct CFinishConfig;
impl ClientPacket for CFinishConfig {
fn write_packet_data(
&self,
_write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
Ok(())
}
}

View File

@@ -1,10 +1,11 @@
use pumpkin_data::packet::clientbound::CONFIG_SELECT_KNOWN_PACKS;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::KnownPack;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize)]
#[java_packet(CONFIG_SELECT_KNOWN_PACKS)]
pub struct CKnownPacks<'a> {
pub known_packs: &'a [KnownPack<'a>],
@@ -16,3 +17,17 @@ impl<'a> CKnownPacks<'a> {
Self { known_packs }
}
}
impl ClientPacket for CKnownPacks<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&crate::VarInt(self.known_packs.len() as i32))?;
for pack in self.known_packs {
pack.write(&mut write)?;
}
Ok(())
}
}

View File

@@ -1,9 +1,11 @@
use crate::Link;
use pumpkin_data::packet::clientbound::CONFIG_SERVER_LINKS;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(CONFIG_SERVER_LINKS)]
pub struct CConfigServerLinks<'a> {
pub links: &'a [Link<'a>],
@@ -15,3 +17,17 @@ impl<'a> CConfigServerLinks<'a> {
Self { links }
}
}
impl ClientPacket for CConfigServerLinks<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&crate::VarInt(self.links.len() as i32))?;
for link in self.links {
link.write(&mut write)?;
}
Ok(())
}
}

View File

@@ -2,9 +2,10 @@ use crate::IdOr;
use crate::java::client::dialog::DialogNBT;
use pumpkin_data::packet::clientbound::CONFIG_SHOW_DIALOG;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(CONFIG_SHOW_DIALOG)]
pub struct CConfigShowDialog<'a> {
pub dialog: IdOr<DialogNBT<'a>>,
@@ -16,3 +17,15 @@ impl<'a> CConfigShowDialog<'a> {
Self { dialog }
}
}
impl ClientPacket for CConfigShowDialog<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
self.dialog
.write(&mut write, |w, dialog| dialog.write_packet_data(w, version))?;
Ok(())
}
}

View File

@@ -2,7 +2,10 @@ use pumpkin_data::packet::clientbound::CONFIG_STORE_COOKIE;
use pumpkin_macros::java_packet;
use pumpkin_util::resource_location::ResourceLocation;
#[derive(serde::Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(CONFIG_STORE_COOKIE)]
/// Stores some arbitrary data on the client, which persists between server transfers.
/// The Notchian (vanilla) client only accepts cookies of up to 5 KiB in size.
@@ -17,3 +20,16 @@ impl<'a> CStoreCookie<'a> {
Self { key, payload }
}
}
impl ClientPacket for CStoreCookie<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_string(self.key)?;
write.write_var_int(&crate::VarInt(self.payload.len() as i32))?;
write.write_all(self.payload)?;
Ok(())
}
}

View File

@@ -1,9 +1,11 @@
use crate::VarInt;
use pumpkin_data::packet::clientbound::CONFIG_TRANSFER;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(CONFIG_TRANSFER)]
pub struct CTransfer<'a> {
pub host: &'a str,
@@ -16,3 +18,15 @@ impl<'a> CTransfer<'a> {
Self { host, port }
}
}
impl ClientPacket for CTransfer<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_string(self.host)?;
write.write_var_int(self.port)?;
Ok(())
}
}

View File

@@ -1,7 +1,8 @@
use pumpkin_util::text::TextComponent;
use serde::Serialize;
#[derive(Serialize)]
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
pub struct DialogNBT<'a>(pub DialogNBTSource<'a>);
impl<'a> DialogNBT<'a> {
@@ -14,70 +15,67 @@ impl<'a> DialogNBT<'a> {
pub const fn from_nbt(compound: &'a pumpkin_nbt::compound::NbtCompound) -> Self {
Self(DialogNBTSource::Nbt(compound))
}
#[allow(clippy::todo)]
pub fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
match &self.0 {
DialogNBTSource::Struct(_dialog) => {
todo!("Serialize Dialog to NbtCompound manually")
}
DialogNBTSource::Nbt(nbt) => {
let tag = pumpkin_nbt::tag::NbtTag::Compound((*nbt).clone());
write.write_nbt(tag)?;
Ok(())
}
}
}
}
#[derive(Serialize)]
#[serde(untagged)]
pub enum DialogNBTSource<'a> {
Struct(&'a Dialog),
Nbt(&'a pumpkin_nbt::compound::NbtCompound),
}
#[derive(Serialize)]
pub struct Dialog {
pub r#type: String,
pub title: TextComponent,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub body: Vec<DialogBody>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub inputs: Vec<DialogInput>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub buttons: Vec<ActionButton>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub links: Vec<DialogLink>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exit_action: Option<DialogAction>,
#[serde(skip_serializing_if = "Option::is_none")]
pub after_action: Option<String>,
pub can_close_with_escape: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub external_title: Option<TextComponent>,
}
#[derive(Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum DialogBody {
#[serde(rename = "minecraft:plain_message")]
PlainMessage { contents: TextComponent },
#[serde(rename = "minecraft:item")]
Item { item: i32 }, // TODO: ItemStack serialization to NBT
}
#[derive(Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum DialogInput {
#[serde(rename = "minecraft:boolean")]
Boolean {
label: TextComponent,
default_value: bool,
},
#[serde(rename = "minecraft:text")]
Text {
label: TextComponent,
placeholder: TextComponent,
default_value: String,
},
#[serde(rename = "minecraft:number_range")]
NumberRange {
label: TextComponent,
min: f32,
max: f32,
initial: f32,
step: f32,
#[serde(skip_serializing_if = "Option::is_none")]
label_format: Option<String>,
},
#[serde(rename = "minecraft:single_option")]
SingleOption {
label: TextComponent,
options: Vec<TextComponent>,
@@ -85,30 +83,23 @@ pub enum DialogInput {
},
}
#[derive(Serialize)]
pub struct ActionButton {
pub text: TextComponent,
#[serde(skip_serializing_if = "Option::is_none")]
pub tooltip: Option<TextComponent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub width: Option<u32>,
pub action: DialogAction,
}
#[derive(Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum DialogAction {
#[serde(rename = "minecraft:open_url")]
OpenUrl { url: String },
#[serde(rename = "minecraft:custom")]
OpenUrl {
url: String,
},
Custom {
id: String,
#[serde(skip_serializing_if = "Option::is_none")]
payload: Option<Vec<u8>>,
},
}
#[derive(Serialize)]
pub struct DialogLink {
pub label: crate::Label,
pub url: String,

View File

@@ -1,13 +1,15 @@
use pumpkin_data::packet::clientbound::LOGIN_COOKIE_REQUEST;
use pumpkin_macros::java_packet;
use pumpkin_util::resource_location::ResourceLocation;
use serde::Serialize;
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(LOGIN_COOKIE_REQUEST)]
pub struct CLoginCookieRequest<'a> {
/// The unique identifier of the cookie being requested.
@@ -20,3 +22,14 @@ impl<'a> CLoginCookieRequest<'a> {
Self { key }
}
}
impl ClientPacket for CLoginCookieRequest<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_string(self.key)?;
Ok(())
}
}

View File

@@ -1,13 +1,15 @@
use pumpkin_data::packet::clientbound::LOGIN_HELLO;
use pumpkin_macros::java_packet;
use serde::{Deserialize, Serialize};
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(LOGIN_HELLO)]
pub struct CEncryptionRequest<'a> {
/// The server's ID string. In modern Minecraft, this is usually
@@ -39,3 +41,19 @@ impl<'a> CEncryptionRequest<'a> {
}
}
}
impl ClientPacket for CEncryptionRequest<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_string(self.server_id)?;
write.write_var_int(&crate::VarInt(self.public_key.len() as i32))?;
write.write_all(self.public_key)?;
write.write_var_int(&crate::VarInt(self.verify_token.len() as i32))?;
write.write_all(self.verify_token)?;
write.write_bool(self.should_authenticate)?;
Ok(())
}
}

View File

@@ -1,12 +1,14 @@
use pumpkin_data::packet::clientbound::LOGIN_LOGIN_DISCONNECT;
use pumpkin_macros::java_packet;
use serde::{Deserialize, Serialize};
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(LOGIN_LOGIN_DISCONNECT)]
pub struct CLoginDisconnect {
/// A JSON-encoded chat component explaining why the player was disconnected.
@@ -19,3 +21,14 @@ impl CLoginDisconnect {
Self { json_reason }
}
}
impl ClientPacket for CLoginDisconnect {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_string(&self.json_reason)?;
Ok(())
}
}

View File

@@ -1,6 +1,5 @@
use pumpkin_data::packet::clientbound::{LOGIN_GAME_PROFILE, LOGIN_LOGIN_FINISHED};
use pumpkin_util::version::JavaMinecraftVersion;
use serde::Serialize;
use crate::{ClientPacket, Property, packet::MultiVersionJavaPacket, ser::NetworkWriteExt};
@@ -60,10 +59,7 @@ impl ClientPacket for CLoginSuccess<'_> {
) -> Result<(), crate::ser::WritingError> {
write.write_uuid(self.uuid)?;
write.write_string(self.username)?;
write.write_list(self.properties, |write, property| {
let mut serializer = crate::ser::serializer::Serializer::new(write);
property.serialize(&mut serializer)
})?;
write.write_list(self.properties, |write, property| property.write(write))?;
if version < &JavaMinecraftVersion::V_1_21_2 {
write.write_bool(self.strict_error_handling)?;
} else if version >= &JavaMinecraftVersion::V_26_2 {

View File

@@ -1,14 +1,15 @@
use pumpkin_data::packet::clientbound::LOGIN_LOGIN_COMPRESSION;
use pumpkin_macros::java_packet;
use serde::{Deserialize, Serialize};
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(LOGIN_LOGIN_COMPRESSION)]
pub struct CSetCompression {
/// The packet size threshold (in bytes) at which compression is applied.
@@ -24,3 +25,14 @@ impl CSetCompression {
Self { threshold }
}
}
impl ClientPacket for CSetCompression {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.threshold)?;
Ok(())
}
}

View File

@@ -1,14 +1,13 @@
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_BLOCK_CHANGED_ACK;
use pumpkin_macros::java_packet;
use serde::{Deserialize, Serialize};
use crate::VarInt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_BLOCK_CHANGED_ACK)]
pub struct CAcknowledgeBlockChange {
/// The ID of the last sequence processed by the server.
@@ -25,3 +24,14 @@ impl CAcknowledgeBlockChange {
Self { sequence_id }
}
}
impl ClientPacket for CAcknowledgeBlockChange {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.sequence_id)?;
Ok(())
}
}

View File

@@ -1,15 +1,15 @@
use pumpkin_data::packet::clientbound::PLAY_SET_ACTION_BAR_TEXT;
use pumpkin_util::text::TextComponent;
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_macros::java_packet;
use serde::Serialize;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_SET_ACTION_BAR_TEXT)]
pub struct CActionBar<'a> {
/// The text component to be displayed.
@@ -22,3 +22,14 @@ impl<'a> CActionBar<'a> {
Self { action_bar }
}
}
impl ClientPacket for CActionBar<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.action_bar.encode())?;
Ok(())
}
}

View File

@@ -1,17 +1,41 @@
use pumpkin_data::packet::clientbound::PLAY_AWARD_STATS;
use pumpkin_macros::java_packet;
use crate::ClientPacket;
use crate::codec::var_int::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(serde::Serialize)]
#[java_packet(PLAY_AWARD_STATS)]
pub struct CAwardStats<'a> {
pub stats: &'a [Statistic],
}
#[derive(serde::Serialize)]
pub struct Statistic {
pub category_id: VarInt,
pub statistic_id: VarInt,
pub value: VarInt,
}
impl Statistic {
pub fn write(&self, mut write: impl std::io::Write) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.category_id)?;
write.write_var_int(&self.statistic_id)?;
write.write_var_int(&self.value)?;
Ok(())
}
}
impl ClientPacket for CAwardStats<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&VarInt(self.stats.len() as i32))?;
for stat in self.stats {
stat.write(&mut write)?;
}
Ok(())
}
}

View File

@@ -1,16 +1,16 @@
use pumpkin_data::packet::clientbound::PLAY_BLOCK_DESTRUCTION;
use pumpkin_util::math::position::BlockPos;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_BLOCK_DESTRUCTION)]
pub struct CSetBlockDestroyStage {
/// A unique ID for this destruction instance. Usually the miner's Entity ID.
@@ -33,3 +33,16 @@ impl CSetBlockDestroyStage {
}
}
}
impl ClientPacket for CSetBlockDestroyStage {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.entity_id)?;
write.write_block_pos(&self.location)?;
write.write_i8(self.destroy_stage)?;
Ok(())
}
}

View File

@@ -1,16 +1,16 @@
use pumpkin_data::packet::clientbound::PLAY_BLOCK_EVENT;
use pumpkin_util::math::position::BlockPos;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_BLOCK_EVENT)]
pub struct CBlockEvent {
/// The coordinates where the event occurs.
@@ -40,3 +40,17 @@ impl CBlockEvent {
}
}
}
impl ClientPacket for CBlockEvent {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_block_pos(&self.location)?;
write.write_u8(self.action_id)?;
write.write_u8(self.action_parameter)?;
write.write_var_int(&self.block_type)?;
Ok(())
}
}

View File

@@ -1,15 +1,15 @@
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_SET_CHUNK_CACHE_CENTER;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::VarInt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_SET_CHUNK_CACHE_CENTER)]
pub struct CCenterChunk {
/// The X coordinate of the center chunk.
@@ -17,3 +17,15 @@ pub struct CCenterChunk {
/// The Z coordinate of the center chunk.
pub chunk_z: VarInt,
}
impl ClientPacket for CCenterChunk {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.chunk_x)?;
write.write_var_int(&self.chunk_z)?;
Ok(())
}
}

View File

@@ -1,13 +1,14 @@
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_CHANGE_DIFFICULTY;
use pumpkin_macros::java_packet;
use serde::Serialize;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_CHANGE_DIFFICULTY)]
pub struct CChangeDifficulty {
/// The current difficulty level of the world.
@@ -28,3 +29,15 @@ impl CChangeDifficulty {
Self { difficulty, locked }
}
}
impl ClientPacket for CChangeDifficulty {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_u8(self.difficulty)?;
write.write_bool(self.locked)?;
Ok(())
}
}

View File

@@ -1,15 +1,15 @@
use crate::ClientPacket;
use crate::codec::var_int::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_CHUNK_BATCH_FINISHED;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::codec::var_int::VarInt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_CHUNK_BATCH_FINISHED)]
pub struct CChunkBatchEnd {
/// The number of chunks sent in the batch that just finished.
@@ -24,3 +24,14 @@ impl CChunkBatchEnd {
}
}
}
impl ClientPacket for CChunkBatchEnd {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.batch_size)?;
Ok(())
}
}

View File

@@ -1,13 +1,22 @@
use crate::ClientPacket;
use pumpkin_data::packet::clientbound::PLAY_CHUNK_BATCH_START;
use pumpkin_macros::java_packet;
use serde::Serialize;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_CHUNK_BATCH_START)]
pub struct CChunkBatchStart;
impl ClientPacket for CChunkBatchStart {
fn write_packet_data(
&self,
_write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
Ok(())
}
}

View File

@@ -1,8 +1,7 @@
use crate::ClientPacket;
use pumpkin_data::packet::clientbound::PLAY_CLEAR_DIALOG;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(PLAY_CLEAR_DIALOG)]
pub struct CPlayClearDialog;
@@ -18,3 +17,13 @@ impl Default for CPlayClearDialog {
Self::new()
}
}
impl ClientPacket for CPlayClearDialog {
fn write_packet_data(
&self,
_write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
Ok(())
}
}

View File

@@ -1,12 +1,12 @@
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_CLEAR_TITLES;
use pumpkin_macros::java_packet;
use serde::Serialize;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_CLEAR_TITLES)]
pub struct CClearTitle {
/// If true, the client also resets the title timings (fade-in, stay, fade-out)
@@ -23,3 +23,14 @@ impl CClearTitle {
Self { reset }
}
}
impl ClientPacket for CClearTitle {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_bool(self.reset)?;
Ok(())
}
}

View File

@@ -1,15 +1,15 @@
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_CONTAINER_CLOSE;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::VarInt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_CONTAINER_CLOSE)]
pub struct CCloseContainer {
/// The ID of the container window to close.
@@ -24,3 +24,14 @@ impl CCloseContainer {
Self { sync_id: window_id }
}
}
impl ClientPacket for CCloseContainer {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.sync_id)?;
Ok(())
}
}

View File

@@ -1,15 +1,15 @@
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_PLAYER_COMBAT_KILL;
use pumpkin_macros::java_packet;
use pumpkin_util::text::TextComponent;
use serde::Serialize;
use crate::VarInt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_PLAYER_COMBAT_KILL)]
pub struct CCombatDeath<'a> {
/// The Entity ID of the player who died.
@@ -24,3 +24,15 @@ impl<'a> CCombatDeath<'a> {
Self { player_id, message }
}
}
impl ClientPacket for CCombatDeath<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.player_id)?;
write.write_slice(&self.message.encode())?;
Ok(())
}
}

View File

@@ -1,16 +1,16 @@
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_COMMAND_SUGGESTIONS;
use pumpkin_macros::java_packet;
use pumpkin_util::text::TextComponent;
use serde::Serialize;
use crate::VarInt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_COMMAND_SUGGESTIONS)]
pub struct CCommandSuggestions {
/// The unique ID of the request this response is for.
@@ -44,7 +44,7 @@ impl CCommandSuggestions {
}
}
#[derive(PartialEq, Eq, Hash, Debug, Serialize)]
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct CommandSuggestion {
pub suggestion: String,
pub tooltip: Option<TextComponent>,
@@ -58,4 +58,32 @@ impl CommandSuggestion {
tooltip,
}
}
pub fn write(&self, mut write: impl std::io::Write) -> Result<(), crate::ser::WritingError> {
write.write_string(&self.suggestion)?;
if let Some(tooltip) = &self.tooltip {
write.write_bool(true)?;
write.write_slice(&tooltip.encode())?;
} else {
write.write_bool(false)?;
}
Ok(())
}
}
impl ClientPacket for CCommandSuggestions {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.id)?;
write.write_var_int(&self.start)?;
write.write_var_int(&self.length)?;
write.write_var_int(&VarInt(self.matches.len() as i32))?;
for match_ in &self.matches {
match_.write(&mut write)?;
}
Ok(())
}
}

View File

@@ -1,14 +1,15 @@
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_COOKIE_REQUEST;
use pumpkin_macros::java_packet;
use pumpkin_util::resource_location::ResourceLocation;
use serde::Serialize;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_COOKIE_REQUEST)]
pub struct CPlayCookieRequest<'a> {
/// The unique identifier (namespace:path) of the cookie to retrieve.
@@ -21,3 +22,14 @@ impl<'a> CPlayCookieRequest<'a> {
Self { key }
}
}
impl ClientPacket for CPlayCookieRequest<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_string(self.key)?;
Ok(())
}
}

View File

@@ -1,9 +1,10 @@
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_DAMAGE_EVENT;
use pumpkin_macros::java_packet;
use pumpkin_util::math::vector3::Vector3;
use serde::Serialize;
use crate::VarInt;
use pumpkin_util::version::JavaMinecraftVersion;
/// Notifies the client that an entity has taken damage.
///
@@ -11,7 +12,6 @@ use crate::VarInt;
/// 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)]
#[java_packet(PLAY_DAMAGE_EVENT)]
pub struct CDamageEvent {
/// The Entity ID of the entity taking damage.
@@ -48,3 +48,25 @@ impl CDamageEvent {
}
}
}
impl ClientPacket for CDamageEvent {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.entity_id)?;
write.write_var_int(&self.source_type_id)?;
write.write_var_int(&self.source_cause_id)?;
write.write_var_int(&self.source_direct_id)?;
if let Some(pos) = &self.source_position {
write.write_bool(true)?;
write.write_f64(pos.x)?;
write.write_f64(pos.y)?;
write.write_f64(pos.z)?;
} else {
write.write_bool(false)?;
}
Ok(())
}
}

View File

@@ -1,15 +1,16 @@
use pumpkin_data::packet::clientbound::PLAY_DISCONNECT;
use pumpkin_util::text::TextComponent;
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_macros::java_packet;
use serde::Serialize;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_DISCONNECT)]
pub struct CPlayDisconnect<'a> {
/// The message shown to the player explaining why they were disconnected.
@@ -23,3 +24,14 @@ impl<'a> CPlayDisconnect<'a> {
Self { reason }
}
}
impl ClientPacket for CPlayDisconnect<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.reason.encode())?;
Ok(())
}
}

View File

@@ -1,17 +1,17 @@
use pumpkin_data::packet::clientbound::PLAY_DISGUISED_CHAT;
use pumpkin_macros::java_packet;
use pumpkin_util::text::TextComponent;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_DISGUISED_CHAT)]
pub struct CDisguisedChatMessage<'a> {
/// The raw content of the message.
@@ -41,3 +41,22 @@ impl<'a> CDisguisedChatMessage<'a> {
}
}
}
impl ClientPacket for CDisguisedChatMessage<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.message.encode())?;
write.write_var_int(&self.chat_type)?;
write.write_slice(&self.sender_name.encode())?;
if let Some(target) = self.target_name {
write.write_bool(true)?;
write.write_slice(&target.encode())?;
} else {
write.write_bool(false)?;
}
Ok(())
}
}

View File

@@ -1,17 +1,17 @@
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_data::{
packet::clientbound::PLAY_SET_DISPLAY_OBJECTIVE, scoreboard::ScoreboardDisplaySlot,
};
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::VarInt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_SET_DISPLAY_OBJECTIVE)]
pub struct CDisplayObjective {
/// The display slot/position for the objective.
@@ -30,3 +30,15 @@ impl CDisplayObjective {
}
}
}
impl ClientPacket for CDisplayObjective {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.position)?;
write.write_string(&self.score_name)?;
Ok(())
}
}

View File

@@ -1,14 +1,14 @@
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_ANIMATE;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::VarInt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_ANIMATE)]
pub struct CEntityAnimation {
/// The Entity ID of the entity performing the animation.
@@ -28,6 +28,18 @@ impl CEntityAnimation {
}
}
impl ClientPacket for CEntityAnimation {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.entity_id)?;
write.write_u8(self.animation)?;
Ok(())
}
}
#[derive(Debug)]
pub enum Animation {
SwingMainArm,

View File

@@ -7,13 +7,22 @@ use pumpkin_data::{
};
use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion;
use serde::Serialize;
use crate::{
ClientPacket, VarInt,
ser::{NetworkWriteExt, WritingError, serializer},
ser::{NetworkWriteExt, WritingError},
};
pub trait MetadataSerializer {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError>;
}
impl<T: MetadataSerializer + ?Sized> MetadataSerializer for &T {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
(*self).write_metadata(writer)
}
}
/// Updates the "Data Tracker" values for an entity.
///
/// Entity Metadata (or `DataWatchers`) controls persistent visual states that
@@ -41,17 +50,13 @@ impl CSetEntityMetadata {
impl ClientPacket for CSetEntityMetadata {
fn write_packet_data(
&self,
write: impl Write,
mut write: impl Write,
_version: &JavaMinecraftVersion,
) -> Result<(), WritingError> {
let mut write = write;
// 1. Entity ID
write.write_var_int(&self.entity_id)?;
write
.write_all(&self.metadata)
.map_err(WritingError::IoError)
write.write_slice(&self.metadata)
}
}
@@ -73,10 +78,10 @@ impl<T> Metadata<T> {
pub fn write<W: std::io::Write>(
&self,
mut writer: W,
version: &pumpkin_util::version::JavaMinecraftVersion,
version: &JavaMinecraftVersion,
) -> Result<(), WritingError>
where
T: Serialize,
T: MetadataSerializer,
{
let resolved_index = self.index.get(version);
@@ -95,12 +100,7 @@ impl<T> Metadata<T> {
if self.r#type == MetaDataType::BLOCK_STATE {
let mut serialized_value = Vec::new();
{
let mut serializer = serializer::Serializer::new(&mut serialized_value);
self.value
.serialize(&mut serializer)
.map_err(|e| WritingError::Serde(e.to_string()))?;
};
self.value.write_metadata(&mut serialized_value)?;
let mut cursor = Cursor::new(serialized_value);
let decoded_state = VarInt::decode(&mut cursor).map_err(|e| {
@@ -115,12 +115,7 @@ impl<T> Metadata<T> {
if self.r#type == MetaDataType::ITEM_STACK {
let mut serialized_value = Vec::new();
{
let mut serializer = serializer::Serializer::new(&mut serialized_value);
self.value
.serialize(&mut serializer)
.map_err(|e| WritingError::Serde(e.to_string()))?;
};
self.value.write_metadata(&mut serialized_value)?;
let mut cursor = Cursor::new(serialized_value);
let item_count = VarInt::decode(&mut cursor).map_err(|e| {
@@ -138,18 +133,134 @@ impl<T> Metadata<T> {
writer.write_var_int(&VarInt(i32::from(remapped_id)))?;
let remainder_start = cursor.position() as usize;
let inner = cursor.into_inner();
writer.write_all(&inner[remainder_start..]).map_err(|e| {
WritingError::Message(format!("Failed to write item stack remainder: {e}"))
})?;
writer.write_slice(&inner[remainder_start..])?;
}
return Ok(());
}
let mut serializer = serializer::Serializer::new(&mut writer);
self.value
.serialize(&mut serializer)
.map_err(|e| WritingError::Serde(e.to_string()))?;
self.value.write_metadata(&mut writer)?;
Ok(())
}
}
impl MetadataSerializer for bool {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
writer.write_bool(*self)
}
}
impl MetadataSerializer for i8 {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
writer.write_i8(*self)
}
}
impl MetadataSerializer for u8 {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
writer.write_u8(*self)
}
}
impl MetadataSerializer for i16 {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
writer.write_i16(*self)
}
}
impl MetadataSerializer for u16 {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
writer.write_u16(*self)
}
}
impl MetadataSerializer for i32 {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
writer.write_i32(*self)
}
}
impl MetadataSerializer for u32 {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
writer.write_u32(*self)
}
}
impl MetadataSerializer for f32 {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
writer.write_f32(*self)
}
}
impl MetadataSerializer for VarInt {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
writer.write_var_int(self)
}
}
impl MetadataSerializer for String {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
writer.write_string(self)
}
}
impl MetadataSerializer for pumpkin_util::text::TextComponent {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
writer.write_slice(&self.encode())
}
}
impl MetadataSerializer for Option<pumpkin_util::text::TextComponent> {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
if let Some(text) = self {
writer.write_bool(true)?;
writer.write_slice(&text.encode())?;
} else {
writer.write_bool(false)?;
}
Ok(())
}
}
impl MetadataSerializer for crate::codec::item_stack_seralizer::ItemStackSerializer<'_> {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
self.write(writer)
}
}
impl MetadataSerializer for Option<String> {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
if let Some(s) = self {
writer.write_bool(true)?;
writer.write_string(s)?;
} else {
writer.write_bool(false)?;
}
Ok(())
}
}
impl MetadataSerializer for pumpkin_util::math::position::BlockPos {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
writer.write_block_pos(self)
}
}
impl MetadataSerializer for Option<pumpkin_util::math::position::BlockPos> {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
if let Some(pos) = self {
writer.write_bool(true)?;
writer.write_block_pos(pos)?;
} else {
writer.write_bool(false)?;
}
Ok(())
}
}
impl MetadataSerializer for crate::codec::optional_int::OptionalInt {
fn write_metadata(&self, writer: &mut impl std::io::Write) -> Result<(), WritingError> {
let val = self.0.map_or(0, |id| id + 1);
writer.write_var_int(&VarInt(val))
}
}

View File

@@ -1,9 +1,10 @@
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_ENTITY_POSITION_SYNC;
use pumpkin_macros::java_packet;
use pumpkin_util::math::vector3::Vector3;
use serde::Serialize;
use crate::VarInt;
use pumpkin_util::version::JavaMinecraftVersion;
/// Updates the exact position, rotation, and velocity of an entity.
///
@@ -14,7 +15,6 @@ use crate::VarInt;
/// Note: This packet must NOT be used for the player receiving the packet or
/// any entity the player is currently riding.
#[java_packet(PLAY_ENTITY_POSITION_SYNC)]
#[derive(Serialize)]
pub struct CEntityPositionSync {
/// The Entity ID of the entity being moved.
pub entity_id: VarInt,
@@ -51,3 +51,23 @@ impl CEntityPositionSync {
}
}
}
impl ClientPacket for CEntityPositionSync {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.entity_id)?;
write.write_f64(self.position.x)?;
write.write_f64(self.position.y)?;
write.write_f64(self.position.z)?;
write.write_f64(self.delta.x)?;
write.write_f64(self.delta.y)?;
write.write_f64(self.delta.z)?;
write.write_f32(self.yaw)?;
write.write_f32(self.pitch)?;
write.write_bool(self.on_ground)?;
Ok(())
}
}

View File

@@ -65,12 +65,15 @@ impl ClientPacket for CEntitySoundEffect {
IdOr::Value(value) => IdOr::Value(value.clone()),
};
write.write_serialize(&sound_event)?;
write.write_serialize(&self.sound_category)?;
write.write_serialize(&self.entity_id)?;
write.write_serialize(&self.volume)?;
write.write_serialize(&self.pitch)?;
write.write_serialize(&self.seed)
crate::IdOr::<crate::SoundEvent>::write(&sound_event, &mut write, |w, e| {
w.write_string(&e.sound_name)?;
w.write_option(&e.range, |w2, r| w2.write_f32(*r))
})?;
write.write_var_int(&self.sound_category)?;
write.write_var_int(&self.entity_id)?;
write.write_f32(self.volume)?;
write.write_f32(self.pitch)?;
write.write_i64(self.seed)
}
}

View File

@@ -1,6 +1,9 @@
use pumpkin_data::packet::clientbound::PLAY_ENTITY_EVENT;
use pumpkin_macros::java_packet;
use serde::{Deserialize, Serialize};
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// Sends a status update for a specific entity.
///
@@ -8,7 +11,6 @@ use serde::{Deserialize, Serialize};
/// 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)]
#[java_packet(PLAY_ENTITY_EVENT)]
pub struct CEntityStatus {
/// The Entity ID of the entity affected by the status change.
@@ -27,3 +29,15 @@ impl CEntityStatus {
}
}
}
impl ClientPacket for CEntityStatus {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_i32_be(self.entity_id)?;
write.write_i8(self.entity_status)?;
Ok(())
}
}

View File

@@ -1,16 +1,17 @@
use pumpkin_data::packet::clientbound::PLAY_EXPLODE;
use pumpkin_macros::java_packet;
use pumpkin_util::math::vector3::Vector3;
use serde::Serialize;
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use crate::{IdOr, SoundEvent, codec::var_int::VarInt};
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_EXPLODE)]
pub struct CExplosion {
/// The center coordinates of the explosion.
@@ -54,3 +55,34 @@ impl CExplosion {
}
}
}
impl ClientPacket for CExplosion {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_f64_be(self.center.x)?;
write.write_f64_be(self.center.y)?;
write.write_f64_be(self.center.z)?;
write.write_f32_be(self.radius)?;
write.write_i32_be(self.block_count)?;
write.write_option(&self.knockback, |w, k| {
w.write_f64_be(k.x)?;
w.write_f64_be(k.y)?;
w.write_f64_be(k.z)?;
Ok(())
})?;
write.write_var_int(&self.particle)?;
match &self.sound {
IdOr::Id(id) => write.write_var_int(&VarInt((*id + 1) as i32))?,
IdOr::Value(event) => {
write.write_var_int(&VarInt(0))?;
write.write_string(&event.sound_name)?;
write.write_option(&event.range, |w, r| w.write_f32_be(*r))?;
}
}
write.write_var_int(&self.block_particles_pool_size)?;
Ok(())
}
}

View File

@@ -1,13 +1,15 @@
use pumpkin_data::packet::clientbound::PLAY_GAME_EVENT;
use pumpkin_macros::java_packet;
use serde::{Deserialize, Serialize};
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_GAME_EVENT)]
pub struct CGameEvent {
/// The ID of the event type.
@@ -43,3 +45,15 @@ pub enum GameEvent {
LimitedCrafting,
StartWaitingChunks,
}
impl ClientPacket for CGameEvent {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_u8(self.event)?;
write.write_f32_be(self.value)?;
Ok(())
}
}

View File

@@ -1,8 +1,10 @@
use pumpkin_data::packet::clientbound::PLAY_ROTATE_HEAD;
use pumpkin_macros::java_packet;
use serde::{Deserialize, Serialize};
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// Rotates an entity's head to a specific yaw.
///
@@ -10,7 +12,6 @@ use crate::VarInt;
/// 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)]
#[java_packet(PLAY_ROTATE_HEAD)]
pub struct CHeadRot {
/// The Entity ID of the entity whose head is rotating.
@@ -28,3 +29,15 @@ impl CHeadRot {
}
}
}
impl ClientPacket for CHeadRot {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.entity_id)?;
write.write_u8(self.head_yaw)?;
Ok(())
}
}

View File

@@ -1,15 +1,16 @@
use pumpkin_data::packet::clientbound::PLAY_HURT_ANIMATION;
use pumpkin_macros::java_packet;
use serde::{Deserialize, Serialize};
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_HURT_ANIMATION)]
pub struct CHurtAnimation {
/// The Entity ID of the entity that was hurt.
@@ -25,3 +26,15 @@ impl CHurtAnimation {
Self { entity_id, yaw }
}
}
impl ClientPacket for CHurtAnimation {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.entity_id)?;
write.write_f32_be(self.yaw)?;
Ok(())
}
}

View File

@@ -1,15 +1,16 @@
use pumpkin_data::packet::clientbound::PLAY_INITIALIZE_BORDER;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use crate::{VarInt, codec::var_long::VarLong};
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_INITIALIZE_BORDER)]
pub struct CInitializeWorldBorder {
/// The X coordinate of the center of the world border.
@@ -57,3 +58,21 @@ impl CInitializeWorldBorder {
}
}
}
impl ClientPacket for CInitializeWorldBorder {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_f64_be(self.x)?;
write.write_f64_be(self.z)?;
write.write_f64_be(self.old_diameter)?;
write.write_f64_be(self.new_diameter)?;
write.write_var_long(&self.speed)?;
write.write_var_int(&self.portal_teleport_boundary)?;
write.write_var_int(&self.warning_blocks)?;
write.write_var_int(&self.warning_time)?;
Ok(())
}
}

View File

@@ -2,9 +2,11 @@ use pumpkin_data::packet::clientbound::PLAY_COOLDOWN;
use crate::codec::var_int::VarInt;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(PLAY_COOLDOWN)]
pub struct CItemCooldown {
pub group: String,
@@ -17,3 +19,15 @@ impl CItemCooldown {
Self { group, cooldown }
}
}
impl ClientPacket for CItemCooldown {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_string(&self.group)?;
write.write_var_int(&self.cooldown)?;
Ok(())
}
}

View File

@@ -1,6 +1,9 @@
use pumpkin_data::packet::clientbound::PLAY_KEEP_ALIVE;
use pumpkin_macros::java_packet;
use serde::{Deserialize, Serialize};
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// Maintains the connection and measures latency (ping) between client and server.
///
@@ -8,7 +11,6 @@ use serde::{Deserialize, Serialize};
/// 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)]
#[java_packet(PLAY_KEEP_ALIVE)]
pub struct CKeepAlive {
/// A unique random identifier for this specific keep-alive request.
@@ -22,3 +24,14 @@ impl CKeepAlive {
Self { keep_alive_id }
}
}
impl ClientPacket for CKeepAlive {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_i64_be(self.keep_alive_id)?;
Ok(())
}
}

View File

@@ -1,11 +1,13 @@
use pumpkin_data::packet::clientbound::PLAY_MERCHANT_OFFERS;
use pumpkin_macros::java_packet;
use serde::{Deserialize, Serialize};
use crate::ClientPacket;
use crate::VarInt;
use crate::codec::item_stack_seralizer::ItemStackSerializer;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize, Deserialize, Clone)]
#[derive(Clone)]
pub struct MerchantOffer {
pub base_cost_a: ItemStackSerializer<'static>, // TODO: item cost
pub output: ItemStackSerializer<'static>,
@@ -19,7 +21,28 @@ pub struct MerchantOffer {
pub demand: i32,
}
#[derive(Serialize)]
impl MerchantOffer {
fn write(
&self,
mut write: impl std::io::Write,
version: JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
self.base_cost_a.write_with_version(&mut write, &version)?;
self.output.write_with_version(&mut write, &version)?;
write.write_option(&self.cost_b, |w, cost_b| {
cost_b.write_with_version(w, &version)
})?;
write.write_bool(self.is_disabled)?;
write.write_i32_be(self.uses)?;
write.write_i32_be(self.max_uses)?;
write.write_i32_be(self.xp)?;
write.write_i32_be(self.special_price)?;
write.write_f32_be(self.price_multiplier)?;
write.write_i32_be(self.demand)?;
Ok(())
}
}
#[java_packet(PLAY_MERCHANT_OFFERS)]
pub struct CMerchantOffers {
pub window_id: VarInt,
@@ -50,3 +73,22 @@ impl CMerchantOffers {
}
}
}
impl ClientPacket for CMerchantOffers {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.window_id)?;
write.write_var_int(&VarInt(self.offers.len() as i32))?;
for offer in &self.offers {
offer.write(&mut write, *version)?;
}
write.write_var_int(&self.villager_level)?;
write.write_var_int(&self.experience)?;
write.write_bool(self.is_regular_villager)?;
write.write_bool(self.can_restock)?;
Ok(())
}
}

View File

@@ -2,9 +2,11 @@ use pumpkin_data::packet::clientbound::PLAY_OPEN_SCREEN;
use pumpkin_util::text::TextComponent;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// Instructs the client to open a specific type of GUI (inventory, chest, etc.).
///
@@ -12,7 +14,6 @@ use crate::VarInt;
/// 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)]
#[java_packet(PLAY_OPEN_SCREEN)]
pub struct COpenScreen<'a> {
/// A unique identifier for the current window session.
@@ -40,3 +41,18 @@ impl<'a> COpenScreen<'a> {
}
}
}
impl ClientPacket for COpenScreen<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.sync_id)?;
write.write_var_int(&self.window_type)?;
// Write the text component as JSON string
let buf = self.window_title.encode();
write.write_slice(&buf)?;
Ok(())
}
}

View File

@@ -1,14 +1,16 @@
use pumpkin_data::packet::clientbound::PLAY_OPEN_SIGN_EDITOR;
use pumpkin_macros::java_packet;
use pumpkin_util::math::position::BlockPos;
use serde::Serialize;
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_OPEN_SIGN_EDITOR)]
pub struct COpenSignEditor {
/// The world coordinates of the sign block to be edited.
@@ -27,3 +29,15 @@ impl COpenSignEditor {
}
}
}
impl ClientPacket for COpenSignEditor {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_block_pos(&self.location)?;
write.write_bool(self.is_front_text)?;
Ok(())
}
}

View File

@@ -1,9 +1,11 @@
use pumpkin_data::packet::clientbound::PLAY_PONG_RESPONSE;
use pumpkin_macros::java_packet;
use serde::{Deserialize, Serialize};
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// Responds to a client-initiated ping request to synchronize game state.
#[derive(Serialize, Deserialize)]
#[java_packet(PLAY_PONG_RESPONSE)]
pub struct CPingResponse {
/// The unique identifier sent by the client in the initial Ping packet.
@@ -17,3 +19,14 @@ impl CPingResponse {
Self { payload }
}
}
impl ClientPacket for CPingResponse {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_i64_be(self.payload)?;
Ok(())
}
}

View File

@@ -1,13 +1,15 @@
use pumpkin_data::packet::clientbound::PLAY_PLAYER_ABILITIES;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_PLAYER_ABILITIES)]
pub struct CPlayerAbilities {
/// A bitmask of player states.
@@ -34,3 +36,16 @@ impl CPlayerAbilities {
}
}
}
impl ClientPacket for CPlayerAbilities {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_i8(self.flags)?;
write.write_f32_be(self.flying_speed)?;
write.write_f32_be(self.field_of_view)?;
Ok(())
}
}

View File

@@ -107,7 +107,9 @@ impl ClientPacket for CPlayerInfoUpdate<'_> {
PlayerAction::UpdateListed(listed) => p.write_bool(*listed)?,
PlayerAction::UpdateLatency(latency) => p.write_var_int(latency)?,
PlayerAction::UpdateDisplayName(display_name) => {
p.write_option(display_name, crate::ser::NetworkWriteExt::write_serialize)?;
p.write_option(display_name, |w, text_component| {
w.write_slice(&text_component.encode())
})?;
}
PlayerAction::UpdateListOrder(order) => p.write_var_int(order)?,
}

View File

@@ -1,19 +1,18 @@
use pumpkin_data::packet::clientbound::PLAY_PLAYER_INFO_REMOVE;
use pumpkin_macros::java_packet;
use serde::{Serialize, ser::SerializeSeq};
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_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],
}
@@ -24,13 +23,16 @@ impl<'a> CRemovePlayerInfo<'a> {
}
}
fn serialize_slice_uuids<S: serde::Serializer>(
uuids: &[uuid::Uuid],
serializer: S,
) -> Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(uuids.len()))?;
for uuid in uuids {
seq.serialize_element(uuid.as_bytes())?;
impl ClientPacket for CRemovePlayerInfo<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&VarInt(self.players.len() as i32))?;
for uuid in self.players {
write.write_uuid(uuid)?;
}
Ok(())
}
seq.end()
}

View File

@@ -1,14 +1,15 @@
use pumpkin_data::packet::clientbound::PLAY_REMOVE_ENTITIES;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// 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)]
#[java_packet(PLAY_REMOVE_ENTITIES)]
pub struct CRemoveEntities<'a> {
/// A list of entity IDs to be removed.
@@ -21,3 +22,17 @@ impl<'a> CRemoveEntities<'a> {
Self { entity_ids }
}
}
impl ClientPacket for CRemoveEntities<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&VarInt(self.entity_ids.len() as i32))?;
for id in self.entity_ids {
write.write_var_int(id)?;
}
Ok(())
}
}

View File

@@ -1,9 +1,11 @@
use crate::codec::var_int::VarInt;
use pumpkin_data::packet::clientbound::PLAY_REMOVE_MOB_EFFECT;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(PLAY_REMOVE_MOB_EFFECT)]
pub struct CRemoveMobEffect {
pub entity_id: VarInt,
@@ -19,3 +21,15 @@ impl CRemoveMobEffect {
}
}
}
impl ClientPacket for CRemoveMobEffect {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.entity_id)?;
write.write_var_int(&self.effect_id)?;
Ok(())
}
}

View File

@@ -1,8 +1,10 @@
use pumpkin_data::packet::clientbound::PLAY_RESET_SCORE;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(PLAY_RESET_SCORE)]
pub struct CResetScore {
pub entity_name: String,
@@ -18,3 +20,15 @@ impl CResetScore {
}
}
}
impl ClientPacket for CResetScore {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_string(&self.entity_name)?;
write.write_option(&self.objective_name, |w, obj| w.write_string(obj))?;
Ok(())
}
}

View File

@@ -1,9 +1,11 @@
use pumpkin_data::packet::clientbound::PLAY_SELECT_ADVANCEMENTS_TAB;
use pumpkin_macros::java_packet;
use pumpkin_util::identifier::Identifier;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(PLAY_SELECT_ADVANCEMENTS_TAB)]
pub struct CSelectAdvancementsTab {
pub tab_id: Option<Identifier>,
@@ -15,3 +17,14 @@ impl CSelectAdvancementsTab {
Self { tab_id }
}
}
impl ClientPacket for CSelectAdvancementsTab {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_option(&self.tab_id, |w, id| w.write_string(&id.to_string()))?;
Ok(())
}
}

View File

@@ -1,9 +1,7 @@
use crate::Link;
use pumpkin_data::packet::clientbound::PLAY_SERVER_LINKS;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
#[java_packet(PLAY_SERVER_LINKS)]
pub struct CPlayServerLinks<'a> {
pub links: &'a [Link<'a>],

View File

@@ -1,8 +1,10 @@
use pumpkin_data::packet::clientbound::PLAY_SET_BORDER_CENTER;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(PLAY_SET_BORDER_CENTER)]
pub struct CSetBorderCenter {
pub x: f64,
@@ -15,3 +17,15 @@ impl CSetBorderCenter {
Self { x, z }
}
}
impl ClientPacket for CSetBorderCenter {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_f64_be(self.x)?;
write.write_f64_be(self.z)?;
Ok(())
}
}

View File

@@ -1,10 +1,11 @@
use pumpkin_data::packet::clientbound::PLAY_SET_BORDER_LERP_SIZE;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::codec::var_long::VarLong;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize)]
#[java_packet(PLAY_SET_BORDER_LERP_SIZE)]
pub struct CSetBorderLerpSize {
pub old_diameter: f64,
@@ -22,3 +23,16 @@ impl CSetBorderLerpSize {
}
}
}
impl ClientPacket for CSetBorderLerpSize {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_f64_be(self.old_diameter)?;
write.write_f64_be(self.new_diameter)?;
write.write_var_long(&self.speed)?;
Ok(())
}
}

View File

@@ -1,8 +1,10 @@
use pumpkin_data::packet::clientbound::PLAY_SET_BORDER_SIZE;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(PLAY_SET_BORDER_SIZE)]
pub struct CSetBorderSize {
pub diameter: f64,
@@ -14,3 +16,14 @@ impl CSetBorderSize {
Self { diameter }
}
}
impl ClientPacket for CSetBorderSize {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_f64_be(self.diameter)?;
Ok(())
}
}

View File

@@ -1,10 +1,11 @@
use pumpkin_data::packet::clientbound::PLAY_SET_BORDER_WARNING_DELAY;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize)]
#[java_packet(PLAY_SET_BORDER_WARNING_DELAY)]
pub struct CSetBorderWarningDelay {
pub warning_time: VarInt,
@@ -16,3 +17,14 @@ impl CSetBorderWarningDelay {
Self { warning_time }
}
}
impl ClientPacket for CSetBorderWarningDelay {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.warning_time)?;
Ok(())
}
}

View File

@@ -1,10 +1,11 @@
use pumpkin_data::packet::clientbound::PLAY_SET_BORDER_WARNING_DISTANCE;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize)]
#[java_packet(PLAY_SET_BORDER_WARNING_DISTANCE)]
pub struct CSetBorderWarningDistance {
pub warning_blocks: VarInt,
@@ -16,3 +17,14 @@ impl CSetBorderWarningDistance {
Self { warning_blocks }
}
}
impl ClientPacket for CSetBorderWarningDistance {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.warning_blocks)?;
Ok(())
}
}

View File

@@ -1,10 +1,10 @@
use pumpkin_data::packet::clientbound::PLAY_SET_CAMERA;
use pumpkin_util::version::JavaMinecraftVersion;
use serde::Serialize;
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use crate::{VarInt, packet::MultiVersionJavaPacket};
#[derive(Serialize)]
pub struct CSetCamera {
pub camera_id: VarInt,
}
@@ -16,6 +16,17 @@ impl CSetCamera {
}
}
impl ClientPacket for CSetCamera {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.camera_id)?;
Ok(())
}
}
impl MultiVersionJavaPacket for CSetCamera {
fn to_id(version: JavaMinecraftVersion) -> i32 {
PLAY_SET_CAMERA.to_id(version)

View File

@@ -1,10 +1,11 @@
use pumpkin_data::packet::clientbound::PLAY_CONTAINER_SET_DATA;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize)]
#[java_packet(PLAY_CONTAINER_SET_DATA)]
pub struct CSetContainerProperty {
pub window_id: VarInt,
@@ -22,3 +23,16 @@ impl CSetContainerProperty {
}
}
}
impl ClientPacket for CSetContainerProperty {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.window_id)?;
write.write_i16(self.property)?;
write.write_i16(self.value)?;
Ok(())
}
}

View File

@@ -22,9 +22,9 @@ impl<'a> CSetCursorItem<'a> {
impl ClientPacket for CSetCursorItem<'_> {
fn write_packet_data(
&self,
write: impl Write,
mut write: impl Write,
version: &JavaMinecraftVersion,
) -> Result<(), WritingError> {
self.stack.write_with_version(write, version)
self.stack.write_with_version(&mut write, version)
}
}

View File

@@ -1,10 +1,11 @@
use pumpkin_data::packet::clientbound::PLAY_SET_EXPERIENCE;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize)]
#[java_packet(PLAY_SET_EXPERIENCE)]
pub struct CSetExperience {
pub progress: f32,
@@ -22,3 +23,16 @@ impl CSetExperience {
}
}
}
impl ClientPacket for CSetExperience {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_f32(self.progress)?;
write.write_var_int(&self.level)?;
write.write_var_int(&self.total_experience)?;
Ok(())
}
}

View File

@@ -1,10 +1,11 @@
use pumpkin_data::packet::clientbound::PLAY_SET_HEALTH;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize)]
#[java_packet(PLAY_SET_HEALTH)]
pub struct CSetHealth {
pub health: f32,
@@ -22,3 +23,16 @@ impl CSetHealth {
}
}
}
impl ClientPacket for CSetHealth {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_f32(self.health)?;
write.write_var_int(&self.food)?;
write.write_f32(self.food_saturation)?;
Ok(())
}
}

View File

@@ -1,10 +1,10 @@
use pumpkin_data::packet::clientbound::{PLAY_SET_CARRIED_ITEM, PLAY_SET_HELD_SLOT};
use pumpkin_util::version::JavaMinecraftVersion;
use serde::Serialize;
use crate::ClientPacket;
use crate::packet::MultiVersionJavaPacket;
use crate::ser::NetworkWriteExt;
#[derive(Serialize)]
pub struct CSetSelectedSlot {
pub slot: i8,
}
@@ -16,6 +16,17 @@ impl CSetSelectedSlot {
}
}
impl ClientPacket for CSetSelectedSlot {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_i8(self.slot)?;
Ok(())
}
}
impl MultiVersionJavaPacket for CSetSelectedSlot {
fn to_id(version: JavaMinecraftVersion) -> i32 {
if version >= JavaMinecraftVersion::V_1_21_2 {

View File

@@ -1,10 +1,11 @@
use pumpkin_data::packet::clientbound::PLAY_SET_PASSENGERS;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize)]
#[java_packet(PLAY_SET_PASSENGERS)]
pub struct CSetPassengers<'a> {
pub entity_id: VarInt,
@@ -20,3 +21,18 @@ impl<'a> CSetPassengers<'a> {
}
}
}
impl ClientPacket for CSetPassengers<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.entity_id)?;
write.write_var_int(&crate::VarInt(self.passengers.len() as i32))?;
for passenger in self.passengers {
write.write_var_int(passenger)?;
}
Ok(())
}
}

View File

@@ -29,6 +29,6 @@ impl ClientPacket for CSetPlayerInventory<'_> {
) -> Result<(), WritingError> {
let mut write = write;
write.write_var_int(&self.slot)?;
self.item.write_with_version(write, version)
self.item.write_with_version(&mut write, version)
}
}

View File

@@ -1,10 +1,11 @@
use pumpkin_data::packet::clientbound::PLAY_SET_TITLE_TEXT;
use pumpkin_util::text::TextComponent;
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_macros::java_packet;
use serde::Serialize;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize)]
#[java_packet(PLAY_SET_TITLE_TEXT)]
pub struct CTitleText<'a> {
pub title: &'a TextComponent,
@@ -16,3 +17,14 @@ impl<'a> CTitleText<'a> {
Self { title }
}
}
impl ClientPacket for CTitleText<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.title.encode())?;
Ok(())
}
}

View File

@@ -1,9 +1,10 @@
use pumpkin_data::packet::clientbound::PLAY_SET_TITLES_ANIMATION;
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_macros::java_packet;
use serde::Serialize;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize)]
#[java_packet(PLAY_SET_TITLES_ANIMATION)]
pub struct CTitleAnimation {
pub fade_in_ticks: i32,
@@ -21,3 +22,16 @@ impl CTitleAnimation {
}
}
}
impl ClientPacket for CTitleAnimation {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_i32_be(self.fade_in_ticks)?;
write.write_i32_be(self.stay_ticks)?;
write.write_i32_be(self.fade_out_ticks)?;
Ok(())
}
}

View File

@@ -1,10 +1,9 @@
use crate::IdOr;
use crate::java::client::dialog::DialogNBT;
use crate::{ClientPacket, IdOr, ser::WritingError};
use pumpkin_data::packet::clientbound::PLAY_SHOW_DIALOG;
use pumpkin_macros::java_packet;
use serde::Serialize;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize)]
#[java_packet(PLAY_SHOW_DIALOG)]
pub struct CPlayShowDialog<'a> {
pub dialog: IdOr<DialogNBT<'a>>,
@@ -16,3 +15,14 @@ impl<'a> CPlayShowDialog<'a> {
Self { dialog }
}
}
impl ClientPacket for CPlayShowDialog<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
version: &JavaMinecraftVersion,
) -> Result<(), WritingError> {
self.dialog
.write(&mut write, |w, value| value.write_packet_data(w, version))
}
}

View File

@@ -55,12 +55,17 @@ impl ClientPacket for CSoundEffect {
IdOr::Value(value) => IdOr::Value(value.clone()),
};
write.write_serialize(&sound_event)?;
write.write_serialize(&self.sound_category)?;
write.write_serialize(&self.position)?;
write.write_serialize(&self.volume)?;
write.write_serialize(&self.pitch)?;
write.write_serialize(&self.seed)
crate::IdOr::<crate::SoundEvent>::write(&sound_event, &mut write, |w, e| {
w.write_string(&e.sound_name)?;
w.write_option(&e.range, |w2, r| w2.write_f32_be(*r))
})?;
write.write_var_int(&self.sound_category)?;
write.write_i32_be(self.position.x * 8)?;
write.write_i32_be(self.position.y * 8)?;
write.write_i32_be(self.position.z * 8)?;
write.write_f32_be(self.volume)?;
write.write_f32_be(self.pitch)?;
write.write_i64_be(self.seed as i64)
}
}

View File

@@ -1,11 +1,13 @@
use pumpkin_data::packet::clientbound::PLAY_STORE_COOKIE;
use pumpkin_macros::java_packet;
use pumpkin_util::resource_location::ResourceLocation;
use serde::Serialize;
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// Stores some arbitrary data on the client, which persists between server transfers.
/// The Notchian client only accepts cookies of up to 5 kiB in size.
#[derive(Serialize)]
#[java_packet(PLAY_STORE_COOKIE)]
pub struct CStoreCookie<'a> {
pub key: &'a ResourceLocation,
@@ -18,3 +20,18 @@ impl<'a> CStoreCookie<'a> {
Self { key, payload }
}
}
impl ClientPacket for CStoreCookie<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_string(self.key)?;
write.write_var_int(&crate::VarInt(self.payload.len() as i32))?;
write
.write_all(self.payload)
.map_err(|_| crate::ser::WritingError::Message("IO Error".into()))?;
Ok(())
}
}

View File

@@ -2,9 +2,11 @@ use pumpkin_data::packet::clientbound::PLAY_SET_SUBTITLE_TEXT;
use pumpkin_util::text::TextComponent;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(PLAY_SET_SUBTITLE_TEXT)]
pub struct CSubtitle<'a> {
pub subtitle: &'a TextComponent,
@@ -16,3 +18,14 @@ impl<'a> CSubtitle<'a> {
Self { subtitle }
}
}
impl ClientPacket for CSubtitle<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.subtitle.encode())?;
Ok(())
}
}

View File

@@ -2,9 +2,11 @@ use pumpkin_data::packet::clientbound::PLAY_SYSTEM_CHAT;
use pumpkin_util::text::TextComponent;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(PLAY_SYSTEM_CHAT)]
pub struct CSystemChatMessage<'a> {
pub content: &'a TextComponent,
@@ -17,3 +19,15 @@ impl<'a> CSystemChatMessage<'a> {
Self { content, overlay }
}
}
impl ClientPacket for CSystemChatMessage<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.content.encode())?;
write.write_bool(self.overlay)?;
Ok(())
}
}

View File

@@ -1,10 +1,12 @@
use pumpkin_data::packet::clientbound::PLAY_TAB_LIST;
use pumpkin_macros::java_packet;
use pumpkin_util::text::TextComponent;
use serde::Serialize;
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
/// Updates the header and footer of the player list (Tab List).
#[derive(Serialize)]
#[java_packet(PLAY_TAB_LIST)]
pub struct CTabList<'a> {
/// The text to be displayed at the top of the player list.
@@ -19,3 +21,15 @@ impl<'a> CTabList<'a> {
Self { header, footer }
}
}
impl ClientPacket for CTabList<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.header.encode())?;
write.write_slice(&self.footer.encode())?;
Ok(())
}
}

View File

@@ -1,9 +1,11 @@
use crate::VarInt;
use pumpkin_data::packet::clientbound::PLAY_TAKE_ITEM_ENTITY;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(PLAY_TAKE_ITEM_ENTITY)]
pub struct CTakeItemEntity {
/// The entity id of the item entity.
@@ -24,3 +26,16 @@ impl CTakeItemEntity {
}
}
}
impl ClientPacket for CTakeItemEntity {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.entity_id)?;
write.write_var_int(&self.collector_entity_id)?;
write.write_var_int(&self.stack_amount)?;
Ok(())
}
}

View File

@@ -1,8 +1,10 @@
use pumpkin_data::packet::clientbound::PLAY_TICKING_STATE;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(PLAY_TICKING_STATE)]
pub struct CTickingState {
pub tick_rate: f32,
@@ -18,3 +20,15 @@ impl CTickingState {
}
}
}
impl ClientPacket for CTickingState {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_f32_be(self.tick_rate)?;
write.write_bool(self.is_frozen)?;
Ok(())
}
}

View File

@@ -1,10 +1,11 @@
use pumpkin_data::packet::clientbound::PLAY_TICKING_STEP;
use pumpkin_macros::java_packet;
use serde::Serialize;
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize)]
#[java_packet(PLAY_TICKING_STEP)]
pub struct CTickingStep {
pub tick_steps: VarInt,
@@ -16,3 +17,14 @@ impl CTickingStep {
Self { tick_steps }
}
}
impl ClientPacket for CTickingStep {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.tick_steps)?;
Ok(())
}
}

View File

@@ -1,9 +1,10 @@
use crate::ClientPacket;
use crate::VarInt;
use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_TRANSFER;
use pumpkin_macros::java_packet;
use serde::{Deserialize, Serialize};
use pumpkin_util::version::JavaMinecraftVersion;
#[derive(Serialize, Deserialize)]
#[java_packet(PLAY_TRANSFER)]
pub struct CTransfer<'a> {
pub host: &'a str,
@@ -16,3 +17,15 @@ impl<'a> CTransfer<'a> {
Self { host, port }
}
}
impl ClientPacket for CTransfer<'_> {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_string(self.host)?;
write.write_var_int(&self.port)?;
Ok(())
}
}

View File

@@ -1,8 +1,10 @@
use pumpkin_data::packet::clientbound::PLAY_FORGET_LEVEL_CHUNK;
use pumpkin_macros::java_packet;
use serde::Serialize;
#[derive(Serialize)]
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(PLAY_FORGET_LEVEL_CHUNK)]
pub struct CUnloadChunk {
pub z: i32,
@@ -15,3 +17,15 @@ impl CUnloadChunk {
Self { z, x }
}
}
impl ClientPacket for CUnloadChunk {
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_i32_be(self.z)?;
write.write_i32_be(self.x)?;
Ok(())
}
}

View File

@@ -1,82 +1,18 @@
use crate::codec::item_stack_seralizer::ItemStackTemplateSerializer;
use crate::codec::var_int::VarInt;
use pumpkin_data::Advancement;
use pumpkin_data::advancement_data::{AdvancementDisplay, AdvancementProgressData};
use pumpkin_data::advancement_data::AdvancementProgressData;
use pumpkin_data::packet::clientbound::PLAY_UPDATE_ADVANCEMENTS;
use pumpkin_macros::java_packet;
use pumpkin_util::identifier::Identifier;
use serde::ser::SerializeStruct;
use serde::{Serialize, Serializer, ser::SerializeSeq};
fn serialize_advancements<S: Serializer>(
advancements: &[&'static Advancement],
serializer: S,
) -> Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(advancements.len()))?;
for adv in advancements {
seq.serialize_element(&AdvancementSer(adv))?;
}
seq.end()
}
use crate::ClientPacket;
use crate::ser::NetworkWriteExt;
use pumpkin_util::version::JavaMinecraftVersion;
pub struct AdvancementSer<'a>(pub &'a Advancement);
impl Serialize for AdvancementSer<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let adv = self.0;
let mut state = serializer.serialize_struct("Advancement", 5)?;
state.serialize_field("id", &adv.id.to_string())?;
state.serialize_field("parent", &adv.parent.clone().map(|p| p.to_string()))?;
if let Some(display) = adv.display {
state.serialize_field("display", &Some(DisplaySerializer(display)))?;
} else {
state.serialize_field("display", &None::<&DisplaySerializer>)?;
}
state.serialize_field("requirements", adv.requirements)?;
state.serialize_field("send_telemetry", &adv.send_telemetry)?;
state.end()
}
}
pub struct DisplaySerializer<'a>(pub &'a AdvancementDisplay);
impl Serialize for DisplaySerializer<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let display = self.0;
let mut state = serializer.serialize_struct("AdvancementDisplay", 8)?;
state.serialize_field("title", &display.get_title())?;
state.serialize_field("description", &display.get_description())?;
state.serialize_field(
"icon",
&ItemStackTemplateSerializer::from(display.item_icon.clone()),
)?;
state.serialize_field("frame_type", &VarInt(display.frame_type as i32))?;
let flags = (display.has_background() as i32)
| ((display.show_toast as i32) << 1)
| ((display.hidden as i32) << 2);
state.serialize_field("flags", &flags)?;
if let Some(bg) = display.background_texture {
state.serialize_field("background_texture", bg)?;
}
state.serialize_field("x", &display.x)?;
state.serialize_field("y", &display.y)?;
state.end()
}
}
#[derive(Serialize)]
#[java_packet(PLAY_UPDATE_ADVANCEMENTS)]
#[allow(unused)]
pub struct CUpdateAdvancements {
pub reset: bool,
#[serde(serialize_with = "serialize_advancements")]
pub added: Vec<&'static Advancement>,
pub removed: Vec<Identifier>,
pub progress: Vec<AdvancementProgressData>,
@@ -102,3 +38,82 @@ impl CUpdateAdvancements {
}
}
}
impl ClientPacket for CUpdateAdvancements {
#[allow(clippy::unimplemented)]
fn write_packet_data(
&self,
mut write: impl std::io::Write,
_version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> {
write.write_bool(self.reset)?;
write.write_var_int(&VarInt(self.added.len() as i32))?;
for adv in &self.added {
write.write_string(&adv.id.to_string())?;
let has_parent = adv.parent.is_some();
write.write_bool(has_parent)?;
if let Some(ref p) = adv.parent {
write.write_string(&p.to_string())?;
}
let has_display = adv.display.is_some();
write.write_bool(has_display)?;
if let Some(display) = adv.display {
write.write_slice(&display.get_title().encode())?;
write.write_slice(&display.get_description().encode())?;
// Item icon
// TODO: serialize item_icon (ItemStack)
unimplemented!("Item serialization is not yet implemented without serde");
/*
write.write_var_int(&VarInt(display.frame_type as i32))?;
let flags = (display.has_background() as i32)
| ((display.show_toast as i32) << 1)
| ((display.hidden as i32) << 2);
write.write_i32_be(flags)?;
if let Some(bg) = display.background_texture {
write.write_string(bg)?;
}
write.write_f32_be(display.x)?;
write.write_f32_be(display.y)?;
*/
}
write.write_var_int(&VarInt(adv.requirements.len() as i32))?;
for req in adv.requirements {
write.write_var_int(&VarInt(req.len() as i32))?;
for r in *req {
write.write_string(r)?;
}
}
write.write_bool(adv.send_telemetry)?;
}
write.write_var_int(&VarInt(self.removed.len() as i32))?;
for rem in &self.removed {
write.write_string(&rem.to_string())?;
}
write.write_var_int(&VarInt(self.progress.len() as i32))?;
for prog in &self.progress {
write.write_string(&prog.id.to_string())?;
write.write_var_int(&VarInt(prog.progress.len() as i32))?;
for crit in &prog.progress {
write.write_string(&crit.criterion_id)?;
let has_date = crit.achieve_date.is_some();
write.write_bool(has_date)?;
if let Some(date) = crit.achieve_date {
write.write_i64_be(date)?;
}
}
}
write.write_bool(self.show_advancements)?;
Ok(())
}
}

Some files were not shown because too many files have changed in this diff Show More