mirror of
https://github.com/Minestom/Minestom.git
synced 2024-09-29 23:17:59 +02:00
Force volatile read for inventory contents
This commit is contained in:
parent
3ffe37591b
commit
f0cf2d946c
@ -1,5 +1,8 @@
|
|||||||
package net.minestom.server.inventory;
|
package net.minestom.server.inventory;
|
||||||
|
|
||||||
|
import net.minestom.server.event.GlobalHandles;
|
||||||
|
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.click.InventoryClickProcessor;
|
||||||
import net.minestom.server.inventory.condition.InventoryCondition;
|
import net.minestom.server.inventory.condition.InventoryCondition;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
@ -9,9 +12,10 @@ import net.minestom.server.utils.MathUtils;
|
|||||||
import net.minestom.server.utils.validate.Check;
|
import net.minestom.server.utils.validate.Check;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
|
||||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.VarHandle;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -24,6 +28,8 @@ import java.util.function.UnaryOperator;
|
|||||||
public sealed abstract class AbstractInventory implements InventoryClickHandler, TagHandler
|
public sealed abstract class AbstractInventory implements InventoryClickHandler, TagHandler
|
||||||
permits Inventory, PlayerInventory {
|
permits Inventory, PlayerInventory {
|
||||||
|
|
||||||
|
private static final VarHandle ITEM_UPDATER = MethodHandles.arrayElementVarHandle(ItemStack[].class);
|
||||||
|
|
||||||
private final int size;
|
private final int size;
|
||||||
protected final ItemStack[] itemStacks;
|
protected final ItemStack[] itemStacks;
|
||||||
|
|
||||||
@ -38,7 +44,6 @@ public sealed abstract class AbstractInventory implements InventoryClickHandler,
|
|||||||
protected AbstractInventory(int size) {
|
protected AbstractInventory(int size) {
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.itemStacks = new ItemStack[getSize()];
|
this.itemStacks = new ItemStack[getSize()];
|
||||||
|
|
||||||
Arrays.fill(itemStacks, ItemStack.AIR);
|
Arrays.fill(itemStacks, ItemStack.AIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +67,6 @@ public sealed abstract class AbstractInventory implements InventoryClickHandler,
|
|||||||
*
|
*
|
||||||
* @param slot the internal slot id
|
* @param slot the internal slot id
|
||||||
* @param itemStack the item to insert (use air instead of null)
|
* @param itemStack the item to insert (use air instead of null)
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if the slot {@code slot} does not exist
|
* @throws IllegalArgumentException if the slot {@code slot} does not exist
|
||||||
*/
|
*/
|
||||||
protected final void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
|
protected final void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
|
||||||
@ -76,13 +80,15 @@ public sealed abstract class AbstractInventory implements InventoryClickHandler,
|
|||||||
previous = itemStacks[slot];
|
previous = itemStacks[slot];
|
||||||
UNSAFE_itemInsert(slot, itemStack);
|
UNSAFE_itemInsert(slot, itemStack);
|
||||||
}
|
}
|
||||||
callItemChangeEvent(slot, previous, itemStack);
|
if (this instanceof PlayerInventory inv) {
|
||||||
|
GlobalHandles.PLAYER_INVENTORY_ITEM_CHANGE_EVENT.call(new PlayerInventoryItemChangeEvent(inv.player, slot, previous, itemStack));
|
||||||
|
} else if (this instanceof Inventory inv) {
|
||||||
|
GlobalHandles.INVENTORY_ITEM_CHANGE_EVENT.call(new InventoryItemChangeEvent(inv, slot, previous, itemStack));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void UNSAFE_itemInsert(int slot, @NotNull ItemStack itemStack);
|
protected abstract void UNSAFE_itemInsert(int slot, @NotNull ItemStack itemStack);
|
||||||
|
|
||||||
protected abstract void callItemChangeEvent(int slot, @NotNull ItemStack previous, @NotNull ItemStack current);
|
|
||||||
|
|
||||||
public synchronized <T> @NotNull T processItemStack(@NotNull ItemStack itemStack,
|
public synchronized <T> @NotNull T processItemStack(@NotNull ItemStack itemStack,
|
||||||
@NotNull TransactionType type,
|
@NotNull TransactionType type,
|
||||||
@NotNull TransactionOption<T> option) {
|
@NotNull TransactionOption<T> option) {
|
||||||
@ -172,7 +178,7 @@ public sealed abstract class AbstractInventory implements InventoryClickHandler,
|
|||||||
* @return the item in the slot {@code slot}
|
* @return the item in the slot {@code slot}
|
||||||
*/
|
*/
|
||||||
public @NotNull ItemStack getItemStack(int slot) {
|
public @NotNull ItemStack getItemStack(int slot) {
|
||||||
return itemStacks[slot];
|
return (ItemStack) ITEM_UPDATER.getVolatile(itemStacks, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,8 +3,6 @@ package net.minestom.server.inventory;
|
|||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.minestom.server.Viewable;
|
import net.minestom.server.Viewable;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.event.GlobalHandles;
|
|
||||||
import net.minestom.server.event.inventory.InventoryItemChangeEvent;
|
|
||||||
import net.minestom.server.inventory.click.ClickType;
|
import net.minestom.server.inventory.click.ClickType;
|
||||||
import net.minestom.server.inventory.click.InventoryClickResult;
|
import net.minestom.server.inventory.click.InventoryClickResult;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
@ -72,8 +70,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
|||||||
*
|
*
|
||||||
* @return the inventory type
|
* @return the inventory type
|
||||||
*/
|
*/
|
||||||
@NotNull
|
public @NotNull InventoryType getInventoryType() {
|
||||||
public InventoryType getInventoryType() {
|
|
||||||
return inventoryType;
|
return inventoryType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,8 +79,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
|||||||
*
|
*
|
||||||
* @return the inventory title
|
* @return the inventory title
|
||||||
*/
|
*/
|
||||||
@NotNull
|
public @NotNull Component getTitle() {
|
||||||
public Component getTitle() {
|
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,9 +120,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void update() {
|
public void update() {
|
||||||
for (Player player : viewers) {
|
this.viewers.forEach(p -> p.sendPacket(createNewWindowItemsPacket(p)));
|
||||||
player.sendPacket(createNewWindowItemsPacket(player));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -141,9 +135,8 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
|||||||
player.sendPacket(createNewWindowItemsPacket(player));
|
player.sendPacket(createNewWindowItemsPacket(player));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Player> getViewers() {
|
public @NotNull Set<Player> getViewers() {
|
||||||
return unmodifiableViewers;
|
return unmodifiableViewers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,8 +173,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
|||||||
* @param player the player to get the cursor item from
|
* @param player the player to get the cursor item from
|
||||||
* @return the player cursor item, air item if the player is not a viewer
|
* @return the player cursor item, air item if the player is not a viewer
|
||||||
*/
|
*/
|
||||||
@NotNull
|
public @NotNull ItemStack getCursorItem(@NotNull Player player) {
|
||||||
public ItemStack getCursorItem(@NotNull Player player) {
|
|
||||||
return cursorPlayersItem.getOrDefault(player, ItemStack.AIR);
|
return cursorPlayersItem.getOrDefault(player, ItemStack.AIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,16 +202,6 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
|||||||
sendPacketToViewers(new SetSlotPacket(getWindowId(), 0, (short) slot, itemStack));
|
sendPacketToViewers(new SetSlotPacket(getWindowId(), 0, (short) slot, itemStack));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void callItemChangeEvent(int slot, @NotNull ItemStack previous, @NotNull ItemStack current) {
|
|
||||||
GlobalHandles.INVENTORY_ITEM_CHANGE_EVENT.call(new InventoryItemChangeEvent(this, slot, previous, current));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a complete new {@link WindowItemsPacket}.
|
|
||||||
*
|
|
||||||
* @return a new {@link WindowItemsPacket} packet
|
|
||||||
*/
|
|
||||||
private @NotNull WindowItemsPacket createNewWindowItemsPacket(Player player) {
|
private @NotNull WindowItemsPacket createNewWindowItemsPacket(Player player) {
|
||||||
return new WindowItemsPacket(getWindowId(), 0, List.of(getItemStacks()), cursorPlayersItem.getOrDefault(player, ItemStack.AIR));
|
return new WindowItemsPacket(getWindowId(), 0, List.of(getItemStacks()), cursorPlayersItem.getOrDefault(player, ItemStack.AIR));
|
||||||
}
|
}
|
||||||
@ -235,22 +217,6 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
|||||||
sendPacketToViewers(new WindowPropertyPacket(getWindowId(), property.getProperty(), value));
|
sendPacketToViewers(new WindowPropertyPacket(getWindowId(), property.getProperty(), value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the internal player's cursor item
|
|
||||||
* <p>
|
|
||||||
* WARNING: the player will not be notified by the change
|
|
||||||
*
|
|
||||||
* @param player the player to change the cursor item
|
|
||||||
* @param itemStack the cursor item
|
|
||||||
*/
|
|
||||||
private void refreshPlayerCursorItem(@NotNull Player player, @NotNull ItemStack itemStack) {
|
|
||||||
this.cursorPlayersItem.put(player, itemStack);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isClickInWindow(int slot) {
|
|
||||||
return slot < getSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean leftClick(@NotNull Player player, int slot) {
|
public boolean leftClick(@NotNull Player player, int slot) {
|
||||||
final PlayerInventory playerInventory = player.getInventory();
|
final PlayerInventory playerInventory = player.getInventory();
|
||||||
@ -269,7 +235,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
|||||||
} else {
|
} else {
|
||||||
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
||||||
}
|
}
|
||||||
refreshPlayerCursorItem(player, clickResult.getCursor());
|
this.cursorPlayersItem.put(player, clickResult.getCursor());
|
||||||
callClickEvent(player, isInWindow ? this : null, slot, ClickType.LEFT_CLICK, clicked, cursor);
|
callClickEvent(player, isInWindow ? this : null, slot, ClickType.LEFT_CLICK, clicked, cursor);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -292,7 +258,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
|||||||
} else {
|
} else {
|
||||||
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
||||||
}
|
}
|
||||||
refreshPlayerCursorItem(player, clickResult.getCursor());
|
this.cursorPlayersItem.put(player, clickResult.getCursor());
|
||||||
callClickEvent(player, isInWindow ? this : null, slot, ClickType.RIGHT_CLICK, clicked, cursor);
|
callClickEvent(player, isInWindow ? this : null, slot, ClickType.RIGHT_CLICK, clicked, cursor);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -319,7 +285,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
|||||||
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
|
||||||
}
|
}
|
||||||
updateAll(player); // FIXME: currently not properly client-predicted
|
updateAll(player); // FIXME: currently not properly client-predicted
|
||||||
refreshPlayerCursorItem(player, clickResult.getCursor());
|
this.cursorPlayersItem.put(player, clickResult.getCursor());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,7 +342,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
|||||||
playerInventory.setItemStack(clickSlot, resultClicked);
|
playerInventory.setItemStack(clickSlot, resultClicked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refreshPlayerCursorItem(player, clickResult.getCursor());
|
this.cursorPlayersItem.put(player, clickResult.getCursor());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,7 +363,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
|||||||
updateAll(player);
|
updateAll(player);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
refreshPlayerCursorItem(player, clickResult.getCursor());
|
this.cursorPlayersItem.put(player, clickResult.getCursor());
|
||||||
updateAll(player); // FIXME: currently not properly client-predicted
|
updateAll(player); // FIXME: currently not properly client-predicted
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -417,11 +383,15 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
|
|||||||
updateAll(player);
|
updateAll(player);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
refreshPlayerCursorItem(player, clickResult.getCursor());
|
this.cursorPlayersItem.put(player, clickResult.getCursor());
|
||||||
updateAll(player); // FIXME: currently not properly client-predicted
|
updateAll(player); // FIXME: currently not properly client-predicted
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isClickInWindow(int slot) {
|
||||||
|
return slot < getSize();
|
||||||
|
}
|
||||||
|
|
||||||
private void updateAll(Player player) {
|
private void updateAll(Player player) {
|
||||||
player.getInventory().update();
|
player.getInventory().update();
|
||||||
update(player);
|
update(player);
|
||||||
|
@ -3,8 +3,6 @@ package net.minestom.server.inventory;
|
|||||||
import net.minestom.server.entity.EquipmentSlot;
|
import net.minestom.server.entity.EquipmentSlot;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.event.EventDispatcher;
|
import net.minestom.server.event.EventDispatcher;
|
||||||
import net.minestom.server.event.GlobalHandles;
|
|
||||||
import net.minestom.server.event.inventory.PlayerInventoryItemChangeEvent;
|
|
||||||
import net.minestom.server.event.item.EntityEquipEvent;
|
import net.minestom.server.event.item.EntityEquipEvent;
|
||||||
import net.minestom.server.inventory.click.ClickType;
|
import net.minestom.server.inventory.click.ClickType;
|
||||||
import net.minestom.server.inventory.click.InventoryClickResult;
|
import net.minestom.server.inventory.click.InventoryClickResult;
|
||||||
@ -169,11 +167,6 @@ public non-sealed class PlayerInventory extends AbstractInventory implements Equ
|
|||||||
sendSlotRefresh((short) convertToPacketSlot(slot), itemStack);
|
sendSlotRefresh((short) convertToPacketSlot(slot), itemStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void callItemChangeEvent(int slot, @NotNull ItemStack previous, @NotNull ItemStack current) {
|
|
||||||
GlobalHandles.PLAYER_INVENTORY_ITEM_CHANGE_EVENT.call(new PlayerInventoryItemChangeEvent(player, slot, previous, current));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes an inventory slot.
|
* Refreshes an inventory slot.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user