mc_core/block/
mod.rs

1use std::collections::HashMap;
2use std::ptr::NonNull;
3use std::fmt::Debug;
4
5use once_cell::sync::OnceCell;
6use bit_vec::BitVec;
7
8use crate::tag::{TagType, TagTypeKey};
9use crate::util::OpaquePtr;
10
11mod state;
12mod property;
13mod util;
14
15pub use state::*;
16pub use property::*;
17pub use util::*;
18
19
20/// A basic block defined by a name, its states and properties. This block structure
21/// is made especially for static definition, its states are computed lazily and
22/// almost all method requires a self reference with static lifetime.
23#[derive(Debug)]
24pub struct Block {
25    name: &'static str,
26    spec: BlockSpec,
27    states: OnceCell<BlockStorage>,
28}
29
30
31/// The type of hashable value that can represent a block as a map key.
32/// See `Block::get_key`, its only usable for statically defined blocks.
33pub type BlockKey = OpaquePtr<Block>;
34
35
36/// Internal enumeration to avoid allocation over-head for single block. This allows
37/// blocks with no properties to avoid allocating a `Vec` and a `HashMap`.
38#[derive(Debug)]
39enum BlockStorage {
40    /// Storage for a single state.
41    Single(BlockState),
42    /// Storage when there is single or multiple properties. This type of storage
43    /// implies that all owned states must have BlockStateProperties::Some.
44    /// By using this storage you assert that properties map is not empty.
45    Complex {
46        states: Vec<BlockState>,
47        properties: HashMap<&'static str, SharedProperty>,
48        default_state_index: usize
49    }
50}
51
52
53/// Made for static definitions of all properties of a block.
54#[derive(Debug)]
55pub enum BlockSpec {
56    /// For blocks with no properties, they have a **single** state.
57    Single,
58    /// For blocks with some properties, requires a slice to a static array of properties
59    /// references. Use the `blocks_specs!` macro to generate such arrays.
60    Complex(&'static [&'static dyn UntypedProperty]),
61    // /// Same a `Complex`, but with a callback function used to set the default block state.
62    // ComplexWithDefault(&'static [&'static dyn UntypedProperty], fn(&BlockState) -> &BlockState)
63}
64
65
66impl Block {
67
68    /// Construct a new block, this method should be used to define blocks statically.
69    /// The preferred way of defining static blocks is to use the `blocks!` macro.
70    pub const fn new(name: &'static str, spec: BlockSpec) -> Self {
71        Self {
72            name,
73            spec,
74            states: OnceCell::new()
75        }
76    }
77
78    #[inline]
79    pub fn get_name(&self) -> &'static str {
80        self.name
81    }
82
83    #[inline]
84    pub fn get_key(&'static self) -> BlockKey {
85        OpaquePtr::new(self)
86    }
87
88    fn get_storage(&'static self) -> &'static BlockStorage {
89        self.states.get_or_init(|| self.make_storage())
90    }
91
92    fn make_storage(&'static self) -> BlockStorage {
93
94        // Internal function to generate new BlockStorage from properties,
95        // if there are no properties, BlockStorage::Single is returned.
96        fn new_storage(properties: &'static [&'static dyn UntypedProperty]) -> BlockStorage {
97            if properties.is_empty() {
98                BlockStorage::Single(BlockState::build_singleton())
99            } else {
100
101                let (
102                    properties,
103                    states
104                ) = BlockState::build_complex(properties);
105
106                BlockStorage::Complex {
107                    states,
108                    properties,
109                    default_state_index: 0
110                }
111
112            }
113        }
114
115        // let mut default_supplier = None;
116
117        let mut storage = match self.spec {
118            BlockSpec::Single => BlockStorage::Single(BlockState::build_singleton()),
119            BlockSpec::Complex(properties) => new_storage(properties),
120            /*BlockSpec::ComplexWithDefault(properties, fun) => {
121                default_supplier = Some(fun);
122                new_storage(properties)
123            }*/
124        };
125
126        let block_ptr = NonNull::from(self);
127
128        match &mut storage {
129            BlockStorage::Single( state) => {
130                state.set_block(block_ptr);
131            },
132            BlockStorage::Complex {
133                states,
134                /*default_state_index,*/ ..
135            } => {
136                for state in states {
137                    state.set_block(block_ptr);
138                }
139                /*if let Some(default_supplier) = default_supplier {
140                    *default_state_index = default_supplier(&states[0]).get_index() as usize;
141                }*/
142            }
143        }
144
145        storage
146
147    }
148
149    #[inline]
150    pub fn get_default_state(&'static self) -> &'static BlockState {
151        self.get_storage().get_default_state()
152    }
153
154    #[inline]
155    pub fn get_states(&'static self) -> &'static [BlockState] {
156        self.get_storage().get_states()
157    }
158
159}
160
161
162impl PartialEq for &'static Block {
163    fn eq(&self, other: &Self) -> bool {
164        std::ptr::eq(*self, *other)
165    }
166}
167
168impl Eq for &'static Block {}
169
170
171impl BlockStorage {
172
173    pub fn get_default_state(&self) -> &BlockState {
174        match self {
175            BlockStorage::Single(state) => state,
176            BlockStorage::Complex {
177                states,
178                default_state_index, ..
179            } => &states[*default_state_index]
180        }
181    }
182
183    pub fn get_states(&self) -> &[BlockState] {
184        match self {
185            BlockStorage::Single(state) => std::slice::from_ref(state),
186            BlockStorage::Complex { states, .. } => &states[..]
187        }
188    }
189
190    /// Internal method for neighbor and values resolution of `BlockState`.
191    fn get_shared_prop(&self, name: &str) -> Option<&SharedProperty> {
192        match self {
193            BlockStorage::Single(_) => None,
194            BlockStorage::Complex {
195                properties, ..
196            } => properties.get(name)
197        }
198    }
199
200    /// Internal method for Debug implementation of `BlockState` and values iteration.
201    /// None is returned if there is no properties and the block has a single state.
202    fn get_shared_props(&self) -> Option<&HashMap<&'static str, SharedProperty>> {
203        match self {
204            BlockStorage::Single(_) => None,
205            BlockStorage::Complex {
206                properties, ..
207            } => Some(properties)
208        }
209    }
210
211    /// Internal method for `BlockState` to get a state a specific index.
212    fn get_state_unchecked(&self, index: usize) -> &BlockState {
213        match self {
214            BlockStorage::Single(state) => {
215                debug_assert!(index == 0, "index != 0 with BlockStorage::Single");
216                state
217            },
218            BlockStorage::Complex { states, .. } => &states[index]
219        }
220    }
221
222}
223
224
225/// This is a global blocks palette, it is used in chunk storage to store block states.
226/// It allows you to register individual blocks in it as well as static blocks arrays
227/// defined using the macro `blocks!`.
228pub struct GlobalBlocks {
229    next_sid: u32,
230    /// Each registered block is mapped to a tuple (index, sid), where index is the index of
231    /// insertion of the block and sid being the save ID of the first state of this block.
232    block_to_indices: HashMap<BlockKey, (usize, u32)>,
233    /// A vector storing references to each block state, the index of each state is called
234    /// its "save ID".
235    ordered_states: Vec<&'static BlockState>,
236    /// A mapping of block's names to them.
237    name_to_blocks: HashMap<&'static str, &'static Block>,
238    /// Contains stores of each tag type. For each tag, either small of big stores are used.
239    tag_stores: HashMap<TagTypeKey, TagStore>
240}
241
242impl GlobalBlocks {
243
244    pub fn new() -> Self {
245        Self {
246            next_sid: 0,
247            block_to_indices: HashMap::new(),
248            ordered_states: Vec::new(),
249            name_to_blocks: HashMap::new(),
250            tag_stores: HashMap::new()
251        }
252    }
253
254    /// A simple constructor to directly call `register_all` with given blocks slice.
255    pub fn with_all(slice: &[&'static Block]) -> Result<Self, ()> {
256        let mut blocks = Self::new();
257        blocks.register_all(slice)?;
258        Ok(blocks)
259    }
260
261    /// Register a single block to this palette, returns `Err` if no more save ID (SID) is
262    /// available, `Ok` is returned if successful, if a block was already in the palette
263    /// it also returns `Ok`.
264    pub fn register(&mut self, block: &'static Block) -> Result<(), ()> {
265
266        let states = block.get_states();
267        let states_count = states.len();
268
269        let sid = self.next_sid;
270        let idx = self.block_to_indices.len();
271        let next_sid = sid.checked_add(states_count as u32).ok_or(())?;
272
273        for store in self.tag_stores.values_mut() {
274            if let TagStore::Big(store) = store {
275                store.push(false);
276            }
277        }
278
279        if self.block_to_indices.insert(block.get_key(), (idx, sid)).is_none() {
280
281            self.next_sid = next_sid;
282
283            self.name_to_blocks.insert(block.name, block);
284            self.ordered_states.reserve(states_count);
285            for state in states {
286                self.ordered_states.push(state);
287            }
288
289        }
290
291        Ok(())
292
293    }
294
295    /// An optimized way to call `register` multiple times for each given block,
296    /// the returned follow the same rules as `register`, if an error happens, it
297    /// return without and previous added blocks are kept.
298    pub fn register_all(&mut self, slice: &[&'static Block]) -> Result<(), ()> {
299        let count = slice.len();
300        self.block_to_indices.reserve(count);
301        self.name_to_blocks.reserve(count);
302        for store in self.tag_stores.values_mut() {
303            if let TagStore::Big(store) = store {
304                store.reserve(count);
305            }
306        }
307        for &block in slice {
308            self.register(block)?;
309        }
310        Ok(())
311    }
312
313    /// Get the save ID from the given state.
314    pub fn get_sid_from(&self, state: &'static BlockState) -> Option<u32> {
315        let (_, block_offset) = *self.block_to_indices.get(&state.get_block().get_key())?;
316        Some(block_offset + state.get_index() as u32)
317    }
318
319    /// Get the block state from the given save ID.
320    pub fn get_state_from(&self, sid: u32) -> Option<&'static BlockState> {
321        self.ordered_states.get(sid as usize).copied()
322    }
323
324    /// Get the default state from the given block name.
325    pub fn get_block_from_name(&self, name: &str) -> Option<&'static Block> {
326        self.name_to_blocks.get(name).cloned()
327    }
328
329    /// Return true if the palette contains the given block.
330    pub fn has_block(&self, block: &'static Block) -> bool {
331        self.block_to_indices.contains_key(&block.get_key())
332    }
333
334    /// Return true if the palette contains the given block state.
335    pub fn has_state(&self, state: &'static BlockState) -> bool {
336        self.has_block(state.get_block())
337    }
338
339    /// Check if the given state is registered in this palette, `Ok` is returned if true, in
340    /// the other case `Err` is returned with the error created by the given `err` closure.
341    pub fn check_state<E>(&self, state: &'static BlockState, err: impl FnOnce() -> E) -> Result<&'static BlockState, E> {
342        if self.has_state(state) { Ok(state) } else { Err(err()) }
343    }
344
345    /// Register a tag type that will be later possible to set to blocks.
346    pub fn register_tag_type(&mut self, tag_type: &'static TagType) {
347        self.tag_stores.insert(tag_type.get_key(), TagStore::Small(Vec::new()));
348    }
349
350    /// Set or unset a tag to some blocks.
351    pub fn set_blocks_tag<I>(&mut self, tag_type: &'static TagType, enabled: bool, blocks: I) -> Result<(), ()>
352    where
353        I: IntoIterator<Item = &'static Block>
354    {
355
356        const MAX_SMALL_LEN: usize = 8;
357
358        let store = self.tag_stores.get_mut(&tag_type.get_key()).ok_or(())?;
359
360        for block in blocks {
361
362            if let TagStore::Small(vec) = store {
363                let idx = vec.iter().position(move |&b| b == block);
364                if enabled {
365                    if idx.is_none() {
366                        if vec.len() >= MAX_SMALL_LEN {
367                            // If the small vector is too big, migrate to a big bit vector.
368                            let mut new_vec = BitVec::from_elem(self.block_to_indices.len(), false);
369                            for old_block in vec {
370                                let (idx, _) = *self.block_to_indices.get(&old_block.get_key()).ok_or(())?;
371                                new_vec.set(idx, true);
372                            }
373                            *store = TagStore::Big(new_vec);
374                        } else {
375                            vec.push(block);
376                        }
377                    }
378                } else if let Some(idx) = idx {
379                    vec.swap_remove(idx);
380                }
381            }
382
383            if let TagStore::Big(vec) = store {
384                let (idx, _) = *self.block_to_indices.get(&block.get_key()).ok_or(())?;
385                vec.set(idx, enabled);
386            }
387
388        }
389
390        Ok(())
391
392    }
393
394    /// Get the tag state on specific block, returning false if unknown block or tag type.
395    pub fn has_block_tag(&self, block: &'static Block, tag_type: &'static TagType) -> bool {
396        match self.tag_stores.get(&tag_type.get_key()) {
397            None => false,
398            Some(store) => {
399                match store {
400                    TagStore::Small(vec) => vec.iter().any(move |&b| b == block),
401                    TagStore::Big(vec) => match self.block_to_indices.get(&block.get_key()) {
402                        None => false,
403                        Some(&(idx, _)) => vec.get(idx).unwrap()
404                    }
405                }
406            }
407        }
408    }
409
410    pub fn blocks_count(&self) -> usize {
411        self.block_to_indices.len()
412    }
413
414    pub fn states_count(&self) -> usize {
415        self.ordered_states.len()
416    }
417
418    pub fn tags_count(&self) -> usize {
419        self.tag_stores.len()
420    }
421
422}
423
424#[derive(Debug)]
425enum TagStore {
426    Small(Vec<&'static Block>),
427    Big(BitVec)
428}
429
430#[macro_export]
431macro_rules! blocks_specs {
432    ($($v:vis $id:ident: [$($prop_const:ident),+];)*) => {
433        $(
434            $v static $id: [&'static dyn $crate::block::UntypedProperty; $crate::count!($($prop_const)+)] = [
435                $(&$prop_const),+
436            ];
437        )*
438    };
439}
440
441#[macro_export]
442macro_rules! blocks {
443    ($global_vis:vis $static_id:ident $namespace:literal [
444        $($block_id:ident $block_name:literal $($spec_id:ident)?),*
445        $(,)?
446    ]) => {
447
448        $($global_vis static $block_id: $crate::block::Block = $crate::block::Block::new(
449            concat!($namespace, ':', $block_name),
450            $crate::_blocks_spec!($($spec_id)?)
451        );)*
452
453        $global_vis static $static_id: [&'static $crate::block::Block; $crate::count!($($block_id)*)] = [
454            $(&$block_id),*
455        ];
456
457    };
458}
459
460#[macro_export]
461macro_rules! _blocks_spec {
462    () => { $crate::block::BlockSpec::Single };
463    ($spec_id:ident) => { $crate::block::BlockSpec::Complex(&$spec_id) }
464}