Skip to main content

steel_core/block_entity/
components.rs

1//! Item-component application for block entities.
2//!
3//! Components read into entity fields are kept separate from the remaining
4//! item component patch.
5
6use rustc_hash::FxHashSet;
7use steel_registry::data_components::{
8    Component, ComponentPatchEntry, DataComponentMap, DataComponentType,
9    vanilla_components::{BLOCK_ENTITY_DATA, BLOCK_STATE},
10};
11use steel_registry::item_stack::ItemStack;
12use steel_utils::{DowncastType, Identifier};
13
14/// Reads effective item components and tracks which ones are represented by
15/// entity fields, excluding them from the stored component map.
16pub struct ImplicitComponentGetter<'a> {
17    stack: &'a ItemStack,
18    consumed: FxHashSet<Identifier>,
19}
20
21impl<'a> ImplicitComponentGetter<'a> {
22    pub(super) fn new(stack: &'a ItemStack) -> Self {
23        let consumed = [BLOCK_ENTITY_DATA.key().clone(), BLOCK_STATE.key().clone()]
24            .into_iter()
25            .collect();
26        Self { stack, consumed }
27    }
28
29    /// Reads a component from the placed stack and marks it implicit.
30    pub fn get<T: Component + DowncastType + Clone>(
31        &mut self,
32        component: DataComponentType<T>,
33    ) -> Option<T> {
34        self.consumed.insert(component.key().clone());
35        self.stack.get(component).cloned()
36    }
37
38    /// Reads a component from the placed stack, falling back to `default`, and marks it implicit.
39    pub fn get_or_default<T: Component + DowncastType + Clone>(
40        &mut self,
41        component: DataComponentType<T>,
42        default: T,
43    ) -> T {
44        self.get(component).unwrap_or(default)
45    }
46
47    /// Returns patched components that were set and not read into entity fields.
48    pub(super) fn remaining_stored_components(self) -> DataComponentMap {
49        let mut components = DataComponentMap::new();
50        for (key, entry) in self.stack.components_patch().iter() {
51            if self.consumed.contains(key) {
52                continue;
53            }
54            if let ComponentPatchEntry::Set(data) = entry {
55                components.set_raw(key.clone(), data.clone());
56            }
57        }
58        components
59    }
60}