From 634cb8d72c6608512aedba19e5cd669104bc35ea Mon Sep 17 00:00:00 2001 From: mat <27899617+mat-1@users.noreply.github.com> Date: Wed, 3 May 2023 20:57:27 -0500 Subject: Inventory (#48) * start adding azalea-inventory * design more of how inventories are defined * start working on az-inv-macros * inventory macro works * start adding inventory codegen * update some deps * add inventory codegen * manually write inventory menus * put the inventories in Client * start on containersetcontent * inventory menu should hopefully work * checks in containersetcontent * format a comment * move some variant matches * inventory.rs * inventory stuff * more inventory stuff * inventory/container tracking works * start adding interact function * sequence number * start adding HitResultComponent * implement traverse_blocks * start adding clip * add clip function * update_hit_result_component * start trying to fix * fix * make some stuff simpler * clippy * lever * chest * container handle * fix ambiguity * fix some doc tests * move some container stuff from az-client to azalea * clicking container * start implementing simulate_click * keep working on simulate click * implement more of simulate_click this is really boring * inventory fixes * start implementing shift clicking * fix panic in azalea-chat i hope * shift clicking implemented * more inventory stuff * fix items not showing in containers sometimes * fix test * fix all warnings * remove a println --------- Co-authored-by: mat --- azalea/src/bot.rs | 18 ++++-- azalea/src/container.rs | 140 +++++++++++++++++++++++++++++++++++++++++++ azalea/src/lib.rs | 5 +- azalea/src/pathfinder/mod.rs | 4 +- azalea/src/prelude.rs | 5 +- azalea/src/swarm/mod.rs | 6 +- 6 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 azalea/src/container.rs (limited to 'azalea/src') diff --git a/azalea/src/bot.rs b/azalea/src/bot.rs index a45ae28d..e5ea4c28 100644 --- a/azalea/src/bot.rs +++ b/azalea/src/bot.rs @@ -1,4 +1,5 @@ use crate::app::{App, CoreSchedule, IntoSystemAppConfig, Plugin, PluginGroup, PluginGroupBuilder}; +use crate::container::ContainerPlugin; use crate::ecs::{ component::Component, entity::Entity, @@ -9,7 +10,8 @@ use crate::ecs::{ }; use azalea_core::Vec3; use azalea_physics::{force_jump_listener, PhysicsSet}; -use azalea_world::entity::{metadata::Player, set_rotation, Jumping, Local, Physics, Position}; +use azalea_world::entity::{clamp_look_direction, EyeHeight, LookDirection}; +use azalea_world::entity::{metadata::Player, Jumping, Local, Position}; use std::f64::consts::PI; use crate::pathfinder::PathfinderPlugin; @@ -22,7 +24,9 @@ impl Plugin for BotPlugin { .add_event::() .add_systems(( insert_bot, - look_at_listener.before(force_jump_listener), + look_at_listener + .before(force_jump_listener) + .before(clamp_look_direction), jump_listener, stop_jumping .in_schedule(CoreSchedule::FixedUpdate) @@ -99,12 +103,13 @@ pub struct LookAtEvent { } fn look_at_listener( mut events: EventReader, - mut query: Query<(&Position, &mut Physics)>, + mut query: Query<(&Position, &EyeHeight, &mut LookDirection)>, ) { for event in events.iter() { - if let Ok((position, mut physics)) = query.get_mut(event.entity) { - let (y_rot, x_rot) = direction_looking_at(position, &event.position); - set_rotation(&mut physics, y_rot, x_rot); + if let Ok((position, eye_height, mut look_direction)) = query.get_mut(event.entity) { + let (y_rot, x_rot) = + direction_looking_at(&position.up(eye_height.into()), &event.position); + (look_direction.y_rot, look_direction.x_rot) = (y_rot, x_rot); } } } @@ -129,5 +134,6 @@ impl PluginGroup for DefaultBotPlugins { PluginGroupBuilder::start::() .add(BotPlugin) .add(PathfinderPlugin) + .add(ContainerPlugin) } } diff --git a/azalea/src/container.rs b/azalea/src/container.rs new file mode 100644 index 00000000..0016caad --- /dev/null +++ b/azalea/src/container.rs @@ -0,0 +1,140 @@ +use std::fmt::Formatter; + +use azalea_client::{ + inventory::{CloseContainerEvent, ContainerClickEvent, InventoryComponent}, + packet_handling::PacketEvent, + Client, TickBroadcast, +}; +use azalea_core::BlockPos; +use azalea_inventory::{operations::ClickOperation, ItemSlot, Menu}; +use azalea_protocol::packets::game::ClientboundGamePacket; +use bevy_app::{App, Plugin}; +use bevy_ecs::{component::Component, prelude::EventReader, system::Commands}; +use std::fmt::Debug; + +pub struct ContainerPlugin; +impl Plugin for ContainerPlugin { + fn build(&self, app: &mut App) { + app.add_system(handle_menu_opened_event); + } +} + +pub trait ContainerClientExt { + async fn open_container(&mut self, pos: BlockPos) -> Option; +} + +impl ContainerClientExt for Client { + /// Open a container in the world, like a chest. + /// + /// ``` + /// # use azalea::prelude::*; + /// # async fn example(mut bot: azalea::Client) { + /// let target_pos = bot + /// .world() + /// .read() + /// .find_block(bot.position(), &azalea::Block::Chest.into()); + /// let Some(target_pos) = target_pos else { + /// bot.chat("no chest found"); + /// return; + /// }; + /// let container = bot.open_container(target_pos).await; + /// # } + /// ``` + async fn open_container(&mut self, pos: BlockPos) -> Option { + self.ecs + .lock() + .entity_mut(self.entity) + .insert(WaitingForInventoryOpen); + self.block_interact(pos); + + let mut receiver = { + let ecs = self.ecs.lock(); + let tick_broadcast = ecs.resource::(); + tick_broadcast.subscribe() + }; + while receiver.recv().await.is_ok() { + let ecs = self.ecs.lock(); + if ecs.get::(self.entity).is_none() { + break; + } + } + + let ecs = self.ecs.lock(); + let inventory = ecs + .get::(self.entity) + .expect("no inventory"); + if inventory.id == 0 { + None + } else { + Some(ContainerHandle { + id: inventory.id, + client: self.clone(), + }) + } + } +} + +/// A handle to the open container. The container will be closed once this is +/// dropped. +pub struct ContainerHandle { + pub id: u8, + client: Client, +} +impl Drop for ContainerHandle { + fn drop(&mut self) { + self.client.ecs.lock().send_event(CloseContainerEvent { + entity: self.client.entity, + id: self.id, + }); + } +} +impl Debug for ContainerHandle { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ContainerHandle") + .field("id", &self.id) + .finish() + } +} +impl ContainerHandle { + /// Returns the menu of the container. If the container is closed, this + /// will return `None`. + pub fn menu(&self) -> Option { + let ecs = self.client.ecs.lock(); + let inventory = ecs + .get::(self.client.entity) + .expect("no inventory"); + if inventory.id == self.id { + Some(inventory.container_menu.clone().unwrap()) + } else { + None + } + } + + /// Returns the item slots in the container, not including the player's + /// inventory. If the container is closed, this will return `None`. + pub fn contents(&self) -> Option> { + self.menu().map(|menu| menu.contents()) + } + + pub fn click(&self, operation: impl Into) { + let operation = operation.into(); + self.client.ecs.lock().send_event(ContainerClickEvent { + entity: self.client.entity, + window_id: self.id, + operation, + }); + } +} + +#[derive(Component, Debug)] +pub struct WaitingForInventoryOpen; + +fn handle_menu_opened_event(mut commands: Commands, mut events: EventReader) { + for event in events.iter() { + if let ClientboundGamePacket::ContainerSetContent { .. } = event.packet { + commands + .entity(event.entity) + .remove::(); + } + } +} diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs index bd1d356a..2e8e4fa1 100644 --- a/azalea/src/lib.rs +++ b/azalea/src/lib.rs @@ -1,7 +1,10 @@ #![doc = include_str!("../README.md")] #![feature(async_closure)] +#![allow(incomplete_features)] +#![feature(async_fn_in_trait)] mod bot; +mod container; pub mod pathfinder; pub mod prelude; pub mod swarm; @@ -12,7 +15,7 @@ pub use azalea_block as blocks; pub use azalea_client::*; pub use azalea_core::{BlockPos, Vec3}; pub use azalea_protocol as protocol; -pub use azalea_registry::EntityKind; +pub use azalea_registry::{Block, EntityKind, Item}; pub use azalea_world::{entity, Instance}; use bot::DefaultBotPlugins; use ecs::component::Component; diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs index 9c06ebb8..56c8e0ce 100644 --- a/azalea/src/pathfinder/mod.rs +++ b/azalea/src/pathfinder/mod.rs @@ -93,7 +93,7 @@ fn goto_listener( mut commands: Commands, mut events: EventReader, mut query: Query<(&Position, &WorldName)>, - world_container: Res, + instance_container: Res, ) { let thread_pool = AsyncComputeTaskPool::get(); @@ -106,7 +106,7 @@ fn goto_listener( vertical_vel: VerticalVel::None, }; - let world_lock = world_container + let world_lock = instance_container .get(world_name) .expect("Entity tried to pathfind but the entity isn't in a valid world"); let end = event.goal.goal_node(); diff --git a/azalea/src/prelude.rs b/azalea/src/prelude.rs index b1a1fed3..87cb0b53 100644 --- a/azalea/src/prelude.rs +++ b/azalea/src/prelude.rs @@ -1,7 +1,10 @@ //! The Azalea prelude. Things that are necessary for a bare-bones bot are //! re-exported here. -pub use crate::{bot::BotClientExt, pathfinder::PathfinderClientExt, ClientBuilder}; +pub use crate::{ + bot::BotClientExt, container::ContainerClientExt, pathfinder::PathfinderClientExt, + ClientBuilder, +}; pub use azalea_client::{Account, Client, Event}; // this is necessary to make the macros that reference bevy_ecs work pub use crate::ecs as bevy_ecs; diff --git a/azalea/src/swarm/mod.rs b/azalea/src/swarm/mod.rs index 6fe11b7d..2253f5bd 100644 --- a/azalea/src/swarm/mod.rs +++ b/azalea/src/swarm/mod.rs @@ -37,7 +37,7 @@ pub struct Swarm { // bot_datas: Arc>>, resolved_address: SocketAddr, address: ServerAddress, - pub world_container: Arc>, + pub instance_container: Arc>, bots_tx: mpsc::UnboundedSender<(Option, Client)>, swarm_tx: mpsc::UnboundedSender, @@ -248,7 +248,7 @@ where // resolve the address let resolved_address = resolver::resolve_address(&address).await?; - let world_container = Arc::new(RwLock::new(InstanceContainer::default())); + let instance_container = Arc::new(RwLock::new(InstanceContainer::default())); // we can't modify the swarm plugins after this let (bots_tx, mut bots_rx) = mpsc::unbounded_channel(); @@ -263,7 +263,7 @@ where resolved_address, address, - world_container, + instance_container, bots_tx, -- cgit v1.2.3