diff --git a/src/de/craftinc/gates/Gate.java b/src/de/craftinc/gates/Gate.java index fc986a6..2a20097 100644 --- a/src/de/craftinc/gates/Gate.java +++ b/src/de/craftinc/gates/Gate.java @@ -19,6 +19,7 @@ package de.craftinc.gates; import de.craftinc.gates.util.FloodUtil; import de.craftinc.gates.persistence.LocationUtil; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.configuration.serialization.ConfigurationSerializable; @@ -29,6 +30,7 @@ public class Gate implements ConfigurationSerializable { protected Location location; /* saving both location and gateBlockLocations is redundant but makes it easy to allow players to reshape gates */ protected Set gateBlockLocations = new HashSet(); /* Locations of the blocks inside the gate */ + protected Set gateFrameBlocks = new HashSet(); protected Location exit; @@ -79,7 +81,10 @@ public class Gate implements ConfigurationSerializable this.location = location; if (isOpen) { - findPortalBlocks(); + if (this.gateBlockLocations == null || this.gateBlockLocations.size() == 0 ) { + findPortalBlocks(); + } + validate(); } } @@ -174,16 +179,29 @@ public class Gate implements ConfigurationSerializable } + /** + * + * @return Will never return 'null' but might return an empty Set. + */ + public Set getGateFrameBlocks() + { + return gateFrameBlocks; + } + + + protected void findPortalBlocks() { gateBlockLocations = new HashSet(); - Set gateBlocks = FloodUtil.getGateFrameBlocks(location.getBlock()); + Set gateBlocks = FloodUtil.getGatePortalBlocks(location.getBlock()); if (gateBlocks != null) { for (Block b : gateBlocks) { gateBlockLocations.add(b.getLocation()); } } + + gateFrameBlocks = FloodUtil.getFrame(gateBlocks); } @@ -205,17 +223,22 @@ public class Gate implements ConfigurationSerializable setOpen(false); throw new Exception("Gate got closed. It has no exit."); } - - if (!isHidden) { - findPortalBlocks(); - } if (gateBlockLocations.size() == 0) { setOpen(false); - throw new Exception("Gate got closed. The frame is missing or broken."); + throw new Exception("Gate got closed. The frame is missing or broken. (no gate blocks)"); } + if (!isHidden() && Plugin.getPlugin().getConfig().getBoolean(Plugin.confCheckForBrokenGateFramesKey)) { + for (Block b : gateFrameBlocks) { + + if (b.getType() == Material.AIR) { + setOpen(false); + throw new Exception("Gate got closed. The frame is missing or broken. (missing frame block(s))"); + } + } + } } @@ -266,6 +289,8 @@ public class Gate implements ConfigurationSerializable for (Map sgb : serializedGateBlocks) { gateBlockLocations.add(LocationUtil.deserializeLocation(sgb)); } + + gateFrameBlocks = FloodUtil.getFrameWithLocations(gateBlockLocations); } catch (Exception e) { Plugin.log("ERROR: Failed to load gate '" + id + "'! (" + e.getMessage() + ")"); diff --git a/src/de/craftinc/gates/GatesManager.java b/src/de/craftinc/gates/GatesManager.java index 336d171..bcfea1f 100644 --- a/src/de/craftinc/gates/GatesManager.java +++ b/src/de/craftinc/gates/GatesManager.java @@ -26,6 +26,7 @@ import java.util.logging.Level; import de.craftinc.gates.persistence.MigrationUtil; import org.bukkit.Chunk; import org.bukkit.Location; +import org.bukkit.block.Block; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -46,8 +47,9 @@ public class GatesManager protected Map gatesById; protected Map> gatesByChunk; protected Map gatesByLocation; - - private List gates; + protected Map gatesByFrameLocation; + + protected List gates; public Gate getGateWithId(String id) @@ -68,7 +70,14 @@ public class GatesManager SimpleLocation simpleLocation = new SimpleLocation(location); return gatesByLocation.get(simpleLocation); } - + + + public Gate getGateAtFrameLocation(Location location) + { + SimpleLocation simpleLocation = new SimpleLocation(location); + return gatesByFrameLocation.get(simpleLocation); + } + public void saveGatesToDisk() { @@ -115,10 +124,25 @@ public class GatesManager break; } } - + + for (Gate g : this.gates) { + try { + g.validate(); + } + catch (Exception e) { + try { + g.setOpen(false); + } + catch (Exception ignored) { } + + Plugin.log(Level.FINER, "closed gate '" + g.getId() + "' reason: " + e.getMessage()); + } + } + fillGatesById(); fillGatesByChunk(); fillGatesByLocation(); + fillGatesByFrameLocation(); Plugin.log("Loaded " + this.gates.size() + " gates."); @@ -204,6 +228,22 @@ public class GatesManager } + protected void fillGatesByFrameLocation() + { + int numFrameBlocks = 0; + + for (Gate g : gates) { + numFrameBlocks += g.gateFrameBlocks.size(); + } + + gatesByFrameLocation = new HashMap((int)(numFrameBlocks*1.25)); + + for (Gate g : gates) { + this.addGateByFrameLocations(g); + } + } + + protected void removeGateById(String id) { gatesById.remove(id); @@ -216,7 +256,7 @@ public class GatesManager } - protected void removeGateFromLocations(Set gateBlocks) + protected void removeGateByLocation(Set gateBlocks) { for (Location l : gateBlocks) { SimpleLocation sl = new SimpleLocation(l); @@ -225,6 +265,15 @@ public class GatesManager } + protected void removeGateByFrameLocation(Set gateFrameBlocks) + { + for (Block block : gateFrameBlocks) { + SimpleLocation sl = new SimpleLocation(block.getLocation()); + gatesByFrameLocation.remove(sl); + } + } + + protected void addGateByLocations(Gate g) { for (Location l : g.getGateBlockLocations()) { @@ -234,6 +283,15 @@ public class GatesManager } + protected void addGateByFrameLocations(Gate g) + { + for (Block block : g.getGateFrameBlocks()) { + SimpleLocation sl = new SimpleLocation(block.getLocation()); + gatesByFrameLocation.put(sl, g); + } + } + + protected void removeGateFromChunk(Gate g, Location l) { if (l != null) { @@ -350,13 +408,16 @@ public class GatesManager } - public void handleGateLocationChange(Gate g, Location oldLocation, Set oldGateBlockLocations) + public void handleGateLocationChange(Gate g, Location oldLocation, Set oldGateBlockLocations, Set oldGateFrameBlocks) { this.removeGateFromChunk(g, oldLocation); this.addGateByChunk(g); - this.removeGateFromLocations(oldGateBlockLocations); + this.removeGateByLocation(oldGateBlockLocations); this.addGateByLocations(g); + + this.removeGateByFrameLocation(oldGateFrameBlocks); + this.addGateByFrameLocations(g); } @@ -365,6 +426,7 @@ public class GatesManager this.addGateByChunk(g); this.addGateByLocations(g); this.addGateWithId(g); + this.addGateByFrameLocations(g); } @@ -372,7 +434,8 @@ public class GatesManager { this.removeGateById(g.getId()); this.removeGateFromChunk(g, g.getLocation()); - this.removeGateFromLocations(g.getGateBlockLocations()); + this.removeGateByLocation(g.getGateBlockLocations()); + this.removeGateByFrameLocation(g.getGateFrameBlocks()); } diff --git a/src/de/craftinc/gates/Plugin.java b/src/de/craftinc/gates/Plugin.java index 7fa0071..e67b406 100644 --- a/src/de/craftinc/gates/Plugin.java +++ b/src/de/craftinc/gates/Plugin.java @@ -61,6 +61,7 @@ public class Plugin extends JavaPlugin protected PlayerRespawnListener respawnListener = new PlayerRespawnListener(); protected PlayerChangedWorldListener worldChangeListener = new PlayerChangedWorldListener(); protected PlayerJoinListener joinListener = new PlayerJoinListener(); + protected BlockBreakListener blockBreakListener = new BlockBreakListener(); public Plugin() @@ -162,7 +163,9 @@ public class Plugin extends JavaPlugin pm.registerEvents(this.worldChangeListener, this); pm.registerEvents(this.joinListener, this); - + if (getConfig().getBoolean(confCheckForBrokenGateFramesKey)) { + pm.registerEvents(this.blockBreakListener, this); + } } diff --git a/src/de/craftinc/gates/commands/CommandInfo.java b/src/de/craftinc/gates/commands/CommandInfo.java index 051896e..b25a897 100644 --- a/src/de/craftinc/gates/commands/CommandInfo.java +++ b/src/de/craftinc/gates/commands/CommandInfo.java @@ -75,6 +75,9 @@ public class CommandInfo extends BaseCommand gate.getExit().getWorld().getName()); else sendMessage(ChatColor.DARK_AQUA + "NOTE: this gate has no exit"); + + + Plugin.log("frame blocks: " + gate.getGateFrameBlocks()); } } diff --git a/src/de/craftinc/gates/listeners/BlockBreakListener.java b/src/de/craftinc/gates/listeners/BlockBreakListener.java new file mode 100644 index 0000000..accd5be --- /dev/null +++ b/src/de/craftinc/gates/listeners/BlockBreakListener.java @@ -0,0 +1,48 @@ +/* Craft Inc. Gates + Copyright (C) 2011-2013 Craft Inc. Gates Team (see AUTHORS.txt) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program (LGPLv3). If not, see . +*/ +package de.craftinc.gates.listeners; + + +import de.craftinc.gates.Gate; +import de.craftinc.gates.Plugin; +import de.craftinc.gates.util.GateBlockChangeSender; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; + +public class BlockBreakListener implements Listener +{ + @EventHandler(priority = EventPriority.NORMAL) + public void onBlockBreak(BlockBreakEvent event) + { + if (event.isCancelled()) { + return; + } + + Gate gate = Plugin.getPlugin().getGatesManager().getGateAtFrameLocation(event.getBlock().getLocation()); + + if (gate != null && !gate.isHidden()) { + try { + gate.setOpen(false); + } + catch (Exception ignored) { } + + GateBlockChangeSender.updateGateBlocks(gate); + } + } +} diff --git a/src/de/craftinc/gates/util/FloodUtil.java b/src/de/craftinc/gates/util/FloodUtil.java index e517ed7..3d4967a 100644 --- a/src/de/craftinc/gates/util/FloodUtil.java +++ b/src/de/craftinc/gates/util/FloodUtil.java @@ -20,6 +20,7 @@ import java.util.HashSet; import java.util.Set; import java.util.logging.Level; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -44,10 +45,100 @@ public class FloodUtil exp2.add(BlockFace.NORTH); exp2.add(BlockFace.SOUTH); } - + + + /** + * Returns the all frame blocks of an gate. + * @param blocks All blocks inside the gate. + * @return A Set containing all frame block. Will never return 'null'. + */ + public static Set getFrame(Set blocks) + { + if (blocks == null || blocks.isEmpty()) { + return new HashSet(); + } + + // try to find gate's direction (north-south or east-west) + Set gateFrameSearchFaces = null; + + for (Block b : blocks) { + + if (blocks.contains(b.getRelative(BlockFace.EAST)) || + blocks.contains(b.getRelative(BlockFace.WEST))) { + + gateFrameSearchFaces = exp1; + break; + } + + if (blocks.contains(b.getRelative(BlockFace.NORTH)) || + blocks.contains(b.getRelative(BlockFace.SOUTH))) { + + gateFrameSearchFaces = exp2; + break; + } + + } + + if (gateFrameSearchFaces != null) { + return _getFrame(blocks, gateFrameSearchFaces); + } + else { // no direction found (the gate might only consist of blocks one over another) + + // Try one direction and check if the found blocks are not air. + // If air is found (frame broken or wrong direction) return the other direction + Set frameBlocks = _getFrame(blocks, exp1); + + for (Block b : frameBlocks) { + + if (b.getType() == Material.AIR) { + return _getFrame(blocks, exp2); + } + } + + return frameBlocks; + } + } + + + + protected static Set _getFrame(Set blocks, Set searchDirections) + { + Set frameBlocks = new HashSet(); + + for (Block b : blocks) { + + for (BlockFace bf : searchDirections) { + Block bb = b.getRelative(bf); + + if (!blocks.contains(bb)) { + frameBlocks.add(bb); + } + } + } + + return frameBlocks; + } + + + /** + * Returns the all frame blocks of an gate. + * @param locations All locations inside the gate. + * @return A Set containing all frame block. Will never return 'null'. + */ + public static Set getFrameWithLocations(Set locations) + { + Set blocks = new HashSet(); + + for (Location l : locations) { + blocks.add(l.getBlock()); + } + + return getFrame(blocks); + } + // For the same frame and location this set of blocks is deterministic - public static Set getGateFrameBlocks(Block block) + public static Set getGatePortalBlocks(Block block) { int frameBlockSearchLimit = Plugin.getPlugin().getConfig().getInt(Plugin.confMaxGateBlocksKey);