Feature/propagate diff and object cleanup (#1190)

* Feature/main/propagate diff annotations (#1187)

* 25% done

* More work

* More work

* 50%

* More work

* 75%

* 100% & cleanup

* Update adapters

* Squish squash, applesauce

commit 275ba9bd84
Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Date:   Sat Jul 17 01:10:20 2021 +0200

    Update dependency com.comphenix.protocol:ProtocolLib to v4.7.0 (#1173)

    Co-authored-by: Renovate Bot <bot@renovateapp.com>

commit 9fd8984804
Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Date:   Sat Jul 17 01:09:29 2021 +0200

    Update dependency org.checkerframework:checker-qual to v3.16.0 (#1184)

    Co-authored-by: Renovate Bot <bot@renovateapp.com>

commit 861fb45e5c
Author: dordsor21 <dordsor21@gmail.com>
Date:   Fri Jul 16 19:07:02 2021 +0100

    Fix #1075

commit 420c45a29a
Author: dordsor21 <dordsor21@gmail.com>
Date:   Fri Jul 16 18:48:21 2021 +0100

    Entity removal should be on the main thread as we're just passing through rather than doing chunk operations
     - Fixes #1164
     - Not working: butcher/remove history

commit 4d4db7dcd0
Author: SirYwell <hannesgreule@outlook.de>
Date:   Fri Jul 16 17:52:44 2021 +0200

    Make sure leaves category is loaded for heightmaps (fixes #1176)

commit c98f6e4f37
Author: dordsor21 <dordsor21@gmail.com>
Date:   Fri Jul 16 10:44:52 2021 +0100

    Do not allow generation commands to generate outside selection

commit 2485f5eccc
Author: dordsor21 <dordsor21@gmail.com>
Date:   Fri Jul 16 10:43:15 2021 +0100

    EditSession needs to override some Extent methods to ensure block changes are correctly set through the various extents
    Fixes #1152

commit d9418ec8ae
Author: dordsor21 <dordsor21@gmail.com>
Date:   Fri Jul 16 09:52:44 2021 +0100

    Undo part of 41073bb1a0
    Fixes #1178

* Update Upstream

fb1fb84 Fixed typo and grammar

* We don't support custom heights yet

* Casing inconsistency

* Address a few comments

* Address comments

* Don't refactor to AP classpath

* Document annotation style

* Refactoring & shade cleanup

* Address a few comments

* More work

* Resolve comments not being resolved yet

* Feature/main/propagate diff annotations (#1187) (#1194)

* Remove beta package, fix history packages, move classes out of object package

* Resolve comments not being resolved yet

* Remove beta package, fix history packages, move classes out of object package

Co-authored-by: NotMyFault <mc.cache@web.de>

* brushes should be under brush

* More refactoring
 - Filters/processors should be in the same place and are related to extents
 - Transforms are in `extent.transform` in upstream

* Move history classes under history

* Update adapters

Co-authored-by: dordsor21 <dordsor21@gmail.com>
This commit is contained in:
NotMyFault
2021-07-23 17:48:51 +02:00
committed by GitHub
parent ad102ab7a9
commit 50ab8ad5c7
799 changed files with 4916 additions and 10589 deletions

View File

@ -0,0 +1,440 @@
package com.fastasyncworldedit.core.history;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.database.DBHandler;
import com.fastasyncworldedit.core.database.RollbackDatabase;
import com.fastasyncworldedit.core.history.changeset.FaweStreamChangeSet;
import com.fastasyncworldedit.core.history.changeset.SimpleChangeSetSummary;
import com.fastasyncworldedit.core.internal.io.FaweInputStream;
import com.fastasyncworldedit.core.internal.io.FaweOutputStream;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.util.MainUtil;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.World;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* Store the change on disk
* - High disk usage
* - Moderate CPU usage
* - Minimal memory usage
* - Slow
*/
public class DiskStorageHistory extends FaweStreamChangeSet {
private static final Map<String, Map<UUID, Integer>> NEXT_INDEX = new ConcurrentHashMap<>();
private UUID uuid;
private File bdFile;
private File bioFile;
private File nbtfFile;
private File nbttFile;
private File entfFile;
private File enttFile;
/*
* Block data
*
* [header]
* {int origin x, int origin z}
*
* [contents]...
* { short rel x, short rel z, unsigned byte y, short combinedFrom, short combinedTo }
*/
private FaweOutputStream osBD;
// biome
private FaweOutputStream osBIO;
// NBT From
private NBTOutputStream osNBTF;
// NBT To
private NBTOutputStream osNBTT;
// Entity Create From
private NBTOutputStream osENTCF;
// Entity Create To
private NBTOutputStream osENTCT;
private int index;
public DiskStorageHistory(World world, UUID uuid) {
super(world);
init(uuid, world.getName());
}
private void init(UUID uuid, String worldName) {
final File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY + File.separator + worldName + File.separator + uuid);
final int max = NEXT_INDEX.computeIfAbsent(worldName, _worldName -> new ConcurrentHashMap<>())
.compute(uuid, (_uuid, id) -> (id == null ? MainUtil.getMaxFileId(folder) : id) + 1) - 1;
init(uuid, max);
}
public DiskStorageHistory(World world, UUID uuid, int index) {
super(world);
init(uuid, index);
}
public DiskStorageHistory(File folder, World world, UUID uuid, int i) {
super(world);
this.uuid = uuid;
this.index = i;
initFiles(folder);
}
private void initFiles(File folder) {
nbtfFile = new File(folder, index + ".nbtf");
nbttFile = new File(folder, index + ".nbtt");
entfFile = new File(folder, index + ".entf");
enttFile = new File(folder, index + ".entt");
bdFile = new File(folder, index + ".bd");
bioFile = new File(folder, index + ".bio");
}
private void init(UUID uuid, int i) {
this.uuid = uuid;
this.index = i;
File folder = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY + File.separator + getWorld().getName() + File.separator + uuid);
initFiles(folder);
}
@Override
public void delete() {
// Fawe.debug("Deleting history: " + getWorld().getName() + "/" + uuid + "/" + index);
deleteFiles();
if (Settings.IMP.HISTORY.USE_DATABASE) {
RollbackDatabase db = DBHandler.IMP.getDatabase(getWorld());
db.delete(uuid, index);
}
}
public void deleteFiles() {
bdFile.delete();
nbtfFile.delete();
nbttFile.delete();
entfFile.delete();
enttFile.delete();
}
public void undo(Player player, Region[] regions) {
EditSession session = toEditSession(player, regions);
session.undo(session);
deleteFiles();
}
public void undo(Player player) {
undo(player, null);
}
public void redo(Player player, Region[] regions) {
EditSession session = toEditSession(player, regions);
session.redo(session);
}
public void redo(Player player) {
undo(player, null);
}
public UUID getUUID() {
return uuid;
}
public File getBDFile() {
return bdFile;
}
public File getNbtfFile() {
return nbtfFile;
}
public File getNbttFile() {
return nbttFile;
}
public File getEntfFile() {
return entfFile;
}
public File getEnttFile() {
return enttFile;
}
public File getBioFile() {
return bioFile;
}
public int getIndex() {
return index;
}
@Override
public void flush() {
super.flush();
synchronized (this) {
try {
if (osBD != null) {
osBD.flush();
}
if (osBIO != null) {
osBIO.flush();
}
if (osNBTF != null) {
osNBTF.flush();
}
if (osNBTT != null) {
osNBTT.flush();
}
if (osENTCF != null) {
osENTCF.flush();
}
if (osENTCT != null) {
osENTCT.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void close() throws IOException {
super.close();
synchronized (this) {
try {
if (osBD != null) {
osBD.close();
osBD = null;
}
if (osBIO != null) {
osBIO.close();
osBIO = null;
}
if (osNBTF != null) {
osNBTF.close();
osNBTF = null;
}
if (osNBTT != null) {
osNBTT.close();
osNBTT = null;
}
if (osENTCF != null) {
osENTCF.close();
osENTCF = null;
}
if (osENTCT != null) {
osENTCT.close();
osENTCT = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public int getCompressedSize() {
return bdFile.exists() ? (int) bdFile.length() : 0;
}
@Override
public long getSizeInMemory() {
return 80;
}
@Override
public long getSizeOnDisk() {
int total = 0;
if (bdFile.exists()) {
total += bdFile.length();
}
if (bioFile.exists()) {
total += bioFile.length();
}
if (nbtfFile.exists()) {
total += entfFile.length();
}
if (nbttFile.exists()) {
total += entfFile.length();
}
if (entfFile.exists()) {
total += entfFile.length();
}
if (enttFile.exists()) {
total += entfFile.length();
}
return total;
}
@Override
public FaweOutputStream getBlockOS(int x, int y, int z) throws IOException {
if (osBD != null) {
return osBD;
}
synchronized (this) {
bdFile.getParentFile().mkdirs();
bdFile.createNewFile();
osBD = getCompressedOS(new FileOutputStream(bdFile));
writeHeader(osBD, x, y, z);
return osBD;
}
}
@Override
public FaweOutputStream getBiomeOS() throws IOException {
if (osBIO != null) {
return osBIO;
}
synchronized (this) {
bioFile.getParentFile().mkdirs();
bioFile.createNewFile();
osBIO = getCompressedOS(new FileOutputStream(bioFile));
return osBIO;
}
}
@Override
public NBTOutputStream getEntityCreateOS() throws IOException {
if (osENTCT != null) {
return osENTCT;
}
enttFile.getParentFile().mkdirs();
enttFile.createNewFile();
osENTCT = new NBTOutputStream(getCompressedOS(new FileOutputStream(enttFile)));
return osENTCT;
}
@Override
public NBTOutputStream getEntityRemoveOS() throws IOException {
if (osENTCF != null) {
return osENTCF;
}
entfFile.getParentFile().mkdirs();
entfFile.createNewFile();
osENTCF = new NBTOutputStream(getCompressedOS(new FileOutputStream(entfFile)));
return osENTCF;
}
@Override
public NBTOutputStream getTileCreateOS() throws IOException {
if (osNBTT != null) {
return osNBTT;
}
nbttFile.getParentFile().mkdirs();
nbttFile.createNewFile();
osNBTT = new NBTOutputStream(getCompressedOS(new FileOutputStream(nbttFile)));
return osNBTT;
}
@Override
public NBTOutputStream getTileRemoveOS() throws IOException {
if (osNBTF != null) {
return osNBTF;
}
nbtfFile.getParentFile().mkdirs();
nbtfFile.createNewFile();
osNBTF = new NBTOutputStream(getCompressedOS(new FileOutputStream(nbtfFile)));
return osNBTF;
}
@Override
public FaweInputStream getBlockIS() throws IOException {
if (!bdFile.exists()) {
return null;
}
FaweInputStream is = MainUtil.getCompressedIS(new FileInputStream(bdFile));
readHeader(is);
return is;
}
@Override
public FaweInputStream getBiomeIS() throws IOException {
if (!bioFile.exists()) {
return null;
}
return MainUtil.getCompressedIS(new FileInputStream(bioFile));
}
@Override
public NBTInputStream getEntityCreateIS() throws IOException {
if (!enttFile.exists()) {
return null;
}
return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(enttFile)));
}
@Override
public NBTInputStream getEntityRemoveIS() throws IOException {
if (!entfFile.exists()) {
return null;
}
return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(entfFile)));
}
@Override
public NBTInputStream getTileCreateIS() throws IOException {
if (!nbttFile.exists()) {
return null;
}
return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbttFile)));
}
@Override
public NBTInputStream getTileRemoveIS() throws IOException {
if (!nbtfFile.exists()) {
return null;
}
return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbtfFile)));
}
@Override
public SimpleChangeSetSummary summarize(Region region, boolean shallow) {
if (bdFile.exists()) {
return super.summarize(region, shallow);
}
return null;
}
public IntPair readHeader() {
int ox = getOriginX();
int oz = getOriginZ();
if (ox == 0 && oz == 0 && bdFile.exists()) {
try (FileInputStream fis = new FileInputStream(bdFile)) {
final FaweInputStream gis = MainUtil.getCompressedIS(fis);
// skip mode
gis.skipFully(1);
// origin
ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + gis.read());
oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + gis.read());
setOrigin(ox, oz);
fis.close();
gis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return new IntPair(ox, oz);
}
@Override
public boolean isRecordingChanges() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setRecordChanges(boolean recordChanges) {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,255 @@
package com.fastasyncworldedit.core.history;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.history.changeset.FaweStreamChangeSet;
import com.fastasyncworldedit.core.internal.io.FaweInputStream;
import com.fastasyncworldedit.core.internal.io.FaweOutputStream;
import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream;
import com.fastasyncworldedit.core.internal.io.FastByteArraysInputStream;
import com.fastasyncworldedit.core.util.MainUtil;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.world.World;
import java.io.IOException;
/**
* ChangeSet optimized for low memory usage
* - No disk usage
* - High CPU usage
* - Low memory usage
*/
public class MemoryOptimizedHistory extends FaweStreamChangeSet {
private byte[][] ids;
private FastByteArrayOutputStream idsStream;
private FaweOutputStream idsStreamZip;
private byte[][] biomes;
private FastByteArrayOutputStream biomeStream;
private FaweOutputStream biomeStreamZip;
private byte[][] entC;
private FastByteArrayOutputStream entCStream;
private NBTOutputStream entCStreamZip;
private byte[][] entR;
private FastByteArrayOutputStream entRStream;
private NBTOutputStream entRStreamZip;
private byte[][] tileC;
private FastByteArrayOutputStream tileCStream;
private NBTOutputStream tileCStreamZip;
private byte[][] tileR;
private FastByteArrayOutputStream tileRStream;
private NBTOutputStream tileRStreamZip;
public MemoryOptimizedHistory(World world) {
super(world);
}
@Override
public void flush() {
super.flush();
synchronized (this) {
try {
if (idsStream != null) {
idsStreamZip.flush();
}
if (biomeStream != null) {
biomeStreamZip.flush();
}
if (entCStream != null) {
entCStreamZip.flush();
}
if (entRStream != null) {
entRStreamZip.flush();
}
if (tileCStream != null) {
tileCStreamZip.flush();
}
if (tileRStream != null) {
tileRStreamZip.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void close() throws IOException {
super.close();
synchronized (this) {
try {
if (idsStream != null) {
idsStreamZip.close();
ids = idsStream.toByteArrays();
idsStream = null;
idsStreamZip = null;
}
if (biomeStream != null) {
biomeStreamZip.close();
biomes = biomeStream.toByteArrays();
biomeStream = null;
biomeStreamZip = null;
}
if (entCStream != null) {
entCStreamZip.close();
entC = entCStream.toByteArrays();
entCStream = null;
entCStreamZip = null;
}
if (entRStream != null) {
entRStreamZip.close();
entR = entRStream.toByteArrays();
entRStream = null;
entRStreamZip = null;
}
if (tileCStream != null) {
tileCStreamZip.close();
tileC = tileCStream.toByteArrays();
tileCStream = null;
tileCStreamZip = null;
}
if (tileRStream != null) {
tileRStreamZip.close();
tileR = tileRStream.toByteArrays();
tileRStream = null;
tileRStreamZip = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public int getCompressedSize() {
if (ids == null) {
return 0;
}
int count = 0;
for (byte[] array : ids) {
count += 4 + array.length;
}
return count;
}
@Override
public long getSizeInMemory() {
return 92 + getCompressedSize();
}
@Override
public FaweOutputStream getBlockOS(int x, int y, int z) throws IOException {
if (idsStreamZip != null) {
return idsStreamZip;
}
synchronized (this) {
setOrigin(x, z);
idsStream = new FastByteArrayOutputStream(Settings.IMP.HISTORY.BUFFER_SIZE);
idsStreamZip = getCompressedOS(idsStream);
writeHeader(idsStreamZip, x, y, z);
return idsStreamZip;
}
}
@Override
public FaweInputStream getBiomeIS() throws IOException {
if (biomes == null) {
return null;
}
return MainUtil.getCompressedIS(new FastByteArraysInputStream(biomes));
}
@Override
public FaweOutputStream getBiomeOS() throws IOException {
if (biomeStreamZip != null) {
return biomeStreamZip;
}
synchronized (this) {
biomeStream = new FastByteArrayOutputStream(Settings.IMP.HISTORY.BUFFER_SIZE);
biomeStreamZip = getCompressedOS(biomeStream);
return biomeStreamZip;
}
}
@Override
public FaweInputStream getBlockIS() throws IOException {
if (ids == null) {
return null;
}
FaweInputStream result = MainUtil.getCompressedIS(new FastByteArraysInputStream(ids));
readHeader(result);
return result;
}
@Override
public NBTOutputStream getEntityCreateOS() throws IOException {
if (entCStreamZip != null) {
return entCStreamZip;
}
entCStream = new FastByteArrayOutputStream(Settings.IMP.HISTORY.BUFFER_SIZE);
return entCStreamZip = new NBTOutputStream(getCompressedOS(entCStream));
}
@Override
public NBTOutputStream getEntityRemoveOS() throws IOException {
if (entRStreamZip != null) {
return entRStreamZip;
}
entRStream = new FastByteArrayOutputStream(Settings.IMP.HISTORY.BUFFER_SIZE);
return entRStreamZip = new NBTOutputStream(getCompressedOS(entRStream));
}
@Override
public NBTOutputStream getTileCreateOS() throws IOException {
if (tileCStreamZip != null) {
return tileCStreamZip;
}
tileCStream = new FastByteArrayOutputStream(Settings.IMP.HISTORY.BUFFER_SIZE);
return tileCStreamZip = new NBTOutputStream(getCompressedOS(tileCStream));
}
@Override
public NBTOutputStream getTileRemoveOS() throws IOException {
if (tileRStreamZip != null) {
return tileRStreamZip;
}
tileRStream = new FastByteArrayOutputStream(Settings.IMP.HISTORY.BUFFER_SIZE);
return tileRStreamZip = new NBTOutputStream(getCompressedOS(tileRStream));
}
@Override
public NBTInputStream getEntityCreateIS() throws IOException {
return entC == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new FastByteArraysInputStream(entC)));
}
@Override
public NBTInputStream getEntityRemoveIS() throws IOException {
return entR == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new FastByteArraysInputStream(entR)));
}
@Override
public NBTInputStream getTileCreateIS() throws IOException {
return tileC == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new FastByteArraysInputStream(tileC)));
}
@Override
public NBTInputStream getTileRemoveIS() throws IOException {
return tileR == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new FastByteArraysInputStream(tileR)));
}
@Override
public boolean isRecordingChanges() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setRecordChanges(boolean recordChanges) {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,138 @@
package com.fastasyncworldedit.core.history;
import com.fastasyncworldedit.core.database.DBHandler;
import com.fastasyncworldedit.core.database.RollbackDatabase;
import com.fastasyncworldedit.core.history.changeset.SimpleChangeSetSummary;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.world.World;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;
public class RollbackOptimizedHistory extends DiskStorageHistory {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private long time;
private int minX;
private int maxX;
private int minY;
private int maxY;
private int minZ;
private int maxZ;
private String command;
public RollbackOptimizedHistory(World world, UUID uuid, int index) {
super(world, uuid, index);
this.time = System.currentTimeMillis();
}
public RollbackOptimizedHistory(World world, UUID uuid) {
super(world, uuid);
this.time = System.currentTimeMillis();
}
public RollbackOptimizedHistory(World world, UUID uuid, int index, long time, long size, CuboidRegion region, String command) {
super(world, uuid, index);
this.time = time;
this.minX = region.getMinimumX();
this.minY = region.getMinimumY();
this.minZ = region.getMinimumZ();
this.maxX = region.getMaximumX();
this.maxY = region.getMaximumY();
this.maxZ = region.getMaximumZ();
this.blockSize = (int) size;
this.command = command;
this.closed = true;
LOGGER.debug("Size: {}", size);
}
public long getTime() {
return time;
}
@Override
protected SimpleChangeSetSummary summarizeShallow() {
SimpleChangeSetSummary summary = super.summarizeShallow();
summary.minX = this.minX;
summary.minZ = this.minZ;
summary.maxX = this.maxX;
summary.maxZ = this.maxZ;
return summary;
}
public void setCommand(String command) {
this.command = command;
}
public String getCommand() {
return command;
}
public void setDimensions(BlockVector3 pos1, BlockVector3 pos2) {
this.minX = pos1.getBlockX();
this.minY = pos1.getBlockY();
this.minZ = pos1.getBlockZ();
this.maxX = pos2.getBlockX();
this.maxY = pos2.getBlockY();
this.maxZ = pos2.getBlockZ();
}
public void setTime(long time) {
this.time = time;
}
@Override
public void close() throws IOException {
super.close();
// Save to DB
RollbackDatabase db = DBHandler.IMP.getDatabase(getWorld());
if (db != null) {
db.logEdit(this);
}
}
@Override
public void add(int x, int y, int z, int combinedFrom, int combinedTo) {
super.add(x, y, z, combinedFrom, combinedTo);
if (x < minX) {
minX = x;
} else if (x > maxX) {
maxX = x;
}
if (y < minY) {
minY = y;
} else if (y > maxY) {
maxY = y;
}
if (z < minZ) {
minZ = z;
} else if (z > maxZ) {
maxZ = z;
}
}
@Override
public void writeHeader(OutputStream os, int x, int y, int z) throws IOException {
minX = x;
maxX = x;
minY = y;
maxY = y;
minZ = z;
maxZ = z;
super.writeHeader(os, x, y, z);
}
public BlockVector3 getMinimumPoint() {
return BlockVector3.at(minX, minY, minZ);
}
public BlockVector3 getMaximumPoint() {
return BlockVector3.at(maxX, maxY, maxZ);
}
}

View File

@ -0,0 +1,35 @@
package com.fastasyncworldedit.core.history.change;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.history.UndoContext;
import com.sk89q.worldedit.history.change.Change;
import com.fastasyncworldedit.core.math.MutableBlockVector3;
import com.sk89q.worldedit.world.biome.BiomeTypes;
public class MutableBiomeChange implements Change {
private final MutableBlockVector3 mutable = new MutableBlockVector3();
private int from;
private int to;
public MutableBiomeChange() {
this.from = 0;
this.to = 0;
}
public void setBiome(int x, int y, int z, int from, int to) {
mutable.setComponents(x, y, z);
this.from = from;
this.to = to;
}
@Override
public void undo(UndoContext context) throws WorldEditException {
context.getExtent().setBiome(mutable, BiomeTypes.get(from));
}
@Override
public void redo(UndoContext context) throws WorldEditException {
context.getExtent().setBiome(mutable, BiomeTypes.get(to));
}
}

View File

@ -0,0 +1,36 @@
package com.fastasyncworldedit.core.history.change;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.history.UndoContext;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.world.block.BlockState;
public class MutableBlockChange implements Change {
public int z;
public int y;
public int x;
public int ordinal;
public MutableBlockChange(int x, int y, int z, int ordinal) {
this.x = x;
this.y = y;
this.z = z;
this.ordinal = ordinal;
}
@Override
public void undo(UndoContext context) throws WorldEditException {
create(context);
}
@Override
public void redo(UndoContext context) throws WorldEditException {
create(context);
}
public void create(UndoContext context) {
context.getExtent().setBlock(x, y, z, BlockState.getFromOrdinal(ordinal));
}
}

View File

@ -0,0 +1,94 @@
package com.fastasyncworldedit.core.history.change;
import com.fastasyncworldedit.core.util.MathMan;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.DoubleTag;
import com.sk89q.jnbt.LongTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.history.UndoContext;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.entity.EntityTypes;
import org.apache.logging.log4j.Logger;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class MutableEntityChange implements Change {
private static final Logger LOGGER = LogManagerCompat.getLogger();
public CompoundTag tag;
public boolean create;
public MutableEntityChange(CompoundTag tag, boolean create) {
this.tag = tag;
this.create = create;
}
@Override
public void undo(UndoContext context) throws WorldEditException {
if (!create) {
create(context);
} else {
delete(context);
}
}
@Override
public void redo(UndoContext context) throws WorldEditException {
if (create) {
create(context);
} else {
delete(context);
}
}
public void delete(UndoContext context) {
Map<String, Tag> map = tag.getValue();
long most;
long least;
if (map.containsKey("UUIDMost")) {
most = ((LongTag) map.get("UUIDMost")).getValue();
least = ((LongTag) map.get("UUIDLeast")).getValue();
} else if (map.containsKey("PersistentIDMSB")) {
most = ((LongTag) map.get("PersistentIDMSB")).getValue();
least = ((LongTag) map.get("PersistentIDLSB")).getValue();
} else {
LOGGER.debug("Skipping entity without uuid.");
return;
}
List<DoubleTag> pos = (List<DoubleTag>) map.get("Pos").getValue();
int x = MathMan.roundInt(pos.get(0).getValue());
int y = MathMan.roundInt(pos.get(1).getValue());
int z = MathMan.roundInt(pos.get(2).getValue());
UUID uuid = new UUID(most, least);
context.getExtent().removeEntity(x, y, z, uuid);
}
public void create(UndoContext context) {
Map<String, Tag> map = tag.getValue();
Tag posTag = map.get("Pos");
if (posTag == null) {
LOGGER.debug("Missing pos tag: " + tag);
return;
}
List<DoubleTag> pos = (List<DoubleTag>) posTag.getValue();
double x = pos.get(0).getValue();
double y = pos.get(1).getValue();
double z = pos.get(2).getValue();
Extent extent = context.getExtent();
Location location = new Location(extent, x, y, z, 0, 0);
String id = tag.getString("Id");
EntityType type = EntityTypes.parse(id);
BaseEntity entity = new BaseEntity(type, tag);
context.getExtent().createEntity(location, entity);
}
}

View File

@ -0,0 +1,59 @@
package com.fastasyncworldedit.core.history.change;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.extent.inventory.BlockBagException;
import com.sk89q.worldedit.history.UndoContext;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.world.block.BlockState;
public class MutableFullBlockChange implements Change {
public int z;
public int y;
public int x;
public int from;
public int to;
public BlockBag blockBag;
public boolean allowFetch;
public boolean allowStore;
public MutableFullBlockChange(BlockBag blockBag, int mode, boolean redo) {
this.blockBag = blockBag;
allowFetch = redo || mode == 1;
allowStore = !redo || mode == 1;
}
@Override
public void undo(UndoContext context) throws WorldEditException {
create(context);
}
@Override
public void redo(UndoContext context) throws WorldEditException {
create(context);
}
public void create(UndoContext context) {
BlockState fromState = BlockState.getFromOrdinal(from);
if (blockBag != null) {
BlockState toState = BlockState.getFromOrdinal(to);
if (fromState != toState) {
if (allowFetch && from != 0) {
try {
blockBag.fetchPlacedBlock(fromState);
} catch (BlockBagException e) {
return;
}
}
if (allowStore && to != 0) {
try {
blockBag.storeDroppedBlock(toState);
} catch (BlockBagException ignored) {
}
}
}
}
context.getExtent().setBlock(x, y, z, fromState);
}
}

View File

@ -0,0 +1,38 @@
package com.fastasyncworldedit.core.history.change;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.history.UndoContext;
import com.sk89q.worldedit.history.change.Change;
public class MutableTileChange implements Change {
public CompoundTag tag;
public boolean create;
public MutableTileChange(CompoundTag tag, boolean create) {
this.tag = tag;
this.create = create;
}
@Override
public void undo(UndoContext context) throws WorldEditException {
if (!create) {
create(context);
}
}
@Override
public void redo(UndoContext context) throws WorldEditException {
if (create) {
create(context);
}
}
public void create(UndoContext context) {
int x = tag.getInt("x");
int y = tag.getInt("y");
int z = tag.getInt("z");
context.getExtent().setTile(x, y, z, tag);
}
}

View File

@ -0,0 +1,52 @@
package com.fastasyncworldedit.core.history.change;
import com.fastasyncworldedit.core.internal.io.FaweInputStream;
import com.fastasyncworldedit.core.internal.io.FaweOutputStream;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public interface StreamChange {
void flushChanges(FaweOutputStream out) throws IOException;
void undoChanges(FaweInputStream in) throws IOException;
void redoChanges(FaweInputStream in) throws IOException;
default void flushChanges(File file) throws IOException {
try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
try (LZ4BlockOutputStream compressed = new LZ4BlockOutputStream(out)) {
// compressed.setLevel(Deflater.BEST_SPEED);
try (FaweOutputStream fos = new FaweOutputStream(compressed)) {
flushChanges(fos);
}
}
}
}
default void undoChanges(File file) throws IOException {
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file))) {
try (LZ4BlockInputStream compressed = new LZ4BlockInputStream(in)) {
try (FaweInputStream fis = new FaweInputStream(compressed)) {
undoChanges(fis);
}
}
}
}
default void redoChanges(File file) throws IOException {
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file))) {
try (LZ4BlockInputStream compressed = new LZ4BlockInputStream(in)) {
try (FaweInputStream fis = new FaweInputStream(compressed)) {
redoChanges(fis);
}
}
}
}
}

View File

@ -0,0 +1,367 @@
package com.fastasyncworldedit.core.history.changeset;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
import com.fastasyncworldedit.core.extent.HistoryExtent;
import com.fastasyncworldedit.core.util.EditSessionBuilder;
import com.fastasyncworldedit.core.util.MainUtil;
import com.fastasyncworldedit.core.util.TaskManager;
import com.google.common.util.concurrent.Futures;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.history.change.BlockChange;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.history.change.EntityCreate;
import com.sk89q.worldedit.history.change.EntityRemove;
import com.sk89q.worldedit.history.changeset.ChangeSet;
import com.sk89q.worldedit.math.BlockVector3;
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.BaseBlock;
import com.fastasyncworldedit.core.world.block.BlockID;
import com.sk89q.worldedit.world.block.BlockState;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import static org.apache.logging.log4j.LogManager.getLogger;
public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
private final World world;
protected AtomicInteger waitingCombined = new AtomicInteger(0);
protected AtomicInteger waitingAsync = new AtomicInteger(0);
protected boolean closed;
public AbstractChangeSet(World world) {
this.world = world;
}
public World getWorld() {
return world;
}
public void closeAsync() {
if (closed) {
return;
}
waitingAsync.incrementAndGet();
TaskManager.IMP.async(() -> {
waitingAsync.decrementAndGet();
synchronized (waitingAsync) {
waitingAsync.notifyAll();
}
try {
close();
} catch (IOException e) {
e.printStackTrace();
}
});
}
@Override
public void flush() {
try {
if (!Fawe.isMainThread()) {
while (waitingAsync.get() > 0) {
synchronized (waitingAsync) {
waitingAsync.wait(1000);
}
}
}
while (waitingCombined.get() > 0) {
synchronized (waitingCombined) {
waitingCombined.wait(1000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void close() throws IOException {
if (!closed) {
flush();
closed = true;
}
}
public abstract void add(int x, int y, int z, int combinedFrom, int combinedTo);
@Override
public Iterator<Change> backwardIterator() {
return getIterator(false);
}
@Override
public Iterator<Change> forwardIterator() {
return getIterator(true);
}
@Override
public Extent construct(Extent child) {
return new HistoryExtent(child, this);
}
@Override
public synchronized IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
int bx = chunk.getX() << 4;
int bz = chunk.getZ() << 4;
Map<BlockVector3, CompoundTag> tilesFrom = get.getTiles();
Map<BlockVector3, CompoundTag> tilesTo = set.getTiles();
if (!tilesFrom.isEmpty()) {
for (Map.Entry<BlockVector3, CompoundTag> entry : tilesFrom.entrySet()) {
BlockVector3 pos = entry.getKey();
BlockState fromBlock = get.getBlock(pos.getX() & 15, pos.getY(), pos.getZ() & 15);
BlockState toBlock = set.getBlock(pos.getX() & 15, pos.getY(), pos.getZ() & 15);
if (fromBlock != toBlock || tilesTo.containsKey(pos)) {
addTileRemove(entry.getValue());
}
}
}
if (!tilesTo.isEmpty()) {
for (Map.Entry<BlockVector3, CompoundTag> entry : tilesTo.entrySet()) {
BlockVector3 pos = entry.getKey();
addTileCreate(MainUtil.setPosition(entry.getValue(), pos.getX() + bx, pos.getY(), pos.getZ() + bz));
}
}
Set<UUID> entRemoves = set.getEntityRemoves();
if (!entRemoves.isEmpty()) {
for (UUID uuid : entRemoves) {
CompoundTag found = get.getEntity(uuid);
if (found != null) {
addEntityRemove(found);
}
}
}
Set<CompoundTag> ents = set.getEntities();
if (!ents.isEmpty()) {
for (CompoundTag tag : ents) {
addEntityCreate(tag);
}
}
for (int layer = 0; layer < 16; layer++) {
if (!set.hasSection(layer)) {
continue;
}
// add each block and tile
char[] blocksGet;
char[] tmp = get.load(layer);
if (tmp == null) {
blocksGet = FaweCache.IMP.EMPTY_CHAR_4096;
} else {
System.arraycopy(tmp, 0, (blocksGet = new char[4096]), 0, 4096);
}
char[] blocksSet;
System.arraycopy(set.load(layer), 0, (blocksSet = new char[4096]), 0, 4096);
int by = layer << 4;
for (int y = 0, index = 0; y < 16; y++) {
int yy = y + by;
for (int z = 0; z < 16; z++) {
int zz = z + bz;
for (int x = 0; x < 16; x++, index++) {
int xx = bx + x;
int from = blocksGet[index];
if (from == 0) {
from = BlockID.AIR;
}
final int combinedFrom = from;
final int combinedTo = blocksSet[index];
if (combinedTo != 0) {
add(xx, yy, zz, combinedFrom, combinedTo);
}
}
}
}
}
BiomeType[] biomes = set.getBiomes();
if (biomes != null) {
for (int y = 0, index = 0; y < 64; y++) {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < 4; x++, index++) {
BiomeType newBiome = biomes[index];
if (newBiome != null) {
BiomeType oldBiome = get.getBiomeType(x, y, z);
if (oldBiome != newBiome) {
addBiomeChange(bx + (x << 2), y << 2,bz + (z << 2), oldBiome, newBiome);
}
}
}
}
}
}
return set;
}
@Override
public Future<IChunkSet> postProcessSet(final IChunk chunk, final IChunkGet get,final IChunkSet set) {
return (Future<IChunkSet>) addWriteTask(() -> processSet(chunk, get, set));
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.READING_SET_BLOCKS;
}
public abstract void addTileCreate(CompoundTag tag);
public abstract void addTileRemove(CompoundTag tag);
public abstract void addEntityRemove(CompoundTag tag);
public abstract void addEntityCreate(CompoundTag tag);
public abstract void addBiomeChange(int x, int y, int z, BiomeType from, BiomeType to);
public Iterator<Change> getIterator(BlockBag blockBag, int mode, boolean redo) {
return getIterator(redo);
}
public abstract Iterator<Change> getIterator(boolean redo);
public EditSession toEditSession(Player player) {
return toEditSession(player, null);
}
public EditSession toEditSession(Player player, Region[] regions) {
EditSessionBuilder builder =
new EditSessionBuilder(getWorld()).player(player).autoQueue(false).fastmode(false)
.checkMemory(false).changeSet(this).limitUnlimited();
if (regions != null) {
builder.allowedRegions(regions);
} else {
builder.allowedRegionsEverywhere();
}
EditSession editSession = builder.build();
editSession.setSize(1);
return editSession;
}
public void add(EntityCreate change) {
CompoundTag tag = change.state.getNbtData();
addEntityCreate(MainUtil.setEntityInfo(tag, change.getEntity()));
}
public void add(EntityRemove change) {
CompoundTag tag = change.state.getNbtData();
addEntityRemove(MainUtil.setEntityInfo(tag, change.getEntity()));
}
@Override
public void add(Change change) {
if (change.getClass() == BlockChange.class) {
add((BlockChange) change);
} else if (change.getClass() == EntityCreate.class) {
add((EntityCreate) change);
} else if (change.getClass() == EntityRemove.class) {
add((EntityRemove) change);
} else {
getLogger(AbstractChangeSet.class).debug("Unknown change: " + change.getClass());
}
}
public void add(BlockChange change) {
try {
BlockVector3 loc = change.getPosition();
BaseBlock from = change.getPrevious();
BaseBlock to = change.getCurrent();
add(loc, from, to);
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean isEmpty() {
return waitingCombined.get() == 0 && waitingAsync.get() == 0 && size() == 0;
}
public void add(BlockVector3 loc, BaseBlock from, BaseBlock to) {
int x = loc.getBlockX();
int y = loc.getBlockY();
int z = loc.getBlockZ();
add(x, y, z, from, to);
}
public void add(int x, int y, int z, BaseBlock from, BaseBlock to) {
try {
if (from.hasNbtData()) {
CompoundTag nbt = from.getNbtData();
assert nbt != null;
addTileRemove(MainUtil.setPosition(nbt, x, y, z));
}
if (to.hasNbtData()) {
CompoundTag nbt = to.getNbtData();
assert nbt != null;
addTileCreate(MainUtil.setPosition(nbt, x, y, z));
}
int combinedFrom = from.getOrdinal();
int combinedTo = to.getOrdinal();
add(x, y, z, combinedFrom, combinedTo);
} catch (Exception e) {
e.printStackTrace();
}
}
public void add(int x, int y, int z, int combinedFrom, BaseBlock to) {
try {
if (to.hasNbtData()) {
CompoundTag nbt = to.getNbtData();
assert nbt != null;
addTileCreate(MainUtil.setPosition(nbt, x, y, z));
}
int combinedTo = to.getInternalId();
add(x, y, z, combinedFrom, combinedTo);
} catch (Exception e) {
e.printStackTrace();
}
}
public Future<?> addWriteTask(Runnable writeTask) {
return addWriteTask(writeTask, Fawe.isMainThread());
}
public Future<?> addWriteTask(Runnable writeTask, boolean completeNow) {
AbstractChangeSet.this.waitingCombined.incrementAndGet();
Runnable wrappedTask = () -> {
try {
writeTask.run();
} finally {
if (AbstractChangeSet.this.waitingCombined.decrementAndGet() <= 0) {
synchronized (AbstractChangeSet.this.waitingAsync) {
AbstractChangeSet.this.waitingAsync.notifyAll();
}
synchronized (AbstractChangeSet.this.waitingCombined) {
AbstractChangeSet.this.waitingCombined.notifyAll();
}
}
}
};
if (completeNow) {
wrappedTask.run();
return Futures.immediateCancelledFuture();
} else {
return Fawe.get().getQueueHandler().submit(wrappedTask);
}
}
}

View File

@ -0,0 +1,189 @@
package com.fastasyncworldedit.core.history.changeset;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.history.change.BlockChange;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.history.change.EntityCreate;
import com.sk89q.worldedit.history.change.EntityRemove;
import com.sk89q.worldedit.math.BlockVector3;
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.BaseBlock;
import java.io.IOException;
import java.util.Iterator;
import java.util.concurrent.Future;
public class AbstractDelegateChangeSet extends AbstractChangeSet {
public final AbstractChangeSet parent;
public AbstractDelegateChangeSet(AbstractChangeSet parent) {
super(parent.getWorld());
this.parent = parent;
this.waitingCombined = parent.waitingCombined;
this.waitingAsync = parent.waitingAsync;
}
public final AbstractChangeSet getParent() {
return parent;
}
@Override
public World getWorld() {
return parent.getWorld();
}
@Override
public void closeAsync() {
parent.closeAsync();
}
@Override
public void flush() {
parent.flush();
}
@Override
public void addTileCreate(CompoundTag tag) {
parent.addTileCreate(tag);
}
@Override
public void addTileRemove(CompoundTag tag) {
parent.addTileRemove(tag);
}
@Override
public void addEntityRemove(CompoundTag tag) {
parent.addEntityRemove(tag);
}
@Override
public void addEntityCreate(CompoundTag tag) {
parent.addEntityCreate(tag);
}
@Override
public void addBiomeChange(int x, int y, int z, BiomeType from, BiomeType to) {
parent.addBiomeChange(x, y, z, from, to);
}
@Override
public Iterator<Change> getIterator(BlockBag blockBag, int mode, boolean redo) {
return parent.getIterator(blockBag, mode, redo);
}
@Override
public Iterator<Change> getIterator(boolean redo) {
return parent.getIterator(redo);
}
@Override
public EditSession toEditSession(Player player) {
return parent.toEditSession(player);
}
@Override
public EditSession toEditSession(Player player, Region[] regions) {
return parent.toEditSession(player, regions);
}
@Override
public void add(int x, int y, int z, int combinedFrom, int combinedTo) {
parent.add(x, y, z, combinedFrom, combinedTo);
}
@Override
public void add(EntityCreate change) {
parent.add(change);
}
@Override
public void add(EntityRemove change) {
parent.add(change);
}
@Override
public void add(Change change) {
parent.add(change);
}
@Override
public void add(BlockChange change) {
parent.add(change);
}
@Override
public void add(BlockVector3 loc, BaseBlock from, BaseBlock to) {
parent.add(loc, from, to);
}
@Override
public void add(int x, int y, int z, BaseBlock from, BaseBlock to) {
parent.add(x, y, z, from, to);
}
@Override
public void add(int x, int y, int z, int combinedFrom, BaseBlock to) {
parent.add(x, y, z, combinedFrom, to);
}
@Override
public Iterator<Change> backwardIterator() {
return parent.backwardIterator();
}
@Override
public Iterator<Change> forwardIterator() {
return parent.forwardIterator();
}
@Override
public void close() throws IOException {
parent.close();
}
@Override
public boolean isEmpty() {
return parent.isEmpty();
}
@Override
public Future<?> addWriteTask(Runnable writeTask) {
return parent.addWriteTask(writeTask);
}
@Override
public Future<?> addWriteTask(Runnable writeTask, boolean completeNow) {
return parent.addWriteTask(writeTask, completeNow);
}
@Override
public boolean isRecordingChanges() {
return parent.isRecordingChanges();
}
@Override
public void setRecordChanges(boolean recordChanges) {
parent.setRecordChanges(recordChanges);
}
@Override
public int size() {
return parent.size();
}
@Override
public void delete() {
parent.delete();
}
@Override
public ChangeSetSummary summarize(Region region, boolean shallow) {
return parent.summarize(region, shallow);
}
}

View File

@ -0,0 +1,120 @@
package com.fastasyncworldedit.core.history.changeset;
import com.fastasyncworldedit.core.FaweCache;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.extent.inventory.BlockBagException;
import com.sk89q.worldedit.extent.inventory.UnplaceableBlockException;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
public class BlockBagChangeSet extends AbstractDelegateChangeSet {
private final boolean mine;
private int[] missingBlocks = new int[BlockTypes.size()];
private BlockBag blockBag;
public BlockBagChangeSet(AbstractChangeSet parent, BlockBag blockBag, boolean mine) {
super(parent);
this.blockBag = blockBag;
this.mine = mine;
}
/**
* Get the block bag.
*
* @return a block bag, which may be null if none is used
*/
@Nullable
public BlockBag getBlockBag() {
return blockBag;
}
/**
* Set the block bag.
*
* @param blockBag a block bag, which may be null if none is used
*/
public void setBlockBag(@Nullable BlockBag blockBag) {
this.blockBag = blockBag;
}
/**
* Gets the list of missing blocks and clears the list for the next
* operation.
*
* @return a map of missing blocks
*/
public Map<BlockType, Integer> popMissing() {
HashMap<BlockType, Integer> map = new HashMap<>();
for (int i = 0; i < missingBlocks.length; i++) {
int count = missingBlocks[i];
if (count > 0) {
map.put(BlockTypes.get(i), count);
}
}
Arrays.fill(missingBlocks, 0);
return map;
}
@Override
public void add(BlockVector3 loc, BaseBlock from, BaseBlock to) {
int x = loc.getBlockX();
int y = loc.getBlockY();
int z = loc.getBlockZ();
add(x, y, z, from, to);
}
@Override
public void add(int x, int y, int z, BaseBlock from, BaseBlock to) {
check(from.getBlockType(), to.getBlockType());
super.add(x, y, z, from, to);
}
public void check(BlockType typeFrom, BlockType typeTo) {
if (!typeTo.getMaterial().isAir()) {
try {
blockBag.fetchPlacedBlock(typeTo.getDefaultState());
} catch (UnplaceableBlockException e) {
throw FaweCache.IMP.BLOCK_BAG;
} catch (BlockBagException e) {
missingBlocks[typeTo.getInternalId()]++;
throw FaweCache.IMP.BLOCK_BAG;
}
}
if (mine) {
if (!typeFrom.getMaterial().isAir()) {
try {
blockBag.storeDroppedBlock(typeFrom.getDefaultState());
} catch (BlockBagException ignored) {
}
}
}
}
@Override
public void add(int x, int y, int z, int combinedFrom, int combinedTo) {
BlockType typeFrom = BlockTypes.getFromStateId(combinedFrom);
BlockType typeTo = BlockTypes.getFromStateId(combinedTo);
check(typeFrom, typeTo);
super.add(x, y, z, combinedFrom, combinedTo);
}
@Override
public void addTileCreate(CompoundTag nbt) {
if (nbt.containsKey("items")) {
Map<String, Tag> map = new HashMap<>(nbt.getValue());
map.remove("items");
}
super.addTileCreate(nbt);
}
}

View File

@ -0,0 +1,36 @@
package com.fastasyncworldedit.core.history.changeset;
import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.world.block.BlockState;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public interface ChangeSetSummary {
Map<BlockState, Integer> getBlocks();
int getSize();
default List<Countable<BlockState>> getBlockDistributionWithData() {
ArrayList<Countable<BlockState>> list = new ArrayList<>();
for (Map.Entry<BlockState, Integer> entry : getBlocks().entrySet()) {
list.add(new Countable<>(entry.getKey(), entry.getValue()));
}
return list;
}
default Map<BlockState, Double> getPercents() {
Map<BlockState, Integer> map = getBlocks();
int count = getSize();
Map<BlockState, Double> newMap = new HashMap<>();
for (Map.Entry<BlockState, Integer> entry : map.entrySet()) {
BlockState id = entry.getKey();
int changes = entry.getValue();
double percent = (changes * 1000L / count) / 10d;
newMap.put(id, percent);
}
return newMap;
}
}

View File

@ -0,0 +1,760 @@
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.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;
public abstract class FaweStreamChangeSet extends AbstractChangeSet {
public static final int HEADER_SIZE = 9;
private int mode;
private final int compression;
protected FaweStreamIdDelegate idDel;
protected FaweStreamPositionDelegate posDel;
public FaweStreamChangeSet(World world) {
this(world, Settings.IMP.HISTORY.COMPRESSION_LEVEL, Settings.IMP.HISTORY.STORE_REDO, Settings.IMP.HISTORY.SMALL_EDITS);
}
public FaweStreamChangeSet(World world, int compression, boolean storeRedo, boolean smallLoc) {
super(world);
this.compression = compression;
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 {
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);
}
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[5];
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((byte) ry);
}
@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;
}
@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);
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();
// 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;
public int entityCreateSize;
public int entityRemoveSize;
public int tileCreateSize;
public int tileRemoveSize;
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 (Throwable e) {
e.printStackTrace();
}
}
@Override
public void addBiomeChange(int x, int y, int z, BiomeType from, BiomeType to) {
blockSize++;
try {
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));
os.write((byte) (y));
os.writeVarInt(from.getInternalId());
os.writeVarInt(to.getInternalId());
} catch (Throwable 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<MutableBlockChange> 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<MutableBlockChange>() {
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<MutableBiomeChange> getBiomeIterator(final boolean dir) throws IOException {
final FaweInputStream is = getBiomeIS();
if (is == null) {
return Collections.emptyIterator();
}
final MutableBiomeChange change = new MutableBiomeChange();
return new Iterator<MutableBiomeChange>() {
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());
int z = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read());
int y = is.read();
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<Change> getIterator(BlockBag blockBag, int mode, boolean redo) {
if (blockBag != null && mode > 0) {
try {
return (Iterator<Change>) (Iterator<?>) getFullBlockIterator(blockBag, mode, redo);
} catch (IOException e) {
e.printStackTrace();
}
}
return getIterator(redo);
}
public Iterator<MutableFullBlockChange> 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<MutableFullBlockChange>() {
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<MutableEntityChange> getEntityIterator(final NBTInputStream is, final boolean create) {
if (is == null) {
return Collections.emptyIterator();
}
final MutableEntityChange change = new MutableEntityChange(null, create);
try {
return new Iterator<MutableEntityChange>() {
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<MutableTileChange> getTileIterator(final NBTInputStream is, final boolean create) {
if (is == null) {
return Collections.emptyIterator();
}
final MutableTileChange change = new MutableTileChange(null, create);
try {
return new Iterator<MutableTileChange>() {
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<Change> getIterator(final boolean dir) {
try {
close();
final Iterator<MutableTileChange> tileCreate = getTileIterator(getTileCreateIS(), true);
final Iterator<MutableTileChange> tileRemove = getTileIterator(getTileRemoveIS(), false);
final Iterator<MutableEntityChange> entityCreate = getEntityIterator(getEntityCreateIS(), true);
final Iterator<MutableEntityChange> entityRemove = getEntityIterator(getEntityRemoveIS(), false);
final Iterator<MutableBlockChange> blockChange = getBlockIterator(dir);
final Iterator<MutableBiomeChange> biomeChange = getBiomeIterator(dir);
return new Iterator<Change>() {
Iterator<Change>[] iterators = new Iterator[]{tileCreate, tileRemove, entityCreate, entityRemove, blockChange, biomeChange};
int i = 0;
Iterator<Change> 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<Change> backwardIterator() {
return getIterator(false);
}
@Override
public Iterator<Change> 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.IMP.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;
}
}

View File

@ -0,0 +1,78 @@
package com.fastasyncworldedit.core.history.changeset;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BiomeType;
import java.util.Collections;
import java.util.Iterator;
public class NullChangeSet extends AbstractChangeSet {
public NullChangeSet(World world) {
super(world);
}
@Override
public final void close() {
}
@Override
public final void add(int x, int y, int z, int combinedFrom, int combinedTo) {
}
@Override
public final void addTileCreate(CompoundTag tag) {
}
@Override
public final void addTileRemove(CompoundTag tag) {
}
@Override
public final void addEntityRemove(CompoundTag tag) {
}
@Override
public final void addEntityCreate(CompoundTag tag) {
}
@Override
public void addBiomeChange(int x, int y, int z, BiomeType from, BiomeType to) {
}
@Override
public Iterator<Change> getIterator(BlockBag blockBag, int mode, boolean redo) {
return getIterator(redo);
}
@Override
public final Iterator<Change> getIterator(boolean undo) {
return Collections.emptyIterator();
}
@Override
public final int size() {
return 0;
}
@Override
public boolean isRecordingChanges() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setRecordChanges(boolean recordChanges) {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,69 @@
package com.fastasyncworldedit.core.history.changeset;
import com.fastasyncworldedit.core.history.changeset.ChangeSetSummary;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import java.util.HashMap;
import java.util.Map;
public class SimpleChangeSetSummary implements ChangeSetSummary {
public int[] blocks;
public int minX;
public int minZ;
public int maxX;
public int maxZ;
public SimpleChangeSetSummary() {
blocks = new int[BlockTypesCache.states.length];
this.minX = Integer.MAX_VALUE;
this.minZ = Integer.MAX_VALUE;
this.maxX = Integer.MIN_VALUE;
this.maxZ = Integer.MIN_VALUE;
}
public SimpleChangeSetSummary(int x, int z) {
blocks = new int[BlockTypesCache.states.length];
minX = x;
maxX = x;
minZ = z;
maxZ = z;
}
public void add(int x, int z, int id) {
blocks[id]++;
if (x < minX) {
minX = x;
} else if (x > maxX) {
maxX = x;
}
if (z < minZ) {
minZ = z;
} else if (z > maxZ) {
maxZ = z;
}
}
@Override
public Map<BlockState, Integer> getBlocks() {
HashMap<BlockState, Integer> map = new HashMap<>();
for (int i = 0; i < blocks.length; i++) {
if (blocks[i] != 0) {
BlockState state = BlockTypesCache.states[i];
map.put(state, blocks[i]);
}
}
return map;
}
@Override
public int getSize() {
int count = 0;
for (int block : blocks) {
count += block;
}
return count;
}
}