diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index 419cb7967..33be01e0b 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -319,7 +319,7 @@ impl LoadConfiguration for AdvancedConfiguration { } fn validate(&self) { - self.resource_pack.validate(); + //self.resource_pack.validate(); } } diff --git a/pumpkin-config/src/resource_pack.rs b/pumpkin-config/src/resource_pack.rs index 1aeab43c1..4480e672b 100644 --- a/pumpkin-config/src/resource_pack.rs +++ b/pumpkin-config/src/resource_pack.rs @@ -1,12 +1,17 @@ use serde::{Deserialize, Serialize}; +use uuid::Uuid; -/// Configuration for server resource pack distribution. -/// -/// Controls whether a resource pack is offered or enforced, -/// along with its metadata and client prompt behaviour. #[derive(Deserialize, Serialize, Default)] #[serde(default)] pub struct ResourcePackConfig { + pub java: JavaResourcePackConfig, + pub bedrock: BedrockResourcePackConfig, +} + +/// Java-specific resource pack configuration (Single URL/Hash) +#[derive(Deserialize, Serialize, Default)] +#[serde(default)] +pub struct JavaResourcePackConfig { /// Whether the resource pack system is enabled. pub enabled: bool, /// The URL to the resource pack. @@ -19,23 +24,33 @@ pub struct ResourcePackConfig { pub force: bool, } -impl ResourcePackConfig { - pub fn validate(&self) { - if !self.enabled { - return; - } +/// Bedrock-specific configuration (Supports multiple local/remote packs) +#[derive(Deserialize, Serialize, Default)] +#[serde(default)] +pub struct BedrockResourcePackConfig { + pub enabled: bool, + /// If true, players cannot join without accepting packs. + pub force: bool, + /// List of packs to be sent to the client. + pub packs: Vec, +} - assert_eq!( - !self.url.is_empty(), - !self.sha1.is_empty(), - "Resource pack path or SHA1 hash is missing" - ); - - let hash_len = self.sha1.len(); - - assert_eq!( - hash_len, 40, - "Resource pack SHA1 hash is the wrong length (should be 40, is {hash_len})" - ); - } +#[derive(Deserialize, Serialize)] +pub struct BedrockPack { + pub uuid: Uuid, + pub version: String, + pub size: u64, + pub download_url: String, + #[serde(default)] + pub content_key: String, + #[serde(default)] + pub sub_pack_name: String, + #[serde(default)] + pub content_id: String, + #[serde(default)] + pub has_scripts: bool, + #[serde(default)] + pub addon_pack: bool, + #[serde(default)] + pub rtx_enabled: bool, } diff --git a/pumpkin-protocol/src/bedrock/client/mod.rs b/pumpkin-protocol/src/bedrock/client/mod.rs index 2b0e514ff..293901eb8 100644 --- a/pumpkin-protocol/src/bedrock/client/mod.rs +++ b/pumpkin-protocol/src/bedrock/client/mod.rs @@ -25,4 +25,4 @@ pub mod set_time; pub mod set_title; pub mod start_game; pub mod update_abilities; -pub mod update_artributes; +pub mod update_attributes; diff --git a/pumpkin-protocol/src/bedrock/client/resource_pack_stack.rs b/pumpkin-protocol/src/bedrock/client/resource_pack_stack.rs index 224f5fe5a..ef29ca5c9 100644 --- a/pumpkin-protocol/src/bedrock/client/resource_pack_stack.rs +++ b/pumpkin-protocol/src/bedrock/client/resource_pack_stack.rs @@ -1,33 +1,35 @@ +use crate::{bedrock::client::start_game::Experiments, serial::PacketWrite}; use pumpkin_macros::packet; -use crate::{ - bedrock::client::start_game::Experiments, codec::var_uint::VarUInt, serial::PacketWrite, -}; +#[derive(PacketWrite)] +pub struct ResourcePackStackEntry { + pub uuid: String, + pub version: String, + pub sub_pack_name: String, +} #[derive(PacketWrite)] #[packet(7)] pub struct CResourcePackStackPacket { - // https://mojang.github.io/bedrock-protocol-docs/html/ResourcePackStackPacket.html - resource_pack_required: bool, - texture_pack_list_size: VarUInt, - game_version: String, - experiments: Experiments, - /// When connecting to an Editor world, include the vanilla editor packs in the stack - include_editor_packs: bool, + pub resource_pack_required: bool, + pub resource_packs: Vec, + pub game_version: String, + pub experiments: Experiments, + pub include_editor_packs: bool, } impl CResourcePackStackPacket { #[must_use] pub const fn new( resource_pack_required: bool, - texture_pack_list_size: VarUInt, + resource_packs: Vec, game_version: String, experiments: Experiments, include_editor_packs: bool, ) -> Self { Self { resource_pack_required, - texture_pack_list_size, + resource_packs, 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 099a9288c..d20f7e7f2 100644 --- a/pumpkin-protocol/src/bedrock/client/resource_packs_info.rs +++ b/pumpkin-protocol/src/bedrock/client/resource_packs_info.rs @@ -1,39 +1,62 @@ -use pumpkin_macros::packet; - use crate::serial::PacketWrite; - -#[derive(PacketWrite)] -#[packet(6)] -pub struct CResourcePacksInfo { - resource_pack_required: bool, - has_addon_packs: bool, - has_scripts: bool, - is_vibrant_visuals_force_disabled: bool, - world_template_id: uuid::Uuid, - world_template_version: String, - resource_packs_size: u16, // TODO: Add more +use pumpkin_macros::packet; +use std::io::{Error, Write}; +pub struct ResourcePackEntry { + pub uuid: String, + pub version: String, + pub size: u64, + pub content_key: String, + pub sub_pack_name: String, + pub content_id: String, + pub has_scripts: bool, + pub addon_pack: bool, + pub rtx_enabled: bool, + pub download_url: String, } -impl CResourcePacksInfo { - #[must_use] - #[expect(clippy::fn_params_excessive_bools)] - pub const fn new( - resource_pack_required: bool, - has_addon_packs: bool, - has_scripts: bool, - is_vibrant_visuals_force_disabled: bool, - world_template_id: uuid::Uuid, - world_template_version: String, - ) -> Self { - Self { - resource_pack_required, - has_addon_packs, - has_scripts, - is_vibrant_visuals_force_disabled, - world_template_id, - world_template_version, - // TODO - resource_packs_size: 0, +impl PacketWrite for ResourcePackEntry { + fn write(&self, writer: &mut W) -> Result<(), Error> { + self.uuid.write(writer)?; + self.version.write(writer)?; + // Bedrock uses Little Endian u64 for size + writer.write_all(&self.size.to_le_bytes())?; + self.content_key.write(writer)?; + self.sub_pack_name.write(writer)?; + self.content_id.write(writer)?; + self.has_scripts.write(writer)?; + Ok(()) + } +} + +#[packet(6)] +pub struct CResourcePacksInfo { + pub resource_pack_required: bool, + pub has_addon_packs: bool, + pub has_scripts: bool, + pub is_vibrant_visuals_force_disabled: bool, + pub world_template_id: uuid::Uuid, + pub world_template_version: String, + pub 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)?; + + let len = self.resource_packs.len() as u16; + writer.write_all(&len.to_le_bytes())?; + + for entry in &self.resource_packs { + entry.write(writer)?; } + + Ok(()) } } diff --git a/pumpkin-protocol/src/bedrock/client/update_artributes.rs b/pumpkin-protocol/src/bedrock/client/update_attributes.rs similarity index 100% rename from pumpkin-protocol/src/bedrock/client/update_artributes.rs rename to pumpkin-protocol/src/bedrock/client/update_attributes.rs diff --git a/pumpkin/src/entity/attributes.rs b/pumpkin/src/entity/attributes.rs index f91ef0669..be7b68025 100644 --- a/pumpkin/src/entity/attributes.rs +++ b/pumpkin/src/entity/attributes.rs @@ -94,7 +94,7 @@ pub async fn send_attribute_updates_for_living( living: &crate::entity::living::LivingEntity, attributes: Vec, ) { - use pumpkin_protocol::bedrock::client::update_artributes::{ + use pumpkin_protocol::bedrock::client::update_attributes::{ Attribute as BeAttribute, CUpdateAttributes as BePacket, }; use pumpkin_protocol::codec::var_int::VarInt; diff --git a/pumpkin/src/net/bedrock/login.rs b/pumpkin/src/net/bedrock/login.rs index 6b7d2038f..bd6060305 100644 --- a/pumpkin/src/net/bedrock/login.rs +++ b/pumpkin/src/net/bedrock/login.rs @@ -2,20 +2,18 @@ use crate::{ net::{ClientPlatform, DisconnectReason, GameProfile, PlayerConfig, bedrock::BedrockClient}, server::Server, }; -use pumpkin_protocol::bedrock::server::{ - login::ClientData, resource_pack_response::SResourcePackResponse, -}; -use pumpkin_protocol::{ - bedrock::{ - client::{ - network_settings::CNetworkSettings, play_status::CPlayStatus, - resource_pack_stack::CResourcePackStackPacket, resource_packs_info::CResourcePacksInfo, - start_game::Experiments, - }, - frame_set::FrameSet, - server::{login::SLogin, request_network_settings::SRequestNetworkSettings}, +use pumpkin_protocol::bedrock::{ + client::{ + network_settings::CNetworkSettings, play_status::CPlayStatus, + resource_pack_stack::CResourcePackStackPacket, resource_packs_info::CResourcePacksInfo, + start_game::Experiments, }, - codec::var_uint::VarUInt, + frame_set::FrameSet, + server::{login::SLogin, request_network_settings::SRequestNetworkSettings}, +}; +use pumpkin_protocol::bedrock::{ + client::{resource_pack_stack::ResourcePackStackEntry, resource_packs_info::ResourcePackEntry}, + server::{login::ClientData, resource_pack_response::SResourcePackResponse}, }; use pumpkin_util::jwt::AuthError; use pumpkin_world::{CURRENT_BEDROCK_MC_PROTOCOL, CURRENT_BEDROCK_MC_VERSION}; @@ -183,11 +181,37 @@ 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, false, Uuid::default(), String::new()), - &mut frame_set, - ) - .await; + let br_config = &server.advanced_config.resource_pack.bedrock; + + let mut entries = Vec::new(); + if br_config.enabled { + for pack in &br_config.packs { + entries.push(ResourcePackEntry { + uuid: pack.uuid.to_string(), + version: pack.version.clone(), + size: pack.size, + download_url: pack.download_url.clone(), + content_key: pack.content_key.clone(), + sub_pack_name: pack.sub_pack_name.clone(), + content_id: pack.content_id.clone(), + has_scripts: pack.has_scripts, + addon_pack: pack.addon_pack, + rtx_enabled: pack.rtx_enabled, + }); + } + } + + let packs_info = CResourcePacksInfo { + resource_pack_required: br_config.force, + has_addon_packs: false, + has_scripts: false, + is_vibrant_visuals_force_disabled: false, + world_template_id: uuid::Uuid::nil(), + world_template_version: String::new(), + resource_packs: entries, + }; + self.write_game_packet_to_set(&packs_info, &mut frame_set) + .await; self.send_frame_set(frame_set, 0x84).await; @@ -234,10 +258,27 @@ impl BedrockClient { debug!("Bedrock: SResourcePackResponse::STATUS_HAVE_ALL_PACKS"); let mut frame_set = FrameSet::default(); + let br_config = &server.advanced_config.resource_pack.bedrock; + + // Convert your config packs into protocol stack entries + let resource_packs = if br_config.enabled { + br_config + .packs + .iter() + .map(|pack| ResourcePackStackEntry { + uuid: pack.uuid.to_string(), + version: pack.version.clone(), + sub_pack_name: String::new(), + }) + .collect() + } else { + Vec::new() + }; + self.write_game_packet_to_set( &CResourcePackStackPacket::new( - false, - VarUInt(0), + br_config.force, + resource_packs, CURRENT_BEDROCK_MC_VERSION.to_string(), Experiments { names_size: 0, @@ -248,6 +289,7 @@ impl BedrockClient { &mut frame_set, ) .await; + self.send_frame_set(frame_set, 0x84).await; } SResourcePackResponse::STATUS_COMPLETED => { diff --git a/pumpkin/src/net/java/config.rs b/pumpkin/src/net/java/config.rs index a0b453f6c..3c190ed73 100644 --- a/pumpkin/src/net/java/config.rs +++ b/pumpkin/src/net/java/config.rs @@ -76,7 +76,7 @@ impl JavaClient { server: &Server, packet: SConfigResourcePack, ) { - let resource_config = &server.advanced_config.resource_pack; + let resource_config = &server.advanced_config.resource_pack.java; if resource_config.enabled { let expected_uuid = uuid::Uuid::new_v3(&uuid::Uuid::NAMESPACE_DNS, resource_config.url.as_bytes()); diff --git a/pumpkin/src/net/java/login.rs b/pumpkin/src/net/java/login.rs index 1bddcc6d6..1e9a6bc48 100644 --- a/pumpkin/src/net/java/login.rs +++ b/pumpkin/src/net/java/login.rs @@ -368,7 +368,7 @@ impl JavaClient { self.send_packet_now(&CConfigServerLinks::new(&links)).await; } - let resource_config = &server.advanced_config.resource_pack; + let resource_config = &server.advanced_config.resource_pack.java; if resource_config.enabled { let uuid = Uuid::new_v3(&uuid::Uuid::NAMESPACE_DNS, resource_config.url.as_bytes()); let resource_pack = CConfigAddResourcePack::new( diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index d59081843..40b95d965 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -80,7 +80,7 @@ use pumpkin_protocol::{ creative_content::{CreativeContent, Group}, gamerules_changed::GameRules, start_game::{Experiments, GamePublishSetting, LevelSettings}, - update_artributes::{Attribute, CUpdateAttributes}, + update_attributes::{Attribute, CUpdateAttributes}, }, network_item::NetworkItemDescriptor, server::text::SText,