/* * This file is part of Libelula Minecraft Edition Project. * * Libelula Minecraft Edition is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Libelula Minecraft Edition is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Libelula Minecraft Edition. * If not, see <http://www.gnu.org/licenses/>. * */ package me.libelula.pb; import com.sk89q.worldedit.commands.UtilityCommands; import com.sk89q.worldguard.bukkit.FlagStateManager; import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.bukkit.ChatColor; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import static org.bukkit.Material.JACK_O_LANTERN; import static org.bukkit.Material.PUMPKIN; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; /** * Class ProtectionBlocks of the plugin. * * @author Diego Lucio D'Onofrio <ddonofrio@member.fsf.org> * @version 1.0 */ public class ProtectionBlocks { private final LibelulaProtectionBlocks plugin; private TreeMap<Material, Integer> compacPSDB; /** * Regular expression for matching new and old PS region names. */ public static String regionIdRegexp = "^ps(-?[0-9]+)x(-?[0-9]+)y(-?[0-9]+)z"; private class RegionManagerComparator implements Comparator<RegionManager> { @Override public int compare(RegionManager o1, RegionManager o2) { return o1.hashCode() - o2.hashCode(); } } private class LocationComparator implements Comparator<Location> { @Override public int compare(Location o1, Location o2) { int resp; resp = o1.getWorld().getUID().compareTo(o2.getWorld().getUID()); if (resp == 0) { resp = o1.getBlockX() - o2.getBlockX(); if (resp == 0) { resp = o1.getBlockY() - o2.getBlockY(); if (resp == 0) { resp = o1.getBlockZ() - o2.getBlockZ(); } } } return resp; } } public class PSBlocks implements Comparable<PSBlocks> { Location location; ProtectedRegion region; Material material; boolean hidden; String name; List<String> lore; Byte materialData; int secondsFromEpoch; @Override public String toString() { String resp; if (location != null) { resp = this.location.getWorld().getName() + " " + this.location.getBlockX() + " " + this.location.getBlockY() + " " + this.location.getBlockZ() + " "; } else { resp = "<NULL Location>"; } if (material != null) { resp = resp + this.material.name() + " "; } else { resp = resp + "<NULL Material>"; } resp = resp + this.hidden; return resp; } @Override public int compareTo(PSBlocks o) { return new LocationComparator().compare(o.location, location); } } private TreeMap<Location, PSBlocks> protectionBlockMap; private TreeMap<Location, Integer> dropCancellationSet; private final Lock _protectionBlock_mutex; private final Lock _dropCancellationSet_mutex; public ProtectionBlocks(LibelulaProtectionBlocks plugin) { protectionBlockMap = new TreeMap<>(new LocationComparator()); dropCancellationSet = new TreeMap<>(new LocationComparator()); _protectionBlock_mutex = new ReentrantLock(true); _dropCancellationSet_mutex = new ReentrantLock(true); this.compacPSDB = new TreeMap<>(); this.plugin = plugin; } public void addDropEventCancellation(Location loc, int quantity) { _dropCancellationSet_mutex.lock(); try { dropCancellationSet.put(loc, quantity); } finally { _dropCancellationSet_mutex.unlock(); } } /** * * @param loc Location * @return true if the event location where marked for cancellation. */ public boolean removeDropEventCancellation(Location loc) { boolean ret; _dropCancellationSet_mutex.lock(); try { ret = dropCancellationSet.containsKey(loc); if (ret) { int remains = dropCancellationSet.remove(loc); if (remains > 0) { dropCancellationSet.put(loc, remains); } } } finally { _dropCancellationSet_mutex.unlock(); } return ret; } public void addProtectionBlock(Location location, ProtectedRegion region, Material material, boolean hidden, String name, List<String> lore, byte materialData, int secondsFromEpoch, boolean insert) { PSBlocks psb = new PSBlocks(); psb.location = location; psb.region = region; psb.material = material; psb.hidden = hidden; psb.name = name; psb.lore = lore; psb.materialData = materialData; psb.secondsFromEpoch = secondsFromEpoch; _protectionBlock_mutex.lock(); try { protectionBlockMap.put(location, psb); } finally { _protectionBlock_mutex.unlock(); } if (insert) { TaskManager.addPSBlock(psb, plugin); } } public boolean removeProtectionBlock(Location location) { boolean resp; PSBlocks pbs = protectionBlockMap.get(location); _protectionBlock_mutex.lock(); try { if (protectionBlockMap.remove(location) != null) { TaskManager.removeProtectionBlock(location, plugin); plugin.wgm.removeProtection(location.getWorld(), pbs.region); resp = true; } else { resp = false; } } finally { _protectionBlock_mutex.unlock(); } return resp; } public boolean removeProtectionBlock(Location location, Player player) { PSBlocks pbs = protectionBlockMap.get(location); boolean resp = removeProtectionBlock(location); if (resp) { addDropEventCancellation(location, location.getBlock().getDrops().size()); } ItemStack protectionBlock = plugin.pc.getItemStack(pbs); plugin.pc.addAvailableId(protectionBlock.getItemMeta().getLore().get(2)); if (!player.getInventory().addItem(protectionBlock).isEmpty()) { location.getWorld().dropItem(location, protectionBlock); } return resp; } public void addProtectionBlock(PSBlocks psb) { _protectionBlock_mutex.lock(); try { protectionBlockMap.put(psb.location, psb); } finally { _protectionBlock_mutex.unlock(); } TaskManager.addPSBlock(psb, plugin); } public void addProtectionBlock(Location location, ProtectedRegion region, Material material, boolean hidden, String name, List<String> lore, Byte materialData, int secondsFromEpoch) { addProtectionBlock(location, region, material, hidden, name, lore, materialData, secondsFromEpoch, true); } public void load() { TaskManager.loadProtectionBlocks(plugin); } /** * * @param location * @return true if the specified location contains a protection stone. */ public boolean contains(Location location) { return protectionBlockMap.containsKey(location); } /** * * @return the number of protection blocks. */ public int size() { return protectionBlockMap.size(); } public PSBlocks get(Location location) { return protectionBlockMap.get(location); } public boolean matches(Block block) { PSBlocks psb = get(block.getLocation()); if (psb == null) { return false; } if (block.getType() != psb.material) { switch (block.getType()) { case REDSTONE_LAMP_ON: if (psb.material == Material.REDSTONE_LAMP_OFF) { return true; } case GLOWING_REDSTONE_ORE: if (psb.material == Material.REDSTONE_ORE) { return true; } } } else { switch (block.getType()) { case PUMPKIN: case JACK_O_LANTERN: return true; default: if (psb.materialData == block.getData()) { return true; } break; } } return false; } public void addOldPsBlock(Material material, int size) { compacPSDB.put(material, size); } public TreeMap<Material, Integer> getoldPSs() { return compacPSDB; } public boolean oldPScontainsBlock(Material material) { return compacPSDB.containsKey(material); } public int oldPSgetSizeFor(Material material) { return compacPSDB.get(material); } public void oldPSPlace(BlockPlaceEvent e) { } public void newBlock(ProtectedCuboidRegion regionToProtect, Location location, Player player, ItemMeta dtMeta, Material material, Byte materialData) { RegionManager rm = plugin.wgm.getRegionManager(location.getWorld()); if (rm.overlapsUnownedRegion(regionToProtect, plugin.wgm.wrapPlayer(player))) { returnBlock(location, player, dtMeta); player.sendMessage(ChatColor.RED + plugin.i18n.getText("overlaps")); return; } if (plugin.config.ignoredWorldContains(location.getWorld())) { returnBlock(location, player, dtMeta); player.sendMessage(ChatColor.RED + plugin.i18n.getText("ignored_world")); return; } if (protectionBlockMap.containsKey(location)) { returnBlock(location, player, dtMeta); player.sendMessage(ChatColor.RED + plugin.i18n.getText("protection_over")); return; } int calculatedPriority = -1; for (ProtectedRegion pr : rm.getApplicableRegions(regionToProtect)) { if (pr.getPriority() > calculatedPriority) { calculatedPriority = pr.getPriority(); } } regionToProtect.setPriority(calculatedPriority + 1); rm.addRegion(regionToProtect); player.sendMessage(ChatColor.GREEN + plugin.i18n.getText("pb_activated")); addProtectionBlock(location, regionToProtect, material, false, dtMeta.getDisplayName(), dtMeta.getLore(), materialData, (int) new Date().getTime() / 1000); try { rm.save(); } catch (ProtectionDatabaseException ex) { Logger.getLogger(ProtectionBlocks.class .getName()).log(Level.SEVERE, null, ex); } if (player.getGameMode() == GameMode.CREATIVE) { player.setItemInHand(new ItemStack(Material.AIR)); } plugin.pc.removeAvailableId(dtMeta.getLore().get(2)); new FlagsProcessor(plugin, location).runTaskAsynchronously(plugin); } private void returnBlock(Location location, Player player, ItemMeta dtMeta) { ItemStack is = new ItemStack(location.getBlock().getType(), 1, location.getBlock().getData()); is.setItemMeta(dtMeta); if (player.getItemInHand().getType() == Material.AIR) { player.setItemInHand(is); } else if (player.getGameMode() == GameMode.CREATIVE) { // return nothing. } else { location.getWorld().dropItem(location, is); } location.getBlock().setType(Material.AIR); } public PSBlocks getPs(Location loc) { PSBlocks psb = null; ProtectedRegion region = plugin.wgm.getRealApplicableRegion(loc); if (region != null && region.getId().matches(regionIdRegexp)) { Pattern p = Pattern.compile("-?\\d+"); Matcher m = p.matcher(region.getId()); m.find(); int x = Integer.parseInt(m.group()); m.find(); int y = Integer.parseInt(m.group()); m.find(); int z = Integer.parseInt(m.group()); Location psLoc = new Location(loc.getWorld(), x, y, z); if (protectionBlockMap.containsKey(psLoc)) { psb = protectionBlockMap.get(psLoc); } } return psb; } @SuppressWarnings("unchecked") public void setFlag(Player player, Flag flag, String value) { PSBlocks psb = getPs(player.getLocation()); if (psb == null) { player.sendMessage(ChatColor.RED + plugin.i18n.getText("not_in_ps_area")); return; } if (!validatePlayerPermission(player, psb)) { return; } if (value == null) { psb.region.setFlag(flag, null); return; } switch (value.toLowerCase()) { case "allow": psb.region.setFlag(flag, StateFlag.State.ALLOW); break; case "deny": psb.region.setFlag(flag, StateFlag.State.DENY); break; default: switch (flag.getName().toLowerCase()) { case "farewell": case "greeting": psb.region.setFlag(flag, value); default: return; } } } private boolean validatePlayerPermission(Player player, PSBlocks psb) { if (psb.region.isOwner(player.getName()) || player.isOp()) { return true; } player.sendMessage(ChatColor.RED + plugin.i18n.getText("you_dont_have_permissions")); return false; } public void hide(Player player) { PSBlocks psb = getPs(player.getLocation()); if (psb == null) { player.sendMessage(ChatColor.RED + plugin.i18n.getText("not_in_ps_area")); return; } if (psb.hidden) { player.sendMessage(ChatColor.RED + plugin.i18n.getText("already_hidden")); return; } if (!validatePlayerPermission(player, psb)) { return; } psb.location.getBlock().setType(Material.AIR); psb.hidden = true; TaskManager.updatePSBlocks(psb, plugin); } public void unhide(Player player, boolean force) { PSBlocks psb = getPs(player.getLocation()); if (psb == null) { player.sendMessage(ChatColor.RED + plugin.i18n.getText("not_in_ps_area")); return; } if (!psb.hidden && !force) { player.sendMessage(ChatColor.RED + plugin.i18n.getText("not_hidden")); if (player.hasPermission("pb.unhide.force")) { player.sendMessage(ChatColor.YELLOW + plugin.i18n.getText("not_hidden_force")); } return; } if (!validatePlayerPermission(player, psb)) { return; } psb.location.getBlock().setType(psb.material); if (psb.hidden) { psb.hidden = false; TaskManager.updatePSBlocks(psb, plugin); } } public PSBlocks addMember(Player player, String[] members) { ProtectionBlocks.PSBlocks psb = getPs(player.getLocation()); if (psb == null) { player.sendMessage(ChatColor.RED + plugin.i18n.getText("not_in_ps_area")); return null; } if (!psb.region.isOwner(player.getName()) && !player.hasPermission("pb.addmember.others")) { player.sendMessage(ChatColor.RED + plugin.i18n.getText("not_owned_by_you")); return null; } plugin.wgm.addMembers(player.getWorld(), psb.region.getId(), members); return psb; } public PSBlocks removeMember(Player player, String[] members) { ProtectionBlocks.PSBlocks psb = getPs(player.getLocation()); if (psb == null) { player.sendMessage(ChatColor.RED + plugin.i18n.getText("not_in_ps_area")); return null; } if (!psb.region.isOwner(player.getName()) && !player.hasPermission("pb.removemember.others")) { player.sendMessage(ChatColor.RED + plugin.i18n.getText("not_owned_by_you")); return null; } plugin.wgm.removeMembers(player.getWorld(), psb.region.getId(), members); return psb; } /** * Gets PSBlocks list owned by a player. * * @param owner * @return A list of Protection blocks owned by given player. */ public PSBlocks[] getOwnedPSList(String owner) { TreeSet<PSBlocks> psbResult = new TreeSet<>(); _protectionBlock_mutex.lock(); try { for (Map.Entry<Location, PSBlocks> psb : protectionBlockMap.entrySet()) { if (psb.getValue().region.isOwner(owner.toLowerCase())) { psbResult.add(psb.getValue()); } } } finally { _protectionBlock_mutex.unlock(); } return psbResult.toArray(new PSBlocks[0]); } public void removeAllPB(String playerName) { PSBlocks[] psbs = getOwnedPSList(playerName); TreeSet<RegionManager> rmsToSave = new TreeSet<>(new RegionManagerComparator()); int qtty = psbs.length; for (PSBlocks psb : psbs) { RegionManager rm = plugin.wgm.getRegionManager(psb.location.getWorld()); rm.removeRegion(psb.region.getId()); TaskManager.removeProtectionBlock(psb.location, plugin); rmsToSave.add(rm); _protectionBlock_mutex.lock(); try { protectionBlockMap.remove(psb.location); } finally { _protectionBlock_mutex.unlock(); } } for (RegionManager rm : rmsToSave) { try { rm.save(); } catch (Exception ex) { plugin.getLogger().severe(ex.toString()); } } if (qtty > 0) { plugin.getLogger().log(Level.INFO, "Erased: {0} protection blocks owned by {1}", new Object[]{qtty, playerName}); } else { plugin.getLogger().log(Level.INFO, "{0} has no protection blocks.", playerName); } } }