mc_core/block/
state.rs

1use std::fmt::{Debug, Formatter, Result as FmtResult};
2use std::collections::HashMap;
3use std::ptr::NonNull;
4
5use super::{Block, UntypedProperty, Property, PropertySerializable};
6use crate::util::OpaquePtr;
7
8
9/// The maximum number of states for a single block.
10pub const MAX_STATES_COUNT: usize = 0x10000;
11
12
13#[derive(Debug)]
14pub(crate) struct SharedProperty {
15    prop: &'static dyn UntypedProperty,
16    index: usize,
17    length: u8,
18    period: usize
19}
20
21
22/// Represent a particular state of a block, this block state also know
23/// all its neighbors by their properties and values.
24///
25/// To build states, use `BlockStateContainerBuilder` and add all wanted
26/// properties.
27pub struct BlockState {
28    /// The index of this state within the shared data's states vector.
29    index: u16,
30    /// Array of property encoded values.
31    properties: Vec<u8>,
32    /// Circular reference back to the owner
33    block: NonNull<Block>
34}
35
36pub type BlockStateKey = OpaquePtr<BlockState>;
37
38unsafe impl Send for BlockState {}
39unsafe impl Sync for BlockState {}
40
41
42impl BlockState {
43
44    pub(crate) fn build_singleton() -> BlockState {
45        BlockState {
46            index: 0,
47            properties: Vec::new(),
48            block: NonNull::dangling()
49        }
50    }
51
52    pub(crate) fn build_complex(properties: &[&'static dyn UntypedProperty]) -> (HashMap<&'static str, SharedProperty>, Vec<BlockState>) {
53
54        debug_assert!(!properties.is_empty(), "building complex states without properties is not allowed");
55
56        let mut states_count = 1;
57        let mut properties_periods = Vec::with_capacity(properties.len());
58
59        for &prop in properties {
60            let length = prop.len();
61            states_count *= length as usize;
62            properties_periods.push((prop, length, 1usize));
63        }
64
65        if states_count > MAX_STATES_COUNT {
66            panic!("Too many properties for this state, the maximum number is {}.", MAX_STATES_COUNT);
67        }
68
69        let mut shared_properties = HashMap::with_capacity(properties.len());
70
71        let mut next_period = 1;
72        for (i, (prop, length, period)) in properties_periods.iter_mut().enumerate().rev() {
73            let prop = *prop;
74            *period = next_period;
75            next_period *= *length as usize;
76            shared_properties.insert(prop.name(), SharedProperty {
77                prop,
78                index: i,
79                length: *length,
80                period: *period
81            });
82        }
83
84        let mut shared_states = Vec::with_capacity(states_count);
85
86        for i in 0..states_count {
87
88            let mut state_properties = Vec::with_capacity(properties.len());
89            for (_, length, period) in &properties_periods {
90                state_properties.push(((i / *period) % (*length as usize)) as u8);
91            }
92
93            shared_states.push(BlockState {
94                index: i as u16,
95                properties: state_properties,
96                block: NonNull::dangling()
97            });
98
99        }
100
101        (shared_properties, shared_states)
102
103    }
104
105    #[inline]
106    pub fn get_index(&self) -> u16 {
107        self.index
108    }
109
110    #[inline]
111    pub fn get_key(&'static self) -> BlockStateKey {
112        OpaquePtr::new(self)
113    }
114
115    #[inline]
116    pub fn get_block(&self) -> &'static Block {
117        // SAFETY: This pointer is always valid since:
118        //  - block state must be owned by a Block, and this Block must be pined in a box
119        //  - this function is not called before the pointer initialization (before set_block)
120        unsafe { self.block.as_ref() }
121    }
122
123    /// Really unsafe method, should only be called by `Block` constructor.
124    #[inline]
125    pub(super) fn set_block(&mut self, block: NonNull<Block>) {
126        // This method is called once in Block::new
127        self.block = block;
128    }
129
130    #[inline]
131    pub fn is_block(&self, block: &'static Block) -> bool {
132        self.get_block() == block
133    }
134
135    #[inline]
136    fn get_block_shared_prop(&self, name: &str) -> Option<&SharedProperty> {
137        self.get_block().get_storage().get_shared_prop(name)
138    }
139
140    /// Get a block state property value if the property exists.
141    pub fn get<T, P>(&self, property: &P) -> Option<T>
142    where
143        T: PropertySerializable,
144        P: Property<T>
145    {
146
147        let prop = self.get_block_shared_prop(&property.name())?;
148        if prop.prop.type_id() == property.type_id() {
149            property.decode(self.properties[prop.index])
150        } else {
151            None
152        }
153
154    }
155
156    pub fn expect<T, P>(&self, property: &P) -> T
157    where
158        T: PropertySerializable,
159        P: Property<T>
160    {
161        self.get(property).unwrap()
162    }
163
164    /// Try to get a neighbor with all the same properties excepts the given one with the given
165    /// value, if the property or its value is not valid for the block, None is returned.
166    pub fn with<T, P>(&self, property: &P, value: T) -> Option<&BlockState>
167    where
168        T: PropertySerializable,
169        P: Property<T>
170    {
171        let prop = self.get_block_shared_prop(&property.name())?;
172        self.with_unchecked(prop, property.encode(value)?)
173    }
174
175    /// Try to get a neighbor with all the same properties excepts the given one with the given
176    /// value, if the property or its value is not valid for the block, None is returned.
177    ///
178    /// This version of `with` method take raw property name and value as strings.
179    pub fn with_raw(&self, prop_name: &str, prop_value: &str) -> Option<&BlockState> {
180        let prop = self.get_block_shared_prop(prop_name)?;
181        self.with_unchecked(prop, prop.prop.prop_from_string(prop_value)?)
182    }
183
184    #[inline]
185    fn with_unchecked(&self, prop: &SharedProperty, prop_value: u8) -> Option<&BlockState> {
186
187        let new_value = prop_value as isize;
188        let current_value = self.properties[prop.index] as isize;
189
190        Some(if new_value == current_value {
191            self
192        } else {
193            let value_diff = new_value - current_value;
194            let neighbor_index = (self.index as isize + value_diff * prop.period as isize) as usize;
195            self.get_block().get_storage().get_state_unchecked(neighbor_index)
196        })
197
198    }
199
200    /// Iterate over representations of each property of this state.
201    /// No iterator is returned if the underlying block as no other state other than this one.
202    pub fn iter_raw_states<'a>(&'a self) -> Option<impl Iterator<Item = (&'static str, String)> + 'a> {
203        self.get_block().get_storage().get_shared_props().map(move |props| {
204            props.iter().map(move |(&name, shared)| {
205                let raw_value = self.properties[shared.index];
206                (name, shared.prop.prop_to_string(raw_value).unwrap())
207            })
208        })
209    }
210
211}
212
213
214// Custom implementation for static block state references.
215impl PartialEq for &'static BlockState {
216    fn eq(&self, other: &Self) -> bool {
217        std::ptr::eq(*self, *other)
218    }
219}
220
221impl Eq for &'static BlockState {}
222
223
224impl Debug for BlockState {
225
226    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
227
228        let reprs = match self.iter_raw_states() {
229            Some(it) => it.collect(),
230            None => Vec::with_capacity(0)
231        };
232
233        f.debug_struct("BlockState")
234            .field("block", &self.get_block().get_name())
235            .field("index", &self.index)
236            .field("properties", &reprs)
237            .field("raw_properties", &self.properties)
238            .finish()
239
240    }
241
242}