1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 23:44:38 +00:00

more inventory stuff

This commit is contained in:
Ubuntu 2023-02-07 01:59:01 +00:00
commit e43cbc09c2
6 changed files with 71 additions and 82 deletions

View file

@ -1,7 +1,7 @@
pub use crate::chat::ChatPacket;
use crate::{
events::{Event, EventPlugin, LocalPlayerEvents},
inventory::{ActiveContainer, InventoryMenu},
inventory::InventoryComponent,
local_player::{
death_event, update_in_loaded_chunk, GameProfileComponent, LocalPlayer, PhysicsState,
},
@ -214,8 +214,7 @@ impl Client {
PhysicsState::default(),
Local,
LocalPlayerEvents(tx),
InventoryMenu::default(),
ActiveContainer::default(),
InventoryComponent::default(),
));
Ok((client, rx))

View file

@ -1,70 +1,48 @@
use std::fmt;
use azalea_core::{Slot, SlotData};
use azalea_core::Slot;
use azalea_ecs::component::Component;
use azalea_inventory::Menu;
use derive_more::{Deref, DerefMut};
/// A component that contains the player's inventory menu. This is guaranteed to
/// be a `Menu::Player`.
///
/// We keep it as a [`Menu`] since `Menu` has some useful functions that bare
/// [`azalea_inventory::Player`] doesn't have.
#[derive(Component, Deref, DerefMut)]
pub struct InventoryMenu(azalea_inventory::Menu);
impl Default for InventoryMenu {
fn default() -> Self {
InventoryMenu(Menu::Player(azalea_inventory::Player::default()))
}
}
impl InventoryMenu {
pub fn as_player(self) -> &azalea_inventory::Player {
if let Menu::Player(player) = &self.0 {
player
} else {
unreachable!("InventoryMenu must always be a Menu::Player")
}
}
}
impl fmt::Debug for InventoryMenu {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("InventoryMenu")
.field(&self.as_player())
.finish()
}
}
impl Clone for InventoryMenu {
fn clone(&self) -> Self {
InventoryMenu(Menu::Player(self.as_player().clone()))
}
}
/// A component that contains information about the container that's currently
/// open.
/// It will be present on all players that can have a container open.
/// A component present on all local players that have an inventory.
#[derive(Component)]
pub struct ActiveContainer {
pub struct InventoryComponent {
/// A component that contains the player's inventory menu. This is
/// guaranteed to be a `Menu::Player`.
///
/// We keep it as a [`Menu`] since `Menu` has some useful functions that
/// bare [`azalea_inventory::Player`] doesn't have.
pub inventory_menu: azalea_inventory::Menu,
/// The ID of the container that's currently open. Its value is not
/// guaranteed to be anything specific, and may change every time you open a
/// container (unless it's 0, in which case it means that no container is
/// open).
pub id: u8,
pub menu: azalea_inventory::Menu,
pub id: i8,
/// The current container menu that the player has open. If no container is
/// open, this will be `None`.
pub container_menu: Option<azalea_inventory::Menu>,
/// The item that is currently held by the cursor. `Slot::Empty` if nothing
/// is currently being held.
pub carried: Slot,
/// An identifier used by the server to track client inventory desyncs.
pub state_id: u32,
// minecraft also has these fields, but i don't need they're necessary?:
// private final NonNullList<ItemStack> remoteSlots;
// private final IntList remoteDataSlots;
// private ItemStack remoteCarried;
}
impl Default for ActiveContainer {
impl InventoryComponent {
/// Returns the currently active menu. If a container is open it'll return
/// [`Self::container_menu`], otherwise [`Self::inventory_menu`].
pub fn menu(&self) -> &azalea_inventory::Menu {
&self.container_menu.unwrap_or(self.inventory_menu)
}
}
impl Default for InventoryComponent {
fn default() -> Self {
ActiveContainer {
InventoryComponent {
inventory_menu: Menu::Player(azalea_inventory::Player::default()),
id: 0,
menu: Menu::Player(azalea_inventory::Player::default()),
container_menu: None,
carried: Slot::Empty,
state_id: 0,
}

View file

@ -1,6 +1,6 @@
use std::{collections::HashSet, io::Cursor, sync::Arc};
use azalea_core::{ChunkPos, ResourceLocation, Slot, Vec3};
use azalea_core::{ChunkPos, ResourceLocation, Vec3};
use azalea_ecs::{
app::{App, Plugin},
component::Component,
@ -35,7 +35,7 @@ use parking_lot::Mutex;
use tokio::sync::mpsc;
use crate::{
inventory::{ActiveContainer, InventoryMenu},
inventory::InventoryComponent,
local_player::{GameProfileComponent, LocalPlayer},
ChatPacket, ClientInformation, PlayerInfo,
};
@ -620,34 +620,26 @@ fn handle_packets(ecs: &mut Ecs) {
ClientboundGamePacket::ContainerSetContent(p) => {
debug!("Got container set content packet {:?}", p);
let mut system_state: SystemState<
Query<(&mut InventoryMenu, Option<&mut ActiveContainer>)>,
> = SystemState::new(ecs);
let mut system_state: SystemState<Query<&mut InventoryComponent>> =
SystemState::new(ecs);
let mut query = system_state.get_mut(ecs);
let (mut inventory_menu, open_container) =
query.get_mut(player_entity).unwrap();
let mut inventory = query.get_mut(player_entity).unwrap();
// container id 0 is always the player's inventory
if p.container_id == 0 {
// this is just so it has the same type as the `else` block
for (i, slot) in p.items.iter().enumerate() {
if let Some(slot_mut) = inventory_menu.slot_mut(i) {
if let Some(slot_mut) = inventory.inventory_menu.slot_mut(i) {
*slot_mut = slot.clone();
}
}
} else if let Some(mut open_container) = open_container {
if open_container.id == p.container_id {
for (i, slot) in p.items.iter().enumerate() {
if let Some(slot_mut) = open_container.menu.slot_mut(i) {
*slot_mut = slot.clone();
}
} else if inventory.id == p.container_id {
let mut menu = inventory.menu();
for (i, slot) in p.items.iter().enumerate() {
if let Some(slot_mut) = menu.slot_mut(i) {
*slot_mut = slot.clone();
}
}
} else {
warn!(
"ContainerSetPacket was received with id {} but no container was open",
p.container_id
);
}
}
ClientboundGamePacket::ContainerSetData(p) => {
@ -656,29 +648,34 @@ fn handle_packets(ecs: &mut Ecs) {
ClientboundGamePacket::ContainerSetSlot(p) => {
debug!("Got container set slot packet {:?}", p);
let mut system_state: SystemState<
Query<(&mut InventoryMenu, &mut ActiveContainer)>,
> = SystemState::new(ecs);
let mut system_state: SystemState<Query<&mut InventoryComponent>> =
SystemState::new(ecs);
let mut query = system_state.get_mut(ecs);
let (mut inventory_menu, mut active_container) =
query.get_mut(player_entity).unwrap();
let mut inventory = query.get_mut(player_entity).unwrap();
if p.container_id == -1 {
// -1 means carried item
active_container.carried = p.item_stack.clone();
inventory.carried = p.item_stack.clone();
} else if p.container_id == -2 {
if let Some(mut slot) = inventory_menu.slot_mut(p.slot.into()) {
if let Some(mut slot) = inventory.inventory_menu.slot_mut(p.slot.into()) {
*slot = p.item_stack.clone();
}
} else {
let is_creative_mode_and_inventory_closed = false;
// technically minecraft has slightly different behavior here if you're in
// creative mode and have your inventory open
if p.container_id == 0
&& inventory_menu.as_player().is_hotbar_slot(p.slot.into())
&& azalea_inventory::Player::is_hotbar_slot(p.slot.into())
{
if let Slot::Present(item) = p.item_stack {
// TODO container stuff
// minecraft also sets a "pop time" here which is used for an animation
// but that's not really necessary
if let Some(slot) = inventory.inventory_menu.slot_mut(p.slot.into()) {
*slot = p.item_stack.clone();
}
} else if p.container_id == inventory.id
&& (p.container_id != 0 || !is_creative_mode_and_inventory_closed)
{
var2.containerMenu.setItem(var4, var1.getStateId(), var3);
}
}
}

View file

@ -34,7 +34,7 @@ pub fn generate(input: &DeclareMenus) -> TokenStream {
quote! {
impl Player {
/// Returns whether the given protocol index is in the player's hotbar.
pub fn is_hotbar_slot(&self, i: usize) -> bool {
pub fn is_hotbar_slot(i: usize) -> bool {
i >= #hotbar_slot_start && i <= #hotbar_slot_end
}
}

View file

@ -140,3 +140,18 @@ declare_menus! {
result: 1,
},
}
impl Menu {
/// Get the [`Player`] from this [`Menu`].
///
/// # Panics
///
/// Will panic if the menu isn't `Menu::Player`.
pub fn as_player(&self) -> &Player {
if let Menu::Player(player) = &self {
player
} else {
unreachable!("Called `Menu::as_player` on a menu that wasn't `Player`.")
}
}
}

View file

@ -4,7 +4,7 @@ use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundContainerSetContentPacket {
pub container_id: u8,
pub container_id: i8,
#[var]
pub state_id: u32,
pub items: Vec<Slot>,