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::{
10 ClientboundGamePacket, c_player_combat_kill::ClientboundPlayerCombatKill,
11};
12use azalea_world::{InstanceName, MinecraftEntityId};
13use bevy_app::{App, Plugin, PreUpdate, Update};
14use bevy_ecs::{
15 component::Component,
16 entity::Entity,
17 event::EventReader,
18 query::{Added, With, Without},
19 schedule::IntoSystemConfigs,
20 system::{Commands, Query},
21};
22use derive_more::{Deref, DerefMut};
23use tokio::sync::mpsc;
24
25use crate::{
26 PlayerInfo,
27 chat::{ChatPacket, ChatReceivedEvent},
28 disconnect::DisconnectEvent,
29 packet::game::{
30 AddPlayerEvent, DeathEvent, KeepAliveEvent, ReceivePacketEvent, RemovePlayerEvent,
31 UpdatePlayerEvent,
32 },
33};
34
35#[derive(Debug, Clone)]
63#[non_exhaustive]
64pub enum Event {
65 Init,
73 Login,
81 Spawn,
87 Chat(ChatPacket),
89 Tick,
91 Packet(Arc<ClientboundGamePacket>),
109 AddPlayer(PlayerInfo),
112 RemovePlayer(PlayerInfo),
115 UpdatePlayer(PlayerInfo),
118 Death(Option<Arc<ClientboundPlayerCombatKill>>),
120 KeepAlive(u64),
122 Disconnect(Option<FormattedText>),
124}
125
126#[derive(Component, Deref, DerefMut)]
132pub struct LocalPlayerEvents(pub mpsc::UnboundedSender<Event>);
133
134pub struct EventsPlugin;
135impl Plugin for EventsPlugin {
136 fn build(&self, app: &mut App) {
137 app.add_systems(
138 Update,
139 (
140 chat_listener,
141 login_listener,
142 spawn_listener,
143 packet_listener,
144 add_player_listener,
145 update_player_listener,
146 remove_player_listener,
147 keepalive_listener,
148 death_listener,
149 disconnect_listener,
150 ),
151 )
152 .add_systems(
153 PreUpdate,
154 init_listener.before(crate::packet::game::process_packet_events),
155 )
156 .add_systems(GameTick, tick_listener);
157 }
158}
159
160pub fn init_listener(query: Query<&LocalPlayerEvents, Added<LocalPlayerEvents>>) {
162 for local_player_events in &query {
163 let _ = local_player_events.send(Event::Init);
164 }
165}
166
167pub fn login_listener(query: Query<&LocalPlayerEvents, Added<MinecraftEntityId>>) {
169 for local_player_events in &query {
170 let _ = local_player_events.send(Event::Login);
171 }
172}
173
174#[derive(Component)]
180pub struct SentSpawnEvent;
181#[allow(clippy::type_complexity)]
182pub fn spawn_listener(
183 query: Query<(Entity, &LocalPlayerEvents), (Added<InLoadedChunk>, Without<SentSpawnEvent>)>,
184 mut commands: Commands,
185) {
186 for (entity, local_player_events) in &query {
187 let _ = local_player_events.send(Event::Spawn);
188 commands.entity(entity).insert(SentSpawnEvent);
189 }
190}
191
192pub fn chat_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<ChatReceivedEvent>) {
193 for event in events.read() {
194 if let Ok(local_player_events) = query.get(event.entity) {
195 let _ = local_player_events.send(Event::Chat(event.packet.clone()));
196 }
197 }
198}
199
200pub fn tick_listener(query: Query<&LocalPlayerEvents, With<InstanceName>>) {
202 for local_player_events in &query {
203 let _ = local_player_events.send(Event::Tick);
204 }
205}
206
207pub fn packet_listener(
208 query: Query<&LocalPlayerEvents>,
209 mut events: EventReader<ReceivePacketEvent>,
210) {
211 for event in events.read() {
212 if let Ok(local_player_events) = query.get(event.entity) {
213 let _ = local_player_events.send(Event::Packet(event.packet.clone()));
214 }
215 }
216}
217
218pub fn add_player_listener(
219 query: Query<&LocalPlayerEvents>,
220 mut events: EventReader<AddPlayerEvent>,
221) {
222 for event in events.read() {
223 let local_player_events = query
224 .get(event.entity)
225 .expect("Non-local entities shouldn't be able to receive add player events");
226 let _ = local_player_events.send(Event::AddPlayer(event.info.clone()));
227 }
228}
229
230pub fn update_player_listener(
231 query: Query<&LocalPlayerEvents>,
232 mut events: EventReader<UpdatePlayerEvent>,
233) {
234 for event in events.read() {
235 let local_player_events = query
236 .get(event.entity)
237 .expect("Non-local entities shouldn't be able to receive update player events");
238 let _ = local_player_events.send(Event::UpdatePlayer(event.info.clone()));
239 }
240}
241
242pub fn remove_player_listener(
243 query: Query<&LocalPlayerEvents>,
244 mut events: EventReader<RemovePlayerEvent>,
245) {
246 for event in events.read() {
247 let local_player_events = query
248 .get(event.entity)
249 .expect("Non-local entities shouldn't be able to receive remove player events");
250 let _ = local_player_events.send(Event::RemovePlayer(event.info.clone()));
251 }
252}
253
254pub fn death_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<DeathEvent>) {
255 for event in events.read() {
256 if let Ok(local_player_events) = query.get(event.entity) {
257 let _ = local_player_events.send(Event::Death(event.packet.clone().map(|p| p.into())));
258 }
259 }
260}
261
262pub fn dead_component_listener(query: Query<&LocalPlayerEvents, Added<Dead>>) {
266 for local_player_events in &query {
267 local_player_events.send(Event::Death(None)).unwrap();
268 }
269}
270
271pub fn keepalive_listener(
272 query: Query<&LocalPlayerEvents>,
273 mut events: EventReader<KeepAliveEvent>,
274) {
275 for event in events.read() {
276 let local_player_events = query
277 .get(event.entity)
278 .expect("Non-local entities shouldn't be able to receive keepalive events");
279 let _ = local_player_events.send(Event::KeepAlive(event.id));
280 }
281}
282
283pub fn disconnect_listener(
284 query: Query<&LocalPlayerEvents>,
285 mut events: EventReader<DisconnectEvent>,
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::Disconnect(event.reason.clone()));
290 }
291 }
292}