mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-04 03:56:41 +00:00
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 commit275ba9bd84
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> commit9fd8984804
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> commit861fb45e5c
Author: dordsor21 <dordsor21@gmail.com> Date: Fri Jul 16 19:07:02 2021 +0100 Fix #1075 commit420c45a29a
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 commit4d4db7dcd0
Author: SirYwell <hannesgreule@outlook.de> Date: Fri Jul 16 17:52:44 2021 +0200 Make sure leaves category is loaded for heightmaps (fixes #1176) commitc98f6e4f37
Author: dordsor21 <dordsor21@gmail.com> Date: Fri Jul 16 10:44:52 2021 +0100 Do not allow generation commands to generate outside selection commit2485f5eccc
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 commitd9418ec8ae
Author: dordsor21 <dordsor21@gmail.com> Date: Fri Jul 16 09:52:44 2021 +0100 Undo part of41073bb1a0
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:
@ -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
|
||||
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user