diff --git a/azalea-client/src/plugins/attack.rs b/azalea-client/src/plugins/attack.rs index 1b2bc1ee..226ae603 100644 --- a/azalea-client/src/plugins/attack.rs +++ b/azalea-client/src/plugins/attack.rs @@ -75,7 +75,7 @@ pub fn handle_attack_event( &mut Sprinting, &mut ShiftKeyDown, )>, - mut send_packet_events: EventWriter, + mut commands: Commands, mut swing_arm_event: EventWriter, ) { for event in events.read() { @@ -85,7 +85,7 @@ pub fn handle_attack_event( swing_arm_event.send(SwingArmEvent { entity: event.entity, }); - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( event.entity, ServerboundInteract { entity_id: event.target, diff --git a/azalea-client/src/plugins/brand.rs b/azalea-client/src/plugins/brand.rs index 5ee8e52c..94783230 100644 --- a/azalea-client/src/plugins/brand.rs +++ b/azalea-client/src/plugins/brand.rs @@ -19,7 +19,7 @@ impl Plugin for BrandPlugin { fn build(&self, app: &mut App) { app.add_systems( Update, - handle_end_login_state.before(crate::packet::config::handle_send_packet_event), + handle_end_login_state.before(crate::packet::config::handle_outgoing_packets), ); } } diff --git a/azalea-client/src/plugins/chat/handler.rs b/azalea-client/src/plugins/chat/handler.rs index 31fbc00e..356d3fba 100644 --- a/azalea-client/src/plugins/chat/handler.rs +++ b/azalea-client/src/plugins/chat/handler.rs @@ -29,7 +29,7 @@ pub struct SendChatKindEvent { pub fn handle_send_chat_kind_event( mut events: EventReader, - mut send_packet_events: EventWriter, + mut commands: Commands, ) { for event in events.read() { 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)); } } diff --git a/azalea-client/src/plugins/chunks.rs b/azalea-client/src/plugins/chunks.rs index cdda3eba..089cc5d4 100644 --- a/azalea-client/src/plugins/chunks.rs +++ b/azalea-client/src/plugins/chunks.rs @@ -154,13 +154,13 @@ pub fn handle_chunk_batch_start_event( pub fn handle_chunk_batch_finished_event( mut query: Query<&mut ChunkBatchInfo>, mut events: EventReader, - mut send_packets: EventWriter, + mut commands: Commands, ) { for event in events.read() { if let Ok(mut chunk_batch_info) = query.get_mut(event.entity) { chunk_batch_info.batch_finished(event.batch_size); let desired_chunks_per_tick = chunk_batch_info.desired_chunks_per_tick(); - send_packets.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( event.entity, ServerboundChunkBatchReceived { desired_chunks_per_tick, diff --git a/azalea-client/src/plugins/interact.rs b/azalea-client/src/plugins/interact.rs index 1a344cc8..a0dfa12a 100644 --- a/azalea-client/src/plugins/interact.rs +++ b/azalea-client/src/plugins/interact.rs @@ -22,7 +22,7 @@ use bevy_app::{App, Plugin, Update}; use bevy_ecs::{ component::Component, entity::Entity, - event::{Event, EventReader, EventWriter}, + event::{Event, EventReader}, query::{Changed, With}, schedule::IntoSystemConfigs, system::{Commands, Query, Res}, @@ -112,7 +112,7 @@ pub struct HitResultComponent(BlockHitResult); pub fn handle_block_interact_event( mut events: EventReader, mut query: Query<(Entity, &mut CurrentSequenceNumber, &HitResultComponent)>, - mut send_packet_events: EventWriter, + mut commands: Commands, ) { for event in events.read() { 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, ServerboundUseItemOn { hand: InteractionHand::MainHand, @@ -299,12 +299,9 @@ pub fn can_use_game_master_blocks( pub struct SwingArmEvent { pub entity: Entity, } -pub fn handle_swing_arm_event( - mut events: EventReader, - mut send_packet_events: EventWriter, -) { +pub fn handle_swing_arm_event(mut events: EventReader, mut commands: Commands) { for event in events.read() { - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( event.entity, ServerboundSwing { hand: InteractionHand::MainHand, diff --git a/azalea-client/src/plugins/inventory.rs b/azalea-client/src/plugins/inventory.rs index 3f823ca2..da450ad7 100644 --- a/azalea-client/src/plugins/inventory.rs +++ b/azalea-client/src/plugins/inventory.rs @@ -21,7 +21,7 @@ use bevy_ecs::{ event::EventReader, prelude::{Event, EventWriter}, schedule::{IntoSystemConfigs, SystemSet}, - system::Query, + system::{Commands, Query}, }; use tracing::warn; @@ -610,7 +610,7 @@ fn handle_container_close_event( query: Query<(Entity, &Inventory)>, mut events: EventReader, mut client_side_events: EventWriter, - mut send_packet_events: EventWriter, + mut commands: Commands, ) { for event in events.read() { let (entity, inventory) = query.get(event.entity).unwrap(); @@ -622,7 +622,7 @@ fn handle_container_close_event( continue; } - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( entity, ServerboundContainerClose { container_id: inventory.id, @@ -662,7 +662,7 @@ pub struct ContainerClickEvent { pub fn handle_container_click_event( mut query: Query<(Entity, &mut Inventory)>, mut events: EventReader, - mut send_packet_events: EventWriter, + mut commands: Commands, ) { for event in events.read() { 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, ServerboundContainerClick { container_id: event.window_id, @@ -744,7 +744,7 @@ pub struct SetSelectedHotbarSlotEvent { } fn handle_set_selected_hotbar_slot_event( mut events: EventReader, - mut send_packet_events: EventWriter, + mut commands: Commands, mut query: Query<&mut Inventory>, ) { for event in events.read() { @@ -756,7 +756,7 @@ fn handle_set_selected_hotbar_slot_event( } inventory.selected_hotbar_slot = event.slot; - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( event.entity, ServerboundSetCarriedItem { slot: event.slot as u16, diff --git a/azalea-client/src/plugins/mining.rs b/azalea-client/src/plugins/mining.rs index beb380b7..d4a6f2a0 100644 --- a/azalea-client/src/plugins/mining.rs +++ b/azalea-client/src/plugins/mining.rs @@ -186,7 +186,7 @@ pub struct StartMiningBlockWithDirectionEvent { fn handle_start_mining_block_with_direction_event( mut events: EventReader, mut finish_mining_events: EventWriter, - mut send_packet_events: EventWriter, + mut commands: Commands, mut attack_block_events: EventWriter, mut mine_block_progress_events: EventWriter, mut query: Query<( @@ -204,7 +204,6 @@ fn handle_start_mining_block_with_direction_event( &mut MineBlockPos, )>, instances: Res, - mut commands: Commands, ) { for event in events.read() { let ( @@ -252,7 +251,7 @@ fn handle_start_mining_block_with_direction_event( { if mining.is_some() { // send a packet to stop mining since we just changed target - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( event.entity, ServerboundPlayerAction { 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, ServerboundPlayerAction { action: s_player_action::Action::StartDestroyBlock, @@ -483,17 +482,16 @@ pub struct StopMiningBlockEvent { } pub fn handle_stop_mining_block_event( mut events: EventReader, - mut send_packet_events: EventWriter, + mut commands: Commands, mut mine_block_progress_events: EventWriter, mut query: Query<(&mut Mining, &MineBlockPos, &mut MineProgress)>, - mut commands: Commands, ) { for event in events.read() { let (mut _mining, mine_block_pos, mut mine_progress) = query.get_mut(event.entity).unwrap(); let mine_block_pos = mine_block_pos.expect("IsMining is true so MineBlockPos must be present"); - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( event.entity, ServerboundPlayerAction { action: s_player_action::Action::AbortDestroyBlock, @@ -529,13 +527,12 @@ pub fn continue_mining_block( &mut MineTicks, &mut CurrentSequenceNumber, )>, - mut send_packet_events: EventWriter, + mut commands: Commands, mut mine_block_progress_events: EventWriter, mut finish_mining_events: EventWriter, mut start_mining_events: EventWriter, mut swing_arm_events: EventWriter, instances: Res, - mut commands: Commands, ) { for ( entity, @@ -566,7 +563,7 @@ pub fn continue_mining_block( position: mining.pos, }); *sequence_number += 1; - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( entity, ServerboundPlayerAction { action: s_player_action::Action::StartDestroyBlock, @@ -611,7 +608,7 @@ pub fn continue_mining_block( entity, position: mining.pos, }); - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( entity, ServerboundPlayerAction { action: s_player_action::Action::StopDestroyBlock, diff --git a/azalea-client/src/plugins/movement.rs b/azalea-client/src/plugins/movement.rs index 17b92e65..54a46bcb 100644 --- a/azalea-client/src/plugins/movement.rs +++ b/azalea-client/src/plugins/movement.rs @@ -17,7 +17,7 @@ use azalea_protocol::packets::{ }; use azalea_world::{MinecraftEntityId, MoveEntityError}; 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::system::Commands; use bevy_ecs::{ @@ -158,7 +158,7 @@ pub fn send_position( ), With, >, - mut send_packet_events: EventWriter, + mut commands: Commands, ) { for ( entity, @@ -245,7 +245,7 @@ pub fn send_position( }; if let Some(packet) = packet { - send_packet_events.send(SendPacketEvent { + commands.trigger(SendPacketEvent { sent_by: entity, packet, }); @@ -257,7 +257,6 @@ pub fn send_position( pub struct LastSentInput(pub ServerboundPlayerInput); pub fn send_player_input_packet( mut query: Query<(Entity, &PhysicsState, &Jumping, Option<&LastSentInput>)>, - mut send_packet_events: EventWriter, mut commands: Commands, ) { 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(); if input != last_sent_input.0 { - send_packet_events.send(SendPacketEvent { + commands.trigger(SendPacketEvent { sent_by: entity, packet: input.clone().into_variant(), }); @@ -290,7 +289,7 @@ pub fn send_player_input_packet( fn send_sprinting_if_needed( mut query: Query<(Entity, &MinecraftEntityId, &Sprinting, &mut PhysicsState)>, - mut send_packet_events: EventWriter, + mut commands: Commands, ) { for (entity, minecraft_entity_id, sprinting, mut physics_state) in query.iter_mut() { let was_sprinting = physics_state.was_sprinting; @@ -300,7 +299,7 @@ fn send_sprinting_if_needed( } else { azalea_protocol::packets::game::s_player_command::Action::StopSprinting }; - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( entity, ServerboundPlayerCommand { id: *minecraft_entity_id, diff --git a/azalea-client/src/plugins/packet/config/events.rs b/azalea-client/src/plugins/packet/config/events.rs index eddcf72f..9ed6c097 100644 --- a/azalea-client/src/plugins/packet/config/events.rs +++ b/azalea-client/src/plugins/packet/config/events.rs @@ -34,7 +34,7 @@ impl SendConfigPacketEvent { } } -pub fn handle_send_packet_event( +pub fn handle_outgoing_packets( mut send_packet_events: EventReader, mut query: Query<(&mut RawConnection, Option<&InConfigState>)>, ) { diff --git a/azalea-client/src/plugins/packet/game/events.rs b/azalea-client/src/plugins/packet/game/events.rs index 5ba9972d..8a5aca3c 100644 --- a/azalea-client/src/plugins/packet/game/events.rs +++ b/azalea-client/src/plugins/packet/game/events.rs @@ -49,7 +49,7 @@ pub struct ReceivePacketEvent { } /// 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 sent_by: Entity, pub packet: ServerboundGamePacket, @@ -61,25 +61,33 @@ impl SendPacketEvent { } } -pub fn handle_outgoing_packets( - mut send_packet_events: EventReader, +pub fn handle_outgoing_packets_observer( + trigger: Trigger, mut query: Query<(&mut RawConnection, Option<&InGameState>)>, ) { - for event in send_packet_events.read() { - 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; - } + let event = trigger.event(); - // debug!("Sending packet: {:?}", event.packet); - if let Err(e) = raw_connection.write_packet(event.packet.clone()) { - error!("Failed to send packet: {e}"); - } + 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 + ); + 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) { + for event in events.read() { + commands.trigger(event.clone()); } } diff --git a/azalea-client/src/plugins/packet/game/mod.rs b/azalea-client/src/plugins/packet/game/mod.rs index c72ce551..ed8c62ea 100644 --- a/azalea-client/src/plugins/packet/game/mod.rs +++ b/azalea-client/src/plugins/packet/game/mod.rs @@ -222,7 +222,6 @@ impl GamePacketHandler<'_> { EventWriter, ResMut, ResMut, - EventWriter, Query<&mut LoadedBy, Without>, )>( self.ecs, @@ -232,7 +231,6 @@ impl GamePacketHandler<'_> { mut instance_loaded_events, mut instance_container, mut entity_uuid_index, - mut send_packet_events, mut loaded_by_query, )| { let ( @@ -342,7 +340,7 @@ impl GamePacketHandler<'_> { "Sending client information because login: {:?}", 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() }, )); }, @@ -435,8 +433,8 @@ impl GamePacketHandler<'_> { &mut Position, &mut LastSentPosition, )>, - EventWriter, - )>(self.ecs, |(mut query, mut send_packet_events)| { + Commands, + )>(self.ecs, |(mut query, mut commands)| { let Ok((mut physics, mut direction, mut position, mut last_sent_position)) = query.get_mut(self.player) else { @@ -507,11 +505,11 @@ impl GamePacketHandler<'_> { // send the relevant packets - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( self.player, ServerboundAcceptTeleportation { id: p.id }, )); - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( self.player, ServerboundMovePlayerPosRot { pos: new_pos, @@ -1043,14 +1041,14 @@ impl GamePacketHandler<'_> { pub fn keep_alive(&mut self, p: &ClientboundKeepAlive) { debug!("Got keep alive packet {p:?} for {:?}", self.player); - as_system::<(EventWriter, EventWriter)>( + as_system::<(EventWriter, Commands)>( self.ecs, - |(mut keepalive_events, mut send_packet_events)| { + |(mut keepalive_events, mut commands)| { keepalive_events.send(KeepAliveEvent { entity: self.player, id: p.id, }); - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( self.player, ServerboundKeepAlive { id: p.id }, )); @@ -1364,8 +1362,8 @@ impl GamePacketHandler<'_> { pub fn ping(&mut self, p: &ClientboundPing) { debug!("Got ping packet {p:?}"); - as_system::>(self.ecs, |mut events| { - events.send(SendPacketEvent::new( + as_system::(self.ecs, |mut commands| { + commands.trigger(SendPacketEvent::new( self.player, ServerboundPong { id: p.id }, )); diff --git a/azalea-client/src/plugins/packet/mod.rs b/azalea-client/src/plugins/packet/mod.rs index 96a396d9..826cd5cf 100644 --- a/azalea-client/src/plugins/packet/mod.rs +++ b/azalea-client/src/plugins/packet/mod.rs @@ -54,11 +54,12 @@ impl Plugin for PacketPlugin { login::process_packet_events, ), ) + .add_observer(game::handle_outgoing_packets_observer) .add_systems( Update, ( ( - config::handle_send_packet_event, + config::handle_outgoing_packets, game::handle_outgoing_packets, ) .chain(), diff --git a/azalea-client/src/plugins/respawn.rs b/azalea-client/src/plugins/respawn.rs index 5797406b..24479805 100644 --- a/azalea-client/src/plugins/respawn.rs +++ b/azalea-client/src/plugins/respawn.rs @@ -20,12 +20,9 @@ impl Plugin for RespawnPlugin { } } -pub fn perform_respawn( - mut events: EventReader, - mut send_packets: EventWriter, -) { +pub fn perform_respawn(mut events: EventReader, mut commands: Commands) { for event in events.read() { - send_packets.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( event.entity, ServerboundClientCommand { action: s_client_command::Action::PerformRespawn, diff --git a/azalea-client/src/plugins/tick_end.rs b/azalea-client/src/plugins/tick_end.rs index c7737eb1..6ad924ee 100644 --- a/azalea-client/src/plugins/tick_end.rs +++ b/azalea-client/src/plugins/tick_end.rs @@ -28,9 +28,9 @@ impl Plugin for TickEndPlugin { pub fn game_tick_packet( query: Query, With)>, - mut send_packets: EventWriter, + mut commands: Commands, ) { for entity in query.iter() { - send_packets.send(SendPacketEvent::new(entity, ServerboundClientTickEnd)); + commands.trigger(SendPacketEvent::new(entity, ServerboundClientTickEnd)); } } diff --git a/azalea-client/src/test_simulation.rs b/azalea-client/src/test_simulation.rs index 564db0cf..0f34c14e 100644 --- a/azalea-client/src/test_simulation.rs +++ b/azalea-client/src/test_simulation.rs @@ -113,6 +113,15 @@ impl Simulation { pub fn has_component(&self) -> bool { self.app.world().get::(self.entity).is_some() } + pub fn resource(&self) -> T { + self.app.world().get_resource::().unwrap().clone() + } + pub fn with_resource(&self, f: impl FnOnce(&T)) { + f(self.app.world().get_resource::().unwrap()); + } + pub fn with_resource_mut(&mut self, f: impl FnOnce(Mut)) { + f(self.app.world_mut().get_resource_mut::().unwrap()); + } pub fn chunk(&self, chunk_pos: ChunkPos) -> Option>> { self.component::() diff --git a/azalea-client/tests/receive_start_config_packet.rs b/azalea-client/tests/receive_start_config_packet.rs new file mode 100644 index 00000000..36762966 --- /dev/null +++ b/azalea-client/tests/receive_start_config_packet.rs @@ -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::()); + simulation.tick(); + + // we shouldn't be using the `SendPacketEvent` event directly, we should be + // using the trigger instead + simulation.with_resource_mut::>(|send_packet_events| { + assert_eq!(send_packet_events.len(), 0); + }); + + simulation.receive_packet(ClientboundStartConfiguration); + + simulation.tick(); + assert!(simulation.has_component::()); + + // check again just in case + simulation.with_resource_mut::>(|send_packet_events| { + assert_eq!(send_packet_events.len(), 0); + }); +} diff --git a/azalea/src/accept_resource_packs.rs b/azalea/src/accept_resource_packs.rs index 560140d0..25caf93d 100644 --- a/azalea/src/accept_resource_packs.rs +++ b/azalea/src/accept_resource_packs.rs @@ -1,7 +1,7 @@ use azalea_client::InConfigState; use azalea_client::chunks::handle_chunk_batch_finished_event; 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::{death_event_on_0_health, game::ResourcePackEvent}; use azalea_client::respawn::perform_respawn; @@ -24,7 +24,7 @@ impl Plugin for AcceptResourcePacksPlugin { .after(death_event_on_0_health) .after(handle_chunk_batch_finished_event) .after(InventorySet) - .before(handle_send_packet_event) + .before(handle_outgoing_packets) .after(azalea_client::brand::handle_end_login_state), ); } @@ -32,7 +32,7 @@ impl Plugin for AcceptResourcePacksPlugin { fn accept_resource_pack( mut events: EventReader, - mut send_packet_events: EventWriter, + mut commands: Commands, mut send_config_packet_events: EventWriter, query_in_config_state: Query>, ) { @@ -57,14 +57,14 @@ fn accept_resource_pack( }, )); } else { - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( event.entity, ServerboundResourcePack { id: event.id, action: s_resource_pack::Action::Accepted, }, )); - send_packet_events.send(SendPacketEvent::new( + commands.trigger(SendPacketEvent::new( event.entity, ServerboundResourcePack { id: event.id,