Big fix to clipboards when relogging/restarting server etc.

- Refactor the way FAWE clipboards work slightly with regards to offsets (FAWE-added) and origin (upstream-present)
 - Better error messages when exceptions are thrown attempting to load DOC
 - No longer attempt to resolve old clipboard versions. Attempt to give informative error instead
 - Don't always delete clipboards held in memory on player logout unless configured to do so
 - Go back to fully closing the DOC on logout. We now attempt to lock the file which will hopefully help with debugging shared clipboard folders
This commit is contained in:
dordsor21 2021-09-24 12:22:01 +01:00
parent bfd9e5b347
commit e97c945b2f
No known key found for this signature in database
GPG Key ID: 1E53E88969FFCF0B
12 changed files with 146 additions and 116 deletions

View File

@ -209,6 +209,7 @@ public class Fawe {
case MAX_ENTITIES: case MAX_ENTITIES:
case MAX_ITERATIONS: case MAX_ITERATIONS:
case OUTSIDE_REGION: case OUTSIDE_REGION:
case CLIPBOARD:
if (!faweExceptionReasonsUsed[type.ordinal()]) { if (!faweExceptionReasonsUsed[type.ordinal()]) {
faweExceptionReasonsUsed[type.ordinal()] = true; faweExceptionReasonsUsed[type.ordinal()] = true;
throw e; throw e;

View File

@ -510,6 +510,11 @@ public class Settings extends Config {
public int COMPRESSION_LEVEL = 1; public int COMPRESSION_LEVEL = 1;
@Comment("Number of days to keep history on disk before deleting it") @Comment("Number of days to keep history on disk before deleting it")
public int DELETE_AFTER_DAYS = 1; public int DELETE_AFTER_DAYS = 1;
@Comment({
"If a players clipboard should be removed from memory upon player logout,",
" - Will not delete clipboards on disk"
})
public boolean DELETE_ON_LOGOUT = false;
} }

View File

@ -34,7 +34,7 @@ public class CPUOptimizedClipboard extends LinearClipboard {
public CPUOptimizedClipboard(Region region) { public CPUOptimizedClipboard(Region region) {
super(region.getDimensions()); super(region.getDimensions(), region.getMinimumPoint());
this.states = new char[getVolume()]; this.states = new char[getVolume()];
nbtMapLoc = new HashMap<>(); nbtMapLoc = new HashMap<>();
nbtMapIndex = new HashMap<>(); nbtMapIndex = new HashMap<>();

View File

@ -2,6 +2,7 @@ package com.fastasyncworldedit.core.extent.clipboard;
import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.internal.exception.FaweClipboardVersionMismatchException;
import com.fastasyncworldedit.core.jnbt.streamer.IntValueReader; import com.fastasyncworldedit.core.jnbt.streamer.IntValueReader;
import com.fastasyncworldedit.core.math.IntTriple; import com.fastasyncworldedit.core.math.IntTriple;
import com.fastasyncworldedit.core.util.MainUtil; import com.fastasyncworldedit.core.util.MainUtil;
@ -55,11 +56,10 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final int VERSION = 1; private static final int VERSION = 1;
private static final int HEADER_SIZE = 22;
private final HashMap<IntTriple, CompoundTag> nbtMap; private final HashMap<IntTriple, CompoundTag> nbtMap;
private final File file; private final File file;
private final int version;
private final int HEADER_SIZE;
private RandomAccessFile braf; private RandomAccessFile braf;
private MappedByteBuffer byteBuffer; private MappedByteBuffer byteBuffer;
@ -77,6 +77,7 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable
) )
); );
setOffset(region.getMinimumPoint()); setOffset(region.getMinimumPoint());
setOrigin(region.getMinimumPoint());
} }
public DiskOptimizedClipboard(BlockVector3 dimensions) { public DiskOptimizedClipboard(BlockVector3 dimensions) {
@ -90,8 +91,7 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable
} }
public DiskOptimizedClipboard(BlockVector3 dimensions, File file) { public DiskOptimizedClipboard(BlockVector3 dimensions, File file) {
super(dimensions); super(dimensions, BlockVector3.ZERO);
HEADER_SIZE = 20;
if (HEADER_SIZE + ((long) getVolume() << 1) >= Integer.MAX_VALUE) { if (HEADER_SIZE + ((long) getVolume() << 1) >= Integer.MAX_VALUE) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Dimensions too large for this clipboard format. Use //lazycopy for large selections."); "Dimensions too large for this clipboard format. Use //lazycopy for large selections.");
@ -119,29 +119,27 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable
braf.setLength(fileLength); braf.setLength(fileLength);
init(); init();
// write getLength() etc // write getLength() etc
byteBuffer.putChar(0, (char) (version = VERSION)); byteBuffer.putChar(2, (char) (VERSION));
byteBuffer.putChar(2, (char) getWidth()); byteBuffer.putChar(4, (char) getWidth());
byteBuffer.putChar(4, (char) getHeight()); byteBuffer.putChar(6, (char) getHeight());
byteBuffer.putChar(6, (char) getLength()); byteBuffer.putChar(8, (char) getLength());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public DiskOptimizedClipboard(File file) { public DiskOptimizedClipboard(File file) {
super(readSize(file)); super(readSize(file), BlockVector3.ZERO);
nbtMap = new HashMap<>(); nbtMap = new HashMap<>();
try { try {
this.file = file; this.file = file;
this.braf = new RandomAccessFile(file, "rw"); this.braf = new RandomAccessFile(file, "rw");
braf.setLength(file.length()); braf.setLength(file.length());
init(); init();
version = byteBuffer.getChar(0);
HEADER_SIZE = (version > 0 ? 20 : 14);
if (braf.length() - HEADER_SIZE == ((long) getVolume() << 1) + (long) ((getHeight() >> 2) + 1) * ((getLength() >> 2) + 1) * ((getWidth() >> 2) + 1)) { if (braf.length() - HEADER_SIZE == ((long) getVolume() << 1) + (long) ((getHeight() >> 2) + 1) * ((getLength() >> 2) + 1) * ((getWidth() >> 2) + 1)) {
hasBiomes = true; hasBiomes = true;
} }
getOffset();
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -149,16 +147,11 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable
private static BlockVector3 readSize(File file) { private static BlockVector3 readSize(File file) {
try (DataInputStream is = new DataInputStream(new FileInputStream(file))) { try (DataInputStream is = new DataInputStream(new FileInputStream(file))) {
is.skipBytes(2);
int version = is.readChar(); int version = is.readChar();
int x; if (version != VERSION) {
if (version != 1) { throw new FaweClipboardVersionMismatchException();
x = version;
} else {
x = is.readChar();
} }
// if (version > VERSION) {
// throw new UnsupportedOperationException("Unsupported clipboard-on-disk version: " + version);
// }
return BlockVector3.at(is.readChar(), is.readChar(), is.readChar()); return BlockVector3.at(is.readChar(), is.readChar(), is.readChar());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@ -178,6 +171,7 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable
private void init() throws IOException { private void init() throws IOException {
if (this.fileChannel == null) { if (this.fileChannel == null) {
this.fileChannel = braf.getChannel(); this.fileChannel = braf.getChannel();
this.fileChannel.lock();
this.byteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, file.length()); this.byteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, file.length());
} }
} }
@ -257,7 +251,6 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable
} }
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@ -278,17 +271,12 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable
BlockVector3.at(0, 0, 0), BlockVector3.at(0, 0, 0),
BlockVector3.at(getWidth() - 1, getHeight() - 1, getLength() - 1) BlockVector3.at(getWidth() - 1, getHeight() - 1, getLength() - 1)
); );
int originX = byteBuffer.getShort(8); int offsetX = byteBuffer.getShort(16);
int originY = byteBuffer.getShort(10); int offsetY = byteBuffer.getShort(18);
int originZ = byteBuffer.getShort(12); int offsetZ = byteBuffer.getShort(20);
if (version == 1) {
int offsetX = byteBuffer.getShort(14);
int offsetY = byteBuffer.getShort(16);
int offsetZ = byteBuffer.getShort(18);
region.shift(BlockVector3.at(offsetX, offsetY, offsetZ)); region.shift(BlockVector3.at(offsetX, offsetY, offsetZ));
}
BlockArrayClipboard clipboard = new BlockArrayClipboard(region, this); BlockArrayClipboard clipboard = new BlockArrayClipboard(region, this);
clipboard.setOrigin(BlockVector3.at(originX, originY, originZ)); clipboard.setOrigin(getOrigin());
return clipboard; return clipboard;
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
@ -298,9 +286,9 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable
@Override @Override
public BlockVector3 getOrigin() { public BlockVector3 getOrigin() {
int ox = byteBuffer.getShort(8); int ox = byteBuffer.getShort(10);
int oy = byteBuffer.getShort(10); int oy = byteBuffer.getShort(12);
int oz = byteBuffer.getShort(12); int oz = byteBuffer.getShort(14);
return BlockVector3.at(ox, oy, oz); return BlockVector3.at(ox, oy, oz);
} }
@ -308,27 +296,33 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable
public void setOrigin(BlockVector3 offset) { public void setOrigin(BlockVector3 offset) {
super.setOrigin(offset); super.setOrigin(offset);
try { try {
byteBuffer.putShort(8, (short) offset.getBlockX()); byteBuffer.putShort(10, (short) offset.getBlockX());
byteBuffer.putShort(10, (short) offset.getBlockY()); byteBuffer.putShort(12, (short) offset.getBlockY());
byteBuffer.putShort(12, (short) offset.getBlockZ()); byteBuffer.putShort(14, (short) offset.getBlockZ());
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
private void setOffset(BlockVector3 offset) { @Override
if (version == 0) { protected void setOffset(BlockVector3 offset) {
return; super.setOffset(offset);
}
try { try {
byteBuffer.putShort(14, (short) offset.getBlockX()); byteBuffer.putShort(16, (short) offset.getBlockX());
byteBuffer.putShort(16, (short) offset.getBlockY()); byteBuffer.putShort(18, (short) offset.getBlockY());
byteBuffer.putShort(18, (short) offset.getBlockZ()); byteBuffer.putShort(20, (short) offset.getBlockZ());
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
private void getOffset() {
int x = byteBuffer.getShort(16);
int y = byteBuffer.getShort(18);
int z = byteBuffer.getShort(20);
super.setOffset(BlockVector3.at(x, y, z));
}
@Override @Override
public void flush() { public void flush() {
byteBuffer.force(); byteBuffer.force();

View File

@ -25,8 +25,8 @@ public abstract class LinearClipboard extends SimpleClipboard {
protected final HashSet<ClipboardEntity> entities = new HashSet<>(); protected final HashSet<ClipboardEntity> entities = new HashSet<>();
public LinearClipboard(BlockVector3 dimensions) { public LinearClipboard(BlockVector3 dimensions, BlockVector3 offset) {
super(dimensions); super(dimensions, offset);
} }
// We shouldn't expose methods that directly reference the index as people cannot be trusted to use it properly. // We shouldn't expose methods that directly reference the index as people cannot be trusted to use it properly.

View File

@ -58,7 +58,7 @@ public class MemoryOptimizedClipboard extends LinearClipboard {
} }
public MemoryOptimizedClipboard(Region region, int compressionLevel) { public MemoryOptimizedClipboard(Region region, int compressionLevel) {
super(region.getDimensions()); super(region.getDimensions(), region.getMinimumPoint());
states = new byte[1 + (getVolume() >> BLOCK_SHIFT)][]; states = new byte[1 + (getVolume() >> BLOCK_SHIFT)][];
nbtMap = new HashMap<>(); nbtMap = new HashMap<>();
this.compressionLevel = compressionLevel; this.compressionLevel = compressionLevel;

View File

@ -10,10 +10,11 @@ public abstract class SimpleClipboard implements Clipboard {
private final BlockVector3 size; private final BlockVector3 size;
private final int area; private final int area;
private final int volume; private final int volume;
private BlockVector3 offset;
private BlockVector3 origin; private BlockVector3 origin;
SimpleClipboard(BlockVector3 dimensions, BlockVector3 offset) {
SimpleClipboard(BlockVector3 dimensions) {
this.size = dimensions; this.size = dimensions;
this.offset = offset;
long longVolume = (long) getWidth() * (long) getHeight() * (long) getLength(); long longVolume = (long) getWidth() * (long) getHeight() * (long) getLength();
if (longVolume >= Integer.MAX_VALUE) { if (longVolume >= Integer.MAX_VALUE) {
throw new IllegalArgumentException("Dimensions are too large for this clipboard format."); throw new IllegalArgumentException("Dimensions are too large for this clipboard format.");
@ -24,12 +25,11 @@ public abstract class SimpleClipboard implements Clipboard {
} }
SimpleClipboard(Region region) { SimpleClipboard(Region region) {
this(region.getDimensions()); this(region.getDimensions(), region.getMinimumPoint());
} }
@Override protected void setOffset(final BlockVector3 offset) {
public void setOrigin(BlockVector3 offset) { this.offset = offset;
this.origin = offset;
} }
@Override @Override
@ -37,6 +37,11 @@ public abstract class SimpleClipboard implements Clipboard {
return origin; return origin;
} }
@Override
public void setOrigin(BlockVector3 origin) {
this.origin = origin.subtract(offset);
}
@Override @Override
public BlockVector3 getMinimumPoint() { public BlockVector3 getMinimumPoint() {
return BlockVector3.ZERO; return BlockVector3.ZERO;

View File

@ -0,0 +1,11 @@
package com.fastasyncworldedit.core.internal.exception;
import com.fastasyncworldedit.core.configuration.Caption;
public class FaweClipboardVersionMismatchException extends FaweException {
public FaweClipboardVersionMismatchException() {
super(Caption.of("fawe.error.clipboard.on.disk.version.mismatch"), Type.CLIPBOARD);
}
}

View File

@ -90,6 +90,7 @@ public class FaweException extends RuntimeException {
CHUNK, CHUNK,
PLAYER_ONLY, PLAYER_ONLY,
ACTOR_REQUIRED, ACTOR_REQUIRED,
CLIPBOARD,
OTHER OTHER
} }

View File

@ -23,6 +23,7 @@ import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.clipboard.DiskOptimizedClipboard; import com.fastasyncworldedit.core.extent.clipboard.DiskOptimizedClipboard;
import com.fastasyncworldedit.core.internal.exception.FaweClipboardVersionMismatchException;
import com.fastasyncworldedit.core.regions.FaweMaskManager; import com.fastasyncworldedit.core.regions.FaweMaskManager;
import com.fastasyncworldedit.core.util.MainUtil; import com.fastasyncworldedit.core.util.MainUtil;
import com.sk89q.worldedit.EmptyClipboardException; import com.sk89q.worldedit.EmptyClipboardException;
@ -46,7 +47,6 @@ import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.HandSide;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockStateHolder;
@ -353,8 +353,6 @@ public interface Player extends Entity, Actor {
Region getLargestRegion(); Region getLargestRegion();
void setSelection(Region region);
/** /**
* Get the player's selection region. If the selection is defined in * Get the player's selection region. If the selection is defined in
* a different world, the {@code IncompleteRegionException} * a different world, the {@code IncompleteRegionException}
@ -367,6 +365,8 @@ public interface Player extends Entity, Actor {
return getSession().getSelection(getWorld()); return getSession().getSelection(getWorld());
} }
void setSelection(Region region);
/** /**
* Set the player's WorldEdit selection. * Set the player's WorldEdit selection.
* *
@ -391,7 +391,9 @@ public interface Player extends Entity, Actor {
*/ */
default void unregister() { default void unregister() {
cancel(true); cancel(true);
getSession().flushClipboard(); if (Settings.IMP.CLIPBOARD.DELETE_ON_LOGOUT || Settings.IMP.CLIPBOARD.USE_DISK) {
getSession().setClipboard(null);
}
if (Settings.IMP.HISTORY.DELETE_ON_LOGOUT) { if (Settings.IMP.HISTORY.DELETE_ON_LOGOUT) {
getSession().clearHistory(); getSession().clearHistory();
} }
@ -421,18 +423,21 @@ public interface Player extends Entity, Actor {
ClipboardHolder holder = new ClipboardHolder(clip); ClipboardHolder holder = new ClipboardHolder(clip);
getSession().setClipboard(holder); getSession().setClipboard(holder);
} }
} catch (Exception event) { } catch (FaweClipboardVersionMismatchException e) {
printError(TextComponent.of("====== INVALID CLIPBOARD ======")); printError(Caption.of("fawe.error.clipboard.on.disk.version.mismatch"));
event.printStackTrace(); } catch (RuntimeException e) {
printError(Caption.of("fawe.error.clipboard.invalid"));
e.printStackTrace();
print(Caption.of("fawe.error.stacktrace"));
print(Caption.of("fawe.error.clipboard.load.failure"));
print(Caption.of("fawe.error.clipboard.invalid.info", file.getName(), file.length()));
print(Caption.of("fawe.error.stacktrace"));
} catch (Exception e) {
printError(Caption.of("fawe.error.clipboard.invalid"));
e.printStackTrace();
print(Caption.of("fawe.error.stacktrace")); print(Caption.of("fawe.error.stacktrace"));
print(Caption.of("fawe.error.no-failure")); print(Caption.of("fawe.error.no-failure"));
print(Caption.of( print(Caption.of("fawe.error.clipboard.invalid.info", file.getName(), file.length()));
"File: ",
TextComponent.of(file.getName()),
TextComponent.of(" (len:"),
TextComponent.of(file.length()),
TextComponent.of(")")
));
print(Caption.of("fawe.error.stacktrace")); print(Caption.of("fawe.error.stacktrace"));
} }
} }

View File

@ -57,8 +57,9 @@ public class BlockArrayClipboard implements Clipboard {
//FAWE start //FAWE start
private final Region region; private final Region region;
private final BlockVector3 origin;
private final Clipboard parent; private final Clipboard parent;
private final BlockVector3 offset;
private BlockVector3 origin;
/** /**
* Create a new instance. Creates a parent clipboard based on the clipboard settings in settings.yml, with a randomly * Create a new instance. Creates a parent clipboard based on the clipboard settings in settings.yml, with a randomly
@ -93,7 +94,8 @@ public class BlockArrayClipboard implements Clipboard {
Region shifted = parent.getRegion().clone(); Region shifted = parent.getRegion().clone();
shifted.shift(offset); shifted.shift(offset);
this.region = shifted; this.region = shifted;
this.origin = shifted.getMinimumPoint(); this.offset = shifted.getMinimumPoint();
this.origin = parent.getOrigin();
} }
/** /**
@ -129,7 +131,8 @@ public class BlockArrayClipboard implements Clipboard {
checkNotNull(region); checkNotNull(region);
this.parent = parent; this.parent = parent;
this.region = region.clone(); this.region = region.clone();
this.origin = region.getMinimumPoint(); this.offset = region.getMinimumPoint();
this.origin = parent.getOrigin();
} }
//FAWE end //FAWE end
@ -140,12 +143,13 @@ public class BlockArrayClipboard implements Clipboard {
@Override @Override
public BlockVector3 getOrigin() { public BlockVector3 getOrigin() {
return getParent().getOrigin().add(region.getMinimumPoint()); return origin;
} }
@Override @Override
public void setOrigin(BlockVector3 origin) { public void setOrigin(BlockVector3 origin) {
getParent().setOrigin(origin.subtract(region.getMinimumPoint())); this.origin = origin;
getParent().setOrigin(origin);
} }
@Override @Override
@ -161,9 +165,9 @@ public class BlockArrayClipboard implements Clipboard {
@Override @Override
public BlockState getBlock(BlockVector3 position) { public BlockState getBlock(BlockVector3 position) {
if (region.contains(position)) { if (region.contains(position)) {
int x = position.getBlockX() - origin.getX(); int x = position.getBlockX() - offset.getX();
int y = position.getBlockY() - origin.getY(); int y = position.getBlockY() - offset.getY();
int z = position.getBlockZ() - origin.getZ(); int z = position.getBlockZ() - offset.getZ();
return getParent().getBlock(x, y, z); return getParent().getBlock(x, y, z);
} }
@ -173,9 +177,9 @@ public class BlockArrayClipboard implements Clipboard {
@Override @Override
public BaseBlock getFullBlock(BlockVector3 position) { public BaseBlock getFullBlock(BlockVector3 position) {
if (region.contains(position)) { if (region.contains(position)) {
int x = position.getBlockX() - origin.getX(); int x = position.getBlockX() - offset.getX();
int y = position.getBlockY() - origin.getY(); int y = position.getBlockY() - offset.getY();
int z = position.getBlockZ() - origin.getZ(); int z = position.getBlockZ() - offset.getZ();
return getParent().getFullBlock(x, y, z); return getParent().getFullBlock(x, y, z);
} }
return BlockTypes.AIR.getDefaultState().toBaseBlock(); return BlockTypes.AIR.getDefaultState().toBaseBlock();
@ -197,9 +201,9 @@ public class BlockArrayClipboard implements Clipboard {
//FAWE start //FAWE start
@Override @Override
public boolean setTile(int x, int y, int z, CompoundTag tag) { public boolean setTile(int x, int y, int z, CompoundTag tag) {
x -= origin.getX(); x -= offset.getX();
y -= origin.getY(); y -= offset.getY();
z -= origin.getZ(); z -= offset.getZ();
return getParent().setTile(x, y, z, tag); return getParent().setTile(x, y, z, tag);
} }
@ -210,9 +214,9 @@ public class BlockArrayClipboard implements Clipboard {
@Override @Override
public <B extends BlockStateHolder<B>> boolean setBlock(int x, int y, int z, B block) throws WorldEditException { public <B extends BlockStateHolder<B>> boolean setBlock(int x, int y, int z, B block) throws WorldEditException {
x -= origin.getX(); x -= offset.getX();
y -= origin.getY(); y -= offset.getY();
z -= origin.getZ(); z -= offset.getZ();
return parent.setBlock(x, y, z, block); return parent.setBlock(x, y, z, block);
} }
@ -223,39 +227,39 @@ public class BlockArrayClipboard implements Clipboard {
@Override @Override
public BiomeType getBiome(BlockVector3 position) { public BiomeType getBiome(BlockVector3 position) {
BlockVector3 v = position.subtract(region.getMinimumPoint()); BlockVector3 v = position.subtract(offset);
return getParent().getBiomeType(v.getX(), v.getY(), v.getZ()); return getParent().getBiomeType(v.getX(), v.getY(), v.getZ());
} }
@Override @Override
public boolean setBiome(BlockVector3 position, BiomeType biome) { public boolean setBiome(BlockVector3 position, BiomeType biome) {
int x = position.getBlockX() - origin.getX(); int x = position.getBlockX() - offset.getX();
int y = position.getBlockY() - origin.getY(); int y = position.getBlockY() - offset.getY();
int z = position.getBlockZ() - origin.getZ(); int z = position.getBlockZ() - offset.getZ();
return getParent().setBiome(x, y, z, biome); return getParent().setBiome(x, y, z, biome);
} }
@Override @Override
public boolean setBiome(int x, int y, int z, BiomeType biome) { public boolean setBiome(int x, int y, int z, BiomeType biome) {
x -= origin.getX(); x -= offset.getX();
y -= origin.getY(); y -= offset.getY();
z -= origin.getZ(); z -= offset.getZ();
return getParent().setBiome(x, y, z, biome); return getParent().setBiome(x, y, z, biome);
} }
@Override @Override
public List<? extends Entity> getEntities(Region region) { public List<? extends Entity> getEntities(Region region) {
region = region.clone(); region = region.clone();
region.shift(BlockVector3.ZERO.subtract(origin)); region.shift(BlockVector3.ZERO.subtract(offset));
return getParent().getEntities(region).stream().map(e -> return getParent().getEntities(region).stream().map(e ->
{ {
if (e instanceof ClipboardEntity) { if (e instanceof ClipboardEntity) {
ClipboardEntity ce = (ClipboardEntity) e; ClipboardEntity ce = (ClipboardEntity) e;
Location oldloc = ce.getLocation(); Location oldloc = ce.getLocation();
Location loc = new Location(oldloc.getExtent(), Location loc = new Location(oldloc.getExtent(),
oldloc.getX() + origin.getBlockX(), oldloc.getX() + offset.getBlockX(),
oldloc.getY() + origin.getBlockY(), oldloc.getY() + offset.getBlockY(),
oldloc.getZ() + origin.getBlockZ(), oldloc.getZ() + offset.getBlockZ(),
oldloc.getYaw(), oldloc.getPitch() oldloc.getYaw(), oldloc.getPitch()
); );
return new ClipboardEntity(loc, ce.entity); return new ClipboardEntity(loc, ce.entity);
@ -272,9 +276,9 @@ public class BlockArrayClipboard implements Clipboard {
ClipboardEntity ce = (ClipboardEntity) e; ClipboardEntity ce = (ClipboardEntity) e;
Location oldloc = ce.getLocation(); Location oldloc = ce.getLocation();
Location loc = new Location(oldloc.getExtent(), Location loc = new Location(oldloc.getExtent(),
oldloc.getX() + origin.getBlockX(), oldloc.getX() + offset.getBlockX(),
oldloc.getY() + origin.getBlockY(), oldloc.getY() + offset.getBlockY(),
oldloc.getZ() + origin.getBlockZ(), oldloc.getZ() + offset.getBlockZ(),
oldloc.getYaw(), oldloc.getPitch() oldloc.getYaw(), oldloc.getPitch()
); );
return new ClipboardEntity(loc, ce.entity); return new ClipboardEntity(loc, ce.entity);
@ -287,9 +291,9 @@ public class BlockArrayClipboard implements Clipboard {
@Nullable @Nullable
public Entity createEntity(Location location, BaseEntity entity) { public Entity createEntity(Location location, BaseEntity entity) {
Location l = new Location(location.getExtent(), Location l = new Location(location.getExtent(),
location.getX() - origin.getBlockX(), location.getX() - offset.getBlockX(),
location.getY() - origin.getBlockY(), location.getY() - offset.getBlockY(),
location.getZ() - origin.getBlockZ(), location.getZ() - offset.getBlockZ(),
location.getYaw(), location.getPitch() location.getYaw(), location.getPitch()
); );
return getParent().createEntity(l, entity); return getParent().createEntity(l, entity);
@ -297,39 +301,39 @@ public class BlockArrayClipboard implements Clipboard {
@Override @Override
public void removeEntity(int x, int y, int z, UUID uuid) { public void removeEntity(int x, int y, int z, UUID uuid) {
x -= origin.getX(); x -= offset.getX();
y -= origin.getY(); y -= offset.getY();
z -= origin.getZ(); z -= offset.getZ();
getParent().removeEntity(x, y, z, uuid); getParent().removeEntity(x, y, z, uuid);
} }
@Override @Override
public BlockState getBlock(int x, int y, int z) { public BlockState getBlock(int x, int y, int z) {
x -= origin.getX(); x -= offset.getX();
y -= origin.getY(); y -= offset.getY();
z -= origin.getZ(); z -= offset.getZ();
return getParent().getBlock(x, y, z); return getParent().getBlock(x, y, z);
} }
@Override @Override
public BaseBlock getFullBlock(int x, int y, int z) { public BaseBlock getFullBlock(int x, int y, int z) {
x -= origin.getX(); x -= offset.getX();
y -= origin.getY(); y -= offset.getY();
z -= origin.getZ(); z -= offset.getZ();
return getParent().getFullBlock(x, y, z); return getParent().getFullBlock(x, y, z);
} }
@Override @Override
public BiomeType getBiomeType(int x, int y, int z) { public BiomeType getBiomeType(int x, int y, int z) {
x -= origin.getX(); x -= offset.getX();
z -= origin.getZ(); z -= offset.getZ();
return getParent().getBiomeType(x, y, z); return getParent().getBiomeType(x, y, z);
} }
@Nonnull @Nonnull
@Override @Override
public Iterator<BlockVector3> iterator() { public Iterator<BlockVector3> iterator() {
OffsetBlockVector3 mutable = new OffsetBlockVector3(origin); OffsetBlockVector3 mutable = new OffsetBlockVector3(offset);
return Iterators.transform(getParent().iterator(), mutable::init); return Iterators.transform(getParent().iterator(), mutable::init);
} }
@ -337,12 +341,12 @@ public class BlockArrayClipboard implements Clipboard {
public Iterator<BlockVector2> iterator2d() { public Iterator<BlockVector2> iterator2d() {
MutableBlockVector2 mutable = new MutableBlockVector2(); MutableBlockVector2 mutable = new MutableBlockVector2();
return Iterators.transform(getParent().iterator2d(), input -> return Iterators.transform(getParent().iterator2d(), input ->
mutable.setComponents(input.getX() + origin.getX(), input.getZ() + origin.getZ())); mutable.setComponents(input.getX() + offset.getX(), input.getZ() + offset.getZ()));
} }
@Override @Override
public Iterator<BlockVector3> iterator(Order order) { public Iterator<BlockVector3> iterator(Order order) {
OffsetBlockVector3 mutable = new OffsetBlockVector3(origin); OffsetBlockVector3 mutable = new OffsetBlockVector3(offset);
return Iterators.transform(getParent().iterator(order), mutable::init); return Iterators.transform(getParent().iterator(order), mutable::init);
} }
//FAWE end //FAWE end

View File

@ -144,6 +144,10 @@
"fawe.error.parse.unknown-transform": "Unknown transform: {0}, See: {1}", "fawe.error.parse.unknown-transform": "Unknown transform: {0}, See: {1}",
"fawe.error.parse.no-clipboard": "To use {0}, please first copy something to your clipboard", "fawe.error.parse.no-clipboard": "To use {0}, please first copy something to your clipboard",
"fawe.error.parse.no-clipboard-source": "No clipboards found at given source: {0}", "fawe.error.parse.no-clipboard-source": "No clipboards found at given source: {0}",
"fawe.error.clipboard.invalid": "====== INVALID CLIPBOARD ======",
"fawe.error.clipboard.invalid.info": "File: {0} (len: {1})",
"fawe.error.clipboard.load.failure": "Could not load clipboard. Possible that the clipboard is still being written to from another server?!",
"fawe.error.clipboard.on.disk.version.mismatch": "Clipboard version mismatch. Please delete your clipboards folder and restart the server.",
"fawe.cancel.worldedit.cancel.count": "Cancelled {0} edits.", "fawe.cancel.worldedit.cancel.count": "Cancelled {0} edits.",
"fawe.cancel.worldedit.cancel.reason.confirm": "Use //confirm to execute {0}", "fawe.cancel.worldedit.cancel.reason.confirm": "Use //confirm to execute {0}",