mirror of
https://github.com/Minestom/Minestom.git
synced 2025-02-11 01:41:47 +01:00
Integrate PlayerInventory (#2332)
* Integrate player inventory and normal inventory * Fix errors and broken tests (drop before adding, wrong order, etc) * Refactor slot refreshing * Fix viewer logic error * Fix incorrect merge * Move inventory update * Minor simplifications * More minor simplifications
This commit is contained in:
parent
41738bb62a
commit
e0939f089b
@ -48,6 +48,7 @@ import net.minestom.server.instance.EntityTracker;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.SharedInstance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.PlayerInventory;
|
||||
import net.minestom.server.item.ItemComponent;
|
||||
@ -175,7 +176,7 @@ public class Player extends LivingEntity implements CommandSender, HoverEventSou
|
||||
private int portalCooldown = 0;
|
||||
|
||||
protected PlayerInventory inventory;
|
||||
private Inventory openInventory;
|
||||
private AbstractInventory openInventory;
|
||||
// Used internally to allow the closing of inventory within the inventory listener
|
||||
private boolean didCloseInventory;
|
||||
|
||||
@ -236,7 +237,7 @@ public class Player extends LivingEntity implements CommandSender, HoverEventSou
|
||||
|
||||
setRespawnPoint(Pos.ZERO);
|
||||
|
||||
this.inventory = new PlayerInventory(this);
|
||||
this.inventory = new PlayerInventory();
|
||||
|
||||
setCanPickupItem(true); // By default
|
||||
|
||||
@ -295,6 +296,9 @@ public class Player extends LivingEntity implements CommandSender, HoverEventSou
|
||||
true);
|
||||
sendPacket(joinGamePacket);
|
||||
|
||||
// Start sending inventory updates
|
||||
inventory.addViewer(this);
|
||||
|
||||
// Difficulty
|
||||
sendPacket(new ServerDifficultyPacket(MinecraftServer.getDifficulty(), true));
|
||||
|
||||
@ -573,8 +577,9 @@ public class Player extends LivingEntity implements CommandSender, HoverEventSou
|
||||
|
||||
super.remove(permanent);
|
||||
|
||||
final Inventory currentInventory = getOpenInventory();
|
||||
final AbstractInventory currentInventory = getOpenInventory();
|
||||
if (currentInventory != null) currentInventory.removeViewer(this);
|
||||
|
||||
MinecraftServer.getBossBarManager().removeAllBossBars(this);
|
||||
// Advancement tabs cache
|
||||
{
|
||||
@ -1724,7 +1729,7 @@ public class Player extends LivingEntity implements CommandSender, HoverEventSou
|
||||
*
|
||||
* @return the currently open inventory, null if there is not (player inventory is not detected)
|
||||
*/
|
||||
public @Nullable Inventory getOpenInventory() {
|
||||
public @Nullable AbstractInventory getOpenInventory() {
|
||||
return openInventory;
|
||||
}
|
||||
|
||||
@ -1738,19 +1743,13 @@ public class Player extends LivingEntity implements CommandSender, HoverEventSou
|
||||
InventoryOpenEvent inventoryOpenEvent = new InventoryOpenEvent(inventory, this);
|
||||
|
||||
EventDispatcher.callCancellable(inventoryOpenEvent, () -> {
|
||||
Inventory openInventory = getOpenInventory();
|
||||
AbstractInventory openInventory = getOpenInventory();
|
||||
if (openInventory != null) {
|
||||
openInventory.removeViewer(this);
|
||||
}
|
||||
|
||||
Inventory newInventory = inventoryOpenEvent.getInventory();
|
||||
if (newInventory == null) {
|
||||
// just close the inventory
|
||||
return;
|
||||
}
|
||||
AbstractInventory newInventory = inventoryOpenEvent.getInventory();
|
||||
|
||||
sendPacket(new OpenWindowPacket(newInventory.getWindowId(),
|
||||
newInventory.getInventoryType().getWindowType(), newInventory.getTitle()));
|
||||
newInventory.addViewer(this);
|
||||
this.openInventory = newInventory;
|
||||
});
|
||||
@ -1767,31 +1766,15 @@ public class Player extends LivingEntity implements CommandSender, HoverEventSou
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void closeInventory(boolean fromClient) {
|
||||
Inventory openInventory = getOpenInventory();
|
||||
AbstractInventory openInventory = getOpenInventory();
|
||||
if (openInventory == null) return;
|
||||
|
||||
// Drop cursor item when closing inventory
|
||||
ItemStack cursorItem = getInventory().getCursorItem();
|
||||
getInventory().setCursorItem(ItemStack.AIR);
|
||||
this.openInventory = null;
|
||||
openInventory.removeViewer(this);
|
||||
inventory.update();
|
||||
|
||||
if (!cursorItem.isAir()) {
|
||||
// Add item to inventory if he hasn't been able to drop it
|
||||
if (!dropItem(cursorItem)) {
|
||||
getInventory().addItemStack(cursorItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (openInventory == getOpenInventory()) {
|
||||
CloseWindowPacket closeWindowPacket;
|
||||
if (openInventory == null) {
|
||||
closeWindowPacket = new CloseWindowPacket((byte) 0);
|
||||
} else {
|
||||
closeWindowPacket = new CloseWindowPacket(openInventory.getWindowId());
|
||||
openInventory.removeViewer(this); // Clear cache
|
||||
this.openInventory = null;
|
||||
}
|
||||
if (!fromClient) sendPacket(closeWindowPacket);
|
||||
inventory.update();
|
||||
this.didCloseInventory = true;
|
||||
if (!fromClient) {
|
||||
didCloseInventory = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2276,12 +2259,12 @@ public class Player extends LivingEntity implements CommandSender, HoverEventSou
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getEquipment(@NotNull EquipmentSlot slot) {
|
||||
return inventory.getEquipment(slot);
|
||||
return inventory.getEquipment(slot, heldSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEquipment(@NotNull EquipmentSlot slot, @NotNull ItemStack itemStack) {
|
||||
inventory.setEquipment(slot, itemStack);
|
||||
inventory.setEquipment(slot, heldSlot, itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -5,7 +5,7 @@ import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.trait.*;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -34,7 +34,7 @@ public interface EventFilter<E extends Event, H> {
|
||||
EventFilter<PlayerEvent, Player> PLAYER = from(PlayerEvent.class, Player.class, PlayerEvent::getPlayer);
|
||||
EventFilter<ItemEvent, ItemStack> ITEM = from(ItemEvent.class, ItemStack.class, ItemEvent::getItemStack);
|
||||
EventFilter<InstanceEvent, Instance> INSTANCE = from(InstanceEvent.class, Instance.class, InstanceEvent::getInstance);
|
||||
EventFilter<InventoryEvent, Inventory> INVENTORY = from(InventoryEvent.class, Inventory.class, InventoryEvent::getInventory);
|
||||
EventFilter<InventoryEvent, AbstractInventory> INVENTORY = from(InventoryEvent.class, AbstractInventory.class, InventoryEvent::getInventory);
|
||||
EventFilter<BlockEvent, Block> BLOCK = from(BlockEvent.class, Block.class, BlockEvent::getBlock);
|
||||
|
||||
static <E extends Event, H> EventFilter<E, H> from(@NotNull Class<E> eventType,
|
||||
|
@ -3,11 +3,10 @@ package net.minestom.server.event.inventory;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.trait.InventoryEvent;
|
||||
import net.minestom.server.event.trait.PlayerInstanceEvent;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import net.minestom.server.inventory.click.ClickType;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Called after {@link InventoryPreClickEvent}, this event cannot be cancelled and items related to the click
|
||||
@ -15,14 +14,14 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public class InventoryClickEvent implements InventoryEvent, PlayerInstanceEvent {
|
||||
|
||||
private final Inventory inventory;
|
||||
private final AbstractInventory inventory;
|
||||
private final Player player;
|
||||
private final int slot;
|
||||
private final ClickType clickType;
|
||||
private final ItemStack clickedItem;
|
||||
private final ItemStack cursorItem;
|
||||
|
||||
public InventoryClickEvent(@Nullable Inventory inventory, @NotNull Player player,
|
||||
public InventoryClickEvent(@NotNull AbstractInventory inventory, @NotNull Player player,
|
||||
int slot, @NotNull ClickType clickType,
|
||||
@NotNull ItemStack clicked, @NotNull ItemStack cursor) {
|
||||
this.inventory = inventory;
|
||||
@ -83,7 +82,7 @@ public class InventoryClickEvent implements InventoryEvent, PlayerInstanceEvent
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Inventory getInventory() {
|
||||
public @NotNull AbstractInventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
}
|
||||
|
@ -3,20 +3,21 @@ package net.minestom.server.event.inventory;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.trait.InventoryEvent;
|
||||
import net.minestom.server.event.trait.PlayerInstanceEvent;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Called when an {@link Inventory} is closed by a player.
|
||||
* Called when an {@link AbstractInventory} is closed by a player.
|
||||
*/
|
||||
public class InventoryCloseEvent implements InventoryEvent, PlayerInstanceEvent {
|
||||
|
||||
private final Inventory inventory;
|
||||
private final AbstractInventory inventory;
|
||||
private final Player player;
|
||||
private Inventory newInventory;
|
||||
|
||||
public InventoryCloseEvent(@Nullable Inventory inventory, @NotNull Player player) {
|
||||
public InventoryCloseEvent(@NotNull AbstractInventory inventory, @NotNull Player player) {
|
||||
this.inventory = inventory;
|
||||
this.player = player;
|
||||
}
|
||||
@ -51,7 +52,7 @@ public class InventoryCloseEvent implements InventoryEvent, PlayerInstanceEvent
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Inventory getInventory() {
|
||||
public @NotNull AbstractInventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,8 @@ package net.minestom.server.event.inventory;
|
||||
import net.minestom.server.event.trait.InventoryEvent;
|
||||
import net.minestom.server.event.trait.RecursiveEvent;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Called when {@link AbstractInventory#safeItemInsert(int, ItemStack)} is being invoked.
|
||||
@ -17,12 +15,12 @@ import org.jetbrains.annotations.Nullable;
|
||||
@SuppressWarnings("JavadocReference")
|
||||
public class InventoryItemChangeEvent implements InventoryEvent, RecursiveEvent {
|
||||
|
||||
private final Inventory inventory;
|
||||
private final AbstractInventory inventory;
|
||||
private final int slot;
|
||||
private final ItemStack previousItem;
|
||||
private final ItemStack newItem;
|
||||
|
||||
public InventoryItemChangeEvent(@Nullable Inventory inventory, int slot,
|
||||
public InventoryItemChangeEvent(@NotNull AbstractInventory inventory, int slot,
|
||||
@NotNull ItemStack previousItem, @NotNull ItemStack newItem) {
|
||||
this.inventory = inventory;
|
||||
this.slot = slot;
|
||||
@ -58,7 +56,7 @@ public class InventoryItemChangeEvent implements InventoryEvent, RecursiveEvent
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Inventory getInventory() {
|
||||
public @NotNull AbstractInventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
}
|
||||
|
@ -4,23 +4,23 @@ import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.trait.CancellableEvent;
|
||||
import net.minestom.server.event.trait.InventoryEvent;
|
||||
import net.minestom.server.event.trait.PlayerInstanceEvent;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Called when a player open an {@link Inventory}.
|
||||
* Called when a player open an {@link AbstractInventory}.
|
||||
* <p>
|
||||
* Executed by {@link Player#openInventory(Inventory)}.
|
||||
*/
|
||||
public class InventoryOpenEvent implements InventoryEvent, PlayerInstanceEvent, CancellableEvent {
|
||||
|
||||
private Inventory inventory;
|
||||
private AbstractInventory inventory;
|
||||
private final Player player;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
public InventoryOpenEvent(@Nullable Inventory inventory, @NotNull Player player) {
|
||||
public InventoryOpenEvent(@NotNull AbstractInventory inventory, @NotNull Player player) {
|
||||
this.inventory = inventory;
|
||||
this.player = player;
|
||||
}
|
||||
@ -36,13 +36,12 @@ public class InventoryOpenEvent implements InventoryEvent, PlayerInstanceEvent,
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the inventory to open, this could have been change by the {@link #setInventory(Inventory)}.
|
||||
* Gets the inventory to open, this could have been change by the {@link #setInventory(AbstractInventory)}.
|
||||
*
|
||||
* @return the inventory to open, null to just close the current inventory if any
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public Inventory getInventory() {
|
||||
public @NotNull AbstractInventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@ -53,7 +52,7 @@ public class InventoryOpenEvent implements InventoryEvent, PlayerInstanceEvent,
|
||||
*
|
||||
* @param inventory the inventory to open
|
||||
*/
|
||||
public void setInventory(@Nullable Inventory inventory) {
|
||||
public void setInventory(@NotNull AbstractInventory inventory) {
|
||||
this.inventory = inventory;
|
||||
}
|
||||
|
||||
|
@ -4,18 +4,17 @@ import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.trait.CancellableEvent;
|
||||
import net.minestom.server.event.trait.InventoryEvent;
|
||||
import net.minestom.server.event.trait.PlayerInstanceEvent;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import net.minestom.server.inventory.click.ClickType;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Called before {@link InventoryClickEvent}, used to potentially cancel the click.
|
||||
*/
|
||||
public class InventoryPreClickEvent implements InventoryEvent, PlayerInstanceEvent, CancellableEvent {
|
||||
|
||||
private final Inventory inventory;
|
||||
private final AbstractInventory inventory;
|
||||
private final Player player;
|
||||
private final int slot;
|
||||
private final ClickType clickType;
|
||||
@ -24,7 +23,7 @@ public class InventoryPreClickEvent implements InventoryEvent, PlayerInstanceEve
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
public InventoryPreClickEvent(@Nullable Inventory inventory,
|
||||
public InventoryPreClickEvent(@NotNull AbstractInventory inventory,
|
||||
@NotNull Player player,
|
||||
int slot, @NotNull ClickType clickType,
|
||||
@NotNull ItemStack clicked, @NotNull ItemStack cursor) {
|
||||
@ -114,7 +113,7 @@ public class InventoryPreClickEvent implements InventoryEvent, PlayerInstanceEve
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Inventory getInventory() {
|
||||
public @NotNull AbstractInventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
package net.minestom.server.event.inventory;
|
||||
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.trait.PlayerInstanceEvent;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import net.minestom.server.inventory.PlayerInventory;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Called when {@link AbstractInventory#safeItemInsert(int, ItemStack)} is being invoked on a {@link PlayerInventory}.
|
||||
* This event cannot be cancelled and items related to the change are already moved.
|
||||
* <p>
|
||||
* When this event is being called, {@link InventoryItemChangeEvent} listeners will also be triggered, so you can
|
||||
* listen only for an ancestor event and check whether it is an instance of that class.
|
||||
*/
|
||||
@SuppressWarnings("JavadocReference")
|
||||
public class PlayerInventoryItemChangeEvent extends InventoryItemChangeEvent implements PlayerInstanceEvent {
|
||||
|
||||
private final Player player;
|
||||
|
||||
public PlayerInventoryItemChangeEvent(@NotNull Player player, int slot, @NotNull ItemStack previousItem, @NotNull ItemStack newItem) {
|
||||
super(null, slot, previousItem, newItem);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
}
|
@ -1,18 +1,16 @@
|
||||
package net.minestom.server.event.trait;
|
||||
|
||||
import net.minestom.server.event.Event;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents any event inside an {@link Inventory}.
|
||||
* Represents any event inside an {@link AbstractInventory}.
|
||||
*/
|
||||
public interface InventoryEvent extends Event {
|
||||
|
||||
/**
|
||||
* Gets the inventory.
|
||||
*
|
||||
* @return the inventory, null if this is a player's inventory
|
||||
* Gets the inventory that was clicked.
|
||||
*/
|
||||
@Nullable Inventory getInventory();
|
||||
@NotNull AbstractInventory getInventory();
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
package net.minestom.server.inventory;
|
||||
|
||||
import net.minestom.server.Viewable;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.EventDispatcher;
|
||||
import net.minestom.server.event.inventory.InventoryItemChangeEvent;
|
||||
import net.minestom.server.event.inventory.PlayerInventoryItemChangeEvent;
|
||||
import net.minestom.server.inventory.click.InventoryClickProcessor;
|
||||
import net.minestom.server.inventory.condition.InventoryCondition;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.network.packet.server.play.CloseWindowPacket;
|
||||
import net.minestom.server.network.packet.server.play.SetSlotPacket;
|
||||
import net.minestom.server.network.packet.server.play.WindowItemsPacket;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.Taggable;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
@ -14,16 +18,15 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* Represents an inventory where items can be modified/retrieved.
|
||||
*/
|
||||
public sealed abstract class AbstractInventory implements InventoryClickHandler, Taggable
|
||||
public sealed abstract class AbstractInventory implements InventoryClickHandler, Taggable, Viewable
|
||||
permits Inventory, PlayerInventory {
|
||||
|
||||
private static final VarHandle ITEM_UPDATER = MethodHandles.arrayElementVarHandle(ItemStack[].class);
|
||||
@ -38,58 +41,97 @@ public sealed abstract class AbstractInventory implements InventoryClickHandler,
|
||||
|
||||
private final TagHandler tagHandler = TagHandler.newHandler();
|
||||
|
||||
// the players currently viewing this inventory
|
||||
protected final Set<Player> viewers = new CopyOnWriteArraySet<>();
|
||||
protected final Set<Player> unmodifiableViewers = Collections.unmodifiableSet(viewers);
|
||||
|
||||
protected AbstractInventory(int size) {
|
||||
this.size = size;
|
||||
this.itemStacks = new ItemStack[getSize()];
|
||||
Arrays.fill(itemStacks, ItemStack.AIR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this window id.
|
||||
* <p>
|
||||
* This is the id that the client will send to identify the affected inventory, mostly used by packets.
|
||||
*
|
||||
* @return the window id
|
||||
*/
|
||||
public abstract byte getWindowId();
|
||||
|
||||
@Override
|
||||
public @NotNull Set<Player> getViewers() {
|
||||
return unmodifiableViewers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addViewer(@NotNull Player player) {
|
||||
if (!this.viewers.add(player)) return false;
|
||||
|
||||
update(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeViewer(@NotNull Player player) {
|
||||
if (!this.viewers.remove(player)) return false;
|
||||
|
||||
// Drop cursor item when closing inventory
|
||||
ItemStack cursorItem = player.getInventory().getCursorItem();
|
||||
player.getInventory().setCursorItem(ItemStack.AIR);
|
||||
|
||||
if (!cursorItem.isAir()) {
|
||||
if (!player.dropItem(cursorItem)) {
|
||||
player.getInventory().addItemStack(cursorItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (player.didCloseInventory()) {
|
||||
player.sendPacket(new CloseWindowPacket(getWindowId()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an {@link ItemStack} at the specified slot and send relevant update to the viewer(s).
|
||||
*
|
||||
* @param slot the slot to set the item
|
||||
* @param itemStack the item to set
|
||||
*/
|
||||
public synchronized void setItemStack(int slot, @NotNull ItemStack itemStack) {
|
||||
Check.argCondition(!MathUtils.isBetween(slot, 0, getSize()),
|
||||
"Inventory does not have the slot " + slot);
|
||||
safeItemInsert(slot, itemStack);
|
||||
public void setItemStack(int slot, @NotNull ItemStack itemStack) {
|
||||
setItemStack(slot, itemStack, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts safely an item into the inventory.
|
||||
* <p>
|
||||
* This will update the slot for all viewers and warn the inventory that
|
||||
* the window items packet is not up-to-date.
|
||||
* Sets an {@link ItemStack} at the specified slot and send relevant update to the viewer(s).
|
||||
*
|
||||
* @param slot the internal slot id
|
||||
* @param itemStack the item to insert (use air instead of null)
|
||||
* @throws IllegalArgumentException if the slot {@code slot} does not exist
|
||||
* @param slot the slot to set the item
|
||||
* @param itemStack the item to set
|
||||
* @param sendPacket whether or not to send packets
|
||||
*/
|
||||
protected final void safeItemInsert(int slot, @NotNull ItemStack itemStack, boolean sendPacket) {
|
||||
public void setItemStack(int slot, @NotNull ItemStack itemStack, boolean sendPacket) {
|
||||
Check.argCondition(!MathUtils.isBetween(slot, 0, getSize()),
|
||||
"Inventory does not have the slot " + slot);
|
||||
|
||||
ItemStack previous;
|
||||
synchronized (this) {
|
||||
Check.argCondition(
|
||||
!MathUtils.isBetween(slot, 0, getSize()),
|
||||
"The slot {0} does not exist in this inventory",
|
||||
slot
|
||||
);
|
||||
previous = itemStacks[slot];
|
||||
if (itemStack.equals(previous)) return; // Avoid sending updates if the item has not changed
|
||||
UNSAFE_itemInsert(slot, itemStack, sendPacket);
|
||||
}
|
||||
if (this instanceof PlayerInventory inv) {
|
||||
EventDispatcher.call(new PlayerInventoryItemChangeEvent(inv.player, slot, previous, itemStack));
|
||||
} else if (this instanceof Inventory inv) {
|
||||
EventDispatcher.call(new InventoryItemChangeEvent(inv, slot, previous, itemStack));
|
||||
UNSAFE_itemInsert(slot, itemStack, previous, sendPacket);
|
||||
}
|
||||
EventDispatcher.call(new InventoryItemChangeEvent(this, slot, previous, itemStack));
|
||||
}
|
||||
|
||||
protected final void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
|
||||
safeItemInsert(slot, itemStack, true);
|
||||
protected void UNSAFE_itemInsert(int slot, @NotNull ItemStack item, @NotNull ItemStack previous, boolean sendPacket) {
|
||||
itemStacks[slot] = item;
|
||||
if (sendPacket) sendSlotRefresh(slot, item, previous);
|
||||
}
|
||||
|
||||
protected abstract void UNSAFE_itemInsert(int slot, @NotNull ItemStack itemStack, boolean sendPacket);
|
||||
public void sendSlotRefresh(int slot, @NotNull ItemStack item, @NotNull ItemStack previous) {
|
||||
sendPacketToViewers(new SetSlotPacket(getWindowId(), 0, (short) slot, item));
|
||||
}
|
||||
|
||||
public synchronized <T> @NotNull T processItemStack(@NotNull ItemStack itemStack,
|
||||
@NotNull TransactionType type,
|
||||
@ -167,13 +209,27 @@ public sealed abstract class AbstractInventory implements InventoryClickHandler,
|
||||
public synchronized void clear() {
|
||||
// Clear the item array
|
||||
for (int i = 0; i < size; i++) {
|
||||
safeItemInsert(i, ItemStack.AIR, false);
|
||||
setItemStack(i, ItemStack.AIR, false);
|
||||
}
|
||||
// Send the cleared inventory to viewers
|
||||
update();
|
||||
}
|
||||
|
||||
public abstract void update();
|
||||
/**
|
||||
* Refreshes the inventory for all viewers.
|
||||
*/
|
||||
public void update() {
|
||||
this.viewers.forEach(this::update);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the inventory for a specific viewer.
|
||||
*
|
||||
* @param player the player to update the inventory for
|
||||
*/
|
||||
public void update(@NotNull Player player) {
|
||||
player.sendPacket(new WindowItemsPacket(getWindowId(), 0, List.of(itemStacks), player.getInventory().getCursorItem()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link ItemStack} at the specified slot.
|
||||
|
@ -1,22 +1,16 @@
|
||||
package net.minestom.server.inventory;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.Viewable;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.inventory.click.ClickType;
|
||||
import net.minestom.server.inventory.click.InventoryClickResult;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.network.packet.server.play.OpenWindowPacket;
|
||||
import net.minestom.server.network.packet.server.play.SetSlotPacket;
|
||||
import net.minestom.server.network.packet.server.play.WindowItemsPacket;
|
||||
import net.minestom.server.network.packet.server.play.WindowPropertyPacket;
|
||||
import net.minestom.server.utils.inventory.PlayerInventoryUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
@ -25,22 +19,15 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* You can create one with {@link Inventory#Inventory(InventoryType, String)} or by making your own subclass.
|
||||
* It can then be opened using {@link Player#openInventory(Inventory)}.
|
||||
*/
|
||||
public non-sealed class Inventory extends AbstractInventory implements Viewable {
|
||||
public non-sealed class Inventory extends AbstractInventory {
|
||||
private static final AtomicInteger ID_COUNTER = new AtomicInteger();
|
||||
|
||||
// the id of this inventory
|
||||
private final byte id;
|
||||
// the type of this inventory
|
||||
private final InventoryType inventoryType;
|
||||
// the title of this inventory
|
||||
private Component title;
|
||||
|
||||
private final int offset;
|
||||
|
||||
// the players currently viewing this inventory
|
||||
private final Set<Player> viewers = new CopyOnWriteArraySet<>();
|
||||
private final Set<Player> unmodifiableViewers = Collections.unmodifiableSet(viewers);
|
||||
|
||||
public Inventory(@NotNull InventoryType inventoryType, @NotNull Component title) {
|
||||
super(inventoryType.getSize());
|
||||
this.id = generateId();
|
||||
@ -89,42 +76,11 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this window id.
|
||||
* <p>
|
||||
* This is the id that the client will send to identify the affected inventory, mostly used by packets.
|
||||
*
|
||||
* @return the window id
|
||||
*/
|
||||
@Override
|
||||
public byte getWindowId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the inventory for all viewers.
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
this.viewers.forEach(p -> p.sendPacket(createNewWindowItemsPacket(p)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the inventory for a specific viewer.
|
||||
* <p>
|
||||
* The player needs to be a viewer, otherwise nothing is sent.
|
||||
*
|
||||
* @param player the player to update the inventory
|
||||
*/
|
||||
public void update(@NotNull Player player) {
|
||||
if (!isViewer(player)) return;
|
||||
player.sendPacket(createNewWindowItemsPacket(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<Player> getViewers() {
|
||||
return unmodifiableViewers;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will not open the inventory for {@code player}, use {@link Player#openInventory(Inventory)}.
|
||||
*
|
||||
@ -133,9 +89,12 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
||||
*/
|
||||
@Override
|
||||
public boolean addViewer(@NotNull Player player) {
|
||||
final boolean result = this.viewers.add(player);
|
||||
if (!this.viewers.add(player)) return false;
|
||||
|
||||
// Also send the open window packet
|
||||
player.sendPacket(new OpenWindowPacket(id, inventoryType.getWindowType(), title));
|
||||
update(player);
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,9 +105,10 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
||||
*/
|
||||
@Override
|
||||
public boolean removeViewer(@NotNull Player player) {
|
||||
final boolean result = this.viewers.remove(player);
|
||||
if (!super.removeViewer(player)) return false;
|
||||
|
||||
this.clickProcessor.clearCache(player);
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -173,16 +133,6 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
||||
player.getInventory().setCursorItem(cursorItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void UNSAFE_itemInsert(int slot, @NotNull ItemStack itemStack, boolean sendPacket) {
|
||||
itemStacks[slot] = itemStack;
|
||||
if (sendPacket) sendPacketToViewers(new SetSlotPacket(getWindowId(), 0, (short) slot, itemStack));
|
||||
}
|
||||
|
||||
private @NotNull WindowItemsPacket createNewWindowItemsPacket(Player player) {
|
||||
return new WindowItemsPacket(getWindowId(), 0, List.of(getItemStacks()), player.getInventory().getCursorItem());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a window property to all viewers.
|
||||
*
|
||||
@ -200,9 +150,9 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
||||
final ItemStack cursor = playerInventory.getCursorItem();
|
||||
final boolean isInWindow = isClickInWindow(slot);
|
||||
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
||||
final AbstractInventory clickedInventory = isInWindow ? this : playerInventory;
|
||||
final ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot);
|
||||
final InventoryClickResult clickResult = clickProcessor.leftClick(player,
|
||||
isInWindow ? this : playerInventory, clickSlot, clicked, cursor);
|
||||
final InventoryClickResult clickResult = clickProcessor.leftClick(player, clickedInventory, clickSlot, clicked, cursor);
|
||||
if (clickResult.isCancel()) {
|
||||
updateAll(player);
|
||||
return false;
|
||||
@ -213,7 +163,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
||||
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
||||
}
|
||||
playerInventory.setCursorItem(clickResult.getCursor());
|
||||
callClickEvent(player, isInWindow ? this : null, slot, ClickType.LEFT_CLICK, clicked, cursor);
|
||||
callClickEvent(player, clickedInventory, slot, ClickType.LEFT_CLICK, clicked, cursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -224,8 +174,8 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
||||
final boolean isInWindow = isClickInWindow(slot);
|
||||
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
||||
final ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot);
|
||||
final InventoryClickResult clickResult = clickProcessor.rightClick(player,
|
||||
isInWindow ? this : playerInventory, clickSlot, clicked, cursor);
|
||||
final AbstractInventory clickedInventory = isInWindow ? this : playerInventory;
|
||||
final InventoryClickResult clickResult = clickProcessor.rightClick(player, clickedInventory, clickSlot, clicked, cursor);
|
||||
if (clickResult.isCancel()) {
|
||||
updateAll(player);
|
||||
return false;
|
||||
@ -236,7 +186,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
||||
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
||||
}
|
||||
playerInventory.setCursorItem(clickResult.getCursor());
|
||||
callClickEvent(player, isInWindow ? this : null, slot, ClickType.RIGHT_CLICK, clicked, cursor);
|
||||
callClickEvent(player, clickedInventory, slot, ClickType.RIGHT_CLICK, clicked, cursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -274,8 +224,8 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
||||
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
||||
final ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(clickSlot);
|
||||
final ItemStack heldItem = playerInventory.getItemStack(convertedKey);
|
||||
final InventoryClickResult clickResult = clickProcessor.changeHeld(player,
|
||||
isInWindow ? this : playerInventory, clickSlot, convertedKey, clicked, heldItem);
|
||||
final AbstractInventory clickedInventory = isInWindow ? this : playerInventory;
|
||||
final InventoryClickResult clickResult = clickProcessor.changeHeld(player, clickedInventory, clickSlot, convertedKey, clicked, heldItem);
|
||||
if (clickResult.isCancel()) {
|
||||
updateAll(player);
|
||||
return false;
|
||||
@ -286,7 +236,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
||||
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
||||
}
|
||||
playerInventory.setItemStack(convertedKey, clickResult.getCursor());
|
||||
callClickEvent(player, isInWindow ? this : null, slot, ClickType.CHANGE_HELD, clicked, playerInventory.getCursorItem());
|
||||
callClickEvent(player, clickedInventory, slot, ClickType.CHANGE_HELD, clicked, playerInventory.getCursorItem());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ public sealed interface InventoryClickHandler permits AbstractInventory {
|
||||
*/
|
||||
boolean doubleClick(@NotNull Player player, int slot);
|
||||
|
||||
default void callClickEvent(@NotNull Player player, Inventory inventory, int slot,
|
||||
default void callClickEvent(@NotNull Player player, @NotNull AbstractInventory inventory, int slot,
|
||||
@NotNull ClickType clickType, @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
|
||||
EventDispatcher.call(new InventoryClickEvent(inventory, player, slot, clickType, clicked, cursor));
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.minestom.server.inventory;
|
||||
|
||||
import net.minestom.server.entity.EquipmentSlot;
|
||||
import net.minestom.server.entity.GameMode;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.EventDispatcher;
|
||||
import net.minestom.server.event.item.EntityEquipEvent;
|
||||
@ -12,6 +11,7 @@ import net.minestom.server.network.packet.server.play.SetSlotPacket;
|
||||
import net.minestom.server.network.packet.server.play.WindowItemsPacket;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -20,24 +20,23 @@ import static net.minestom.server.utils.inventory.PlayerInventoryUtils.*;
|
||||
/**
|
||||
* Represents the inventory of a {@link Player}, retrieved with {@link Player#getInventory()}.
|
||||
*/
|
||||
public non-sealed class PlayerInventory extends AbstractInventory implements EquipmentHandler {
|
||||
public non-sealed class PlayerInventory extends AbstractInventory {
|
||||
public static final int INVENTORY_SIZE = 46;
|
||||
public static final int INNER_INVENTORY_SIZE = 36;
|
||||
|
||||
protected final Player player;
|
||||
private ItemStack cursorItem = ItemStack.AIR;
|
||||
|
||||
public PlayerInventory(@NotNull Player player) {
|
||||
public PlayerInventory() {
|
||||
super(INVENTORY_SIZE);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void clear() {
|
||||
cursorItem = ItemStack.AIR;
|
||||
super.clear();
|
||||
|
||||
// Update equipments
|
||||
this.player.sendPacketToViewersAndSelf(player.getEquipmentsPacket());
|
||||
viewers.forEach(viewer -> viewer.sendPacketToViewersAndSelf(viewer.getEquipmentsPacket()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -45,35 +44,45 @@ public non-sealed class PlayerInventory extends AbstractInventory implements Equ
|
||||
return INNER_INVENTORY_SIZE;
|
||||
}
|
||||
|
||||
private int getSlotId(@NotNull EquipmentSlot slot) {
|
||||
@Override
|
||||
public byte getWindowId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int getSlotId(@NotNull EquipmentSlot slot, byte heldSlot) {
|
||||
return switch (slot) {
|
||||
case MAIN_HAND -> player.getHeldSlot();
|
||||
case MAIN_HAND -> heldSlot;
|
||||
case OFF_HAND -> OFFHAND_SLOT;
|
||||
default -> slot.armorSlot();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getEquipment(@NotNull EquipmentSlot slot) {
|
||||
if (slot == EquipmentSlot.BODY) return ItemStack.AIR;
|
||||
return getItemStack(getSlotId(slot));
|
||||
private @Nullable EquipmentSlot getEquipmentSlot(int slot, byte heldSlot) {
|
||||
return switch (slot) {
|
||||
case OFFHAND_SLOT -> EquipmentSlot.OFF_HAND;
|
||||
case HELMET_SLOT -> EquipmentSlot.HELMET;
|
||||
case CHESTPLATE_SLOT -> EquipmentSlot.CHESTPLATE;
|
||||
case LEGGINGS_SLOT -> EquipmentSlot.LEGGINGS;
|
||||
case BOOTS_SLOT -> EquipmentSlot.BOOTS;
|
||||
default -> slot == heldSlot ? EquipmentSlot.MAIN_HAND : null;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEquipment(@NotNull EquipmentSlot slot, @NotNull ItemStack itemStack) {
|
||||
public @NotNull ItemStack getEquipment(@NotNull EquipmentSlot slot, byte heldSlot) {
|
||||
if (slot == EquipmentSlot.BODY) return ItemStack.AIR;
|
||||
return getItemStack(getSlotId(slot, heldSlot));
|
||||
}
|
||||
|
||||
public void setEquipment(@NotNull EquipmentSlot slot, byte heldSlot, @NotNull ItemStack itemStack) {
|
||||
if (slot == EquipmentSlot.BODY)
|
||||
Check.fail("PlayerInventory does not support body equipment");
|
||||
|
||||
safeItemInsert(getSlotId(slot), itemStack);
|
||||
setItemStack(getSlotId(slot, heldSlot), itemStack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the player inventory by sending a {@link WindowItemsPacket} containing all.
|
||||
* the inventory items
|
||||
*/
|
||||
@Override
|
||||
public void update() {
|
||||
this.player.sendPacket(createWindowItemsPacket());
|
||||
public void update(@NotNull Player player) {
|
||||
player.sendPacket(createWindowItemsPacket());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,49 +102,42 @@ public non-sealed class PlayerInventory extends AbstractInventory implements Equ
|
||||
public void setCursorItem(@NotNull ItemStack cursorItem) {
|
||||
if (this.cursorItem.equals(cursorItem)) return;
|
||||
this.cursorItem = cursorItem;
|
||||
final SetSlotPacket setSlotPacket = SetSlotPacket.createCursorPacket(cursorItem);
|
||||
this.player.sendPacket(setSlotPacket);
|
||||
sendPacketToViewers(SetSlotPacket.createCursorPacket(cursorItem));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void UNSAFE_itemInsert(int slot, @NotNull ItemStack itemStack, boolean sendPacket) {
|
||||
final EquipmentSlot equipmentSlot = switch (slot) {
|
||||
case HELMET_SLOT -> EquipmentSlot.HELMET;
|
||||
case CHESTPLATE_SLOT -> EquipmentSlot.CHESTPLATE;
|
||||
case LEGGINGS_SLOT -> EquipmentSlot.LEGGINGS;
|
||||
case BOOTS_SLOT -> EquipmentSlot.BOOTS;
|
||||
case OFFHAND_SLOT -> EquipmentSlot.OFF_HAND;
|
||||
default -> slot == player.getHeldSlot() ? EquipmentSlot.MAIN_HAND : null;
|
||||
};
|
||||
if (equipmentSlot != null) {
|
||||
EntityEquipEvent entityEquipEvent = new EntityEquipEvent(player, itemStack, equipmentSlot);
|
||||
EventDispatcher.call(entityEquipEvent);
|
||||
itemStack = entityEquipEvent.getEquippedItem();
|
||||
this.player.updateEquipmentAttributes(this.itemStacks[slot], itemStack, equipmentSlot);
|
||||
}
|
||||
this.itemStacks[slot] = itemStack;
|
||||
protected void UNSAFE_itemInsert(int slot, @NotNull ItemStack item, @NotNull ItemStack previous, boolean sendPacket) {
|
||||
for (Player player : getViewers()) {
|
||||
final EquipmentSlot equipmentSlot = getEquipmentSlot(slot, player.getHeldSlot());
|
||||
if (equipmentSlot == null) continue;
|
||||
|
||||
if (sendPacket) {
|
||||
// Sync equipment
|
||||
if (equipmentSlot != null) this.player.syncEquipment(equipmentSlot);
|
||||
// Refresh slot
|
||||
sendSlotRefresh((short) convertToPacketSlot(slot), itemStack);
|
||||
EntityEquipEvent entityEquipEvent = new EntityEquipEvent(player, item, equipmentSlot);
|
||||
EventDispatcher.call(entityEquipEvent);
|
||||
item = entityEquipEvent.getEquippedItem();
|
||||
}
|
||||
|
||||
super.UNSAFE_itemInsert(slot, item, previous, sendPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes an inventory slot.
|
||||
*
|
||||
* @param slot the packet slot,
|
||||
* see {@link net.minestom.server.utils.inventory.PlayerInventoryUtils#convertToPacketSlot(int)}
|
||||
* @param itemStack the item stack in the slot
|
||||
*/
|
||||
protected void sendSlotRefresh(short slot, ItemStack itemStack) {
|
||||
var openInventory = player.getOpenInventory();
|
||||
if (openInventory != null && slot >= OFFSET && slot < OFFSET + INNER_INVENTORY_SIZE) {
|
||||
this.player.sendPacket(new SetSlotPacket(openInventory.getWindowId(), 0, (short) (slot + openInventory.getSize() - OFFSET), itemStack));
|
||||
} else if (openInventory == null || slot == OFFHAND_SLOT) {
|
||||
this.player.sendPacket(new SetSlotPacket((byte) 0, 0, slot, itemStack));
|
||||
@Override
|
||||
public void sendSlotRefresh(int slot, @NotNull ItemStack item, @NotNull ItemStack previous) {
|
||||
SetSlotPacket defaultPacket = new SetSlotPacket(getWindowId(), 0, (byte) slot, item);
|
||||
|
||||
for (Player player : getViewers()) {
|
||||
// Equipment handling
|
||||
final EquipmentSlot equipmentSlot = getEquipmentSlot(slot, player.getHeldSlot());
|
||||
if (equipmentSlot != null) {
|
||||
player.updateEquipmentAttributes(previous, item, equipmentSlot);
|
||||
player.syncEquipment(equipmentSlot);
|
||||
}
|
||||
|
||||
// Slot handling
|
||||
AbstractInventory openInventory = player.getOpenInventory();
|
||||
if (openInventory != null && slot >= OFFSET && slot < OFFSET + INNER_INVENTORY_SIZE) {
|
||||
player.sendPacket(new SetSlotPacket(openInventory.getWindowId(), 0, (short) (slot + openInventory.getSize() - OFFSET), item));
|
||||
} else if (openInventory == null || slot == OFFHAND_SLOT) {
|
||||
player.sendPacket(defaultPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ public interface TransactionOption<T> {
|
||||
* The remaining, can be air.
|
||||
*/
|
||||
TransactionOption<ItemStack> ALL = (inventory, result, itemChangesMap) -> {
|
||||
itemChangesMap.forEach(inventory::safeItemInsert);
|
||||
itemChangesMap.forEach(inventory::setItemStack);
|
||||
return result;
|
||||
};
|
||||
|
||||
@ -26,7 +26,7 @@ public interface TransactionOption<T> {
|
||||
TransactionOption<Boolean> ALL_OR_NOTHING = (inventory, result, itemChangesMap) -> {
|
||||
if (result.isAir()) {
|
||||
// Item can be fully placed inside the inventory, do so
|
||||
itemChangesMap.forEach(inventory::safeItemInsert);
|
||||
itemChangesMap.forEach(inventory::setItemStack);
|
||||
return true;
|
||||
} else {
|
||||
// Inventory cannot accept the item fully
|
||||
|
@ -454,10 +454,9 @@ public final class InventoryClickProcessor {
|
||||
return clickResult;
|
||||
}
|
||||
|
||||
private void callClickEvent(@NotNull Player player, @Nullable AbstractInventory inventory, int slot,
|
||||
private void callClickEvent(@NotNull Player player, @NotNull AbstractInventory inventory, int slot,
|
||||
@NotNull ClickType clickType, @NotNull ItemStack clicked, @NotNull ItemStack cursor) {
|
||||
final Inventory eventInventory = inventory instanceof Inventory ? (Inventory) inventory : null;
|
||||
EventDispatcher.call(new InventoryClickEvent(eventInventory, player, slot, clickType, clicked, cursor));
|
||||
EventDispatcher.call(new InventoryClickEvent(inventory, player, slot, clickType, clicked, cursor));
|
||||
}
|
||||
|
||||
public void clearCache(@NotNull Player player) {
|
||||
|
@ -37,7 +37,6 @@ public class BlockPlacementListener {
|
||||
private static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
|
||||
|
||||
public static void listener(ClientPlayerBlockPlacementPacket packet, Player player) {
|
||||
final PlayerInventory playerInventory = player.getInventory();
|
||||
final PlayerHand hand = packet.hand();
|
||||
final BlockFace blockFace = packet.blockFace();
|
||||
Point blockPosition = packet.blockPosition();
|
||||
@ -179,10 +178,10 @@ public class BlockPlacementListener {
|
||||
if (playerBlockPlaceEvent.doesConsumeBlock()) {
|
||||
// Consume the block in the player's hand
|
||||
final ItemStack newUsedItem = usedItem.consume(1);
|
||||
playerInventory.setItemInHand(hand, newUsedItem);
|
||||
player.setItemInHand(hand, newUsedItem);
|
||||
} else {
|
||||
// Prevent invisible item on client
|
||||
playerInventory.update();
|
||||
player.getInventory().update();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@ import net.minestom.server.event.player.PlayerSwapItemEvent;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.instance.block.BlockFace;
|
||||
import net.minestom.server.inventory.PlayerInventory;
|
||||
import net.minestom.server.item.ItemComponent;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.component.BlockPredicates;
|
||||
@ -128,12 +127,12 @@ public final class PlayerDiggingListener {
|
||||
}
|
||||
|
||||
private static void dropStack(Player player) {
|
||||
final ItemStack droppedItemStack = player.getInventory().getItemInMainHand();
|
||||
final ItemStack droppedItemStack = player.getItemInMainHand();
|
||||
dropItem(player, droppedItemStack, ItemStack.AIR);
|
||||
}
|
||||
|
||||
private static void dropSingle(Player player) {
|
||||
final ItemStack handItem = player.getInventory().getItemInMainHand();
|
||||
final ItemStack handItem = player.getItemInMainHand();
|
||||
final int handAmount = handItem.amount();
|
||||
if (handAmount <= 1) {
|
||||
// Drop the whole item without copy
|
||||
@ -162,13 +161,12 @@ public final class PlayerDiggingListener {
|
||||
}
|
||||
|
||||
private static void swapItemHand(Player player) {
|
||||
final PlayerInventory inventory = player.getInventory();
|
||||
final ItemStack mainHand = inventory.getItemInMainHand();
|
||||
final ItemStack offHand = inventory.getItemInOffHand();
|
||||
final ItemStack mainHand = player.getItemInMainHand();
|
||||
final ItemStack offHand = player.getItemInOffHand();
|
||||
PlayerSwapItemEvent swapItemEvent = new PlayerSwapItemEvent(player, offHand, mainHand);
|
||||
EventDispatcher.callCancellable(swapItemEvent, () -> {
|
||||
inventory.setItemInMainHand(swapItemEvent.getMainHandItem());
|
||||
inventory.setItemInOffHand(swapItemEvent.getOffHandItem());
|
||||
player.setItemInMainHand(swapItemEvent.getMainHandItem());
|
||||
player.setItemInOffHand(swapItemEvent.getOffHandItem());
|
||||
});
|
||||
}
|
||||
|
||||
@ -192,11 +190,10 @@ public final class PlayerDiggingListener {
|
||||
|
||||
private static void dropItem(@NotNull Player player,
|
||||
@NotNull ItemStack droppedItem, @NotNull ItemStack handItem) {
|
||||
final PlayerInventory playerInventory = player.getInventory();
|
||||
if (player.dropItem(droppedItem)) {
|
||||
playerInventory.setItemInMainHand(handItem);
|
||||
player.setItemInMainHand(handItem);
|
||||
} else {
|
||||
playerInventory.update();
|
||||
player.getInventory().update();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ public class UseItemListener {
|
||||
|
||||
public static void useItemListener(ClientUseItemPacket packet, Player player) {
|
||||
final PlayerHand hand = packet.hand();
|
||||
final ItemStack itemStack = player.getInventory().getItemInHand(hand);
|
||||
final ItemStack itemStack = player.getItemInHand(hand);
|
||||
final Material material = itemStack.material();
|
||||
|
||||
boolean usingMainHand = player.getItemUseHand() == Player.Hand.MAIN && hand == Player.Hand.OFF;
|
||||
|
@ -27,7 +27,7 @@ public class PlayerCreativeSlotTest {
|
||||
player.setGameMode(GameMode.CREATIVE);
|
||||
player.addPacketToQueue(new ClientCreativeInventoryActionPacket((short) PlayerInventoryUtils.OFFHAND_SLOT, ItemStack.of(Material.STICK)));
|
||||
player.interpretPacketQueue();
|
||||
assertEquals(Material.STICK, player.getInventory().getItemInOffHand().material());
|
||||
assertEquals(Material.STICK, player.getItemInOffHand().material());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -118,19 +118,19 @@ public class PlayerInventoryIntegrationTest {
|
||||
var equipmentTracker = connectionViewer.trackIncoming(EntityEquipmentPacket.class);
|
||||
|
||||
// Setting to an item should send EntityEquipmentPacket to viewer
|
||||
playerArmored.getInventory().setEquipment(EquipmentSlot.HELMET, MAGIC_STACK);
|
||||
playerArmored.setEquipment(EquipmentSlot.HELMET, MAGIC_STACK);
|
||||
equipmentTracker.assertSingle(entityEquipmentPacket -> {
|
||||
assertEquals(MAGIC_STACK, entityEquipmentPacket.equipments().get(EquipmentSlot.HELMET));
|
||||
});
|
||||
|
||||
// Setting to the same item shouldn't send packet
|
||||
equipmentTracker = connectionViewer.trackIncoming(EntityEquipmentPacket.class);
|
||||
playerArmored.getInventory().setEquipment(EquipmentSlot.HELMET, MAGIC_STACK);
|
||||
playerArmored.setEquipment(EquipmentSlot.HELMET, MAGIC_STACK);
|
||||
equipmentTracker.assertEmpty();
|
||||
|
||||
// Setting to air should send packet
|
||||
equipmentTracker = connectionViewer.trackIncoming(EntityEquipmentPacket.class);
|
||||
playerArmored.getInventory().setEquipment(EquipmentSlot.HELMET, ItemStack.AIR);
|
||||
playerArmored.setEquipment(EquipmentSlot.HELMET, ItemStack.AIR);
|
||||
equipmentTracker.assertSingle(entityEquipmentPacket -> {
|
||||
assertEquals(ItemStack.AIR, entityEquipmentPacket.equipments().get(EquipmentSlot.HELMET));
|
||||
});
|
||||
|
@ -3,6 +3,7 @@ package net.minestom.server.inventory.click.integration;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.inventory.InventoryPreClickEvent;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.InventoryType;
|
||||
import net.minestom.server.inventory.click.ClickType;
|
||||
@ -82,7 +83,7 @@ public class HeldClickIntegrationTest {
|
||||
});
|
||||
heldClick(player, 3, 40);
|
||||
assertEquals(ItemStack.AIR, inventory.getItemStack(3));
|
||||
assertEquals(ItemStack.of(Material.EGG), inventory.getItemInOffHand());
|
||||
assertEquals(ItemStack.of(Material.EGG), player.getItemInOffHand());
|
||||
}
|
||||
// Cancel event
|
||||
{
|
||||
@ -151,7 +152,7 @@ public class HeldClickIntegrationTest {
|
||||
});
|
||||
heldClickOpenInventory(player, 3, 40);
|
||||
assertEquals(ItemStack.AIR, inventory.getItemStack(3));
|
||||
assertEquals(ItemStack.of(Material.EGG), playerInv.getItemInOffHand());
|
||||
assertEquals(ItemStack.of(Material.EGG), player.getItemInOffHand());
|
||||
}
|
||||
// Cancel event
|
||||
{
|
||||
@ -171,7 +172,7 @@ public class HeldClickIntegrationTest {
|
||||
_heldClick(player.getOpenInventory(), false, player, slot, target);
|
||||
}
|
||||
|
||||
private void _heldClick(Inventory openInventory, boolean clickOpenInventory, Player player, int slot, int target) {
|
||||
private void _heldClick(AbstractInventory openInventory, boolean clickOpenInventory, Player player, int slot, int target) {
|
||||
final byte windowId = openInventory != null ? openInventory.getWindowId() : 0;
|
||||
if (clickOpenInventory) {
|
||||
assert openInventory != null;
|
||||
|
@ -4,6 +4,7 @@ package net.minestom.server.inventory.click.integration;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.inventory.InventoryPreClickEvent;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.InventoryType;
|
||||
import net.minestom.server.inventory.click.ClickType;
|
||||
@ -154,7 +155,7 @@ public class LeftClickIntegrationTest {
|
||||
_leftClick(player.getOpenInventory(), false, player, slot);
|
||||
}
|
||||
|
||||
private void _leftClick(Inventory openInventory, boolean clickOpenInventory, Player player, int slot) {
|
||||
private void _leftClick(AbstractInventory openInventory, boolean clickOpenInventory, Player player, int slot) {
|
||||
final byte windowId = openInventory != null ? openInventory.getWindowId() : 0;
|
||||
if (clickOpenInventory) {
|
||||
assert openInventory != null;
|
||||
|
@ -3,6 +3,7 @@ package net.minestom.server.inventory.click.integration;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.inventory.InventoryPreClickEvent;
|
||||
import net.minestom.server.inventory.AbstractInventory;
|
||||
import net.minestom.server.inventory.Inventory;
|
||||
import net.minestom.server.inventory.InventoryType;
|
||||
import net.minestom.server.inventory.click.ClickType;
|
||||
@ -175,7 +176,7 @@ public class RightClickIntegrationTest {
|
||||
_rightClick(player.getOpenInventory(), false, player, slot);
|
||||
}
|
||||
|
||||
private void _rightClick(Inventory openInventory, boolean clickOpenInventory, Player player, int slot) {
|
||||
private void _rightClick(AbstractInventory openInventory, boolean clickOpenInventory, Player player, int slot) {
|
||||
final byte windowId = openInventory != null ? openInventory.getWindowId() : 0;
|
||||
if (clickOpenInventory) {
|
||||
assert openInventory != null;
|
||||
|
Loading…
Reference in New Issue
Block a user