Skip to content

Commit

Permalink
Merge pull request #106 from kralverde/block_id_tweaks
Browse files Browse the repository at this point in the history
Create Block, BlockCategory enums. Create block state struct.
  • Loading branch information
Snowiiii authored Oct 2, 2024
2 parents 0603060 + b7b975a commit 1726c06
Show file tree
Hide file tree
Showing 14 changed files with 285 additions and 97 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.

135 changes: 126 additions & 9 deletions pumpkin-macros/src/block_id.rs → pumpkin-macros/src/block_state.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::{collections::HashMap, sync::LazyLock};
use std::{
collections::{HashMap, HashSet},
sync::LazyLock,
};

use itertools::Itertools;
use proc_macro::TokenStream;
Expand Down Expand Up @@ -50,7 +53,108 @@ static BLOCKS: LazyLock<HashMap<String, RegistryBlockType>> = LazyLock::new(|| {
.expect("Could not parse block.json registry.")
});

pub fn block_id_impl(item: TokenStream) -> TokenStream {
fn pascal_case(original: &str) -> String {
let mut pascal = String::new();
let mut capitalize = true;
for ch in original.chars() {
if ch == '_' {
capitalize = true;
} else if capitalize {
pascal.push(ch.to_ascii_uppercase());
capitalize = false;
} else {
pascal.push(ch);
}
}
pascal
}

pub fn block_type_enum_impl() -> TokenStream {
let categories: &HashSet<&str> = &BLOCKS
.values()
.map(|val| val.definition.category.as_str())
.collect();

let original_and_converted_stream = categories.iter().map(|key| {
(
key,
pascal_case(key.split_once(':').expect("Bad minecraft id").1),
)
});
let new_names: proc_macro2::TokenStream = original_and_converted_stream
.clone()
.map(|(_, x)| x)
.join(",\n")
.parse()
.unwrap();

let from_string: proc_macro2::TokenStream = original_and_converted_stream
.clone()
.map(|(original, converted)| format!("\"{}\" => BlockCategory::{},", original, converted))
.join("\n")
.parse()
.unwrap();

// I;ve never used macros before so call me out on this lol
quote! {
#[derive(PartialEq, Clone)]
pub enum BlockCategory {
#new_names
}

impl BlockCategory {
pub fn from_registry_id(id: &str) -> BlockCategory {
match id {
#from_string
_ => panic!("Not a valid block type id"),
}
}
}
}
.into()
}

pub fn block_enum_impl() -> TokenStream {
let original_and_converted_stream = &BLOCKS.keys().map(|key| {
(
key,
pascal_case(key.split_once(':').expect("Bad minecraft id").1),
)
});
let new_names: proc_macro2::TokenStream = original_and_converted_stream
.clone()
.map(|(_, x)| x)
.join(",\n")
.parse()
.unwrap();

let from_string: proc_macro2::TokenStream = original_and_converted_stream
.clone()
.map(|(original, converted)| format!("\"{}\" => Block::{},", original, converted))
.join("\n")
.parse()
.unwrap();

// I;ve never used macros before so call me out on this lol
quote! {
#[derive(PartialEq, Clone)]
pub enum Block {
#new_names
}

impl Block {
pub fn from_registry_id(id: &str) -> Block {
match id {
#from_string
_ => panic!("Not a valid block id"),
}
}
}
}
.into()
}

pub fn block_state_impl(item: TokenStream) -> TokenStream {
let data = syn::punctuated::Punctuated::<syn::Expr, syn::Token![,]>::parse_terminated
.parse(item)
.unwrap();
Expand All @@ -61,9 +165,12 @@ pub fn block_id_impl(item: TokenStream) -> TokenStream {
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, have: {:?}", lit),
},
_ => panic!("The first argument should be a string"),
_ => panic!(
"The first argument should be a string, have: {:?}",
block_name
),
};

let mut properties = HashMap::new();
Expand Down Expand Up @@ -104,22 +211,21 @@ pub fn block_id_impl(item: TokenStream) -> TokenStream {
.get(&block_name)
.expect("Block with that name does not exist");

let id = if properties.is_empty() {
let state = 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,
Some(state) => state,
None => panic!(
"Could not find block with these properties, the following are valid properties: \n{}",
block_info
Expand All @@ -131,13 +237,24 @@ pub fn block_id_impl(item: TokenStream) -> TokenStream {
}
};

let id = state.id;
let category_name = block_info.definition.category.clone();

if std::env::var("CARGO_PKG_NAME").unwrap() == "pumpkin-world" {
quote! {
crate::block::block_id::BlockId::from_id(#id as u16)
crate::block::block_state::BlockState::new_unchecked(
#id as u16,
crate::block::Block::from_registry_id(#block_name),
crate::block::BlockCategory::from_registry_id(#category_name),
)
}
} else {
quote! {
pumpkin_world::block::block_id::BlockId::from_id(#id as u16)
pumpkin_world::block::block_id::BlockStateId::new_unchecked(
#id as u16,
pumpkin_world::block::Block::from_registry_id(#block_name),
pumpkin_world::block::BlockCategory::from_registry_id(#category_name),
)
}
}
.into()
Expand Down
18 changes: 15 additions & 3 deletions pumpkin-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,20 @@ pub fn packet(input: TokenStream, item: TokenStream) -> TokenStream {
gen.into()
}

mod block_id;
mod block_state;
#[proc_macro]
pub fn block_id(item: TokenStream) -> TokenStream {
block_id::block_id_impl(item)
pub fn block(item: TokenStream) -> TokenStream {
block_state::block_state_impl(item)
}

#[proc_macro]
/// Creates an enum for all block types. Should only be used once
pub fn blocks_enum(_item: TokenStream) -> TokenStream {
block_state::block_enum_impl()
}

#[proc_macro]
/// Creates an enum for all block categories. Should only be used once
pub fn block_categories_enum(_item: TokenStream) -> TokenStream {
block_state::block_type_enum_impl()
}
1 change: 1 addition & 0 deletions pumpkin-world/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition.workspace = true

[dependencies]
pumpkin-core = { path = "../pumpkin-core" }
pumpkin-macros = { path = "../pumpkin-macros" }

fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
tokio.workspace = true
Expand Down
57 changes: 0 additions & 57 deletions pumpkin-world/src/block/block_id.rs

This file was deleted.

33 changes: 32 additions & 1 deletion pumpkin-world/src/block/block_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ use std::{collections::HashMap, sync::LazyLock};

use serde::Deserialize;

use super::block_id::BlockId;
use super::BlockState;

pub static BLOCKS: LazyLock<HashMap<String, RegistryBlockType>> = LazyLock::new(|| {
serde_json::from_str(include_str!("../../../assets/blocks.json"))
.expect("Could not parse block.json registry.")
});

pumpkin_macros::blocks_enum!();
pumpkin_macros::block_categories_enum!();

#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct RegistryBlockDefinition {
/// e.g. minecraft:door or minecraft:button
Expand Down Expand Up @@ -48,3 +51,31 @@ pub struct RegistryBlockType {
#[serde(default, rename = "properties")]
valid_properties: HashMap<String, Vec<String>>,
}

#[derive(Default, Copy, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(transparent)]
pub struct BlockId {
pub data: u16,
}

impl BlockId {
pub fn is_air(&self) -> bool {
self.data == 0 || self.data == 12959 || self.data == 12958
}

pub fn get_id_mojang_repr(&self) -> i32 {
self.data as i32
}

pub fn get_id(&self) -> u16 {
self.data
}
}

impl From<BlockState> for BlockId {
fn from(value: BlockState) -> Self {
Self {
data: value.get_id(),
}
}
}
Loading

0 comments on commit 1726c06

Please sign in to comment.