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,
68 Login,
79 Spawn,
88 Chat(ChatPacket),
90 Tick,
92 #[cfg(feature = "packet-event")]
93 Packet(Arc<azalea_protocol::packets::game::ClientboundGamePacket>),
111 AddPlayer(PlayerInfo),
114 RemovePlayer(PlayerInfo),
117 UpdatePlayer(PlayerInfo),
120 Death(Option<Arc<ClientboundPlayerCombatKill>>),
122 KeepAlive(u64),
124 Disconnect(Option<FormattedText>),
126 ReceiveChunk(ChunkPos),
127}
128
129#[derive(Component, Deref, DerefMut)]
136pub struct LocalPlayerEvents(pub mpsc::UnboundedSender<Event>);
137
138pub struct EventsPlugin;
139impl Plugin for EventsPlugin {
140 fn build(&self, app: &mut App) {
141 app.add_systems(
142 Update,
143 (
144 chat_listener,
145 login_listener,
146 spawn_listener,
147 #[cfg(feature = "packet-event")]
148 packet_listener,
149 add_player_listener,
150 update_player_listener,
151 remove_player_listener,
152 keepalive_listener,
153 death_listener,
154 disconnect_listener,
155 receive_chunk_listener,
156 ),
157 )
158 .add_systems(
159 PreUpdate,
160 init_listener.before(super::connection::read_packets),
161 )
162 .add_systems(GameTick, tick_listener);
163 }
164}
165
166pub fn init_listener(query: Query<&LocalPlayerEvents, Added<LocalPlayerEvents>>) {
168 for local_player_events in &query {
169 let _ = local_player_events.send(Event::Init);
170 }
171}
172
173pub fn login_listener(
175 query: Query<(Entity, &LocalPlayerEvents), Added<MinecraftEntityId>>,
176 mut commands: Commands,
177) {
178 for (entity, local_player_events) in &query {
179 let _ = local_player_events.send(Event::Login);
180 commands.entity(entity).remove::<SentSpawnEvent>();
181 }
182}
183
184#[derive(Component)]
191pub struct SentSpawnEvent;
192#[allow(clippy::type_complexity)]
193pub fn spawn_listener(
194 query: Query<(Entity, &LocalPlayerEvents), (Added<InLoadedChunk>, Without<SentSpawnEvent>)>,
195 mut commands: Commands,
196) {
197 for (entity, local_player_events) in &query {
198 let _ = local_player_events.send(Event::Spawn);
199 commands.entity(entity).insert(SentSpawnEvent);
200 }
201}
202
203pub fn chat_listener(
204 query: Query<&LocalPlayerEvents>,
205 mut events: MessageReader<ChatReceivedEvent>,
206) {
207 for event in events.read() {
208 if let Ok(local_player_events) = query.get(event.entity) {
209 let _ = local_player_events.send(Event::Chat(event.packet.clone()));
210 }
211 }
212}
213
214pub fn tick_listener(query: Query<&LocalPlayerEvents, With<InstanceName>>) {
216 for local_player_events in &query {
217 let _ = local_player_events.send(Event::Tick);
218 }
219}
220
221#[cfg(feature = "packet-event")]
222pub fn packet_listener(
223 query: Query<&LocalPlayerEvents>,
224 mut events: MessageReader<super::packet::game::ReceiveGamePacketEvent>,
225) {
226 for event in events.read() {
227 if let Ok(local_player_events) = query.get(event.entity) {
228 let _ = local_player_events.send(Event::Packet(event.packet.clone()));
229 }
230 }
231}
232
233pub fn add_player_listener(
234 query: Query<&LocalPlayerEvents>,
235 mut events: MessageReader<AddPlayerEvent>,
236) {
237 for event in events.read() {
238 let local_player_events = query
239 .get(event.entity)
240 .expect("Non-local entities shouldn't be able to receive add player events");
241 let _ = local_player_events.send(Event::AddPlayer(event.info.clone()));
242 }
243}
244
245pub fn update_player_listener(
246 query: Query<&LocalPlayerEvents>,
247 mut events: MessageReader<UpdatePlayerEvent>,
248) {
249 for event in events.read() {
250 let local_player_events = query
251 .get(event.entity)
252 .expect("Non-local entities shouldn't be able to receive update player events");
253 let _ = local_player_events.send(Event::UpdatePlayer(event.info.clone()));
254 }
255}
256
257pub fn remove_player_listener(
258 query: Query<&LocalPlayerEvents>,
259 mut events: MessageReader<RemovePlayerEvent>,
260) {
261 for event in events.read() {
262 let local_player_events = query
263 .get(event.entity)
264 .expect("Non-local entities shouldn't be able to receive remove player events");
265 let _ = local_player_events.send(Event::RemovePlayer(event.info.clone()));
266 }
267}
268
269pub fn death_listener(query: Query<&LocalPlayerEvents>, mut events: MessageReader<DeathEvent>) {
270 for event in events.read() {
271 if let Ok(local_player_events) = query.get(event.entity) {
272 let _ = local_player_events.send(Event::Death(event.packet.clone().map(|p| p.into())));
273 }
274 }
275}
276
277pub fn dead_component_listener(query: Query<&LocalPlayerEvents, Added<Dead>>) {
281 for local_player_events in &query {
282 local_player_events.send(Event::Death(None)).unwrap();
283 }
284}
285
286pub fn keepalive_listener(
287 query: Query<&LocalPlayerEvents>,
288 mut events: MessageReader<KeepAliveEvent>,
289) {
290 for event in events.read() {
291 let local_player_events = query
292 .get(event.entity)
293 .expect("Non-local entities shouldn't be able to receive keepalive events");
294 let _ = local_player_events.send(Event::KeepAlive(event.id));
295 }
296}
297
298pub fn disconnect_listener(
299 query: Query<&LocalPlayerEvents>,
300 mut events: MessageReader<DisconnectEvent>,
301) {
302 for event in events.read() {
303 if let Ok(local_player_events) = query.get(event.entity) {
304 let _ = local_player_events.send(Event::Disconnect(event.reason.clone()));
305 }
306 }
307}
308
309pub fn receive_chunk_listener(
310 query: Query<&LocalPlayerEvents>,
311 mut events: MessageReader<ReceiveChunkEvent>,
312) {
313 for event in events.read() {
314 if let Ok(local_player_events) = query.get(event.entity) {
315 let _ = local_player_events.send(Event::ReceiveChunk(ChunkPos::new(
316 event.packet.x,
317 event.packet.z,
318 )));
319 }
320 }
321}