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