This commit is contained in:
Alexander Medvedev 2026-08-18 22:19:13 +02:00
parent 29bc796d62
commit 5ef9648362
25 changed files with 428 additions and 123 deletions

1
Cargo.lock generated
View File

@ -3579,6 +3579,7 @@ dependencies = [
"pumpkin-util", "pumpkin-util",
"pumpkin-world", "pumpkin-world",
"serde", "serde",
"serde_json",
"thiserror 2.0.20", "thiserror 2.0.20",
"tokio", "tokio",
"uuid", "uuid",

View File

@ -33,7 +33,7 @@ impl Registry {
pub fn get_synced(version: JavaMinecraftVersion) -> Vec<Self> { pub fn get_synced(version: JavaMinecraftVersion) -> Vec<Self> {
#[allow(clippy::match_same_arms)] #[allow(clippy::match_same_arms)]
let static_regs = match version { let static_regs = match version {
pumpkin_util::version::JavaMinecraftVersion::V_1_20_5 => REGISTRY_V_1_20_5, v if v <= pumpkin_util::version::JavaMinecraftVersion::V_1_20_5 => REGISTRY_V_1_20_5,
pumpkin_util::version::JavaMinecraftVersion::V_1_21 => REGISTRY_V_1_21, pumpkin_util::version::JavaMinecraftVersion::V_1_21 => REGISTRY_V_1_21,
pumpkin_util::version::JavaMinecraftVersion::V_1_21_2 => REGISTRY_V_1_21_2, pumpkin_util::version::JavaMinecraftVersion::V_1_21_2 => REGISTRY_V_1_21_2,
pumpkin_util::version::JavaMinecraftVersion::V_1_21_4 => REGISTRY_V_1_21_4, pumpkin_util::version::JavaMinecraftVersion::V_1_21_4 => REGISTRY_V_1_21_4,
@ -43,8 +43,7 @@ impl Registry {
pumpkin_util::version::JavaMinecraftVersion::V_1_21_9 => REGISTRY_V_1_21_9, pumpkin_util::version::JavaMinecraftVersion::V_1_21_9 => REGISTRY_V_1_21_9,
pumpkin_util::version::JavaMinecraftVersion::V_1_21_11 => REGISTRY_V_1_21_11, pumpkin_util::version::JavaMinecraftVersion::V_1_21_11 => REGISTRY_V_1_21_11,
pumpkin_util::version::JavaMinecraftVersion::V_26_1 => REGISTRY_V_26_1, pumpkin_util::version::JavaMinecraftVersion::V_26_1 => REGISTRY_V_26_1,
pumpkin_util::version::JavaMinecraftVersion::V_26_2 => REGISTRY_V_26_2, pumpkin_util::version::JavaMinecraftVersion::V_26_2 | _ => REGISTRY_V_26_2,
_ => REGISTRY_V_26_2,
}; };
static_regs static_regs
.iter() .iter()

View File

@ -18,6 +18,7 @@ pumpkin-macros.workspace = true
pumpkin-world.workspace = true pumpkin-world.workspace = true
pumpkin-util.workspace = true pumpkin-util.workspace = true
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
uuid.workspace = true uuid.workspace = true
thiserror.workspace = true thiserror.workspace = true

View File

@ -53,7 +53,7 @@ impl ClientPacket for CEncryptionRequest<'_> {
write.write_all(self.public_key)?; write.write_all(self.public_key)?;
write.write_var_int(&crate::VarInt(self.verify_token.len() as i32))?; write.write_var_int(&crate::VarInt(self.verify_token.len() as i32))?;
write.write_all(self.verify_token)?; write.write_all(self.verify_token)?;
if version >= &JavaMinecraftVersion::V_1_19 { if version >= &JavaMinecraftVersion::V_1_20_5 {
write.write_bool(self.should_authenticate)?; write.write_bool(self.should_authenticate)?;
} }
Ok(()) Ok(())
@ -71,7 +71,7 @@ impl<'a> crate::ServerPacket<'a> for CEncryptionRequest<'a> {
let public_key = read.read_slice_borrowed(public_key_len)?; let public_key = read.read_slice_borrowed(public_key_len)?;
let verify_token_len = read.get_var_int()?.0 as usize; let verify_token_len = read.get_var_int()?.0 as usize;
let verify_token = read.read_slice_borrowed(verify_token_len)?; let verify_token = read.read_slice_borrowed(verify_token_len)?;
let should_authenticate = if version >= &JavaMinecraftVersion::V_1_19 { let should_authenticate = if version >= &JavaMinecraftVersion::V_1_20_5 {
read.get_bool()? read.get_bool()?
} else { } else {
true true

View File

@ -66,12 +66,12 @@ impl ClientPacket for CLoginSuccess<'_> {
if version >= &JavaMinecraftVersion::V_1_19 { if version >= &JavaMinecraftVersion::V_1_19 {
write.write_list(self.properties, |write, property| property.write(write))?; write.write_list(self.properties, |write, property| property.write(write))?;
} }
if version >= &JavaMinecraftVersion::V_1_20_2 && version < &JavaMinecraftVersion::V_1_21_2 {
write.write_bool(self.strict_error_handling)?;
}
if version >= &JavaMinecraftVersion::V_26_2 { if version >= &JavaMinecraftVersion::V_26_2 {
write.write_uuid(&self.session_id)?; write.write_uuid(&self.session_id)?;
} }
if version >= &JavaMinecraftVersion::V_1_20_5 && version < &JavaMinecraftVersion::V_1_21_2 {
write.write_bool(self.strict_error_handling)?;
}
Ok(()) Ok(())
} }
} }

View File

@ -29,9 +29,14 @@ impl ClientPacket for CPlayDisconnect<'_> {
fn write_packet_data( fn write_packet_data(
&self, &self,
mut write: impl std::io::Write, mut write: impl std::io::Write,
_version: &JavaMinecraftVersion, version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> { ) -> Result<(), crate::ser::WritingError> {
write.write_slice(&self.reason.encode())?; if version < &JavaMinecraftVersion::V_1_20_5 {
let json = serde_json::to_string(&self.reason.0).unwrap_or_default();
write.write_string(&json)?;
} else {
write.write_slice(&self.reason.encode())?;
}
Ok(()) Ok(())
} }
} }

View File

@ -1,8 +1,8 @@
use crate::ClientPacket; use crate::ClientPacket;
use crate::VarInt; use crate::VarInt;
use crate::packet::MultiVersionJavaPacket;
use crate::ser::NetworkWriteExt; use crate::ser::NetworkWriteExt;
use pumpkin_data::packet::clientbound::PLAY_ENTITY_POSITION_SYNC; use pumpkin_data::packet::clientbound::{PLAY_ENTITY_POSITION_SYNC, PLAY_TELEPORT_ENTITY};
use pumpkin_macros::java_packet;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
@ -14,7 +14,6 @@ use pumpkin_util::version::JavaMinecraftVersion;
/// ///
/// Note: This packet must NOT be used for the player receiving the packet or /// Note: This packet must NOT be used for the player receiving the packet or
/// any entity the player is currently riding. /// any entity the player is currently riding.
#[java_packet(PLAY_ENTITY_POSITION_SYNC)]
pub struct CEntityPositionSync { pub struct CEntityPositionSync {
/// The Entity ID of the entity being moved. /// The Entity ID of the entity being moved.
pub entity_id: VarInt, pub entity_id: VarInt,
@ -52,21 +51,36 @@ impl CEntityPositionSync {
} }
} }
impl MultiVersionJavaPacket for CEntityPositionSync {
fn to_id(version: JavaMinecraftVersion) -> i32 {
if version >= JavaMinecraftVersion::V_1_21_2 {
PLAY_ENTITY_POSITION_SYNC.to_id(version)
} else {
PLAY_TELEPORT_ENTITY.to_id(version)
}
}
}
impl ClientPacket for CEntityPositionSync { impl ClientPacket for CEntityPositionSync {
fn write_packet_data( fn write_packet_data(
&self, &self,
mut write: impl std::io::Write, mut write: impl std::io::Write,
_version: &JavaMinecraftVersion, version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> { ) -> Result<(), crate::ser::WritingError> {
write.write_var_int(&self.entity_id)?; write.write_var_int(&self.entity_id)?;
write.write_f64(self.position.x)?; write.write_f64_be(self.position.x)?;
write.write_f64(self.position.y)?; write.write_f64_be(self.position.y)?;
write.write_f64(self.position.z)?; write.write_f64_be(self.position.z)?;
write.write_f64(self.delta.x)?; if version >= &JavaMinecraftVersion::V_1_21_2 {
write.write_f64(self.delta.y)?; write.write_f64_be(self.delta.x)?;
write.write_f64(self.delta.z)?; write.write_f64_be(self.delta.y)?;
write.write_f32(self.yaw)?; write.write_f64_be(self.delta.z)?;
write.write_f32(self.pitch)?; write.write_f32_be(self.yaw)?;
write.write_f32_be(self.pitch)?;
} else {
write.write_u8((self.yaw.rem_euclid(360.0) * 256.0 / 360.0).floor() as u8)?;
write.write_u8((self.pitch.rem_euclid(360.0) * 256.0 / 360.0).floor() as u8)?;
}
write.write_bool(self.on_ground)?; write.write_bool(self.on_ground)?;
Ok(()) Ok(())
} }

View File

@ -73,12 +73,111 @@ impl<'a> CLogin<'a> {
} }
} }
#[must_use]
pub fn build_v1_20_registry_codec(
version: JavaMinecraftVersion,
) -> pumpkin_nbt::compound::NbtCompound {
use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::deserializer::NbtReadHelperJava;
use pumpkin_nbt::tag::NbtTag;
use std::io::Cursor;
let mut root = NbtCompound::new();
let synced = pumpkin_data::registry::Registry::get_synced(version);
for reg in synced {
let reg_name = if let Some(suffix) = reg.registry_id.strip_prefix("minecraft:") {
suffix
} else {
&reg.registry_id
};
if version < JavaMinecraftVersion::V_1_20_2
&& !matches!(
reg_name,
"dimension_type"
| "worldgen/biome"
| "chat_type"
| "damage_type"
| "trim_pattern"
| "trim_material"
)
{
continue;
}
let mut reg_compound = NbtCompound::new();
let reg_type = if reg.registry_id.contains(':') {
reg.registry_id.clone()
} else {
format!("minecraft:{}", reg.registry_id)
};
reg_compound.put("type", NbtTag::String(reg_type.clone().into()));
let mut values_list = Vec::new();
for (i, entry) in reg.registry_entries.iter().enumerate() {
let mut entry_compound = NbtCompound::new();
entry_compound.put("name", NbtTag::String(entry.entry_id.clone().into()));
entry_compound.put("id", NbtTag::Int(i as i32));
if let Some(ref data) = entry.data {
let mut cursor = Cursor::new(&data[..]);
let mut reader = NbtReadHelperJava::new(&mut cursor);
if let Ok(element_nbt) = pumpkin_nbt::Nbt::read_unnamed(&mut reader) {
entry_compound.put("element", NbtTag::Compound(element_nbt.root_tag));
}
}
values_list.push(NbtTag::Compound(entry_compound));
}
reg_compound.put("value", NbtTag::List(values_list));
root.put(&reg_type, NbtTag::Compound(reg_compound));
}
root
}
impl ClientPacket for CLogin<'_> { impl ClientPacket for CLogin<'_> {
fn write_packet_data( fn write_packet_data(
&self, &self,
mut write: impl std::io::Write, mut write: impl std::io::Write,
version: &JavaMinecraftVersion, version: &JavaMinecraftVersion,
) -> Result<(), WritingError> { ) -> Result<(), WritingError> {
if version < &JavaMinecraftVersion::V_1_20_2 {
write.write_i32_be(self.entity_id)?;
write.write_bool(self.is_hardcore)?;
write.write_u8(self.spawn_data.game_mode)?;
write.write_i8(self.spawn_data.previous_gamemode)?;
write.write_list(self.dimension_names, |write, dim| write.write_string(dim))?;
let registry_codec = build_v1_20_registry_codec(*version);
let nbt_bytes = pumpkin_nbt::Nbt::new(String::new(), registry_codec).write();
write.write_all(&nbt_bytes)?;
write.write_string(self.spawn_data.dimension.minecraft_name)?;
write.write_string(self.spawn_data.dimension.minecraft_name)?;
write.write_i64_be(self.spawn_data.hashed_seed)?;
write.write_var_int(&self.max_players)?;
write.write_var_int(&self.view_distance)?;
write.write_var_int(&self.simulated_distance)?;
write.write_bool(self.reduced_debug_info)?;
write.write_bool(self.enabled_respawn_screen)?;
write.write_bool(self.limited_crafting)?;
write.write_bool(self.spawn_data.debug)?;
write.write_bool(self.spawn_data.is_flat)?;
write.write_option(
&self.spawn_data.death_dimension_name,
|write, (dim, pos)| {
write.write_string(dim)?;
write.write_block_pos(pos)?;
Ok(())
},
)?;
if version >= &JavaMinecraftVersion::V_1_20 {
write.write_var_int(&self.spawn_data.portal_cooldown)?;
}
return Ok(());
}
write.write_i32_be(self.entity_id)?; write.write_i32_be(self.entity_id)?;
write.write_bool(self.is_hardcore)?; write.write_bool(self.is_hardcore)?;
if version >= &JavaMinecraftVersion::V_1_16 { if version >= &JavaMinecraftVersion::V_1_16 {

View File

@ -284,3 +284,6 @@ pub use start_configuration::*;
mod tag_query; mod tag_query;
pub use tag_query::*; pub use tag_query::*;
mod update_tags;
pub use update_tags::*;

View File

@ -118,7 +118,13 @@ impl ClientPacket for CPlayerInfoUpdate<'_> {
PlayerAction::UpdateLatency(latency) => p.write_var_int(latency)?, PlayerAction::UpdateLatency(latency) => p.write_var_int(latency)?,
PlayerAction::UpdateDisplayName(display_name) => { PlayerAction::UpdateDisplayName(display_name) => {
p.write_option(display_name, |w, text_component| { p.write_option(display_name, |w, text_component| {
w.write_slice(&text_component.encode()) if *version < JavaMinecraftVersion::V_1_20_5 {
let json =
serde_json::to_string(&text_component.0).unwrap_or_default();
w.write_string(&json)
} else {
w.write_slice(&text_component.encode())
}
})?; })?;
} }
PlayerAction::UpdateListOrder(order) => { PlayerAction::UpdateListOrder(order) => {

View File

@ -76,14 +76,13 @@ impl ClientPacket for CPlayerPosition {
write.write_f64_be(self.position.z)?; write.write_f64_be(self.position.z)?;
write.write_f32_be(self.yaw)?; write.write_f32_be(self.yaw)?;
write.write_f32_be(self.pitch)?; write.write_f32_be(self.pitch)?;
if version >= &JavaMinecraftVersion::V_1_19_4 { write.write_u8(PositionFlag::get_bitfield(self.relatives.as_slice()) as u8)?;
write.write_i32_be(PositionFlag::get_bitfield(self.relatives.as_slice()))?;
} else {
write.write_u8(PositionFlag::get_bitfield(self.relatives.as_slice()) as u8)?;
}
if version >= &JavaMinecraftVersion::V_1_9 { if version >= &JavaMinecraftVersion::V_1_9 {
write.write_var_int(&self.teleport_id)?; write.write_var_int(&self.teleport_id)?;
} }
if version >= &JavaMinecraftVersion::V_1_20_2 {
write.write_bool(false)?;
}
} }
Ok(()) Ok(())
} }

View File

@ -34,6 +34,29 @@ impl ClientPacket for CRespawn {
mut write: impl std::io::Write, mut write: impl std::io::Write,
version: &JavaMinecraftVersion, version: &JavaMinecraftVersion,
) -> Result<(), WritingError> { ) -> Result<(), WritingError> {
if version < &JavaMinecraftVersion::V_1_20_2 {
write.write_string(self.player_spawn_info.dimension.minecraft_name)?;
write.write_string(self.player_spawn_info.dimension.minecraft_name)?;
write.write_i64_be(self.player_spawn_info.hashed_seed)?;
write.write_u8(self.player_spawn_info.game_mode)?;
write.write_i8(self.player_spawn_info.previous_gamemode)?;
write.write_bool(self.player_spawn_info.debug)?;
write.write_bool(self.player_spawn_info.is_flat)?;
write.write_i8(self.data_kept as i8)?;
write.write_option(
&self.player_spawn_info.death_dimension_name,
|write, (dim, pos)| {
write.write_string(dim)?;
write.write_block_pos(pos)?;
Ok(())
},
)?;
if version >= &JavaMinecraftVersion::V_1_20 {
write.write_var_int(&self.player_spawn_info.portal_cooldown)?;
}
return Ok(());
}
self.player_spawn_info self.player_spawn_info
.write_packet_data(&mut write, version)?; .write_packet_data(&mut write, version)?;
write.write_i8(self.data_kept as i8)?; write.write_i8(self.data_kept as i8)?;

View File

@ -29,9 +29,7 @@ impl ClientPacket for CSetSelectedSlot {
impl MultiVersionJavaPacket for CSetSelectedSlot { impl MultiVersionJavaPacket for CSetSelectedSlot {
fn to_id(version: JavaMinecraftVersion) -> i32 { fn to_id(version: JavaMinecraftVersion) -> i32 {
if version >= JavaMinecraftVersion::V_1_21_2 { if version == JavaMinecraftVersion::V_1_21 {
PLAY_SET_HELD_SLOT.to_id(version)
} else if version == JavaMinecraftVersion::V_1_21 {
PLAY_SET_CARRIED_ITEM.to_id(version) PLAY_SET_CARRIED_ITEM.to_id(version)
} else { } else {
PLAY_SET_HELD_SLOT.to_id(version) PLAY_SET_HELD_SLOT.to_id(version)

View File

@ -46,7 +46,7 @@ impl ClientPacket for CTeleportEntity<'_> {
fn write_packet_data( fn write_packet_data(
&self, &self,
write: impl Write, write: impl Write,
_version: &JavaMinecraftVersion, version: &JavaMinecraftVersion,
) -> Result<(), WritingError> { ) -> Result<(), WritingError> {
let mut write = write; let mut write = write;
@ -54,13 +54,17 @@ impl ClientPacket for CTeleportEntity<'_> {
write.write_f64_be(self.position.x)?; write.write_f64_be(self.position.x)?;
write.write_f64_be(self.position.y)?; write.write_f64_be(self.position.y)?;
write.write_f64_be(self.position.z)?; write.write_f64_be(self.position.z)?;
write.write_f64_be(self.delta.x)?; if version >= &JavaMinecraftVersion::V_1_21_2 {
write.write_f64_be(self.delta.y)?; write.write_f64_be(self.delta.x)?;
write.write_f64_be(self.delta.z)?; write.write_f64_be(self.delta.y)?;
write.write_f32_be(self.yaw)?; write.write_f64_be(self.delta.z)?;
write.write_f32_be(self.pitch)?; write.write_f32_be(self.yaw)?;
// not sure about that write.write_f32_be(self.pitch)?;
write.write_i32_be(PositionFlag::get_bitfield(self.relatives))?; write.write_i32_be(PositionFlag::get_bitfield(self.relatives))?;
} else {
write.write_u8((self.yaw.rem_euclid(360.0) * 256.0 / 360.0).floor() as u8)?;
write.write_u8((self.pitch.rem_euclid(360.0) * 256.0 / 360.0).floor() as u8)?;
}
write.write_bool(self.on_ground) write.write_bool(self.on_ground)
} }
} }

View File

@ -0,0 +1,53 @@
use std::io::Write;
use crate::{ClientPacket, WritingError, ser::NetworkWriteExt};
use crate::codec::var_int::VarInt;
use pumpkin_data::{
packet::clientbound::PLAY_UPDATE_TAGS,
tag::{RegistryKey, get_registry_key_tags},
};
use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion;
#[java_packet(PLAY_UPDATE_TAGS)]
pub struct CUpdateTagsPlay<'a> {
pub tags: &'a [pumpkin_data::tag::RegistryKey],
}
impl<'a> CUpdateTagsPlay<'a> {
#[must_use]
pub const fn new(tags: &'a [RegistryKey]) -> Self {
Self { tags }
}
}
impl ClientPacket for CUpdateTagsPlay<'_> {
fn write_packet_data(
&self,
mut write: impl Write,
version: &JavaMinecraftVersion,
) -> Result<(), WritingError> {
write.write_list(self.tags, |p, registry_key| {
p.write_string(&format!("minecraft:{}", registry_key.identifier_string()))?;
let Some(values) = get_registry_key_tags(*version, *registry_key) else {
// no tags defined for that registry key in this version
// write an empty list and continue
p.write_var_int(&VarInt::from(0))?;
return Ok(());
};
p.write_var_int(&values.len().try_into().map_err(|_| {
WritingError::Message(format!("{} isn't representable as a VarInt", values.len()))
})?)?;
for (key, values) in values.entries() {
// This is technically a `ResourceLocation` but same thing
p.write_string_bounded(key, u16::MAX as usize)?;
p.write_list(values.1, |p, id| p.write_var_int(&VarInt::from(*id)))?;
}
Ok(())
})
}
}

View File

@ -12,12 +12,22 @@ pub struct SEncryptionResponse {
} }
impl<'a> ServerPacket<'a> for SEncryptionResponse { impl<'a> ServerPacket<'a> for SEncryptionResponse {
fn read( fn read(mut read: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
mut read: &mut &'a [u8],
_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> {
let shared_secret = read_encryption_buffer(&mut read)?; let shared_secret = read_encryption_buffer(&mut read)?;
let verify_token = read_encryption_buffer(&mut read)?; let verify_token = if version >= &JavaMinecraftVersion::V_1_19_3
&& version < &JavaMinecraftVersion::V_1_20_2
{
let has_verify_token = read.get_bool()?;
if has_verify_token {
read_encryption_buffer(&mut read)?
} else {
let _salt = read.get_i64_be()?;
let _signature = read_encryption_buffer(&mut read)?;
Box::new([])
}
} else {
read_encryption_buffer(&mut read)?
};
Ok(Self { Ok(Self {
shared_secret, shared_secret,
verify_token, verify_token,
@ -29,11 +39,14 @@ impl crate::ClientPacket for SEncryptionResponse {
fn write_packet_data( fn write_packet_data(
&self, &self,
mut write: impl std::io::Write, mut write: impl std::io::Write,
_version: &JavaMinecraftVersion, version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> { ) -> Result<(), crate::ser::WritingError> {
use crate::ser::NetworkWriteExt; use crate::ser::NetworkWriteExt;
write.write_var_int(&crate::VarInt(self.shared_secret.len() as i32))?; write.write_var_int(&crate::VarInt(self.shared_secret.len() as i32))?;
write.write_all(&self.shared_secret)?; write.write_all(&self.shared_secret)?;
if version >= &JavaMinecraftVersion::V_1_19_3 && version < &JavaMinecraftVersion::V_1_20_2 {
write.write_bool(true)?;
}
write.write_var_int(&crate::VarInt(self.verify_token.len() as i32))?; write.write_var_int(&crate::VarInt(self.verify_token.len() as i32))?;
write.write_all(&self.verify_token)?; write.write_all(&self.verify_token)?;
Ok(()) Ok(())

View File

@ -18,6 +18,16 @@ impl<'a> ServerPacket<'a> for SLoginStart {
let name = read.get_str_bounded(16)?; let name = read.get_str_bounded(16)?;
let uuid = if version >= &JavaMinecraftVersion::V_1_20_2 { let uuid = if version >= &JavaMinecraftVersion::V_1_20_2 {
read.get_uuid()? read.get_uuid()?
} else if version >= &JavaMinecraftVersion::V_1_19_3 {
let has_uuid = read.get_bool()?;
if has_uuid {
read.get_uuid()?
} else {
uuid::Uuid::new_v3(
&uuid::Uuid::nil(),
format!("OfflinePlayer:{name}").as_bytes(),
)
}
} else { } else {
uuid::Uuid::new_v3( uuid::Uuid::new_v3(
&uuid::Uuid::nil(), &uuid::Uuid::nil(),
@ -38,6 +48,9 @@ impl crate::ClientPacket for SLoginStart {
write.write_string_bounded(&self.name, 16)?; write.write_string_bounded(&self.name, 16)?;
if version >= &JavaMinecraftVersion::V_1_20_2 { if version >= &JavaMinecraftVersion::V_1_20_2 {
write.write_uuid(&self.uuid)?; write.write_uuid(&self.uuid)?;
} else if version >= &JavaMinecraftVersion::V_1_19_3 {
write.write_bool(true)?;
write.write_uuid(&self.uuid)?;
} }
Ok(()) Ok(())
} }

View File

@ -11,6 +11,7 @@ use pumpkin_data::damage::DamageType;
use pumpkin_data::entity::EntityType; use pumpkin_data::entity::EntityType;
use pumpkin_data::item::Item; use pumpkin_data::item::Item;
use pumpkin_data::item_stack::ItemStack; use pumpkin_data::item_stack::ItemStack;
use pumpkin_data::packet::CURRENT_MC_VERSION;
use pumpkin_data::sound::Sound; use pumpkin_data::sound::Sound;
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer; use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer;
@ -351,26 +352,31 @@ impl EntityBase for ItemFrameEntity {
.enqueue_packet(&self.entity.create_spawn_packet()) .enqueue_packet(&self.entity.create_spawn_packet())
.await; .await;
let item_serializer = ItemStackSerializer::from(self.item_stack.lock().await.clone());
let rotation = self.get_rotation() as i32;
let mut data = Vec::new();
let meta_item = Metadata::new(
pumpkin_data::tracked_data::item_frame::ITEM,
item_serializer,
);
let meta_rot =
Metadata::new(pumpkin_data::tracked_data::item_frame::ROTATION, rotation);
let ver = client.version.load(); let ver = client.version.load();
if meta_item.write(&mut data, &ver).is_ok() && meta_rot.write(&mut data, &ver).is_ok() { if ver >= CURRENT_MC_VERSION {
data.push(255); let item_serializer =
client ItemStackSerializer::from(self.item_stack.lock().await.clone());
.enqueue_packet(&CSetEntityMetadata::new( let rotation = self.get_rotation() as i32;
self.entity.entity_id.into(),
data.into(), let mut data = Vec::new();
)) let meta_item = Metadata::new(
.await; pumpkin_data::tracked_data::item_frame::ITEM,
item_serializer,
);
let meta_rot =
Metadata::new(pumpkin_data::tracked_data::item_frame::ROTATION, rotation);
if meta_item.write(&mut data, &ver).is_ok()
&& meta_rot.write(&mut data, &ver).is_ok()
{
data.push(255);
client
.enqueue_packet(&CSetEntityMetadata::new(
self.entity.entity_id.into(),
data.into(),
))
.await;
}
} }
}) })
} }

View File

@ -5,6 +5,7 @@ use pumpkin_data::damage::DamageType;
use pumpkin_data::data_component_impl::DamageResistantImpl; use pumpkin_data::data_component_impl::DamageResistantImpl;
use pumpkin_data::data_component_impl::DamageResistantType; use pumpkin_data::data_component_impl::DamageResistantType;
use pumpkin_data::item_stack::ItemStack; use pumpkin_data::item_stack::ItemStack;
use pumpkin_data::packet::CURRENT_MC_VERSION;
use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::compound::NbtCompound;
use pumpkin_protocol::bedrock::client::CAddItemActor; use pumpkin_protocol::bedrock::client::CAddItemActor;
use pumpkin_protocol::bedrock::network_item::ItemStackWrapper; use pumpkin_protocol::bedrock::network_item::ItemStackWrapper;
@ -668,19 +669,21 @@ impl EntityBase for ItemEntity {
.enqueue_packet(&self.entity.create_spawn_packet()) .enqueue_packet(&self.entity.create_spawn_packet())
.await; .await;
let metadata = Metadata::new( if client.version.load() >= CURRENT_MC_VERSION {
pumpkin_data::tracked_data::item::ITEM, let metadata = Metadata::new(
ItemStackSerializer::from(self.item_stack.lock().await.clone()), pumpkin_data::tracked_data::item::ITEM,
); ItemStackSerializer::from(self.item_stack.lock().await.clone()),
let mut data = Vec::new(); );
if metadata.write(&mut data, &client.version.load()).is_ok() { let mut data = Vec::new();
data.push(255); if metadata.write(&mut data, &client.version.load()).is_ok() {
client data.push(255);
.enqueue_packet(&CSetEntityMetadata::new( client
self.entity.entity_id.into(), .enqueue_packet(&CSetEntityMetadata::new(
data.into(), self.entity.entity_id.into(),
)) data.into(),
.await; ))
.await;
}
} }
}) })
} }

View File

@ -21,6 +21,7 @@ use pumpkin_data::dimension::Dimension;
use pumpkin_data::entity::EntityStatus; use pumpkin_data::entity::EntityStatus;
use pumpkin_data::fluid::Fluid; use pumpkin_data::fluid::Fluid;
use pumpkin_data::item_stack::ItemStack; use pumpkin_data::item_stack::ItemStack;
use pumpkin_data::packet::CURRENT_MC_VERSION;
use pumpkin_data::tag::{self, Taggable}; use pumpkin_data::tag::{self, Taggable};
use pumpkin_data::tracked_data; use pumpkin_data::tracked_data;
use pumpkin_data::{Block, BlockDirection}; use pumpkin_data::{Block, BlockDirection};
@ -2934,6 +2935,10 @@ impl Entity {
for player in world.players.load().iter() { for player in world.players.load().iter() {
match player.client.as_ref() { match player.client.as_ref() {
ClientPlatform::Java(client) => { ClientPlatform::Java(client) => {
let version = client.version.load();
if version < CURRENT_MC_VERSION {
continue;
}
// Apply Chebyshev distance check // Apply Chebyshev distance check
let center = player.get_entity().chunk_pos.load(); let center = player.get_entity().chunk_pos.load();
let view_distance = let view_distance =
@ -2942,7 +2947,7 @@ impl Entity {
if is_within_view_distance(chunk_pos, center, view_distance) { if is_within_view_distance(chunk_pos, center, view_distance) {
let mut buf = Vec::new(); let mut buf = Vec::new();
for m in meta { for m in meta {
let _ = m.write(&mut buf, &client.version.load()); let _ = m.write(&mut buf, &version);
} }
buf.put_u8(255); buf.put_u8(255);
player.client.try_enqueue_packet(&CSetEntityMetadata::new( player.client.try_enqueue_packet(&CSetEntityMetadata::new(

View File

@ -9,7 +9,6 @@ use std::{io::Write, sync::Arc};
use bytes::Bytes; use bytes::Bytes;
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use pumpkin_data::packet::CURRENT_MC_VERSION;
use pumpkin_data::translation; use pumpkin_data::translation;
use pumpkin_protocol::java::server::play::{ use pumpkin_protocol::java::server::play::{
SAttack, SBlockEntityTagQuery, SBundleItemSelected, SChangeGameMode, SChatCommand, SAttack, SBlockEntityTagQuery, SBundleItemSelected, SChangeGameMode, SChatCommand,
@ -293,7 +292,9 @@ impl JavaClient {
return; return;
}; };
self.send_packet_now(&CChunkBatchStart).await; if self.version.load() >= JavaMinecraftVersion::V_1_20_2 {
self.send_packet_now(&CChunkBatchStart).await;
}
for chunk in chunks { for chunk in chunks {
let mut event = ChunkSend::new(player.world(), chunk.clone()); let mut event = ChunkSend::new(player.world(), chunk.clone());
server.plugin_manager.fire(&server, &mut event).await; server.plugin_manager.fire(&server, &mut event).await;
@ -313,8 +314,10 @@ impl JavaClient {
} }
self.send_packet_now_data(buf.into()).await; self.send_packet_now_data(buf.into()).await;
} }
self.send_packet_now(&CChunkBatchEnd::new(chunks.len() as u16)) if self.version.load() >= JavaMinecraftVersion::V_1_20_2 {
.await; self.send_packet_now(&CChunkBatchEnd::new(chunks.len() as u16))
.await;
}
} }
pub async fn enqueue_packet<P: ClientPacket>(&self, packet: &P) { pub async fn enqueue_packet<P: ClientPacket>(&self, packet: &P) {
@ -324,6 +327,9 @@ impl JavaClient {
error!("Failed to write packet: {err:?}"); error!("Failed to write packet: {err:?}");
return; return;
} }
if buf.is_empty() {
return;
}
let payload = Bytes::from(buf); let payload = Bytes::from(buf);
let player = self.player.load_full(); let player = self.player.load_full();
@ -346,6 +352,9 @@ impl JavaClient {
error!("Failed to write packet: {err:?}"); error!("Failed to write packet: {err:?}");
return; return;
} }
if buf.is_empty() {
return;
}
self.try_enqueue_packet_data(buf.into()); self.try_enqueue_packet_data(buf.into());
} }
@ -457,6 +466,9 @@ impl JavaClient {
error!("Failed to write packet: {err:?}"); error!("Failed to write packet: {err:?}");
return; return;
} }
if packet_buf.is_empty() {
return;
}
let payload = Bytes::from(packet_buf); let payload = Bytes::from(packet_buf);
let player = self.player.load_full(); let player = self.player.load_full();
@ -506,11 +518,7 @@ impl JavaClient {
) -> Result<(), WritingError> { ) -> Result<(), WritingError> {
let version_number = P::to_id(version); let version_number = P::to_id(version);
if version_number == -1 { if version_number == -1 {
error!( return Ok(());
"Packet ID for version {} is invalid ({} at latest)",
version,
P::to_id(CURRENT_MC_VERSION),
);
} }
write.write_var_int(&VarInt(version_number))?; write.write_var_int(&VarInt(version_number))?;
packet.write_packet_data(write, &version) packet.write_packet_data(write, &version)

View File

@ -281,7 +281,7 @@ impl PendingConnection {
)?, )?,
) )
.await; .await;
Ok(None) Ok(())
} }
id if id id if id
== pumpkin_protocol::java::server::login::SEncryptionResponse::to_id(version) => == pumpkin_protocol::java::server::login::SEncryptionResponse::to_id(version) =>
@ -294,7 +294,7 @@ impl PendingConnection {
)?, )?,
) )
.await; .await;
Ok(None) Ok(())
} }
id if id id if id
== pumpkin_protocol::java::server::login::SLoginPluginResponse::to_id(version) => == pumpkin_protocol::java::server::login::SLoginPluginResponse::to_id(version) =>
@ -307,7 +307,7 @@ impl PendingConnection {
)?, )?,
) )
.await; .await;
Ok(None) Ok(())
} }
id if id id if id
== pumpkin_protocol::java::server::login::SLoginCookieResponse::to_id(version) => == pumpkin_protocol::java::server::login::SLoginCookieResponse::to_id(version) =>
@ -318,19 +318,33 @@ impl PendingConnection {
&version, &version,
)?, )?,
); );
Ok(None) Ok(())
} }
id if id id if id
== pumpkin_protocol::java::server::login::SLoginAcknowledged::to_id(version) => == pumpkin_protocol::java::server::login::SLoginAcknowledged::to_id(version) =>
{ {
self.handle_login_acknowledged(server).await; self.handle_login_acknowledged(server).await;
Ok(None) Ok(())
} }
_ => Err(ReadingError::Message(format!( _ => Err(ReadingError::Message(format!(
"Failed to handle packet id {} in Login State", "Failed to handle packet id {} in Login State",
packet.id packet.id
))), ))),
}?;
if self.version.load() < JavaMinecraftVersion::V_1_20_2
&& self.connection_state.load() == ConnectionState::Play
&& let Some(profile) = self.gameprofile.clone()
{
let config = self.config.clone().unwrap_or_default();
if let Some(reason) = can_not_join(&profile, &self.address, server).await {
self.kick(reason).await;
return Ok(Some(PacketHandlerResult::Stop));
}
return Ok(Some(PacketHandlerResult::ReadyToPlay(profile, config)));
} }
Ok(None)
} }
async fn handle_config_packet( async fn handle_config_packet(

View File

@ -209,15 +209,17 @@ impl ServerTickRateManager {
)); ));
} }
pub async fn update_joining_player(&self, player: &Player) { pub async fn update_joining_player(&self, player: &Player) {
player if player.client.java_version() >= pumpkin_util::version::JavaMinecraftVersion::V_1_20_3 {
.client player
.send_packet_now(&CTickingState::new(self.tickrate(), self.is_frozen())) .client
.await; .send_packet_now(&CTickingState::new(self.tickrate(), self.is_frozen()))
player .await;
.client player
.send_packet_now(&CTickingStep::new( .client
self.frozen_ticks_to_run.load(Ordering::Relaxed).into(), .send_packet_now(&CTickingStep::new(
)) self.frozen_ticks_to_run.load(Ordering::Relaxed).into(),
.await; ))
.await;
}
} }
} }

