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;