mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Make block_id macro
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2001,6 +2001,7 @@ dependencies = [
|
||||
name = "pumpkin-macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"itertools 0.13.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
|
||||
@@ -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"
|
||||
|
||||
136
pumpkin-macros/src/block_id.rs
Normal file
136
pumpkin-macros/src/block_id.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
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<String>,
|
||||
}
|
||||
|
||||
/// 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<String, String>,
|
||||
}
|
||||
|
||||
/// 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<RegistryBlockState>,
|
||||
|
||||
// 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<String, Vec<String>>,
|
||||
}
|
||||
|
||||
static BLOCKS: LazyLock<HashMap<String, RegistryBlockType>> = 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::<syn::Expr, syn::Token![,]>::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()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user