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