1use 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(Clone, Debug)]
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)]
135pub struct LocalPlayerEvents(pub mpsc::UnboundedSender<Event>);
136
137pub struct EventsPlugin;
138impl Plugin for EventsPlugin {
139 fn build(&self, app: &mut App) {
140 app.add_systems(
141 Update,
142 (
143 chat_listener,
144 login_listener,
145 spawn_listener,
146 #[cfg(feature = "packet-event")]
147 packet_listener,
148 add_player_listener,
149 update_player_listener,
150 remove_player_listener,
151 keepalive_listener,
152 death_listener.after(azalea_client::packet::death_event_on_0_health),
153 disconnect_listener,
154 receive_chunk_listener,
155 ),
156 )
157 .add_systems(
158 PreUpdate,
159 init_listener.before(super::connection::read_packets),
160 )
161 .add_systems(GameTick, tick_listener);
162 }
163}
164
165pub fn init_listener(query: Query<&LocalPlayerEvents, Added<LocalPlayerEvents>>) {
167 for local_player_events in &query {
168 let _ = local_player_events.send(Event::Init);
169 }
170}
171
172pub fn login_listener(
174 query: Query<(Entity, &LocalPlayerEvents), Added<MinecraftEntityId>>,
175 mut commands: Commands,
176) {
177 for (entity, local_player_events) in &query {
178 let _ = local_player_events.send(Event::Login);
179 commands.entity(entity).remove::<SentSpawnEvent>();
180 }
181}
182
183#[derive(Component)]
190pub struct SentSpawnEvent;
191#[allow(clippy::type_complexity)]
192pub fn spawn_listener(
193 query: Query<(Entity, &LocalPlayerEvents), (Added<InLoadedChunk>, Without<SentSpawnEvent>)>,
194 mut commands: Commands,
195) {
196 for (entity, local_player_events) in &query {
197 let _ = local_player_events.send(Event::Spawn);
198 commands.entity(entity).insert(SentSpawnEvent);
199 }
200}
201
202pub fn chat_listener(
203 query: Query<&LocalPlayerEvents>,
204 mut events: MessageReader<ChatReceivedEvent>,
205) {
206 for event in events.read() {
207 if let Ok(local_player_events) = query.get(event.entity) {
208 let _ = local_player_events.send(Event::Chat(event.packet.clone()));
209 }
210 }
211}
212
213pub fn tick_listener(query: Query<&LocalPlayerEvents, With<InstanceName>>) {
215 for local_player_events in &query {
216 let _ = local_player_events.send(Event::Tick);
217 }
218}
219
220#[cfg(feature = "packet-event")]
221pub fn packet_listener(
222 query: Query<&LocalPlayerEvents>,
223 mut events: MessageReader<super::packet::game::ReceiveGamePacketEvent>,
224) {
225 for event in events.read() {
226 if let Ok(local_player_events) = query.get(event.entity) {
227 let _ = local_player_events.send(Event::Packet(event.packet.clone()));
228 }
229 }
230}
231
232pub fn add_player_listener(
233 query: Query<&LocalPlayerEvents>,
234 mut events: MessageReader<AddPlayerEvent>,
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::AddPlayer(event.info.clone()));
239 }
240 }
241}
242
243pub fn update_player_listener(
244 query: Query<&LocalPlayerEvents>,
245 mut events: MessageReader<UpdatePlayerEvent>,
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::UpdatePlayer(event.info.clone()));
250 }
251 }
252}
253
254pub fn remove_player_listener(
255 query: Query<&LocalPlayerEvents>,
256 mut events: MessageReader<RemovePlayerEvent>,
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::RemovePlayer(event.info.clone()));
261 }
262 }
263}
264
265pub fn death_listener(query: Query<&LocalPlayerEvents>, mut events: MessageReader<DeathEvent>) {
266 for event in events.read() {
267 if let Ok(local_player_events) = query.get(event.entity) {
268 let _ = local_player_events.send(Event::Death(event.packet.clone().map(|p| p.into())));
269 }
270 }
271}
272
273pub fn dead_component_listener(query: Query<&LocalPlayerEvents, Added<Dead>>) {
277 for local_player_events in &query {
278 local_player_events.send(Event::Death(None)).unwrap();
279 }
280}
281
282pub fn keepalive_listener(
283 query: Query<&LocalPlayerEvents>,
284 mut events: MessageReader<KeepAliveEvent>,
285) {
286 for event in events.read() {
287 if let Ok(local_player_events) = query.get(event.entity) {
288 let _ = local_player_events.send(Event::KeepAlive(event.id));
289 }
290 }
291}
292
293pub fn disconnect_listener(
294 query: Query<&LocalPlayerEvents>,
295 mut events: MessageReader<DisconnectEvent>,
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::Disconnect(event.reason.clone()));
300 }
301 }
302}
303
304pub fn receive_chunk_listener(
305 query: Query<&LocalPlayerEvents>,
306 mut events: MessageReader<ReceiveChunkEvent>,
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::ReceiveChunk(ChunkPos::new(
311 event.packet.x,
312 event.packet.z,
313 )));
314 }
315 }
316}