1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00

move some container stuff from az-client to azalea

This commit is contained in:
mat 2023-03-14 19:22:15 -05:00
parent 12c1558c17
commit 0018442597
7 changed files with 141 additions and 105 deletions

1
Cargo.lock generated
View file

@ -477,6 +477,7 @@ dependencies = [
"azalea-block",
"azalea-buf",
"azalea-chat",
"azalea-client",
"azalea-core",
"azalea-inventory",
"azalea-nbt",

View file

@ -1,7 +1,5 @@
use std::fmt::{Debug, Formatter};
use azalea_chat::FormattedText;
use azalea_core::BlockPos;
use azalea_inventory::{ItemSlot, Menu};
use azalea_protocol::packets::game::serverbound_container_close_packet::ServerboundContainerClosePacket;
use azalea_registry::MenuKind;
@ -12,10 +10,10 @@ use bevy_ecs::{
event::EventReader,
prelude::EventWriter,
schedule::{IntoSystemConfig, IntoSystemConfigs},
system::{Commands, Query},
system::{Query},
};
use crate::{client::TickBroadcast, local_player::handle_send_packet_event, Client, LocalPlayer};
use crate::{local_player::handle_send_packet_event, Client, LocalPlayer};
pub struct InventoryPlugin;
impl Plugin for InventoryPlugin {
@ -34,58 +32,7 @@ impl Plugin for InventoryPlugin {
}
}
#[derive(Component, Debug)]
pub struct WaitingForInventoryOpen;
impl Client {
/// Open a container in the world, like a chest.
///
/// ```
/// # async fn example(mut bot: azalea_client::Client) {
/// let target_pos = bot
/// .world()
/// .read()
/// .find_block(bot.position(), &azalea_registry::Block::Chest.into());
/// let Some(target_pos) = target_pos else {
/// bot.chat("no chest found");
/// return;
/// };
/// let container = bot.open_container(target_pos).await;
/// # }
/// ```
pub async fn open_container(&mut self, pos: BlockPos) -> Option<ContainerHandle> {
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::<TickBroadcast>();
tick_broadcast.subscribe()
};
while receiver.recv().await.is_ok() {
let ecs = self.ecs.lock();
if ecs.get::<WaitingForInventoryOpen>(self.entity).is_none() {
break;
}
}
let ecs = self.ecs.lock();
let inventory = ecs
.get::<InventoryComponent>(self.entity)
.expect("no inventory");
if inventory.id == 0 {
None
} else {
Some(ContainerHandle {
id: inventory.id,
client: self.clone(),
})
}
}
/// Return the menu that is currently open. If no menu is open, this will
/// have the player's inventory.
pub fn menu(&self) -> Menu {
@ -95,49 +42,6 @@ impl Client {
}
}
/// A handle to the open container. The container will be closed once this is
/// dropped.
pub struct ContainerHandle {
pub id: i8,
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<Menu> {
let ecs = self.client.ecs.lock();
let inventory = ecs
.get::<InventoryComponent>(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. If the container is closed,
/// this will return `None`.
pub fn contents(&self) -> Option<Vec<ItemSlot>> {
self.menu().map(|menu| menu.contents())
}
}
/// A component present on all local players that have an inventory.
#[derive(Component)]
pub struct InventoryComponent {
@ -207,15 +111,10 @@ pub struct MenuOpenedEvent {
pub title: FormattedText,
}
fn handle_menu_opened_event(
mut commands: Commands,
mut events: EventReader<MenuOpenedEvent>,
mut query: Query<&mut InventoryComponent>,
) {
for event in events.iter() {
commands
.entity(event.entity)
.remove::<WaitingForInventoryOpen>();
let mut inventory = query.get_mut(event.entity).unwrap();
inventory.id = event.window_id as i8;
inventory.container_menu = Some(Menu::from_kind(event.menu_type));

View file

@ -31,3 +31,6 @@ uuid = "1.1.2"
[profile.release]
lto = true
[dev-dependencies]
azalea-client = { path = "../azalea-client" }

View file

@ -198,7 +198,7 @@ impl Instance {
///
/// ```
/// # fn example(client: &azalea_client::Client) {
/// client.find_block(client.position(), &azalea_registry::Block::Chest.into());
/// client.world().read().find_block(client.position(), &azalea_registry::Block::Chest.into());
/// # }
/// ```
pub fn find_block(

127
azalea/src/container.rs Normal file
View file

@ -0,0 +1,127 @@
use std::fmt::Formatter;
use azalea_client::{
inventory_plugin::{CloseContainerEvent, InventoryComponent, MenuOpenedEvent},
Client, TickBroadcast,
};
use azalea_core::BlockPos;
use azalea_inventory::{ItemSlot, Menu};
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<ContainerHandle>;
}
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<ContainerHandle> {
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::<TickBroadcast>();
tick_broadcast.subscribe()
};
while receiver.recv().await.is_ok() {
let ecs = self.ecs.lock();
if ecs.get::<WaitingForInventoryOpen>(self.entity).is_none() {
break;
}
}
let ecs = self.ecs.lock();
let inventory = ecs
.get::<InventoryComponent>(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: i8,
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<Menu> {
let ecs = self.client.ecs.lock();
let inventory = ecs
.get::<InventoryComponent>(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. If the container is closed,
/// this will return `None`.
pub fn contents(&self) -> Option<Vec<ItemSlot>> {
self.menu().map(|menu| menu.contents())
}
}
#[derive(Component, Debug)]
pub struct WaitingForInventoryOpen;
fn handle_menu_opened_event(mut commands: Commands, mut events: EventReader<MenuOpenedEvent>) {
for event in events.iter() {
commands
.entity(event.entity)
.remove::<WaitingForInventoryOpen>();
}
}

View file

@ -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;

View file

@ -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;