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 if let Ok(local_player_events) = query.get(event.entity) {
239 let _ = local_player_events.send(Event::AddPlayer(event.info.clone()));
240 }
241 }
242}
243
244pub fn update_player_listener(
245 query: Query<&LocalPlayerEvents>,
246 mut events: MessageReader<UpdatePlayerEvent>,
247) {
248 for event in events.read() {
249 if let Ok(local_player_events) = query.get(event.entity) {
250 let _ = local_player_events.send(Event::UpdatePlayer(event.info.clone()));
251 }
252 }
253}
254
255pub fn remove_player_listener(
256 query: Query<&LocalPlayerEvents>,
257 mut events: MessageReader<RemovePlayerEvent>,
258) {
259 for event in events.read() {
260 if let Ok(local_player_events) = query.get(event.entity) {
261 let _ = local_player_events.send(Event::RemovePlayer(event.info.clone()));
262 }
263 }
264}
265
266pub fn death_listener(query: Query<&LocalPlayerEvents>, mut events: MessageReader<DeathEvent>) {
267 for event in events.read() {
268 if let Ok(local_player_events) = query.get(event.entity) {
269 let _ = local_player_events.send(Event::Death(event.packet.clone().map(|p| p.into())));
270 }
271 }
272}
273
274pub fn dead_component_listener(query: Query<&LocalPlayerEvents, Added<Dead>>) {
278 for local_player_events in &query {
279 local_player_events.send(Event::Death(None)).unwrap();
280 }
281}
282
283pub fn keepalive_listener(
284 query: Query<&LocalPlayerEvents>,
285 mut events: MessageReader<KeepAliveEvent>,
286) {
287 for event in events.read() {
288 if let Ok(local_player_events) = query.get(event.entity) {
289 let _ = local_player_events.send(Event::KeepAlive(event.id));
290 }
291 }
292}
293
294pub fn disconnect_listener(
295 query: Query<&LocalPlayerEvents>,
296 mut events: MessageReader<DisconnectEvent>,
297) {
298 for event in events.read() {
299 if let Ok(local_player_events) = query.get(event.entity) {
300 let _ = local_player_events.send(Event::Disconnect(event.reason.clone()));
301 }
302 }
303}
304
305pub fn receive_chunk_listener(
306 query: Query<&LocalPlayerEvents>,
307 mut events: MessageReader<ReceiveChunkEvent>,
308) {
309 for event in events.read() {
310 if let Ok(local_player_events) = query.get(event.entity) {
311 let _ = local_player_events.send(Event::ReceiveChunk(ChunkPos::new(
312 event.packet.x,
313 event.packet.z,
314 )));
315 }
316 }
317}