diff --git a/src/main/java/com/intellectualcrafters/jnbt/NBTInputStream.java b/src/main/java/com/intellectualcrafters/jnbt/NBTInputStream.java index ef73bb881..b01defb44 100644 --- a/src/main/java/com/intellectualcrafters/jnbt/NBTInputStream.java +++ b/src/main/java/com/intellectualcrafters/jnbt/NBTInputStream.java @@ -17,6 +17,8 @@ import java.util.Map; public final class NBTInputStream implements Closeable { private final DataInputStream is; + private int count; + /** * Creates a new {@code NBTInputStream}, which will source its data from the specified input stream. * @@ -36,7 +38,18 @@ public final class NBTInputStream implements Closeable { * @throws IOException if an I/O error occurs. */ public Tag readTag() throws IOException { - return readTag(0); + return readTag(0, Integer.MAX_VALUE); + } + + /** + * Reads an NBT tag from the stream. + * + * @return The tag that was read. + * + * @throws IOException if an I/O error occurs. + */ + public Tag readTag(int maxDepth) throws IOException { + return readTag(0, maxDepth); } /** @@ -48,7 +61,9 @@ public final class NBTInputStream implements Closeable { * * @throws IOException if an I/O error occurs. */ - private Tag readTag(final int depth) throws IOException { + private Tag readTag(final int depth, int maxDepth) throws IOException { + System.out.print("READING TAG!!!"); + if ((count++) > maxDepth) throw new IOException("Exceeds max depth: " + count); final int type = this.is.readByte() & 0xFF; String name; if (type != NBTConstants.TYPE_END) { @@ -59,7 +74,7 @@ public final class NBTInputStream implements Closeable { } else { name = ""; } - return readTagPayload(type, name, depth); + return readTagPayload(type, name, depth, maxDepth); } /** @@ -73,7 +88,10 @@ public final class NBTInputStream implements Closeable { * * @throws IOException if an I/O error occurs. */ - private Tag readTagPayload(final int type, final String name, final int depth) throws IOException { + private Tag readTagPayload(final int type, final String name, final int depth, int maxDepth) throws IOException { + if ((count++) > maxDepth) throw new IOException("Exceeds max depth: " + count); + count++; + System.out.print(count + " | " + type); switch (type) { case NBTConstants.TYPE_END: if (depth == 0) { @@ -95,20 +113,35 @@ public final class NBTInputStream implements Closeable { return new DoubleTag(name, this.is.readDouble()); case NBTConstants.TYPE_BYTE_ARRAY: int length = this.is.readInt(); + + // Max depth + if ((count += length) > maxDepth) throw new IOException("Exceeds max depth: " + count); + // + byte[] bytes = new byte[length]; this.is.readFully(bytes); return new ByteArrayTag(name, bytes); case NBTConstants.TYPE_STRING: length = this.is.readShort(); + + // Max depth + if ((count += length) > maxDepth) throw new IOException("Exceeds max depth: " + count); + // + bytes = new byte[length]; this.is.readFully(bytes); return new StringTag(name, new String(bytes, NBTConstants.CHARSET)); case NBTConstants.TYPE_LIST: final int childType = this.is.readByte(); length = this.is.readInt(); + + // Max depth + if ((count += length) > maxDepth) throw new IOException("Exceeds max depth: " + count); + // + final List tagList = new ArrayList(); for (int i = 0; i < length; ++i) { - final Tag tag = readTagPayload(childType, "", depth + 1); + final Tag tag = readTagPayload(childType, "", depth + 1, maxDepth); if (tag instanceof EndTag) { throw new IOException("TAG_End not permitted in a list."); } @@ -118,7 +151,7 @@ public final class NBTInputStream implements Closeable { case NBTConstants.TYPE_COMPOUND: final Map tagMap = new HashMap(); while (true) { - final Tag tag = readTag(depth + 1); + final Tag tag = readTag(depth + 1, maxDepth); if (tag instanceof EndTag) { break; } else { @@ -128,6 +161,9 @@ public final class NBTInputStream implements Closeable { return new CompoundTag(name, tagMap); case NBTConstants.TYPE_INT_ARRAY: length = this.is.readInt(); + // Max depth + if ((count += length) > maxDepth) throw new IOException("Exceeds max depth: " + count); + // final int[] data = new int[length]; for (int i = 0; i < length; i++) { data[i] = this.is.readInt(); diff --git a/src/main/java/com/intellectualcrafters/plot/PS.java b/src/main/java/com/intellectualcrafters/plot/PS.java index 38b713b3c..3196e1b9b 100644 --- a/src/main/java/com/intellectualcrafters/plot/PS.java +++ b/src/main/java/com/intellectualcrafters/plot/PS.java @@ -293,7 +293,6 @@ public class PS { if (!plots.containsKey(world)) { plots.put(world, new HashMap()); } - plot.hasChanged = true; plots.get(world).put(plot.id, plot); } diff --git a/src/main/java/com/intellectualcrafters/plot/commands/DebugClaimTest.java b/src/main/java/com/intellectualcrafters/plot/commands/DebugClaimTest.java index de467d6d2..452ce03cc 100644 --- a/src/main/java/com/intellectualcrafters/plot/commands/DebugClaimTest.java +++ b/src/main/java/com/intellectualcrafters/plot/commands/DebugClaimTest.java @@ -124,7 +124,6 @@ public class DebugClaimTest extends SubCommand { if (uuid != null) { MainUtil.sendMessage(null, " - &aFound plot: " + plot.id + " : " + line); plot.owner = uuid; - plot.hasChanged = true; plots.add(plot); } else { MainUtil.sendMessage(null, " - &cInvalid playername: " + plot.id + " : " + line); diff --git a/src/main/java/com/intellectualcrafters/plot/commands/Download.java b/src/main/java/com/intellectualcrafters/plot/commands/Download.java index e7a49088e..60af149da 100644 --- a/src/main/java/com/intellectualcrafters/plot/commands/Download.java +++ b/src/main/java/com/intellectualcrafters/plot/commands/Download.java @@ -44,7 +44,7 @@ public class Download extends SubCommand { TaskManager.runTaskAsync(new Runnable() { @Override public void run() { - URL url = SchematicHandler.manager.upload(value); + URL url = SchematicHandler.manager.upload(value, null); if (url == null) { MainUtil.sendMessage(plr, C.GENERATING_LINK_FAILED); MainUtil.runners.remove(plot); diff --git a/src/main/java/com/intellectualcrafters/plot/commands/SchematicCmd.java b/src/main/java/com/intellectualcrafters/plot/commands/SchematicCmd.java index 6a11db9c9..e02fbc016 100644 --- a/src/main/java/com/intellectualcrafters/plot/commands/SchematicCmd.java +++ b/src/main/java/com/intellectualcrafters/plot/commands/SchematicCmd.java @@ -20,12 +20,16 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// package com.intellectualcrafters.plot.commands; +import java.io.File; +import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.UUID; import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.config.C; +import com.intellectualcrafters.plot.config.Settings; import com.intellectualcrafters.plot.object.Location; import com.intellectualcrafters.plot.object.Plot; import com.intellectualcrafters.plot.object.PlotId; @@ -84,13 +88,30 @@ public class SchematicCmd extends SubCommand { MainUtil.sendMessage(plr, "&cTask is already running."); return false; } - final String file2 = args[1]; + final String location = args[1]; this.running = true; this.counter = 0; TaskManager.runTaskAsync(new Runnable() { @Override public void run() { - final Schematic schematic = SchematicHandler.manager.getSchematic(file2); + Schematic schematic; + if (location.startsWith("url:")) { + try { + UUID uuid = UUID.fromString(location.substring(4)); + URL base = new URL(Settings.WEB_URL); + URL url = new URL(base, "uploads/" + uuid + ".schematic"); + schematic = SchematicHandler.manager.getSchematic(url); + } + catch (Exception e) { + e.printStackTrace(); + sendMessage(plr, C.SCHEMATIC_INVALID, "non-existent url: " + location); + SchematicCmd.this.running = false; + return; + } + } + else { + schematic = SchematicHandler.manager.getSchematic(location); + } if (schematic == null) { SchematicCmd.this.running = false; sendMessage(plr, C.SCHEMATIC_INVALID, "non-existent or not in gzip format"); diff --git a/src/main/java/com/intellectualcrafters/plot/object/Plot.java b/src/main/java/com/intellectualcrafters/plot/object/Plot.java index 226245341..53ec57222 100644 --- a/src/main/java/com/intellectualcrafters/plot/object/Plot.java +++ b/src/main/java/com/intellectualcrafters/plot/object/Plot.java @@ -52,30 +52,37 @@ public class Plot { * plot owner */ public UUID owner; + /** * List of trusted (with plot permissions) + * Direct access is Deprecated: use getTrusted() */ + @Deprecated public HashSet trusted; /** * List of members users (with plot permissions) + * Direct access is Deprecated: use getMembers() */ + @Deprecated public HashSet members; /** * List of denied players + * Direct access is Deprecated: use getDenied() */ + @Deprecated public HashSet denied; /** * External settings class
* - Please favor the methods over direct access to this class
* - The methods are more likely to be left unchanged from version changes
+ * Direct access is Deprecated: use getSettings() */ + @Deprecated public PlotSettings settings; /** * Has the plot changed since the last save cycle? */ - public boolean hasChanged = false; public boolean countsTowardsMax = true; - /** * If this plot is temporary i.e. not stored in the DB */ diff --git a/src/main/java/com/intellectualcrafters/plot/util/BukkitSchematicHandler.java b/src/main/java/com/intellectualcrafters/plot/util/BukkitSchematicHandler.java index d99ea8db9..63f40e158 100644 --- a/src/main/java/com/intellectualcrafters/plot/util/BukkitSchematicHandler.java +++ b/src/main/java/com/intellectualcrafters/plot/util/BukkitSchematicHandler.java @@ -77,6 +77,8 @@ public class BukkitSchematicHandler extends SchematicHandler { final int height = (pos2.getY() - pos1.getY()) + 1; final int length = (pos2.getZ() - pos1.getZ()) + 1; + System.out.print(width + "," + height + "," + length); + // Main Schematic tag final HashMap schematic = new HashMap<>(); @@ -116,6 +118,7 @@ public class BukkitSchematicHandler extends SchematicHandler { Chunk bc = worldObj.getChunkAt(chunk.x, chunk.z); if (!bc.load(false)) { + System.out.print("FAILED TO LOAD CHUNK: " + chunk.x + "," + chunk.z); continue; } diff --git a/src/main/java/com/intellectualcrafters/plot/util/SchematicHandler.java b/src/main/java/com/intellectualcrafters/plot/util/SchematicHandler.java index 7dffacbd9..559c83a35 100644 --- a/src/main/java/com/intellectualcrafters/plot/util/SchematicHandler.java +++ b/src/main/java/com/intellectualcrafters/plot/util/SchematicHandler.java @@ -11,6 +11,8 @@ import java.io.PrintWriter; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -33,6 +35,7 @@ import com.intellectualcrafters.jnbt.StringTag; import com.intellectualcrafters.jnbt.Tag; import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.config.Settings; +import com.intellectualcrafters.plot.object.ChunkLoc; import com.intellectualcrafters.plot.object.Location; import com.intellectualcrafters.plot.object.Plot; import com.intellectualcrafters.plot.object.PlotBlock; @@ -142,11 +145,9 @@ public abstract class SchematicHandler { * @return boolean true if succeeded */ public void paste(final Schematic schematic, final Plot plot, final int x_offset, final int z_offset, final RunnableVal whenDone) { - System.out.print(1); TaskManager.runTaskAsync(new Runnable() { @Override public void run() { - System.out.print(2); if (whenDone != null) whenDone.value = false; if (schematic == null) { PS.log("Schematic == null :|"); @@ -154,147 +155,209 @@ public abstract class SchematicHandler { return; } try { - System.out.print(3); final Dimension demensions = schematic.getSchematicDimension(); final int WIDTH = demensions.getX(); final int LENGTH = demensions.getZ(); final int HEIGHT = demensions.getY(); - byte[] ids = schematic.ids; - byte[] datas = schematic.datas; + // Validate dimensions + Location bottom = plot.getBottom(); + Location top = plot.getTop(); + if (top.getX() - bottom.getX() < WIDTH || top.getZ() - bottom.getZ() < LENGTH || HEIGHT > 256) { + System.out.print((top.getX() - bottom.getX() + 1) + "," + (top.getZ() - bottom.getZ() + 1)); + System.out.print(WIDTH + "," + HEIGHT + "," + LENGTH); + PS.log("Schematic is too large"); + TaskManager.runTask(whenDone); + return; + } - int y_offset; + final byte[] ids = schematic.ids; + final byte[] datas = schematic.datas; + + final int y_offset; if (HEIGHT >= 256) { - y_offset = -1; + y_offset = 0; } else { - y_offset = BukkitUtil.getMaxHeight(plot.world) - 1; + y_offset = BukkitUtil.getMaxHeight(plot.world); } - Location bottom = MainUtil.getPlotBottomLoc(plot.world, plot.id).add(1 + x_offset, y_offset, 1 + z_offset); + Location pos1 = MainUtil.getPlotBottomLoc(plot.world, plot.id).add(1 + x_offset, y_offset - 1, 1 + z_offset); + Location pos2 = pos1.clone().add(WIDTH - 1, HEIGHT - 1, LENGTH - 1); - int X = bottom.getX(); - int Y = bottom.getY(); - int Z = bottom.getZ(); + final int p1x = pos1.getX(); + final int p1z = pos1.getZ(); + final int p2x = pos2.getX(); + final int p2z = pos2.getZ(); + final int bcx = p1x >> 4; + final int bcz = p1z >> 4; + final int tcx = p2x >> 4; + final int tcz = p2z >> 4; - int id; + final ArrayList chunks = new ArrayList(); - System.out.print(4); - - System.out.print("HEIGHT: " + HEIGHT); - - for (int y = 0; y < Math.max(256, HEIGHT); y++) { - int i1 = y * WIDTH * LENGTH; - for (int z = 0; z < LENGTH; z++) { - int i2 = z * WIDTH + i1; - for (int x = 0; x < WIDTH; x++) { - int i = i2 + x; - id = ids[i]; - switch(id) { - case 0: - case 2: - case 4: - case 13: - case 14: - case 15: - case 20: - case 21: - case 22: - case 24: - case 30: - case 32: - case 37: - case 39: - case 40: - case 41: - case 42: - case 45: - case 46: - case 47: - case 48: - case 49: - case 50: - case 51: - case 55: - case 56: - case 57: - case 58: - case 60: - case 7: - case 8: - case 9: - case 10: - case 11: - case 73: - case 74: - case 75: - case 76: - case 78: - case 79: - case 80: - case 81: - case 82: - case 83: - case 85: - case 87: - case 88: - case 101: - case 102: - case 103: - case 110: - case 112: - case 113: - case 121: - case 122: - case 129: - case 133: - case 165: - case 166: - case 169: - case 170: - case 172: - case 173: - case 174: - case 181: - case 182: - case 188: - case 189: - case 190: - case 191: - case 192: { - SetBlockQueue.setBlock(plot.world, X + x, Y + y, Z + z, id); - break; - } - default: { - SetBlockQueue.setBlock(plot.world, X + x, Y + y, Z + z, new PlotBlock((short) id, (byte) datas[i])); - break; - } - } - // set block - } + for (int x = bcx; x <= tcx; x++) { + for (int z = bcz; z <= tcz; z++) { + chunks.add(new ChunkLoc(x, z)); } } - System.out.print(5); + System.out.print("chunks: " + chunks.size()); - - SetBlockQueue.addNotify(new Runnable() { + TaskManager.runTaskAsync(new Runnable() { @Override public void run() { - - System.out.print(6); - - pasteStates(schematic, plot, x_offset, z_offset); - - System.out.print(7); - - if (whenDone != null) { - whenDone.value = true; - whenDone.run(); + int count = 0; + System.out.print("RUNNING: " + chunks.size()); + while (chunks.size() > 0 && count < 256) { + count++; + ChunkLoc chunk = chunks.remove(0); + int x = chunk.x; + int z = chunk.z; + int xxb = x << 4; + int zzb = z << 4; + int xxt = xxb + 15; + int zzt = zzb + 15; + if (x == bcx) { + xxb = p1x; + } + if (x == tcx) { + xxt = p2x; + } + if (z == bcz) { + zzb = p1z; + } + if (z == tcz) { + zzt = p2z; + } + // Paste schematic here + int id; + + for (int ry = 0; ry < Math.max(256, HEIGHT); ry++) { + int i1 = ry * WIDTH * LENGTH; + for (int rz = zzb - p1z; rz <= zzt - p1z; rz++) { + int i2 = rz * WIDTH + i1; + for (int rx = xxb - p1x; rx <= xxt - p1x; rx++) { + int i = i2 + rx; + + int xx = p1x + rx; + int zz = p1z + rz; + int yy = y_offset + ry; + + id = ids[i]; + + switch(id) { + case 0: + case 2: + case 4: + case 13: + case 14: + case 15: + case 20: + case 21: + case 22: + case 24: + case 30: + case 32: + case 37: + case 39: + case 40: + case 41: + case 42: + case 45: + case 46: + case 47: + case 48: + case 49: + case 50: + case 51: + case 55: + case 56: + case 57: + case 58: + case 60: + case 7: + case 8: + case 9: + case 10: + case 11: + case 73: + case 74: + case 75: + case 76: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 85: + case 87: + case 88: + case 101: + case 102: + case 103: + case 110: + case 112: + case 113: + case 121: + case 122: + case 129: + case 133: + case 165: + case 166: + case 169: + case 170: + case 172: + case 173: + case 174: + case 181: + case 182: + case 188: + case 189: + case 190: + case 191: + case 192: { + SetBlockQueue.setBlock(plot.world, xx, yy, zz, id); + break; + } + default: { + SetBlockQueue.setBlock(plot.world, xx, yy, zz, new PlotBlock((short) id, (byte) datas[i])); + break; + } + } + } + } + } + } + if (chunks.size() != 0) { + final Runnable task = this; + // Run when the queue is free + SetBlockQueue.addNotify(new Runnable() { + @Override + public void run() { + System.gc(); + TaskManager.runTaskLaterAsync(task, 80); + } + }); + } + else { + System.gc(); + // Finished + SetBlockQueue.addNotify(new Runnable() { + @Override + public void run() { + pasteStates(schematic, plot, x_offset, z_offset); + if (whenDone != null) { + whenDone.value = true; + whenDone.run(); + } + } + }); + return; } } }); - return; } catch (final Exception e) { e.printStackTrace(); TaskManager.runTask(whenDone); @@ -334,7 +397,7 @@ public abstract class SchematicHandler { return true; } - public Schematic getSchematic(final CompoundTag tag, final File file) { + public Schematic getSchematic(final CompoundTag tag) { final Map tagMap = tag.getValue(); // Slow // byte[] addId = new byte[0]; @@ -369,7 +432,7 @@ public abstract class SchematicHandler { // Schematic schem = new Schematic(collection, dimension, file); Dimension dimensions = new Dimension(width, height, length); - Schematic schem = new Schematic(block, data, dimensions , file); + Schematic schem = new Schematic(block, data, dimensions); // Slow try { @@ -422,25 +485,58 @@ public abstract class SchematicHandler { return null; } try { - final InputStream iStream = new FileInputStream(file); - final NBTInputStream stream = new NBTInputStream(new GZIPInputStream(iStream)); - final CompoundTag tag = (CompoundTag) stream.readTag(); - stream.close(); - return getSchematic(tag, file); + return getSchematic(new FileInputStream(file)); } catch (final Exception e) { - PS.log(file.toString() + " is not in GZIP format"); - return null; + e.printStackTrace(); } + return null; } - public URL upload(final CompoundTag tag) { + public Schematic getSchematic(URL url) { + try { + ReadableByteChannel rbc = Channels.newChannel(url.openStream()); + InputStream is = Channels.newInputStream(rbc); + return getSchematic(is); +// fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + } catch (final Exception e) { + e.printStackTrace(); + } + return null; + } + + public Schematic getSchematic(InputStream is) { + if (is == null) { + System.out.print("SCHEM IS NULL!!!!"); + return null; + } + try { + final NBTInputStream stream = new NBTInputStream(new GZIPInputStream(is)); + final CompoundTag tag = (CompoundTag) stream.readTag(1073741824); + is.close(); + stream.close(); + return getSchematic(tag); + } + catch (Exception e) { + e.printStackTrace(); + PS.log(is.toString() + " | " + is.getClass().getCanonicalName() + " is not in GZIP format : " + e.getMessage()); + } + return null; + } + + public URL upload(final CompoundTag tag, UUID uuid) { if (tag == null) { PS.log("&cCannot save empty tag"); return null; } try { - UUID uuid = UUID.randomUUID(); - String website = Settings.WEB_URL + "upload.php?" + uuid; + String website; + if (uuid == null) { + uuid = UUID.randomUUID(); + website = Settings.WEB_URL + "upload.php?" + uuid; + } + else { + website = Settings.WEB_URL + "save.php?" + uuid; + } String charset = "UTF-8"; String param = "value"; String boundary = Long.toHexString(System.currentTimeMillis()); @@ -632,38 +728,39 @@ public abstract class SchematicHandler { private DataCollection[] collection; private final Dimension schematicDimension; - private final File file; private HashSet items; /** - * This is deprecated as having a wrapper for each block is really slow.
- * - There's also a performance hit by having the cast the DataCollection short / byte + * This is deprecated as having a wrapper for each block is slow.
+ * - There's also a performance hit by having to cast the DataCollection short / byte * - * @param blockCollection * @param schematicDimension * @param file */ -// @Deprecated -// public Schematic(final DataCollection[] blockCollection, final Dimension schematicDimension, final File file) { -// ids = new byte[blockCollection.length]; -// datas = new byte[blockCollection.length]; -// for (int i = 0; i < blockCollection.length; i++) { -// DataCollection block = blockCollection[i]; -// ids[i] = (byte) block.block; -// datas[i] = block.data; -// } -// this.collection = blockCollection; -// this.schematicDimension = schematicDimension; -// this.file = file; -// } + @Deprecated + public Schematic(final DataCollection[] blockCollection, final Dimension schematicDimension) { + ids = new byte[blockCollection.length]; + datas = new byte[blockCollection.length]; + for (int i = 0; i < blockCollection.length; i++) { + DataCollection block = blockCollection[i]; + ids[i] = (byte) block.block; + datas[i] = block.data; + } + this.collection = blockCollection; + this.schematicDimension = schematicDimension; + } - public Schematic(final byte[] i, final byte[] b, final Dimension d, final File f) { + public Schematic(final byte[] i, final byte[] b, final Dimension d) { ids = i; datas = b; schematicDimension = d; - file = f; } + /** + * Add an item to the schematic + * @param item + */ public void addItem(PlotItem item) { if (this.items == null) { this.items = new HashSet<>(); @@ -671,36 +768,52 @@ public abstract class SchematicHandler { items.add(item); } + /** + * Get any items associated with this schematic + * @return + */ public HashSet getItems() { return this.items; } - public File getFile() { - return this.file; - } - + /** + * Get the schematic dimensions + * @return + */ public Dimension getSchematicDimension() { return this.schematicDimension; } + /** + * Get the block id array + * @return + */ public byte[] getIds() { return ids; } + /** + * Get the block data array + * @return + */ public byte[] getDatas() { return datas; } -// @Deprecated -// public DataCollection[] getBlockCollection() { -// if (this.collection == null) { -// collection = new DataCollection[ids.length]; -// for (int i = 0; i < ids.length; i++) { -// collection[i] = new DataCollection(ids[i], datas[i]); -// } -// } -// return this.collection; -// } + /** + * @deprecated as it is slow to wrap each block + * @return DataCollection of schematic blocks + */ + @Deprecated + public DataCollection[] getBlockCollection() { + if (this.collection == null) { + collection = new DataCollection[ids.length]; + for (int i = 0; i < ids.length; i++) { + collection[i] = new DataCollection(ids[i], datas[i]); + } + } + return this.collection; + } } /** @@ -734,9 +847,10 @@ public abstract class SchematicHandler { /** * Schematic Data Collection - * + * @deprecated as it is slow to wrap each block * @author Citymonstret */ + @Deprecated public class DataCollection { private final short block; private final byte data; diff --git a/src/main/java/com/intellectualcrafters/plot/util/SetBlockQueue.java b/src/main/java/com/intellectualcrafters/plot/util/SetBlockQueue.java index d73259a2d..9eaa86987 100644 --- a/src/main/java/com/intellectualcrafters/plot/util/SetBlockQueue.java +++ b/src/main/java/com/intellectualcrafters/plot/util/SetBlockQueue.java @@ -68,6 +68,7 @@ public class SetBlockQueue { TaskManager.runTask(runnable); } } + lastBlock = null; runnables = null; blocks = new HashMap<>(); running = false;