diff --git a/common/src/main/java/net/onelitefeather/cygnus/common/ListenerHandling.java b/common/src/main/java/net/onelitefeather/cygnus/common/ListenerHandling.java index 88bdcb60..728dde3f 100644 --- a/common/src/main/java/net/onelitefeather/cygnus/common/ListenerHandling.java +++ b/common/src/main/java/net/onelitefeather/cygnus/common/ListenerHandling.java @@ -2,12 +2,14 @@ import net.minestom.server.event.Event; import net.minestom.server.event.EventNode; +import net.minestom.server.event.inventory.InventoryPreClickEvent; import net.minestom.server.event.item.ItemDropEvent; import net.minestom.server.event.player.PlayerBlockBreakEvent; import net.minestom.server.event.player.PlayerBlockInteractEvent; import net.minestom.server.event.player.PlayerBlockPlaceEvent; import net.minestom.server.event.player.PlayerSwapItemEvent; import net.minestom.server.event.trait.CancellableEvent; +import net.minestom.server.inventory.PlayerInventory; import java.util.function.Consumer; @@ -35,4 +37,18 @@ default void registerCancelListener(EventNode eventNode) { eventNode.addListener(PlayerSwapItemEvent.class, CANCELLABLE_EVENT::accept); eventNode.addListener(PlayerBlockInteractEvent.class, CANCELLABLE_EVENT::accept); } + + /** + * Keeps players from moving the items in their own inventory, so the game items stay in the + * slots the game put them in. Clicks in an opened inventory, like the spectator menu, are left + * to that inventory. Not part of {@link #registerCancelListener(EventNode)}, the setup needs + * its players to move their items. + * + * @param eventNode the event node to register the listener + */ + default void registerInventoryLock(EventNode eventNode) { + eventNode.addListener(InventoryPreClickEvent.class, event -> { + if (event.getInventory() instanceof PlayerInventory) event.setCancelled(true); + }); + } } diff --git a/common/src/test/java/net/onelitefeather/cygnus/common/ListenerHandlingTest.java b/common/src/test/java/net/onelitefeather/cygnus/common/ListenerHandlingTest.java index c5b02a80..1da1f0a0 100644 --- a/common/src/test/java/net/onelitefeather/cygnus/common/ListenerHandlingTest.java +++ b/common/src/test/java/net/onelitefeather/cygnus/common/ListenerHandlingTest.java @@ -1,11 +1,20 @@ package net.onelitefeather.cygnus.common; +import net.kyori.adventure.text.Component; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.entity.Player; +import net.minestom.server.event.EventDispatcher; import net.minestom.server.event.GlobalEventHandler; +import net.minestom.server.event.inventory.InventoryPreClickEvent; import net.minestom.server.event.item.ItemDropEvent; import net.minestom.server.event.player.PlayerBlockBreakEvent; import net.minestom.server.event.player.PlayerBlockInteractEvent; import net.minestom.server.event.player.PlayerBlockPlaceEvent; import net.minestom.server.event.player.PlayerSwapItemEvent; +import net.minestom.server.instance.Instance; +import net.minestom.server.inventory.Inventory; +import net.minestom.server.inventory.InventoryType; +import net.minestom.server.inventory.click.Click; import net.minestom.testing.Env; import net.minestom.testing.extension.MicrotusExtension; import org.jetbrains.annotations.NotNull; @@ -28,5 +37,25 @@ void testCancelListenerCreation(@NotNull Env env) { assertTrue(eventHandler.hasListener(ItemDropEvent.class)); assertTrue(eventHandler.hasListener(PlayerSwapItemEvent.class)); assertTrue(eventHandler.hasListener(PlayerBlockInteractEvent.class)); + assertFalse(eventHandler.hasListener(InventoryPreClickEvent.class)); + } + + @Test + void testOnlyOwnInventoryClicksAreCancelled(@NotNull Env env) { + ListenerHandling listenerHandling = new ListenerHandling() {}; + GlobalEventHandler eventHandler = env.process().eventHandler(); + listenerHandling.registerInventoryLock(eventHandler); + Instance instance = env.createFlatInstance(); + Player player = env.createPlayer(instance, new Pos(0, 40, 0)); + + InventoryPreClickEvent own = new InventoryPreClickEvent(player.getInventory(), player, new Click.Left(0)); + InventoryPreClickEvent opened = new InventoryPreClickEvent( + new Inventory(InventoryType.CHEST_1_ROW, Component.text("Menu")), player, new Click.Left(0)); + EventDispatcher.call(own); + EventDispatcher.call(opened); + + assertTrue(own.isCancelled()); + assertFalse(opened.isCancelled()); + env.destroyInstance(instance, true); } } \ No newline at end of file diff --git a/game/src/main/java/net/onelitefeather/cygnus/Cygnus.java b/game/src/main/java/net/onelitefeather/cygnus/Cygnus.java index 60e0ac69..cae19f13 100644 --- a/game/src/main/java/net/onelitefeather/cygnus/Cygnus.java +++ b/game/src/main/java/net/onelitefeather/cygnus/Cygnus.java @@ -86,7 +86,6 @@ import net.onelitefeather.cygnus.phase.RestartPhase; import net.onelitefeather.cygnus.phase.WaitingPhase; import net.onelitefeather.cygnus.player.CygnusPlayer; -import net.onelitefeather.cygnus.player.InventoryLock; import net.onelitefeather.cygnus.possession.PossessionFeature; import net.onelitefeather.cygnus.resourcepack.ResourcePackService; import net.onelitefeather.cygnus.sanity.SanityService; @@ -234,7 +233,6 @@ sanityService, new HuntedTabWitness(new TracingCreekWitness(sanityService, this. MinecraftServer.getConnectionManager()::getOnlinePlayerByUuid), this.jumpscareManager, this.staminaService, tickSections); this.features = Stream.concat(this.resourcePackService.stream(), Stream.of( new EpilepsyDisclaimer(), - new InventoryLock(), this.slenderTakeover, this.spectatorService, // Not part of the OverlayModule: the sound is the feedback a hit owes the player @@ -336,6 +334,7 @@ private void initListener(Team spectatorTeam) { this.scoreboardDisplay.sync(this.teamService); }); registerCancelListener(manager); + registerInventoryLock(manager); } private void registerGameListener() { diff --git a/game/src/main/java/net/onelitefeather/cygnus/player/InventoryLock.java b/game/src/main/java/net/onelitefeather/cygnus/player/InventoryLock.java deleted file mode 100644 index dc9ba3fc..00000000 --- a/game/src/main/java/net/onelitefeather/cygnus/player/InventoryLock.java +++ /dev/null @@ -1,58 +0,0 @@ -package net.onelitefeather.cygnus.player; - -import net.minestom.server.entity.GameMode; -import net.minestom.server.entity.Player; -import net.minestom.server.event.Event; -import net.minestom.server.event.EventNode; -import net.minestom.server.event.inventory.InventoryPreClickEvent; -import net.minestom.server.event.item.ItemDropEvent; -import net.minestom.server.event.player.PlayerSwapItemEvent; -import net.minestom.server.inventory.PlayerInventory; -import net.onelitefeather.cygnus.GameFeature; - -/** - * Keeps the items the game hands out where the game put them. - *

- * Players may neither drop them, nor move them around in their own inventory, nor swap them into - * the offhand. A dropped item would not even land on the ground, Minestom only takes it out of the - * inventory, and the SlenderEye or the tracker would be gone for the rest of the round. Clicks in - * an opened inventory, like the spectator menu, are left to that inventory. Creative players stay - * free, so builders can still work. - *

- * - * @author theEvilReaper - * @version 1.0.0 - * @since 2.16.0 - */ -public final class InventoryLock implements GameFeature { - - private final EventNode node = EventNode.all("inventory-lock"); - - /** - * Creates the lock. - */ - public InventoryLock() { - this.node.addListener(ItemDropEvent.class, event -> event.setCancelled(isLocked(event.getPlayer()))); - this.node.addListener(PlayerSwapItemEvent.class, event -> event.setCancelled(isLocked(event.getPlayer()))); - this.node.addListener(InventoryPreClickEvent.class, event -> { - if (event.getInventory() instanceof PlayerInventory && isLocked(event.getPlayer())) { - event.setCancelled(true); - } - }); - } - - @Override - public EventNode node() { - return this.node; - } - - /** - * Tells whether a player's inventory is locked. - * - * @param player the player - * @return {@code true} for everyone outside creative mode - */ - static boolean isLocked(Player player) { - return player.getGameMode() != GameMode.CREATIVE; - } -} diff --git a/game/src/test/java/net/onelitefeather/cygnus/player/InventoryLockTest.java b/game/src/test/java/net/onelitefeather/cygnus/player/InventoryLockTest.java deleted file mode 100644 index 447e8866..00000000 --- a/game/src/test/java/net/onelitefeather/cygnus/player/InventoryLockTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package net.onelitefeather.cygnus.player; - -import net.kyori.adventure.text.Component; -import net.minestom.server.coordinate.Pos; -import net.minestom.server.entity.GameMode; -import net.minestom.server.entity.Player; -import net.minestom.server.event.EventDispatcher; -import net.minestom.server.event.inventory.InventoryPreClickEvent; -import net.minestom.server.event.item.ItemDropEvent; -import net.minestom.server.event.player.PlayerSwapItemEvent; -import net.minestom.server.instance.Instance; -import net.minestom.server.inventory.Inventory; -import net.minestom.server.inventory.InventoryType; -import net.minestom.server.inventory.click.Click; -import net.minestom.server.item.ItemStack; -import net.minestom.server.item.Material; -import net.minestom.testing.Env; -import net.onelitefeather.cygnus.CygnusPlayerTestBase; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -class InventoryLockTest extends CygnusPlayerTestBase { - - private static final ItemStack ITEM = ItemStack.of(Material.ENDER_EYE); - - private static InventoryLock lock(Env env) { - InventoryLock lock = new InventoryLock(); - env.process().eventHandler().addChild(lock.node()); - return lock; - } - - @Test - @DisplayName("Dropping, moving and swapping into the offhand are cancelled") - void locked(Env env) { - Instance instance = env.createFlatInstance(); - Player player = env.createPlayer(instance, new Pos(0, 40, 0)); - player.setGameMode(GameMode.ADVENTURE); - InventoryLock lock = lock(env); - - ItemDropEvent drop = new ItemDropEvent(player, ITEM); - PlayerSwapItemEvent swap = new PlayerSwapItemEvent(player, ITEM, ItemStack.AIR); - InventoryPreClickEvent click = new InventoryPreClickEvent(player.getInventory(), player, new Click.Left(0)); - EventDispatcher.call(drop); - EventDispatcher.call(swap); - EventDispatcher.call(click); - - assertTrue(drop.isCancelled()); - assertTrue(swap.isCancelled()); - assertTrue(click.isCancelled()); - env.process().eventHandler().removeChild(lock.node()); - env.destroyInstance(instance, true); - } - - @Test - @DisplayName("Clicks in an opened inventory are left to that inventory") - void openedInventoryIsFree(Env env) { - Instance instance = env.createFlatInstance(); - Player player = env.createPlayer(instance, new Pos(0, 40, 0)); - InventoryLock lock = lock(env); - - InventoryPreClickEvent click = new InventoryPreClickEvent( - new Inventory(InventoryType.CHEST_1_ROW, Component.text("Menu")), player, new Click.Left(0)); - EventDispatcher.call(click); - - assertFalse(click.isCancelled()); - env.process().eventHandler().removeChild(lock.node()); - env.destroyInstance(instance, true); - } - - @Test - @DisplayName("Creative players stay free") - void creativeIsFree(Env env) { - Instance instance = env.createFlatInstance(); - Player player = env.createPlayer(instance, new Pos(0, 40, 0)); - player.setGameMode(GameMode.CREATIVE); - InventoryLock lock = lock(env); - - ItemDropEvent drop = new ItemDropEvent(player, ITEM); - EventDispatcher.call(drop); - - assertFalse(drop.isCancelled()); - env.process().eventHandler().removeChild(lock.node()); - env.destroyInstance(instance, true); - } -}