diff --git a/Cargo.lock b/Cargo.lock index fff734d23..c7f798fb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2001,8 +2001,11 @@ dependencies = [ name = "pumpkin-macros" version = "0.1.0" dependencies = [ + "itertools 0.13.0", "proc-macro2", "quote", + "serde", + "serde_json", "syn", ] diff --git a/Cargo.toml b/Cargo.toml index 3770fdc9d..8577de730 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,3 +41,5 @@ crossbeam = "0.8.4" uuid = { version = "1.10.0", features = ["serde", "v3", "v4"] } derive_more = { version = "1.0.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } + +itertools = "0.13.0" diff --git a/pumpkin-world/assets/blocks.json b/assets/blocks.json similarity index 100% rename from pumpkin-world/assets/blocks.json rename to assets/blocks.json diff --git a/pumpkin-world/assets/items.json b/assets/items.json similarity index 100% rename from pumpkin-world/assets/items.json rename to assets/items.json diff --git a/pumpkin-world/assets/registries.json b/assets/registries.json similarity index 100% rename from pumpkin-world/assets/registries.json rename to assets/registries.json diff --git a/pumpkin-inventory/Cargo.toml b/pumpkin-inventory/Cargo.toml index 66c4c427a..abeb7cfd1 100644 --- a/pumpkin-inventory/Cargo.toml +++ b/pumpkin-inventory/Cargo.toml @@ -10,6 +10,6 @@ pumpkin-world = { path = "../pumpkin-world" } num-traits = "0.2" num-derive = "0.4" thiserror = "1.0.63" -itertools = "0.13.0" +itertools.workspace = true parking_lot.workspace = true crossbeam.workspace = true diff --git a/pumpkin-macros/Cargo.toml b/pumpkin-macros/Cargo.toml index c677c7c01..5dca73b94 100644 --- a/pumpkin-macros/Cargo.toml +++ b/pumpkin-macros/Cargo.toml @@ -10,3 +10,6 @@ proc-macro = true proc-macro2 = "1.0" quote = "1.0" syn = "2.0" +serde.workspace = true +itertools.workspace = true +serde_json = "1.0.128" diff --git a/pumpkin-macros/src/block_id.rs b/pumpkin-macros/src/block_id.rs new file mode 100644 index 000000000..796eeca56 --- /dev/null +++ b/pumpkin-macros/src/block_id.rs @@ -0,0 +1,138 @@ +use std::{collections::HashMap, sync::LazyLock}; + +use itertools::Itertools; +use proc_macro::TokenStream; +use quote::quote; +use syn::parse::Parser; + +#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)] +struct RegistryBlockDefinition { + /// e.g. minecraft:door or minecraft:button + #[serde(rename = "type")] + pub category: String, + + /// Specifies the variant of the blocks category. + /// e.g. minecraft:iron_door has the variant iron + #[serde(rename = "block_set_type")] + pub variant: Option, +} + +/// One possible state of a Block. +/// This could e.g. be an extended piston facing left. +#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)] +struct RegistryBlockState { + pub id: i32, + + /// Whether this is the default state of the Block + #[serde(default, rename = "default")] + pub is_default: bool, + + /// The propertise active for this `BlockState`. + #[serde(default)] + pub properties: HashMap, +} + +/// A fully-fledged block definition. +/// Stores the category, variant, all of the possible states and all of the possible properties. +#[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)] +struct RegistryBlockType { + pub definition: RegistryBlockDefinition, + pub states: Vec, + + // TODO is this safe to remove? It's currently not used in the Project. @lukas0008 @Snowiiii + /// A list of valid property keys/values for a block. + #[serde(default, rename = "properties")] + valid_properties: HashMap>, +} + +static BLOCKS: LazyLock> = LazyLock::new(|| { + serde_json::from_str(include_str!("../../assets/blocks.json")) + .expect("Could not parse block.json registry.") +}); + +pub fn block_id_impl(item: TokenStream) -> TokenStream { + let data = syn::punctuated::Punctuated::::parse_terminated + .parse(item) + .unwrap(); + let block_name = data + .first() + .expect("The first argument should be a block name"); + + let block_name = match block_name { + syn::Expr::Lit(lit) => match &lit.lit { + syn::Lit::Str(name) => name.value(), + _ => panic!("The first argument should be a string"), + }, + _ => panic!("The first argument should be a string"), + }; + + let mut properties = HashMap::new(); + for expr_thingy in data.into_iter().skip(1) { + match expr_thingy { + syn::Expr::Assign(assign) => { + let left = match assign.left.as_ref() { + syn::Expr::Lit(lit) => match &lit.lit { + syn::Lit::Str(name) => name.value(), + _ => panic!( + "All not-first arguments should be assignments (\"foo\" = \"bar\")" + ), + }, + _ => { + panic!("All not-first arguments should be assignments (\"foo\" = \"bar\")") + } + }; + let right = match assign.right.as_ref() { + syn::Expr::Lit(lit) => match &lit.lit { + syn::Lit::Str(name) => name.value(), + _ => panic!( + "All not-first arguments should be assignments (\"foo\" = \"bar\")" + ), + }, + _ => { + panic!("All not-first arguments should be assignments (\"foo\" = \"bar\")") + } + }; + properties.insert(left, right); + } + _ => panic!("All not-first arguments should be assignments (\"foo\" = \"bar\")"), + } + } + + // panic!("{:?}", properties); + + let block_info = &BLOCKS + .get(&block_name) + .expect("Block with that name does not exist"); + + let id = if properties.is_empty() { + block_info + .states + .iter() + .find(|state| state.is_default) + .expect( + "Error inside blocks.json file: Every Block should have at least 1 default state", + ) + .id + } else { + match block_info + .states + .iter() + .find(|state| state.properties == properties) + { + Some(state) => state.id, + None => panic!( + "Could not find block with these properties, the following are valid properties: \n{}", + block_info + .valid_properties + .iter() + .map(|(name, values)| format!("{name} = {}", values.join(" | "))) + .join("\n") + ), + } + }; + + quote! { + pumpkin_world::block::block_id::BlockId::from_id(#id as u16) + } + .into() +} diff --git a/pumpkin-macros/src/lib.rs b/pumpkin-macros/src/lib.rs index 346159229..da53c14be 100644 --- a/pumpkin-macros/src/lib.rs +++ b/pumpkin-macros/src/lib.rs @@ -22,3 +22,9 @@ pub fn packet(input: TokenStream, item: TokenStream) -> TokenStream { gen.into() } + +mod block_id; +#[proc_macro] +pub fn block_id(item: TokenStream) -> TokenStream { + block_id::block_id_impl(item) +} diff --git a/pumpkin-protocol/Cargo.toml b/pumpkin-protocol/Cargo.toml index 32d0a92bd..3ae52b6eb 100644 --- a/pumpkin-protocol/Cargo.toml +++ b/pumpkin-protocol/Cargo.toml @@ -25,5 +25,5 @@ num-derive = "0.4" aes = "0.8.4" cfb8 = "0.8.1" -itertools = "0.13.0" +itertools.workspace = true fastnbt = { git = "https://github.com/owengage/fastnbt.git" } diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index 44ebeddaf..43d335c13 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -10,7 +10,7 @@ fastnbt = { git = "https://github.com/owengage/fastnbt.git" } tokio.workspace = true rayon.workspace = true derive_more.workspace = true -itertools = "0.13.0" +itertools.workspace = true thiserror = "1.0" futures = "0.3" flate2 = "1.0" diff --git a/pumpkin-world/src/block/block_registry.rs b/pumpkin-world/src/block/block_registry.rs index 7d7b3f69f..cd06e98de 100644 --- a/pumpkin-world/src/block/block_registry.rs +++ b/pumpkin-world/src/block/block_registry.rs @@ -5,7 +5,7 @@ use serde::Deserialize; use super::block_id::BlockId; pub static BLOCKS: LazyLock> = LazyLock::new(|| { - serde_json::from_str(include_str!("../../assets/blocks.json")) + serde_json::from_str(include_str!("../../../assets/blocks.json")) .expect("Could not parse block.json registry.") }); diff --git a/pumpkin-world/src/global_registry.rs b/pumpkin-world/src/global_registry.rs index 19f8a6e6f..f822b57ab 100644 --- a/pumpkin-world/src/global_registry.rs +++ b/pumpkin-world/src/global_registry.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::LazyLock}; pub const ITEM_REGISTRY: &str = "minecraft:item"; -const REGISTRY_JSON: &str = include_str!("../assets/registries.json"); +const REGISTRY_JSON: &str = include_str!("../../assets/registries.json"); #[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)] pub struct RegistryElement { diff --git a/pumpkin-world/src/item/item_registry.rs b/pumpkin-world/src/item/item_registry.rs index f91dd300c..756c30b4e 100644 --- a/pumpkin-world/src/item/item_registry.rs +++ b/pumpkin-world/src/item/item_registry.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, sync::LazyLock}; use super::Rarity; use crate::global_registry::{self, ITEM_REGISTRY}; -const ITEMS_JSON: &str = include_str!("../../assets/items.json"); +const ITEMS_JSON: &str = include_str!("../../../assets/items.json"); pub static ITEMS: LazyLock> = LazyLock::new(|| { serde_json::from_str(ITEMS_JSON).expect("Could not parse items.json registry.") diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index 931be7113..2b6a35929 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -19,7 +19,7 @@ pumpkin-entity = { path = "../pumpkin-entity" } pumpkin-protocol = { path = "../pumpkin-protocol" } pumpkin-registry = { path = "../pumpkin-registry" } -itertools = "0.13.0" +itertools.workspace = true # config serde.workspace = true