1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00
azalea/azalea-client/src/events.rs
mat 08958c2278
Refactor azalea-protocol (#190)
* start updating to 1.21.4

* fix block codegen and stop using block data from burger

* rename packet related modules and structs to be simpler

* ItemSlot -> ItemStack for more consistency with mojmap

* .get() -> .into_packet()

* simplify declare_state_packets by removing packet ids

* rename read_from and write_into to azalea_read and azalea_write

* rename McBufReadable and McBufWritable to AzaleaRead and AzaleaWrite

* McBuf -> AzBuf

* remove most uses of into_variant

* update codegen and use resourcelocation names for packets

* implement #[limit(i)] attribute for AzBuf derive macro

* fixes for 1.21.4

* fix examples

* update some physics code and fix ChatType

* remove unused imports in codegen

* re-add some things to migrate.py and update +mc version numbers automatically

* downgrade to 1.21.3 lol
2024-11-27 19:31:40 -06:00

240 lines
8.2 KiB
Rust

//! Defines the [`Event`] enum and makes those events trigger when they're sent
//! in the ECS.
use std::sync::Arc;
use azalea_chat::FormattedText;
use azalea_core::tick::GameTick;
use azalea_protocol::packets::game::{
c_player_combat_kill::ClientboundPlayerCombatKill, ClientboundGamePacket,
};
use azalea_world::{InstanceName, MinecraftEntityId};
use bevy_app::{App, Plugin, PreUpdate, Update};
use bevy_ecs::{
component::Component,
event::EventReader,
query::{Added, With},
schedule::IntoSystemConfigs,
system::Query,
};
use derive_more::{Deref, DerefMut};
use tokio::sync::mpsc;
use crate::{
chat::{ChatPacket, ChatReceivedEvent},
disconnect::DisconnectEvent,
packet_handling::game::{
AddPlayerEvent, DeathEvent, KeepAliveEvent, PacketEvent, RemovePlayerEvent,
UpdatePlayerEvent,
},
PlayerInfo,
};
// (for contributors):
// HOW TO ADD A NEW (packet based) EVENT:
// - make a struct that contains an entity field and a data field (look in
// packet_handling.rs for examples, also you should end the struct name with
// "Event")
// - the entity field is the local player entity that's receiving the event
// - in packet_handling, you always have a variable called player_entity that
// you can use
// - add the event struct in the `impl Plugin for PacketHandlerPlugin`
// - to get the event writer, you have to get an
// EventWriter<SomethingHappenedEvent> from the SystemState (the convention is
// to end your variable with the word "events", like "something_events")
//
// - then here in this file, add it to the Event enum
// - and make an event listener system/function like the other ones and put the
// function in the `impl Plugin for EventPlugin`
/// Something that happened in-game, such as a tick passing or chat message
/// being sent.
///
/// Note: Events are sent before they're processed, so for example game ticks
/// happen at the beginning of a tick before anything has happened.
#[derive(Debug, Clone)]
pub enum Event {
/// Happens right after the bot switches into the Game state, but before
/// it's actually spawned. This can be useful for setting the client
/// information with `Client::set_client_information`, so the packet
/// doesn't have to be sent twice.
Init,
/// The client is now in the world. Fired when we receive a login packet.
Login,
/// A chat message was sent in the game chat.
Chat(ChatPacket),
/// Happens 20 times per second, but only when the world is loaded.
Tick,
/// We received a packet from the server.
///
/// ```
/// # use azalea_client::Event;
/// # use azalea_protocol::packets::game::ClientboundGamePacket;
/// # async fn example(event: Event) {
/// # match event {
/// Event::Packet(packet) => match *packet {
/// ClientboundGamePacket::Login(_) => {
/// println!("login packet");
/// }
/// _ => {}
/// },
/// # _ => {}
/// # }
/// # }
/// ```
Packet(Arc<ClientboundGamePacket>),
/// A player joined the game (or more specifically, was added to the tab
/// list).
AddPlayer(PlayerInfo),
/// A player left the game (or maybe is still in the game and was just
/// removed from the tab list).
RemovePlayer(PlayerInfo),
/// A player was updated in the tab list (gamemode, display
/// name, or latency changed).
UpdatePlayer(PlayerInfo),
/// The client player died in-game.
Death(Option<Arc<ClientboundPlayerCombatKill>>),
/// A `KeepAlive` packet was sent by the server.
KeepAlive(u64),
/// The client disconnected from the server.
Disconnect(Option<FormattedText>),
}
/// A component that contains an event sender for events that are only
/// received by local players. The receiver for this is returned by
/// [`Client::start_client`].
///
/// [`Client::start_client`]: crate::Client::start_client
#[derive(Component, Deref, DerefMut)]
pub struct LocalPlayerEvents(pub mpsc::UnboundedSender<Event>);
pub struct EventPlugin;
impl Plugin for EventPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
Update,
(
chat_listener,
login_listener,
packet_listener,
add_player_listener,
update_player_listener,
remove_player_listener,
keepalive_listener,
death_listener,
disconnect_listener,
),
)
.add_systems(
PreUpdate,
init_listener.before(crate::packet_handling::game::process_packet_events),
)
.add_systems(GameTick, tick_listener);
}
}
// when LocalPlayerEvents is added, it means the client just started
pub fn init_listener(query: Query<&LocalPlayerEvents, Added<LocalPlayerEvents>>) {
for local_player_events in &query {
let _ = local_player_events.send(Event::Init);
}
}
// when MinecraftEntityId is added, it means the player is now in the world
pub fn login_listener(query: Query<&LocalPlayerEvents, Added<MinecraftEntityId>>) {
for local_player_events in &query {
let _ = local_player_events.send(Event::Login);
}
}
pub fn chat_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<ChatReceivedEvent>) {
for event in events.read() {
let local_player_events = query
.get(event.entity)
.expect("Non-local entities shouldn't be able to receive chat events");
let _ = local_player_events.send(Event::Chat(event.packet.clone()));
}
}
// only tick if we're in a world
pub fn tick_listener(query: Query<&LocalPlayerEvents, With<InstanceName>>) {
for local_player_events in &query {
let _ = local_player_events.send(Event::Tick);
}
}
pub fn packet_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<PacketEvent>) {
for event in events.read() {
let local_player_events = query
.get(event.entity)
.expect("Non-local entities shouldn't be able to receive packet events");
let _ = local_player_events.send(Event::Packet(event.packet.clone()));
}
}
pub fn add_player_listener(
query: Query<&LocalPlayerEvents>,
mut events: EventReader<AddPlayerEvent>,
) {
for event in events.read() {
let local_player_events = query
.get(event.entity)
.expect("Non-local entities shouldn't be able to receive add player events");
let _ = local_player_events.send(Event::AddPlayer(event.info.clone()));
}
}
pub fn update_player_listener(
query: Query<&LocalPlayerEvents>,
mut events: EventReader<UpdatePlayerEvent>,
) {
for event in events.read() {
let local_player_events = query
.get(event.entity)
.expect("Non-local entities shouldn't be able to receive update player events");
let _ = local_player_events.send(Event::UpdatePlayer(event.info.clone()));
}
}
pub fn remove_player_listener(
query: Query<&LocalPlayerEvents>,
mut events: EventReader<RemovePlayerEvent>,
) {
for event in events.read() {
let local_player_events = query
.get(event.entity)
.expect("Non-local entities shouldn't be able to receive remove player events");
let _ = local_player_events.send(Event::RemovePlayer(event.info.clone()));
}
}
pub fn death_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<DeathEvent>) {
for event in events.read() {
if let Ok(local_player_events) = query.get(event.entity) {
let _ = local_player_events.send(Event::Death(event.packet.clone().map(|p| p.into())));
}
}
}
pub fn keepalive_listener(
query: Query<&LocalPlayerEvents>,
mut events: EventReader<KeepAliveEvent>,
) {
for event in events.read() {
let local_player_events = query
.get(event.entity)
.expect("Non-local entities shouldn't be able to receive keepalive events");
let _ = local_player_events.send(Event::KeepAlive(event.id));
}
}
pub fn disconnect_listener(
query: Query<&LocalPlayerEvents>,
mut events: EventReader<DisconnectEvent>,
) {
for event in events.read() {
if let Ok(local_player_events) = query.get(event.entity) {
let _ = local_player_events.send(Event::Disconnect(event.reason.clone()));
}
}
}