mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
Merge branch 'main' into 1.21.5
This commit is contained in:
commit
ff94264ec2
28 changed files with 768 additions and 345 deletions
639
Cargo.lock
generated
639
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -45,7 +45,7 @@ criterion = "0.5.1"
|
||||||
derive_more = "2.0.1"
|
derive_more = "2.0.1"
|
||||||
enum-as-inner = "0.6.1"
|
enum-as-inner = "0.6.1"
|
||||||
env_logger = "0.11.6"
|
env_logger = "0.11.6"
|
||||||
flate2 = "1.0.35"
|
flate2 = { version = "1.0.35", features = ["zlib-rs"] }
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
futures-lite = "2.6.0"
|
futures-lite = "2.6.0"
|
||||||
md-5 = "0.10.6"
|
md-5 = "0.10.6"
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::{
|
||||||
io,
|
io,
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
thread,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,7 +40,7 @@ use azalea_protocol::{
|
||||||
resolver,
|
resolver,
|
||||||
};
|
};
|
||||||
use azalea_world::{Instance, InstanceContainer, InstanceName, PartialInstance};
|
use azalea_world::{Instance, InstanceContainer, InstanceName, PartialInstance};
|
||||||
use bevy_app::{App, Plugin, PluginGroup, PluginGroupBuilder, Update};
|
use bevy_app::{App, Plugin, PluginGroup, PluginGroupBuilder, PluginsState, Update};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
bundle::Bundle,
|
bundle::Bundle,
|
||||||
component::Component,
|
component::Component,
|
||||||
|
@ -60,7 +61,7 @@ use tokio::{
|
||||||
},
|
},
|
||||||
time,
|
time,
|
||||||
};
|
};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error, info};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -83,6 +84,7 @@ use crate::{
|
||||||
login::{self, InLoginState, LoginSendPacketQueue},
|
login::{self, InLoginState, LoginSendPacketQueue},
|
||||||
},
|
},
|
||||||
player::retroactively_add_game_profile_component,
|
player::retroactively_add_game_profile_component,
|
||||||
|
pong::PongPlugin,
|
||||||
raw_connection::RawConnection,
|
raw_connection::RawConnection,
|
||||||
respawn::RespawnPlugin,
|
respawn::RespawnPlugin,
|
||||||
task_pool::TaskPoolPlugin,
|
task_pool::TaskPoolPlugin,
|
||||||
|
@ -149,6 +151,7 @@ pub struct StartClientOpts<'a> {
|
||||||
pub resolved_address: &'a SocketAddr,
|
pub resolved_address: &'a SocketAddr,
|
||||||
pub proxy: Option<Proxy>,
|
pub proxy: Option<Proxy>,
|
||||||
pub run_schedule_sender: mpsc::Sender<()>,
|
pub run_schedule_sender: mpsc::Sender<()>,
|
||||||
|
pub event_sender: Option<mpsc::UnboundedSender<Event>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StartClientOpts<'a> {
|
impl<'a> StartClientOpts<'a> {
|
||||||
|
@ -156,6 +159,7 @@ impl<'a> StartClientOpts<'a> {
|
||||||
account: &'a Account,
|
account: &'a Account,
|
||||||
address: &'a ServerAddress,
|
address: &'a ServerAddress,
|
||||||
resolved_address: &'a SocketAddr,
|
resolved_address: &'a SocketAddr,
|
||||||
|
event_sender: Option<mpsc::UnboundedSender<Event>>,
|
||||||
) -> StartClientOpts<'a> {
|
) -> StartClientOpts<'a> {
|
||||||
// An event that causes the schedule to run. This is only used internally.
|
// An event that causes the schedule to run. This is only used internally.
|
||||||
let (run_schedule_sender, run_schedule_receiver) = mpsc::channel(1);
|
let (run_schedule_sender, run_schedule_receiver) = mpsc::channel(1);
|
||||||
|
@ -172,6 +176,7 @@ impl<'a> StartClientOpts<'a> {
|
||||||
resolved_address,
|
resolved_address,
|
||||||
proxy: None,
|
proxy: None,
|
||||||
run_schedule_sender,
|
run_schedule_sender,
|
||||||
|
event_sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,8 +234,16 @@ impl Client {
|
||||||
) -> Result<(Self, mpsc::UnboundedReceiver<Event>), JoinError> {
|
) -> Result<(Self, mpsc::UnboundedReceiver<Event>), JoinError> {
|
||||||
let address: ServerAddress = address.try_into().map_err(|_| JoinError::InvalidAddress)?;
|
let address: ServerAddress = address.try_into().map_err(|_| JoinError::InvalidAddress)?;
|
||||||
let resolved_address = resolver::resolve_address(&address).await?;
|
let resolved_address = resolver::resolve_address(&address).await?;
|
||||||
|
let (tx, rx) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
Self::start_client(StartClientOpts::new(account, &address, &resolved_address)).await
|
let client = Self::start_client(StartClientOpts::new(
|
||||||
|
account,
|
||||||
|
&address,
|
||||||
|
&resolved_address,
|
||||||
|
Some(tx),
|
||||||
|
))
|
||||||
|
.await?;
|
||||||
|
Ok((client, rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn join_with_proxy(
|
pub async fn join_with_proxy(
|
||||||
|
@ -240,9 +253,13 @@ impl Client {
|
||||||
) -> Result<(Self, mpsc::UnboundedReceiver<Event>), JoinError> {
|
) -> Result<(Self, mpsc::UnboundedReceiver<Event>), JoinError> {
|
||||||
let address: ServerAddress = address.try_into().map_err(|_| JoinError::InvalidAddress)?;
|
let address: ServerAddress = address.try_into().map_err(|_| JoinError::InvalidAddress)?;
|
||||||
let resolved_address = resolver::resolve_address(&address).await?;
|
let resolved_address = resolver::resolve_address(&address).await?;
|
||||||
|
let (tx, rx) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
Self::start_client(StartClientOpts::new(account, &address, &resolved_address).proxy(proxy))
|
let client = Self::start_client(
|
||||||
.await
|
StartClientOpts::new(account, &address, &resolved_address, Some(tx)).proxy(proxy),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok((client, rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a [`Client`] when you already have the ECS made with
|
/// Create a [`Client`] when you already have the ECS made with
|
||||||
|
@ -255,8 +272,9 @@ impl Client {
|
||||||
resolved_address,
|
resolved_address,
|
||||||
proxy,
|
proxy,
|
||||||
run_schedule_sender,
|
run_schedule_sender,
|
||||||
|
event_sender,
|
||||||
}: StartClientOpts<'_>,
|
}: StartClientOpts<'_>,
|
||||||
) -> Result<(Self, mpsc::UnboundedReceiver<Event>), JoinError> {
|
) -> Result<Self, JoinError> {
|
||||||
// check if an entity with our uuid already exists in the ecs and if so then
|
// check if an entity with our uuid already exists in the ecs and if so then
|
||||||
// just use that
|
// just use that
|
||||||
let entity = {
|
let entity = {
|
||||||
|
@ -298,8 +316,6 @@ impl Client {
|
||||||
|
|
||||||
// we did the handshake, so now we're connected to the server
|
// we did the handshake, so now we're connected to the server
|
||||||
|
|
||||||
let (tx, rx) = mpsc::unbounded_channel();
|
|
||||||
|
|
||||||
let mut ecs = ecs_lock.lock();
|
let mut ecs = ecs_lock.lock();
|
||||||
|
|
||||||
// we got the ConfigurationConnection, so the client is now connected :)
|
// we got the ConfigurationConnection, so the client is now connected :)
|
||||||
|
@ -318,7 +334,8 @@ impl Client {
|
||||||
Arc::new(RwLock::new(instance)),
|
Arc::new(RwLock::new(instance)),
|
||||||
);
|
);
|
||||||
|
|
||||||
ecs.entity_mut(entity).insert((
|
let mut entity = ecs.entity_mut(entity);
|
||||||
|
entity.insert((
|
||||||
// these stay when we switch to the game state
|
// these stay when we switch to the game state
|
||||||
LocalPlayerBundle {
|
LocalPlayerBundle {
|
||||||
raw_connection: RawConnection::new(
|
raw_connection: RawConnection::new(
|
||||||
|
@ -327,7 +344,6 @@ impl Client {
|
||||||
read_conn,
|
read_conn,
|
||||||
write_conn,
|
write_conn,
|
||||||
),
|
),
|
||||||
local_player_events: LocalPlayerEvents(tx),
|
|
||||||
game_profile: GameProfileComponent(game_profile),
|
game_profile: GameProfileComponent(game_profile),
|
||||||
client_information: crate::ClientInformation::default(),
|
client_information: crate::ClientInformation::default(),
|
||||||
instance_holder,
|
instance_holder,
|
||||||
|
@ -337,8 +353,12 @@ impl Client {
|
||||||
// this component is never removed
|
// this component is never removed
|
||||||
LocalEntity,
|
LocalEntity,
|
||||||
));
|
));
|
||||||
|
if let Some(event_sender) = event_sender {
|
||||||
|
// this is optional so we don't leak memory in case the user
|
||||||
|
entity.insert(LocalPlayerEvents(event_sender));
|
||||||
|
}
|
||||||
|
|
||||||
Ok((client, rx))
|
Ok(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Do a handshake with the server and get to the game state from the
|
/// Do a handshake with the server and get to the game state from the
|
||||||
|
@ -683,8 +703,16 @@ impl Client {
|
||||||
/// Get the position of this client.
|
/// Get the position of this client.
|
||||||
///
|
///
|
||||||
/// This is a shortcut for `Vec3::from(&bot.component::<Position>())`.
|
/// This is a shortcut for `Vec3::from(&bot.component::<Position>())`.
|
||||||
|
///
|
||||||
|
/// Note that this value is given a default of [`Vec3::ZERO`] when it
|
||||||
|
/// receives the login packet, its true position may be set ticks
|
||||||
|
/// later.
|
||||||
pub fn position(&self) -> Vec3 {
|
pub fn position(&self) -> Vec3 {
|
||||||
Vec3::from(&self.component::<Position>())
|
Vec3::from(
|
||||||
|
&self
|
||||||
|
.get_component::<Position>()
|
||||||
|
.expect("the client's position hasn't been initialized yet"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the position of this client's eyes.
|
/// Get the position of this client's eyes.
|
||||||
|
@ -789,7 +817,6 @@ impl Client {
|
||||||
#[derive(Bundle)]
|
#[derive(Bundle)]
|
||||||
pub struct LocalPlayerBundle {
|
pub struct LocalPlayerBundle {
|
||||||
pub raw_connection: RawConnection,
|
pub raw_connection: RawConnection,
|
||||||
pub local_player_events: LocalPlayerEvents,
|
|
||||||
pub game_profile: GameProfileComponent,
|
pub game_profile: GameProfileComponent,
|
||||||
pub client_information: ClientInformation,
|
pub client_information: ClientInformation,
|
||||||
pub instance_holder: InstanceHolder,
|
pub instance_holder: InstanceHolder,
|
||||||
|
@ -855,6 +882,21 @@ pub fn start_ecs_runner(
|
||||||
run_schedule_receiver: mpsc::Receiver<()>,
|
run_schedule_receiver: mpsc::Receiver<()>,
|
||||||
run_schedule_sender: mpsc::Sender<()>,
|
run_schedule_sender: mpsc::Sender<()>,
|
||||||
) -> Arc<Mutex<World>> {
|
) -> Arc<Mutex<World>> {
|
||||||
|
// this block is based on Bevy's default runner:
|
||||||
|
// https://github.com/bevyengine/bevy/blob/390877cdae7a17095a75c8f9f1b4241fe5047e83/crates/bevy_app/src/schedule_runner.rs#L77-L85
|
||||||
|
if app.plugins_state() != PluginsState::Cleaned {
|
||||||
|
// Wait for plugins to load
|
||||||
|
if app.plugins_state() == PluginsState::Adding {
|
||||||
|
info!("Waiting for plugins to load ...");
|
||||||
|
while app.plugins_state() == PluginsState::Adding {
|
||||||
|
thread::yield_now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Finish adding plugins and cleanup
|
||||||
|
app.finish();
|
||||||
|
app.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
// all resources should have been added by now so we can take the ecs from the
|
// all resources should have been added by now so we can take the ecs from the
|
||||||
// app
|
// app
|
||||||
let ecs = Arc::new(Mutex::new(std::mem::take(app.world_mut())));
|
let ecs = Arc::new(Mutex::new(std::mem::take(app.world_mut())));
|
||||||
|
@ -994,7 +1036,8 @@ impl PluginGroup for DefaultPlugins {
|
||||||
.add(ChunksPlugin)
|
.add(ChunksPlugin)
|
||||||
.add(TickEndPlugin)
|
.add(TickEndPlugin)
|
||||||
.add(BrandPlugin)
|
.add(BrandPlugin)
|
||||||
.add(TickBroadcastPlugin);
|
.add(TickBroadcastPlugin)
|
||||||
|
.add(PongPlugin);
|
||||||
#[cfg(feature = "log")]
|
#[cfg(feature = "log")]
|
||||||
{
|
{
|
||||||
group = group.add(bevy_log::LogPlugin::default());
|
group = group.add(bevy_log::LogPlugin::default());
|
||||||
|
|
|
@ -75,7 +75,7 @@ pub fn handle_attack_event(
|
||||||
&mut Sprinting,
|
&mut Sprinting,
|
||||||
&mut ShiftKeyDown,
|
&mut ShiftKeyDown,
|
||||||
)>,
|
)>,
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
mut swing_arm_event: EventWriter<SwingArmEvent>,
|
mut swing_arm_event: EventWriter<SwingArmEvent>,
|
||||||
) {
|
) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
|
@ -85,7 +85,7 @@ pub fn handle_attack_event(
|
||||||
swing_arm_event.send(SwingArmEvent {
|
swing_arm_event.send(SwingArmEvent {
|
||||||
entity: event.entity,
|
entity: event.entity,
|
||||||
});
|
});
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
event.entity,
|
event.entity,
|
||||||
ServerboundInteract {
|
ServerboundInteract {
|
||||||
entity_id: event.target,
|
entity_id: event.target,
|
||||||
|
|
|
@ -19,7 +19,7 @@ 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.before(crate::packet::config::handle_send_packet_event),
|
handle_end_login_state.before(crate::packet::config::handle_outgoing_packets),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub struct SendChatKindEvent {
|
||||||
|
|
||||||
pub fn handle_send_chat_kind_event(
|
pub fn handle_send_chat_kind_event(
|
||||||
mut events: EventReader<SendChatKindEvent>,
|
mut events: EventReader<SendChatKindEvent>,
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
let content = event
|
let content = event
|
||||||
|
@ -58,6 +58,6 @@ pub fn handle_send_chat_kind_event(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
send_packet_events.send(SendPacketEvent::new(event.entity, packet));
|
commands.trigger(SendPacketEvent::new(event.entity, packet));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,13 +149,13 @@ pub fn handle_chunk_batch_start_event(
|
||||||
pub fn handle_chunk_batch_finished_event(
|
pub fn handle_chunk_batch_finished_event(
|
||||||
mut query: Query<&mut ChunkBatchInfo>,
|
mut query: Query<&mut ChunkBatchInfo>,
|
||||||
mut events: EventReader<ChunkBatchFinishedEvent>,
|
mut events: EventReader<ChunkBatchFinishedEvent>,
|
||||||
mut send_packets: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
if let Ok(mut chunk_batch_info) = query.get_mut(event.entity) {
|
if let Ok(mut chunk_batch_info) = query.get_mut(event.entity) {
|
||||||
chunk_batch_info.batch_finished(event.batch_size);
|
chunk_batch_info.batch_finished(event.batch_size);
|
||||||
let desired_chunks_per_tick = chunk_batch_info.desired_chunks_per_tick();
|
let desired_chunks_per_tick = chunk_batch_info.desired_chunks_per_tick();
|
||||||
send_packets.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
event.entity,
|
event.entity,
|
||||||
ServerboundChunkBatchReceived {
|
ServerboundChunkBatchReceived {
|
||||||
desired_chunks_per_tick,
|
desired_chunks_per_tick,
|
||||||
|
|
|
@ -5,7 +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_entity::{Dead, InLoadedChunk};
|
||||||
use azalea_protocol::packets::game::{
|
use azalea_protocol::packets::game::{
|
||||||
ClientboundGamePacket, c_player_combat_kill::ClientboundPlayerCombatKill,
|
ClientboundGamePacket, c_player_combat_kill::ClientboundPlayerCombatKill,
|
||||||
};
|
};
|
||||||
|
@ -13,10 +13,11 @@ use azalea_world::{InstanceName, MinecraftEntityId};
|
||||||
use bevy_app::{App, Plugin, PreUpdate, Update};
|
use bevy_app::{App, Plugin, PreUpdate, Update};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
|
entity::Entity,
|
||||||
event::EventReader,
|
event::EventReader,
|
||||||
query::{Added, With},
|
query::{Added, With, Without},
|
||||||
schedule::IntoSystemConfigs,
|
schedule::IntoSystemConfigs,
|
||||||
system::Query,
|
system::{Commands, Query},
|
||||||
};
|
};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
@ -59,17 +60,30 @@ use crate::{
|
||||||
/// Note: Events are sent before they're processed, so for example game ticks
|
/// Note: Events are sent before they're processed, so for example game ticks
|
||||||
/// happen at the beginning of a tick before anything has happened.
|
/// happen at the beginning of a tick before anything has happened.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
/// Happens right after the bot switches into the Game state, but before
|
/// Happens right after the bot switches into the Game state, but before
|
||||||
/// it's actually spawned. This can be useful for setting the client
|
/// it's actually spawned. This can be useful for setting the client
|
||||||
/// information with `Client::set_client_information`, so the packet
|
/// information with `Client::set_client_information`, so the packet
|
||||||
/// doesn't have to be sent twice.
|
/// doesn't have to be sent twice.
|
||||||
///
|
///
|
||||||
/// You may want to use [`Event::Login`] instead to wait for the bot to be
|
/// You may want to use [`Event::Spawn`] instead to wait for the bot to be
|
||||||
/// in the world.
|
/// in the world.
|
||||||
Init,
|
Init,
|
||||||
/// The client is now in the world. Fired when we receive a login packet.
|
/// Fired when we receive a login packet, which is after [`Event::Init`] but
|
||||||
|
/// before [`Event::Spawn`]. You usually want [`Event::Spawn`] instead.
|
||||||
|
///
|
||||||
|
/// Your position may be [`Vec3::ZERO`] immediately after you receive this
|
||||||
|
/// event, but it'll be ready by the time you get [`Event::Spawn`].
|
||||||
|
///
|
||||||
|
/// [`Vec3::ZERO`]: azalea_core::position::Vec3::ZERO
|
||||||
Login,
|
Login,
|
||||||
|
/// Fired when the player fully spawns into the world and is ready to
|
||||||
|
/// interact with it.
|
||||||
|
///
|
||||||
|
/// This is usually the event you should listen for when waiting for the bot
|
||||||
|
/// to be ready.
|
||||||
|
Spawn,
|
||||||
/// A chat message was sent in the game chat.
|
/// A chat message was sent in the game chat.
|
||||||
Chat(ChatPacket),
|
Chat(ChatPacket),
|
||||||
/// Happens 20 times per second, but only when the world is loaded.
|
/// Happens 20 times per second, but only when the world is loaded.
|
||||||
|
@ -125,6 +139,7 @@ impl Plugin for EventsPlugin {
|
||||||
(
|
(
|
||||||
chat_listener,
|
chat_listener,
|
||||||
login_listener,
|
login_listener,
|
||||||
|
spawn_listener,
|
||||||
packet_listener,
|
packet_listener,
|
||||||
add_player_listener,
|
add_player_listener,
|
||||||
update_player_listener,
|
update_player_listener,
|
||||||
|
@ -156,12 +171,29 @@ pub fn login_listener(query: Query<&LocalPlayerEvents, Added<MinecraftEntityId>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A unit struct component that indicates that the entity has sent
|
||||||
|
/// [`Event::Spawn`].
|
||||||
|
///
|
||||||
|
/// This is just used internally by the [`spawn_listener`] system to avoid
|
||||||
|
/// sending the event twice for the same client.
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct SentSpawnEvent;
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn spawn_listener(
|
||||||
|
query: Query<(Entity, &LocalPlayerEvents), (Added<InLoadedChunk>, Without<SentSpawnEvent>)>,
|
||||||
|
mut commands: Commands,
|
||||||
|
) {
|
||||||
|
for (entity, local_player_events) in &query {
|
||||||
|
let _ = local_player_events.send(Event::Spawn);
|
||||||
|
commands.entity(entity).insert(SentSpawnEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn chat_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<ChatReceivedEvent>) {
|
pub fn chat_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<ChatReceivedEvent>) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
let local_player_events = query
|
if let Ok(local_player_events) = query.get(event.entity) {
|
||||||
.get(event.entity)
|
let _ = local_player_events.send(Event::Chat(event.packet.clone()));
|
||||||
.expect("Non-local entities shouldn't be able to receive chat events");
|
}
|
||||||
let _ = local_player_events.send(Event::Chat(event.packet.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,10 +209,9 @@ pub fn packet_listener(
|
||||||
mut events: EventReader<ReceivePacketEvent>,
|
mut events: EventReader<ReceivePacketEvent>,
|
||||||
) {
|
) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
let local_player_events = query
|
if let Ok(local_player_events) = query.get(event.entity) {
|
||||||
.get(event.entity)
|
let _ = local_player_events.send(Event::Packet(event.packet.clone()));
|
||||||
.expect("Non-local entities shouldn't be able to receive packet events");
|
}
|
||||||
let _ = local_player_events.send(Event::Packet(event.packet.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ use bevy_app::{App, Plugin, Update};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
event::{Event, EventReader, EventWriter},
|
event::{Event, EventReader},
|
||||||
query::{Changed, With},
|
query::{Changed, With},
|
||||||
schedule::IntoSystemConfigs,
|
schedule::IntoSystemConfigs,
|
||||||
system::{Commands, Query, Res},
|
system::{Commands, Query, Res},
|
||||||
|
@ -112,7 +112,7 @@ pub struct HitResultComponent(BlockHitResult);
|
||||||
pub fn handle_block_interact_event(
|
pub fn handle_block_interact_event(
|
||||||
mut events: EventReader<BlockInteractEvent>,
|
mut events: EventReader<BlockInteractEvent>,
|
||||||
mut query: Query<(Entity, &mut CurrentSequenceNumber, &HitResultComponent)>,
|
mut query: Query<(Entity, &mut CurrentSequenceNumber, &HitResultComponent)>,
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
let Ok((entity, mut sequence_number, hit_result)) = query.get_mut(event.entity) else {
|
let Ok((entity, mut sequence_number, hit_result)) = query.get_mut(event.entity) else {
|
||||||
|
@ -150,7 +150,7 @@ pub fn handle_block_interact_event(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
entity,
|
entity,
|
||||||
ServerboundUseItemOn {
|
ServerboundUseItemOn {
|
||||||
hand: InteractionHand::MainHand,
|
hand: InteractionHand::MainHand,
|
||||||
|
@ -299,12 +299,9 @@ pub fn can_use_game_master_blocks(
|
||||||
pub struct SwingArmEvent {
|
pub struct SwingArmEvent {
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
}
|
}
|
||||||
pub fn handle_swing_arm_event(
|
pub fn handle_swing_arm_event(mut events: EventReader<SwingArmEvent>, mut commands: Commands) {
|
||||||
mut events: EventReader<SwingArmEvent>,
|
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
|
||||||
) {
|
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
event.entity,
|
event.entity,
|
||||||
ServerboundSwing {
|
ServerboundSwing {
|
||||||
hand: InteractionHand::MainHand,
|
hand: InteractionHand::MainHand,
|
||||||
|
|
|
@ -21,7 +21,7 @@ use bevy_ecs::{
|
||||||
event::EventReader,
|
event::EventReader,
|
||||||
prelude::{Event, EventWriter},
|
prelude::{Event, EventWriter},
|
||||||
schedule::{IntoSystemConfigs, SystemSet},
|
schedule::{IntoSystemConfigs, SystemSet},
|
||||||
system::Query,
|
system::{Commands, Query},
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
|
@ -610,7 +610,7 @@ fn handle_container_close_event(
|
||||||
query: Query<(Entity, &Inventory)>,
|
query: Query<(Entity, &Inventory)>,
|
||||||
mut events: EventReader<CloseContainerEvent>,
|
mut events: EventReader<CloseContainerEvent>,
|
||||||
mut client_side_events: EventWriter<ClientSideCloseContainerEvent>,
|
mut client_side_events: EventWriter<ClientSideCloseContainerEvent>,
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
let (entity, inventory) = query.get(event.entity).unwrap();
|
let (entity, inventory) = query.get(event.entity).unwrap();
|
||||||
|
@ -622,7 +622,7 @@ fn handle_container_close_event(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
entity,
|
entity,
|
||||||
ServerboundContainerClose {
|
ServerboundContainerClose {
|
||||||
container_id: inventory.id,
|
container_id: inventory.id,
|
||||||
|
@ -662,7 +662,7 @@ pub struct ContainerClickEvent {
|
||||||
pub fn handle_container_click_event(
|
pub fn handle_container_click_event(
|
||||||
mut query: Query<(Entity, &mut Inventory)>,
|
mut query: Query<(Entity, &mut Inventory)>,
|
||||||
mut events: EventReader<ContainerClickEvent>,
|
mut events: EventReader<ContainerClickEvent>,
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
let (entity, mut inventory) = query.get_mut(event.entity).unwrap();
|
let (entity, mut inventory) = query.get_mut(event.entity).unwrap();
|
||||||
|
@ -689,7 +689,7 @@ pub fn handle_container_click_event(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
entity,
|
entity,
|
||||||
ServerboundContainerClick {
|
ServerboundContainerClick {
|
||||||
container_id: event.window_id,
|
container_id: event.window_id,
|
||||||
|
@ -744,7 +744,7 @@ pub struct SetSelectedHotbarSlotEvent {
|
||||||
}
|
}
|
||||||
fn handle_set_selected_hotbar_slot_event(
|
fn handle_set_selected_hotbar_slot_event(
|
||||||
mut events: EventReader<SetSelectedHotbarSlotEvent>,
|
mut events: EventReader<SetSelectedHotbarSlotEvent>,
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
mut query: Query<&mut Inventory>,
|
mut query: Query<&mut Inventory>,
|
||||||
) {
|
) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
|
@ -756,7 +756,7 @@ fn handle_set_selected_hotbar_slot_event(
|
||||||
}
|
}
|
||||||
|
|
||||||
inventory.selected_hotbar_slot = event.slot;
|
inventory.selected_hotbar_slot = event.slot;
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
event.entity,
|
event.entity,
|
||||||
ServerboundSetCarriedItem {
|
ServerboundSetCarriedItem {
|
||||||
slot: event.slot as u16,
|
slot: event.slot as u16,
|
||||||
|
|
|
@ -186,7 +186,7 @@ pub struct StartMiningBlockWithDirectionEvent {
|
||||||
fn handle_start_mining_block_with_direction_event(
|
fn handle_start_mining_block_with_direction_event(
|
||||||
mut events: EventReader<StartMiningBlockWithDirectionEvent>,
|
mut events: EventReader<StartMiningBlockWithDirectionEvent>,
|
||||||
mut finish_mining_events: EventWriter<FinishMiningBlockEvent>,
|
mut finish_mining_events: EventWriter<FinishMiningBlockEvent>,
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
mut attack_block_events: EventWriter<AttackBlockEvent>,
|
mut attack_block_events: EventWriter<AttackBlockEvent>,
|
||||||
mut mine_block_progress_events: EventWriter<MineBlockProgressEvent>,
|
mut mine_block_progress_events: EventWriter<MineBlockProgressEvent>,
|
||||||
mut query: Query<(
|
mut query: Query<(
|
||||||
|
@ -204,7 +204,6 @@ fn handle_start_mining_block_with_direction_event(
|
||||||
&mut MineBlockPos,
|
&mut MineBlockPos,
|
||||||
)>,
|
)>,
|
||||||
instances: Res<InstanceContainer>,
|
instances: Res<InstanceContainer>,
|
||||||
mut commands: Commands,
|
|
||||||
) {
|
) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
let (
|
let (
|
||||||
|
@ -252,7 +251,7 @@ fn handle_start_mining_block_with_direction_event(
|
||||||
{
|
{
|
||||||
if mining.is_some() {
|
if mining.is_some() {
|
||||||
// send a packet to stop mining since we just changed target
|
// send a packet to stop mining since we just changed target
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
event.entity,
|
event.entity,
|
||||||
ServerboundPlayerAction {
|
ServerboundPlayerAction {
|
||||||
action: s_player_action::Action::AbortDestroyBlock,
|
action: s_player_action::Action::AbortDestroyBlock,
|
||||||
|
@ -324,7 +323,7 @@ fn handle_start_mining_block_with_direction_event(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
event.entity,
|
event.entity,
|
||||||
ServerboundPlayerAction {
|
ServerboundPlayerAction {
|
||||||
action: s_player_action::Action::StartDestroyBlock,
|
action: s_player_action::Action::StartDestroyBlock,
|
||||||
|
@ -483,17 +482,16 @@ pub struct StopMiningBlockEvent {
|
||||||
}
|
}
|
||||||
pub fn handle_stop_mining_block_event(
|
pub fn handle_stop_mining_block_event(
|
||||||
mut events: EventReader<StopMiningBlockEvent>,
|
mut events: EventReader<StopMiningBlockEvent>,
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
mut mine_block_progress_events: EventWriter<MineBlockProgressEvent>,
|
mut mine_block_progress_events: EventWriter<MineBlockProgressEvent>,
|
||||||
mut query: Query<(&mut Mining, &MineBlockPos, &mut MineProgress)>,
|
mut query: Query<(&mut Mining, &MineBlockPos, &mut MineProgress)>,
|
||||||
mut commands: Commands,
|
|
||||||
) {
|
) {
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
let (mut _mining, mine_block_pos, mut mine_progress) = query.get_mut(event.entity).unwrap();
|
let (mut _mining, mine_block_pos, mut mine_progress) = query.get_mut(event.entity).unwrap();
|
||||||
|
|
||||||
let mine_block_pos =
|
let mine_block_pos =
|
||||||
mine_block_pos.expect("IsMining is true so MineBlockPos must be present");
|
mine_block_pos.expect("IsMining is true so MineBlockPos must be present");
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
event.entity,
|
event.entity,
|
||||||
ServerboundPlayerAction {
|
ServerboundPlayerAction {
|
||||||
action: s_player_action::Action::AbortDestroyBlock,
|
action: s_player_action::Action::AbortDestroyBlock,
|
||||||
|
@ -529,13 +527,12 @@ pub fn continue_mining_block(
|
||||||
&mut MineTicks,
|
&mut MineTicks,
|
||||||
&mut CurrentSequenceNumber,
|
&mut CurrentSequenceNumber,
|
||||||
)>,
|
)>,
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
mut mine_block_progress_events: EventWriter<MineBlockProgressEvent>,
|
mut mine_block_progress_events: EventWriter<MineBlockProgressEvent>,
|
||||||
mut finish_mining_events: EventWriter<FinishMiningBlockEvent>,
|
mut finish_mining_events: EventWriter<FinishMiningBlockEvent>,
|
||||||
mut start_mining_events: EventWriter<StartMiningBlockWithDirectionEvent>,
|
mut start_mining_events: EventWriter<StartMiningBlockWithDirectionEvent>,
|
||||||
mut swing_arm_events: EventWriter<SwingArmEvent>,
|
mut swing_arm_events: EventWriter<SwingArmEvent>,
|
||||||
instances: Res<InstanceContainer>,
|
instances: Res<InstanceContainer>,
|
||||||
mut commands: Commands,
|
|
||||||
) {
|
) {
|
||||||
for (
|
for (
|
||||||
entity,
|
entity,
|
||||||
|
@ -566,7 +563,7 @@ pub fn continue_mining_block(
|
||||||
position: mining.pos,
|
position: mining.pos,
|
||||||
});
|
});
|
||||||
*sequence_number += 1;
|
*sequence_number += 1;
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
entity,
|
entity,
|
||||||
ServerboundPlayerAction {
|
ServerboundPlayerAction {
|
||||||
action: s_player_action::Action::StartDestroyBlock,
|
action: s_player_action::Action::StartDestroyBlock,
|
||||||
|
@ -611,7 +608,7 @@ pub fn continue_mining_block(
|
||||||
entity,
|
entity,
|
||||||
position: mining.pos,
|
position: mining.pos,
|
||||||
});
|
});
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
entity,
|
entity,
|
||||||
ServerboundPlayerAction {
|
ServerboundPlayerAction {
|
||||||
action: s_player_action::Action::StopDestroyBlock,
|
action: s_player_action::Action::StopDestroyBlock,
|
||||||
|
|
|
@ -9,6 +9,7 @@ pub mod inventory;
|
||||||
pub mod mining;
|
pub mod mining;
|
||||||
pub mod movement;
|
pub mod movement;
|
||||||
pub mod packet;
|
pub mod packet;
|
||||||
|
pub mod pong;
|
||||||
pub mod respawn;
|
pub mod respawn;
|
||||||
pub mod task_pool;
|
pub mod task_pool;
|
||||||
pub mod tick_end;
|
pub mod tick_end;
|
||||||
|
|
|
@ -17,7 +17,7 @@ use azalea_protocol::packets::{
|
||||||
};
|
};
|
||||||
use azalea_world::{MinecraftEntityId, MoveEntityError};
|
use azalea_world::{MinecraftEntityId, MoveEntityError};
|
||||||
use bevy_app::{App, Plugin, Update};
|
use bevy_app::{App, Plugin, Update};
|
||||||
use bevy_ecs::prelude::{Event, EventWriter};
|
use bevy_ecs::prelude::Event;
|
||||||
use bevy_ecs::schedule::SystemSet;
|
use bevy_ecs::schedule::SystemSet;
|
||||||
use bevy_ecs::system::Commands;
|
use bevy_ecs::system::Commands;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
@ -158,7 +158,7 @@ pub fn send_position(
|
||||||
),
|
),
|
||||||
With<InLoadedChunk>,
|
With<InLoadedChunk>,
|
||||||
>,
|
>,
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (
|
for (
|
||||||
entity,
|
entity,
|
||||||
|
@ -245,7 +245,7 @@ pub fn send_position(
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(packet) = packet {
|
if let Some(packet) = packet {
|
||||||
send_packet_events.send(SendPacketEvent {
|
commands.trigger(SendPacketEvent {
|
||||||
sent_by: entity,
|
sent_by: entity,
|
||||||
packet,
|
packet,
|
||||||
});
|
});
|
||||||
|
@ -257,7 +257,6 @@ pub fn send_position(
|
||||||
pub struct LastSentInput(pub ServerboundPlayerInput);
|
pub struct LastSentInput(pub ServerboundPlayerInput);
|
||||||
pub fn send_player_input_packet(
|
pub fn send_player_input_packet(
|
||||||
mut query: Query<(Entity, &PhysicsState, &Jumping, Option<&LastSentInput>)>,
|
mut query: Query<(Entity, &PhysicsState, &Jumping, Option<&LastSentInput>)>,
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, physics_state, jumping, last_sent_input) in query.iter_mut() {
|
for (entity, physics_state, jumping, last_sent_input) in query.iter_mut() {
|
||||||
|
@ -279,7 +278,7 @@ pub fn send_player_input_packet(
|
||||||
let last_sent_input = last_sent_input.cloned().unwrap_or_default();
|
let last_sent_input = last_sent_input.cloned().unwrap_or_default();
|
||||||
|
|
||||||
if input != last_sent_input.0 {
|
if input != last_sent_input.0 {
|
||||||
send_packet_events.send(SendPacketEvent {
|
commands.trigger(SendPacketEvent {
|
||||||
sent_by: entity,
|
sent_by: entity,
|
||||||
packet: input.clone().into_variant(),
|
packet: input.clone().into_variant(),
|
||||||
});
|
});
|
||||||
|
@ -290,7 +289,7 @@ pub fn send_player_input_packet(
|
||||||
|
|
||||||
fn send_sprinting_if_needed(
|
fn send_sprinting_if_needed(
|
||||||
mut query: Query<(Entity, &MinecraftEntityId, &Sprinting, &mut PhysicsState)>,
|
mut query: Query<(Entity, &MinecraftEntityId, &Sprinting, &mut PhysicsState)>,
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for (entity, minecraft_entity_id, sprinting, mut physics_state) in query.iter_mut() {
|
for (entity, minecraft_entity_id, sprinting, mut physics_state) in query.iter_mut() {
|
||||||
let was_sprinting = physics_state.was_sprinting;
|
let was_sprinting = physics_state.was_sprinting;
|
||||||
|
@ -300,7 +299,7 @@ fn send_sprinting_if_needed(
|
||||||
} else {
|
} else {
|
||||||
azalea_protocol::packets::game::s_player_command::Action::StopSprinting
|
azalea_protocol::packets::game::s_player_command::Action::StopSprinting
|
||||||
};
|
};
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
entity,
|
entity,
|
||||||
ServerboundPlayerCommand {
|
ServerboundPlayerCommand {
|
||||||
id: *minecraft_entity_id,
|
id: *minecraft_entity_id,
|
||||||
|
|
|
@ -34,7 +34,7 @@ impl SendConfigPacketEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_send_packet_event(
|
pub fn handle_outgoing_packets(
|
||||||
mut send_packet_events: EventReader<SendConfigPacketEvent>,
|
mut send_packet_events: EventReader<SendConfigPacketEvent>,
|
||||||
mut query: Query<(&mut RawConnection, Option<&InConfigState>)>,
|
mut query: Query<(&mut RawConnection, Option<&InConfigState>)>,
|
||||||
) {
|
) {
|
||||||
|
@ -109,3 +109,13 @@ fn packet_interrupts(packet: &ClientboundConfigPacket) -> bool {
|
||||||
| ClientboundConfigPacket::Transfer(_)
|
| ClientboundConfigPacket::Transfer(_)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A Bevy trigger that's sent when our client receives a [`ClientboundPing`]
|
||||||
|
/// packet in the config state.
|
||||||
|
///
|
||||||
|
/// See [`PingEvent`] for more information.
|
||||||
|
///
|
||||||
|
/// [`ClientboundPing`]: azalea_protocol::packets::config::ClientboundPing
|
||||||
|
/// [`PingEvent`]: crate::packet::game::PingEvent
|
||||||
|
#[derive(Event, Debug, Clone)]
|
||||||
|
pub struct ConfigPingEvent(pub azalea_protocol::packets::config::ClientboundPing);
|
||||||
|
|
|
@ -142,10 +142,8 @@ impl ConfigPacketHandler<'_> {
|
||||||
pub fn ping(&mut self, p: ClientboundPing) {
|
pub fn ping(&mut self, p: ClientboundPing) {
|
||||||
debug!("Got ping packet (in configuration) {p:?}");
|
debug!("Got ping packet (in configuration) {p:?}");
|
||||||
|
|
||||||
as_system::<Query<&RawConnection>>(self.ecs, |query| {
|
as_system::<Commands>(self.ecs, |mut commands| {
|
||||||
let raw_conn = query.get(self.player).unwrap();
|
commands.trigger_targets(ConfigPingEvent(p), self.player);
|
||||||
|
|
||||||
raw_conn.write_packet(ServerboundPong { id: p.id }).unwrap();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ pub struct ReceivePacketEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An event for sending a packet to the server while we're in the `game` state.
|
/// An event for sending a packet to the server while we're in the `game` state.
|
||||||
#[derive(Event)]
|
#[derive(Event, Clone, Debug)]
|
||||||
pub struct SendPacketEvent {
|
pub struct SendPacketEvent {
|
||||||
pub sent_by: Entity,
|
pub sent_by: Entity,
|
||||||
pub packet: ServerboundGamePacket,
|
pub packet: ServerboundGamePacket,
|
||||||
|
@ -61,25 +61,33 @@ impl SendPacketEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_outgoing_packets(
|
pub fn handle_outgoing_packets_observer(
|
||||||
mut send_packet_events: EventReader<SendPacketEvent>,
|
trigger: Trigger<SendPacketEvent>,
|
||||||
mut query: Query<(&mut RawConnection, Option<&InGameState>)>,
|
mut query: Query<(&mut RawConnection, Option<&InGameState>)>,
|
||||||
) {
|
) {
|
||||||
for event in send_packet_events.read() {
|
let event = trigger.event();
|
||||||
if let Ok((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
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// debug!("Sending packet: {:?}", event.packet);
|
if let Ok((raw_connection, in_game_state)) = query.get_mut(event.sent_by) {
|
||||||
if let Err(e) = raw_connection.write_packet(event.packet.clone()) {
|
if in_game_state.is_none() {
|
||||||
error!("Failed to send packet: {e}");
|
error!(
|
||||||
}
|
"Tried to send a game packet {:?} while not in game state",
|
||||||
|
event.packet
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// debug!("Sending packet: {:?}", event.packet);
|
||||||
|
if let Err(e) = raw_connection.write_packet(event.packet.clone()) {
|
||||||
|
error!("Failed to send packet: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A system that converts [`SendPacketEvent`] events into triggers so they get
|
||||||
|
/// received by [`handle_outgoing_packets_observer`].
|
||||||
|
pub fn handle_outgoing_packets(mut commands: Commands, mut events: EventReader<SendPacketEvent>) {
|
||||||
|
for event in events.read() {
|
||||||
|
commands.trigger(event.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,3 +212,21 @@ pub struct InstanceLoadedEvent {
|
||||||
pub name: ResourceLocation,
|
pub name: ResourceLocation,
|
||||||
pub instance: Weak<RwLock<Instance>>,
|
pub instance: Weak<RwLock<Instance>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// This is not an event and can't be listened to from a normal system,
|
||||||
|
///so `EventReader<PingEvent>` will not work.
|
||||||
|
///
|
||||||
|
/// To use it, add your "system" with `add_observer` instead of `add_systems`
|
||||||
|
/// and use `Trigger<PingEvent>` instead of `EventReader`.
|
||||||
|
///
|
||||||
|
/// The client Entity that received the packet will be attached to the trigger.
|
||||||
|
///
|
||||||
|
/// [`ClientboundPing`]: azalea_protocol::packets::game::ClientboundPing
|
||||||
|
/// [`ConfigPingEvent`]: crate::packet::config::ConfigPingEvent
|
||||||
|
#[derive(Event, Debug, Clone)]
|
||||||
|
pub struct PingEvent(pub azalea_protocol::packets::game::ClientboundPing);
|
||||||
|
|
|
@ -223,7 +223,6 @@ impl GamePacketHandler<'_> {
|
||||||
EventWriter<InstanceLoadedEvent>,
|
EventWriter<InstanceLoadedEvent>,
|
||||||
ResMut<InstanceContainer>,
|
ResMut<InstanceContainer>,
|
||||||
ResMut<EntityUuidIndex>,
|
ResMut<EntityUuidIndex>,
|
||||||
EventWriter<SendPacketEvent>,
|
|
||||||
Query<&mut LoadedBy, Without<LocalEntity>>,
|
Query<&mut LoadedBy, Without<LocalEntity>>,
|
||||||
)>(
|
)>(
|
||||||
self.ecs,
|
self.ecs,
|
||||||
|
@ -233,7 +232,6 @@ impl GamePacketHandler<'_> {
|
||||||
mut instance_loaded_events,
|
mut instance_loaded_events,
|
||||||
mut instance_container,
|
mut instance_container,
|
||||||
mut entity_uuid_index,
|
mut entity_uuid_index,
|
||||||
mut send_packet_events,
|
|
||||||
mut loaded_by_query,
|
mut loaded_by_query,
|
||||||
)| {
|
)| {
|
||||||
let (
|
let (
|
||||||
|
@ -300,7 +298,7 @@ impl GamePacketHandler<'_> {
|
||||||
|
|
||||||
let entity_bundle = EntityBundle::new(
|
let entity_bundle = EntityBundle::new(
|
||||||
game_profile.uuid,
|
game_profile.uuid,
|
||||||
Vec3::default(),
|
Vec3::ZERO,
|
||||||
azalea_registry::EntityKind::Player,
|
azalea_registry::EntityKind::Player,
|
||||||
new_instance_name,
|
new_instance_name,
|
||||||
);
|
);
|
||||||
|
@ -343,7 +341,7 @@ impl GamePacketHandler<'_> {
|
||||||
"Sending client information because login: {:?}",
|
"Sending client information because login: {:?}",
|
||||||
client_information
|
client_information
|
||||||
);
|
);
|
||||||
send_packet_events.send(SendPacketEvent::new(self.player,
|
commands.trigger(SendPacketEvent::new(self.player,
|
||||||
azalea_protocol::packets::game::s_client_information::ServerboundClientInformation { information: client_information.clone() },
|
azalea_protocol::packets::game::s_client_information::ServerboundClientInformation { information: client_information.clone() },
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
|
@ -436,8 +434,8 @@ impl GamePacketHandler<'_> {
|
||||||
&mut Position,
|
&mut Position,
|
||||||
&mut LastSentPosition,
|
&mut LastSentPosition,
|
||||||
)>,
|
)>,
|
||||||
EventWriter<SendPacketEvent>,
|
Commands,
|
||||||
)>(self.ecs, |(mut query, mut send_packet_events)| {
|
)>(self.ecs, |(mut query, mut commands)| {
|
||||||
let Ok((mut physics, mut direction, mut position, mut last_sent_position)) =
|
let Ok((mut physics, mut direction, mut position, mut last_sent_position)) =
|
||||||
query.get_mut(self.player)
|
query.get_mut(self.player)
|
||||||
else {
|
else {
|
||||||
|
@ -508,11 +506,11 @@ impl GamePacketHandler<'_> {
|
||||||
|
|
||||||
// send the relevant packets
|
// send the relevant packets
|
||||||
|
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
self.player,
|
self.player,
|
||||||
ServerboundAcceptTeleportation { id: p.id },
|
ServerboundAcceptTeleportation { id: p.id },
|
||||||
));
|
));
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
self.player,
|
self.player,
|
||||||
ServerboundMovePlayerPosRot {
|
ServerboundMovePlayerPosRot {
|
||||||
pos: new_pos,
|
pos: new_pos,
|
||||||
|
@ -1044,14 +1042,14 @@ impl GamePacketHandler<'_> {
|
||||||
pub fn keep_alive(&mut self, p: &ClientboundKeepAlive) {
|
pub fn keep_alive(&mut self, p: &ClientboundKeepAlive) {
|
||||||
debug!("Got keep alive packet {p:?} for {:?}", self.player);
|
debug!("Got keep alive packet {p:?} for {:?}", self.player);
|
||||||
|
|
||||||
as_system::<(EventWriter<KeepAliveEvent>, EventWriter<SendPacketEvent>)>(
|
as_system::<(EventWriter<KeepAliveEvent>, Commands)>(
|
||||||
self.ecs,
|
self.ecs,
|
||||||
|(mut keepalive_events, mut send_packet_events)| {
|
|(mut keepalive_events, mut commands)| {
|
||||||
keepalive_events.send(KeepAliveEvent {
|
keepalive_events.send(KeepAliveEvent {
|
||||||
entity: self.player,
|
entity: self.player,
|
||||||
id: p.id,
|
id: p.id,
|
||||||
});
|
});
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
self.player,
|
self.player,
|
||||||
ServerboundKeepAlive { id: p.id },
|
ServerboundKeepAlive { id: p.id },
|
||||||
));
|
));
|
||||||
|
@ -1363,11 +1361,8 @@ impl GamePacketHandler<'_> {
|
||||||
pub fn ping(&mut self, p: &ClientboundPing) {
|
pub fn ping(&mut self, p: &ClientboundPing) {
|
||||||
debug!("Got ping packet {p:?}");
|
debug!("Got ping packet {p:?}");
|
||||||
|
|
||||||
as_system::<EventWriter<_>>(self.ecs, |mut events| {
|
as_system::<Commands>(self.ecs, |mut commands| {
|
||||||
events.send(SendPacketEvent::new(
|
commands.trigger_targets(PingEvent(p.clone()), self.player);
|
||||||
self.player,
|
|
||||||
ServerboundPong { id: p.id },
|
|
||||||
));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1483,7 +1478,7 @@ impl GamePacketHandler<'_> {
|
||||||
// this resets a bunch of our components like physics and stuff
|
// this resets a bunch of our components like physics and stuff
|
||||||
let entity_bundle = EntityBundle::new(
|
let entity_bundle = EntityBundle::new(
|
||||||
game_profile.uuid,
|
game_profile.uuid,
|
||||||
Vec3::default(),
|
Vec3::ZERO,
|
||||||
azalea_registry::EntityKind::Player,
|
azalea_registry::EntityKind::Player,
|
||||||
new_instance_name,
|
new_instance_name,
|
||||||
);
|
);
|
||||||
|
|
|
@ -54,11 +54,12 @@ impl Plugin for PacketPlugin {
|
||||||
login::process_packet_events,
|
login::process_packet_events,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.add_observer(game::handle_outgoing_packets_observer)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
config::handle_send_packet_event,
|
config::handle_outgoing_packets,
|
||||||
game::handle_outgoing_packets,
|
game::handle_outgoing_packets,
|
||||||
)
|
)
|
||||||
.chain(),
|
.chain(),
|
||||||
|
|
37
azalea-client/src/plugins/pong.rs
Normal file
37
azalea-client/src/plugins/pong.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use bevy_app::{App, Plugin};
|
||||||
|
use bevy_ecs::prelude::*;
|
||||||
|
|
||||||
|
use super::packet::{
|
||||||
|
config::{ConfigPingEvent, SendConfigPacketEvent},
|
||||||
|
game::PingEvent,
|
||||||
|
};
|
||||||
|
use crate::packet::game::SendPacketEvent;
|
||||||
|
|
||||||
|
/// A plugin that replies to [`ClientboundPing`] packets with
|
||||||
|
/// [`ServerboundPong`].
|
||||||
|
///
|
||||||
|
/// This works in both the `game` and `config` states.
|
||||||
|
///
|
||||||
|
/// [`ClientboundPing`]: azalea_protocol::packets::game::ClientboundPing
|
||||||
|
/// [`ServerboundPong`]: azalea_protocol::packets::game::ServerboundPong
|
||||||
|
pub struct PongPlugin;
|
||||||
|
impl Plugin for PongPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_observer(reply_to_game_ping)
|
||||||
|
.add_observer(reply_to_config_ping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reply_to_game_ping(trigger: Trigger<PingEvent>, mut commands: Commands) {
|
||||||
|
commands.trigger(SendPacketEvent::new(
|
||||||
|
trigger.entity(),
|
||||||
|
azalea_protocol::packets::game::ServerboundPong { id: trigger.0.id },
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reply_to_config_ping(trigger: Trigger<ConfigPingEvent>, mut commands: Commands) {
|
||||||
|
commands.trigger(SendConfigPacketEvent::new(
|
||||||
|
trigger.entity(),
|
||||||
|
azalea_protocol::packets::config::ServerboundPong { id: trigger.0.id },
|
||||||
|
));
|
||||||
|
}
|
|
@ -20,12 +20,9 @@ impl Plugin for RespawnPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn perform_respawn(
|
pub fn perform_respawn(mut events: EventReader<PerformRespawnEvent>, mut commands: Commands) {
|
||||||
mut events: EventReader<PerformRespawnEvent>,
|
|
||||||
mut send_packets: EventWriter<SendPacketEvent>,
|
|
||||||
) {
|
|
||||||
for event in events.read() {
|
for event in events.read() {
|
||||||
send_packets.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
event.entity,
|
event.entity,
|
||||||
ServerboundClientCommand {
|
ServerboundClientCommand {
|
||||||
action: s_client_command::Action::PerformRespawn,
|
action: s_client_command::Action::PerformRespawn,
|
||||||
|
|
|
@ -28,9 +28,9 @@ impl Plugin for TickEndPlugin {
|
||||||
|
|
||||||
pub fn game_tick_packet(
|
pub fn game_tick_packet(
|
||||||
query: Query<Entity, (With<LocalEntity>, With<InstanceName>)>,
|
query: Query<Entity, (With<LocalEntity>, With<InstanceName>)>,
|
||||||
mut send_packets: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
for entity in query.iter() {
|
for entity in query.iter() {
|
||||||
send_packets.send(SendPacketEvent::new(entity, ServerboundClientTickEnd));
|
commands.trigger(SendPacketEvent::new(entity, ServerboundClientTickEnd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ use uuid::Uuid;
|
||||||
use crate::disconnect::DisconnectEvent;
|
use crate::disconnect::DisconnectEvent;
|
||||||
use crate::{
|
use crate::{
|
||||||
ClientInformation, GameProfileComponent, InConfigState, InstanceHolder, LocalPlayerBundle,
|
ClientInformation, GameProfileComponent, InConfigState, InstanceHolder, LocalPlayerBundle,
|
||||||
events::LocalPlayerEvents,
|
|
||||||
raw_connection::{RawConnection, RawConnectionReader, RawConnectionWriter},
|
raw_connection::{RawConnection, RawConnectionReader, RawConnectionWriter},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -113,6 +112,15 @@ impl Simulation {
|
||||||
pub fn has_component<T: Component>(&self) -> bool {
|
pub fn has_component<T: Component>(&self) -> bool {
|
||||||
self.app.world().get::<T>(self.entity).is_some()
|
self.app.world().get::<T>(self.entity).is_some()
|
||||||
}
|
}
|
||||||
|
pub fn resource<T: Resource + Clone>(&self) -> T {
|
||||||
|
self.app.world().get_resource::<T>().unwrap().clone()
|
||||||
|
}
|
||||||
|
pub fn with_resource<T: Resource>(&self, f: impl FnOnce(&T)) {
|
||||||
|
f(self.app.world().get_resource::<T>().unwrap());
|
||||||
|
}
|
||||||
|
pub fn with_resource_mut<T: Resource>(&mut self, f: impl FnOnce(Mut<T>)) {
|
||||||
|
f(self.app.world_mut().get_resource_mut::<T>().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn chunk(&self, chunk_pos: ChunkPos) -> Option<Arc<RwLock<Chunk>>> {
|
pub fn chunk(&self, chunk_pos: ChunkPos) -> Option<Arc<RwLock<Chunk>>> {
|
||||||
self.component::<InstanceHolder>()
|
self.component::<InstanceHolder>()
|
||||||
|
@ -182,14 +190,11 @@ fn create_local_player_bundle(
|
||||||
connection_protocol,
|
connection_protocol,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (local_player_events_sender, _local_player_events_receiver) = mpsc::unbounded_channel();
|
|
||||||
|
|
||||||
let instance = Instance::default();
|
let instance = Instance::default();
|
||||||
let instance_holder = InstanceHolder::new(entity, Arc::new(RwLock::new(instance)));
|
let instance_holder = InstanceHolder::new(entity, Arc::new(RwLock::new(instance)));
|
||||||
|
|
||||||
let local_player_bundle = LocalPlayerBundle {
|
let local_player_bundle = LocalPlayerBundle {
|
||||||
raw_connection,
|
raw_connection,
|
||||||
local_player_events: LocalPlayerEvents(local_player_events_sender),
|
|
||||||
game_profile: GameProfileComponent(GameProfile::new(Uuid::nil(), "azalea".to_owned())),
|
game_profile: GameProfileComponent(GameProfile::new(Uuid::nil(), "azalea".to_owned())),
|
||||||
client_information: ClientInformation::default(),
|
client_information: ClientInformation::default(),
|
||||||
instance_holder,
|
instance_holder,
|
||||||
|
|
37
azalea-client/tests/receive_start_config_packet.rs
Normal file
37
azalea-client/tests/receive_start_config_packet.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use azalea_client::{InConfigState, packet::game::SendPacketEvent, test_simulation::*};
|
||||||
|
use azalea_core::resource_location::ResourceLocation;
|
||||||
|
use azalea_protocol::packets::{ConnectionProtocol, game::ClientboundStartConfiguration};
|
||||||
|
use azalea_registry::DimensionType;
|
||||||
|
use azalea_world::InstanceName;
|
||||||
|
use bevy_ecs::event::Events;
|
||||||
|
use bevy_log::tracing_subscriber;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_receive_start_config_packet() {
|
||||||
|
let _ = tracing_subscriber::fmt::try_init();
|
||||||
|
|
||||||
|
let mut simulation = Simulation::new(ConnectionProtocol::Game);
|
||||||
|
simulation.receive_packet(make_basic_login_packet(
|
||||||
|
DimensionType::new_raw(0),
|
||||||
|
ResourceLocation::new("minecraft:overworld"),
|
||||||
|
));
|
||||||
|
simulation.tick();
|
||||||
|
assert!(simulation.has_component::<InstanceName>());
|
||||||
|
simulation.tick();
|
||||||
|
|
||||||
|
// we shouldn't be using the `SendPacketEvent` event directly, we should be
|
||||||
|
// using the trigger instead
|
||||||
|
simulation.with_resource_mut::<Events<SendPacketEvent>>(|send_packet_events| {
|
||||||
|
assert_eq!(send_packet_events.len(), 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
simulation.receive_packet(ClientboundStartConfiguration);
|
||||||
|
|
||||||
|
simulation.tick();
|
||||||
|
assert!(simulation.has_component::<InConfigState>());
|
||||||
|
|
||||||
|
// check again just in case
|
||||||
|
simulation.with_resource_mut::<Events<SendPacketEvent>>(|send_packet_events| {
|
||||||
|
assert_eq!(send_packet_events.len(), 0);
|
||||||
|
});
|
||||||
|
}
|
35
azalea-client/tests/reply_to_ping_with_pong.rs
Normal file
35
azalea-client/tests/reply_to_ping_with_pong.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use azalea_client::{packet::game::SendPacketEvent, test_simulation::*};
|
||||||
|
use azalea_protocol::packets::{
|
||||||
|
ConnectionProtocol,
|
||||||
|
game::{ClientboundPing, ServerboundGamePacket},
|
||||||
|
};
|
||||||
|
use bevy_ecs::observer::Trigger;
|
||||||
|
use bevy_log::tracing_subscriber;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reply_to_ping_with_pong() {
|
||||||
|
let _ = tracing_subscriber::fmt::try_init();
|
||||||
|
|
||||||
|
let mut simulation = Simulation::new(ConnectionProtocol::Game);
|
||||||
|
let reply_count = Arc::new(Mutex::new(0));
|
||||||
|
let reply_count_clone = reply_count.clone();
|
||||||
|
simulation
|
||||||
|
.app
|
||||||
|
.add_observer(move |trigger: Trigger<SendPacketEvent>| {
|
||||||
|
if trigger.sent_by == simulation.entity {
|
||||||
|
if let ServerboundGamePacket::Pong(packet) = &trigger.packet {
|
||||||
|
assert_eq!(packet.id, 123);
|
||||||
|
*reply_count_clone.lock() += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
simulation.tick();
|
||||||
|
simulation.receive_packet(ClientboundPing { id: 123 });
|
||||||
|
simulation.tick();
|
||||||
|
|
||||||
|
assert_eq!(*reply_count.lock(), 1);
|
||||||
|
}
|
|
@ -50,7 +50,7 @@ pub fn move_relative(
|
||||||
pub fn input_vector(direction: &LookDirection, speed: f32, acceleration: &Vec3) -> Vec3 {
|
pub fn input_vector(direction: &LookDirection, speed: f32, acceleration: &Vec3) -> Vec3 {
|
||||||
let distance = acceleration.length_squared();
|
let distance = acceleration.length_squared();
|
||||||
if distance < 1.0E-7 {
|
if distance < 1.0E-7 {
|
||||||
return Vec3::default();
|
return Vec3::ZERO;
|
||||||
}
|
}
|
||||||
let acceleration = if distance > 1.0 {
|
let acceleration = if distance > 1.0 {
|
||||||
acceleration.normalize()
|
acceleration.normalize()
|
||||||
|
@ -140,8 +140,11 @@ impl Debug for EntityUuid {
|
||||||
|
|
||||||
/// The position of the entity right now.
|
/// The position of the entity right now.
|
||||||
///
|
///
|
||||||
/// You are free to change this; there's systems that update the indexes
|
/// You are free to change the value of this component; there's systems that
|
||||||
/// automatically.
|
/// update the indexes automatically.
|
||||||
|
///
|
||||||
|
/// Its value is set to a default of [`Vec3::ZERO`] when it receives the login
|
||||||
|
/// packet, its true position may be set ticks later.
|
||||||
#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
|
#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
|
||||||
pub struct Position(Vec3);
|
pub struct Position(Vec3);
|
||||||
impl Position {
|
impl Position {
|
||||||
|
@ -304,7 +307,7 @@ pub struct Physics {
|
||||||
impl Physics {
|
impl Physics {
|
||||||
pub fn new(dimensions: EntityDimensions, pos: Vec3) -> Self {
|
pub fn new(dimensions: EntityDimensions, pos: Vec3) -> Self {
|
||||||
Self {
|
Self {
|
||||||
velocity: Vec3::default(),
|
velocity: Vec3::ZERO,
|
||||||
vec_delta_codec: VecDeltaCodec::new(pos),
|
vec_delta_codec: VecDeltaCodec::new(pos),
|
||||||
|
|
||||||
old_position: pos,
|
old_position: pos,
|
||||||
|
|
|
@ -109,7 +109,7 @@ fn update_fluid_height_and_do_fluid_pushing(
|
||||||
let mut min_height_touching = 0.;
|
let mut min_height_touching = 0.;
|
||||||
let is_entity_pushable_by_fluid = true;
|
let is_entity_pushable_by_fluid = true;
|
||||||
let mut touching_fluid = false;
|
let mut touching_fluid = false;
|
||||||
let mut additional_player_delta = Vec3::default();
|
let mut additional_player_delta = Vec3::ZERO;
|
||||||
let mut num_fluids_being_touched = 0;
|
let mut num_fluids_being_touched = 0;
|
||||||
|
|
||||||
for cur_x in min_x..max_x {
|
for cur_x in min_x..max_x {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use azalea_client::InConfigState;
|
use azalea_client::InConfigState;
|
||||||
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::config::{SendConfigPacketEvent, handle_send_packet_event};
|
use azalea_client::packet::config::{SendConfigPacketEvent, handle_outgoing_packets};
|
||||||
use azalea_client::packet::game::SendPacketEvent;
|
use azalea_client::packet::game::SendPacketEvent;
|
||||||
use azalea_client::packet::{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;
|
||||||
|
@ -24,7 +24,7 @@ impl Plugin for AcceptResourcePacksPlugin {
|
||||||
.after(death_event_on_0_health)
|
.after(death_event_on_0_health)
|
||||||
.after(handle_chunk_batch_finished_event)
|
.after(handle_chunk_batch_finished_event)
|
||||||
.after(InventorySet)
|
.after(InventorySet)
|
||||||
.before(handle_send_packet_event)
|
.before(handle_outgoing_packets)
|
||||||
.after(azalea_client::brand::handle_end_login_state),
|
.after(azalea_client::brand::handle_end_login_state),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ impl Plugin for AcceptResourcePacksPlugin {
|
||||||
|
|
||||||
fn accept_resource_pack(
|
fn accept_resource_pack(
|
||||||
mut events: EventReader<ResourcePackEvent>,
|
mut events: EventReader<ResourcePackEvent>,
|
||||||
mut send_packet_events: EventWriter<SendPacketEvent>,
|
mut commands: Commands,
|
||||||
mut send_config_packet_events: EventWriter<SendConfigPacketEvent>,
|
mut send_config_packet_events: EventWriter<SendConfigPacketEvent>,
|
||||||
query_in_config_state: Query<Option<&InConfigState>>,
|
query_in_config_state: Query<Option<&InConfigState>>,
|
||||||
) {
|
) {
|
||||||
|
@ -57,14 +57,14 @@ fn accept_resource_pack(
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
event.entity,
|
event.entity,
|
||||||
ServerboundResourcePack {
|
ServerboundResourcePack {
|
||||||
id: event.id,
|
id: event.id,
|
||||||
action: s_resource_pack::Action::Accepted,
|
action: s_resource_pack::Action::Accepted,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
send_packet_events.send(SendPacketEvent::new(
|
commands.trigger(SendPacketEvent::new(
|
||||||
event.entity,
|
event.entity,
|
||||||
ServerboundResourcePack {
|
ServerboundResourcePack {
|
||||||
id: event.id,
|
id: event.id,
|
||||||
|
|
|
@ -539,6 +539,7 @@ impl Default for SwarmBuilder<NoState, NoSwarmState, (), ()> {
|
||||||
|
|
||||||
/// An event about something that doesn't have to do with a single bot.
|
/// An event about something that doesn't have to do with a single bot.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum SwarmEvent {
|
pub enum SwarmEvent {
|
||||||
/// All the bots in the swarm have successfully joined the server.
|
/// All the bots in the swarm have successfully joined the server.
|
||||||
Login,
|
Login,
|
||||||
|
@ -656,13 +657,16 @@ impl Swarm {
|
||||||
.custom_resolved_address
|
.custom_resolved_address
|
||||||
.unwrap_or_else(|| *self.resolved_address.read());
|
.unwrap_or_else(|| *self.resolved_address.read());
|
||||||
|
|
||||||
let (bot, rx) = Client::start_client(StartClientOpts {
|
let (tx, rx) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
|
let bot = Client::start_client(StartClientOpts {
|
||||||
ecs_lock: self.ecs_lock.clone(),
|
ecs_lock: self.ecs_lock.clone(),
|
||||||
account,
|
account,
|
||||||
address: &address,
|
address: &address,
|
||||||
resolved_address: &resolved_address,
|
resolved_address: &resolved_address,
|
||||||
proxy: join_opts.proxy.clone(),
|
proxy: join_opts.proxy.clone(),
|
||||||
run_schedule_sender: self.run_schedule_sender.clone(),
|
run_schedule_sender: self.run_schedule_sender.clone(),
|
||||||
|
event_sender: Some(tx),
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
// add the state to the client
|
// add the state to the client
|
||||||
|
|
Loading…
Add table
Reference in a new issue