Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
kralverde committed Feb 16, 2025
1 parent 5a77b21 commit 9dcde3f
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 37 deletions.
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

0 comments on commit 9dcde3f

Please sign in to comment.