com.fastasyncworldedit.core.history.changeset.FaweStreamChangeSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of FastAsyncWorldEdit-Core Show documentation
Show all versions of FastAsyncWorldEdit-Core Show documentation
Blazingly fast Minecraft world manipulation for artists, builders and everyone else.
package com.fastasyncworldedit.core.history.changeset;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.history.change.MutableBiomeChange;
import com.fastasyncworldedit.core.history.change.MutableBlockChange;
import com.fastasyncworldedit.core.history.change.MutableEntityChange;
import com.fastasyncworldedit.core.history.change.MutableFullBlockChange;
import com.fastasyncworldedit.core.history.change.MutableTileChange;
import com.fastasyncworldedit.core.internal.exception.FaweSmallEditUnsupportedException;
import com.fastasyncworldedit.core.internal.io.FaweInputStream;
import com.fastasyncworldedit.core.internal.io.FaweOutputStream;
import com.fastasyncworldedit.core.util.MainUtil;
import com.fastasyncworldedit.core.util.MathMan;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* FAWE stream ChangeSet offering support for extended-height worlds
*/
public abstract class FaweStreamChangeSet extends AbstractChangeSet {
public static final int HEADER_SIZE = 9;
private static final int version = 1;
private int mode;
private final int compression;
private final int minY;
protected FaweStreamIdDelegate idDel;
protected FaweStreamPositionDelegate posDel;
public FaweStreamChangeSet(World world) {
this(world, Settings.settings().HISTORY.COMPRESSION_LEVEL, Settings.settings().HISTORY.STORE_REDO, Settings.settings().HISTORY.SMALL_EDITS);
}
public FaweStreamChangeSet(World world, int compression, boolean storeRedo, boolean smallLoc) {
super(world);
this.compression = compression;
this.minY = world.getMinY();
init(storeRedo, smallLoc);
}
private void init(boolean storeRedo, boolean smallLoc) {
if (storeRedo) {
if (smallLoc) {
mode = 4;
} else {
mode = 3;
}
} else if (smallLoc) {
mode = 1;
} else {
mode = 2;
}
}
public interface FaweStreamPositionDelegate {
void write(OutputStream out, int x, int y, int z) throws IOException;
int readX(FaweInputStream in) throws IOException;
int readY(FaweInputStream in) throws IOException;
int readZ(FaweInputStream in) throws IOException;
}
public interface FaweStreamIdDelegate {
void writeChange(FaweOutputStream out, int from, int to) throws IOException;
void readCombined(FaweInputStream in, MutableBlockChange change, boolean dir) throws IOException;
void readCombined(FaweInputStream in, MutableFullBlockChange change) throws IOException;
}
protected void setupStreamDelegates(int mode) {
this.mode = mode;
if (mode == 3 || mode == 4) {
idDel = new FaweStreamIdDelegate() {
@Override
public void writeChange(FaweOutputStream stream, int combinedFrom, int combinedTo) throws IOException {
stream.writeVarInt(combinedFrom);
stream.writeVarInt(combinedTo);
}
@Override
public void readCombined(FaweInputStream is, MutableBlockChange change, boolean dir) throws IOException {
if (dir) {
is.readVarInt();
change.ordinal = is.readVarInt();
} else {
change.ordinal = is.readVarInt();
is.readVarInt();
}
}
@Override
public void readCombined(FaweInputStream is, MutableFullBlockChange change) throws IOException {
change.from = is.readVarInt();
change.to = is.readVarInt();
}
};
} else {
idDel = new FaweStreamIdDelegate() {
@Override
public void writeChange(FaweOutputStream stream, int combinedFrom, int to) throws IOException {
stream.writeVarInt(combinedFrom);
}
@Override
public void readCombined(FaweInputStream in, MutableBlockChange change, boolean dir) throws IOException {
int from1 = in.read();
int from2 = in.read();
change.ordinal = in.readVarInt();
}
@Override
public void readCombined(FaweInputStream is, MutableFullBlockChange change) throws IOException {
change.from = is.readVarInt();
change.to = BlockTypes.AIR.getInternalId();
}
};
}
if (mode == 1 || mode == 4) { // small
posDel = new FaweStreamPositionDelegate() {
int lx;
int ly;
int lz;
@Override
public void write(OutputStream out, int x, int y, int z) throws IOException {
if (y < 0 || y > 255) {
throw new FaweSmallEditUnsupportedException();
}
int rx = -lx + (lx = x);
int ry = -ly + (ly = y);
int rz = -lz + (lz = z);
byte b1 = (byte) (ry);
byte b2 = (byte) (rx);
byte b3 = (byte) (rz);
int x16 = (rx >> 8) & 0xF;
int z16 = (rz >> 8) & 0xF;
byte b4 = MathMan.pair16(x16, z16);
out.write(b1);
out.write(b2);
out.write(b3);
out.write(b4);
}
final byte[] buffer = new byte[4];
@Override
public int readX(FaweInputStream in) throws IOException {
in.readFully(buffer);
return lx = lx + ((((buffer[1] & 0xFF) + ((MathMan.unpair16x(buffer[3])) << 8)) << 20) >> 20);
}
@Override
public int readY(FaweInputStream in) {
return (ly = ly + buffer[0]) & 0xFF;
}
@Override
public int readZ(FaweInputStream in) throws IOException {
return lz = lz + ((((buffer[2] & 0xFF) + ((MathMan.unpair16y(buffer[3])) << 8)) << 20) >> 20);
}
};
} else {
posDel = new FaweStreamPositionDelegate() {
final byte[] buffer = new byte[6];
int lx;
int ly;
int lz;
@Override
public void write(OutputStream stream, int x, int y, int z) throws IOException {
int rx = -lx + (lx = x);
int ry = -ly + (ly = y);
int rz = -lz + (lz = z);
stream.write((rx) & 0xff);
stream.write(((rx) >> 8) & 0xff);
stream.write((rz) & 0xff);
stream.write(((rz) >> 8) & 0xff);
stream.write((ry) & 0xff);
stream.write(((ry) >> 8) & 0xff);
}
@Override
public int readX(FaweInputStream is) throws IOException {
is.readFully(buffer);
return lx = (lx + (buffer[0] & 0xFF) + (buffer[1] << 8));
}
@Override
public int readY(FaweInputStream is) throws IOException {
return ly = (ly + (buffer[4] & 0xFF) + (buffer[5] << 8));
}
@Override
public int readZ(FaweInputStream is) throws IOException {
return lz = (lz + (buffer[2] & 0xFF) + (buffer[3] << 8));
}
};
}
}
public void writeHeader(OutputStream os, int x, int y, int z) throws IOException {
os.write(mode);
// Allows for version detection of history in case of changes to format.
os.write(version);
setOrigin(x, z);
os.write((byte) (x >> 24));
os.write((byte) (x >> 16));
os.write((byte) (x >> 8));
os.write((byte) (x));
os.write((byte) (z >> 24));
os.write((byte) (z >> 16));
os.write((byte) (z >> 8));
os.write((byte) (z));
setupStreamDelegates(mode);
}
public void readHeader(InputStream is) throws IOException {
// skip mode
int mode = is.read();
int version = is.read();
if (version != FaweStreamChangeSet.version) {
throw new UnsupportedOperationException(String.format("Version %s history not supported!", version));
}
// origin
int x = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read());
int z = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read());
setOrigin(x, z);
setupStreamDelegates(mode);
}
public FaweOutputStream getCompressedOS(OutputStream os) throws IOException {
return MainUtil.getCompressedOS(os, compression);
}
@Override
public boolean isEmpty() {
if (blockSize > 0) {
return false;
}
if (waitingCombined.get() != 0 || waitingAsync.get() != 0) {
return false;
}
flush();
return blockSize == 0;
}
@Override
public int size() {
// Flush so we can accurately get the size
flush();
return blockSize;
}
public abstract int getCompressedSize();
public abstract long getSizeInMemory();
public long getSizeOnDisk() {
return 0;
}
public abstract FaweOutputStream getBlockOS(int x, int y, int z) throws IOException;
public abstract FaweOutputStream getBiomeOS() throws IOException;
public abstract NBTOutputStream getEntityCreateOS() throws IOException;
public abstract NBTOutputStream getEntityRemoveOS() throws IOException;
public abstract NBTOutputStream getTileCreateOS() throws IOException;
public abstract NBTOutputStream getTileRemoveOS() throws IOException;
public abstract FaweInputStream getBlockIS() throws IOException;
public abstract FaweInputStream getBiomeIS() throws IOException;
public abstract NBTInputStream getEntityCreateIS() throws IOException;
public abstract NBTInputStream getEntityRemoveIS() throws IOException;
public abstract NBTInputStream getTileCreateIS() throws IOException;
public abstract NBTInputStream getTileRemoveIS() throws IOException;
protected int blockSize;
private int originX;
private int originZ;
public void setOrigin(int x, int z) {
originX = x;
originZ = z;
}
public int getOriginX() {
return originX;
}
public int getOriginZ() {
return originZ;
}
@Override
public void add(int x, int y, int z, int combinedFrom, int combinedTo) {
blockSize++;
try {
FaweOutputStream stream = getBlockOS(x, y, z);
//x
posDel.write(stream, x - originX, y, z - originZ);
idDel.writeChange(stream, combinedFrom, combinedTo);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void addBiomeChange(int bx, int by, int bz, BiomeType from, BiomeType to) {
blockSize++;
try {
int x = bx >> 2;
int y = by >> 2;
int z = bz >> 2;
FaweOutputStream os = getBiomeOS();
os.write((byte) (x >> 24));
os.write((byte) (x >> 16));
os.write((byte) (x >> 8));
os.write((byte) (x));
os.write((byte) (z >> 24));
os.write((byte) (z >> 16));
os.write((byte) (z >> 8));
os.write((byte) (z));
// only need to store biomes in the 4x4x4 chunks so only need one byte for y still (signed byte -128 -> 127)
// means -512 -> 508. Add 128 to avoid negative value casting.
os.write((byte) (y + 128));
os.writeVarInt(from.getInternalId());
os.writeVarInt(to.getInternalId());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void addTileCreate(CompoundTag tag) {
if (tag == null) {
return;
}
blockSize++;
try {
NBTOutputStream nbtos = getTileCreateOS();
nbtos.writeTag(tag);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void addTileRemove(CompoundTag tag) {
if (tag == null) {
return;
}
blockSize++;
try {
NBTOutputStream nbtos = getTileRemoveOS();
nbtos.writeTag(tag);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void addEntityRemove(CompoundTag tag) {
if (tag == null) {
return;
}
blockSize++;
try {
NBTOutputStream nbtos = getEntityRemoveOS();
nbtos.writeTag(tag);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void addEntityCreate(CompoundTag tag) {
if (tag == null) {
return;
}
blockSize++;
try {
NBTOutputStream nbtos = getEntityCreateOS();
nbtos.writeTag(tag);
} catch (IOException e) {
e.printStackTrace();
}
}
public Iterator getBlockIterator(final boolean dir) throws IOException {
final FaweInputStream is = getBlockIS();
if (is == null) {
return Collections.emptyIterator();
}
final MutableBlockChange change = new MutableBlockChange(0, 0, 0, BlockTypes.AIR.getInternalId());
return new Iterator() {
private MutableBlockChange last = read();
public MutableBlockChange read() {
try {
change.x = posDel.readX(is) + originX;
change.y = posDel.readY(is);
change.z = posDel.readZ(is) + originZ;
idDel.readCombined(is, change, dir);
return change;
} catch (EOFException ignored) {
} catch (Exception e) {
e.printStackTrace();
e.printStackTrace();
}
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
public boolean hasNext() {
return last != null || ((last = read()) != null);
}
@Override
public MutableBlockChange next() {
MutableBlockChange tmp = last;
if (tmp == null) {
tmp = read();
}
last = null;
return tmp;
}
@Override
public void remove() {
throw new IllegalArgumentException("CANNOT REMOVE");
}
};
}
public Iterator getBiomeIterator(final boolean dir) throws IOException {
final FaweInputStream is = getBiomeIS();
if (is == null) {
return Collections.emptyIterator();
}
final MutableBiomeChange change = new MutableBiomeChange();
return new Iterator() {
private MutableBiomeChange last = new MutableBiomeChange();
public MutableBiomeChange read() {
try {
int int1 = is.read();
if (int1 != -1) {
int x = ((int1 << 24) + (is.read() << 16) + (is.read() << 8) + is.read()) << 2;
int z = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read()) << 2;
int y = (is.read() - 128) << 2;
int from = is.readVarInt();
int to = is.readVarInt();
change.setBiome(x, y, z, from, to);
return change;
}
} catch (EOFException ignored) {
} catch (Exception e) {
e.printStackTrace();
e.printStackTrace();
}
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
public boolean hasNext() {
return last != null || ((last = read()) != null);
}
@Override
public MutableBiomeChange next() {
MutableBiomeChange tmp = last;
if (tmp == null) {
tmp = read();
}
last = null;
return tmp;
}
@Override
public void remove() {
throw new IllegalArgumentException("CANNOT REMOVE");
}
};
}
@Override
public Iterator getIterator(BlockBag blockBag, int mode, boolean redo) {
if (blockBag != null && mode > 0) {
try {
return (Iterator) (Iterator) getFullBlockIterator(blockBag, mode, redo);
} catch (IOException e) {
e.printStackTrace();
}
}
return getIterator(redo);
}
public Iterator getFullBlockIterator(BlockBag blockBag, int inventory, final boolean dir) throws
IOException {
final FaweInputStream is = new FaweInputStream(getBlockIS());
final MutableFullBlockChange change = new MutableFullBlockChange(blockBag, inventory, dir);
return new Iterator() {
private MutableFullBlockChange last = read();
public MutableFullBlockChange read() {
try {
change.x = posDel.readX(is) + originX;
change.y = posDel.readY(is);
change.z = posDel.readZ(is) + originZ;
idDel.readCombined(is, change);
return change;
} catch (EOFException ignored) {
} catch (Exception e) {
e.printStackTrace();
e.printStackTrace();
}
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
public boolean hasNext() {
return last != null || ((last = read()) != null);
}
@Override
public MutableFullBlockChange next() {
MutableFullBlockChange tmp = last;
if (tmp == null) {
tmp = read();
}
last = null;
return tmp;
}
@Override
public void remove() {
throw new IllegalArgumentException("CANNOT REMOVE");
}
};
}
public Iterator getEntityIterator(final NBTInputStream is, final boolean create) {
if (is == null) {
return Collections.emptyIterator();
}
final MutableEntityChange change = new MutableEntityChange(null, create);
try {
return new Iterator() {
private MutableEntityChange last = read();
public MutableEntityChange read() {
try {
change.tag = (CompoundTag) is.readTag();
return change;
} catch (Exception ignored) {
}
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
public boolean hasNext() {
return last != null || ((last = read()) != null);
}
@Override
public MutableEntityChange next() {
MutableEntityChange tmp = last;
if (tmp == null) {
tmp = read();
}
last = null;
return tmp;
}
@Override
public void remove() {
throw new IllegalArgumentException("CANNOT REMOVE");
}
};
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public Iterator getTileIterator(final NBTInputStream is, final boolean create) {
if (is == null) {
return Collections.emptyIterator();
}
final MutableTileChange change = new MutableTileChange(null, create);
try {
return new Iterator() {
private MutableTileChange last = read();
public MutableTileChange read() {
try {
change.tag = (CompoundTag) is.readTag();
return change;
} catch (Exception ignored) {
}
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
public boolean hasNext() {
return last != null || ((last = read()) != null);
}
@Override
public MutableTileChange next() {
MutableTileChange tmp = last;
if (tmp == null) {
tmp = read();
}
last = null;
return tmp;
}
@Override
public void remove() {
throw new IllegalArgumentException("CANNOT REMOVE");
}
};
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public Iterator getIterator(final boolean dir) {
try {
close();
final Iterator tileCreate = getTileIterator(getTileCreateIS(), true);
final Iterator tileRemove = getTileIterator(getTileRemoveIS(), false);
final Iterator entityCreate = getEntityIterator(getEntityCreateIS(), true);
final Iterator entityRemove = getEntityIterator(getEntityRemoveIS(), false);
final Iterator blockChange = getBlockIterator(dir);
final Iterator biomeChange = getBiomeIterator(dir);
return new Iterator<>() {
final Iterator[] iterators = new Iterator[]{tileCreate, tileRemove, entityCreate, entityRemove, blockChange, biomeChange};
int i = 0;
Iterator current = iterators[0];
@Override
public boolean hasNext() {
if (current.hasNext()) {
return true;
} else if (i >= iterators.length - 1) {
return false;
} else {
current = iterators[++i];
}
return hasNext();
}
@Override
public void remove() {
current.remove();
}
@Override
public Change next() {
try {
return current.next();
} catch (Throwable ignored) {
if (i >= iterators.length - 1) {
throw new NoSuchElementException("End of iterator");
}
current = iterators[++i];
return next();
}
}
};
} catch (Exception e) {
e.printStackTrace();
}
return Collections.emptyIterator();
}
@Override
public Iterator backwardIterator() {
return getIterator(false);
}
@Override
public Iterator forwardIterator() {
return getIterator(true);
}
protected SimpleChangeSetSummary summarizeShallow() {
return new SimpleChangeSetSummary(getOriginX(), getOriginZ());
}
@Override
public SimpleChangeSetSummary summarize(Region region, boolean shallow) {
int ox = getOriginX();
int oz = getOriginZ();
SimpleChangeSetSummary summary = summarizeShallow();
if (region != null && !region.contains(ox, oz)) {
return summary;
}
try (FaweInputStream fis = getBlockIS()) {
if (!shallow) {
int amount = (Settings.settings().HISTORY.BUFFER_SIZE - HEADER_SIZE) / 9;
MutableFullBlockChange change = new MutableFullBlockChange(null, 0, false);
for (int i = 0; i < amount; i++) {
int x = posDel.readX(fis) + ox;
int y = posDel.readY(fis);
int z = posDel.readZ(fis) + ox;
idDel.readCombined(fis, change);
summary.add(x, z, change.to);
}
}
} catch (EOFException ignored) {
} catch (IOException e) {
e.printStackTrace();
}
return summary;
}
}