Skip to main content

steel_core/block_entity/entities/spawner/
mod.rs

1//! Normal mob-spawner block entity and its persistent spawn configuration.
2
3mod base_spawner;
4mod spawn_data;
5#[cfg(test)]
6mod tests;
7
8use std::sync::{Arc, Weak};
9
10use simdnbt::borrow::BaseNbtCompound as BorrowedNbtCompound;
11use simdnbt::owned::NbtCompound;
12use steel_registry::data_components::components::EntityData;
13use steel_registry::entity_type::EntityTypeRef;
14use steel_registry::vanilla_block_entity_types;
15use steel_utils::{BlockPos, BlockStateId, DowncastType, DowncastTypeKey};
16
17use crate::block_entity::{BlockEntity, BlockEntityBase};
18use crate::world::World;
19
20pub use base_spawner::{BaseSpawner, BaseSpawnerConfig};
21pub use spawn_data::{CustomSpawnRules, EquipmentTable, LightRange, SpawnData, WeightedSpawnData};
22
23/// Capability implemented by normal spawner block entities.
24pub trait Spawner {
25    /// Sets the next spawn entity type.
26    fn set_entity_id(&self, entity_type: EntityTypeRef);
27
28    /// Sets the next spawn entity type and merges the rest of `entity_data` into it.
29    fn set_entity_data(&self, entity_data: &EntityData);
30
31    /// Returns the ticks left until the next spawn cycle.
32    fn spawn_delay(&self) -> i32;
33
34    /// Sets the ticks left until the next spawn cycle.
35    fn set_spawn_delay(&self, delay: i32);
36
37    /// Returns the spawn-cycle limits.
38    fn config(&self) -> BaseSpawnerConfig;
39
40    /// Replaces the spawn-cycle limits.
41    fn set_config(&self, config: BaseSpawnerConfig);
42
43    /// Returns the weighted entries the next spawn data is picked from.
44    fn spawn_potentials(&self) -> Vec<WeightedSpawnData>;
45
46    /// Replaces the weighted entries the next spawn data is picked from.
47    fn set_spawn_potentials(&self, potentials: Vec<WeightedSpawnData>);
48
49    /// Returns the active spawn data, if one has been selected.
50    fn next_spawn_data(&self) -> Option<SpawnData>;
51
52    /// Replaces the active spawn data.
53    fn set_next_spawn_data(&self, data: SpawnData);
54}
55
56/// Concrete normal mob-spawner block entity.
57pub struct SpawnerBlockEntity {
58    base: BlockEntityBase,
59    spawner: BaseSpawner,
60}
61
62// SAFETY: This key is owned by Steel and uniquely identifies `SpawnerBlockEntity`.
63unsafe impl DowncastType for SpawnerBlockEntity {
64    const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:block_entity/spawner");
65}
66
67impl SpawnerBlockEntity {
68    /// Creates a normal mob-spawner block entity.
69    #[must_use]
70    pub fn new(level: Weak<World>, pos: BlockPos, state: BlockStateId) -> Self {
71        Self {
72            base: BlockEntityBase::new(&vanilla_block_entity_types::MOB_SPAWNER, level, pos, state),
73            spawner: BaseSpawner::new(),
74        }
75    }
76
77    fn save_and_sync(&self) {
78        self.set_changed();
79        if let Some(world) = self.get_level() {
80            world.send_block_updated(self.get_block_pos());
81        }
82    }
83}
84
85impl Spawner for SpawnerBlockEntity {
86    fn set_entity_id(&self, entity_type: EntityTypeRef) {
87        self.spawner.set_entity_id(entity_type);
88        self.save_and_sync();
89    }
90
91    fn set_entity_data(&self, entity_data: &EntityData) {
92        self.spawner.set_entity_data(entity_data);
93        self.save_and_sync();
94    }
95
96    fn spawn_delay(&self) -> i32 {
97        self.spawner.spawn_delay()
98    }
99
100    fn set_spawn_delay(&self, delay: i32) {
101        self.spawner.set_spawn_delay(delay);
102        self.save_and_sync();
103    }
104
105    fn config(&self) -> BaseSpawnerConfig {
106        self.spawner.config()
107    }
108
109    fn set_config(&self, config: BaseSpawnerConfig) {
110        self.spawner.set_config(config);
111        self.save_and_sync();
112    }
113
114    fn spawn_potentials(&self) -> Vec<WeightedSpawnData> {
115        self.spawner.spawn_potentials()
116    }
117
118    fn set_spawn_potentials(&self, potentials: Vec<WeightedSpawnData>) {
119        self.spawner.set_spawn_potentials(potentials);
120        self.save_and_sync();
121    }
122
123    fn next_spawn_data(&self) -> Option<SpawnData> {
124        self.spawner.next_spawn_data()
125    }
126
127    fn set_next_spawn_data(&self, data: SpawnData) {
128        self.spawner.set_next_spawn_data(data);
129        self.save_and_sync();
130    }
131}
132
133impl BlockEntity for SpawnerBlockEntity {
134    fn base(&self) -> &BlockEntityBase {
135        &self.base
136    }
137
138    fn load_additional(&self, nbt: &BorrowedNbtCompound<'_>) {
139        self.spawner.load(nbt);
140    }
141
142    fn save_additional(&self, nbt: &mut NbtCompound) {
143        self.spawner.save(nbt);
144    }
145
146    fn get_update_tag(&self) -> Option<NbtCompound> {
147        let mut nbt = self.save_custom_only();
148        nbt.remove("SpawnPotentials");
149        Some(nbt)
150    }
151
152    fn trigger_event(&self, event: i32, _data: i32) -> bool {
153        self.spawner.on_event_triggered(event)
154    }
155
156    fn as_spawner(&self) -> Option<&dyn Spawner> {
157        Some(self)
158    }
159
160    fn tick(&self, world: &Arc<World>) {
161        let result = self.spawner.server_tick(world, self.get_block_pos());
162        if result.state_changed {
163            self.set_changed();
164        }
165        if result.next_spawn_data_changed {
166            world.send_block_updated(self.get_block_pos());
167        }
168    }
169}