1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
#![doc = include_str!("../README.md")]
mod behavior;
pub mod block_state;
pub mod fluid_state;
mod generated;
mod range;
use core::fmt::Debug;
use std::{any::Any, collections::HashMap, str::FromStr};
use azalea_registry::builtin::BlockKind;
pub use behavior::BlockBehavior;
// re-exported for convenience
pub use block_state::BlockState;
pub use generated::{blocks, properties};
pub use range::BlockStates;
pub trait BlockTrait: Debug + Any {
fn behavior(&self) -> BlockBehavior;
/// Get the Minecraft string ID for this block.
///
/// For example, `stone` or `grass_block`.
fn id(&self) -> &'static str;
/// Convert the block struct to a [`BlockState`].
///
/// This is a lossless conversion, as [`BlockState`] also contains state
/// data.
fn as_block_state(&self) -> BlockState;
/// Convert the block struct to a [`BlockKind`].
///
/// This is a lossy conversion, as [`BlockKind`] doesn't contain any state
/// data.
fn as_registry_block(&self) -> BlockKind;
/// Returns a map of property names on this block to their values as
/// strings.
///
/// Consider using [`Self::get_property`] if you only need a single
/// property.
fn property_map(&self) -> HashMap<&'static str, &'static str>;
/// Get a property's value as a string by its name, or `None` if the block
/// has no property with that name.
///
/// To get all properties, you may use [`Self::property_map`].
///
/// To set a property, use [`Self::set_property`].
fn get_property(&self, name: &str) -> Option<&'static str>;
/// Update a property on this block, with the name and value being strings.
///
/// Returns `Ok(())`, if the property name and value are valid, otherwise it
/// returns `Err(InvalidPropertyError)`.
///
/// To get a property, use [`Self::get_property`].
fn set_property(&mut self, name: &str, new_value: &str) -> Result<(), InvalidPropertyError>;
}
#[derive(Debug)]
pub struct InvalidPropertyError;
impl dyn BlockTrait {
pub fn downcast_ref<T: BlockTrait>(&self) -> Option<&T> {
(self as &dyn Any).downcast_ref::<T>()
}
}
pub trait Property: FromStr {
type Value;
fn try_from_block_state(state: BlockState) -> Option<Self::Value>;
/// Convert the value of the property to a string, like "x" or "true".
fn to_static_str(&self) -> &'static str;
}
#[cfg(test)]
mod tests {
use crate::BlockTrait;
#[test]
pub fn roundtrip_block_state() {
let block = crate::blocks::OakTrapdoor {
facing: crate::properties::FacingCardinal::East,
half: crate::properties::TopBottom::Bottom,
open: true,
powered: false,
waterlogged: false,
};
let block_state = block.as_block_state();
let block_from_state = Box::<dyn BlockTrait>::from(block_state);
let block_from_state = *block_from_state
.downcast_ref::<crate::blocks::OakTrapdoor>()
.unwrap();
assert_eq!(block, block_from_state);
}
#[test]
pub fn test_property_map() {
let block = crate::blocks::OakTrapdoor {
facing: crate::properties::FacingCardinal::East,
half: crate::properties::TopBottom::Bottom,
open: true,
powered: false,
waterlogged: false,
};
let property_map = block.property_map();
assert_eq!(property_map.len(), 5);
assert_eq!(property_map.get("facing"), Some(&"east"));
assert_eq!(property_map.get("half"), Some(&"bottom"));
assert_eq!(property_map.get("open"), Some(&"true"));
assert_eq!(property_map.get("powered"), Some(&"false"));
assert_eq!(property_map.get("waterlogged"), Some(&"false"));
}
#[test]
pub fn test_integer_properties() {
// Test with oak sapling that has an integer-like stage property
let sapling_stage_0 = crate::blocks::OakSapling {
stage: crate::properties::OakSaplingStage::_0,
};
let sapling_stage_1 = crate::blocks::OakSapling {
stage: crate::properties::OakSaplingStage::_1,
};
// Test stage 0
let properties_0 = sapling_stage_0.property_map();
assert_eq!(properties_0.len(), 1);
assert_eq!(properties_0.get("stage"), Some(&"0"));
assert_eq!(sapling_stage_0.get_property("stage"), Some("0"));
// Test stage 1
let properties_1 = sapling_stage_1.property_map();
assert_eq!(properties_1.len(), 1);
assert_eq!(properties_1.get("stage"), Some(&"1"));
assert_eq!(sapling_stage_1.get_property("stage"), Some("1"));
// Test non-existent property
assert_eq!(sapling_stage_0.get_property("nonexistent"), None);
}
}
|