Add #clipboard transform
Add #fullcopy pattern
- similar to transform, it pastes the full clipboard at any changed
block
- e.g. //replace <marker> #fullcopy
Async block get optimizations for bukkit by running tasks multiple times
during a single tick
Tweak the OOM message to be more informative
Tweak the max memory config comment to be more informative
Restructured transforms to use resettable extent + reduce code
duplication
Clipboards can now be streamed to a schematic file without significant
memory overhead
- This means you can now load/paste/copy/save arbitrarily large sizes
with fixed memory usage
Optimizations to the various clipboard implementations
Add optimized extent block translation (no additional object creation
for set block)
Optimized forward extent block copy
- use optimized extent transform/translate
- pre calculate required functions/extents outside iterations
- short circuit certain functions depending on input parameters
Use edit session for patterns rather than world (faster/safer)
This commit is contained in:
Jesse Boyd 2016-12-06 15:57:24 +11:00
parent 98b79e7ccc
commit 1c948cf0ed
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
36 changed files with 1352 additions and 474 deletions

View File

@ -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();

View File

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

View File

@ -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"),

View File

@ -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;

View File

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

View File

@ -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<Vector, BaseBlock> 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<CompoundTag> getTileEntities() {
return parent.getTileEntities();
}
}

View File

@ -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<IntegerTrio, CompoundTag> 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<CompoundTag> getTileEntities() {
convertTilesToIndex();
for (Map.Entry<Integer, CompoundTag> entry : nbtMapIndex.entrySet()) {
int index = entry.getKey();
CompoundTag tag = entry.getValue();
Map<String, Tag> 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<String, Tag> 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;
}

View File

@ -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<CompoundTag> getTileEntities() {
return new ArrayList<>(nbtMap.values());
}
@Override
public void forEach(final RunnableVal2<Vector,BaseBlock> 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<String, Tag> 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();

View File

@ -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<Vector,BaseBlock> task, boolean air);
public void streamIds(final NBTStreamer.ByteReader task) {
forEach(new RunnableVal2<Vector, BaseBlock>() {
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<Vector, BaseBlock>() {
private int index = 0;
@Override
public void run(Vector pos, BaseBlock block) {
task.run(index++, block.getData());
}
}, true);
}
public List<CompoundTag> getTileEntities() {
final List<CompoundTag> tiles = new ArrayList<>();
forEach(new RunnableVal2<Vector, BaseBlock>() {
private int index = 0;
@Override
public void run(Vector pos, BaseBlock block) {
CompoundTag tag = block.getNbtData();
if (tag != null) {
Map<String, Tag> 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.
*/

View File

@ -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<IntegerTrio, CompoundTag> 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<CompoundTag> getTileEntities() {
convertTilesToIndex();
for (Map.Entry<Integer, CompoundTag> entry : nbtMapIndex.entrySet()) {
int index = entry.getKey();
CompoundTag tag = entry.getValue();
Map<String, Tag> 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<String, Tag> 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;
}

View File

@ -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<Vector, BaseBlock> task, boolean air) {
Iterator<BlockVector> 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<String, Tag> 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<String, Tag> 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<String, Tag> 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);
}
}
};

View File

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

View File

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

View File

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

View File

@ -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<TransformExtent> {
public class DefaultTransformParser extends InputParser<ResettableExtent> {
public DefaultTransformParser(WorldEdit worldEdit) {
super(worldEdit);
@ -44,7 +49,7 @@ public class DefaultTransformParser extends InputParser<TransformExtent> {
}
@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<TransformExtent> {
}
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<TransformExtent> {
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:<dx>:<dy>:<dz>");
throw new InputParseException("The correct format is #rotate:<dx>:<dy>:<dz>");
}
}
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 + "'");

View File

@ -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<Entity> getEntities(Region region) {
return Collections.emptyList();
}
public List<Entity> 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;
}
}

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<? extends Entity> getEntities() {
return editSession.getEntities(region);
}
@Override
public void forEach(RunnableVal2<Vector, BaseBlock> 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);

View File

@ -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) {

View File

@ -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<String, Tag> 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?
}
/**

View File

@ -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<AbstractDelegateExtent> traverser = new ExtentTraverser(this.extent).find(TransformExtent.class);
ExtentTraverser<AbstractDelegateExtent> 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();
}

View File

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

View File

@ -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<? extends Entity> getEntities() {
return editSession.getEntities(region);
}
@Override
public void forEach(RunnableVal2<Vector, BaseBlock> task, boolean air) {
Iterator<BlockVector> 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);

View File

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

View File

@ -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) {

View File

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

View File

@ -57,7 +57,21 @@ public class HashTagPatternParser extends InputParser<Pattern> {
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<Pattern> {
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":

View File

@ -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<CompoundTag> tileEntities = clipboard.IMP.getTileEntities();
out.writeNamedTag("TileEntities", new ListTag(CompoundTag.class, tileEntities));
List<Tag> entities = new ArrayList<Tag>();
for (Entity entity : clipboard.getEntities()) {
BaseEntity state = entity.getState();
if (state != null) {
Map<String, Tag> values = new HashMap<String, Tag>();
// 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();
}

View File

@ -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<String, Tag> 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<String, Tag> 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);
}
/**

View File

@ -0,0 +1,120 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<String, Tag> values = ReflectionUtils.getMap(tag.getValue());
values.put("Rot", new ByteTag((byte) MCDirections.toRotation(newDirection)));
}
}
}
}
return state;
}
}

View File

@ -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<? extends Entity> 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;