Finish disk storage history and memory optimized history
history.compress: Enables compression of player history (slower, but reduced memory usage) history.use-disk: Player history is saved to disk (slower, but saves a lot of memory)
This commit is contained in:
parent
3cf106842d
commit
466739e31d
2
pom.xml
2
pom.xml
@ -8,7 +8,7 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
<artifactId>FastAsyncWorldEdit</artifactId>
|
<artifactId>FastAsyncWorldEdit</artifactId>
|
||||||
<version>3.3.3</version>
|
<version>3.3.4</version>
|
||||||
<name>FastAsyncWorldEdit</name>
|
<name>FastAsyncWorldEdit</name>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<build>
|
<build>
|
||||||
|
@ -19,12 +19,14 @@ import com.boydti.fawe.command.WorldEditRegion;
|
|||||||
import com.boydti.fawe.config.BBC;
|
import com.boydti.fawe.config.BBC;
|
||||||
import com.boydti.fawe.config.Settings;
|
import com.boydti.fawe.config.Settings;
|
||||||
import com.boydti.fawe.util.Lag;
|
import com.boydti.fawe.util.Lag;
|
||||||
|
import com.boydti.fawe.util.MainUtil;
|
||||||
import com.boydti.fawe.util.MemUtil;
|
import com.boydti.fawe.util.MemUtil;
|
||||||
import com.boydti.fawe.util.SetQueue;
|
import com.boydti.fawe.util.SetQueue;
|
||||||
import com.boydti.fawe.util.TaskManager;
|
import com.boydti.fawe.util.TaskManager;
|
||||||
import com.boydti.fawe.util.WEManager;
|
import com.boydti.fawe.util.WEManager;
|
||||||
import com.boydti.fawe.util.WESubscriber;
|
import com.boydti.fawe.util.WESubscriber;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
|
import com.sk89q.worldedit.LocalSession;
|
||||||
import com.sk89q.worldedit.WorldEdit;
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
import com.sk89q.worldedit.command.SchematicCommands;
|
import com.sk89q.worldedit.command.SchematicCommands;
|
||||||
import com.sk89q.worldedit.command.ScriptingCommands;
|
import com.sk89q.worldedit.command.ScriptingCommands;
|
||||||
@ -38,11 +40,9 @@ import com.sk89q.worldedit.function.visitor.LayerVisitor;
|
|||||||
import com.sk89q.worldedit.function.visitor.NonRisingVisitor;
|
import com.sk89q.worldedit.function.visitor.NonRisingVisitor;
|
||||||
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
||||||
import com.sk89q.worldedit.function.visitor.RegionVisitor;
|
import com.sk89q.worldedit.function.visitor.RegionVisitor;
|
||||||
|
import com.sk89q.worldedit.history.change.EntityCreate;
|
||||||
/**
|
import com.sk89q.worldedit.history.change.EntityRemove;
|
||||||
* Simplified overview:
|
[ WorldEdit action]
|
||||||
*
|
|
||||||
* [ WorldEdit action]
|
|
||||||
* |
|
* |
|
||||||
* \|/
|
* \|/
|
||||||
* [ EditSession ] - The change is processed (area restrictions, change limit, block type)
|
* [ EditSession ] - The change is processed (area restrictions, change limit, block type)
|
||||||
@ -149,6 +149,9 @@ public class Fawe {
|
|||||||
TaskManager.IMP = this.IMP.getTaskManager();
|
TaskManager.IMP = this.IMP.getTaskManager();
|
||||||
SetQueue.IMP.queue = this.IMP.getQueue();
|
SetQueue.IMP.queue = this.IMP.getQueue();
|
||||||
|
|
||||||
|
// Delete old history
|
||||||
|
MainUtil.deleteDirectory(new File(IMP.getDirectory(), "history"));
|
||||||
|
|
||||||
// Delayed setup
|
// Delayed setup
|
||||||
TaskManager.IMP.later(new Runnable() {
|
TaskManager.IMP.later(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@ -214,7 +217,9 @@ public class Fawe {
|
|||||||
RecursiveVisitor.inject();
|
RecursiveVisitor.inject();
|
||||||
RegionVisitor.inject();
|
RegionVisitor.inject();
|
||||||
CommandManager.inject();
|
CommandManager.inject();
|
||||||
// DispatcherWrapper.inject();
|
EntityCreate.inject();
|
||||||
|
EntityRemove.inject();
|
||||||
|
LocalSession.inject();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupMemoryListener() {
|
private void setupMemoryListener() {
|
||||||
|
@ -24,7 +24,8 @@ public class Settings {
|
|||||||
public static List<String> WE_BLACKLIST = Arrays.asList("cs", ".s", "restore", "snapshot", "delchunks", "listchunks");
|
public static List<String> WE_BLACKLIST = Arrays.asList("cs", ".s", "restore", "snapshot", "delchunks", "listchunks");
|
||||||
public static long MEM_FREE = 95;
|
public static long MEM_FREE = 95;
|
||||||
public static boolean ENABLE_HARD_LIMIT = true;
|
public static boolean ENABLE_HARD_LIMIT = true;
|
||||||
public static boolean STORE_HISTORY_ON_DISK = true;
|
public static boolean STORE_HISTORY_ON_DISK = false;
|
||||||
|
public static boolean COMPRESS_HISTORY = false;
|
||||||
|
|
||||||
public static void setup(final File file) {
|
public static void setup(final File file) {
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
@ -48,7 +49,8 @@ public class Settings {
|
|||||||
options.put("max-memory-percent", MEM_FREE);
|
options.put("max-memory-percent", MEM_FREE);
|
||||||
options.put("crash-mitigation", ENABLE_HARD_LIMIT);
|
options.put("crash-mitigation", ENABLE_HARD_LIMIT);
|
||||||
options.put("fix-all-lighting", FIX_ALL_LIGHTING);
|
options.put("fix-all-lighting", FIX_ALL_LIGHTING);
|
||||||
options.put("store-history-on-disk", STORE_HISTORY_ON_DISK);
|
options.put("history.use-disk", STORE_HISTORY_ON_DISK);
|
||||||
|
options.put("history.compress", COMPRESS_HISTORY);
|
||||||
|
|
||||||
for (final Entry<String, Object> node : options.entrySet()) {
|
for (final Entry<String, Object> node : options.entrySet()) {
|
||||||
if (!config.contains(node.getKey())) {
|
if (!config.contains(node.getKey())) {
|
||||||
@ -65,7 +67,8 @@ public class Settings {
|
|||||||
REQUIRE_SELECTION = config.getBoolean("require-selection-in-mask");
|
REQUIRE_SELECTION = config.getBoolean("require-selection-in-mask");
|
||||||
WE_BLACKLIST = config.getStringList("command-blacklist");
|
WE_BLACKLIST = config.getStringList("command-blacklist");
|
||||||
ENABLE_HARD_LIMIT = config.getBoolean("crash-mitigation");
|
ENABLE_HARD_LIMIT = config.getBoolean("crash-mitigation");
|
||||||
if (STORE_HISTORY_ON_DISK = config.getBoolean("store-history-on-disk")) {
|
COMPRESS_HISTORY = config.getBoolean("history.compress");
|
||||||
|
if (STORE_HISTORY_ON_DISK = config.getBoolean("history.use-disk")) {
|
||||||
LocalSession.MAX_HISTORY_SIZE = Integer.MAX_VALUE;
|
LocalSession.MAX_HISTORY_SIZE = Integer.MAX_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,22 +4,29 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.UUID;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
import java.util.zip.GZIPOutputStream;
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
import com.boydti.fawe.Fawe;
|
import com.boydti.fawe.Fawe;
|
||||||
import com.boydti.fawe.FaweCache;
|
import com.boydti.fawe.FaweCache;
|
||||||
|
import com.boydti.fawe.util.MainUtil;
|
||||||
|
import com.boydti.fawe.util.ReflectionUtils;
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
|
import com.sk89q.jnbt.IntTag;
|
||||||
|
import com.sk89q.jnbt.NBTInputStream;
|
||||||
import com.sk89q.jnbt.NBTOutputStream;
|
import com.sk89q.jnbt.NBTOutputStream;
|
||||||
|
import com.sk89q.jnbt.NamedTag;
|
||||||
|
import com.sk89q.jnbt.Tag;
|
||||||
import com.sk89q.worldedit.BlockVector;
|
import com.sk89q.worldedit.BlockVector;
|
||||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||||
import com.sk89q.worldedit.history.change.BlockChange;
|
import com.sk89q.worldedit.history.change.BlockChange;
|
||||||
import com.sk89q.worldedit.history.change.Change;
|
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.history.changeset.ChangeSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,27 +36,67 @@ import com.sk89q.worldedit.history.changeset.ChangeSet;
|
|||||||
* - Minimal memory usage
|
* - Minimal memory usage
|
||||||
* - Slow
|
* - Slow
|
||||||
*/
|
*/
|
||||||
public class DiskStorageHistory implements ChangeSet {
|
public class DiskStorageHistory implements ChangeSet, FlushableChangeSet {
|
||||||
|
|
||||||
private final File bdFile;
|
private final File bdFile;
|
||||||
private final File nbtFile;
|
private final File nbtfFile;
|
||||||
private final File anyFile;
|
private final File nbttFile;
|
||||||
|
private final File entfFile;
|
||||||
|
private final 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 GZIPOutputStream osBD;
|
private GZIPOutputStream osBD;
|
||||||
private ObjectOutputStream osANY;
|
|
||||||
private NBTOutputStream osNBT;
|
// NBT From
|
||||||
|
private NBTOutputStream osNBTF;
|
||||||
|
private GZIPOutputStream osNBTFG;
|
||||||
|
private AtomicInteger osNBTFI;
|
||||||
|
|
||||||
|
// NBT To
|
||||||
|
private NBTOutputStream osNBTT;
|
||||||
|
private GZIPOutputStream osNBTTG;
|
||||||
|
private AtomicInteger osNBTTI;
|
||||||
|
|
||||||
|
// Entity From
|
||||||
|
private NBTOutputStream osENTF;
|
||||||
|
private GZIPOutputStream osENTFG;
|
||||||
|
private AtomicInteger osENTFI;
|
||||||
|
|
||||||
|
// Entity To
|
||||||
|
private NBTOutputStream osENTT;
|
||||||
|
private GZIPOutputStream osENTTG;
|
||||||
|
private AtomicInteger osENTTI;
|
||||||
|
|
||||||
private int ox;
|
private int ox;
|
||||||
private int oz;
|
private int oz;
|
||||||
|
|
||||||
private final AtomicInteger size;
|
private final AtomicInteger size;
|
||||||
|
|
||||||
public DiskStorageHistory() {
|
public DiskStorageHistory(String name) {
|
||||||
size = new AtomicInteger();
|
size = new AtomicInteger();
|
||||||
UUID uuid = UUID.randomUUID();
|
String base = "history" + File.separator + name;
|
||||||
nbtFile = new File(Fawe.imp().getDirectory(), "history" + File.separator + uuid.toString() + ".nbt");
|
File folder = new File(Fawe.imp().getDirectory(), base);
|
||||||
bdFile = new File(Fawe.imp().getDirectory(), "history" + File.separator + uuid.toString() + ".bd");
|
int i;
|
||||||
anyFile = new File(Fawe.imp().getDirectory(), "history" + File.separator + uuid.toString() + ".any");
|
for (i = 0;; i++) {
|
||||||
|
File test = new File(folder, i + ".bd");
|
||||||
|
if (!test.exists()) {
|
||||||
|
base += File.separator + i;
|
||||||
|
nbtfFile = new File(Fawe.imp().getDirectory(), base + ".nbtf");
|
||||||
|
nbttFile = new File(Fawe.imp().getDirectory(), base + ".nbtt");
|
||||||
|
entfFile = new File(Fawe.imp().getDirectory(), base + ".entf");
|
||||||
|
enttFile = new File(Fawe.imp().getDirectory(), base + ".entt");
|
||||||
|
bdFile = new File(Fawe.imp().getDirectory(), base + ".bd");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -62,24 +109,39 @@ public class DiskStorageHistory implements ChangeSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void flush() {
|
public void flush() {
|
||||||
try {
|
try {
|
||||||
if (osBD != null) {
|
if (osBD != null) {
|
||||||
osBD.flush();
|
osBD.flush();
|
||||||
osBD.close();
|
osBD.close();
|
||||||
|
osBD = null;
|
||||||
}
|
}
|
||||||
if (osANY != null) {
|
if (osNBTF != null) {
|
||||||
osANY.flush();
|
osNBTFG.flush();
|
||||||
osANY.close();
|
osNBTF.close();
|
||||||
|
osNBTF = null;
|
||||||
|
osNBTFG = null;
|
||||||
}
|
}
|
||||||
if (osNBT != null) {
|
if (osNBTT != null) {
|
||||||
osNBT.close();
|
osNBTTG.flush();
|
||||||
|
osNBTT.close();
|
||||||
|
osNBTT = null;
|
||||||
|
osNBTTG = null;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void add(EntityCreate change) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(EntityRemove change) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void add(BlockChange change) {
|
public void add(BlockChange change) {
|
||||||
try {
|
try {
|
||||||
BlockVector loc = change.getPosition();
|
BlockVector loc = change.getPosition();
|
||||||
@ -91,13 +153,12 @@ public class DiskStorageHistory implements ChangeSet {
|
|||||||
BaseBlock to = change.getCurrent();
|
BaseBlock to = change.getCurrent();
|
||||||
|
|
||||||
int idfrom = from.getId();
|
int idfrom = from.getId();
|
||||||
int combinedFrom = (byte) (FaweCache.hasData(idfrom) ? ((idfrom << 4) + from.getData()) : (idfrom << 4));
|
int combinedFrom = (FaweCache.hasData(idfrom) ? ((idfrom << 4) + from.getData()) : (idfrom << 4));
|
||||||
CompoundTag nbtFrom = FaweCache.hasData(idfrom) ? from.getNbtData() : null;
|
CompoundTag nbtFrom = FaweCache.hasNBT(idfrom) ? from.getNbtData() : null;
|
||||||
|
|
||||||
int idTo = to.getId();
|
int idTo = to.getId();
|
||||||
int combinedTo = (byte) (FaweCache.hasData(idTo) ? ((idTo << 4) + to.getData()) : (idTo << 4));
|
int combinedTo = (FaweCache.hasData(idTo) ? ((idTo << 4) + to.getData()) : (idTo << 4));
|
||||||
CompoundTag nbtTo = FaweCache.hasData(idTo) ? to.getNbtData() : null;
|
CompoundTag nbtTo = FaweCache.hasNBT(idTo) ? to.getNbtData() : null;
|
||||||
|
|
||||||
GZIPOutputStream stream = getBAOS(x, y, z);
|
GZIPOutputStream stream = getBAOS(x, y, z);
|
||||||
//x
|
//x
|
||||||
stream.write((x - ox) & 0xff);
|
stream.write((x - ox) & 0xff);
|
||||||
@ -114,14 +175,23 @@ public class DiskStorageHistory implements ChangeSet {
|
|||||||
stream.write((combinedTo) & 0xff);
|
stream.write((combinedTo) & 0xff);
|
||||||
stream.write(((combinedTo) >> 8) & 0xff);
|
stream.write(((combinedTo) >> 8) & 0xff);
|
||||||
|
|
||||||
/*
|
if (nbtFrom != null && MainUtil.isValidTag(nbtFrom)) {
|
||||||
* [header]
|
Map<String, Tag> value = ReflectionUtils.getMap(nbtFrom.getValue());
|
||||||
* [int x][int z] origin
|
value.put("x", new IntTag(x));
|
||||||
* [contents]
|
value.put("y", new IntTag(y));
|
||||||
* relative: short x,short z,unsigned byte y
|
value.put("z", new IntTag(z));
|
||||||
* from: char
|
NBTOutputStream nbtos = getNBTFOS(x, y, z);
|
||||||
* to: char
|
nbtos.writeNamedTag(osNBTFI.getAndIncrement() + "", nbtFrom);
|
||||||
*/
|
}
|
||||||
|
|
||||||
|
if (nbtTo != null && MainUtil.isValidTag(nbtTo)) {
|
||||||
|
Map<String, Tag> value = ReflectionUtils.getMap(nbtTo.getValue());
|
||||||
|
value.put("x", new IntTag(x));
|
||||||
|
value.put("y", new IntTag(y));
|
||||||
|
value.put("z", new IntTag(z));
|
||||||
|
NBTOutputStream nbtos = getNBTTOS(x, y, z);
|
||||||
|
nbtos.writeNamedTag(osNBTTI.getAndIncrement() + "", nbtTo);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -136,22 +206,63 @@ public class DiskStorageHistory implements ChangeSet {
|
|||||||
osBD = new GZIPOutputStream(new FileOutputStream(bdFile), true);
|
osBD = new GZIPOutputStream(new FileOutputStream(bdFile), true);
|
||||||
ox = x;
|
ox = x;
|
||||||
oz = z;
|
oz = z;
|
||||||
osBD.write((ox) & 0xff);
|
osBD.write((byte) (ox >> 24));
|
||||||
osBD.write(((ox) >> 8) & 0xff);
|
osBD.write((byte) (ox >> 16));
|
||||||
osBD.write((oz) & 0xff);
|
osBD.write((byte) (ox >> 8));
|
||||||
osBD.write(((oz) >> 8) & 0xff);
|
osBD.write((byte) (ox));
|
||||||
|
osBD.write((byte) (oz >> 24));
|
||||||
|
osBD.write((byte) (oz >> 16));
|
||||||
|
osBD.write((byte) (oz >> 8));
|
||||||
|
osBD.write((byte) (oz));
|
||||||
return osBD;
|
return osBD;
|
||||||
}
|
}
|
||||||
|
|
||||||
private NBTOutputStream getNBTOS(int x, int y, int z) throws IOException {
|
private NBTOutputStream getNBTFOS(int x, int y, int z) throws IOException {
|
||||||
if (osNBT != null) {
|
if (osNBTF != null) {
|
||||||
return osNBT;
|
return osNBTF;
|
||||||
}
|
}
|
||||||
nbtFile.getParentFile().mkdirs();
|
nbtfFile.getParentFile().mkdirs();
|
||||||
nbtFile.createNewFile();
|
nbtfFile.createNewFile();
|
||||||
// osNBT = new FileOutputStream(bdFile);
|
osNBTFG = new GZIPOutputStream(new FileOutputStream(nbtfFile), true);
|
||||||
// TODO FIXME
|
osNBTF = new NBTOutputStream(osNBTFG);
|
||||||
return osNBT;
|
osNBTFI = new AtomicInteger();
|
||||||
|
return osNBTF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NBTOutputStream getNBTTOS(int x, int y, int z) throws IOException {
|
||||||
|
if (osNBTT != null) {
|
||||||
|
return osNBTT;
|
||||||
|
}
|
||||||
|
nbttFile.getParentFile().mkdirs();
|
||||||
|
nbttFile.createNewFile();
|
||||||
|
osNBTTG = new GZIPOutputStream(new FileOutputStream(nbttFile), true);
|
||||||
|
osNBTT = new NBTOutputStream(osNBTTG);
|
||||||
|
osNBTTI = new AtomicInteger();
|
||||||
|
return osNBTT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NBTOutputStream getENTFOS(int x, int y, int z) throws IOException {
|
||||||
|
if (osNBTF != null) {
|
||||||
|
return osNBTF;
|
||||||
|
}
|
||||||
|
entfFile.getParentFile().mkdirs();
|
||||||
|
entfFile.createNewFile();
|
||||||
|
osENTFG = new GZIPOutputStream(new FileOutputStream(entfFile), true);
|
||||||
|
osENTF = new NBTOutputStream(osENTFG);
|
||||||
|
osENTFI = new AtomicInteger();
|
||||||
|
return osENTF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NBTOutputStream getENTTOS(int x, int y, int z) throws IOException {
|
||||||
|
if (osENTT != null) {
|
||||||
|
return osENTT;
|
||||||
|
}
|
||||||
|
enttFile.getParentFile().mkdirs();
|
||||||
|
enttFile.createNewFile();
|
||||||
|
osENTTG = new GZIPOutputStream(new FileOutputStream(enttFile), true);
|
||||||
|
osENTT = new NBTOutputStream(osENTTG);
|
||||||
|
osENTTI = new AtomicInteger();
|
||||||
|
return osENTT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
@ -159,12 +270,31 @@ public class DiskStorageHistory implements ChangeSet {
|
|||||||
flush();
|
flush();
|
||||||
try {
|
try {
|
||||||
if (bdFile.exists()) {
|
if (bdFile.exists()) {
|
||||||
|
if (osNBTF != null) {
|
||||||
|
NBTInputStream os = new NBTInputStream(new GZIPInputStream(new FileInputStream(nbtfFile)));
|
||||||
|
NamedTag tag = os.readNamedTag();
|
||||||
|
}
|
||||||
|
final NBTInputStream nbtf = osNBTF != null ? new NBTInputStream(new GZIPInputStream(new FileInputStream(nbtfFile))) : null;
|
||||||
|
final NBTInputStream nbtt = osNBTT != null ? new NBTInputStream(new GZIPInputStream(new FileInputStream(nbttFile))) : null;
|
||||||
|
|
||||||
final GZIPInputStream gis = new GZIPInputStream(new FileInputStream(bdFile));
|
final GZIPInputStream gis = new GZIPInputStream(new FileInputStream(bdFile));
|
||||||
gis.skip(4);
|
gis.skip(8);
|
||||||
return new Iterator<Change>() {
|
return new Iterator<Change>() {
|
||||||
|
|
||||||
|
private CompoundTag lastFrom = read(nbtf);
|
||||||
|
private CompoundTag lastTo = read(nbtt);
|
||||||
private Change last = read();
|
private Change last = read();
|
||||||
|
|
||||||
|
public CompoundTag read(NBTInputStream stream) {
|
||||||
|
if (stream != null) {
|
||||||
|
try {
|
||||||
|
NamedTag nt = stream.readNamedTag();
|
||||||
|
return nt != null ? ((CompoundTag) nt.getTag()) : null;
|
||||||
|
} catch (IOException e) {}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public Change read() {
|
public Change read() {
|
||||||
try {
|
try {
|
||||||
int x = gis.read() + (gis.read() << 8) + ox;
|
int x = gis.read() + (gis.read() << 8) + ox;
|
||||||
@ -173,9 +303,23 @@ public class DiskStorageHistory implements ChangeSet {
|
|||||||
int from1 = gis.read();
|
int from1 = gis.read();
|
||||||
int from2 = gis.read();
|
int from2 = gis.read();
|
||||||
BaseBlock from = new BaseBlock(((from2 << 4) + (from1 >> 4)), (from1 & 0xf));
|
BaseBlock from = new BaseBlock(((from2 << 4) + (from1 >> 4)), (from1 & 0xf));
|
||||||
|
if (lastFrom != null && FaweCache.hasNBT(from.getId())) {
|
||||||
|
Map<String, Tag> t = lastFrom.getValue();
|
||||||
|
if (((IntTag) t.get("x")).getValue() == x && ((IntTag) t.get("z")).getValue() == z && ((IntTag) t.get("y")).getValue() == y) {
|
||||||
|
from.setNbtData(lastFrom);
|
||||||
|
lastFrom = read(nbtf);
|
||||||
|
}
|
||||||
|
}
|
||||||
int to1 = gis.read();
|
int to1 = gis.read();
|
||||||
int to2 = gis.read();
|
int to2 = gis.read();
|
||||||
BaseBlock to = new BaseBlock(((to2 << 4) + (to1 >> 4)), (to1 & 0xf));
|
BaseBlock to = new BaseBlock(((to2 << 4) + (to1 >> 4)), (to1 & 0xf));
|
||||||
|
if (lastTo != null && FaweCache.hasNBT(to.getId())) {
|
||||||
|
Map<String, Tag> t = lastTo.getValue();
|
||||||
|
if (((IntTag) t.get("x")).getValue() == x && ((IntTag) t.get("z")).getValue() == z && ((IntTag) t.get("y")).getValue() == y) {
|
||||||
|
to.setNbtData(lastTo);
|
||||||
|
lastTo = read(nbtt);
|
||||||
|
}
|
||||||
|
}
|
||||||
BlockVector position = new BlockVector(x, y, z);
|
BlockVector position = new BlockVector(x, y, z);
|
||||||
return dir ? new BlockChange(position, to, from) : new BlockChange(position, from, to);
|
return dir ? new BlockChange(position, to, from) : new BlockChange(position, from, to);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -190,6 +334,12 @@ public class DiskStorageHistory implements ChangeSet {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
gis.close();
|
gis.close();
|
||||||
|
if (nbtf != null) {
|
||||||
|
nbtf.close();
|
||||||
|
}
|
||||||
|
if (nbtt != null) {
|
||||||
|
nbtt.close();
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.boydti.fawe.object.changeset;
|
||||||
|
|
||||||
|
public interface FlushableChangeSet {
|
||||||
|
public void flush();
|
||||||
|
}
|
@ -1,7 +1,26 @@
|
|||||||
package com.boydti.fawe.object.changeset;
|
package com.boydti.fawe.object.changeset;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
|
import com.boydti.fawe.FaweCache;
|
||||||
|
import com.boydti.fawe.util.MainUtil;
|
||||||
|
import com.boydti.fawe.util.ReflectionUtils;
|
||||||
|
import com.google.common.collect.Iterators;
|
||||||
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
|
import com.sk89q.jnbt.IntTag;
|
||||||
|
import com.sk89q.jnbt.Tag;
|
||||||
|
import com.sk89q.worldedit.BlockVector;
|
||||||
|
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||||
|
import com.sk89q.worldedit.history.change.BlockChange;
|
||||||
import com.sk89q.worldedit.history.change.Change;
|
import com.sk89q.worldedit.history.change.Change;
|
||||||
import com.sk89q.worldedit.history.changeset.ChangeSet;
|
import com.sk89q.worldedit.history.changeset.ChangeSet;
|
||||||
|
|
||||||
@ -11,30 +30,225 @@ import com.sk89q.worldedit.history.changeset.ChangeSet;
|
|||||||
* - High CPU usage
|
* - High CPU usage
|
||||||
* - Low memory usage
|
* - Low memory usage
|
||||||
*/
|
*/
|
||||||
public class MemoryOptimizedHistory implements ChangeSet {
|
public class MemoryOptimizedHistory implements ChangeSet, FlushableChangeSet {
|
||||||
|
|
||||||
|
private ArrayDeque<CompoundTag> fromTags;
|
||||||
|
private ArrayDeque<CompoundTag> toTags;
|
||||||
|
|
||||||
|
private byte[] ids;
|
||||||
|
private ByteArrayOutputStream idsStream;
|
||||||
|
private GZIPOutputStream idsStreamZip;
|
||||||
|
|
||||||
|
private ArrayDeque<Change> entities;
|
||||||
|
|
||||||
|
int ox;
|
||||||
|
int oz;
|
||||||
|
|
||||||
|
private final AtomicInteger size;
|
||||||
|
|
||||||
|
public MemoryOptimizedHistory() {
|
||||||
|
size = new AtomicInteger();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void add(Change paramChange) {
|
public void add(Change arg) {
|
||||||
// TODO Auto-generated method stub
|
size.incrementAndGet();
|
||||||
|
if ((arg instanceof BlockChange)) {
|
||||||
|
try {
|
||||||
|
BlockChange change = (BlockChange) arg;
|
||||||
|
BlockVector loc = change.getPosition();
|
||||||
|
int x = loc.getBlockX();
|
||||||
|
int y = loc.getBlockY();
|
||||||
|
int z = loc.getBlockZ();
|
||||||
|
|
||||||
|
BaseBlock from = change.getPrevious();
|
||||||
|
BaseBlock to = change.getCurrent();
|
||||||
|
|
||||||
|
int idfrom = from.getId();
|
||||||
|
int combinedFrom = (FaweCache.hasData(idfrom) ? ((idfrom << 4) + from.getData()) : (idfrom << 4));
|
||||||
|
CompoundTag nbtFrom = FaweCache.hasNBT(idfrom) ? from.getNbtData() : null;
|
||||||
|
|
||||||
|
int idTo = to.getId();
|
||||||
|
int combinedTo = (FaweCache.hasData(idTo) ? ((idTo << 4) + to.getData()) : (idTo << 4));
|
||||||
|
CompoundTag nbtTo = FaweCache.hasNBT(idTo) ? to.getNbtData() : null;
|
||||||
|
GZIPOutputStream stream = getBAOS(x, y, z);
|
||||||
|
//x
|
||||||
|
stream.write((x - ox) & 0xff);
|
||||||
|
stream.write(((x - ox) >> 8) & 0xff);
|
||||||
|
//z
|
||||||
|
stream.write((z - oz) & 0xff);
|
||||||
|
stream.write(((z - oz) >> 8) & 0xff);
|
||||||
|
//y
|
||||||
|
stream.write((byte) y);
|
||||||
|
//from
|
||||||
|
stream.write((combinedFrom) & 0xff);
|
||||||
|
stream.write(((combinedFrom) >> 8) & 0xff);
|
||||||
|
//to
|
||||||
|
stream.write((combinedTo) & 0xff);
|
||||||
|
stream.write(((combinedTo) >> 8) & 0xff);
|
||||||
|
|
||||||
|
if (nbtFrom != null && MainUtil.isValidTag(nbtFrom)) {
|
||||||
|
Map<String, Tag> value = ReflectionUtils.getMap(nbtFrom.getValue());
|
||||||
|
value.put("x", new IntTag(x));
|
||||||
|
value.put("y", new IntTag(y));
|
||||||
|
value.put("z", new IntTag(z));
|
||||||
|
if (fromTags == null) {
|
||||||
|
fromTags = new ArrayDeque<>();
|
||||||
|
}
|
||||||
|
fromTags.add(nbtFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbtTo != null && MainUtil.isValidTag(nbtTo)) {
|
||||||
|
Map<String, Tag> value = ReflectionUtils.getMap(nbtTo.getValue());
|
||||||
|
value.put("x", new IntTag(x));
|
||||||
|
value.put("y", new IntTag(y));
|
||||||
|
value.put("z", new IntTag(z));
|
||||||
|
if (toTags == null) {
|
||||||
|
toTags = new ArrayDeque<>();
|
||||||
|
}
|
||||||
|
toTags.add(nbtTo);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (entities == null) {
|
||||||
|
entities = new ArrayDeque<>();
|
||||||
|
}
|
||||||
|
entities.add(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GZIPOutputStream getBAOS(int x, int y, int z) throws IOException {
|
||||||
|
if (idsStreamZip != null) {
|
||||||
|
return idsStreamZip;
|
||||||
|
}
|
||||||
|
idsStream = new ByteArrayOutputStream(9216);
|
||||||
|
idsStreamZip = new GZIPOutputStream(idsStream, true);
|
||||||
|
ox = x;
|
||||||
|
oz = z;
|
||||||
|
return idsStreamZip;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
public Iterator<Change> getIterator(final boolean dir) {
|
||||||
|
flush();
|
||||||
|
try {
|
||||||
|
Iterator<Change> idsIterator;
|
||||||
|
Iterator<Change> entsIterator = entities != null ? entities.iterator() : new ArrayList().iterator();
|
||||||
|
if (ids == null) {
|
||||||
|
idsIterator = new ArrayList().iterator();
|
||||||
|
} else {
|
||||||
|
final GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(ids));
|
||||||
|
idsIterator = new Iterator<Change>() {
|
||||||
|
|
||||||
|
private final Iterator<CompoundTag> lastFromIter = fromTags != null ? fromTags.iterator() : null;
|
||||||
|
private final Iterator<CompoundTag> lastToIter = toTags != null ? toTags.iterator() : null;
|
||||||
|
private CompoundTag lastFrom = read(lastFromIter);
|
||||||
|
private CompoundTag lastTo = read(lastToIter);
|
||||||
|
private Change last = read();
|
||||||
|
|
||||||
|
public CompoundTag read(Iterator<CompoundTag> tags) {
|
||||||
|
if (tags != null && tags.hasNext()) {
|
||||||
|
return tags.next();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Change read() {
|
||||||
|
try {
|
||||||
|
int x = gis.read() + (gis.read() << 8) + ox;
|
||||||
|
int z = gis.read() + (gis.read() << 8) + oz;
|
||||||
|
int y = gis.read() & 0xff;
|
||||||
|
int from1 = gis.read();
|
||||||
|
int from2 = gis.read();
|
||||||
|
BaseBlock from = new BaseBlock(((from2 << 4) + (from1 >> 4)), (from1 & 0xf));
|
||||||
|
if (lastFrom != null && FaweCache.hasNBT(from.getId())) {
|
||||||
|
Map<String, Tag> t = lastFrom.getValue();
|
||||||
|
if (((IntTag) t.get("x")).getValue() == x && ((IntTag) t.get("z")).getValue() == z && ((IntTag) t.get("y")).getValue() == y) {
|
||||||
|
from.setNbtData(lastFrom);
|
||||||
|
lastFrom = read(lastFromIter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int to1 = gis.read();
|
||||||
|
int to2 = gis.read();
|
||||||
|
BaseBlock to = new BaseBlock(((to2 << 4) + (to1 >> 4)), (to1 & 0xf));
|
||||||
|
if (lastTo != null && FaweCache.hasNBT(to.getId())) {
|
||||||
|
Map<String, Tag> t = lastTo.getValue();
|
||||||
|
if (((IntTag) t.get("x")).getValue() == x && ((IntTag) t.get("z")).getValue() == z && ((IntTag) t.get("y")).getValue() == y) {
|
||||||
|
to.setNbtData(lastTo);
|
||||||
|
lastTo = read(lastToIter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BlockVector position = new BlockVector(x, y, z);
|
||||||
|
return dir ? new BlockChange(position, to, from) : new BlockChange(position, from, to);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (last != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
gis.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Change next() {
|
||||||
|
Change tmp = last;
|
||||||
|
last = read();
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new IllegalArgumentException("CANNOT REMIVE");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Iterators.concat(idsIterator, entsIterator);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return new ArrayList<Change>().iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Change> backwardIterator() {
|
public Iterator<Change> backwardIterator() {
|
||||||
// TODO Auto-generated method stub
|
return getIterator(false);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Change> forwardIterator() {
|
public Iterator<Change> forwardIterator() {
|
||||||
// TODO Auto-generated method stub
|
return getIterator(false);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
// TODO Auto-generated method stub
|
return size.get();
|
||||||
return 0;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() {
|
||||||
|
if (idsStreamZip != null) {
|
||||||
|
try {
|
||||||
|
idsStream.flush();
|
||||||
|
idsStreamZip.flush();
|
||||||
|
idsStreamZip.close();
|
||||||
|
ids = idsStream.toByteArray();
|
||||||
|
idsStream = null;
|
||||||
|
idsStreamZip = null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
package com.boydti.fawe.util;
|
|
||||||
|
|
||||||
public class FileUtil {
|
|
||||||
|
|
||||||
}
|
|
@ -1,11 +1,18 @@
|
|||||||
package com.boydti.fawe.util;
|
package com.boydti.fawe.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import com.boydti.fawe.Fawe;
|
import com.boydti.fawe.Fawe;
|
||||||
import com.boydti.fawe.object.FawePlayer;
|
import com.boydti.fawe.object.FawePlayer;
|
||||||
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
|
import com.sk89q.jnbt.EndTag;
|
||||||
|
import com.sk89q.jnbt.ListTag;
|
||||||
|
import com.sk89q.jnbt.Tag;
|
||||||
|
|
||||||
public class MainUtil {
|
public class MainUtil {
|
||||||
/*
|
/*
|
||||||
@ -29,4 +36,39 @@ public class MainUtil {
|
|||||||
}
|
}
|
||||||
Fawe.debug(s);
|
Fawe.debug(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean deleteDirectory(File directory) {
|
||||||
|
if (directory.exists()) {
|
||||||
|
File[] files = directory.listFiles();
|
||||||
|
if (null != files) {
|
||||||
|
for (int i = 0; i < files.length; i++) {
|
||||||
|
if (files[i].isDirectory()) {
|
||||||
|
deleteDirectory(files[i]);
|
||||||
|
} else {
|
||||||
|
files[i].delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (directory.delete());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isValidTag(Tag tag) {
|
||||||
|
if (tag instanceof EndTag) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (tag instanceof ListTag) {
|
||||||
|
if (((ListTag) tag).getType() == EndTag.class) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tag instanceof CompoundTag) {
|
||||||
|
for (Entry<String, Tag> entry : ((CompoundTag) tag).getValue().entrySet()) {
|
||||||
|
if (!isValidTag(entry.getValue())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
@ -56,6 +57,18 @@ public class ReflectionUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T, V> Map<T, V> getMap(Map<T, V> map) {
|
||||||
|
try {
|
||||||
|
Class<? extends Map> clazz = map.getClass();
|
||||||
|
Field m = clazz.getDeclaredField("m");
|
||||||
|
m.setAccessible(true);
|
||||||
|
return (Map<T, V>) m.get(map);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Class<?> getNmsClass(final String name) {
|
public static Class<?> getNmsClass(final String name) {
|
||||||
final String className = "net.minecraft.server." + getVersion() + "." + name;
|
final String className = "net.minecraft.server." + getVersion() + "." + name;
|
||||||
return getClass(className);
|
return getClass(className);
|
||||||
|
@ -41,10 +41,14 @@ import javax.annotation.Nullable;
|
|||||||
import com.boydti.fawe.Fawe;
|
import com.boydti.fawe.Fawe;
|
||||||
import com.boydti.fawe.FaweCache;
|
import com.boydti.fawe.FaweCache;
|
||||||
import com.boydti.fawe.config.BBC;
|
import com.boydti.fawe.config.BBC;
|
||||||
|
import com.boydti.fawe.config.Settings;
|
||||||
import com.boydti.fawe.object.EditSessionWrapper;
|
import com.boydti.fawe.object.EditSessionWrapper;
|
||||||
import com.boydti.fawe.object.FawePlayer;
|
import com.boydti.fawe.object.FawePlayer;
|
||||||
|
import com.boydti.fawe.object.NullChangeSet;
|
||||||
import com.boydti.fawe.object.RegionWrapper;
|
import com.boydti.fawe.object.RegionWrapper;
|
||||||
|
import com.boydti.fawe.object.changeset.CPUOptimizedHistory;
|
||||||
import com.boydti.fawe.object.changeset.DiskStorageHistory;
|
import com.boydti.fawe.object.changeset.DiskStorageHistory;
|
||||||
|
import com.boydti.fawe.object.changeset.MemoryOptimizedHistory;
|
||||||
import com.boydti.fawe.object.extent.FastWorldEditExtent;
|
import com.boydti.fawe.object.extent.FastWorldEditExtent;
|
||||||
import com.boydti.fawe.object.extent.NullExtent;
|
import com.boydti.fawe.object.extent.NullExtent;
|
||||||
import com.boydti.fawe.object.extent.ProcessedWEExtent;
|
import com.boydti.fawe.object.extent.ProcessedWEExtent;
|
||||||
@ -215,7 +219,6 @@ public class EditSession implements Extent {
|
|||||||
this.world = world;
|
this.world = world;
|
||||||
this.wrapper = Fawe.imp().getEditSessionWrapper(this);
|
this.wrapper = Fawe.imp().getEditSessionWrapper(this);
|
||||||
// this.changeSet = new BlockOptimizedHistory();
|
// this.changeSet = new BlockOptimizedHistory();
|
||||||
this.changeSet = new DiskStorageHistory();
|
|
||||||
|
|
||||||
// Invalid; return null extent
|
// Invalid; return null extent
|
||||||
if (world == null) {
|
if (world == null) {
|
||||||
@ -223,6 +226,7 @@ public class EditSession implements Extent {
|
|||||||
this.bypassReorderHistory = extent;
|
this.bypassReorderHistory = extent;
|
||||||
this.bypassHistory = extent;
|
this.bypassHistory = extent;
|
||||||
this.bypassNone = extent;
|
this.bypassNone = extent;
|
||||||
|
this.changeSet = new NullChangeSet();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Actor actor = event.getActor();
|
final Actor actor = event.getActor();
|
||||||
@ -237,9 +241,11 @@ public class EditSession implements Extent {
|
|||||||
this.bypassReorderHistory = extent;
|
this.bypassReorderHistory = extent;
|
||||||
this.bypassHistory = extent;
|
this.bypassHistory = extent;
|
||||||
this.bypassNone = extent;
|
this.bypassNone = extent;
|
||||||
|
this.changeSet = new NullChangeSet();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.changeSet = Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(actor.getUniqueId().toString()) : Settings.COMPRESS_HISTORY ? new MemoryOptimizedHistory() : new CPUOptimizedHistory();
|
||||||
Extent extent;
|
Extent extent;
|
||||||
final String name = actor.getName();
|
final String name = actor.getName();
|
||||||
final FawePlayer<Object> fp = FawePlayer.wrap(name);
|
final FawePlayer<Object> fp = FawePlayer.wrap(name);
|
||||||
|
931
src/main/java/com/sk89q/worldedit/LocalSession.java
Normal file
931
src/main/java/com/sk89q/worldedit/LocalSession.java
Normal file
@ -0,0 +1,931 @@
|
|||||||
|
/*
|
||||||
|
* WorldEdit, a Minecraft world manipulation toolkit
|
||||||
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||||
|
* Copyright (C) WorldEdit team and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.boydti.fawe.object.changeset.FlushableChangeSet;
|
||||||
|
import com.sk89q.jchronic.Chronic;
|
||||||
|
import com.sk89q.jchronic.Options;
|
||||||
|
import com.sk89q.jchronic.utils.Span;
|
||||||
|
import com.sk89q.jchronic.utils.Time;
|
||||||
|
import com.sk89q.worldedit.command.tool.BlockTool;
|
||||||
|
import com.sk89q.worldedit.command.tool.BrushTool;
|
||||||
|
import com.sk89q.worldedit.command.tool.InvalidToolBindException;
|
||||||
|
import com.sk89q.worldedit.command.tool.SinglePickaxe;
|
||||||
|
import com.sk89q.worldedit.command.tool.Tool;
|
||||||
|
import com.sk89q.worldedit.entity.Player;
|
||||||
|
import com.sk89q.worldedit.extension.platform.Actor;
|
||||||
|
import com.sk89q.worldedit.extent.inventory.BlockBag;
|
||||||
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
|
import com.sk89q.worldedit.function.mask.Masks;
|
||||||
|
import com.sk89q.worldedit.history.changeset.ChangeSet;
|
||||||
|
import com.sk89q.worldedit.internal.cui.CUIEvent;
|
||||||
|
import com.sk89q.worldedit.internal.cui.CUIRegion;
|
||||||
|
import com.sk89q.worldedit.internal.cui.SelectionShapeEvent;
|
||||||
|
import com.sk89q.worldedit.regions.Region;
|
||||||
|
import com.sk89q.worldedit.regions.RegionSelector;
|
||||||
|
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
|
||||||
|
import com.sk89q.worldedit.regions.selector.RegionSelectorType;
|
||||||
|
import com.sk89q.worldedit.session.ClipboardHolder;
|
||||||
|
import com.sk89q.worldedit.session.request.Request;
|
||||||
|
import com.sk89q.worldedit.world.World;
|
||||||
|
import com.sk89q.worldedit.world.snapshot.Snapshot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores session information.
|
||||||
|
*/
|
||||||
|
public class LocalSession {
|
||||||
|
|
||||||
|
public transient static int MAX_HISTORY_SIZE = 15;
|
||||||
|
|
||||||
|
// Non-session related fields
|
||||||
|
private transient LocalConfiguration config;
|
||||||
|
private transient final AtomicBoolean dirty = new AtomicBoolean();
|
||||||
|
|
||||||
|
// Session related
|
||||||
|
private transient RegionSelector selector = new CuboidRegionSelector();
|
||||||
|
private transient boolean placeAtPos1 = false;
|
||||||
|
private transient LinkedList<EditSession> history = new LinkedList<EditSession>();
|
||||||
|
private transient int historyPointer = 0;
|
||||||
|
private transient ClipboardHolder clipboard;
|
||||||
|
private transient boolean toolControl = true;
|
||||||
|
private transient boolean superPickaxe = false;
|
||||||
|
private transient BlockTool pickaxeMode = new SinglePickaxe();
|
||||||
|
private transient Map<Integer, Tool> tools = new HashMap<Integer, Tool>();
|
||||||
|
private transient int maxBlocksChanged = -1;
|
||||||
|
private transient boolean useInventory;
|
||||||
|
private transient Snapshot snapshot;
|
||||||
|
private transient boolean hasCUISupport = false;
|
||||||
|
private transient int cuiVersion = -1;
|
||||||
|
private transient boolean fastMode = false;
|
||||||
|
private transient Mask mask;
|
||||||
|
private transient TimeZone timezone = TimeZone.getDefault();
|
||||||
|
|
||||||
|
// Saved properties
|
||||||
|
private String lastScript;
|
||||||
|
private RegionSelectorType defaultSelector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the object.
|
||||||
|
*
|
||||||
|
* <p>{@link #setConfiguration(LocalConfiguration)} should be called
|
||||||
|
* later with configuration.</p>
|
||||||
|
*/
|
||||||
|
public LocalSession() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the object.
|
||||||
|
*
|
||||||
|
* @param config the configuration
|
||||||
|
*/
|
||||||
|
public LocalSession(@Nullable LocalConfiguration config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the configuration.
|
||||||
|
*
|
||||||
|
* @param config the configuration
|
||||||
|
*/
|
||||||
|
public void setConfiguration(LocalConfiguration config) {
|
||||||
|
checkNotNull(config);
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on post load of the session from persistent storage.
|
||||||
|
*/
|
||||||
|
public void postLoad() {
|
||||||
|
if (defaultSelector != null) {
|
||||||
|
this.selector = defaultSelector.createSelector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether this session is "dirty" and has changes that needs to
|
||||||
|
* be committed.
|
||||||
|
*
|
||||||
|
* @return true if dirty
|
||||||
|
*/
|
||||||
|
public boolean isDirty() {
|
||||||
|
return dirty.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this session as dirty.
|
||||||
|
*/
|
||||||
|
private void setDirty() {
|
||||||
|
dirty.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether this session is "dirty" and has changes that needs to
|
||||||
|
* be committed, and reset it to {@code false}.
|
||||||
|
*
|
||||||
|
* @return true if the dirty value was {@code true}
|
||||||
|
*/
|
||||||
|
public boolean compareAndResetDirty() {
|
||||||
|
return dirty.compareAndSet(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the session's timezone.
|
||||||
|
*
|
||||||
|
* @return the timezone
|
||||||
|
*/
|
||||||
|
public TimeZone getTimeZone() {
|
||||||
|
return timezone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the session's timezone.
|
||||||
|
*
|
||||||
|
* @param timezone the user's timezone
|
||||||
|
*/
|
||||||
|
public void setTimezone(TimeZone timezone) {
|
||||||
|
checkNotNull(timezone);
|
||||||
|
this.timezone = timezone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear history.
|
||||||
|
*/
|
||||||
|
public void clearHistory() {
|
||||||
|
history.clear();
|
||||||
|
historyPointer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remember an edit session for the undo history. If the history maximum
|
||||||
|
* size is reached, old edit sessions will be discarded.
|
||||||
|
*
|
||||||
|
* @param editSession the edit session
|
||||||
|
*/
|
||||||
|
public void remember(EditSession editSession) {
|
||||||
|
checkNotNull(editSession);
|
||||||
|
|
||||||
|
// Don't store anything if no changes were made
|
||||||
|
if (editSession.size() == 0) return;
|
||||||
|
|
||||||
|
// Destroy any sessions after this undo point
|
||||||
|
while (historyPointer < history.size()) {
|
||||||
|
history.remove(historyPointer);
|
||||||
|
}
|
||||||
|
ChangeSet set = editSession.getChangeSet();
|
||||||
|
if (set instanceof FlushableChangeSet) {
|
||||||
|
((FlushableChangeSet) set).flush();
|
||||||
|
}
|
||||||
|
history.add(editSession);
|
||||||
|
while (history.size() > MAX_HISTORY_SIZE) {
|
||||||
|
history.remove(0);
|
||||||
|
}
|
||||||
|
historyPointer = history.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an undo.
|
||||||
|
*
|
||||||
|
* @param newBlockBag a new block bag
|
||||||
|
* @param player the player
|
||||||
|
* @return whether anything was undone
|
||||||
|
*/
|
||||||
|
public EditSession undo(@Nullable BlockBag newBlockBag, LocalPlayer player) {
|
||||||
|
return undo(newBlockBag, (Player) player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an undo.
|
||||||
|
*
|
||||||
|
* @param newBlockBag a new block bag
|
||||||
|
* @param player the player
|
||||||
|
* @return whether anything was undone
|
||||||
|
*/
|
||||||
|
public EditSession undo(@Nullable BlockBag newBlockBag, Player player) {
|
||||||
|
checkNotNull(player);
|
||||||
|
--historyPointer;
|
||||||
|
if (historyPointer >= 0) {
|
||||||
|
EditSession editSession = history.get(historyPointer);
|
||||||
|
EditSession newEditSession = WorldEdit.getInstance().getEditSessionFactory()
|
||||||
|
.getEditSession(editSession.getWorld(), -1, newBlockBag, player);
|
||||||
|
newEditSession.enableQueue();
|
||||||
|
newEditSession.setFastMode(fastMode);
|
||||||
|
editSession.undo(newEditSession);
|
||||||
|
return editSession;
|
||||||
|
} else {
|
||||||
|
historyPointer = 0;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a redo
|
||||||
|
*
|
||||||
|
* @param newBlockBag a new block bag
|
||||||
|
* @param player the player
|
||||||
|
* @return whether anything was redone
|
||||||
|
*/
|
||||||
|
public EditSession redo(@Nullable BlockBag newBlockBag, LocalPlayer player) {
|
||||||
|
return redo(newBlockBag, (Player) player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a redo
|
||||||
|
*
|
||||||
|
* @param newBlockBag a new block bag
|
||||||
|
* @param player the player
|
||||||
|
* @return whether anything was redone
|
||||||
|
*/
|
||||||
|
public EditSession redo(@Nullable BlockBag newBlockBag, Player player) {
|
||||||
|
checkNotNull(player);
|
||||||
|
if (historyPointer < history.size()) {
|
||||||
|
EditSession editSession = history.get(historyPointer);
|
||||||
|
EditSession newEditSession = WorldEdit.getInstance().getEditSessionFactory()
|
||||||
|
.getEditSession(editSession.getWorld(), -1, newBlockBag, player);
|
||||||
|
newEditSession.enableQueue();
|
||||||
|
newEditSession.setFastMode(fastMode);
|
||||||
|
editSession.redo(newEditSession);
|
||||||
|
++historyPointer;
|
||||||
|
return editSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default region selector.
|
||||||
|
*
|
||||||
|
* @return the default region selector
|
||||||
|
*/
|
||||||
|
public RegionSelectorType getDefaultRegionSelector() {
|
||||||
|
return defaultSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default region selector.
|
||||||
|
*
|
||||||
|
* @param defaultSelector the default region selector
|
||||||
|
*/
|
||||||
|
public void setDefaultRegionSelector(RegionSelectorType defaultSelector) {
|
||||||
|
checkNotNull(defaultSelector);
|
||||||
|
this.defaultSelector = defaultSelector;
|
||||||
|
setDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #getRegionSelector(World)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public RegionSelector getRegionSelector(LocalWorld world) {
|
||||||
|
return getRegionSelector((World) world);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the region selector for defining the selection. If the selection
|
||||||
|
* was defined for a different world, the old selection will be discarded.
|
||||||
|
*
|
||||||
|
* @param world the world
|
||||||
|
* @return position the position
|
||||||
|
*/
|
||||||
|
public RegionSelector getRegionSelector(World world) {
|
||||||
|
checkNotNull(world);
|
||||||
|
if (selector.getWorld() == null || !selector.getWorld().equals(world)) {
|
||||||
|
selector.setWorld(world);
|
||||||
|
selector.clear();
|
||||||
|
}
|
||||||
|
return selector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #getRegionSelector(World)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public RegionSelector getRegionSelector() {
|
||||||
|
return selector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #setRegionSelector(World, RegionSelector)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setRegionSelector(LocalWorld world, RegionSelector selector) {
|
||||||
|
setRegionSelector((World) world, selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the region selector.
|
||||||
|
*
|
||||||
|
* @param world the world
|
||||||
|
* @param selector the selector
|
||||||
|
*/
|
||||||
|
public void setRegionSelector(World world, RegionSelector selector) {
|
||||||
|
checkNotNull(world);
|
||||||
|
checkNotNull(selector);
|
||||||
|
selector.setWorld(world);
|
||||||
|
this.selector = selector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the region is fully defined.
|
||||||
|
*
|
||||||
|
* @return true if a region selection is defined
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public boolean isRegionDefined() {
|
||||||
|
return selector.isDefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #isSelectionDefined(World)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public boolean isSelectionDefined(LocalWorld world) {
|
||||||
|
return isSelectionDefined((World) world);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the region is fully defined for the specified world.
|
||||||
|
*
|
||||||
|
* @param world the world
|
||||||
|
* @return true if a region selection is defined
|
||||||
|
*/
|
||||||
|
public boolean isSelectionDefined(World world) {
|
||||||
|
checkNotNull(world);
|
||||||
|
if (selector.getIncompleteRegion().getWorld() == null || !selector.getIncompleteRegion().getWorld().equals(world)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return selector.isDefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #getSelection(World)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public Region getRegion() throws IncompleteRegionException {
|
||||||
|
return selector.getRegion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #getSelection(World)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public Region getSelection(LocalWorld world) throws IncompleteRegionException {
|
||||||
|
return getSelection((World) world);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the selection region. If you change the region, you should
|
||||||
|
* call learnRegionChanges(). If the selection is defined in
|
||||||
|
* a different world, the {@code IncompleteRegionException}
|
||||||
|
* exception will be thrown.
|
||||||
|
*
|
||||||
|
* @param world the world
|
||||||
|
* @return a region
|
||||||
|
* @throws IncompleteRegionException if no region is selected
|
||||||
|
*/
|
||||||
|
public Region getSelection(World world) throws IncompleteRegionException {
|
||||||
|
checkNotNull(world);
|
||||||
|
if (selector.getIncompleteRegion().getWorld() == null || !selector.getIncompleteRegion().getWorld().equals(world)) {
|
||||||
|
throw new IncompleteRegionException();
|
||||||
|
}
|
||||||
|
return selector.getRegion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the selection world.
|
||||||
|
*
|
||||||
|
* @return the the world of the selection
|
||||||
|
*/
|
||||||
|
public World getSelectionWorld() {
|
||||||
|
return selector.getIncompleteRegion().getWorld();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the clipboard.
|
||||||
|
*
|
||||||
|
* @return clipboard
|
||||||
|
* @throws EmptyClipboardException thrown if no clipboard is set
|
||||||
|
*/
|
||||||
|
public ClipboardHolder getClipboard() throws EmptyClipboardException {
|
||||||
|
if (clipboard == null) {
|
||||||
|
throw new EmptyClipboardException();
|
||||||
|
}
|
||||||
|
return clipboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the clipboard.
|
||||||
|
*
|
||||||
|
* <p>Pass {@code null} to clear the clipboard.</p>
|
||||||
|
*
|
||||||
|
* @param clipboard the clipboard, or null if the clipboard is to be cleared
|
||||||
|
*/
|
||||||
|
public void setClipboard(@Nullable ClipboardHolder clipboard) {
|
||||||
|
this.clipboard = clipboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See if tool control is enabled.
|
||||||
|
*
|
||||||
|
* @return true if enabled
|
||||||
|
*/
|
||||||
|
public boolean isToolControlEnabled() {
|
||||||
|
return toolControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change tool control setting.
|
||||||
|
*
|
||||||
|
* @param toolControl true to enable tool control
|
||||||
|
*/
|
||||||
|
public void setToolControl(boolean toolControl) {
|
||||||
|
this.toolControl = toolControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the maximum number of blocks that can be changed in an edit session.
|
||||||
|
*
|
||||||
|
* @return block change limit
|
||||||
|
*/
|
||||||
|
public int getBlockChangeLimit() {
|
||||||
|
return maxBlocksChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maximum number of blocks that can be changed.
|
||||||
|
*
|
||||||
|
* @param maxBlocksChanged the maximum number of blocks changed
|
||||||
|
*/
|
||||||
|
public void setBlockChangeLimit(int maxBlocksChanged) {
|
||||||
|
this.maxBlocksChanged = maxBlocksChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the super pick axe is enabled.
|
||||||
|
*
|
||||||
|
* @return status
|
||||||
|
*/
|
||||||
|
public boolean hasSuperPickAxe() {
|
||||||
|
return superPickaxe;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable super pick axe.
|
||||||
|
*/
|
||||||
|
public void enableSuperPickAxe() {
|
||||||
|
superPickaxe = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable super pick axe.
|
||||||
|
*/
|
||||||
|
public void disableSuperPickAxe() {
|
||||||
|
superPickaxe = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the super pick axe.
|
||||||
|
*
|
||||||
|
* @return whether the super pick axe is now enabled
|
||||||
|
*/
|
||||||
|
public boolean toggleSuperPickAxe() {
|
||||||
|
superPickaxe = !superPickaxe;
|
||||||
|
return superPickaxe;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the position use for commands that take a center point
|
||||||
|
* (i.e. //forestgen, etc.).
|
||||||
|
*
|
||||||
|
* @param player the player
|
||||||
|
* @return the position to use
|
||||||
|
* @throws IncompleteRegionException thrown if a region is not fully selected
|
||||||
|
*/
|
||||||
|
public Vector getPlacementPosition(Player player) throws IncompleteRegionException {
|
||||||
|
checkNotNull(player);
|
||||||
|
if (!placeAtPos1) {
|
||||||
|
return player.getBlockIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
return selector.getPrimaryPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle placement position.
|
||||||
|
*
|
||||||
|
* @return whether "place at position 1" is now enabled
|
||||||
|
*/
|
||||||
|
public boolean togglePlacementPosition() {
|
||||||
|
placeAtPos1 = !placeAtPos1;
|
||||||
|
return placeAtPos1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a block bag for a player.
|
||||||
|
*
|
||||||
|
* @param player the player to get the block bag for
|
||||||
|
* @return a block bag
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public BlockBag getBlockBag(Player player) {
|
||||||
|
checkNotNull(player);
|
||||||
|
if (!useInventory) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return player.getInventoryBlockBag();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the snapshot that has been selected.
|
||||||
|
*
|
||||||
|
* @return the snapshot
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Snapshot getSnapshot() {
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select a snapshot.
|
||||||
|
*
|
||||||
|
* @param snapshot a snapshot
|
||||||
|
*/
|
||||||
|
public void setSnapshot(@Nullable Snapshot snapshot) {
|
||||||
|
this.snapshot = snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the assigned block tool.
|
||||||
|
*
|
||||||
|
* @return the super pickaxe tool mode
|
||||||
|
*/
|
||||||
|
public BlockTool getSuperPickaxe() {
|
||||||
|
return pickaxeMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the super pick axe tool.
|
||||||
|
*
|
||||||
|
* @param tool the tool to set
|
||||||
|
*/
|
||||||
|
public void setSuperPickaxe(BlockTool tool) {
|
||||||
|
checkNotNull(tool);
|
||||||
|
this.pickaxeMode = tool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the tool assigned to the item.
|
||||||
|
*
|
||||||
|
* @param item the item type ID
|
||||||
|
* @return the tool, which may be {@link null}
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Tool getTool(int item) {
|
||||||
|
return tools.get(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the brush tool assigned to the item. If there is no tool assigned
|
||||||
|
* or the tool is not assigned, the slot will be replaced with the
|
||||||
|
* brush tool.
|
||||||
|
*
|
||||||
|
* @param item the item type ID
|
||||||
|
* @return the tool, or {@code null}
|
||||||
|
* @throws InvalidToolBindException if the item can't be bound to that item
|
||||||
|
*/
|
||||||
|
public BrushTool getBrushTool(int item) throws InvalidToolBindException {
|
||||||
|
Tool tool = getTool(item);
|
||||||
|
|
||||||
|
if (tool == null || !(tool instanceof BrushTool)) {
|
||||||
|
tool = new BrushTool("worldedit.brush.sphere");
|
||||||
|
setTool(item, tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (BrushTool) tool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the tool.
|
||||||
|
*
|
||||||
|
* @param item the item type ID
|
||||||
|
* @param tool the tool to set, which can be {@code null}
|
||||||
|
* @throws InvalidToolBindException if the item can't be bound to that item
|
||||||
|
*/
|
||||||
|
public void setTool(int item, @Nullable Tool tool) throws InvalidToolBindException {
|
||||||
|
if (item > 0 && item < 255) {
|
||||||
|
throw new InvalidToolBindException(item, "Blocks can't be used");
|
||||||
|
} else if (item == config.wandItem) {
|
||||||
|
throw new InvalidToolBindException(item, "Already used for the wand");
|
||||||
|
} else if (item == config.navigationWand) {
|
||||||
|
throw new InvalidToolBindException(item, "Already used for the navigation wand");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tools.put(item, tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether inventory usage is enabled for this session.
|
||||||
|
*
|
||||||
|
* @return if inventory is being used
|
||||||
|
*/
|
||||||
|
public boolean isUsingInventory() {
|
||||||
|
return useInventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the state of inventory usage.
|
||||||
|
*
|
||||||
|
* @param useInventory if inventory is to be used
|
||||||
|
*/
|
||||||
|
public void setUseInventory(boolean useInventory) {
|
||||||
|
this.useInventory = useInventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the last script used.
|
||||||
|
*
|
||||||
|
* @return the last script's name
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public String getLastScript() {
|
||||||
|
return lastScript;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the last script used.
|
||||||
|
*
|
||||||
|
* @param lastScript the last script's name
|
||||||
|
*/
|
||||||
|
public void setLastScript(@Nullable String lastScript) {
|
||||||
|
this.lastScript = lastScript;
|
||||||
|
setDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the player the WorldEdit version.
|
||||||
|
*
|
||||||
|
* @param player the player
|
||||||
|
*/
|
||||||
|
public void tellVersion(Actor player) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch a CUI event but only if the actor has CUI support.
|
||||||
|
*
|
||||||
|
* @param actor the actor
|
||||||
|
* @param event the event
|
||||||
|
*/
|
||||||
|
public void dispatchCUIEvent(Actor actor, CUIEvent event) {
|
||||||
|
checkNotNull(actor);
|
||||||
|
checkNotNull(event);
|
||||||
|
|
||||||
|
if (hasCUISupport) {
|
||||||
|
actor.dispatchCUIEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch the initial setup CUI messages.
|
||||||
|
*
|
||||||
|
* @param actor the actor
|
||||||
|
*/
|
||||||
|
public void dispatchCUISetup(Actor actor) {
|
||||||
|
if (selector != null) {
|
||||||
|
dispatchCUISelection(actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the selection information.
|
||||||
|
*
|
||||||
|
* @param actor the actor
|
||||||
|
*/
|
||||||
|
public void dispatchCUISelection(Actor actor) {
|
||||||
|
checkNotNull(actor);
|
||||||
|
|
||||||
|
if (!hasCUISupport) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selector instanceof CUIRegion) {
|
||||||
|
CUIRegion tempSel = (CUIRegion) selector;
|
||||||
|
|
||||||
|
if (tempSel.getProtocolVersion() > cuiVersion) {
|
||||||
|
actor.dispatchCUIEvent(new SelectionShapeEvent(tempSel.getLegacyTypeID()));
|
||||||
|
tempSel.describeLegacyCUI(this, actor);
|
||||||
|
} else {
|
||||||
|
actor.dispatchCUIEvent(new SelectionShapeEvent(tempSel.getTypeID()));
|
||||||
|
tempSel.describeCUI(this, actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describe the selection to the CUI actor.
|
||||||
|
*
|
||||||
|
* @param actor the actor
|
||||||
|
*/
|
||||||
|
public void describeCUI(Actor actor) {
|
||||||
|
checkNotNull(actor);
|
||||||
|
|
||||||
|
if (!hasCUISupport) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selector instanceof CUIRegion) {
|
||||||
|
CUIRegion tempSel = (CUIRegion) selector;
|
||||||
|
|
||||||
|
if (tempSel.getProtocolVersion() > cuiVersion) {
|
||||||
|
tempSel.describeLegacyCUI(this, actor);
|
||||||
|
} else {
|
||||||
|
tempSel.describeCUI(this, actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a CUI initialization message.
|
||||||
|
*
|
||||||
|
* @param text the message
|
||||||
|
*/
|
||||||
|
public void handleCUIInitializationMessage(String text) {
|
||||||
|
checkNotNull(text);
|
||||||
|
|
||||||
|
String[] split = text.split("\\|");
|
||||||
|
if (split.length > 1 && split[0].equalsIgnoreCase("v")) { // enough fields and right message
|
||||||
|
setCUISupport(true);
|
||||||
|
try {
|
||||||
|
setCUIVersion(Integer.parseInt(split[1]));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
WorldEdit.logger.warning("Error while reading CUI init message: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the status of CUI support.
|
||||||
|
*
|
||||||
|
* @return true if CUI is enabled
|
||||||
|
*/
|
||||||
|
public boolean hasCUISupport() {
|
||||||
|
return hasCUISupport;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the status of CUI support.
|
||||||
|
*
|
||||||
|
* @param support true if CUI is enabled
|
||||||
|
*/
|
||||||
|
public void setCUISupport(boolean support) {
|
||||||
|
hasCUISupport = support;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the client's CUI protocol version
|
||||||
|
*
|
||||||
|
* @return the CUI version
|
||||||
|
*/
|
||||||
|
public int getCUIVersion() {
|
||||||
|
return cuiVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the client's CUI protocol version
|
||||||
|
*
|
||||||
|
* @param cuiVersion the CUI version
|
||||||
|
*/
|
||||||
|
public void setCUIVersion(int cuiVersion) {
|
||||||
|
this.cuiVersion = cuiVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect date from a user's input.
|
||||||
|
*
|
||||||
|
* @param input the input to parse
|
||||||
|
* @return a date
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Calendar detectDate(String input) {
|
||||||
|
checkNotNull(input);
|
||||||
|
|
||||||
|
Time.setTimeZone(getTimeZone());
|
||||||
|
Options opt = new com.sk89q.jchronic.Options();
|
||||||
|
opt.setNow(Calendar.getInstance(getTimeZone()));
|
||||||
|
Span date = Chronic.parse(input, opt);
|
||||||
|
if (date == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return date.getBeginCalendar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #createEditSession(Player)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public EditSession createEditSession(LocalPlayer player) {
|
||||||
|
return createEditSession((Player) player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new edit session.
|
||||||
|
*
|
||||||
|
* @param player the player
|
||||||
|
* @return an edit session
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public EditSession createEditSession(Player player) {
|
||||||
|
checkNotNull(player);
|
||||||
|
|
||||||
|
BlockBag blockBag = getBlockBag(player);
|
||||||
|
|
||||||
|
// Create an edit session
|
||||||
|
EditSession editSession = WorldEdit.getInstance().getEditSessionFactory()
|
||||||
|
.getEditSession(player.isPlayer() ? player.getWorld() : null,
|
||||||
|
getBlockChangeLimit(), blockBag, player);
|
||||||
|
editSession.setFastMode(fastMode);
|
||||||
|
Request.request().setEditSession(editSession);
|
||||||
|
editSession.setMask(mask);
|
||||||
|
|
||||||
|
return editSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the session has fast mode enabled.
|
||||||
|
*
|
||||||
|
* @return true if fast mode is enabled
|
||||||
|
*/
|
||||||
|
public boolean hasFastMode() {
|
||||||
|
return fastMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set fast mode.
|
||||||
|
*
|
||||||
|
* @param fastMode true if fast mode is enabled
|
||||||
|
*/
|
||||||
|
public void setFastMode(boolean fastMode) {
|
||||||
|
this.fastMode = fastMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the mask.
|
||||||
|
*
|
||||||
|
* @return mask, may be null
|
||||||
|
*/
|
||||||
|
public Mask getMask() {
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a mask.
|
||||||
|
*
|
||||||
|
* @param mask mask or null
|
||||||
|
*/
|
||||||
|
public void setMask(Mask mask) {
|
||||||
|
this.mask = mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a mask.
|
||||||
|
*
|
||||||
|
* @param mask mask or null
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public void setMask(com.sk89q.worldedit.masks.Mask mask) {
|
||||||
|
setMask(mask != null ? Masks.wrap(mask) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> inject() {
|
||||||
|
return LocalSession.class;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* WorldEdit, a Minecraft world manipulation toolkit
|
||||||
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||||
|
* Copyright (C) WorldEdit team and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.history.change;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
|
import com.sk89q.worldedit.entity.BaseEntity;
|
||||||
|
import com.sk89q.worldedit.entity.Entity;
|
||||||
|
import com.sk89q.worldedit.history.UndoContext;
|
||||||
|
import com.sk89q.worldedit.util.Location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs the creation of an entity and removes the entity upon undo.
|
||||||
|
*/
|
||||||
|
public class EntityCreate implements Change {
|
||||||
|
|
||||||
|
public final Location location;
|
||||||
|
public final BaseEntity state;
|
||||||
|
public Entity entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance.
|
||||||
|
*
|
||||||
|
* @param location the location
|
||||||
|
* @param state the state of the created entity
|
||||||
|
* @param entity the entity that was created
|
||||||
|
*/
|
||||||
|
public EntityCreate(Location location, BaseEntity state, Entity entity) {
|
||||||
|
checkNotNull(location);
|
||||||
|
checkNotNull(state);
|
||||||
|
checkNotNull(entity);
|
||||||
|
this.location = location;
|
||||||
|
this.state = state;
|
||||||
|
this.entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undo(UndoContext context) throws WorldEditException {
|
||||||
|
if (entity != null) {
|
||||||
|
entity.remove();
|
||||||
|
entity = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void redo(UndoContext context) throws WorldEditException {
|
||||||
|
entity = checkNotNull(context.getExtent()).createEntity(location, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> inject() {
|
||||||
|
return EntityCreate.class;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* WorldEdit, a Minecraft world manipulation toolkit
|
||||||
|
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||||
|
* Copyright (C) WorldEdit team and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.history.change;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
|
import com.sk89q.worldedit.entity.BaseEntity;
|
||||||
|
import com.sk89q.worldedit.entity.Entity;
|
||||||
|
import com.sk89q.worldedit.history.UndoContext;
|
||||||
|
import com.sk89q.worldedit.util.Location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks the removal of an entity.
|
||||||
|
*/
|
||||||
|
public class EntityRemove implements Change {
|
||||||
|
|
||||||
|
public final Location location;
|
||||||
|
public final BaseEntity state;
|
||||||
|
public Entity entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance.
|
||||||
|
*
|
||||||
|
* @param location the location
|
||||||
|
* @param state the state of the created entity
|
||||||
|
*/
|
||||||
|
public EntityRemove(Location location, BaseEntity state) {
|
||||||
|
checkNotNull(location);
|
||||||
|
checkNotNull(state);
|
||||||
|
this.location = location;
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undo(UndoContext context) throws WorldEditException {
|
||||||
|
entity = checkNotNull(context.getExtent()).createEntity(location, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void redo(UndoContext context) throws WorldEditException {
|
||||||
|
if (entity != null) {
|
||||||
|
entity.remove();
|
||||||
|
entity = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> inject() {
|
||||||
|
return EntityRemove.class;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
name: FastAsyncWorldEdit
|
name: FastAsyncWorldEdit
|
||||||
main: com.boydti.fawe.bukkit.FaweBukkit
|
main: com.boydti.fawe.bukkit.FaweBukkit
|
||||||
version: 3.3.3
|
version: 3.3.4
|
||||||
description: Fast Async WorldEdit plugin
|
description: Fast Async WorldEdit plugin
|
||||||
authors: [Empire92]
|
authors: [Empire92]
|
||||||
loadbefore: [WorldEdit]
|
loadbefore: [WorldEdit]
|
||||||
|
Loading…
Reference in New Issue
Block a user