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},
|
entity::{EntityPlugin, EntityUpdateSet, Local, Position, WorldName},
|
||||||
Instance, InstanceContainer, PartialInstance,
|
Instance, InstanceContainer, PartialInstance,
|
||||||
};
|
};
|
||||||
use bevy_app::{App, CoreSchedule, Plugin, PluginGroup, PluginGroupBuilder};
|
use bevy_app::{App, CoreSchedule, IntoSystemAppConfig, Plugin, PluginGroup, PluginGroupBuilder};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
bundle::Bundle,
|
bundle::Bundle,
|
||||||
component::Component,
|
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
|
/// 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.
|
/// This is useful for running code every schedule from async user code.
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// let mut receiver = {
|
/// let mut receiver = {
|
||||||
/// let ecs = client.ecs.lock();
|
/// let ecs = client.ecs.lock();
|
||||||
/// let schedule_broadcast = ecs.resource::<RanScheduleBroadcast>();
|
/// let tick_broadcast = ecs.resource::<TickBroadcast>();
|
||||||
/// schedule_broadcast.subscribe()
|
/// tick_broadcast.subscribe()
|
||||||
/// };
|
/// };
|
||||||
/// while receiver.recv().await.is_ok() {
|
/// while receiver.recv().await.is_ok() {
|
||||||
/// // do something
|
/// // do something
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Resource, Deref)]
|
#[derive(Resource, Deref)]
|
||||||
pub struct RanScheduleBroadcast(broadcast::Sender<()>);
|
pub struct TickBroadcast(broadcast::Sender<()>);
|
||||||
|
|
||||||
fn send_ran_schedule_event(ran_schedule_broadcast: ResMut<RanScheduleBroadcast>) {
|
fn send_tick_broadcast(tick_broadcast: ResMut<TickBroadcast>) {
|
||||||
let _ = ran_schedule_broadcast.0.send(());
|
let _ = tick_broadcast.0.send(());
|
||||||
}
|
}
|
||||||
/// A plugin that makes the [`RanScheduleBroadcast`] resource available.
|
/// A plugin that makes the [`RanScheduleBroadcast`] resource available.
|
||||||
pub struct RanSchedulePlugin;
|
pub struct TickBroadcastPlugin;
|
||||||
impl Plugin for RanSchedulePlugin {
|
impl Plugin for TickBroadcastPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.insert_resource(RanScheduleBroadcast(broadcast::channel(1).0))
|
app.insert_resource(TickBroadcast(broadcast::channel(1).0))
|
||||||
.add_system(send_ran_schedule_event);
|
.add_system(send_tick_broadcast.in_schedule(CoreSchedule::FixedUpdate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -681,6 +681,6 @@ impl PluginGroup for DefaultPlugins {
|
||||||
.add(DisconnectPlugin)
|
.add(DisconnectPlugin)
|
||||||
.add(PlayerMovePlugin)
|
.add(PlayerMovePlugin)
|
||||||
.add(InteractPlugin)
|
.add(InteractPlugin)
|
||||||
.add(RanSchedulePlugin)
|
.add(TickBroadcastPlugin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,39 @@
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
use azalea_chat::FormattedText;
|
use azalea_chat::FormattedText;
|
||||||
use azalea_core::BlockPos;
|
use azalea_core::BlockPos;
|
||||||
use azalea_inventory::{ItemSlot, Menu};
|
use azalea_inventory::{ItemSlot, Menu};
|
||||||
|
use azalea_protocol::packets::game::serverbound_container_close_packet::ServerboundContainerClosePacket;
|
||||||
use azalea_registry::MenuKind;
|
use azalea_registry::MenuKind;
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
event::EventReader,
|
event::EventReader,
|
||||||
schedule::IntoSystemConfigs,
|
prelude::EventWriter,
|
||||||
|
schedule::{IntoSystemConfig, IntoSystemConfigs},
|
||||||
system::{Commands, Query},
|
system::{Commands, Query},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{client::RanScheduleBroadcast, Client};
|
use crate::{client::TickBroadcast, local_player::handle_send_packet_event, Client, LocalPlayer};
|
||||||
|
|
||||||
pub struct InventoryPlugin;
|
pub struct InventoryPlugin;
|
||||||
impl Plugin for InventoryPlugin {
|
impl Plugin for InventoryPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_event::<ClientSideCloseContainerEvent>()
|
app.add_event::<ClientSideCloseContainerEvent>()
|
||||||
.add_event::<MenuOpenedEvent>()
|
.add_event::<MenuOpenedEvent>()
|
||||||
|
.add_event::<CloseContainerEvent>()
|
||||||
.add_systems(
|
.add_systems(
|
||||||
(
|
(
|
||||||
handle_menu_opened_event,
|
handle_menu_opened_event,
|
||||||
handle_client_side_close_container_event,
|
handle_client_side_close_container_event,
|
||||||
)
|
)
|
||||||
.chain(),
|
.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;
|
pub struct WaitingForInventoryOpen;
|
||||||
|
|
||||||
impl Client {
|
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
|
self.ecs
|
||||||
.lock()
|
.lock()
|
||||||
.entity_mut(self.entity)
|
.entity_mut(self.entity)
|
||||||
|
@ -41,8 +51,8 @@ impl Client {
|
||||||
|
|
||||||
let mut receiver = {
|
let mut receiver = {
|
||||||
let ecs = self.ecs.lock();
|
let ecs = self.ecs.lock();
|
||||||
let schedule_broadcast = ecs.resource::<RanScheduleBroadcast>();
|
let tick_broadcast = ecs.resource::<TickBroadcast>();
|
||||||
schedule_broadcast.subscribe()
|
tick_broadcast.subscribe()
|
||||||
};
|
};
|
||||||
while receiver.recv().await.is_ok() {
|
while receiver.recv().await.is_ok() {
|
||||||
let ecs = self.ecs.lock();
|
let ecs = self.ecs.lock();
|
||||||
|
@ -52,13 +62,69 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
let ecs = self.ecs.lock();
|
let ecs = self.ecs.lock();
|
||||||
let inventory = ecs.get::<InventoryComponent>(self.entity);
|
let inventory = ecs
|
||||||
if let Some(inventory) = inventory {
|
.get::<InventoryComponent>(self.entity)
|
||||||
inventory.container_menu.clone()
|
.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 {
|
} else {
|
||||||
None
|
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.
|
/// 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.
|
/// Close a container without notifying the server.
|
||||||
///
|
///
|
||||||
/// Note that this also gets fired when we get a [`CloseContainerEvent`].
|
/// 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::entity::{EyeHeight, Position};
|
||||||
use azalea::interact::HitResultComponent;
|
use azalea::interact::HitResultComponent;
|
||||||
use azalea::inventory::ItemSlot;
|
use azalea::inventory::ItemSlot;
|
||||||
use azalea::inventory_plugin::InventoryComponent;
|
|
||||||
use azalea::pathfinder::BlockPosGoal;
|
use azalea::pathfinder::BlockPosGoal;
|
||||||
use azalea::{prelude::*, swarm::prelude::*, BlockPos, GameProfileComponent, WalkDirection};
|
use azalea::{prelude::*, swarm::prelude::*, BlockPos, GameProfileComponent, WalkDirection};
|
||||||
use azalea::{Account, Client, Event};
|
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));
|
std::thread::sleep(Duration::from_millis(1000));
|
||||||
}
|
}
|
||||||
"inventory" => {
|
"inventory" => {
|
||||||
let mut ecs = bot.ecs.lock();
|
println!("inventory: {:?}", bot.menu());
|
||||||
let inventory = bot.query::<&InventoryComponent>(&mut ecs);
|
|
||||||
println!("inventory: {:?}", inventory.menu());
|
|
||||||
}
|
}
|
||||||
"findblock" => {
|
"findblock" => {
|
||||||
let target_pos = bot
|
let target_pos = bot
|
||||||
|
@ -196,11 +193,16 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
|
||||||
};
|
};
|
||||||
bot.look_at(target_pos.center());
|
bot.look_at(target_pos.center());
|
||||||
let container = bot.open_container(target_pos).await;
|
let container = bot.open_container(target_pos).await;
|
||||||
|
println!("container: {:?}", container);
|
||||||
if let Some(container) = container {
|
if let Some(container) = container {
|
||||||
for item in container.contents() {
|
if let Some(contents) = container.contents() {
|
||||||
if let ItemSlot::Present(item) = item {
|
for item in contents {
|
||||||
println!("item: {:?}", item);
|
if let ItemSlot::Present(item) = item {
|
||||||
|
println!("item: {:?}", item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
println!("container was immediately closed");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("no container found");
|
println!("no container found");
|
||||||
|
|
Loading…
Add table
Reference in a new issue