aboutsummaryrefslogtreecommitdiff
path: root/azalea-client/src/plugins/packet/game/events.rs
blob: 02be26f3b51dff020343adfb768033c8dba3ea46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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<ReceiveGamePacketEvent>) {
///     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<ClientboundGamePacket>,
}

/// 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<ServerboundGamePacket>) -> Self {
        let packet = packet.into_variant();
        Self { sent_by, packet }
    }
}

pub fn handle_outgoing_packets_observer(
    trigger: On<SendGamePacketEvent>,
    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<ClientboundPlayerCombatKill>,
}

/// 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<FormattedText>,
}

/// 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<RwLock<World>>,
}
#[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,
}