From 6cae448efbdd3babce4bb492bddba9194f996cc1 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 19 Aug 2023 18:20:41 -0700 Subject: [PATCH] More work on multi island. Fixed tests so clean compile. --- pom.xml | 2 +- .../world/bentobox/bentobox/BentoBox.java | 3 + .../bentobox/listeners/JoinLeaveListener.java | 49 ++++++++-------- .../listeners/PrimaryIslandListener.java | 57 +++++++++++++++++++ .../bentobox/managers/IslandsManager.java | 9 +++ .../bentobox/managers/island/IslandCache.java | 12 ++-- .../bentobox/managers/island/NewIsland.java | 3 - .../admin/AdminDeleteCommandTest.java | 27 ++++++--- .../island/IslandCreateCommandTest.java | 15 ++++- .../listeners/JoinLeaveListenerTest.java | 4 ++ .../managers/island/IslandCacheTest.java | 3 +- .../managers/island/NewIslandTest.java | 42 -------------- 12 files changed, 139 insertions(+), 87 deletions(-) create mode 100644 src/main/java/world/bentobox/bentobox/listeners/PrimaryIslandListener.java diff --git a/pom.xml b/pom.xml index 2200b852f..b04a46fc0 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ -LOCAL - 1.24.2 + 2.0.0 bentobox-world https://sonarcloud.io ${project.basedir}/lib diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java index 56d02eb37..ad43f723b 100644 --- a/src/main/java/world/bentobox/bentobox/BentoBox.java +++ b/src/main/java/world/bentobox/bentobox/BentoBox.java @@ -31,6 +31,7 @@ import world.bentobox.bentobox.listeners.BlockEndDragon; import world.bentobox.bentobox.listeners.DeathListener; import world.bentobox.bentobox.listeners.JoinLeaveListener; import world.bentobox.bentobox.listeners.PanelListenerManager; +import world.bentobox.bentobox.listeners.PrimaryIslandListener; import world.bentobox.bentobox.listeners.StandardSpawnProtectionListener; import world.bentobox.bentobox.listeners.teleports.EntityTeleportListener; import world.bentobox.bentobox.listeners.teleports.PlayerTeleportListener; @@ -306,6 +307,8 @@ public class BentoBox extends JavaPlugin implements Listener { // Island Delete Manager islandDeletionManager = new IslandDeletionManager(this); manager.registerEvents(islandDeletionManager, this); + // Primary Island Listener + manager.registerEvents(new PrimaryIslandListener(this), this); } @Override diff --git a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java index 48de86840..001aa46db 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/JoinLeaveListener.java @@ -215,33 +215,30 @@ public class JoinLeaveListener implements Listener { } private void updateIslandRange(User user) { - plugin.getIWM().getOverWorlds().stream() - .filter(world -> plugin.getIslands().isOwner(world, user.getUniqueId())) - .forEach(world -> { - Island island = plugin.getIslands().getIsland(world, user); - if (island != null) { - // Check if new owner has a different range permission than the island size - int range = user.getPermissionValue(plugin.getIWM().getAddon(island.getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("") + "island.range", island.getRawProtectionRange()); - // Range cannot be greater than the island distance - range = Math.min(range, plugin.getIWM().getIslandDistance(island.getWorld())); - // Range can go up or down - if (range != island.getRawProtectionRange()) { - user.sendMessage("commands.admin.setrange.range-updated", TextVariables.NUMBER, String.valueOf(range)); - int oldRange = island.getProtectionRange(); - island.setProtectionRange(range); + plugin.getIslands().getIslands().stream() + .filter(island -> island.getOwner() != null && island.getOwner().equals(user.getUniqueId())) + .forEach(island -> { + // Check if new owner has a different range permission than the island size + int range = user.getPermissionValue(plugin.getIWM().getAddon(island.getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("") + "island.range", island.getRawProtectionRange()); + // Range cannot be greater than the island distance + range = Math.min(range, plugin.getIWM().getIslandDistance(island.getWorld())); + // Range can go up or down + if (range != island.getRawProtectionRange()) { + user.sendMessage("commands.admin.setrange.range-updated", TextVariables.NUMBER, String.valueOf(range)); + int oldRange = island.getProtectionRange(); + island.setProtectionRange(range); - plugin.log("Island protection range changed from " + oldRange + " to " - + island.getProtectionRange() + " for " + user.getName() + " due to permission."); - // Call Protection Range Change event. Does not support canceling. - IslandEvent.builder() - .island(island) - .location(island.getProtectionCenter()) - .reason(IslandEvent.Reason.RANGE_CHANGE) - .involvedPlayer(user.getUniqueId()) - .admin(true) - .protectionRange(island.getProtectionRange(), oldRange) - .build(); - } + plugin.log("Island protection range changed from " + oldRange + " to " + + island.getProtectionRange() + " for " + user.getName() + " due to permission."); + // Call Protection Range Change event. Does not support canceling. + IslandEvent.builder() + .island(island) + .location(island.getProtectionCenter()) + .reason(IslandEvent.Reason.RANGE_CHANGE) + .involvedPlayer(user.getUniqueId()) + .admin(true) + .protectionRange(island.getProtectionRange(), oldRange) + .build(); } }); } diff --git a/src/main/java/world/bentobox/bentobox/listeners/PrimaryIslandListener.java b/src/main/java/world/bentobox/bentobox/listeners/PrimaryIslandListener.java new file mode 100644 index 000000000..122b42f33 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/listeners/PrimaryIslandListener.java @@ -0,0 +1,57 @@ +package world.bentobox.bentobox.listeners; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.eclipse.jdt.annotation.NonNull; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.managers.IslandsManager; + +/** + * Sets the player's primary island based on where they teleported or moved to + * @author tastybento + * + */ +public class PrimaryIslandListener implements Listener { + + private final IslandsManager im; + + /** + * @param plugin - plugin object + */ + public PrimaryIslandListener(@NonNull BentoBox plugin) { + this.im = plugin.getIslands(); + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onPlayerJoin(final PlayerJoinEvent event) { + setIsland(event.getPlayer(), event.getPlayer().getLocation()); + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onPlayerMove(final PlayerMoveEvent event) { + if (event.getTo() != null && !event.getFrom().toVector().equals(event.getTo().toVector())) { + setIsland(event.getPlayer(), event.getTo()); + } + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onPlayerMove(final PlayerTeleportEvent event) { + if (event.getTo() != null) { + setIsland(event.getPlayer(), event.getTo()); + } + } + + private void setIsland(Player player, Location location) { + im.getIslandAt(location) + .filter(i -> i.getOwner() != null && i.getOwner().equals(player.getUniqueId())) + .ifPresent(i -> im.setPrimaryIsland(player.getUniqueId(), i)); + } + +} diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index e6312e3e3..cda8b224b 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -1901,4 +1901,13 @@ public class IslandsManager { return islandCache.getAllIslands(world, uuid).size(); } + /** + * Sets the user's primary island + * @param uuid user's uuid + * @param i island + */ + public void setPrimaryIsland(UUID uuid, Island i) { + this.getIslandCache().setPrimaryIsland(uuid, i); + } + } 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 64f061f5a..e823ca6c2 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/IslandCache.java @@ -274,9 +274,10 @@ public class IslandCache { }*/ /** + * Get the UUID of the owner of the island of the player, which may be their UUID * @param world the world to check * @param uuid the player's UUID - * @return island owner's UUID, the player UUID if they are not in a team, or null if there is no island + * @return island owner's UUID or null if there is no island */ @Nullable public UUID getOwner(@NonNull World world, @NonNull UUID uuid) { @@ -285,11 +286,11 @@ public class IslandCache { return null; } List islands = islandsByUUID.computeIfAbsent(w, k -> new HashMap<>()).get(uuid); - return islands != null ? islands.get(0).getOwner() : null; - + return islands == null ? null : islands.get(0).getOwner(); } /** + * Checks is a player has an island and owns it * @param world the world to check * @param uuid the player * @return true if player has island and owns it @@ -299,8 +300,9 @@ public class IslandCache { if (w == null) { return false; } - Island island = islandsByUUID.computeIfAbsent(w, k -> new HashMap<>()).get(uuid).get(0); - return island != null && uuid.equals(island.getOwner()); + List island = islandsByUUID.computeIfAbsent(w, k -> new HashMap<>()).get(uuid); + if (island == null) return false; + return !island.isEmpty() && uuid.equals(island.get(0).getOwner()); } /** diff --git a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java index 708783d73..732abaa1e 100644 --- a/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java +++ b/src/main/java/world/bentobox/bentobox/managers/island/NewIsland.java @@ -297,9 +297,6 @@ public class NewIsland { // Clear the reservation island.setReserved(false); return l; - } else { - // This should never happen unless we allow another way to paste over islands without reserving - plugin.logError("New island for user " + user.getName() + " was not reserved!"); } } return null; 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 1649e3599..2af179c88 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 @@ -11,18 +11,22 @@ import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.Vector; +import org.eclipse.jdt.annotation.Nullable; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -49,12 +53,20 @@ import world.bentobox.bentobox.util.Util; @PrepareForTest({Bukkit.class, BentoBox.class, User.class }) public class AdminDeleteCommandTest { + @Mock private CompositeCommand ac; + @Mock private User user; + @Mock private IslandsManager im; + @Mock private PlayersManager pm; private UUID notUUID; private UUID uuid; + @Mock + private World world; + @Mock + private @Nullable Island island; /** */ @@ -79,7 +91,6 @@ public class AdminDeleteCommandTest { // Player Player p = mock(Player.class); // Sometimes use Mockito.withSettings().verboseLogging() - user = mock(User.class); when(user.isOp()).thenReturn(false); uuid = UUID.randomUUID(); notUUID = UUID.randomUUID(); @@ -92,9 +103,9 @@ public class AdminDeleteCommandTest { User.setPlugin(plugin); // Parent command has no aliases - ac = mock(CompositeCommand.class); when(ac.getSubCommandAliases()).thenReturn(new HashMap<>()); when(ac.getTopLabel()).thenReturn("admin"); + when(ac.getWorld()).thenReturn(world); // Island World Manager IslandWorldManager iwm = mock(IslandWorldManager.class); @@ -102,15 +113,17 @@ public class AdminDeleteCommandTest { // Player has island to begin with - im = mock(IslandsManager.class); when(im.hasIsland(any(), any(UUID.class))).thenReturn(true); when(im.hasIsland(any(), any(User.class))).thenReturn(true); when(im.isOwner(any(),any())).thenReturn(true); when(im.getOwner(any(),any())).thenReturn(uuid); + when(im.getIsland(world, user)).thenReturn(island); when(plugin.getIslands()).thenReturn(im); + // Island + when(island.getOwner()).thenReturn(uuid); + // Has team - pm = mock(PlayersManager.class); when(im.inTeam(any(), eq(uuid))).thenReturn(true); when(plugin.getPlayers()).thenReturn(pm); @@ -162,10 +175,9 @@ public class AdminDeleteCommandTest { @Test public void testExecutePlayerNoIsland() { AdminDeleteCommand itl = new AdminDeleteCommand(ac); - String[] name = {"tastybento"}; when(pm.getUUID(any())).thenReturn(notUUID); - when(im.getOwner(any(), any())).thenReturn(null); - assertFalse(itl.canExecute(user, itl.getLabel(), Arrays.asList(name))); + when(im.getIsland(world, user)).thenReturn(null); + assertFalse(itl.canExecute(user, "", List.of("tastybento"))); verify(user).sendMessage(eq("general.errors.player-has-no-island")); } @@ -174,6 +186,7 @@ public class AdminDeleteCommandTest { */ @Test public void testExecuteOwner() { + when(im.inTeam(any(),any())).thenReturn(true); when(im.getOwner(any(), any())).thenReturn(notUUID); String[] name = {"tastybento"}; 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 3ded14952..604bda0a8 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 @@ -18,8 +18,10 @@ import java.util.Optional; import java.util.UUID; import org.bukkit.Bukkit; +import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitScheduler; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.junit.After; import org.junit.Before; @@ -37,6 +39,7 @@ import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.events.island.IslandEvent.Reason; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBundle; @@ -75,6 +78,10 @@ public class IslandCreateCommandTest { private CompositeCommand ic; @Mock private BlueprintsManager bpm; + @Mock + private World world; + @Mock + private @NonNull WorldSettings ws; /** */ @@ -115,6 +122,7 @@ public class IslandCreateCommandTest { when(ic.getUsage()).thenReturn(""); when(ic.getSubCommand(Mockito.anyString())).thenReturn(Optional.empty()); when(ic.getAddon()).thenReturn(addon); + when(ic.getWorld()).thenReturn(world); // No island for player to begin with (set it later in the tests) @@ -136,6 +144,8 @@ public class IslandCreateCommandTest { // IWM friendly name when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + when(ws.getConcurrentIslands()).thenReturn(1); // One island allowed + when(iwm.getWorldSettings(world)).thenReturn(ws); when(plugin.getIWM()).thenReturn(iwm); // NewIsland @@ -190,9 +200,12 @@ public class IslandCreateCommandTest { */ @Test public void testCanExecuteUserStringListOfStringHasIsland() { + // Currently user has two islands + when(im.getNumberOfConcurrentIslands(user.getUniqueId(), world)).thenReturn(2); + // Player has an island @Nullable Island island = mock(Island.class); - when(im.getIsland(any(), Mockito.any(User.class))).thenReturn(island); + when(im.getIsland(any(), any(User.class))).thenReturn(island); assertFalse(cc.canExecute(user, "", Collections.emptyList())); verify(user).sendMessage(eq("general.errors.already-have-island")); } diff --git a/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java index 9e767a9b7..6e8804349 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/JoinLeaveListenerTest.java @@ -12,6 +12,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -46,6 +47,8 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox; +import com.mysql.cj.x.protobuf.MysqlxCrud.CollectionOrBuilder; + import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.Settings; import world.bentobox.bentobox.api.addons.GameModeAddon; @@ -162,6 +165,7 @@ public class JoinLeaveListenerTest { when(im.getIsland(any(), any(User.class))).thenReturn(island); when(im.getIsland(any(), any(UUID.class))).thenReturn(island); + when(im.getIslands()).thenReturn(Collections.singletonList(island)); Map memberMap = new HashMap<>(); memberMap.put(uuid, RanksManager.OWNER_RANK); diff --git a/src/test/java/world/bentobox/bentobox/managers/island/IslandCacheTest.java b/src/test/java/world/bentobox/bentobox/managers/island/IslandCacheTest.java index e372d9de9..81b976335 100644 --- a/src/test/java/world/bentobox/bentobox/managers/island/IslandCacheTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/island/IslandCacheTest.java @@ -16,7 +16,6 @@ import java.util.UUID; import org.bukkit.Location; import org.bukkit.World; -import org.bukkit.plugin.PluginAwareness.Flags; import org.eclipse.jdt.annotation.NonNull; import org.junit.After; import org.junit.Before; @@ -253,7 +252,7 @@ public class IslandCacheTest { @Test public void testGetOwner() { ic.addIsland(island); - + // Should be no owner, so null assertEquals(owner, ic.getOwner(world, owner)); assertNull(ic.getOwner(world, UUID.randomUUID())); } diff --git a/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java b/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java index e0e7c8958..4347cce4b 100644 --- a/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java +++ b/src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java @@ -275,46 +275,4 @@ public class NewIslandTest { verify(island).setReserved(eq(false)); } - /** - * Test method for {@link world.bentobox.bentobox.managers.island.NewIsland#builder()}. - */ - @Test - public void testBuilderHasIslandFail() throws Exception { - when(im.getIsland(any(), any(User.class))).thenReturn(null); - when(im.hasIsland(any(), any(User.class))).thenReturn(true); - NewIsland.builder().addon(addon).name(NAME).player(user).noPaste().reason(Reason.CREATE).oldIsland(oldIsland).build(); - // Verifications - verify(im).save(eq(island)); - verify(island).setFlagsDefaults(); - verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); - verify(builder, times(2)).build(); - verify(bpb).getUniqueId(); - verify(ice).getBlueprintBundle(); - verify(pm).setDeaths(eq(world), eq(uuid), eq(0)); - verify(im).setHomeLocation(eq(user), any()); - verify(island).setProtectionRange(eq(20)); - verify(plugin).logError("New island for user tastybento was not reserved!"); - } - - /** - * Test method for {@link world.bentobox.bentobox.managers.island.NewIsland#builder()}. - */ - @Test - public void testBuilderHasIslandFailnoReserve() throws Exception { - when(island.isReserved()).thenReturn(false); - when(im.hasIsland(any(), any(User.class))).thenReturn(true); - NewIsland.builder().addon(addon).name(NAME).player(user).noPaste().reason(Reason.CREATE).oldIsland(oldIsland).build(); - // Verifications - verify(im).save(eq(island)); - verify(island).setFlagsDefaults(); - verify(scheduler).runTask(any(BentoBox.class), any(Runnable.class)); - verify(builder, times(2)).build(); - verify(bpb).getUniqueId(); - verify(ice).getBlueprintBundle(); - verify(pm).setDeaths(eq(world), eq(uuid), eq(0)); - verify(im).setHomeLocation(eq(user), any()); - verify(island).setProtectionRange(eq(20)); - verify(plugin).logError("New island for user tastybento was not reserved!"); - } - }