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; 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);
} }
/** /**

View File

@ -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);

View File

@ -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.
* *