use std::sync::{Arc, Weak}; use azalea_chat::FormattedText; use azalea_protocol::packets::{ Packet, game::{ClientboundGamePacket, ClientboundPlayerCombatKill, ServerboundGamePacket}, }; use azalea_world::{World, WorldName}; use bevy_ecs::prelude::*; use parking_lot::RwLock; use tracing::{error, trace}; use uuid::Uuid; use crate::{client::InGameState, connection::RawConnection, player::PlayerInfo}; /// An event that's sent when we receive a packet. /// ``` /// # use azalea_client::packet::game::ReceiveGamePacketEvent; /// # use azalea_protocol::packets::game::ClientboundGamePacket; /// # use bevy_ecs::message::MessageReader; /// /// fn handle_packets(mut events: MessageReader) { /// for ReceiveGamePacketEvent { entity, packet } in events.read() { /// match packet.as_ref() { /// ClientboundGamePacket::LevelParticles(p) => { /// // ... /// } /// _ => {} /// } /// } /// } /// ``` #[derive(Clone, Debug, Message)] pub struct ReceiveGamePacketEvent { /// The client entity that received the packet. pub entity: Entity, /// The packet that was actually received. pub packet: Arc, } /// An event for sending a packet to the server while we're in the `game` state. #[derive(Clone, Debug, EntityEvent)] pub struct SendGamePacketEvent { #[event_target] pub sent_by: Entity, pub packet: ServerboundGamePacket, } impl SendGamePacketEvent { pub fn new(sent_by: Entity, packet: impl Packet) -> Self { let packet = packet.into_variant(); Self { sent_by, packet } } } pub fn handle_outgoing_packets_observer( trigger: On, mut query: Query<(&mut RawConnection, Option<&InGameState>)>, ) { let event = trigger.event(); if let Ok((mut raw_connection, in_game_state)) = query.get_mut(event.sent_by) { if in_game_state.is_none() { error!( "Tried to send a game packet {:?} while not in game state", event.packet ); return; } trace!("Sending game packet: {:?}", event.packet); if let Err(e) = raw_connection.write(event.packet.clone()) { error!("Failed to send packet: {e}"); } } else { trace!("Not sending game packet: {:?}", event.packet); } } /// A player joined the game (or more specifically, was added to the tab /// list of a local player). #[derive(Clone, Debug, Message)] pub struct AddPlayerEvent { /// The local player entity that received this event. pub entity: Entity, pub info: PlayerInfo, } /// A player left the game (or maybe is still in the game and was just /// removed from the tab list of a local player). #[derive(Clone, Debug, Message)] pub struct RemovePlayerEvent { /// The local player entity that received this event. pub entity: Entity, pub info: PlayerInfo, } /// A player was updated in the tab list of a local player (gamemode, display /// name, or latency changed). #[derive(Clone, Debug, Message)] pub struct UpdatePlayerEvent { /// The local player entity that received this event. pub entity: Entity, pub info: PlayerInfo, } /// Event for when an entity dies. /// /// If it's a local player and there's a reason in the death screen, the /// [`ClientboundPlayerCombatKill`] will be included. #[derive(Clone, Debug, Message)] pub struct DeathEvent { pub entity: Entity, pub packet: Option, } /// A KeepAlive packet is sent from the server to verify that the client is /// still connected. #[derive(Clone, Debug, EntityEvent)] pub struct KeepAliveEvent { pub entity: Entity, /// The ID of the keepalive. /// /// This is an arbitrary number, but vanilla servers use the current time to /// generate this. pub id: u64, } #[derive(Clone, Debug, Message)] pub struct ResourcePackEvent { pub entity: Entity, /// The random ID for this request to download the resource pack. /// /// The packet for replying to a resource pack push must contain the same /// ID. pub id: Uuid, pub url: String, pub hash: String, pub required: bool, pub prompt: Option, } /// A world instance (aka dimension) was loaded by a client. /// /// Since the world is given to you as a weak reference, it won't be able to be /// `upgrade`d if all local players unload it. #[derive(Clone, Debug, Message)] pub struct WorldLoadedEvent { pub entity: Entity, pub name: WorldName, pub world: Weak>, } #[deprecated = "renamed to `WorldLoadedEvent`."] pub type InstanceLoadedEvent = WorldLoadedEvent; /// A Bevy trigger that's sent when our client receives a [`ClientboundPing`] /// packet in the game state. /// /// Also see [`ConfigPingEvent`] which is used for the config state. /// /// [`ClientboundPing`]: azalea_protocol::packets::game::ClientboundPing /// [`ConfigPingEvent`]: crate::packet::config::ConfigPingEvent #[derive(Clone, Debug, EntityEvent)] pub struct GamePingEvent { pub entity: Entity, pub packet: azalea_protocol::packets::game::ClientboundPing, }