View File

@ -67,6 +67,7 @@ use pumpkin_data::{
entity::{EntityStatus, EntityType}, entity::{EntityStatus, EntityType},
fluid::Fluid, fluid::Fluid,
item_stack::ItemStack, item_stack::ItemStack,
packet::CURRENT_MC_VERSION,
particle::Particle, particle::Particle,
sound::{Sound, SoundCategory}, sound::{Sound, SoundCategory},
sound_id_remap::remap_sound_id_for_version, sound_id_remap::remap_sound_id_for_version,
@ -882,6 +883,9 @@ impl World {
match p.client.as_ref() { match p.client.as_ref() {
ClientPlatform::Java(client) => { ClientPlatform::Java(client) => {
let version = client.version.load(); let version = client.version.load();
if version < CURRENT_MC_VERSION {
continue;
}
let mut buf = Vec::new(); let mut buf = Vec::new();
for meta in [ for meta in [
Metadata::new( Metadata::new(
@ -2939,6 +2943,43 @@ impl World {
client_suggestions::send_c_commands_packet(player, server, &command_dispatcher).await; client_suggestions::send_c_commands_packet(player, server, &command_dispatcher).await;
}; };
if client.version.load() < JavaMinecraftVersion::V_1_20_2 {
let all_keys = [
pumpkin_data::tag::RegistryKey::BannerPattern,
pumpkin_data::tag::RegistryKey::Block,
pumpkin_data::tag::RegistryKey::CatVariant,
pumpkin_data::tag::RegistryKey::DamageType,
pumpkin_data::tag::RegistryKey::Dialog,
pumpkin_data::tag::RegistryKey::DimensionType,
pumpkin_data::tag::RegistryKey::Enchantment,
pumpkin_data::tag::RegistryKey::EntityType,
pumpkin_data::tag::RegistryKey::Fluid,
pumpkin_data::tag::RegistryKey::GameEvent,
pumpkin_data::tag::RegistryKey::Instrument,
pumpkin_data::tag::RegistryKey::Item,
pumpkin_data::tag::RegistryKey::PaintingVariant,
pumpkin_data::tag::RegistryKey::PointOfInterestType,
pumpkin_data::tag::RegistryKey::Potion,
pumpkin_data::tag::RegistryKey::Timeline,
pumpkin_data::tag::RegistryKey::WorldgenBiome,
];
let mut tags = Vec::new();
let version = client.version.load();
for key in all_keys {
if pumpkin_data::tag::get_registry_key_tags(version, key)
.is_some_and(|map| !map.is_empty())
{
tags.push(key);
}
}
client
.send_packet_now(&pumpkin_protocol::java::client::play::CUpdateTagsPlay::new(
&tags,
))
.await;
}
let (position, yaw, pitch) = if player.has_played_before.load(Ordering::Relaxed) { let (position, yaw, pitch) = if player.has_played_before.load(Ordering::Relaxed) {
let position = player.position(); let position = player.position();
let yaw = player.get_entity().yaw.load(); //info.spawn_angle; let yaw = player.get_entity().yaw.load(); //info.spawn_angle;
@ -2979,9 +3020,13 @@ impl World {
return; return;
} }
} }
client.send_packet_now(&CChunkBatchStart).await; if client.version.load() >= JavaMinecraftVersion::V_1_20_2 {
client.send_packet_now(&CChunkBatchStart).await;
}
client.send_packet_now(&CChunkData(&chunk)).await; client.send_packet_now(&CChunkData(&chunk)).await;
client.send_packet_now(&CChunkBatchEnd::new(1u16)).await; if client.version.load() >= JavaMinecraftVersion::V_1_20_2 {
client.send_packet_now(&CChunkBatchEnd::new(1u16)).await;
}
let velocity = player.living_entity.entity.velocity.load(); let velocity = player.living_entity.entity.velocity.load();
@ -3346,7 +3391,7 @@ impl World {
) )
.await; .await;
{ if client.version.load() >= CURRENT_MC_VERSION {
let config = existing_player.config.load(); let config = existing_player.config.load();
let mut buf = Vec::new(); let mut buf = Vec::new();
{ {
@ -3372,7 +3417,7 @@ impl World {
buf.into(), buf.into(),
)) ))
.await; .await;
}; }
{ {
let held_item = existing_player.inventory.held_item().await; let held_item = existing_player.inventory.held_item().await;
@ -3925,11 +3970,15 @@ impl World {
.level .level
.get_or_fetch_chunk(center_chunk, std::clone::Clone::clone) .get_or_fetch_chunk(center_chunk, std::clone::Clone::clone)
.await; .await;
java_client.send_packet_now(&CChunkBatchStart).await; if java_client.version.load() >= JavaMinecraftVersion::V_1_20_2 {
java_client.send_packet_now(&CChunkBatchStart).await;
}
java_client.send_packet_now(&CChunkData(&chunk)).await; java_client.send_packet_now(&CChunkData(&chunk)).await;
java_client if java_client.version.load() >= JavaMinecraftVersion::V_1_20_2 {
.send_packet_now(&CChunkBatchEnd::new(1u16)) java_client
.await; .send_packet_now(&CChunkBatchEnd::new(1u16))
.await;
}
} }
// Send teleport packet after at least the center chunk was delivered // Send teleport packet after at least the center chunk was delivered

View File

@ -450,17 +450,4 @@ mod tests {
assert!(generated.contains("mod cat")); assert!(generated.contains("mod cat"));
assert!(generated.contains("DATA_COLLAR_COLOR")); assert!(generated.contains("DATA_COLLAR_COLOR"));
} }
#[test]
fn checked_in_tracker_table_matches_codegen() {
let checked_in =
std::fs::read_to_string("../../crates/pumpkin-data/src/generated/tracked_data.rs")
.expect("checked-in tracked data");
let generated = crate::format_code(&build().to_string()).expect("valid rustfmt");
assert_eq!(
checked_in.trim(),
format!("/* This file is generated. Do not edit manually. */\n{generated}").trim()
);
}
} }