steel_core/block_entity/entities/
beehive.rs1use std::sync::Weak;
4
5use simdnbt::borrow::{BaseNbtCompound as BorrowedNbtCompound, NbtCompound as NbtCompoundView};
6use simdnbt::owned::{NbtCompound, NbtList};
7use steel_registry::{vanilla_block_entity_types, vanilla_entities};
8use steel_utils::nbt::NbtNumeric as _;
9use steel_utils::{BlockPos, BlockStateId, DowncastType, DowncastTypeKey, locks::SyncMutex};
10
11use crate::block_entity::{BlockEntity, BlockEntityBase};
12use crate::world::World;
13
14pub const BEEHIVE_MAX_OCCUPANTS: usize = 3;
16pub const BEEHIVE_MIN_OCCUPATION_TICKS_NECTARLESS: i32 = 600;
18
19struct BeeOccupant {
20 entity_data: NbtCompound,
21 ticks_in_hive: i32,
22 min_ticks_in_hive: i32,
23}
24
25impl BeeOccupant {
26 fn worldgen(ticks_in_hive: i32) -> Self {
27 Self {
28 entity_data: default_bee_entity_data(),
29 ticks_in_hive,
30 min_ticks_in_hive: BEEHIVE_MIN_OCCUPATION_TICKS_NECTARLESS,
31 }
32 }
33
34 fn load(nbt: NbtCompoundView<'_, '_>) -> Self {
35 let entity_data = nbt
36 .compound("entity_data")
37 .map_or_else(default_bee_entity_data, |entity_data| {
38 entity_data.to_owned()
39 });
40 let ticks_in_hive = nbt
41 .get("ticks_in_hive")
42 .and_then(|tag| tag.codec_i32())
43 .unwrap_or(0);
44 let min_ticks_in_hive = nbt
45 .get("min_ticks_in_hive")
46 .and_then(|tag| tag.codec_i32())
47 .unwrap_or(BEEHIVE_MIN_OCCUPATION_TICKS_NECTARLESS);
48
49 Self {
50 entity_data,
51 ticks_in_hive,
52 min_ticks_in_hive,
53 }
54 }
55
56 fn save(&self) -> NbtCompound {
57 let mut nbt = NbtCompound::new();
58 nbt.insert("entity_data", self.entity_data.clone());
59 nbt.insert("ticks_in_hive", self.ticks_in_hive);
60 nbt.insert("min_ticks_in_hive", self.min_ticks_in_hive);
61 nbt
62 }
63}
64
65fn default_bee_entity_data() -> NbtCompound {
66 let mut entity_data = NbtCompound::new();
67 entity_data.insert("id", vanilla_entities::BEE.key.to_string());
68 entity_data
69}
70
71struct BeehiveState {
72 stored: Vec<BeeOccupant>,
73}
74
75impl BeehiveState {
76 fn push_occupant(&mut self, occupant: BeeOccupant) -> bool {
77 if self.stored.len() >= BEEHIVE_MAX_OCCUPANTS {
78 return false;
79 }
80
81 self.stored.push(occupant);
82 true
83 }
84}
85
86pub struct BeehiveBlockEntity {
91 base: BlockEntityBase,
92 state: SyncMutex<BeehiveState>,
93}
94
95unsafe impl DowncastType for BeehiveBlockEntity {
97 const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:block_entity/beehive");
98}
99
100impl BeehiveBlockEntity {
101 #[must_use]
103 pub fn new(level: Weak<World>, pos: BlockPos, state: BlockStateId) -> Self {
104 Self {
105 base: BlockEntityBase::new(&vanilla_block_entity_types::BEEHIVE, level, pos, state),
106 state: SyncMutex::new(BeehiveState { stored: Vec::new() }),
107 }
108 }
109
110 pub fn store_worldgen_bee(&self, ticks_in_hive: i32) {
114 let stored = {
115 self.state
116 .lock()
117 .push_occupant(BeeOccupant::worldgen(ticks_in_hive))
118 };
119 if stored {
120 BlockEntity::set_changed(self);
121 }
122 }
123
124 #[must_use]
126 pub fn occupant_count(&self) -> usize {
127 self.state.lock().stored.len()
128 }
129
130 #[must_use]
132 pub fn is_empty(&self) -> bool {
133 self.state.lock().stored.is_empty()
134 }
135}
136
137impl BlockEntity for BeehiveBlockEntity {
138 fn base(&self) -> &BlockEntityBase {
139 &self.base
140 }
141
142 fn load_additional(&self, nbt: &BorrowedNbtCompound<'_>) {
143 let nbt: NbtCompoundView<'_, '_> = nbt.into();
144 let mut stored = Vec::new();
145
146 if let Some(bees) = nbt.list("bees")
147 && let Some(compounds) = bees.compounds()
148 {
149 for compound in compounds {
150 if stored.len() >= BEEHIVE_MAX_OCCUPANTS {
151 break;
152 }
153 stored.push(BeeOccupant::load(compound));
154 }
155 }
156
157 self.state.lock().stored = stored;
158 }
159
160 fn save_additional(&self, nbt: &mut NbtCompound) {
161 let bees = self
162 .state
163 .lock()
164 .stored
165 .iter()
166 .map(BeeOccupant::save)
167 .collect::<Vec<_>>();
168 nbt.insert("bees", NbtList::Compound(bees));
169 }
170}