Skip to content

Commit

Permalink
Make block_id macro
Browse files Browse the repository at this point in the history
  • Loading branch information
lukas0008 committed Sep 18, 2024
1 parent ee7d652 commit 79c6730
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pumpkin-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 136 additions & 0 deletions pumpkin-macros/src/block_id.rs
Original file line number Diff line number Diff line change
@@ -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()
}
6 changes: 6 additions & 0 deletions pumpkin-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

0 comments on commit 79c6730

Please sign in to comment.