basalt_api/events/block.rs
1//! Block interaction events: breaking, placing, right-click.
2
3use crate::components::BlockPosition;
4
5/// A player broke a block.
6///
7/// Fired when the server receives a `BlockDig` packet with status 0.
8/// The breaking player is available via `ctx.player()`.
9#[derive(Debug, Clone)]
10pub struct BlockBrokenEvent {
11 /// Position of the broken block.
12 pub position: BlockPosition,
13 /// Block state that was at this position before breaking.
14 pub block_state: u16,
15 /// Sequence number for client acknowledgement.
16 pub sequence: i32,
17 /// Whether this event has been cancelled by a Validate handler.
18 pub cancelled: bool,
19}
20crate::game_cancellable_event!(BlockBrokenEvent);
21
22/// A player placed a block.
23///
24/// Fired when the server receives a `BlockPlace` packet with a valid
25/// held item. The placement position has already been computed from
26/// the target block + face offset. The placing player is available
27/// via `ctx.player()`.
28#[derive(Debug, Clone)]
29pub struct BlockPlacedEvent {
30 /// Position where the block was placed.
31 pub position: BlockPosition,
32 /// The block state ID that was placed.
33 pub block_state: u16,
34 /// Sequence number for client acknowledgement.
35 pub sequence: i32,
36 /// Whether this event has been cancelled by a Validate handler.
37 pub cancelled: bool,
38}
39crate::game_cancellable_event!(BlockPlacedEvent);
40
41/// A player right-clicked on a block.
42///
43/// Fired before any container interaction or block placement.
44/// If cancelled (e.g., ContainerPlugin opens a chest), the game
45/// loop skips block placement. The interacting player is available
46/// via `ctx.player()`.
47#[derive(Debug, Clone)]
48pub struct PlayerInteractEvent {
49 /// Position of the clicked block.
50 pub position: BlockPosition,
51 /// Block state at the clicked position.
52 pub block_state: u16,
53 /// Face direction clicked (0-5).
54 pub direction: i32,
55 /// Sequence number for acknowledgement.
56 pub sequence: i32,
57 /// Whether this event has been cancelled.
58 pub cancelled: bool,
59}
60crate::game_cancellable_event!(PlayerInteractEvent);
61
62#[cfg(test)]
63mod tests {
64 use crate::events::Event;
65
66 use super::*;
67
68 #[test]
69 fn block_broken_cancellation() {
70 let mut event = BlockBrokenEvent {
71 position: BlockPosition { x: 0, y: 64, z: 0 },
72 block_state: 1,
73 sequence: 1,
74 cancelled: false,
75 };
76 assert!(!event.is_cancelled());
77 event.cancel();
78 assert!(event.is_cancelled());
79 }
80
81 #[test]
82 fn block_placed_downcast_roundtrip() {
83 let mut event = BlockPlacedEvent {
84 position: BlockPosition { x: 5, y: 64, z: 3 },
85 block_state: 1,
86 sequence: 42,
87 cancelled: false,
88 };
89 let any = event.as_any_mut();
90 let concrete = any.downcast_mut::<BlockPlacedEvent>().unwrap();
91 assert_eq!(concrete.block_state, 1);
92 }
93
94 #[test]
95 fn event_routing() {
96 use crate::events::{BusKind, EventRouting};
97 assert_eq!(BlockBrokenEvent::BUS, BusKind::Game);
98 assert_eq!(BlockPlacedEvent::BUS, BusKind::Game);
99 assert_eq!(PlayerInteractEvent::BUS, BusKind::Game);
100 }
101}