From d81acb0ab1d9c8de0fe1cbbe17df64bd809288a4 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Fri, 23 Jan 2026 15:17:56 +0100 Subject: [PATCH] fix: bedrock edition and port it to 1.21.30 --- .../src/bedrock/client/creative_content.rs | 2 +- .../client/network_chunk_publisher_update.rs | 10 +- .../src/bedrock/client/resource_pack_stack.rs | 8 +- .../src/bedrock/client/resource_packs_info.rs | 43 +-- .../src/bedrock/client/start_game.rs | 5 +- .../src/bedrock/packet_decoder.rs | 4 +- pumpkin-protocol/src/bedrock/server/text.rs | 246 ++++++++++++------ pumpkin-world/src/lib.rs | 2 +- pumpkin/src/entity/player.rs | 23 +- pumpkin/src/net/bedrock/login.rs | 12 +- pumpkin/src/net/bedrock/mod.rs | 1 - pumpkin/src/world/mod.rs | 72 +++-- 12 files changed, 235 insertions(+), 193 deletions(-) diff --git a/pumpkin-protocol/src/bedrock/client/creative_content.rs b/pumpkin-protocol/src/bedrock/client/creative_content.rs index e83f0a629..5a75594a5 100644 --- a/pumpkin-protocol/src/bedrock/client/creative_content.rs +++ b/pumpkin-protocol/src/bedrock/client/creative_content.rs @@ -29,7 +29,7 @@ impl PacketWrite for CreativeContent<'_> { } #[repr(i32)] -#[expect(unused)] +#[allow(unused)] enum CreativeCategory { Construction = 1, Nature = 2, diff --git a/pumpkin-protocol/src/bedrock/client/network_chunk_publisher_update.rs b/pumpkin-protocol/src/bedrock/client/network_chunk_publisher_update.rs index 36a0ac11b..dffd4ccf6 100644 --- a/pumpkin-protocol/src/bedrock/client/network_chunk_publisher_update.rs +++ b/pumpkin-protocol/src/bedrock/client/network_chunk_publisher_update.rs @@ -4,14 +4,14 @@ use pumpkin_macros::packet; use pumpkin_util::math::{position::BlockPos, vector2::Vector2}; use crate::{ - codec::{bedrock_block_pos::NetworkPos, var_int::VarInt, var_uint::VarUInt}, + codec::{var_int::VarInt, var_uint::VarUInt}, serial::PacketWrite, }; #[packet(121)] pub struct CNetworkChunkPublisherUpdate { // https://mojang.github.io/bedrock-protocol-docs/html/NetworkChunkPublisherUpdatePacket.html - pub pos_for_view: NetworkPos, + pub pos_for_view: BlockPos, // Is in blocks, not chunks! pub new_radius: VarUInt, // TODO @@ -21,8 +21,8 @@ pub struct CNetworkChunkPublisherUpdate { impl CNetworkChunkPublisherUpdate { pub fn new(pos_for_view: BlockPos, new_radius: u32) -> Self { Self { - pos_for_view: NetworkPos(pos_for_view), - new_radius: VarUInt(new_radius), + pos_for_view, + new_radius: VarUInt(new_radius << 4), server_build_chunk_list: Vec::new(), } } @@ -30,7 +30,7 @@ impl CNetworkChunkPublisherUpdate { impl PacketWrite for CNetworkChunkPublisherUpdate { fn write(&self, writer: &mut W) -> Result<(), Error> { - self.pos_for_view.write_signed(writer)?; + self.pos_for_view.write(writer)?; self.new_radius.write(writer)?; (self.server_build_chunk_list.len() as u32).write(writer)?; diff --git a/pumpkin-protocol/src/bedrock/client/resource_pack_stack.rs b/pumpkin-protocol/src/bedrock/client/resource_pack_stack.rs index f90265774..321c5e2c7 100644 --- a/pumpkin-protocol/src/bedrock/client/resource_pack_stack.rs +++ b/pumpkin-protocol/src/bedrock/client/resource_pack_stack.rs @@ -6,10 +6,10 @@ use crate::{ #[derive(PacketWrite)] #[packet(7)] -/// https://mojang.github.io/bedrock-protocol-docs/html/ResourcePackStackPacket.html pub struct CResourcePackStackPacket { + // https://mojang.github.io/bedrock-protocol-docs/html/ResourcePackStackPacket.html resource_pack_required: bool, - addons_list_size: VarUInt, + texture_pack_list_size: VarUInt, game_version: String, experiments: Experiments, /// When connecting to an Editor world, include the vanilla editor packs in the stack @@ -19,14 +19,14 @@ pub struct CResourcePackStackPacket { impl CResourcePackStackPacket { pub fn new( resource_pack_required: bool, - addons_list_size: VarUInt, + texture_pack_list_size: VarUInt, game_version: String, experiments: Experiments, include_editor_packs: bool, ) -> Self { Self { resource_pack_required, - addons_list_size, + texture_pack_list_size, game_version, experiments, include_editor_packs, diff --git a/pumpkin-protocol/src/bedrock/client/resource_packs_info.rs b/pumpkin-protocol/src/bedrock/client/resource_packs_info.rs index d4c40a61e..b26031931 100644 --- a/pumpkin-protocol/src/bedrock/client/resource_packs_info.rs +++ b/pumpkin-protocol/src/bedrock/client/resource_packs_info.rs @@ -1,10 +1,9 @@ -use std::io::{Error, Write}; - -use crate::serial::PacketWrite; use pumpkin_macros::packet; +use crate::serial::PacketWrite; + +#[derive(PacketWrite)] #[packet(6)] -/// https://mojang.github.io/bedrock-protocol-docs/html/ResourcePacksInfoPacket.html pub struct CResourcePacksInfo { resource_pack_required: bool, has_addon_packs: bool, @@ -12,36 +11,7 @@ pub struct CResourcePacksInfo { is_vibrant_visuals_force_disabled: bool, world_template_id: uuid::Uuid, world_template_version: String, - resource_packs_size: u16, - resource_packs: Vec, -} - -impl PacketWrite for CResourcePacksInfo { - fn write(&self, writer: &mut W) -> Result<(), Error> { - self.resource_pack_required.write(writer)?; - self.has_addon_packs.write(writer)?; - self.has_scripts.write(writer)?; - self.is_vibrant_visuals_force_disabled.write(writer)?; - self.world_template_id.write(writer)?; - self.world_template_version.write(writer)?; - self.resource_packs_size.write(writer)?; - self.resource_packs.write(writer)?; - Ok(()) - } -} - -#[derive(PacketWrite)] -pub struct ResourcePack { - pack_id: uuid::Uuid, - version: String, - size: u64, - content_key: String, - subpack_name: String, - content_identity: String, - has_scripts: bool, - is_addon_pack: bool, - is_raytracing_capable: bool, - cdn_url: String, + resource_packs_size: u16, // TODO: Add more } impl CResourcePacksInfo { @@ -52,7 +22,6 @@ impl CResourcePacksInfo { is_vibrant_visuals_force_disabled: bool, world_template_id: uuid::Uuid, world_template_version: String, - resource_packs: Vec, ) -> Self { Self { resource_pack_required, @@ -61,8 +30,8 @@ impl CResourcePacksInfo { is_vibrant_visuals_force_disabled, world_template_id, world_template_version, - resource_packs_size: resource_packs.len() as u16, - resource_packs, + // TODO + resource_packs_size: 0, } } } diff --git a/pumpkin-protocol/src/bedrock/client/start_game.rs b/pumpkin-protocol/src/bedrock/client/start_game.rs index f61fd62e7..b771a1479 100644 --- a/pumpkin-protocol/src/bedrock/client/start_game.rs +++ b/pumpkin-protocol/src/bedrock/client/start_game.rs @@ -34,7 +34,6 @@ pub struct CStartGame { pub current_level_time: u64, pub enchantment_seed: VarInt, - pub block_properties_size: VarUInt, //pub block_properties: [GG; 2], pub multiplayer_correlation_id: String, @@ -51,8 +50,7 @@ pub struct CStartGame { pub enable_clientside_generation: bool, pub blocknetwork_ids_are_hashed: bool, - // NetworkPermissions - pub disable_client_sounds: bool, + pub server_auth_sounds: bool, } #[derive(PacketWrite)] @@ -115,7 +113,6 @@ pub struct LevelSettings { pub new_nether: bool, pub edu_shared_uri_button_name: String, pub edu_shared_uri_link_uri: String, - pub override_force_experimental_gameplay_has_value: bool, pub chat_restriction_level: i8, pub disable_player_interactions: bool, diff --git a/pumpkin-protocol/src/bedrock/packet_decoder.rs b/pumpkin-protocol/src/bedrock/packet_decoder.rs index 9756b68e2..6b2980c04 100644 --- a/pumpkin-protocol/src/bedrock/packet_decoder.rs +++ b/pumpkin-protocol/src/bedrock/packet_decoder.rs @@ -134,7 +134,7 @@ impl UDPNetworkDecoder { err => PacketDecodeError::MalformedLength(err.to_string()), })?; - let packet_len = packet_len.0 as usize; + let packet_len = packet_len.0 as u64; let var_header = VarUInt::decode(&mut reader)?; @@ -157,7 +157,7 @@ impl UDPNetworkDecoder { let gamepacket_id = (header & 0x3FF) as u16; // 0x3FF is 10 bits set to 1 let payload = reader - .read_boxed_slice(packet_len - var_header.written_size()) + .read_boxed_slice(packet_len as usize - var_header.written_size()) .map_err(|err| PacketDecodeError::FailedDecompression(err.to_string()))?; Ok(RawPacket { diff --git a/pumpkin-protocol/src/bedrock/server/text.rs b/pumpkin-protocol/src/bedrock/server/text.rs index fcb2ab456..1fa35efca 100644 --- a/pumpkin-protocol/src/bedrock/server/text.rs +++ b/pumpkin-protocol/src/bedrock/server/text.rs @@ -1,97 +1,157 @@ -use std::io::{Error, Read, Write}; - -use pumpkin_macros::packet; - use crate::{ codec::var_uint::VarUInt, serial::{PacketRead, PacketWrite}, }; +use pumpkin_macros::packet; +use std::io::{Error, ErrorKind, Read, Write}; #[derive(Debug)] #[packet(9)] pub struct SText { - // https://mojang.github.io/bedrock-protocol-docs/html/TextPacket.html + pub needs_translation: bool, pub r#type: TextPacketType, - pub localize: bool, - pub player_name: String, + pub source_name: String, pub message: String, pub parameters: Vec, - pub sender_xuid: String, - pub platform_id: String, - pub filtered_message: String, + pub xuid: String, + pub platform_chat_id: String, + pub filtered_message: Option, } impl SText { - pub fn new(message: String, player_name: String) -> Self { + pub fn new(message: String, source_name: String) -> Self { Self { + needs_translation: false, r#type: TextPacketType::Chat, - localize: false, - player_name, - message, + source_name, + message: if message.is_empty() { + " ".to_string() + } else { + message + }, parameters: Vec::new(), - sender_xuid: String::new(), - platform_id: String::new(), - filtered_message: String::new(), + xuid: String::new(), + platform_chat_id: String::new(), + filtered_message: None, } } pub fn system_message(message: String) -> Self { Self { - r#type: TextPacketType::SystemMessage, - localize: false, - player_name: String::new(), - message, + needs_translation: false, + r#type: TextPacketType::System, + source_name: String::new(), + message: if message.is_empty() { + " ".to_string() + } else { + message + }, parameters: Vec::new(), - sender_xuid: String::new(), - platform_id: String::new(), - filtered_message: String::new(), + xuid: String::new(), + platform_chat_id: String::new(), + filtered_message: None, + } + } + + fn get_dummy_strings(category: u8) -> &'static [&'static str] { + match category { + 0 => &[ + "raw", + "tip", + "systemMessage", + "textObjectWhisper", + "textObjectAnnouncement", + "textObject", + ], + 1 => &["chat", "whisper", "announcement"], + 2 => &["translate", "popup", "jukeboxPopup"], + _ => &[], + } + } + + fn get_category(&self) -> u8 { + match self.r#type { + TextPacketType::Raw + | TextPacketType::Tip + | TextPacketType::System + | TextPacketType::JsonWhisper + | TextPacketType::JsonAnnouncement + | TextPacketType::Json => 0, // CATEGORY_MESSAGE_ONLY + + TextPacketType::Chat | TextPacketType::Whisper | TextPacketType::Announcement => 1, // CATEGORY_AUTHORED_MESSAGE + + TextPacketType::Translation | TextPacketType::Popup | TextPacketType::JukeboxPopup => 2, // CATEGORY_MESSAGE_WITH_PARAMETERS } } } impl PacketRead for SText { fn read(reader: &mut R) -> Result { - let r#type = TextPacketType::read(reader)?; - let localize = bool::read(reader)?; + let needs_translation = bool::read(reader)?; - let mut player_name = String::new(); - let message; + let category = u8::read(reader)?; + + let expected_dummies = Self::get_dummy_strings(category); + for &expected in expected_dummies { + let actual = String::read(reader)?; + if actual != expected { + return Err(Error::new( + ErrorKind::InvalidData, + format!( + "Dummy string mismatch: expected {}, got {}", + expected, actual + ), + )); + } + } + + let r#type = TextPacketType::read(reader)?; + + let mut source_name = String::new(); + #[expect(unused)] + let mut message = String::new(); let mut parameters = Vec::new(); match r#type { + TextPacketType::Chat | TextPacketType::Whisper | TextPacketType::Announcement => { + source_name = String::read(reader)?; + message = String::read(reader)?; + } TextPacketType::Raw | TextPacketType::Tip - | TextPacketType::SystemMessage - | TextPacketType::TextObjectWhisper - | TextPacketType::TextObject - | TextPacketType::TextObjectAnnouncement => { + | TextPacketType::System + | TextPacketType::JsonWhisper + | TextPacketType::Json + | TextPacketType::JsonAnnouncement => { message = String::read(reader)?; } - TextPacketType::Chat | TextPacketType::Whisper | TextPacketType::Announcement => { - player_name = String::read(reader)?; - message = String::read(reader)?; - } - TextPacketType::Translate | TextPacketType::Popup | TextPacketType::JukeboxPopup => { + TextPacketType::Translation | TextPacketType::Popup | TextPacketType::JukeboxPopup => { message = String::read(reader)?; let count = VarUInt::read(reader)?.0 as usize; - parameters = Vec::with_capacity(count); for _ in 0..count { parameters.push(String::read(reader)?); } } } - let sender_xuid = String::read(reader)?; - let platform_id = String::read(reader)?; - let filtered_message = String::read(reader)?; + + let xuid = String::read(reader)?; + let platform_chat_id = String::read(reader)?; + + let has_filtered = bool::read(reader)?; + let filtered_message = if has_filtered { + Some(String::read(reader)?) + } else { + None + }; Ok(Self { + needs_translation, r#type, - localize, - player_name, + source_name, message, parameters, - sender_xuid, - platform_id, + xuid, + platform_chat_id, filtered_message, }) } @@ -99,22 +159,31 @@ impl PacketRead for SText { impl PacketWrite for SText { fn write(&self, writer: &mut W) -> Result<(), Error> { + self.needs_translation.write(writer)?; + + let category = self.get_category(); + category.write(writer)?; + + for &dummy in Self::get_dummy_strings(category) { + dummy.to_string().write(writer)?; + } + self.r#type.write(writer)?; - self.localize.write(writer)?; + match self.r#type { + TextPacketType::Chat | TextPacketType::Whisper | TextPacketType::Announcement => { + self.source_name.write(writer)?; + self.message.write(writer)?; + } TextPacketType::Raw | TextPacketType::Tip - | TextPacketType::SystemMessage - | TextPacketType::TextObjectWhisper - | TextPacketType::TextObject - | TextPacketType::TextObjectAnnouncement => { + | TextPacketType::System + | TextPacketType::JsonWhisper + | TextPacketType::Json + | TextPacketType::JsonAnnouncement => { self.message.write(writer)?; } - TextPacketType::Chat | TextPacketType::Whisper | TextPacketType::Announcement => { - self.player_name.write(writer)?; - self.message.write(writer)?; - } - TextPacketType::Translate | TextPacketType::Popup | TextPacketType::JukeboxPopup => { + TextPacketType::Translation | TextPacketType::Popup | TextPacketType::JukeboxPopup => { self.message.write(writer)?; VarUInt(self.parameters.len() as u32).write(writer)?; for param in &self.parameters { @@ -122,47 +191,36 @@ impl PacketWrite for SText { } } } - self.sender_xuid.write(writer)?; - self.platform_id.write(writer)?; - self.filtered_message.write(writer) + + self.xuid.write(writer)?; + self.platform_chat_id.write(writer)?; + + if let Some(msg) = &self.filtered_message { + true.write(writer)?; + msg.write(writer)?; + } else { + false.write(writer)?; + } + + Ok(()) } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] #[repr(u8)] pub enum TextPacketType { Raw = 0, Chat = 1, - Translate = 2, + Translation = 2, Popup = 3, JukeboxPopup = 4, Tip = 5, - SystemMessage = 6, + System = 6, Whisper = 7, Announcement = 8, - TextObjectWhisper = 9, - TextObject = 10, - TextObjectAnnouncement = 11, -} - -impl PacketRead for TextPacketType { - fn read(reader: &mut R) -> Result { - Ok(match u8::read(reader)? { - 0 => Self::Raw, - 1 => Self::Chat, - 2 => Self::Translate, - 3 => Self::Popup, - 4 => Self::JukeboxPopup, - 5 => Self::Tip, - 6 => Self::SystemMessage, - 7 => Self::Whisper, - 8 => Self::Announcement, - 9 => Self::TextObjectWhisper, - 10 => Self::TextObject, - 11 => Self::TextObjectAnnouncement, - _ => return Err(Error::new(std::io::ErrorKind::InvalidData, "")), - }) - } + JsonWhisper = 9, + Json = 10, + JsonAnnouncement = 11, } impl PacketWrite for TextPacketType { @@ -170,3 +228,23 @@ impl PacketWrite for TextPacketType { (*self as u8).write(writer) } } + +impl PacketRead for TextPacketType { + fn read(reader: &mut R) -> Result { + match u8::read(reader)? { + 0 => Ok(Self::Raw), + 1 => Ok(Self::Chat), + 2 => Ok(Self::Translation), + 3 => Ok(Self::Popup), + 4 => Ok(Self::JukeboxPopup), + 5 => Ok(Self::Tip), + 6 => Ok(Self::System), + 7 => Ok(Self::Whisper), + 8 => Ok(Self::Announcement), + 9 => Ok(Self::JsonWhisper), + 10 => Ok(Self::Json), + 11 => Ok(Self::JsonAnnouncement), + _ => Err(Error::new(ErrorKind::InvalidData, "Unknown Text Type")), + } + } +} diff --git a/pumpkin-world/src/lib.rs b/pumpkin-world/src/lib.rs index 0b59c99b3..199ddbedc 100644 --- a/pumpkin-world/src/lib.rs +++ b/pumpkin-world/src/lib.rs @@ -24,7 +24,7 @@ pub type BlockStateId = u16; pub const CURRENT_MC_VERSION: &str = "1.21.11"; pub const LOWEST_SUPPRORTED_PROTOCOL_VERSION: u32 = 773; -pub const CURRENT_BEDROCK_MC_VERSION: &str = "1.21.32"; +pub const CURRENT_BEDROCK_MC_VERSION: &str = "1.21.130"; pub const CURRENT_BEDROCK_MC_PROTOCOL: u32 = 898; #[macro_export] diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 0740315fb..6b1d1e5b8 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -1785,18 +1785,7 @@ impl Player { } pub async fn send_system_message(&self, text: &TextComponent) { - match &self.client { - ClientPlatform::Java(client) => { - client - .enqueue_packet(&CSystemChatMessage::new(text, false)) - .await; - } - ClientPlatform::Bedrock(client) => { - client - .send_game_packet(&SText::system_message(text.clone().get_text())) - .await; - } - } + self.send_system_message_raw(text, false).await; } pub async fn send_system_message_raw(&self, text: &TextComponent, overlay: bool) { @@ -2508,13 +2497,6 @@ impl EntityBase for Player { if self.abilities.lock().await.invulnerable && damage_type != DamageType::GENERIC_KILL { return false; } - let dyn_self = self - .living_entity - .entity - .world - .get_entity_by_id(self.living_entity.entity.entity_id) - .await - .expect("Entity not found in world"); let result = self .living_entity .damage_with_context(caller, amount, damage_type, position, source, cause) @@ -2523,8 +2505,7 @@ impl EntityBase for Player { let health = self.living_entity.health.load(); if health <= 0.0 { let death_message = - LivingEntity::get_death_message(&*dyn_self, damage_type, source, cause) - .await; + LivingEntity::get_death_message(caller, damage_type, source, cause).await; self.handle_killed(death_message).await; } } diff --git a/pumpkin/src/net/bedrock/login.rs b/pumpkin/src/net/bedrock/login.rs index 5ac92a84d..b09337a26 100644 --- a/pumpkin/src/net/bedrock/login.rs +++ b/pumpkin/src/net/bedrock/login.rs @@ -49,14 +49,12 @@ struct CertificateChainPayload { impl BedrockClient { pub async fn handle_request_network_settings(&self, _packet: SRequestNetworkSettings) { - log::debug!("Bedrock: Sending Network settings"); self.send_game_packet(&CNetworkSettings::new(0, 0, false, 0, 0.0)) .await; self.set_compression(CompressionInfo::default()).await; } pub async fn handle_login(self: &Arc, packet: SLogin, server: &Server) -> Option<()> { - log::debug!("Bedrock: handle login"); match self.try_handle_login(packet, server).await { Ok(()) => Some(()), Err(error) => { @@ -103,15 +101,7 @@ impl BedrockClient { self.write_game_packet_to_set(&CPlayStatus::LoginSuccess, &mut frame_set) .await; self.write_game_packet_to_set( - &CResourcePacksInfo::new( - false, - false, - false, - true, - uuid::Uuid::default(), - String::new(), - Vec::new(), - ), + &CResourcePacksInfo::new(false, false, false, false, Uuid::default(), String::new()), &mut frame_set, ) .await; diff --git a/pumpkin/src/net/bedrock/mod.rs b/pumpkin/src/net/bedrock/mod.rs index 4c66bd737..e8bf27aaf 100644 --- a/pumpkin/src/net/bedrock/mod.rs +++ b/pumpkin/src/net/bedrock/mod.rs @@ -484,7 +484,6 @@ impl BedrockClient { packet: RawPacket, ) -> Result<(), Error> { let payload = &mut Cursor::new(&packet.payload); - log::error!("packet id {}", packet.id); match packet.id { SRequestNetworkSettings::PACKET_ID => { self.handle_request_network_settings(SRequestNetworkSettings::read(payload)?) diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 45f0d1119..7dc139c46 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -1,3 +1,4 @@ +use std::num::NonZeroU32; use std::pin::Pin; use std::sync::Weak; use std::sync::atomic::Ordering::Relaxed; @@ -56,7 +57,10 @@ use pumpkin_data::{ use pumpkin_data::{BlockDirection, BlockState}; use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_nbt::{compound::NbtCompound, to_bytes_unnamed}; +use pumpkin_protocol::bedrock::client::chunk_radius_update::CChunkRadiusUpdate; +use pumpkin_protocol::bedrock::client::network_chunk_publisher_update::CNetworkChunkPublisherUpdate; use pumpkin_protocol::bedrock::client::start_game::CStartGame; +use pumpkin_protocol::bedrock::frame_set::FrameSet; use pumpkin_protocol::java::client::play::CPlayerSpawnPosition; use pumpkin_protocol::java::client::play::{CSetEntityMetadata, Metadata}; use pumpkin_protocol::ser::serializer::Serializer; @@ -1161,7 +1165,7 @@ impl World { self.dimension.min_y } - #[expect(clippy::too_many_lines)] + #[allow(clippy::too_many_lines)] pub async fn spawn_bedrock_player( &self, base_config: &BasicConfiguration, @@ -1249,15 +1253,15 @@ impl World { entity_id: VarLong(runtime_id as _), runtime_entity_id: VarULong(runtime_id), player_gamemode: player.gamemode.load(), - position: Vector3::new(0.0, 100.0, 0.0), + position: Vector3::new(0.0, 200.0, 0.0), pitch: 0.0, yaw: 0.0, level_settings, level_id: String::new(), - level_name: "World".to_string(), + level_name: "Pumpkin world".to_string(), premium_world_template_id: String::new(), is_trial: false, - rewind_history_size: VarInt(0), + rewind_history_size: VarInt(40), server_authoritative_block_breaking: false, current_level_time: self.level_time.lock().await.world_age as _, enchantment_seed: VarInt(0), @@ -1278,10 +1282,15 @@ impl World { // TODO The client needs extra biome data for this enable_clientside_generation: false, blocknetwork_ids_are_hashed: false, - disable_client_sounds: false, + server_auth_sounds: false, }) .await; - // chunker::update_position(&player).await; + client + .send_game_packet(&CChunkRadiusUpdate { + chunk_radius: VarInt(player.config.read().await.view_distance.get().into()), + }) + .await; + chunker::update_position(&player).await; client .send_game_packet(&CreativeContent { groups: &[Group { @@ -1293,29 +1302,48 @@ impl World { }) .await; + let mut frame_set = FrameSet::default(); client - .send_game_packet(&CUpdateAttributes { - runtime_id: VarULong(runtime_id), - attributes: vec![Attribute { - min_value: 0.0, - max_value: f32::MAX, - current_value: 0.1, - default_min_value: 0.0, - default_max_value: f32::MAX, - default_value: 0.1, - name: "minecraft:movement".to_string(), - modifiers_list_size: VarUInt(0), - }], - player_tick: VarULong(0), - }) + .write_game_packet_to_set( + &CNetworkChunkPublisherUpdate::new( + BlockPos::new(0, 200, 0), + NonZeroU32::from(player.config.read().await.view_distance).into(), + ), + &mut frame_set, + ) .await; - player.send_abilities_update().await; + + client + .write_game_packet_to_set( + &CUpdateAttributes { + runtime_id: VarULong(runtime_id), + attributes: vec![Attribute { + min_value: 0.0, + max_value: f32::MAX, + current_value: 0.1, + default_min_value: 0.0, + default_max_value: f32::MAX, + default_value: 0.1, + name: "minecraft:movement".to_string(), + modifiers_list_size: VarUInt(0), + }], + player_tick: VarULong(0), + }, + &mut frame_set, + ) + .await; + + client + .write_game_packet_to_set(&CPlayStatus::PlayerSpawn, &mut frame_set) + .await; + client.send_frame_set(frame_set, 0x84).await; + { let mut abilities = player.abilities.lock().await; abilities.set_for_gamemode(player.gamemode.load()); }; - client.send_game_packet(&CPlayStatus::PlayerSpawn).await; + player.send_abilities_update().await; } #[expect(clippy::too_many_lines)]