azalea_client/plugins/
events.rs1use std::sync::Arc;
5
6use azalea_chat::FormattedText;
7use azalea_core::{position::ChunkPos, tick::GameTick};
8use azalea_entity::{Dead, InLoadedChunk};
9use azalea_protocol::packets::game::c_player_combat_kill::ClientboundPlayerCombatKill;
10use azalea_world::{InstanceName, MinecraftEntityId};
11use bevy_app::{App, Plugin, PreUpdate, Update};
12use bevy_ecs::prelude::*;
13use derive_more::{Deref, DerefMut};
14use tokio::sync::mpsc;
15
16use crate::{
17 chat::{ChatPacket, ChatReceivedEvent},
18 chunks::ReceiveChunkEvent,
19 disconnect::DisconnectEvent,
20 packet::game::{
21 AddPlayerEvent, DeathEvent, KeepAliveEvent, RemovePlayerEvent, UpdatePlayerEvent,
22 },
23 player::PlayerInfo,
24};
25
26#[derive(Debug, Clone)]
54#[non_exhaustive]
55pub enum Event {
56 Init,
64 Login,
75 Spawn,
84 Chat(ChatPacket),
86 Tick,
88 #[cfg(feature = "packet-event")]
89 Packet(Arc<azalea_protocol::packets::game::ClientboundGamePacket>),
107 AddPlayer(PlayerInfo),
110 RemovePlayer(PlayerInfo),
113 UpdatePlayer(PlayerInfo),
116 Death(Option<Arc<ClientboundPlayerCombatKill>>),
118 KeepAlive(u64),
120 Disconnect(Option<FormattedText>),
122 ReceiveChunk(ChunkPos),
123}
124
125#[derive(Component, Deref, DerefMut)]
131pub struct LocalPlayerEvents(pub mpsc::UnboundedSender<Event>);
132
133pub struct EventsPlugin;
134impl Plugin for EventsPlugin {
135 fn build(&self, app: &mut App) {
136 app.add_systems(
137 Update,
138 (
139 chat_listener,
140 login_listener,
141 spawn_listener,
142 #[cfg(feature = "packet-event")]
143 packet_listener,
144 add_player_listener,
145 update_player_listener,
146 remove_player_listener,
147 keepalive_listener,
148 death_listener,
149 disconnect_listener,
150 receive_chunk_listener,
151 ),
152 )
153 .add_systems(
154 PreUpdate,
155 init_listener.before(super::connection::read_packets),
156 )
157 .add_systems(GameTick, tick_listener);
158 }
159}
160
161pub fn init_listener(query: Query<&LocalPlayerEvents, Added<LocalPlayerEvents>>) {
163 for local_player_events in &query {
164 let _ = local_player_events.send(Event::Init);
165 }
166}
167
168pub fn login_listener(
170 query: Query<(Entity, &LocalPlayerEvents), Added<MinecraftEntityId>>,
171 mut commands: Commands,
172) {
173 for (entity, local_player_events) in &query {
174 let _ = local_player_events.send(Event::Login);
175 commands.entity(entity).remove::<SentSpawnEvent>();
176 }
177}
178
179#[derive(Component)]
186pub struct SentSpawnEvent;
187#[allow(clippy::type_complexity)]
188pub fn spawn_listener(
189 query: Query<(Entity, &LocalPlayerEvents), (Added<InLoadedChunk>, Without<SentSpawnEvent>)>,
190 mut commands: Commands,
191) {
192 for (entity, local_player_events) in &query {
193 let _ = local_player_events.send(Event::Spawn);
194 commands.entity(entity).insert(SentSpawnEvent);
195 }
196}
197
198pub fn chat_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<ChatReceivedEvent>) {
199 for event in events.read() {
200 if let Ok(local_player_events) = query.get(event.entity) {
201 let _ = local_player_events.send(Event::Chat(event.packet.clone()));
202 }
203 }
204}
205
206pub fn tick_listener(query: Query<&LocalPlayerEvents, With<InstanceName>>) {
208 for local_player_events in &query {
209 let _ = local_player_events.send(Event::Tick);
210 }
211}
212
213#[cfg(feature = "packet-event")]
214pub fn packet_listener(
215 query: Query<&LocalPlayerEvents>,
216 mut events: EventReader<super::packet::game::ReceiveGamePacketEvent>,
217) {
218 for event in events.read() {
219 if let Ok(local_player_events) = query.get(event.entity) {
220 let _ = local_player_events.send(Event::Packet(event.packet.clone()));
221 }
222 }
223}
224
225pub fn add_player_listener(
226 query: Query<&LocalPlayerEvents>,
227 mut events: EventReader<AddPlayerEvent>,
228) {
229 for event in events.read() {
230 let local_player_events = query
231 .get(event.entity)
232 .expect("Non-local entities shouldn't be able to receive add player events");
233 let _ = local_player_events.send(Event::AddPlayer(event.info.clone()));
234 }
235}
236
237pub fn update_player_listener(
238 query: Query<&LocalPlayerEvents>,
239 mut events: EventReader<UpdatePlayerEvent>,
240) {
241 for event in events.read() {
242 let local_player_events = query
243 .get(event.entity)
244 .expect("Non-local entities shouldn't be able to receive update player events");
245 let _ = local_player_events.send(Event::UpdatePlayer(event.info.clone()));
246 }
247}
248
249pub fn remove_player_listener(
250 query: Query<&LocalPlayerEvents>,
251 mut events: EventReader<RemovePlayerEvent>,
252) {
253 for event in events.read() {
254 let local_player_events = query
255 .get(event.entity)
256 .expect("Non-local entities shouldn't be able to receive remove player events");
257 let _ = local_player_events.send(Event::RemovePlayer(event.info.clone()));
258 }
259}
260
261pub fn death_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<DeathEvent>) {
262 for event in events.read() {
263 if let Ok(local_player_events) = query.get(event.entity) {
264 let _ = local_player_events.send(Event::Death(event.packet.clone().map(|p| p.into())));
265 }
266 }
267}
268
269pub fn dead_component_listener(query: Query<&LocalPlayerEvents, Added<Dead>>) {
273 for local_player_events in &query {
274 local_player_events.send(Event::Death(None)).unwrap();
275 }
276}
277
278pub fn keepalive_listener(
279 query: Query<&LocalPlayerEvents>,
280 mut events: EventReader<KeepAliveEvent>,
281) {
282 for event in events.read() {
283 let local_player_events = query
284 .get(event.entity)
285 .expect("Non-local entities shouldn't be able to receive keepalive events");
286 let _ = local_player_events.send(Event::KeepAlive(event.id));
287 }
288}
289
290pub fn disconnect_listener(
291 query: Query<&LocalPlayerEvents>,
292 mut events: EventReader<DisconnectEvent>,
293) {
294 for event in events.read() {
295 if let Ok(local_player_events) = query.get(event.entity) {
296 let _ = local_player_events.send(Event::Disconnect(event.reason.clone()));
297 }
298 }
299}
300
301pub fn receive_chunk_listener(
302 query: Query<&LocalPlayerEvents>,
303 mut events: EventReader<ReceiveChunkEvent>,
304) {
305 for event in events.read() {
306 if let Ok(local_player_events) = query.get(event.entity) {
307 let _ = local_player_events.send(Event::ReceiveChunk(ChunkPos::new(
308 event.packet.x,
309 event.packet.z,
310 )));
311 }
312 }
313}