1use std::sync::Arc;
5
6use azalea_chat::FormattedText;
7use azalea_client::join::ConnectionFailedEvent;
8use azalea_core::{entity_id::MinecraftEntityId, position::ChunkPos, tick::GameTick};
9use azalea_entity::{Dead, InLoadedChunk};
10use azalea_protocol::{
11 connect::ConnectionError, packets::game::c_player_combat_kill::ClientboundPlayerCombatKill,
12};
13use azalea_world::WorldName;
14use bevy_app::{App, Plugin, PreUpdate, Update};
15use bevy_ecs::prelude::*;
16use derive_more::{Deref, DerefMut};
17use tokio::sync::mpsc;
18
19use crate::{
20 chat::{ChatPacket, ChatReceivedEvent},
21 chunks::ReceiveChunkEvent,
22 disconnect::DisconnectEvent,
23 packet::game::{
24 AddPlayerEvent, DeathEvent, KeepAliveEvent, RemovePlayerEvent, UpdatePlayerEvent,
25 },
26 player::PlayerInfo,
27};
28
29#[derive(Clone, Debug)]
57#[non_exhaustive]
58pub enum Event {
59 Init,
71 Login,
82 Spawn,
91 Chat(ChatPacket),
93 Tick,
95 #[cfg(feature = "packet-event")]
96 Packet(Arc<azalea_protocol::packets::game::ClientboundGamePacket>),
114 AddPlayer(PlayerInfo),
117 RemovePlayer(PlayerInfo),
120 UpdatePlayer(PlayerInfo),
123 Death(Option<Arc<ClientboundPlayerCombatKill>>),
125 KeepAlive(u64),
127 Disconnect(Option<FormattedText>),
131 ConnectionFailed(Arc<ConnectionError>),
136 ReceiveChunk(ChunkPos),
137}
138
139#[derive(Component, Deref, DerefMut)]
145pub struct LocalPlayerEvents(pub mpsc::UnboundedSender<Event>);
146
147pub struct EventsPlugin;
148impl Plugin for EventsPlugin {
149 fn build(&self, app: &mut App) {
150 app.add_systems(
151 Update,
152 (
153 chat_listener,
154 login_listener,
155 spawn_listener,
156 #[cfg(feature = "packet-event")]
157 packet_listener,
158 add_player_listener,
159 update_player_listener,
160 remove_player_listener,
161 keepalive_listener,
162 death_listener.after(azalea_client::packet::death_event_on_0_health),
163 disconnect_listener,
164 connection_failed_listener.after(azalea_client::join::poll_create_connection_task),
165 receive_chunk_listener,
166 ),
167 )
168 .add_systems(
169 PreUpdate,
170 init_listener.before(super::connection::read_packets),
171 )
172 .add_systems(GameTick, tick_listener);
173 }
174}
175
176pub fn init_listener(query: Query<&LocalPlayerEvents, Added<LocalPlayerEvents>>) {
178 for local_player_events in &query {
179 let _ = local_player_events.send(Event::Init);
180 }
181}
182
183pub fn login_listener(
185 query: Query<(Entity, &LocalPlayerEvents), Added<MinecraftEntityId>>,
186 mut commands: Commands,
187) {
188 for (entity, local_player_events) in &query {
189 let _ = local_player_events.send(Event::Login);
190 commands.entity(entity).remove::<SentSpawnEvent>();
191 }
192}
193
194#[derive(Component)]
201pub struct SentSpawnEvent;
202#[allow(clippy::type_complexity)]
203pub fn spawn_listener(
204 query: Query<(Entity, &LocalPlayerEvents), (Added<InLoadedChunk>, Without<SentSpawnEvent>)>,
205 mut commands: Commands,
206) {
207 for (entity, local_player_events) in &query {
208 let _ = local_player_events.send(Event::Spawn);
209 commands.entity(entity).insert(SentSpawnEvent);
210 }
211}
212
213pub fn chat_listener(
214 query: Query<&LocalPlayerEvents>,
215 mut events: MessageReader<ChatReceivedEvent>,
216) {
217 for event in events.read() {
218 if let Ok(local_player_events) = query.get(event.entity) {
219 let _ = local_player_events.send(Event::Chat(event.packet.clone()));
220 }
221 }
222}
223
224pub fn tick_listener(query: Query<&LocalPlayerEvents, With<WorldName>>) {
226 for local_player_events in &query {
227 let _ = local_player_events.send(Event::Tick);
228 }
229}
230
231#[cfg(feature = "packet-event")]
232pub fn packet_listener(
233 query: Query<&LocalPlayerEvents>,
234 mut events: MessageReader<super::packet::game::ReceiveGamePacketEvent>,
235) {
236 for event in events.read() {
237 if let Ok(local_player_events) = query.get(event.entity) {
238 let _ = local_player_events.send(Event::Packet(event.packet.clone()));
239 }
240 }
241}
242
243pub fn add_player_listener(
244 query: Query<&LocalPlayerEvents>,
245 mut events: MessageReader<AddPlayerEvent>,
246) {
247 for event in events.read() {
248 if let Ok(local_player_events) = query.get(event.entity) {
249 let _ = local_player_events.send(Event::AddPlayer(event.info.clone()));
250 }
251 }
252}
253
254pub fn update_player_listener(
255 query: Query<&LocalPlayerEvents>,
256 mut events: MessageReader<UpdatePlayerEvent>,
257) {
258 for event in events.read() {
259 if let Ok(local_player_events) = query.get(event.entity) {
260 let _ = local_player_events.send(Event::UpdatePlayer(event.info.clone()));
261 }
262 }
263}
264
265pub fn remove_player_listener(
266 query: Query<&LocalPlayerEvents>,
267 mut events: MessageReader<RemovePlayerEvent>,
268) {
269 for event in events.read() {
270 if let Ok(local_player_events) = query.get(event.entity) {
271 let _ = local_player_events.send(Event::RemovePlayer(event.info.clone()));
272 }
273 }
274}
275
276pub fn death_listener(query: Query<&LocalPlayerEvents>, mut events: MessageReader<DeathEvent>) {
277 for event in events.read() {
278 if let Ok(local_player_events) = query.get(event.entity) {
279 let _ = local_player_events.send(Event::Death(event.packet.clone().map(|p| p.into())));
280 }
281 }
282}
283
284pub fn dead_component_listener(query: Query<&LocalPlayerEvents, Added<Dead>>) {
288 for local_player_events in &query {
289 local_player_events.send(Event::Death(None)).unwrap();
290 }
291}
292
293pub fn keepalive_listener(
294 query: Query<&LocalPlayerEvents>,
295 mut events: MessageReader<KeepAliveEvent>,
296) {
297 for event in events.read() {
298 if let Ok(local_player_events) = query.get(event.entity) {
299 let _ = local_player_events.send(Event::KeepAlive(event.id));
300 }
301 }
302}
303
304pub fn disconnect_listener(
305 query: Query<&LocalPlayerEvents>,
306 mut events: MessageReader<DisconnectEvent>,
307) {
308 for event in events.read() {
309 if let Ok(local_player_events) = query.get(event.entity) {
310 let _ = local_player_events.send(Event::Disconnect(event.reason.clone()));
311 }
312 }
313}
314
315pub fn connection_failed_listener(
316 query: Query<&LocalPlayerEvents>,
317 mut events: MessageReader<ConnectionFailedEvent>,
318) {
319 for event in events.read() {
320 if let Ok(local_player_events) = query.get(event.entity) {
321 let _ = local_player_events.send(Event::ConnectionFailed(event.error.clone()));
322 }
323 }
324}
325
326pub fn receive_chunk_listener(
327 query: Query<&LocalPlayerEvents>,
328 mut events: MessageReader<ReceiveChunkEvent>,
329) {
330 for event in events.read() {
331 if let Ok(local_player_events) = query.get(event.entity) {
332 let _ = local_player_events.send(Event::ReceiveChunk(ChunkPos::new(
333 event.packet.x,
334 event.packet.z,
335 )));
336 }
337 }
338}