diff --git a/pumpkin-data/build/item.rs b/pumpkin-data/build/item.rs index 52f7ed6f0..2d0197cab 100644 --- a/pumpkin-data/build/item.rs +++ b/pumpkin-data/build/item.rs @@ -94,14 +94,17 @@ impl ToTokens for ItemComponents { Some(tool) => { let rules_code = tool.rules.iter().map(|rule| { let mut block_array = Vec::new(); + + // TODO: According to the wiki, this can be a string or a list, + // I dont think there'll be any issues with always using a list, but we can + // probably save bandwidth doing single strings for reg in rule.blocks.get_values() { - for tag in reg.get_values(RegistryKey::Block) { - match tag { - Some(tag) => block_array.extend(quote! { #tag }), - None => continue, - } - } + let tag_string = reg.serialize(); + // The client knows what tags are, just send them the tag instead of all the + // blocks that is a part of the tag. + block_array.extend(quote! { #tag_string }); } + let speed = match rule.speed { Some(speed) => { quote! { Some(#speed) } diff --git a/pumpkin-data/src/tag.rs b/pumpkin-data/src/tag.rs index d4b645ae2..158f493d4 100644 --- a/pumpkin-data/src/tag.rs +++ b/pumpkin-data/src/tag.rs @@ -24,12 +24,28 @@ pub enum RegistryKey { GameEvent, } +impl RegistryKey { + // IDK why the linter is saying this isnt used + #[allow(dead_code)] + pub fn identifier_string(&self) -> &str { + match self { + Self::Block => "block", + Self::Item => "item", + Self::Fluid => "fluid", + Self::EntityType => "entity_type", + Self::GameEvent => "game_event", + _ => unimplemented!(), + } + } +} + const TAG_JSON: &str = include_str!("../../assets/tags.json"); #[expect(clippy::type_complexity)] pub static TAGS: LazyLock>>>> = LazyLock::new(|| serde_json::from_str(TAG_JSON).expect("Valid tag collections")); +#[allow(dead_code)] pub fn get_tag_values(tag_category: RegistryKey, tag: &str) -> Option<&Vec>> { TAGS.get(&tag_category) .expect("Should deserialize all tag categories") @@ -38,15 +54,15 @@ pub fn get_tag_values(tag_category: RegistryKey, tag: &str) -> Option<&Vec Vec> { + pub fn serialize(&self) -> String { match self { - TagType::Item(s) => vec![Some(s.clone())], - TagType::Tag(s) => get_tag_values(registry, s).unwrap().clone(), + TagType::Block(name) => name.clone(), + TagType::Tag(tag) => format!("#{}", tag), } } } @@ -63,7 +79,7 @@ impl Visitor<'_> for TagVisitor { { match v.strip_prefix('#') { Some(v) => Ok(TagType::Tag(v.to_string())), - None => Ok(TagType::Item(v.to_string())), + None => Ok(TagType::Block(v.to_string())), } } } diff --git a/pumpkin-inventory/src/crafting.rs b/pumpkin-inventory/src/crafting.rs index 0ab0dda0e..33a6e780a 100644 --- a/pumpkin-inventory/src/crafting.rs +++ b/pumpkin-inventory/src/crafting.rs @@ -18,7 +18,7 @@ fn check_ingredient_type(ingredient_type: &TagType, input: ItemStack) -> bool { false // items.iter().any(|tag| check_ingredient_type(&tag, input)) } - TagType::Item(item) => Item::from_name(item).is_some_and(|item| item.id == input.item.id), + TagType::Block(item) => Item::from_name(item).is_some_and(|item| item.id == input.item.id), } } diff --git a/pumpkin-protocol/src/client/config/mod.rs b/pumpkin-protocol/src/client/config/mod.rs index 73478b721..e9cfd4de5 100644 --- a/pumpkin-protocol/src/client/config/mod.rs +++ b/pumpkin-protocol/src/client/config/mod.rs @@ -8,6 +8,7 @@ mod registry_data; mod server_links; mod store_cookie; mod transfer; +mod update_tags; pub use add_resource_pack::*; pub use config_disconnect::*; @@ -19,3 +20,4 @@ pub use registry_data::*; pub use server_links::*; pub use store_cookie::*; pub use transfer::*; +pub use update_tags::*; diff --git a/pumpkin-protocol/src/client/config/update_tags.rs b/pumpkin-protocol/src/client/config/update_tags.rs new file mode 100644 index 000000000..15b559ea6 --- /dev/null +++ b/pumpkin-protocol/src/client/config/update_tags.rs @@ -0,0 +1,49 @@ +use bytes::BufMut; +use pumpkin_data::{ + packet::clientbound::CONFIG_UPDATE_TAGS, + tag::{RegistryKey, TAGS}, +}; +use pumpkin_macros::client_packet; +use pumpkin_world::block::registry; + +use crate::{ + bytebuf::ByteBufMut, + codec::{identifier::Identifier, var_int::VarInt}, + ClientPacket, +}; + +#[client_packet(CONFIG_UPDATE_TAGS)] +pub struct CUpdateTags<'a> { + tags: &'a [pumpkin_data::tag::RegistryKey], +} + +impl<'a> CUpdateTags<'a> { + pub fn new(tags: &'a [pumpkin_data::tag::RegistryKey]) -> Self { + Self { tags } + } +} + +impl ClientPacket for CUpdateTags<'_> { + fn write(&self, bytebuf: &mut impl BufMut) { + bytebuf.put_list(self.tags, |p, registry_key| { + p.put_identifier(&Identifier::vanilla(registry_key.identifier_string())); + + let values = TAGS.get(registry_key).unwrap(); + p.put_var_int(&VarInt::from(values.len() as i32)); + for (key, values) in values.iter() { + // This is technically a Identifier but same thing + p.put_string_len(key, u16::MAX as usize); + p.put_list(values, |p, v| { + if let Some(string_id) = v { + let id = match registry_key { + RegistryKey::Block => registry::get_block(string_id).unwrap().id, + _ => unimplemented!(), + }; + + p.put_var_int(&VarInt::from(id as i32)); + } + }); + } + }); + } +} diff --git a/pumpkin-world/src/block/registry.rs b/pumpkin-world/src/block/registry.rs index 5a3caa3b5..d92f38989 100644 --- a/pumpkin-world/src/block/registry.rs +++ b/pumpkin-world/src/block/registry.rs @@ -10,26 +10,28 @@ pub static BLOCKS: LazyLock = LazyLock::new(|| { .expect("Could not parse blocks.json registry.") }); -pub static BLOCKS_BY_ID: LazyLock> = LazyLock::new(|| { +pub static BLOCKS_BY_ID: LazyLock> = LazyLock::new(|| { let mut map = HashMap::new(); for block in &BLOCKS.blocks { - map.insert(block.id, block.clone()); + map.insert(block.id, block); } map }); -pub static BLOCK_ID_BY_REGISTRY_ID: LazyLock> = LazyLock::new(|| { +pub static BLOCK_ID_BY_REGISTRY_ID: LazyLock> = LazyLock::new(|| { let mut map = HashMap::new(); for block in &BLOCKS.blocks { - map.insert(block.name.clone(), block.id); + map.insert(block.name.as_str(), block.id); } map }); -pub static BLOCK_ID_TO_REGISTRY_ID: LazyLock> = LazyLock::new(|| { +pub static STATE_ID_TO_REGISTRY_ID: LazyLock> = LazyLock::new(|| { let mut map = HashMap::new(); - for block in &*BLOCKS.blocks { - map.insert(block.default_state_id, block.name.clone()); + for block in &BLOCKS.blocks { + for state in &block.states { + map.insert(state.id, block.name.as_str()); + } } map }); @@ -63,12 +65,13 @@ pub static BLOCK_ID_BY_ITEM_ID: LazyLock> = LazyLock::new(|| { }); pub fn get_block(registry_id: &str) -> Option<&Block> { - let id = BLOCK_ID_BY_REGISTRY_ID.get(®istry_id.replace("minecraft:", ""))?; - BLOCKS_BY_ID.get(id) + let key = registry_id.replace("minecraft:", ""); + let id = BLOCK_ID_BY_REGISTRY_ID.get(key.as_str())?; + BLOCKS_BY_ID.get(id).cloned() } pub fn get_block_by_id<'a>(id: u16) -> Option<&'a Block> { - BLOCKS_BY_ID.get(&id) + BLOCKS_BY_ID.get(&id).cloned() } pub fn get_state_by_state_id<'a>(id: u16) -> Option<&'a State> { @@ -77,7 +80,7 @@ pub fn get_state_by_state_id<'a>(id: u16) -> Option<&'a State> { pub fn get_block_by_state_id<'a>(id: u16) -> Option<&'a Block> { let block_id = BLOCK_ID_BY_STATE_ID.get(&id)?; - BLOCKS_BY_ID.get(block_id) + BLOCKS_BY_ID.get(block_id).cloned() } pub fn get_block_and_state_by_state_id<'a>(id: u16) -> Option<(&'a Block, &'a State)> { @@ -90,7 +93,7 @@ pub fn get_block_and_state_by_state_id<'a>(id: u16) -> Option<(&'a Block, &'a St pub fn get_block_by_item<'a>(item_id: u16) -> Option<&'a Block> { let block_id = BLOCK_ID_BY_ITEM_ID.get(&item_id)?; - BLOCKS_BY_ID.get(block_id) + BLOCKS_BY_ID.get(block_id).cloned() } pub fn get_block_collision_shapes(block_id: u16) -> Option> { diff --git a/pumpkin-world/src/chunk/anvil.rs b/pumpkin-world/src/chunk/anvil.rs index 43c853c70..f6a2d8cf7 100644 --- a/pumpkin-world/src/chunk/anvil.rs +++ b/pumpkin-world/src/chunk/anvil.rs @@ -11,9 +11,8 @@ use std::{ io::{Read, Seek, SeekFrom, Write}, }; -use crate::{ - block::registry::BLOCK_ID_TO_REGISTRY_ID, chunk::ChunkWritingError, level::LevelFolder, -}; +use crate::block::registry::STATE_ID_TO_REGISTRY_ID; +use crate::{chunk::ChunkWritingError, level::LevelFolder}; use super::{ ChunkData, ChunkNbt, ChunkReader, ChunkReadingError, ChunkSection, ChunkSectionBlockStates, @@ -248,8 +247,7 @@ impl ChunkWriter for AnvilChunkFormat { .map_err(|err| ChunkWritingError::IoError(err.kind()))?; // Serialize chunk data - let raw_bytes = self - .to_bytes(chunk_data) + let raw_bytes = Self::to_bytes(chunk_data) .map_err(|err| ChunkWritingError::ChunkSerializingError(err.to_string()))?; // Compress chunk data @@ -366,7 +364,7 @@ impl ChunkWriter for AnvilChunkFormat { } impl AnvilChunkFormat { - pub fn to_bytes(&self, chunk_data: &ChunkData) -> Result, ChunkSerializingError> { + pub fn to_bytes(chunk_data: &ChunkData) -> Result, ChunkSerializingError> { let mut sections = Vec::new(); for (i, blocks) in chunk_data.subchunks.array_iter().enumerate() { @@ -377,7 +375,7 @@ impl AnvilChunkFormat { .into_iter() .enumerate() .map(|(i, block)| { - let name = BLOCK_ID_TO_REGISTRY_ID.get(block).unwrap().as_str(); + let name = STATE_ID_TO_REGISTRY_ID.get(block).unwrap(); (block, (name, i)) }) .collect(); @@ -429,8 +427,18 @@ impl AnvilChunkFormat { palette: palette .into_iter() .map(|entry| PaletteEntry { - name: entry.1 .0.to_owned(), - properties: None, + name: entry.1 .0.to_string(), + properties: { + /* + let properties = &get_block(entry.1 .0).unwrap().properties; + let mut map = HashMap::new(); + for property in properties { + map.insert(property.name.to_string(), property.values.clone()); + } + Some(map) + */ + None + }, }) .collect(), }), diff --git a/pumpkin-world/src/chunk/linear.rs b/pumpkin-world/src/chunk/linear.rs index 30c386db2..91ee440fa 100644 --- a/pumpkin-world/src/chunk/linear.rs +++ b/pumpkin-world/src/chunk/linear.rs @@ -342,8 +342,8 @@ impl LinearFile { at: &pumpkin_util::math::vector2::Vector2, ) -> Result<(), ChunkSerializingError> { let chunk_index: usize = LinearChunkFormat::get_chunk_index(at); - let chunk_raw = AnvilChunkFormat {} //We use Anvil format to serialize the chunk - .to_bytes(chunk)?; + let chunk_raw = AnvilChunkFormat :: //We use Anvil format to serialize the chunk + to_bytes(chunk)?; let new_chunk_size = chunk_raw.len(); let old_chunk_size = self.chunks_headers[chunk_index].size as usize; diff --git a/pumpkin-world/src/chunk/mod.rs b/pumpkin-world/src/chunk/mod.rs index 7c2b1abbe..8d46abb85 100644 --- a/pumpkin-world/src/chunk/mod.rs +++ b/pumpkin-world/src/chunk/mod.rs @@ -161,7 +161,7 @@ pub enum Subchunk { struct PaletteEntry { // block name name: String, - properties: Option>, + properties: Option>>, } #[derive(Deserialize, Serialize, Debug, Clone)] diff --git a/pumpkin/src/net/packet/login.rs b/pumpkin/src/net/packet/login.rs index 8fb222323..24415720b 100644 --- a/pumpkin/src/net/packet/login.rs +++ b/pumpkin/src/net/packet/login.rs @@ -3,7 +3,7 @@ use std::sync::LazyLock; use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_protocol::{ client::{ - config::{CConfigAddResourcePack, CConfigServerLinks, CKnownPacks}, + config::{CConfigAddResourcePack, CConfigServerLinks, CKnownPacks, CUpdateTags}, login::{CLoginSuccess, CSetCompression}, }, codec::var_int::VarInt, @@ -352,6 +352,14 @@ impl Client { .await; } + // TODO: Is this the right place to send them? + // send tags + self.send_packet(&CUpdateTags::new(&[ + pumpkin_data::tag::RegistryKey::Block, + //pumpkin_data::tag::RegistryKey::Fluid, + ])) + .await; + // known data packs self.send_packet(&CKnownPacks::new(&[KnownPack { namespace: "minecraft",