Added checks for broken gate frame blocks.

This commit is contained in:
Tobias Ottenweller 2013-06-18 19:53:04 +02:00
parent 9e36cf189b
commit 99be2905be
6 changed files with 251 additions and 18 deletions

View File

@ -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<Location> gateBlockLocations = new HashSet<Location>(); /* Locations of the blocks inside the gate */
protected Set<Block> gateFrameBlocks = new HashSet<Block>();
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<Block> getGateFrameBlocks()
{
return gateFrameBlocks;
}
protected void findPortalBlocks()
{
gateBlockLocations = new HashSet<Location>();
Set<Block> gateBlocks = FloodUtil.getGateFrameBlocks(location.getBlock());
Set<Block> gateBlocks = FloodUtil.getGatePortalBlocks(location.getBlock());
if (gateBlocks != null) {
for (Block b : gateBlocks) {
gateBlockLocations.add(b.getLocation());
}
}
gateFrameBlocks = FloodUtil.getFrame(gateBlocks);
}
@ -206,16 +224,21 @@ public class Gate implements ConfigurationSerializable
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<String, Object> sgb : serializedGateBlocks) {
gateBlockLocations.add(LocationUtil.deserializeLocation(sgb));
}
gateFrameBlocks = FloodUtil.getFrameWithLocations(gateBlockLocations);
}
catch (Exception e) {
Plugin.log("ERROR: Failed to load gate '" + id + "'! (" + e.getMessage() + ")");

View File

@ -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<String, Gate> gatesById;
protected Map<SimpleChunk, Set<Gate>> gatesByChunk;
protected Map<SimpleLocation, Gate> gatesByLocation;
protected Map<SimpleLocation, Gate> gatesByFrameLocation;
private List<Gate> gates;
protected List<Gate> gates;
public Gate getGateWithId(String id)
@ -70,6 +72,13 @@ public class GatesManager
}
public Gate getGateAtFrameLocation(Location location)
{
SimpleLocation simpleLocation = new SimpleLocation(location);
return gatesByFrameLocation.get(simpleLocation);
}
public void saveGatesToDisk()
{
gatesConfig.set(gatesPath, new ArrayList<Object>(gatesById.values()));
@ -116,9 +125,24 @@ public class GatesManager
}
}
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<SimpleLocation, Gate>((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<Location> gateBlocks)
protected void removeGateByLocation(Set<Location> gateBlocks)
{
for (Location l : gateBlocks) {
SimpleLocation sl = new SimpleLocation(l);
@ -225,6 +265,15 @@ public class GatesManager
}
protected void removeGateByFrameLocation(Set<Block> 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<Location> oldGateBlockLocations)
public void handleGateLocationChange(Gate g, Location oldLocation, Set<Location> oldGateBlockLocations, Set<Block> 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());
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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);
}
}
}

View File

@ -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;
@ -46,8 +47,98 @@ public class FloodUtil
}
/**
* 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<Block> getFrame(Set<Block> blocks)
{
if (blocks == null || blocks.isEmpty()) {
return new HashSet<Block>();
}
// try to find gate's direction (north-south or east-west)
Set<BlockFace> 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<Block> frameBlocks = _getFrame(blocks, exp1);
for (Block b : frameBlocks) {
if (b.getType() == Material.AIR) {
return _getFrame(blocks, exp2);
}
}
return frameBlocks;
}
}
protected static Set<Block> _getFrame(Set<Block> blocks, Set<BlockFace> searchDirections)
{
Set<Block> frameBlocks = new HashSet<Block>();
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<Block> getFrameWithLocations(Set<Location> locations)
{
Set<Block> blocks = new HashSet<Block>();
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<Block> getGateFrameBlocks(Block block)
public static Set<Block> getGatePortalBlocks(Block block)
{
int frameBlockSearchLimit = Plugin.getPlugin().getConfig().getInt(Plugin.confMaxGateBlocksKey);