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, PartialEq, Eq)]
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 for property in &input.properties {
143 let property_value_type = get_property_value_type(&property.data);
144 let property_variant_types = get_property_variant_types(&property.data);
145 properties_map.insert(property_value_type.to_string(), property_variant_types);
146 }
147
148 let property_struct_names_to_names = make_property_struct_names_to_names(&input.properties);
149
150 let mut block_state_enum_variants = quote! {};
151 let mut block_structs = quote! {};
152
153 let mut from_state_to_block_match = quote! {};
154 let mut from_registry_block_to_block_match = quote! {};
155 let mut from_registry_block_to_blockstate_match = quote! {};
156 let mut from_registry_block_to_blockstates_match = quote! {};
157
158 let mut properties_to_state_ids = HashMap::<String, Vec<PropertyVariantData>>::new();
160
161 let mut state_id: BlockStateIntegerRepr = 0;
162 for block in &input.blocks.blocks {
163 let block_property_names = &block
164 .properties_and_defaults
165 .iter()
166 .map(|p| p.property_value_type.to_string())
167 .collect::<Vec<_>>();
168 let mut block_properties_vec = Vec::new();
169 for property_name in block_property_names {
170 let property_variants = properties_map
171 .get(property_name)
172 .unwrap_or_else(|| panic!("Property '{property_name}' not found"))
173 .clone();
174 block_properties_vec.push(property_variants);
175 }
176
177 let mut properties_with_name: Vec<PropertyWithNameAndDefault> =
178 Vec::with_capacity(block.properties_and_defaults.len());
179 let mut previous_names: Vec<String> = Vec::new();
182 for property in &block.properties_and_defaults {
183 let index: Option<usize> = if block
184 .properties_and_defaults
185 .iter()
186 .filter(|p| p.name == property.name)
187 .count()
188 > 1
189 {
190 Some(
191 previous_names
192 .iter()
193 .filter(|&p| p == &property.name)
194 .count(),
195 )
196 } else {
197 None
198 };
199
200 let mut property_name = property_struct_names_to_names
201 .get(&property.name)
202 .cloned()
203 .unwrap_or_else(|| property.name.clone());
204 previous_names.push(property_name.clone());
205 if let Some(index) = index {
206 write!(property_name, "_{index}").unwrap();
208 }
209 properties_with_name.push(PropertyWithNameAndDefault {
210 name_ident: name_to_ident(&property_name),
211 name: property_name,
212 property_type: property.property_type.clone(),
213 property_value_type: property.property_value_type.clone(),
214 kind: property.kind,
215 default: property.default.clone(),
216 });
217 }
218 drop(previous_names);
219
220 let mut block_struct_fields = quote! {};
228 for PropertyWithNameAndDefault {
229 property_value_type,
230 name_ident,
231 kind,
232 ..
233 } in &properties_with_name
234 {
235 block_struct_fields.extend(match kind {
236 PropertyKind::Enum => {
237 quote! { pub #name_ident: properties::#property_value_type, }
238 }
239 PropertyKind::Bool => {
240 quote! { pub #name_ident: #property_value_type, }
241 }
242 });
243 }
244
245 let block_name_pascal_case = Ident::new(
246 &to_pascal_case(&block.name.to_string()),
247 proc_macro2::Span::call_site(),
248 );
249 let block_struct_name = Ident::new(
250 &block_name_pascal_case.to_string(),
251 proc_macro2::Span::call_site(),
252 );
253
254 let first_state_id = state_id;
255 let mut default_state_id = None;
256
257 if block_properties_vec.is_empty() {
259 block_state_enum_variants.extend(quote! {
260 #block_name_pascal_case,
261 });
262 default_state_id = Some(state_id);
263 state_id += 1;
264 }
265 for combination in combinations_of(&block_properties_vec) {
266 let mut is_default = true;
267
268 let mut from_block_to_state_combination_match_inner = quote! {};
272 for i in 0..properties_with_name.len() {
273 let property = &properties_with_name[i];
274 let property_name_ident = &property.name_ident;
275 let property_value_name_ident = &property.property_type;
276 let variant = &combination[i];
277 let variant_ident = variant.ident.clone();
278
279 let property_default_ident = property
281 .default
282 .clone()
283 .into_iter()
284 .last()
285 .and_then(|tt| match tt {
286 TokenTree::Ident(ident) => Some(ident),
287 _ => None,
288 })
289 .unwrap();
290 if variant.ident != property_default_ident {
291 is_default = false;
292 }
293
294 let property_variant = match property.kind {
295 PropertyKind::Enum => {
296 quote! { properties::#property_value_name_ident::#variant_ident }
297 }
298 PropertyKind::Bool => {
299 quote! { #variant_ident }
300 }
301 };
302
303 from_block_to_state_combination_match_inner.extend(quote! {
304 #property_name_ident: #property_variant,
305 });
306
307 let property_variants = properties_to_state_ids
309 .entry(property_value_name_ident.to_string())
310 .or_default();
311 let property_variant_data = property_variants
312 .iter_mut()
313 .find(|v| v.ident == variant_ident);
314 if let Some(property_variant_data) = property_variant_data {
315 property_variant_data.block_state_ids.push(state_id);
316 } else {
317 property_variants.push(PropertyVariantData {
318 block_state_ids: vec![state_id],
319 ident: variant_ident,
320 index: variant.index,
321 kind: property.kind,
322 });
323 }
324 }
325
326 if is_default {
327 default_state_id = Some(state_id);
328 }
329
330 state_id += 1;
331 }
332
333 let Some(default_state_id) = default_state_id else {
334 let defaults = properties_with_name
335 .iter()
336 .map(|p| match p.default.clone().into_iter().last().unwrap() {
337 TokenTree::Ident(i) => i.to_string(),
338 _ => {
339 panic!()
340 }
341 })
342 .collect::<Vec<_>>();
343 panic!(
344 "Couldn't get default state id for {block_name_pascal_case}, combinations={block_properties_vec:?}, defaults={defaults:?}"
345 )
346 };
347
348 let mut from_state_to_block_inner = quote! {};
357 let mut division: BlockStateIntegerRepr = 1;
358 for i in (0..properties_with_name.len()).rev() {
359 let PropertyWithNameAndDefault {
360 property_type: property_struct_name_ident,
361 name_ident: property_name_ident,
362 property_value_type,
363 ..
364 } = &properties_with_name[i];
365
366 let property_variants = &block_properties_vec[i];
367 let property_variants_count = property_variants.len() as crate::BlockStateIntegerRepr;
368 let conversion_code = {
369 if &property_value_type.to_string() == "bool" {
370 assert_eq!(property_variants_count, 2);
371 quote! {(b / #division) % #property_variants_count == 0}
373 } else {
374 quote! {properties::#property_struct_name_ident::from((b / #division) % #property_variants_count)}
375 }
376 };
377 from_state_to_block_inner.extend(quote! {
378 #property_name_ident: #conversion_code,
379 });
380
381 division *= property_variants_count;
382 }
383
384 let mut as_block_state_inner = quote! { #first_state_id };
385 let mut factor: BlockStateIntegerRepr = 1;
386 for i in (0..properties_with_name.len()).rev() {
387 let PropertyWithNameAndDefault {
388 name_ident: property_name_ident,
389 property_value_type,
390 ..
391 } = &properties_with_name[i];
392
393 let property_variants = &block_properties_vec[i];
394 let property_variants_count = property_variants.len() as crate::BlockStateIntegerRepr;
395 if &property_value_type.to_string() == "bool" {
396 as_block_state_inner.extend(
399 quote! { + (!self.#property_name_ident as BlockStateIntegerRepr) * #factor},
400 );
401 } else {
402 as_block_state_inner.extend(
403 quote! { + (self.#property_name_ident as BlockStateIntegerRepr) * #factor},
404 );
405 };
406
407 factor *= property_variants_count;
408 }
409
410 let last_state_id = state_id - 1;
411 from_state_to_block_match.extend(if first_state_id == last_state_id {
412 quote! {
413 #first_state_id => {
414 Box::new(#block_struct_name { #from_state_to_block_inner })
415 },
416 }
417 } else {
418 quote! {
419 #first_state_id..=#last_state_id => {
420 let b = b - #first_state_id;
421 Box::new(#block_struct_name { #from_state_to_block_inner })
422 },
423 }
424 });
425
426 from_registry_block_to_block_match.extend(quote! {
427 azalea_registry::Block::#block_name_pascal_case => Box::new(#block_struct_name::default()),
428 });
429 from_registry_block_to_blockstate_match.extend(quote! {
430 azalea_registry::Block::#block_name_pascal_case => BlockState::new_const(#default_state_id),
431 });
432 from_registry_block_to_blockstates_match.extend(quote! {
433 azalea_registry::Block::#block_name_pascal_case => BlockStates::from(#first_state_id..=#last_state_id),
434 });
435
436 let mut property_map_inner = quote! {};
437 let mut get_property_inner = quote! {};
438
439 for PropertyWithNameAndDefault {
440 name,
441 name_ident,
442 kind,
443 ..
444 } in &properties_with_name
445 {
446 let variant_name_tokens = match kind {
447 PropertyKind::Enum => quote! { self.#name_ident.to_static_str() },
448 PropertyKind::Bool => quote! { if self.#name_ident { "true" } else { "false" } },
449 };
450 property_map_inner.extend(quote! {
451 map.insert(#name, #variant_name_tokens);
452 });
453 get_property_inner.extend(quote! {
454 #name => Some(#variant_name_tokens),
455 });
456 }
457
458 let mut block_default_fields = quote! {};
459 for PropertyWithNameAndDefault {
460 name_ident,
461 default: property_default,
462 ..
463 } in properties_with_name
464 {
465 block_default_fields.extend(quote! { #name_ident: #property_default, });
466 }
467
468 let block_behavior = &block.behavior;
469 let block_id = block.name.to_string();
470
471 let as_block_state = quote! { BlockState::new_const(#as_block_state_inner) };
472
473 let mut block_struct = quote! {
474 #[derive(Debug, Copy, Clone, PartialEq)]
475 pub struct #block_struct_name
476 };
477 if block_struct_fields.is_empty() {
478 block_struct.extend(quote! {;});
479 } else {
480 block_struct.extend(quote! { { #block_struct_fields } });
481 }
482
483 block_struct.extend(quote! {
484 impl BlockTrait for #block_struct_name {
485 fn behavior(&self) -> BlockBehavior {
486 #block_behavior
487 }
488 fn id(&self) -> &'static str {
489 #block_id
490 }
491 fn as_block_state(&self) -> BlockState {
492 #as_block_state
493 }
494 fn as_registry_block(&self) -> azalea_registry::Block {
495 azalea_registry::Block::#block_name_pascal_case
496 }
497
498 fn property_map(&self) -> std::collections::HashMap<&'static str, &'static str> {
499 let mut map = std::collections::HashMap::new();
500 #property_map_inner
501 map
502 }
503 fn get_property(&self, name: &str) -> Option<&'static str> {
504 match name {
505 #get_property_inner
506 _ => None,
507 }
508 }
509 }
510
511 impl From<#block_struct_name> for BlockState {
512 fn from(b: #block_struct_name) -> Self {
513 b.as_block_state()
514 }
515 }
516
517 impl Default for #block_struct_name {
518 fn default() -> Self {
519 Self {
520 #block_default_fields
521 }
522 }
523 }
524 });
525
526 block_structs.extend(block_struct);
527 }
528
529 let last_state_id = state_id - 1;
530 let mut generated = quote! {
531 impl BlockState {
532 pub const MAX_STATE: BlockStateIntegerRepr = #last_state_id;
534
535 pub fn property<P: Property>(self) -> Option<P::Value> {
543 P::try_from_block_state(self)
544 }
545 }
546 };
547
548 let properties_code =
549 generate_properties_code(&input.properties, &properties_to_state_ids, last_state_id);
550
551 generated.extend(quote! {
552 pub mod properties {
553 use super::*;
554
555 #properties_code
556 }
557
558 pub mod blocks {
559 use super::*;
560
561 #block_structs
562
563 impl From<BlockState> for Box<dyn BlockTrait> {
564 fn from(block_state: BlockState) -> Self {
565 let b = block_state.id();
566 match b {
567 #from_state_to_block_match
568 _ => panic!("Invalid block state: {}", b),
569 }
570 }
571 }
572 impl From<azalea_registry::Block> for Box<dyn BlockTrait> {
573 fn from(block: azalea_registry::Block) -> Self {
574 match block {
575 #from_registry_block_to_block_match
576 _ => unreachable!("There should always be a block struct for every azalea_registry::Block variant")
577 }
578 }
579 }
580 impl From<azalea_registry::Block> for BlockState {
581 fn from(block: azalea_registry::Block) -> Self {
582 match block {
583 #from_registry_block_to_blockstate_match
584 _ => unreachable!("There should always be a block state for every azalea_registry::Block variant")
585 }
586 }
587 }
588 impl From<azalea_registry::Block> for BlockStates {
589 fn from(block: azalea_registry::Block) -> Self {
590 match block {
591 #from_registry_block_to_blockstates_match
592 _ => unreachable!("There should always be a block state for every azalea_registry::Block variant")
593 }
594 }
595 }
596 }
597 });
598
599 generated.into()
600}
601
602fn name_to_ident(name: &str) -> Ident {
605 let ident_str = match name {
606 "type" => "kind",
607 _ => name,
608 };
609 Ident::new(ident_str, proc_macro2::Span::call_site())
610}