diff --git a/src/main/java/me/goodandevil/skyblock/SkyBlock.java b/src/main/java/me/goodandevil/skyblock/SkyBlock.java index 9c09b010..57fd6c06 100644 --- a/src/main/java/me/goodandevil/skyblock/SkyBlock.java +++ b/src/main/java/me/goodandevil/skyblock/SkyBlock.java @@ -17,6 +17,7 @@ import me.goodandevil.skyblock.invite.InviteManager; import me.goodandevil.skyblock.island.IslandManager; import me.goodandevil.skyblock.leaderboard.LeaderboardManager; import me.goodandevil.skyblock.levelling.LevellingManager; +import me.goodandevil.skyblock.limit.LimitManager; import me.goodandevil.skyblock.listeners.*; import me.goodandevil.skyblock.menus.Rollback; import me.goodandevil.skyblock.menus.admin.Creator; @@ -61,6 +62,7 @@ public class SkyBlock extends JavaPlugin { private UpgradeManager upgradeManager; private PlayerDataManager playerDataManager; private CooldownManager cooldownManager; + private LimitManager limitManager; private ScoreboardManager scoreboardManager; private InviteManager inviteManager; private BiomeManager biomeManager; @@ -96,6 +98,7 @@ public class SkyBlock extends JavaPlugin { upgradeManager = new UpgradeManager(this); playerDataManager = new PlayerDataManager(this); cooldownManager = new CooldownManager(this); + limitManager = new LimitManager(this); if (fileManager.getConfig(new File(getDataFolder(), "config.yml")).getFileConfiguration() .getBoolean("Island.Scoreboard.Enable")) { @@ -258,6 +261,10 @@ public class SkyBlock extends JavaPlugin { return cooldownManager; } + public LimitManager getLimitManager() { + return limitManager; + } + public ScoreboardManager getScoreboardManager() { return scoreboardManager; } diff --git a/src/main/java/me/goodandevil/skyblock/command/commands/island/ChatCommand.java b/src/main/java/me/goodandevil/skyblock/command/commands/island/ChatCommand.java index 91be0a72..e362a592 100644 --- a/src/main/java/me/goodandevil/skyblock/command/commands/island/ChatCommand.java +++ b/src/main/java/me/goodandevil/skyblock/command/commands/island/ChatCommand.java @@ -32,6 +32,17 @@ public class ChatCommand extends SubCommand { Island island = islandManager.getIsland(player); + PlayerData playerData = playerDataManager.getPlayerData(player); + if (playerData.isChat() && island != null) { + Bukkit.getServer().getPluginManager() + .callEvent(new PlayerIslandChatSwitchEvent(player, island.getAPIWrapper(), false)); + playerData.setChat(false); + + messageManager.sendMessage(player, configLoad.getString("Command.Island.Chat.Untoggled.Message")); + soundManager.playSound(player, Sounds.IRONGOLEM_HIT.bukkitSound(), 1.0F, 1.0F); + return; + } + if (island == null) { messageManager.sendMessage(player, configLoad.getString("Command.Island.Chat.Owner.Message")); soundManager.playSound(player, Sounds.ANVIL_LAND.bukkitSound(), 1.0F, 1.0F); @@ -42,23 +53,12 @@ public class ChatCommand extends SubCommand { messageManager.sendMessage(player, configLoad.getString("Command.Island.Chat.Offline.Message")); soundManager.playSound(player, Sounds.ANVIL_LAND.bukkitSound(), 1.0F, 1.0F); } else { - PlayerData playerData = playerDataManager.getPlayerData(player); + Bukkit.getServer().getPluginManager() + .callEvent(new PlayerIslandChatSwitchEvent(player, island.getAPIWrapper(), true)); + playerData.setChat(true); - if (playerData.isChat()) { - Bukkit.getServer().getPluginManager() - .callEvent(new PlayerIslandChatSwitchEvent(player, island.getAPIWrapper(), false)); - playerData.setChat(false); - - messageManager.sendMessage(player, configLoad.getString("Command.Island.Chat.Untoggled.Message")); - soundManager.playSound(player, Sounds.IRONGOLEM_HIT.bukkitSound(), 1.0F, 1.0F); - } else { - Bukkit.getServer().getPluginManager() - .callEvent(new PlayerIslandChatSwitchEvent(player, island.getAPIWrapper(), true)); - playerData.setChat(true); - - messageManager.sendMessage(player, configLoad.getString("Command.Island.Chat.Toggled.Message")); - soundManager.playSound(player, Sounds.NOTE_PLING.bukkitSound(), 1.0F, 1.0F); - } + messageManager.sendMessage(player, configLoad.getString("Command.Island.Chat.Toggled.Message")); + soundManager.playSound(player, Sounds.NOTE_PLING.bukkitSound(), 1.0F, 1.0F); } } diff --git a/src/main/java/me/goodandevil/skyblock/config/FileManager.java b/src/main/java/me/goodandevil/skyblock/config/FileManager.java index f30bea82..d626fa93 100644 --- a/src/main/java/me/goodandevil/skyblock/config/FileManager.java +++ b/src/main/java/me/goodandevil/skyblock/config/FileManager.java @@ -54,6 +54,7 @@ public class FileManager { } Map configFiles = new LinkedHashMap<>(); + configFiles.put("limits.yml", new File(skyblock.getDataFolder(), "limits.yml")); configFiles.put("worlds.yml", new File(skyblock.getDataFolder(), "worlds.yml")); configFiles.put("levelling.yml", new File(skyblock.getDataFolder(), "levelling.yml")); configFiles.put("config.yml", new File(skyblock.getDataFolder(), "config.yml")); diff --git a/src/main/java/me/goodandevil/skyblock/island/IslandManager.java b/src/main/java/me/goodandevil/skyblock/island/IslandManager.java index 4a35c27e..4a01976f 100644 --- a/src/main/java/me/goodandevil/skyblock/island/IslandManager.java +++ b/src/main/java/me/goodandevil/skyblock/island/IslandManager.java @@ -1403,7 +1403,8 @@ public class IslandManager { return false; double size = island.getRadius(); - size += size % 2 == 0 ? 1 : 0; + if (size % 2 == 1) + size++; return LocationUtil.isLocationAtLocationRadius(location.clone().add(0.5, 0, 0.5), islandLocation, size); } diff --git a/src/main/java/me/goodandevil/skyblock/limit/LimitManager.java b/src/main/java/me/goodandevil/skyblock/limit/LimitManager.java new file mode 100644 index 00000000..0f5ae5c0 --- /dev/null +++ b/src/main/java/me/goodandevil/skyblock/limit/LimitManager.java @@ -0,0 +1,105 @@ +package me.goodandevil.skyblock.limit; + +import me.goodandevil.skyblock.SkyBlock; +import me.goodandevil.skyblock.island.Island; +import me.goodandevil.skyblock.island.IslandManager; +import me.goodandevil.skyblock.utils.version.Materials; +import org.bukkit.block.Block; +import org.bukkit.block.CreatureSpawner; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.permissions.PermissionAttachmentInfo; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class LimitManager { + + private final SkyBlock skyblock; + private Map blockLimits; + + public LimitManager(SkyBlock skyblock) { + this.skyblock = skyblock; + this.blockLimits = new HashMap<>(); + + FileConfiguration limitsConfig = skyblock.getFileManager().getConfig(new File(skyblock.getDataFolder(), "limits.yml")).getFileConfiguration(); + ConfigurationSection blockLimitSection = limitsConfig.getConfigurationSection("block"); + + if (blockLimitSection != null) { + for (String materialString : blockLimitSection.getKeys(false)) { + Materials material = Materials.fromString(materialString); + if (material != null) { + long limit = blockLimitSection.getLong(materialString); + this.blockLimits.put(material, limit); + } + } + } + } + + /** + * Gets the max number of a type of block a player can place + * @param player The player to check + * @param block The block to check + * @return The max number of the type of block the player can place + */ + public long getBlockLimit(Player player, Block block) { + if (player.hasPermission("fabledskyblock.limit.block.*")) + return -1; + + long limit = -1; + Materials material; + if (block.getType() == Materials.SPAWNER.parseMaterial()) { + material = Materials.getSpawner(((CreatureSpawner) block.getState()).getSpawnedType()); + if (this.blockLimits.containsKey(Materials.SPAWNER)) + limit = Math.max(limit, this.blockLimits.get(Materials.SPAWNER)); + } else { + material = Materials.getMaterials(block.getType(), block.getData()); + } + + if (this.blockLimits.containsKey(material)) + limit = Math.max(limit, this.blockLimits.get(material)); + + Set permissions = player.getEffectivePermissions() + .stream() + .filter(x -> x.getPermission().toLowerCase().startsWith("fabledskyblock.limit.block." + material.name().toLowerCase()) + || (block.getType() == Materials.SPAWNER.parseMaterial() && x.getPermission().toLowerCase().startsWith("fabledskyblock.limit.block.spawner"))) + .collect(Collectors.toSet()); + + for (PermissionAttachmentInfo permission : permissions) { + try { + String permString = permission.getPermission(); + String numberString = permString.substring(permString.lastIndexOf(".") + 1); + if (numberString.equals("*")) + return -1; + + limit = Math.max(limit, Integer.parseInt(numberString)); + } catch (Exception ignored) { } + } + + return limit; + } + + /** + * Checks if a player has exceeded the number of blocks they can place + * @param player The player to check + * @param block The block to check + * @return true if the player has exceeded the block limit, otherwise false + */ + public boolean isBlockLimitExceeded(Player player, Block block) { + IslandManager islandManager = this.skyblock.getIslandManager(); + + long limit = this.getBlockLimit(player, block); + if (limit == -1) + return false; + + Island island = islandManager.getIslandAtLocation(block.getLocation()); + long totalPlaced = island.getLevel().getMaterialAmount(Materials.getMaterials(block.getType(), block.getData()).name()); + + return limit < totalPlaced + 1; + } + +} diff --git a/src/main/java/me/goodandevil/skyblock/listeners/Block.java b/src/main/java/me/goodandevil/skyblock/listeners/Block.java index ba5bb4af..6df581c2 100644 --- a/src/main/java/me/goodandevil/skyblock/listeners/Block.java +++ b/src/main/java/me/goodandevil/skyblock/listeners/Block.java @@ -5,14 +5,18 @@ import me.goodandevil.skyblock.config.FileManager.Config; import me.goodandevil.skyblock.generator.Generator; import me.goodandevil.skyblock.generator.GeneratorManager; import me.goodandevil.skyblock.island.*; +import me.goodandevil.skyblock.limit.LimitManager; import me.goodandevil.skyblock.stackable.Stackable; import me.goodandevil.skyblock.stackable.StackableManager; import me.goodandevil.skyblock.upgrade.Upgrade; +import me.goodandevil.skyblock.utils.NumberUtil; +import me.goodandevil.skyblock.utils.StringUtil; import me.goodandevil.skyblock.utils.version.Materials; import me.goodandevil.skyblock.utils.version.NMSUtil; import me.goodandevil.skyblock.utils.version.Sounds; import me.goodandevil.skyblock.utils.world.LocationUtil; import me.goodandevil.skyblock.world.WorldManager; +import org.apache.commons.lang3.text.WordUtils; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -202,13 +206,11 @@ public class Block implements Listener { } // Specific check for beds - if (block.getType().name().equals("BED") || block.getType().name().contains("_BED")) { + if (!isObstructing && event.getBlock().getState().getData() instanceof org.bukkit.material.Bed) { BlockFace bedDirection = ((org.bukkit.material.Bed) event.getBlock().getState().getData()).getFacing(); org.bukkit.block.Block bedBlock = block.getRelative(bedDirection); - if (LocationUtil.isLocationAffectingLocation(bedBlock.getLocation(), island.getLocation(world, IslandEnvironment.Main))) { + if (LocationUtil.isLocationAffectingLocation(bedBlock.getLocation(), island.getLocation(world, IslandEnvironment.Main))) isObstructing = true; - - } } if (isObstructing) { @@ -222,6 +224,26 @@ public class Block implements Listener { } } + LimitManager limitManager = skyblock.getLimitManager(); + if (limitManager.isBlockLimitExceeded(player, block)) { + Materials material; + if (block.getType() == Materials.SPAWNER.parseMaterial()) { + material = Materials.getSpawner(((CreatureSpawner) block.getState()).getSpawnedType()); + } else { + material = Materials.getMaterials(block.getType(), block.getData()); + } + + skyblock.getMessageManager().sendMessage(player, + skyblock.getFileManager().getConfig(new File(skyblock.getDataFolder(), "language.yml")) + .getFileConfiguration().getString("Island.Limit.Block.Exceeded.Message") + .replace("%type", WordUtils.capitalizeFully(material.name().replace("_", " "))) + .replace("%limit", NumberUtil.formatNumber(limitManager.getBlockLimit(player, block)))); + skyblock.getSoundManager().playSound(player, Sounds.VILLAGER_NO.bukkitSound(), 1.0F, 1.0F); + + event.setCancelled(true); + return; + } + if (!configLoad.getBoolean("Island.Block.Level.Enable")) return; @@ -335,7 +357,7 @@ public class Block implements Listener { FileConfiguration configLoad = config.getFileConfiguration(); for (org.bukkit.block.Block block : event.getBlocks()) { - if (!islandManager.isLocationAtIsland(island, block.getLocation(), world)) { + if (!islandManager.isLocationAtIsland(island, block.getLocation(), world) || !islandManager.isLocationAtIsland(island, block.getRelative(event.getDirection()).getLocation(), world)) { event.setCancelled(true); return; } @@ -629,20 +651,21 @@ public class Block implements Listener { if (!skyblock.getFileManager().getConfig(new File(skyblock.getDataFolder(), "config.yml")).getFileConfiguration().getBoolean("Island.Spawn.Protection")) return; - if (event.getBlocks().isEmpty()) - return; - - Island island = islandManager.getIslandAtLocation(event.getBlocks().get(0).getLocation()); - if (island == null) - return; - - // Check spawn block protection - IslandWorld world = worldManager.getIslandWorld(event.getBlocks().get(0).getWorld()); - Location islandLocation = island.getLocation(world, IslandEnvironment.Main); - // PortalCreateEvent.getBlocks() changed from ArrayList to ArrayList in 1.14.1... why... if (NMSUtil.getVersionNumber() > 13) { - for (BlockState block : event.getBlocks()) { + List blocks = event.getBlocks(); + if (event.getBlocks().isEmpty()) + return; + + Island island = islandManager.getIslandAtLocation(event.getBlocks().get(0).getLocation()); + if (island == null) + return; + + // Check spawn block protection + IslandWorld world = worldManager.getIslandWorld(event.getBlocks().get(0).getWorld()); + Location islandLocation = island.getLocation(world, IslandEnvironment.Main); + + for (BlockState block : blocks) { if (LocationUtil.isLocationAffectingLocation(block.getLocation(), islandLocation)) { event.setCancelled(true); return; @@ -652,6 +675,17 @@ public class Block implements Listener { try { @SuppressWarnings("unchecked") List blocks = (List) event.getClass().getMethod("getBlocks").invoke(event); + if (blocks.isEmpty()) + return; + + Island island = islandManager.getIslandAtLocation(blocks.get(0).getLocation()); + if (island == null) + return; + + // Check spawn block protection + IslandWorld world = worldManager.getIslandWorld(blocks.get(0).getWorld()); + Location islandLocation = island.getLocation(world, IslandEnvironment.Main); + for (org.bukkit.block.Block block : blocks) { if (LocationUtil.isLocationAffectingLocation(block.getLocation(), islandLocation)) { event.setCancelled(true); diff --git a/src/main/java/me/goodandevil/skyblock/listeners/Interact.java b/src/main/java/me/goodandevil/skyblock/listeners/Interact.java index e742052a..71f68df2 100644 --- a/src/main/java/me/goodandevil/skyblock/listeners/Interact.java +++ b/src/main/java/me/goodandevil/skyblock/listeners/Interact.java @@ -3,6 +3,9 @@ package me.goodandevil.skyblock.listeners; import java.io.File; import java.util.Set; +import me.goodandevil.skyblock.limit.LimitManager; +import me.goodandevil.skyblock.utils.NumberUtil; +import org.apache.commons.lang3.text.WordUtils; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; @@ -120,10 +123,31 @@ public class Interact implements Listener { if (stackableManager != null && stackableManager.getStackableMaterials().contains(event.getMaterial()) && event.getClickedBlock().getType() == event.getMaterial() - && !player.isSneaking()) { + && !player.isSneaking() && islandManager.hasPermission(player, block.getLocation(), "Place")) { if (NMSUtil.getVersionNumber() > 8) { if (event.getHand() == EquipmentSlot.OFF_HAND) return; } + + LimitManager limitManager = skyblock.getLimitManager(); + if (limitManager.isBlockLimitExceeded(player, block)) { + Materials material; + if (block.getType() == Materials.SPAWNER.parseMaterial()) { + material = Materials.getSpawner(((CreatureSpawner) block.getState()).getSpawnedType()); + } else { + material = Materials.getMaterials(block.getType(), block.getData()); + } + + skyblock.getMessageManager().sendMessage(player, + skyblock.getFileManager().getConfig(new File(skyblock.getDataFolder(), "language.yml")) + .getFileConfiguration().getString("Island.Limit.Block.Exceeded.Message") + .replace("%type", WordUtils.capitalizeFully(material.name().replace("_", " "))) + .replace("%limit", NumberUtil.formatNumber(limitManager.getBlockLimit(player, block)))); + skyblock.getSoundManager().playSound(player, Sounds.VILLAGER_NO.bukkitSound(), 1.0F, 1.0F); + + event.setCancelled(true); + return; + } + Location location = event.getClickedBlock().getLocation(); if (stackableManager.isStacked(location)) { Stackable stackable = stackableManager.getStack(location, event.getMaterial()); diff --git a/src/main/java/me/goodandevil/skyblock/listeners/Join.java b/src/main/java/me/goodandevil/skyblock/listeners/Join.java index dd93b44c..86c801e8 100644 --- a/src/main/java/me/goodandevil/skyblock/listeners/Join.java +++ b/src/main/java/me/goodandevil/skyblock/listeners/Join.java @@ -166,5 +166,11 @@ public class Join implements Listener { scoreboard.run(); scoreboardManager.storeScoreboard(player, scoreboard); } + + Island island = islandManager.getIslandPlayerAt(player); + if (island != null) { + islandManager.updateBorder(island); + islandManager.updateFlight(player); + } } } diff --git a/src/main/java/me/goodandevil/skyblock/listeners/Move.java b/src/main/java/me/goodandevil/skyblock/listeners/Move.java index b75905cd..1b83af85 100644 --- a/src/main/java/me/goodandevil/skyblock/listeners/Move.java +++ b/src/main/java/me/goodandevil/skyblock/listeners/Move.java @@ -182,19 +182,7 @@ public class Move implements Listener { } } } else { - Config config = skyblock.getFileManager() - .getConfig(new File(skyblock.getDataFolder(), "config.yml")); - FileConfiguration configLoad = config.getFileConfiguration(); - - if (LocationUtil.isLocationAtLocationRadius(to, - island.getLocation(world, IslandEnvironment.Island), island.getRadius() + 2)) { - if (!configLoad.getBoolean("Island.WorldBorder.Enable") || !island.isBorder()) { - player.teleport(player.getLocation() - .add(from.toVector().subtract(to.toVector()).normalize().multiply(2.0D))); - player.setFallDistance(0.0F); - soundManager.playSound(player, Sounds.ENDERMAN_TELEPORT.bukkitSound(), 1.0F, 1.0F); - } - } else { + if (!LocationUtil.isLocationAtLocationRadius(island.getLocation(world, IslandEnvironment.Island), to, island.getRadius() + 0.5)) { if (island.getVisit().isVisitor(player.getUniqueId())) { player.teleport(island.getLocation(world, IslandEnvironment.Visitor)); } else { diff --git a/src/main/java/me/goodandevil/skyblock/utils/world/WorldBorder.java b/src/main/java/me/goodandevil/skyblock/utils/world/WorldBorder.java index e3f2ff6f..6a8410d2 100644 --- a/src/main/java/me/goodandevil/skyblock/utils/world/WorldBorder.java +++ b/src/main/java/me/goodandevil/skyblock/utils/world/WorldBorder.java @@ -36,14 +36,12 @@ public final class WorldBorder { public static void send(Player player, Color color, double size, Location centerLocation) { try { - // Adjust border size to fit around whole-blocks, odd numbers only! - size += size % 2 == 0 ? 1 : 0; - - if (centerLocation == null) + if (centerLocation == null || centerLocation.getWorld() == null) return; - centerLocation = centerLocation.clone(); - centerLocation.add(.5, 0, .5); + if (size % 2 == 1) + size++; + Object worldBorder = worldBorderClass.getConstructor().newInstance(); if (NMSUtil.getVersionNumber() < 9) { @@ -58,7 +56,7 @@ public final class WorldBorder { } Method setCenter = worldBorder.getClass().getMethod("setCenter", double.class, double.class); - setCenter.invoke(worldBorder, centerLocation.getX(), centerLocation.getZ()); + setCenter.invoke(worldBorder, centerLocation.getBlockX(), centerLocation.getBlockZ()); Method setSize = worldBorder.getClass().getMethod("setSize", double.class); setSize.invoke(worldBorder, size); diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml index 29415788..aa4cf364 100644 --- a/src/main/resources/language.yml +++ b/src/main/resources/language.yml @@ -3178,6 +3178,10 @@ Placeholder: Non-empty: Message: '&f%placeholder' Island: + Limit: + Block: + Exceeded: + Message: '&bSkyBlock &8| &cError&8: &eYou''ve exceeded the limit of &b%types &eyou can place (&b%limit&e).' Chat: Format: Role: diff --git a/src/main/resources/limits.yml b/src/main/resources/limits.yml new file mode 100644 index 00000000..8bd308b4 --- /dev/null +++ b/src/main/resources/limits.yml @@ -0,0 +1,3 @@ +block: + BEDROCK: 0 + END_PORTAL_FRAME: 12