Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Item Tool Components and Partially Implement Tag Updates #554

Merged
merged 5 commits into from
Feb 16, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions pumpkin-data/build/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
Expand Down
26 changes: 21 additions & 5 deletions pumpkin-data/src/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<HashMap<RegistryKey, HashMap<String, Vec<Option<String>>>>> =
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<Option<String>>> {
TAGS.get(&tag_category)
.expect("Should deserialize all tag categories")
Expand All @@ -38,15 +54,15 @@ pub fn get_tag_values(tag_category: RegistryKey, tag: &str) -> Option<&Vec<Optio

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum TagType {
Item(String),
Block(String),
Tag(String),
}

impl TagType {
pub fn get_values(&self, registry: RegistryKey) -> Vec<Option<String>> {
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),
}
}
}
Expand All @@ -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())),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion pumpkin-inventory/src/crafting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}

Expand Down
2 changes: 2 additions & 0 deletions pumpkin-protocol/src/client/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -19,3 +20,4 @@ pub use registry_data::*;
pub use server_links::*;
pub use store_cookie::*;
pub use transfer::*;
pub use update_tags::*;
49 changes: 49 additions & 0 deletions pumpkin-protocol/src/client/config/update_tags.rs
Original file line number Diff line number Diff line change
@@ -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));
}
});
}
});
}
}
27 changes: 15 additions & 12 deletions pumpkin-world/src/block/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,28 @@ pub static BLOCKS: LazyLock<TopLevel> = LazyLock::new(|| {
.expect("Could not parse blocks.json registry.")
});

pub static BLOCKS_BY_ID: LazyLock<HashMap<u16, Block>> = LazyLock::new(|| {
pub static BLOCKS_BY_ID: LazyLock<HashMap<u16, &'static Block>> = 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<HashMap<String, u16>> = LazyLock::new(|| {
pub static BLOCK_ID_BY_REGISTRY_ID: LazyLock<HashMap<&'static str, u16>> = 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<HashMap<u16, String>> = LazyLock::new(|| {
pub static STATE_ID_TO_REGISTRY_ID: LazyLock<HashMap<u16, &'static str>> = 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
});
Expand Down Expand Up @@ -63,12 +65,13 @@ pub static BLOCK_ID_BY_ITEM_ID: LazyLock<HashMap<u16, u16>> = LazyLock::new(|| {
});

pub fn get_block(registry_id: &str) -> Option<&Block> {
let id = BLOCK_ID_BY_REGISTRY_ID.get(&registry_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> {
Expand All @@ -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)> {
Expand All @@ -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<Vec<Shape>> {
Expand Down
26 changes: 17 additions & 9 deletions pumpkin-world/src/chunk/anvil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -366,7 +364,7 @@ impl ChunkWriter for AnvilChunkFormat {
}

impl AnvilChunkFormat {
pub fn to_bytes(&self, chunk_data: &ChunkData) -> Result<Vec<u8>, ChunkSerializingError> {
pub fn to_bytes(chunk_data: &ChunkData) -> Result<Vec<u8>, ChunkSerializingError> {
let mut sections = Vec::new();

for (i, blocks) in chunk_data.subchunks.array_iter().enumerate() {
Expand All @@ -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();
Expand Down Expand Up @@ -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(),
}),
Expand Down
4 changes: 2 additions & 2 deletions pumpkin-world/src/chunk/linear.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,8 @@ impl LinearFile {
at: &pumpkin_util::math::vector2::Vector2<i32>,
) -> 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;
Expand Down
2 changes: 1 addition & 1 deletion pumpkin-world/src/chunk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ pub enum Subchunk {
struct PaletteEntry {
// block name
name: String,
properties: Option<HashMap<String, String>>,
properties: Option<HashMap<String, Vec<String>>>,
}

#[derive(Deserialize, Serialize, Debug, Clone)]
Expand Down
10 changes: 9 additions & 1 deletion pumpkin/src/net/packet/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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",
Expand Down