diff --git a/build.gradle b/build.gradle
index a30f2435..603c999f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -18,20 +18,48 @@ group = 'com.boydti.fawe'
def revision = ""
def buildNumber = ""
-final def date = new Date().format("yy.MM.dd")
+def semver = ""
+def date = ""
ext {
git = org.ajoberstar.grgit.Grgit.open(file(".git"))
+ date = git.head().date.format("yy.MM.dd")
revision = "-${git.head().abbreviatedId}"
parents = git.head().parentIds;
index = -45; // Offset to mach CI
+ int major, minor, patch;
+ major = minor = patch = 0;
for (;parents != null && !parents.isEmpty();index++) {
+ int majorCount, minorCount, patchCount;
+ patchCount = 1;
commit = git.getResolve().toCommit(parents.get(0));
- parents = commit.getParentIds();
+ for (String line : commit.fullMessage.tokenize("\n")) {
+ switch (line.replaceAll("- ", "").split(" ")[0].toLowerCase()) {
+ case "minor":
+ case "added":
+ case "add":
+ case "change":
+ case "changed":
+ case "changes":
+ if (majorCount == 0) {minorCount = 1; patch = patchCount = 0;}
+ break;
+ case "refactor":
+ case "remove":
+ case "major":
+ patch = minor = minorCount = patchCount = 0;
+ majorCount = 1;
+ break;
+ }
+ }
+ major += majorCount;
+ minor += minorCount;
+ patch += patchCount;
+ parents = commit.getParentIds()
}
buildNumber = "-${index}"
+ semver = "-${major}.${minor}.${patch}"
}
-version = date + revision + buildNumber
+version = date + revision + buildNumber + semver
if ( project.hasProperty("lzNoVersion") ) { // gradle build -PlzNoVersion
version = "unknown";
}
diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java
index 5c0f8a13..0dcb7987 100644
--- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java
+++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java
@@ -3,6 +3,7 @@ package com.boydti.fawe.bukkit.wrapper;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.object.FaweQueue;
+import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.StringMan;
@@ -57,7 +58,7 @@ import org.bukkit.util.Vector;
* @see #wrap(org.bukkit.World)
* @see #create(org.bukkit.WorldCreator)
*/
-public class AsyncWorld implements World {
+public class AsyncWorld implements World, HasFaweQueue {
private World parent;
private FaweQueue queue;
diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java
index 397cb4ef..21987150 100644
--- a/core/src/main/java/com/boydti/fawe/Fawe.java
+++ b/core/src/main/java/com/boydti/fawe/Fawe.java
@@ -33,10 +33,12 @@ import com.sk89q.worldedit.command.RegionCommands;
import com.sk89q.worldedit.command.SchematicCommands;
import com.sk89q.worldedit.command.ScriptingCommands;
import com.sk89q.worldedit.command.ToolCommands;
+import com.sk89q.worldedit.command.ToolUtilCommands;
import com.sk89q.worldedit.command.composition.SelectionCommand;
import com.sk89q.worldedit.command.tool.LongRangeBuildTool;
import com.sk89q.worldedit.command.tool.brush.GravityBrush;
import com.sk89q.worldedit.event.extent.EditSessionEvent;
+import com.sk89q.worldedit.extension.factory.DefaultMaskParser;
import com.sk89q.worldedit.extension.platform.CommandManager;
import com.sk89q.worldedit.extension.platform.PlatformManager;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
@@ -48,6 +50,8 @@ import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
import com.sk89q.worldedit.function.entity.ExtentEntityCopy;
import com.sk89q.worldedit.function.mask.BlockMask;
import com.sk89q.worldedit.function.mask.FuzzyBlockMask;
+import com.sk89q.worldedit.function.mask.Masks;
+import com.sk89q.worldedit.function.mask.OffsetMask;
import com.sk89q.worldedit.function.mask.SolidBlockMask;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.pattern.ClipboardPattern;
@@ -337,6 +341,7 @@ public class Fawe {
HistoryCommands.inject(); // Translations
NavigationCommands.inject(); // Translations + thru fix
ParametricBuilder.inject(); // Translations
+ ToolUtilCommands.inject(); // Fixes + Translations
// Schematic
SchematicReader.inject();
SchematicWriter.inject();
@@ -379,6 +384,9 @@ public class Fawe {
BlockMask.inject(); // Optimizations
SolidBlockMask.inject(); // Optimizations
FuzzyBlockMask.inject(); // Optimizations
+ OffsetMask.inject(); // Optimizations
+ DefaultMaskParser.inject(); // Add new masks
+ Masks.inject(); //
// Operations
Operations.inject(); // Optimizations
// BlockData
diff --git a/core/src/main/java/com/boydti/fawe/FaweVersion.java b/core/src/main/java/com/boydti/fawe/FaweVersion.java
index 56f6e218..3e5b7a07 100644
--- a/core/src/main/java/com/boydti/fawe/FaweVersion.java
+++ b/core/src/main/java/com/boydti/fawe/FaweVersion.java
@@ -1,7 +1,7 @@
package com.boydti.fawe;
public class FaweVersion {
- public final int year, month, day, hash, build;
+ public final int year, month, day, hash, build, major, minor, patch;
public FaweVersion(String version) {
String[] split = version.substring(version.indexOf('=') + 1).split("-");
@@ -11,6 +11,10 @@ public class FaweVersion {
this.day = Integer.parseInt(date[2]);
this.hash = Integer.parseInt(split[1], 16);
this.build = Integer.parseInt(split[2]);
+ String[] semver = split[3].split("\\.");
+ this.major = Integer.parseInt(semver[0]);
+ this.minor = Integer.parseInt(semver[1]);
+ this.patch = Integer.parseInt(semver[2]);
}
@Override
diff --git a/core/src/main/java/com/boydti/fawe/config/BBC.java b/core/src/main/java/com/boydti/fawe/config/BBC.java
index 2460cb5f..1d6886bb 100644
--- a/core/src/main/java/com/boydti/fawe/config/BBC.java
+++ b/core/src/main/java/com/boydti/fawe/config/BBC.java
@@ -95,7 +95,11 @@ public enum BBC {
BRUSH_BLEND_BALL("Blend ball brush equipped (%s0).", "WorldEdit.Brush"),
BRUSH_ERODE("Erode brush equipped (%s0).", "WorldEdit.Brush"),
BRUSH_PASTE_NONE("Nothing to paste", "WorldEdit.Brush"),
-
+ BRUSH_SIZE("Brush size set", "WorldEdit.Brush"),
+ BRUSH_RANGE("Brush size set", "WorldEdit.Brush"),
+ BRUSH_MASK_DISABLED("Brush mask disabled", "WorldEdit.Brush"),
+ BRUSH_MASK("Brush mask set", "WorldEdit.Brush"),
+ BRUSH_MATERIAL("Brush material set", "WorldEdit.Brush"),
ROLLBACK_ELEMENT("Undoing %s0", "WorldEdit.Rollback"),
@@ -114,6 +118,8 @@ public enum BBC {
TOOL_FARWAND("Far wand tool bound to %s0.", "WorldEdit.Tool"),
TOOL_LRBUILD_BOUND("Long-range building tool bound to %s0.", "WorldEdit.Tool"),
TOOL_LRBUILD_INFO("Left-click set to %s0; right-click set to %s1.", "WorldEdit.Tool"),
+ SUPERPICKAXE_ENABLED("Super Pickaxe enabled.", "WorldEdit.Tool"),
+ SUPERPICKAXE_DISABLED("Super Pickaxe disabled.", "WorldEdit.Tool"),
SCHEMATIC_DELETE("%s0 has been deleted.", "Worldedit.Schematic"),
diff --git a/core/src/main/java/com/boydti/fawe/config/Settings.java b/core/src/main/java/com/boydti/fawe/config/Settings.java
index c85b5276..bc3f09a7 100644
--- a/core/src/main/java/com/boydti/fawe/config/Settings.java
+++ b/core/src/main/java/com/boydti/fawe/config/Settings.java
@@ -154,6 +154,17 @@ public class Settings extends Config {
" - Use of the FAWE API will not be effected"
})
public static boolean ENABLE_FOR_CONSOLE = true;
+ @Comment({
+ "Should redo information be stored:",
+ " - History is about 20% larger",
+ " - Enables use of /redo",
+ })
+ public static boolean STORE_REDO = true;
+ @Comment({
+ "Assumes all edits are smaller than 4096x256x4096:",
+ " - Reduces history size by ~10%",
+ })
+ public static boolean SMALL_EDITS = false;
}
public static class QUEUE {
diff --git a/core/src/main/java/com/boydti/fawe/logging/rollback/RollbackOptimizedHistory.java b/core/src/main/java/com/boydti/fawe/logging/rollback/RollbackOptimizedHistory.java
index ad77c35e..d7f3fbe6 100644
--- a/core/src/main/java/com/boydti/fawe/logging/rollback/RollbackOptimizedHistory.java
+++ b/core/src/main/java/com/boydti/fawe/logging/rollback/RollbackOptimizedHistory.java
@@ -6,6 +6,7 @@ import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.world.World;
import java.io.IOException;
+import java.io.OutputStream;
import java.util.UUID;
public class RollbackOptimizedHistory extends DiskStorageHistory {
@@ -101,13 +102,13 @@ public class RollbackOptimizedHistory extends DiskStorageHistory {
}
@Override
- public void writeHeader(int x, int y, int z) throws IOException {
+ public void writeHeader(OutputStream os, int x, int y, int z) throws IOException {
minX = x;
maxX = x;
minY = y;
maxY = y;
minZ = z;
maxZ = z;
- super.writeHeader(x, y, z);
+ super.writeHeader(os, x, y, z);
}
}
diff --git a/core/src/main/java/com/boydti/fawe/object/FaweLimit.java b/core/src/main/java/com/boydti/fawe/object/FaweLimit.java
index ad1d59a1..4781cd98 100644
--- a/core/src/main/java/com/boydti/fawe/object/FaweLimit.java
+++ b/core/src/main/java/com/boydti/fawe/object/FaweLimit.java
@@ -76,6 +76,26 @@ public class FaweLimit {
return MAX_ENTITIES-- > 0;
}
+ public boolean isUnlimited() {
+ return MAX_CHANGES == Integer.MAX_VALUE &&
+ MAX_FAILS == Integer.MAX_VALUE &&
+ MAX_CHECKS == Integer.MAX_VALUE &&
+ MAX_ITERATIONS == Integer.MAX_VALUE &&
+ MAX_BLOCKSTATES == Integer.MAX_VALUE &&
+ MAX_ENTITIES == Integer.MAX_VALUE &&
+ MAX_HISTORY == Integer.MAX_VALUE;
+ }
+
+ public void set(FaweLimit limit) {
+ MAX_ACTIONS = limit.MAX_ACTIONS;
+ MAX_CHANGES = limit.MAX_CHANGES;
+ MAX_BLOCKSTATES = limit.MAX_BLOCKSTATES;
+ MAX_CHECKS = limit.MAX_CHECKS;
+ MAX_ENTITIES = limit.MAX_ENTITIES;
+ MAX_FAILS = limit.MAX_FAILS;
+ MAX_ITERATIONS = limit.MAX_ITERATIONS;
+ MAX_HISTORY = limit.MAX_HISTORY;
+ }
public FaweLimit copy() {
FaweLimit limit = new FaweLimit();
diff --git a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java
index fa4f6b2e..ed4c2209 100644
--- a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java
+++ b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java
@@ -145,7 +145,7 @@ public abstract class FawePlayer {
ConcurrentLinkedDeque adder = getMeta("fawe_action_v2");
if (adder == null) {
adder = new ConcurrentLinkedDeque();
- ConcurrentLinkedDeque previous = (ConcurrentLinkedDeque) setMeta("fawe_action_v2", adder);
+ ConcurrentLinkedDeque previous = (ConcurrentLinkedDeque) getAndSetMeta("fawe_action_v2", adder);
if (previous != null) {
setMeta("fawe_action_v2", adder = previous);
}
@@ -397,11 +397,18 @@ public abstract class FawePlayer {
* @param value
* @return previous value
*/
- public Object setMeta(String key, Object value) {
+ public void setMeta(String key, Object value) {
if (this.meta == null) {
this.meta = new ConcurrentHashMap<>(8, 0.9f, 1);
}
- return this.meta.put(key, value);
+ this.meta.put(key, value);
+ }
+
+ public T getAndSetMeta(String key, T value) {
+ if (this.meta == null) {
+ this.meta = new ConcurrentHashMap<>(8, 0.9f, 1);
+ }
+ return (T) this.meta.put(key, value);
}
/**
diff --git a/core/src/main/java/com/boydti/fawe/object/HasFaweQueue.java b/core/src/main/java/com/boydti/fawe/object/HasFaweQueue.java
new file mode 100644
index 00000000..8ad5b840
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/HasFaweQueue.java
@@ -0,0 +1,5 @@
+package com.boydti.fawe.object;
+
+public interface HasFaweQueue {
+ FaweQueue getQueue();
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/brush/RecurseBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/RecurseBrush.java
new file mode 100644
index 00000000..3df2784d
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/brush/RecurseBrush.java
@@ -0,0 +1,56 @@
+package com.boydti.fawe.object.brush;
+
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.MaxChangedBlocksException;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.blocks.BaseBlock;
+import com.sk89q.worldedit.command.tool.BrushTool;
+import com.sk89q.worldedit.command.tool.brush.Brush;
+import com.sk89q.worldedit.function.block.BlockReplace;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Masks;
+import com.sk89q.worldedit.function.operation.Operations;
+import com.sk89q.worldedit.function.pattern.Pattern;
+import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
+
+public class RecurseBrush implements Brush {
+
+ private final BrushTool tool;
+
+ public RecurseBrush(BrushTool tool) {
+ this.tool = tool;
+ }
+
+ @Override
+ public void build(final EditSession editSession, final Vector position, Pattern to, double size) throws MaxChangedBlocksException {
+ Mask mask = tool.getMask();
+ if (mask == null) {
+ mask = Masks.alwaysTrue();
+ }
+ final int radius = (int) size;
+ BaseBlock block = editSession.getBlock(position);
+ if (block.getId() == 0) {
+ return;
+ }
+ final BlockReplace replace = new BlockReplace(editSession, to);
+ editSession.setMask((Mask) null);
+ RecursiveVisitor visitor = new RecursiveVisitor(mask, replace) {
+ @Override
+ public boolean isVisitable(Vector from, Vector to) {
+ if (super.isVisitable(from, to)) {
+ int dx = Math.abs((int) (position.x - to.x));
+ if (dx > radius) return false;
+ int dz = Math.abs((int) (position.z - to.z));
+ if (dz > radius) return false;
+ int dy = Math.abs((int) (position.y - to.y));
+ if (dy > radius) return false;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ };
+ visitor.visit(position);
+ Operations.completeBlindly(visitor);
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/change/MutableBlockChange.java b/core/src/main/java/com/boydti/fawe/object/change/MutableBlockChange.java
index d23cc258..d058d766 100644
--- a/core/src/main/java/com/boydti/fawe/object/change/MutableBlockChange.java
+++ b/core/src/main/java/com/boydti/fawe/object/change/MutableBlockChange.java
@@ -1,8 +1,8 @@
package com.boydti.fawe.object.change;
import com.boydti.fawe.Fawe;
-import com.boydti.fawe.object.extent.FastWorldEditExtent;
-import com.boydti.fawe.util.ExtentTraverser;
+import com.boydti.fawe.object.FaweQueue;
+import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.history.UndoContext;
@@ -35,14 +35,21 @@ public class MutableBlockChange implements Change {
create(context);
}
+ private FaweQueue queue;
+ private boolean checkedQueue;
+
public void create(UndoContext context) {
- Extent extent = context.getExtent();
- ExtentTraverser find = new ExtentTraverser(extent).find(FastWorldEditExtent.class);
- if (find != null) {
- FastWorldEditExtent fwee = find.get();
- fwee.getQueue().setBlock(x, y, z, id, data);
- } else {
- Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)");
+ if (queue != null) {
+ queue.setBlock(x, y, z, id, data);
+ }
+ if (!checkedQueue) {
+ checkedQueue = true;
+ Extent extent = context.getExtent();
+ if (extent instanceof HasFaweQueue) {
+ (queue = ((HasFaweQueue) extent).getQueue()).setBlock(x, y, z, id, data);
+ } else {
+ Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)");
+ }
}
}
}
diff --git a/core/src/main/java/com/boydti/fawe/object/change/MutableChunkChange.java b/core/src/main/java/com/boydti/fawe/object/change/MutableChunkChange.java
index 77e638dd..65eee4a6 100644
--- a/core/src/main/java/com/boydti/fawe/object/change/MutableChunkChange.java
+++ b/core/src/main/java/com/boydti/fawe/object/change/MutableChunkChange.java
@@ -2,8 +2,8 @@ package com.boydti.fawe.object.change;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.FaweChunk;
-import com.boydti.fawe.object.extent.FastWorldEditExtent;
-import com.boydti.fawe.util.ExtentTraverser;
+import com.boydti.fawe.object.FaweQueue;
+import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.history.UndoContext;
@@ -29,18 +29,29 @@ public class MutableChunkChange implements Change {
create(context, false);
}
+ private FaweQueue queue;
+ private boolean checkedQueue;
+
public void create(UndoContext context, boolean undo) {
- Extent extent = context.getExtent();
- ExtentTraverser find = new ExtentTraverser(extent).find(FastWorldEditExtent.class);
- if (find != null) {
- FastWorldEditExtent fwee = find.get();
- if (undo) {
- fwee.getQueue().setChunk(from);
+ if (queue != null) {
+ perform(queue, undo);
+ }
+ if (!checkedQueue) {
+ checkedQueue = true;
+ Extent extent = context.getExtent();
+ if (extent instanceof HasFaweQueue) {
+ perform(queue = ((HasFaweQueue) extent).getQueue(), undo);
} else {
- fwee.getQueue().setChunk(to);
+ Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)");
}
+ }
+ }
+
+ public void perform(FaweQueue queue, boolean undo) {
+ if (undo) {
+ queue.setChunk(from);
} else {
- Fawe.debug("FAWE doesn't support: " + context + " for " + getClass());
+ queue.setChunk(to);
}
}
}
diff --git a/core/src/main/java/com/boydti/fawe/object/change/MutableEntityChange.java b/core/src/main/java/com/boydti/fawe/object/change/MutableEntityChange.java
index bfd4ff95..aa9fb664 100644
--- a/core/src/main/java/com/boydti/fawe/object/change/MutableEntityChange.java
+++ b/core/src/main/java/com/boydti/fawe/object/change/MutableEntityChange.java
@@ -1,6 +1,8 @@
package com.boydti.fawe.object.change;
import com.boydti.fawe.Fawe;
+import com.boydti.fawe.object.FaweQueue;
+import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.object.extent.FastWorldEditExtent;
import com.boydti.fawe.util.ExtentTraverser;
import com.sk89q.jnbt.CompoundTag;
@@ -72,19 +74,30 @@ public class MutableEntityChange implements Change {
}
}
+ private FaweQueue queue;
+ private boolean checkedQueue;
+
public void create(UndoContext context) {
- Extent extent = context.getExtent();
- ExtentTraverser find = new ExtentTraverser(extent).find(FastWorldEditExtent.class);
- if (find != null) {
- FastWorldEditExtent fwee = find.get();
- Map map = tag.getValue();
- List pos = (List) map.get("Pos").getValue();
- int x = (int) Math.round(pos.get(0).getValue());
- int y = (int) Math.round(pos.get(1).getValue());
- int z = (int) Math.round(pos.get(2).getValue());
- fwee.getQueue().setEntity(x, y, z, tag);
- } else {
- Fawe.debug("FAWE doesn't support: " + context + " for " + getClass() + " (bug Empire92)");
+ if (queue != null) {
+ perform(queue);
+ }
+ if (!checkedQueue) {
+ checkedQueue = true;
+ Extent extent = context.getExtent();
+ if (extent instanceof HasFaweQueue) {
+ perform(queue = ((HasFaweQueue) extent).getQueue());
+ } else {
+ Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)");
+ }
}
}
+
+ public void perform(FaweQueue queue) {
+ Map map = tag.getValue();
+ List pos = (List) map.get("Pos").getValue();
+ int x = (int) Math.round(pos.get(0).getValue());
+ int y = (int) Math.round(pos.get(1).getValue());
+ int z = (int) Math.round(pos.get(2).getValue());
+ queue.setEntity(x, y, z, tag);
+ }
}
diff --git a/core/src/main/java/com/boydti/fawe/object/change/MutableFullBlockChange.java b/core/src/main/java/com/boydti/fawe/object/change/MutableFullBlockChange.java
index 2d4c5d87..5335cd6d 100644
--- a/core/src/main/java/com/boydti/fawe/object/change/MutableFullBlockChange.java
+++ b/core/src/main/java/com/boydti/fawe/object/change/MutableFullBlockChange.java
@@ -2,8 +2,8 @@ package com.boydti.fawe.object.change;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
-import com.boydti.fawe.object.extent.FastWorldEditExtent;
-import com.boydti.fawe.util.ExtentTraverser;
+import com.boydti.fawe.object.FaweQueue;
+import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.history.UndoContext;
@@ -36,14 +36,25 @@ public class MutableFullBlockChange implements Change {
create(context);
}
+ private FaweQueue queue;
+ private boolean checkedQueue;
+
public void create(UndoContext context) {
- Extent extent = context.getExtent();
- ExtentTraverser find = new ExtentTraverser(extent).find(FastWorldEditExtent.class);
- if (find != null) {
- FastWorldEditExtent fwee = find.get();
- fwee.getQueue().setBlock(x, y, z, FaweCache.getId(from), FaweCache.getData(from));
- } else {
- Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)");
+ if (queue != null) {
+ perform(queue);
+ }
+ if (!checkedQueue) {
+ checkedQueue = true;
+ Extent extent = context.getExtent();
+ if (extent instanceof HasFaweQueue) {
+ perform(queue = ((HasFaweQueue) extent).getQueue());
+ } else {
+ Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)");
+ }
}
}
+
+ public void perform(FaweQueue queue) {
+ queue.setBlock(x, y, z, FaweCache.getId(from), FaweCache.getData(from));
+ }
}
diff --git a/core/src/main/java/com/boydti/fawe/object/change/MutableTileChange.java b/core/src/main/java/com/boydti/fawe/object/change/MutableTileChange.java
index 33b99dbb..73e9efa3 100644
--- a/core/src/main/java/com/boydti/fawe/object/change/MutableTileChange.java
+++ b/core/src/main/java/com/boydti/fawe/object/change/MutableTileChange.java
@@ -1,8 +1,8 @@
package com.boydti.fawe.object.change;
import com.boydti.fawe.Fawe;
-import com.boydti.fawe.object.extent.FastWorldEditExtent;
-import com.boydti.fawe.util.ExtentTraverser;
+import com.boydti.fawe.object.FaweQueue;
+import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.Tag;
@@ -36,18 +36,29 @@ public class MutableTileChange implements Change {
}
}
+ private FaweQueue queue;
+ private boolean checkedQueue;
+
public void create(UndoContext context) {
- Extent extent = context.getExtent();
- ExtentTraverser find = new ExtentTraverser(extent).find(FastWorldEditExtent.class);
- if (find != null) {
- FastWorldEditExtent fwee = find.get();
- Map map = tag.getValue();
- int x = ((IntTag) map.get("x")).getValue();
- int y = ((IntTag) map.get("y")).getValue();
- int z = ((IntTag) map.get("z")).getValue();
- fwee.getQueue().setTile(x, y, z, tag);
- } else {
- Fawe.debug("FAWE doesn't support: " + context + " for " + getClass());
+ if (queue != null) {
+ perform(queue);
+ }
+ if (!checkedQueue) {
+ checkedQueue = true;
+ Extent extent = context.getExtent();
+ if (extent instanceof HasFaweQueue) {
+ perform(queue = ((HasFaweQueue) extent).getQueue());
+ } else {
+ Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)");
+ }
}
}
+
+ public void perform(FaweQueue queue) {
+ Map map = tag.getValue();
+ int x = ((IntTag) map.get("x")).getValue();
+ int y = ((IntTag) map.get("y")).getValue();
+ int z = ((IntTag) map.get("z")).getValue();
+ queue.setTile(x, y, z, tag);
+ }
}
diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java b/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java
index 84c17ec8..32124eff 100644
--- a/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java
+++ b/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java
@@ -199,26 +199,11 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (osBD != null) {
return osBD;
}
- writeHeader(x, y, z);
- return osBD;
- }
-
- public void writeHeader(int x, int y, int z) throws IOException {
bdFile.getParentFile().mkdirs();
bdFile.createNewFile();
osBD = getCompressedOS(new FileOutputStream(bdFile));
- // Mode
- osBD.write((byte) MODE);
- // Origin
- setOrigin(x, z);
- osBD.write((byte) (x >> 24));
- osBD.write((byte) (x >> 16));
- osBD.write((byte) (x >> 8));
- osBD.write((byte) (x));
- osBD.write((byte) (z >> 24));
- osBD.write((byte) (z >> 16));
- osBD.write((byte) (z >> 8));
- osBD.write((byte) (z));
+ writeHeader(osBD, x, y, z);
+ return osBD;
}
@Override
@@ -271,12 +256,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
return null;
}
InputStream is = MainUtil.getCompressedIS(new FileInputStream(bdFile));
- // skip mode
- is.skip(1);
- // origin
- int x = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read() << 0));
- int z = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read() << 0));
- setOrigin(x, z);
+ readHeader(is);
return is;
}
diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/FaweStreamChangeSet.java b/core/src/main/java/com/boydti/fawe/object/changeset/FaweStreamChangeSet.java
index c9f6c542..a4e7d475 100644
--- a/core/src/main/java/com/boydti/fawe/object/changeset/FaweStreamChangeSet.java
+++ b/core/src/main/java/com/boydti/fawe/object/changeset/FaweStreamChangeSet.java
@@ -8,11 +8,13 @@ import com.boydti.fawe.object.change.MutableEntityChange;
import com.boydti.fawe.object.change.MutableFullBlockChange;
import com.boydti.fawe.object.change.MutableTileChange;
import com.boydti.fawe.util.MainUtil;
+import com.boydti.fawe.util.MathMan;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.world.World;
+import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -21,18 +23,197 @@ import java.util.Iterator;
public abstract class FaweStreamChangeSet extends FaweChangeSet {
- public static final int MODE = 3;
public static final int HEADER_SIZE = 9;
-
+ private int mode;
private final int compression;
+ private FaweStreamIdDelegate idDel;
+ private FaweStreamPositionDelegate posDel;
+
public FaweStreamChangeSet(World world) {
- this(world, Settings.HISTORY.COMPRESSION_LEVEL);
+ this(world, Settings.HISTORY.COMPRESSION_LEVEL, Settings.HISTORY.STORE_REDO, Settings.HISTORY.SMALL_EDITS);
}
- public FaweStreamChangeSet(World world, int compression) {
+ public FaweStreamChangeSet(World world, int compression, boolean storeRedo, boolean smallLoc) {
super(world);
this.compression = compression;
+ if (storeRedo) {
+ if (smallLoc) {
+ mode = 4;
+ } else {
+ mode = 3;
+ }
+ } else if (smallLoc) {
+ mode = 1;
+ } else {
+ mode = 2;
+ }
+ }
+
+ public interface FaweStreamPositionDelegate {
+ void write(OutputStream out, int x, int y, int z) throws IOException;
+ int readX(InputStream in) throws IOException;
+ int readY(InputStream in) throws IOException;
+ int readZ(InputStream in) throws IOException;
+ }
+
+ public interface FaweStreamIdDelegate {
+ void writeChange(OutputStream out, int from, int to) throws IOException;
+ void readCombined(InputStream in, MutableBlockChange change, boolean dir) throws IOException;
+ void readCombined(InputStream in, MutableFullBlockChange change, boolean dir) throws IOException;
+ }
+
+ private void setupStreamDelegates(int mode) {
+ this.mode = mode;
+ if (mode == 3 || mode == 4) {
+ idDel = new FaweStreamIdDelegate() {
+ @Override
+ public void writeChange(OutputStream stream, int combinedFrom, int combinedTo) throws IOException {
+ stream.write((combinedFrom) & 0xff);
+ stream.write(((combinedFrom) >> 8) & 0xff);
+ stream.write((combinedTo) & 0xff);
+ stream.write(((combinedTo) >> 8) & 0xff);
+ }
+
+ @Override
+ public void readCombined(InputStream is, MutableBlockChange change, boolean dir) throws IOException {
+ if (dir) {
+ is.skip(2);
+ int to1 = is.read();
+ int to2 = is.read();
+ change.id = (short) ((to2 << 4) + (to1 >> 4));
+ change.data = (byte) (to1 & 0xf);
+ } else {
+ int from1 = is.read();
+ int from2 = is.read();
+ is.skip(2);
+ change.id = (short) ((from2 << 4) + (from1 >> 4));
+ change.data = (byte) (from1 & 0xf);
+ }
+ }
+
+ @Override
+ public void readCombined(InputStream is, MutableFullBlockChange change, boolean dir) throws IOException {
+ change.from = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8);
+ change.to = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8);
+ }
+ };
+ } else {
+ idDel = new FaweStreamIdDelegate() {
+ @Override
+ public void writeChange(OutputStream stream, int combinedFrom, int to) throws IOException {
+ stream.write((combinedFrom) & 0xff);
+ stream.write(((combinedFrom) >> 8) & 0xff);
+ }
+
+ @Override
+ public void readCombined(InputStream in, MutableBlockChange change, boolean dir) throws IOException {
+ int from1 = in.read();
+ int from2 = in.read();
+ change.id = (short) ((from2 << 4) + (from1 >> 4));
+ change.data = (byte) (from1 & 0xf);
+ }
+
+ @Override
+ public void readCombined(InputStream is, MutableFullBlockChange change, boolean dir) throws IOException {
+ change.from = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8);
+ change.to = 0;
+ }
+ };
+ }
+ if (mode == 1 || mode == 4) { // small
+ posDel = new FaweStreamPositionDelegate() {
+ @Override
+ public void write(OutputStream out, int x, int y, int z) throws IOException {
+ byte b1 = (byte) y;
+ byte b2 = (byte) (x);
+ byte b3 = (byte) (z);
+ int x16 = (x >> 8) & 0xF;
+ int z16 = (z >> 8) & 0xF;
+ byte b4 = MathMan.pair16(x16, z16);
+ out.write(b1);
+ out.write(b2);
+ out.write(b3);
+ out.write(b4);
+ }
+
+ byte[] buffer = new byte[4];
+
+ @Override
+ public int readX(InputStream in) throws IOException {
+ if (in.read(buffer) == -1) {
+ throw new EOFException();
+ }
+ return (((buffer[1] & 0xFF) + ((MathMan.unpair16x(buffer[3])) << 8)) << 20) >> 20;
+ }
+
+ @Override
+ public int readY(InputStream in) {
+ return buffer[0] & 0xFF;
+ }
+
+ @Override
+ public int readZ(InputStream in) throws IOException {
+ return (((buffer[2] & 0xFF) + ((MathMan.unpair16y(buffer[3])) << 8)) << 20) >> 20;
+ }
+ };
+ } else {
+ posDel = new FaweStreamPositionDelegate() {
+
+ byte[] buffer = new byte[5];
+
+ @Override
+ public void write(OutputStream stream, int x, int y, int z) throws IOException {
+ stream.write((x) & 0xff);
+ stream.write(((x) >> 8) & 0xff);
+ stream.write((z) & 0xff);
+ stream.write(((z) >> 8) & 0xff);
+ stream.write((byte) y);
+ }
+
+ @Override
+ public int readX(InputStream is) throws IOException {
+ if (is.read(buffer) == -1) {
+ throw new EOFException();
+ }
+ return (buffer[0] & 0xFF) + (buffer[1] << 8);
+ }
+
+ @Override
+ public int readY(InputStream is) throws IOException {
+ return buffer[4] & 0xFF;
+ }
+
+ @Override
+ public int readZ(InputStream is) throws IOException {
+ return (buffer[2] & 0xFF) + (buffer[3] << 8);
+ }
+ };
+ }
+ }
+
+ public void writeHeader(OutputStream os, int x, int y, int z) throws IOException {
+ os.write(mode);
+ setOrigin(x, z);
+ os.write((byte) (x >> 24));
+ os.write((byte) (x >> 16));
+ os.write((byte) (x >> 8));
+ os.write((byte) (x));
+ os.write((byte) (z >> 24));
+ os.write((byte) (z >> 16));
+ os.write((byte) (z >> 8));
+ os.write((byte) (z));
+ setupStreamDelegates(mode);
+ }
+
+ public void readHeader(InputStream is) throws IOException {
+ // skip mode
+ int mode = is.read();
+ // origin
+ int x = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read() << 0));
+ int z = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read() << 0));
+ setOrigin(x, z);
+ setupStreamDelegates(mode);
}
public FaweOutputStream getCompressedOS(OutputStream os) throws IOException {
@@ -93,21 +274,8 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
try {
OutputStream stream = getBlockOS(x, y, z);
//x
- x-=originX;
- stream.write((x) & 0xff);
- stream.write(((x) >> 8) & 0xff);
- //z
- z-=originZ;
- stream.write((z) & 0xff);
- stream.write(((z) >> 8) & 0xff);
- //y
- stream.write((byte) y);
- //from
- stream.write((combinedFrom) & 0xff);
- stream.write(((combinedFrom) >> 8) & 0xff);
- //to
- stream.write((combinedTo) & 0xff);
- stream.write(((combinedTo) >> 8) & 0xff);
+ posDel.write(stream, x - originX, y, z - originZ);
+ idDel.writeChange(stream, combinedFrom, combinedTo);
}
catch (Throwable e) {
MainUtil.handleError(e);
@@ -172,43 +340,25 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
private MutableBlockChange last = read();
public MutableBlockChange read() {
try {
- int read0 = is.read();
- if (read0 == -1) {
- return null;
- }
- int x = ((byte) read0 & 0xFF) + ((byte) is.read() << 8) + originX;
- int z = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8) + originZ;
- int y = is.read() & 0xff;
- change.x = x;
- change.y = y;
- change.z = z;
- if (dir) {
- is.skip(2);
- int to1 = is.read();
- int to2 = is.read();
- change.id = (short) ((to2 << 4) + (to1 >> 4));
- change.data = (byte) (to1 & 0xf);
- } else {
- int from1 = is.read();
- int from2 = is.read();
- is.skip(2);
- change.id = (short) ((from2 << 4) + (from1 >> 4));
- change.data = (byte) (from1 & 0xf);
- }
+ change.x = posDel.readX(is) + originX;
+ change.y = posDel.readY(is);
+ change.z = posDel.readZ(is) + originZ;
+ idDel.readCombined(is, change, dir);
return change;
- } catch (Exception ignoreEOF) {
- MainUtil.handleError(ignoreEOF);
+ } catch (EOFException ignoreOEF) {
+ return null;
+ } catch (Exception e) {
+ MainUtil.handleError(e);
}
return null;
}
@Override
public boolean hasNext() {
- if (last == null) {
- last = read();
- }
if (last != null) {
return true;
+ } else if ((last = read()) != null) {
+ return true;
}
try {
is.close();
@@ -242,21 +392,15 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
private MutableFullBlockChange last = read();
public MutableFullBlockChange read() {
try {
- int read0 = is.read();
- if (read0 == -1) {
- return null;
- }
- int x = ((byte) read0 & 0xFF) + ((byte) is.read() << 8) + originX;
- int z = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8) + originZ;
- int y = is.read() & 0xff;
- change.x = x;
- change.y = y;
- change.z = z;
- change.from = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8);
- change.to = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8);
+ change.x = posDel.readX(is) + originX;
+ change.y = posDel.readY(is);
+ change.z = posDel.readZ(is) + originZ;
+ idDel.readCombined(is, change, dir);
return change;
- } catch (Exception ignoreEOF) {
- MainUtil.handleError(ignoreEOF);
+ } catch (EOFException ignoreOEF) {
+ return null;
+ } catch (Exception e) {
+ MainUtil.handleError(e);
}
return null;
}
diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java b/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java
index 1037d8bb..db5c2500 100644
--- a/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java
+++ b/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java
@@ -113,9 +113,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet {
setOrigin(x, z);
idsStream = new FastByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE);
idsStreamZip = getCompressedOS(idsStream);
- idsStreamZip.write(FaweStreamChangeSet.MODE);
- idsStreamZip.writeInt(x);
- idsStreamZip.writeInt(z);
+ writeHeader(idsStreamZip, x, y, z);
return idsStreamZip;
}
@@ -125,7 +123,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet {
return null;
}
FaweInputStream result = MainUtil.getCompressedIS(new FastByteArraysInputStream(ids));
- result.skip(FaweStreamChangeSet.HEADER_SIZE);
+ readHeader(result);
return result;
}
diff --git a/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java
index 373a8a8f..ec0d65f3 100644
--- a/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java
+++ b/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java
@@ -2,6 +2,7 @@ package com.boydti.fawe.object.extent;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.FaweQueue;
+import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.ReflectionUtils;
import com.sk89q.jnbt.CompoundTag;
@@ -24,9 +25,9 @@ import com.sk89q.worldedit.world.biome.BaseBiome;
import java.util.List;
import java.util.Map;
-public class FastWorldEditExtent extends AbstractDelegateExtent {
+public class FastWorldEditExtent extends AbstractDelegateExtent implements HasFaweQueue {
- private final FaweQueue queue;
+ private FaweQueue queue;
private final int maxY;
public FastWorldEditExtent(final World world, FaweQueue queue) {
diff --git a/core/src/main/java/com/boydti/fawe/object/extent/MCAExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/MCAExtent.java
index fd1b5e6a..fcc3094f 100644
--- a/core/src/main/java/com/boydti/fawe/object/extent/MCAExtent.java
+++ b/core/src/main/java/com/boydti/fawe/object/extent/MCAExtent.java
@@ -2,6 +2,7 @@ package com.boydti.fawe.object.extent;
import com.boydti.fawe.jnbt.anvil.MCAFile;
import com.boydti.fawe.object.FaweQueue;
+import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
@@ -21,7 +22,7 @@ import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
-public class MCAExtent extends AbstractDelegateExtent {
+public class MCAExtent extends AbstractDelegateExtent implements HasFaweQueue {
private final FaweQueue queue;
private final File folder;
diff --git a/core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java
index 90fafdfe..13dd5fb8 100644
--- a/core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java
+++ b/core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java
@@ -4,6 +4,7 @@ import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.RegionWrapper;
+import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.WEManager;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
@@ -38,7 +39,7 @@ public class ProcessedWEExtent extends FaweRegionExtent {
}
if (WEManager.IMP.maskContains(this.mask, location.getBlockX(), location.getBlockZ())) {
if (!limit.MAX_ENTITIES()) {
- WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES);
+ WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_ENTITIES);
return null;
}
return super.createEntity(location, entity);
@@ -63,11 +64,14 @@ public class ProcessedWEExtent extends FaweRegionExtent {
return super.getEntities(region);
}
+ int count = 0;
+
@Override
public BaseBlock getLazyBlock(int x, int y, int z) {
+ count++;
if (WEManager.IMP.maskContains(this.mask, x, z)) {
if (!limit.MAX_CHECKS()) {
- WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES);
+ WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS);
return EditSession.nullBlock;
} else {
return extent.getLazyBlock(x, y, z);
diff --git a/core/src/main/java/com/boydti/fawe/object/function/mask/AbstractDelegateMask.java b/core/src/main/java/com/boydti/fawe/object/function/mask/AbstractDelegateMask.java
index fdd92282..7c541e1b 100644
--- a/core/src/main/java/com/boydti/fawe/object/function/mask/AbstractDelegateMask.java
+++ b/core/src/main/java/com/boydti/fawe/object/function/mask/AbstractDelegateMask.java
@@ -7,20 +7,20 @@ import javax.annotation.Nullable;
public class AbstractDelegateMask implements Mask {
- private final Mask parent;
+ private final Mask mask;
public AbstractDelegateMask(Mask parent) {
- this.parent = parent;
+ this.mask = parent;
}
@Override
public boolean test(Vector vector) {
- return parent.test(vector);
+ return mask.test(vector);
}
@Nullable
@Override
public Mask2D toMask2D() {
- return parent.toMask2D();
+ return mask.toMask2D();
}
}
diff --git a/core/src/main/java/com/boydti/fawe/object/mask/AngleMask.java b/core/src/main/java/com/boydti/fawe/object/mask/AngleMask.java
new file mode 100644
index 00000000..13f5868d
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/mask/AngleMask.java
@@ -0,0 +1,186 @@
+package com.boydti.fawe.object.mask;
+
+import com.boydti.fawe.Fawe;
+import com.boydti.fawe.util.MathMan;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.blocks.BaseBlock;
+import com.sk89q.worldedit.blocks.BlockType;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Mask2D;
+import java.util.HashMap;
+import javax.annotation.Nullable;
+
+public class AngleMask implements Mask, ResettableMask {
+ private final Extent extent;
+ private final int max;
+ private final int min;
+ private int maxY;
+
+ public AngleMask(Extent extent, int min, int max) {
+ this.extent = extent;
+ this.maxY = extent.getMaximumPoint().getBlockY();
+ this.min = min;
+ this.max = max;
+ }
+
+ private HashMap angles = new HashMap<>();
+ private long tick = 0;
+
+ @Override
+ public boolean test(Vector vector) {
+ long currentTick = Fawe.get().getTimer().getTick();
+ if (tick != (tick = currentTick)) {
+ angles.clear();
+ tick = currentTick;
+ }
+ long pair = MathMan.pairInt(vector.getBlockX(), vector.getBlockZ());
+ if (!angles.isEmpty()) {
+ Boolean value = angles.get(pair);
+ if (value != null) {
+ return value;
+ }
+ }
+ boolean result = getAngle(vector);
+ angles.put(pair, result);
+ return result;
+ }
+
+ @Override
+ public void reset() {
+ this.angles.clear();
+ }
+
+ public boolean getAngle(Vector vector) {
+ int x = vector.getBlockX();
+ int z = vector.getBlockZ();
+ boolean n = false;
+ int o = getHighestTerrainBlock(x, z, 0, maxY, n);
+ if (getHighestTerrainBlock(x - 1, z, o - min, o - max, n) != -1) {
+ return true;
+ }
+ if (getHighestTerrainBlock(x + 1, z, o - min, o - max, n) != -1) {
+ return true;
+ }
+ if (getHighestTerrainBlock(x, z - 1, o - min, o - max, n) != -1) {
+ return true;
+ }
+ if (getHighestTerrainBlock(x, z + 1, o - min, o - max, n) != -1) {
+ return true;
+ }
+ return false;
+ }
+
+ private Vector mutable = new Vector();
+
+ private int getHighestTerrainBlock(final int x, final int z, int minY, int maxY, final boolean naturalOnly) {
+ maxY = Math.min(this.maxY, Math.max(0, maxY));
+ minY = Math.max(0, minY);
+ mutable.x = x;
+ mutable.z = z;
+ for (int y = maxY; y >= minY; --y) {
+ mutable.y = y;
+ BaseBlock block = extent.getLazyBlock(mutable);
+ final int id = block.getId();
+ int data;
+ switch (id) {
+ case 0: {
+ continue;
+ }
+ case 2:
+ case 4:
+ case 13:
+ case 14:
+ case 15:
+ case 20:
+ case 21:
+ case 22:
+ case 25:
+ 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 51:
+ case 52:
+ case 54:
+ case 55:
+ case 56:
+ case 57:
+ case 58:
+ case 60:
+ case 61:
+ case 62:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 73:
+ case 74:
+ case 78:
+ case 79:
+ case 80:
+ case 81:
+ case 82:
+ case 83:
+ case 84:
+ case 85:
+ case 87:
+ case 88:
+ case 101:
+ case 102:
+ case 103:
+ case 110:
+ case 112:
+ case 113:
+ case 117:
+ case 121:
+ case 122:
+ case 123:
+ case 124:
+ case 129:
+ case 133:
+ case 138:
+ case 137:
+ case 140:
+ case 165:
+ case 166:
+ case 169:
+ case 170:
+ case 172:
+ case 173:
+ case 174:
+ case 176:
+ case 177:
+ case 181:
+ case 182:
+ case 188:
+ case 189:
+ case 190:
+ case 191:
+ case 192:
+ return y;
+ default:
+ data = 0;
+ }
+ if (naturalOnly ? BlockType.isNaturalTerrainBlock(id, data) : !BlockType.canPassThrough(id, data)) {
+ return y;
+ }
+ }
+ return -1;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/mask/DataMask.java b/core/src/main/java/com/boydti/fawe/object/mask/DataMask.java
new file mode 100644
index 00000000..ea1a1e9d
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/mask/DataMask.java
@@ -0,0 +1,39 @@
+package com.boydti.fawe.object.mask;
+
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Mask2D;
+import javax.annotation.Nullable;
+
+public class DataMask implements Mask, ResettableMask {
+
+ private final Extent extent;
+
+ public DataMask(Extent extent) {
+ this.extent = extent;
+ }
+
+ int data = -1;
+
+ @Override
+ public boolean test(Vector vector) {
+ if (data != -1) {
+ return extent.getLazyBlock(vector).getData() == data;
+ } else {
+ data = extent.getLazyBlock(vector).getData();
+ return true;
+ }
+ }
+
+ @Override
+ public void reset() {
+ this.data = -1;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/mask/IdDataMask.java b/core/src/main/java/com/boydti/fawe/object/mask/IdDataMask.java
new file mode 100644
index 00000000..34ea5f73
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/mask/IdDataMask.java
@@ -0,0 +1,40 @@
+package com.boydti.fawe.object.mask;
+
+import com.boydti.fawe.FaweCache;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Mask2D;
+import javax.annotation.Nullable;
+
+public class IdDataMask implements Mask, ResettableMask {
+
+ private final Extent extent;
+
+ public IdDataMask(Extent extent) {
+ this.extent = extent;
+ }
+
+ int combined = -1;
+
+ @Override
+ public boolean test(Vector vector) {
+ if (combined != -1) {
+ return FaweCache.getCombined(extent.getLazyBlock(vector)) == combined;
+ } else {
+ combined = FaweCache.getCombined(extent.getLazyBlock(vector));
+ return true;
+ }
+ }
+
+ @Override
+ public void reset() {
+ this.combined = -1;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/mask/IdMask.java b/core/src/main/java/com/boydti/fawe/object/mask/IdMask.java
new file mode 100644
index 00000000..ba9df5d4
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/mask/IdMask.java
@@ -0,0 +1,39 @@
+package com.boydti.fawe.object.mask;
+
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Mask2D;
+import javax.annotation.Nullable;
+
+public class IdMask implements Mask, ResettableMask {
+
+ private Extent extent;
+
+ public IdMask(Extent extent) {
+ this.extent = extent;
+ }
+
+ int id = -1;
+
+ @Override
+ public boolean test(Vector vector) {
+ if (id != -1) {
+ return extent.getLazyBlock(vector).getId() == id;
+ } else {
+ id = extent.getLazyBlock(vector).getId();
+ return true;
+ }
+ }
+
+ @Override
+ public void reset() {
+ this.id = -1;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/mask/ResettableMask.java b/core/src/main/java/com/boydti/fawe/object/mask/ResettableMask.java
new file mode 100644
index 00000000..67d9a00a
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/mask/ResettableMask.java
@@ -0,0 +1,5 @@
+package com.boydti.fawe.object.mask;
+
+public interface ResettableMask {
+ void reset();
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/mask/XAxisMask.java b/core/src/main/java/com/boydti/fawe/object/mask/XAxisMask.java
new file mode 100644
index 00000000..2fd4f082
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/mask/XAxisMask.java
@@ -0,0 +1,33 @@
+package com.boydti.fawe.object.mask;
+
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Mask2D;
+import javax.annotation.Nullable;
+
+/**
+ * Restricts the
+ */
+public class XAxisMask implements Mask, ResettableMask {
+
+ private int layer = -1;
+
+ @Override
+ public boolean test(Vector vector) {
+ if (layer == -1) {
+ layer = vector.getBlockX();
+ }
+ return vector.getBlockX() == layer;
+ }
+
+ @Override
+ public void reset() {
+ this.layer = -1;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/mask/YAxisMask.java b/core/src/main/java/com/boydti/fawe/object/mask/YAxisMask.java
new file mode 100644
index 00000000..3724606b
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/mask/YAxisMask.java
@@ -0,0 +1,33 @@
+package com.boydti.fawe.object.mask;
+
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Mask2D;
+import javax.annotation.Nullable;
+
+/**
+ * Restricts the
+ */
+public class YAxisMask implements Mask, ResettableMask {
+
+ private int layer = -1;
+
+ @Override
+ public boolean test(Vector vector) {
+ if (layer == -1) {
+ layer = vector.getBlockY();
+ }
+ return vector.getBlockY() == layer;
+ }
+
+ @Override
+ public void reset() {
+ this.layer = -1;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/mask/ZAxisMask.java b/core/src/main/java/com/boydti/fawe/object/mask/ZAxisMask.java
new file mode 100644
index 00000000..15d5a2ef
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/mask/ZAxisMask.java
@@ -0,0 +1,33 @@
+package com.boydti.fawe.object.mask;
+
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Mask2D;
+import javax.annotation.Nullable;
+
+/**
+ * Restricts the
+ */
+public class ZAxisMask implements Mask, ResettableMask {
+
+ private int layer = -1;
+
+ @Override
+ public boolean test(Vector vector) {
+ if (layer == -1) {
+ layer = vector.getBlockZ();
+ }
+ return vector.getBlockZ() == layer;
+ }
+
+ @Override
+ public void reset() {
+ this.layer = -1;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/schematic/FaweFormat.java b/core/src/main/java/com/boydti/fawe/object/schematic/FaweFormat.java
index bf805ffd..4798c80f 100644
--- a/core/src/main/java/com/boydti/fawe/object/schematic/FaweFormat.java
+++ b/core/src/main/java/com/boydti/fawe/object/schematic/FaweFormat.java
@@ -84,115 +84,121 @@ public class FaweFormat implements ClipboardReader, ClipboardWriter {
BlockArrayClipboard clipboard;
int ox, oy, oz;
oy = 0;
-
boolean from = false;
- boolean small = true;
- switch (mode) {
- default:
- return null;
+ boolean small = false;
+ boolean knownSize = false;
+ switch(mode) {
+ case 0:
+ knownSize = true;
+ break;
+ case 1:
+ small = true;
+ break;
+ case 2:
+ break;
case 3:
from = true;
- case 2:
- small = false;
- case 1: { // Unknown size
- ox = in.readInt();
- oz = in.readInt();
- FaweOutputStream tmp = new FaweOutputStream(new FastByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE));
- int width = 0;
- int height = 0;
- int length = 0;
- while (true) {
+ break;
+ case 4:
+ small = true;
+ from = true;
+ break;
+ }
+ if (knownSize) {
+ int width = in.readUnsignedShort();
+ int height = in.readUnsignedShort();
+ int length = in.readUnsignedShort();
+ ox = in.readShort();
+ oy = in.readShort();
+ oz = in.readShort();
+
+ Vector origin = new Vector(0, 0, 0);
+ CuboidRegion region = new CuboidRegion(origin, origin.add(width, height, length).subtract(Vector.ONE));
+ clipboard = new BlockArrayClipboard(region, clipboardId);
+ try {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ for (int z = 0; z < length; z++) {
+ int combined = in.readUnsignedShort();
+ int id = FaweCache.getId(combined);
+ int data = FaweCache.getData(combined);
+ BaseBlock block = FaweCache.getBlock(id, data);
+ clipboard.setBlock(x, y, z, block);
+ }
+ }
+ }
+ } catch (WorldEditException e) {
+ e.printStackTrace();
+ return null;
+ }
+ } else {
+ ox = in.readInt();
+ oz = in.readInt();
+ FaweOutputStream tmp = new FaweOutputStream(new FastByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE));
+ int width = 0;
+ int height = 0;
+ int length = 0;
+ while (true) {
+ int x, y, z;
+ if (small) {
+ tmp.write(x = in.read());
+ tmp.write(y = in.read());
+ tmp.write(z = in.read());
+ } else {
+ tmp.writeShort((short) (x = in.readUnsignedShort()));
+ tmp.write(y = in.read());
+ tmp.writeShort((short) (z = in.readUnsignedShort()));
+ }
+ if (from) {
+ in.skip(2);
+ }
+ short combined;
+ tmp.writeShort(combined = in.readShort());
+ if (combined == 0 || y == -1) {
+ break;
+ }
+ if (x > width) {
+ width = x;
+ }
+ if (y > height) {
+ height = y;
+ }
+ if(z > length) {
+ length = z;
+ }
+ }
+ Vector origin = new Vector(0, 0, 0);
+ CuboidRegion region = new CuboidRegion(origin, origin.add(width, height, length));
+ clipboard = new BlockArrayClipboard(region, clipboardId);
+ width++;
+ height++;
+ length++;
+ byte[] array = ((ByteArrayOutputStream) tmp.getParent()).toByteArray();
+ FaweInputStream part = new FaweInputStream(new FastByteArrayInputStream(array));
+ try {
+ for (int i = 0; i< array.length; i+= 9) {
int x, y, z;
if (small) {
- tmp.write(x = in.read());
- tmp.write(y = in.read());
- tmp.write(z = in.read());
+ x = in.read();
+ y = in.read();
+ z = in.read();
} else {
- tmp.writeShort((short) (x = in.readUnsignedShort()));
- tmp.write(y = in.read());
- tmp.writeShort((short) (z = in.readUnsignedShort()));
+ x = in.readUnsignedShort();
+ y = in.read();
+ z = in.readUnsignedShort();
}
if (from) {
in.skip(2);
}
- short combined;
- tmp.writeShort(combined = in.readShort());
- if (combined == 0 || y == -1) {
- break;
- }
- if (x > width) {
- width = x;
- }
- if (y > height) {
- height = y;
- }
- if(z > length) {
- length = z;
- }
+ int combined = in.readShort();
+ int id = FaweCache.getId(combined);
+ int data = FaweCache.getData(combined);
+ BaseBlock block = FaweCache.getBlock(id, data);
+ clipboard.setBlock(x, y, z, block);
}
- Vector origin = new Vector(0, 0, 0);
- CuboidRegion region = new CuboidRegion(origin, origin.add(width, height, length));
- clipboard = new BlockArrayClipboard(region, clipboardId);
- width++;
- height++;
- length++;
- byte[] array = ((ByteArrayOutputStream) tmp.getParent()).toByteArray();
- FaweInputStream part = new FaweInputStream(new FastByteArrayInputStream(array));
- try {
- for (int i = 0; i< array.length; i+= 9) {
- int x, y, z;
- if (small) {
- x = in.read();
- y = in.read();
- z = in.read();
- } else {
- x = in.readUnsignedShort();
- y = in.read();
- z = in.readUnsignedShort();
- }
- if (from) {
- in.skip(2);
- }
- int combined = in.readShort();
- int id = FaweCache.getId(combined);
- int data = FaweCache.getData(combined);
- BaseBlock block = FaweCache.getBlock(id, data);
- clipboard.setBlock(x, y, z, block);
- }
- } catch (WorldEditException e) {
- e.printStackTrace();
- return null;
- }
- break;
- }
- case 0: {
- int width = in.readUnsignedShort();
- int height = in.readUnsignedShort();
- int length = in.readUnsignedShort();
- ox = in.readShort();
- oy = in.readShort();
- oz = in.readShort();
-
- Vector origin = new Vector(0, 0, 0);
- CuboidRegion region = new CuboidRegion(origin, origin.add(width, height, length).subtract(Vector.ONE));
- clipboard = new BlockArrayClipboard(region, clipboardId);
- try {
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- for (int z = 0; z < length; z++) {
- int combined = in.readUnsignedShort();
- int id = FaweCache.getId(combined);
- int data = FaweCache.getData(combined);
- BaseBlock block = FaweCache.getBlock(id, data);
- clipboard.setBlock(x, y, z, block);
- }
- }
- }
- } catch (WorldEditException e) {
- e.printStackTrace();
- return null;
- }
- break;
+ } catch (WorldEditException e) {
+ e.printStackTrace();
+ return null;
}
}
try {
diff --git a/core/src/main/java/com/boydti/fawe/util/MaskTraverser.java b/core/src/main/java/com/boydti/fawe/util/MaskTraverser.java
new file mode 100644
index 00000000..71485d64
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/util/MaskTraverser.java
@@ -0,0 +1,48 @@
+package com.boydti.fawe.util;
+
+import com.boydti.fawe.object.mask.ResettableMask;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.function.mask.Mask;
+import java.lang.reflect.Field;
+import java.util.Collection;
+
+public class MaskTraverser {
+ private final Mask mask;
+
+ public MaskTraverser(Mask start) {
+ this.mask = start;
+ }
+
+ public void reset(Extent newExtent) {
+ reset(mask, newExtent);
+ }
+
+ private void reset(Mask mask, Extent newExtent) {
+ if (mask instanceof ResettableMask) {
+ ((ResettableMask) mask).reset();
+ }
+ Class> current = mask.getClass();
+ while(current.getSuperclass() != null) {
+ try {
+ Field field = current.getDeclaredField("extent");
+ field.setAccessible(true);
+ field.set(mask, newExtent);
+ } catch (NoSuchFieldException | IllegalAccessException ignore) {}
+ try {
+ Field field = current.getDeclaredField("mask");
+ field.setAccessible(true);
+ Mask next = (Mask) field.get(mask);
+ reset(next, newExtent);
+ } catch (NoSuchFieldException | IllegalAccessException ignore) {}
+ try {
+ Field field = current.getDeclaredField("masks");
+ field.setAccessible(true);
+ Collection masks = (Collection) field.get(mask);
+ for (Mask next : masks) {
+ reset(next, newExtent);
+ }
+ } catch (NoSuchFieldException | IllegalAccessException ignore) {}
+ current = current.getSuperclass();
+ }
+ }
+}
diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java
index 99fd1fae..eb62e964 100644
--- a/core/src/main/java/com/sk89q/worldedit/EditSession.java
+++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java
@@ -31,6 +31,7 @@ import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
+import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.object.HistoryExtent;
import com.boydti.fawe.object.NullChangeSet;
import com.boydti.fawe.object.RegionWrapper;
@@ -44,7 +45,9 @@ import com.boydti.fawe.object.extent.FastWorldEditExtent;
import com.boydti.fawe.object.extent.FaweRegionExtent;
import com.boydti.fawe.object.extent.NullExtent;
import com.boydti.fawe.object.extent.ProcessedWEExtent;
+import com.boydti.fawe.object.mask.ResettableMask;
import com.boydti.fawe.util.ExtentTraverser;
+import com.boydti.fawe.util.MaskTraverser;
import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.Perm;
import com.boydti.fawe.util.SetQueue;
@@ -149,7 +152,7 @@ import static com.sk89q.worldedit.regions.Regions.minimumBlockY;
* {@link Extent}s that are chained together. For example, history is logged
* using the {@link ChangeSetExtent}.
*/
-public class EditSession extends AbstractWorld {
+public class EditSession extends AbstractWorld implements HasFaweQueue {
/**
* Used by {@link #setBlock(Vector, BaseBlock, Stage)} to
* determine which {@link Extent}s should be bypassed.
@@ -232,9 +235,6 @@ public class EditSession extends AbstractWorld {
if (allowedRegions == null) {
if (player != null && !player.hasWorldEditBypass()) {
allowedRegions = player.getCurrentRegions();
- if (allowedRegions.length == 1 && allowedRegions[0].isGlobal()) {
- allowedRegions = null;
- }
}
}
if (autoQueue == null) {
@@ -292,7 +292,7 @@ public class EditSession extends AbstractWorld {
if (allowedRegions.length == 0) {
this.extent = new NullExtent(this.extent, BBC.WORLDEDIT_CANCEL_REASON_NO_REGION);
} else {
- this.extent = new ProcessedWEExtent(this.extent, allowedRegions, limit);
+ this.extent = new ProcessedWEExtent(this.extent, allowedRegions, this.limit);
}
}
this.extent = wrapExtent(this.extent, bus, event, Stage.BEFORE_HISTORY);
@@ -577,9 +577,15 @@ public class EditSession extends AbstractWorld {
public void setMask(Mask mask) {
if (mask == null) {
mask = Masks.alwaysTrue();
+ } else {
+ new MaskTraverser(mask).reset(this);
}
ExtentTraverser maskingExtent = new ExtentTraverser(this.extent).find(MaskingExtent.class);
- if (maskingExtent != null) {
+ if (maskingExtent != null && maskingExtent.get() != null) {
+ Mask oldMask = maskingExtent.get().getMask();
+ if (oldMask instanceof ResettableMask) {
+ ((ResettableMask) oldMask).reset();
+ }
maskingExtent.get().setMask(mask);
} else if (mask != Masks.alwaysTrue()) {
this.extent = new MaskingExtent(this.extent, mask);
@@ -1159,6 +1165,8 @@ public class EditSession extends AbstractWorld {
BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS.send(player);
}
}
+ // Reset limit
+ limit.set(originalLimit);
// Enqueue it
if (queue == null || queue.size() == 0) {
queue.dequeue();
diff --git a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java
index 1020fd74..9782bd4f 100644
--- a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java
+++ b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java
@@ -31,6 +31,8 @@ import com.boydti.fawe.object.brush.BlendBall;
import com.boydti.fawe.object.brush.DoubleActionBrushTool;
import com.boydti.fawe.object.brush.ErodeBrush;
import com.boydti.fawe.object.brush.LineBrush;
+import com.boydti.fawe.object.brush.RecurseBrush;
+import com.boydti.fawe.object.mask.IdMask;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandPermissions;
@@ -117,6 +119,25 @@ public class BrushCommands {
BBC.BRUSH_SPHERE.send(player, radius);
}
+ @Command(
+ aliases = { "recursive", "recurse", "r" },
+ usage = " [radius]",
+ desc = "Choose the recursive brush",
+ help = "Chooses the recursive brush",
+ min = 0,
+ max = 2
+ )
+ @CommandPermissions("worldedit.brush.recursive")
+ public void recursiveBrush(Player player, LocalSession session, EditSession editSession, Pattern fill, @Optional("2") double radius) throws WorldEditException {
+ worldEdit.checkMaxBrushRadius(radius);
+ BrushTool tool = session.getBrushTool(player.getItemInHand());
+ tool.setSize(radius);
+ tool.setBrush(new RecurseBrush(tool), "worldedit.brush.recursive");
+ tool.setMask(new IdMask(editSession));
+ tool.setFill(fill);
+ BBC.BRUSH_SPHERE.send(player, radius);
+ }
+
@Command(
aliases = { "line", "l" },
usage = " [radius]",
@@ -152,8 +173,7 @@ public class BrushCommands {
max = 2
)
@CommandPermissions("worldedit.brush.sphere")
- public void sphereBrush(Player player, LocalSession session, EditSession editSession, Pattern fill,
- @Optional("2") double radius, @Switch('h') boolean hollow) throws WorldEditException {
+ public void sphereBrush(Player player, LocalSession session, EditSession editSession, Pattern fill, @Optional("2") double radius, @Switch('h') boolean hollow) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
BrushTool tool = session.getBrushTool(player.getItemInHand());
diff --git a/core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java b/core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java
index 79fd5a4f..5159717c 100644
--- a/core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java
+++ b/core/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java
@@ -26,9 +26,11 @@ import com.boydti.fawe.config.Settings;
import com.boydti.fawe.database.DBHandler;
import com.boydti.fawe.database.RollbackDatabase;
import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
+import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
+import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
@@ -72,7 +74,7 @@ public class HistoryCommands {
min = 3,
max = 3
)
- @CommandPermissions("worldedit.history.undo")
+ @CommandPermissions("worldedit.history.rollback")
public void faweRollback(final Player player, LocalSession session, final String user, int radius, String time) throws WorldEditException {
if (!Settings.HISTORY.USE_DATABASE) {
BBC.SETTING_DISABLE.send(player, "history.use-database");
@@ -136,7 +138,7 @@ public class HistoryCommands {
return;
}
radius = Math.max(Math.min(500, radius), 0);
- World world = player.getWorld();
+ final World world = player.getWorld();
WorldVector origin = player.getPosition();
Vector bot = origin.subtract(radius, radius, radius);
bot = bot.setY(Math.max(0, bot.getY()));
@@ -144,10 +146,20 @@ public class HistoryCommands {
top = top.setY(Math.min(255, top.getY()));
RollbackDatabase database = DBHandler.IMP.getDatabase(world);
final AtomicInteger count = new AtomicInteger();
+ final FawePlayer fp = FawePlayer.wrap(player);
database.getPotentialEdits(other, System.currentTimeMillis() - timeDiff, bot, top, new RunnableVal() {
@Override
public void run(DiskStorageHistory edit) {
- EditSession session = edit.toEditSession(null);
+ EditSession session = new EditSessionBuilder(world)
+ .player(fp)
+ .autoQueue(false)
+ .fastmode(false)
+ .checkMemory(false)
+ .changeSet(edit)
+ .limitUnlimited()
+ .queue(fp.getMaskedFaweQueue(false))
+ .build();
+ session.setSize(1);
session.undo(session);
edit.deleteFiles();
BBC.ROLLBACK_ELEMENT.send(player, Fawe.imp().getWorldName(edit.getWorld()) + "/" + user + "-" + edit.getIndex());
diff --git a/core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java b/core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java
index 9b1ec053..d9f1507c 100644
--- a/core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java
+++ b/core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java
@@ -152,7 +152,6 @@ public class ToolCommands {
)
@CommandPermissions("worldedit.tool.flood-fill")
public void floodFill(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
-
LocalConfiguration config = we.getConfiguration();
int range = args.getInteger(1);
diff --git a/core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java b/core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java
new file mode 100644
index 00000000..ddcd488d
--- /dev/null
+++ b/core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java
@@ -0,0 +1,130 @@
+package com.sk89q.worldedit.command;
+
+import com.boydti.fawe.config.BBC;
+import com.sk89q.minecraft.util.commands.Command;
+import com.sk89q.minecraft.util.commands.CommandContext;
+import com.sk89q.minecraft.util.commands.CommandPermissions;
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.LocalSession;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.WorldEditException;
+import com.sk89q.worldedit.entity.Player;
+import com.sk89q.worldedit.extension.input.ParserContext;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.pattern.Pattern;
+import com.sk89q.worldedit.util.command.parametric.Optional;
+
+/**
+ * Tool commands.
+ */
+public class ToolUtilCommands {
+ private final WorldEdit we;
+
+ public ToolUtilCommands(WorldEdit we) {
+ this.we = we;
+ }
+
+ @Command(
+ aliases = { "/", "," },
+ usage = "[on|off]",
+ desc = "Toggle the super pickaxe function",
+ min = 0,
+ max = 1
+ )
+ @CommandPermissions("worldedit.superpickaxe")
+ public void togglePickaxe(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
+ String newState = args.getString(0, null);
+ if (session.hasSuperPickAxe()) {
+ if ("on".equals(newState)) {
+ player.printError("Super pick axe already enabled.");
+ BBC.SUPERPICKAXE_ENABLED.send(player);
+ return;
+ }
+
+ session.disableSuperPickAxe();
+ player.print("Super pick axe disabled.");
+ } else {
+ if ("off".equals(newState)) {
+ player.printError("Super pick axe already disabled.");
+ BBC.SUPERPICKAXE_DISABLED.send(player);
+ return;
+ }
+ session.enableSuperPickAxe();
+ player.print("Super pick axe enabled.");
+ BBC.SUPERPICKAXE_ENABLED.send(player);
+ }
+
+ }
+
+ @Command(
+ aliases = { "mask" },
+ usage = "[mask]",
+ desc = "Set the brush mask",
+ min = 0,
+ max = -1
+ )
+ @CommandPermissions("worldedit.brush.options.mask")
+ public void mask(Player player, LocalSession session, EditSession editSession, @Optional CommandContext context) throws WorldEditException {
+ if (context == null || context.argsLength() == 0) {
+ session.getBrushTool(player.getItemInHand()).setMask(null);
+ BBC.BRUSH_MASK_DISABLED.send(player);
+ } else {
+ ParserContext parserContext = new ParserContext();
+ parserContext.setActor(player);
+ parserContext.setWorld(player.getWorld());
+ parserContext.setSession(session);
+ parserContext.setExtent(editSession);
+ Mask mask = we.getMaskFactory().parseFromInput(context.getJoinedStrings(0), parserContext);
+ session.getBrushTool(player.getItemInHand()).setMask(mask);
+ BBC.BRUSH_MASK.send(player);
+ }
+ }
+
+ @Command(
+ aliases = { "mat", "material" },
+ usage = "[pattern]",
+ desc = "Set the brush material",
+ min = 1,
+ max = 1
+ )
+ @CommandPermissions("worldedit.brush.options.material")
+ public void material(Player player, LocalSession session, EditSession editSession, Pattern pattern) throws WorldEditException {
+ session.getBrushTool(player.getItemInHand()).setFill(pattern);
+ BBC.BRUSH_MATERIAL.send(player);
+ }
+
+ @Command(
+ aliases = { "range" },
+ usage = "[pattern]",
+ desc = "Set the brush range",
+ min = 1,
+ max = 1
+ )
+ @CommandPermissions("worldedit.brush.options.range")
+ public void range(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
+ int range = args.getInteger(0);
+ session.getBrushTool(player.getItemInHand()).setRange(range);
+ BBC.BRUSH_RANGE.send(player);
+ }
+
+ @Command(
+ aliases = { "size" },
+ usage = "[pattern]",
+ desc = "Set the brush size",
+ min = 1,
+ max = 1
+ )
+ @CommandPermissions("worldedit.brush.options.size")
+ public void size(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
+
+ int radius = args.getInteger(0);
+ we.checkMaxBrushRadius(radius);
+
+ session.getBrushTool(player.getItemInHand()).setSize(radius);
+ BBC.BRUSH_SIZE.send(player);
+ }
+
+ public static Class> inject() {
+ return ToolUtilCommands.class;
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java
new file mode 100644
index 00000000..a316515b
--- /dev/null
+++ b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java
@@ -0,0 +1,187 @@
+package com.sk89q.worldedit.extension.factory;
+
+import com.boydti.fawe.object.mask.AngleMask;
+import com.boydti.fawe.object.mask.DataMask;
+import com.boydti.fawe.object.mask.IdDataMask;
+import com.boydti.fawe.object.mask.IdMask;
+import com.boydti.fawe.object.mask.XAxisMask;
+import com.boydti.fawe.object.mask.YAxisMask;
+import com.boydti.fawe.object.mask.ZAxisMask;
+import com.sk89q.worldedit.IncompleteRegionException;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.extension.input.InputParseException;
+import com.sk89q.worldedit.extension.input.NoMatchException;
+import com.sk89q.worldedit.extension.input.ParserContext;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.function.mask.BiomeMask2D;
+import com.sk89q.worldedit.function.mask.BlockMask;
+import com.sk89q.worldedit.function.mask.ExistingBlockMask;
+import com.sk89q.worldedit.function.mask.ExpressionMask;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.MaskIntersection;
+import com.sk89q.worldedit.function.mask.Masks;
+import com.sk89q.worldedit.function.mask.NoiseFilter;
+import com.sk89q.worldedit.function.mask.OffsetMask;
+import com.sk89q.worldedit.function.mask.RegionMask;
+import com.sk89q.worldedit.function.mask.SolidBlockMask;
+import com.sk89q.worldedit.internal.expression.Expression;
+import com.sk89q.worldedit.internal.expression.ExpressionException;
+import com.sk89q.worldedit.internal.registry.InputParser;
+import com.sk89q.worldedit.math.noise.RandomNoise;
+import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment;
+import com.sk89q.worldedit.session.request.Request;
+import com.sk89q.worldedit.session.request.RequestSelection;
+import com.sk89q.worldedit.world.biome.BaseBiome;
+import com.sk89q.worldedit.world.biome.Biomes;
+import com.sk89q.worldedit.world.registry.BiomeRegistry;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Parses mask input strings.
+ */
+public class DefaultMaskParser extends InputParser {
+
+ public DefaultMaskParser(WorldEdit worldEdit) {
+ super(worldEdit);
+ }
+
+ @Override
+ public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
+ List masks = new ArrayList();
+
+ for (String component : input.split(" ")) {
+ if (component.isEmpty()) {
+ continue;
+ }
+
+ Mask current = getBlockMaskComponent(masks, component, context);
+
+ masks.add(current);
+ }
+
+ switch (masks.size()) {
+ case 0:
+ return null;
+
+ case 1:
+ return masks.get(0);
+
+ default:
+ return new MaskIntersection(masks);
+ }
+ }
+
+ private Mask getBlockMaskComponent(List masks, String component, ParserContext context) throws InputParseException {
+ Extent extent = Request.request().getEditSession();
+
+ final char firstChar = component.charAt(0);
+ switch (firstChar) {
+ case '#':
+ switch (component.toLowerCase()) {
+ case "#existing":
+ return new ExistingBlockMask(extent);
+ case "#solid":
+ return new SolidBlockMask(extent);
+ case "#dregion":
+ case "#dselection":
+ case "#dsel":
+ return new RegionMask(new RequestSelection());
+ case "#selection":
+ case "#region":
+ case "#sel":
+ try {
+ return new RegionMask(context.requireSession().getSelection(context.requireWorld()).clone());
+ } catch (IncompleteRegionException e) {
+ throw new InputParseException("Please make a selection first.");
+ }
+ case "#xaxis":
+ return new XAxisMask();
+ case "#yaxis":
+ return new YAxisMask();
+ case "#zaxis":
+ return new ZAxisMask();
+ case "#id":
+ return new IdMask(extent);
+ case "#data":
+ return new DataMask(extent);
+ case "#iddata":
+ return new IdDataMask(extent);
+ default:
+ throw new NoMatchException("Unrecognized mask '" + component + "'");
+ }
+ case '\\':
+ case '/': {
+ String[] split = component.substring(1).split(",");
+ if (split.length != 2) {
+ throw new InputParseException("Unknown angle '" + component + "' (not in form /#,#)");
+ }
+ try {
+ int y1 = Integer.parseInt(split[0]);
+ int y2 = Integer.parseInt(split[1]);
+ return new AngleMask(extent, y1, y2);
+ } catch (NumberFormatException e) {
+ throw new InputParseException("Unknown angle '" + component + "' (not in form /#,#)");
+ }
+ }
+ case '>':
+ case '<':
+ Mask submask;
+ if (component.length() > 1) {
+ submask = getBlockMaskComponent(masks, component.substring(1), context);
+ } else {
+ submask = new ExistingBlockMask(extent);
+ }
+ OffsetMask offsetMask = new OffsetMask(submask, new Vector(0, firstChar == '>' ? -1 : 1, 0));
+ return new MaskIntersection(offsetMask, Masks.negate(submask));
+
+ case '$':
+ Set biomes = new HashSet();
+ String[] biomesList = component.substring(1).split(",");
+ BiomeRegistry biomeRegistry = context.requireWorld().getWorldData().getBiomeRegistry();
+ List knownBiomes = biomeRegistry.getBiomes();
+ for (String biomeName : biomesList) {
+ BaseBiome biome = Biomes.findBiomeByName(knownBiomes, biomeName, biomeRegistry);
+ if (biome == null) {
+ throw new InputParseException("Unknown biome '" + biomeName + "'");
+ }
+ biomes.add(biome);
+ }
+
+ return Masks.asMask(new BiomeMask2D(context.requireExtent(), biomes));
+
+ case '%':
+ int i = Integer.parseInt(component.substring(1));
+ return new NoiseFilter(new RandomNoise(), ((double) i) / 100);
+
+ case '=':
+ try {
+ Expression exp = Expression.compile(component.substring(1), "x", "y", "z");
+ WorldEditExpressionEnvironment env = new WorldEditExpressionEnvironment(
+ Request.request().getEditSession(), Vector.ONE, Vector.ZERO);
+ exp.setEnvironment(env);
+ return new ExpressionMask(exp);
+ } catch (ExpressionException e) {
+ throw new InputParseException("Invalid expression: " + e.getMessage());
+ }
+
+ case '!':
+ if (component.length() > 1) {
+ return Masks.negate(getBlockMaskComponent(masks, component.substring(1), context));
+ }
+
+ default:
+ ParserContext tempContext = new ParserContext(context);
+ tempContext.setRestricted(false);
+ tempContext.setPreferringWildcard(true);
+ return new BlockMask(extent, worldEdit.getBlockFactory().parseFromListInput(component, tempContext));
+ }
+ }
+
+ public static Class> inject() {
+ return DefaultMaskParser.class;
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/sk89q/worldedit/function/mask/Masks.java b/core/src/main/java/com/sk89q/worldedit/function/mask/Masks.java
new file mode 100644
index 00000000..d5119042
--- /dev/null
+++ b/core/src/main/java/com/sk89q/worldedit/function/mask/Masks.java
@@ -0,0 +1,234 @@
+package com.sk89q.worldedit.function.mask;
+
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.LocalPlayer;
+import com.sk89q.worldedit.LocalSession;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.Vector2D;
+import com.sk89q.worldedit.session.request.Request;
+import javax.annotation.Nullable;
+
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Various utility functions related to {@link Mask} and {@link Mask2D}.
+ */
+public final class Masks {
+
+ private static final AlwaysTrue ALWAYS_TRUE = new AlwaysTrue();
+ private static final AlwaysFalse ALWAYS_FALSE = new AlwaysFalse();
+
+ private Masks() {
+ }
+
+ /**
+ * Return a 3D mask that always returns true;
+ *
+ * @return a mask
+ */
+ public static Mask alwaysTrue() {
+ return ALWAYS_TRUE;
+ }
+
+ /**
+ * Return a 2D mask that always returns true;
+ *
+ * @return a mask
+ */
+ public static Mask2D alwaysTrue2D() {
+ return ALWAYS_TRUE;
+ }
+
+ /**
+ * Negate the given mask.
+ *
+ * @param finalMask the mask
+ * @return a new mask
+ */
+ public static Mask negate(final Mask finalMask) {
+ if (finalMask instanceof AlwaysTrue) {
+ return ALWAYS_FALSE;
+ } else if (finalMask instanceof AlwaysFalse) {
+ return ALWAYS_TRUE;
+ }
+ checkNotNull(finalMask);
+ return new AbstractMask() {
+ private Mask mask = finalMask;
+ @Override
+ public boolean test(Vector vector) {
+ return !mask.test(vector);
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ Mask2D mask2d = mask.toMask2D();
+ if (mask2d != null) {
+ return negate(mask2d);
+ } else {
+ return null;
+ }
+ }
+ };
+ }
+
+ /**
+ * Negate the given mask.
+ *
+ * @param mask the mask
+ * @return a new mask
+ */
+ public static Mask2D negate(final Mask2D mask) {
+ if (mask instanceof AlwaysTrue) {
+ return ALWAYS_FALSE;
+ } else if (mask instanceof AlwaysFalse) {
+ return ALWAYS_TRUE;
+ }
+
+ checkNotNull(mask);
+ return new AbstractMask2D() {
+ @Override
+ public boolean test(Vector2D vector) {
+ return !mask.test(vector);
+ }
+ };
+ }
+
+ /**
+ * Return a 3-dimensional version of a 2D mask.
+ *
+ * @param mask the mask to make 3D
+ * @return a 3D mask
+ */
+ public static Mask asMask(final Mask2D mask) {
+ return new AbstractMask() {
+ @Override
+ public boolean test(Vector vector) {
+ return mask.test(vector.toVector2D());
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return mask;
+ }
+ };
+ }
+
+ /**
+ * Wrap an old-style mask and convert it to a new mask.
+ *
+ * Note, however, that this is strongly not recommended because
+ * {@link com.sk89q.worldedit.masks.Mask#prepare(LocalSession, LocalPlayer, Vector)}
+ * is not called.
+ *
+ * @param mask the old-style mask
+ * @param editSession the edit session to bind to
+ * @return a new-style mask
+ * @deprecated Please avoid if possible
+ */
+ @Deprecated
+ @SuppressWarnings("deprecation")
+ public static Mask wrap(final com.sk89q.worldedit.masks.Mask mask, final EditSession editSession) {
+ checkNotNull(mask);
+ return new AbstractMask() {
+ @Override
+ public boolean test(Vector vector) {
+ return mask.matches(editSession, vector);
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Wrap an old-style mask and convert it to a new mask.
+ *
+ * As an {@link EditSession} is not provided in this case, one will be
+ * taken from the {@link Request}, if possible. If not possible, then the
+ * mask will return false.
+ *
+ * @param mask the old-style mask
+ * @return a new-style mask
+ */
+ @SuppressWarnings("deprecation")
+ public static Mask wrap(final com.sk89q.worldedit.masks.Mask mask) {
+ checkNotNull(mask);
+ return new AbstractMask() {
+ @Override
+ public boolean test(Vector vector) {
+ EditSession editSession = Request.request().getEditSession();
+ return editSession != null && mask.matches(editSession, vector);
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Convert a new-style mask to an old-style mask.
+ *
+ * @param mask the new-style mask
+ * @return an old-style mask
+ */
+ @SuppressWarnings("deprecation")
+ public static com.sk89q.worldedit.masks.Mask wrap(final Mask mask) {
+ checkNotNull(mask);
+ return new com.sk89q.worldedit.masks.AbstractMask() {
+ @Override
+ public boolean matches(EditSession editSession, Vector position) {
+ Request.request().setEditSession(editSession);
+ return mask.test(position);
+ }
+ };
+ }
+
+ private static class AlwaysTrue implements Mask, Mask2D {
+ @Override
+ public boolean test(Vector vector) {
+ return true;
+ }
+
+ @Override
+ public boolean test(Vector2D vector) {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return this;
+ }
+ }
+
+ private static class AlwaysFalse implements Mask, Mask2D {
+ @Override
+ public boolean test(Vector vector) {
+ return false;
+ }
+
+ @Override
+ public boolean test(Vector2D vector) {
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return this;
+ }
+ }
+
+ public static Class> inject() {
+ return Masks.class;
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java b/core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java
new file mode 100644
index 00000000..78729eb1
--- /dev/null
+++ b/core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java
@@ -0,0 +1,92 @@
+package com.sk89q.worldedit.function.mask;
+
+import com.sk89q.worldedit.Vector;
+
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Checks whether another mask tests true for a position that is offset
+ * a given vector.
+ */
+public class OffsetMask extends AbstractMask {
+
+ private Mask mask;
+ private Vector offset;
+ private Vector mutable = new Vector();
+
+ /**
+ * Create a new instance.
+ *
+ * @param mask the mask
+ * @param offset the offset
+ */
+ public OffsetMask(Mask mask, Vector offset) {
+ checkNotNull(mask);
+ checkNotNull(offset);
+ this.mask = mask;
+ this.offset = offset;
+ }
+
+ /**
+ * Get the mask.
+ *
+ * @return the mask
+ */
+ public Mask getMask() {
+ return mask;
+ }
+
+ /**
+ * Set the mask.
+ *
+ * @param mask the mask
+ */
+ public void setMask(Mask mask) {
+ checkNotNull(mask);
+ this.mask = mask;
+ }
+
+ /**
+ * Get the offset.
+ *
+ * @return the offset
+ */
+ public Vector getOffset() {
+ return offset;
+ }
+
+ /**
+ * Set the offset.
+ *
+ * @param offset the offset
+ */
+ public void setOffset(Vector offset) {
+ checkNotNull(offset);
+ this.offset = offset;
+ }
+
+ @Override
+ public boolean test(Vector vector) {
+ mutable.x = vector.x + offset.x;
+ mutable.y = vector.y + offset.y;
+ mutable.z = vector.z + offset.z;
+ return getMask().test(mutable);
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ Mask2D childMask = getMask().toMask2D();
+ if (childMask != null) {
+ return new OffsetMask2D(childMask, getOffset().toVector2D());
+ } else {
+ return null;
+ }
+ }
+
+ public static Class> inject() {
+ return OffsetMask.class;
+ }
+}
diff --git a/core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java b/core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java
index e61e7eb7..d7279a70 100644
--- a/core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java
+++ b/core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java
@@ -120,6 +120,9 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
* Clamps the cuboid according to boundaries of the world.
*/
private void recalculate() {
+ if (pos1 == null || pos2 == null) {
+ return;
+ }
pos1 = pos1.clampY(0, world == null ? 255 : world.getMaxY());
pos2 = pos2.clampY(0, world == null ? 255 : world.getMaxY());
Vector min = getMinimumPoint();
diff --git a/core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java b/core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java
index 173916de..3022a16e 100644
--- a/core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java
+++ b/core/src/main/java/com/sk89q/worldedit/regions/selector/CuboidRegionSelector.java
@@ -88,7 +88,6 @@ public class CuboidRegionSelector extends com.sk89q.worldedit.regions.CuboidRegi
position1 = oldRegion.getMinimumPoint().toBlockVector();
position2 = oldRegion.getMaximumPoint().toBlockVector();
}
-
region.setPos1(position1);
region.setPos2(position2);
}