From 5f6b91bc640d57576f6ba6dc3455179cc117e015 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 28 May 2023 12:11:27 -0700 Subject: [PATCH 01/36] Version 1.23.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f857231fa..a34a84405 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ -LOCAL - 1.23.2 + 1.23.3 bentobox-world https://sonarcloud.io ${project.basedir}/lib From 7bb1907897643f922b3bd58ccc9c7846ab3b753b Mon Sep 17 00:00:00 2001 From: tastybento Date: Tue, 6 Jun 2023 18:41:06 -0700 Subject: [PATCH 02/36] Version 1.24.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a34a84405..2256c7bfe 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ -LOCAL - 1.23.3 + 1.24.0 bentobox-world https://sonarcloud.io ${project.basedir}/lib From e4a38915900dd0eefe46ebe6bdb1bb41cdea4e94 Mon Sep 17 00:00:00 2001 From: tastybento Date: Tue, 6 Jun 2023 20:38:59 -0700 Subject: [PATCH 03/36] Add new IslandNameEvent and test class for command --- .../commands/island/IslandSetnameCommand.java | 13 +- .../api/events/island/IslandEvent.java | 24 +- .../api/events/island/IslandNameEvent.java | 44 +++ .../island/IslandSetnameCommandTest.java | 281 ++++++++++++++++++ 4 files changed, 360 insertions(+), 2 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/api/events/island/IslandNameEvent.java create mode 100644 src/test/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommandTest.java diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommand.java index c74349d20..f8b66f38e 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommand.java @@ -6,6 +6,7 @@ import java.util.Objects; import org.bukkit.ChatColor; import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; @@ -93,8 +94,18 @@ public class IslandSetnameCommand extends CompositeCommand { } // Everything's good! - Objects.requireNonNull(getIslands().getIsland(getWorld(), user)).setName(name); + Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), user)); + String previousName = island.getName(); + island.setName(name); user.sendMessage("commands.island.setname.success", TextVariables.NAME, name); + // Fire the IslandNameEvent + new IslandEvent.IslandEventBuilder() + .island(island) + .involvedPlayer(user.getUniqueId()) + .reason(IslandEvent.Reason.NAME) + .previousName(previousName) + .admin(false) + .build(); return true; } } diff --git a/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java b/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java index 701d553d3..45d190c2d 100644 --- a/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java +++ b/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java @@ -7,6 +7,7 @@ import org.bukkit.Location; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle; @@ -169,7 +170,12 @@ public class IslandEvent extends IslandBaseEvent { * Event that will fire any time a player's rank changes on an island. * @since 1.13.0 */ - RANK_CHANGE + RANK_CHANGE, + /** + * Event that will fire when an island is named or renamed + * @since 1.24.0 + */ + NAME } public static IslandEventBuilder builder() { @@ -213,6 +219,10 @@ public class IslandEvent extends IslandBaseEvent { * @since 1.13.0 */ private int newRank; + /** + * @since 1.24.0 Previous name of island + */ + private String previousName; public IslandEventBuilder island(Island island) { this.island = island; @@ -305,6 +315,16 @@ public class IslandEvent extends IslandBaseEvent { return this; } + /** + * Sets the previous name of the island + * @param previousName previous name. May be null. + * @since 1.24.0 + */ + public IslandEventBuilder previousName(@Nullable String previousName) { + this.previousName = previousName; + return this; + } + private IslandBaseEvent getEvent() { return switch (reason) { case EXPEL -> new IslandExpelEvent(island, player, admin, location); @@ -329,6 +349,7 @@ public class IslandEvent extends IslandBaseEvent { case RESERVED -> new IslandReservedEvent(island, player, admin, location); case RANK_CHANGE -> new IslandRankChangeEvent(island, player, admin, location, oldRank, newRank); case NEW_ISLAND -> new IslandNewIslandEvent(island, player, admin, location); + case NAME -> new IslandNameEvent(island, player, admin, location, previousName); default -> new IslandGeneralEvent(island, player, admin, location); }; } @@ -345,5 +366,6 @@ public class IslandEvent extends IslandBaseEvent { Bukkit.getPluginManager().callEvent(newEvent); return newEvent; } + } } diff --git a/src/main/java/world/bentobox/bentobox/api/events/island/IslandNameEvent.java b/src/main/java/world/bentobox/bentobox/api/events/island/IslandNameEvent.java new file mode 100644 index 000000000..81b02e1f2 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/events/island/IslandNameEvent.java @@ -0,0 +1,44 @@ +package world.bentobox.bentobox.api.events.island; + +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.event.HandlerList; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +import world.bentobox.bentobox.api.events.IslandBaseEvent; +import world.bentobox.bentobox.database.objects.Island; + +/** + * Fired when an a player names or renames an island. + * Cancellation has no effect. + */ +public class IslandNameEvent extends IslandBaseEvent { + + private final String previousName; + private static final HandlerList handlers = new HandlerList(); + + @Override + public @NonNull HandlerList getHandlers() { + return getHandlerList(); + } + + public static HandlerList getHandlerList() { + return handlers; + } + + public IslandNameEvent(Island island, UUID player, boolean admin, Location location, @Nullable String previousName) { + // Final variables have to be declared in the constructor + super(island, player, admin, location); + this.previousName = previousName; + } + + /** + * @return the previous name of the island, if any. May be null if no name previously used. + */ + @Nullable + public String getPreviousNameIsland() { + return previousName; + } +} \ No newline at end of file diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommandTest.java new file mode 100644 index 000000000..bdc919f7d --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommandTest.java @@ -0,0 +1,281 @@ +/** + * + */ +package world.bentobox.bentobox.api.commands.island; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.plugin.PluginManager; +import org.eclipse.jdt.annotation.NonNull; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.Settings; +import world.bentobox.bentobox.api.addons.Addon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.managers.LocalesManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; +import world.bentobox.bentobox.managers.PlayersManager; +import world.bentobox.bentobox.managers.RanksManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class}) +public class IslandSetnameCommandTest { + + @Mock + private CompositeCommand ic; + private UUID uuid; + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private PlayersManager pm; + @Mock + private Island island; + @Mock + private Addon addon; + + private IslandSetnameCommand isc; + @Mock + private @NonNull World world; + private RanksManager rm; + private Settings settings; + @Mock + private PluginManager pim; + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + User.setPlugin(plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Settings + settings = new Settings(); + when(plugin.getSettings()).thenReturn(settings); + + // User + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + Player p = mock(Player.class); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + when(user.getDisplayName()).thenReturn("&Ctastybento"); + when(user.getPermissionValue(anyString(), anyInt())).thenReturn(-1); + when(user.getTranslation(any())).thenAnswer(invocation -> invocation.getArgument(0, String.class)); + + // Parent command has no aliases + when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); + when(ic.getWorld()).thenReturn(world); + + // IWM friendly name + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + when(plugin.getIWM()).thenReturn(iwm); + + // Player has island to begin with + when(im.getIsland(world, user)).thenReturn(island); + when(plugin.getIslands()).thenReturn(im); + when(island.getName()).thenReturn("previous-name"); + + // Server and Plugin Manager for events + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + when(Bukkit.getPluginManager()).thenReturn(pim); + + // Locales + LocalesManager lm = mock(LocalesManager.class); + when(lm.get(any(), any())).thenAnswer(invocation -> invocation.getArgument(1, String.class)); + when(plugin.getLocalesManager()).thenReturn(lm); + PlaceholdersManager phm = mock(PlaceholdersManager.class); + when(phm.replacePlaceholders(any(), any())).thenAnswer(invocation -> invocation.getArgument(1, String.class)); + // Placeholder manager + when(plugin.getPlaceholdersManager()).thenReturn(phm); + + // Ranks Manager + rm = new RanksManager(); + when(plugin.getRanksManager()).thenReturn(rm); + + + // Test + isc = new IslandSetnameCommand(ic); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + User.clearUsers(); + Mockito.framework().clearInlineMocks(); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#IslandSetnameCommand(world.bentobox.bentobox.api.commands.CompositeCommand)}. + */ + @Test + public void testIslandSetnameCommand() { + assertEquals("setname", isc.getLabel()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#setup()}. + */ + @Test + public void testSetup() { + assertTrue(isc.isOnlyPlayer()); + assertEquals("commands.island.setname.parameters", isc.getParameters()); + assertEquals("commands.island.setname.description", isc.getDescription()); + assertEquals("island.name", isc.getPermission()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNoArgs() { + assertFalse(isc.canExecute(user, isc.getLabel(), new ArrayList<>())); + verify(user).sendMessage("commands.help.header", "[label]", "BSkyBlock"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNoIsland() { + when(im.getIsland(world, user)).thenReturn(null); + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("name"))); + verify(user).sendMessage("general.errors.no-island"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTooLowRank() { + when(island.getRank(any(User.class))).thenReturn(RanksManager.MEMBER_RANK); + when(island.getRankCommand(anyString())).thenReturn(RanksManager.OWNER_RANK); + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("name"))); + verify(user).sendMessage("general.errors.insufficient-rank", TextVariables.RANK, "ranks.member"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNameTooShort() { + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("x"))); + verify(user).sendMessage("commands.island.setname.name-too-short", TextVariables.NUMBER, "4"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNameOnlyColors() { + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("§b§c§d§e"))); + verify(user).sendMessage("commands.island.setname.name-too-short", TextVariables.NUMBER, "4"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNameTooLong() { + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("This is a very long name that is not allowed and will have to be prevented"))); + verify(user).sendMessage("commands.island.setname.name-too-long", TextVariables.NUMBER, "20"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNameNotUnique() { + settings.setNameUniqueness(true); + when(im.nameExists(eq(world), anyString())).thenReturn(true); + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("name2"))); + verify(user).sendMessage("commands.island.setname.name-already-exists"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandNameApplyColors() { + when(user.hasPermission(anyString())).thenReturn(true); + settings.setNameUniqueness(true); + when(im.nameExists(world, "name§b")).thenReturn(true); + assertFalse(isc.canExecute(user, isc.getLabel(), List.of("name&b"))); + verify(user).sendMessage("commands.island.setname.name-already-exists"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testIslandSetnameCommandAllOK() { + assertTrue(isc.canExecute(user, isc.getLabel(), List.of("name-okay"))); + verify(user, never()).sendMessage(anyString()); + } + + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandSetnameCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfString() { + when(user.hasPermission(anyString())).thenReturn(true); + assertTrue(isc.execute(user, isc.getLabel(), List.of("name-okay"))); + verify(island).setName("name-okay"); + verify(user).sendMessage("commands.island.setname.success", TextVariables.NAME, "name-okay"); + verify(pim, times(2)).callEvent(any()); + } + +} From 6932ce375902f1ac70df6bf65a0523f1de5d0833 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 10 Jun 2023 10:03:17 -0700 Subject: [PATCH 04/36] Added support for 1.20 --- pom.xml | 4 ++-- .../nms/{v1_19_R3 => v1_20_R1}/PasteHandlerImpl.java | 6 +++--- .../nms/{v1_19_R3 => v1_20_R1}/WorldRegeneratorImpl.java | 6 +++--- .../bentobox/bentobox/versions/ServerCompatibility.java | 4 ++++ 4 files changed, 12 insertions(+), 8 deletions(-) rename src/main/java/world/bentobox/bentobox/nms/{v1_19_R3 => v1_20_R1}/PasteHandlerImpl.java (95%) rename src/main/java/world/bentobox/bentobox/nms/{v1_19_R3 => v1_20_R1}/WorldRegeneratorImpl.java (87%) diff --git a/pom.xml b/pom.xml index 2256c7bfe..1f2a80388 100644 --- a/pom.xml +++ b/pom.xml @@ -73,10 +73,10 @@ 42.2.18 5.0.1 - 1.19.4-R0.1-SNAPSHOT + 1.20-R0.1-SNAPSHOT - 1.19.3-R0.1-SNAPSHOT + 1.20-R0.1-SNAPSHOT 3.0.0 1.7.1 2.10.9 diff --git a/src/main/java/world/bentobox/bentobox/nms/v1_19_R3/PasteHandlerImpl.java b/src/main/java/world/bentobox/bentobox/nms/v1_20_R1/PasteHandlerImpl.java similarity index 95% rename from src/main/java/world/bentobox/bentobox/nms/v1_19_R3/PasteHandlerImpl.java rename to src/main/java/world/bentobox/bentobox/nms/v1_20_R1/PasteHandlerImpl.java index 14af30231..44c23adb9 100644 --- a/src/main/java/world/bentobox/bentobox/nms/v1_19_R3/PasteHandlerImpl.java +++ b/src/main/java/world/bentobox/bentobox/nms/v1_20_R1/PasteHandlerImpl.java @@ -1,4 +1,4 @@ -package world.bentobox.bentobox.nms.v1_19_R3; +package world.bentobox.bentobox.nms.v1_20_R1; import java.util.List; import java.util.Map; @@ -11,8 +11,8 @@ import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; -import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData; import net.minecraft.core.BlockPosition; import net.minecraft.world.level.block.state.IBlockData; diff --git a/src/main/java/world/bentobox/bentobox/nms/v1_19_R3/WorldRegeneratorImpl.java b/src/main/java/world/bentobox/bentobox/nms/v1_20_R1/WorldRegeneratorImpl.java similarity index 87% rename from src/main/java/world/bentobox/bentobox/nms/v1_19_R3/WorldRegeneratorImpl.java rename to src/main/java/world/bentobox/bentobox/nms/v1_20_R1/WorldRegeneratorImpl.java index 2d1aecc76..c1bd78391 100644 --- a/src/main/java/world/bentobox/bentobox/nms/v1_19_R3/WorldRegeneratorImpl.java +++ b/src/main/java/world/bentobox/bentobox/nms/v1_20_R1/WorldRegeneratorImpl.java @@ -1,10 +1,10 @@ -package world.bentobox.bentobox.nms.v1_19_R3; +package world.bentobox.bentobox.nms.v1_20_R1; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; -import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData; import net.minecraft.core.BlockPosition; import net.minecraft.world.level.World; diff --git a/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java b/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java index 50c58f533..5fbec7ebe 100644 --- a/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java +++ b/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java @@ -218,6 +218,10 @@ public class ServerCompatibility { * @since 1.22.1 */ V1_19_4(Compatibility.COMPATIBLE), + /** + * @since 1.24.0 + */ + V1_20(Compatibility.COMPATIBLE), ; private final Compatibility compatibility; From d25d1713f8da306aa4060e46d33845a86288a794 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 10 Jun 2023 15:49:10 -0700 Subject: [PATCH 05/36] Adds protection for sign editing for 1.20 Fixes #2135 --- .../flags/protection/BlockInteractionListener.java | 5 +++++ .../java/world/bentobox/bentobox/lists/Flags.java | 11 ++++++++--- src/main/resources/locales/en-US.yml | 6 ++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java index 2dbd511a3..801968894 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java @@ -155,6 +155,11 @@ public class BlockInteractionListener extends FlagListener return; } + if (Tag.SIGNS.isTagged(type)) { + this.checkIsland(e, player, loc, Flags.SIGN_EDITING); + return; + } + if (Tag.FENCE_GATES.isTagged(type)) { this.checkIsland(e, player, loc, Flags.GATE); diff --git a/src/main/java/world/bentobox/bentobox/lists/Flags.java b/src/main/java/world/bentobox/bentobox/lists/Flags.java index 4f74ca46e..3fd2ac9f7 100644 --- a/src/main/java/world/bentobox/bentobox/lists/Flags.java +++ b/src/main/java/world/bentobox/bentobox/lists/Flags.java @@ -653,16 +653,21 @@ public final class Flags { * Controls who gets to harvest any crop related contents. e.g. Wheat, Sugar Cane, melon blocks, not stems, pumpkin blocks, etc. * @since 1.23.0 */ - public static final Flag HARVEST = new Flag.Builder("HARVEST", Material.PUMPKIN).type(Type.PROTECTION).build(); + public static final Flag HARVEST = new Flag.Builder("HARVEST", Material.PUMPKIN).mode(Flag.Mode.BASIC).type(Type.PROTECTION).build(); /** * Crop Planting * Controls who gets to plant crops on tilled soil. * @since 1.23.0 */ - public static final Flag CROP_PLANTING = new Flag.Builder("CROP_PLANTING", Material.PUMPKIN_SEEDS).type(Type.PROTECTION).build(); + public static final Flag CROP_PLANTING = new Flag.Builder("CROP_PLANTING", Material.PUMPKIN_SEEDS).mode(Flag.Mode.BASIC).type(Type.PROTECTION).build(); + + /** + * Sign edit protection + * @since 1.24.0 + */ + public static final Flag SIGN_EDITING = new Flag.Builder("SIGN_EDITING", Material.DARK_OAK_SIGN).mode(Flag.Mode.BASIC).type(Type.PROTECTION).build(); - /** * Provides a list of all the Flag instances contained in this class using reflection. * Deprecated Flags are ignored. diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 7fead2e3b..dbddac83a 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -1325,6 +1325,12 @@ protection: &a activation. name: "Sculk Shrieker" hint: "sculk shrieker activation is disabled" + SIGN_EDITING: + description: |- + &a Allows text editing + &a of signs + name: "Sign Editing" + hint: "sign editing is disabled" TNT_DAMAGE: description: |- &a Allow TNT and TNT minecarts From 2672ee5d1bd0f57f94cfc723332facd72a71baf5 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 10 Jun 2023 21:31:39 -0700 Subject: [PATCH 06/36] Handle Boats and other Materials for 1.20. Uses Tags a lot. May break compatibility with older server versions. --- .../blueprints/BlueprintClipboard.java | 7 +- .../dataobjects/BlueprintBlock.java | 67 ++++++++++++++ .../protection/BlockInteractionListener.java | 40 ++------- .../flags/protection/PlaceBlocksListener.java | 5 +- .../bentobox/managers/IslandsManager.java | 87 ++++++++----------- .../bentobox/nms/CopyWorldRegenerator.java | 85 ++++++++++-------- .../bentobox/util/DefaultPasteUtil.java | 23 +++-- .../listeners/flags/AbstractCommonSetup.java | 28 +++++- .../protection/PlaceBlocksListenerTest.java | 9 +- .../IslandRespawnListenerTest.java | 9 +- .../worldsettings/RemoveMobsListenerTest.java | 7 +- .../BlueprintClipboardManagerTest.java | 1 - .../managers/BlueprintsManagerTest.java | 1 - .../managers/IslandWorldManagerTest.java | 2 + .../bentobox/managers/IslandsManagerTest.java | 33 ++++--- 15 files changed, 238 insertions(+), 166 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java index c0dff17bc..dd035adc3 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java @@ -18,6 +18,7 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.CreatureSpawner; import org.bukkit.block.Sign; +import org.bukkit.block.sign.Side; import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.Ageable; import org.bukkit.entity.ChestedHorse; @@ -221,8 +222,10 @@ public class BlueprintClipboard { // Signs if (blockState instanceof Sign sign) { - b.setSignLines(Arrays.asList(sign.getLines())); - b.setGlowingText(sign.isGlowingText()); + for (Side side : Side.values()) { + b.setSignLines(side, Arrays.asList(sign.getSide(side).getLines())); + b.setGlowingText(side, sign.getSide(side).isGlowingText()); + } } // Set block data if (blockState.getData() instanceof Attachable) { diff --git a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java index 7b2584087..f7b9e5a4e 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBlock.java @@ -6,6 +6,7 @@ import java.util.Map; import org.bukkit.block.Biome; import org.bukkit.block.banner.Pattern; +import org.bukkit.block.sign.Side; import org.bukkit.inventory.ItemStack; import com.google.gson.annotations.Expose; @@ -21,6 +22,8 @@ public class BlueprintBlock { @Expose private List signLines; @Expose + private List signLines2; + @Expose private Map inventory; @Expose private BlueprintCreatureSpawner creatureSpawner; @@ -36,6 +39,8 @@ public class BlueprintBlock { private List bannerPatterns; @Expose private boolean glowingText; + @Expose + private boolean glowingText2; public BlueprintBlock(String blockData) { this.blockData = blockData; @@ -57,14 +62,20 @@ public class BlueprintBlock { /** * @return the signLines + * @deprecated signs now have two sides + * @since 1.24.0 */ + @Deprecated public List getSignLines() { return signLines; } /** * @param signLines the signLines to set + * @deprecated signs now have two sides + * @since 1.24.0 */ + @Deprecated public void setSignLines(List signLines) { this.signLines = signLines; } @@ -129,17 +140,73 @@ public class BlueprintBlock { /** * @return the glowingText + * @deprecated signs now have two sides + * @since 1.24.0 */ + @Deprecated public boolean isGlowingText() { return glowingText; } /** * @param glowingText the glowingText to set + * @deprecated signs now have two sides + * @since 1.24.0 */ + @Deprecated public void setGlowingText(boolean glowingText) { this.glowingText = glowingText; } + /** + * @param side side of sign + * @param glowingText the glowingText to set + * @since 1.24.0 + */ + public void setGlowingText(Side side, boolean glowingText) { + switch (side) { + case FRONT -> this.glowingText = glowingText; + default -> this.glowingText2 = glowingText; + }; + + } + + /** + * @param side side of sign + * @return the glowingText + * @since 1.24.0 + */ + public boolean isGlowingText(Side side) { + return switch (side) { + case FRONT -> glowingText; + default -> glowingText2; + }; + } + + /** + * @param side side of sign + * @return the signLines + * @since 1.24.0 + */ + public List getSignLines(Side side) { + return switch (side) { + case FRONT -> signLines; + default -> signLines2; + }; + } + + /** + * @param side side of sign + * @param signLines the signLines to set + * @since 1.24.0 + */ + public void setSignLines(Side side, List signLines) { + switch (side) { + case FRONT -> this.signLines = signLines; + default -> this.signLines2 = signLines; + }; + } + + } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java index 801968894..400bd8eb3 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java @@ -1,6 +1,5 @@ package world.bentobox.bentobox.listeners.flags.protection; -import java.util.Map; import java.util.Optional; import org.bukkit.FluidCollisionMode; @@ -18,8 +17,6 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockFromToEvent; import org.bukkit.event.player.PlayerInteractEvent; -import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.flags.Flag; import world.bentobox.bentobox.api.flags.FlagListener; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.lists.Flags; @@ -31,25 +28,6 @@ import world.bentobox.bentobox.lists.Flags; public class BlockInteractionListener extends FlagListener { - /** - * These cover materials in another server version. This avoids run time errors due to unknown enum values, at the - * expense of a string comparison - */ - private static final Map stringFlags; - private static final String CHEST = "CHEST"; - - static - { - stringFlags = Map.of( - "ACACIA_CHEST_BOAT", CHEST, - "BIRCH_CHEST_BOAT", CHEST, - "JUNGLE_CHEST_BOAT", CHEST, - "DARK_OAK_CHEST_BOAT", CHEST, - "MANGROVE_CHEST_BOAT", CHEST, - "OAK_CHEST_BOAT", CHEST, - "SPRUCE_CHEST_BOAT", CHEST); - } - /** * Handle interaction with blocks * @@ -71,7 +49,7 @@ public class BlockInteractionListener extends FlagListener if (e.getItem() != null && !e.getItem().getType().equals(Material.AIR)) { // Boats - if (e.getItem().getType().name().endsWith("BOAT")) + if (Tag.ITEMS_BOATS.isTagged(e.getItem().getType())) { this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.BOAT); } @@ -164,10 +142,11 @@ public class BlockInteractionListener extends FlagListener { this.checkIsland(e, player, loc, Flags.GATE); } - // TODO: 1.18 compatibility - // if (Tag.ITEMS_CHEST_BOATS.isTagged(type)) { - // this.checkIsland(e, player, loc, Flags.CHEST); - // } + + if (Tag.ITEMS_CHEST_BOATS.isTagged(type)) + { + this.checkIsland(e, player, loc, Flags.CHEST); + } switch (type) { @@ -237,12 +216,7 @@ public class BlockInteractionListener extends FlagListener } } default -> - { - if (stringFlags.containsKey(type.name())) - { - Optional f = BentoBox.getInstance().getFlagsManager().getFlag(stringFlags.get(type.name())); - f.ifPresent(flag -> this.checkIsland(e, player, loc, flag)); - } + { // nothing to do } } } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListener.java index 924e38fb7..b68487729 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListener.java @@ -3,6 +3,7 @@ package world.bentobox.bentobox.listeners.flags.protection; import java.util.Set; import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -44,7 +45,7 @@ public class PlaceBlocksListener extends FlagListener // Crops if (against.equals(Material.FARMLAND) && SEEDS.contains(e.getItemInHand().getType())) { this.checkIsland(e, e.getPlayer(), e.getBlock().getLocation(), Flags.CROP_PLANTING); - } else { + } else { this.checkIsland(e, e.getPlayer(), e.getBlock().getLocation(), Flags.PLACE_BLOCKS); } } @@ -124,7 +125,7 @@ public class PlaceBlocksListener extends FlagListener { this.checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.PLACE_BLOCKS); } - else if (e.getMaterial().name().contains("BOAT")) + else if (Tag.ITEMS_BOATS.isTagged(e.getMaterial())) { this.checkIsland(e, e.getPlayer(), e.getPlayer().getLocation(), Flags.BOAT); } diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index ce585a6cb..bcdb75b90 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -22,11 +22,11 @@ import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Boat; -import org.bukkit.entity.Boat.Type; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @@ -38,8 +38,6 @@ import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; -import com.google.common.collect.ImmutableMap; - import io.papermc.lib.PaperLib; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.events.IslandBaseEvent; @@ -67,15 +65,6 @@ public class IslandsManager { private final BentoBox plugin; - // Tree species to boat material map - private static final Map TREE_TO_BOAT = ImmutableMap.builder(). - put(Type.ACACIA, Material.ACACIA_BOAT). - put(Type.BIRCH, Material.BIRCH_BOAT). - put(Type.DARK_OAK, Material.DARK_OAK_BOAT). - put(Type.JUNGLE, Material.JUNGLE_BOAT). - put(Type.OAK, Material.OAK_BOAT). - put(Type.SPRUCE, Material.SPRUCE_BOAT).build(); - /** * One island can be spawn, this is the one - otherwise, this value is null */ @@ -250,8 +239,8 @@ public class IslandsManager { public boolean checkIfSafe(@Nullable World world, @NonNull Material ground, @NonNull Material space1, @NonNull Material space2) { // Ground must be solid, space 1 and 2 must not be solid if (world == null || !ground.isSolid() - || (space1.isSolid() && !space1.name().contains("SIGN")) - || (space2.isSolid() && !space2.name().contains("SIGN"))) { + || (space1.isSolid() && !Tag.SIGNS.isTagged(space1)) + || (space2.isSolid() && !Tag.SIGNS.isTagged(space2))) { return false; } // Cannot be submerged or water cannot be dangerous @@ -262,14 +251,19 @@ public class IslandsManager { if (ground.equals(Material.LAVA) || space1.equals(Material.LAVA) || space2.equals(Material.LAVA) - || ground.name().contains("FENCE") - || ground.name().contains("DOOR") - || ground.name().contains("GATE") - || ground.name().contains("PLATE") - || ground.name().contains("SIGN") - || ground.name().contains("BANNER") - || ground.name().contains("BUTTON") - || ground.name().contains("BOAT") + || Tag.SIGNS.isTagged(ground) + || Tag.TRAPDOORS.isTagged(ground) + || Tag.BANNERS.isTagged(ground) + || Tag.PRESSURE_PLATES.isTagged(ground) + || Tag.FENCE_GATES.isTagged(ground) + || Tag.DOORS.isTagged(ground) + || Tag.FENCES.isTagged(ground) + || Tag.BUTTONS.isTagged(ground) + || Tag.ITEMS_BOATS.isTagged(ground) + || Tag.ITEMS_CHEST_BOATS.isTagged(ground) + || Tag.CAMPFIRES.isTagged(ground) + || Tag.FIRE.isTagged(ground) + || Tag.FIRE.isTagged(space1) || space1.equals(Material.END_PORTAL) || space2.equals(Material.END_PORTAL) || space1.equals(Material.END_GATEWAY) @@ -1072,19 +1066,7 @@ public class IslandsManager { User user = User.getInstance(player); user.sendMessage("commands.island.go.teleport"); goingHome.add(user.getUniqueId()); - // Stop any gliding - player.setGliding(false); - // Check if the player is a passenger in a boat - if (player.isInsideVehicle()) { - Entity boat = player.getVehicle(); - if (boat instanceof Boat boaty) { - player.leaveVehicle(); - // Remove the boat so they don't lie around everywhere - boat.remove(); - player.getInventory().addItem(new ItemStack(TREE_TO_BOAT.getOrDefault(boaty.getBoatType(), Material.OAK_BOAT))); - player.updateInventory(); - } - } + readyPlayer(player); this.getAsyncSafeHomeLocation(world, user, name).thenAccept(home -> { Island island = getIsland(world, user); if (home == null) { @@ -1196,23 +1178,7 @@ public class IslandsManager { user.sendMessage("commands.island.spawn.no-spawn"); } else { // Teleport the player to the spawn - // Stop any gliding - player.setGliding(false); - // Check if the player is a passenger in a boat - if (player.isInsideVehicle()) { - Entity boat = player.getVehicle(); - if (boat instanceof Boat boaty) { - player.leaveVehicle(); - // Remove the boat so they don't lie around everywhere - boat.remove(); - Material boatMat = Material.getMaterial(boaty.getType() + "_BOAT"); - if (boatMat == null) { - boatMat = Material.OAK_BOAT; - } - player.getInventory().addItem(new ItemStack(boatMat, 1)); - player.updateInventory(); - } - } + readyPlayer(player); user.sendMessage("commands.island.spawn.teleporting"); // Safe teleport @@ -1220,6 +1186,23 @@ public class IslandsManager { } } + private void readyPlayer(@NonNull Player player) { + // Stop any gliding + player.setGliding(false); + // Check if the player is a passenger in a boat + if (player.isInsideVehicle()) { + Entity boat = player.getVehicle(); + if (boat instanceof Boat boaty) { + player.leaveVehicle(); + // Remove the boat so they don't lie around everywhere + boat.remove(); + player.getInventory().addItem(new ItemStack(boaty.getBoatType().getMaterial())); + player.updateInventory(); + } + } + + } + /** * Indicates whether a player is at an island spawn or not * diff --git a/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java b/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java index 141aa0852..e8c60c53d 100644 --- a/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java +++ b/src/main/java/world/bentobox/bentobox/nms/CopyWorldRegenerator.java @@ -17,6 +17,8 @@ import org.bukkit.block.BlockState; import org.bukkit.block.CreatureSpawner; import org.bukkit.block.Sign; import org.bukkit.block.data.BlockData; +import org.bukkit.block.sign.Side; +import org.bukkit.block.sign.SignSide; import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.Ageable; import org.bukkit.entity.ChestedHorse; @@ -45,6 +47,7 @@ import world.bentobox.bentobox.util.MyBiomeGrid; * */ public abstract class CopyWorldRegenerator implements WorldRegenerator { + private final BentoBox plugin; protected CopyWorldRegenerator() { @@ -68,7 +71,7 @@ public abstract class CopyWorldRegenerator implements WorldRegenerator { public CompletableFuture regenerate(GameModeAddon gm, IslandDeletion di, World world) { return gm.isUsesNewChunkGeneration() ? regenerateCopy(gm, di, world) : regenerateSimple(gm, di, world); } - + public CompletableFuture regenerateCopy(GameModeAddon gm, IslandDeletion di, World world) { CompletableFuture bigFuture = new CompletableFuture<>(); new BukkitRunnable() { @@ -110,7 +113,7 @@ public abstract class CopyWorldRegenerator implements WorldRegenerator { @Override public CompletableFuture regenerateChunk(Chunk chunk) { - return regenerateChunk(null, chunk.getWorld(), chunk.getX(), chunk.getZ()); + return regenerateChunk(null, chunk.getWorld(), chunk.getX(), chunk.getZ()); } private CompletableFuture regenerateChunk(@Nullable IslandDeletion di, World world, int chunkX, int chunkZ) { @@ -132,7 +135,7 @@ public abstract class CopyWorldRegenerator implements WorldRegenerator { copyChunkDataToChunk(chunkTo, chunkFrom, di != null ? di.getBox() : null); } catch (InterruptedException | ExecutionException e) { - Thread.currentThread().interrupt(); + Thread.currentThread().interrupt(); } }); return CompletableFuture.allOf(cleanFuture, copyFuture); @@ -159,7 +162,7 @@ public abstract class CopyWorldRegenerator implements WorldRegenerator { ); // Similarly, when the chunk is loaded, remove all the entities in the chunk apart from players - CompletableFuture entitiesFuture = chunkFuture.thenAccept(chunk -> + CompletableFuture entitiesFuture = chunkFuture.thenAccept(chunk -> // Remove all entities in chunk, including any dropped items as a result of clearing the blocks above Arrays.stream(chunk.getEntities()) .filter(e -> !(e instanceof Player) && di.inBounds(e.getLocation().getBlockX(), e.getLocation().getBlockZ())) @@ -200,35 +203,35 @@ public abstract class CopyWorldRegenerator implements WorldRegenerator { } private void processEntity(Entity entity, Location location) { - Entity bpe = location.getWorld().spawnEntity(location, entity.getType()); - bpe.setCustomName(entity.getCustomName()); - if (entity instanceof Villager villager && bpe instanceof Villager villager2) { - setVillager(villager, villager2); - } - if (entity instanceof Colorable c && bpe instanceof Colorable cc) { - if (c.getColor() != null) { - cc.setColor(c.getColor()); - } - } - if (entity instanceof Tameable t && bpe instanceof Tameable tt) { - tt.setTamed(t.isTamed()); - } - if (entity instanceof ChestedHorse ch && bpe instanceof ChestedHorse ch2) { - ch2.setCarryingChest(ch.isCarryingChest()); - } - // Only set if child. Most animals are adults - if (entity instanceof Ageable a && bpe instanceof Ageable aa) { - if (a.isAdult()) aa.setAdult(); - } - if (entity instanceof AbstractHorse horse && bpe instanceof AbstractHorse horse2) { - horse2.setDomestication(horse.getDomestication()); - horse2.getInventory().setContents(horse.getInventory().getContents()); - } - - if (entity instanceof Horse horse && bpe instanceof Horse horse2) { - horse2.setStyle(horse.getStyle()); + Entity bpe = location.getWorld().spawnEntity(location, entity.getType()); + bpe.setCustomName(entity.getCustomName()); + if (entity instanceof Villager villager && bpe instanceof Villager villager2) { + setVillager(villager, villager2); + } + if (entity instanceof Colorable c && bpe instanceof Colorable cc) { + if (c.getColor() != null) { + cc.setColor(c.getColor()); } } + if (entity instanceof Tameable t && bpe instanceof Tameable tt) { + tt.setTamed(t.isTamed()); + } + if (entity instanceof ChestedHorse ch && bpe instanceof ChestedHorse ch2) { + ch2.setCarryingChest(ch.isCarryingChest()); + } + // Only set if child. Most animals are adults + if (entity instanceof Ageable a && bpe instanceof Ageable aa) { + if (a.isAdult()) aa.setAdult(); + } + if (entity instanceof AbstractHorse horse && bpe instanceof AbstractHorse horse2) { + horse2.setDomestication(horse.getDomestication()); + horse2.getInventory().setContents(horse.getInventory().getContents()); + } + + if (entity instanceof Horse horse && bpe instanceof Horse horse2) { + horse2.setStyle(horse.getStyle()); + } + } /** * Set the villager stats @@ -241,7 +244,7 @@ public abstract class CopyWorldRegenerator implements WorldRegenerator { villager2.setProfession(v.getProfession()); villager2.setVillagerType(v.getVillagerType()); } - + private void processTileEntity(Block fromBlock, Block toBlock) { // Block state BlockState blockState = fromBlock.getState(); @@ -249,11 +252,9 @@ public abstract class CopyWorldRegenerator implements WorldRegenerator { // Signs if (blockState instanceof Sign fromSign && b instanceof Sign toSign) { - int i = 0; - for (String line : fromSign.getLines()) { - toSign.setLine(i++, line); + for (Side side : Side.values()) { + writeSign(fromSign, toSign, side); } - toSign.setGlowingText(fromSign.isGlowingText()); } // Chests else if (blockState instanceof InventoryHolder ih && b instanceof InventoryHolder toChest) { @@ -270,7 +271,17 @@ public abstract class CopyWorldRegenerator implements WorldRegenerator { toBanner.setPatterns(banner.getPatterns()); } } - + + + private void writeSign(Sign fromSign, Sign toSign, Side side) { + SignSide fromSide = fromSign.getSide(side); + SignSide toSide = toSign.getSide(side); + int i = 0; + for (String line : fromSide.getLines()) { + toSide.setLine(i++, line); + } + toSide.setGlowingText(fromSide.isGlowingText()); + } public CompletableFuture regenerateSimple(GameModeAddon gm, IslandDeletion di, World world) { CompletableFuture bigFuture = new CompletableFuture<>(); diff --git a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java index 7cd43cef5..8231ffd8b 100644 --- a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java +++ b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java @@ -19,6 +19,8 @@ import org.bukkit.block.CreatureSpawner; import org.bukkit.block.Sign; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.WallSign; +import org.bukkit.block.sign.Side; +import org.bukkit.block.sign.SignSide; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; @@ -120,7 +122,9 @@ public class DefaultPasteUtil { BlockState bs = block.getState(); // Signs if (bs instanceof Sign) { - writeSign(island, block, bpBlock.getSignLines(), bpBlock.isGlowingText()); + for (Side side : Side.values()) { + writeSign(island, block, bpBlock, side); + } } // Chests, in general else if (bs instanceof InventoryHolder holder) { @@ -200,8 +204,12 @@ public class DefaultPasteUtil { * @param block - block * @param lines - lines * @param glow - is sign glowing? + * @param side - the side being writted */ - public static void writeSign(Island island, final Block block, final List lines, boolean glow) { + public static void writeSign(Island island, final Block block, BlueprintBlock bpSign, Side side) { + List lines = bpSign.getSignLines(side); + boolean glow = bpSign.isGlowingText(side); + BlockFace bf; if (block.getType().name().contains("WALL_SIGN")) { WallSign wallSign = (WallSign) block.getBlockData(); @@ -211,7 +219,7 @@ public class DefaultPasteUtil { bf = sign.getRotation(); } // Handle spawn sign - if (island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.SPAWN_HERE)) { + if (side == Side.FRONT && island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.SPAWN_HERE)) { block.setType(Material.AIR); // Orient to face same direction as sign Location spawnPoint = new Location(block.getWorld(), block.getX() + 0.5D, block.getY(), @@ -225,7 +233,8 @@ public class DefaultPasteUtil { name = plugin.getPlayers().getName(island.getOwner()); } // Handle locale text for starting sign - org.bukkit.block.Sign s = (org.bukkit.block.Sign) block.getState(); + Sign s = (org.bukkit.block.Sign) block.getState(); + SignSide signSide = s.getSide(side); // Sign text must be stored under the addon's name.sign.line0,1,2,3 in the yaml file if (island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.START_TEXT)) { // Get the addon that is operating in this world @@ -233,17 +242,17 @@ public class DefaultPasteUtil { Optional user = Optional.ofNullable(island.getOwner()).map(User::getInstance); if (user.isPresent()) { for (int i = 0; i < 4; i++) { - s.setLine(i, Util.translateColorCodes(plugin.getLocalesManager().getOrDefault(user.get(), + signSide.setLine(i, Util.translateColorCodes(plugin.getLocalesManager().getOrDefault(user.get(), addonName + ".sign.line" + i, "").replace(TextVariables.NAME, name))); } } } else { // Just paste for (int i = 0; i < 4; i++) { - s.setLine(i, lines.get(i)); + signSide.setLine(i, lines.get(i)); } } - s.setGlowingText(glow); + signSide.setGlowingText(glow); // Update the sign s.update(); } diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java b/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java index 6d5406a37..84613ccf3 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java @@ -12,6 +12,8 @@ import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemFactory; @@ -169,12 +171,36 @@ public abstract class AbstractCommonSetup { // Util translate color codes (used in user translate methods) when(Util.translateColorCodes(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + // Tags + for (Material m : Material.values()) { + if (m.name().contains("_SIGN")) { + when(Tag.ALL_SIGNS.isTagged(m)).thenReturn(true); + when(Tag.SIGNS.isTagged(m)).thenReturn(true); + } + if (m.name().contains("_WALL_SIGN")) { + when(Tag.WALL_SIGNS.isTagged(m)).thenReturn(true); + } + if (m.name().contains("_TRAPDOOR")) { + when(Tag.TRAPDOORS.isTagged(m)).thenReturn(true); + } + if (m.name().contains("FENCE")) { + when(Tag.FENCES.isTagged(m)).thenReturn(true); + } + if (m.name().contains("_DOOR")) { + when(Tag.DOORS.isTagged(m)).thenReturn(true); + } + if (m.name().contains("_BOAT") || m.name().contains("_RAFT")) { + when(Tag.ITEMS_BOATS.isTagged(m)).thenReturn(true); + } + + } } /** + * @throws Exception */ @After - public void tearDown() { + public void tearDown() throws Exception { User.clearUsers(); Mockito.framework().clearInlineMocks(); } diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListenerTest.java index e04f72b41..8c39590aa 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/PlaceBlocksListenerTest.java @@ -13,7 +13,6 @@ import static org.mockito.Mockito.when; import org.bukkit.Bukkit; import org.bukkit.Material; -import org.bukkit.Tag; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; @@ -45,7 +44,7 @@ import world.bentobox.bentobox.util.Util; * */ @RunWith(PowerMockRunner.class) -@PrepareForTest( {BentoBox.class, Flags.class, Util.class, Bukkit.class, Tag.class} ) +@PrepareForTest( {BentoBox.class, Flags.class, Util.class, Bukkit.class} ) public class PlaceBlocksListenerTest extends AbstractCommonSetup { private PlaceBlocksListener pbl; @@ -163,7 +162,7 @@ public class PlaceBlocksListenerTest extends AbstractCommonSetup { assertTrue(e.isCancelled()); verify(notifier).notify(any(), eq("protection.protected")); } - + /** * Test method for {@link PlaceBlocksListener#onBlockPlace(org.bukkit.event.block.BlockPlaceEvent)}. */ @@ -184,7 +183,7 @@ public class PlaceBlocksListenerTest extends AbstractCommonSetup { pbl.onBlockPlace(e); assertFalse(e.isCancelled()); } - + /** * Test method for {@link PlaceBlocksListener#onBlockPlace(org.bukkit.event.block.BlockPlaceEvent)}. */ @@ -206,7 +205,7 @@ public class PlaceBlocksListenerTest extends AbstractCommonSetup { assertTrue(e.isCancelled()); verify(notifier).notify(any(), eq("protection.protected")); } - + /** * Test method for {@link PlaceBlocksListener#onBlockPlace(org.bukkit.event.block.BlockPlaceEvent)}. */ diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListenerTest.java index 54c805df4..3bd91e968 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListenerTest.java @@ -23,6 +23,7 @@ import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerRespawnEvent; +import org.bukkit.event.player.PlayerRespawnEvent.RespawnReason; import org.bukkit.inventory.ItemStack; import org.junit.After; import org.junit.Before; @@ -213,7 +214,7 @@ public class IslandRespawnListenerTest { // Has island when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); // Respawn - PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false); + PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, RespawnReason.DEATH); l.onPlayerRespawn(ev); assertEquals(safeLocation, ev.getRespawnLocation()); // Verify commands @@ -232,7 +233,7 @@ public class IslandRespawnListenerTest { // Has island when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); // Respawn - PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false); + PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, RespawnReason.DEATH); l.onPlayerRespawn(ev); assertEquals(location, ev.getRespawnLocation()); } @@ -255,7 +256,7 @@ public class IslandRespawnListenerTest { // Has island when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); // Respawn - PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false); + PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, RespawnReason.DEATH); l.onPlayerRespawn(ev); assertEquals(location, ev.getRespawnLocation()); } @@ -276,7 +277,7 @@ public class IslandRespawnListenerTest { // Has island when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); // Respawn - PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false); + PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, RespawnReason.DEATH); l.onPlayerRespawn(ev); assertEquals(location, ev.getRespawnLocation()); } diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/RemoveMobsListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/RemoveMobsListenerTest.java index 8cfb2cf8c..a74929e35 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/RemoveMobsListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/RemoveMobsListenerTest.java @@ -17,6 +17,7 @@ import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.player.PlayerRespawnEvent.RespawnReason; import org.bukkit.scheduler.BukkitScheduler; import org.junit.After; import org.junit.Before; @@ -200,7 +201,7 @@ public class RemoveMobsListenerTest { */ @Test public void testOnUserRespawn() { - PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false); + PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false, RespawnReason.DEATH); new RemoveMobsListener().onUserRespawn(e); verify(scheduler).runTask(any(), any(Runnable.class)); } @@ -211,7 +212,7 @@ public class RemoveMobsListenerTest { @Test public void testOnUserRespawnDoNotRemove() { Flags.REMOVE_MOBS.setSetting(world, false); - PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false); + PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false, RespawnReason.DEATH); new RemoveMobsListener().onUserRespawn(e); verify(scheduler, never()).runTask(any(), any(Runnable.class)); } @@ -223,7 +224,7 @@ public class RemoveMobsListenerTest { public void testOnUserRespawnNotIsland() { // Not on island when(im.locationIsOnIsland(any(), any())).thenReturn(false); - PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false); + PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false, RespawnReason.DEATH); new RemoveMobsListener().onUserRespawn(e); verify(scheduler, never()).runTask(any(), any(Runnable.class)); } diff --git a/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java index 66f90c51a..30e24cb16 100644 --- a/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/BlueprintClipboardManagerTest.java @@ -12,7 +12,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; diff --git a/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java index 055efe1b3..b3b24ab42 100644 --- a/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/BlueprintsManagerTest.java @@ -40,7 +40,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandWorldManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandWorldManagerTest.java index 8b4af6d1e..c846a168d 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandWorldManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandWorldManagerTest.java @@ -614,6 +614,7 @@ public class IslandWorldManagerTest { /** * Test method for {@link world.bentobox.bentobox.managers.IslandWorldManager#getDefaultIslandFlags(org.bukkit.World)}. */ + @SuppressWarnings("removal") @Test public void testGetDefaultIslandFlags() { Map flags = new HashMap<>(); @@ -634,6 +635,7 @@ public class IslandWorldManagerTest { /** * Test method for {@link world.bentobox.bentobox.managers.IslandWorldManager#getDefaultIslandSettings(org.bukkit.World)}. */ + @SuppressWarnings("removal") @Test public void testGetDefaultIslandSettings() { Map flags = new HashMap<>(); diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java index 169551906..f9bc0105c 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java @@ -78,13 +78,14 @@ import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType; import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.listeners.flags.AbstractCommonSetup; import world.bentobox.bentobox.lists.Flags; import world.bentobox.bentobox.managers.island.IslandCache; import world.bentobox.bentobox.util.Util; @RunWith(PowerMockRunner.class) @PrepareForTest( { Bukkit.class, BentoBox.class, Util.class, Location.class }) -public class IslandsManagerTest { +public class IslandsManagerTest extends AbstractCommonSetup { @Mock private BentoBox plugin; @@ -141,11 +142,12 @@ public class IslandsManagerTest { // Class under test IslandsManager im; - /** - */ + @Override @SuppressWarnings("unchecked") @Before public void setUp() throws Exception { + super.setUp(); + // Clear any lingering database tearDown(); // Set up plugin @@ -315,14 +317,8 @@ public class IslandsManagerTest { db = mock(Database.class); // Signs - sign = Material.getMaterial("SIGN"); - if (sign == null) { - sign = Material.getMaterial("OAK_SIGN"); - } - wallSign = Material.getMaterial("WALL_SIGN"); - if (wallSign == null) { - wallSign = Material.getMaterial("OAK_WALL_SIGN"); - } + sign = Material.BIRCH_SIGN; + wallSign = Material.ACACIA_WALL_SIGN; // PaperLib env = new CraftBukkitEnvironment(); @@ -337,10 +333,10 @@ public class IslandsManagerTest { //im.setIslandCache(islandCache); } - /** - */ + @Override @After public void tearDown() throws Exception { + super.tearDown(); Mockito.framework().clearInlineMocks(); deleteAll(new File("database")); deleteAll(new File("database_backup")); @@ -393,12 +389,13 @@ public class IslandsManagerTest { assertFalse(im.isSafeLocation(location)); } + @SuppressWarnings("deprecation") @Test public void testCheckIfSafeTrapdoor() { for (Material d : Material.values()) { if (d.name().contains("DOOR")) { for (Material s : Material.values()) { - if (s.name().contains("_SIGN")) { + if (s.name().contains("_SIGN") && !s.isLegacy()) { assertFalse("Fail " + d.name() + " " + s.name(), im.checkIfSafe(world, d, s, Material.AIR)); } } @@ -486,13 +483,13 @@ public class IslandsManagerTest { @Test public void testBadBlocks() { // Fences - Arrays.stream(Material.values()).filter(m -> m.toString().contains("FENCE")).forEach(m -> { - when(ground.getType()).thenReturn(m); - assertFalse("Fence :" + m.toString(), im.isSafeLocation(location)); - }); + when(ground.getType()).thenReturn(Material.SPRUCE_FENCE); + assertFalse("Fence :" + Material.SPRUCE_FENCE.toString(), im.isSafeLocation(location)); // Signs + sign = Material.BIRCH_SIGN; when(ground.getType()).thenReturn(sign); assertFalse("Sign", im.isSafeLocation(location)); + wallSign = Material.ACACIA_WALL_SIGN; when(ground.getType()).thenReturn(wallSign); assertFalse("Sign", im.isSafeLocation(location)); // Bad Blocks From c000da6e6f1d5858d8368063b1c1488e8528f457 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sun, 11 Jun 2023 18:50:48 +0200 Subject: [PATCH 07/36] Update GitHub actions to NodeJS 16 --- .github/workflows/build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1683344a2..4191081bb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,22 +10,22 @@ jobs: name: Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Set up JDK 17 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'adopt' java-version: '17' - name: Cache SonarCloud packages - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache Maven packages - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} @@ -38,7 +38,7 @@ jobs: - run: mvn --batch-mode clean org.jacoco:jacoco-maven-plugin:prepare-agent install - run: mkdir staging && cp target/*.jar staging - name: Save artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: Package path: staging From b64015e3b610d73e000b1cc3a80f86c9825d55c7 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 15 Jun 2023 18:43:17 -0700 Subject: [PATCH 08/36] Update ServerCompatibility.java Added 1.21.1 --- .../world/bentobox/bentobox/versions/ServerCompatibility.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java b/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java index 5fbec7ebe..664003420 100644 --- a/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java +++ b/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java @@ -222,6 +222,10 @@ public class ServerCompatibility { * @since 1.24.0 */ V1_20(Compatibility.COMPATIBLE), + /** + * @since 1.24.0 + */ + V1_20_1(Compatibility.COMPATIBLE), ; private final Compatibility compatibility; From 5a52978803629f492516691f7e474ebaf33d60f7 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 18 Jun 2023 13:35:16 -0700 Subject: [PATCH 09/36] Add perms test for admin teleport command --- .../admin/AdminTeleportCommandTest.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommandTest.java index 5d0bb3a76..a585c5817 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommandTest.java @@ -44,6 +44,7 @@ import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.LocalesManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; import world.bentobox.bentobox.managers.PlayersManager; import world.bentobox.bentobox.util.Util; @@ -74,6 +75,8 @@ public class AdminTeleportCommandTest { private World netherWorld; @Mock private World endWorld; + @Mock + private PlaceholdersManager phm; /** @@ -96,16 +99,24 @@ public class AdminTeleportCommandTest { while(notUUID.equals(uuid)) { notUUID = UUID.randomUUID(); } + when(p.getUniqueId()).thenReturn(uuid); + when(p.hasPermission("admin.tp")).thenReturn(true); + when(p.hasPermission("admin")).thenReturn(false); + when(user.getUniqueId()).thenReturn(uuid); when(user.getPlayer()).thenReturn(p); when(user.getName()).thenReturn("tastybento"); when(user.isPlayer()).thenReturn(true); + when(user.hasPermission("admin.tp")).thenReturn(true); + when(user.hasPermission("admin")).thenReturn(false); + User.setPlugin(plugin); // Parent command has no aliases when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); when(ac.getTopLabel()).thenReturn("bskyblock"); when(ac.getWorld()).thenReturn(world); + when(ac.getPermission()).thenReturn("admin"); // World when(world.getEnvironment()).thenReturn(Environment.NORMAL); @@ -132,7 +143,7 @@ public class AdminTeleportCommandTest { // Server & Scheduler BukkitScheduler sch = mock(BukkitScheduler.class); - PowerMockito.mockStatic(Bukkit.class); + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); when(Bukkit.getScheduler()).thenReturn(sch); // Locales @@ -165,6 +176,9 @@ public class AdminTeleportCommandTest { // Util PowerMockito.mockStatic(Util.class, Mockito.RETURNS_MOCKS); when(Util.getUUID(anyString())).thenCallRealMethod(); + + // Placeholder manager + when(plugin.getPlaceholdersManager()).thenReturn(phm); } @After @@ -276,5 +290,17 @@ public class AdminTeleportCommandTest { verify(user).getTranslation(eq("commands.admin.tp.manual"), eq("[location]"), eq("0 0 0")); } + @Test + public void testPermissions() { + when(pm.getUUID(eq("tastybento"))).thenReturn(notUUID); + when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); + AdminTeleportCommand atc = new AdminTeleportCommand(ac,"tpend"); + assertTrue(atc.canExecute(user, "tpend", Collections.singletonList("tastybento"))); + String[] list = new String[1]; + list[0] = "tastybento"; + System.out.println("UUID " + p.getUniqueId()); + assertTrue(atc.execute(p, "tpend", list)); + verify(user).getTranslation(eq("commands.admin.tp.manual"), eq("[location]"), eq("0 0 0")); + } } From e9067cfb284d830b30829c0ccc41fd99938d6b39 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 18 Jun 2023 13:55:21 -0700 Subject: [PATCH 10/36] Add bbox admin perms command and adjust perms for commands Made some admin commands have their own perm so that they can be controlled individually by permissions. --- pom.xml | 4 +- .../api/commands/CompositeCommand.java | 61 +++++++++++--- .../blueprints/AdminBlueprintCopyCommand.java | 4 +- .../AdminBlueprintDeleteCommand.java | 10 +-- .../blueprints/AdminBlueprintListCommand.java | 6 +- .../blueprints/AdminBlueprintLoadCommand.java | 2 +- .../AdminBlueprintOriginCommand.java | 2 +- .../AdminBlueprintPasteCommand.java | 2 +- .../blueprints/AdminBlueprintPos1Command.java | 2 +- .../blueprints/AdminBlueprintPos2Command.java | 2 +- .../AdminBlueprintRenameCommand.java | 18 ++--- .../blueprints/AdminBlueprintSaveCommand.java | 2 +- .../admin/deaths/AdminDeathsAddCommand.java | 2 +- .../deaths/AdminDeathsRemoveCommand.java | 2 +- .../admin/deaths/AdminDeathsResetCommand.java | 2 +- .../admin/deaths/AdminDeathsSetCommand.java | 2 +- .../admin/purge/AdminPurgeProtectCommand.java | 2 +- .../admin/purge/AdminPurgeStatusCommand.java | 2 +- .../admin/purge/AdminPurgeStopCommand.java | 2 +- .../admin/purge/AdminPurgeUnownedCommand.java | 2 +- .../admin/range/AdminRangeAddCommand.java | 2 +- .../admin/range/AdminRangeRemoveCommand.java | 2 +- .../admin/resets/AdminResetsAddCommand.java | 2 +- .../resets/AdminResetsRemoveCommand.java | 2 +- .../admin/resets/AdminResetsResetCommand.java | 2 +- .../admin/resets/AdminResetsSetCommand.java | 2 +- .../admin/team/AdminTeamAddCommand.java | 2 +- .../admin/team/AdminTeamDisbandCommand.java | 2 +- .../admin/team/AdminTeamFixCommand.java | 2 +- .../admin/team/AdminTeamKickCommand.java | 2 +- .../admin/team/AdminTeamSetownerCommand.java | 2 +- .../commands/BentoBoxAboutCommand.java | 2 +- .../bentobox/commands/BentoBoxCommand.java | 1 + .../commands/BentoBoxPermsCommand.java | 80 +++++++++++++++++++ src/main/resources/locales/en-US.yml | 3 + 35 files changed, 182 insertions(+), 57 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/commands/BentoBoxPermsCommand.java diff --git a/pom.xml b/pom.xml index 1f2a80388..d7c1a3747 100644 --- a/pom.xml +++ b/pom.xml @@ -73,10 +73,10 @@ 42.2.18 5.0.1 - 1.20-R0.1-SNAPSHOT + 1.20.1-R0.1-SNAPSHOT - 1.20-R0.1-SNAPSHOT + 1.20.1-R0.1-SNAPSHOT 3.0.0 1.7.1 2.10.9 diff --git a/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java index 8301b13b3..cb04f9b55 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java @@ -48,6 +48,13 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi */ private boolean onlyPlayer = false; + /** + * True if the command is only for the console + * @since 1.24.0 + */ + private boolean onlyConsole = false; + + /** * True if command is a configurable rank */ @@ -257,10 +264,14 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi */ public boolean call(User user, String cmdLabel, List cmdArgs) { // Check for console and permissions - if (onlyPlayer && !user.isPlayer()) { + if (isOnlyPlayer() && !user.isPlayer()) { user.sendMessage("general.errors.use-in-game"); return false; } + if (isOnlyConsole() && user.isPlayer()) { + user.sendMessage("general.errors.use-in-console"); + return false; + } if (!this.runPermissionCheck(user)) { @@ -513,6 +524,14 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi return onlyPlayer; } + /** + * Check if this command is only for consoles. + * @return true or false + */ + public boolean isOnlyConsole() { + return onlyConsole; + } + /** * Sets whether this command should only be run by players. * If this is set to {@code true}, this command will only be runnable by objects implementing {@link Player}. @@ -525,6 +544,18 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi this.onlyPlayer = onlyPlayer; } + /** + * Sets whether this command should only be run in the console. + * This is for commands that dump a lot of data or are for debugging. + * The default value provided when instantiating this CompositeCommand is {@code false}. + * Therefore, this method should only be used in case you want to explicitly edit the value. + * @param onlyConsole {@code true} if this command should only be run in the console. + * @since 1.24.0 + */ + public void setOnlyConsole(boolean onlyConsole) { + this.onlyConsole = onlyConsole; + } + /** * Sets locale reference to this command's description. * It is used to display the help of this command. @@ -623,16 +654,17 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi @Override @NonNull public List tabComplete(final @NonNull CommandSender sender, final @NonNull String alias, final String[] args) { - List options = new ArrayList<>(); // Get command object based on args entered so far CompositeCommand command = getCommandFromArgs(args); // Check for console and permissions - if (command.isOnlyPlayer() && !(sender instanceof Player)) { - return options; + if ((command.isOnlyPlayer() && !(sender instanceof Player)) + || (command.isOnlyConsole() && sender instanceof Player)) { + return List.of(); } if (command.getPermission() != null && !command.getPermission().isEmpty() && !sender.hasPermission(command.getPermission()) && !sender.isOp()) { - return options; + return List.of(); } + List options = new ArrayList<>(); // Add any tab completion from the subcommand options.addAll(command.tabComplete(User.getInstance(sender), alias, new LinkedList<>(Arrays.asList(args))).orElseGet(ArrayList::new)); if (command.hasSubCommands()) { @@ -654,17 +686,26 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi } /** - * Returns a list containing all the labels of the subcommands for the provided CompositeCommand excluding any hidden commands + * Returns a list containing all the labels of the subcommands for the provided + * CompositeCommand excluding any hidden commands * @param sender the CommandSender * @param command the CompositeCommand to get the subcommands from * @return a list of subcommands labels or an empty list. */ @NonNull private List getSubCommandLabels(@NonNull CommandSender sender, @NonNull CompositeCommand command) { - return command.getSubCommands().values().stream() - .filter(cmd -> !cmd.isHidden()) - .filter(cmd -> !cmd.isOnlyPlayer() || sender.isOp() || (sender instanceof Player && cmd.getPermission() != null && (cmd.getPermission().isEmpty() || sender.hasPermission(cmd.getPermission()))) ) - .map(CompositeCommand::getLabel).toList(); + List result = new ArrayList<>(); + for (CompositeCommand cc: command.getSubCommands().values()) { + // Player or not + if (sender instanceof Player) { + if (!cc.isHidden() && !cc.isOnlyConsole() && (cc.getPermission().isEmpty() || sender.hasPermission(cc.getPermission()))) { + result.add(cc.getLabel()); + } + } else if (!cc.isOnlyPlayer()) { + result.add(cc.getLabel()); + } + } + return result; } /** diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintCopyCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintCopyCommand.java index 8b54ac28f..403ec8891 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintCopyCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintCopyCommand.java @@ -19,7 +19,7 @@ public class AdminBlueprintCopyCommand extends CompositeCommand @Override public void setup() { - inheritPermission(); + setPermission("admin.blueprint.copy"); setParametersHelp("commands.admin.blueprint.copy.parameters"); setDescription("commands.admin.blueprint.copy.description"); } @@ -37,7 +37,7 @@ public class AdminBlueprintCopyCommand extends CompositeCommand AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent(); BlueprintClipboard clipboard = - parent.getClipboards().computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard()); + parent.getClipboards().computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard()); boolean copyAir = args.stream().anyMatch(key -> key.equalsIgnoreCase("air")); boolean copyBiome = args.stream().anyMatch(key -> key.equalsIgnoreCase("biome")); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintDeleteCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintDeleteCommand.java index 65bbb830f..082ffef35 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintDeleteCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintDeleteCommand.java @@ -26,7 +26,7 @@ public class AdminBlueprintDeleteCommand extends ConfirmableCommand @Override public void setup() { - this.inheritPermission(); + setPermission("admin.blueprint.delete"); this.setParametersHelp("commands.admin.blueprint.delete.parameters"); this.setDescription("commands.admin.blueprint.delete.description"); } @@ -47,10 +47,10 @@ public class AdminBlueprintDeleteCommand extends ConfirmableCommand if (this.getPlugin().getBlueprintsManager().getBlueprints(this.getAddon()).containsKey(blueprintName)) { this.askConfirmation(user, user.getTranslation("commands.admin.blueprint.delete.confirmation"), - () -> { - this.getPlugin().getBlueprintsManager().deleteBlueprint(this.getAddon(), blueprintName); - user.sendMessage("commands.admin.blueprint.delete.success", TextVariables.NAME, blueprintName); - }); + () -> { + this.getPlugin().getBlueprintsManager().deleteBlueprint(this.getAddon(), blueprintName); + user.sendMessage("commands.admin.blueprint.delete.success", TextVariables.NAME, blueprintName); + }); return true; } else diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintListCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintListCommand.java index 57f19325a..27ff01429 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintListCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintListCommand.java @@ -22,7 +22,7 @@ public class AdminBlueprintListCommand extends CompositeCommand @Override public void setup() { - this.inheritPermission(); + setPermission("admin.blueprint.list"); this.setDescription("commands.admin.blueprint.list.description"); } @@ -54,8 +54,8 @@ public class AdminBlueprintListCommand extends CompositeCommand FilenameFilter blueprintFilter = (File dir, String name) -> name.endsWith(BlueprintsManager.BLUEPRINT_SUFFIX); List blueprintList = Arrays.stream(Objects.requireNonNull(blueprints.list(blueprintFilter))). - map(name -> name.substring(0, name.length() - BlueprintsManager.BLUEPRINT_SUFFIX.length())). - toList(); + map(name -> name.substring(0, name.length() - BlueprintsManager.BLUEPRINT_SUFFIX.length())). + toList(); if (blueprintList.isEmpty()) { diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintLoadCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintLoadCommand.java index 6bbef1502..1d0d766fa 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintLoadCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintLoadCommand.java @@ -17,7 +17,7 @@ public class AdminBlueprintLoadCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.blueprint.load"); setParametersHelp("commands.admin.blueprint.load.parameters"); setDescription("commands.admin.blueprint.load.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintOriginCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintOriginCommand.java index df254b3bb..066e6b9fa 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintOriginCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintOriginCommand.java @@ -19,7 +19,7 @@ public class AdminBlueprintOriginCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.blueprint.origin"); setParametersHelp("commands.admin.blueprint.origin.parameters"); setDescription("commands.admin.blueprint.origin.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPasteCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPasteCommand.java index 046222cf4..eb50009c1 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPasteCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPasteCommand.java @@ -15,7 +15,7 @@ public class AdminBlueprintPasteCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.blueprint.paste"); setParametersHelp("commands.admin.blueprint.paste.parameters"); setDescription("commands.admin.blueprint.paste.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos1Command.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos1Command.java index 534124ce6..18d004f68 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos1Command.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos1Command.java @@ -15,7 +15,7 @@ public class AdminBlueprintPos1Command extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.blueprint.pos1"); setParametersHelp("commands.admin.blueprint.pos1.parameters"); setDescription("commands.admin.blueprint.pos1.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos2Command.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos2Command.java index 9c1bcae44..76ea8e0fb 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos2Command.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintPos2Command.java @@ -15,7 +15,7 @@ public class AdminBlueprintPos2Command extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.blueprint.pos2"); setParametersHelp("commands.admin.blueprint.pos2.parameters"); setDescription("commands.admin.blueprint.pos2.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintRenameCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintRenameCommand.java index 568e061e3..e677dfa0b 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintRenameCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintRenameCommand.java @@ -27,7 +27,7 @@ public class AdminBlueprintRenameCommand extends ConfirmableCommand @Override public void setup() { - this.inheritPermission(); + setPermission("admin.blueprint.rename"); this.setParametersHelp("commands.admin.blueprint.rename.parameters"); this.setDescription("commands.admin.blueprint.rename.description"); } @@ -83,8 +83,8 @@ public class AdminBlueprintRenameCommand extends ConfirmableCommand { // Ask for confirmation to overwrite this.askConfirmation(user, - user.getTranslation("commands.admin.blueprint.file-exists"), - () -> this.rename(user, from, to, args.get(1))); + user.getTranslation("commands.admin.blueprint.file-exists"), + () -> this.rename(user, from, to, args.get(1))); } else { @@ -102,11 +102,11 @@ public class AdminBlueprintRenameCommand extends ConfirmableCommand this.getPlugin().getBlueprintsManager().renameBlueprint(this.getAddon(), blueprint, fileName, displayName); user.sendMessage("commands.admin.blueprint.rename.success", - "[old]", - blueprintName, - TextVariables.NAME, - blueprint.getName(), - "[display]", - blueprint.getDisplayName()); + "[old]", + blueprintName, + TextVariables.NAME, + blueprint.getName(), + "[display]", + blueprint.getDisplayName()); } } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintSaveCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintSaveCommand.java index f4c5a749d..9f1baeb90 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintSaveCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/blueprints/AdminBlueprintSaveCommand.java @@ -25,7 +25,7 @@ public class AdminBlueprintSaveCommand extends ConfirmableCommand @Override public void setup() { - this.inheritPermission(); + setPermission("admin.blueprint.save"); this.setParametersHelp("commands.admin.blueprint.save.parameters"); this.setDescription("commands.admin.blueprint.save.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsAddCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsAddCommand.java index 4ef5a2241..32fbe2e06 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsAddCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsAddCommand.java @@ -22,7 +22,7 @@ public class AdminDeathsAddCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.deaths.add"); setDescription("commands.admin.deaths.add.description"); setParametersHelp("commands.admin.deaths.add.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsRemoveCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsRemoveCommand.java index 74824702e..bdad9bff9 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsRemoveCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsRemoveCommand.java @@ -22,7 +22,7 @@ public class AdminDeathsRemoveCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.deaths.remove"); setDescription("commands.admin.deaths.remove.description"); setParametersHelp("commands.admin.deaths.remove.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsResetCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsResetCommand.java index 4780ce3cb..d7b8a01bc 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsResetCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsResetCommand.java @@ -21,7 +21,7 @@ public class AdminDeathsResetCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.deaths.reset"); setDescription("commands.admin.deaths.reset.description"); setParametersHelp("commands.admin.deaths.reset.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsSetCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsSetCommand.java index ce99330c1..8d69486c6 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsSetCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/deaths/AdminDeathsSetCommand.java @@ -21,7 +21,7 @@ public class AdminDeathsSetCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.deaths.set"); setDescription("commands.admin.deaths.set.description"); setParametersHelp("commands.admin.deaths.set.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeProtectCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeProtectCommand.java index 272d3ddd0..4e07099c2 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeProtectCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeProtectCommand.java @@ -16,7 +16,7 @@ public class AdminPurgeProtectCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.purge.protect"); setOnlyPlayer(true); setDescription("commands.admin.purge.protect.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStatusCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStatusCommand.java index bb5a04a0c..3f5bde9aa 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStatusCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStatusCommand.java @@ -19,7 +19,7 @@ public class AdminPurgeStatusCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.purge.status"); setOnlyPlayer(false); setParametersHelp("commands.admin.purge.status.parameters"); setDescription("commands.admin.purge.status.description"); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStopCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStopCommand.java index da1a4830f..84aeb6190 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStopCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStopCommand.java @@ -13,7 +13,7 @@ public class AdminPurgeStopCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.purge.stop"); setOnlyPlayer(false); setDescription("commands.admin.purge.stop.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeUnownedCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeUnownedCommand.java index 7d9696d15..b137f1e43 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeUnownedCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeUnownedCommand.java @@ -17,7 +17,7 @@ public class AdminPurgeUnownedCommand extends ConfirmableCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.purge.unowned"); setOnlyPlayer(false); setParametersHelp("commands.admin.purge.unowned.parameters"); setDescription("commands.admin.purge.unowned.description"); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeAddCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeAddCommand.java index 70a1b876a..566e4a30c 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeAddCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeAddCommand.java @@ -25,7 +25,7 @@ public class AdminRangeAddCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.range.add"); setDescription("commands.admin.range.add.description"); setParametersHelp("commands.admin.range.add.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeRemoveCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeRemoveCommand.java index 5615ec5e7..4679bc2bc 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeRemoveCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeRemoveCommand.java @@ -25,7 +25,7 @@ public class AdminRangeRemoveCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.range.remove"); setDescription("commands.admin.range.remove.description"); setParametersHelp("commands.admin.range.remove.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsAddCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsAddCommand.java index ff46727f9..cc681ddea 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsAddCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsAddCommand.java @@ -22,7 +22,7 @@ public class AdminResetsAddCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.resets.add"); setDescription("commands.admin.resets.add.description"); setParametersHelp("commands.admin.resets.add.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsRemoveCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsRemoveCommand.java index b4feaed2c..3b459b032 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsRemoveCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsRemoveCommand.java @@ -22,7 +22,7 @@ public class AdminResetsRemoveCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.resets.remove"); setDescription("commands.admin.resets.remove.description"); setParametersHelp("commands.admin.resets.remove.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsResetCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsResetCommand.java index 00925a187..490f654b0 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsResetCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsResetCommand.java @@ -22,7 +22,7 @@ public class AdminResetsResetCommand extends ConfirmableCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.resets.remove"); setDescription("commands.admin.resets.reset.description"); setParametersHelp("commands.admin.resets.reset.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsSetCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsSetCommand.java index 34d5c9997..025dbcad5 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsSetCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/resets/AdminResetsSetCommand.java @@ -16,7 +16,7 @@ public class AdminResetsSetCommand extends CompositeCommand { @Override public void setup() { - inheritPermission(); + setPermission("admin.resets.set"); setDescription("commands.admin.resets.set.description"); setParametersHelp("commands.admin.resets.set.parameters"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommand.java index b5feecab1..78816d6cc 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommand.java @@ -21,7 +21,7 @@ public class AdminTeamAddCommand extends CompositeCommand { @Override public void setup() { - setPermission("mod.team"); + setPermission("mod.team.add"); setParametersHelp("commands.admin.team.add.parameters"); setDescription("commands.admin.team.add.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandCommand.java index 954022967..a682d0abc 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandCommand.java @@ -20,7 +20,7 @@ public class AdminTeamDisbandCommand extends CompositeCommand { @Override public void setup() { - setPermission("mod.team"); + setPermission("mod.team.disband"); setParametersHelp("commands.admin.team.disband.parameters"); setDescription("commands.admin.team.disband.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamFixCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamFixCommand.java index 601a4cb8f..b739b2a24 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamFixCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamFixCommand.java @@ -14,7 +14,7 @@ public class AdminTeamFixCommand extends CompositeCommand { @Override public void setup() { - setPermission("mod.team"); + setPermission("mod.team.fix"); setDescription("commands.admin.team.fix.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommand.java index 77c5174a3..efce9f268 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommand.java @@ -30,7 +30,7 @@ public class AdminTeamKickCommand extends CompositeCommand { @Override public void setup() { - setPermission("mod.team"); + setPermission("mod.team.kick"); setParametersHelp("commands.admin.team.kick.parameters"); setDescription("commands.admin.team.kick.description"); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamSetownerCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamSetownerCommand.java index 2001367cc..fa7800283 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamSetownerCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamSetownerCommand.java @@ -25,7 +25,7 @@ public class AdminTeamSetownerCommand extends CompositeCommand { @Override public void setup() { - setPermission("mod.team"); + setPermission("mod.team.setowner"); setParametersHelp("commands.admin.team.setowner.parameters"); setDescription("commands.admin.team.setowner.description"); } diff --git a/src/main/java/world/bentobox/bentobox/commands/BentoBoxAboutCommand.java b/src/main/java/world/bentobox/bentobox/commands/BentoBoxAboutCommand.java index 4f769afbc..1f511565e 100644 --- a/src/main/java/world/bentobox/bentobox/commands/BentoBoxAboutCommand.java +++ b/src/main/java/world/bentobox/bentobox/commands/BentoBoxAboutCommand.java @@ -28,7 +28,7 @@ public class BentoBoxAboutCommand extends CompositeCommand { @Override public boolean execute(User user, String label, List args) { user.sendRawMessage("About " + BentoBox.getInstance().getDescription().getName() + " v" + BentoBox.getInstance().getDescription().getVersion() + ":"); - user.sendRawMessage("Copyright (c) 2017 - 2021 Tastybento, Poslovitch and the BentoBoxWorld contributors"); + user.sendRawMessage("Copyright (c) 2017 - 2023 Tastybento, Poslovitch and the BentoBoxWorld contributors"); user.sendRawMessage("See https://www.eclipse.org/legal/epl-2.0/ for license information."); return true; } diff --git a/src/main/java/world/bentobox/bentobox/commands/BentoBoxCommand.java b/src/main/java/world/bentobox/bentobox/commands/BentoBoxCommand.java index 8fa9f3480..9662a88ee 100644 --- a/src/main/java/world/bentobox/bentobox/commands/BentoBoxCommand.java +++ b/src/main/java/world/bentobox/bentobox/commands/BentoBoxCommand.java @@ -24,6 +24,7 @@ public class BentoBoxCommand extends CompositeCommand { new BentoBoxReloadCommand(this); new BentoBoxLocaleCommand(this); new BentoBoxHelpCommand(this); + new BentoBoxPermsCommand(this); // Database names with a 2 in them are migration databases if (getPlugin().getSettings().getDatabaseType().name().contains("2")) { new BentoBoxMigrateCommand(this); diff --git a/src/main/java/world/bentobox/bentobox/commands/BentoBoxPermsCommand.java b/src/main/java/world/bentobox/bentobox/commands/BentoBoxPermsCommand.java new file mode 100644 index 000000000..a0c7a64dc --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/commands/BentoBoxPermsCommand.java @@ -0,0 +1,80 @@ +package world.bentobox.bentobox.commands; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.permissions.Permission; + +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; + +/** + * Displays permissions that have been set by BentoBox. + * + * @author tastybento + */ +public class BentoBoxPermsCommand extends CompositeCommand { + + /** + * Info command + * @param parent - command parent + */ + public BentoBoxPermsCommand(CompositeCommand parent) { + super(parent, "perms"); + } + + @Override + public void setup() { + setPermission("bentobox.perms"); + this.isOnlyConsole(); + } + + @Override + public boolean execute(User user, String label, List args) { + // Loop all the known top-level commands + getPlugin().getCommandsManager().getCommands().values().stream().distinct().forEach(cc -> { + if (cc.getAddon() == null) { + user.sendMessage("*** BentoBox effective perms:"); + } else if (cc.getAddon() instanceof GameModeAddon gma) { + user.sendRawMessage("**** " + gma.getDescription().getName() + " effective perms:"); + } else { + user.sendRawMessage("**** " + cc.getAddon().getDescription().getName() + " effective perms:"); + } + user.sendRawMessage("permissions:"); + printData(user, cc, cc.getLabel()); + printSubCommandData(user, cc, cc.getLabel()); + }); + return true; + } + + private void printData(User user, CompositeCommand cc, String label) { + if (cc.getPermission().isBlank()) return; + String desc = user.getTranslation(cc.getWorld(), cc.getDescription()); + user.sendRawMessage(" " + cc.getPermission() + ":"); + user.sendRawMessage(" description: Allow use of '/" + label + "' command - " + desc); + Permission p = Bukkit.getPluginManager().getPermission(cc.getPermission()); + if (p != null) { + user.sendRawMessage(" default: " + p.getDefault().name()); + } else { + user.sendRawMessage(" default: OP"); // If not def + } + + } + + /** + * Iterates over sub-commands + * @param user user + * @param parent parent command + * @param label + */ + private void printSubCommandData(User user, CompositeCommand parent, String label) { + for (CompositeCommand cc : parent.getSubCommands().values()) { + if (cc.getLabel().equalsIgnoreCase("help")) continue; // Ignore the help command + String newLabel = label + " " + cc.getLabel(); + printData(user, cc, newLabel); + printSubCommandData(user, cc, newLabel); + } + + } +} diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index dbddac83a..7c444cefd 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -21,6 +21,7 @@ general: no-permission: "&c You don't have the permission to execute this command (&7 [permission]&c )." insufficient-rank: "&c Your rank is not high enough to do that! (&7 [rank]&c )" use-in-game: "&c This command is only available in-game." + use-in-console: "&c This command is only available in the console." no-team: "&c You do not have a team!" no-island: "&c You do not have an island!" player-has-island: "&c Player already has an island!" @@ -428,6 +429,8 @@ commands: success: "&a Successfully reset [name]'s island name." bentobox: description: "BentoBox admin command" + perms: + description: "displays the effective perms for BentoBox and Addons in a YAML format" about: description: "displays copyright and license information" reload: From b512975b13e23f388f34235fff7e2a2359bc15e7 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 18 Jun 2023 13:55:50 -0700 Subject: [PATCH 11/36] Updated BentoBox perms in plugin.yml to add perm command --- src/main/resources/plugin.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 1d84e6f85..4dff30944 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -60,4 +60,6 @@ permissions: bentobox.version: description: Allows to use /bentobox version default: op - + bentobox.perms: + description: Allow use of '/bentobox perms' command + default: OP From 09c60f24cf1f404156a334658c3b35bdcec0bf89 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 18 Jun 2023 20:58:52 -0700 Subject: [PATCH 12/36] Add IslandInfoEvent to allow addons to add to the island info command --- .../api/events/island/IslandEvent.java | 8 +++- .../api/events/island/IslandInfoEvent.java | 40 +++++++++++++++++++ .../bentobox/bentobox/util/IslandInfo.java | 16 ++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java diff --git a/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java b/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java index 45d190c2d..6077e5da5 100644 --- a/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java +++ b/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java @@ -175,7 +175,12 @@ public class IslandEvent extends IslandBaseEvent { * Event that will fire when an island is named or renamed * @since 1.24.0 */ - NAME + NAME, + /** + * Event that will fire when the info command is executed. Allows addons to add to it + * @since 1.24.0 + */ + INFO } public static IslandEventBuilder builder() { @@ -350,6 +355,7 @@ public class IslandEvent extends IslandBaseEvent { case RANK_CHANGE -> new IslandRankChangeEvent(island, player, admin, location, oldRank, newRank); case NEW_ISLAND -> new IslandNewIslandEvent(island, player, admin, location); case NAME -> new IslandNameEvent(island, player, admin, location, previousName); + case INFO -> new IslandInfoEvent(island, player, admin, location); default -> new IslandGeneralEvent(island, player, admin, location); }; } diff --git a/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java b/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java new file mode 100644 index 000000000..d5078e4c1 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java @@ -0,0 +1,40 @@ +package world.bentobox.bentobox.api.events.island; + +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.event.HandlerList; +import org.eclipse.jdt.annotation.NonNull; + +import world.bentobox.bentobox.api.events.IslandBaseEvent; +import world.bentobox.bentobox.database.objects.Island; + +/** + * Fired when an a player reuqets info about an island + * Cancellation has no effect. + */ +public class IslandInfoEvent extends IslandBaseEvent { + + private static final HandlerList handlers = new HandlerList(); + + @Override + public @NonNull HandlerList getHandlers() { + return getHandlerList(); + } + + public static HandlerList getHandlerList() { + return handlers; + } + + /** + * @param island island + * @param player player asking for the info + * @param admin true if this is an admin request + * @param location location of the player asking for the info + */ + public IslandInfoEvent(Island island, UUID player, boolean admin, Location location) { + // Final variables have to be declared in the constructor + super(island, player, admin, location); + } + +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/util/IslandInfo.java b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java index e772b6ba5..8f5933e6a 100644 --- a/src/main/java/world/bentobox/bentobox/util/IslandInfo.java +++ b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java @@ -10,6 +10,7 @@ import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; @@ -98,6 +99,14 @@ public class IslandInfo { if (island.getPurgeProtected()) { user.sendMessage("commands.admin.info.purge-protected"); } + // Fire info event + IslandEvent.builder() + .island(island) + .location(island.getCenter()) + .reason(IslandEvent.Reason.INFO) + .involvedPlayer(user.getUniqueId()) + .admin(true) + .build(); } @@ -130,6 +139,13 @@ public class IslandInfo { user.sendMessage("commands.admin.info.banned-players"); island.getBanned().forEach(u -> user.sendMessage("commands.admin.info.banned-format", TextVariables.NAME, plugin.getPlayers().getName(u))); } + // Fire info event + IslandEvent.builder() + .island(island) + .location(island.getCenter()) + .reason(IslandEvent.Reason.INFO) + .involvedPlayer(user.getUniqueId()) + .build(); return true; } From a90a00b09b637a2b1c98ca3eed2ba43cf9cfae03 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 19 Jun 2023 09:36:45 -0700 Subject: [PATCH 13/36] Fix new permission tests --- .../admin/AdminTeleportCommandTest.java | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommandTest.java index a585c5817..bb4b800fe 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminTeleportCommandTest.java @@ -13,6 +13,7 @@ import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -115,6 +116,7 @@ public class AdminTeleportCommandTest { // Parent command has no aliases when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); when(ac.getTopLabel()).thenReturn("bskyblock"); + when(ac.getLabel()).thenReturn("bskyblock"); when(ac.getWorld()).thenReturn(world); when(ac.getPermission()).thenReturn("admin"); @@ -148,7 +150,7 @@ public class AdminTeleportCommandTest { // Locales LocalesManager lm = mock(LocalesManager.class); - when(lm.get(any(), any())).thenReturn("mock translation"); + when(lm.get(any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); when(plugin.getLocalesManager()).thenReturn(lm); when(user.getTranslation(Mockito.anyString(),Mockito.anyString(), Mockito.anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); @@ -291,16 +293,35 @@ public class AdminTeleportCommandTest { } @Test - public void testPermissions() { + public void testPermissionsNoRootPermission() { + when(p.hasPermission("admin.tp")).thenReturn(true); + when(p.hasPermission("admin")).thenReturn(false); when(pm.getUUID(eq("tastybento"))).thenReturn(notUUID); when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); AdminTeleportCommand atc = new AdminTeleportCommand(ac,"tpend"); - assertTrue(atc.canExecute(user, "tpend", Collections.singletonList("tastybento"))); - String[] list = new String[1]; - list[0] = "tastybento"; - System.out.println("UUID " + p.getUniqueId()); + assertTrue(atc.canExecute(user, "tpend", List.of("tastybento"))); + String[] list = new String[2]; + list[0] = "tpend"; + list[1] = "tastybento"; + // Should fail + assertFalse(atc.execute(p, "tpend", list)); + } + + @Test + public void testPermissionsHasRootPermission() { + when(p.hasPermission("admin.tp")).thenReturn(true); + when(p.hasPermission("admin")).thenReturn(true); + when(pm.getUUID(eq("tastybento"))).thenReturn(notUUID); + when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); + AdminTeleportCommand atc = new AdminTeleportCommand(ac,"tpend"); + assertTrue(atc.canExecute(user, "tpend", List.of("tastybento"))); + String[] list = new String[2]; + list[0] = "tpend"; + list[1] = "tastybento"; + // Should pass assertTrue(atc.execute(p, "tpend", list)); - verify(user).getTranslation(eq("commands.admin.tp.manual"), eq("[location]"), eq("0 0 0")); + verify(p).hasPermission("admin.tp"); + verify(p).hasPermission("admin"); } } From 5c2166fc93dfdaed1ca9423790dfc41185563025 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 19 Jun 2023 09:37:14 -0700 Subject: [PATCH 14/36] Add some NPE protection for variable substitutions --- src/main/java/world/bentobox/bentobox/api/user/User.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/world/bentobox/bentobox/api/user/User.java b/src/main/java/world/bentobox/bentobox/api/user/User.java index 7af3d6b8a..70bd4781b 100644 --- a/src/main/java/world/bentobox/bentobox/api/user/User.java +++ b/src/main/java/world/bentobox/bentobox/api/user/User.java @@ -472,7 +472,10 @@ public class User implements MetaDataAble { // Then replace variables if (variables.length > 1) { for (int i = 0; i < variables.length; i += 2) { - translation = translation.replace(variables[i], variables[i + 1]); + // Prevent a NPE if the substituting variable is null + if (variables[i + 1] != null) { + translation = translation.replace(variables[i], variables[i + 1]); + } } } From c0beba7467e521dbc6d1b5e233974211527f0423 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 19 Jun 2023 09:38:00 -0700 Subject: [PATCH 15/36] Formatting --- .../world/bentobox/bentobox/api/commands/CompositeCommand.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java index 8301b13b3..6158d6fab 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java @@ -241,7 +241,6 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi CompositeCommand cmd = getCommandFromArgs(args); String cmdLabel = (cmd.subCommandLevel > 0) ? args[cmd.subCommandLevel-1] : label; List cmdArgs = Arrays.asList(args).subList(cmd.subCommandLevel, args.length); - // Call return cmd.call(user, cmdLabel, cmdArgs); } @@ -261,13 +260,11 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi user.sendMessage("general.errors.use-in-game"); return false; } - if (!this.runPermissionCheck(user)) { // Error message is displayed by permission check. return false; } - // Set the user's addon context user.setAddon(addon); // Execute and trim args From 393a0131f98414502140f580654f6f34fe30a3b3 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 19 Jun 2023 09:38:49 -0700 Subject: [PATCH 16/36] Improved IslandInfoEvent to include the calling addon. This makes it easier for listeners to determine if they should react or not. --- .../api/commands/admin/AdminInfoCommand.java | 4 ++-- .../bentobox/api/events/island/IslandEvent.java | 17 ++++++++++++++++- .../api/events/island/IslandInfoEvent.java | 15 ++++++++++++++- .../bentobox/bentobox/util/IslandInfo.java | 7 +++++-- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommand.java index f335769cc..ca3281996 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminInfoCommand.java @@ -35,7 +35,7 @@ public class AdminInfoCommand extends CompositeCommand { } // If there are no args, then the player wants info on the island at this location if (args.isEmpty()) { - getIslands().getIslandAt(user.getLocation()).ifPresentOrElse(i -> new IslandInfo(i).showAdminInfo(user), () -> + getIslands().getIslandAt(user.getLocation()).ifPresentOrElse(i -> new IslandInfo(i).showAdminInfo(user, getAddon()), () -> user.sendMessage("commands.admin.info.no-island")); return true; } @@ -48,7 +48,7 @@ public class AdminInfoCommand extends CompositeCommand { // Show info for this player Island island = getIslands().getIsland(getWorld(), targetUUID); if (island != null) { - new IslandInfo(island).showAdminInfo(user); + new IslandInfo(island).showAdminInfo(user, getAddon()); if (!getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID).isEmpty()) { user.sendMessage("commands.admin.info.islands-in-trash"); } diff --git a/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java b/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java index 6077e5da5..15c0109db 100644 --- a/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java +++ b/src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java @@ -9,6 +9,7 @@ import org.bukkit.event.HandlerList; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle; import world.bentobox.bentobox.database.objects.Island; @@ -228,6 +229,10 @@ public class IslandEvent extends IslandBaseEvent { * @since 1.24.0 Previous name of island */ private String previousName; + /** + * @since 1.24.0 GameMode addon causing this event + */ + private Addon addon; public IslandEventBuilder island(Island island) { this.island = island; @@ -329,6 +334,16 @@ public class IslandEvent extends IslandBaseEvent { this.previousName = previousName; return this; } + + /** + * Addon that triggered this event, e.g. BSkyBlock + * @param addon Addon. + * @since 1.24.0 + */ + public IslandEventBuilder addon(Addon addon) { + this.addon = addon; + return this; + } private IslandBaseEvent getEvent() { return switch (reason) { @@ -355,7 +370,7 @@ public class IslandEvent extends IslandBaseEvent { case RANK_CHANGE -> new IslandRankChangeEvent(island, player, admin, location, oldRank, newRank); case NEW_ISLAND -> new IslandNewIslandEvent(island, player, admin, location); case NAME -> new IslandNameEvent(island, player, admin, location, previousName); - case INFO -> new IslandInfoEvent(island, player, admin, location); + case INFO -> new IslandInfoEvent(island, player, admin, location, addon); default -> new IslandGeneralEvent(island, player, admin, location); }; } diff --git a/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java b/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java index d5078e4c1..f990e54b4 100644 --- a/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java +++ b/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java @@ -6,6 +6,7 @@ import org.bukkit.Location; import org.bukkit.event.HandlerList; import org.eclipse.jdt.annotation.NonNull; +import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.database.objects.Island; @@ -15,6 +16,7 @@ import world.bentobox.bentobox.database.objects.Island; */ public class IslandInfoEvent extends IslandBaseEvent { + private final Addon addon; private static final HandlerList handlers = new HandlerList(); @Override @@ -31,10 +33,21 @@ public class IslandInfoEvent extends IslandBaseEvent { * @param player player asking for the info * @param admin true if this is an admin request * @param location location of the player asking for the info + * @param addon the addon parent that is calling this info command, e.g., BSkyBlock */ - public IslandInfoEvent(Island island, UUID player, boolean admin, Location location) { + public IslandInfoEvent(Island island, UUID player, boolean admin, Location location, Addon addon) { // Final variables have to be declared in the constructor super(island, player, admin, location); + this.addon = addon; } + /** + * @return the gameMode that is for this island + */ + public Addon getGameMode() { + return addon; + } + + + } \ No newline at end of file diff --git a/src/main/java/world/bentobox/bentobox/util/IslandInfo.java b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java index 8f5933e6a..42428beab 100644 --- a/src/main/java/world/bentobox/bentobox/util/IslandInfo.java +++ b/src/main/java/world/bentobox/bentobox/util/IslandInfo.java @@ -10,6 +10,7 @@ import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; @@ -44,8 +45,9 @@ public class IslandInfo { /** * Shows admin info of this island * @param user user asking + * @param addon Addon executing this command */ - public void showAdminInfo(User user) { + public void showAdminInfo(User user, Addon addon) { user.sendMessage("commands.admin.info.title"); user.sendMessage("commands.admin.info.island-uuid", TextVariables.UUID, island.getUniqueId()); if (owner == null) { @@ -99,12 +101,13 @@ public class IslandInfo { if (island.getPurgeProtected()) { user.sendMessage("commands.admin.info.purge-protected"); } - // Fire info event + // Fire info event to allow other addons to add to info IslandEvent.builder() .island(island) .location(island.getCenter()) .reason(IslandEvent.Reason.INFO) .involvedPlayer(user.getUniqueId()) + .addon(addon) .admin(true) .build(); } From 372f3f14c68b4eaf486146db8eda9910ad05a670 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 19 Jun 2023 09:45:09 -0700 Subject: [PATCH 17/36] Rename getter to be more generic --- .../bentobox/bentobox/api/events/island/IslandInfoEvent.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java b/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java index f990e54b4..d2a38e611 100644 --- a/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java +++ b/src/main/java/world/bentobox/bentobox/api/events/island/IslandInfoEvent.java @@ -13,6 +13,7 @@ import world.bentobox.bentobox.database.objects.Island; /** * Fired when an a player reuqets info about an island * Cancellation has no effect. + * @since 1.24.0 */ public class IslandInfoEvent extends IslandBaseEvent { @@ -44,10 +45,11 @@ public class IslandInfoEvent extends IslandBaseEvent { /** * @return the gameMode that is for this island */ - public Addon getGameMode() { + public Addon getAddon() { return addon; } + } \ No newline at end of file From c6a8f7c0957f9f678cbf85fb12741677533c37cc Mon Sep 17 00:00:00 2001 From: tastybento Date: Tue, 20 Jun 2023 21:03:47 -0700 Subject: [PATCH 18/36] Add API to enable gamemodes to register ownership over additional worlds --- .../bentobox/bentobox/managers/IslandWorldManager.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java index cbb8a31d6..3b2678e8b 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java @@ -143,6 +143,16 @@ public class IslandWorldManager { .anyMatch(gm -> gm.getWorldSettings().getFriendlyName().equalsIgnoreCase(name)); } + /** + * Associate a world with a game mode. This enables game modes to register more worlds than just the standard + * overworld, nether, and end worlds. + * @param world world + * @param gameMode game mode + * @since 1.24.0 + */ + public void addWorld(World world, GameModeAddon gameMode) { + gameModes.put(world, gameMode); + } /** * Adds a GameMode to island world manager From bcb4ed28b88de92f7e7add6e8c4f8f62a988e30b Mon Sep 17 00:00:00 2001 From: tastybento Date: Tue, 20 Jun 2023 22:15:53 -0700 Subject: [PATCH 19/36] Fixes #2142 (#2144) Fixes the logic and adds a bunch of tests to prove the logic works. --- .../bentobox/listeners/BannedCommands.java | 13 ++- .../listeners/BannedCommandsTest.java | 100 ++++++++++++++++++ 2 files changed, 106 insertions(+), 7 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/listeners/BannedCommands.java b/src/main/java/world/bentobox/bentobox/listeners/BannedCommands.java index c644eb1a5..cb3678ee3 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/BannedCommands.java +++ b/src/main/java/world/bentobox/bentobox/listeners/BannedCommands.java @@ -57,20 +57,19 @@ public class BannedCommands implements Listener { } private boolean checkCmd(String cmd, String[] args) { - // Commands are guilty until proven innocent :-) - boolean banned = true; // Get the elements of the banned command by splitting it String[] bannedSplit = cmd.toLowerCase(java.util.Locale.ENGLISH).split(" "); // If the banned command has the same number of elements or less than the entered command then it may be banned - if (bannedSplit.length <= args.length) { + if (bannedSplit.length <= args.length) { for (int i = 0; i < bannedSplit.length; i++) { - if (!bannedSplit[i].equals(args[i])) { - banned = false; - break; + if (!bannedSplit[i].equalsIgnoreCase(args[i])) { + return false; } } + } else { + return false; } - return banned; + return true; } /** diff --git a/src/test/java/world/bentobox/bentobox/listeners/BannedCommandsTest.java b/src/test/java/world/bentobox/bentobox/listeners/BannedCommandsTest.java index 0061942f3..40e69ae69 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/BannedCommandsTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/BannedCommandsTest.java @@ -262,6 +262,106 @@ public class BannedCommandsTest { } + /** + * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} + */ + @Test + public void testBannedCommandsWithBannedCommand2() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/spawn"); + BannedCommands bvc = new BannedCommands(plugin); + List banned = new ArrayList<>(); + banned.add("cmi sethome"); + when(iwm.getVisitorBannedCommands(any())).thenReturn(banned); + bvc.onVisitorCommand(e); + verify(iwm).getVisitorBannedCommands(any()); + assertFalse(e.isCancelled()); + + } + + /** + * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} + */ + @Test + public void testBannedCommandsWithBannedCommand3() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/cmi sethome"); + BannedCommands bvc = new BannedCommands(plugin); + List banned = new ArrayList<>(); + banned.add("cmi sethome"); + when(iwm.getVisitorBannedCommands(any())).thenReturn(banned); + bvc.onVisitorCommand(e); + verify(iwm).getVisitorBannedCommands(any()); + assertTrue(e.isCancelled()); + + } + + /** + * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} + */ + @Test + public void testBannedCommandsWithBannedComman4() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/cmi"); + BannedCommands bvc = new BannedCommands(plugin); + List banned = new ArrayList<>(); + banned.add("cmi sethome"); + when(iwm.getVisitorBannedCommands(any())).thenReturn(banned); + bvc.onVisitorCommand(e); + verify(iwm).getVisitorBannedCommands(any()); + assertFalse(e.isCancelled()); + + } + + /** + * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} + */ + @Test + public void testBannedCommandsWithBannedCommand5() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/cmi homey"); + BannedCommands bvc = new BannedCommands(plugin); + List banned = new ArrayList<>(); + banned.add("cmi sethome"); + when(iwm.getVisitorBannedCommands(any())).thenReturn(banned); + bvc.onVisitorCommand(e); + verify(iwm).getVisitorBannedCommands(any()); + assertFalse(e.isCancelled()); + + } + + /** + * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} + */ + @Test + public void testBannedCommandsWithBannedCommand6() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/spawn"); + BannedCommands bvc = new BannedCommands(plugin); + List banned = new ArrayList<>(); + banned.add("cmi sethome"); + banned.add("spawn sethome now"); + when(iwm.getVisitorBannedCommands(any())).thenReturn(banned); + bvc.onVisitorCommand(e); + verify(iwm).getVisitorBannedCommands(any()); + assertFalse(e.isCancelled()); + + } + + /** + * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} + */ + @Test + public void testBannedCommandsWithBannedCommand7() { + PlayerCommandPreprocessEvent e = new PlayerCommandPreprocessEvent(player, "/spawn"); + BannedCommands bvc = new BannedCommands(plugin); + List banned = new ArrayList<>(); + banned.add("cmi sethome"); + banned.add("spawn sethome now"); + banned.add("cmi multi now"); + when(iwm.getVisitorBannedCommands(any())).thenReturn(banned); + bvc.onVisitorCommand(e); + verify(iwm).getVisitorBannedCommands(any()); + assertFalse(e.isCancelled()); + + } + + /** * Test for {@link BannedCommands#onCommand(PlayerCommandPreprocessEvent)} */ From 4d295068cf4a8f149a09a85873d1409af5e0ea22 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 24 Jun 2023 09:45:40 -0700 Subject: [PATCH 20/36] Fix onlyConsole usage and added test class --- .../commands/BentoBoxPermsCommand.java | 6 +- .../commands/BentoBoxPermsCommandTest.java | 192 ++++++++++++++++++ 2 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 src/test/java/world/bentobox/bentobox/commands/BentoBoxPermsCommandTest.java diff --git a/src/main/java/world/bentobox/bentobox/commands/BentoBoxPermsCommand.java b/src/main/java/world/bentobox/bentobox/commands/BentoBoxPermsCommand.java index a0c7a64dc..fc20c211f 100644 --- a/src/main/java/world/bentobox/bentobox/commands/BentoBoxPermsCommand.java +++ b/src/main/java/world/bentobox/bentobox/commands/BentoBoxPermsCommand.java @@ -26,8 +26,10 @@ public class BentoBoxPermsCommand extends CompositeCommand { @Override public void setup() { - setPermission("bentobox.perms"); - this.isOnlyConsole(); + setPermission("bentobox.admin.perms"); + setParametersHelp("commands.bentobox.perms.parameters"); + setDescription("commands.bentobox.perms.description"); + this.setOnlyConsole(true); } @Override diff --git a/src/test/java/world/bentobox/bentobox/commands/BentoBoxPermsCommandTest.java b/src/test/java/world/bentobox/bentobox/commands/BentoBoxPermsCommandTest.java new file mode 100644 index 000000000..0604f1af8 --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/commands/BentoBoxPermsCommandTest.java @@ -0,0 +1,192 @@ +package world.bentobox.bentobox.commands; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.plugin.PluginManager; +import org.eclipse.jdt.annotation.NonNull; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.LocalesManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, User.class }) +public class BentoBoxPermsCommandTest { + + @Mock + private BentoBox plugin; + @Mock + private CompositeCommand ac; + @Mock + private User user; + @Mock + private LocalesManager lm; + + BentoBoxPermsCommand cmd; + @Mock + private PlaceholdersManager phm; + @Mock + private PluginManager pim; + @Mock + private Permission perm; + + private PermissionDefault defaultPerm = PermissionDefault.OP; + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + @NonNull + Map cmdMap = new HashMap<>(); + cmdMap.put("test", ac); + when(cm.getCommands()).thenReturn(cmdMap); + + + // Parent command has no aliases + when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); + when(ac.getSubCommands()).thenReturn(new HashMap<>()); + when(ac.getLabel()).thenReturn("bbox"); + when(ac.getTopLabel()).thenReturn("bbox"); + when(ac.getPermission()).thenReturn("admin.bbox"); + when(ac.getDescription()).thenReturn("description"); + + + // User + when(user.getTranslation(Mockito.anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + when(user.isPlayer()).thenReturn(false); + User.setPlugin(plugin); + + // Bukkit + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + when(perm.getDefault()).thenReturn(defaultPerm); + when(pim.getPermission(anyString())).thenReturn(perm); + when(Bukkit.getPluginManager()).thenReturn(pim); + + // Placeholders + when(phm.replacePlaceholders(any(), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); + + // BentoBox + when(plugin.getLocalesManager()).thenReturn(lm); + when(plugin.getPlaceholdersManager()).thenReturn(phm); + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + + // Commands for perms + + + cmd = new BentoBoxPermsCommand(ac); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + Mockito.framework().clearInlineMocks(); + } + + /** + * Test method for {@link world.bentobox.bentobox.commands.BentoBoxPermsCommand#BentoBoxPermsCommand(world.bentobox.bentobox.api.commands.CompositeCommand)}. + */ + @Test + public void testBentoBoxPermsCommand() { + assertNotNull(cmd); + } + + /** + * Test method for {@link world.bentobox.bentobox.commands.BentoBoxPermsCommand#setup()}. + */ + @Test + public void testSetup() { + assertTrue(cmd.isOnlyConsole()); + assertFalse(cmd.isOnlyPlayer()); + assertEquals("bentobox.admin.perms", cmd.getPermission()); + assertEquals("commands.bentobox.perms.description", cmd.getDescription()); + assertEquals("commands.bentobox.perms.parameters", cmd.getParameters()); + } + + /** + * Test method for {@link world.bentobox.bentobox.commands.BentoBoxPermsCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfString() { + assertTrue(cmd.execute(user, "perms", List.of())); + verify(user).sendMessage("*** BentoBox effective perms:"); + verify(user).sendRawMessage("permissions:"); + verify(user).sendRawMessage(" admin.bbox:"); + verify(user).sendRawMessage(" description: Allow use of '/bbox' command - null"); + verify(user).sendRawMessage(" bentobox.admin.perms:"); + verify(user).sendRawMessage(" description: Allow use of '/bbox perms' command - null"); + verify(user, times(2)).sendRawMessage(" default: OP"); + + } + + /** + * Test method for {@link world.bentobox.bentobox.commands.BentoBoxPermsCommand#execute(Player, java.lang.String, String[])}. + */ + @Test + public void testExecuteUserStringListOfStringConsole() { + String[] args = new String[1]; + args[0] = ""; + CommandSender p = mock(CommandSender.class); + assertTrue(cmd.execute(p, "perms", args)); + verify(p, never()).sendMessage("general.errors.use-in-console"); + } + + /** + * Test method for {@link world.bentobox.bentobox.commands.BentoBoxPermsCommand#execute(Player, java.lang.String, String[])}. + */ + @Test + public void testExecuteUserStringListOfStringIsPlayer() { + when(user.isPlayer()).thenReturn(true); + String[] args = new String[1]; + args[0] = ""; + Player p = mock(Player.class); + assertFalse(cmd.execute(p, "perms", args)); + verify(p).sendMessage("general.errors.use-in-console"); + } +} From 0856d484706317843b857c76dfe9b2928babe216 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 24 Jun 2023 10:13:11 -0700 Subject: [PATCH 21/36] Proactively clear Users before tests just in case. --- .../bentobox/bentobox/listeners/flags/AbstractCommonSetup.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java b/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java index 84613ccf3..e780d3b84 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/AbstractCommonSetup.java @@ -123,6 +123,7 @@ public abstract class AbstractCommonSetup { when(player.getInventory()).thenReturn(inv); User.setPlugin(plugin); + User.clearUsers(); User.getInstance(player); // IWM From b50063685f0459f0df1ec090bab1e4cb80cc7b84 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 24 Jun 2023 10:31:51 -0700 Subject: [PATCH 22/36] If sign is waxed (not editable) then no check is required --- .../protection/BlockInteractionListener.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java index 400bd8eb3..f906d2d8f 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java @@ -7,6 +7,7 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.block.Block; +import org.bukkit.block.Sign; import org.bukkit.block.data.Waterlogged; import org.bukkit.entity.Player; import org.bukkit.event.Event; @@ -33,7 +34,7 @@ public class BlockInteractionListener extends FlagListener * * @param e - event */ - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onPlayerInteract(final PlayerInteractEvent e) { // We only care about the RIGHT_CLICK_BLOCK action. @@ -43,7 +44,7 @@ public class BlockInteractionListener extends FlagListener } // Check clicked block - this.checkClickedBlock(e, e.getPlayer(), e.getClickedBlock().getLocation(), e.getClickedBlock().getType()); + this.checkClickedBlock(e, e.getPlayer(), e.getClickedBlock()); // Now check for in-hand items if (e.getItem() != null && !e.getItem().getType().equals(Material.AIR)) @@ -85,11 +86,12 @@ public class BlockInteractionListener extends FlagListener * * @param e - event called * @param player - player - * @param loc - location of clicked block - * @param type - material type of clicked block + * @param block - block being clicked or used */ - private void checkClickedBlock(Event e, Player player, Location loc, Material type) + private void checkClickedBlock(Event e, Player player, Block block) { + Material type = block.getType(); + Location loc = block.getLocation(); // Handle pots if (type.name().startsWith("POTTED")) { @@ -133,7 +135,8 @@ public class BlockInteractionListener extends FlagListener return; } - if (Tag.SIGNS.isTagged(type)) { + if (Tag.SIGNS.isTagged(type) && block instanceof Sign sign && sign.isEditable()) { + // If waxed, then sign cannot be edited otherwise check this.checkIsland(e, player, loc, Flags.SIGN_EDITING); return; } @@ -232,7 +235,7 @@ public class BlockInteractionListener extends FlagListener @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onBlockBreak(final BlockBreakEvent e) { - this.checkClickedBlock(e, e.getPlayer(), e.getBlock().getLocation(), e.getBlock().getType()); + this.checkClickedBlock(e, e.getPlayer(), e.getBlock()); } From 89a9b13d81447021970b470bfdf6588b05b33b44 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 24 Jun 2023 10:40:44 -0700 Subject: [PATCH 23/36] Hidden commands can be seen by console, but are hidden from players --- .../api/commands/HiddenCommandTest.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/test/java/world/bentobox/bentobox/api/commands/HiddenCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/HiddenCommandTest.java index d00950143..3809a61f9 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/HiddenCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/HiddenCommandTest.java @@ -13,6 +13,7 @@ import static org.mockito.Mockito.when; import java.util.List; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -85,6 +86,21 @@ public class HiddenCommandTest { CommandSender sender = mock(CommandSender.class); String[] args = {"h"}; List opList = tlc.tabComplete(sender, "top", args); + assertEquals(2, opList.size()); + assertEquals("help", opList.get(0)); // Console can see all commands + assertEquals("hidden", opList.get(1)); // Console can see all commands + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.CompositeCommand#tabComplete(Player, java.lang.String, java.lang.String[])}. + */ + @Test + public void testTabCompletePlayerStringStringArrayHidden() { + TopLevelCommand tlc = new TopLevelCommand(); + Player sender = mock(Player.class); + String[] args = {"h"}; + List opList = tlc.tabComplete(sender, "top", args); + opList.forEach(System.out::println); assertEquals(1, opList.size()); assertEquals("help", opList.get(0)); // Only help } @@ -95,7 +111,7 @@ public class HiddenCommandTest { @Test public void testTabCompleteCommandSenderStringStringArrayInvisible() { TopLevelCommand tlc = new TopLevelCommand(); - CommandSender sender = mock(CommandSender.class); + Player sender = mock(Player.class); String[] args = {"i"}; List opList = tlc.tabComplete(sender, "top", args); assertTrue(opList.isEmpty()); From 2b4e1dd5ebf73670b344017387c3230a0046b9a7 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 24 Jun 2023 11:11:51 -0700 Subject: [PATCH 24/36] Update to 1.21.1 to get isWaxed API Remove 1.20 from server compatibility as a result. --- pom.xml | 4 ++-- .../listeners/flags/protection/BlockInteractionListener.java | 2 +- .../world/bentobox/bentobox/versions/ServerCompatibility.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 1f2a80388..d7c1a3747 100644 --- a/pom.xml +++ b/pom.xml @@ -73,10 +73,10 @@ 42.2.18 5.0.1 - 1.20-R0.1-SNAPSHOT + 1.20.1-R0.1-SNAPSHOT - 1.20-R0.1-SNAPSHOT + 1.20.1-R0.1-SNAPSHOT 3.0.0 1.7.1 2.10.9 diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java index f906d2d8f..f7bcdc286 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java @@ -135,7 +135,7 @@ public class BlockInteractionListener extends FlagListener return; } - if (Tag.SIGNS.isTagged(type) && block instanceof Sign sign && sign.isEditable()) { + if (Tag.SIGNS.isTagged(type) && block.getState() instanceof Sign sign && !sign.isWaxed()) { // If waxed, then sign cannot be edited otherwise check this.checkIsland(e, player, loc, Flags.SIGN_EDITING); return; diff --git a/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java b/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java index 664003420..feb5a270e 100644 --- a/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java +++ b/src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java @@ -221,7 +221,7 @@ public class ServerCompatibility { /** * @since 1.24.0 */ - V1_20(Compatibility.COMPATIBLE), + V1_20(Compatibility.INCOMPATIBLE), /** * @since 1.24.0 */ From 74e053ef8ae076d6733a2ce50ae50b571d328153 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 24 Jun 2023 11:31:14 -0700 Subject: [PATCH 25/36] Protect against brushing Fixes #2138 --- .../flags/protection/BlockInteractionListener.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java index f7bcdc286..d2edf16b8 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java @@ -7,6 +7,7 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.block.Block; +import org.bukkit.block.BrushableBlock; import org.bukkit.block.Sign; import org.bukkit.block.data.Waterlogged; import org.bukkit.entity.Player; @@ -98,6 +99,12 @@ public class BlockInteractionListener extends FlagListener this.checkIsland(e, player, loc, Flags.FLOWER_POT); return; } + + if (block.getState() instanceof BrushableBlock bb && BlockInteractionListener.holds(player, Material.BRUSH)) { + // Protect this using break blocks flag for now. Maybe in the future it can have its own flag. + this.checkIsland(e, player, loc, Flags.BREAK_BLOCKS); + return; + } if (Tag.ANVIL.isTagged(type)) { From 1a416d9a23164c8c463bfde817683b4f134b1a7e Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 24 Jun 2023 12:11:08 -0700 Subject: [PATCH 26/36] Fixes #2137 adds calibrated sculk sensor and test class --- .../bentobox/api/flags/FlagListener.java | 1 + .../flags/protection/SculkSensorListener.java | 16 +- .../protection/SculkSensorListenerTest.java | 158 ++++++++++++++++++ 3 files changed, 169 insertions(+), 6 deletions(-) create mode 100644 src/test/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListenerTest.java diff --git a/src/main/java/world/bentobox/bentobox/api/flags/FlagListener.java b/src/main/java/world/bentobox/bentobox/api/flags/FlagListener.java index b1fbd273b..369f700f0 100644 --- a/src/main/java/world/bentobox/bentobox/api/flags/FlagListener.java +++ b/src/main/java/world/bentobox/bentobox/api/flags/FlagListener.java @@ -125,6 +125,7 @@ public abstract class FlagListener implements Listener { * @return true if the check is okay, false if it was disallowed */ public boolean checkIsland(@NonNull Event e, @Nullable Player player, @Nullable Location loc, @NonNull Flag flag, boolean silent) { + // Set user user = player == null ? null : User.getInstance(player); if (loc == null) { diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListener.java index f40a07ea4..4dd960a1e 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListener.java @@ -13,6 +13,8 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.block.BlockReceiveGameEvent; +import com.google.common.base.Enums; + import world.bentobox.bentobox.api.flags.FlagListener; import world.bentobox.bentobox.lists.Flags; @@ -25,20 +27,22 @@ public class SculkSensorListener extends FlagListener /** * This listener detects if a visitor activates sculk sensor, and block it, if required. * @param event Sculk activation event. + * @return true if the check is okay, false if it was disallowed */ @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onSculkSensor(BlockReceiveGameEvent event) + public boolean onSculkSensor(BlockReceiveGameEvent event) { if (!this.getIWM().inWorld(event.getBlock().getWorld())) { - return; + return true; } - if (event.getBlock().getType() == Material.SCULK_SENSOR && - event.getEntity() != null && - event.getEntity() instanceof Player player) + if ((event.getBlock().getType() == Material.SCULK_SENSOR + || event.getBlock().getType() == Enums.getIfPresent(Material.class, "CALIBRATED_SCULK_SENSOR").or(Material.SCULK_SENSOR)) + && event.getEntity() != null && event.getEntity() instanceof Player player) { - this.checkIsland(event, player, event.getBlock().getLocation(), Flags.SCULK_SENSOR, true); + return this.checkIsland(event, player, event.getBlock().getLocation(), Flags.SCULK_SENSOR, true); } + return true; } } diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListenerTest.java new file mode 100644 index 000000000..417200ce3 --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListenerTest.java @@ -0,0 +1,158 @@ +package world.bentobox.bentobox.listeners.flags.protection; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.bukkit.Bukkit; +import org.bukkit.GameEvent; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.event.block.BlockReceiveGameEvent; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.listeners.flags.AbstractCommonSetup; +import world.bentobox.bentobox.lists.Flags; +import world.bentobox.bentobox.util.Util; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest( {BentoBox.class, Flags.class, Util.class, Bukkit.class} ) +public class SculkSensorListenerTest extends AbstractCommonSetup { + + private SculkSensorListener ssl; + @Mock + private Block block; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + super.setUp(); + // Default is that everything is allowed + when(island.isAllowed(any(), any())).thenReturn(true); + + // In world + when(iwm.inWorld(any(World.class))).thenReturn(true); + + // Block + when(block.getType()).thenReturn(Material.SCULK_SENSOR); + when(block.getWorld()).thenReturn(world); + when(block.getLocation()).thenReturn(location); + + // User + when(player.getWorld()).thenReturn(world); + when(player.getLocation()).thenReturn(location); + User.getInstance(player); + + ssl = new SculkSensorListener(); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorNotAllowed() { + when(island.isAllowed(any(), any())).thenReturn(false); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + assertFalse(ssl.onSculkSensor(e)); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorAllowed() { + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + assertTrue(ssl.onSculkSensor(e)); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorNotInWorld() { + when(iwm.inWorld(any(World.class))).thenReturn(false); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + assertTrue(ssl.onSculkSensor(e)); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorNotAllowedCalibrated() { + when(block.getType()).thenReturn(Material.CALIBRATED_SCULK_SENSOR); + when(island.isAllowed(any(), any())).thenReturn(false); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + assertFalse(ssl.onSculkSensor(e)); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorAllowedCalibrated() { + when(block.getType()).thenReturn(Material.CALIBRATED_SCULK_SENSOR); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + assertTrue(ssl.onSculkSensor(e)); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorNotInWorldCalibrated() { + when(block.getType()).thenReturn(Material.CALIBRATED_SCULK_SENSOR); + when(iwm.inWorld(any(World.class))).thenReturn(false); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + assertTrue(ssl.onSculkSensor(e)); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorNotAllowedNotSculk() { + when(block.getType()).thenReturn(Material.SHULKER_BOX); + when(island.isAllowed(any(), any())).thenReturn(false); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + assertTrue(ssl.onSculkSensor(e)); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorAllowedNotSculk() { + when(block.getType()).thenReturn(Material.SHULKER_BOX); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + assertTrue(ssl.onSculkSensor(e)); + } + + /** + * Test method for {@link world.bentobox.bentobox.listeners.flags.protection.SculkSensorListener#onSculkSensor(org.bukkit.event.block.BlockReceiveGameEvent)}. + */ + @Test + public void testOnSculkSensorNotInWorldNotSculk() { + when(block.getType()).thenReturn(Material.SHULKER_BOX); + when(iwm.inWorld(any(World.class))).thenReturn(false); + BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); + assertTrue(ssl.onSculkSensor(e)); + } + +} From 0038c3f2b7fd7cc375bbdc0edac356c6ede6c4f4 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 24 Jun 2023 15:22:23 -0700 Subject: [PATCH 27/36] Remove boolean return for listener. Check event cancelation instead. --- .../flags/protection/SculkSensorListener.java | 9 +++--- .../protection/SculkSensorListenerTest.java | 32 ++++++++++++------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListener.java index 4dd960a1e..8bef10902 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListener.java @@ -27,22 +27,21 @@ public class SculkSensorListener extends FlagListener /** * This listener detects if a visitor activates sculk sensor, and block it, if required. * @param event Sculk activation event. - * @return true if the check is okay, false if it was disallowed */ @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public boolean onSculkSensor(BlockReceiveGameEvent event) + public void onSculkSensor(BlockReceiveGameEvent event) { if (!this.getIWM().inWorld(event.getBlock().getWorld())) { - return true; + return; } if ((event.getBlock().getType() == Material.SCULK_SENSOR || event.getBlock().getType() == Enums.getIfPresent(Material.class, "CALIBRATED_SCULK_SENSOR").or(Material.SCULK_SENSOR)) && event.getEntity() != null && event.getEntity() instanceof Player player) { - return this.checkIsland(event, player, event.getBlock().getLocation(), Flags.SCULK_SENSOR, true); + this.checkIsland(event, player, event.getBlock().getLocation(), Flags.SCULK_SENSOR, true); } - return true; + return; } } diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListenerTest.java index 417200ce3..9e8443725 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/SculkSensorListenerTest.java @@ -1,9 +1,8 @@ package world.bentobox.bentobox.listeners.flags.protection; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.bukkit.Bukkit; @@ -69,7 +68,8 @@ public class SculkSensorListenerTest extends AbstractCommonSetup { public void testOnSculkSensorNotAllowed() { when(island.isAllowed(any(), any())).thenReturn(false); BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); - assertFalse(ssl.onSculkSensor(e)); + ssl.onSculkSensor(e); + assertTrue(e.isCancelled()); } /** @@ -78,7 +78,8 @@ public class SculkSensorListenerTest extends AbstractCommonSetup { @Test public void testOnSculkSensorAllowed() { BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); - assertTrue(ssl.onSculkSensor(e)); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); } /** @@ -88,7 +89,8 @@ public class SculkSensorListenerTest extends AbstractCommonSetup { public void testOnSculkSensorNotInWorld() { when(iwm.inWorld(any(World.class))).thenReturn(false); BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); - assertTrue(ssl.onSculkSensor(e)); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); } /** @@ -99,7 +101,8 @@ public class SculkSensorListenerTest extends AbstractCommonSetup { when(block.getType()).thenReturn(Material.CALIBRATED_SCULK_SENSOR); when(island.isAllowed(any(), any())).thenReturn(false); BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); - assertFalse(ssl.onSculkSensor(e)); + ssl.onSculkSensor(e); + assertTrue(e.isCancelled()); } /** @@ -109,7 +112,8 @@ public class SculkSensorListenerTest extends AbstractCommonSetup { public void testOnSculkSensorAllowedCalibrated() { when(block.getType()).thenReturn(Material.CALIBRATED_SCULK_SENSOR); BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); - assertTrue(ssl.onSculkSensor(e)); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); } /** @@ -120,7 +124,8 @@ public class SculkSensorListenerTest extends AbstractCommonSetup { when(block.getType()).thenReturn(Material.CALIBRATED_SCULK_SENSOR); when(iwm.inWorld(any(World.class))).thenReturn(false); BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); - assertTrue(ssl.onSculkSensor(e)); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); } /** @@ -131,7 +136,8 @@ public class SculkSensorListenerTest extends AbstractCommonSetup { when(block.getType()).thenReturn(Material.SHULKER_BOX); when(island.isAllowed(any(), any())).thenReturn(false); BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); - assertTrue(ssl.onSculkSensor(e)); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); } /** @@ -141,7 +147,8 @@ public class SculkSensorListenerTest extends AbstractCommonSetup { public void testOnSculkSensorAllowedNotSculk() { when(block.getType()).thenReturn(Material.SHULKER_BOX); BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); - assertTrue(ssl.onSculkSensor(e)); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); } /** @@ -152,7 +159,8 @@ public class SculkSensorListenerTest extends AbstractCommonSetup { when(block.getType()).thenReturn(Material.SHULKER_BOX); when(iwm.inWorld(any(World.class))).thenReturn(false); BlockReceiveGameEvent e = new BlockReceiveGameEvent(GameEvent.BLOCK_ACTIVATE, block, player); - assertTrue(ssl.onSculkSensor(e)); + ssl.onSculkSensor(e); + assertFalse(e.isCancelled()); } } From 4c0cb9f17cbd302df48ee2ca1300dc6d441cff00 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 26 Jun 2023 22:08:49 -0700 Subject: [PATCH 28/36] Unregisters worlds with MV on shutdown. Fixes #2149 --- .../world/bentobox/bentobox/BentoBox.java | 18 ++++- .../bentobox/hooks/MultiverseCoreHook.java | 6 ++ .../bentobox/hooks/WorldManagementHook.java | 9 +++ .../bentobox/managers/IslandWorldManager.java | 78 +++++++++++-------- 4 files changed, 76 insertions(+), 35 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java index 956ff030e..1caeea7fe 100644 --- a/src/main/java/world/bentobox/bentobox/BentoBox.java +++ b/src/main/java/world/bentobox/bentobox/BentoBox.java @@ -5,6 +5,9 @@ import java.util.Optional; import org.apache.commons.lang.exception.ExceptionUtils; import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.server.ServerCommandEvent; import org.bukkit.generator.ChunkGenerator; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -52,7 +55,7 @@ import world.bentobox.bentobox.versions.ServerCompatibility; * Main BentoBox class * @author tastybento, Poslovitch */ -public class BentoBox extends JavaPlugin { +public class BentoBox extends JavaPlugin implements Listener { private static BentoBox instance; @@ -227,7 +230,7 @@ public class BentoBox extends JavaPlugin { // Make sure all worlds are already registered to Multiverse. hooksManager.registerHook(new MultiverseCoreHook()); hooksManager.registerHook(new MyWorldsHook()); - islandWorldManager.registerWorldsToMultiverse(); + islandWorldManager.registerWorldsToMultiverse(true); // TODO: re-enable after implementation //hooksManager.registerHook(new DynmapHook()); @@ -300,6 +303,8 @@ public class BentoBox extends JavaPlugin { manager.registerEvents(new BannedCommands(this), this); // Death counter manager.registerEvents(new DeathListener(this), this); + // MV unregister + manager.registerEvents(this, this); // Island Delete Manager islandChunkDeletionManager = new IslandChunkDeletionManager(this); islandDeletionManager = new IslandDeletionManager(this); @@ -321,6 +326,15 @@ public class BentoBox extends JavaPlugin { if (islandsManager != null) { islandsManager.shutdown(); } + + } + + @EventHandler + public void onServerStop(ServerCommandEvent e) { + if (islandWorldManager != null && (e.getCommand().equalsIgnoreCase("stop") || e.getCommand().equalsIgnoreCase("restart"))) { + // Unregister any MV worlds if () { + islandWorldManager.registerWorldsToMultiverse(false); + } } /** diff --git a/src/main/java/world/bentobox/bentobox/hooks/MultiverseCoreHook.java b/src/main/java/world/bentobox/bentobox/hooks/MultiverseCoreHook.java index 62ba2abe9..f58bfd87f 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/MultiverseCoreHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/MultiverseCoreHook.java @@ -49,6 +49,12 @@ public class MultiverseCoreHook extends Hook implements WorldManagementHook { } } + @Override + public void unregisterWorld(World world) { + String cmd = "mv remove " + world.getName(); + Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), cmd); + } + @Override public boolean hook() { return true; // The hook process shouldn't fail diff --git a/src/main/java/world/bentobox/bentobox/hooks/WorldManagementHook.java b/src/main/java/world/bentobox/bentobox/hooks/WorldManagementHook.java index 4f79a96c3..daa9fcaa2 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/WorldManagementHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/WorldManagementHook.java @@ -13,8 +13,17 @@ public interface WorldManagementHook { /** * Register the world with the World Management hook * + * * @param world - world to register * @param islandWorld - if true, then this is an island world */ void registerWorld(World world, boolean islandWorld); + + /** + * Unregisters a world. + * @param world - world to unregister + */ + default void unregisterWorld(World world) { + // Do nothing + } } diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java index 3b2678e8b..cdc64a64a 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java @@ -50,36 +50,48 @@ public class IslandWorldManager { gameModes = new HashMap<>(); } - public void registerWorldsToMultiverse() { + /** + * Registers or unregisters worlds with world management plugins + * + * @param reg true to register, false to remove registration + * + * Updated 1.24.0 + */ + public void registerWorldsToMultiverse(boolean reg) { gameModes.values().stream().distinct().forEach(gm -> { - registerToWorldManagementPlugins(gm.getOverWorld(), true); + registerToWorldManagementPlugins(gm.getOverWorld(), true, reg); if (gm.getWorldSettings().isNetherGenerate()) { - registerToWorldManagementPlugins(gm.getNetherWorld(), gm.getWorldSettings().isNetherIslands()); + registerToWorldManagementPlugins(gm.getNetherWorld(), gm.getWorldSettings().isNetherIslands(), reg); } if (gm.getWorldSettings().isEndGenerate()) { - registerToWorldManagementPlugins(gm.getEndWorld(), gm.getWorldSettings().isEndIslands()); + registerToWorldManagementPlugins(gm.getEndWorld(), gm.getWorldSettings().isEndIslands(), reg); } }); } - /** - * Registers a world with world management plugins - * - * @param world the World to register - * @param islandWorld true if this is an island world - */ - private void registerToWorldManagementPlugins(@NonNull World world, boolean islandWorld) { - if (plugin.getHooks() != null) { - for (Hook hook : plugin.getHooks().getHooks()) { - if (hook instanceof final WorldManagementHook worldManagementHook) { - if (Bukkit.isPrimaryThread()) { - worldManagementHook.registerWorld(world, islandWorld); - } else { - Bukkit.getScheduler().runTask(plugin, () -> worldManagementHook.registerWorld(world, islandWorld)); - } + + private void registerToWorldManagementPlugins(@NonNull World world, boolean islandWorld, boolean reg) { + if (plugin.getHooks() == null) { + return; + } + for (Hook hook : plugin.getHooks().getHooks()) { + if (hook instanceof final WorldManagementHook worldManagementHook) { + if (Bukkit.isPrimaryThread()) { + runTask(worldManagementHook, world, islandWorld, reg); + } else { + Bukkit.getScheduler().runTask(plugin, () -> runTask(worldManagementHook, world, islandWorld, reg)); } } } + + } + + private void runTask(WorldManagementHook worldManagementHook, @NonNull World world, boolean islandWorld, boolean reg) { + if (reg) { + worldManagementHook.registerWorld(world, islandWorld); + } else { + worldManagementHook.unregisterWorld(world); + } } /** @@ -169,27 +181,27 @@ public class IslandWorldManager { // Add worlds to map gameModes.put(world, gameMode); // Call Multiverse - registerToWorldManagementPlugins(world, true); + registerToWorldManagementPlugins(world, true, true); if (settings.isNetherGenerate()) { gameModes.put(gameMode.getNetherWorld(), gameMode); if (settings.isNetherIslands()) { - registerToWorldManagementPlugins(gameMode.getNetherWorld(), true); + registerToWorldManagementPlugins(gameMode.getNetherWorld(), true, true); } } if (settings.isEndGenerate()) { gameModes.put(gameMode.getEndWorld(), gameMode); if (settings.isEndIslands()) { - registerToWorldManagementPlugins(gameMode.getEndWorld(), true); + registerToWorldManagementPlugins(gameMode.getEndWorld(), true, true); } } // Set default island settings plugin.getFlagsManager().getFlags().stream(). - filter(f -> f.getType().equals(Flag.Type.PROTECTION)). - forEach(f -> settings.getDefaultIslandFlagNames().putIfAbsent(f.getID(), f.getDefaultRank())); + filter(f -> f.getType().equals(Flag.Type.PROTECTION)). + forEach(f -> settings.getDefaultIslandFlagNames().putIfAbsent(f.getID(), f.getDefaultRank())); plugin.getFlagsManager().getFlags().stream(). - filter(f -> f.getType().equals(Flag.Type.SETTING)). - forEach(f -> settings.getDefaultIslandSettingNames().putIfAbsent(f.getID(), f.getDefaultRank())); + filter(f -> f.getType().equals(Flag.Type.SETTING)). + forEach(f -> settings.getDefaultIslandSettingNames().putIfAbsent(f.getID(), f.getDefaultRank())); Bukkit.getScheduler().runTask(plugin, () -> { // Set world difficulty @@ -494,8 +506,8 @@ public class IslandWorldManager { */ public String getFriendlyName(@NonNull World world) { return gameModes.containsKey(world) ? - gameModes.get(world).getWorldSettings().getFriendlyName() : - world.getName(); + gameModes.get(world).getWorldSettings().getFriendlyName() : + world.getName(); } /** @@ -720,8 +732,8 @@ public class IslandWorldManager { public Map getDefaultIslandFlags(@NonNull World world) { return this.gameModes.containsKey(world) ? - this.convertToFlags(this.gameModes.get(world).getWorldSettings().getDefaultIslandFlagNames()) : - Collections.emptyMap(); + this.convertToFlags(this.gameModes.get(world).getWorldSettings().getDefaultIslandFlagNames()) : + Collections.emptyMap(); } /** @@ -742,8 +754,8 @@ public class IslandWorldManager { public Map getDefaultIslandSettings(@NonNull World world) { return this.gameModes.containsKey(world) ? - this.convertToFlags(this.gameModes.get(world).getWorldSettings().getDefaultIslandSettingNames()) : - Collections.emptyMap(); + this.convertToFlags(this.gameModes.get(world).getWorldSettings().getDefaultIslandSettingNames()) : + Collections.emptyMap(); } public boolean isUseOwnGenerator(@NonNull World world) { @@ -955,7 +967,7 @@ public class IslandWorldManager { { Map flagMap = new HashMap<>(); flagNamesMap.forEach((key, value) -> - this.plugin.getFlagsManager().getFlag(key).ifPresent(flag -> flagMap.put(flag, value))); + this.plugin.getFlagsManager().getFlag(key).ifPresent(flag -> flagMap.put(flag, value))); return flagMap; } } From e846d26fab227ce2582fd810eb6fcfa5de0deabb Mon Sep 17 00:00:00 2001 From: tastybento Date: Wed, 28 Jun 2023 21:02:39 -0700 Subject: [PATCH 29/36] Fix test Test doesn't do anything though... --- .../bentobox/bentobox/managers/IslandWorldManagerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandWorldManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandWorldManagerTest.java index c846a168d..00474ac58 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandWorldManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandWorldManagerTest.java @@ -114,7 +114,7 @@ public class IslandWorldManagerTest { */ @Test public void testRegisterWorldsToMultiverse() { - iwm.registerWorldsToMultiverse(); + iwm.registerWorldsToMultiverse(true); } /** From c28ecb1c7494d3186d58134ff82889321eaad06d Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 29 Jun 2023 17:29:04 -0700 Subject: [PATCH 30/36] Add null check. Not really needed but make Sonar happy. --- .../world/bentobox/bentobox/managers/IslandWorldManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java index cdc64a64a..fbe8ce645 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java @@ -182,13 +182,13 @@ public class IslandWorldManager { gameModes.put(world, gameMode); // Call Multiverse registerToWorldManagementPlugins(world, true, true); - if (settings.isNetherGenerate()) { + if (settings.isNetherGenerate() && gameMode.getNetherWorld() != null) { gameModes.put(gameMode.getNetherWorld(), gameMode); if (settings.isNetherIslands()) { registerToWorldManagementPlugins(gameMode.getNetherWorld(), true, true); } } - if (settings.isEndGenerate()) { + if (settings.isEndGenerate() && gameMode.getEndWorld() != null) { gameModes.put(gameMode.getEndWorld(), gameMode); if (settings.isEndIslands()) { registerToWorldManagementPlugins(gameMode.getEndWorld(), true, true); From 9c6cc78c32831defd79ed7522614ddcaa22e2715 Mon Sep 17 00:00:00 2001 From: tastybento Date: Fri, 30 Jun 2023 18:35:55 -0700 Subject: [PATCH 31/36] Adjust priority of PlayerInteraction event listener to NORMAL This may help avoid mob shop clashes. --- .../listeners/flags/protection/EntityInteractListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java index 2d27c69e8..406e30202 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java @@ -39,7 +39,7 @@ public class EntityInteractListener extends FlagListener { } } - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onPlayerInteractEntity(PlayerInteractEntityEvent e) { Player p = e.getPlayer(); From 7b8e18afff8f35664b78e65a55cb76a1b2121f8b Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 1 Jul 2023 10:04:05 -0700 Subject: [PATCH 32/36] Adds Chiseled Bookshelf protection #2136 --- .../protection/BlockInteractionListener.java | 9 ++++--- .../world/bentobox/bentobox/lists/Flags.java | 24 ++++++++++++------- src/main/resources/locales/en-US.yml | 6 +++++ 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java index d2edf16b8..8f082e66c 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java @@ -99,7 +99,7 @@ public class BlockInteractionListener extends FlagListener this.checkIsland(e, player, loc, Flags.FLOWER_POT); return; } - + if (block.getState() instanceof BrushableBlock bb && BlockInteractionListener.holds(player, Material.BRUSH)) { // Protect this using break blocks flag for now. Maybe in the future it can have its own flag. this.checkIsland(e, player, loc, Flags.BREAK_BLOCKS); @@ -171,14 +171,12 @@ public class BlockInteractionListener extends FlagListener case DISPENSER -> this.checkIsland(e, player, loc, Flags.DISPENSER); case DROPPER -> this.checkIsland(e, player, loc, Flags.DROPPER); case HOPPER, HOPPER_MINECART -> this.checkIsland(e, player, loc, Flags.HOPPER); - case BLAST_FURNACE, CAMPFIRE, FURNACE_MINECART, FURNACE, SMOKER -> - this.checkIsland(e, player, loc, Flags.FURNACE); + case BLAST_FURNACE, CAMPFIRE, FURNACE_MINECART, FURNACE, SMOKER -> this.checkIsland(e, player, loc, Flags.FURNACE); case ENCHANTING_TABLE -> this.checkIsland(e, player, loc, Flags.ENCHANTING); case ENDER_CHEST -> this.checkIsland(e, player, loc, Flags.ENDER_CHEST); case JUKEBOX -> this.checkIsland(e, player, loc, Flags.JUKEBOX); case NOTE_BLOCK -> this.checkIsland(e, player, loc, Flags.NOTE_BLOCK); - case CRAFTING_TABLE, CARTOGRAPHY_TABLE, GRINDSTONE, STONECUTTER, LOOM -> - this.checkIsland(e, player, loc, Flags.CRAFTING); + case CRAFTING_TABLE, CARTOGRAPHY_TABLE, GRINDSTONE, STONECUTTER, LOOM -> this.checkIsland(e, player, loc, Flags.CRAFTING); case LEVER -> this.checkIsland(e, player, loc, Flags.LEVER); case REDSTONE_WIRE, REPEATER, COMPARATOR, DAYLIGHT_DETECTOR -> this.checkIsland(e, player, loc, Flags.REDSTONE); case DRAGON_EGG -> this.checkIsland(e, player, loc, Flags.DRAGON_EGG); @@ -186,6 +184,7 @@ public class BlockInteractionListener extends FlagListener case GLOW_ITEM_FRAME, ITEM_FRAME -> this.checkIsland(e, player, loc, Flags.ITEM_FRAME); case SWEET_BERRY_BUSH, CAVE_VINES -> this.checkIsland(e, player, loc, Flags.BREAK_BLOCKS); case CAKE -> this.checkIsland(e, player, loc, Flags.CAKE); + case CHISELED_BOOKSHELF -> this.checkIsland(e, player, loc, Flags.BOOKSHELF); case LAVA_CAULDRON -> { if (BlockInteractionListener.holds(player, Material.BUCKET)) diff --git a/src/main/java/world/bentobox/bentobox/lists/Flags.java b/src/main/java/world/bentobox/bentobox/lists/Flags.java index 3fd2ac9f7..9d47bfc10 100644 --- a/src/main/java/world/bentobox/bentobox/lists/Flags.java +++ b/src/main/java/world/bentobox/bentobox/lists/Flags.java @@ -9,6 +9,7 @@ import org.bukkit.Material; import com.google.common.base.Enums; import world.bentobox.bentobox.api.flags.Flag; +import world.bentobox.bentobox.api.flags.Flag.Mode; import world.bentobox.bentobox.api.flags.Flag.Type; import world.bentobox.bentobox.api.flags.clicklisteners.CycleClick; import world.bentobox.bentobox.listeners.flags.clicklisteners.CommandRankClickListener; @@ -158,11 +159,18 @@ public final class Flags { * @since 1.10.0 * @see LecternListener */ - public static final Flag LECTERN = new Flag.Builder("LECTERN", Material.LECTERN).listener(new LecternListener()).build(); + public static final Flag LECTERN = new Flag.Builder("LECTERN", Material.LECTERN).mode(Mode.ADVANCED).listener(new LecternListener()).build(); + + /** + * Prevents players from placing a book in a bookshelf or taking the book from it. + * @since 1.24.0 + * @see BlockInteractionListener + */ + public static final Flag BOOKSHELF = new Flag.Builder("BOOKSHELF", Material.CHISELED_BOOKSHELF).mode(Mode.ADVANCED).build(); // Entity interactions - public static final Flag ARMOR_STAND = new Flag.Builder("ARMOR_STAND", Material.ARMOR_STAND).listener(new EntityInteractListener()).mode(Flag.Mode.ADVANCED).build(); - public static final Flag RIDING = new Flag.Builder("RIDING", Material.GOLDEN_HORSE_ARMOR).build(); + public static final Flag ARMOR_STAND = new Flag.Builder("ARMOR_STAND", Material.ARMOR_STAND).listener(new EntityInteractListener()).mode(Mode.ADVANCED).build(); + public static final Flag RIDING = new Flag.Builder("RIDING", Material.GOLDEN_HORSE_ARMOR).mode(Mode.ADVANCED).build(); /** * Prevents players from issuing any kind of interactions with Minecarts (entering, placing and opening if chest). * @since 1.3.0 @@ -188,7 +196,7 @@ public final class Flags { // Buckets. All bucket use is covered by one listener public static final Flag BUCKET = new Flag.Builder("BUCKET", Material.BUCKET).listener(new BucketListener()).mode(Flag.Mode.BASIC).build(); - public static final Flag COLLECT_LAVA = new Flag.Builder("COLLECT_LAVA", Material.LAVA_BUCKET).build(); + public static final Flag COLLECT_LAVA = new Flag.Builder("COLLECT_LAVA", Material.LAVA_BUCKET).mode(Mode.ADVANCED).build(); public static final Flag COLLECT_WATER = new Flag.Builder("COLLECT_WATER", Material.WATER_BUCKET).mode(Flag.Mode.ADVANCED).build(); /** * @since 1.21 @@ -215,7 +223,7 @@ public final class Flags { * Prevents players from throwing eggs. * @see EggListener */ - public static final Flag EGGS = new Flag.Builder("EGGS", Material.EGG).listener(new EggListener()).build(); + public static final Flag EGGS = new Flag.Builder("EGGS", Material.EGG).mode(Mode.ADVANCED).listener(new EggListener()).build(); /** * Prevents players from throwing potions / experience bottles. * @since 1.1 @@ -259,7 +267,7 @@ public final class Flags { * Prevents players from extinguishing fires. * @see FireListener */ - public static final Flag FIRE_EXTINGUISH = new Flag.Builder("FIRE_EXTINGUISH", Material.POTION).build(); + public static final Flag FIRE_EXTINGUISH = new Flag.Builder("FIRE_EXTINGUISH", Material.POTION).mode(Mode.ADVANCED).build(); // Inventories public static final Flag MOUNT_INVENTORY = new Flag.Builder("MOUNT_INVENTORY", Material.IRON_HORSE_ARMOR).listener(new InventoryListener()).mode(Flag.Mode.ADVANCED).build(); @@ -277,12 +285,12 @@ public final class Flags { * Prevents players from going through the Nether Portal. * @see PortalListener */ - public static final Flag NETHER_PORTAL = new Flag.Builder("NETHER_PORTAL", Material.NETHERRACK).listener(new PortalListener()).build(); + public static final Flag NETHER_PORTAL = new Flag.Builder("NETHER_PORTAL", Material.NETHERRACK).mode(Mode.ADVANCED).listener(new PortalListener()).build(); /** * Prevents players from going through the End Portal. * @see PortalListener */ - public static final Flag END_PORTAL = new Flag.Builder("END_PORTAL", Material.END_PORTAL_FRAME).build(); + public static final Flag END_PORTAL = new Flag.Builder("END_PORTAL", Material.END_PORTAL_FRAME).mode(Mode.ADVANCED).build(); // Shearing public static final Flag SHEARING = new Flag.Builder("SHEARING", Material.SHEARS).listener(new ShearingListener()).mode(Flag.Mode.ADVANCED).build(); diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 7c444cefd..a0f906643 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -776,6 +776,12 @@ protection: Toggle placing, breaking and entering into boats. hint: "No boat interaction allowed" + BOOKSHELF: + name: "Bookshelves" + description: |- + &a Allow to place books + &a or to take books. + hint: "cannot place a book or take a book." BREAK_BLOCKS: description: "Toggle breaking" name: "Break blocks" From 9bd0104a3d5cc9dec94d4d8df21d074bf2f34162 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 1 Jul 2023 12:49:18 -0700 Subject: [PATCH 33/36] Put island deletion under one class manager The goal is to make BentoBox less of a monster class and have fewer dependent classes. --- src/main/java/world/bentobox/bentobox/BentoBox.java | 10 ---------- .../bentobox/managers/IslandDeletionManager.java | 12 +++++++++++- .../bentobox/bentobox/managers/IslandsManager.java | 2 +- .../bentobox/managers/IslandDeletionManagerTest.java | 5 ----- .../bentobox/managers/IslandsManagerTest.java | 9 +++++---- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java index 1caeea7fe..56d02eb37 100644 --- a/src/main/java/world/bentobox/bentobox/BentoBox.java +++ b/src/main/java/world/bentobox/bentobox/BentoBox.java @@ -39,7 +39,6 @@ import world.bentobox.bentobox.managers.BlueprintsManager; import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.FlagsManager; import world.bentobox.bentobox.managers.HooksManager; -import world.bentobox.bentobox.managers.IslandChunkDeletionManager; import world.bentobox.bentobox.managers.IslandDeletionManager; import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandsManager; @@ -74,7 +73,6 @@ public class BentoBox extends JavaPlugin implements Listener { private HooksManager hooksManager; private PlaceholdersManager placeholdersManager; private IslandDeletionManager islandDeletionManager; - private IslandChunkDeletionManager islandChunkDeletionManager; private WebManager webManager; // Settings @@ -306,7 +304,6 @@ public class BentoBox extends JavaPlugin implements Listener { // MV unregister manager.registerEvents(this, this); // Island Delete Manager - islandChunkDeletionManager = new IslandChunkDeletionManager(this); islandDeletionManager = new IslandDeletionManager(this); manager.registerEvents(islandDeletionManager, this); } @@ -545,13 +542,6 @@ public class BentoBox extends JavaPlugin implements Listener { return islandDeletionManager; } - /** - * @return the islandChunkDeletionManager - */ - public IslandChunkDeletionManager getIslandChunkDeletionManager() { - return islandChunkDeletionManager; - } - /** * @return an optional of the Bstats instance * @since 1.1 diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java index 8c06a6ee2..e3195e230 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandDeletionManager.java @@ -31,11 +31,13 @@ public class IslandDeletionManager implements Listener { */ private final Database handler; private final Set inDeletion; + private final IslandChunkDeletionManager islandChunkDeletionManager; public IslandDeletionManager(BentoBox plugin) { this.plugin = plugin; handler = new Database<>(plugin, IslandDeletion.class); inDeletion = new HashSet<>(); + islandChunkDeletionManager = new IslandChunkDeletionManager(plugin); } /** @@ -56,7 +58,7 @@ public class IslandDeletionManager implements Listener { } else { plugin.log("Resuming deletion of island at " + di.getLocation().getWorld().getName() + " " + Util.xyz(di.getLocation().toVector())); inDeletion.add(di.getLocation()); - plugin.getIslandChunkDeletionManager().add(di); + this.islandChunkDeletionManager.add(di); } }); } @@ -86,4 +88,12 @@ public class IslandDeletionManager implements Listener { public boolean inDeletion(Location location) { return inDeletion.contains(location); } + + /** + * @return the islandChunkDeletionManager + */ + public IslandChunkDeletionManager getIslandChunkDeletionManager() { + return islandChunkDeletionManager; + } + } diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index bcdb75b90..60fe88449 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -343,7 +343,7 @@ public class IslandsManager { // Remove players from island removePlayersFromIsland(island); // Remove blocks from world - plugin.getIslandChunkDeletionManager().add(new IslandDeletion(island)); + plugin.getIslandDeletionManager().getIslandChunkDeletionManager().add(new IslandDeletion(island)); } } diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandDeletionManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandDeletionManagerTest.java index 62f0a3879..36acd6362 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandDeletionManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandDeletionManagerTest.java @@ -67,9 +67,6 @@ public class IslandDeletionManagerTest { private BukkitScheduler scheduler; @Mock private IslandWorldManager iwm; - @Mock - private IslandChunkDeletionManager chunkDeletionManager; - /** */ @@ -102,8 +99,6 @@ public class IslandDeletionManagerTest { // IWM when(plugin.getIWM()).thenReturn(iwm); when(iwm.getIslandDistance(any())).thenReturn(64); - // Chunk deletion manager - when(plugin.getIslandChunkDeletionManager()).thenReturn(chunkDeletionManager); // Island Deletion Manager idm = new IslandDeletionManager(plugin); diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java index f9bc0105c..b1f626c43 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java @@ -109,6 +109,8 @@ public class IslandsManagerTest extends AbstractCommonSetup { @Mock private IslandWorldManager iwm; @Mock + private IslandDeletionManager deletionManager; + @Mock private IslandChunkDeletionManager chunkDeletionManager; @Mock private IslandCache islandCache; @@ -161,9 +163,6 @@ public class IslandsManagerTest extends AbstractCommonSetup { when(iwm.inWorld(any(Location.class))).thenReturn(true); when(plugin.getIWM()).thenReturn(iwm); - // Chunk deletion manager - when(plugin.getIslandChunkDeletionManager()).thenReturn(chunkDeletionManager); - // Settings Settings s = mock(Settings.class); when(plugin.getSettings()).thenReturn(s); @@ -311,7 +310,9 @@ public class IslandsManagerTest extends AbstractCommonSetup { .getNearbyEntities(any(Location.class), Mockito.anyDouble(), Mockito.anyDouble(), Mockito.anyDouble())) .thenReturn(collection); - + // Deletion Manager + when(deletionManager.getIslandChunkDeletionManager()).thenReturn(chunkDeletionManager); + when(plugin.getIslandDeletionManager()).thenReturn(deletionManager); // database must be mocked here db = mock(Database.class); From f47e87af829b809a15ac1faa970a179413a67424 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 2 Jul 2023 15:10:23 -0700 Subject: [PATCH 34/36] Added test class for PanelItem --- .../bentobox/api/panels/PanelItem.java | 10 +- .../bentobox/api/panels/PanelItemTest.java | 201 ++++++++++++++++++ 2 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 src/test/java/world/bentobox/bentobox/api/panels/PanelItemTest.java diff --git a/src/main/java/world/bentobox/bentobox/api/panels/PanelItem.java b/src/main/java/world/bentobox/bentobox/api/panels/PanelItem.java index e347f200a..6354bae6a 100644 --- a/src/main/java/world/bentobox/bentobox/api/panels/PanelItem.java +++ b/src/main/java/world/bentobox/bentobox/api/panels/PanelItem.java @@ -19,6 +19,9 @@ import world.bentobox.bentobox.api.user.User; */ public class PanelItem { + /** + * @return an empty PanelItem + */ public static PanelItem empty() { return new PanelItemBuilder().build(); } @@ -58,6 +61,9 @@ public class PanelItem { } + /** + * @return the icon itemstack + */ public ItemStack getItem() { return icon; } @@ -135,14 +141,14 @@ public class PanelItem { public boolean isPlayerHead() { return playerHeadName != null && !playerHeadName.isEmpty(); } - + /** * @return the playerHeadName * @since 1.9.0 */ public String getPlayerHeadName() { return playerHeadName; - } + } /** * Click handler interface diff --git a/src/test/java/world/bentobox/bentobox/api/panels/PanelItemTest.java b/src/test/java/world/bentobox/bentobox/api/panels/PanelItemTest.java new file mode 100644 index 000000000..9779feeeb --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/api/panels/PanelItemTest.java @@ -0,0 +1,201 @@ +package world.bentobox.bentobox.api.panels; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import world.bentobox.bentobox.api.panels.PanelItem.ClickHandler; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({ Bukkit.class }) +public class PanelItemTest { + + @Mock + private PanelItemBuilder pib; + private PanelItem pi; + @Mock + private ClickHandler clickHandler; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + // Builder + when(pib.getAmount()).thenReturn(2); + when(pib.getClickHandler()).thenReturn(clickHandler); + when(pib.getDescription()).thenReturn(List.of("Description", "hello")); + when(pib.getIcon()).thenReturn(new ItemStack(Material.STONE)); + when(pib.getName()).thenReturn("Name"); + when(pib.getPlayerHeadName()).thenReturn("tastybento"); + pi = new PanelItem(pib); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + Mockito.framework().clearInlineMocks(); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#empty()}. + */ + @Test + public void testEmpty() { + PanelItem panelItem = PanelItem.empty(); + assertTrue(panelItem.getName().isEmpty()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#getItem()}. + */ + @Test + public void testGetItem() { + ItemStack i = pi.getItem(); + assertNotNull(i); + assertEquals(Material.STONE, i.getType()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#getDescription()}. + */ + @Test + public void testGetDescription() { + assertEquals(2, pi.getDescription().size()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#setDescription(java.util.List)}. + */ + @Test + public void testSetDescription() { + assertEquals(2, pi.getDescription().size()); + pi.setDescription(List.of("1","2","3")); + assertEquals(3, pi.getDescription().size()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#getName()}. + */ + @Test + public void testGetName() { + assertEquals("Name", pi.getName()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#setName(java.lang.String)}. + */ + @Test + public void testSetName() { + assertEquals("Name", pi.getName()); + pi.setName("Name2"); + assertEquals("Name2", pi.getName()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#isInvisible()}. + */ + @Test + public void testIsInvisible() { + assertFalse(pi.isInvisible()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#setInvisible(boolean)}. + */ + @Test + public void testSetInvisible() { + assertFalse(pi.isInvisible()); + pi.setInvisible(true); + assertTrue(pi.isInvisible()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#getClickHandler()}. + */ + @Test + public void testGetClickHandler() { + assertEquals(clickHandler, pi.getClickHandler().get()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#setClickHandler(world.bentobox.bentobox.api.panels.PanelItem.ClickHandler)}. + */ + @Test + public void testSetClickHandler() { + assertEquals(clickHandler, pi.getClickHandler().get()); + pi.setClickHandler(null); + assertEquals(Optional.empty(), pi.getClickHandler()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#isGlow()}. + */ + @Test + public void testIsGlow() { + assertFalse(pi.isGlow()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#setGlow(boolean)}. + */ + @Test + public void testSetGlow() { + assertFalse(pi.isGlow()); + pi.setGlow(true); + assertTrue(pi.isGlow()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#isPlayerHead()}. + */ + @Test + public void testIsPlayerHead() { + assertTrue(pi.isPlayerHead()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#getPlayerHeadName()}. + */ + @Test + public void testGetPlayerHeadName() { + assertEquals("tastybento", pi.getPlayerHeadName()); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.panels.PanelItem#setHead(org.bukkit.inventory.ItemStack)}. + */ + @Test + public void testSetHead() { + pi.setHead(new ItemStack(Material.PLAYER_HEAD)); + } + +} From 7d25aff28f74eb5e008f10c7f5ed4456b3b800f0 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 2 Jul 2023 15:52:20 -0700 Subject: [PATCH 35/36] Added test class for DefaultPlayerCommand --- .../island/DefaultPlayerCommandTest.java | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 src/test/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommandTest.java diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommandTest.java new file mode 100644 index 000000000..e1ee89ca1 --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/DefaultPlayerCommandTest.java @@ -0,0 +1,211 @@ +package world.bentobox.bentobox.api.commands.island; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.configuration.WorldSettings; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.IslandsManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class}) +public class DefaultPlayerCommandTest { + + @Mock + GameModeAddon addon; + private PlayerCommand dpc; + @Mock + private WorldSettings ws; + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private @Nullable Island island; + + + class PlayerCommand extends DefaultPlayerCommand { + + protected PlayerCommand(GameModeAddon addon) { + super(addon); + } + + } + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + + // Addon + + // User + when(user.getUniqueId()).thenReturn(UUID.randomUUID()); + // IM + when(plugin.getIslandsManager()).thenReturn(im); + when(plugin.getIslands()).thenReturn(im); + when(im.getIsland(any(World.class), any(UUID.class))).thenReturn(island); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // World Settings + when(ws.getDefaultPlayerAction()).thenReturn("go"); + when(ws.getDefaultNewPlayerAction()).thenReturn("create"); + + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + + when(ws.getPlayerCommandAliases()).thenReturn("island is"); + when(addon.getWorldSettings()).thenReturn(ws); + dpc = new PlayerCommand(addon); + dpc.setWorld(mock(World.class)); + + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#DefaultPlayerCommand(world.bentobox.bentobox.api.addons.GameModeAddon)}. + */ + @Test + public void testDefaultPlayerCommand() { + assertNotNull(dpc); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#setup()}. + */ + @Test + public void testSetup() { + assertEquals("commands.island.help.description", dpc.getDescription()); + assertTrue(dpc.isOnlyPlayer()); + assertEquals("island", dpc.getPermission()); + // 20 = 19 subcommands + help command + assertEquals(20, dpc.getSubCommands().size()); // Update when commands are added or removed + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringUnknownCommand() { + assertFalse(dpc.execute(user, "label", List.of("unknown"))); + verify(user).sendMessage("general.errors.unknown-command", TextVariables.LABEL, "island"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringNullUser() { + assertFalse(dpc.execute(null, "label", List.of())); + } + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringEmptyArgsHasIsland() { + assertFalse(dpc.execute(user, "label", List.of())); + verify(user).sendMessage("general.errors.use-in-game"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringEmptyArgsHasNoIsland() { + when(im.getIsland(any(World.class), any(UUID.class))).thenReturn(null); + assertFalse(dpc.execute(user, "label", List.of())); + verify(user).sendMessage("general.errors.use-in-game"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringEmptyArgsHasIslandUnknownCommand() { + when(ws.getDefaultPlayerAction()).thenReturn("goxxx"); + + assertFalse(dpc.execute(user, "label", List.of())); + verify(user).performCommand("label goxxx"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringEmptyArgsHasNoIslandUnknownCommand() { + + when(ws.getDefaultNewPlayerAction()).thenReturn("createxxx"); + + when(im.getIsland(any(World.class), any(UUID.class))).thenReturn(null); + assertFalse(dpc.execute(user, "label", List.of())); + verify(user).performCommand("label createxxx"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringEmptyArgsHasIslandUnknownCommandSlash() { + when(ws.getDefaultPlayerAction()).thenReturn("/goxxx"); + + assertFalse(dpc.execute(user, "label", List.of())); + verify(user).performCommand("goxxx"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.DefaultPlayerCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringEmptyArgsHasNoIslandUnknownCommandSlash() { + + when(ws.getDefaultNewPlayerAction()).thenReturn("/createxxx"); + + when(im.getIsland(any(World.class), any(UUID.class))).thenReturn(null); + assertFalse(dpc.execute(user, "label", List.of())); + verify(user).performCommand("createxxx"); + } +} \ No newline at end of file From 9334858675ce1ac4b5c488f19f29b430221c7e26 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 3 Jul 2023 16:11:37 -0700 Subject: [PATCH 36/36] Added IslandDeletehomeCommand test class --- .../island/IslandDeletehomeCommand.java | 8 +- .../island/IslandDeletehomeCommandTest.java | 265 ++++++++++++++++++ 2 files changed, 271 insertions(+), 2 deletions(-) create mode 100644 src/test/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommandTest.java diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommand.java index 8b10540fb..a60aeeb41 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommand.java @@ -24,6 +24,10 @@ public class IslandDeletehomeCommand extends ConfirmableCommand { private @Nullable Island island; + /** + * Deletes a home + * @param islandCommand parent command + */ public IslandDeletehomeCommand(CompositeCommand islandCommand) { super(islandCommand, "deletehome"); } @@ -51,11 +55,11 @@ public class IslandDeletehomeCommand extends ConfirmableCommand { return false; } - // check command permission + // check command ranks int rank = Objects.requireNonNull(island).getRank(user); if (rank < island.getRankCommand(getUsage())) { user.sendMessage("general.errors.insufficient-rank", - TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank))); + TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank))); return false; } diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommandTest.java new file mode 100644 index 000000000..6d39a6e04 --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommandTest.java @@ -0,0 +1,265 @@ +package world.bentobox.bentobox.api.commands.island; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.Settings; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.managers.PlayersManager; +import world.bentobox.bentobox.managers.RanksManager; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, User.class }) +public class IslandDeletehomeCommandTest { + + @Mock + private CompositeCommand ic; + private UUID uuid; + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private PlayersManager pm; + @Mock + private Island island; + private IslandDeletehomeCommand idh; + @Mock + private IslandWorldManager iwm; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Ranks Manager + RanksManager rm = new RanksManager(); + when(plugin.getRanksManager()).thenReturn(rm); + + // Addon + GameModeAddon addon = mock(GameModeAddon.class); + + // Settings + Settings settings = new Settings(); + when(plugin.getSettings()).thenReturn(settings); + + // Parent command has no aliases + when(ic.getSubCommandAliases()).thenReturn(new HashMap<>()); + when(ic.getParameters()).thenReturn("parameters"); + when(ic.getDescription()).thenReturn("description"); + when(ic.getPermissionPrefix()).thenReturn("permission."); + when(ic.getUsage()).thenReturn(""); + when(ic.getSubCommand(Mockito.anyString())).thenReturn(Optional.empty()); + when(ic.getAddon()).thenReturn(addon); + when(plugin.getIslands()).thenReturn(im); + // Player + Player player = mock(Player.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(player); + when(user.getName()).thenReturn("tastybento"); + when(user.getWorld()).thenReturn(mock(World.class)); + when(user.getTranslation(anyString())).thenAnswer(i -> i.getArgument(0, String.class)); + // Island + when(island.getOwner()).thenReturn(uuid); + when(island.onIsland(any())).thenReturn(true); + when(im.getIsland(any(), any(UUID.class))).thenReturn(island); + when(im.getIsland(any(), any(User.class))).thenReturn(island); + @NotNull + Map homeMap = new HashMap<>(); + homeMap.put("Home", null); + homeMap.put("Home2", null); + homeMap.put("Home3", null); + homeMap.put("Home4", null); + when(island.getHomes()).thenReturn(homeMap); + + // IWM friendly name + when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + // Not in nether + when(iwm.isNether(any())).thenReturn(false); + // Not in end + when(iwm.isEnd(any())).thenReturn(false); + // Number of homes default + when(iwm.getMaxHomes(any())).thenReturn(3); + when(plugin.getIWM()).thenReturn(iwm); + + // Bukkit + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + + idh = new IslandDeletehomeCommand(ic); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + User.clearUsers(); + Mockito.framework().clearInlineMocks(); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#IslandDeletehomeCommand(world.bentobox.bentobox.api.commands.CompositeCommand)}. + */ + @Test + public void testIslandDeletehomeCommand() { + assertEquals("deletehome", idh.getLabel()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#setup()}. + */ + @Test + public void testSetup() { + assertTrue(idh.isOnlyPlayer()); + assertEquals("commands.island.deletehome.parameters", idh.getParameters()); + assertEquals("commands.island.deletehome.description", idh.getDescription()); + assertEquals("permission.island.deletehome", idh.getPermission()); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteHelp() { + idh.canExecute(user, "label", List.of()); + verify(user).sendMessage("commands.help.header","[label]","commands.help.console"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteNoIsland() { + when(im.getIsland(any(), eq(user))).thenReturn(null); + assertFalse(idh.canExecute(user, "label", List.of("something"))); + verify(user).sendMessage("general.errors.no-island"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteLowRank() { + when(island.getRank(user)).thenReturn(RanksManager.COOP_RANK); + when(island.getRankCommand(anyString())).thenReturn(RanksManager.OWNER_RANK); + assertFalse(idh.canExecute(user, "label", List.of("something"))); + verify(user).sendMessage("general.errors.insufficient-rank", + TextVariables.RANK, "ranks.coop"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteUnknownHome() { + when(island.getRank(user)).thenReturn(RanksManager.OWNER_RANK); + when(island.getRankCommand(anyString())).thenReturn(RanksManager.COOP_RANK); + when(island.getHomes()).thenReturn(Map.of("home", mock(Location.class))); + + when(im.isHomeLocation(eq(island), anyString())).thenReturn(false); + + assertFalse(idh.canExecute(user, "label", List.of("something"))); + verify(user).sendMessage("commands.island.go.unknown-home"); + verify(user).sendMessage("commands.island.sethome.homes-are"); + verify(user).sendMessage("home-list-syntax", TextVariables.NAME, "home"); + + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteKnownHome() { + when(island.getRank(user)).thenReturn(RanksManager.OWNER_RANK); + when(island.getRankCommand(anyString())).thenReturn(RanksManager.COOP_RANK); + when(island.getHomes()).thenReturn(Map.of("home", mock(Location.class))); + + when(im.isHomeLocation(eq(island), anyString())).thenReturn(true); + + assertTrue(idh.canExecute(user, "label", List.of("home"))); + } + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfString() { + assertTrue(idh.execute(user, "label", List.of("home"))); + verify(user).sendMessage("commands.confirmation.confirm", "[seconds]", "10"); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabCompleteUserStringListOfString() { + when(island.getHomes()).thenReturn(Map.of("home", mock(Location.class))); + Optional> list = idh.tabComplete(user, "label", List.of("hom")); + assertTrue(list.isPresent()); + assertEquals("home", list.get().get(0)); + } + + /** + * Test method for {@link world.bentobox.bentobox.api.commands.island.IslandDeletehomeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabCompleteUserStringListOfStringNothing() { + when(island.getHomes()).thenReturn(Map.of("home", mock(Location.class))); + Optional> list = idh.tabComplete(user, "label", List.of("f")); + assertTrue(list.isPresent()); + assertTrue(list.get().isEmpty()); + } + +}