azalea_client/plugins/
events.rs1use std::sync::Arc;
5
6use azalea_chat::FormattedText;
7use azalea_core::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 PlayerInfo,
18 chat::{ChatPacket, ChatReceivedEvent},
19 disconnect::DisconnectEvent,
20 packet::game::{
21 AddPlayerEvent, DeathEvent, KeepAliveEvent, RemovePlayerEvent, UpdatePlayerEvent,
22 },
23};
24
25#[derive(Debug, Clone)]
53#[non_exhaustive]
54pub enum Event {
55 Init,
63 Login,
74 Spawn,
83 Chat(ChatPacket),
85 Tick,
87 #[cfg(feature = "packet-event")]
88 Packet(Arc<azalea_protocol::packets::game::ClientboundGamePacket>),
106 AddPlayer(PlayerInfo),
109 RemovePlayer(PlayerInfo),
112 UpdatePlayer(PlayerInfo),
115 Death(Option<Arc<ClientboundPlayerCombatKill>>),
117 KeepAlive(u64),
119 Disconnect(Option<FormattedText>),
121}
122
123#[derive(Component, Deref, DerefMut)]
129pub struct LocalPlayerEvents(pub mpsc::UnboundedSender<Event>);
130
131pub struct EventsPlugin;
132impl Plugin for EventsPlugin {
133 fn build(&self, app: &mut App) {
134 app.add_systems(
135 Update,
136 (
137 chat_listener,
138 login_listener,
139 spawn_listener,
140 #[cfg(feature = "packet-event")]
141 packet_listener,
142 add_player_listener,
143 update_player_listener,
144 remove_player_listener,
145 keepalive_listener,
146 death_listener,
147 disconnect_listener,
148 ),
149 )
150 .add_systems(
151 PreUpdate,
152 init_listener.before(super::connection::read_packets),
153 )
154 .add_systems(GameTick, tick_listener);
155 }
156}
157
158pub fn init_listener(query: Query<&LocalPlayerEvents, Added<LocalPlayerEvents>>) {
160 for local_player_events in &query {
161 let _ = local_player_events.send(Event::Init);
162 }
163}
164
165pub fn login_listener(
167 query: Query<(Entity, &LocalPlayerEvents), Added<MinecraftEntityId>>,
168 mut commands: Commands,
169) {
170 for (entity, local_player_events) in &query {
171 let _ = local_player_events.send(Event::Login);
172 commands.entity(entity).remove::<SentSpawnEvent>();
173 }
174}
175
176#[derive(Component)]
183pub struct SentSpawnEvent;
184#[allow(clippy::type_complexity)]
185pub fn spawn_listener(
186 query: Query<(Entity, &LocalPlayerEvents), (Added<InLoadedChunk>, Without<SentSpawnEvent>)>,
187 mut commands: Commands,
188) {
189 for (entity, local_player_events) in &query {
190 let _ = local_player_events.send(Event::Spawn);
191 commands.entity(entity).insert(SentSpawnEvent);
192 }
193}
194
195pub fn chat_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<ChatReceivedEvent>) {
196 for event in events.read() {
197 if let Ok(local_player_events) = query.get(event.entity) {
198 let _ = local_player_events.send(Event::Chat(event.packet.clone()));
199 }
200 }
201}
202
203pub fn tick_listener(query: Query<&LocalPlayerEvents, With<InstanceName>>) {
205 for local_player_events in &query {
206 let _ = local_player_events.send(Event::Tick);
207 }
208}
209
210#[cfg(feature = "packet-event")]
211pub fn packet_listener(
212 query: Query<&LocalPlayerEvents>,
213 mut events: EventReader<super::packet::game::ReceiveGamePacketEvent>,
214) {
215 for event in events.read() {
216 if let Ok(local_player_events) = query.get(event.entity) {
217 let _ = local_player_events.send(Event::Packet(event.packet.clone()));
218 }
219 }
220}
221
222pub fn add_player_listener(
223 query: Query<&LocalPlayerEvents>,
224 mut events: EventReader<AddPlayerEvent>,
225) {
226 for event in events.read() {
227 let local_player_events = query
228 .get(event.entity)
229 .expect("Non-local entities shouldn't be able to receive add player events");
230 let _ = local_player_events.send(Event::AddPlayer(event.info.clone()));
231 }
232}
233
234pub fn update_player_listener(
235 query: Query<&LocalPlayerEvents>,
236 mut events: EventReader<UpdatePlayerEvent>,
237) {
238 for event in events.read() {
239 let local_player_events = query
240 .get(event.entity)
241 .expect("Non-local entities shouldn't be able to receive update player events");
242 let _ = local_player_events.send(Event::UpdatePlayer(event.info.clone()));
243 }
244}
245
246pub fn remove_player_listener(
247 query: Query<&LocalPlayerEvents>,
248 mut events: EventReader<RemovePlayerEvent>,
249) {
250 for event in events.read() {
251 let local_player_events = query
252 .get(event.entity)
253 .expect("Non-local entities shouldn't be able to receive remove player events");
254 let _ = local_player_events.send(Event::RemovePlayer(event.info.clone()));
255 }
256}
257
258pub fn death_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<DeathEvent>) {
259 for event in events.read() {
260 if let Ok(local_player_events) = query.get(event.entity) {
261 let _ = local_player_events.send(Event::Death(event.packet.clone().map(|p| p.into())));
262 }
263 }
264}
265
266pub fn dead_component_listener(query: Query<&LocalPlayerEvents, Added<Dead>>) {
270 for local_player_events in &query {
271 local_player_events.send(Event::Death(None)).unwrap();
272 }
273}
274
275pub fn keepalive_listener(
276 query: Query<&LocalPlayerEvents>,
277 mut events: EventReader<KeepAliveEvent>,
278) {
279 for event in events.read() {
280 let local_player_events = query
281 .get(event.entity)
282 .expect("Non-local entities shouldn't be able to receive keepalive events");
283 let _ = local_player_events.send(Event::KeepAlive(event.id));
284 }
285}
286
287pub fn disconnect_listener(
288 query: Query<&LocalPlayerEvents>,
289 mut events: EventReader<DisconnectEvent>,
290) {
291 for event in events.read() {
292 if let Ok(local_player_events) = query.get(event.entity) {
293 let _ = local_player_events.send(Event::Disconnect(event.reason.clone()));
294 }
295 }
296}