steel_core/world/
events.rs1use super::{
2 Arc, BlockEntityTypeRef, BlockPos, CBlockDestruction, ChunkPos, DVec3, ItemEntity, ItemStack,
3 NbtCompound, RegistryEntry, SectionPos, World,
4};
5
6fn triangle_random(mode: f64, deviation: f64) -> f64 {
11 mode + deviation * (rand::random::<f64>() - rand::random::<f64>())
12}
13
14impl World {
15 #[expect(
25 clippy::cast_sign_loss,
26 reason = "value is clamped to -1..=9 before cast; -1 wraps intentionally to 255 as sentinel"
27 )]
28 pub fn broadcast_block_destruction(&self, entity_id: i32, pos: BlockPos, progress: i32) {
29 let chunk = ChunkPos::new(
30 SectionPos::block_to_section_coord(pos.x()),
31 SectionPos::block_to_section_coord(pos.z()),
32 );
33 let packet = CBlockDestruction {
34 id: entity_id,
35 pos,
36 progress: progress.clamp(-1, 9) as u8,
37 };
38 self.broadcast_to_nearby(chunk, packet, Some(entity_id));
39 }
40
41 pub fn broadcast_block_entity_update(
50 &self,
51 pos: BlockPos,
52 block_entity_type: BlockEntityTypeRef,
53 nbt: NbtCompound,
54 ) {
55 use steel_protocol::packets::game::CBlockEntityData;
56 use steel_utils::serial::OptionalNbt;
57
58 let chunk = ChunkPos::new(
59 SectionPos::block_to_section_coord(pos.x()),
60 SectionPos::block_to_section_coord(pos.z()),
61 );
62
63 let type_id = block_entity_type.id();
65
66 let packet = CBlockEntityData {
67 pos,
68 block_entity_type: type_id as i32,
69 nbt: OptionalNbt(Some(nbt)),
70 };
71
72 self.broadcast_to_nearby(chunk, packet, None);
73 }
74
75 pub(crate) fn broadcast_block_entity_if_needed(&self, pos: BlockPos) {
78 let Some(block_entity) = self.get_block_entity(pos) else {
79 return;
80 };
81 let update = block_entity
82 .get_update_tag()
83 .map(|tag| (block_entity.get_type(), tag));
84 if let Some((block_entity_type, tag)) = update {
85 self.broadcast_block_entity_update(pos, block_entity_type, tag);
86 }
87 }
88
89 pub fn drop_item_stack(self: &Arc<Self>, pos: BlockPos, mut item: ItemStack) {
99 use crate::entity::next_entity_id;
100 use steel_registry::vanilla_entities;
101
102 const VELOCITY_SPREAD: f64 = 0.114_850_001_711_398_36;
105
106 if item.is_empty() {
107 return;
108 }
109
110 let item_width = f64::from(vanilla_entities::ITEM.dimensions.width);
112 let center_range = 1.0 - item_width;
113 let half_size = item_width / 2.0;
114
115 while !item.is_empty() {
118 let split_count = (rand::random::<u32>() % 21 + 10) as i32;
120 let split_stack = item.split(split_count);
121
122 if split_stack.is_empty() {
123 break;
124 }
125
126 let x = f64::from(pos.x()).floor() + rand::random::<f64>() * center_range + half_size;
128 let y = f64::from(pos.y()).floor() + rand::random::<f64>() * center_range;
129 let z = f64::from(pos.z()).floor() + rand::random::<f64>() * center_range + half_size;
130
131 let vx = triangle_random(0.0, VELOCITY_SPREAD);
133 let vy = triangle_random(0.2, VELOCITY_SPREAD);
134 let vz = triangle_random(0.0, VELOCITY_SPREAD);
135
136 let entity_id = next_entity_id();
137 let entity = Arc::new(ItemEntity::with_item_and_velocity(
138 &vanilla_entities::ITEM,
139 entity_id,
140 DVec3::new(x, y, z),
141 split_stack,
142 DVec3::new(vx, vy, vz),
143 Arc::downgrade(self),
144 ));
145 entity.set_default_pickup_delay();
146 if let Err(error) = self.try_add_entity(entity) {
147 log::warn!("Failed to drop item stack entity: {error}");
148 }
149 }
150 }
151}