From d240e9c8d84ab34139cf984f857af981abdbe354 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 16 Mar 2024 09:15:46 -0700 Subject: [PATCH 01/23] Version 2.2.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6169abb16..181e7cc25 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ -LOCAL - 2.2.0 + 2.2.1 bentobox-world https://sonarcloud.io ${project.basedir}/lib From d77c94c30c65d2cfc81ace56143edfe0959277f1 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 16 Mar 2024 10:04:28 -0700 Subject: [PATCH 02/23] Fix #2320. Enables hiding of flags when in another world The world was being taken from the user's location not the panel's world --- .../api/flags/clicklisteners/CycleClick.java | 20 ++-- .../flags/clicklisteners/CycleClickTest.java | 102 +++++++++++++++--- 2 files changed, 98 insertions(+), 24 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/CycleClick.java b/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/CycleClick.java index 296a79fed..78240fb8d 100644 --- a/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/CycleClick.java +++ b/src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/CycleClick.java @@ -4,6 +4,7 @@ import java.util.Objects; import org.bukkit.Bukkit; import org.bukkit.Sound; +import org.bukkit.World; import org.bukkit.event.inventory.ClickType; import world.bentobox.bentobox.BentoBox; @@ -59,6 +60,11 @@ public class CycleClick implements PanelItem.ClickHandler { @Override public boolean onClick(Panel panel, User user2, ClickType click, int slot) { + if (panel.getWorld().isEmpty()) { + plugin.logError("Panel " + panel.getName() + + " has no world associated with it. Please report this bug to the author."); + return true; + } // This click listener is used with TabbedPanel and SettingsTabs only TabbedPanel tp = (TabbedPanel)panel; SettingsTab st = (SettingsTab)tp.getActiveTab(); @@ -67,7 +73,7 @@ public class CycleClick implements PanelItem.ClickHandler { this.user = user2; changeOccurred = false; // Permission prefix - String prefix = plugin.getIWM().getPermissionPrefix(Util.getWorld(user.getWorld())); + String prefix = plugin.getIWM().getPermissionPrefix(Util.getWorld(panel.getWorld().get())); String reqPerm = prefix + "settings." + id; String allPerms = prefix + "settings.*"; if (!user.hasPermission(reqPerm) && !user.hasPermission(allPerms) @@ -91,7 +97,7 @@ public class CycleClick implements PanelItem.ClickHandler { rightClick(flag, currentRank); } else if (click.equals(ClickType.SHIFT_LEFT) && user2.isOp()) { - leftShiftClick(flag); + leftShiftClick(flag, panel.getWorld().get()); } }); } else { @@ -149,16 +155,16 @@ public class CycleClick implements PanelItem.ClickHandler { } - private void leftShiftClick(Flag flag) { - if (!plugin.getIWM().getHiddenFlags(user.getWorld()).contains(flag.getID())) { - plugin.getIWM().getHiddenFlags(user.getWorld()).add(flag.getID()); + private void leftShiftClick(Flag flag, World world) { + if (!plugin.getIWM().getHiddenFlags(world).contains(flag.getID())) { + plugin.getIWM().getHiddenFlags(world).add(flag.getID()); user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_GLASS_BREAK, 1F, 1F); } else { - plugin.getIWM().getHiddenFlags(user.getWorld()).remove(flag.getID()); + plugin.getIWM().getHiddenFlags(world).remove(flag.getID()); user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1F, 1F); } // Save changes - plugin.getIWM().getAddon(user.getWorld()).ifPresent(GameModeAddon::saveWorldSettings); + plugin.getIWM().getAddon(world).ifPresent(GameModeAddon::saveWorldSettings); } diff --git a/src/test/java/world/bentobox/bentobox/api/flags/clicklisteners/CycleClickTest.java b/src/test/java/world/bentobox/bentobox/api/flags/clicklisteners/CycleClickTest.java index 57a69c20a..9c86acc54 100644 --- a/src/test/java/world/bentobox/bentobox/api/flags/clicklisteners/CycleClickTest.java +++ b/src/test/java/world/bentobox/bentobox/api/flags/clicklisteners/CycleClickTest.java @@ -1,28 +1,35 @@ package world.bentobox.bentobox.api.flags.clicklisteners; +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.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.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Optional; import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.Sound; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.Inventory; import org.bukkit.plugin.PluginManager; import org.bukkit.scheduler.BukkitScheduler; +import org.eclipse.jdt.annotation.NonNull; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -59,6 +66,8 @@ public class CycleClickTest { private static final Integer X = 600; private static final Integer Y = 120; private static final Integer Z = 10000; + private static final int SLOT = 5; + private static final String LOCK = "LOCK"; @Mock private BentoBox plugin; private UUID uuid; @@ -82,6 +91,9 @@ public class CycleClickTest { private SettingsTab settingsTab; @Mock private RanksManager rm; + private List hiddenFlags; + @Mock + private @NonNull Player p; /** * @throws java.lang.Exception - exception @@ -99,9 +111,7 @@ public class CycleClickTest { Settings s = mock(Settings.class); when(plugin.getSettings()).thenReturn(s); - // Player - Player p = mock(Player.class); - // Sometimes use Mockito.withSettings().verboseLogging() + // User User.setPlugin(plugin); when(user.isOp()).thenReturn(false); uuid = UUID.randomUUID(); @@ -178,6 +188,7 @@ public class CycleClickTest { when(im.getIslandAt(any())).thenReturn(opIsland); FlagsManager fm = mock(FlagsManager.class); + when(flag.getID()).thenReturn(LOCK); when(fm.getFlag(anyString())).thenReturn(Optional.of(flag)); when(plugin.getFlagsManager()).thenReturn(fm); @@ -210,8 +221,13 @@ public class CycleClickTest { // Active tab when(panel.getActiveTab()).thenReturn(settingsTab); + when(panel.getWorld()).thenReturn(Optional.of(world)); + when(panel.getName()).thenReturn("name"); when(settingsTab.getIsland()).thenReturn(island); + // Hidden flags + hiddenFlags = new ArrayList<>(); + when(iwm.getHiddenFlags(world)).thenReturn(hiddenFlags); } @After @@ -222,25 +238,28 @@ public class CycleClickTest { @Test public void testNoPremission() { when(user.hasPermission(anyString())).thenReturn(false); - CycleClick udc = new CycleClick("LOCK"); + CycleClick udc = new CycleClick(LOCK); assertTrue(udc.onClick(panel, user, ClickType.LEFT, 5)); verify(user).sendMessage(eq("general.errors.no-permission"), eq("[permission]"), eq("bskyblock.settings.LOCK")); } @Test public void testUpDownClick() { - CycleClick udc = new CycleClick("LOCK"); + CycleClick udc = new CycleClick(LOCK); assertNotNull(udc); } + /** + * Test for {@link CycleClick#onClick(world.bentobox.bentobox.api.panels.Panel, User, ClickType, int)} + */ @Test public void testOnLeftClick() { final int SLOT = 5; - CycleClick udc = new CycleClick("LOCK"); + CycleClick udc = new CycleClick(LOCK); // Rank starts at member // Click left assertTrue(udc.onClick(panel, user, ClickType.LEFT, SLOT)); - verify(island).setFlag(eq(flag), eq(RanksManager.OWNER_RANK)); + verify(island).setFlag(flag, RanksManager.OWNER_RANK); // Check rollover // Clicking when Owner should go to Visitor when(island.getFlag(any())).thenReturn(RanksManager.OWNER_RANK); @@ -249,65 +268,114 @@ public class CycleClickTest { verify(pim, times(2)).callEvent(any(FlagProtectionChangeEvent.class)); } + /** + * Test for {@link CycleClick#onClick(world.bentobox.bentobox.api.panels.Panel, User, ClickType, int)} + */ @Test public void testOnLeftClickSetMinMax() { // Provide a current rank value - coop when(island.getFlag(any())).thenReturn(RanksManager.COOP_RANK); final int SLOT = 5; - CycleClick udc = new CycleClick("LOCK", RanksManager.COOP_RANK, RanksManager.MEMBER_RANK); + CycleClick udc = new CycleClick(LOCK, RanksManager.COOP_RANK, RanksManager.MEMBER_RANK); // Rank starts at member // Click left assertTrue(udc.onClick(panel, user, ClickType.LEFT, SLOT)); - verify(island).setFlag(eq(flag), eq(RanksManager.TRUSTED_RANK)); + verify(island).setFlag(flag, RanksManager.TRUSTED_RANK); // Check rollover // Clicking when Member should go to Coop when(island.getFlag(any())).thenReturn(RanksManager.MEMBER_RANK); assertTrue(udc.onClick(panel, user, ClickType.LEFT, SLOT)); - verify(island).setFlag(eq(flag), eq(RanksManager.COOP_RANK)); + verify(island).setFlag(flag, RanksManager.COOP_RANK); verify(pim, times(2)).callEvent(any(FlagProtectionChangeEvent.class)); } + /** + * Test for {@link CycleClick#onClick(world.bentobox.bentobox.api.panels.Panel, User, ClickType, int)} + */ @Test public void testOnRightClick() { final int SLOT = 5; - CycleClick udc = new CycleClick("LOCK"); + CycleClick udc = new CycleClick(LOCK); // Rank starts at member // Right click - down rank to Trusted assertTrue(udc.onClick(panel, user, ClickType.RIGHT, SLOT)); - verify(island).setFlag(eq(flag), eq(RanksManager.TRUSTED_RANK)); + verify(island).setFlag(flag, RanksManager.TRUSTED_RANK); // Check rollover // Clicking when Visitor should go to Owner when(island.getFlag(any())).thenReturn(RanksManager.VISITOR_RANK); assertTrue(udc.onClick(panel, user, ClickType.RIGHT, SLOT)); - verify(island).setFlag(eq(flag), eq(RanksManager.OWNER_RANK)); + verify(island).setFlag(flag, RanksManager.OWNER_RANK); verify(pim, times(2)).callEvent(any(FlagProtectionChangeEvent.class)); } + /** + * Test for {@link CycleClick#onClick(world.bentobox.bentobox.api.panels.Panel, User, ClickType, int)} + */ @Test public void testOnRightClickMinMaxSet() { // Provide a current rank value - coop when(island.getFlag(any())).thenReturn(RanksManager.TRUSTED_RANK); final int SLOT = 5; - CycleClick udc = new CycleClick("LOCK", RanksManager.COOP_RANK, RanksManager.MEMBER_RANK); + CycleClick udc = new CycleClick(LOCK, RanksManager.COOP_RANK, RanksManager.MEMBER_RANK); // Rank starts at member // Right click assertTrue(udc.onClick(panel, user, ClickType.RIGHT, SLOT)); - verify(island).setFlag(eq(flag), eq(RanksManager.COOP_RANK)); + verify(island).setFlag(flag, RanksManager.COOP_RANK); // Check rollover // Clicking when Coop should go to Member when(island.getFlag(any())).thenReturn(RanksManager.COOP_RANK); assertTrue(udc.onClick(panel, user, ClickType.RIGHT, SLOT)); - verify(island).setFlag(eq(flag), eq(RanksManager.MEMBER_RANK)); + verify(island).setFlag(flag, RanksManager.MEMBER_RANK); verify(pim, times(2)).callEvent(any(FlagProtectionChangeEvent.class)); } + /** + * Test for {@link CycleClick#onClick(world.bentobox.bentobox.api.panels.Panel, User, ClickType, int)} + */ @Test public void testAllClicks() { // Test all possible click types - CycleClick udc = new CycleClick("LOCK"); + CycleClick udc = new CycleClick(LOCK); Arrays.asList(ClickType.values()).forEach(c -> assertTrue(udc.onClick(panel, user, c, 0))); verify(pim, times(2)).callEvent(any(FlagProtectionChangeEvent.class)); } + @Test + public void testNoWorld() { + CycleClick udc = new CycleClick(LOCK); + when(panel.getWorld()).thenReturn(Optional.empty()); + assertTrue(udc.onClick(panel, user, ClickType.SHIFT_LEFT, SLOT)); + verify(plugin).logError("Panel name has no world associated with it. Please report this bug to the author."); + } + /** + * Test for {@link CycleClick#onClick(world.bentobox.bentobox.api.panels.Panel, User, ClickType, int)} + */ + @Test + public void testOnShiftLeftClickNotOp() { + CycleClick udc = new CycleClick(LOCK); + // Click shift left + assertTrue(udc.onClick(panel, user, ClickType.SHIFT_LEFT, SLOT)); + verify(user, never()).sendMessage(anyString()); + } + + /** + * Test for {@link CycleClick#onClick(world.bentobox.bentobox.api.panels.Panel, User, ClickType, int)} + */ + @Test + public void testOnShiftLeftClickIsOp() { + when(user.isOp()).thenReturn(true); + CycleClick udc = new CycleClick(LOCK); + // Click shift left + assertTrue(hiddenFlags.isEmpty()); + assertTrue(udc.onClick(panel, user, ClickType.SHIFT_LEFT, SLOT)); + assertFalse(hiddenFlags.isEmpty()); + assertEquals(LOCK, hiddenFlags.get(0)); + // Click shift left again to remove flag + assertTrue(udc.onClick(panel, user, ClickType.SHIFT_LEFT, SLOT)); + assertTrue(hiddenFlags.isEmpty()); + // Verify sounds + verify(p).playSound(user.getLocation(), Sound.BLOCK_GLASS_BREAK, 1F, 1F); + verify(p).playSound(user.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1F, 1F); + } } From 6599e3de80bfb0132de24e17d13416a3d5d69015 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 21 Mar 2024 19:20:31 -0700 Subject: [PATCH 03/23] Sort player's islands by age so they are always in the same order. --- .../api/commands/island/IslandGoCommand.java | 5 ++--- .../api/commands/island/IslandHomesCommand.java | 3 +-- .../team/IslandTeamInviteAcceptCommand.java | 3 +-- .../bentobox/managers/IslandsManager.java | 4 ++-- .../bentobox/managers/island/IslandCache.java | 16 ++++++++++------ .../island/IslandDeletehomeCommandTest.java | 3 +-- .../api/commands/island/IslandGoCommandTest.java | 4 ++-- .../commands/island/IslandHomesCommandTest.java | 6 +++--- .../island/IslandSethomeCommandTest.java | 5 ++--- .../island/IslandSetnameCommandTest.java | 1 - .../bentobox/managers/IslandsManagerTest.java | 3 ++- 11 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandGoCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandGoCommand.java index 852e92366..3552580d8 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandGoCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandGoCommand.java @@ -6,7 +6,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.DelayedTeleportCommand; @@ -41,7 +40,7 @@ public class IslandGoCommand extends DelayedTeleportCommand { user.sendMessage("commands.island.go.teleport"); return false; } - Set islands = getIslands().getIslands(getWorld(), user.getUniqueId()); + List islands = getIslands().getIslands(getWorld(), user.getUniqueId()); if (islands.isEmpty()) { user.sendMessage("general.errors.no-island"); return false; @@ -86,7 +85,7 @@ public class IslandGoCommand extends DelayedTeleportCommand { return true; } - private boolean checkReserved(User user, Set islands) { + private boolean checkReserved(User user, List islands) { for (Island island : islands) { if (island.isReserved()) { // Send player to create an island diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandHomesCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandHomesCommand.java index bb31708f4..33b077c5f 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandHomesCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandHomesCommand.java @@ -3,7 +3,6 @@ package world.bentobox.bentobox.api.commands.island; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.Set; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.ConfirmableCommand; @@ -14,7 +13,7 @@ import world.bentobox.bentobox.util.Util; public class IslandHomesCommand extends ConfirmableCommand { - private Set islands; + private List islands; public IslandHomesCommand(CompositeCommand islandCommand) { super(islandCommand, "homes"); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java index db2a23be0..c11ff606a 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java @@ -1,7 +1,6 @@ package world.bentobox.bentobox.api.commands.island.team; import java.util.List; -import java.util.Set; import java.util.UUID; import world.bentobox.bentobox.api.commands.CompositeCommand; @@ -144,7 +143,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { // Remove the invite itc.removeInvite(user.getUniqueId()); // Get the player's island - may be null if the player has no island - Set islands = getIslands().getIslands(getWorld(), user.getUniqueId()); + List islands = getIslands().getIslands(getWorld(), user.getUniqueId()); // Get the team's island Island teamIsland = invite.getIsland(); if (teamIsland == null) { diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index af7dea9cd..5c78f52a8 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -321,7 +321,7 @@ public class IslandsManager { * @return List of islands or empty list if none found for user */ @NonNull - public Set getIslands(@NonNull World world, @NonNull User user) { + public List getIslands(@NonNull World world, @NonNull User user) { return getIslands(world, user.getUniqueId()); } @@ -333,7 +333,7 @@ public class IslandsManager { * @return List of islands or empty list if none found for user */ @NonNull - public Set getIslands(@NonNull World world, UUID uniqueId) { + public List getIslands(@NonNull World world, UUID uniqueId) { return islandCache.getIslands(world, uniqueId); } diff --git a/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java b/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java index 360e3b023..a72560ded 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java @@ -1,10 +1,13 @@ package world.bentobox.bentobox.managers.island; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -170,7 +173,7 @@ public class IslandCache { */ @Nullable public Island get(@NonNull World world, @NonNull UUID uuid) { - Set islands = getIslands(world, uuid); + List islands = getIslands(world, uuid); if (islands.isEmpty()) { return null; } @@ -190,15 +193,16 @@ public class IslandCache { * * @param world world to check. Includes nether and end worlds. * @param uuid player's UUID - * @return list of island or empty list if none + * @return list of island or empty list if none sorted from oldest to youngest */ - public Set getIslands(@NonNull World world, @NonNull UUID uuid) { + public List getIslands(@NonNull World world, @NonNull UUID uuid) { World w = Util.getWorld(world); if (w == null) { - return new HashSet<>(); + return new ArrayList<>(); } - return islandsByUUID.computeIfAbsent(uuid, k -> new HashSet<>()).stream().filter(i -> w.equals(i.getWorld())) - .collect(Collectors.toSet()); + return islandsByUUID.computeIfAbsent(uuid, k -> new HashSet<>()).stream().filter(island -> w.equals(island.getWorld())) + .sorted(Comparator.comparingLong(Island::getCreatedDate)) + .collect(Collectors.toList()); } /** 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 index 738101e7d..a95434c81 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandDeletehomeCommandTest.java @@ -14,7 +14,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.UUID; import org.bukkit.Bukkit; @@ -115,7 +114,7 @@ public class IslandDeletehomeCommandTest extends RanksManagerBeforeClassTest { when(island.onIsland(any())).thenReturn(true); when(im.getIsland(world, uuid)).thenReturn(island); when(im.getIsland(world, user)).thenReturn(island); - when(im.getIslands(world, uuid)).thenReturn(Set.of(island)); + when(im.getIslands(world, uuid)).thenReturn(List.of(island)); @NotNull Map homeMap = new HashMap<>(); homeMap.put("Home", null); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandGoCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandGoCommandTest.java index 113da5661..cf55bd3d5 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandGoCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandGoCommandTest.java @@ -126,7 +126,7 @@ public class IslandGoCommandTest { when(ic.getWorld()).thenReturn(world); // Player has island by default - when(im.getIslands(world, uuid)).thenReturn(Set.of(island)); + when(im.getIslands(world, uuid)).thenReturn(List.of(island)); when(im.hasIsland(world, uuid)).thenReturn(true); // when(im.isOwner(world, uuid)).thenReturn(true); when(plugin.getIslands()).thenReturn(im); @@ -204,7 +204,7 @@ public class IslandGoCommandTest { */ @Test public void testExecuteNoArgsNoIsland() { - when(im.getIslands(world, uuid)).thenReturn(Set.of()); + when(im.getIslands(world, uuid)).thenReturn(List.of()); assertFalse(igc.canExecute(user, igc.getLabel(), Collections.emptyList())); verify(player).sendMessage("general.errors.no-island"); } diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandHomesCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandHomesCommandTest.java index a60261a11..b551c3ff5 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandHomesCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandHomesCommandTest.java @@ -14,8 +14,8 @@ import static org.mockito.Mockito.when; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.Set; import java.util.UUID; import org.bukkit.Bukkit; @@ -189,7 +189,7 @@ public class IslandHomesCommandTest { */ @Test public void testCanExecute() { - when(im.getIslands(world, user)).thenReturn(Set.of(island)); + when(im.getIslands(world, user)).thenReturn(List.of(island)); IslandHomesCommand isc = new IslandHomesCommand(ic); assertTrue(isc.canExecute(user, "island", Collections.emptyList())); verify(user, never()).sendMessage("general.errors.no-island"); @@ -200,7 +200,7 @@ public class IslandHomesCommandTest { */ @Test public void testExecuteUserStringListOfString() { - when(im.getIslands(world, user)).thenReturn(Set.of(island)); + when(im.getIslands(world, user)).thenReturn(List.of(island)); IslandHomesCommand isc = new IslandHomesCommand(ic); assertTrue(isc.canExecute(user, "island", Collections.emptyList())); assertTrue(isc.execute(user, "island", Collections.emptyList())); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSethomeCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSethomeCommandTest.java index c80f05563..cf4624dc3 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSethomeCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSethomeCommandTest.java @@ -14,7 +14,7 @@ import static org.mockito.Mockito.when; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Set; +import java.util.List; import java.util.UUID; import org.bukkit.Bukkit; @@ -104,8 +104,7 @@ public class IslandSethomeCommandTest { // Island for player to begin with when(im.hasIsland(world, user)).thenReturn(true); - // when(im.isOwner(world, uuid)).thenReturn(true); - when(im.getIslands(world, user)).thenReturn(Set.of(island)); + when(im.getIslands(world, user)).thenReturn(List.of(island)); when(plugin.getIslands()).thenReturn(im); // Has team 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 index 95d5e2bcd..890c4cde9 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandSetnameCommandTest.java @@ -77,7 +77,6 @@ public class IslandSetnameCommandTest { private IslandSetnameCommand isc; @Mock private @NonNull World world; - private RanksManager rm; private Settings settings; @Mock private PluginManager pim; diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java index d9333195c..153f528cf 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -250,7 +251,7 @@ public class IslandsManagerTest extends AbstractCommonSetup { when(islandCache.getIslandAt(any(Location.class))).thenReturn(island); when(islandCache.get(any(), any())).thenReturn(island); optionalIsland = Optional.ofNullable(island); - when(islandCache.getIslands(world, uuid)).thenReturn(Set.of(island)); + when(islandCache.getIslands(world, uuid)).thenReturn(List.of(island)); // User location when(user.getLocation()).thenReturn(location); From 44454f585489cc77c23c3e4e467bedc105890416 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 24 Mar 2024 18:44:24 -0700 Subject: [PATCH 04/23] Adds support for multi islands to the admin range command --- .../range/AbstractAdminRangeCommand.java | 103 +++++++++++++++ .../admin/range/AdminRangeAddCommand.java | 54 ++------ .../admin/range/AdminRangeCommand.java | 3 +- .../admin/range/AdminRangeRemoveCommand.java | 37 ++---- .../admin/range/AdminRangeSetCommand.java | 51 ++------ .../bentobox/util/DefaultPasteUtil.java | 4 +- src/main/resources/locales/en-US.yml | 6 +- .../admin/range/AdminRangeSetCommandTest.java | 123 ++++++++++-------- 8 files changed, 211 insertions(+), 170 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/api/commands/admin/range/AbstractAdminRangeCommand.java diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AbstractAdminRangeCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AbstractAdminRangeCommand.java new file mode 100644 index 000000000..7f0876e9c --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AbstractAdminRangeCommand.java @@ -0,0 +1,103 @@ +package world.bentobox.bentobox.api.commands.admin.range; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + +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.util.Util; + +/** + * @author Poslovitch + */ +public abstract class AbstractAdminRangeCommand extends CompositeCommand { + + protected @Nullable UUID targetUUID; + protected Island targetIsland; + + public AbstractAdminRangeCommand(CompositeCommand parent, String string) { + super(parent, string); + } + + @Override + public boolean canExecute(User user, String label, @NonNull List args) { + if (args.size() <= 1) { + showHelp(this, user); + return false; + } + + targetUUID = Util.getUUID(args.get(0)); + if (targetUUID == null) { + user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); + return false; + } + + if (!Util.isInteger(args.get(1), true) || Integer.parseInt(args.get(1)) < 0) { + user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(1)); + return false; + } + // Check if the player has more than one island + Map islands = getIslandsXYZ(targetUUID); + if (islands.size() == 0) { + user.sendMessage("general.errors.player-has-no-island"); + return false; + } else if (args.size() == 2) { + // If they only have one island, 2 args are fine + if (islands.size() == 1) { + targetIsland = islands.values().iterator().next(); + return true; + } else { + // They need to specify which island + user.sendMessage("commands.admin.unregister.errors.player-has-more-than-one-island"); + user.sendMessage("commands.admin.unregister.errors.specify-island-location"); + return false; + } + } else if (args.size() != 3) { + // No location + user.sendMessage("commands.admin.unregister.errors.specify-island-location"); + return false; + } else if (!islands.containsKey(args.get(2))) { + if (args.get(2).equalsIgnoreCase("help")) { + this.showHelp(this, user); + return false; + } + user.sendMessage("commands.admin.unregister.errors.unknown-island-location"); + return false; + } + targetIsland = islands.get(args.get(2)); + return true; + } + + protected Map getIslandsXYZ(UUID target) { + return getIslands().getOwnedIslands(getWorld(), target).stream() + .collect(Collectors.toMap(island -> Util.xyz(island.getCenter().toVector()), island -> island)); + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : ""; + if (args.isEmpty()) { + // Don't show every player on the server. Require at least the first letter + return Optional.empty(); + } else if (args.size() == 3) { + List options = new ArrayList<>(Util.getOnlinePlayerList(user)); + return Optional.of(Util.tabLimit(options, lastArg)); + } else if (args.size() > 4) { + // Find out which user + UUID uuid = getPlayers().getUUID(args.get(2)); + if (uuid != null) { + return Optional.of(Util.tabLimit(new ArrayList<>(getIslandsXYZ(uuid).keySet()), lastArg)); + } + } + return Optional.empty(); + } +} 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 566e4a30c..158ae1739 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 @@ -1,23 +1,18 @@ package world.bentobox.bentobox.api.commands.admin.range; import java.util.List; -import java.util.Objects; -import java.util.UUID; import org.eclipse.jdt.annotation.NonNull; -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; -import world.bentobox.bentobox.util.Util; /** * @since 1.10.0 * @author Poslovitch */ -public class AdminRangeAddCommand extends CompositeCommand { +public class AdminRangeAddCommand extends AbstractAdminRangeCommand { public AdminRangeAddCommand(AdminRangeCommand parent) { super(parent, "add"); @@ -32,53 +27,28 @@ public class AdminRangeAddCommand extends CompositeCommand { @Override public boolean execute(User user, String label, @NonNull List args) { - if (args.size() != 2) { - showHelp(this, user); - return false; - } + int newRange = targetIsland.getProtectionRange() + Integer.parseInt(args.get(1)); - UUID targetUUID = Util.getUUID(args.get(0)); - if (targetUUID == null) { - user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); + if (newRange > targetIsland.getRange()) { + user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, + String.valueOf(targetIsland.getRange())); return false; - } - - if (!(getIslands().hasIsland(getWorld(), targetUUID) || getIslands().inTeam(getWorld(), targetUUID))) { - user.sendMessage("general.errors.player-has-no-island"); - return false; - } - - if (!Util.isInteger(args.get(1), true) || Integer.parseInt(args.get(1)) < 0) { - user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(1)); - return false; - } - - Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID)); - int newRange = island.getProtectionRange() + Integer.parseInt(args.get(1)); - - if (newRange > island.getRange()) { - user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, String.valueOf(island.getRange())); - return false; - } else if (newRange == island.getProtectionRange()) { + } else if (newRange == targetIsland.getProtectionRange()) { user.sendMessage("commands.admin.range.invalid-value.same-as-before", TextVariables.NUMBER, args.get(1)); return false; } // Get old range for event - int oldRange = island.getProtectionRange(); + int oldRange = targetIsland.getProtectionRange(); // Well, now it can be applied without taking any risks! - island.setProtectionRange(newRange); + targetIsland.setProtectionRange(newRange); // Call Protection Range Change event. Does not support cancelling. IslandEvent.builder() - .island(island) - .location(island.getCenter()) - .reason(IslandEvent.Reason.RANGE_CHANGE) - .involvedPlayer(targetUUID) - .admin(true) - .protectionRange(newRange, oldRange) - .build(); + .island(targetIsland).location(targetIsland.getCenter()) + .reason(IslandEvent.Reason.RANGE_CHANGE).involvedPlayer(targetUUID).admin(true) + .protectionRange(newRange, oldRange).build(); user.sendMessage("commands.admin.range.add.success", TextVariables.NAME, args.get(0), TextVariables.NUMBER, args.get(1), @@ -86,4 +56,6 @@ public class AdminRangeAddCommand extends CompositeCommand { return true; } + + } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeCommand.java index a7b43ba78..05b4afa39 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeCommand.java @@ -31,4 +31,5 @@ public class AdminRangeCommand extends CompositeCommand { showHelp(this, user); return true; } -} + +} \ No newline at end of file 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 4679bc2bc..74a89fa00 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 @@ -1,23 +1,19 @@ package world.bentobox.bentobox.api.commands.admin.range; import java.util.List; -import java.util.Objects; -import java.util.UUID; import org.eclipse.jdt.annotation.NonNull; -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; import world.bentobox.bentobox.util.Util; /** * @since 1.10.0 * @author Poslovitch */ -public class AdminRangeRemoveCommand extends CompositeCommand { +public class AdminRangeRemoveCommand extends AbstractAdminRangeCommand { public AdminRangeRemoveCommand(AdminRangeCommand parent) { super(parent, "remove"); @@ -32,48 +28,31 @@ public class AdminRangeRemoveCommand extends CompositeCommand { @Override public boolean execute(User user, String label, @NonNull List args) { - if (args.size() != 2) { - showHelp(this, user); - return false; - } - - UUID targetUUID = Util.getUUID(args.get(0)); - if (targetUUID == null) { - user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); - return false; - } - - if (!(getIslands().hasIsland(getWorld(), targetUUID) || getIslands().inTeam(getWorld(), targetUUID))) { - user.sendMessage("general.errors.player-has-no-island"); - return false; - } - if (!Util.isInteger(args.get(1), true) || Integer.parseInt(args.get(1)) < 0) { user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(1)); return false; } - Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID)); - int newRange = island.getProtectionRange() - Integer.parseInt(args.get(1)); + int newRange = targetIsland.getProtectionRange() - Integer.parseInt(args.get(1)); if (newRange <= 1) { - user.sendMessage("commands.admin.range.invalid-value.too-low", TextVariables.NUMBER, String.valueOf(island.getRange())); + user.sendMessage("commands.admin.range.invalid-value.too-low", TextVariables.NUMBER, + String.valueOf(targetIsland.getRange())); return false; - } else if (newRange == island.getProtectionRange()) { + } else if (newRange == targetIsland.getProtectionRange()) { user.sendMessage("commands.admin.range.invalid-value.same-as-before", TextVariables.NUMBER, args.get(1)); return false; } // Get old range for event - int oldRange = island.getProtectionRange(); + int oldRange = targetIsland.getProtectionRange(); // Well, now it can be applied without taking any risks! - island.setProtectionRange(newRange); + targetIsland.setProtectionRange(newRange); // Call Protection Range Change event. Does not support cancelling. IslandEvent.builder() - .island(island) - .location(island.getCenter()) + .island(targetIsland).location(targetIsland.getCenter()) .reason(IslandEvent.Reason.RANGE_CHANGE) .involvedPlayer(targetUUID) .admin(true) diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeSetCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeSetCommand.java index c63912004..bf3022306 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeSetCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeSetCommand.java @@ -1,19 +1,14 @@ package world.bentobox.bentobox.api.commands.admin.range; -import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; 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; import world.bentobox.bentobox.util.Util; -public class AdminRangeSetCommand extends CompositeCommand { +public class AdminRangeSetCommand extends AbstractAdminRangeCommand { public AdminRangeSetCommand(CompositeCommand parent) { super(parent, "set"); @@ -28,23 +23,6 @@ public class AdminRangeSetCommand extends CompositeCommand { @Override public boolean execute(User user, String label, List args) { - if (args.size() != 2) { - // Show help - showHelp(this, user); - return false; - } - - // Get target player - UUID targetUUID = Util.getUUID(args.get(0)); - if (targetUUID == null) { - user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); - return false; - } - if (!(getIslands().hasIsland(getWorld(), targetUUID) || getIslands().inTeam(getWorld(), targetUUID))) { - user.sendMessage("general.errors.player-has-no-island"); - return false; - } - // Get new range if (!Util.isInteger(args.get(1), true) || Integer.parseInt(args.get(1)) < 0) { user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(1)); @@ -52,33 +30,30 @@ public class AdminRangeSetCommand extends CompositeCommand { } int range = Integer.parseInt(args.get(1)); - // Get island - Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID)); - // Do some sanity checks to make sure the new protection range won't cause problems if (range < 1) { user.sendMessage("commands.admin.range.invalid-value.too-low", TextVariables.NUMBER, args.get(1)); return false; } - if (range > island.getRange() * 2) { - user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, String.valueOf(2 * island.getRange())); + if (range > targetIsland.getRange() * 2) { + user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, + String.valueOf(2 * targetIsland.getRange())); return false; } - if (range == island.getProtectionRange()) { + if (range == targetIsland.getProtectionRange()) { user.sendMessage("commands.admin.range.invalid-value.same-as-before", TextVariables.NUMBER, args.get(1)); return false; } // Get old range for event - int oldRange = island.getProtectionRange(); + int oldRange = targetIsland.getProtectionRange(); // Well, now it can be applied without taking any risks! - island.setProtectionRange(range); + targetIsland.setProtectionRange(range); // Call Protection Range Change event. Does not support canceling. IslandEvent.builder() - .island(island) - .location(island.getCenter()) + .island(targetIsland).location(targetIsland.getCenter()) .reason(IslandEvent.Reason.RANGE_CHANGE) .involvedPlayer(targetUUID) .admin(true) @@ -90,14 +65,4 @@ public class AdminRangeSetCommand extends CompositeCommand { return true; } - @Override - public Optional> tabComplete(User user, String alias, List args) { - String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; - if (args.isEmpty()) { - // Don't show every player on the server. Require at least the first letter - return Optional.empty(); - } - List options = new ArrayList<>(Util.getOnlinePlayerList(user)); - return Optional.of(Util.tabLimit(options, lastArg)); - } } diff --git a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java index 76fb68be9..2559f3e2b 100644 --- a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java +++ b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java @@ -81,8 +81,10 @@ public class DefaultPasteUtil { */ public static BlockData createBlockData(BlueprintBlock block) { try { - return Bukkit.createBlockData(block.getBlockData()); + return Material.STONE.createBlockData(); + //return Bukkit.createBlockData(block.getBlockData()); } catch (Exception e) { + BentoBox.getInstance().logStacktrace(e); return convertBlockData(block); } } diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 61af1f1a1..9c0838c47 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -166,7 +166,7 @@ commands: &a Green Particles &f show the default protected range if the island protection range differs from it. showing: '&2 Showing range indicators' set: - parameters: + parameters: [island location] description: sets the island protected range success: '&a Set island protection range to &b [number]&a .' reset: @@ -175,12 +175,12 @@ commands: success: '&a Reset island protection range to &b [number]&a .' add: description: increases the island protected range - parameters: + parameters: [island location] success: '&a Successfully increased &b [name]&a ''s island protected range to &b [total] &7 (&b +[number]&7 )&a .' remove: description: decreases the island protected range - parameters: + parameters: [island location] success: '&a Successfully decreased &b [name]&a ''s island protected range to &b [total] &7 (&b -[number]&7 )&a .' register: diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeSetCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeSetCommandTest.java index 03378be18..f7c3b940a 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeSetCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeSetCommandTest.java @@ -1,7 +1,11 @@ package world.bentobox.bentobox.api.commands.admin.range; +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.Mockito.mock; -import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; @@ -9,14 +13,18 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.util.Vector; +import org.eclipse.jdt.annotation.NonNull; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -56,6 +64,8 @@ public class AdminRangeSetCommandTest { private PlayersManager pm; @Mock private PluginManager pim; + @Mock + private @NonNull Location location; /** */ @@ -92,23 +102,25 @@ public class AdminRangeSetCommandTest { // Island World Manager IslandWorldManager iwm = mock(IslandWorldManager.class); - when(iwm.getFriendlyName(Mockito.any())).thenReturn("BSkyBlock"); - when(iwm.getIslandProtectionRange(Mockito.any())).thenReturn(200); + when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + when(iwm.getIslandProtectionRange(any())).thenReturn(200); when(plugin.getIWM()).thenReturn(iwm); // Player has island to begin with im = mock(IslandsManager.class); - when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); - when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true); + when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); + when(im.hasIsland(any(), any(User.class))).thenReturn(true); Island island = mock(Island.class); when(island.getRange()).thenReturn(50); when(island.getProtectionRange()).thenReturn(50); - when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + when(location.toVector()).thenReturn(new Vector(2, 3, 4)); + when(island.getCenter()).thenReturn(location); + when(im.getOwnedIslands(any(), any(UUID.class))).thenReturn(Set.of(island)); when(plugin.getIslands()).thenReturn(im); // Has team pm = mock(PlayersManager.class); - when(im.inTeam(Mockito.any(), Mockito.eq(uuid))).thenReturn(true); + when(im.inTeam(any(), Mockito.eq(uuid))).thenReturn(true); when(plugin.getPlayers()).thenReturn(pm); @@ -122,11 +134,11 @@ public class AdminRangeSetCommandTest { LocalesManager lm = mock(LocalesManager.class); Answer answer = invocation -> invocation.getArgument(1, String.class); - when(lm.get(Mockito.any(), Mockito.any())).thenAnswer(answer); + when(lm.get(any(), any())).thenAnswer(answer); when(plugin.getLocalesManager()).thenReturn(lm); // Addon - when(iwm.getAddon(Mockito.any())).thenReturn(Optional.empty()); + when(iwm.getAddon(any())).thenReturn(Optional.empty()); } @After @@ -137,69 +149,72 @@ public class AdminRangeSetCommandTest { /** * Test method for - * {@link world.bentobox.bentobox.api.commands.admin.range.AdminRangeSetCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * {@link world.bentobox.bentobox.api.commands.admin.range.AdminRangeSetCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testExecuteConsoleNoArgs() { AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); CommandSender sender = mock(CommandSender.class); User console = User.getInstance(sender); - arc.execute(console, "", new ArrayList<>()); + assertFalse(arc.canExecute(console, "", new ArrayList<>())); // Show help - Mockito.verify(sender).sendMessage("commands.help.header"); + verify(sender).sendMessage("commands.help.header"); } /** * Test method for - * {@link world.bentobox.bentobox.api.commands.admin.range.AdminRangeSetCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * {@link world.bentobox.bentobox.api.commands.admin.range.AdminRangeSetCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testExecutePlayerNoArgs() { AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); - arc.execute(user, "", new ArrayList<>()); + assertFalse(arc.canExecute(user, "", List.of())); // Show help - Mockito.verify(user).sendMessage("commands.help.header", "[label]", "BSkyBlock"); + verify(user).sendMessage("commands.help.header", "[label]", "BSkyBlock"); } /** * Test method for - * {@link world.bentobox.bentobox.api.commands.admin.range.AdminRangeSetCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * {@link world.bentobox.bentobox.api.commands.admin.range.AdminRangeSetCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testExecuteUnknownPlayer() { AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); String[] args = { "tastybento", "100" }; - arc.execute(user, "", Arrays.asList(args)); - Mockito.verify(user).sendMessage("general.errors.unknown-player", "[name]", args[0]); + assertFalse(arc.canExecute(user, "", Arrays.asList(args))); + verify(user).sendMessage("general.errors.unknown-player", "[name]", args[0]); } /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.range.AdminRangeSetCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link world.bentobox.bentobox.api.commands.admin.range.AdminRangeSetCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testExecuteKnownPlayerNotOwnerNoTeam() { - when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); - when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); - when(im.inTeam(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); + when(pm.getUUID(anyString())).thenReturn(uuid); + when(im.getOwnedIslands(any(), any(UUID.class))).thenReturn(Set.of()); + when(im.inTeam(any(), any(UUID.class))).thenReturn(false); AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); List args = new ArrayList<>(); args.add("tastybento"); args.add("100"); - arc.execute(user, "", args); - Mockito.verify(user).sendMessage("general.errors.player-has-no-island"); + assertFalse(arc.canExecute(user, "", args)); + verify(user).sendMessage("general.errors.player-has-no-island"); } + /** + * Test method for {@link world.bentobox.bentobox.api.commands.admin.range.AdminRangeSetCommand#canExecute(User, String, List)} + */ @Test public void testExecuteKnownPlayerNotOwnerButInTeam() { - when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); - when(im.hasIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(false); - when(im.inTeam(Mockito.any(), Mockito.any(UUID.class))).thenReturn(true); + when(pm.getUUID(anyString())).thenReturn(uuid); + when(im.getOwnedIslands(any(), any(UUID.class))).thenReturn(Set.of()); + when(im.inTeam(any(), any(UUID.class))).thenReturn(true); AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); List args = new ArrayList<>(); args.add("tastybento"); args.add("100"); - arc.execute(user, "", args); - Mockito.verify(user, never()).sendMessage("general.errors.player-has-no-island"); + assertFalse(arc.canExecute(user, "", args)); + verify(user).sendMessage("general.errors.player-has-no-island"); } /** @@ -207,41 +222,42 @@ public class AdminRangeSetCommandTest { */ @Test public void testExecuteTooHigh() { - when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + when(pm.getUUID(anyString())).thenReturn(uuid); AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); List args = new ArrayList<>(); args.add("tastybento"); args.add("1000"); - arc.execute(user, "", args); - Mockito.verify(user).sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, "100"); + assertTrue(arc.canExecute(user, "", args)); + assertFalse(arc.execute(user, "", args)); + verify(user).sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, "100"); } /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.range.AdminRangeSetCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link world.bentobox.bentobox.api.commands.admin.range.AdminRangeSetCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test public void testExecuteNotANumber() { - when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + when(pm.getUUID(anyString())).thenReturn(uuid); AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); List args = new ArrayList<>(); args.add("tastybento"); args.add("NAN"); - arc.execute(user, "", args); - Mockito.verify(user).sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, "NAN"); + assertFalse(arc.canExecute(user, "", args)); + verify(user).sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, "NAN"); } /** - * Test method for {@link world.bentobox.bentobox.api.commands.admin.range.AdminRangeSetCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + * Test method for {@link world.bentobox.bentobox.api.commands.admin.range.AdminRangeSetCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. */ @Test() public void testExecuteDoubleNumber() { - when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + when(pm.getUUID(anyString())).thenReturn(uuid); AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); List args = new ArrayList<>(); args.add("tastybento"); args.add("3.141592654"); - arc.execute(user, "", args); - Mockito.verify(user).sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, "3.141592654"); + assertFalse(arc.canExecute(user, "", args)); + verify(user).sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, "3.141592654"); } /** @@ -249,13 +265,14 @@ public class AdminRangeSetCommandTest { */ @Test public void testExecuteZero() { - when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + when(pm.getUUID(anyString())).thenReturn(uuid); AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); List args = new ArrayList<>(); args.add("tastybento"); args.add("0"); - arc.execute(user, "", args); - Mockito.verify(user).sendMessage("commands.admin.range.invalid-value.too-low", TextVariables.NUMBER, "0"); + assertTrue(arc.canExecute(user, "", args)); + assertFalse(arc.execute(user, "", args)); + verify(user).sendMessage("commands.admin.range.invalid-value.too-low", TextVariables.NUMBER, "0"); } /** @@ -263,13 +280,13 @@ public class AdminRangeSetCommandTest { */ @Test public void testExecuteNegNumber() { - when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + when(pm.getUUID(anyString())).thenReturn(uuid); AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); List args = new ArrayList<>(); args.add("tastybento"); args.add("-437645"); - arc.execute(user, "", args); - Mockito.verify(user).sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, "-437645"); + assertFalse(arc.canExecute(user, "", args)); + verify(user).sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, "-437645"); } /** @@ -277,13 +294,14 @@ public class AdminRangeSetCommandTest { */ @Test public void testExecuteSame() { - when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + when(pm.getUUID(anyString())).thenReturn(uuid); AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); List args = new ArrayList<>(); args.add("tastybento"); args.add("50"); - arc.execute(user, "", args); - Mockito.verify(user).sendMessage("commands.admin.range.invalid-value.same-as-before", TextVariables.NUMBER, "50"); + assertTrue(arc.canExecute(user, "", args)); + assertFalse(arc.execute(user, "", args)); + verify(user).sendMessage("commands.admin.range.invalid-value.same-as-before", TextVariables.NUMBER, "50"); } /** @@ -291,13 +309,14 @@ public class AdminRangeSetCommandTest { */ @Test public void testExecute() { - when(pm.getUUID(Mockito.anyString())).thenReturn(uuid); + when(pm.getUUID(anyString())).thenReturn(uuid); AdminRangeSetCommand arc = new AdminRangeSetCommand(ac); List args = new ArrayList<>(); args.add("tastybento"); args.add("48"); - arc.execute(user, "", args); - Mockito.verify(user).sendMessage("commands.admin.range.set.success", TextVariables.NUMBER, "48"); + assertTrue(arc.canExecute(user, "", args)); + assertTrue(arc.execute(user, "", args)); + verify(user).sendMessage("commands.admin.range.set.success", TextVariables.NUMBER, "48"); } } From d8f2c12fe5b88459aefc5a1298dbfaa8759e0755 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 28 Mar 2024 21:27:48 -0700 Subject: [PATCH 05/23] Remove pasting blueprints as stone debug. --- .../java/world/bentobox/bentobox/util/DefaultPasteUtil.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java index 2559f3e2b..f917b0e2d 100644 --- a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java +++ b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java @@ -81,8 +81,7 @@ public class DefaultPasteUtil { */ public static BlockData createBlockData(BlueprintBlock block) { try { - return Material.STONE.createBlockData(); - //return Bukkit.createBlockData(block.getBlockData()); + return Bukkit.createBlockData(block.getBlockData()); } catch (Exception e) { BentoBox.getInstance().logStacktrace(e); return convertBlockData(block); From ea8562f35103611626db509a8f98e2a412176226 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 28 Mar 2024 21:42:33 -0700 Subject: [PATCH 06/23] Remove more debug --- src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java index f917b0e2d..76fb68be9 100644 --- a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java +++ b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java @@ -83,7 +83,6 @@ public class DefaultPasteUtil { try { return Bukkit.createBlockData(block.getBlockData()); } catch (Exception e) { - BentoBox.getInstance().logStacktrace(e); return convertBlockData(block); } } From 1bce4ec1b996ae8ec2425fc566746b1ba68c7310 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 28 Mar 2024 21:58:01 -0700 Subject: [PATCH 07/23] Limit blueprint pasting to world y coords. Addresses #2334 --- .../world/bentobox/bentobox/blueprints/BlueprintPaster.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java index 00a067a97..d62f8807d 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java @@ -119,6 +119,9 @@ public class BlueprintPaster { Vector off = bp.getBedrock() != null ? bp.getBedrock() : new Vector(0,0,0); // Calculate location for pasting this.location = island.getProtectionCenter().toVector().subtract(off).toLocation(world); + // Ensure the y coordinate is within the world limits + int y = Math.min(world.getMaxHeight() - 1, Math.max(world.getMinHeight(), location.getBlockY())); + location.setY(y); } private record Bits(Map blocks, From 2b19d43c856023d185ef5defb63d5811f1862fae Mon Sep 17 00:00:00 2001 From: tastybento Date: Fri, 29 Mar 2024 19:38:44 -0700 Subject: [PATCH 08/23] Remove restrictions on having multiple islands for team members. Added API to enable checking for teams on islands easier. --- pom.xml | 2 +- .../api/commands/CompositeCommand.java | 1 + .../commands/admin/AdminDeleteCommand.java | 2 +- .../admin/team/AdminTeamAddCommand.java | 6 +++--- .../admin/team/AdminTeamDisbandCommand.java | 10 ++++++---- .../api/commands/island/IslandBanCommand.java | 2 +- .../commands/island/IslandCreateCommand.java | 3 ++- .../commands/island/IslandExpelCommand.java | 2 +- .../team/IslandTeamInviteAcceptCommand.java | 16 ++++++++++------ .../island/team/IslandTeamInviteCommand.java | 6 ++++-- .../island/team/IslandTeamKickCommand.java | 2 +- .../island/team/IslandTeamPromoteCommand.java | 2 +- .../team/IslandTeamSetownerCommand.java | 4 ++-- .../island/team/IslandTeamUncoopCommand.java | 2 +- .../island/team/IslandTeamUntrustCommand.java | 2 +- .../api/configuration/WorldSettings.java | 8 ++++++++ .../bentobox/database/objects/Island.java | 19 +++++++++++++++++++ .../bentobox/listeners/JoinLeaveListener.java | 2 +- .../worldsettings/EnterExitListener.java | 4 ++-- .../worldsettings/PetTeleportListener.java | 2 +- .../VisitorsStartingRaidListener.java | 2 +- .../bentobox/managers/IslandsManager.java | 9 +++++---- .../admin/AdminDeleteCommandTest.java | 4 ++++ .../admin/team/AdminTeamAddCommandTest.java | 8 +++++++- .../team/AdminTeamDisbandCommandTest.java | 2 +- .../commands/island/IslandBanCommandTest.java | 1 + .../island/IslandExpelCommandTest.java | 4 ++++ .../IslandTeamInviteAcceptCommandTest.java | 3 +++ .../team/IslandTeamInviteCommandTest.java | 5 +++++ .../team/IslandTeamKickCommandTest.java | 4 ++-- .../team/IslandTeamPromoteCommandTest.java | 6 ++++++ .../team/IslandTeamSetownerCommandTest.java | 11 ++++++++--- .../team/IslandTeamUncoopCommandTest.java | 1 + .../team/IslandTeamUntrustCommandTest.java | 1 + .../PetTeleportListenerTest.java | 2 ++ .../bentobox/managers/IslandsManagerTest.java | 3 +++ 36 files changed, 121 insertions(+), 42 deletions(-) diff --git a/pom.xml b/pom.xml index 181e7cc25..8aa65b84f 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ -LOCAL - 2.2.1 + 2.3.0 bentobox-world https://sonarcloud.io ${project.basedir}/lib 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 abf71ea5e..57081bac1 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/CompositeCommand.java @@ -524,6 +524,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi * @param world - the world to check * @param user - the User * @return true if player is in a team + * @see Consider checking the island itself {@link Island#inTeam(UUID)} */ protected boolean inTeam(World world, User user) { return plugin.getIslands().inTeam(world, user.getUniqueId()); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommand.java index 117785ac8..1c3a4c652 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommand.java @@ -46,7 +46,7 @@ public class AdminDeleteCommand extends ConfirmableCommand { } // Team members should be kicked before deleting otherwise the whole team will become weird - if (getIslands().inTeam(getWorld(), targetUUID) && user.getUniqueId().equals(island.getOwner())) { + if (island.hasTeam() && user.getUniqueId().equals(island.getOwner())) { user.sendMessage("commands.admin.delete.cannot-delete-owner"); return false; } 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 98d6dd2d0..29d3599d7 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 @@ -44,17 +44,17 @@ public class AdminTeamAddCommand extends CompositeCommand { user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(1)); return false; } - if (!getIslands().hasIsland(getWorld(), ownerUUID)) { + Island island = getIslands().getPrimaryIsland(getWorld(), ownerUUID); + if (island == null || !getIslands().hasIsland(getWorld(), ownerUUID)) { user.sendMessage("general.errors.player-has-no-island"); return false; } - Island island = getIslands().getPrimaryIsland(getWorld(), ownerUUID); if (getIslands().inTeam(getWorld(), ownerUUID) && island != null && !ownerUUID.equals(island.getOwner())) { user.sendMessage("commands.admin.team.add.name-not-owner", TextVariables.NAME, args.get(0)); new IslandInfo(island).showMembers(user); return false; } - if (getIslands().inTeam(getWorld(), targetUUID)) { + if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland() && island.inTeam(targetUUID)) { user.sendMessage("commands.island.team.invite.errors.already-on-team"); return false; } 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 727eae5f3..64db4dd52 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 @@ -52,10 +52,7 @@ public class AdminTeamDisbandCommand extends CompositeCommand { user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); return false; } - if (!getIslands().inTeam(getWorld(), targetUUID)) { - user.sendMessage("general.errors.player-is-not-owner", TextVariables.NAME, args.get(0)); - return false; - } + // Find the island the player is an owner of Map islands = getIslandsXYZ(targetUUID); if (islands.isEmpty()) { @@ -77,6 +74,11 @@ public class AdminTeamDisbandCommand extends CompositeCommand { // Get the only island island = islands.values().iterator().next(); } + // Check that the target owns the island + if (!island.getOwner().equals(targetUUID)) { + user.sendMessage("general.errors.player-is-not-owner", TextVariables.NAME, args.get(0)); + return false; + } return true; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandBanCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandBanCommand.java index 403eecc88..24645119e 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandBanCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandBanCommand.java @@ -70,7 +70,7 @@ public class IslandBanCommand extends CompositeCommand { user.sendMessage("commands.island.ban.cannot-ban-yourself"); return false; } - if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().contains(targetUUID)) { + if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).inTeam(targetUUID)) { user.sendMessage("commands.island.ban.cannot-ban-member"); return false; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java index 54d3c7a12..15655d4ce 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java @@ -51,7 +51,8 @@ public class IslandCreateCommand extends CompositeCommand { } } // Check if this player is on a team in this world - if (getIslands().inTeam(getWorld(), user.getUniqueId()) && island != null + if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland() + && getIslands().inTeam(getWorld(), user.getUniqueId()) && island != null && !user.getUniqueId().equals(island.getOwner())) { // Team members who are not owners cannot make additional islands user.sendMessage("commands.island.create.you-cannot-make-team"); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandExpelCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandExpelCommand.java index 6d9de5fc4..b463d9667 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandExpelCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandExpelCommand.java @@ -76,7 +76,7 @@ public class IslandExpelCommand extends CompositeCommand { return false; } // Or team member - if (island.getMemberSet().contains(targetUUID)) { + if (island.inTeam(targetUUID)) { user.sendMessage("commands.island.expel.cannot-expel-member"); return false; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java index c11ff606a..35c3188b3 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java @@ -61,7 +61,8 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { } // Check if player is already in a team - if (getIslands().inTeam(getWorld(), playerUUID)) { + if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland() + && getIslands().inTeam(getWorld(), playerUUID)) { user.sendMessage("commands.island.team.invite.errors.you-already-are-in-team"); return false; } @@ -155,17 +156,20 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { user.sendMessage("commands.island.team.invite.errors.island-is-full"); return; } - // Remove the player's other islands - getIslands().removePlayer(getWorld(), user.getUniqueId()); + if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland()) { + // Remove the player's other islands + getIslands().removePlayer(getWorld(), user.getUniqueId()); + } // Remove money inventory etc. for leaving cleanPlayer(user); // Add the player as a team member of the new island getIslands().setJoinTeam(teamIsland, user.getUniqueId()); // Move player to team's island getIslands().homeTeleportAsync(getWorld(), user.getPlayer()).thenRun(() -> { - // Delete the old islands - islands.forEach(island -> getIslands().deleteIsland(island, true, user.getUniqueId())); - + if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland()) { + // Delete the old islands + islands.forEach(island -> getIslands().deleteIsland(island, true, user.getUniqueId())); + } // Put player back into normal mode user.setGameMode(getIWM().getDefaultGameMode(getWorld())); diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java index c291e65ca..30cfbfea8 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java @@ -105,7 +105,8 @@ public class IslandTeamInviteCommand extends CompositeCommand { } // Player cannot invite someone already on a team - if (getIslands().inTeam(getWorld(), invitedPlayerUUID)) { + if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland() + && getIslands().inTeam(getWorld(), invitedPlayerUUID)) { user.sendMessage("commands.island.team.invite.errors.already-on-team"); return false; } @@ -170,7 +171,8 @@ public class IslandTeamInviteCommand extends CompositeCommand { // Send message to online player invitedPlayer.sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()); invitedPlayer.sendMessage("commands.island.team.invite.to-accept-or-reject", TextVariables.LABEL, getTopLabel()); - if (getIslands().hasIsland(getWorld(), invitedPlayer.getUniqueId())) { + if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland() + && getIslands().hasIsland(getWorld(), invitedPlayer.getUniqueId())) { invitedPlayer.sendMessage("commands.island.team.invite.you-will-lose-your-island"); } return true; diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java index 465956d4b..7c0d9a5d0 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java @@ -63,7 +63,7 @@ public class IslandTeamKickCommand extends ConfirmableCommand { user.sendMessage("commands.island.team.kick.cannot-kick"); return false; } - if (!getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().contains(targetUUID)) { + if (!getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).inTeam(targetUUID)) { user.sendMessage("general.errors.not-in-team"); return false; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamPromoteCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamPromoteCommand.java index e35e8aeea..a13c050e3 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamPromoteCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamPromoteCommand.java @@ -69,7 +69,7 @@ public class IslandTeamPromoteCommand extends CompositeCommand { return false; } // Check that target is a member of this island - if (!island.getMemberSet().contains(target.getUniqueId())) { + if (!island.inTeam(target.getUniqueId())) { user.sendMessage("commands.island.team.promote.errors.must-be-member"); return false; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java index bd1d2a1a0..e80580aa9 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java @@ -42,7 +42,7 @@ public class IslandTeamSetownerCommand extends CompositeCommand { } // Can use if in a team Island is = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()); - if (is == null || !is.getMemberSet().contains(user.getUniqueId())) { + if (is == null || !is.inTeam(user.getUniqueId())) { user.sendMessage("general.errors.no-team"); return false; } @@ -60,7 +60,7 @@ public class IslandTeamSetownerCommand extends CompositeCommand { user.sendMessage("commands.island.team.setowner.errors.cant-transfer-to-yourself"); return false; } - if (!is.getMemberSet().contains(targetUUID)) { + if (!is.inTeam(targetUUID)) { user.sendMessage("commands.island.team.setowner.errors.target-is-not-member"); return false; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUncoopCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUncoopCommand.java index c79b3e219..b4929b122 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUncoopCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUncoopCommand.java @@ -74,7 +74,7 @@ public class IslandTeamUncoopCommand extends CompositeCommand { user.sendMessage("commands.island.team.uncoop.cannot-uncoop-yourself"); return false; } - if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().contains(targetUUID)) { + if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).inTeam(targetUUID)) { user.sendMessage("commands.island.team.uncoop.cannot-uncoop-member"); return false; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUntrustCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUntrustCommand.java index 11bdb82aa..16dec5d03 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUntrustCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUntrustCommand.java @@ -74,7 +74,7 @@ public class IslandTeamUntrustCommand extends CompositeCommand { user.sendMessage("commands.island.team.untrust.cannot-untrust-yourself"); return false; } - if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().contains(targetUUID)) { + if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).inTeam(targetUUID)) { user.sendMessage("commands.island.team.untrust.cannot-untrust-member"); return false; } diff --git a/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java b/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java index 3aa960bd1..4f964892c 100644 --- a/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java +++ b/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java @@ -644,4 +644,12 @@ public interface WorldSettings extends ConfigObject { default int getConcurrentIslands() { return BentoBox.getInstance().getSettings().getIslandNumber(); } + + /** + * Remove islands when players join a team and not allow players to have other islands if they are in a team. + * @return true or false + */ + default boolean isTeamMembersDropIsland() { + return true; + } } diff --git a/src/main/java/world/bentobox/bentobox/database/objects/Island.java b/src/main/java/world/bentobox/bentobox/database/objects/Island.java index 92e1b1f33..60d95c89f 100644 --- a/src/main/java/world/bentobox/bentobox/database/objects/Island.java +++ b/src/main/java/world/bentobox/bentobox/database/objects/Island.java @@ -1950,6 +1950,25 @@ public class Island implements DataObject, MetaDataAble { setChanged(); } + /** + * Check if a player is in this island's team + * @param playerUUID player's UUID + * @return true if in team + * @since 2.3.0 + */ + public boolean inTeam(UUID playerUUID) { + return this.getMemberSet().contains(playerUUID); + } + + /** + * Check if this island has a team + * @return true if this island has a team + * @since 2.3.0 + */ + public boolean hasTeam() { + return this.getMemberSet().size() > 1; + } + /* * (non-Javadoc) * diff --git a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java index 40317c15c..37f5f0464 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java @@ -219,7 +219,7 @@ public class JoinLeaveListener implements Listener { .filter(island -> island.getMembers().containsKey(event.getPlayer().getUniqueId())).forEach(island -> { // Are there any online players still for this island? if (Bukkit.getOnlinePlayers().stream().filter(p -> !event.getPlayer().equals(p)) - .noneMatch(p -> island.getMemberSet().contains(p.getUniqueId()))) { + .noneMatch(p -> island.inTeam(p.getUniqueId()))) { // No, there are no more players online on this island // Tell players they are being removed island.getMembers().entrySet().stream().filter(e -> e.getValue() == RanksManager.COOP_RANK) diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/EnterExitListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/EnterExitListener.java index 02404a7bc..bdcff20dd 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/EnterExitListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/EnterExitListener.java @@ -107,7 +107,7 @@ public class EnterExitListener extends FlagListener { // Leave messages are always specific to this world String islandMessage = user.getTranslation(island.getWorld(), ISLAND_MESSAGE, TextVariables.NAME, getPlugin().getPlayers().getName(island.getOwner())); // Send specific message if the player is member of this island - if (island.getMemberSet().contains(user.getUniqueId())) { + if (island.inTeam(user.getUniqueId())) { user.notify(island.getWorld(), "protection.flags.ENTER_EXIT_MESSAGES.now-leaving-your-island", TextVariables.NAME, (island.getName() != null) ? island.getName() : islandMessage); } else { user.notify(island.getWorld(), "protection.flags.ENTER_EXIT_MESSAGES.now-leaving", TextVariables.NAME, (island.getName() != null) ? island.getName() : islandMessage); @@ -135,7 +135,7 @@ public class EnterExitListener extends FlagListener { // Enter messages are always specific to this world String islandMessage = user.getTranslation(island.getWorld(), ISLAND_MESSAGE, TextVariables.NAME, getPlugin().getPlayers().getName(island.getOwner())); // Send specific message if the player is member of this island - if (island.getMemberSet().contains(user.getUniqueId())) { + if (island.inTeam(user.getUniqueId())) { user.notify(island.getWorld(), "protection.flags.ENTER_EXIT_MESSAGES.now-entering-your-island", TextVariables.NAME, (island.getName() != null) ? island.getName() : islandMessage); } else { user.notify(island.getWorld(), "protection.flags.ENTER_EXIT_MESSAGES.now-entering", TextVariables.NAME, (island.getName() != null) ? island.getName() : islandMessage); diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/PetTeleportListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/PetTeleportListener.java index ced124ed8..fc10ed0e7 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/PetTeleportListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/PetTeleportListener.java @@ -31,7 +31,7 @@ public class PetTeleportListener extends FlagListener { // Get where the pet is going e.setCancelled(getIslands().getProtectedIslandAt(e.getTo()) // Not home island - .map(i -> !i.getMemberSet().contains(t.getOwner().getUniqueId())) + .map(i -> !i.inTeam(t.getOwner().getUniqueId())) // Not any island .orElse(true)); } diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/VisitorsStartingRaidListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/VisitorsStartingRaidListener.java index bb8c2b03e..33dc06982 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/VisitorsStartingRaidListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/VisitorsStartingRaidListener.java @@ -49,7 +49,7 @@ public class VisitorsStartingRaidListener extends FlagListener Optional island = this.getIslands().getProtectedIslandAt(event.getPlayer().getLocation()); - if (island.isPresent() && !island.get().getMemberSet().contains(event.getPlayer().getUniqueId())) + if (island.isPresent() && !island.get().inTeam(event.getPlayer().getUniqueId())) { event.setCancelled(true); this.report(User.getInstance(event.getPlayer()), diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index 5c78f52a8..486edb811 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -1319,7 +1319,7 @@ public class IslandsManager { return false; } // Get the player's island - return getIslandAt(loc).filter(i -> i.onIsland(loc)).map(i -> i.getMemberSet().contains(player.getUniqueId())) + return getIslandAt(loc).filter(i -> i.onIsland(loc)).map(i -> i.inTeam(player.getUniqueId())) .orElse(false); } @@ -1389,7 +1389,7 @@ public class IslandsManager { .filter(p -> p.getGameMode().equals(plugin.getIWM().getDefaultGameMode(island.getWorld()))) .filter(p -> island.onIsland(p.getLocation())).forEach(p -> { // Teleport island players to their island home - if (!island.getMemberSet().contains(p.getUniqueId()) + if (!island.inTeam(p.getUniqueId()) && (hasIsland(w, p.getUniqueId()) || inTeam(w, p.getUniqueId()))) { homeTeleportAsync(w, p); } else { @@ -1493,16 +1493,17 @@ public class IslandsManager { } /** - * Checks if a player is in a team in this world. Note that the player may have + * Checks if a player is in any team in this world. Note that the player may have * multiple islands in the world, any one of which may have a team. * * @param world - world * @param playerUUID - player's UUID * @return true if in team, false if not + * @see Consider checking the island itself {@link Island#inTeam(UUID)} */ public boolean inTeam(World world, @NonNull UUID playerUUID) { return this.islandCache.getIslands(world, playerUUID).stream() - .anyMatch(island -> island.getMemberSet().size() > 1 && island.getMemberSet().contains(playerUUID)); + .anyMatch(island -> island.getMemberSet().size() > 1 && island.inTeam(playerUUID)); } /** diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommandTest.java index 24f6f0bdd..b678482fa 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommandTest.java @@ -121,6 +121,7 @@ public class AdminDeleteCommandTest { // Island when(island.getOwner()).thenReturn(uuid); + when(island.hasTeam()).thenReturn(true); // Has team when(im.inTeam(any(), eq(uuid))).thenReturn(true); @@ -190,6 +191,7 @@ public class AdminDeleteCommandTest { public void testExecuteOwner() { when(im.inTeam(any(),any())).thenReturn(true); + when(island.inTeam(notUUID)).thenReturn(true); //when(im.getOwner(any(), any())).thenReturn(notUUID); String[] name = {"tastybento"}; when(pm.getUUID(any())).thenReturn(notUUID); @@ -203,6 +205,7 @@ public class AdminDeleteCommandTest { */ @Test public void testcanExecuteSuccessUUID() { + when(island.hasTeam()).thenReturn(false); when(im.inTeam(any(), any())).thenReturn(false); //when(im.getOwner(any(), any())).thenReturn(uuid); Island is = mock(Island.class); @@ -243,6 +246,7 @@ public class AdminDeleteCommandTest { */ @Test public void testCanExecuteSuccess() { + when(island.hasTeam()).thenReturn(false); when(im.inTeam(any(), any())).thenReturn(false); //when(im.getOwner(any(), any())).thenReturn(uuid); Island is = mock(Island.class); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommandTest.java index 4652490a5..8a559854d 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommandTest.java @@ -19,6 +19,7 @@ import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; import org.bukkit.scheduler.BukkitScheduler; +import org.eclipse.jdt.annotation.NonNull; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -31,7 +32,9 @@ import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox; import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.TestWorldSettings; import world.bentobox.bentobox.api.commands.CompositeCommand; +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; @@ -123,6 +126,9 @@ public class AdminTeamAddCommandTest { // Island World Manager IslandWorldManager iwm = mock(IslandWorldManager.class); when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + @NonNull + WorldSettings ws = new TestWorldSettings(); + when(iwm.getWorldSettings(any())).thenReturn(ws); when(plugin.getIWM()).thenReturn(iwm); // Addon @@ -187,7 +193,7 @@ public class AdminTeamAddCommandTest { when(pm.getUUID(eq("poslovich"))).thenReturn(notUUID); when(im.inTeam(any(), eq(notUUID))).thenReturn(true); - + when(island.inTeam(notUUID)).thenReturn(true); assertFalse(itl.execute(user, itl.getLabel(), Arrays.asList(name))); verify(user).sendMessage(eq("commands.island.team.invite.errors.already-on-team")); } diff --git a/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandCommandTest.java index 38307ca88..5166a608f 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandCommandTest.java @@ -205,7 +205,7 @@ public class AdminTeamDisbandCommandTest { public void testExecutePlayerNotInTeam() { when(Util.getUUID("tastybento")).thenReturn(notUUID); assertFalse(itl.canExecute(user, itl.getLabel(), List.of("tastybento"))); - verify(user).sendMessage("general.errors.player-is-not-owner", "[name]", "tastybento"); + verify(user).sendMessage("general.errors.player-has-no-island"); } /** diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandBanCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandBanCommandTest.java index 6e0718294..e7fe5e71c 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandBanCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandBanCommandTest.java @@ -228,6 +228,7 @@ public class IslandBanCommandTest extends RanksManagerBeforeClassTest { UUID teamMate = UUID.randomUUID(); when(pm.getUUID(anyString())).thenReturn(teamMate); when(island.getMemberSet()).thenReturn(ImmutableSet.of(uuid, teamMate)); + when(island.inTeam(teamMate)).thenReturn(true); assertFalse(ibc.canExecute(user, ibc.getLabel(), Collections.singletonList("bill"))); verify(user).sendMessage("commands.island.ban.cannot-ban-member"); } diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandExpelCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandExpelCommandTest.java index 941a19ae5..a21b03bd3 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandExpelCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandExpelCommandTest.java @@ -40,6 +40,7 @@ import com.google.common.collect.ImmutableSet; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; +import world.bentobox.bentobox.TestWorldSettings; import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.commands.CompositeCommand; @@ -140,6 +141,8 @@ public class IslandExpelCommandTest extends RanksManagerBeforeClassTest { // IWM friendly name when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + TestWorldSettings worldSettings = new TestWorldSettings(); + when(iwm.getWorldSettings(any())).thenReturn(worldSettings); when(plugin.getIWM()).thenReturn(iwm); // Server and Plugin Manager for events @@ -268,6 +271,7 @@ public class IslandExpelCommandTest extends RanksManagerBeforeClassTest { UUID target = UUID.randomUUID(); when(pm.getUUID(anyString())).thenReturn(target); when(island.getMemberSet()).thenReturn(ImmutableSet.of(target)); + when(island.inTeam(target)).thenReturn(true); assertFalse(iec.canExecute(user, "", Collections.singletonList("tasty"))); verify(user).sendMessage("commands.island.expel.cannot-expel-member"); } diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommandTest.java index 4516362c6..ca056a9ac 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommandTest.java @@ -31,6 +31,7 @@ import org.powermock.reflect.Whitebox; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; +import world.bentobox.bentobox.TestWorldSettings; import world.bentobox.bentobox.api.commands.island.team.Invite.Type; import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.api.events.team.TeamEvent; @@ -142,6 +143,8 @@ public class IslandTeamInviteAcceptCommandTest { // IWM friendly name IslandWorldManager iwm = mock(IslandWorldManager.class); when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + TestWorldSettings worldSettings = new TestWorldSettings(); + when(iwm.getWorldSettings(any())).thenReturn(worldSettings); when(plugin.getIWM()).thenReturn(iwm); // Invite diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommandTest.java index 6bf25e07b..3a50c1445 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommandTest.java @@ -39,7 +39,9 @@ import com.google.common.collect.ImmutableSet; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; +import world.bentobox.bentobox.TestWorldSettings; import world.bentobox.bentobox.api.commands.island.team.Invite.Type; +import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.events.IslandBaseEvent; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; @@ -170,6 +172,9 @@ public class IslandTeamInviteCommandTest extends RanksManagerBeforeClassTest { // IWM friendly name IslandWorldManager iwm = mock(IslandWorldManager.class); when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + @NonNull + WorldSettings ws = new TestWorldSettings(); + when(iwm.getWorldSettings(world)).thenReturn(ws); when(plugin.getIWM()).thenReturn(iwm); // Parent command diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommandTest.java index a1cc1b546..2a6001b28 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommandTest.java @@ -211,7 +211,7 @@ public class IslandTeamKickCommandTest extends RanksManagerBeforeClassTest { when(pm.getName(notUUID)).thenReturn("poslovitch"); when(island.getMemberSet()).thenReturn(ImmutableSet.of(notUUID)); - + when(island.inTeam(notUUID)).thenReturn(true); IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); assertFalse(itl.canExecute(user, itl.getLabel(), Collections.singletonList("poslovitch"))); verify(user).sendMessage(eq("commands.island.team.kick.cannot-kick-rank"), eq(TextVariables.NAME), eq("poslovitch")); @@ -229,7 +229,7 @@ public class IslandTeamKickCommandTest extends RanksManagerBeforeClassTest { when(pm.getName(notUUID)).thenReturn("poslovitch"); when(island.getMemberSet()).thenReturn(ImmutableSet.of(notUUID)); - + when(island.inTeam(notUUID)).thenReturn(true); IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); assertFalse(itl.canExecute(user, itl.getLabel(), Collections.singletonList("poslovitch"))); verify(user).sendMessage(eq("commands.island.team.kick.cannot-kick-rank"), eq(TextVariables.NAME), eq("poslovitch")); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamPromoteCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamPromoteCommandTest.java index c3dcb2f2e..2dfb203b7 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamPromoteCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamPromoteCommandTest.java @@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableSet; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; +import world.bentobox.bentobox.TestWorldSettings; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; @@ -119,6 +120,11 @@ public class IslandTeamPromoteCommandTest extends RanksManagerBeforeClassTest { // In team when(im.inTeam(world, uuid)).thenReturn(true); + when(island.inTeam(uuid)).thenReturn(true); + + // IWM + TestWorldSettings worldSettings = new TestWorldSettings(); + when(iwm.getWorldSettings(any())).thenReturn(worldSettings); // Ranks when(island.getRankCommand(anyString())).thenReturn(RanksManager.SUB_OWNER_RANK); // Allow sub owners diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommandTest.java index 6525563db..63d6f1bd8 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommandTest.java @@ -38,6 +38,7 @@ import com.google.common.collect.ImmutableSet; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; +import world.bentobox.bentobox.TestWorldSettings; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.localization.TextVariables; @@ -119,7 +120,8 @@ public class IslandTeamSetownerCommandTest { when(plugin.getIslands()).thenReturn(im); // Has team - when(im.inTeam(any(), eq(uuid))).thenReturn(true); + when(im.inTeam(world, uuid)).thenReturn(true); + when(island.inTeam(uuid)).thenReturn(true); when(plugin.getPlayers()).thenReturn(pm); // Server & Scheduler @@ -128,6 +130,8 @@ public class IslandTeamSetownerCommandTest { when(Bukkit.getScheduler()).thenReturn(sch); // Island World Manager + TestWorldSettings worldSettings = new TestWorldSettings(); + when(iwm.getWorldSettings(any())).thenReturn(worldSettings); when(plugin.getIWM()).thenReturn(iwm); @NonNull WorldSettings ws = mock(WorldSettings.class); @@ -194,7 +198,7 @@ public class IslandTeamSetownerCommandTest { */ @Test public void testCanExecuteUserStringListOfStringNotInTeam() { - when(island.getMemberSet()).thenReturn(ImmutableSet.of()); + when(island.inTeam(uuid)).thenReturn(false); assertFalse(its.canExecute(user, "", List.of("gibby"))); verify(user).sendMessage("general.errors.no-team"); } @@ -267,6 +271,7 @@ public class IslandTeamSetownerCommandTest { UUID target = UUID.randomUUID(); when(pm.getUUID(anyString())).thenReturn(target); when(island.getMemberSet()).thenReturn(ImmutableSet.of(uuid, target)); + when(island.inTeam(any())).thenReturn(true); when(im.getIsland(any(), any(User.class))).thenReturn(island); assertTrue(its.canExecute(user, "", List.of("tastybento"))); assertTrue(its.execute(user, "", List.of("tastybento"))); @@ -282,7 +287,7 @@ public class IslandTeamSetownerCommandTest { when(im.inTeam(any(), any())).thenReturn(true); UUID target = UUID.randomUUID(); when(pm.getUUID(anyString())).thenReturn(target); - when(island.getMemberSet()).thenReturn(ImmutableSet.of(uuid, target)); + when(island.inTeam(any())).thenReturn(true); when(im.getIsland(any(), any(User.class))).thenReturn(island); assertTrue(its.canExecute(user, "", List.of("tastybento"))); assertTrue(its.execute(user, "", List.of("tastybento"))); diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUncoopCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUncoopCommandTest.java index 0e941c2e9..c5bb7976f 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUncoopCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUncoopCommandTest.java @@ -211,6 +211,7 @@ public class IslandTeamUncoopCommandTest extends RanksManagerBeforeClassTest { when(pm.getUUID(any())).thenReturn(notUUID); when(im.inTeam(any(), any())).thenReturn(true); when(island.getMemberSet()).thenReturn(ImmutableSet.of(notUUID)); + when(island.inTeam(notUUID)).thenReturn(true); assertFalse(itl.execute(user, itl.getLabel(), Collections.singletonList("bento"))); verify(user).sendMessage(eq("commands.island.team.uncoop.cannot-uncoop-member")); } diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUntrustCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUntrustCommandTest.java index 0f0a34f94..4022b8c40 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUntrustCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamUntrustCommandTest.java @@ -211,6 +211,7 @@ public class IslandTeamUntrustCommandTest extends RanksManagerBeforeClassTest { when(pm.getUUID(any())).thenReturn(notUUID); when(im.inTeam(any(), any())).thenReturn(true); when(island.getMemberSet()).thenReturn(ImmutableSet.of(notUUID)); + when(island.inTeam(notUUID)).thenReturn(true); assertFalse(itl.execute(user, itl.getLabel(), Collections.singletonList("bento"))); verify(user).sendMessage(eq("commands.island.team.untrust.cannot-untrust-member")); } diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/PetTeleportListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/PetTeleportListenerTest.java index 55ed897a4..a44122b8c 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/PetTeleportListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/PetTeleportListenerTest.java @@ -47,6 +47,8 @@ public class PetTeleportListenerTest extends AbstractCommonSetup { @Before public void setUp() throws Exception { super.setUp(); + // Island + when(this.island.inTeam(uuid)).thenReturn(true); when(tamed.isTamed()).thenReturn(true); when(tamed.getOwner()).thenReturn(tamer); when(tamer.getUniqueId()).thenReturn(uuid); diff --git a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java index 153f528cf..7931f9df7 100644 --- a/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/IslandsManagerTest.java @@ -830,6 +830,7 @@ public class IslandsManagerTest extends AbstractCommonSetup { Builder members = new ImmutableSet.Builder<>(); members.add(uuid); when(is.getMemberSet()).thenReturn(members.build()); + when(is.inTeam(uuid)).thenReturn(true); when(player.getUniqueId()).thenReturn(uuid); @@ -842,10 +843,12 @@ public class IslandsManagerTest extends AbstractCommonSetup { // No members Builder mem = new ImmutableSet.Builder<>(); when(is.getMemberSet()).thenReturn(mem.build()); + when(is.inTeam(uuid)).thenReturn(false); assertFalse(im.locationIsOnIsland(player, location)); // Not on island when(is.getMemberSet()).thenReturn(members.build()); + when(is.inTeam(uuid)).thenReturn(true); when(is.onIsland(any())).thenReturn(false); assertFalse(im.locationIsOnIsland(player, location)); } From 0e6a25d74b0a82ceda2711c0c964e5b13bf36462 Mon Sep 17 00:00:00 2001 From: tastybento Date: Fri, 29 Mar 2024 20:26:07 -0700 Subject: [PATCH 09/23] WIP - needs work on team invites. --- .../commands/admin/team/AdminTeamAddCommand.java | 2 +- .../api/commands/island/IslandCreateCommand.java | 2 +- .../team/IslandTeamInviteAcceptCommand.java | 16 +++++++++++----- .../island/team/IslandTeamInviteCommand.java | 4 ++-- .../api/configuration/WorldSettings.java | 2 +- 5 files changed, 16 insertions(+), 10 deletions(-) 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 29d3599d7..99c7b754b 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 @@ -54,7 +54,7 @@ public class AdminTeamAddCommand extends CompositeCommand { new IslandInfo(island).showMembers(user); return false; } - if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland() && island.inTeam(targetUUID)) { + if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands() && island.inTeam(targetUUID)) { user.sendMessage("commands.island.team.invite.errors.already-on-team"); return false; } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java index 15655d4ce..77d38cff3 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java @@ -51,7 +51,7 @@ public class IslandCreateCommand extends CompositeCommand { } } // Check if this player is on a team in this world - if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland() + if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands() && getIslands().inTeam(getWorld(), user.getUniqueId()) && island != null && !user.getUniqueId().equals(island.getOwner())) { // Team members who are not owners cannot make additional islands diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java index 35c3188b3..101412fc7 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java @@ -61,7 +61,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { } // Check if player is already in a team - if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland() + if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands() && getIslands().inTeam(getWorld(), playerUUID)) { user.sendMessage("commands.island.team.invite.errors.you-already-are-in-team"); return false; @@ -82,8 +82,14 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { switch (invite.getType()) { case COOP -> askConfirmation(user, () -> acceptCoopInvite(user, invite)); case TRUST -> askConfirmation(user, () -> acceptTrustInvite(user, invite)); - default -> askConfirmation(user, user.getTranslation("commands.island.team.invite.accept.confirmation"), - () -> acceptTeamInvite(user, invite)); + default -> { + if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()) { + askConfirmation(user, user.getTranslation("commands.island.team.invite.accept.confirmation"), + () -> acceptTeamInvite(user, invite)); + } else { + acceptTeamInvite(user, invite); + } + } } return true; } @@ -156,7 +162,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { user.sendMessage("commands.island.team.invite.errors.island-is-full"); return; } - if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland()) { + if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()) { // Remove the player's other islands getIslands().removePlayer(getWorld(), user.getUniqueId()); } @@ -166,7 +172,7 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { getIslands().setJoinTeam(teamIsland, user.getUniqueId()); // Move player to team's island getIslands().homeTeleportAsync(getWorld(), user.getPlayer()).thenRun(() -> { - if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland()) { + if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()) { // Delete the old islands islands.forEach(island -> getIslands().deleteIsland(island, true, user.getUniqueId())); } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java index 30cfbfea8..77a37dd30 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java @@ -105,7 +105,7 @@ public class IslandTeamInviteCommand extends CompositeCommand { } // Player cannot invite someone already on a team - if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland() + if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands() && getIslands().inTeam(getWorld(), invitedPlayerUUID)) { user.sendMessage("commands.island.team.invite.errors.already-on-team"); return false; @@ -171,7 +171,7 @@ public class IslandTeamInviteCommand extends CompositeCommand { // Send message to online player invitedPlayer.sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()); invitedPlayer.sendMessage("commands.island.team.invite.to-accept-or-reject", TextVariables.LABEL, getTopLabel()); - if (getIWM().getWorldSettings(getWorld()).isTeamMembersDropIsland() + if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands() && getIslands().hasIsland(getWorld(), invitedPlayer.getUniqueId())) { invitedPlayer.sendMessage("commands.island.team.invite.you-will-lose-your-island"); } diff --git a/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java b/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java index 4f964892c..5efd73f59 100644 --- a/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java +++ b/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java @@ -649,7 +649,7 @@ public interface WorldSettings extends ConfigObject { * Remove islands when players join a team and not allow players to have other islands if they are in a team. * @return true or false */ - default boolean isTeamMembersDropIsland() { + default boolean isDisallowTeamMemberIslands() { return true; } } From 81f765df3672e67a819d03d8559db57100880f4f Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 30 Mar 2024 20:57:20 -0700 Subject: [PATCH 10/23] Fix invites and accepts. --- .../team/IslandTeamInviteAcceptCommand.java | 13 +++++---- .../bentobox/listeners/JoinLeaveListener.java | 4 +++ src/main/resources/locales/en-US.yml | 27 ++++++++++++------- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java index 101412fc7..4f4d47cd2 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java @@ -165,12 +165,13 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()) { // Remove the player's other islands getIslands().removePlayer(getWorld(), user.getUniqueId()); + // Remove money inventory etc. for leaving + cleanPlayer(user); } - // Remove money inventory etc. for leaving - cleanPlayer(user); // Add the player as a team member of the new island getIslands().setJoinTeam(teamIsland, user.getUniqueId()); // Move player to team's island + getIslands().setPrimaryIsland(user.getUniqueId(), teamIsland); getIslands().homeTeleportAsync(getWorld(), user.getPlayer()).thenRun(() -> { if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()) { // Delete the old islands @@ -184,9 +185,11 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { Util.runCommands(user, ownerName, getIWM().getOnJoinCommands(getWorld()), "join"); }); - // Reset deaths - if (getIWM().isTeamJoinDeathReset(getWorld())) { - getPlayers().setDeaths(getWorld(), user.getUniqueId(), 0); + if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()) { + // Reset deaths + if (getIWM().isTeamJoinDeathReset(getWorld())) { + getPlayers().setDeaths(getWorld(), user.getUniqueId(), 0); + } } user.sendMessage("commands.island.team.invite.accept.you-joined-island", TextVariables.LABEL, getTopLabel()); User inviter = User.getInstance(invite.getInviter()); diff --git a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java index 37f5f0464..f73f3dac3 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java @@ -79,6 +79,10 @@ public class JoinLeaveListener implements Listener { plugin.logWarning("Player that just logged in has no name! " + playerUUID); } + // Set the primary island to the player's location if this is their island + plugin.getIslands().getIslandAt(user.getLocation()).filter(i -> user.getUniqueId().equals(i.getOwner())) + .ifPresent(i -> plugin.getIslands().setPrimaryIsland(playerUUID, i)); + // If mobs have to be removed when a player joins, then wipe all the mobs on his // island. if (plugin.getIslands().locationIsOnIsland(event.getPlayer(), user.getLocation()) diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 9c0838c47..0b9fe73fe 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -678,8 +678,10 @@ commands: already-has-rank: '&c Player already has a rank!' you-are-a-coop-member: '&2 You were cooped by &b[name]&a.' success: '&a You cooped &b [name]&a.' - name-has-invited-you: '&a [name] has invited you to join as a coop member - of their island.' + name-has-invited-you: | + &a [name] has invited you + &a to join as a coop member + &a of their island. uncoop: description: remove a coop rank from player parameters: @@ -696,8 +698,10 @@ commands: description: give a player trusted rank on your island parameters: trust-in-yourself: '&c Trust in yourself!' - name-has-invited-you: '&a [name] has invited you to join as a trusted member - of their island.' + name-has-invited-you: | + &a [name] has invited you + &a to join as a trusted member + &a of their island. player-already-trusted: '&c Player is already trusted!' you-are-trusted: '&2 You are trusted by &b [name]&a !' success: '&a You trusted &b [name]&a .' @@ -714,10 +718,14 @@ commands: description: invite a player to join your island invitation-sent: '&a Invitation sent to &b[name]&a.' removing-invite: '&c Removing invite.' - name-has-invited-you: '&a [name] has invited you to join their island.' + name-has-invited-you: | + &a [name] has invited you + &a to join their island. to-accept-or-reject: '&a Do /[label] team accept to accept, or /[label] team reject to reject' - you-will-lose-your-island: '&c WARNING! You will lose all your islands if you accept!' + you-will-lose-your-island: | + &c WARNING! You will lose all + &c your islands if you accept! gui: titles: team-invite-panel: "Invite Players" @@ -758,9 +766,10 @@ commands: you-joined-island: '&a You joined an island! Use &b/[label] team &a to see the other members.' name-joined-your-island: '&a [name] joined your island!' - confirmation: |- - &c Are you sure you want to accept this invite? - &c&l You will &n LOSE ALL &r&c&l your islands! + confirmation: | + &c Are you sure you + &c want to accept this + &c invite? reject: description: reject an invitation you-rejected-invite: '&a You rejected the invitation to join an island.' From 1215a437666aa57dddfecc61b6e7c292ebf01471 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 31 Mar 2024 17:29:18 -0700 Subject: [PATCH 11/23] Add feature to limit blueprint availability. --- pom.xml | 2 +- .../world/bentobox/bentobox/Settings.java | 20 + .../commands/island/IslandCreateCommand.java | 31 +- .../commands/island/IslandResetCommand.java | 2 +- .../dataobjects/BlueprintBundle.java | 19 + .../bentobox/managers/BlueprintsManager.java | 3 + .../panels/BlueprintManagementPanel.java | 34 ++ .../customizable/IslandCreationPanel.java | 341 ++++++++++-------- src/main/resources/locales/en-US.yml | 9 + .../island/IslandCreateCommandTest.java | 4 + .../panels/BlueprintManagementPanelTest.java | 4 + .../customizable/IslandCreationPanelTest.java | 4 +- 12 files changed, 314 insertions(+), 159 deletions(-) diff --git a/pom.xml b/pom.xml index 181e7cc25..8aa65b84f 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ -LOCAL - 2.2.1 + 2.3.0 bentobox-world https://sonarcloud.io ${project.basedir}/lib diff --git a/src/main/java/world/bentobox/bentobox/Settings.java b/src/main/java/world/bentobox/bentobox/Settings.java index 2f2b85e2d..c149907b2 100644 --- a/src/main/java/world/bentobox/bentobox/Settings.java +++ b/src/main/java/world/bentobox/bentobox/Settings.java @@ -195,6 +195,12 @@ public class Settings implements ConfigObject { @ConfigEntry(path = "island.concurrent-islands") private int islandNumber = 1; + @ConfigComment("Hide used blueprints.") + @ConfigComment("Blueprints can have a maximum use when players have concurrent islands.") + @ConfigComment("If this is true, then ones that are used up will not be shown in the island create menu.") + @ConfigEntry(path = "island.hide-used-blueprints", since = "2.3.0") + private boolean hideUsedBlueprints = false; + // Cooldowns @ConfigComment("How long a player must wait until they can rejoin a team island after being kicked in minutes.") @ConfigComment("This slows the effectiveness of players repeating challenges") @@ -1014,4 +1020,18 @@ public class Settings implements ConfigObject { this.islandNumber = islandNumber; } + /** + * @return the hideUsedBlueprints + */ + public boolean isHideUsedBlueprints() { + return hideUsedBlueprints; + } + + /** + * @param hideUsedBlueprints the hideUsedBlueprints to set + */ + public void setHideUsedBlueprints(boolean hideUsedBlueprints) { + this.hideUsedBlueprints = hideUsedBlueprints; + } + } diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java index 54d3c7a12..1d4bbaf84 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java @@ -86,22 +86,51 @@ public class IslandCreateCommand extends CompositeCommand { user.sendMessage("commands.island.create.unknown-blueprint"); return false; } + // Check perm if (!getPlugin().getBlueprintsManager().checkPerm(getAddon(), user, Util.sanitizeInput(args.get(0)))) { return false; } + // Check maximum uses + if (checkMaxUses(user, name)) { + return false; + } // Make island return makeIsland(user, name); } else { + if (getPlugin().getSettings().getIslandNumber() > 1 + && checkMaxUses(user, BlueprintsManager.DEFAULT_BUNDLE_NAME)) { + return false; + } // Show panel only if there are multiple bundles available if (getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).size() > 1) { // Show panel - IslandCreationPanel.openPanel(this, user, label); + IslandCreationPanel.openPanel(this, user, label, false); return true; } return makeIsland(user, BlueprintsManager.DEFAULT_BUNDLE_NAME); } } + private boolean checkMaxUses(User user, String name) { + if (getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).containsKey(name)) { + int maxTimes = getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).get(name).getTimes(); + if (maxTimes > 0) { + // Check how many times this player has used this bundle + if (getBundleUses(user, name) >= maxTimes) { + user.sendMessage("commands.island.create.max-uses"); + return true; + } + } + } + return false; + } + + private long getBundleUses(User user, String name) { + return getIslands().getIslands(getWorld(), user).stream() + .filter(is -> is.getMetaData("bundle").map(mdv -> name.equalsIgnoreCase(mdv.asString())).orElse(false)) + .count(); + } + private boolean makeIsland(User user, String name) { user.sendMessage("commands.island.create.creating-island"); try { diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandResetCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandResetCommand.java index feb7fb78b..8096f7a20 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandResetCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandResetCommand.java @@ -111,7 +111,7 @@ public class IslandResetCommand extends ConfirmableCommand { // Show panel only if there are multiple bundles available if (getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).size() > 1) { // Show panel - once the player selected a bundle, this will re-run this command - IslandCreationPanel.openPanel(this, user, label); + IslandCreationPanel.openPanel(this, user, label, true); } else { resetIsland(user, BlueprintsManager.DEFAULT_BUNDLE_NAME); } diff --git a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBundle.java b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBundle.java index ce7474653..f5be3b417 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBundle.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBundle.java @@ -61,6 +61,11 @@ public class BlueprintBundle implements DataObject { @Expose private int slot = 0; + /** + * Number of times this bundle can be used by a single player. 0 = unlimited + */ + @Expose + private int times = 0; /** @@ -188,4 +193,18 @@ public class BlueprintBundle implements DataObject { this.slot = slot; } + /** + * @return the times + */ + public int getTimes() { + return times; + } + + /** + * @param times the times to set + */ + public void setTimes(int times) { + this.times = times; + } + } diff --git a/src/main/java/world/bentobox/bentobox/managers/BlueprintsManager.java b/src/main/java/world/bentobox/bentobox/managers/BlueprintsManager.java index 8fec85175..d13332ed5 100644 --- a/src/main/java/world/bentobox/bentobox/managers/BlueprintsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/BlueprintsManager.java @@ -40,6 +40,7 @@ import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.metadata.MetaDataValue; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.blueprints.Blueprint; import world.bentobox.bentobox.blueprints.BlueprintPaster; @@ -484,6 +485,8 @@ public class BlueprintsManager { b2 -> pasteEnd(addon, bb, island).thenAccept(message -> sendMessage(island)).thenAccept(b3 -> Bukkit.getScheduler().runTask(plugin, task)))); } + // Set the bundle name + island.putMetaData("bundle", new MetaDataValue(name)); return true; } diff --git a/src/main/java/world/bentobox/bentobox/panels/BlueprintManagementPanel.java b/src/main/java/world/bentobox/bentobox/panels/BlueprintManagementPanel.java index e0556413f..05b505fd8 100644 --- a/src/main/java/world/bentobox/bentobox/panels/BlueprintManagementPanel.java +++ b/src/main/java/world/bentobox/bentobox/panels/BlueprintManagementPanel.java @@ -77,10 +77,21 @@ public class BlueprintManagementPanel { environmentToBlueprint = Map.of(World.Environment.NORMAL, normalBlueprint, World.Environment.NETHER, netherBlueprint, World.Environment.THE_END, endBlueprint); } + /** + * Translate "commands.admin.blueprint.management." + t reference + * @param t - end of reference + * @return translation + */ private String t(String t) { return user.getTranslation("commands.admin.blueprint.management." + t); } + /** + * Translate "commands.admin.blueprint.management." + t + vars reference + * @param t end of reference + * @param vars any other parameters + * @return transmation + */ private String t(String t, String... vars) { return user.getTranslation("commands.admin.blueprint.management." + t, vars); } @@ -186,6 +197,10 @@ public class BlueprintManagementPanel { // Toggle permission - default is always allowed pb.item(39, getPermissionIcon(addon, bb)); } + if (plugin.getSettings().getIslandNumber() > 1) { + // Number of times allowed + pb.item(42, getTimesIcon(addon, bb)); + } // Preferred slot pb.item(40, getSlotIcon(addon, bb)); // Panel has a Back icon. @@ -198,6 +213,25 @@ public class BlueprintManagementPanel { } + private PanelItem getTimesIcon(GameModeAddon addon2, BlueprintBundle bb) { + return new PanelItemBuilder().icon(Material.CLOCK).name(t("times")) + .description(bb.getTimes() == 0 ? t("unlimited-times") + : t("maximum-times", TextVariables.NUMBER, String.valueOf(bb.getTimes()))) + .clickHandler((panel, u, clickType, slot) -> { + // Left click up, right click down + u.getPlayer().playSound(u.getLocation(), Sound.UI_BUTTON_CLICK, 1F, 1F); + if (clickType == ClickType.LEFT) { + bb.setTimes(bb.getTimes() + 1); + } else if (clickType == ClickType.RIGHT && bb.getTimes() > 0) { + bb.setTimes(bb.getTimes() - 1); + } + // Save + plugin.getBlueprintsManager().saveBlueprintBundle(addon, bb); + panel.getInventory().setItem(42, getTimesIcon(addon, bb).getItem()); + return true; + }).build(); + } + /** * Gets the preferred slot icon * @param addon - addon diff --git a/src/main/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanel.java b/src/main/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanel.java index 6824e7eac..681c3cd36 100644 --- a/src/main/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanel.java +++ b/src/main/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanel.java @@ -7,6 +7,7 @@ package world.bentobox.bentobox.panels.customizable; +import org.bukkit.World; import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import org.eclipse.jdt.annotation.NonNull; @@ -37,6 +38,94 @@ import world.bentobox.bentobox.util.Util; */ public class IslandCreationPanel { + // --------------------------------------------------------------------- + // Section: Constants + // --------------------------------------------------------------------- + + /** + * This constant is used for button to indicate that it is Blueprint Bundle type. + */ + private static final String BUNDLES = "BUNDLE"; + + /** + * This constant is used for button to indicate that it is previous page type. + */ + private static final String PREVIOUS = "PREVIOUS"; + + /** + * This constant is used for button to indicate that it is next page type. + */ + private static final String NEXT = "NEXT"; + + /** + * This constant is used for indicating that pages should contain numbering. + */ + private static final String INDEXING = "indexing"; + + /** + * This constant stores value for SELECT action that is used in panels. + */ + private static final String SELECT_ACTION = "SELECT"; + + /** + * This constant stores value for COMMAND action that is used in panels. + */ + private static final String COMMANDS_ACTION = "COMMANDS"; + + /** + * This constant stores value for ERROR message that will be displayed upon failing to run creation commands. + */ + private static final String ISLAND_CREATION_COMMANDS = "ISLAND_CREATION_COMMANDS"; + + /** + * Button reference + */ + private static final String BUNDLE_BUTTON_REF = "panels.island_creation.buttons.bundle."; + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + /** + * This variable allows to access plugin object. + */ + private final BentoBox plugin; + + /** + * This variable stores main command that was triggered. + */ + private final CompositeCommand mainCommand; + + /** + * This variable holds user who opens panel. Without it panel cannot be opened. + */ + private final User user; + + /** + * This variable holds world where panel is opened. Without it panel cannot be opened. + */ + private final String mainLabel; + + /** + * This variable stores filtered elements. + */ + private final List elementList; + + /** + * This variable holds current pageIndex for multi-page island choosing. + */ + private int pageIndex; + + /** + * The world that this command applies to + */ + private final World world; + + /** + * true if this panel has been called by a reset command. Changes how the count of used islands is done. + */ + private final boolean reset; + // --------------------------------------------------------------------- // Section: Constructor // --------------------------------------------------------------------- @@ -48,20 +137,22 @@ public class IslandCreationPanel * @param command CompositeCommand object * @param label The main command label * @param user User who opens panel + * @param reset */ private IslandCreationPanel(@NonNull CompositeCommand command, - @NonNull User user, - @NonNull String label) + @NonNull User user, @NonNull String label, boolean reset) { this.plugin = BentoBox.getInstance(); this.user = user; this.mainLabel = label; + this.world = command.getWorld(); + this.reset = reset; this.elementList = this.plugin.getBlueprintsManager().getBlueprintBundles(command.getAddon()).values().stream(). - sorted(Comparator.comparingInt(BlueprintBundle::getSlot).thenComparing(BlueprintBundle::getUniqueId)). - filter(bundle -> !bundle.isRequirePermission() || - this.user.hasPermission(command.getPermissionPrefix() + "island.create." + bundle.getUniqueId())). - toList(); + sorted(Comparator.comparingInt(BlueprintBundle::getSlot).thenComparing(BlueprintBundle::getUniqueId)) + .filter(bundle -> !bundle.isRequirePermission() || this.user + .hasPermission(command.getPermissionPrefix() + "island.create." + bundle.getUniqueId())) + .toList(); this.mainCommand = command; } @@ -83,7 +174,7 @@ public class IslandCreationPanel { this.plugin.logError("There are no available phases for selection!"); this.user.sendMessage("no-phases", - TextVariables.GAMEMODE, this.plugin.getDescription().getName()); + TextVariables.GAMEMODE, this.plugin.getDescription().getName()); return; } @@ -126,8 +217,8 @@ public class IslandCreationPanel private boolean doesCustomPanelExists(GameModeAddon addon, String name) { return addon.getDataFolder().exists() && - new File(addon.getDataFolder(), "panels").exists() && - new File(addon.getDataFolder(), "panels" + File.separator + name + ".yml").exists(); + new File(addon.getDataFolder(), "panels").exists() + && new File(addon.getDataFolder(), "panels" + File.separator + name + ".yml").exists(); } @@ -149,7 +240,7 @@ public class IslandCreationPanel int size = this.elementList.size(); if (size <= slot.amountMap().getOrDefault(BUNDLES, 1) || - 1.0 * size / slot.amountMap().getOrDefault(BUNDLES, 1) <= this.pageIndex + 1) + 1.0 * size / slot.amountMap().getOrDefault(BUNDLES, 1) <= this.pageIndex + 1) { // There are no next elements return null; @@ -179,7 +270,7 @@ public class IslandCreationPanel if (template.description() != null) { builder.description(this.user.getTranslation(this.mainCommand.getWorld(), template.description(), - TextVariables.NUMBER, String.valueOf(nextPageIndex))); + TextVariables.NUMBER, String.valueOf(nextPageIndex))); } // Add ClickHandler @@ -187,7 +278,7 @@ public class IslandCreationPanel { template.actions().forEach(action -> { if ((clickType == action.clickType() || - action.clickType() == ClickType.UNKNOWN) && NEXT.equalsIgnoreCase(action.actionType())) + action.clickType() == ClickType.UNKNOWN) && NEXT.equalsIgnoreCase(action.actionType())) { // Next button ignores click type currently. this.pageIndex++; @@ -202,10 +293,10 @@ public class IslandCreationPanel // Collect tooltips. List tooltips = template.actions().stream(). - filter(action -> action.tooltip() != null). - map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())). - filter(text -> !text.isBlank()). - collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + filter(action -> action.tooltip() != null) + .map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())) + .filter(text -> !text.isBlank()) + .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); // Add tooltips. if (!tooltips.isEmpty()) @@ -259,16 +350,15 @@ public class IslandCreationPanel if (template.description() != null) { builder.description(this.user.getTranslation(this.mainCommand.getWorld(), template.description(), - TextVariables.NUMBER, String.valueOf(previousPageIndex))); + TextVariables.NUMBER, String.valueOf(previousPageIndex))); } - // Add ClickHandler // Add ClickHandler builder.clickHandler((panel, user, clickType, i) -> { template.actions().forEach(action -> { if ((clickType == action.clickType() || - action.clickType() == ClickType.UNKNOWN) && PREVIOUS.equalsIgnoreCase(action.actionType())) + action.clickType() == ClickType.UNKNOWN) && PREVIOUS.equalsIgnoreCase(action.actionType())) { // Next button ignores click type currently. this.pageIndex--; @@ -283,10 +373,10 @@ public class IslandCreationPanel // Collect tooltips. List tooltips = template.actions().stream(). - filter(action -> action.tooltip() != null). - map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())). - filter(text -> !text.isBlank()). - collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + filter(action -> action.tooltip() != null) + .map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())) + .filter(text -> !text.isBlank()) + .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); // Add tooltips. if (!tooltips.isEmpty()) @@ -310,7 +400,7 @@ public class IslandCreationPanel { if (this.elementList.isEmpty()) { - // Does not contain any sticks. + // Does not contain any blueprints. return null; } @@ -332,9 +422,8 @@ public class IslandCreationPanel { // Try to find bundle with requested ID. if not found, use already collected bundle. blueprintBundle = this.elementList.stream(). - filter(bundle -> bundle.getUniqueId().equals(template.dataMap().get("unique_id"))). - findFirst(). - orElse(blueprintBundle); + filter(bundle -> bundle.getUniqueId().equals(template.dataMap().get("unique_id"))).findFirst() + .orElse(blueprintBundle); } return this.createBundleButton(template, blueprintBundle); @@ -359,8 +448,6 @@ public class IslandCreationPanel return null; } - final String reference = "panels.island_creation.buttons.bundle."; - // Get settings for island. PanelItemBuilder builder = new PanelItemBuilder(); @@ -376,72 +463,92 @@ public class IslandCreationPanel if (template.title() != null) { builder.name(this.user.getTranslation(this.mainCommand.getWorld(), template.title(), - TextVariables.NAME, bundle.getDisplayName())); + TextVariables.NAME, bundle.getDisplayName())); } else { - builder.name(this.user.getTranslation(reference + "name", - TextVariables.NAME, bundle.getDisplayName())); + builder.name(this.user.getTranslation(BUNDLE_BUTTON_REF + "name", + TextVariables.NAME, bundle.getDisplayName())); } if (template.description() != null) { builder.description(this.user.getTranslation(this.mainCommand.getWorld(), template.description(), - TextVariables.DESCRIPTION, String.join("\n", bundle.getDescription()))); + TextVariables.DESCRIPTION, String.join("\n", bundle.getDescription()))); } else { - builder.description(this.user.getTranslation(reference + "description", - TextVariables.DESCRIPTION, String.join("\n", bundle.getDescription()))); + builder.description(this.user.getTranslation(BUNDLE_BUTTON_REF + "description", + TextVariables.DESCRIPTION, String.join("\n", bundle.getDescription()))); + } + boolean usedUp = false; + if (plugin.getSettings().getIslandNumber() > 1) { + // Show how many times this bundle can be used + int maxTimes = bundle.getTimes(); + if (maxTimes > 0) { + long uses = plugin.getIslands().getIslands(world, user).stream() + .filter(is -> is.getMetaData("bundle") + .map(mdv -> bundle.getDisplayName().equalsIgnoreCase(mdv.asString()) + && !(reset && is.isPrimary())) // If this is a reset, then ignore the use of the island being reset + .orElse(false)) + .count(); + builder.description(this.user.getTranslation(BUNDLE_BUTTON_REF + "uses", TextVariables.NUMBER, + String.valueOf(uses), "[max]", String.valueOf(maxTimes))); + if (uses >= maxTimes) { + usedUp = true; + } + } else { + builder.description(this.user.getTranslation(BUNDLE_BUTTON_REF + "unlimited")); + } } - List actions = template.actions().stream(). - filter(action -> SELECT_ACTION.equalsIgnoreCase(action.actionType()) || - COMMANDS_ACTION.equalsIgnoreCase(action.actionType())). - toList(); + if (usedUp) { + if (plugin.getSettings().isHideUsedBlueprints()) { + // Do not show used up blueprints + return null; + } + } else { + List actions = template.actions().stream() + .filter(action -> SELECT_ACTION.equalsIgnoreCase(action.actionType()) + || COMMANDS_ACTION.equalsIgnoreCase(action.actionType())) + .toList(); + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> { + actions.forEach(action -> { + if (clickType == action.clickType() || action.clickType() == ClickType.UNKNOWN) + { + if (SELECT_ACTION.equalsIgnoreCase(action.actionType())) { + user.closeInventory(); + this.mainCommand.execute(user, this.mainLabel, + Collections.singletonList(bundle.getUniqueId())); + } else if (COMMANDS_ACTION.equalsIgnoreCase(action.actionType())) { + Util.runCommands(user, + Arrays.stream(action.content() + .replaceAll(Pattern.quote(TextVariables.LABEL), + this.mainCommand.getTopLabel()) + .split("\n")).toList(), + ISLAND_CREATION_COMMANDS); + } + } + }); - // Add ClickHandler - builder.clickHandler((panel, user, clickType, i) -> - { - actions.forEach(action -> { - if (clickType == action.clickType() || action.clickType() == ClickType.UNKNOWN) - { - if (SELECT_ACTION.equalsIgnoreCase(action.actionType())) - { - user.closeInventory(); - this.mainCommand.execute(user, this.mainLabel, Collections.singletonList(bundle.getUniqueId())); - } - else if (COMMANDS_ACTION.equalsIgnoreCase(action.actionType())) - { - Util.runCommands(user, - Arrays.stream(action.content(). - replaceAll(Pattern.quote(TextVariables.LABEL), this.mainCommand.getTopLabel()). - split("\n")). - toList(), - ISLAND_CREATION_COMMANDS); - } - } + // Always return true. + return true; }); - // Always return true. - return true; - }); + // Collect tooltips. + List tooltips = actions.stream().filter(action -> action.tooltip() != null) + .map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())) + .filter(text -> !text.isBlank()) + .collect(Collectors.toCollection(() -> new ArrayList<>(actions.size()))); - // Collect tooltips. - List tooltips = actions.stream(). - filter(action -> action.tooltip() != null). - map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())). - filter(text -> !text.isBlank()). - collect(Collectors.toCollection(() -> new ArrayList<>(actions.size()))); - - // Add tooltips. - if (!tooltips.isEmpty()) - { - // Empty line and tooltips. - builder.description(""); - builder.description(tooltips); + // Add tooltips. + if (!tooltips.isEmpty()) { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } } - return builder.build(); } @@ -458,87 +565,13 @@ public class IslandCreationPanel * @param command CompositeCommand object * @param label The main command label * @param user User who opens panel + * @param reset true if this is an island reset */ public static void openPanel(@NonNull CompositeCommand command, - @NonNull User user, - @NonNull String label) + @NonNull User user, @NonNull String label, boolean reset) { - new IslandCreationPanel(command, user, label).build(); + new IslandCreationPanel(command, user, label, reset).build(); } -// --------------------------------------------------------------------- -// Section: Constants -// --------------------------------------------------------------------- - - - /** - * This constant is used for button to indicate that it is Blueprint Bundle type. - */ - private static final String BUNDLES = "BUNDLE"; - - /** - * This constant is used for button to indicate that it is previous page type. - */ - private static final String PREVIOUS = "PREVIOUS"; - - /** - * This constant is used for button to indicate that it is next page type. - */ - private static final String NEXT = "NEXT"; - - /** - * This constant is used for indicating that pages should contain numbering. - */ - private static final String INDEXING = "indexing"; - - /** - * This constant stores value for SELECT action that is used in panels. - */ - private static final String SELECT_ACTION = "SELECT"; - - /** - * This constant stores value for COMMAND action that is used in panels. - */ - private static final String COMMANDS_ACTION = "COMMANDS"; - - /** - * This constant stores value for ERROR message that will be displayed upon failing to run creation commands. - */ - private static final String ISLAND_CREATION_COMMANDS = "ISLAND_CREATION_COMMANDS"; - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - - /** - * This variable allows to access plugin object. - */ - private final BentoBox plugin; - - /** - * This variable stores main command that was triggered. - */ - private final CompositeCommand mainCommand; - - /** - * This variable holds user who opens panel. Without it panel cannot be opened. - */ - private final User user; - - /** - * This variable holds world where panel is opened. Without it panel cannot be opened. - */ - private final String mainLabel; - - /** - * This variable stores filtered elements. - */ - private final List elementList; - - /** - * This variable holds current pageIndex for multi-page island choosing. - */ - private int pageIndex; } diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 9c0838c47..6161572fd 100644 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -414,6 +414,12 @@ commands: slot-instructions: | &a Left click to increment &a Right click to decrement + times: | + &a Max concurrent uses by player + &a Left click to increment + &a Right click to decrement + unlimited-times: &a Unlimited + maximum-times: &a Max [number] times resetflags: parameters: '[flag]' description: Reset all islands to default flag settings in config.yml @@ -547,6 +553,7 @@ commands: an administrator.' creating-island: '&a Finding a spot for your island...' you-cannot-make: '&c You cannot make any more islands!' + max-uses: "&c You cannot make any more of that type of island!" you-cannot-make-team: '&c Team members cannot make islands in the same world as their team island.' pasting: estimated-time: '&a Estimated time: &b [number] &a seconds.' @@ -1811,6 +1818,8 @@ panels: name: "&l [name]" description: |- [description] + uses: "&a Used [number]/[max]" + unlimited: "&a Unlimited uses allowed" # The section of translations used in Language Panel language: title: "&2&l Select your language" diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommandTest.java index 5d35d71a7..d1f44e7fb 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommandTest.java @@ -167,6 +167,10 @@ public class IslandCreateCommandTest { when(builder.build()).thenReturn(mock(Island.class)); // Bundles manager + + @NonNull + Map map = new HashMap<>(); + when(bpm.getBlueprintBundles(addon)).thenReturn(map); when(plugin.getBlueprintsManager()).thenReturn(bpm); // IslandCreationPanel diff --git a/src/test/java/world/bentobox/bentobox/panels/BlueprintManagementPanelTest.java b/src/test/java/world/bentobox/bentobox/panels/BlueprintManagementPanelTest.java index 4ff22b87e..59516412e 100644 --- a/src/test/java/world/bentobox/bentobox/panels/BlueprintManagementPanelTest.java +++ b/src/test/java/world/bentobox/bentobox/panels/BlueprintManagementPanelTest.java @@ -32,6 +32,7 @@ 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.panels.PanelItem; import world.bentobox.bentobox.api.user.User; @@ -123,6 +124,9 @@ public class BlueprintManagementPanelTest { // Blueprint when(blueprint.getName()).thenReturn("blueprint name"); + // Settings + Settings settings = new Settings(); + when(plugin.getSettings()).thenReturn(settings); // Set up bmp = new BlueprintManagementPanel(plugin, user, addon); diff --git a/src/test/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanelTest.java b/src/test/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanelTest.java index 19f5ca654..12b982e36 100644 --- a/src/test/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanelTest.java +++ b/src/test/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanelTest.java @@ -203,7 +203,7 @@ public class IslandCreationPanelTest { */ @Test public void testOpenPanel() { - IslandCreationPanel.openPanel(ic, user, ""); + IslandCreationPanel.openPanel(ic, user, "", false); // Set correctly verify(inv).setItem(eq(0), any()); @@ -220,7 +220,7 @@ public class IslandCreationPanelTest { public void testOpenPanelSameSlot() { when(bb2.getSlot()).thenReturn(5); when(bb3.getSlot()).thenReturn(5); - IslandCreationPanel.openPanel(ic, user, ""); + IslandCreationPanel.openPanel(ic, user, "", false); verify(inv).setItem(eq(0), any()); verify(inv).setItem(eq(1), any()); verify(meta).setDisplayName(eq("test")); From 83eaa50b49bdfeaa8c81c03818b53e7ebea4e719 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 31 Mar 2024 18:16:45 -0700 Subject: [PATCH 12/23] Refactor to improve code quality --- .../api/commands/island/IslandCreateCommand.java | 10 ++++------ .../bentobox/panels/BlueprintManagementPanel.java | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java index 1d4bbaf84..aebd0cb32 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/IslandCreateCommand.java @@ -114,12 +114,10 @@ public class IslandCreateCommand extends CompositeCommand { private boolean checkMaxUses(User user, String name) { if (getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).containsKey(name)) { int maxTimes = getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).get(name).getTimes(); - if (maxTimes > 0) { - // Check how many times this player has used this bundle - if (getBundleUses(user, name) >= maxTimes) { - user.sendMessage("commands.island.create.max-uses"); - return true; - } + // Check how many times this player has used this bundle + if (maxTimes > 0 && getBundleUses(user, name) >= maxTimes) { + user.sendMessage("commands.island.create.max-uses"); + return true; } } return false; diff --git a/src/main/java/world/bentobox/bentobox/panels/BlueprintManagementPanel.java b/src/main/java/world/bentobox/bentobox/panels/BlueprintManagementPanel.java index 05b505fd8..6abf5a6e1 100644 --- a/src/main/java/world/bentobox/bentobox/panels/BlueprintManagementPanel.java +++ b/src/main/java/world/bentobox/bentobox/panels/BlueprintManagementPanel.java @@ -199,7 +199,7 @@ public class BlueprintManagementPanel { } if (plugin.getSettings().getIslandNumber() > 1) { // Number of times allowed - pb.item(42, getTimesIcon(addon, bb)); + pb.item(42, getTimesIcon(bb)); } // Preferred slot pb.item(40, getSlotIcon(addon, bb)); @@ -213,7 +213,7 @@ public class BlueprintManagementPanel { } - private PanelItem getTimesIcon(GameModeAddon addon2, BlueprintBundle bb) { + private PanelItem getTimesIcon(BlueprintBundle bb) { return new PanelItemBuilder().icon(Material.CLOCK).name(t("times")) .description(bb.getTimes() == 0 ? t("unlimited-times") : t("maximum-times", TextVariables.NUMBER, String.valueOf(bb.getTimes()))) @@ -227,7 +227,7 @@ public class BlueprintManagementPanel { } // Save plugin.getBlueprintsManager().saveBlueprintBundle(addon, bb); - panel.getInventory().setItem(42, getTimesIcon(addon, bb).getItem()); + panel.getInventory().setItem(42, getTimesIcon(bb).getItem()); return true; }).build(); } From 24b7d26fbe8b1500872afdda473de5c3b296375a Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 4 Apr 2024 09:09:46 -0700 Subject: [PATCH 13/23] NPE avoid. --- .../api/commands/admin/team/AdminTeamDisbandCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 64db4dd52..478cc81e7 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 @@ -75,7 +75,7 @@ public class AdminTeamDisbandCommand extends CompositeCommand { island = islands.values().iterator().next(); } // Check that the target owns the island - if (!island.getOwner().equals(targetUUID)) { + if (island.getOwner() == null || !island.getOwner().equals(targetUUID)) { user.sendMessage("general.errors.player-is-not-owner", TextVariables.NAME, args.get(0)); return false; } From 1c19703f447908b902e7e5db09d953de663aebd1 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 4 Apr 2024 09:11:49 -0700 Subject: [PATCH 14/23] Merge if statement --- .../island/team/IslandTeamInviteAcceptCommand.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java index 4f4d47cd2..88c91c162 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java @@ -185,11 +185,10 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand { Util.runCommands(user, ownerName, getIWM().getOnJoinCommands(getWorld()), "join"); }); - if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()) { + if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands() + && getIWM().isTeamJoinDeathReset(getWorld())) { // Reset deaths - if (getIWM().isTeamJoinDeathReset(getWorld())) { - getPlayers().setDeaths(getWorld(), user.getUniqueId(), 0); - } + getPlayers().setDeaths(getWorld(), user.getUniqueId(), 0); } user.sendMessage("commands.island.team.invite.accept.you-joined-island", TextVariables.LABEL, getTopLabel()); User inviter = User.getInstance(invite.getInviter()); From e1536fcae0e51833261bca98dd451d5a8b49366c Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 4 Apr 2024 09:12:45 -0700 Subject: [PATCH 15/23] Remove superfluous null check --- .../bentobox/api/commands/admin/team/AdminTeamAddCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 99c7b754b..69f38cd1c 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 @@ -49,7 +49,7 @@ public class AdminTeamAddCommand extends CompositeCommand { user.sendMessage("general.errors.player-has-no-island"); return false; } - if (getIslands().inTeam(getWorld(), ownerUUID) && island != null && !ownerUUID.equals(island.getOwner())) { + if (getIslands().inTeam(getWorld(), ownerUUID) && !ownerUUID.equals(island.getOwner())) { user.sendMessage("commands.admin.team.add.name-not-owner", TextVariables.NAME, args.get(0)); new IslandInfo(island).showMembers(user); return false; From f6f4da1c89a119761956ecb52b6990b27860ae92 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 4 Apr 2024 12:59:20 -0700 Subject: [PATCH 16/23] Added placeholders for island names and member lists #2329 --- .../bentobox/lists/GameModePlaceholder.java | 3 ++ .../managers/PlaceholdersManager.java | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java b/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java index 2e74d6440..e03b2eb81 100644 --- a/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java +++ b/src/main/java/world/bentobox/bentobox/lists/GameModePlaceholder.java @@ -17,6 +17,9 @@ import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.managers.RanksManager; import world.bentobox.bentobox.util.Util; +/** + * Common Game Mode Placeholders + */ public enum GameModePlaceholder { /* World-related */ diff --git a/src/main/java/world/bentobox/bentobox/managers/PlaceholdersManager.java b/src/main/java/world/bentobox/bentobox/managers/PlaceholdersManager.java index 06a7431f8..1af141e58 100644 --- a/src/main/java/world/bentobox/bentobox/managers/PlaceholdersManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/PlaceholdersManager.java @@ -3,6 +3,9 @@ package world.bentobox.bentobox.managers; import java.util.Arrays; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; @@ -13,6 +16,7 @@ import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.placeholders.PlaceholderReplacer; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; @@ -72,6 +76,50 @@ public class PlaceholdersManager { .forEach(placeholder -> registerPlaceholder(addon, placeholder.getPlaceholder(), new DefaultPlaceholder(addon, placeholder))); // Register team member placeholders registerTeamMemberPlaceholders(addon); + // Register potential island names and member info + registerOwnedIslandPlaceholders(addon); + } + + private void registerOwnedIslandPlaceholders(@NonNull GameModeAddon addon) { + int maxIslands = plugin.getIWM().getWorldSettings(addon.getOverWorld()).getConcurrentIslands(); + IntStream.range(0, maxIslands).forEach(i -> registerPlaceholder(addon, "island_name_" + (i + 1), user -> { + if (user == null) + return ""; + + AtomicInteger generatedCount = new AtomicInteger(1); // To increment within lambda + return plugin.getIslands().getIslands(addon.getOverWorld(), user).stream().map(island -> { + IslandName islandName = getIslandName(island, user, generatedCount.get()); + if (islandName.generatated()) { + generatedCount.getAndIncrement(); // Increment if the name was generated + } + return islandName.name; + }).skip(i) // Skip to the island at index 'i' + .findFirst() // Take the first island after skipping, effectively the (i+1)th + .orElse(""); // Default to empty string if no island is found + })); + + // Island_memberlist + IntStream.range(0, maxIslands) + .forEach(i -> registerPlaceholder(addon, "island_memberlist_" + (i + 1), user -> user == null ? "" + : plugin.getIslands().getIslands(addon.getOverWorld(), user).stream().skip(i).findFirst() + .map(island -> island.getMemberSet().stream() + .map(addon.getPlayers()::getName).collect(Collectors.joining(","))) + .orElse(""))); + } + + private record IslandName(String name, boolean generatated) { + } + + private IslandName getIslandName(Island island, User user, int index) { + if (island.getName() != null && !island.getName().isBlank()) { + // Name has been set + return new IslandName(island.getName(), false); + } else { + // Name has not been set + return new IslandName(user.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", TextVariables.NAME, + user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()) + " " + index, true); + } + } private void registerTeamMemberPlaceholders(@NonNull GameModeAddon addon) { From 2c75939bc381d72a72c9a5333775508fcdbfc11f Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 6 Apr 2024 09:08:11 -0700 Subject: [PATCH 17/23] JavaDoc and import changes --- .../bentobox/api/commands/island/team/IslandTeamInviteGUI.java | 1 - .../world/bentobox/bentobox/api/configuration/WorldSettings.java | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteGUI.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteGUI.java index 7cf35345f..4fba9e558 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteGUI.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteGUI.java @@ -21,7 +21,6 @@ import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.localization.TextVariables; -import world.bentobox.bentobox.api.panels.Panel; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.TemplatedPanel; import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; diff --git a/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java b/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java index 5efd73f59..cd3546411 100644 --- a/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java +++ b/src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java @@ -648,6 +648,7 @@ public interface WorldSettings extends ConfigObject { /** * Remove islands when players join a team and not allow players to have other islands if they are in a team. * @return true or false + * @since 2.3.0 */ default boolean isDisallowTeamMemberIslands() { return true; From 5834dcbb59dc6e7645f8e07b13e901d5cbd439f4 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 8 Apr 2024 20:15:34 -0700 Subject: [PATCH 18/23] Fix placeholders manager test --- .../managers/PlaceholdersManagerTest.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/test/java/world/bentobox/bentobox/managers/PlaceholdersManagerTest.java b/src/test/java/world/bentobox/bentobox/managers/PlaceholdersManagerTest.java index ded48fb62..4b97fdeb7 100644 --- a/src/test/java/world/bentobox/bentobox/managers/PlaceholdersManagerTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/PlaceholdersManagerTest.java @@ -18,10 +18,14 @@ import org.mockito.Mock; import org.mockito.Mockito; 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.TestWorldSettings; import world.bentobox.bentobox.api.addons.AddonDescription; import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.hooks.Hook; import world.bentobox.bentobox.hooks.placeholders.PlaceholderAPIHook; import world.bentobox.bentobox.lists.GameModePlaceholder; @@ -43,9 +47,17 @@ public class PlaceholdersManagerTest { private HooksManager hm; @Mock private PlaceholderAPIHook hook; + @Mock + private IslandWorldManager iwm; + private Settings settings; @Before public void setUp() throws Exception { + // Set up plugin + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + settings = new Settings(); + when(plugin.getSettings()).thenReturn(settings); + // Addon @NonNull AddonDescription desc = new AddonDescription.Builder("main", "bskyblock", "1.0").build(); @@ -61,6 +73,13 @@ public class PlaceholdersManagerTest { when(hm.getHook(eq("PlaceholderAPI"))).thenReturn(optionalHook); when(hook.isPlaceholder(any(), any())).thenReturn(false); + // World settings + + @NonNull + WorldSettings ws = new TestWorldSettings(); + when(iwm.getWorldSettings(any())).thenReturn(ws); + when(plugin.getIWM()).thenReturn(iwm); + // Placeholder manager pm = new PlaceholdersManager(plugin); } @@ -77,7 +96,7 @@ public class PlaceholdersManagerTest { public void testRegisterGameModePlaceholdersAllDefaults() { pm.registerDefaultPlaceholders(addon); // + 300 because we register team member placeholders up to 50 members - verify(hook, times(GameModePlaceholder.values().length + 302)).registerPlaceholder(any(), anyString(), any()); + verify(hook, times(GameModePlaceholder.values().length + 304)).registerPlaceholder(any(), anyString(), any()); } /** @@ -91,7 +110,7 @@ public class PlaceholdersManagerTest { pm.registerDefaultPlaceholders(addon); // 3 less registrations for this addon - verify(hook, times(GameModePlaceholder.values().length - 3 + 302)).registerPlaceholder(any(), anyString(), + verify(hook, times(GameModePlaceholder.values().length - 3 + 304)).registerPlaceholder(any(), anyString(), any()); } } From e7055c6cbab82c5584f6f1f9790207d0698df853 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 8 Apr 2024 21:53:18 -0700 Subject: [PATCH 19/23] Remove player from island, not all islands when kicked. --- .../api/commands/island/team/IslandTeamKickCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java index 7c0d9a5d0..a9fbad126 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java @@ -108,7 +108,7 @@ public class IslandTeamKickCommand extends ConfirmableCommand { getAddon().getDescription().getName(), TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()); - getIslands().removePlayer(getWorld(), targetUUID); + getIslands().removePlayer(oldIsland, targetUUID); // Clean the target player getPlayers().cleanLeavingPlayer(getWorld(), target, true, oldIsland); From c86eb6a19ceaf438678db19bfb003630fd5b92c3 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 8 Apr 2024 22:11:52 -0700 Subject: [PATCH 20/23] Make sure it's the user's island that the target is being kicked from --- .../api/commands/island/team/IslandTeamKickCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java index a9fbad126..a3e1c0b8b 100644 --- a/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java +++ b/src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java @@ -95,7 +95,7 @@ public class IslandTeamKickCommand extends ConfirmableCommand { return false; } User target = User.getInstance(targetUUID); - Island oldIsland = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID)); // Should never be + Island oldIsland = Objects.requireNonNull(getIslands().getIsland(getWorld(), user)); // Should never be // null because of // checks above // Fire event From 3de0ff236ec67b97e3bccab96c3dabd6790bcd2f Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 8 Apr 2024 23:01:45 -0700 Subject: [PATCH 21/23] Fix tests --- .../commands/island/team/IslandTeamKickCommandTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommandTest.java b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommandTest.java index 2a6001b28..b12d5d922 100644 --- a/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommandTest.java +++ b/src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommandTest.java @@ -250,7 +250,7 @@ public class IslandTeamKickCommandTest extends RanksManagerBeforeClassTest { IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); assertTrue(itl.execute(user, itl.getLabel(), Collections.singletonList("poslovitch"))); - verify(im).removePlayer(any(World.class), eq(notUUID)); + verify(im).removePlayer(any(Island.class), eq(notUUID)); verify(user).sendMessage("commands.island.team.kick.success", TextVariables.NAME, "poslovitch", TextVariables.DISPLAY_NAME, "&Cposlovich"); } @@ -343,7 +343,7 @@ public class IslandTeamKickCommandTest extends RanksManagerBeforeClassTest { IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); assertTrue(itl.execute(user, itl.getLabel(), Collections.singletonList("poslovitch"))); - verify(im).removePlayer(any(World.class), eq(notUUID)); + verify(im).removePlayer(any(Island.class), eq(notUUID)); verify(user).sendMessage("commands.island.team.kick.success", TextVariables.NAME, "poslovitch", TextVariables.DISPLAY_NAME, "&Cposlovich"); } @@ -363,7 +363,7 @@ public class IslandTeamKickCommandTest extends RanksManagerBeforeClassTest { IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); assertTrue(itl.execute(user, itl.getLabel(), Collections.singletonList("poslovitch"))); - verify(im).removePlayer(any(World.class), eq(notUUID)); + verify(im).removePlayer(any(Island.class), eq(notUUID)); verify(user).sendMessage("commands.island.team.kick.success", TextVariables.NAME, "poslovitch", TextVariables.DISPLAY_NAME, "&Cposlovich"); verify(target, never()).getInventory(); @@ -389,7 +389,7 @@ public class IslandTeamKickCommandTest extends RanksManagerBeforeClassTest { IslandTeamKickCommand itl = new IslandTeamKickCommand(ic); assertTrue(itl.execute(user, itl.getLabel(), Collections.singletonList("poslovitch"))); - verify(im).removePlayer(any(World.class), eq(notUUID)); + verify(im).removePlayer(any(Island.class), eq(notUUID)); verify(user).sendMessage("commands.island.team.kick.success", TextVariables.NAME, "poslovitch", TextVariables.DISPLAY_NAME, "&Cposlovich"); verify(target, Mockito.never()).getInventory(); verify(pm).cleanLeavingPlayer(any(), any(User.class), eq(true), eq(island)); From ffb955b22b049e15e99c25a920ec222e5a614414 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 14 Apr 2024 22:00:37 -0700 Subject: [PATCH 22/23] Fix bug with MythicMobs changes #2340 --- .../bentobox/blueprints/dataobjects/BlueprintEntity.java | 3 +++ .../java/world/bentobox/bentobox/hooks/MythicMobsHook.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java index bf6257d59..4c4eb1de1 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java @@ -286,6 +286,9 @@ public class BlueprintEntity { * @return the mythicMobsRecord */ public MythicMobRecord getMythicMobsRecord() { + if (this.MMtype == null || this.MMLevel == null || this.MMpower == null || this.MMStance == null) { + return null; + } return new MythicMobRecord(this.MMtype, this.getCustomName(), this.MMLevel, this.MMpower, this.MMStance); } diff --git a/src/main/java/world/bentobox/bentobox/hooks/MythicMobsHook.java b/src/main/java/world/bentobox/bentobox/hooks/MythicMobsHook.java index 5db726a27..616ea0622 100644 --- a/src/main/java/world/bentobox/bentobox/hooks/MythicMobsHook.java +++ b/src/main/java/world/bentobox/bentobox/hooks/MythicMobsHook.java @@ -55,6 +55,9 @@ public class MythicMobsHook extends Hook { * @return true if spawn is successful */ public boolean spawnMythicMob(MythicMobRecord mmr, Location spawnLocation) { + if (!this.isPluginAvailable()) { + return false; + } return MythicBukkit.inst().getMobManager().getMythicMob(mmr.type()).map(mob -> { // A delay is required before spawning, I assume because the blocks are pasted using NMS Bukkit.getScheduler().runTaskLater(getPlugin(), () -> { From 69a22e917eae2777e408cdeafd53a695d6ac5d7b Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 14 Apr 2024 22:11:41 -0700 Subject: [PATCH 23/23] Fix MythicMobs test --- .../bentobox/bentobox/hooks/MythicMobsHookTest.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/test/java/world/bentobox/bentobox/hooks/MythicMobsHookTest.java b/src/test/java/world/bentobox/bentobox/hooks/MythicMobsHookTest.java index 360befc6f..87fe88631 100644 --- a/src/test/java/world/bentobox/bentobox/hooks/MythicMobsHookTest.java +++ b/src/test/java/world/bentobox/bentobox/hooks/MythicMobsHookTest.java @@ -156,7 +156,17 @@ public class MythicMobsHookTest { * Test method for {@link world.bentobox.bentobox.hooks.MythicMobsHook#spawnMythicMob(world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity.MythicMobRecord, org.bukkit.Location)}. */ @Test - public void testSpawnMythicMob() { + public void testSpawnMythicMobNoPLugin() { + MythicMobRecord mmr = hook.getMythicMob(entity); + assertFalse(hook.spawnMythicMob(mmr, location)); + } + + /** + * Test method for {@link world.bentobox.bentobox.hooks.MythicMobsHook#spawnMythicMob(world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity.MythicMobRecord, org.bukkit.Location)}. + */ + @Test + public void testSpawnMythicMobHasPlugin() { + when(mythicMobs.isEnabled()).thenReturn(true); MythicMobRecord mmr = hook.getMythicMob(entity); assertTrue(hook.spawnMythicMob(mmr, location)); verify(mm).getMythicMob("GIANT");