mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
Refactor azalea-client (#205)
* start organizing packet_handling more by moving packet handlers into their own functions * finish writing all the handler functions for packets * use macro for generating match statement for packet handler functions * fix set_entity_data * update config state to also use handler functions * organize az-client file structure by moving things into plugins directory * fix merge issues
This commit is contained in:
parent
f8130c3c92
commit
e21e1b97bf
39 changed files with 2342 additions and 2087 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -356,6 +356,7 @@ dependencies = [
|
||||||
"derive_more 2.0.1",
|
"derive_more 2.0.1",
|
||||||
"minecraft_folder_path",
|
"minecraft_folder_path",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
"paste",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"simdnbt",
|
"simdnbt",
|
||||||
|
@ -2219,6 +2220,12 @@ dependencies = [
|
||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pem-rfc7468"
|
name = "pem-rfc7468"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
|
|
@ -32,7 +32,6 @@ repository = "https://github.com/azalea-rs/azalea"
|
||||||
aes = "0.8.4"
|
aes = "0.8.4"
|
||||||
anyhow = "1.0.95"
|
anyhow = "1.0.95"
|
||||||
async-recursion = "1.1.1"
|
async-recursion = "1.1.1"
|
||||||
async-trait = "0.1.86"
|
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
bevy_app = "0.15.2"
|
bevy_app = "0.15.2"
|
||||||
bevy_ecs = { version = "0.15.2", default-features = false }
|
bevy_ecs = { version = "0.15.2", default-features = false }
|
||||||
|
@ -49,7 +48,6 @@ env_logger = "0.11.6"
|
||||||
flate2 = "1.0.35"
|
flate2 = "1.0.35"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
futures-lite = "2.6.0"
|
futures-lite = "2.6.0"
|
||||||
log = "0.4.25"
|
|
||||||
md-5 = "0.10.6"
|
md-5 = "0.10.6"
|
||||||
minecraft_folder_path = "0.1.2"
|
minecraft_folder_path = "0.1.2"
|
||||||
nohash-hasher = "0.2.0"
|
nohash-hasher = "0.2.0"
|
||||||
|
@ -80,6 +78,7 @@ hickory-resolver = { version = "0.24.3", default-features = false }
|
||||||
uuid = "1.12.1"
|
uuid = "1.12.1"
|
||||||
num-format = "0.4.4"
|
num-format = "0.4.4"
|
||||||
indexmap = "2.7.1"
|
indexmap = "2.7.1"
|
||||||
|
paste = "1.0.15"
|
||||||
compact_str = "0.8.1"
|
compact_str = "0.8.1"
|
||||||
|
|
||||||
# --- Profile Settings ---
|
# --- Profile Settings ---
|
||||||
|
|
|
@ -288,21 +288,16 @@ impl<S> CommandDispatcher<S> {
|
||||||
next.push(child.copy_for(context.source.clone()));
|
next.push(child.copy_for(context.source.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if let Some(context_command) = &context.command {
|
||||||
match &context.command {
|
found_command = true;
|
||||||
Some(context_command) => {
|
|
||||||
found_command = true;
|
|
||||||
|
|
||||||
let value = context_command(context);
|
let value = context_command(context);
|
||||||
result += value;
|
result += value;
|
||||||
// consumer.on_command_complete(context, true, value);
|
// consumer.on_command_complete(context, true, value);
|
||||||
successful_forks += 1;
|
successful_forks += 1;
|
||||||
|
|
||||||
// TODO: allow context_command to error and handle
|
// TODO: allow context_command to error and handle
|
||||||
// those errors
|
// those errors
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ bevy_time.workspace = true
|
||||||
derive_more = { workspace = true, features = ["deref", "deref_mut"] }
|
derive_more = { workspace = true, features = ["deref", "deref_mut"] }
|
||||||
minecraft_folder_path.workspace = true
|
minecraft_folder_path.workspace = true
|
||||||
parking_lot.workspace = true
|
parking_lot.workspace = true
|
||||||
|
paste.workspace = true
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
reqwest.workspace = true
|
reqwest.workspace = true
|
||||||
simdnbt.workspace = true
|
simdnbt.workspace = true
|
||||||
|
|
|
@ -63,28 +63,27 @@ use uuid::Uuid;
|
||||||
use crate::{
|
use crate::{
|
||||||
Account, PlayerInfo,
|
Account, PlayerInfo,
|
||||||
attack::{self, AttackPlugin},
|
attack::{self, AttackPlugin},
|
||||||
|
brand::BrandPlugin,
|
||||||
chat::ChatPlugin,
|
chat::ChatPlugin,
|
||||||
chunks::{ChunkBatchInfo, ChunkPlugin},
|
chunks::{ChunkBatchInfo, ChunksPlugin},
|
||||||
configuration::ConfigurationPlugin,
|
|
||||||
disconnect::{DisconnectEvent, DisconnectPlugin},
|
disconnect::{DisconnectEvent, DisconnectPlugin},
|
||||||
events::{Event, EventPlugin, LocalPlayerEvents},
|
events::{Event, EventsPlugin, LocalPlayerEvents},
|
||||||
interact::{CurrentSequenceNumber, InteractPlugin},
|
interact::{CurrentSequenceNumber, InteractPlugin},
|
||||||
inventory::{Inventory, InventoryPlugin},
|
inventory::{Inventory, InventoryPlugin},
|
||||||
local_player::{
|
local_player::{
|
||||||
GameProfileComponent, Hunger, InstanceHolder, PermissionLevel, PlayerAbilities, TabList,
|
GameProfileComponent, Hunger, InstanceHolder, PermissionLevel, PlayerAbilities, TabList,
|
||||||
death_event,
|
|
||||||
},
|
},
|
||||||
mining::{self, MinePlugin},
|
mining::{self, MiningPlugin},
|
||||||
movement::{LastSentLookDirection, PhysicsState, PlayerMovePlugin},
|
movement::{LastSentLookDirection, MovementPlugin, PhysicsState},
|
||||||
packet_handling::{
|
packet::{
|
||||||
PacketHandlerPlugin,
|
PacketPlugin,
|
||||||
login::{self, InLoginState, LoginSendPacketQueue},
|
login::{self, InLoginState, LoginSendPacketQueue},
|
||||||
},
|
},
|
||||||
player::retroactively_add_game_profile_component,
|
player::retroactively_add_game_profile_component,
|
||||||
raw_connection::RawConnection,
|
raw_connection::RawConnection,
|
||||||
respawn::RespawnPlugin,
|
respawn::RespawnPlugin,
|
||||||
send_client_end::TickEndPlugin,
|
|
||||||
task_pool::TaskPoolPlugin,
|
task_pool::TaskPoolPlugin,
|
||||||
|
tick_end::TickEndPlugin,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// `Client` has the things that a user interacting with the library will want.
|
/// `Client` has the things that a user interacting with the library will want.
|
||||||
|
@ -370,7 +369,7 @@ impl Client {
|
||||||
let (ecs_packets_tx, mut ecs_packets_rx) = mpsc::unbounded_channel();
|
let (ecs_packets_tx, mut ecs_packets_rx) = mpsc::unbounded_channel();
|
||||||
ecs_lock.lock().entity_mut(entity).insert((
|
ecs_lock.lock().entity_mut(entity).insert((
|
||||||
LoginSendPacketQueue { tx: ecs_packets_tx },
|
LoginSendPacketQueue { tx: ecs_packets_tx },
|
||||||
login::IgnoreQueryIds::default(),
|
crate::packet::login::IgnoreQueryIds::default(),
|
||||||
InLoginState,
|
InLoginState,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -468,7 +467,7 @@ impl Client {
|
||||||
ClientboundLoginPacket::CustomQuery(p) => {
|
ClientboundLoginPacket::CustomQuery(p) => {
|
||||||
debug!("Got custom query {:?}", p);
|
debug!("Got custom query {:?}", p);
|
||||||
// replying to custom query is done in
|
// replying to custom query is done in
|
||||||
// packet_handling::login::process_packet_events
|
// packet::login::process_packet_events
|
||||||
}
|
}
|
||||||
ClientboundLoginPacket::CookieRequest(p) => {
|
ClientboundLoginPacket::CookieRequest(p) => {
|
||||||
debug!("Got cookie request {:?}", p);
|
debug!("Got cookie request {:?}", p);
|
||||||
|
@ -794,7 +793,7 @@ pub struct LocalPlayerBundle {
|
||||||
/// A bundle for the components that are present on a local player that is
|
/// A bundle for the components that are present on a local player that is
|
||||||
/// currently in the `game` protocol state. If you want to filter for this, just
|
/// currently in the `game` protocol state. If you want to filter for this, just
|
||||||
/// use [`LocalEntity`].
|
/// use [`LocalEntity`].
|
||||||
#[derive(Bundle)]
|
#[derive(Bundle, Default)]
|
||||||
pub struct JoinedClientBundle {
|
pub struct JoinedClientBundle {
|
||||||
// note that InstanceHolder isn't here because it's set slightly before we fully join the world
|
// note that InstanceHolder isn't here because it's set slightly before we fully join the world
|
||||||
pub physics_state: PhysicsState,
|
pub physics_state: PhysicsState,
|
||||||
|
@ -826,8 +825,6 @@ impl Plugin for AzaleaPlugin {
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
// fire the Death event when the player dies.
|
|
||||||
death_event,
|
|
||||||
// add GameProfileComponent when we get an AddPlayerEvent
|
// add GameProfileComponent when we get an AddPlayerEvent
|
||||||
retroactively_add_game_profile_component.after(EntityUpdateSet::Index),
|
retroactively_add_game_profile_component.after(EntityUpdateSet::Index),
|
||||||
),
|
),
|
||||||
|
@ -972,23 +969,23 @@ impl PluginGroup for DefaultPlugins {
|
||||||
let mut group = PluginGroupBuilder::start::<Self>()
|
let mut group = PluginGroupBuilder::start::<Self>()
|
||||||
.add(AmbiguityLoggerPlugin)
|
.add(AmbiguityLoggerPlugin)
|
||||||
.add(TimePlugin)
|
.add(TimePlugin)
|
||||||
.add(PacketHandlerPlugin)
|
.add(PacketPlugin)
|
||||||
.add(AzaleaPlugin)
|
.add(AzaleaPlugin)
|
||||||
.add(EntityPlugin)
|
.add(EntityPlugin)
|
||||||
.add(PhysicsPlugin)
|
.add(PhysicsPlugin)
|
||||||
.add(EventPlugin)
|
.add(EventsPlugin)
|
||||||
.add(TaskPoolPlugin::default())
|
.add(TaskPoolPlugin::default())
|
||||||
.add(InventoryPlugin)
|
.add(InventoryPlugin)
|
||||||
.add(ChatPlugin)
|
.add(ChatPlugin)
|
||||||
.add(DisconnectPlugin)
|
.add(DisconnectPlugin)
|
||||||
.add(PlayerMovePlugin)
|
.add(MovementPlugin)
|
||||||
.add(InteractPlugin)
|
.add(InteractPlugin)
|
||||||
.add(RespawnPlugin)
|
.add(RespawnPlugin)
|
||||||
.add(MinePlugin)
|
.add(MiningPlugin)
|
||||||
.add(AttackPlugin)
|
.add(AttackPlugin)
|
||||||
.add(ChunkPlugin)
|
.add(ChunksPlugin)
|
||||||
.add(TickEndPlugin)
|
.add(TickEndPlugin)
|
||||||
.add(ConfigurationPlugin)
|
.add(BrandPlugin)
|
||||||
.add(TickBroadcastPlugin);
|
.add(TickBroadcastPlugin);
|
||||||
#[cfg(feature = "log")]
|
#[cfg(feature = "log")]
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,26 +8,13 @@
|
||||||
#![feature(error_generic_member_access)]
|
#![feature(error_generic_member_access)]
|
||||||
|
|
||||||
mod account;
|
mod account;
|
||||||
pub mod attack;
|
|
||||||
pub mod chat;
|
|
||||||
pub mod chunks;
|
|
||||||
mod client;
|
mod client;
|
||||||
pub mod configuration;
|
|
||||||
pub mod disconnect;
|
|
||||||
mod entity_query;
|
mod entity_query;
|
||||||
pub mod events;
|
|
||||||
pub mod interact;
|
|
||||||
pub mod inventory;
|
|
||||||
mod local_player;
|
mod local_player;
|
||||||
pub mod mining;
|
|
||||||
pub mod movement;
|
|
||||||
pub mod packet_handling;
|
|
||||||
pub mod ping;
|
pub mod ping;
|
||||||
mod player;
|
mod player;
|
||||||
|
mod plugins;
|
||||||
pub mod raw_connection;
|
pub mod raw_connection;
|
||||||
pub mod respawn;
|
|
||||||
pub mod send_client_end;
|
|
||||||
pub mod task_pool;
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod test_simulation;
|
pub mod test_simulation;
|
||||||
|
@ -44,3 +31,4 @@ pub use movement::{
|
||||||
PhysicsState, SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection,
|
PhysicsState, SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection,
|
||||||
};
|
};
|
||||||
pub use player::PlayerInfo;
|
pub use player::PlayerInfo;
|
||||||
|
pub use plugins::*;
|
||||||
|
|
|
@ -2,7 +2,6 @@ use std::{collections::HashMap, io, sync::Arc};
|
||||||
|
|
||||||
use azalea_auth::game_profile::GameProfile;
|
use azalea_auth::game_profile::GameProfile;
|
||||||
use azalea_core::game_type::GameMode;
|
use azalea_core::game_type::GameMode;
|
||||||
use azalea_entity::Dead;
|
|
||||||
use azalea_protocol::packets::game::c_player_abilities::ClientboundPlayerAbilities;
|
use azalea_protocol::packets::game::c_player_abilities::ClientboundPlayerAbilities;
|
||||||
use azalea_world::{Instance, PartialInstance};
|
use azalea_world::{Instance, PartialInstance};
|
||||||
use bevy_ecs::{component::Component, prelude::*};
|
use bevy_ecs::{component::Component, prelude::*};
|
||||||
|
@ -13,10 +12,7 @@ use tokio::sync::mpsc;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{ClientInformation, PlayerInfo, events::Event as AzaleaEvent};
|
||||||
ClientInformation, PlayerInfo,
|
|
||||||
events::{Event as AzaleaEvent, LocalPlayerEvents},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A component that keeps strong references to our [`PartialInstance`] and
|
/// A component that keeps strong references to our [`PartialInstance`] and
|
||||||
/// [`Instance`] for local players.
|
/// [`Instance`] for local players.
|
||||||
|
@ -150,13 +146,6 @@ impl InstanceHolder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send the "Death" event for [`LocalEntity`]s that died with no reason.
|
|
||||||
pub fn death_event(query: Query<&LocalPlayerEvents, Added<Dead>>) {
|
|
||||||
for local_player_events in &query {
|
|
||||||
local_player_events.send(AzaleaEvent::Death(None)).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum HandlePacketError {
|
pub enum HandlePacketError {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
|
|
|
@ -1,271 +0,0 @@
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
use azalea_entity::indexing::EntityIdIndex;
|
|
||||||
use azalea_protocol::packets::config::s_finish_configuration::ServerboundFinishConfiguration;
|
|
||||||
use azalea_protocol::packets::config::s_keep_alive::ServerboundKeepAlive;
|
|
||||||
use azalea_protocol::packets::config::s_select_known_packs::ServerboundSelectKnownPacks;
|
|
||||||
use azalea_protocol::packets::config::{
|
|
||||||
self, ClientboundConfigPacket, ServerboundConfigPacket, ServerboundCookieResponse,
|
|
||||||
ServerboundResourcePack,
|
|
||||||
};
|
|
||||||
use azalea_protocol::packets::{ConnectionProtocol, Packet};
|
|
||||||
use azalea_protocol::read::deserialize_packet;
|
|
||||||
use bevy_ecs::prelude::*;
|
|
||||||
use bevy_ecs::system::SystemState;
|
|
||||||
use tracing::{debug, error, warn};
|
|
||||||
|
|
||||||
use crate::InstanceHolder;
|
|
||||||
use crate::client::InConfigState;
|
|
||||||
use crate::disconnect::DisconnectEvent;
|
|
||||||
use crate::local_player::Hunger;
|
|
||||||
use crate::packet_handling::game::KeepAliveEvent;
|
|
||||||
use crate::raw_connection::RawConnection;
|
|
||||||
|
|
||||||
#[derive(Event, Debug, Clone)]
|
|
||||||
pub struct ConfigurationEvent {
|
|
||||||
/// The client entity that received the packet.
|
|
||||||
pub entity: Entity,
|
|
||||||
/// The packet that was actually received.
|
|
||||||
pub packet: ClientboundConfigPacket,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_packet_events(
|
|
||||||
query: Query<(Entity, &RawConnection), With<InConfigState>>,
|
|
||||||
mut packet_events: ResMut<Events<ConfigurationEvent>>,
|
|
||||||
) {
|
|
||||||
// we manually clear and send the events at the beginning of each update
|
|
||||||
// since otherwise it'd cause issues with events in process_packet_events
|
|
||||||
// running twice
|
|
||||||
packet_events.clear();
|
|
||||||
for (player_entity, raw_conn) in &query {
|
|
||||||
let packets_lock = raw_conn.incoming_packet_queue();
|
|
||||||
let mut packets = packets_lock.lock();
|
|
||||||
if !packets.is_empty() {
|
|
||||||
for raw_packet in packets.iter() {
|
|
||||||
let packet = match deserialize_packet::<ClientboundConfigPacket>(&mut Cursor::new(
|
|
||||||
raw_packet,
|
|
||||||
)) {
|
|
||||||
Ok(packet) => packet,
|
|
||||||
Err(err) => {
|
|
||||||
error!("failed to read packet: {err:?}");
|
|
||||||
debug!("packet bytes: {raw_packet:?}");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
packet_events.send(ConfigurationEvent {
|
|
||||||
entity: player_entity,
|
|
||||||
packet,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// clear the packets right after we read them
|
|
||||||
packets.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_packet_events(ecs: &mut World) {
|
|
||||||
let mut events_owned = Vec::new();
|
|
||||||
let mut system_state: SystemState<EventReader<ConfigurationEvent>> = SystemState::new(ecs);
|
|
||||||
let mut events = system_state.get_mut(ecs);
|
|
||||||
for ConfigurationEvent {
|
|
||||||
entity: player_entity,
|
|
||||||
packet,
|
|
||||||
} in events.read()
|
|
||||||
{
|
|
||||||
// we do this so `ecs` isn't borrowed for the whole loop
|
|
||||||
events_owned.push((*player_entity, packet.clone()));
|
|
||||||
}
|
|
||||||
for (player_entity, packet) in events_owned {
|
|
||||||
match packet {
|
|
||||||
ClientboundConfigPacket::RegistryData(p) => {
|
|
||||||
let mut system_state: SystemState<Query<&mut InstanceHolder>> =
|
|
||||||
SystemState::new(ecs);
|
|
||||||
let mut query = system_state.get_mut(ecs);
|
|
||||||
let instance_holder = query.get_mut(player_entity).unwrap();
|
|
||||||
let mut instance = instance_holder.instance.write();
|
|
||||||
|
|
||||||
// add the new registry data
|
|
||||||
instance.registries.append(p.registry_id, p.entries);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientboundConfigPacket::CustomPayload(p) => {
|
|
||||||
debug!("Got custom payload packet {p:?}");
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::Disconnect(p) => {
|
|
||||||
warn!("Got disconnect packet {p:?}");
|
|
||||||
let mut system_state: SystemState<EventWriter<DisconnectEvent>> =
|
|
||||||
SystemState::new(ecs);
|
|
||||||
let mut disconnect_events = system_state.get_mut(ecs);
|
|
||||||
disconnect_events.send(DisconnectEvent {
|
|
||||||
entity: player_entity,
|
|
||||||
reason: Some(p.reason.clone()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::FinishConfiguration(p) => {
|
|
||||||
debug!("got FinishConfiguration packet: {p:?}");
|
|
||||||
|
|
||||||
let mut system_state: SystemState<Query<&mut RawConnection>> =
|
|
||||||
SystemState::new(ecs);
|
|
||||||
let mut query = system_state.get_mut(ecs);
|
|
||||||
let mut raw_conn = query.get_mut(player_entity).unwrap();
|
|
||||||
|
|
||||||
raw_conn
|
|
||||||
.write_packet(ServerboundFinishConfiguration)
|
|
||||||
.expect(
|
|
||||||
"we should be in the right state and encoding this packet shouldn't fail",
|
|
||||||
);
|
|
||||||
raw_conn.set_state(ConnectionProtocol::Game);
|
|
||||||
|
|
||||||
// these components are added now that we're going to be in the Game state
|
|
||||||
ecs.entity_mut(player_entity)
|
|
||||||
.remove::<InConfigState>()
|
|
||||||
.insert(crate::JoinedClientBundle {
|
|
||||||
physics_state: crate::PhysicsState::default(),
|
|
||||||
inventory: crate::inventory::Inventory::default(),
|
|
||||||
tab_list: crate::local_player::TabList::default(),
|
|
||||||
current_sequence_number: crate::interact::CurrentSequenceNumber::default(),
|
|
||||||
last_sent_direction: crate::movement::LastSentLookDirection::default(),
|
|
||||||
abilities: crate::local_player::PlayerAbilities::default(),
|
|
||||||
permission_level: crate::local_player::PermissionLevel::default(),
|
|
||||||
hunger: Hunger::default(),
|
|
||||||
chunk_batch_info: crate::chunks::ChunkBatchInfo::default(),
|
|
||||||
|
|
||||||
entity_id_index: EntityIdIndex::default(),
|
|
||||||
|
|
||||||
mining: crate::mining::MineBundle::default(),
|
|
||||||
attack: crate::attack::AttackBundle::default(),
|
|
||||||
|
|
||||||
_local_entity: azalea_entity::LocalEntity,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::KeepAlive(p) => {
|
|
||||||
debug!("Got keep alive packet (in configuration) {p:?} for {player_entity:?}");
|
|
||||||
|
|
||||||
let mut system_state: SystemState<(
|
|
||||||
Query<&RawConnection>,
|
|
||||||
EventWriter<KeepAliveEvent>,
|
|
||||||
)> = SystemState::new(ecs);
|
|
||||||
let (query, mut keepalive_events) = system_state.get_mut(ecs);
|
|
||||||
let raw_conn = query.get(player_entity).unwrap();
|
|
||||||
|
|
||||||
keepalive_events.send(KeepAliveEvent {
|
|
||||||
entity: player_entity,
|
|
||||||
id: p.id,
|
|
||||||
});
|
|
||||||
raw_conn
|
|
||||||
.write_packet(ServerboundKeepAlive { id: p.id })
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::Ping(p) => {
|
|
||||||
debug!("Got ping packet {p:?}");
|
|
||||||
|
|
||||||
let mut system_state: SystemState<Query<&RawConnection>> = SystemState::new(ecs);
|
|
||||||
let mut query = system_state.get_mut(ecs);
|
|
||||||
let raw_conn = query.get_mut(player_entity).unwrap();
|
|
||||||
|
|
||||||
raw_conn
|
|
||||||
.write_packet(config::s_pong::ServerboundPong { id: p.id })
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::ResourcePackPush(p) => {
|
|
||||||
debug!("Got resource pack packet {p:?}");
|
|
||||||
|
|
||||||
let mut system_state: SystemState<Query<&RawConnection>> = SystemState::new(ecs);
|
|
||||||
let mut query = system_state.get_mut(ecs);
|
|
||||||
let raw_conn = query.get_mut(player_entity).unwrap();
|
|
||||||
|
|
||||||
// always accept resource pack
|
|
||||||
raw_conn
|
|
||||||
.write_packet(ServerboundResourcePack {
|
|
||||||
id: p.id,
|
|
||||||
action: config::s_resource_pack::Action::Accepted,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::ResourcePackPop(_) => {
|
|
||||||
// we can ignore this
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::UpdateEnabledFeatures(p) => {
|
|
||||||
debug!("Got update enabled features packet {p:?}");
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::UpdateTags(_p) => {
|
|
||||||
debug!("Got update tags packet");
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::CookieRequest(p) => {
|
|
||||||
debug!("Got cookie request packet {p:?}");
|
|
||||||
|
|
||||||
let mut system_state: SystemState<Query<&RawConnection>> = SystemState::new(ecs);
|
|
||||||
let mut query = system_state.get_mut(ecs);
|
|
||||||
let raw_conn = query.get_mut(player_entity).unwrap();
|
|
||||||
|
|
||||||
raw_conn
|
|
||||||
.write_packet(ServerboundCookieResponse {
|
|
||||||
key: p.key,
|
|
||||||
// cookies aren't implemented
|
|
||||||
payload: None,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::ResetChat(p) => {
|
|
||||||
debug!("Got reset chat packet {p:?}");
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::StoreCookie(p) => {
|
|
||||||
debug!("Got store cookie packet {p:?}");
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::Transfer(p) => {
|
|
||||||
debug!("Got transfer packet {p:?}");
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::SelectKnownPacks(p) => {
|
|
||||||
debug!("Got select known packs packet {p:?}");
|
|
||||||
|
|
||||||
let mut system_state: SystemState<Query<&RawConnection>> = SystemState::new(ecs);
|
|
||||||
let mut query = system_state.get_mut(ecs);
|
|
||||||
let raw_conn = query.get_mut(player_entity).unwrap();
|
|
||||||
|
|
||||||
// resource pack management isn't implemented
|
|
||||||
raw_conn
|
|
||||||
.write_packet(ServerboundSelectKnownPacks {
|
|
||||||
known_packs: vec![],
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
ClientboundConfigPacket::ServerLinks(_) => {}
|
|
||||||
ClientboundConfigPacket::CustomReportDetails(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An event for sending a packet to the server while we're in the
|
|
||||||
/// `configuration` state.
|
|
||||||
#[derive(Event)]
|
|
||||||
pub struct SendConfigurationEvent {
|
|
||||||
pub sent_by: Entity,
|
|
||||||
pub packet: ServerboundConfigPacket,
|
|
||||||
}
|
|
||||||
impl SendConfigurationEvent {
|
|
||||||
pub fn new(sent_by: Entity, packet: impl Packet<ServerboundConfigPacket>) -> Self {
|
|
||||||
let packet = packet.into_variant();
|
|
||||||
Self { sent_by, packet }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_send_packet_event(
|
|
||||||
mut send_packet_events: EventReader<SendConfigurationEvent>,
|
|
||||||
mut query: Query<(&mut RawConnection, Option<&InConfigState>)>,
|
|
||||||
) {
|
|
||||||
for event in send_packet_events.read() {
|
|
||||||
if let Ok((raw_conn, in_configuration_state)) = query.get_mut(event.sent_by) {
|
|
||||||
if in_configuration_state.is_none() {
|
|
||||||
error!(
|
|
||||||
"Tried to send a configuration packet {:?} while not in configuration state",
|
|
||||||
event.packet
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
debug!("Sending packet: {:?}", event.packet);
|
|
||||||
if let Err(e) = raw_conn.write_packet(event.packet.clone()) {
|
|
||||||
error!("Failed to send packet: {e}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,7 @@ use bevy_ecs::{
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{GameProfileComponent, packet_handling::game::AddPlayerEvent};
|
use crate::{GameProfileComponent, packet::game::AddPlayerEvent};
|
||||||
|
|
||||||
/// A player in the tab list.
|
/// A player in the tab list.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
@ -11,9 +11,10 @@ use bevy_app::{App, Plugin, Update};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use super::packet::game::SendPacketEvent;
|
||||||
use crate::{
|
use crate::{
|
||||||
Client, interact::SwingArmEvent, local_player::LocalGameMode, movement::MoveEventsSet,
|
Client, interact::SwingArmEvent, local_player::LocalGameMode, movement::MoveEventsSet,
|
||||||
packet_handling::game::SendPacketEvent, respawn::perform_respawn,
|
respawn::perform_respawn,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct AttackPlugin;
|
pub struct AttackPlugin;
|
|
@ -11,15 +11,15 @@ use bevy_app::prelude::*;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use crate::packet_handling::{configuration::SendConfigurationEvent, login::InLoginState};
|
use super::packet::config::SendConfigPacketEvent;
|
||||||
|
use crate::packet::login::InLoginState;
|
||||||
|
|
||||||
pub struct ConfigurationPlugin;
|
pub struct BrandPlugin;
|
||||||
impl Plugin for ConfigurationPlugin {
|
impl Plugin for BrandPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
handle_end_login_state
|
handle_end_login_state.before(crate::packet::config::handle_send_packet_event),
|
||||||
.before(crate::packet_handling::configuration::handle_send_packet_event),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,13 +27,14 @@ impl Plugin for ConfigurationPlugin {
|
||||||
fn handle_end_login_state(
|
fn handle_end_login_state(
|
||||||
mut removed: RemovedComponents<InLoginState>,
|
mut removed: RemovedComponents<InLoginState>,
|
||||||
query: Query<&ClientInformation>,
|
query: Query<&ClientInformation>,
|
||||||
mut send_packet_events: EventWriter<SendConfigurationEvent>,
|
mut send_packet_events: EventWriter<SendConfigPacketEvent>,
|
||||||
) {
|
) {
|
||||||
for entity in removed.read() {
|
for entity in removed.read() {
|
||||||
let mut brand_data = Vec::new();
|
let mut brand_data = Vec::new();
|
||||||
// they don't have to know :)
|
// azalea pretends to be vanilla everywhere else so it makes sense to lie here
|
||||||
|
// too
|
||||||
"vanilla".azalea_write(&mut brand_data).unwrap();
|
"vanilla".azalea_write(&mut brand_data).unwrap();
|
||||||
send_packet_events.send(SendConfigurationEvent::new(
|
send_packet_events.send(SendConfigPacketEvent::new(
|
||||||
entity,
|
entity,
|
||||||
ServerboundCustomPayload {
|
ServerboundCustomPayload {
|
||||||
identifier: ResourceLocation::new("brand"),
|
identifier: ResourceLocation::new("brand"),
|
||||||
|
@ -52,7 +53,7 @@ fn handle_end_login_state(
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("Writing ClientInformation while in config state: {client_information:?}");
|
debug!("Writing ClientInformation while in config state: {client_information:?}");
|
||||||
send_packet_events.send(SendConfigurationEvent::new(
|
send_packet_events.send(SendConfigPacketEvent::new(
|
||||||
entity,
|
entity,
|
||||||
ServerboundClientInformation {
|
ServerboundClientInformation {
|
||||||
information: client_information.clone(),
|
information: client_information.clone(),
|
61
azalea-client/src/plugins/chat/handler.rs
Normal file
61
azalea-client/src/plugins/chat/handler.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
use azalea_protocol::packets::{
|
||||||
|
game::{s_chat::LastSeenMessagesUpdate, ServerboundChat, ServerboundChatCommand},
|
||||||
|
Packet,
|
||||||
|
};
|
||||||
|
use bevy_ecs::prelude::*;
|
||||||
|
|
||||||
|
use super::ChatKind;
|
||||||
|
use crate::packet::game::SendPacketEvent;
|
||||||
|
|
||||||
|
/// Send a chat packet to the server of a specific kind (chat message or
|
||||||
|
/// command). Usually you just want [`SendChatEvent`] instead.
|
||||||
|
///
|
||||||
|
/// Usually setting the kind to `Message` will make it send a chat message even
|
||||||
|
/// if it starts with a slash, but some server implementations will always do a
|
||||||
|
/// command if it starts with a slash.
|
||||||
|
///
|
||||||
|
/// If you're wondering why this isn't two separate events, it's so ordering is
|
||||||
|
/// preserved if multiple chat messages and commands are sent at the same time.
|
||||||
|
#[derive(Event)]
|
||||||
|
pub struct SendChatKindEvent {
|
||||||
|
pub entity: Entity,
|
||||||
|
pub content: String,
|
||||||
|
pub kind: ChatKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_send_chat_kind_event(
|
||||||
|
mut events: EventReader<SendChatKindEvent>,
|
||||||
|
mut send_packet_events: EventWriter<SendPacketEvent>,
|
||||||
|
) {
|
||||||
|
for event in events.read() {
|
||||||
|
let content = event
|
||||||
|
.content
|
||||||
|
.chars()
|
||||||
|
.filter(|c| !matches!(c, '\x00'..='\x1F' | '\x7F' | '§'))
|
||||||
|
.take(256)
|
||||||
|
.collect::<String>();
|
||||||
|
let packet = match event.kind {
|
||||||
|
ChatKind::Message => ServerboundChat {
|
||||||
|
message: content,
|
||||||
|
timestamp: SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("Time shouldn't be before epoch")
|
||||||
|
.as_millis()
|
||||||
|
.try_into()
|
||||||
|
.expect("Instant should fit into a u64"),
|
||||||
|
salt: azalea_crypto::make_salt(),
|
||||||
|
signature: None,
|
||||||
|
last_seen_messages: LastSeenMessagesUpdate::default(),
|
||||||
|
}
|
||||||
|
.into_variant(),
|
||||||
|
ChatKind::Command => {
|
||||||
|
// TODO: chat signing
|
||||||
|
ServerboundChatCommand { command: content }.into_variant()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
send_packet_events.send(SendPacketEvent::new(event.entity, packet));
|
||||||
|
}
|
||||||
|
}
|
111
azalea-client/src/chat.rs → azalea-client/src/plugins/chat/mod.rs
Executable file → Normal file
111
azalea-client/src/chat.rs → azalea-client/src/plugins/chat/mod.rs
Executable file → Normal file
|
@ -1,20 +1,13 @@
|
||||||
//! Implementations of chat-related features.
|
//! Implementations of chat-related features.
|
||||||
|
|
||||||
use std::{
|
pub mod handler;
|
||||||
sync::Arc,
|
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
use std::sync::Arc;
|
||||||
};
|
|
||||||
|
|
||||||
use azalea_chat::FormattedText;
|
use azalea_chat::FormattedText;
|
||||||
use azalea_protocol::packets::{
|
use azalea_protocol::packets::game::{
|
||||||
Packet,
|
c_disguised_chat::ClientboundDisguisedChat, c_player_chat::ClientboundPlayerChat,
|
||||||
game::{
|
c_system_chat::ClientboundSystemChat,
|
||||||
c_disguised_chat::ClientboundDisguisedChat,
|
|
||||||
c_player_chat::ClientboundPlayerChat,
|
|
||||||
c_system_chat::ClientboundSystemChat,
|
|
||||||
s_chat::{LastSeenMessagesUpdate, ServerboundChat},
|
|
||||||
s_chat_command::ServerboundChatCommand,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use bevy_app::{App, Plugin, Update};
|
use bevy_app::{App, Plugin, Update};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
@ -23,12 +16,28 @@ use bevy_ecs::{
|
||||||
prelude::Event,
|
prelude::Event,
|
||||||
schedule::IntoSystemConfigs,
|
schedule::IntoSystemConfigs,
|
||||||
};
|
};
|
||||||
|
use handler::{SendChatKindEvent, handle_send_chat_kind_event};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use super::packet::game::handle_outgoing_packets;
|
||||||
client::Client,
|
use crate::client::Client;
|
||||||
packet_handling::game::{SendPacketEvent, handle_send_packet_event},
|
|
||||||
};
|
pub struct ChatPlugin;
|
||||||
|
impl Plugin for ChatPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_event::<SendChatEvent>()
|
||||||
|
.add_event::<SendChatKindEvent>()
|
||||||
|
.add_event::<ChatReceivedEvent>()
|
||||||
|
.add_systems(
|
||||||
|
Update,
|
||||||
|
(
|
||||||
|
handle_send_chat_event,
|
||||||
|
handle_send_chat_kind_event.after(handle_outgoing_packets),
|
||||||
|
)
|
||||||
|
.chain(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A chat packet, either a system message or a chat message.
|
/// A chat packet, either a system message or a chat message.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -183,23 +192,6 @@ impl Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ChatPlugin;
|
|
||||||
impl Plugin for ChatPlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.add_event::<SendChatEvent>()
|
|
||||||
.add_event::<SendChatKindEvent>()
|
|
||||||
.add_event::<ChatReceivedEvent>()
|
|
||||||
.add_systems(
|
|
||||||
Update,
|
|
||||||
(
|
|
||||||
handle_send_chat_event,
|
|
||||||
handle_send_chat_kind_event.after(handle_send_packet_event),
|
|
||||||
)
|
|
||||||
.chain(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A client received a chat message packet.
|
/// A client received a chat message packet.
|
||||||
#[derive(Event, Debug, Clone)]
|
#[derive(Event, Debug, Clone)]
|
||||||
pub struct ChatReceivedEvent {
|
pub struct ChatReceivedEvent {
|
||||||
|
@ -235,63 +227,12 @@ pub fn handle_send_chat_event(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a chat packet to the server of a specific kind (chat message or
|
|
||||||
/// command). Usually you just want [`SendChatEvent`] instead.
|
|
||||||
///
|
|
||||||
/// Usually setting the kind to `Message` will make it send a chat message even
|
|
||||||
/// if it starts with a slash, but some server implementations will always do a
|
|
||||||
/// command if it starts with a slash.
|
|
||||||
///
|
|
||||||
/// If you're wondering why this isn't two separate events, it's so ordering is
|
|
||||||
/// preserved if multiple chat messages and commands are sent at the same time.
|
|
||||||
#[derive(Event)]
|
|
||||||
pub struct SendChatKindEvent {
|
|
||||||
pub entity: Entity,
|
|
||||||
pub content: String,
|
|
||||||
pub kind: ChatKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A kind of chat packet, either a chat message or a command.
|
/// A kind of chat packet, either a chat message or a command.
|
||||||
pub enum ChatKind {
|
pub enum ChatKind {
|
||||||
Message,
|
Message,
|
||||||
Command,
|
Command,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_send_chat_kind_event(
|
|
||||||
mut events: EventReader<SendChatKindEvent>,
|
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
|
||||||
) {
|
|
||||||
for event in events.read() {
|
|
||||||
let content = event
|
|
||||||
.content
|
|
||||||
.chars()
|
|
||||||
.filter(|c| !matches!(c, '\x00'..='\x1F' | '\x7F' | '§'))
|
|
||||||
.take(256)
|
|
||||||
.collect::<String>();
|
|
||||||
let packet = match event.kind {
|
|
||||||
ChatKind::Message => ServerboundChat {
|
|
||||||
message: content,
|
|
||||||
timestamp: SystemTime::now()
|
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.expect("Time shouldn't be before epoch")
|
|
||||||
.as_millis()
|
|
||||||
.try_into()
|
|
||||||
.expect("Instant should fit into a u64"),
|
|
||||||
salt: azalea_crypto::make_salt(),
|
|
||||||
signature: None,
|
|
||||||
last_seen_messages: LastSeenMessagesUpdate::default(),
|
|
||||||
}
|
|
||||||
.into_variant(),
|
|
||||||
ChatKind::Command => {
|
|
||||||
// TODO: chat signing
|
|
||||||
ServerboundChatCommand { command: content }.into_variant()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
send_packet_events.send(SendPacketEvent::new(event.entity, packet));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// MessageSigner, ChatMessageContent, LastSeenMessages
|
// MessageSigner, ChatMessageContent, LastSeenMessages
|
||||||
// fn sign_message() -> MessageSignature {
|
// fn sign_message() -> MessageSignature {
|
|
@ -18,16 +18,14 @@ use bevy_ecs::prelude::*;
|
||||||
use simdnbt::owned::BaseNbt;
|
use simdnbt::owned::BaseNbt;
|
||||||
use tracing::{error, trace};
|
use tracing::{error, trace};
|
||||||
|
|
||||||
|
use super::packet::game::handle_outgoing_packets;
|
||||||
use crate::{
|
use crate::{
|
||||||
InstanceHolder,
|
InstanceHolder, interact::handle_block_interact_event, inventory::InventorySet,
|
||||||
interact::handle_block_interact_event,
|
packet::game::SendPacketEvent, respawn::perform_respawn,
|
||||||
inventory::InventorySet,
|
|
||||||
packet_handling::game::{SendPacketEvent, handle_send_packet_event},
|
|
||||||
respawn::perform_respawn,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ChunkPlugin;
|
pub struct ChunksPlugin;
|
||||||
impl Plugin for ChunkPlugin {
|
impl Plugin for ChunksPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
|
@ -37,7 +35,7 @@ impl Plugin for ChunkPlugin {
|
||||||
handle_chunk_batch_finished_event,
|
handle_chunk_batch_finished_event,
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()
|
||||||
.before(handle_send_packet_event)
|
.before(handle_outgoing_packets)
|
||||||
.before(InventorySet)
|
.before(InventorySet)
|
||||||
.before(handle_block_interact_event)
|
.before(handle_block_interact_event)
|
||||||
.before(perform_respawn),
|
.before(perform_respawn),
|
|
@ -5,6 +5,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use azalea_chat::FormattedText;
|
use azalea_chat::FormattedText;
|
||||||
use azalea_core::tick::GameTick;
|
use azalea_core::tick::GameTick;
|
||||||
|
use azalea_entity::Dead;
|
||||||
use azalea_protocol::packets::game::{
|
use azalea_protocol::packets::game::{
|
||||||
ClientboundGamePacket, c_player_combat_kill::ClientboundPlayerCombatKill,
|
ClientboundGamePacket, c_player_combat_kill::ClientboundPlayerCombatKill,
|
||||||
};
|
};
|
||||||
|
@ -24,28 +25,33 @@ use crate::{
|
||||||
PlayerInfo,
|
PlayerInfo,
|
||||||
chat::{ChatPacket, ChatReceivedEvent},
|
chat::{ChatPacket, ChatReceivedEvent},
|
||||||
disconnect::DisconnectEvent,
|
disconnect::DisconnectEvent,
|
||||||
packet_handling::game::{
|
packet::game::{
|
||||||
AddPlayerEvent, DeathEvent, KeepAliveEvent, PacketEvent, RemovePlayerEvent,
|
AddPlayerEvent, DeathEvent, KeepAliveEvent, ReceivePacketEvent, RemovePlayerEvent,
|
||||||
UpdatePlayerEvent,
|
UpdatePlayerEvent,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// (for contributors):
|
// (for contributors):
|
||||||
// HOW TO ADD A NEW (packet based) EVENT:
|
// HOW TO ADD A NEW (packet based) EVENT:
|
||||||
// - make a struct that contains an entity field and a data field (look in
|
// - Add it as an ECS event first:
|
||||||
// packet_handling.rs for examples, also you should end the struct name with
|
// - Make a struct that contains an entity field and some data fields (look
|
||||||
// "Event")
|
// in packet/game/events.rs for examples. These structs should always have
|
||||||
// - the entity field is the local player entity that's receiving the event
|
// their names end with "Event".
|
||||||
// - in packet_handling, you always have a variable called player_entity that
|
// - (the `entity` field is the local player entity that's receiving the
|
||||||
// you can use
|
// event)
|
||||||
// - add the event struct in the `impl Plugin for PacketHandlerPlugin`
|
// - In the GamePacketHandler, you always have a `player` field that you can
|
||||||
// - to get the event writer, you have to get an
|
// use.
|
||||||
// EventWriter<SomethingHappenedEvent> from the SystemState (the convention is
|
// - Add the event struct in PacketPlugin::build
|
||||||
// to end your variable with the word "events", like "something_events")
|
// - (in the `impl Plugin for PacketPlugin`)
|
||||||
|
// - To get the event writer, you have to get an EventWriter<ThingEvent>.
|
||||||
|
// Look at other packets in packet/game/mod.rs for examples.
|
||||||
//
|
//
|
||||||
// - then here in this file, add it to the Event enum
|
// At this point, you've created a new ECS event. That's annoying for bots to
|
||||||
// - and make an event listener system/function like the other ones and put the
|
// use though, so you might wanna add it to the Event enum too:
|
||||||
// function in the `impl Plugin for EventPlugin`
|
// - In this file, add a new variant to that Event enum with the same name
|
||||||
|
// as your event (without the "Event" suffix).
|
||||||
|
// - Create a new system function like the other ones here, and put that
|
||||||
|
// system function in the `impl Plugin for EventsPlugin`
|
||||||
|
|
||||||
/// Something that happened in-game, such as a tick passing or chat message
|
/// Something that happened in-game, such as a tick passing or chat message
|
||||||
/// being sent.
|
/// being sent.
|
||||||
|
@ -111,8 +117,8 @@ pub enum Event {
|
||||||
#[derive(Component, Deref, DerefMut)]
|
#[derive(Component, Deref, DerefMut)]
|
||||||
pub struct LocalPlayerEvents(pub mpsc::UnboundedSender<Event>);
|
pub struct LocalPlayerEvents(pub mpsc::UnboundedSender<Event>);
|
||||||
|
|
||||||
pub struct EventPlugin;
|
pub struct EventsPlugin;
|
||||||
impl Plugin for EventPlugin {
|
impl Plugin for EventsPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
|
@ -130,7 +136,7 @@ impl Plugin for EventPlugin {
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
init_listener.before(crate::packet_handling::game::process_packet_events),
|
init_listener.before(crate::packet::game::process_packet_events),
|
||||||
)
|
)
|
||||||
.add_systems(GameTick, tick_listener);
|
.add_systems(GameTick, tick_listener);
|
||||||
}
|
}
|
||||||
|
@ -166,7 +172,10 @@ pub fn tick_listener(query: Query<&LocalPlayerEvents, With<InstanceName>>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn packet_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<PacketEvent>) {
|
pub fn packet_listener(
|
||||||
|
query: Query<&LocalPlayerEvents>,
|
||||||
|
mut events: EventReader<ReceivePacketEvent>,
|
||||||
|
) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
let local_player_events = query
|
let local_player_events = query
|
||||||
.get(event.entity)
|
.get(event.entity)
|
||||||
|
@ -219,6 +228,13 @@ pub fn death_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send the "Death" event for [`LocalEntity`]s that died with no reason.
|
||||||
|
pub fn dead_component_listener(query: Query<&LocalPlayerEvents, Added<Dead>>) {
|
||||||
|
for local_player_events in &query {
|
||||||
|
local_player_events.send(Event::Death(None)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn keepalive_listener(
|
pub fn keepalive_listener(
|
||||||
query: Query<&LocalPlayerEvents>,
|
query: Query<&LocalPlayerEvents>,
|
||||||
mut events: EventReader<KeepAliveEvent>,
|
mut events: EventReader<KeepAliveEvent>,
|
|
@ -30,13 +30,14 @@ use bevy_ecs::{
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
|
use super::packet::game::handle_outgoing_packets;
|
||||||
use crate::{
|
use crate::{
|
||||||
Client,
|
Client,
|
||||||
attack::handle_attack_event,
|
attack::handle_attack_event,
|
||||||
inventory::{Inventory, InventorySet},
|
inventory::{Inventory, InventorySet},
|
||||||
local_player::{LocalGameMode, PermissionLevel, PlayerAbilities},
|
local_player::{LocalGameMode, PermissionLevel, PlayerAbilities},
|
||||||
movement::MoveEventsSet,
|
movement::MoveEventsSet,
|
||||||
packet_handling::game::{SendPacketEvent, handle_send_packet_event},
|
packet::game::SendPacketEvent,
|
||||||
respawn::perform_respawn,
|
respawn::perform_respawn,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ impl Plugin for InteractPlugin {
|
||||||
handle_block_interact_event,
|
handle_block_interact_event,
|
||||||
handle_swing_arm_event,
|
handle_swing_arm_event,
|
||||||
)
|
)
|
||||||
.before(handle_send_packet_event)
|
.before(handle_outgoing_packets)
|
||||||
.after(InventorySet)
|
.after(InventorySet)
|
||||||
.after(perform_respawn)
|
.after(perform_respawn)
|
||||||
.after(handle_attack_event)
|
.after(handle_attack_event)
|
|
@ -25,11 +25,9 @@ use bevy_ecs::{
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
|
use super::packet::game::handle_outgoing_packets;
|
||||||
use crate::{
|
use crate::{
|
||||||
Client,
|
Client, local_player::PlayerAbilities, packet::game::SendPacketEvent, respawn::perform_respawn,
|
||||||
local_player::PlayerAbilities,
|
|
||||||
packet_handling::game::{SendPacketEvent, handle_send_packet_event},
|
|
||||||
respawn::perform_respawn,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct InventoryPlugin;
|
pub struct InventoryPlugin;
|
||||||
|
@ -48,7 +46,7 @@ impl Plugin for InventoryPlugin {
|
||||||
handle_menu_opened_event,
|
handle_menu_opened_event,
|
||||||
handle_set_container_content_event,
|
handle_set_container_content_event,
|
||||||
handle_container_click_event,
|
handle_container_click_event,
|
||||||
handle_container_close_event.before(handle_send_packet_event),
|
handle_container_close_event.before(handle_outgoing_packets),
|
||||||
handle_client_side_close_container_event,
|
handle_client_side_close_container_event,
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()
|
|
@ -18,12 +18,12 @@ use crate::{
|
||||||
inventory::{Inventory, InventorySet},
|
inventory::{Inventory, InventorySet},
|
||||||
local_player::{LocalGameMode, PermissionLevel, PlayerAbilities},
|
local_player::{LocalGameMode, PermissionLevel, PlayerAbilities},
|
||||||
movement::MoveEventsSet,
|
movement::MoveEventsSet,
|
||||||
packet_handling::game::SendPacketEvent,
|
packet::game::SendPacketEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A plugin that allows clients to break blocks in the world.
|
/// A plugin that allows clients to break blocks in the world.
|
||||||
pub struct MinePlugin;
|
pub struct MiningPlugin;
|
||||||
impl Plugin for MinePlugin {
|
impl Plugin for MiningPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_event::<StartMiningBlockEvent>()
|
app.add_event::<StartMiningBlockEvent>()
|
||||||
.add_event::<StartMiningBlockWithDirectionEvent>()
|
.add_event::<StartMiningBlockWithDirectionEvent>()
|
||||||
|
@ -59,6 +59,7 @@ impl Plugin for MinePlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The Bevy system set for things related to mining.
|
||||||
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
|
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
pub struct MiningSet;
|
pub struct MiningSet;
|
||||||
|
|
14
azalea-client/src/plugins/mod.rs
Normal file
14
azalea-client/src/plugins/mod.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
pub mod attack;
|
||||||
|
pub mod brand;
|
||||||
|
pub mod chat;
|
||||||
|
pub mod chunks;
|
||||||
|
pub mod disconnect;
|
||||||
|
pub mod events;
|
||||||
|
pub mod interact;
|
||||||
|
pub mod inventory;
|
||||||
|
pub mod mining;
|
||||||
|
pub mod movement;
|
||||||
|
pub mod packet;
|
||||||
|
pub mod respawn;
|
||||||
|
pub mod task_pool;
|
||||||
|
pub mod tick_end;
|
|
@ -27,7 +27,7 @@ use bevy_ecs::{
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::client::Client;
|
use crate::client::Client;
|
||||||
use crate::packet_handling::game::SendPacketEvent;
|
use crate::packet::game::SendPacketEvent;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum MovePlayerError {
|
pub enum MovePlayerError {
|
||||||
|
@ -47,9 +47,9 @@ impl From<MoveEntityError> for MovePlayerError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PlayerMovePlugin;
|
pub struct MovementPlugin;
|
||||||
|
|
||||||
impl Plugin for PlayerMovePlugin {
|
impl Plugin for MovementPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_event::<StartWalkEvent>()
|
app.add_event::<StartWalkEvent>()
|
||||||
.add_event::<StartSprintEvent>()
|
.add_event::<StartSprintEvent>()
|
90
azalea-client/src/plugins/packet/config/events.rs
Normal file
90
azalea-client/src/plugins/packet/config/events.rs
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use azalea_protocol::{
|
||||||
|
packets::{
|
||||||
|
config::{ClientboundConfigPacket, ServerboundConfigPacket},
|
||||||
|
Packet,
|
||||||
|
},
|
||||||
|
read::deserialize_packet,
|
||||||
|
};
|
||||||
|
use bevy_ecs::prelude::*;
|
||||||
|
use tracing::{debug, error};
|
||||||
|
|
||||||
|
use crate::{raw_connection::RawConnection, InConfigState};
|
||||||
|
|
||||||
|
#[derive(Event, Debug, Clone)]
|
||||||
|
pub struct ReceiveConfigPacketEvent {
|
||||||
|
/// The client entity that received the packet.
|
||||||
|
pub entity: Entity,
|
||||||
|
/// The packet that was actually received.
|
||||||
|
pub packet: ClientboundConfigPacket,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An event for sending a packet to the server while we're in the
|
||||||
|
/// `configuration` state.
|
||||||
|
#[derive(Event)]
|
||||||
|
pub struct SendConfigPacketEvent {
|
||||||
|
pub sent_by: Entity,
|
||||||
|
pub packet: ServerboundConfigPacket,
|
||||||
|
}
|
||||||
|
impl SendConfigPacketEvent {
|
||||||
|
pub fn new(sent_by: Entity, packet: impl Packet<ServerboundConfigPacket>) -> Self {
|
||||||
|
let packet = packet.into_variant();
|
||||||
|
Self { sent_by, packet }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_send_packet_event(
|
||||||
|
mut send_packet_events: EventReader<SendConfigPacketEvent>,
|
||||||
|
mut query: Query<(&mut RawConnection, Option<&InConfigState>)>,
|
||||||
|
) {
|
||||||
|
for event in send_packet_events.read() {
|
||||||
|
if let Ok((raw_conn, in_configuration_state)) = query.get_mut(event.sent_by) {
|
||||||
|
if in_configuration_state.is_none() {
|
||||||
|
error!(
|
||||||
|
"Tried to send a configuration packet {:?} while not in configuration state",
|
||||||
|
event.packet
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
debug!("Sending packet: {:?}", event.packet);
|
||||||
|
if let Err(e) = raw_conn.write_packet(event.packet.clone()) {
|
||||||
|
error!("Failed to send packet: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_packet_events(
|
||||||
|
query: Query<(Entity, &RawConnection), With<InConfigState>>,
|
||||||
|
mut packet_events: ResMut<Events<ReceiveConfigPacketEvent>>,
|
||||||
|
) {
|
||||||
|
// we manually clear and send the events at the beginning of each update
|
||||||
|
// since otherwise it'd cause issues with events in process_packet_events
|
||||||
|
// running twice
|
||||||
|
packet_events.clear();
|
||||||
|
for (player_entity, raw_conn) in &query {
|
||||||
|
let packets_lock = raw_conn.incoming_packet_queue();
|
||||||
|
let mut packets = packets_lock.lock();
|
||||||
|
if !packets.is_empty() {
|
||||||
|
for raw_packet in packets.iter() {
|
||||||
|
let packet = match deserialize_packet::<ClientboundConfigPacket>(&mut Cursor::new(
|
||||||
|
raw_packet,
|
||||||
|
)) {
|
||||||
|
Ok(packet) => packet,
|
||||||
|
Err(err) => {
|
||||||
|
error!("failed to read packet: {err:?}");
|
||||||
|
debug!("packet bytes: {raw_packet:?}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
packet_events.send(ReceiveConfigPacketEvent {
|
||||||
|
entity: player_entity,
|
||||||
|
packet,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// clear the packets right after we read them
|
||||||
|
packets.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
223
azalea-client/src/plugins/packet/config/mod.rs
Normal file
223
azalea-client/src/plugins/packet/config/mod.rs
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
mod events;
|
||||||
|
|
||||||
|
use azalea_protocol::packets::config::*;
|
||||||
|
use azalea_protocol::packets::ConnectionProtocol;
|
||||||
|
use bevy_ecs::prelude::*;
|
||||||
|
use bevy_ecs::system::SystemState;
|
||||||
|
pub use events::*;
|
||||||
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
|
use super::as_system;
|
||||||
|
use crate::client::InConfigState;
|
||||||
|
use crate::disconnect::DisconnectEvent;
|
||||||
|
use crate::packet::game::KeepAliveEvent;
|
||||||
|
use crate::raw_connection::RawConnection;
|
||||||
|
use crate::{declare_packet_handlers, InstanceHolder};
|
||||||
|
|
||||||
|
pub fn process_packet_events(ecs: &mut World) {
|
||||||
|
let mut events_owned = Vec::new();
|
||||||
|
let mut system_state: SystemState<EventReader<ReceiveConfigPacketEvent>> =
|
||||||
|
SystemState::new(ecs);
|
||||||
|
let mut events = system_state.get_mut(ecs);
|
||||||
|
for ReceiveConfigPacketEvent {
|
||||||
|
entity: player_entity,
|
||||||
|
packet,
|
||||||
|
} in events.read()
|
||||||
|
{
|
||||||
|
// we do this so `ecs` isn't borrowed for the whole loop
|
||||||
|
events_owned.push((*player_entity, packet.clone()));
|
||||||
|
}
|
||||||
|
for (player_entity, packet) in events_owned {
|
||||||
|
let mut handler = ConfigPacketHandler {
|
||||||
|
player: player_entity,
|
||||||
|
ecs,
|
||||||
|
};
|
||||||
|
|
||||||
|
declare_packet_handlers!(
|
||||||
|
ClientboundConfigPacket,
|
||||||
|
packet,
|
||||||
|
handler,
|
||||||
|
[
|
||||||
|
cookie_request,
|
||||||
|
custom_payload,
|
||||||
|
disconnect,
|
||||||
|
finish_configuration,
|
||||||
|
keep_alive,
|
||||||
|
ping,
|
||||||
|
reset_chat,
|
||||||
|
registry_data,
|
||||||
|
resource_pack_pop,
|
||||||
|
resource_pack_push,
|
||||||
|
store_cookie,
|
||||||
|
transfer,
|
||||||
|
update_enabled_features,
|
||||||
|
update_tags,
|
||||||
|
select_known_packs,
|
||||||
|
custom_report_details,
|
||||||
|
server_links,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ConfigPacketHandler<'a> {
|
||||||
|
pub ecs: &'a mut World,
|
||||||
|
pub player: Entity,
|
||||||
|
}
|
||||||
|
impl ConfigPacketHandler<'_> {
|
||||||
|
pub fn registry_data(&mut self, p: ClientboundRegistryData) {
|
||||||
|
as_system::<Query<&mut InstanceHolder>>(self.ecs, |mut query| {
|
||||||
|
let instance_holder = query.get_mut(self.player).unwrap();
|
||||||
|
let mut instance = instance_holder.instance.write();
|
||||||
|
|
||||||
|
// add the new registry data
|
||||||
|
instance.registries.append(p.registry_id, p.entries);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn custom_payload(&mut self, p: ClientboundCustomPayload) {
|
||||||
|
debug!("Got custom payload packet {p:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disconnect(&mut self, p: ClientboundDisconnect) {
|
||||||
|
warn!("Got disconnect packet {p:?}");
|
||||||
|
as_system::<EventWriter<_>>(self.ecs, |mut events| {
|
||||||
|
events.send(DisconnectEvent {
|
||||||
|
entity: self.player,
|
||||||
|
reason: Some(p.reason),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish_configuration(&mut self, p: ClientboundFinishConfiguration) {
|
||||||
|
debug!("got FinishConfiguration packet: {p:?}");
|
||||||
|
|
||||||
|
as_system::<(Commands, Query<&mut RawConnection>)>(
|
||||||
|
self.ecs,
|
||||||
|
|(mut commands, mut query)| {
|
||||||
|
let mut raw_conn = query.get_mut(self.player).unwrap();
|
||||||
|
|
||||||
|
raw_conn
|
||||||
|
.write_packet(ServerboundFinishConfiguration)
|
||||||
|
.expect(
|
||||||
|
"we should be in the right state and encoding this packet shouldn't fail",
|
||||||
|
);
|
||||||
|
raw_conn.set_state(ConnectionProtocol::Game);
|
||||||
|
|
||||||
|
// these components are added now that we're going to be in the Game state
|
||||||
|
commands
|
||||||
|
.entity(self.player)
|
||||||
|
.remove::<InConfigState>()
|
||||||
|
.insert(crate::JoinedClientBundle::default());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn keep_alive(&mut self, p: ClientboundKeepAlive) {
|
||||||
|
debug!(
|
||||||
|
"Got keep alive packet (in configuration) {p:?} for {:?}",
|
||||||
|
self.player
|
||||||
|
);
|
||||||
|
|
||||||
|
as_system::<(Query<&RawConnection>, EventWriter<_>)>(self.ecs, |(query, mut events)| {
|
||||||
|
let raw_conn = query.get(self.player).unwrap();
|
||||||
|
|
||||||
|
events.send(KeepAliveEvent {
|
||||||
|
entity: self.player,
|
||||||
|
id: p.id,
|
||||||
|
});
|
||||||
|
raw_conn
|
||||||
|
.write_packet(ServerboundKeepAlive { id: p.id })
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ping(&mut self, p: ClientboundPing) {
|
||||||
|
debug!("Got ping packet (in configuration) {p:?}");
|
||||||
|
|
||||||
|
as_system::<Query<&RawConnection>>(self.ecs, |query| {
|
||||||
|
let raw_conn = query.get(self.player).unwrap();
|
||||||
|
|
||||||
|
raw_conn.write_packet(ServerboundPong { id: p.id }).unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resource_pack_push(&mut self, p: ClientboundResourcePackPush) {
|
||||||
|
debug!("Got resource pack push packet {p:?}");
|
||||||
|
|
||||||
|
as_system::<Query<&RawConnection>>(self.ecs, |query| {
|
||||||
|
let raw_conn = query.get(self.player).unwrap();
|
||||||
|
|
||||||
|
// always accept resource pack
|
||||||
|
raw_conn
|
||||||
|
.write_packet(ServerboundResourcePack {
|
||||||
|
id: p.id,
|
||||||
|
action: s_resource_pack::Action::Accepted,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resource_pack_pop(&mut self, p: ClientboundResourcePackPop) {
|
||||||
|
debug!("Got resource pack pop packet {p:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_enabled_features(&mut self, p: ClientboundUpdateEnabledFeatures) {
|
||||||
|
debug!("Got update enabled features packet {p:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_tags(&mut self, _p: ClientboundUpdateTags) {
|
||||||
|
debug!("Got update tags packet");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cookie_request(&mut self, p: ClientboundCookieRequest) {
|
||||||
|
debug!("Got cookie request packet {p:?}");
|
||||||
|
|
||||||
|
as_system::<Query<&RawConnection>>(self.ecs, |query| {
|
||||||
|
let raw_conn = query.get(self.player).unwrap();
|
||||||
|
|
||||||
|
raw_conn
|
||||||
|
.write_packet(ServerboundCookieResponse {
|
||||||
|
key: p.key,
|
||||||
|
// cookies aren't implemented
|
||||||
|
payload: None,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_chat(&mut self, p: ClientboundResetChat) {
|
||||||
|
debug!("Got reset chat packet {p:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store_cookie(&mut self, p: ClientboundStoreCookie) {
|
||||||
|
debug!("Got store cookie packet {p:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transfer(&mut self, p: ClientboundTransfer) {
|
||||||
|
debug!("Got transfer packet {p:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_known_packs(&mut self, p: ClientboundSelectKnownPacks) {
|
||||||
|
debug!("Got select known packs packet {p:?}");
|
||||||
|
|
||||||
|
as_system::<Query<&RawConnection>>(self.ecs, |query| {
|
||||||
|
let raw_conn = query.get(self.player).unwrap();
|
||||||
|
|
||||||
|
// resource pack management isn't implemented
|
||||||
|
raw_conn
|
||||||
|
.write_packet(ServerboundSelectKnownPacks {
|
||||||
|
known_packs: vec![],
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn server_links(&mut self, p: ClientboundServerLinks) {
|
||||||
|
debug!("Got server links packet {p:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn custom_report_details(&mut self, p: ClientboundCustomReportDetails) {
|
||||||
|
debug!("Got custom report details packet {p:?}");
|
||||||
|
}
|
||||||
|
}
|
178
azalea-client/src/plugins/packet/game/events.rs
Normal file
178
azalea-client/src/plugins/packet/game/events.rs
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
use std::{
|
||||||
|
io::Cursor,
|
||||||
|
sync::{Arc, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
|
use azalea_chat::FormattedText;
|
||||||
|
use azalea_core::resource_location::ResourceLocation;
|
||||||
|
use azalea_entity::LocalEntity;
|
||||||
|
use azalea_protocol::{
|
||||||
|
packets::{
|
||||||
|
Packet,
|
||||||
|
game::{ClientboundGamePacket, ClientboundPlayerCombatKill, ServerboundGamePacket},
|
||||||
|
},
|
||||||
|
read::deserialize_packet,
|
||||||
|
};
|
||||||
|
use azalea_world::Instance;
|
||||||
|
use bevy_ecs::prelude::*;
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
use tracing::{debug, error};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::{PlayerInfo, raw_connection::RawConnection};
|
||||||
|
|
||||||
|
/// An event that's sent when we receive a packet.
|
||||||
|
/// ```
|
||||||
|
/// # use azalea_client::packet::game::ReceivePacketEvent;
|
||||||
|
/// # use azalea_protocol::packets::game::ClientboundGamePacket;
|
||||||
|
/// # use bevy_ecs::event::EventReader;
|
||||||
|
///
|
||||||
|
/// fn handle_packets(mut events: EventReader<ReceivePacketEvent>) {
|
||||||
|
/// for ReceivePacketEvent {
|
||||||
|
/// entity,
|
||||||
|
/// packet,
|
||||||
|
/// } in events.read() {
|
||||||
|
/// match packet.as_ref() {
|
||||||
|
/// ClientboundGamePacket::LevelParticles(p) => {
|
||||||
|
/// // ...
|
||||||
|
/// }
|
||||||
|
/// _ => {}
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Event, Debug, Clone)]
|
||||||
|
pub struct ReceivePacketEvent {
|
||||||
|
/// 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(Event)]
|
||||||
|
pub struct SendPacketEvent {
|
||||||
|
pub sent_by: Entity,
|
||||||
|
pub packet: ServerboundGamePacket,
|
||||||
|
}
|
||||||
|
impl SendPacketEvent {
|
||||||
|
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(
|
||||||
|
mut send_packet_events: EventReader<SendPacketEvent>,
|
||||||
|
mut query: Query<&mut RawConnection>,
|
||||||
|
) {
|
||||||
|
for event in send_packet_events.read() {
|
||||||
|
if let Ok(raw_connection) = query.get_mut(event.sent_by) {
|
||||||
|
// debug!("Sending packet: {:?}", event.packet);
|
||||||
|
if let Err(e) = raw_connection.write_packet(event.packet.clone()) {
|
||||||
|
error!("Failed to send packet: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_receivepacketevent(
|
||||||
|
query: Query<(Entity, &RawConnection), With<LocalEntity>>,
|
||||||
|
mut packet_events: ResMut<Events<ReceivePacketEvent>>,
|
||||||
|
) {
|
||||||
|
// we manually clear and send the events at the beginning of each update
|
||||||
|
// since otherwise it'd cause issues with events in process_packet_events
|
||||||
|
// running twice
|
||||||
|
packet_events.clear();
|
||||||
|
for (player_entity, raw_connection) in &query {
|
||||||
|
let packets_lock = raw_connection.incoming_packet_queue();
|
||||||
|
let mut packets = packets_lock.lock();
|
||||||
|
if !packets.is_empty() {
|
||||||
|
for raw_packet in packets.iter() {
|
||||||
|
let packet =
|
||||||
|
match deserialize_packet::<ClientboundGamePacket>(&mut Cursor::new(raw_packet))
|
||||||
|
{
|
||||||
|
Ok(packet) => packet,
|
||||||
|
Err(err) => {
|
||||||
|
error!("failed to read packet: {err:?}");
|
||||||
|
debug!("packet bytes: {raw_packet:?}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
packet_events.send(ReceivePacketEvent {
|
||||||
|
entity: player_entity,
|
||||||
|
packet: Arc::new(packet),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// clear the packets right after we read them
|
||||||
|
packets.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A player joined the game (or more specifically, was added to the tab
|
||||||
|
/// list of a local player).
|
||||||
|
#[derive(Event, Debug, Clone)]
|
||||||
|
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(Event, Debug, Clone)]
|
||||||
|
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(Event, Debug, Clone)]
|
||||||
|
pub struct UpdatePlayerEvent {
|
||||||
|
/// The local player entity that received this event.
|
||||||
|
pub entity: Entity,
|
||||||
|
pub info: PlayerInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Event for when an entity dies. dies. If it's a local player and there's a
|
||||||
|
/// reason in the death screen, the [`ClientboundPlayerCombatKill`] will
|
||||||
|
/// be included.
|
||||||
|
#[derive(Event, Debug, Clone)]
|
||||||
|
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(Event, Debug, Clone)]
|
||||||
|
pub struct KeepAliveEvent {
|
||||||
|
pub entity: Entity,
|
||||||
|
/// The ID of the keepalive. This is an arbitrary number, but vanilla
|
||||||
|
/// servers use the time to generate this.
|
||||||
|
pub id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Event, Debug, Clone)]
|
||||||
|
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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An instance (aka world, dimension) was loaded by a client.
|
||||||
|
///
|
||||||
|
/// Since the instance is given to you as a weak reference, it won't be able to
|
||||||
|
/// be `upgrade`d if all local players leave it.
|
||||||
|
#[derive(Event, Debug, Clone)]
|
||||||
|
pub struct InstanceLoadedEvent {
|
||||||
|
pub entity: Entity,
|
||||||
|
pub name: ResourceLocation,
|
||||||
|
pub instance: Weak<RwLock<Instance>>,
|
||||||
|
}
|
1583
azalea-client/src/plugins/packet/game/mod.rs
Normal file
1583
azalea-client/src/plugins/packet/game/mod.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -20,7 +20,7 @@ use tracing::error;
|
||||||
|
|
||||||
/// An event that's sent when we receive a login packet from the server. Note
|
/// An event that's sent when we receive a login packet from the server. Note
|
||||||
/// that if you want to handle this in a system, you must add
|
/// that if you want to handle this in a system, you must add
|
||||||
/// `.before(azalea::packet_handling::login::process_packet_events)` to it
|
/// `.before(azalea::packet::login::process_packet_events)` to it
|
||||||
/// because that system clears the events.
|
/// because that system clears the events.
|
||||||
#[derive(Event, Debug, Clone)]
|
#[derive(Event, Debug, Clone)]
|
||||||
pub struct LoginPacketEvent {
|
pub struct LoginPacketEvent {
|
|
@ -1,6 +1,9 @@
|
||||||
use azalea_entity::{EntityUpdateSet, metadata::Health};
|
use azalea_entity::{EntityUpdateSet, metadata::Health};
|
||||||
use bevy_app::{App, First, Plugin, PreUpdate, Update};
|
use bevy_app::{App, First, Plugin, PreUpdate, Update};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::{
|
||||||
|
prelude::*,
|
||||||
|
system::{SystemParam, SystemState},
|
||||||
|
};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
game::{
|
game::{
|
||||||
|
@ -11,11 +14,11 @@ use self::{
|
||||||
};
|
};
|
||||||
use crate::{chat::ChatReceivedEvent, events::death_listener};
|
use crate::{chat::ChatReceivedEvent, events::death_listener};
|
||||||
|
|
||||||
pub mod configuration;
|
pub mod config;
|
||||||
pub mod game;
|
pub mod game;
|
||||||
pub mod login;
|
pub mod login;
|
||||||
|
|
||||||
pub struct PacketHandlerPlugin;
|
pub struct PacketPlugin;
|
||||||
|
|
||||||
pub fn death_event_on_0_health(
|
pub fn death_event_on_0_health(
|
||||||
query: Query<(Entity, &Health), Changed<Health>>,
|
query: Query<(Entity, &Health), Changed<Health>>,
|
||||||
|
@ -31,11 +34,11 @@ pub fn death_event_on_0_health(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plugin for PacketHandlerPlugin {
|
impl Plugin for PacketPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
First,
|
First,
|
||||||
(game::send_packet_events, configuration::send_packet_events),
|
(game::send_receivepacketevent, config::send_packet_events),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
|
@ -43,7 +46,7 @@ impl Plugin for PacketHandlerPlugin {
|
||||||
game::process_packet_events
|
game::process_packet_events
|
||||||
// we want to index and deindex right after
|
// we want to index and deindex right after
|
||||||
.before(EntityUpdateSet::Deindex),
|
.before(EntityUpdateSet::Deindex),
|
||||||
configuration::process_packet_events,
|
config::process_packet_events,
|
||||||
login::handle_send_packet_event,
|
login::handle_send_packet_event,
|
||||||
login::process_packet_events,
|
login::process_packet_events,
|
||||||
),
|
),
|
||||||
|
@ -52,18 +55,18 @@ impl Plugin for PacketHandlerPlugin {
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
configuration::handle_send_packet_event,
|
config::handle_send_packet_event,
|
||||||
game::handle_send_packet_event,
|
game::handle_outgoing_packets,
|
||||||
)
|
)
|
||||||
.chain(),
|
.chain(),
|
||||||
death_event_on_0_health.before(death_listener),
|
death_event_on_0_health.before(death_listener),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
// we do this instead of add_event so we can handle the events ourselves
|
// we do this instead of add_event so we can handle the events ourselves
|
||||||
.init_resource::<Events<game::PacketEvent>>()
|
.init_resource::<Events<game::ReceivePacketEvent>>()
|
||||||
.init_resource::<Events<configuration::ConfigurationEvent>>()
|
.init_resource::<Events<config::ReceiveConfigPacketEvent>>()
|
||||||
.add_event::<game::SendPacketEvent>()
|
.add_event::<game::SendPacketEvent>()
|
||||||
.add_event::<configuration::SendConfigurationEvent>()
|
.add_event::<config::SendConfigPacketEvent>()
|
||||||
.add_event::<AddPlayerEvent>()
|
.add_event::<AddPlayerEvent>()
|
||||||
.add_event::<RemovePlayerEvent>()
|
.add_event::<RemovePlayerEvent>()
|
||||||
.add_event::<UpdatePlayerEvent>()
|
.add_event::<UpdatePlayerEvent>()
|
||||||
|
@ -76,3 +79,31 @@ impl Plugin for PacketHandlerPlugin {
|
||||||
.add_event::<SendLoginPacketEvent>();
|
.add_event::<SendLoginPacketEvent>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! declare_packet_handlers {
|
||||||
|
(
|
||||||
|
$packetenum:ident,
|
||||||
|
$packetvar:expr,
|
||||||
|
$handler:ident,
|
||||||
|
[$($packet:path),+ $(,)?]
|
||||||
|
) => {
|
||||||
|
paste::paste! {
|
||||||
|
match $packetvar {
|
||||||
|
$(
|
||||||
|
$packetenum::[< $packet:camel >](p) => $handler.$packet(p),
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_system<T>(ecs: &mut World, f: impl FnOnce(T::Item<'_, '_>))
|
||||||
|
where
|
||||||
|
T: SystemParam + 'static,
|
||||||
|
{
|
||||||
|
let mut system_state = SystemState::<T>::new(ecs);
|
||||||
|
let values = system_state.get_mut(ecs);
|
||||||
|
f(values);
|
||||||
|
system_state.apply(ecs);
|
||||||
|
}
|
|
@ -2,7 +2,8 @@ use azalea_protocol::packets::game::s_client_command::{self, ServerboundClientCo
|
||||||
use bevy_app::{App, Plugin, Update};
|
use bevy_app::{App, Plugin, Update};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
|
||||||
use crate::packet_handling::game::{SendPacketEvent, handle_send_packet_event};
|
use super::packet::game::handle_outgoing_packets;
|
||||||
|
use crate::packet::game::SendPacketEvent;
|
||||||
|
|
||||||
/// Tell the server that we're respawning.
|
/// Tell the server that we're respawning.
|
||||||
#[derive(Event, Debug, Clone)]
|
#[derive(Event, Debug, Clone)]
|
||||||
|
@ -15,7 +16,7 @@ pub struct RespawnPlugin;
|
||||||
impl Plugin for RespawnPlugin {
|
impl Plugin for RespawnPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_event::<PerformRespawnEvent>()
|
app.add_event::<PerformRespawnEvent>()
|
||||||
.add_systems(Update, perform_respawn.before(handle_send_packet_event));
|
.add_systems(Update, perform_respawn.before(handle_outgoing_packets));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use azalea_world::InstanceName;
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
|
||||||
use crate::{mining::MiningSet, packet_handling::game::SendPacketEvent};
|
use crate::{mining::MiningSet, packet::game::SendPacketEvent};
|
||||||
|
|
||||||
/// A plugin that makes clients send a [`ServerboundClientTickEnd`] packet every
|
/// A plugin that makes clients send a [`ServerboundClientTickEnd`] packet every
|
||||||
/// tick.
|
/// tick.
|
|
@ -482,7 +482,7 @@ impl EntityBundle {
|
||||||
/// be updated by other clients.
|
/// be updated by other clients.
|
||||||
///
|
///
|
||||||
/// If this is for a client then all of our clients will have this.
|
/// If this is for a client then all of our clients will have this.
|
||||||
#[derive(Component, Clone, Debug)]
|
#[derive(Component, Clone, Debug, Default)]
|
||||||
pub struct LocalEntity;
|
pub struct LocalEntity;
|
||||||
|
|
||||||
#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
|
#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use azalea_buf::AzBuf;
|
use azalea_buf::AzBuf;
|
||||||
use azalea_core::{position::Vec3, resource_location::ResourceLocation};
|
use azalea_core::{delta::PositionDelta8, position::Vec3, resource_location::ResourceLocation};
|
||||||
use azalea_entity::{metadata::apply_default_metadata, EntityBundle};
|
use azalea_entity::{metadata::apply_default_metadata, EntityBundle};
|
||||||
use azalea_protocol_macros::ClientboundGamePacket;
|
use azalea_protocol_macros::ClientboundGamePacket;
|
||||||
use azalea_world::MinecraftEntityId;
|
use azalea_world::MinecraftEntityId;
|
||||||
|
@ -18,9 +18,7 @@ pub struct ClientboundAddEntity {
|
||||||
pub y_head_rot: i8,
|
pub y_head_rot: i8,
|
||||||
#[var]
|
#[var]
|
||||||
pub data: u32,
|
pub data: u32,
|
||||||
pub x_vel: i16,
|
pub velocity: PositionDelta8,
|
||||||
pub y_vel: i16,
|
|
||||||
pub z_vel: i16,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientboundAddEntity {
|
impl ClientboundAddEntity {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use azalea_buf::AzBuf;
|
use azalea_buf::AzBuf;
|
||||||
|
use azalea_core::delta::PositionDelta8;
|
||||||
use azalea_protocol_macros::ClientboundGamePacket;
|
use azalea_protocol_macros::ClientboundGamePacket;
|
||||||
use azalea_world::MinecraftEntityId;
|
use azalea_world::MinecraftEntityId;
|
||||||
|
|
||||||
|
@ -6,7 +7,5 @@ use azalea_world::MinecraftEntityId;
|
||||||
pub struct ClientboundSetEntityMotion {
|
pub struct ClientboundSetEntityMotion {
|
||||||
#[var]
|
#[var]
|
||||||
pub id: MinecraftEntityId,
|
pub id: MinecraftEntityId,
|
||||||
pub xa: i16,
|
pub delta: PositionDelta8,
|
||||||
pub ya: i16,
|
|
||||||
pub za: i16,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use azalea::{
|
||||||
chunks::ReceiveChunkEvent,
|
chunks::ReceiveChunkEvent,
|
||||||
entity::{LookDirection, Position},
|
entity::{LookDirection, Position},
|
||||||
interact::HitResultComponent,
|
interact::HitResultComponent,
|
||||||
packet_handling::game,
|
packet::game,
|
||||||
pathfinder::{ExecutingPath, Pathfinder},
|
pathfinder::{ExecutingPath, Pathfinder},
|
||||||
world::MinecraftEntityId,
|
world::MinecraftEntityId,
|
||||||
};
|
};
|
||||||
|
@ -240,8 +240,8 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"bevy_ecs::event::collections::Events<azalea_client::packet_handling::game::PacketEvent>" => {
|
"bevy_ecs::event::collections::Events<azalea_client::packet::game::ReceivePacketEvent>" => {
|
||||||
let events = ecs.resource::<Events<game::PacketEvent>>();
|
let events = ecs.resource::<Events<game::ReceivePacketEvent>>();
|
||||||
writeln!(report, "- Event count: {}", events.len()).unwrap();
|
writeln!(report, "- Event count: {}", events.len()).unwrap();
|
||||||
}
|
}
|
||||||
"bevy_ecs::event::collections::Events<azalea_client::chunks::ReceiveChunkEvent>" => {
|
"bevy_ecs::event::collections::Events<azalea_client::chunks::ReceiveChunkEvent>" => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use azalea_client::chunks::handle_chunk_batch_finished_event;
|
use azalea_client::chunks::handle_chunk_batch_finished_event;
|
||||||
use azalea_client::inventory::InventorySet;
|
use azalea_client::inventory::InventorySet;
|
||||||
use azalea_client::packet_handling::game::SendPacketEvent;
|
use azalea_client::packet::game::SendPacketEvent;
|
||||||
use azalea_client::packet_handling::{death_event_on_0_health, game::ResourcePackEvent};
|
use azalea_client::packet::{death_event_on_0_health, game::ResourcePackEvent};
|
||||||
use azalea_client::respawn::perform_respawn;
|
use azalea_client::respawn::perform_respawn;
|
||||||
use azalea_protocol::packets::game::s_resource_pack::{self, ServerboundResourcePack};
|
use azalea_protocol::packets::game::s_resource_pack::{self, ServerboundResourcePack};
|
||||||
use bevy_app::Update;
|
use bevy_app::Update;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use azalea_client::{
|
use azalea_client::{
|
||||||
packet_handling::{death_event_on_0_health, game::DeathEvent},
|
packet::{death_event_on_0_health, game::DeathEvent},
|
||||||
respawn::{PerformRespawnEvent, perform_respawn},
|
respawn::{PerformRespawnEvent, perform_respawn},
|
||||||
};
|
};
|
||||||
use bevy_app::Update;
|
use bevy_app::Update;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
|
|
||||||
|
use azalea_client::packet::game::ReceivePacketEvent;
|
||||||
use azalea_client::{
|
use azalea_client::{
|
||||||
Client,
|
Client,
|
||||||
inventory::{CloseContainerEvent, ContainerClickEvent, Inventory},
|
inventory::{CloseContainerEvent, ContainerClickEvent, Inventory},
|
||||||
packet_handling::game::PacketEvent,
|
|
||||||
};
|
};
|
||||||
use azalea_core::position::BlockPos;
|
use azalea_core::position::BlockPos;
|
||||||
use azalea_inventory::{ItemStack, Menu, operations::ClickOperation};
|
use azalea_inventory::{ItemStack, Menu, operations::ClickOperation};
|
||||||
|
@ -234,7 +234,7 @@ impl ContainerHandle {
|
||||||
#[derive(Component, Debug)]
|
#[derive(Component, Debug)]
|
||||||
pub struct WaitingForInventoryOpen;
|
pub struct WaitingForInventoryOpen;
|
||||||
|
|
||||||
fn handle_menu_opened_event(mut commands: Commands, mut events: EventReader<PacketEvent>) {
|
fn handle_menu_opened_event(mut commands: Commands, mut events: EventReader<ReceivePacketEvent>) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
if let ClientboundGamePacket::ContainerSetContent { .. } = event.packet.as_ref() {
|
if let ClientboundGamePacket::ContainerSetContent { .. } = event.packet.as_ref() {
|
||||||
commands
|
commands
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use azalea_client::{PhysicsState, inventory::Inventory, packet_handling::game::SendPacketEvent};
|
use azalea_client::{PhysicsState, inventory::Inventory, packet::game::SendPacketEvent};
|
||||||
use azalea_core::{position::Vec3, resource_location::ResourceLocation, tick::GameTick};
|
use azalea_core::{position::Vec3, resource_location::ResourceLocation, tick::GameTick};
|
||||||
use azalea_entity::{
|
use azalea_entity::{
|
||||||
Attributes, EntityDimensions, LookDirection, Physics, Position, attributes::AttributeInstance,
|
Attributes, EntityDimensions, LookDirection, Physics, Position, attributes::AttributeInstance,
|
||||||
|
@ -60,13 +60,13 @@ fn create_simulation_instance(chunks: ChunkStorage) -> (App, Arc<RwLock<Instance
|
||||||
app.add_plugins((
|
app.add_plugins((
|
||||||
azalea_physics::PhysicsPlugin,
|
azalea_physics::PhysicsPlugin,
|
||||||
azalea_entity::EntityPlugin,
|
azalea_entity::EntityPlugin,
|
||||||
azalea_client::movement::PlayerMovePlugin,
|
azalea_client::movement::MovementPlugin,
|
||||||
super::PathfinderPlugin,
|
super::PathfinderPlugin,
|
||||||
crate::BotPlugin,
|
crate::BotPlugin,
|
||||||
azalea_client::task_pool::TaskPoolPlugin::default(),
|
azalea_client::task_pool::TaskPoolPlugin::default(),
|
||||||
// for mining
|
// for mining
|
||||||
azalea_client::inventory::InventoryPlugin,
|
azalea_client::inventory::InventoryPlugin,
|
||||||
azalea_client::mining::MinePlugin,
|
azalea_client::mining::MiningPlugin,
|
||||||
azalea_client::interact::InteractPlugin,
|
azalea_client::interact::InteractPlugin,
|
||||||
))
|
))
|
||||||
.insert_resource(InstanceContainer {
|
.insert_resource(InstanceContainer {
|
||||||
|
|
Loading…
Add table
Reference in a new issue