From 1c948cf0eddb6c6e3d18b03d8489d99e4d06e9b6 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Tue, 6 Dec 2016 15:57:24 +1100 Subject: [PATCH] Various Add #clipboard transform Add #fullcopy pattern - similar to transform, it pastes the full clipboard at any changed block - e.g. //replace #fullcopy Async block get optimizations for bukkit by running tasks multiple times during a single tick Tweak the OOM message to be more informative Tweak the max memory config comment to be more informative Restructured transforms to use resettable extent + reduce code duplication Clipboards can now be streamed to a schematic file without significant memory overhead - This means you can now load/paste/copy/save arbitrarily large sizes with fixed memory usage Optimizations to the various clipboard implementations Add optimized extent block translation (no additional object creation for set block) Optimized forward extent block copy - use optimized extent transform/translate - pre calculate required functions/extents outside iterations - short circuit certain functions depending on input parameters Use edit session for patterns rather than world (faster/safer) --- .../com/boydti/fawe/bukkit/FaweBukkit.java | 19 ++ .../fawe/bukkit/v1_11/FaweAdapter_1_11.java | 8 +- .../main/java/com/boydti/fawe/config/BBC.java | 2 +- .../java/com/boydti/fawe/config/Settings.java | 6 +- .../object/brush/DoubleActionBrushTool.java | 8 +- .../AbstractDelegateFaweClipboard.java | 16 ++ .../clipboard/CPUOptimizedClipboard.java | 74 ++++- .../clipboard/DiskOptimizedClipboard.java | 264 ++++++++++-------- .../fawe/object/clipboard/FaweClipboard.java | 45 +++ .../clipboard/MemoryOptimizedClipboard.java | 74 ++++- .../object/clipboard/ReadOnlyClipboard.java | 94 ++++++- .../object/extent/AffineTransformExtent.java | 138 --------- .../object/extent/BlockTranslateExtent.java | 64 +++++ .../fawe/object/extent/ClipboardExtent.java | 58 ++++ .../object/extent/DefaultTransformParser.java | 46 ++- .../fawe/object/extent/EmptyExtent.java | 69 +++++ .../fawe/object/extent/PatternTransform.java | 2 +- .../fawe/object/extent/ResettableExtent.java | 24 ++ .../fawe/object/extent/ScaleTransform.java | 4 +- .../fawe/object/extent/TransformExtent.java | 107 ++++++- .../function/block/SimpleBlockCopy.java | 22 ++ .../object/pattern/FullClipboardPattern.java | 47 ++++ .../general/plot/FaweSchematicHandler.java | 47 +--- .../java/com/boydti/fawe/util/SetQueue.java | 14 +- .../java/com/sk89q/jnbt/NBTOutputStream.java | 34 ++- .../java/com/sk89q/worldedit/EditSession.java | 8 +- .../com/sk89q/worldedit/LocalSession.java | 8 +- .../worldedit/command/ClipboardCommands.java | 36 +-- .../worldedit/command/GeneralCommands.java | 4 +- .../worldedit/command/ToolUtilCommands.java | 4 +- .../worldedit/command/tool/BrushTool.java | 8 +- .../factory/HashTagPatternParser.java | 20 +- .../extent/clipboard/io/SchematicWriter.java | 122 +++++++- .../transform/BlockTransformExtent.java | 157 ++++++++--- .../function/block/ExtentBlockCopy.java | 120 ++++++++ .../function/operation/ForwardExtentCopy.java | 53 +++- 36 files changed, 1352 insertions(+), 474 deletions(-) delete mode 100644 core/src/main/java/com/boydti/fawe/object/extent/AffineTransformExtent.java create mode 100644 core/src/main/java/com/boydti/fawe/object/extent/BlockTranslateExtent.java create mode 100644 core/src/main/java/com/boydti/fawe/object/extent/ClipboardExtent.java create mode 100644 core/src/main/java/com/boydti/fawe/object/extent/EmptyExtent.java create mode 100644 core/src/main/java/com/boydti/fawe/object/extent/ResettableExtent.java create mode 100644 core/src/main/java/com/boydti/fawe/object/function/block/SimpleBlockCopy.java create mode 100644 core/src/main/java/com/boydti/fawe/object/pattern/FullClipboardPattern.java create mode 100644 core/src/main/java/com/sk89q/worldedit/function/block/ExtentBlockCopy.java diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index 43eaf50d..599a87a9 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -21,6 +21,7 @@ import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.ReflectionUtils; +import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.TaskManager; import com.sk89q.worldedit.bukkit.EditSessionBlockChangeDelegate; import com.sk89q.worldedit.bukkit.WorldEditPlugin; @@ -37,6 +38,9 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.ChunkPopulateEvent; +import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.plugin.Plugin; import org.primesoft.blockshub.BlocksHubBukkit; @@ -352,6 +356,21 @@ public class FaweBukkit implements IFawe, Listener { return managers; } + @EventHandler + public void onChunkLoad(ChunkLoadEvent event) { + SetQueue.IMP.runMiscTasks(); + } + + @EventHandler + public void onChunkUnload(ChunkUnloadEvent event) { + SetQueue.IMP.runMiscTasks(); + } + + @EventHandler + public void onChunkPopulate(ChunkPopulateEvent event) { + SetQueue.IMP.runMiscTasks(); + } + @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_11/FaweAdapter_1_11.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_11/FaweAdapter_1_11.java index 5b16c35c..8ae8fdde 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_11/FaweAdapter_1_11.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_11/FaweAdapter_1_11.java @@ -30,12 +30,10 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Nullable; import net.minecraft.server.v1_11_R1.*; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Biome; import org.bukkit.block.Block; -import org.bukkit.craftbukkit.v1_11_R1.CraftServer; import org.bukkit.craftbukkit.v1_11_R1.CraftWorld; import org.bukkit.craftbukkit.v1_11_R1.block.CraftBlock; import org.bukkit.craftbukkit.v1_11_R1.entity.CraftEntity; @@ -47,11 +45,7 @@ public final class FaweAdapter_1_11 implements BukkitImplAdapter private final Field nbtListTagListField; private final Method nbtCreateTagMethod; - public FaweAdapter_1_11() - throws NoSuchFieldException, NoSuchMethodException - { - CraftServer.class.cast(Bukkit.getServer()); - + public FaweAdapter_1_11() throws NoSuchFieldException, NoSuchMethodException { this.nbtListTagListField = NBTTagList.class.getDeclaredField("list"); this.nbtListTagListField.setAccessible(true); 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 3bcbc96c..be07f18b 100644 --- a/core/src/main/java/com/boydti/fawe/config/BBC.java +++ b/core/src/main/java/com/boydti/fawe/config/BBC.java @@ -40,7 +40,7 @@ public enum BBC { WORLDEDIT_BYPASSED("&7Currently bypassing WorldEdit restriction.", "Info"), WORLDEDIT_UNMASKED("&6Your WorldEdit is now unrestricted.", "Info"), WORLDEDIT_RESTRICTED("&6Your WorldEdit is now restricted.", "Info"), - WORLDEDIT_OOM_ADMIN("&cPossible options:\n&8 - &7//fast\n&8 - &7Do smaller edits\n&8 - &7Allocate more memory\n&8 - &7Disable this safeguard", "Info"), + WORLDEDIT_OOM_ADMIN("&cPossible options:\n&8 - &7//fast\n&8 - &7Do smaller edits\n&8 - &7Allocate more memory\n&8 - &7Disable `max-memory-percent`", "Info"), COMPRESSED("History compressed. Saved ~ %s0b (%s1x smaller)", "Info"), ACTION_COMPLETE("Action completed in %s0 seconds", "Info"), 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 41daf173..d2c2df36 100644 --- a/core/src/main/java/com/boydti/fawe/config/Settings.java +++ b/core/src/main/java/com/boydti/fawe/config/Settings.java @@ -37,9 +37,9 @@ public class Settings extends Config { }) public static boolean REGION_RESTRICTIONS = true; @Comment({ - "FAWE will start cancelling non-admin edits if used-memory % exceeds", - "this value. Effects anyone who doesn't have bypass enabled", - "(e.g. /wea , or fastmode //fast , or fawe.bypass permission )." + "FAWE will cancel non admin edits when memory consumption exceeds this %", + " - Bypass with `/wea` or `//fast` or `fawe.bypass`", + " - Disable with 100 or -1." }) public static int MAX_MEMORY_PERCENT = 95; diff --git a/core/src/main/java/com/boydti/fawe/object/brush/DoubleActionBrushTool.java b/core/src/main/java/com/boydti/fawe/object/brush/DoubleActionBrushTool.java index 465ae125..0971e36e 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/DoubleActionBrushTool.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/DoubleActionBrushTool.java @@ -1,6 +1,6 @@ package com.boydti.fawe.object.brush; -import com.boydti.fawe.object.extent.TransformExtent; +import com.boydti.fawe.object.extent.ResettableExtent; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; @@ -30,7 +30,7 @@ public class DoubleActionBrushTool implements DoubleActionTraceTool { protected static int MAX_RANGE = 500; protected int range = -1; private Mask mask = null; - private TransformExtent transform = null; + private ResettableExtent transform = null; private DoubleActionBrush brush = null; @Nullable private Pattern material; @@ -52,11 +52,11 @@ public class DoubleActionBrushTool implements DoubleActionTraceTool { return player.hasPermission(permission); } - public TransformExtent getTransform() { + public ResettableExtent getTransform() { return transform; } - public void setTransform(TransformExtent transform) { + public void setTransform(ResettableExtent transform) { this.transform = transform; } diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/AbstractDelegateFaweClipboard.java b/core/src/main/java/com/boydti/fawe/object/clipboard/AbstractDelegateFaweClipboard.java index 44d27953..ced7a649 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/AbstractDelegateFaweClipboard.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/AbstractDelegateFaweClipboard.java @@ -1,5 +1,6 @@ package com.boydti.fawe.object.clipboard; +import com.boydti.fawe.jnbt.NBTStreamer; import com.boydti.fawe.object.RunnableVal2; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.Vector; @@ -80,4 +81,19 @@ public class AbstractDelegateFaweClipboard extends FaweClipboard { public void forEach(RunnableVal2 task, boolean air) { parent.forEach(task, air); } + + @Override + public void streamIds(NBTStreamer.ByteReader task) { + parent.streamIds(task); + } + + @Override + public void streamDatas(NBTStreamer.ByteReader task) { + parent.streamDatas(task); + } + + @Override + public List getTileEntities() { + return parent.getTileEntities(); + } } diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java b/core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java index e0007d2a..822f561f 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java @@ -1,9 +1,13 @@ package com.boydti.fawe.object.clipboard; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.jnbt.NBTStreamer; import com.boydti.fawe.object.IntegerTrio; import com.boydti.fawe.object.RunnableVal2; +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.Vector; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.entity.BaseEntity; @@ -50,7 +54,7 @@ public class CPUOptimizedClipboard extends FaweClipboard { } for (Map.Entry entry : nbtMapLoc.entrySet()) { IntegerTrio key = entry.getKey(); - nbtMapIndex.put(getIndex(key.x, key.y, key.z), entry.getValue()); + setTile(getIndex(key.x, key.y, key.z), entry.getValue()); } nbtMapLoc.clear(); } @@ -170,12 +174,78 @@ public class CPUOptimizedClipboard extends FaweClipboard { } } + @Override + public void streamIds(NBTStreamer.ByteReader task) { + int index = 0; + if (add != null) { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + int id = getId(index) + (getAdd(index) << 8); + task.run(index++, id); + } + } + } + } else { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + int id = getId(index); + task.run(index++, id); + } + } + } + } + } + + @Override + public void streamDatas(NBTStreamer.ByteReader task) { + int index = 0; + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + int data = getData(index); + task.run(index++, data); + } + } + } + } + + @Override + public List getTileEntities() { + convertTilesToIndex(); + for (Map.Entry entry : nbtMapIndex.entrySet()) { + int index = entry.getKey(); + CompoundTag tag = entry.getValue(); + Map values = ReflectionUtils.getMap(tag.getValue()); + if (!values.containsKey("x")) { + int y = index / area; + index -= y * area; + int z = index / width; + int x = index - (z * width); + values.put("x", new IntTag(x)); + values.put("y", new IntTag(y)); + values.put("z", new IntTag(z)); + } + } + return new ArrayList<>(nbtMapIndex.values()); + } + @Override public boolean setTile(int x, int y, int z, CompoundTag tag) { nbtMapLoc.put(new IntegerTrio(x, y, z), tag); return true; } + public boolean setTile(int index, CompoundTag tag) { + nbtMapIndex.put(index, tag); + Map values = ReflectionUtils.getMap(tag.getValue()); + values.remove("x"); + values.remove("y"); + values.remove("z"); + return true; + } + @Override public boolean setBlock(int x, int y, int z, BaseBlock block) { return setBlock(getIndex(x, y, z), block); @@ -190,7 +260,7 @@ public class CPUOptimizedClipboard extends FaweClipboard { } CompoundTag tile = block.getNbtData(); if (tile != null) { - nbtMapIndex.put(index, tile); + setTile(index, tile); } return true; } 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 fc255e2f..ae022383 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 @@ -3,11 +3,15 @@ package com.boydti.fawe.object.clipboard; 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.object.IntegerTrio; import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.io.BufferedRandomAccessFile; 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.BlockVector; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.Vector; @@ -20,12 +24,11 @@ import com.sk89q.worldedit.regions.CuboidRegion; import java.io.Closeable; import java.io.File; import java.io.IOException; -import java.io.PrintWriter; -import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.UUID; /** @@ -51,7 +54,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { private final File file; private final byte[] buffer; - private volatile BufferedRandomAccessFile raf; + private final BufferedRandomAccessFile raf; private long lastAccessed; private int last; @@ -59,21 +62,26 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { this(width, height, length, MainUtil.getFile(Fawe.imp().getDirectory(), Settings.PATHS.CLIPBOARD + File.separator + uuid + ".bd")); } - public DiskOptimizedClipboard(File file) throws IOException { - nbtMap = new HashMap<>(); - entities = new HashSet<>();this.buffer = new byte[2]; - this.file = file; - this.lastAccessed = System.currentTimeMillis(); - this.raf = new BufferedRandomAccessFile(file, "rw", Settings.HISTORY.BUFFER_SIZE); - raf.setLength(file.length()); - long size = (raf.length() - HEADER_SIZE) >> 1; - raf.seek(2); - last = Integer.MIN_VALUE; - width = (int) raf.readChar(); - height = (int) raf.readChar(); - length = (int) raf.readChar(); - area = width * length; - autoCloseTask(); + public DiskOptimizedClipboard(File file) { + try { + nbtMap = new HashMap<>(); + entities = new HashSet<>(); + this.buffer = new byte[2]; + this.file = file; + this.lastAccessed = System.currentTimeMillis(); + this.raf = new BufferedRandomAccessFile(file, "rw", 16); + raf.setLength(file.length()); + long size = (raf.length() - HEADER_SIZE) >> 1; + raf.seek(2); + last = Integer.MIN_VALUE; + width = (int) raf.readChar(); + height = (int) raf.readChar(); + length = (int) raf.readChar(); + area = width * length; + autoCloseTask(); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override @@ -89,9 +97,6 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { return true; } }; - if (raf == null) { - open(); - } raf.seek(8); last = Integer.MIN_VALUE; int ox = raf.readShort(); @@ -107,35 +112,41 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { } public DiskOptimizedClipboard(int width, int height, int length, File file) { - nbtMap = new HashMap<>(); - entities = new HashSet<>(); - this.file = file; - this.buffer = new byte[2]; - this.lastAccessed = System.currentTimeMillis(); - this.width = width; - this.height = height; - this.length = length; - this.area = width * length; try { - if (!file.exists()) { - file.getParentFile().mkdirs(); - file.createNewFile(); - } else { - PrintWriter writer = new PrintWriter(file); - writer.print(""); - writer.close(); + nbtMap = new HashMap<>(); + entities = new HashSet<>(); + this.file = file; + this.buffer = new byte[2]; + this.lastAccessed = System.currentTimeMillis(); + this.width = width; + this.height = height; + this.length = length; + this.area = width * length; + try { + if (!file.exists()) { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + } catch (Exception e) { + MainUtil.handleError(e); } - } catch (Exception e) { - MainUtil.handleError(e); + this.raf = new BufferedRandomAccessFile(file, "rw", 16); + long volume = width * height * length * 2l + HEADER_SIZE; + raf.setLength(volume); + // write length etc + raf.seek(2); + last = Integer.MIN_VALUE; + raf.writeChar(width); + raf.writeChar(height); + raf.writeChar(length); + } catch (IOException e) { + throw new RuntimeException(e); } } @Override public void setOrigin(Vector offset) { try { - if (raf == null) { - open(); - } raf.seek(8); last = Integer.MIN_VALUE; raf.writeShort(offset.getBlockX()); @@ -149,9 +160,6 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { @Override public void setDimensions(Vector dimensions) { try { - if (raf == null) { - open(); - } width = dimensions.getBlockX(); height = dimensions.getBlockY(); length = dimensions.getBlockZ(); @@ -163,7 +171,6 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { raf.writeChar(width); raf.writeChar(height); raf.writeChar(length); - raf.flush(); } catch (IOException e) { MainUtil.handleError(e); } @@ -171,10 +178,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { public void flush() { try { - raf.close(); - raf = null; - file.setWritable(true); - System.gc(); + raf.flush(); } catch (IOException e) { MainUtil.handleError(e); } @@ -186,36 +190,14 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { public void close() { try { - raf.flush(); - RandomAccessFile tmp = raf; - raf = null; - tmp.close(); - tmp = null; + raf.close(); + file.setWritable(true); System.gc(); } catch (IOException e) { MainUtil.handleError(e); } } - public void open() throws IOException { - if (raf != null) { - close(); - } - lastAccessed = System.currentTimeMillis(); - this.raf = new BufferedRandomAccessFile(file, "rw", Settings.HISTORY.BUFFER_SIZE); - long size = width * height * length * 2l + HEADER_SIZE; - if (raf.length() != size) { - raf.setLength(size); - // write length etc - raf.seek(2); - last = Integer.MIN_VALUE; - raf.writeChar(width); - raf.writeChar(height); - raf.writeChar(length); - } - autoCloseTask(); - } - private void autoCloseTask() { // TaskManager.IMP.laterAsync(new Runnable() { // @Override @@ -236,43 +218,97 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { private int zlast; private int zlasti; + @Override + public void streamIds(NBTStreamer.ByteReader task) { + try { + raf.seek(HEADER_SIZE); + int index = 0; + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + int combinedId = raf.readChar(); + task.run(index++, FaweCache.getId(combinedId)); + } + } + } + } catch (IOException e) { + MainUtil.handleError(e); + } + } + + @Override + public void streamDatas(NBTStreamer.ByteReader task) { + try { + raf.seek(HEADER_SIZE); + int index = 0; + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + int combinedId = raf.readChar(); + task.run(index++, FaweCache.getData(combinedId)); + } + } + } + } catch (IOException e) { + MainUtil.handleError(e); + } + } + + @Override + public List getTileEntities() { + return new ArrayList<>(nbtMap.values()); + } + @Override public void forEach(final RunnableVal2 task, boolean air) { try { - if (raf == null) { - open(); - } raf.seek(HEADER_SIZE); BlockVector pos = new BlockVector(0, 0, 0); - int x = 0; - int y = 0; - int z = 0; - long len = (raf.length()); - for (long i = HEADER_SIZE; i < len; i+=2) { - pos.x = x; - pos.y = y; - pos.z = z; - if (++x >= width) { - x = 0; - if (++z >= length) { - z = 0; - ++y; + IntegerTrio trio = new IntegerTrio(); + if (air) { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + int combinedId = raf.readChar(); + BaseBlock block = FaweCache.CACHE_BLOCK[combinedId]; + if (FaweCache.hasNBT(block.getId())) { + trio.set(x, y, z); + CompoundTag nbt = nbtMap.get(trio); + if (nbt != null) { + block = new BaseBlock(block.getId(), block.getData()); + block.setNbtData(nbt); + } + } + pos.x = x; + pos.y = y; + pos.z = z; + task.run(pos, block); + } } } - raf.seek(i); - int combinedId = raf.readChar(); - if (combinedId == 0 && !air) { - continue; - } - BaseBlock block = FaweCache.CACHE_BLOCK[combinedId]; - if (FaweCache.hasNBT(block.getId())) { - CompoundTag nbt = nbtMap.get(new IntegerTrio((int) pos.x, (int) pos.y, (int) pos.z)); - if (nbt != null) { - block = new BaseBlock(block.getId(), block.getData()); - block.setNbtData(nbt); + } else { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + int combinedId = raf.readChar(); + if (combinedId != 0) { + BaseBlock block = FaweCache.CACHE_BLOCK[combinedId]; + if (FaweCache.hasNBT(block.getId())) { + trio.set(x, y, z); + CompoundTag nbt = nbtMap.get(trio); + if (nbt != null) { + block = new BaseBlock(block.getId(), block.getData()); + block.setNbtData(nbt); + } + } + pos.x = x; + pos.y = y; + pos.z = z; + task.run(pos, block); + } + } } } - task.run(pos, block); } } catch (IOException e) { MainUtil.handleError(e); @@ -286,9 +322,6 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { @Override public BaseBlock getBlock(int x, int y, int z) { try { - if (raf == null) { - open(); - } int i = getIndex(x, y, z); if (i != last + 1) { raf.seek((HEADER_SIZE) + (i << 1)); @@ -314,15 +347,16 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { @Override public boolean setTile(int x, int y, int z, CompoundTag tag) { nbtMap.put(new IntegerTrio(x, y, z), tag); + Map values = ReflectionUtils.getMap(tag.getValue()); + values.put("x", new IntTag(x)); + values.put("y", new IntTag(y)); + values.put("z", new IntTag(z)); return true; } @Override public boolean setBlock(int x, int y, int z, BaseBlock block) { try { - if (raf == null) { - open(); - } int i = x + ((ylast == y) ? ylasti : (ylasti = ((ylast = y)) * area)) + ((zlast == z) ? zlasti : (zlasti = (zlast = z) * width)); if (i != last + 1) { raf.seek((HEADER_SIZE) + (i << 1)); @@ -334,7 +368,7 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { int combined = (id << 4) + data; raf.writeChar(combined); if (FaweCache.hasNBT(id)) { - nbtMap.put(new IntegerTrio(x, y, z), block.getNbtData()); + setTile(x, y, z, block.getNbtData()); } return true; } catch (Exception e) { @@ -346,9 +380,6 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { @Override public void setId(int i, int id) { try { - if (raf == null) { - open(); - } if (i != last + 1) { raf.seek((HEADER_SIZE) + (i << 1)); lastAccessed = System.currentTimeMillis(); @@ -367,9 +398,6 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { public void setCombined(int i, int combined) { try { - if (raf == null) { - open(); - } if (i != last + 1) { raf.seek((HEADER_SIZE) + (i << 1)); lastAccessed = System.currentTimeMillis(); @@ -384,9 +412,6 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { @Override public void setAdd(int i, int add) { try { - if (raf == null) { - open(); - } if (i != last + 1) { raf.seek((HEADER_SIZE) + (i << 1)); lastAccessed = System.currentTimeMillis(); @@ -404,9 +429,6 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { @Override public void setData(int i, int data) { try { - if (raf == null) { - open(); - } if (i != last + 1) { raf.seek((HEADER_SIZE) + (i << 1)); lastAccessed = System.currentTimeMillis(); diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/FaweClipboard.java b/core/src/main/java/com/boydti/fawe/object/clipboard/FaweClipboard.java index e07325db..58927f0a 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/FaweClipboard.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/FaweClipboard.java @@ -1,14 +1,20 @@ package com.boydti.fawe.object.clipboard; +import com.boydti.fawe.jnbt.NBTStreamer; import com.boydti.fawe.object.RunnableVal2; +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.Vector; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.util.Location; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import javax.annotation.Nullable; @@ -46,6 +52,45 @@ public abstract class FaweClipboard { */ public abstract void forEach(final RunnableVal2 task, boolean air); + public void streamIds(final NBTStreamer.ByteReader task) { + forEach(new RunnableVal2() { + private int index = 0; + @Override + public void run(Vector pos, BaseBlock block) { + task.run(index++, block.getId()); + } + }, true); + } + + public void streamDatas(final NBTStreamer.ByteReader task) { + forEach(new RunnableVal2() { + private int index = 0; + @Override + public void run(Vector pos, BaseBlock block) { + task.run(index++, block.getData()); + } + }, true); + } + + public List getTileEntities() { + final List tiles = new ArrayList<>(); + forEach(new RunnableVal2() { + private int index = 0; + @Override + public void run(Vector pos, BaseBlock block) { + CompoundTag tag = block.getNbtData(); + if (tag != null) { + Map values = ReflectionUtils.getMap(tag.getValue()); + values.put("x", new IntTag((int) pos.x)); + values.put("y", new IntTag((int) pos.y)); + values.put("z", new IntTag((int) pos.z)); + tiles.add(tag); + } + } + }, false); + return tiles; + } + /** * Stores entity data. */ diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java b/core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java index b65a66e1..61ea905f 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java @@ -2,10 +2,14 @@ package com.boydti.fawe.object.clipboard; import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.jnbt.NBTStreamer; import com.boydti.fawe.object.IntegerTrio; 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.Vector; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.entity.BaseEntity; @@ -78,7 +82,7 @@ public class MemoryOptimizedClipboard extends FaweClipboard { } for (Map.Entry entry : nbtMapLoc.entrySet()) { IntegerTrio key = entry.getKey(); - nbtMapIndex.put(getIndex(key.x, key.y, key.z), entry.getValue()); + setTile(getIndex(key.x, key.y, key.z), entry.getValue()); } nbtMapLoc.clear(); } @@ -276,6 +280,63 @@ public class MemoryOptimizedClipboard extends FaweClipboard { saveAdd = true; } + @Override + public void streamIds(NBTStreamer.ByteReader task) { + int index = 0; + if (add != null) { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + int id = getId(index) + (getAdd(index) << 8); + task.run(index++, id); + } + } + } + } else { + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + int id = getId(index); + task.run(index++, id); + } + } + } + } + } + + @Override + public void streamDatas(NBTStreamer.ByteReader task) { + int index = 0; + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + int data = getData(index); + task.run(index++, data); + } + } + } + } + + @Override + public List getTileEntities() { + convertTilesToIndex(); + for (Map.Entry entry : nbtMapIndex.entrySet()) { + int index = entry.getKey(); + CompoundTag tag = entry.getValue(); + Map values = ReflectionUtils.getMap(tag.getValue()); + if (!values.containsKey("x")) { + int y = index / area; + index -= y * area; + int z = index / width; + int x = index - (z * width); + values.put("x", new IntTag(x)); + values.put("y", new IntTag(y)); + values.put("z", new IntTag(z)); + } + } + return new ArrayList<>(nbtMapIndex.values()); + } + private int ylast; private int ylasti; private int zlast; @@ -358,6 +419,15 @@ public class MemoryOptimizedClipboard extends FaweClipboard { return true; } + public boolean setTile(int index, CompoundTag tag) { + nbtMapIndex.put(index, tag); + Map values = ReflectionUtils.getMap(tag.getValue()); + values.remove("x"); + values.remove("y"); + values.remove("z"); + return true; + } + @Override public boolean setBlock(int x, int y, int z, BaseBlock block) { return setBlock(getIndex(x, y, z), block); @@ -372,7 +442,7 @@ public class MemoryOptimizedClipboard extends FaweClipboard { setData(index, block.getData()); CompoundTag tile = block.getNbtData(); if (tile != null) { - nbtMapIndex.put(index, tile); + setTile(index, tile); } return true; } diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/ReadOnlyClipboard.java b/core/src/main/java/com/boydti/fawe/object/clipboard/ReadOnlyClipboard.java index bcf89aa2..c02b2053 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/ReadOnlyClipboard.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/ReadOnlyClipboard.java @@ -1,17 +1,20 @@ package com.boydti.fawe.object.clipboard; import com.boydti.fawe.object.RunnableVal2; +import com.boydti.fawe.util.ReflectionUtils; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.BlockVector; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; -import java.util.Iterator; import java.util.List; +import java.util.Map; public abstract class ReadOnlyClipboard extends FaweClipboard { private final Region region; @@ -42,17 +45,84 @@ public abstract class ReadOnlyClipboard extends FaweClipboard { @Override public void forEach(RunnableVal2 task, boolean air) { - Iterator iter = getRegion().iterator(); - while (iter.hasNext()) { - BlockVector pos = iter.next(); - BaseBlock block = getBlockAbs((int) pos.x, (int) pos.y, (int) pos.z); - if (!air && block == EditSession.nullBlock) { - continue; + Vector min = region.getMinimumPoint(); + Vector max = region.getMaximumPoint(); + 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.x = x - mx; + pos.y = y - my; + pos.z = z - mz; + CompoundTag tag = block.getNbtData(); + if (tag != null) { + Map values = ReflectionUtils.getMap(tag.getValue()); + values.put("x", new IntTag((int) pos.x)); + values.put("y", new IntTag((int) pos.y)); + values.put("z", new IntTag((int) pos.z)); + } + task.run(pos, block); + } + } + } + } 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.x = x - mx; + pos.y = y - my; + pos.z = z - mz; + CompoundTag tag = block.getNbtData(); + if (tag != null) { + Map values = ReflectionUtils.getMap(tag.getValue()); + values.put("x", new IntTag((int) pos.x)); + values.put("y", new IntTag((int) pos.y)); + values.put("z", new IntTag((int) pos.z)); + } + task.run(pos, block); + } + } + } + } + } 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++) { + pos.x = x; + pos.y = y; + pos.z = z; + if (region.contains(pos)) { + BaseBlock block = getBlockAbs(x, y, z); + if (!air && block == EditSession.nullBlock) { + continue; + } + pos.x -= mx; + pos.y -= my; + pos.z -= mz; + CompoundTag tag = block.getNbtData(); + if (tag != null) { + Map values = ReflectionUtils.getMap(tag.getValue()); + values.put("x", new IntTag((int) pos.x)); + values.put("y", new IntTag((int) pos.y)); + values.put("z", new IntTag((int) pos.z)); + } + task.run(pos, block); + } else if (air) { + pos.x -= mx; + pos.y -= my; + pos.z -= mz; + task.run(pos, EditSession.nullBlock); + } + } + } } - pos.x -= mx; - pos.y -= my; - pos.z -= mz; - task.run(pos, block); } } }; diff --git a/core/src/main/java/com/boydti/fawe/object/extent/AffineTransformExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/AffineTransformExtent.java deleted file mode 100644 index 1c2d5caa..00000000 --- a/core/src/main/java/com/boydti/fawe/object/extent/AffineTransformExtent.java +++ /dev/null @@ -1,138 +0,0 @@ -package com.boydti.fawe.object.extent; - -import com.boydti.fawe.FaweCache; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.Vector2D; -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.extent.transform.BlockTransformExtent; -import com.sk89q.worldedit.math.transform.AffineTransform; -import com.sk89q.worldedit.math.transform.Transform; -import com.sk89q.worldedit.world.biome.BaseBiome; -import com.sk89q.worldedit.world.registry.BlockRegistry; - -public class AffineTransformExtent extends TransformExtent { - private final Vector mutable = new Vector(); - private final BlockRegistry registry; - private int maxy; - private AffineTransform affine; - private BaseBlock[] BLOCK_TRANSFORM; - private BaseBlock[] BLOCK_TRANSFORM_INVERSE; - - private Vector min; - - public AffineTransformExtent(Extent parent, BlockRegistry registry) { - super(parent); - this.maxy = parent.getMaximumPoint().getBlockY(); - this.affine = new AffineTransform(); - this.registry = registry; - } - - private void cache() { - BLOCK_TRANSFORM = new BaseBlock[FaweCache.CACHE_BLOCK.length]; - BLOCK_TRANSFORM_INVERSE = new BaseBlock[FaweCache.CACHE_BLOCK.length]; - Transform inverse = affine.inverse(); - for (int i = 0; i < BLOCK_TRANSFORM.length; i++) { - BaseBlock block = FaweCache.CACHE_BLOCK[i]; - if (block != null) { - BLOCK_TRANSFORM[i] = BlockTransformExtent.transform(new BaseBlock(block), affine, registry); - BLOCK_TRANSFORM_INVERSE[i] = BlockTransformExtent.transform(new BaseBlock(block), inverse, registry); - } - } - } - - @Override - public TransformExtent setExtent(Extent extent) { - min = null; - maxy = extent.getMaximumPoint().getBlockY(); - return super.setExtent(extent); - } - - public AffineTransform getAffine() { - return affine; - } - - public void setAffine(AffineTransform affine) { - this.affine = affine; - cache(); - } - - private Vector getPos(Vector pos) { - if (min == null) { - min = new Vector(pos); - } - mutable.x = (pos.x - min.x); - mutable.y = (pos.y - min.y); - mutable.z = (pos.z - min.z); - Vector tmp = affine.apply(mutable); - tmp.x += min.x; - tmp.y += min.y; - tmp.z += min.z; - return tmp; - } - - private Vector getPos(int x, int y, int z) { - if (min == null) { - min = new Vector(x, y, z); - } - mutable.x = (x - min.x); - mutable.y = (y - min.y); - mutable.z = (z - min.z); - Vector tmp = affine.apply(mutable); - tmp.x += min.x; - tmp.y += min.y; - tmp.z += min.z; - return tmp; - } - - private final BaseBlock transformFast(BaseBlock block) { - return BLOCK_TRANSFORM[FaweCache.getCombined(block)]; - } - - private final BaseBlock transformFastInverse(BaseBlock block) { - return BLOCK_TRANSFORM_INVERSE[FaweCache.getCombined(block)]; - } - - @Override - public BaseBlock getLazyBlock(int x, int y, int z) { - return transformFast(super.getLazyBlock(getPos(x, y, z))); - } - - @Override - public BaseBlock getLazyBlock(Vector position) { - return transformFast(super.getLazyBlock(getPos(position))); - } - - @Override - public BaseBlock getBlock(Vector position) { - return transformFast(super.getBlock(getPos(position))); - } - - @Override - public BaseBiome getBiome(Vector2D position) { - mutable.x = position.getBlockX(); - mutable.z = position.getBlockZ(); - mutable.y = 0; - return super.getBiome(getPos(mutable).toVector2D()); - } - - @Override - public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { - return super.setBlock(getPos(x, y, z), transformFastInverse(block)); - } - - - @Override - public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException { - return super.setBlock(getPos(location), transformFastInverse(block)); - } - - @Override - public boolean setBiome(Vector2D position, BaseBiome biome) { - mutable.x = position.getBlockX(); - mutable.z = position.getBlockZ(); - mutable.y = 0; - return super.setBiome(getPos(mutable).toVector2D(), biome); - } -} diff --git a/core/src/main/java/com/boydti/fawe/object/extent/BlockTranslateExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/BlockTranslateExtent.java new file mode 100644 index 00000000..c7603e10 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/extent/BlockTranslateExtent.java @@ -0,0 +1,64 @@ +package com.boydti.fawe.object.extent; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.world.biome.BaseBiome; + +public class BlockTranslateExtent extends AbstractDelegateExtent { + private final int dx,dy,dz; + private final Extent extent; + private Vector mutable = new Vector(); + + public BlockTranslateExtent(Extent extent, int dx, int dy, int dz) { + super(extent); + this.dx = dx; + this.dy = dy; + this.dz = dz; + this.extent = extent; + } + + @Override + public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException { + mutable.x = location.x + dx; + mutable.y = location.y + dy; + mutable.z = location.z + dz; + return extent.setBlock(mutable, block); + } + + @Override + public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { + mutable.x = x + dx; + mutable.y = y + dy; + mutable.z = z + dz; + return extent.setBlock(mutable, block); + } + + @Override + public boolean setBiome(Vector2D position, BaseBiome biome) { + return super.setBiome(position.add(dx, dz), biome); + } + + @Override + public BaseBiome getBiome(Vector2D position) { + return super.getBiome(position.add(dx, dz)); + } + + @Override + public BaseBlock getBlock(Vector location) { + return getLazyBlock((int) location.x, (int) location.y, (int) location.z); + } + + @Override + public BaseBlock getLazyBlock(Vector location) { + return getLazyBlock((int) location.x, (int) location.y, (int) location.z); + } + + @Override + public BaseBlock getLazyBlock(int x, int y, int z) { + return super.getLazyBlock(x + dx, y + dy, z + dz); + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/extent/ClipboardExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/ClipboardExtent.java new file mode 100644 index 00000000..852c1a28 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/extent/ClipboardExtent.java @@ -0,0 +1,58 @@ +package com.boydti.fawe.object.extent; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.operation.ForwardExtentCopy; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.regions.Region; + + +import static com.google.common.base.Preconditions.checkNotNull; + +public class ClipboardExtent extends ResettableExtent { + + private final Clipboard clipboard; + private final Vector origin; + private final boolean ignoreAir; + private Extent extent; + + private final Vector mutable = new Vector(); + + public ClipboardExtent(Extent parent, Clipboard clipboard, boolean ignoreAir) { + super(parent); + checkNotNull(clipboard); + this.extent = parent; + this.clipboard = clipboard; + this.origin = clipboard.getOrigin(); + this.ignoreAir = ignoreAir; + } + + @Override + public boolean setBlock(Vector to, BaseBlock block) throws WorldEditException { + Region region = clipboard.getRegion(); + ForwardExtentCopy copy = new ForwardExtentCopy(clipboard, clipboard.getRegion(), clipboard.getOrigin(), extent, to); + if (ignoreAir) { + copy.setSourceMask(new ExistingBlockMask(clipboard)); + } + Operations.completeLegacy(copy); + return true; + } + + @Override + public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { + mutable.x = x; + mutable.y = y; + mutable.z = z; + return setBlock(mutable, block); + } + + @Override + public ResettableExtent setExtent(Extent extent) { + this.extent = extent; + return super.setExtent(extent); + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/extent/DefaultTransformParser.java b/core/src/main/java/com/boydti/fawe/object/extent/DefaultTransformParser.java index fe730253..887941bd 100644 --- a/core/src/main/java/com/boydti/fawe/object/extent/DefaultTransformParser.java +++ b/core/src/main/java/com/boydti/fawe/object/extent/DefaultTransformParser.java @@ -2,6 +2,8 @@ package com.boydti.fawe.object.extent; import com.boydti.fawe.object.mask.CustomMask; import com.boydti.fawe.util.ExtentTraverser; +import com.sk89q.worldedit.EmptyClipboardException; +import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.factory.DefaultMaskParser; import com.sk89q.worldedit.extension.input.InputParseException; @@ -9,11 +11,14 @@ import com.sk89q.worldedit.extension.input.NoMatchException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.NullExtent; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.transform.BlockTransformExtent; import com.sk89q.worldedit.function.pattern.Pattern; 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.transform.AffineTransform; +import com.sk89q.worldedit.session.ClipboardHolder; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -24,7 +29,7 @@ import static com.google.common.base.Preconditions.checkNotNull; /** * Parses mask input strings. */ -public class DefaultTransformParser extends InputParser { +public class DefaultTransformParser extends InputParser { public DefaultTransformParser(WorldEdit worldEdit) { super(worldEdit); @@ -44,7 +49,7 @@ public class DefaultTransformParser extends InputParser { } @Override - public TransformExtent parseFromInput(String input, ParserContext context) throws InputParseException { + public ResettableExtent parseFromInput(String input, ParserContext context) throws InputParseException { Extent extent = new NullExtent(); for (String component : input.split(" ")) { if (component.isEmpty()) { @@ -52,13 +57,13 @@ public class DefaultTransformParser extends InputParser { } extent = getTansformComponent(extent, component, context); } - if (extent instanceof TransformExtent) { - return (TransformExtent) extent; + if (extent instanceof ResettableExtent) { + return (ResettableExtent) extent; } return null; } - private TransformExtent getTansformComponent(Extent parent, String component, ParserContext context) throws InputParseException { + private ResettableExtent getTansformComponent(Extent parent, String component, ParserContext context) throws InputParseException { final char firstChar = component.charAt(0); switch (firstChar) { case '#': @@ -95,24 +100,41 @@ public class DefaultTransformParser extends InputParser { if (!rest.isEmpty()) { parent = parseFromInput(rest, context); } - ExtentTraverser traverser = new ExtentTraverser(parent).find(AffineTransformExtent.class); - AffineTransformExtent affine = (AffineTransformExtent) (traverser != null ? traverser.get() : null); + ExtentTraverser traverser = new ExtentTraverser(parent).find(BlockTransformExtent.class); + BlockTransformExtent affine = (BlockTransformExtent) (traverser != null ? traverser.get() : null); if (affine == null) { - parent = affine = new AffineTransformExtent(parent, context.requireWorld().getWorldData().getBlockRegistry()); + parent = affine = new BlockTransformExtent(parent, context.requireWorld().getWorldData().getBlockRegistry()); } - AffineTransform transform = affine.getAffine(); + AffineTransform transform = (AffineTransform) affine.getTransform(); transform = transform.rotateX(x); transform = transform.rotateY(y); transform = transform.rotateZ(z); - affine.setAffine(transform); - return (TransformExtent) parent; + affine.setTransform(transform); + return (ResettableExtent) parent; } catch (NumberFormatException | ExpressionException e) { - throw new InputParseException("The correct format is #scale:::"); + throw new InputParseException("The correct format is #rotate:::"); } } default: throw new NoMatchException("Unrecognized transform '" + component + "'"); } + } else { + switch (component) { + case "#clipboard": { + LocalSession session = context.requireSession(); + if (session != null) { + try { + ClipboardHolder holder = session.getClipboard(); + Clipboard clipboard = holder.getClipboard(); + return new ClipboardExtent(parent, clipboard, true); + } catch (EmptyClipboardException e) { + throw new InputParseException("To use #clipboard, please first copy something to your clipboard"); + } + } else { + throw new InputParseException("No session is available, so no clipboard is available"); + } + } + } } default: throw new NoMatchException("Unrecognized transform '" + component + "'"); diff --git a/core/src/main/java/com/boydti/fawe/object/extent/EmptyExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/EmptyExtent.java new file mode 100644 index 00000000..7e0aa4e6 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/extent/EmptyExtent.java @@ -0,0 +1,69 @@ +package com.boydti.fawe.object.extent; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.biome.BaseBiome; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nullable; + +public class EmptyExtent implements Extent { + public EmptyExtent() { + } + + public Vector getMinimumPoint() { + return Vector.ZERO; + } + + public Vector getMaximumPoint() { + return Vector.ZERO; + } + + public List getEntities(Region region) { + return Collections.emptyList(); + } + + public List getEntities() { + return Collections.emptyList(); + } + + @Nullable + public Entity createEntity(Location location, BaseEntity entity) { + return null; + } + + public BaseBlock getBlock(Vector position) { + return EditSession.nullBlock; + } + + public BaseBlock getLazyBlock(Vector position) { + return EditSession.nullBlock; + } + + @Nullable + public BaseBiome getBiome(Vector2D position) { + return null; + } + + public boolean setBlock(Vector position, BaseBlock block) throws WorldEditException { + return false; + } + + public boolean setBiome(Vector2D position, BaseBiome biome) { + return false; + } + + @Nullable + public Operation commit() { + return null; + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/extent/PatternTransform.java b/core/src/main/java/com/boydti/fawe/object/extent/PatternTransform.java index 9e071244..bc76c699 100644 --- a/core/src/main/java/com/boydti/fawe/object/extent/PatternTransform.java +++ b/core/src/main/java/com/boydti/fawe/object/extent/PatternTransform.java @@ -6,7 +6,7 @@ import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.pattern.Pattern; -public class PatternTransform extends TransformExtent { +public class PatternTransform extends ResettableExtent { private final Pattern pattern; public PatternTransform(Extent parent, Pattern pattern) { diff --git a/core/src/main/java/com/boydti/fawe/object/extent/ResettableExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/ResettableExtent.java new file mode 100644 index 00000000..9ce59b58 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/extent/ResettableExtent.java @@ -0,0 +1,24 @@ +package com.boydti.fawe.object.extent; + +import com.boydti.fawe.util.ExtentTraverser; +import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import com.sk89q.worldedit.extent.Extent; + + +import static com.google.common.base.Preconditions.checkNotNull; + +public class ResettableExtent extends AbstractDelegateExtent { + public ResettableExtent(Extent parent) { + super(parent); + } + + public ResettableExtent setExtent(Extent extent) { + checkNotNull(extent); + if (getExtent() instanceof ResettableExtent) { + ((ResettableExtent) getExtent()).setExtent(extent); + } else { + new ExtentTraverser(this).setNext(extent); + } + return this; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/extent/ScaleTransform.java b/core/src/main/java/com/boydti/fawe/object/extent/ScaleTransform.java index 3f8d5f7e..cf6b8c85 100644 --- a/core/src/main/java/com/boydti/fawe/object/extent/ScaleTransform.java +++ b/core/src/main/java/com/boydti/fawe/object/extent/ScaleTransform.java @@ -11,7 +11,7 @@ import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.biome.BaseBiome; import javax.annotation.Nullable; -public class ScaleTransform extends TransformExtent { +public class ScaleTransform extends ResettableExtent { private final Vector mutable = new Vector(); private final double dx,dy,dz; private int maxy; @@ -27,7 +27,7 @@ public class ScaleTransform extends TransformExtent { } @Override - public TransformExtent setExtent(Extent extent) { + public ResettableExtent setExtent(Extent extent) { min = null; maxy = extent.getMaximumPoint().getBlockY(); return super.setExtent(extent); diff --git a/core/src/main/java/com/boydti/fawe/object/extent/TransformExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/TransformExtent.java index c3e01a97..1a36c26c 100644 --- a/core/src/main/java/com/boydti/fawe/object/extent/TransformExtent.java +++ b/core/src/main/java/com/boydti/fawe/object/extent/TransformExtent.java @@ -1,24 +1,103 @@ package com.boydti.fawe.object.extent; -import com.boydti.fawe.util.ExtentTraverser; -import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.transform.BlockTransformExtent; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.registry.BlockRegistry; +public class TransformExtent extends BlockTransformExtent { -import static com.google.common.base.Preconditions.checkNotNull; + private final Vector mutable = new Vector(); + private Vector min; + private int maxy; -public class TransformExtent extends AbstractDelegateExtent { - public TransformExtent(Extent parent) { - super(parent); + public TransformExtent(Extent parent, BlockRegistry registry) { + super(parent, registry); + this.maxy = parent.getMaximumPoint().getBlockY(); } - public TransformExtent setExtent(Extent extent) { - checkNotNull(extent); - if (getExtent() instanceof TransformExtent) { - ((TransformExtent) getExtent()).setExtent(extent); - } else { - new ExtentTraverser(this).setNext(extent); + @Override + public ResettableExtent setExtent(Extent extent) { + min = null; + maxy = extent.getMaximumPoint().getBlockY(); + return super.setExtent(extent); + } + + public void setOrigin(Vector pos) { + this.min = pos; + } + + private Vector getPos(Vector pos) { + if (min == null) { + min = new Vector(pos); } - return this; + mutable.x = (pos.x - min.x); + mutable.y = (pos.y - min.y); + mutable.z = (pos.z - min.z); + Vector tmp = getTransform().apply(mutable); + tmp.x += min.x; + tmp.y += min.y; + tmp.z += min.z; + return tmp; } -} \ No newline at end of file + + private Vector getPos(int x, int y, int z) { + if (min == null) { + min = new Vector(x, y, z); + } + mutable.x = (x - min.x); + mutable.y = (y - min.y); + mutable.z = (z - min.z); + Vector tmp = getTransform().apply(mutable); + tmp.x += min.x; + tmp.y += min.y; + tmp.z += min.z; + return tmp; + } + + @Override + public BaseBlock getLazyBlock(int x, int y, int z) { + return transformFast(super.getLazyBlock(getPos(x, y, z))); + } + + @Override + public BaseBlock getLazyBlock(Vector position) { + return transformFast(super.getLazyBlock(getPos(position))); + } + + @Override + public BaseBlock getBlock(Vector position) { + return transformFast(super.getBlock(getPos(position))); + } + + @Override + public BaseBiome getBiome(Vector2D position) { + mutable.x = position.getBlockX(); + mutable.z = position.getBlockZ(); + mutable.y = 0; + return super.getBiome(getPos(mutable).toVector2D()); + } + + @Override + public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { + return super.setBlock(getPos(x, y, z), transformFastInverse(block)); + } + + + @Override + public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException { + return super.setBlock(getPos(location), transformFastInverse(block)); + } + + @Override + public boolean setBiome(Vector2D position, BaseBiome biome) { + mutable.x = position.getBlockX(); + mutable.z = position.getBlockZ(); + mutable.y = 0; + return super.setBiome(getPos(mutable).toVector2D(), biome); + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/function/block/SimpleBlockCopy.java b/core/src/main/java/com/boydti/fawe/object/function/block/SimpleBlockCopy.java new file mode 100644 index 00000000..8458780a --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/function/block/SimpleBlockCopy.java @@ -0,0 +1,22 @@ +package com.boydti.fawe.object.function.block; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.RegionFunction; + +public class SimpleBlockCopy implements RegionFunction { + + private final Extent source; + private final Extent destination; + + public SimpleBlockCopy(Extent source, Extent destination) { + this.source = source; + this.destination = destination; + } + + @Override + public boolean apply(Vector position) throws WorldEditException { + return destination.setBlock(position, source.getBlock(position)); + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/pattern/FullClipboardPattern.java b/core/src/main/java/com/boydti/fawe/object/pattern/FullClipboardPattern.java new file mode 100644 index 00000000..37a9ce5a --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/pattern/FullClipboardPattern.java @@ -0,0 +1,47 @@ +package com.boydti.fawe.object.pattern; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.operation.ForwardExtentCopy; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.function.pattern.AbstractPattern; +import com.sk89q.worldedit.regions.Region; + + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A pattern that reads from {@link Clipboard}. + */ +public class FullClipboardPattern extends AbstractPattern { + private final Extent extent; + private final Clipboard clipboard; + private final BaseBlock block; + + private final Vector mutable = new Vector(); + + /** + * Create a new clipboard pattern. + * + * @param clipboard the clipboard + */ + public FullClipboardPattern(Extent extent, Clipboard clipboard) { + checkNotNull(clipboard); + this.clipboard = clipboard; + this.extent = extent; + Vector origin = clipboard.getOrigin(); + block = clipboard.getBlock(origin); + } + + @Override + public BaseBlock apply(Vector to) { + Region region = clipboard.getRegion(); + ForwardExtentCopy copy = new ForwardExtentCopy(clipboard, clipboard.getRegion(), clipboard.getOrigin(), extent, to); + copy.setSourceMask(new ExistingBlockMask(clipboard)); + Operations.completeBlindly(copy); + return block; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java b/core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java index 57c06031..1c8c3485 100644 --- a/core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java +++ b/core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java @@ -2,7 +2,6 @@ package com.boydti.fawe.regions.general.plot; import com.boydti.fawe.FaweCache; import com.boydti.fawe.object.FaweQueue; -import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.clipboard.ReadOnlyClipboard; import com.boydti.fawe.object.io.PGZIPOutputStream; import com.boydti.fawe.util.EditSessionBuilder; @@ -20,15 +19,16 @@ import com.intellectualcrafters.plot.util.block.LocalBlockQueue; import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.SchematicWriter; import com.sk89q.worldedit.regions.CuboidRegion; -import java.io.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; import java.net.URL; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -61,42 +61,7 @@ public class FaweSchematicHandler extends SchematicHandler { final int my = pos1.getY(); final int mz = pos1.getZ(); - ReadOnlyClipboard clipboard = new ReadOnlyClipboard(region) { - @Override - public BaseBlock getBlock(int x, int y, int z) { - return editSession.getLazyBlock(mx + x, my + y, mz + z); - } - - public BaseBlock getBlockAbs(int x, int y, int z) { - return editSession.getLazyBlock(x, y, z); - } - - @Override - public List getEntities() { - return editSession.getEntities(region); - } - - @Override - public void forEach(RunnableVal2 task, boolean air) { - Vector mutable = new Vector(0, 0, 0); - for (RegionWrapper region : regions) { - for (int z = region.minZ; z <= region.maxZ; z++) { - mutable.z = z - region.minZ; - for (int y = region.minY; y <= Math.min(255, region.maxY); y++) { - mutable.y = y - region.minY; - for (int x = region.minX; x <= region.maxX; x++) { - mutable.x = x - region.minX; - BaseBlock block = editSession.getLazyBlock(x, y, z); - if (!air && block == editSession.nullBlock) { - continue; - } - task.run(mutable, block); - } - } - } - } - } - }; + ReadOnlyClipboard clipboard = ReadOnlyClipboard.of(editSession, region); Clipboard holder = new BlockArrayClipboard(region, clipboard); com.sk89q.jnbt.CompoundTag weTag = SchematicWriter.writeTag(holder); diff --git a/core/src/main/java/com/boydti/fawe/util/SetQueue.java b/core/src/main/java/com/boydti/fawe/util/SetQueue.java index 42cb3e17..25f2e2a3 100644 --- a/core/src/main/java/com/boydti/fawe/util/SetQueue.java +++ b/core/src/main/java/com/boydti/fawe/util/SetQueue.java @@ -19,6 +19,7 @@ public class SetQueue { * The implementation specific queue */ public static final SetQueue IMP = new SetQueue(); + private double targetTPS = 18; public enum QueueStage { INACTIVE, ACTIVE, NONE; @@ -52,6 +53,17 @@ public class SetQueue { return completer; } + public void runMiscTasks() { + while (Fawe.get().getTimer().isAbove(targetTPS)) { + Runnable task = tasks.poll(); + if (task != null) { + task.run(); + } else { + break; + } + } + } + public SetQueue() { tasks = new ConcurrentLinkedDeque<>(); activeQueues = new ConcurrentLinkedDeque(); @@ -60,7 +72,7 @@ public class SetQueue { @Override public void run() { try { - double targetTPS = 18 - Math.max(Settings.QUEUE.EXTRA_TIME_MS * 0.05, 0); + targetTPS = 18 - Math.max(Settings.QUEUE.EXTRA_TIME_MS * 0.05, 0); do { Runnable task = tasks.poll(); if (task != null) { diff --git a/core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java b/core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java index 0436330d..693367f4 100644 --- a/core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java +++ b/core/src/main/java/com/sk89q/jnbt/NBTOutputStream.java @@ -57,6 +57,10 @@ public final class NBTOutputStream implements Closeable { this.os = new DataOutputStream(os); } + public DataOutputStream getOutputStream() { + return os; + } + /** * Writes a tag. * @@ -70,19 +74,33 @@ public final class NBTOutputStream implements Closeable { checkNotNull(tag); int type = NBTUtils.getTypeCode(tag.getClass()); - byte[] nameBytes = name.getBytes(NBTConstants.CHARSET); - - os.writeByte(type); - os.writeShort(nameBytes.length); - os.write(nameBytes); - + writeNamedTagName(name, type); if (type == NBTConstants.TYPE_END) { throw new IOException("Named TAG_End not permitted."); } - writeTagPayload(tag); } + public void writeNamedTagName(String name, int type) throws IOException { + byte[] nameBytes = name.getBytes(NBTConstants.CHARSET); + os.writeByte(type); + os.writeShort(nameBytes.length); + os.write(nameBytes); + } + + public void writeLazyCompoundTag(String name, LazyWrite next) throws IOException{ + byte[] nameBytes = name.getBytes(NBTConstants.CHARSET); + os.writeByte(NBTConstants.TYPE_COMPOUND); + os.writeShort(nameBytes.length); + os.write(nameBytes); + next.write(this); + os.writeByte(NBTConstants.TYPE_END); + } + + public interface LazyWrite { + void write(NBTOutputStream out) throws IOException; + } + public void writeTag(Tag tag) throws IOException { int type = NBTUtils.getTypeCode(tag.getClass()); os.writeByte(type); @@ -183,7 +201,7 @@ public final class NBTOutputStream implements Closeable { for (Map.Entry entry : tag.getValue().entrySet()) { writeNamedTag(entry.getKey(), entry.getValue()); } - os.writeByte((byte) 0); // end tag - better way? + os.writeByte(NBTConstants.TYPE_END); // end tag - better way? } /** diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index 40f3c698..b4d6a96c 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -46,8 +46,8 @@ 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.extent.ResettableExtent; import com.boydti.fawe.object.extent.SlowExtent; -import com.boydti.fawe.object.extent.TransformExtent; import com.boydti.fawe.object.mask.ResettableMask; import com.boydti.fawe.object.progress.DefaultProgressTracker; import com.boydti.fawe.util.ExtentTraverser; @@ -584,11 +584,11 @@ public class EditSession extends AbstractWorld implements HasFaweQueue { return maskingExtent != null ? maskingExtent.get().getMask() : null; } - public void addTransform(TransformExtent transform) { + public void addTransform(ResettableExtent transform) { if (transform == null) { - ExtentTraverser traverser = new ExtentTraverser(this.extent).find(TransformExtent.class); + ExtentTraverser traverser = new ExtentTraverser(this.extent).find(ResettableExtent.class); AbstractDelegateExtent next = extent; - while (traverser != null && traverser.get() instanceof TransformExtent) { + while (traverser != null && traverser.get() instanceof ResettableExtent) { traverser = traverser.next(); next = traverser.get(); } diff --git a/core/src/main/java/com/sk89q/worldedit/LocalSession.java b/core/src/main/java/com/sk89q/worldedit/LocalSession.java index 12689f93..942a7a6e 100644 --- a/core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -29,7 +29,7 @@ import com.boydti.fawe.object.brush.DoubleActionBrushTool; import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.object.changeset.FaweChangeSet; import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; -import com.boydti.fawe.object.extent.TransformExtent; +import com.boydti.fawe.object.extent.ResettableExtent; import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.wrappers.WorldWrapper; @@ -139,7 +139,7 @@ public class LocalSession { private transient int cuiVersion = -1; private transient boolean fastMode = false; private transient Mask mask; - private TransformExtent transform = null; + private ResettableExtent transform = null; private transient TimeZone timezone = TimeZone.getDefault(); private transient World currentWorld; @@ -1270,11 +1270,11 @@ public class LocalSession { setMask(mask != null ? Masks.wrap(mask) : null); } - public TransformExtent getTransform() { + public ResettableExtent getTransform() { return transform; } - public void setTransform(TransformExtent transform) { + public void setTransform(ResettableExtent transform) { this.transform = transform; } 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 171ca070..ad28bac9 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -45,6 +45,7 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; 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.ForwardExtentCopy; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.Operations; @@ -64,7 +65,6 @@ import com.sk89q.worldedit.util.command.parametric.Optional; import java.io.IOException; import java.net.URL; import java.util.Iterator; -import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; @@ -110,37 +110,7 @@ public class ClipboardCommands { final int mx = origin.getBlockX(); final int my = origin.getBlockY(); final int mz = origin.getBlockZ(); - ReadOnlyClipboard lazyClipboard = new ReadOnlyClipboard(region) { - @Override - public BaseBlock getBlock(int x, int y, int z) { - return editSession.getLazyBlock(mx + x, my + y, mz + z); - } - - public BaseBlock getBlockAbs(int x, int y, int z) { - return editSession.getLazyBlock(x, y, z); - } - - @Override - public List getEntities() { - return editSession.getEntities(region); - } - - @Override - public void forEach(RunnableVal2 task, boolean air) { - Iterator iter = region.iterator(); - while (iter.hasNext()) { - BlockVector pos = iter.next(); - BaseBlock block = getBlockAbs((int) pos.x, (int) pos.y, (int) pos.z); - if (!air && block == EditSession.nullBlock) { - continue; - } - pos.x -= mx; - pos.y -= my; - pos.z -= mz; - task.run(pos, block); - } - } - }; + ReadOnlyClipboard lazyClipboard = ReadOnlyClipboard.of(editSession, region); BlockArrayClipboard clipboard = new BlockArrayClipboard(region, lazyClipboard); clipboard.setOrigin(session.getPlacementPosition(player)); @@ -171,7 +141,7 @@ public class ClipboardCommands { clipboard.setOrigin(session.getPlacementPosition(player)); ForwardExtentCopy copy = new ForwardExtentCopy(editSession, region, clipboard, region.getMinimumPoint()); - if (mask != null) { + if (mask != null && mask != Masks.alwaysTrue()) { copy.setSourceMask(mask); } Operations.completeLegacy(copy); diff --git a/core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java b/core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java index 82995528..f740713c 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java @@ -2,7 +2,7 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.extent.DefaultTransformParser; -import com.boydti.fawe.object.extent.TransformExtent; +import com.boydti.fawe.object.extent.ResettableExtent; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; @@ -142,7 +142,7 @@ public class GeneralCommands { parserContext.setWorld(player.getWorld()); parserContext.setSession(session); parserContext.setExtent(editSession); - TransformExtent transform = transformParser.parseFromInput(context.getJoinedStrings(0), parserContext); + ResettableExtent transform = transformParser.parseFromInput(context.getJoinedStrings(0), parserContext); session.setTransform(transform); BBC.TRANSFORM.send(player); } diff --git a/core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java b/core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java index f4ab52fe..48a8fbc4 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java @@ -3,7 +3,7 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.brush.DoubleActionBrushTool; import com.boydti.fawe.object.extent.DefaultTransformParser; -import com.boydti.fawe.object.extent.TransformExtent; +import com.boydti.fawe.object.extent.ResettableExtent; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; @@ -122,7 +122,7 @@ public class ToolUtilCommands { parserContext.setWorld(player.getWorld()); parserContext.setSession(session); parserContext.setExtent(editSession); - TransformExtent transform = transformParser.parseFromInput(context.getJoinedStrings(0), parserContext); + ResettableExtent transform = transformParser.parseFromInput(context.getJoinedStrings(0), parserContext); if (tool instanceof BrushTool) { ((BrushTool) tool).setTransform(transform); } else if (tool instanceof DoubleActionBrushTool) { diff --git a/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java b/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java index 6e61142f..bee42015 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java +++ b/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java @@ -1,6 +1,6 @@ package com.sk89q.worldedit.command.tool; -import com.boydti.fawe.object.extent.TransformExtent; +import com.boydti.fawe.object.extent.ResettableExtent; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; @@ -29,7 +29,7 @@ public class BrushTool implements TraceTool { protected static int MAX_RANGE = 500; protected int range = -1; private Mask mask = null; - private TransformExtent transform = null; + private ResettableExtent transform = null; private Brush brush = new SphereBrush(); @Nullable private Pattern material; @@ -51,11 +51,11 @@ public class BrushTool implements TraceTool { return player.hasPermission(permission); } - public TransformExtent getTransform() { + public ResettableExtent getTransform() { return transform; } - public void setTransform(TransformExtent transform) { + public void setTransform(ResettableExtent transform) { this.transform = transform; } diff --git a/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java b/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java index c9659e1a..e4bd8553 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java @@ -57,7 +57,21 @@ public class HashTagPatternParser extends InputParser { switch (input) { case "#*": case "#existing": { - return new ExistingPattern(context.requireExtent()); + return new ExistingPattern(Request.request().getEditSession()); + } + case "#fullcopy": { + LocalSession session = context.requireSession(); + if (session != null) { + try { + ClipboardHolder holder = session.getClipboard(); + Clipboard clipboard = holder.getClipboard(); + return new FullClipboardPattern(Request.request().getEditSession(), clipboard); + } catch (EmptyClipboardException e) { + throw new InputParseException("To use #fullcopy, please first copy something to your clipboard"); + } + } else { + throw new InputParseException("No session is available, so no clipboard is available"); + } } case "#clipboard": case "#copy": { @@ -80,10 +94,10 @@ public class HashTagPatternParser extends InputParser { String rest = input.substring(split2[0].length() + 1); switch (split2[0].toLowerCase()) { case "#id": { - return new IdPattern(context.requireExtent(), parseFromInput(rest, context)); + return new IdPattern(Request.request().getEditSession(), parseFromInput(rest, context)); } case "#data": { - return new DataPattern(context.requireExtent(), parseFromInput(rest, context)); + return new DataPattern(Request.request().getEditSession(), parseFromInput(rest, context)); } case "#~": case "#r": 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 b5edc8c7..c3fdfab3 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 @@ -1,6 +1,7 @@ package com.sk89q.worldedit.extent.clipboard.io; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.jnbt.NBTStreamer; import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.clipboard.FaweClipboard; import com.boydti.fawe.util.ReflectionUtils; @@ -10,6 +11,7 @@ import com.sk89q.jnbt.DoubleTag; import com.sk89q.jnbt.FloatTag; import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTConstants; import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.jnbt.ShortTag; import com.sk89q.jnbt.StringTag; @@ -23,11 +25,13 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.registry.WorldData; +import java.io.DataOutputStream; import java.io.IOException; 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; @@ -106,8 +110,122 @@ public class SchematicWriter implements ClipboardWriter { } @Override - public void write(Clipboard clipboard, WorldData data) throws IOException { - outputStream.writeNamedTag("Schematic", writeTag(clipboard)); + public void write(final Clipboard clipboard, WorldData data) throws IOException { + if (clipboard instanceof BlockArrayClipboard) { + stream((BlockArrayClipboard) clipboard); + } else { + outputStream.writeNamedTag("Schematic", writeTag(clipboard)); + outputStream.flush(); + } + } + + public void stream(final BlockArrayClipboard clipboard) throws IOException { + final Region region = clipboard.getRegion(); + final Vector origin = clipboard.getOrigin(); + final Vector min = region.getMinimumPoint(); + final Vector offset = min.subtract(origin); + final int width = region.getWidth(); + final int height = region.getHeight(); + final int length = region.getLength(); + if (width > MAX_SIZE) { + throw new IllegalArgumentException("Width of region too large for a .schematic"); + } + if (height > MAX_SIZE) { + throw new IllegalArgumentException("Height of region too large for a .schematic"); + } + if (length > MAX_SIZE) { + throw new IllegalArgumentException("Length of region too large for a .schematic"); + } + final DataOutputStream rawStream = outputStream.getOutputStream(); + outputStream.writeLazyCompoundTag("Schematic", new NBTOutputStream.LazyWrite() { + @Override + public void write(NBTOutputStream out) throws IOException { + int volume = width * height * length; + + out.writeNamedTag("Width", new ShortTag((short) width)); + out.writeNamedTag("Length", new ShortTag((short) length)); + out.writeNamedTag("Height", new ShortTag((short) height)); + out.writeNamedTag("Materials", new StringTag("Alpha")); + out.writeNamedTag("WEOriginX", new IntTag(min.getBlockX())); + out.writeNamedTag("WEOriginY", new IntTag(min.getBlockY())); + out.writeNamedTag("WEOriginZ", new IntTag(min.getBlockZ())); + out.writeNamedTag("WEOffsetX", new IntTag(offset.getBlockX())); + 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); + } + rawStream.writeByte(byteValue); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + + if (hasAdd.get()) { + out.writeNamedTagName("AddBlocks", NBTConstants.TYPE_BYTE_ARRAY); + out.getOutputStream().writeInt(volume); + clipboard.IMP.streamIds(new NBTStreamer.ByteReader() { + @Override + public void run(int index, int byteValue) { + try { + rawStream.writeByte(byteValue >> 8); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } + + final List tileEntities = clipboard.IMP.getTileEntities(); + out.writeNamedTag("TileEntities", new ListTag(CompoundTag.class, tileEntities)); + + List entities = new ArrayList(); + for (Entity entity : clipboard.getEntities()) { + BaseEntity state = entity.getState(); + + if (state != null) { + Map values = new HashMap(); + + // Put NBT provided data + CompoundTag rawTag = state.getNbtData(); + if (rawTag != null) { + values.putAll(rawTag.getValue()); + } + + // Store our location data, overwriting any + values.put("id", new StringTag(state.getTypeId())); + values.put("Pos", writeVector(entity.getLocation().toVector(), "Pos")); + values.put("Rotation", writeRotation(entity.getLocation(), "Rotation")); + + CompoundTag entityTag = new CompoundTag(values); + entities.add(entityTag); + } + } + out.writeNamedTag("Entities", new ListTag(CompoundTag.class, entities)); + } + }); outputStream.flush(); } diff --git a/core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java b/core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java index 67cf7444..84409fe7 100644 --- a/core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java +++ b/core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java @@ -1,12 +1,16 @@ package com.sk89q.worldedit.extent.transform; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.object.extent.ResettableExtent; +import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.registry.BlockRegistry; import com.sk89q.worldedit.world.registry.State; import com.sk89q.worldedit.world.registry.StateValue; @@ -20,51 +24,113 @@ import static com.google.common.base.Preconditions.checkNotNull; * Transforms blocks themselves (but not their position) according to a * given transform. */ -public class BlockTransformExtent extends AbstractDelegateExtent { +public class BlockTransformExtent extends ResettableExtent { + private final BlockRegistry registry; + private Transform transform; + private Transform transformInverse; + private BaseBlock[] BLOCK_TRANSFORM; + private BaseBlock[] BLOCK_TRANSFORM_INVERSE; - private static final double RIGHT_ANGLE = Math.toRadians(90); + public BlockTransformExtent(Extent parent, BlockRegistry registry) { + this(parent, new AffineTransform(), registry); + } - private final Transform transform; - private final BlockRegistry blockRegistry; - private final BaseBlock[] BLOCK_TRANSFORM; - private final BaseBlock[] BLOCK_TRANSFORM_INVERSE; - - /** - * Create a new instance. - * - * @param extent the extent - * @param blockRegistry the block registry used for block direction data - */ - public BlockTransformExtent(Extent extent, Transform transform, BlockRegistry blockRegistry) { - super(extent); - checkNotNull(transform); - checkNotNull(blockRegistry); + public BlockTransformExtent(Extent parent, Transform transform, BlockRegistry registry) { + super(parent); this.transform = transform; - this.blockRegistry = blockRegistry; + this.transformInverse = this.transform.inverse(); + this.registry = registry; + cache(); + } + + private void cache() { BLOCK_TRANSFORM = new BaseBlock[FaweCache.CACHE_BLOCK.length]; BLOCK_TRANSFORM_INVERSE = new BaseBlock[FaweCache.CACHE_BLOCK.length]; - Transform inverse = transform.inverse(); for (int i = 0; i < BLOCK_TRANSFORM.length; i++) { BaseBlock block = FaweCache.CACHE_BLOCK[i]; if (block != null) { - BLOCK_TRANSFORM[i] = transform(new BaseBlock(block), transform, blockRegistry); - BLOCK_TRANSFORM_INVERSE[i] = transform(new BaseBlock(block), inverse, blockRegistry); + BLOCK_TRANSFORM[i] = BlockTransformExtent.transform(new BaseBlock(block), transform, registry); + BLOCK_TRANSFORM_INVERSE[i] = BlockTransformExtent.transform(new BaseBlock(block), transformInverse, registry); } } } - /** - * Get the transform. - * - * @return the transform - */ + @Override + public ResettableExtent setExtent(Extent extent) { + return super.setExtent(extent); + } + public Transform getTransform() { return transform; } + public void setTransform(Transform affine) { + this.transform = affine; + this.transformInverse = this.transform.inverse(); + cache(); + } + + public final BaseBlock transformFast(BaseBlock block) { + BaseBlock newBlock = BLOCK_TRANSFORM[FaweCache.getCombined(block)]; + CompoundTag tag = block.getNbtData(); + if (tag != null) { + newBlock = new BaseBlock(newBlock.getId(), newBlock.getData(), tag); +// if (tag.containsKey("Rot")) { +// int rot = tag.asInt("Rot"); +// +// Direction direction = MCDirections.fromRotation(rot); +// +// if (direction != null) { +// Vector applyAbsolute = transform.apply(direction.toVector()); +// Vector applyOrigin = transform.apply(Vector.ZERO); +// applyAbsolute.x -= applyOrigin.x; +// applyAbsolute.y -= applyOrigin.y; +// applyAbsolute.z -= applyOrigin.z; +// +// Direction newDirection = Direction.findClosest(applyAbsolute, Direction.Flag.CARDINAL | Direction.Flag.ORDINAL | Direction.Flag.SECONDARY_ORDINAL); +// +// if (newDirection != null) { +// Map values = ReflectionUtils.getMap(tag.getValue()); +// values.put("Rot", new ByteTag((byte) MCDirections.toRotation(newDirection))); +// } +// } +// } + } + return newBlock; + } + + public final BaseBlock transformFastInverse(BaseBlock block) { + BaseBlock newBlock = BLOCK_TRANSFORM_INVERSE[FaweCache.getCombined(block)]; + CompoundTag tag = block.getNbtData(); + if (tag != null) { + newBlock = new BaseBlock(newBlock.getId(), newBlock.getData(), tag); +// if (tag.containsKey("Rot")) { +// int rot = tag.asInt("Rot"); +// +// Direction direction = MCDirections.fromRotation(rot); +// +// if (direction != null) { +// Vector applyAbsolute = transformInverse.apply(direction.toVector()); +// Vector applyOrigin = transformInverse.apply(Vector.ZERO); +// applyAbsolute.x -= applyOrigin.x; +// applyAbsolute.y -= applyOrigin.y; +// applyAbsolute.z -= applyOrigin.z; +// +// Direction newDirection = Direction.findClosest(applyAbsolute, Direction.Flag.CARDINAL | Direction.Flag.ORDINAL | Direction.Flag.SECONDARY_ORDINAL); +// +// if (newDirection != null) { +// Map values = ReflectionUtils.getMap(tag.getValue()); +// values.put("Rot", new ByteTag((byte) MCDirections.toRotation(newDirection))); +// } +// } +// } + } + return newBlock; + } + @Override - public BaseBlock getBlock(Vector position) { - return transformFast(super.getBlock(position)); + public BaseBlock getLazyBlock(int x, int y, int z) { + return transformFast(super.getLazyBlock(x, y, z)); } @Override @@ -72,25 +138,30 @@ public class BlockTransformExtent extends AbstractDelegateExtent { return transformFast(super.getLazyBlock(position)); } + @Override + public BaseBlock getBlock(Vector position) { + return transformFast(super.getBlock(position)); + } + + @Override + public BaseBiome getBiome(Vector2D position) { + return super.getBiome(position); + } + + @Override + public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { + return super.setBlock(x, y, z, transformFastInverse(block)); + } + + @Override public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException { return super.setBlock(location, transformFastInverse(block)); } - private final BaseBlock transformFast(BaseBlock block) { - BaseBlock newBlock = BLOCK_TRANSFORM[FaweCache.getCombined(block)]; - if (block.hasNbtData()) { - newBlock.setNbtData(block.getNbtData()); - } - return newBlock; - } - - private final BaseBlock transformFastInverse(BaseBlock block) { - BaseBlock newBlock = BLOCK_TRANSFORM_INVERSE[FaweCache.getCombined(block)]; - if (block.hasNbtData()) { - newBlock.setNbtData(block.getNbtData()); - } - return newBlock; + @Override + public boolean setBiome(Vector2D position, BaseBiome biome) { + return super.setBiome(position, biome); } /** diff --git a/core/src/main/java/com/sk89q/worldedit/function/block/ExtentBlockCopy.java b/core/src/main/java/com/sk89q/worldedit/function/block/ExtentBlockCopy.java new file mode 100644 index 00000000..2a81b44e --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/function/block/ExtentBlockCopy.java @@ -0,0 +1,120 @@ +/* + * 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.function.block; + +import com.boydti.fawe.util.ReflectionUtils; +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.RegionFunction; +import com.sk89q.worldedit.internal.helper.MCDirections; +import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.Direction.Flag; +import java.util.Map; + + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Copies blocks from one extent to another. + */ +public class ExtentBlockCopy implements RegionFunction { + + private final Extent source; + private final Extent destination; + private final Vector from; + private final Vector to; + private final Transform transform; + + /** + * Make a new copy. + * + * @param source the source extent + * @param from the source offset + * @param destination the destination extent + * @param to the destination offset + * @param transform a transform to apply to positions (after source offset, before destination offset) + */ + public ExtentBlockCopy(Extent source, Vector from, Extent destination, Vector to, Transform transform) { + checkNotNull(source); + checkNotNull(from); + checkNotNull(destination); + checkNotNull(to); + checkNotNull(transform); + this.source = source; + this.from = from; + this.destination = destination; + this.to = to; + this.transform = transform; + } + + @Override + public boolean apply(Vector position) throws WorldEditException { + BaseBlock block = source.getBlock(position); + Vector orig = position.subtract(from); + Vector transformed = transform.apply(orig); + + // Apply transformations to NBT data if necessary + block = transformNbtData(block); + + return destination.setBlock(transformed.add(to), block); + } + + /** + * Transform NBT data in the given block state and return a new instance + * if the NBT data needs to be transformed. + * + * @param state the existing state + * @return a new state or the existing one + */ + private BaseBlock transformNbtData(BaseBlock state) { + CompoundTag tag = state.getNbtData(); + if (tag != null) { + // Handle blocks which store their rotation in NBT + if (tag.containsKey("Rot")) { + int rot = tag.asInt("Rot"); + + Direction direction = MCDirections.fromRotation(rot); + + if (direction != null) { + Vector applyAbsolute = transform.apply(direction.toVector()); + Vector applyOrigin = transform.apply(Vector.ZERO); + applyAbsolute.x -= applyOrigin.x; + applyAbsolute.y -= applyOrigin.y; + applyAbsolute.z -= applyOrigin.z; + + Direction newDirection = Direction.findClosest(applyAbsolute, Flag.CARDINAL | Flag.ORDINAL | Flag.SECONDARY_ORDINAL); + + if (newDirection != null) { + Map values = ReflectionUtils.getMap(tag.getValue()); + values.put("Rot", new ByteTag((byte) MCDirections.toRotation(newDirection))); + } + } + } + } + return state; + } + +} diff --git a/core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java b/core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java index 2a98e7d0..95ef6d08 100644 --- a/core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java +++ b/core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java @@ -19,14 +19,17 @@ package com.sk89q.worldedit.function.operation; +import com.boydti.fawe.object.extent.BlockTranslateExtent; +import com.boydti.fawe.object.extent.TransformExtent; +import com.boydti.fawe.object.function.block.SimpleBlockCopy; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.CombinedRegionFunction; import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.RegionMaskingFilter; -import com.sk89q.worldedit.function.block.ExtentBlockCopy; import com.sk89q.worldedit.function.entity.ExtentEntityCopy; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Masks; @@ -35,9 +38,12 @@ import com.sk89q.worldedit.function.visitor.RegionVisitor; import com.sk89q.worldedit.math.transform.Identity; import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.regions.Region; - +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.registry.BlockRegistry; +import com.sk89q.worldedit.world.registry.WorldData; import java.util.List; + import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -215,22 +221,53 @@ public class ForwardExtentCopy implements Operation { currentTransform = transform; } + Extent finalDest = destination; + TransformExtent transExt; + if (!currentTransform.isIdentity()) { + WorldData wd; + if (destination instanceof World) { + wd = ((World) destination).getWorldData(); + } else if (source instanceof World) { + wd = ((World) source).getWorldData(); + } else { + wd = WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData(); + } + BlockRegistry registry = wd.getBlockRegistry(); + transExt = new TransformExtent(finalDest, registry); + transExt.setTransform(currentTransform); + transExt.setOrigin(from); + finalDest = transExt; + } else { + transExt = null; + } + Vector translation = to.subtract(from); + if (!translation.equals(Vector.ZERO)) { + finalDest = new BlockTranslateExtent(finalDest, translation.getBlockX(), translation.getBlockY(), translation.getBlockZ()); + } + RegionFunction copy = new SimpleBlockCopy(source, finalDest); + if (sourceMask != Masks.alwaysTrue()) { + copy = new RegionMaskingFilter(sourceMask, copy); + } + if (sourceFunction != null) { + copy = new CombinedRegionFunction(copy, sourceFunction); + } + RegionVisitor blockVisitor = new RegionVisitor(region, copy); + List entities = source.getEntities(region); for (int i = 0; i < repetitions; i++) { - ExtentBlockCopy blockCopy = new ExtentBlockCopy(source, from, destination, to, currentTransform); - RegionMaskingFilter filter = new RegionMaskingFilter(sourceMask, blockCopy); - RegionFunction function = sourceFunction != null ? new CombinedRegionFunction(filter, sourceFunction) : filter; - RegionVisitor blockVisitor = new RegionVisitor(region, function); - ExtentEntityCopy entityCopy = new ExtentEntityCopy(from, destination, to, currentTransform); entityCopy.setRemoving(removingEntities); EntityVisitor entityVisitor = new EntityVisitor(entities.iterator(), entityCopy); - currentTransform = currentTransform.combine(transform); Operations.completeBlindly(blockVisitor); Operations.completeBlindly(entityVisitor); + if (transExt != null) { + currentTransform = currentTransform.combine(transform); + transExt.setTransform(currentTransform); + } + affected += blockVisitor.getAffected(); } return null;