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:
parent
6286e953a6
commit
9254f388c1
3 changed files with 128 additions and 27 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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`].
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue