1mod property;
4mod utils;
5
6use std::{collections::HashMap, fmt::Write};
7
8use proc_macro::TokenStream;
9use proc_macro2::TokenTree;
10use quote::quote;
11use syn::{
12 Expr, Ident, Token, braced,
13 parse::{Parse, ParseStream, Result},
14 parse_macro_input,
15};
16use utils::{combinations_of, to_pascal_case};
17
18use crate::property::{
19 generate::{
20 generate_properties_code, get_property_value_type, get_property_variant_types,
21 make_property_struct_names_to_names,
22 },
23 parse::{PropertyDefinition, PropertyWithNameAndDefault, parse_property_definitions},
24};
25
26type BlockStateIntegerRepr = u16;
28
29struct BlockDefinition {
35 name: Ident,
36 behavior: Expr,
37 properties_and_defaults: Vec<PropertyWithNameAndDefault>,
38}
39
40struct BlockDefinitions {
41 blocks: Vec<BlockDefinition>,
42}
43
44impl Parse for BlockDefinition {
45 fn parse(input: ParseStream) -> Result<Self> {
46 let name = input.parse()?;
52 input.parse::<Token![=>]>()?;
53 let behavior = input.parse()?;
54
55 input.parse::<Token![,]>()?;
56 let content;
57 braced!(content in input);
58
59 let mut properties_and_defaults = Vec::new();
60
61 let property_and_default_punctuated =
63 content.parse_terminated(PropertyWithNameAndDefault::parse, Token![,])?;
64
65 for property_and_default in property_and_default_punctuated {
66 properties_and_defaults.push(property_and_default);
67 }
68
69 Ok(BlockDefinition {
70 name,
71 behavior,
72 properties_and_defaults,
73 })
74 }
75}
76
77impl Parse for BlockDefinitions {
78 fn parse(input: ParseStream) -> Result<Self> {
79 let mut blocks = Vec::new();
80
81 let block_definitions_punctuated =
82 input.parse_terminated(BlockDefinition::parse, Token![,])?;
83 for block_definition in block_definitions_punctuated {
84 blocks.push(block_definition);
85 }
86
87 Ok(BlockDefinitions { blocks })
88 }
89}
90
91struct MakeBlockStates {
92 pub properties: Vec<PropertyDefinition>,
93 pub blocks: BlockDefinitions,
94}
95impl Parse for MakeBlockStates {
96 fn parse(input: ParseStream) -> Result<Self> {
97 let properties_ident = input.parse::<Ident>()?;
99 assert_eq!(properties_ident.to_string(), "Properties");
100 input.parse::<Token![=>]>()?;
101 let content;
102 braced!(content in input);
103 let properties = parse_property_definitions(&content)?;
104
105 input.parse::<Token![,]>()?;
106
107 let blocks_ident = input.parse::<Ident>()?;
108 assert_eq!(blocks_ident.to_string(), "Blocks");
109 input.parse::<Token![=>]>()?;
110 let content;
111 braced!(content in input);
112 let blocks = content.parse()?;
113
114 Ok(MakeBlockStates { properties, blocks })
115 }
116}
117
118struct PropertyVariantData {
119 pub block_state_ids: Vec<BlockStateIntegerRepr>,
120 pub kind: PropertyKind,
121 pub ident: Ident,
122 pub index: usize,
123}
124
125#[derive(Clone, Copy, Debug, Eq, PartialEq)]
126enum PropertyKind {
127 Enum,
128 Bool,
129}
130
131#[derive(Clone, Debug)]
132struct PropertyVariantMeta {
133 pub ident: Ident,
134 pub index: usize,
135}
136
137#[proc_macro]
138pub fn make_block_states(input: TokenStream) -> TokenStream {
139 let input = parse_macro_input!(input as MakeBlockStates);
140
141 let mut properties_map = HashMap::new();
142
143 for property in &input.properties {
144 let property_value_type = get_property_value_type(&property.data);
145 let property_variant_types = get_property_variant_types(&property.data);
146 properties_map.insert(property_value_type.to_string(), property_variant_types);
147 }
148
149 let property_struct_names_to_names = make_property_struct_names_to_names(&input.properties);
150
151 let mut block_state_enum_variants = quote! {};
152 let mut block_structs = quote! {};
153 let mut block_statics = quote! {};
154
155 let mut from_state_to_block_match = quote! {};
156 let mut from_kind_to_block_match = quote! {};
157 let mut from_kind_to_state_match = quote! {};
158 let mut from_kind_to_states_match = quote! {};
159
160 let mut from_state_to_kind_table = quote! {};
161
162 let mut properties_to_state_ids = HashMap::<String, Vec<PropertyVariantData>>::new();
164
165 let last_block_kind_id = u32::try_from(input.blocks.blocks.len() - 1).unwrap();
166
167 let mut state_id: BlockStateIntegerRepr = 0;
168 for (block_kind_id, block) in input.blocks.blocks.iter().enumerate() {
169 let block_property_names = &block
170 .properties_and_defaults
171 .iter()
172 .map(|p| p.property_value_type.to_string())
173 .collect::<Vec<_>>();
174 let mut block_properties_vec = Vec::new();
175 for property_name in block_property_names {
176 let property_variants = properties_map
177 .get(property_name)
178 .unwrap_or_else(|| panic!("Property '{property_name}' not found"))
179 .clone();
180 block_properties_vec.push(property_variants);
181 }
182
183 let mut properties_with_name: Vec<PropertyWithNameAndDefault> =
184 Vec::with_capacity(block.properties_and_defaults.len());
185 let mut previous_names: Vec<String> = Vec::new();
188 for property in &block.properties_and_defaults {
189 let index: Option<usize> = if block
190 .properties_and_defaults
191 .iter()
192 .filter(|p| p.name == property.name)
193 .count()
194 > 1
195 {
196 Some(
197 previous_names
198 .iter()
199 .filter(|&p| p == &property.name)
200 .count(),
201 )
202 } else {
203 None
204 };
205
206 let mut property_name = property_struct_names_to_names
207 .get(&property.name)
208 .cloned()
209 .unwrap_or_else(|| property.name.clone());
210 previous_names.push(property_name.clone());
211 if let Some(index) = index {
212 write!(property_name, "_{index}").unwrap();
214 }
215 properties_with_name.push(PropertyWithNameAndDefault {
216 name_ident: name_to_ident(&property_name),
217 name: property_name,
218 property_type: property.property_type.clone(),
219 property_value_type: property.property_value_type.clone(),
220 kind: property.kind,
221 default: property.default.clone(),
222 });
223 }
224 drop(previous_names);
225
226 let mut block_struct_fields = quote! {};
234 for PropertyWithNameAndDefault {
235 property_value_type,
236 name_ident,
237 kind,
238 ..
239 } in &properties_with_name
240 {
241 block_struct_fields.extend(match kind {
242 PropertyKind::Enum => {
243 quote! { pub #name_ident: properties::#property_value_type, }
244 }
245 PropertyKind::Bool => {
246 quote! { pub #name_ident: #property_value_type, }
247 }
248 });
249 }
250
251 let block_name_pascal_case = Ident::new(
252 &to_pascal_case(&block.name.to_string()),
253 proc_macro2::Span::call_site(),
254 );
255 let block_struct_name = Ident::new(
256 &block_name_pascal_case.to_string(),
257 proc_macro2::Span::call_site(),
258 );
259
260 let first_state_id = state_id;
261 let mut default_state_id = None;
262
263 if block_properties_vec.is_empty() {
265 block_state_enum_variants.extend(quote! {
266 #block_name_pascal_case,
267 });
268 default_state_id = Some(state_id);
269
270 let static_name = Ident::new(
271 &format!("__BLOCK_STATE_{}_{}", block_struct_name, state_id).to_uppercase(),
272 proc_macro2::Span::call_site(),
273 );
274
275 block_statics.extend(quote! {
276 static #static_name: #block_struct_name = #block_struct_name {};
277 });
278
279 from_state_to_block_match.extend(quote! { &#static_name, });
280
281 state_id += 1;
282 }
283 for combination in combinations_of(&block_properties_vec) {
284 let mut is_default = true;
285
286 let mut from_block_to_state_combination_match_inner = quote! {};
290 for i in 0..properties_with_name.len() {
291 let property = &properties_with_name[i];
292 let property_name_ident = &property.name_ident;
293 let property_value_name_ident = &property.property_type;
294 let variant = &combination[i];
295 let variant_ident = variant.ident.clone();
296
297 let property_default_ident = property
300 .default
301 .clone()
302 .into_iter()
303 .last()
304 .and_then(|tt| match tt {
305 TokenTree::Ident(ident) => Some(ident),
306 _ => None,
307 })
308 .unwrap();
309 if variant.ident != property_default_ident {
310 is_default = false;
311 }
312
313 let property_variant = match property.kind {
314 PropertyKind::Enum => {
315 quote! { properties::#property_value_name_ident::#variant_ident }
316 }
317 PropertyKind::Bool => {
318 quote! { #variant_ident }
319 }
320 };
321
322 from_block_to_state_combination_match_inner.extend(quote! {
323 #property_name_ident: #property_variant,
324 });
325
326 let property_variants = properties_to_state_ids
328 .entry(property_value_name_ident.to_string())
329 .or_default();
330 let property_variant_data = property_variants
331 .iter_mut()
332 .find(|v| v.ident == variant_ident);
333 if let Some(property_variant_data) = property_variant_data {
334 property_variant_data.block_state_ids.push(state_id);
335 } else {
336 property_variants.push(PropertyVariantData {
337 block_state_ids: vec![state_id],
338 ident: variant_ident,
339 index: variant.index,
340 kind: property.kind,
341 });
342 }
343 }
344
345 if is_default {
346 default_state_id = Some(state_id);
347 }
348
349 let static_name = Ident::new(
350 &format!("__BLOCK_STATE_{}_{}", block_struct_name, state_id).to_uppercase(),
351 proc_macro2::Span::call_site(),
352 );
353
354 block_statics.extend(quote! {
355 static #static_name: #block_struct_name = #block_struct_name {
356 #from_block_to_state_combination_match_inner
357 };
358 });
359
360 from_state_to_block_match.extend(quote! { &#static_name, });
361
362 state_id += 1;
363 }
364
365 let Some(default_state_id) = default_state_id else {
366 let defaults = properties_with_name
367 .iter()
368 .map(|p| match p.default.clone().into_iter().last().unwrap() {
369 TokenTree::Ident(i) => i.to_string(),
370 _ => {
371 panic!()
372 }
373 })
374 .collect::<Vec<_>>();
375 panic!(
376 "Couldn't get default state id for {block_name_pascal_case}, combinations={block_properties_vec:?}, defaults={defaults:?}"
377 )
378 };
379
380 let mut from_state_to_block_inner = quote! {};
389
390 let mut division: BlockStateIntegerRepr = 1;
391 for i in (0..properties_with_name.len()).rev() {
392 let PropertyWithNameAndDefault {
393 property_type: property_struct_name_ident,
394 name_ident: property_name_ident,
395 property_value_type,
396 ..
397 } = &properties_with_name[i];
398
399 let property_variants = &block_properties_vec[i];
400 let property_variants_count = property_variants.len() as crate::BlockStateIntegerRepr;
401 let conversion_code = {
402 if &property_value_type.to_string() == "bool" {
403 assert_eq!(property_variants_count, 2);
404 quote! {(b / #division) % #property_variants_count == 0}
407 } else {
408 quote! {properties::#property_struct_name_ident::from((b / #division) % #property_variants_count)}
409 }
410 };
411 from_state_to_block_inner.extend(quote! {
412 #property_name_ident: #conversion_code,
413 });
414
415 division *= property_variants_count;
416 }
417
418 let mut as_block_state_inner = quote! { #first_state_id };
419 let mut factor: BlockStateIntegerRepr = 1;
420 for i in (0..properties_with_name.len()).rev() {
421 let PropertyWithNameAndDefault {
422 name_ident: property_name_ident,
423 property_value_type,
424 ..
425 } = &properties_with_name[i];
426
427 let property_variants = &block_properties_vec[i];
428 let property_variants_count = property_variants.len() as crate::BlockStateIntegerRepr;
429 if &property_value_type.to_string() == "bool" {
430 as_block_state_inner.extend(
433 quote! { + (!self.#property_name_ident as BlockStateIntegerRepr) * #factor},
434 );
435 } else {
436 as_block_state_inner.extend(
437 quote! { + (self.#property_name_ident as BlockStateIntegerRepr) * #factor},
438 );
439 };
440
441 factor *= property_variants_count;
442 }
443
444 let last_state_id = state_id - 1;
445
446 from_kind_to_block_match.extend(quote! {
447 BlockKind::#block_name_pascal_case => Box::new(#block_struct_name::default()),
448 });
449 from_kind_to_state_match.extend(quote! {
450 BlockKind::#block_name_pascal_case => BlockState::new_const(#default_state_id),
451 });
452 from_kind_to_states_match.extend(quote! {
453 BlockKind::#block_name_pascal_case => BlockStates::from(#first_state_id..=#last_state_id),
454 });
455 for _ in first_state_id..=last_state_id {
456 let block_kind_id = block_kind_id as u32;
457 from_state_to_kind_table.extend(quote! { #block_kind_id, });
458 }
459
460 let mut property_map_inner = quote! {};
461 let mut get_property_match_inner = quote! {};
462 let mut set_property_match_inner = quote! {};
463
464 for PropertyWithNameAndDefault {
465 name,
466 name_ident,
467 kind,
468 ..
469 } in &properties_with_name
470 {
471 let variant_name_tokens = match kind {
472 PropertyKind::Enum => quote! { self.#name_ident.to_static_str() },
473 PropertyKind::Bool => quote! { if self.#name_ident { "true" } else { "false" } },
474 };
475 property_map_inner.extend(quote! {
476 map.insert(#name, #variant_name_tokens);
477 });
478 get_property_match_inner.extend(quote! {
479 #name => Some(#variant_name_tokens),
480 });
481
482 set_property_match_inner.extend(match kind {
483 PropertyKind::Enum => quote! { #name => self.#name_ident = new_value.parse()?, },
484 PropertyKind::Bool => {
485 quote! { #name => self.#name_ident = new_value.parse::<bool>().map_err(|_| InvalidPropertyError)?, }
486 }
487 });
488 }
489 let set_property = if set_property_match_inner.is_empty() {
490 quote! {
491 Err(InvalidPropertyError)
492 }
493 } else {
494 quote! {
495 match name {
496 #set_property_match_inner
497 _ => return Err(InvalidPropertyError),
498 }
499 Ok(())
500 }
501 };
502
503 let mut block_default_fields = quote! {};
504 for PropertyWithNameAndDefault {
505 name_ident,
506 default: property_default,
507 ..
508 } in properties_with_name
509 {
510 block_default_fields.extend(quote! { #name_ident: #property_default, });
511 }
512
513 let block_behavior = &block.behavior;
514 let block_id = block.name.to_string();
515
516 let as_block_state = quote! { BlockState::new_const(#as_block_state_inner) };
517
518 let mut block_struct = quote! {
519 #[derive(Clone, Copy, Debug, PartialEq)]
520 pub struct #block_struct_name
521 };
522 if block_struct_fields.is_empty() {
523 block_struct.extend(quote! {;});
524 } else {
525 block_struct.extend(quote! { { #block_struct_fields } });
526 }
527
528 block_struct.extend(quote! {
529 impl BlockTrait for #block_struct_name {
530 fn boxed(&self) -> Box<dyn BlockTrait>{
531 Box::new(*self)
532 }
533 fn behavior(&self) -> BlockBehavior {
534 #block_behavior
535 }
536 fn id(&self) -> &'static str {
537 #block_id
538 }
539 fn as_block_state(&self) -> BlockState {
540 #as_block_state
541 }
542 fn as_block_kind(&self) -> BlockKind {
543 BlockKind::#block_name_pascal_case
544 }
545
546
547 fn property_map(&self) -> std::collections::HashMap<&'static str, &'static str> {
548 let mut map = std::collections::HashMap::new();
549 #property_map_inner
550 map
551 }
552 fn get_property(&self, name: &str) -> Option<&'static str> {
553 match name {
554 #get_property_match_inner
555 _ => None,
556 }
557 }
558 fn set_property(&mut self, name: &str, new_value: &str) -> Result<(), InvalidPropertyError> {
559 #set_property
560 }
561 }
562
563 impl From<#block_struct_name> for BlockState {
564 fn from(b: #block_struct_name) -> Self {
565 b.as_block_state()
566 }
567 }
568
569 impl Default for #block_struct_name {
570 fn default() -> Self {
571 Self {
572 #block_default_fields
573 }
574 }
575 }
576 });
577
578 block_structs.extend(block_struct);
579 }
580
581 let last_state_id = state_id - 1;
582
583 let mut generated = quote! {
584 impl BlockState {
585 pub const MAX_STATE: BlockStateIntegerRepr = #last_state_id;
587
588 pub fn property<P: Property>(self) -> Option<P::Value> {
596 P::try_from_block_state(self)
597 }
598
599 pub fn as_block_kind(self) -> BlockKind {
600 static TABLE: &[u32] = &[
601 #from_state_to_kind_table
602 ];
603 const _: () = assert!(BlockKind::is_valid_id(#last_block_kind_id));
604
605 unsafe { BlockKind::from_u32(TABLE[self.id() as usize]).unwrap_unchecked() }
608 }
609 }
610 };
611
612 let properties_code =
613 generate_properties_code(&input.properties, &properties_to_state_ids, last_state_id);
614
615 generated.extend(quote! {
616 pub mod properties {
617 use super::*;
618
619 #properties_code
620 }
621
622 pub mod blocks {
623 use super::*;
624 use azalea_registry::builtin::BlockKind;
625
626 #block_structs
627
628 #block_statics
629
630 static BLOCK_STATE_TABLE: &[&'static dyn BlockTrait; BlockState::MAX_STATE as usize + 1] = &[
631 #from_state_to_block_match
632 ];
633
634
635 #[inline]
637 #[must_use]
638 pub const fn blockstate_to_blocktrait(state: BlockState) -> &'static dyn BlockTrait {
639 BLOCK_STATE_TABLE[state.id() as usize]
640 }
641
642 impl From<BlockState> for &'static dyn BlockTrait {
643 #[inline]
644 fn from(block_state: BlockState) -> Self {
645 blockstate_to_blocktrait(block_state)
646 }
647 }
648
649 impl From<BlockKind> for Box<dyn BlockTrait> {
650 fn from(block: BlockKind) -> Self {
651 match block {
652 #from_kind_to_block_match
653 _ => unreachable!("There should always be a block struct for every BlockKind variant")
654 }
655 }
656 }
657 impl From<BlockKind> for BlockState {
658 fn from(block: BlockKind) -> Self {
659 match block {
660 #from_kind_to_state_match
661 _ => unreachable!("There should always be a block state for every BlockKind variant")
662 }
663 }
664 }
665 impl From<BlockKind> for BlockStates {
666 fn from(block: BlockKind) -> Self {
667 match block {
668 #from_kind_to_states_match
669 _ => unreachable!("There should always be a block state for every BlockKind variant")
670 }
671 }
672 }
673 }
674 });
675
676 generated.into()
677}
678
679fn name_to_ident(name: &str) -> Ident {
682 let ident_str = match name {
683 "type" => "kind",
684 _ => name,
685 };
686 Ident::new(ident_str, proc_macro2::Span::call_site())
687}