From 47999d323ba6e473ae164853459e722b04497552 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Mon, 13 Feb 2017 02:55:19 +1100 Subject: [PATCH] Fix some chunk loading issues Affected 1.8/1.7 Also improves performance for various operations --- .../fawe/bukkit/v1_7/BukkitQueue17.java | 2 +- .../fawe/bukkit/v1_8/BukkitQueue18R3.java | 2 +- .../com/boydti/fawe/object/FaweQueue.java | 8 +- .../fawe/object/brush/FlattenBrush.java | 3 +- .../boydti/fawe/object/brush/HeightBrush.java | 6 +- .../heightmap/FlatScalableHeightMap.java | 17 ++ .../brush/heightmap/ScalableHeightMap.java | 15 + .../object/clipboard/WorldCopyClipboard.java | 88 +++--- .../function/block/LegacyBlockReplace.java | 29 ++ .../fawe/object/visitor/Fast2DIterator.java | 114 ++++++++ .../object/visitor/FastChunkIterator.java | 77 +++++ .../fawe/object/visitor/FastIterator.java | 114 ++++++++ .../com/boydti/fawe/util/ExtentTraverser.java | 5 + .../java/com/sk89q/jnbt/NBTOutputStream.java | 1 - .../java/com/sk89q/worldedit/EditSession.java | 276 ++++++++---------- .../com/sk89q/worldedit/LocalSession.java | 2 +- .../worldedit/command/BiomeCommands.java | 60 ++-- .../worldedit/command/ClipboardCommands.java | 37 +-- .../worldedit/command/SchematicCommands.java | 2 + .../extent/clipboard/io/SchematicWriter.java | 54 ++-- .../function/visitor/RegionVisitor.java | 70 +++-- .../com/sk89q/worldedit/patterns/Pattern.java | 55 ++++ .../fawe/forge/v1710/ForgeQueue_All.java | 2 +- .../fawe/forge/v189/ForgeQueue_All.java | 2 +- 24 files changed, 762 insertions(+), 279 deletions(-) create mode 100644 core/src/main/java/com/boydti/fawe/object/brush/heightmap/FlatScalableHeightMap.java create mode 100644 core/src/main/java/com/boydti/fawe/object/function/block/LegacyBlockReplace.java create mode 100644 core/src/main/java/com/boydti/fawe/object/visitor/Fast2DIterator.java create mode 100644 core/src/main/java/com/boydti/fawe/object/visitor/FastChunkIterator.java create mode 100644 core/src/main/java/com/boydti/fawe/object/visitor/FastIterator.java create mode 100644 core/src/main/java/com/sk89q/worldedit/patterns/Pattern.java diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java index 8892e457..9cc1bb74 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java @@ -193,7 +193,7 @@ public class BukkitQueue17 extends BukkitQueue_0 size2) { + return 0; + } + return size; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java index 62e7f89b..1adfb322 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java @@ -28,6 +28,11 @@ public class ScalableHeightMap { public int size2; public int size; + public enum Shape { + CONE, + CYLINDER, + } + public ScalableHeightMap() { setSize(5); } @@ -51,6 +56,16 @@ public class ScalableHeightMap { return size - MathMan.sqrtApprox(d2); } + public static ScalableHeightMap fromShape(Shape shape) { + switch (shape) { + default: + case CONE: + return new ScalableHeightMap(); + case CYLINDER: + return new FlatScalableHeightMap(); + } + } + public static ScalableHeightMap fromClipboard(Clipboard clipboard) { Vector dim = clipboard.getDimensions(); byte[][] heightArray = new byte[dim.getBlockX()][dim.getBlockZ()]; diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java b/core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java index 4e97d7fb..70efe8b3 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java @@ -1,14 +1,19 @@ package com.boydti.fawe.object.clipboard; import com.boydti.fawe.object.RunnableVal2; +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.Tag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.function.RegionFunction; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.function.visitor.RegionVisitor; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import java.util.List; @@ -43,52 +48,61 @@ public class WorldCopyClipboard extends ReadOnlyClipboard { } @Override - public void forEach(RunnableVal2 task, boolean air) { + public void forEach(final RunnableVal2 task, boolean air) { + MainUtil.stacktrace(); Vector min = region.getMinimumPoint(); Vector max = region.getMaximumPoint(); - Vector pos = new Vector(); + final Vector pos = new Vector(); if (region instanceof CuboidRegion) { if (air) { - for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { - for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { - for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { - BaseBlock block = getBlockAbs(x, y, z); - pos.mutX(x - mx); - pos.mutY(y - my); - pos.mutZ(z - mz); - CompoundTag tag = block.getNbtData(); - if (tag != null) { - Map values = ReflectionUtils.getMap(tag.getValue()); - values.put("x", new IntTag(pos.getBlockX())); - values.put("y", new IntTag(pos.getBlockY())); - values.put("z", new IntTag(pos.getBlockZ())); - } - task.run(pos, block); + RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { + @Override + public boolean apply(Vector pos) throws WorldEditException { + int x = pos.getBlockX(); + int y = pos.getBlockY(); + int z = pos.getBlockZ(); + BaseBlock block = getBlockAbs(x, y, z); + pos.mutX(x - mx); + pos.mutY(y - my); + pos.mutZ(z - mz); + CompoundTag tag = block.getNbtData(); + if (tag != null) { + Map values = ReflectionUtils.getMap(tag.getValue()); + values.put("x", new IntTag(pos.getBlockX())); + values.put("y", new IntTag(pos.getBlockY())); + values.put("z", new IntTag(pos.getBlockZ())); } + task.run(pos, block); + return true; } - } + }, editSession); + Operations.completeBlindly(visitor); } else { - for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { - for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { - for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { - BaseBlock block = getBlockAbs(x, y, z); - if (block == EditSession.nullBlock) { - continue; - } - pos.mutX(x - mx); - pos.mutY(y - my); - pos.mutZ(z - mz); - CompoundTag tag = block.getNbtData(); - if (tag != null) { - Map values = ReflectionUtils.getMap(tag.getValue()); - values.put("x", new IntTag(pos.getBlockX())); - values.put("y", new IntTag(pos.getBlockY())); - values.put("z", new IntTag(pos.getBlockZ())); - } - task.run(pos, block); + RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { + @Override + public boolean apply(Vector pos) throws WorldEditException { + int x = pos.getBlockX(); + int y = pos.getBlockY(); + int z = pos.getBlockZ(); + BaseBlock block = getBlockAbs(x, y, z); + if (block == EditSession.nullBlock) { + return false; } + pos.mutX(x - mx); + pos.mutY(y - my); + pos.mutZ(z - mz); + CompoundTag tag = block.getNbtData(); + if (tag != null) { + Map values = ReflectionUtils.getMap(tag.getValue()); + values.put("x", new IntTag(pos.getBlockX())); + values.put("y", new IntTag(pos.getBlockY())); + values.put("z", new IntTag(pos.getBlockZ())); + } + task.run(pos, block); + return true; } - } + }, editSession); + Operations.completeBlindly(visitor); } } else { for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { diff --git a/core/src/main/java/com/boydti/fawe/object/function/block/LegacyBlockReplace.java b/core/src/main/java/com/boydti/fawe/object/function/block/LegacyBlockReplace.java new file mode 100644 index 00000000..19b4eb9a --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/function/block/LegacyBlockReplace.java @@ -0,0 +1,29 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package com.boydti.fawe.object.function.block; + +import com.google.common.base.Preconditions; +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.patterns.Pattern; + +public class LegacyBlockReplace implements RegionFunction { + private final Extent extent; + private Pattern pattern; + + public LegacyBlockReplace(Extent extent, Pattern pattern) { + Preconditions.checkNotNull(extent); + Preconditions.checkNotNull(pattern); + this.extent = extent; + this.pattern = pattern; + } + + public boolean apply(Vector position) throws WorldEditException { + return this.extent.setBlock(position, this.pattern.next(position)); + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/visitor/Fast2DIterator.java b/core/src/main/java/com/boydti/fawe/object/visitor/Fast2DIterator.java new file mode 100644 index 00000000..d7946e71 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/visitor/Fast2DIterator.java @@ -0,0 +1,114 @@ +package com.boydti.fawe.object.visitor; + +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.example.MappedFaweQueue; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.HasFaweQueue; +import com.boydti.fawe.util.ExtentTraverser; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.extent.Extent; +import java.util.Iterator; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class Fast2DIterator implements Iterable { + + private final Iterable iterable; + private final MappedFaweQueue queue; + + public Fast2DIterator(@Nonnull Iterable iter, @Nullable EditSession extent) { + this(iter, (HasFaweQueue) extent); + } + + public Fast2DIterator(@Nonnull Iterable iter, @Nullable Extent extent) { + this(iter, (HasFaweQueue) (extent != null ? (extent instanceof HasFaweQueue ? extent : new ExtentTraverser(extent).findAndGet(HasFaweQueue.class)) : null)); + } + + public Fast2DIterator(@Nonnull Iterable iter, @Nullable HasFaweQueue editSession) { + this(iter, (FaweQueue) editSession != null ? editSession.getQueue() : null); + } + + public Fast2DIterator(@Nonnull Iterable iter, @Nullable FaweQueue faweQueue) { + this.iterable = iter; + this.queue = faweQueue != null && faweQueue instanceof MappedFaweQueue ? (MappedFaweQueue) faweQueue : null; + } + + public Iterable getIterable() { + return iterable; + } + + @Override + public Iterator iterator() { + if (queue == null || Settings.IMP.QUEUE.PRELOAD_CHUNKS <= 1) { + return (Iterator) iterable; + } + return new Iterator() { + Iterator trailIter = iterable.iterator(); + Iterator leadIter = iterable.iterator(); + int lastTrailChunkX = Integer.MIN_VALUE; + int lastTrailChunkZ = Integer.MIN_VALUE; + int lastLeadChunkX = Integer.MIN_VALUE; + int lastLeadChunkZ = Integer.MIN_VALUE; + int loadingTarget = Settings.IMP.QUEUE.PRELOAD_CHUNKS; + int cx,cz; + + @Override + public void remove() { + trailIter.remove(); + } + + @Override + public boolean hasNext() { + return trailIter.hasNext(); + } + + @Override + public Vector2D next() { + Vector2D pt = trailIter.next(); + if (lastTrailChunkX != (lastTrailChunkX = pt.getBlockX() >> 4) || lastTrailChunkZ != (lastTrailChunkZ = pt.getBlockZ() >> 4)) { + if (leadIter.hasNext()) { + try { + int amount; + if (lastLeadChunkX == Integer.MIN_VALUE) { + lastLeadChunkX = cx; + lastLeadChunkZ = cz; + amount = loadingTarget; + } else { + amount = 1; + } + for (int count = 0; count < amount; ) { + Vector2D v = leadIter.next(); + int vcx = v.getBlockX() >> 4; + int vcz = v.getBlockZ() >> 4; + if (vcx != lastLeadChunkX || vcz != lastLeadChunkZ) { + lastLeadChunkX = vcx; + lastLeadChunkZ = vcz; + queue.queueChunkLoad(vcx, vcz); + count++; + } + // Skip the next 15 blocks + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + } + } catch (Throwable ignore) {} + } + } + return pt; + } + }; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/visitor/FastChunkIterator.java b/core/src/main/java/com/boydti/fawe/object/visitor/FastChunkIterator.java new file mode 100644 index 00000000..26406a06 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/visitor/FastChunkIterator.java @@ -0,0 +1,77 @@ +package com.boydti.fawe.object.visitor; + +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.example.MappedFaweQueue; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.HasFaweQueue; +import com.boydti.fawe.util.ExtentTraverser; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.extent.Extent; +import java.util.Iterator; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class FastChunkIterator implements Iterable { + + private final Iterable iterable; + private final MappedFaweQueue queue; + + public FastChunkIterator(@Nonnull Iterable iter, @Nullable EditSession extent) { + this(iter, (HasFaweQueue) extent); + } + + public FastChunkIterator(@Nonnull Iterable iter, @Nullable Extent extent) { + this(iter, (HasFaweQueue) (extent != null ? (extent instanceof HasFaweQueue ? extent : new ExtentTraverser(extent).findAndGet(HasFaweQueue.class)) : null)); + } + + public FastChunkIterator(@Nonnull Iterable iter, @Nullable HasFaweQueue editSession) { + this(iter, (FaweQueue) editSession != null ? editSession.getQueue() : null); + } + + public FastChunkIterator(@Nonnull Iterable iter, @Nullable FaweQueue faweQueue) { + this.iterable = iter; + this.queue = faweQueue != null && faweQueue instanceof MappedFaweQueue ? (MappedFaweQueue) faweQueue : null; + } + + public Iterable getIterable() { + return iterable; + } + + @Override + public Iterator iterator() { + if (queue == null || Settings.IMP.QUEUE.PRELOAD_CHUNKS <= 1) { + return (Iterator) iterable; + } + final Iterator trailIter = iterable.iterator(); + final Iterator leadIter = iterable.iterator(); + int amount = Settings.IMP.QUEUE.PRELOAD_CHUNKS; + for (int i = 0; i < Settings.IMP.QUEUE.PRELOAD_CHUNKS && leadIter.hasNext(); i++) { + Vector2D toLoad = leadIter.next(); + queue.queueChunkLoad(toLoad.getBlockX(), toLoad.getBlockZ()); + } + if (!leadIter.hasNext()) { + return (Iterator) trailIter; + } + return new Iterator() { + @Override + public void remove() { + trailIter.remove(); + } + + @Override + public boolean hasNext() { + return trailIter.hasNext(); + } + + @Override + public Vector2D next() { + if (leadIter.hasNext()) { + Vector2D toLoad = leadIter.next(); + queue.queueChunkLoad(toLoad.getBlockX(), toLoad.getBlockZ()); + } + return trailIter.next(); + } + }; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/visitor/FastIterator.java b/core/src/main/java/com/boydti/fawe/object/visitor/FastIterator.java new file mode 100644 index 00000000..6983ab4c --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/visitor/FastIterator.java @@ -0,0 +1,114 @@ +package com.boydti.fawe.object.visitor; + +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.example.MappedFaweQueue; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.HasFaweQueue; +import com.boydti.fawe.util.ExtentTraverser; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.extent.Extent; +import java.util.Iterator; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class FastIterator implements Iterable { + + private final Iterable iterable; + private final MappedFaweQueue queue; + + public FastIterator(@Nonnull Iterable iter, @Nullable EditSession extent) { + this(iter, (HasFaweQueue) extent); + } + + public FastIterator(@Nonnull Iterable iter, @Nullable Extent extent) { + this(iter, (HasFaweQueue) (extent != null ? (extent instanceof HasFaweQueue ? extent : new ExtentTraverser(extent).findAndGet(HasFaweQueue.class)) : null)); + } + + public FastIterator(@Nonnull Iterable iter, @Nullable HasFaweQueue editSession) { + this(iter, (FaweQueue) editSession != null ? editSession.getQueue() : null); + } + + public FastIterator(@Nonnull Iterable iter, @Nullable FaweQueue faweQueue) { + this.iterable = iter; + this.queue = faweQueue != null && faweQueue instanceof MappedFaweQueue ? (MappedFaweQueue) faweQueue : null; + } + + public Iterable getIterable() { + return iterable; + } + + @Override + public Iterator iterator() { + if (queue == null || Settings.IMP.QUEUE.PRELOAD_CHUNKS <= 1) { + return (Iterator) iterable; + } + return new Iterator() { + Iterator trailIter = iterable.iterator(); + Iterator leadIter = iterable.iterator(); + int lastTrailChunkX = Integer.MIN_VALUE; + int lastTrailChunkZ = Integer.MIN_VALUE; + int lastLeadChunkX = Integer.MIN_VALUE; + int lastLeadChunkZ = Integer.MIN_VALUE; + int loadingTarget = Settings.IMP.QUEUE.PRELOAD_CHUNKS; + int cx,cz; + + @Override + public void remove() { + trailIter.remove(); + } + + @Override + public boolean hasNext() { + return trailIter.hasNext(); + } + + @Override + public Vector next() { + Vector pt = trailIter.next(); + if (lastTrailChunkX != (lastTrailChunkX = pt.getBlockX() >> 4) || lastTrailChunkZ != (lastTrailChunkZ = pt.getBlockZ() >> 4)) { + if (leadIter.hasNext()) { + try { + int amount; + if (lastLeadChunkX == Integer.MIN_VALUE) { + lastLeadChunkX = cx; + lastLeadChunkZ = cz; + amount = loadingTarget; + } else { + amount = 1; + } + for (int count = 0; count < amount; ) { + Vector v = leadIter.next(); + int vcx = v.getBlockX() >> 4; + int vcz = v.getBlockZ() >> 4; + if (vcx != lastLeadChunkX || vcz != lastLeadChunkZ) { + lastLeadChunkX = vcx; + lastLeadChunkZ = vcz; + queue.queueChunkLoad(vcx, vcz); + count++; + } + // Skip the next 15 blocks + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + leadIter.next(); + } + } catch (Throwable ignore) {} + } + } + return pt; + } + }; + } +} diff --git a/core/src/main/java/com/boydti/fawe/util/ExtentTraverser.java b/core/src/main/java/com/boydti/fawe/util/ExtentTraverser.java index bd7f74a4..32fb3868 100644 --- a/core/src/main/java/com/boydti/fawe/util/ExtentTraverser.java +++ b/core/src/main/java/com/boydti/fawe/util/ExtentTraverser.java @@ -50,6 +50,11 @@ public class ExtentTraverser { } } + public U findAndGet(Class clazz) { + ExtentTraverser traverser = find(clazz); + return (traverser != null) ? (U) traverser.get() : null; + } + public ExtentTraverser find(Class clazz) { try { ExtentTraverser value = this; diff --git a/core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java b/core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java index 693367f4..19e2dfad 100644 --- a/core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java +++ b/core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java @@ -72,7 +72,6 @@ public final class NBTOutputStream implements Closeable { public void writeNamedTag(String name, Tag tag) throws IOException { checkNotNull(name); checkNotNull(tag); - int type = NBTUtils.getTypeCode(tag.getClass()); writeNamedTagName(name, type); if (type == NBTConstants.TYPE_END) { diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index 356ed607..1ce63737 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -52,8 +52,10 @@ import com.boydti.fawe.object.extent.ProcessedWEExtent; import com.boydti.fawe.object.extent.ResettableExtent; import com.boydti.fawe.object.extent.SlowExtent; import com.boydti.fawe.object.extent.SourceMaskExtent; +import com.boydti.fawe.object.function.block.LegacyBlockReplace; import com.boydti.fawe.object.mask.ResettableMask; import com.boydti.fawe.object.progress.DefaultProgressTracker; +import com.boydti.fawe.object.visitor.FastChunkIterator; import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.util.MaskTraverser; import com.boydti.fawe.util.MemUtil; @@ -76,6 +78,7 @@ import com.sk89q.worldedit.extent.MaskingExtent; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.extent.world.SurvivalModeExtent; import com.sk89q.worldedit.function.GroundFunction; +import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.RegionMaskingFilter; import com.sk89q.worldedit.function.block.BlockReplace; import com.sk89q.worldedit.function.block.Naturalizer; @@ -106,6 +109,7 @@ import com.sk89q.worldedit.history.change.BlockChange; import com.sk89q.worldedit.history.changeset.ChangeSet; import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.ExpressionException; +import com.sk89q.worldedit.internal.expression.runtime.EvaluationException; import com.sk89q.worldedit.internal.expression.runtime.RValue; import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation; import com.sk89q.worldedit.math.interpolation.Node; @@ -216,20 +220,6 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting player = FawePlayer.wrap(event.getActor()); } this.player = player; - if (changeSet == null) { - if (Settings.IMP.HISTORY.USE_DISK) { - UUID uuid = player == null ? CONSOLE : player.getUUID(); - if (Settings.IMP.HISTORY.USE_DATABASE) { - changeSet = new RollbackOptimizedHistory(world, uuid); - } else { - changeSet = new DiskStorageHistory(world, uuid); - } - } else if (Settings.IMP.HISTORY.COMBINE_STAGES && Settings.IMP.HISTORY.COMPRESSION_LEVEL == 0 && !(queue instanceof MCAQueue)) { - changeSet = new CPUOptimizedChangeSet(world); - } else { - changeSet = new MemoryOptimizedHistory(world); - } - } if (limit == null) { if (player == null) { limit = FaweLimit.MAX; @@ -286,7 +276,21 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting } this.bypassAll = wrapExtent(new FastWorldEditExtent(world, queue), bus, event, Stage.BEFORE_CHANGE); this.bypassHistory = (this.extent = wrapExtent(bypassAll, bus, event, Stage.BEFORE_REORDER)); - if (!fastmode) { + if (!fastmode || changeSet != null) { + if (changeSet == null) { + if (Settings.IMP.HISTORY.USE_DISK) { + UUID uuid = player == null ? CONSOLE : player.getUUID(); + if (Settings.IMP.HISTORY.USE_DATABASE) { + changeSet = new RollbackOptimizedHistory(world, uuid); + } else { + changeSet = new DiskStorageHistory(world, uuid); + } + } else if (Settings.IMP.HISTORY.COMBINE_STAGES && Settings.IMP.HISTORY.COMPRESSION_LEVEL == 0 && !(queue instanceof MCAQueue)) { + changeSet = new CPUOptimizedChangeSet(world); + } else { + changeSet = new MemoryOptimizedHistory(world); + } + } if (limit.SPEED_REDUCTION > 0) { this.bypassHistory = new SlowExtent(this.bypassHistory, limit.SPEED_REDUCTION); } @@ -610,6 +614,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting } public void addTransform(ResettableExtent transform) { + wrapped = true; if (transform == null) { ExtentTraverser traverser = new ExtentTraverser(this.extent).find(ResettableExtent.class); AbstractDelegateExtent next = extent; @@ -1152,9 +1157,9 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting */ @SuppressWarnings("deprecation") private int setBlocks(final Set vset, final Pattern pattern) throws MaxChangedBlocksException { - for (final Vector v : vset) { - changes += this.setBlock(v, pattern) ? 1 : 0; - } + RegionVisitor visitor = new RegionVisitor(vset, new LegacyBlockReplace(extent, pattern), this); + Operations.completeBlindly(visitor); + changes += visitor.getAffected(); return changes; } @@ -1219,12 +1224,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting context.setExtent(editSession.bypassAll); ChangeSet changeSet = getChangeSet(); editSession.getQueue().setChangeTask(null); - Operations.completeSmart(ChangeSetExecutor.create(changeSet, context, ChangeSetExecutor.Type.UNDO, editSession.getBlockBag(), editSession.getLimit().INVENTORY_MODE), new Runnable() { - @Override - public void run() { - editSession.flushQueue(); - } - }, true); + Operations.completeBlindly(ChangeSetExecutor.create(changeSet, context, ChangeSetExecutor.Type.UNDO, editSession.getBlockBag(), editSession.getLimit().INVENTORY_MODE)); editSession.changes = 1; } @@ -1238,12 +1238,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting context.setExtent(editSession.bypassAll); ChangeSet changeSet = getChangeSet(); editSession.getQueue().setChangeTask(null); - Operations.completeSmart(ChangeSetExecutor.create(changeSet, context, ChangeSetExecutor.Type.REDO, editSession.getBlockBag(), editSession.getLimit().INVENTORY_MODE), new Runnable() { - @Override - public void run() { - editSession.flushQueue(); - } - }, true); + Operations.completeBlindly(ChangeSetExecutor.create(changeSet, context, ChangeSetExecutor.Type.REDO, editSession.getBlockBag(), editSession.getLimit().INVENTORY_MODE)); editSession.changes = 1; } @@ -1340,12 +1335,15 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting return 0; } if (searchIDs.size() == 1) { - int count = 0; - int id = searchIDs.iterator().next(); - for (final Vector pt : region) { - if (this.getBlockType(pt) == id) count++; - } - return count; + final int id = searchIDs.iterator().next(); + RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { + @Override + public boolean apply(Vector position) throws WorldEditException { + return getBlockType(position) == id; + } + }, this); + Operations.completeBlindly(visitor); + return visitor.getAffected(); } final boolean[] ids = new boolean[256]; for (final int id : searchIDs) { @@ -1357,14 +1355,14 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting } public int countBlock(final Region region, final boolean[] ids) { - int count = 0; - for (final Vector pt : region) { - final int id = this.getBlockType(pt); - if (ids[id]) { - count++; + RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { + @Override + public boolean apply(Vector position) throws WorldEditException { + return ids[getBlockType(position)]; } - } - return count; + }, this); + Operations.completeBlindly(visitor); + return visitor.getAffected(); } /** @@ -1375,42 +1373,48 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting * @return the number of blocks that matched the pattern */ public int countBlocks(final Region region, final Set searchBlocks) { - BlockMask mask = new BlockMask(extent, searchBlocks); - int count = 0; - for (final Vector pt : region) { - if (mask.test(pt)) { - count++; + final BlockMask mask = new BlockMask(extent, searchBlocks); + RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { + @Override + public boolean apply(Vector position) throws WorldEditException { + return mask.test(position); } - } - return count; + }, this); + Operations.completeBlindly(visitor); + return visitor.getAffected(); } - public int fall(final Region region, boolean fullHeight, BaseBlock replace) { + public int fall(final Region region, boolean fullHeight, final BaseBlock replace) { FlatRegion flat = asFlatRegion(region); - int startPerformY = region.getMinimumPoint().getBlockY(); - int startCheckY = fullHeight ? 0 : startPerformY; - int endY = region.getMaximumPoint().getBlockY(); - for (BlockVector pos : flat) { - int x = pos.getBlockX(); - int z = pos.getBlockZ(); - int freeSpot = startCheckY; - for (int y = startCheckY; y <= endY; y++) { - if (y < startPerformY) { - if (getLazyBlock(x, y, z) != EditSession.nullBlock) { - freeSpot = y + 1; + final int startPerformY = region.getMinimumPoint().getBlockY(); + final int startCheckY = fullHeight ? 0 : startPerformY; + final int endY = region.getMaximumPoint().getBlockY(); + RegionVisitor visitor = new RegionVisitor(flat, new RegionFunction() { + @Override + public boolean apply(Vector pos) throws WorldEditException { + int x = pos.getBlockX(); + int z = pos.getBlockZ(); + int freeSpot = startCheckY; + for (int y = startCheckY; y <= endY; y++) { + if (y < startPerformY) { + if (getLazyBlock(x, y, z) != EditSession.nullBlock) { + freeSpot = y + 1; + } + continue; } - continue; - } - BaseBlock block = getLazyBlock(x, y, z); - if (block != EditSession.nullBlock) { - if (freeSpot != y) { - setBlock(x, freeSpot, z, block); - setBlock(x, y, z, replace); + BaseBlock block = getLazyBlock(x, y, z); + if (block != EditSession.nullBlock) { + if (freeSpot != y) { + setBlock(x, freeSpot, z, block); + setBlock(x, y, z, replace); + } + freeSpot++; } - freeSpot++; } + return true; } - } + }, this); + Operations.completeBlindly(visitor); return this.changes; } @@ -1466,12 +1470,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting visitor.visit(origin); // Execute - Operations.completeSmart(visitor, new Runnable() { - @Override - public void run() { - EditSession.this.flushQueue(); - } - }, true); + Operations.completeBlindly(visitor); return this.changes = visitor.getAffected(); } @@ -1565,6 +1564,10 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting return true; } + public boolean hasExtraExtents() { + return wrapped || getMask() != null || getSourceMask() != null || history != null; + } + /** * Sets all the blocks inside a region to a given block type. * @@ -1580,11 +1583,17 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting if (canBypassAll(region, false, true) && !block.hasNbtData()) { return changes = queue.setBlocks((CuboidRegion) region, block.getId(), block.getData()); } - Iterator iter = region.iterator(); try { - while (iter.hasNext()) { - if (this.extent.setBlock(iter.next(), block)) { - changes++; + if (hasExtraExtents()) { + RegionVisitor visitor = new RegionVisitor(region, new LegacyBlockReplace(extent, new SingleBlockPattern(block)), this); + Operations.completeBlindly(visitor); + this.changes += visitor.getAffected(); + } else { + Iterator iter = region.iterator(); + while (iter.hasNext()) { + if (this.extent.setBlock(iter.next(), block)) { + changes++; + } } } } catch (final MaxChangedBlocksException e) { @@ -1612,12 +1621,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting } final BlockReplace replace = new BlockReplace(EditSession.this, Patterns.wrap(pattern)); final RegionVisitor visitor = new RegionVisitor(region, replace, queue instanceof MappedFaweQueue ? (MappedFaweQueue) queue : null); - Operations.completeSmart(visitor, new Runnable() { - @Override - public void run() { - EditSession.this.flushQueue(); - } - }, true); + Operations.completeBlindly(visitor); return this.changes = visitor.getAffected(); } @@ -1682,12 +1686,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting final BlockReplace replace = new BlockReplace(EditSession.this, Patterns.wrap(pattern)); final RegionMaskingFilter filter = new RegionMaskingFilter(mask, replace); final RegionVisitor visitor = new RegionVisitor(region, filter, queue instanceof MappedFaweQueue ? (MappedFaweQueue) queue : null); - Operations.completeSmart(visitor, new Runnable() { - @Override - public void run() { - EditSession.this.flushQueue(); - } - }, true); + Operations.completeBlindly(visitor); return this.changes = visitor.getAffected(); } @@ -1866,12 +1865,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting final RegionOffset offset = new RegionOffset(new Vector(0, 1, 0), replace); final GroundFunction ground = new GroundFunction(new ExistingBlockMask(EditSession.this), offset); final LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground); - Operations.completeSmart(visitor, new Runnable() { - @Override - public void run() { - EditSession.this.flushQueue(); - } - }, true); + Operations.completeBlindly(visitor); return this.changes = ground.getAffected(); } @@ -1888,12 +1882,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting final Naturalizer naturalizer = new Naturalizer(EditSession.this); final FlatRegion flatRegion = Regions.asFlatRegion(region); final LayerVisitor visitor = new LayerVisitor(flatRegion, minimumBlockY(region), maximumBlockY(region), naturalizer); - Operations.completeSmart(visitor, new Runnable() { - @Override - public void run() { - EditSession.this.flushQueue(); - } - }, true); + Operations.completeBlindly(visitor); return this.changes = naturalizer.getAffected(); } @@ -1925,12 +1914,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting if (!copyAir) { copy.setSourceMask(new ExistingBlockMask(EditSession.this)); } - Operations.completeSmart(copy, new Runnable() { - @Override - public void run() { - EditSession.this.flushQueue(); - } - }, true); + Operations.completeBlindly(copy); return this.changes = copy.getAffected(); } @@ -1982,12 +1966,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting if (!copyAir) { copy.setSourceMask(new ExistingBlockMask(EditSession.this)); } - Operations.completeSmart(copy, new Runnable() { - @Override - public void run() { - EditSession.this.flushQueue(); - } - }, true); + Operations.completeBlindly(copy); return this.changes = copy.getAffected(); } @@ -2044,12 +2023,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting } } - Operations.completeSmart(visitor, new Runnable() { - @Override - public void run() { - EditSession.this.flushQueue(); - } - }, true); + Operations.completeBlindly(visitor); return this.changes = visitor.getAffected(); } @@ -2108,12 +2082,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting } } - Operations.completeSmart(visitor, new Runnable() { - @Override - public void run() { - EditSession.this.flushQueue(); - } - }, true); + Operations.completeBlindly(visitor); return visitor.getAffected(); } @@ -2524,12 +2493,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting final GroundFunction ground = new GroundFunction(new ExistingBlockMask(EditSession.this), generator); final LayerVisitor visitor = new LayerVisitor(region, minimumBlockY(region), maximumBlockY(region), ground); visitor.setMask(new NoiseFilter2D(new RandomNoise(), density)); - Operations.completeSmart(visitor, new Runnable() { - @Override - public void run() { - EditSession.this.flushQueue(); - } - }, true); + Operations.completeBlindly(visitor); return this.changes = ground.getAffected(); } @@ -2734,18 +2698,32 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting final RValue z = expression.getVariable("z", false); final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, unit, zero); expression.setEnvironment(environment); - - for (BlockVector position : region) { - // offset, scale - final Vector scaled = position.subtract(zero).divide(unit); - // transform - expression.evaluate(scaled.getX(), scaled.getY(), scaled.getZ()); - final BlockVector sourcePosition = environment.toWorld(x.getValue(), y.getValue(), z.getValue()); - // read block from world - BaseBlock material = FaweCache.CACHE_BLOCK[this.queue.getCombinedId4DataDebug(sourcePosition.getBlockX(), sourcePosition.getBlockY(), sourcePosition.getBlockZ(), 0, this)]; - // queue operation - this.setBlockFast(position, material); - } + RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { + + private MutableBlockVector mutable = new MutableBlockVector(); + + @Override + public boolean apply(Vector position) throws WorldEditException { + try { + int mx = (position.getBlockX() - zero.getBlockX()) / unit.getBlockX(); + int my = (position.getBlockY() - zero.getBlockY()) / unit.getBlockY(); + int mz = (position.getBlockZ() - zero.getBlockZ()) / unit.getBlockZ(); + mutable.setComponents(mx, my, mz); +// final Vector scaled = position.subtract(zero).divide(unit); + // transform + expression.evaluate(mutable.getX(), mutable.getY(), mutable.getZ()); + final BlockVector sourcePosition = environment.toWorld(x.getValue(), y.getValue(), z.getValue()); + // read block from world + BaseBlock material = FaweCache.CACHE_BLOCK[queue.getCombinedId4DataDebug(sourcePosition.getBlockX(), sourcePosition.getBlockY(), sourcePosition.getBlockZ(), 0, EditSession.this)]; + // queue operation + return extent.setBlock(position, material); + } catch (EvaluationException e) { + throw new RuntimeException(e); + } + } + }, this); + Operations.completeBlindly(visitor); + changes += visitor.getAffected(); return changes; } @@ -3044,7 +3022,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting for (final Vector recurseDirection : this.recurseDirections) { queue.addLast(current.add(recurseDirection).toBlockVector()); } - } // while + } } public int makeBiomeShape(final Region region, final Vector zero, final Vector unit, final BaseBiome biomeType, final String expressionString, final boolean hollow) throws ExpressionException, @@ -3160,7 +3138,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting } } final Set chunks = region.getChunks(); - for (Vector2D chunk : chunks) { + for (Vector2D chunk : new FastChunkIterator(chunks, this)) { final int cx = chunk.getBlockX(); final int cz = chunk.getBlockZ(); final int bx = cx << 4; diff --git a/core/src/main/java/com/sk89q/worldedit/LocalSession.java b/core/src/main/java/com/sk89q/worldedit/LocalSession.java index 13038979..bd0795cd 100644 --- a/core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -439,7 +439,7 @@ public class LocalSession { return null; } - public void remember(final EditSession editSession, final boolean append, final boolean sendMessage, int limitMb) { + public synchronized void remember(final EditSession editSession, final boolean append, final boolean sendMessage, int limitMb) { // It should have already been flushed, but just in case! editSession.flushQueue(); if (editSession == null || editSession.getChangeSet() == null || limitMb == 0 || ((historySize >> 20) > limitMb && !append)) { diff --git a/core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java b/core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java index 1462da3a..df010c92 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.config.BBC; +import com.boydti.fawe.object.visitor.Fast2DIterator; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; @@ -33,23 +34,26 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.function.FlatRegionFunction; import com.sk89q.worldedit.function.FlatRegionMaskingFilter; +import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.biome.BiomeReplace; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Mask2D; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.visitor.FlatRegionVisitor; +import com.sk89q.worldedit.function.visitor.RegionVisitor; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.FlatRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Regions; +import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.biome.BiomeData; import com.sk89q.worldedit.world.registry.BiomeRegistry; -import java.util.HashSet; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.Set; import static com.google.common.base.Preconditions.checkNotNull; @@ -124,11 +128,12 @@ public class BiomeCommands { max = 0 ) @CommandPermissions("worldedit.biome.info") - public void biomeInfo(Player player, LocalSession session, CommandContext args) throws WorldEditException { + public void biomeInfo(Player player, LocalSession session, final EditSession editSession, CommandContext args) throws WorldEditException { BiomeRegistry biomeRegistry = player.getWorld().getWorldData().getBiomeRegistry(); - Set biomes = new HashSet(); - String qualifier; + final int[] biomes = new int[256]; + final String qualifier; + int size = 0; if (args.hasFlag('t')) { Vector blockPosition = player.getBlockTrace(300); if (blockPosition == null) { @@ -137,34 +142,53 @@ public class BiomeCommands { } BaseBiome biome = player.getWorld().getBiome(blockPosition.toVector2D()); - biomes.add(biome); + biomes[biome.getId()]++; + size = 1; } else if (args.hasFlag('p')) { BaseBiome biome = player.getWorld().getBiome(player.getPosition().toVector2D()); - biomes.add(biome); + biomes[biome.getId()]++; + size = 1; } else { World world = player.getWorld(); Region region = session.getSelection(world); if (region instanceof FlatRegion) { - for (Vector2D pt : ((FlatRegion) region).asFlatRegion()) { - biomes.add(world.getBiome(pt)); + for (Vector2D pt : new Fast2DIterator(((FlatRegion) region).asFlatRegion(), editSession)) { + biomes[editSession.getBiome(pt).getId()]++; + size++; } } else { - for (Vector pt : region) { - biomes.add(world.getBiome(pt.toVector2D())); - } + RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { + @Override + public boolean apply(Vector position) throws WorldEditException { + biomes[editSession.getBiome(position.toVector2D()).getId()]++; + return true; + } + }, editSession); + Operations.completeBlindly(visitor); + size += visitor.getAffected(); } } BBC.BIOME_LIST_HEADER.send(player, 1, 1); - for (BaseBiome biome : biomes) { - BiomeData data = biomeRegistry.getData(biome); - if (data != null) { - player.print(BBC.getPrefix() + " " + data.getName()); - } else { - player.print(BBC.getPrefix() + " "); + + List> distribution = new ArrayList<>(); + for (int i = 0; i < biomes.length; i++) { + int count = biomes[i]; + if (count != 0) { + distribution.add(new Countable(new BaseBiome(i), count)); } } + Collections.sort(distribution); + for (Countable c : distribution) { + BiomeData data = biomeRegistry.getData(c.getID()); + String str = String.format("%-7s (%.3f%%) %s #%d", + String.valueOf(c.getAmount()), + c.getAmount() / (double) size * 100, + data == null ? "Unknown" : data.getName(), + c.getID().getId()); + player.print(BBC.getPrefix() + str); + } } @Command( diff --git a/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index ec8b6521..e6ea7e2b 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -35,7 +35,6 @@ import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Logging; -import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.Vector; @@ -48,6 +47,7 @@ import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; +import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.block.BlockReplace; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Masks; @@ -55,6 +55,7 @@ import com.sk89q.worldedit.function.operation.ForwardExtentCopy; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.visitor.RegionVisitor; import com.sk89q.worldedit.internal.annotation.Direction; import com.sk89q.worldedit.internal.annotation.Selection; import com.sk89q.worldedit.math.transform.AffineTransform; @@ -69,7 +70,6 @@ import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.parametric.Optional; import java.io.IOException; import java.net.URL; -import java.util.Iterator; import static com.google.common.base.Preconditions.checkNotNull; @@ -379,10 +379,10 @@ public class ClipboardCommands { @CommandPermissions("worldedit.clipboard.place") @Logging(PLACEMENT) public void place(Player player, LocalSession session, final EditSession editSession, - @Switch('a') boolean ignoreAirBlocks, @Switch('o') boolean atOrigin, + @Switch('a') final boolean ignoreAirBlocks, @Switch('o') boolean atOrigin, @Switch('s') boolean selectPasted) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); - Clipboard clipboard = holder.getClipboard(); + final Clipboard clipboard = holder.getClipboard(); Region region = clipboard.getRegion().clone(); final int maxY = editSession.getMaxY(); @@ -412,20 +412,23 @@ public class ClipboardCommands { final int relx = to.getBlockX() - origin.getBlockX(); final int rely = to.getBlockY() - origin.getBlockY(); final int relz = to.getBlockZ() - origin.getBlockZ(); - Iterator iter = region.iterator(); - while (iter.hasNext()) { - BlockVector mutable = iter.next(); - BaseBlock block = clipboard.getBlock(mutable); - if (block == EditSession.nullBlock && ignoreAirBlocks) { - continue; + RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { + @Override + public boolean apply(Vector mutable) throws WorldEditException { + BaseBlock block = clipboard.getBlock(mutable); + if (block == EditSession.nullBlock && ignoreAirBlocks) { + return false; + } + mutable.mutX(mutable.getX() + relx); + mutable.mutY(mutable.getY() + rely); + mutable.mutZ(mutable.getZ() + relz); + if (mutable.getY() >= 0 && mutable.getY() <= maxY) { + return editSession.setBlockFast(mutable, block); + } + return false; } - mutable.mutX(mutable.getX() + relx); - mutable.mutY(mutable.getY() + rely); - mutable.mutZ(mutable.getZ() + relz); - if (mutable.getY() >= 0 && mutable.getY() <= maxY) { - editSession.setBlockFast(mutable, block); - } - } + }, editSession); + Operations.completeBlindly(visitor); } // Entity offset is the paste location subtract the clipboard origin (entity's location is already relative to the world origin) final int entityOffsetX = to.getBlockX() - origin.getBlockX(); diff --git a/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index 7f779c06..6d9268c0 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -204,8 +204,10 @@ public class SchematicCommands { } } } catch (IllegalArgumentException e) { + e.printStackTrace(); player.printError("Unknown filename: " + filename); } catch (IOException e) { + e.printStackTrace(); player.printError("Schematic could not written: " + e.getMessage()); log.log(Level.WARNING, "Failed to write a saved clipboard", e); } diff --git a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicWriter.java b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicWriter.java index 09654632..babdc1e1 100644 --- a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicWriter.java +++ b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicWriter.java @@ -32,7 +32,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; import static com.google.common.base.Preconditions.checkNotNull; @@ -139,6 +138,10 @@ public class SchematicWriter implements ClipboardWriter { } final DataOutputStream rawStream = outputStream.getOutputStream(); outputStream.writeLazyCompoundTag("Schematic", new NBTOutputStream.LazyWrite() { + private boolean hasAdd = false; + private boolean hasTile = false; + private boolean hasData = false; + @Override public void write(NBTOutputStream out) throws IOException { int volume = width * height * length; @@ -154,28 +157,20 @@ public class SchematicWriter implements ClipboardWriter { out.writeNamedTag("WEOffsetY", new IntTag(offset.getBlockY())); out.writeNamedTag("WEOffsetZ", new IntTag(offset.getBlockZ())); - out.writeNamedTagName("Data", NBTConstants.TYPE_BYTE_ARRAY); - out.getOutputStream().writeInt(volume); - clipboard.IMP.streamDatas(new NBTStreamer.ByteReader() { - @Override - public void run(int index, int byteValue) { - try { - rawStream.writeByte(byteValue); - } catch (IOException e) { - e.printStackTrace(); - } - } - }); - out.writeNamedTagName("Blocks", NBTConstants.TYPE_BYTE_ARRAY); out.getOutputStream().writeInt(volume); - final AtomicBoolean hasAdd = new AtomicBoolean(false); clipboard.IMP.streamIds(new NBTStreamer.ByteReader() { @Override public void run(int index, int byteValue) { try { if (byteValue >= 256) { - hasAdd.set(true); + hasAdd = true; + } + if (FaweCache.hasData(byteValue)) { + hasData = true; + } + if (FaweCache.hasNBT(byteValue)) { + hasTile = true; } rawStream.writeByte(byteValue); } catch (IOException e) { @@ -184,7 +179,26 @@ public class SchematicWriter implements ClipboardWriter { } }); - if (hasAdd.get()) { + out.writeNamedTagName("Data", NBTConstants.TYPE_BYTE_ARRAY); + out.getOutputStream().writeInt(volume); + if (hasData) { + clipboard.IMP.streamDatas(new NBTStreamer.ByteReader() { + @Override + public void run(int index, int byteValue) { + try { + rawStream.writeByte(byteValue); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } else { + for (int i = 0; i < volume; i++) { + rawStream.write(0); + } + } + + if (hasAdd) { out.writeNamedTagName("AddBlocks", NBTConstants.TYPE_BYTE_ARRAY); out.getOutputStream().writeInt(volume); clipboard.IMP.streamIds(new NBTStreamer.ByteReader() { @@ -199,8 +213,10 @@ public class SchematicWriter implements ClipboardWriter { }); } - final List tileEntities = clipboard.IMP.getTileEntities(); - out.writeNamedTag("TileEntities", new ListTag(CompoundTag.class, tileEntities)); + if (hasTile) { + final List tileEntities = clipboard.IMP.getTileEntities(); + out.writeNamedTag("TileEntities", new ListTag(CompoundTag.class, tileEntities)); + } List entities = new ArrayList(); for (Entity entity : clipboard.getEntities()) { diff --git a/core/src/main/java/com/sk89q/worldedit/function/visitor/RegionVisitor.java b/core/src/main/java/com/sk89q/worldedit/function/visitor/RegionVisitor.java index d938b9dc..ffdba211 100644 --- a/core/src/main/java/com/sk89q/worldedit/function/visitor/RegionVisitor.java +++ b/core/src/main/java/com/sk89q/worldedit/function/visitor/RegionVisitor.java @@ -23,6 +23,7 @@ import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.example.MappedFaweQueue; import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.HasFaweQueue; import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.Vector; @@ -41,6 +42,7 @@ import java.util.List; public class RegionVisitor implements Operation { public final Region region; + public final Iterable iterable; public final RegionFunction function; private final MappedFaweQueue queue; public int affected = 0; @@ -60,9 +62,14 @@ public class RegionVisitor implements Operation { } public RegionVisitor(Region region, RegionFunction function, FaweQueue queue) { - this.region = region; + this((Iterable) region, function, queue); + } + + public RegionVisitor(Iterable iterable, RegionFunction function, HasFaweQueue hasQueue) { + region = (iterable instanceof Region) ? (Region) iterable : null; + this.iterable = iterable; this.function = function; - this.queue = queue instanceof MappedFaweQueue ? (MappedFaweQueue) queue : null; + this.queue = hasQueue != null && hasQueue.getQueue() instanceof MappedFaweQueue ? (MappedFaweQueue) hasQueue.getQueue() : null; } /** @@ -76,7 +83,7 @@ public class RegionVisitor implements Operation { @Override public Operation resume(final RunContext run) throws WorldEditException { - if (queue != null && Settings.IMP.QUEUE.PRELOAD_CHUNKS <= 1) { + if (queue != null && Settings.IMP.QUEUE.PRELOAD_CHUNKS > 1) { /* * The following is done to reduce iteration cost * - Preload chunks just in time @@ -84,8 +91,8 @@ public class RegionVisitor implements Operation { * - Stop iteration on exception instead of hasNext * - Do not calculate the stacktrace as it is expensive */ - Iterator trailIter = region.iterator(); - Iterator leadIter = region.iterator(); + Iterator trailIter = iterable.iterator(); + Iterator leadIter = iterable.iterator(); int lastTrailChunkX = Integer.MIN_VALUE; int lastTrailChunkZ = Integer.MIN_VALUE; int lastLeadChunkX = Integer.MIN_VALUE; @@ -93,8 +100,8 @@ public class RegionVisitor implements Operation { int loadingTarget = Settings.IMP.QUEUE.PRELOAD_CHUNKS; try { for (;;) { - BlockVector pt = trailIter.next(); - function.apply(pt); + Vector pt = trailIter.next(); + apply(pt); int cx = pt.getBlockX() >> 4; int cz = pt.getBlockZ() >> 4; if (cx != lastTrailChunkX || cz != lastTrailChunkZ) { @@ -109,7 +116,7 @@ public class RegionVisitor implements Operation { amount = 1; } for (int count = 0; count < amount;) { - BlockVector v = leadIter.next(); + Vector v = leadIter.next(); int vcx = v.getBlockX() >> 4; int vcz = v.getBlockZ() >> 4; if (vcx != lastLeadChunkX || vcz != lastLeadChunkZ) { @@ -136,40 +143,43 @@ public class RegionVisitor implements Operation { leadIter.next(); } } - function.apply(trailIter.next()); - function.apply(trailIter.next()); - function.apply(trailIter.next()); - function.apply(trailIter.next()); - function.apply(trailIter.next()); - function.apply(trailIter.next()); - function.apply(trailIter.next()); - function.apply(trailIter.next()); - function.apply(trailIter.next()); - function.apply(trailIter.next()); - function.apply(trailIter.next()); - function.apply(trailIter.next()); - function.apply(trailIter.next()); - function.apply(trailIter.next()); - function.apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); } } catch (Throwable ignore) {} try { for (;;) { - function.apply(trailIter.next()); - function.apply(trailIter.next()); + apply(trailIter.next()); + apply(trailIter.next()); } } catch (Throwable ignore) {} - affected = region.getArea(); } else { - for (Vector pt : region) { - if (function.apply(pt)) { - affected++; - } + for (Vector pt : iterable) { + apply(pt); } } return null; } + private void apply(Vector pt) throws WorldEditException { + if (function.apply(pt)) { + affected++; + } + } + @Override public void cancel() {} diff --git a/core/src/main/java/com/sk89q/worldedit/patterns/Pattern.java b/core/src/main/java/com/sk89q/worldedit/patterns/Pattern.java new file mode 100644 index 00000000..cd6ba723 --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/patterns/Pattern.java @@ -0,0 +1,55 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.patterns; + +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.blocks.BaseBlock; + +/** + * @deprecated See {@link com.sk89q.worldedit.function.pattern.Pattern} + */ +@Deprecated +public interface Pattern { //extends com.sk89q.worldedit.function.pattern.Pattern { + + /** + * Get a block for a position. This return value of this method does + * not have to be consistent for the same position. + * + * @param position the position where a block is needed + * @return a block + */ + public BaseBlock next(Vector position); + + /** + * Get a block for a position. This return value of this method does + * not have to be consistent for the same position. + * + * @param x the X coordinate + * @param y the Y coordinate + * @param z the Z coordinate + * @return a block + */ + public BaseBlock next(int x, int y, int z); + +// @Override +// default BaseBlock apply(Vector position) { +// return next(position); +// } +} diff --git a/forge1710/src/main/java/com/boydti/fawe/forge/v1710/ForgeQueue_All.java b/forge1710/src/main/java/com/boydti/fawe/forge/v1710/ForgeQueue_All.java index 8a81b54d..25a544bf 100644 --- a/forge1710/src/main/java/com/boydti/fawe/forge/v1710/ForgeQueue_All.java +++ b/forge1710/src/main/java/com/boydti/fawe/forge/v1710/ForgeQueue_All.java @@ -129,7 +129,7 @@ public class ForgeQueue_All extends NMSMappedFaweQueue