valence_server/
action.rs

1use bevy_app::prelude::*;
2use bevy_ecs::prelude::*;
3use derive_more::Deref;
4use valence_protocol::packets::play::player_action_c2s::PlayerAction;
5use valence_protocol::packets::play::{PlayerActionC2s, PlayerActionResponseS2c};
6use valence_protocol::{BlockPos, Direction, VarInt, WritePacket};
7
8use crate::client::{Client, UpdateClientsSet};
9use crate::event_loop::{EventLoopPreUpdate, PacketEvent};
10
11pub struct ActionPlugin;
12
13impl Plugin for ActionPlugin {
14    fn build(&self, app: &mut App) {
15        app.add_event::<DiggingEvent>()
16            .add_systems(EventLoopPreUpdate, handle_player_action)
17            .add_systems(
18                PostUpdate,
19                acknowledge_player_actions.in_set(UpdateClientsSet),
20            );
21    }
22}
23
24#[derive(Event, Copy, Clone, Debug)]
25pub struct DiggingEvent {
26    pub client: Entity,
27    pub position: BlockPos,
28    pub direction: Direction,
29    pub state: DiggingState,
30}
31
32#[derive(Copy, Clone, PartialEq, Eq, Debug)]
33pub enum DiggingState {
34    Start,
35    Abort,
36    Stop,
37}
38
39#[derive(Component, Copy, Clone, PartialEq, Eq, Default, Debug, Deref)]
40pub struct ActionSequence(i32);
41
42impl ActionSequence {
43    pub fn update(&mut self, val: i32) {
44        self.0 = self.0.max(val);
45    }
46
47    pub fn get(&self) -> i32 {
48        self.0
49    }
50}
51
52fn handle_player_action(
53    mut clients: Query<&mut ActionSequence>,
54    mut packets: EventReader<PacketEvent>,
55    mut digging_events: EventWriter<DiggingEvent>,
56) {
57    for packet in packets.read() {
58        if let Some(pkt) = packet.decode::<PlayerActionC2s>() {
59            if let Ok(mut seq) = clients.get_mut(packet.client) {
60                seq.update(pkt.sequence.0);
61            }
62
63            // TODO: check that digging is happening within configurable distance to client.
64            // TODO: check that blocks are being broken at the appropriate speeds.
65
66            match pkt.action {
67                PlayerAction::StartDestroyBlock => {
68                    digging_events.send(DiggingEvent {
69                        client: packet.client,
70                        position: pkt.position,
71                        direction: pkt.direction,
72                        state: DiggingState::Start,
73                    });
74                }
75                PlayerAction::AbortDestroyBlock => {
76                    digging_events.send(DiggingEvent {
77                        client: packet.client,
78                        position: pkt.position,
79                        direction: pkt.direction,
80                        state: DiggingState::Abort,
81                    });
82                }
83                PlayerAction::StopDestroyBlock => {
84                    digging_events.send(DiggingEvent {
85                        client: packet.client,
86                        position: pkt.position,
87                        direction: pkt.direction,
88                        state: DiggingState::Stop,
89                    });
90                }
91                PlayerAction::DropAllItems => {}
92                PlayerAction::DropItem => {}
93                PlayerAction::ReleaseUseItem => {}
94                PlayerAction::SwapItemWithOffhand => {}
95            }
96        }
97    }
98}
99
100fn acknowledge_player_actions(
101    mut clients: Query<(&mut Client, &mut ActionSequence), Changed<ActionSequence>>,
102) {
103    for (mut client, mut action_seq) in &mut clients {
104        if action_seq.0 != 0 {
105            client.write_packet(&PlayerActionResponseS2c {
106                sequence: VarInt(action_seq.0),
107            });
108
109            action_seq.0 = 0;
110        }
111    }
112}