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 a30daefe..c5817742 100644 --- a/core/src/main/java/com/boydti/fawe/config/BBC.java +++ b/core/src/main/java/com/boydti/fawe/config/BBC.java @@ -149,6 +149,8 @@ public enum BBC { VISITOR_ENTITY("%s0 entities affected", "WorldEdit.Visitor"), VISITOR_FLAT("%s0 columns affected", "WorldEdit.Visitor"), + SELECTOR_FUZZY_POS1("Region set and expanded from %s0 %s1.", "WorldEdit.Selector"), + SELECTOR_FUZZY_POS2("Added expansion of %s0 %s1.", "WorldEdit.Selector"), SELECTOR_CUBOID_POS1("pos1 set to %s0 %s1.", "WorldEdit.Selector"), SELECTOR_CUBOID_POS2("pos2 set to %s0 %s1.", "WorldEdit.Selector"), SELECTOR_INVALID_COORDINATES("Invalid coordinates %s0", "WorldEdit.Selector"), diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java b/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java index 22cf61f6..ea278120 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java @@ -4,7 +4,6 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.Settings; import com.boydti.fawe.jnbt.NBTStreamer; -import com.boydti.fawe.jnbt.SchematicStreamer; import com.boydti.fawe.object.IntegerTrio; import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.io.BufferedRandomAccessFile; @@ -12,7 +11,6 @@ import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.ReflectionUtils; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.IntTag; -import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.EditSession; @@ -23,10 +21,8 @@ import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.regions.CuboidRegion; -import java.io.BufferedInputStream; import java.io.Closeable; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -34,7 +30,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.zip.GZIPInputStream; /** * A clipboard with disk backed storage. (lower memory + loads on crash) @@ -426,17 +421,6 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { } } - public static void main(String[] args) throws IOException{ - long start = System.currentTimeMillis(); - File file = new File("C:/Users/Jesse/Desktop/OTHER/mc/plugins/WorldEdit/schematics/50mil.schematic"); - BufferedInputStream in = new BufferedInputStream(new GZIPInputStream(new BufferedInputStream(new FileInputStream(file)))); - NBTInputStream nbtin = new NBTInputStream(in); - Settings.CLIPBOARD.USE_DISK = true; - SchematicStreamer streamer = new SchematicStreamer(nbtin, UUID.randomUUID()); - streamer.getClipboard(); - System.out.println(System.currentTimeMillis() - start); - } - @Override public void setData(int i, int data) { try { diff --git a/core/src/main/java/com/boydti/fawe/object/regions/MagicRegion.java b/core/src/main/java/com/boydti/fawe/object/regions/FuzzyRegion.java similarity index 54% rename from core/src/main/java/com/boydti/fawe/object/regions/MagicRegion.java rename to core/src/main/java/com/boydti/fawe/object/regions/FuzzyRegion.java index 638346b6..a93bf348 100644 --- a/core/src/main/java/com/boydti/fawe/object/regions/MagicRegion.java +++ b/core/src/main/java/com/boydti/fawe/object/regions/FuzzyRegion.java @@ -1,55 +1,92 @@ package com.boydti.fawe.object.regions; -import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.MathMan; import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.RegionFunction; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.function.visitor.RecursiveVisitor; import com.sk89q.worldedit.regions.AbstractRegion; import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.world.World; import java.util.BitSet; import java.util.Iterator; -public class MagicRegion extends AbstractRegion { +public class FuzzyRegion extends AbstractRegion { + private final Mask mask; private BitSet set = new BitSet(); + private boolean populated; private int minX, minY, minZ, maxX, maxY, maxZ; private int offsetX, offsetY, offsetZ; + private Extent extent; { minX = minY = minZ = Integer.MAX_VALUE; maxX = maxY = maxZ = Integer.MIN_VALUE; } - public MagicRegion(World world) { + public FuzzyRegion(World world, Extent editSession, Mask mask) { super(world); + this.extent = editSession; + this.mask = mask; + } + + public Mask getMask() { + return mask; } private static int pair(int x, int y, int z) { 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); - return (b1 & 0xFF) + int x16 = (x >> 8) & 0x7; + int z16 = (z >> 8) & 0x7; + byte b4 = MathMan.pair8(x16, z16); + return ((b1 & 0xFF) + ((b2 & 0xFF) << 8) + ((b3 & 0xFF) << 16) - + ((b4 & 0xFF) << 24) + + ((b4 & 0x7F) << 24)) ; } - public void select(int x, int y, int z) { - EditSession editSession = new EditSessionBuilder(getWorld()) - .limitUnlimited() - .changeSetNull() - .fastmode(true) - .allowedRegionsEverywhere() - .checkMemory(false) - .autoQueue(false) - .build(); + @Override + public int getArea() { + return set.length(); + } + public void select(int x, int y, int z) { + RecursiveVisitor visitor = new RecursiveVisitor(mask, new RegionFunction() { + @Override + public boolean apply(Vector position) throws WorldEditException { + return true; + } + }) { + @Override + public boolean isVisited(Node node) { + return contains(node.getX(), node.getY(), node.getZ()); + } + + @Override + public void addVisited(Node node, int depth) { + try { + set(node.getX(), node.getY(), node.getZ()); + } catch (RegionOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public void cleanVisited(int layer) { + // Do nothing + } + }; + visitor.visit(new Vector(x, y, z)); + Operations.completeBlindly(visitor); } @Override @@ -67,13 +104,13 @@ public class MagicRegion extends AbstractRegion { @Override public BlockVector next() { - int b1 = index & 0xFF; - int b2 = (index >> 8) & 0xFF; - int b3 = (index >> 16) & 0xFF; - byte b4 = (byte) ((index >> 24) & 0xFF); - pos.x = offsetX + (((b2 & 0xFF) + ((MathMan.unpair16x(b4)) << 8)) << 20) >> 20; + int b1 = ((byte) index) & 0xFF; + int b2 = ((byte) (index >> 8)) & 0xFF; + int b3 = ((byte)(index >> 16)) & 0xFF; + byte b4 = (byte) (index >> 24); + pos.x = offsetX + (((b2 + ((MathMan.unpair8x(b4)) << 8)) << 21) >> 21); pos.y = offsetY + b1; - pos.z = offsetZ + (((b3) + ((MathMan.unpair16y(b4)) << 8)) << 20) >> 20; + pos.z = offsetZ + (((b3 + ((MathMan.unpair8y(b4)) << 8)) << 21) >> 21); return pos; } @@ -84,13 +121,20 @@ public class MagicRegion extends AbstractRegion { }; } - public void set(int x, int y, int z) throws RegionOperationException{ - x -= offsetX; - y -= offsetY; - z -= offsetZ; + if (populated) { + x -= offsetX; + y -= offsetY; + z -= offsetZ; + } else { + offsetX = x; + offsetZ = z; + x = 0; + z = 0; + populated = true; + } set.set(pair(x, y, z), true); - if (x >= 2048 || x <= -2048 || z >= 2048 || z <= -2048) { + if (x >= 1024 || x <= -1024 || z >= 1024 || z <= -1024) { throw new RegionOperationException("Selection is too large!"); } if (x > maxX) { @@ -114,7 +158,11 @@ public class MagicRegion extends AbstractRegion { } public boolean contains(int x, int y, int z) { - return set.get(pair(x - offsetX, y - offsetY, z - offsetZ)); + try { + return set.get(pair(x - offsetX, y - offsetY, z - offsetZ)); + } catch (IndexOutOfBoundsException e) { + throw new RuntimeException(e); + } } @Override @@ -154,4 +202,8 @@ public class MagicRegion extends AbstractRegion { maxY += change.getBlockY(); maxZ += change.getBlockZ(); } + + public void setExtent(EditSession extent) { + this.extent = extent; + } } diff --git a/core/src/main/java/com/boydti/fawe/object/regions/selector/FuzzyRegionSelector.java b/core/src/main/java/com/boydti/fawe/object/regions/selector/FuzzyRegionSelector.java new file mode 100644 index 00000000..4db634a7 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/regions/selector/FuzzyRegionSelector.java @@ -0,0 +1,161 @@ +package com.boydti.fawe.object.regions.selector; + +import com.boydti.fawe.config.BBC; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.regions.FuzzyRegion; +import com.boydti.fawe.util.EditSessionBuilder; +import com.boydti.fawe.util.ExtentTraverser; +import com.boydti.fawe.util.MaskTraverser; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.RegionSelector; +import com.sk89q.worldedit.regions.selector.limit.SelectorLimits; +import com.sk89q.worldedit.world.World; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; + +public class FuzzyRegionSelector extends AbstractDelegateExtent implements RegionSelector { + + private final Player player; + private FuzzyRegion region; + private ArrayList positions; + + public FuzzyRegionSelector(Player player, @Nullable World world, Mask mask) { + super(new EditSessionBuilder(world) + .player(FawePlayer.wrap(player)) + .changeSetNull() + .checkMemory(false) + .autoQueue(false) + .build()); + this.player = player; + this.region = new FuzzyRegion(world, getExtent(), mask); + this.positions = new ArrayList<>(); + new MaskTraverser(mask).reset(getExtent()); + } + + @Nullable + @Override + public World getWorld() { + return this.region.getWorld(); + } + + @Override + public void setWorld(@Nullable World world) { + EditSession extent = new EditSessionBuilder(world) + .player(FawePlayer.wrap(player)) + .changeSetNull() + .checkMemory(false) + .autoQueue(false) + .build(); + new ExtentTraverser(this).setNext(extent); + this.region.setWorld(world); + this.region.setExtent(extent); + new MaskTraverser(getMask()).reset(extent); + } + + public Mask getMask() { + return region.getMask(); + } + + @Override + public boolean selectPrimary(Vector position, SelectorLimits limits) { + positions.clear(); + positions.add(position); + Extent extent = getExtent(); + if (extent instanceof EditSession) { + ((EditSession) extent).resetLimit(); + } + new MaskTraverser(getMask()).reset(extent); + this.region = new FuzzyRegion(getWorld(), getExtent(), getMask()); + this.region.select((int) position.x, (int) position.y, (int) position.z); + return true; + } + + @Override + public boolean selectSecondary(Vector position, SelectorLimits limits) { + this.positions.add(position); + new MaskTraverser(getMask()).reset(getExtent()); + this.region.select((int) position.x, (int) position.y, (int) position.z); + return true; + } + + @Override + public void explainPrimarySelection(Actor actor, LocalSession session, Vector position) { + int size = this.region.getArea(); + BBC.SELECTOR_FUZZY_POS1.send(player, position, "(" + region.getArea() + ")"); + } + + @Override + public void explainSecondarySelection(Actor actor, LocalSession session, Vector position) { + int size = this.region.getArea(); + BBC.SELECTOR_FUZZY_POS2.send(player, position, "(" + region.getArea() + ")"); + } + + @Override + public void explainRegionAdjust(Actor actor, LocalSession session) { + + } + + @Override + public BlockVector getPrimaryPosition() throws IncompleteRegionException { + if (positions.isEmpty()) { + throw new IncompleteRegionException(); + } + return new BlockVector(positions.get(0)); + } + + @Override + public Region getRegion() throws IncompleteRegionException { + return region; + } + + @Override + public Region getIncompleteRegion() { + return region; + } + + @Override + public boolean isDefined() { + return true; + } + + @Override + public int getArea() { + return region.getArea(); + } + + @Override + public void learnChanges() { + + } + + @Override + public void clear() { + positions.clear(); + this.region = new FuzzyRegion(getWorld(), getExtent(), getMask()); + } + + @Override + public String getTypeName() { + return "fuzzy"; + } + + @Override + public List getInformationLines() { + final List lines = new ArrayList(); + for (int i = 0; i < positions.size(); i++) { + lines.add("Position " + i + ": " + positions.get(i)); + } + return lines; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/regions/selector/MagicRegionSelector.java b/core/src/main/java/com/boydti/fawe/object/regions/selector/MagicRegionSelector.java deleted file mode 100644 index 43a9ecc1..00000000 --- a/core/src/main/java/com/boydti/fawe/object/regions/selector/MagicRegionSelector.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.boydti.fawe.object.regions.selector; - -import com.boydti.fawe.object.regions.MagicRegion; -import com.sk89q.worldedit.BlockVector; -import com.sk89q.worldedit.IncompleteRegionException; -import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.regions.RegionSelector; -import com.sk89q.worldedit.regions.selector.limit.SelectorLimits; -import com.sk89q.worldedit.world.World; -import java.util.List; -import javax.annotation.Nullable; - -public class MagicRegionSelector implements RegionSelector { - - private MagicRegion region; - - public MagicRegionSelector(@Nullable World world, Vector pos) { - this.region = new MagicRegion(world); - } - - @Nullable - @Override - public World getWorld() { - return this.region.getWorld(); - } - - @Override - public void setWorld(@Nullable World world) { - this.region.setWorld(world); - } - - @Override - public boolean selectPrimary(Vector position, SelectorLimits limits) { - - } - - @Override - public boolean selectSecondary(Vector position, SelectorLimits limits) { - - } - - @Override - public void explainPrimarySelection(Actor actor, LocalSession session, Vector position) { - - } - - @Override - public void explainSecondarySelection(Actor actor, LocalSession session, Vector position) { - - } - - @Override - public void explainRegionAdjust(Actor actor, LocalSession session) { - - } - - @Override - public BlockVector getPrimaryPosition() throws IncompleteRegionException { - return null; - } - - @Override - public Region getRegion() throws IncompleteRegionException { - return null; - } - - @Override - public Region getIncompleteRegion() { - return null; - } - - @Override - public boolean isDefined() { - return false; - } - - @Override - public int getArea() { - return 0; - } - - @Override - public void learnChanges() { - - } - - @Override - public void clear() { - - } - - @Override - public String getTypeName() { - return null; - } - - @Override - public List getInformationLines() { - return null; - } -} diff --git a/core/src/main/java/com/boydti/fawe/util/MathMan.java b/core/src/main/java/com/boydti/fawe/util/MathMan.java index 8dcd746b..9b96bd20 100644 --- a/core/src/main/java/com/boydti/fawe/util/MathMan.java +++ b/core/src/main/java/com/boydti/fawe/util/MathMan.java @@ -92,6 +92,18 @@ public class MathMan { return (byte) ((value >> 4) & 0xF); } + public static final byte pair8(int x, int y) { + return (byte) (x + (y << 3)); + } + + public static byte unpair8x(byte value) { + return (byte) (value & 0x7); + } + + public static byte unpair8y(byte value) { + return (byte) ((value >> 3) & 0x7F); + } + public static final int lossyFastDivide(int a, int b) { return (a*((1<<16)/b))>>16; } diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index b4d6a96c..3f2f6bbb 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -360,6 +360,10 @@ public class EditSession extends AbstractWorld implements HasFaweQueue { return originalLimit; } + public void resetLimit() { + this.limit = this.originalLimit.copy(); + } + /** * Returns a new limit representing how much of this edit's limit has been used so far * @return diff --git a/core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java b/core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java index 6ac397da..3dc26603 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java @@ -20,6 +20,8 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.config.BBC; +import com.boydti.fawe.object.mask.IdMask; +import com.boydti.fawe.object.regions.selector.FuzzyRegionSelector; import com.google.common.base.Optional; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; @@ -35,8 +37,10 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockType; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits; import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.regions.RegionSelector; @@ -56,11 +60,11 @@ import com.sk89q.worldedit.util.formatting.StyledFragment; import com.sk89q.worldedit.util.formatting.component.CommandListBox; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.storage.ChunkStore; - import java.util.ArrayList; import java.util.List; import java.util.Set; + import static com.sk89q.minecraft.util.commands.Logging.LogMode.POSITION; import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; @@ -748,6 +752,20 @@ public class SelectionCommands { if (limit.isPresent()) { player.print(limit.get() + " points maximum."); } + } else if (typeName.startsWith("fuzzy") || typeName.startsWith("magic")) { + Mask mask; + if (typeName.length() > 6) { + ParserContext parserContext = new ParserContext(); + parserContext.setActor(player); + parserContext.setWorld(player.getWorld()); + parserContext.setSession(session); + parserContext.setExtent(editSession); + mask = we.getMaskFactory().parseFromInput(typeName.substring(6),parserContext); + } else { + mask = new IdMask(editSession); + } + selector = new FuzzyRegionSelector(player, editSession, mask); + player.print("Fuzzy selector: Left click to select all contingent blocks, right click to add"); } else { CommandListBox box = new CommandListBox("Selection modes"); StyledFragment contents = box.getContents(); @@ -761,6 +779,7 @@ public class SelectionCommands { box.appendCommand("sphere", "Select a sphere"); box.appendCommand("cyl", "Select a cylinder"); box.appendCommand("convex", "Select a convex polyhedral"); + box.appendCommand("fuzzy[=]", "Select all connected blocks"); player.printRaw(ColorCodeBuilder.asColorCodes(box)); return; diff --git a/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java b/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java index d64875ee..0e555fa6 100644 --- a/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java +++ b/core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java @@ -58,10 +58,38 @@ public abstract class BreadthFirstSearch implements Operation { public void visit(final Vector pos) { Node node = new Node((int) pos.x, (int) pos.y, (int) pos.z); - if (!this.visited.containsKey(node)) { + if (!isVisited(node)) { isVisitable(pos, pos); // Ignore this, just to initialize mask on this point - visited.put(node, 0); queue.add(node); + addVisited(node, 0); + } + } + + public void addVisited(Node node, int depth) { + visited.put(node, depth); + } + + public boolean isVisited(Node node) { + return visited.containsKey(node); + } + + public void cleanVisited(int layer) { + if (layer == Integer.MAX_VALUE) { + visited.clear(); + return; + } + int size = visited.size(); + if (size > 16384) { + Iterator> iter = visited.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + Integer val = entry.getValue(); + if (val < layer) { + iter.remove(); + } else { + break; + } + } } } @@ -76,7 +104,7 @@ public abstract class BreadthFirstSearch implements Operation { for (int layer = 0; !queue.isEmpty() && layer <= maxDepth; layer++) { int size = queue.size(); if (layer == maxDepth) { - visited.clear(); + cleanVisited(Integer.MAX_VALUE); for (Node current : queue) { mutable.x = current.getX(); mutable.y = current.getY(); @@ -99,27 +127,15 @@ public abstract class BreadthFirstSearch implements Operation { mutable2.z = from.getZ() + direction.z; if (isVisitable(mutable, mutable2)) { adjacent = new Node(mutable2.getBlockX(), mutable2.getBlockY(), mutable2.getBlockZ()); - if (!visited.containsKey(adjacent)) { - visited.put(adjacent, layer); + if (!isVisited(adjacent)) { + addVisited(adjacent, layer); queue.add(adjacent); } } } } int lastLayer = layer - 1; - size = visited.size(); - if (shouldTrim || (shouldTrim = size > 16384)) { - Iterator> iter = visited.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - Integer val = entry.getValue(); - if (val < lastLayer) { - iter.remove(); - } else { - break; - } - } - } + cleanVisited(lastLayer); } return null; }