chunkedge_server/
movement.rs

1use bevy_app::prelude::*;
2use bevy_ecs::prelude::*;
3use chunkedge_entity::{HeadYaw, Look, OnGround, Position};
4use chunkedge_math::DVec3;
5use chunkedge_protocol::packets::play::{
6    MovePlayerPosC2s, MovePlayerPosRotC2s, MovePlayerRotC2s, MovePlayerStatusOnlyC2s,
7    MoveVehicleC2s,
8};
9
10use crate::event_loop::{EventLoopPreUpdate, PacketEvent};
11use crate::teleport::TeleportState;
12
13pub struct MovementPlugin;
14
15impl Plugin for MovementPlugin {
16    fn build(&self, app: &mut App) {
17        app.init_resource::<MovementSettings>()
18            .add_event::<MovementEvent>()
19            .add_systems(EventLoopPreUpdate, handle_client_movement);
20    }
21}
22
23/// Configuration resource for client movement checks.
24#[derive(Resource, Default)]
25pub struct MovementSettings; // TODO
26
27/// Event sent when a client successfully moves.
28#[derive(Event, Clone, Debug)]
29pub struct MovementEvent {
30    pub client: Entity,
31    pub position: DVec3,
32    pub old_position: DVec3,
33    pub look: Look,
34    pub old_look: Look,
35    pub on_ground: bool,
36    pub old_on_ground: bool,
37}
38
39fn handle_client_movement(
40    mut packets: EventReader<PacketEvent>,
41    mut clients: Query<(
42        &mut Position,
43        &mut Look,
44        &mut HeadYaw,
45        &mut OnGround,
46        &mut TeleportState,
47    )>,
48    mut movement_events: EventWriter<MovementEvent>,
49) {
50    for packet in packets.read() {
51        if let Some(pkt) = packet.decode::<MovePlayerPosC2s>() {
52            if let Ok((pos, look, head_yaw, on_ground, teleport_state)) =
53                clients.get_mut(packet.client)
54            {
55                let mov = MovementEvent {
56                    client: packet.client,
57                    position: pkt.position,
58                    old_position: pos.0,
59                    look: *look,
60                    old_look: *look,
61                    on_ground: pkt.flags.on_ground(),
62                    old_on_ground: on_ground.0,
63                };
64
65                handle(
66                    mov,
67                    pos,
68                    look,
69                    head_yaw,
70                    on_ground,
71                    teleport_state,
72                    &mut movement_events,
73                );
74            }
75        } else if let Some(pkt) = packet.decode::<MovePlayerPosRotC2s>() {
76            if let Ok((pos, look, head_yaw, on_ground, teleport_state)) =
77                clients.get_mut(packet.client)
78            {
79                let mov = MovementEvent {
80                    client: packet.client,
81                    position: pkt.position,
82                    old_position: pos.0,
83                    look: Look {
84                        yaw: pkt.yaw,
85                        pitch: pkt.pitch,
86                    },
87                    old_look: *look,
88                    on_ground: pkt.flags.on_ground(),
89                    old_on_ground: on_ground.0,
90                };
91
92                handle(
93                    mov,
94                    pos,
95                    look,
96                    head_yaw,
97                    on_ground,
98                    teleport_state,
99                    &mut movement_events,
100                );
101            }
102        } else if let Some(pkt) = packet.decode::<MovePlayerRotC2s>() {
103            if let Ok((pos, look, head_yaw, on_ground, teleport_state)) =
104                clients.get_mut(packet.client)
105            {
106                let mov = MovementEvent {
107                    client: packet.client,
108                    position: pos.0,
109                    old_position: pos.0,
110                    look: Look {
111                        yaw: pkt.yaw,
112                        pitch: pkt.pitch,
113                    },
114                    old_look: *look,
115                    on_ground: pkt.flags.on_ground(),
116                    old_on_ground: on_ground.0,
117                };
118
119                handle(
120                    mov,
121                    pos,
122                    look,
123                    head_yaw,
124                    on_ground,
125                    teleport_state,
126                    &mut movement_events,
127                );
128            }
129        } else if let Some(pkt) = packet.decode::<MovePlayerStatusOnlyC2s>() {
130            if let Ok((pos, look, head_yaw, on_ground, teleport_state)) =
131                clients.get_mut(packet.client)
132            {
133                let mov = MovementEvent {
134                    client: packet.client,
135                    position: pos.0,
136                    old_position: pos.0,
137                    look: *look,
138                    old_look: *look,
139                    on_ground: pkt.flags.on_ground(),
140                    old_on_ground: on_ground.0,
141                };
142
143                handle(
144                    mov,
145                    pos,
146                    look,
147                    head_yaw,
148                    on_ground,
149                    teleport_state,
150                    &mut movement_events,
151                );
152            }
153        } else if let Some(pkt) = packet.decode::<MoveVehicleC2s>() {
154            if let Ok((pos, look, head_yaw, on_ground, teleport_state)) =
155                clients.get_mut(packet.client)
156            {
157                let mov = MovementEvent {
158                    client: packet.client,
159                    position: pkt.position,
160                    old_position: pos.0,
161                    look: Look {
162                        yaw: pkt.yaw,
163                        pitch: pkt.pitch,
164                    },
165                    old_look: *look,
166                    on_ground: on_ground.0,
167                    old_on_ground: on_ground.0,
168                };
169
170                handle(
171                    mov,
172                    pos,
173                    look,
174                    head_yaw,
175                    on_ground,
176                    teleport_state,
177                    &mut movement_events,
178                );
179            }
180        }
181    }
182}
183
184fn handle(
185    mov: MovementEvent,
186    mut pos: Mut<Position>,
187    mut look: Mut<Look>,
188    mut head_yaw: Mut<HeadYaw>,
189    mut on_ground: Mut<OnGround>,
190    mut teleport_state: Mut<TeleportState>,
191    movement_events: &mut EventWriter<MovementEvent>,
192) {
193    if teleport_state.pending_teleports() != 0 {
194        return;
195    }
196
197    // TODO: check that the client isn't moving too fast / flying.
198    // TODO: check that the client isn't clipping through blocks.
199
200    pos.set_if_neq(Position(mov.position));
201    teleport_state.synced_pos = mov.position;
202    look.set_if_neq(mov.look);
203    teleport_state.synced_look = mov.look;
204    head_yaw.set_if_neq(HeadYaw(mov.look.yaw));
205    on_ground.set_if_neq(OnGround(mov.on_ground));
206
207    movement_events.send(mov);
208}