diff --git a/pom.xml b/pom.xml index 4ee21256..ec25141e 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ UTF-8 FastAsyncWorldEdit - 3.3.3 + 3.3.4 FastAsyncWorldEdit jar diff --git a/src/main/java/com/boydti/fawe/Fawe.java b/src/main/java/com/boydti/fawe/Fawe.java index 2c9b0d3b..9be6a33a 100644 --- a/src/main/java/com/boydti/fawe/Fawe.java +++ b/src/main/java/com/boydti/fawe/Fawe.java @@ -19,12 +19,14 @@ import com.boydti.fawe.command.WorldEditRegion; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.util.Lag; +import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MemUtil; import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.WEManager; import com.boydti.fawe.util.WESubscriber; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.command.SchematicCommands; 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.RecursiveVisitor; import com.sk89q.worldedit.function.visitor.RegionVisitor; - -/** - * Simplified overview: - * - * [ WorldEdit action] +import com.sk89q.worldedit.history.change.EntityCreate; +import com.sk89q.worldedit.history.change.EntityRemove; + [ WorldEdit action] * | * \|/ * [ EditSession ] - The change is processed (area restrictions, change limit, block type) @@ -148,6 +148,9 @@ public class Fawe { TaskManager.IMP = this.IMP.getTaskManager(); SetQueue.IMP.queue = this.IMP.getQueue(); + + // Delete old history + MainUtil.deleteDirectory(new File(IMP.getDirectory(), "history")); // Delayed setup TaskManager.IMP.later(new Runnable() { @@ -214,7 +217,9 @@ public class Fawe { RecursiveVisitor.inject(); RegionVisitor.inject(); CommandManager.inject(); - // DispatcherWrapper.inject(); + EntityCreate.inject(); + EntityRemove.inject(); + LocalSession.inject(); } private void setupMemoryListener() { diff --git a/src/main/java/com/boydti/fawe/config/Settings.java b/src/main/java/com/boydti/fawe/config/Settings.java index d3532002..4a3536a8 100644 --- a/src/main/java/com/boydti/fawe/config/Settings.java +++ b/src/main/java/com/boydti/fawe/config/Settings.java @@ -24,7 +24,8 @@ public class Settings { public static List WE_BLACKLIST = Arrays.asList("cs", ".s", "restore", "snapshot", "delchunks", "listchunks"); public static long MEM_FREE = 95; 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) { if (!file.exists()) { @@ -48,7 +49,8 @@ public class Settings { options.put("max-memory-percent", MEM_FREE); options.put("crash-mitigation", ENABLE_HARD_LIMIT); 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 node : options.entrySet()) { if (!config.contains(node.getKey())) { @@ -65,7 +67,8 @@ public class Settings { REQUIRE_SELECTION = config.getBoolean("require-selection-in-mask"); WE_BLACKLIST = config.getStringList("command-blacklist"); 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; } diff --git a/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java b/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java index 8443f82a..6091fe59 100644 --- a/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java +++ b/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java @@ -4,22 +4,29 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Iterator; -import java.util.UUID; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import com.boydti.fawe.Fawe; 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.IntTag; +import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.jnbt.NamedTag; +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.EntityCreate; +import com.sk89q.worldedit.history.change.EntityRemove; import com.sk89q.worldedit.history.changeset.ChangeSet; /** @@ -29,27 +36,67 @@ import com.sk89q.worldedit.history.changeset.ChangeSet; * - Minimal memory usage * - Slow */ -public class DiskStorageHistory implements ChangeSet { +public class DiskStorageHistory implements ChangeSet, FlushableChangeSet { private final File bdFile; - private final File nbtFile; - private final File anyFile; + private final File nbtfFile; + 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 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 oz; private final AtomicInteger size; - public DiskStorageHistory() { + public DiskStorageHistory(String name) { size = new AtomicInteger(); - UUID uuid = UUID.randomUUID(); - nbtFile = new File(Fawe.imp().getDirectory(), "history" + File.separator + uuid.toString() + ".nbt"); - bdFile = new File(Fawe.imp().getDirectory(), "history" + File.separator + uuid.toString() + ".bd"); - anyFile = new File(Fawe.imp().getDirectory(), "history" + File.separator + uuid.toString() + ".any"); + String base = "history" + File.separator + name; + File folder = new File(Fawe.imp().getDirectory(), base); + int i; + 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 @@ -62,66 +109,89 @@ public class DiskStorageHistory implements ChangeSet { } } + @Override public void flush() { try { if (osBD != null) { osBD.flush(); osBD.close(); + osBD = null; } - if (osANY != null) { - osANY.flush(); - osANY.close(); + if (osNBTF != null) { + osNBTFG.flush(); + osNBTF.close(); + osNBTF = null; + osNBTFG = null; } - if (osNBT != null) { - osNBT.close(); + if (osNBTT != null) { + osNBTTG.flush(); + osNBTT.close(); + osNBTT = null; + osNBTTG = null; } } catch (Exception e) { e.printStackTrace(); } } + + public void add(EntityCreate change) { + + } + + public void add(EntityRemove change) { + + } public void add(BlockChange change) { try { - 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 = (byte) (FaweCache.hasData(idfrom) ? ((idfrom << 4) + from.getData()) : (idfrom << 4)); - CompoundTag nbtFrom = FaweCache.hasData(idfrom) ? from.getNbtData() : null; - - int idTo = to.getId(); - int combinedTo = (byte) (FaweCache.hasData(idTo) ? ((idTo << 4) + to.getData()) : (idTo << 4)); - CompoundTag nbtTo = FaweCache.hasData(idTo) ? to.getNbtData() : null; - + 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); - - /* - * [header] - * [int x][int z] origin - * [contents] - * relative: short x,short z,unsigned byte y - * from: char - * to: char - */ + //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 value = ReflectionUtils.getMap(nbtFrom.getValue()); + value.put("x", new IntTag(x)); + value.put("y", new IntTag(y)); + value.put("z", new IntTag(z)); + NBTOutputStream nbtos = getNBTFOS(x, y, z); + nbtos.writeNamedTag(osNBTFI.getAndIncrement() + "", nbtFrom); + } + + if (nbtTo != null && MainUtil.isValidTag(nbtTo)) { + Map 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) { e.printStackTrace(); } @@ -136,22 +206,63 @@ public class DiskStorageHistory implements ChangeSet { osBD = new GZIPOutputStream(new FileOutputStream(bdFile), true); ox = x; oz = z; - osBD.write((ox) & 0xff); - osBD.write(((ox) >> 8) & 0xff); - osBD.write((oz) & 0xff); - osBD.write(((oz) >> 8) & 0xff); + osBD.write((byte) (ox >> 24)); + osBD.write((byte) (ox >> 16)); + osBD.write((byte) (ox >> 8)); + 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; } - private NBTOutputStream getNBTOS(int x, int y, int z) throws IOException { - if (osNBT != null) { - return osNBT; + private NBTOutputStream getNBTFOS(int x, int y, int z) throws IOException { + if (osNBTF != null) { + return osNBTF; } - nbtFile.getParentFile().mkdirs(); - nbtFile.createNewFile(); - // osNBT = new FileOutputStream(bdFile); - // TODO FIXME - return osNBT; + nbtfFile.getParentFile().mkdirs(); + nbtfFile.createNewFile(); + osNBTFG = new GZIPOutputStream(new FileOutputStream(nbtfFile), true); + osNBTF = new NBTOutputStream(osNBTFG); + 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") @@ -159,11 +270,30 @@ public class DiskStorageHistory implements ChangeSet { flush(); try { 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)); - gis.skip(4); + gis.skip(8); return new Iterator() { - + + private CompoundTag lastFrom = read(nbtf); + private CompoundTag lastTo = read(nbtt); 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() { try { @@ -173,9 +303,23 @@ public class DiskStorageHistory implements ChangeSet { 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 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 to2 = gis.read(); BaseBlock to = new BaseBlock(((to2 << 4) + (to1 >> 4)), (to1 & 0xf)); + if (lastTo != null && FaweCache.hasNBT(to.getId())) { + Map 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); return dir ? new BlockChange(position, to, from) : new BlockChange(position, from, to); } catch (Exception e) { @@ -190,6 +334,12 @@ public class DiskStorageHistory implements ChangeSet { } try { gis.close(); + if (nbtf != null) { + nbtf.close(); + } + if (nbtt != null) { + nbtt.close(); + } } catch (IOException e) { e.printStackTrace(); } diff --git a/src/main/java/com/boydti/fawe/object/changeset/FlushableChangeSet.java b/src/main/java/com/boydti/fawe/object/changeset/FlushableChangeSet.java new file mode 100644 index 00000000..652d15c9 --- /dev/null +++ b/src/main/java/com/boydti/fawe/object/changeset/FlushableChangeSet.java @@ -0,0 +1,5 @@ +package com.boydti.fawe.object.changeset; + +public interface FlushableChangeSet { + public void flush(); +} diff --git a/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java b/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java index 39a5231b..42905ba0 100644 --- a/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java +++ b/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java @@ -1,7 +1,26 @@ 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.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.changeset.ChangeSet; @@ -11,30 +30,225 @@ import com.sk89q.worldedit.history.changeset.ChangeSet; * - High CPU usage * - Low memory usage */ -public class MemoryOptimizedHistory implements ChangeSet { +public class MemoryOptimizedHistory implements ChangeSet, FlushableChangeSet { + private ArrayDeque fromTags; + private ArrayDeque toTags; + + private byte[] ids; + private ByteArrayOutputStream idsStream; + private GZIPOutputStream idsStreamZip; + + private ArrayDeque entities; + + int ox; + int oz; + + private final AtomicInteger size; + + public MemoryOptimizedHistory() { + size = new AtomicInteger(); + } + @Override - public void add(Change paramChange) { - // TODO Auto-generated method stub - + public void add(Change arg) { + 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 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 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 getIterator(final boolean dir) { + flush(); + try { + Iterator idsIterator; + Iterator 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() { + + private final Iterator lastFromIter = fromTags != null ? fromTags.iterator() : null; + private final Iterator lastToIter = toTags != null ? toTags.iterator() : null; + private CompoundTag lastFrom = read(lastFromIter); + private CompoundTag lastTo = read(lastToIter); + private Change last = read(); + + public CompoundTag read(Iterator 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 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 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().iterator(); + } + @Override public Iterator backwardIterator() { - // TODO Auto-generated method stub - return null; + return getIterator(false); } @Override public Iterator forwardIterator() { - // TODO Auto-generated method stub - return null; + return getIterator(false); } @Override public int size() { - // TODO Auto-generated method stub - return 0; + return size.get(); + } + + @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(); + } + } } } diff --git a/src/main/java/com/boydti/fawe/util/FileUtil.java b/src/main/java/com/boydti/fawe/util/FileUtil.java deleted file mode 100644 index 96a6edf4..00000000 --- a/src/main/java/com/boydti/fawe/util/FileUtil.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.boydti.fawe.util; - -public class FileUtil { - -} diff --git a/src/main/java/com/boydti/fawe/util/MainUtil.java b/src/main/java/com/boydti/fawe/util/MainUtil.java index f33c77be..7cde68a3 100644 --- a/src/main/java/com/boydti/fawe/util/MainUtil.java +++ b/src/main/java/com/boydti/fawe/util/MainUtil.java @@ -1,11 +1,18 @@ package com.boydti.fawe.util; +import java.io.File; +import java.util.Map.Entry; + import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.entity.Player; import com.boydti.fawe.Fawe; 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 { /* @@ -29,4 +36,39 @@ public class MainUtil { } 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 entry : ((CompoundTag) tag).getValue().entrySet()) { + if (!isValidTag(entry.getValue())) { + return false; + } + } + } + return true; + } } diff --git a/src/main/java/com/boydti/fawe/util/ReflectionUtils.java b/src/main/java/com/boydti/fawe/util/ReflectionUtils.java index 8fae979c..c796afbd 100644 --- a/src/main/java/com/boydti/fawe/util/ReflectionUtils.java +++ b/src/main/java/com/boydti/fawe/util/ReflectionUtils.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import org.bukkit.Bukkit; import org.bukkit.Server; @@ -55,6 +56,18 @@ public class ReflectionUtils { } catch (final Exception ignored) {} } } + + public static Map getMap(Map map) { + try { + Class clazz = map.getClass(); + Field m = clazz.getDeclaredField("m"); + m.setAccessible(true); + return (Map) m.get(map); + } catch (Throwable e) { + e.printStackTrace(); + return map; + } + } public static Class getNmsClass(final String name) { final String className = "net.minecraft.server." + getVersion() + "." + name; diff --git a/src/main/java/com/sk89q/worldedit/EditSession.java b/src/main/java/com/sk89q/worldedit/EditSession.java index 4312d966..4dc232cb 100644 --- a/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/src/main/java/com/sk89q/worldedit/EditSession.java @@ -41,10 +41,14 @@ import javax.annotation.Nullable; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.BBC; +import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.EditSessionWrapper; import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.NullChangeSet; 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.MemoryOptimizedHistory; import com.boydti.fawe.object.extent.FastWorldEditExtent; import com.boydti.fawe.object.extent.NullExtent; import com.boydti.fawe.object.extent.ProcessedWEExtent; @@ -215,7 +219,6 @@ public class EditSession implements Extent { this.world = world; this.wrapper = Fawe.imp().getEditSessionWrapper(this); // this.changeSet = new BlockOptimizedHistory(); - this.changeSet = new DiskStorageHistory(); // Invalid; return null extent if (world == null) { @@ -223,6 +226,7 @@ public class EditSession implements Extent { this.bypassReorderHistory = extent; this.bypassHistory = extent; this.bypassNone = extent; + this.changeSet = new NullChangeSet(); return; } final Actor actor = event.getActor(); @@ -237,9 +241,11 @@ public class EditSession implements Extent { this.bypassReorderHistory = extent; this.bypassHistory = extent; this.bypassNone = extent; + this.changeSet = new NullChangeSet(); return; } - + + this.changeSet = Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(actor.getUniqueId().toString()) : Settings.COMPRESS_HISTORY ? new MemoryOptimizedHistory() : new CPUOptimizedHistory(); Extent extent; final String name = actor.getName(); final FawePlayer fp = FawePlayer.wrap(name); diff --git a/src/main/java/com/sk89q/worldedit/LocalSession.java b/src/main/java/com/sk89q/worldedit/LocalSession.java new file mode 100644 index 00000000..0f23d63e --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -0,0 +1,931 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * 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 . + */ + +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 history = new LinkedList(); + 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 tools = new HashMap(); + 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. + * + *

{@link #setConfiguration(LocalConfiguration)} should be called + * later with configuration.

+ */ + 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. + * + *

Pass {@code null} to clear the clipboard.

+ * + * @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; + } +} \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldedit/history/change/EntityCreate.java b/src/main/java/com/sk89q/worldedit/history/change/EntityCreate.java new file mode 100644 index 00000000..71efd823 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/history/change/EntityCreate.java @@ -0,0 +1,71 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * 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 . + */ + +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; + } +} \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldedit/history/change/EntityRemove.java b/src/main/java/com/sk89q/worldedit/history/change/EntityRemove.java new file mode 100644 index 00000000..0ac942e3 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/history/change/EntityRemove.java @@ -0,0 +1,68 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * 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 . + */ + +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; + } +} \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 0309fb4b..c38f7c85 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ name: FastAsyncWorldEdit main: com.boydti.fawe.bukkit.FaweBukkit -version: 3.3.3 +version: 3.3.4 description: Fast Async WorldEdit plugin authors: [Empire92] loadbefore: [WorldEdit]