From fb6f6757d5185c097e061a2bc639f4416dd8086a Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 9 Jan 2020 14:42:37 -0800 Subject: [PATCH] Added permission limits for entities https://github.com/BentoBoxWorld/Limits/issues/47 --- .../limits/listeners/EntityLimitListener.java | 8 +- .../limits/listeners/JoinListener.java | 76 +++++++++++-------- .../limits/objects/IslandBlockCount.java | 43 +++++++++++ .../limits/listeners/JoinListenerTest.java | 67 +++++++++++++--- 4 files changed, 154 insertions(+), 40 deletions(-) diff --git a/src/main/java/world/bentobox/limits/listeners/EntityLimitListener.java b/src/main/java/world/bentobox/limits/listeners/EntityLimitListener.java index 3a3be99..d4ae0da 100644 --- a/src/main/java/world/bentobox/limits/listeners/EntityLimitListener.java +++ b/src/main/java/world/bentobox/limits/listeners/EntityLimitListener.java @@ -173,7 +173,13 @@ public class EntityLimitListener implements Listener { long count = ent.getWorld().getEntities().stream() .filter(e -> e.getType().equals(ent.getType())) .filter(e -> island.inIslandSpace(e.getLocation())).count(); - return addon.getSettings().getLimits().containsKey(ent.getType()) && count >= addon.getSettings().getLimits().get(ent.getType()); + // Check island settings first + int limitAmount = addon.getBlockLimitListener().getIsland(island.getUniqueId()).getEntityLimit(ent.getType()); + // If no island settings then try global settings + if (limitAmount < 0 && addon.getSettings().getLimits().containsKey(ent.getType())) { + limitAmount = addon.getSettings().getLimits().get(ent.getType()); + } + return count >= limitAmount; } } diff --git a/src/main/java/world/bentobox/limits/listeners/JoinListener.java b/src/main/java/world/bentobox/limits/listeners/JoinListener.java index 3381849..5a233a5 100644 --- a/src/main/java/world/bentobox/limits/listeners/JoinListener.java +++ b/src/main/java/world/bentobox/limits/listeners/JoinListener.java @@ -1,5 +1,6 @@ package world.bentobox.limits.listeners; +import java.util.Arrays; import java.util.Locale; import java.util.Objects; import java.util.UUID; @@ -9,6 +10,7 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.World; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -38,41 +40,55 @@ public class JoinListener implements Listener { private void checkPerms(Player player, String permissionPrefix, String islandId, String gameMode) { IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(islandId); + for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) { - if (perms.getPermission().startsWith(permissionPrefix)) { - // No wildcards - if (perms.getPermission().contains(permissionPrefix + "*")) { - logError(player.getName(), perms.getPermission(), "wildcards are not allowed."); - return; - } - // Get the Material - String[] split = perms.getPermission().split("\\."); - if (split.length != 5) { - logError(player.getName(), perms.getPermission(), "format must be '" + permissionPrefix + "MATERIAL.NUMBER'"); - return; - } - Material m = Material.getMaterial(split[3].toUpperCase(Locale.ENGLISH)); - if (m == null) { - logError(player.getName(), perms.getPermission(), split[3].toUpperCase(Locale.ENGLISH) + " is not a valid material."); - return; - } - // Get the max value should there be more than one - if (!NumberUtils.isDigits(split[4])) { - logError(player.getName(), perms.getPermission(), "the last part MUST be a number!"); - } else { - // Set the limit - if (ibc == null) { - ibc = new IslandBlockCount(islandId, gameMode); - } + if (!perms.getPermission().startsWith(permissionPrefix)) continue; + // No wildcards + if (perms.getPermission().contains(permissionPrefix + "*")) { + logError(player.getName(), perms.getPermission(), "wildcards are not allowed."); + return; + } + // Check formatting + String[] split = perms.getPermission().split("\\."); + if (split.length != 5) { + logError(player.getName(), perms.getPermission(), "format must be '" + permissionPrefix + "MATERIAL.NUMBER' or '" + permissionPrefix + "ENTITY-TYPE.NUMBER'"); + return; + } + // Check value + if (!NumberUtils.isDigits(split[4])) { + logError(player.getName(), perms.getPermission(), "the last part MUST be a number!"); + return; + } + // Entities & materials + EntityType et = Arrays.stream(EntityType.values()).filter(t -> t.name().equalsIgnoreCase(split[3])).findFirst().orElse(null); + Material m = Arrays.stream(Material.values()).filter(t -> t.name().equalsIgnoreCase(split[3])).findFirst().orElse(null); + + if (et == null && m == null) { + logError(player.getName(), perms.getPermission(), split[3].toUpperCase(Locale.ENGLISH) + " is not a valid material or entity type."); + break; + } + // Make an ibc if required + if (ibc == null) { + ibc = new IslandBlockCount(islandId, gameMode); + } + if (et != null && m == null) { + // Entity limit + ibc.setEntityLimit(et, Math.max(ibc.getEntityLimit(et), Integer.valueOf(split[4]))); + } else if (m != null && et == null) { + // Material limit + ibc.setBlockLimit(m, Math.max(ibc.getBlockLimit(m), Integer.valueOf(split[4]))); + } else { + if (m.isBlock()) { + // Material limit ibc.setBlockLimit(m, Math.max(ibc.getBlockLimit(m), Integer.valueOf(split[4]))); + } else { + // This is an entity setting + ibc.setEntityLimit(et, Math.max(ibc.getEntityLimit(et), Integer.valueOf(split[4]))); } } } - // If any changes have been made then store it - if (ibc != null) { - addon.getBlockLimitListener().setIsland(islandId, ibc); - } - + // If any changes have been made then store it - don't make files unless they are needed + if (ibc != null) addon.getBlockLimitListener().setIsland(islandId, ibc); } private void logError(String name, String perm, String error) { diff --git a/src/main/java/world/bentobox/limits/objects/IslandBlockCount.java b/src/main/java/world/bentobox/limits/objects/IslandBlockCount.java index 98db422..b94b265 100644 --- a/src/main/java/world/bentobox/limits/objects/IslandBlockCount.java +++ b/src/main/java/world/bentobox/limits/objects/IslandBlockCount.java @@ -4,6 +4,7 @@ import java.util.EnumMap; import java.util.Map; import org.bukkit.Material; +import org.bukkit.entity.EntityType; import com.google.gson.annotations.Expose; @@ -29,6 +30,8 @@ public class IslandBlockCount implements DataObject { */ @Expose private Map blockLimits = new EnumMap<>(Material.class); + @Expose + private Map entityLimits = new EnumMap<>(EntityType.class); // Required for YAML database public IslandBlockCount() {} @@ -158,4 +161,44 @@ public class IslandBlockCount implements DataObject { public void setGameMode(String gameMode) { this.gameMode = gameMode; } + + /** + * @return the entityLimits + */ + public Map getEntityLimits() { + return entityLimits; + } + + /** + * @param entityLimits the entityLimits to set + */ + public void setEntityLimits(Map entityLimits) { + this.entityLimits = entityLimits; + } + + /** + * Set an island-specific entity type limit + * @param t - entity type + * @param limit - limit + */ + public void setEntityLimit(EntityType t, int limit) { + entityLimits.put(t, limit); + } + + /** + * Get the limit for an entity type + * @param t - entity type + * @return limit or -1 for unlimited + */ + public int getEntityLimit(EntityType t) { + return entityLimits.getOrDefault(t, -1); + } + + /** + * Clear all island-specific entity type limits + */ + public void clearEntityLimits() { + entityLimits.clear(); + } + } diff --git a/src/test/java/bentobox/addon/limits/listeners/JoinListenerTest.java b/src/test/java/bentobox/addon/limits/listeners/JoinListenerTest.java index d722f88..a7b33d4 100644 --- a/src/test/java/bentobox/addon/limits/listeners/JoinListenerTest.java +++ b/src/test/java/bentobox/addon/limits/listeners/JoinListenerTest.java @@ -1,14 +1,13 @@ package bentobox.addon.limits.listeners; +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 static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.times; - import java.util.Collections; import java.util.HashSet; @@ -19,6 +18,7 @@ import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.OfflinePlayer; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.permissions.PermissionAttachmentInfo; @@ -243,7 +243,7 @@ public class JoinListenerTest { when(player.getEffectivePermissions()).thenReturn(perms); PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome"); jl.onPlayerJoin(e); - verify(addon).logError("Player tastybento has permission: 'bskyblock.island.limit.my.perm.for.game' but format must be 'bskyblock.island.limit.MATERIAL.NUMBER' Ignoring..."); + verify(addon).logError("Player tastybento has permission: 'bskyblock.island.limit.my.perm.for.game' but format must be 'bskyblock.island.limit.MATERIAL.NUMBER' or 'bskyblock.island.limit.ENTITY-TYPE.NUMBER' Ignoring..."); } /** @@ -258,7 +258,7 @@ public class JoinListenerTest { when(player.getEffectivePermissions()).thenReturn(perms); PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome"); jl.onPlayerJoin(e); - verify(addon).logError("Player tastybento has permission: 'bskyblock.island.limit.mumbo.34' but MUMBO is not a valid material. Ignoring..."); + verify(addon).logError("Player tastybento has permission: 'bskyblock.island.limit.mumbo.34' but MUMBO is not a valid material or entity type. Ignoring..."); } /** @@ -306,6 +306,22 @@ public class JoinListenerTest { verify(addon, never()).logError(anyString()); verify(ibc).setBlockLimit(eq(Material.STONE), eq(24)); } + + /** + * Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}. + */ + @Test + public void testOnPlayerJoinWithPermLimitsSuccessEntity() { + Set perms = new HashSet<>(); + PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class); + when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.BAT.24"); + perms.add(permAtt); + when(player.getEffectivePermissions()).thenReturn(perms); + PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome"); + jl.onPlayerJoin(e); + verify(addon, never()).logError(anyString()); + verify(ibc).setEntityLimit(eq(EntityType.BAT), eq(24)); + } /** * Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}. @@ -322,6 +338,12 @@ public class JoinListenerTest { PermissionAttachmentInfo permAtt3 = mock(PermissionAttachmentInfo.class); when(permAtt3.getPermission()).thenReturn("bskyblock.island.limit.dirt.34"); perms.add(permAtt3); + PermissionAttachmentInfo permAtt4 = mock(PermissionAttachmentInfo.class); + when(permAtt4.getPermission()).thenReturn("bskyblock.island.limit.chicken.34"); + perms.add(permAtt4); + PermissionAttachmentInfo permAtt5 = mock(PermissionAttachmentInfo.class); + when(permAtt5.getPermission()).thenReturn("bskyblock.island.limit.cave_spider.4"); + perms.add(permAtt5); when(player.getEffectivePermissions()).thenReturn(perms); PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome"); jl.onPlayerJoin(e); @@ -329,6 +351,8 @@ public class JoinListenerTest { verify(ibc).setBlockLimit(eq(Material.STONE), eq(24)); verify(ibc).setBlockLimit(eq(Material.GRASS), eq(14)); verify(ibc).setBlockLimit(eq(Material.DIRT), eq(34)); + verify(ibc).setEntityLimit(eq(EntityType.CHICKEN), eq(34)); + verify(ibc).setEntityLimit(eq(EntityType.CAVE_SPIDER), eq(4)); } /** @@ -357,8 +381,33 @@ public class JoinListenerTest { verify(ibc, never()).setBlockLimit(eq(Material.STONE), eq(14)); verify(ibc).setBlockLimit(eq(Material.STONE), eq(34)); } - - + + /** + * Test method for {@link world.bentobox.limits.listeners.JoinListener#onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent)}. + */ + @Test + public void testOnPlayerJoinWithPermLimitsMultiPermsSameEntity() { + // IBC - set the entity limit for BAT to be 25 already + when(ibc.getEntityLimit(any())).thenReturn(25); + Set perms = new HashSet<>(); + PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class); + when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.BAT.24"); + perms.add(permAtt); + PermissionAttachmentInfo permAtt2 = mock(PermissionAttachmentInfo.class); + when(permAtt2.getPermission()).thenReturn("bskyblock.island.limit.BAT.14"); + perms.add(permAtt2); + PermissionAttachmentInfo permAtt3 = mock(PermissionAttachmentInfo.class); + when(permAtt3.getPermission()).thenReturn("bskyblock.island.limit.BAT.34"); + perms.add(permAtt3); + when(player.getEffectivePermissions()).thenReturn(perms); + PlayerJoinEvent e = new PlayerJoinEvent(player, "welcome"); + jl.onPlayerJoin(e); + verify(addon, never()).logError(anyString()); + // Only the limit over 25 should be set + verify(ibc, never()).setEntityLimit(eq(EntityType.BAT), eq(24)); + verify(ibc, never()).setEntityLimit(eq(EntityType.BAT), eq(14)); + verify(ibc).setEntityLimit(eq(EntityType.BAT), eq(34)); + } /** * Test method for {@link world.bentobox.limits.listeners.JoinListener#onUnregisterIsland(world.bentobox.bentobox.api.events.island.IslandEvent)}.