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

container handle

This commit is contained in:
mat 2023-03-13 21:53:53 -05:00
parent 6286e953a6
commit 9254f388c1
3 changed files with 128 additions and 27 deletions

View file

@ -43,7 +43,7 @@ use azalea_world::{
entity::{EntityPlugin, EntityUpdateSet, Local, Position, WorldName},
Instance, InstanceContainer, PartialInstance,
};
use bevy_app::{App, CoreSchedule, Plugin, PluginGroup, PluginGroupBuilder};
use bevy_app::{App, CoreSchedule, IntoSystemAppConfig, Plugin, PluginGroup, PluginGroupBuilder};
use bevy_ecs::{
bundle::Bundle,
component::Component,
@ -632,32 +632,32 @@ pub async fn tick_run_schedule_loop(run_schedule_sender: mpsc::UnboundedSender<(
}
/// A resource that contains a [`broadcast::Sender`] that will be sent every
/// time the ECS schedule is run.
/// Minecraft tick.
///
/// This is useful for running code every schedule from async user code.
///
/// ```no_run
/// let mut receiver = {
/// let ecs = client.ecs.lock();
/// let schedule_broadcast = ecs.resource::<RanScheduleBroadcast>();
/// schedule_broadcast.subscribe()
/// let tick_broadcast = ecs.resource::<TickBroadcast>();
/// tick_broadcast.subscribe()
/// };
/// while receiver.recv().await.is_ok() {
/// // do something
/// }
/// ```
#[derive(Resource, Deref)]
pub struct RanScheduleBroadcast(broadcast::Sender<()>);
pub struct TickBroadcast(broadcast::Sender<()>);
fn send_ran_schedule_event(ran_schedule_broadcast: ResMut<RanScheduleBroadcast>) {
let _ = ran_schedule_broadcast.0.send(());
fn send_tick_broadcast(tick_broadcast: ResMut<TickBroadcast>) {
let _ = tick_broadcast.0.send(());
}
/// A plugin that makes the [`RanScheduleBroadcast`] resource available.
pub struct RanSchedulePlugin;
impl Plugin for RanSchedulePlugin {
pub struct TickBroadcastPlugin;
impl Plugin for TickBroadcastPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(RanScheduleBroadcast(broadcast::channel(1).0))
.add_system(send_ran_schedule_event);
app.insert_resource(TickBroadcast(broadcast::channel(1).0))
.add_system(send_tick_broadcast.in_schedule(CoreSchedule::FixedUpdate));
}
}
@ -681,6 +681,6 @@ impl PluginGroup for DefaultPlugins {
.add(DisconnectPlugin)
.add(PlayerMovePlugin)
.add(InteractPlugin)
.add(RanSchedulePlugin)
.add(TickBroadcastPlugin)
}
}

View file

@ -1,29 +1,39 @@
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;
use bevy_app::{App, Plugin};
use bevy_ecs::{
component::Component,
entity::Entity,
event::EventReader,
schedule::IntoSystemConfigs,
prelude::EventWriter,
schedule::{IntoSystemConfig, IntoSystemConfigs},
system::{Commands, Query},
};
use crate::{client::RanScheduleBroadcast, Client};
use crate::{client::TickBroadcast, local_player::handle_send_packet_event, Client, LocalPlayer};
pub struct InventoryPlugin;
impl Plugin for InventoryPlugin {
fn build(&self, app: &mut App) {
app.add_event::<ClientSideCloseContainerEvent>()
.add_event::<MenuOpenedEvent>()
.add_event::<CloseContainerEvent>()
.add_systems(
(
handle_menu_opened_event,
handle_client_side_close_container_event,
)
.chain(),
)
.add_system(
handle_container_close_event
.before(handle_send_packet_event)
.before(handle_client_side_close_container_event),
);
}
}
@ -32,7 +42,7 @@ impl Plugin for InventoryPlugin {
pub struct WaitingForInventoryOpen;
impl Client {
pub async fn open_container(&mut self, pos: BlockPos) -> Option<Menu> {
pub async fn open_container(&mut self, pos: BlockPos) -> Option<ContainerHandle> {
self.ecs
.lock()
.entity_mut(self.entity)
@ -41,8 +51,8 @@ impl Client {
let mut receiver = {
let ecs = self.ecs.lock();
let schedule_broadcast = ecs.resource::<RanScheduleBroadcast>();
schedule_broadcast.subscribe()
let tick_broadcast = ecs.resource::<TickBroadcast>();
tick_broadcast.subscribe()
};
while receiver.recv().await.is_ok() {
let ecs = self.ecs.lock();
@ -52,13 +62,69 @@ impl Client {
}
let ecs = self.ecs.lock();
let inventory = ecs.get::<InventoryComponent>(self.entity);
if let Some(inventory) = inventory {
inventory.container_menu.clone()
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 {
let mut ecs = self.ecs.lock();
let inventory = self.query::<&InventoryComponent>(&mut ecs);
inventory.menu().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())
}
}
/// A component present on all local players that have an inventory.
@ -145,6 +211,39 @@ fn handle_menu_opened_event(
}
}
/// Tell the server that we want to close a container.
///
/// Note that this is also sent when the client closes its own inventory, even
/// though there is no packet for opening its inventory.
pub struct CloseContainerEvent {
pub entity: Entity,
/// The ID of the container to close. 0 for the player's inventory. If this
/// is not the same as the currently open inventory, nothing will happen.
pub id: i8,
}
fn handle_container_close_event(
mut events: EventReader<CloseContainerEvent>,
mut client_side_events: EventWriter<ClientSideCloseContainerEvent>,
query: Query<(&LocalPlayer, &InventoryComponent)>,
) {
for event in events.iter() {
let (local_player, inventory) = query.get(event.entity).unwrap();
if event.id != inventory.id {
continue;
}
local_player.write_packet(
ServerboundContainerClosePacket {
container_id: inventory.id as u8,
}
.get(),
);
client_side_events.send(ClientSideCloseContainerEvent {
entity: event.entity,
});
}
}
/// Close a container without notifying the server.
///
/// Note that this also gets fired when we get a [`CloseContainerEvent`].

View file

@ -7,7 +7,6 @@ use azalea::entity::metadata::Player;
use azalea::entity::{EyeHeight, Position};
use azalea::interact::HitResultComponent;
use azalea::inventory::ItemSlot;
use azalea::inventory_plugin::InventoryComponent;
use azalea::pathfinder::BlockPosGoal;
use azalea::{prelude::*, swarm::prelude::*, BlockPos, GameProfileComponent, WalkDirection};
use azalea::{Account, Client, Event};
@ -145,9 +144,7 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
std::thread::sleep(Duration::from_millis(1000));
}
"inventory" => {
let mut ecs = bot.ecs.lock();
let inventory = bot.query::<&InventoryComponent>(&mut ecs);
println!("inventory: {:?}", inventory.menu());
println!("inventory: {:?}", bot.menu());
}
"findblock" => {
let target_pos = bot
@ -196,12 +193,17 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
};
bot.look_at(target_pos.center());
let container = bot.open_container(target_pos).await;
println!("container: {:?}", container);
if let Some(container) = container {
for item in container.contents() {
if let Some(contents) = container.contents() {
for item in contents {
if let ItemSlot::Present(item) = item {
println!("item: {:?}", item);
}
}
} else {
println!("container was immediately closed");
}
} else {
println!("no container found");
}