Force volatile read for inventory contents

This commit is contained in:
themode 2021-12-22 10:51:25 +01:00 committed by TheMode
parent 3ffe37591b
commit f0cf2d946c
3 changed files with 28 additions and 59 deletions

View File

@ -1,5 +1,8 @@
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.condition.InventoryCondition;
import net.minestom.server.item.ItemStack;
@ -9,9 +12,10 @@ import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -24,6 +28,8 @@ import java.util.function.UnaryOperator;
public sealed abstract class AbstractInventory implements InventoryClickHandler, TagHandler
permits Inventory, PlayerInventory {
private static final VarHandle ITEM_UPDATER = MethodHandles.arrayElementVarHandle(ItemStack[].class);
private final int size;
protected final ItemStack[] itemStacks;
@ -38,7 +44,6 @@ public sealed abstract class AbstractInventory implements InventoryClickHandler,
protected AbstractInventory(int size) {
this.size = size;
this.itemStacks = new ItemStack[getSize()];
Arrays.fill(itemStacks, ItemStack.AIR);
}
@ -62,7 +67,6 @@ public sealed abstract class AbstractInventory implements InventoryClickHandler,
*
* @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
*/
protected final void safeItemInsert(int slot, @NotNull ItemStack itemStack) {
@ -76,13 +80,15 @@ public sealed abstract class AbstractInventory implements InventoryClickHandler,
previous = itemStacks[slot];
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 callItemChangeEvent(int slot, @NotNull ItemStack previous, @NotNull ItemStack current);
public synchronized <T> @NotNull T processItemStack(@NotNull ItemStack itemStack,
@NotNull TransactionType type,
@NotNull TransactionOption<T> option) {
@ -172,7 +178,7 @@ public sealed abstract class AbstractInventory implements InventoryClickHandler,
* @return the item in the slot {@code slot}
*/
public @NotNull ItemStack getItemStack(int slot) {
return itemStacks[slot];
return (ItemStack) ITEM_UPDATER.getVolatile(itemStacks, slot);
}
/**

View File

@ -3,8 +3,6 @@ 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.event.GlobalHandles;
import net.minestom.server.event.inventory.InventoryItemChangeEvent;
import net.minestom.server.inventory.click.ClickType;
import net.minestom.server.inventory.click.InventoryClickResult;
import net.minestom.server.item.ItemStack;
@ -72,8 +70,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
*
* @return the inventory type
*/
@NotNull
public InventoryType getInventoryType() {
public @NotNull InventoryType getInventoryType() {
return inventoryType;
}
@ -82,8 +79,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
*
* @return the inventory title
*/
@NotNull
public Component getTitle() {
public @NotNull Component getTitle() {
return title;
}
@ -124,9 +120,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
*/
@Override
public void update() {
for (Player player : viewers) {
player.sendPacket(createNewWindowItemsPacket(player));
}
this.viewers.forEach(p -> p.sendPacket(createNewWindowItemsPacket(p)));
}
/**
@ -141,9 +135,8 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
player.sendPacket(createNewWindowItemsPacket(player));
}
@NotNull
@Override
public Set<Player> getViewers() {
public @NotNull Set<Player> getViewers() {
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
* @return the player cursor item, air item if the player is not a viewer
*/
@NotNull
public ItemStack getCursorItem(@NotNull Player player) {
public @NotNull ItemStack getCursorItem(@NotNull Player player) {
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));
}
@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) {
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));
}
/**
* 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
public boolean leftClick(@NotNull Player player, int slot) {
final PlayerInventory playerInventory = player.getInventory();
@ -269,7 +235,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
} else {
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);
return true;
}
@ -292,7 +258,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
} else {
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);
return true;
}
@ -319,7 +285,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
playerInventory.setItemStack(clickSlot, clickResult.getClicked());
}
updateAll(player); // FIXME: currently not properly client-predicted
refreshPlayerCursorItem(player, clickResult.getCursor());
this.cursorPlayersItem.put(player, clickResult.getCursor());
return true;
}
@ -376,7 +342,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
playerInventory.setItemStack(clickSlot, resultClicked);
}
}
refreshPlayerCursorItem(player, clickResult.getCursor());
this.cursorPlayersItem.put(player, clickResult.getCursor());
return true;
}
@ -397,7 +363,7 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
updateAll(player);
return false;
}
refreshPlayerCursorItem(player, clickResult.getCursor());
this.cursorPlayersItem.put(player, clickResult.getCursor());
updateAll(player); // FIXME: currently not properly client-predicted
return true;
}
@ -417,11 +383,15 @@ public non-sealed class Inventory extends AbstractInventory implements Viewable
updateAll(player);
return false;
}
refreshPlayerCursorItem(player, clickResult.getCursor());
this.cursorPlayersItem.put(player, clickResult.getCursor());
updateAll(player); // FIXME: currently not properly client-predicted
return true;
}
private boolean isClickInWindow(int slot) {
return slot < getSize();
}
private void updateAll(Player player) {
player.getInventory().update();
update(player);

View File

@ -3,8 +3,6 @@ package net.minestom.server.inventory;
import net.minestom.server.entity.EquipmentSlot;
import net.minestom.server.entity.Player;
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.inventory.click.ClickType;
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);
}
@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.
*