diff --git a/build.gradle b/build.gradle index f81729b5..3ec1aaf5 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ subprojects { exclude(module: 'bukkit-classloader-check') } compile 'com.sk89q:worldguard:6.0.0-SNAPSHOT' - compile 'com.intellectualcrafters.plot:plotsquared:3.3.1' + compile 'com.plotsquared:PlotSquared:3.4.1-SNAPSHOT' } repositories { diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/BukkitPlayer.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/BukkitPlayer.java index b277fd53..104dc54c 100644 --- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/BukkitPlayer.java +++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/BukkitPlayer.java @@ -3,6 +3,7 @@ package com.boydti.fawe.bukkit; import com.boydti.fawe.Fawe; import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.wrappers.PlayerWrapper; import java.lang.reflect.Method; import java.util.UUID; import org.bukkit.Bukkit; @@ -92,7 +93,7 @@ public class BukkitPlayer extends FawePlayer { @Override public com.sk89q.worldedit.entity.Player getPlayer() { - return Fawe. imp().getWorldEditPlugin().wrapPlayer(this.parent); + return PlayerWrapper.wrap(Fawe. imp().getWorldEditPlugin().wrapPlayer(this.parent)); } } diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java index a819bf0c..a33dd646 100644 --- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java +++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java @@ -8,6 +8,7 @@ import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.TaskManager; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.bukkit.entity.BukkitEntity; import com.sk89q.worldedit.world.biome.BaseBiome; import java.io.File; import java.lang.reflect.Field; diff --git a/bukkit0/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java b/bukkit0/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java index 95343d3d..c50d535a 100644 --- a/bukkit0/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java +++ b/bukkit0/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java @@ -399,7 +399,7 @@ public class Sniper { FawePlayer fp = FawePlayer.wrap(getPlayer()); LocalSession session = fp.getSession(); baseQueue.enqueue(); - session.remember(changeSet.toEditSession(fp.getPlayer())); + session.remember(changeSet.toEditSession(fp)); changeQueue.flush(); com.sk89q.worldedit.world.World worldEditWorld = fp.getWorld(); changeQueue.setChangeSet(Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(worldEditWorld, fp.getUUID()) : new MemoryOptimizedHistory(worldEditWorld)); diff --git a/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java index 9641373c..737767f7 100644 --- a/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java +++ b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java @@ -131,7 +131,7 @@ public class BukkitQueue_1_10 extends BukkitQueue_0 entities = new HashSet<>(); - List[] entitieSlices = playerChunk.chunk.getEntitySlices(); + List[] entitieSlices = nmsChunk.getEntitySlices(); for (List slice : entitieSlices) { if (slice == null) { continue; @@ -152,7 +152,7 @@ public class BukkitQueue_1_10 extends BukkitQueue_0 - * - The EditSession can be used from another thread
- * - FAWE will handle - * @see com.boydti.fawe.object.FawePlayer#wrap(Object) - * @param player - * @return - */ - public static EditSession getNewEditSession(@Nonnull FawePlayer player) { - if (player == null) { - throw new IllegalArgumentException("Player may not be null"); - } - return player.getNewEditSession(); - } - - /** - * Get a new non-player EditSession - * @see #getWorld(String) + * Offers a lot of options for building an EditSession + * @see com.boydti.fawe.util.EditSessionBuilder * @param world - * @return + * @return A new EditSessionBuilder */ - public static EditSession getNewEditSession(World world) { - return WorldEdit.getInstance().getEditSessionFactory().getEditSession(world, -1); + public static EditSessionBuilder getEditSessionBuilder(World world) { + return new EditSessionBuilder(world); } /** @@ -124,7 +111,7 @@ public class FaweAPI { public static World getWorld(String worldName) { for (World current : WorldEdit.getInstance().getServer().getWorlds()) { if (Fawe.imp().getWorldName(current).equals(worldName)) { - return current; + return WorldWrapper.wrap((AbstractWorld) current); } } return null; @@ -325,7 +312,7 @@ public class FaweAPI { /** * The DiskStorageHistory class is what FAWE uses to represent the undo on disk. - * @see com.boydti.fawe.object.changeset.DiskStorageHistory#toEditSession(com.sk89q.worldedit.entity.Player) + * @see com.boydti.fawe.object.changeset.DiskStorageHistory#toEditSession(com.boydti.fawe.object.FawePlayer) * @param world * @param uuid * @param index @@ -403,6 +390,7 @@ public class FaweAPI { } /** + * @deprecated Since I haven't finished it yet * If a schematic is too large to be pasted normally
* - Skips any block history * - Ignores nbt @@ -421,6 +409,7 @@ public class FaweAPI { } /** + * @deprecated Since I haven't finished it yet * If a schematic is too large to be pasted normally
* - Skips any block history * - Ignores some block data @@ -449,9 +438,6 @@ public class FaweAPI { final int y_offset = loc.y + IntTag.class.cast(tagMap.get("WEOffsetY")).getValue(); final int z_offset = loc.z + IntTag.class.cast(tagMap.get("WEOffsetZ")).getValue(); - tagMap = null; - tag = null; - FaweQueue queue = SetQueue.IMP.getNewQueue(loc.world, true, true); for (int y = 0; y < height; y++) { @@ -477,9 +463,6 @@ public class FaweAPI { } queue.enqueue(); - - ids = null; - datas = null; } /** @@ -513,4 +496,26 @@ public class FaweAPI { public static BBC[] getTranslations() { return BBC.values(); } + + /** + * @deprecated + * @see #getEditSessionBuilder(com.sk89q.worldedit.world.World) + */ + @Deprecated + public static EditSession getNewEditSession(@Nonnull FawePlayer player) { + if (player == null) { + throw new IllegalArgumentException("Player may not be null"); + } + return player.getNewEditSession(); + } + + /** + * @deprecated + * @see #getEditSessionBuilder(com.sk89q.worldedit.world.World) + */ + @Deprecated + public static EditSession getNewEditSession(World world) { + return WorldEdit.getInstance().getEditSessionFactory().getEditSession(world, -1); + } + } diff --git a/core/src/main/java/com/boydti/fawe/FaweCache.java b/core/src/main/java/com/boydti/fawe/FaweCache.java index 8f99043e..4c3b1ed5 100644 --- a/core/src/main/java/com/boydti/fawe/FaweCache.java +++ b/core/src/main/java/com/boydti/fawe/FaweCache.java @@ -5,7 +5,9 @@ import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.ByteTag; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.EndTag; import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntArrayTag; import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.LongTag; @@ -14,6 +16,7 @@ import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.CuboidClipboard; import com.sk89q.worldedit.blocks.BaseBlock; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -332,6 +335,10 @@ public class FaweCache { return new ByteArrayTag(value); } + public static IntArrayTag asTag(int[] value) { + return new IntArrayTag(value); + } + public static StringTag asTag(String value) { return new StringTag(value); } @@ -369,7 +376,22 @@ public class FaweCache { return asTag((byte[]) value); } else if (value instanceof Tag) { return (Tag) value; + } else if (value == null) { + return null; } else { + Class clazz = value.getClass(); + if (clazz.getName().startsWith("com.intellectualcrafters.jnbt")) { + try { + if (clazz.getName().equals("com.intellectualcrafters.jnbt.EndTag")) { + return new EndTag(); + } + Field field = clazz.getDeclaredField("value"); + field.setAccessible(true); + return asTag(field.get(value)); + } catch (Throwable e) { + e.printStackTrace(); + } + } return null; } } diff --git a/core/src/main/java/com/boydti/fawe/command/Cancel.java b/core/src/main/java/com/boydti/fawe/command/Cancel.java index 25ec15d1..704456db 100644 --- a/core/src/main/java/com/boydti/fawe/command/Cancel.java +++ b/core/src/main/java/com/boydti/fawe/command/Cancel.java @@ -6,7 +6,6 @@ import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.util.SetQueue; import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.extension.platform.Actor; import java.util.List; import java.util.Set; import java.util.UUID; @@ -28,11 +27,8 @@ public class Cancel extends FaweCommand { for (FaweQueue queue : queues) { Set sessions = queue.getEditSessions(); for (EditSession session : sessions) { - Actor actor = session.getActor(); - if (actor == null) { - continue; - } - if (uuid.equals(actor.getUniqueId())) { + FawePlayer currentPlayer = session.getPlayer(); + if (currentPlayer == player) { if (session.cancel()) { cancelled++; } diff --git a/core/src/main/java/com/boydti/fawe/config/Settings.java b/core/src/main/java/com/boydti/fawe/config/Settings.java index 2a2a8559..fd790abe 100644 --- a/core/src/main/java/com/boydti/fawe/config/Settings.java +++ b/core/src/main/java/com/boydti/fawe/config/Settings.java @@ -41,7 +41,7 @@ public class Settings { // Maybe confusing? // - `compression: false` just uses cheaper compression, but still compresses - public static int COMPRESSION_LEVEL = 0; + public static int COMPRESSION_LEVEL = 1; public static boolean COMBINE_HISTORY_STAGE = false; public static int PARALLEL_THREADS = 1; @@ -96,11 +96,12 @@ public class Settings { options.put("clipboard.use-disk", STORE_CLIPBOARD_ON_DISK); options.put("clipboard.delete-after-days", DELETE_CLIPBOARD_AFTER_DAYS); options.put("history.use-disk", STORE_HISTORY_ON_DISK); - options.put("history.compress", false); options.put("history.chunk-wait-ms", CHUNK_WAIT); options.put("history.delete-after-days", DELETE_HISTORY_AFTER_DAYS); options.put("history.delete-on-logout", CLEAN_HISTORY_ON_LOGOUT); options.put("history.enable-for-console", CONSOLE_HISTORY); + options.put("history.compression-level", config.getBoolean("history.compress", false) ? 4 : COMPRESSION_LEVEL); + options.put("history.compress", null); options.put("region-restrictions", REGION_RESTRICTIONS); options.put("queue.extra-time-ms", ALLOCATE); options.put("queue.progress.display", DISPLAY_PROGRESS); @@ -144,7 +145,7 @@ public class Settings { ENABLE_HARD_LIMIT = config.getBoolean("crash-mitigation"); REGION_RESTRICTIONS = config.getBoolean("region-restrictions"); METRICS = config.getBoolean("metrics"); - COMPRESSION_LEVEL = config.getInt("history.compression-level", config.getBoolean("history.compress") ? 1 : 0); + COMPRESSION_LEVEL = config.getInt("history.compression-level"); DELETE_HISTORY_AFTER_DAYS = config.getInt("history.delete-after-days"); CLEAN_HISTORY_ON_LOGOUT = config.getBoolean("history.delete-on-logout"); CHUNK_WAIT = config.getInt("history.chunk-wait-ms"); diff --git a/core/src/main/java/com/boydti/fawe/object/FaweInputStream.java b/core/src/main/java/com/boydti/fawe/object/FaweInputStream.java new file mode 100644 index 00000000..82cff181 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/FaweInputStream.java @@ -0,0 +1,86 @@ +package com.boydti.fawe.object; + +import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.jnbt.NamedTag; +import java.io.IOException; +import java.io.InputStream; + +public class FaweInputStream extends InputStream { + + private final InputStream parent; + + public FaweInputStream(InputStream parent) { + this.parent = parent; + } + + public InputStream getParent() { + return parent; + } + + @Override + public int read() throws IOException { + return parent.read(); + } + + public long readLong() throws IOException { + return (long) + (read() << 64) + + (read() << 56) + + (read() << 48) + + (read() << 36) + + (read() << 24) + + (read() << 16) + + (read() << 8) + + (read()); + } + + public int readInt() throws IOException { + return (int) + (read() << 24) + + (read() << 16) + + (read() << 8) + + (read()); + } + + public short readShort() throws IOException { + return (short) ( + (read() << 8) + + read()); + } + + public byte readByte() throws IOException { + return (byte) read(); + } + + public int readUnsignedByte() throws IOException { + return read(); + } + + public int readUnsignedShort() throws IOException { + return (int) readShort() & 0xFFFF; + } + + public int readMedium() throws IOException { + return (int) ( + (read() << 16) + + (read() << 8) + + read()); + } + + private NBTInputStream nbtIn; + + public NamedTag readNBT() throws IOException { + if (nbtIn == null) { + nbtIn = new NBTInputStream(parent); + } + return nbtIn.readNamedTag(); + } + + @Override + public void close() throws IOException { + if (nbtIn != null) { + nbtIn.close(); + } + parent.close(); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/FaweLimit.java b/core/src/main/java/com/boydti/fawe/object/FaweLimit.java index b7e4fe76..817b3495 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweLimit.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweLimit.java @@ -17,7 +17,32 @@ public class FaweLimit { public static FaweLimit MAX; static { - MAX = new FaweLimit(); + MAX = new FaweLimit() { + @Override + public boolean MAX_CHANGES() { + return true; + } + @Override + public boolean MAX_BLOCKSTATES() { + return true; + } + @Override + public boolean MAX_CHECKS() { + return true; + } + @Override + public boolean MAX_ENTITIES() { + return true; + } + @Override + public boolean MAX_FAILS() { + return true; + } + @Override + public boolean MAX_ITERATIONS() { + return true; + } + }; MAX.MAX_CHANGES = Integer.MAX_VALUE; MAX.MAX_FAILS = Integer.MAX_VALUE; MAX.MAX_CHECKS = Integer.MAX_VALUE; @@ -26,6 +51,30 @@ public class FaweLimit { MAX.MAX_ENTITIES = Integer.MAX_VALUE; } + public boolean MAX_CHANGES() { + return MAX_CHANGES-- > 0; + } + + public boolean MAX_FAILS() { + return MAX_FAILS-- > 0; + } + + public boolean MAX_CHECKS() { + return MAX_CHECKS-- > 0; + } + + public boolean MAX_ITERATIONS() { + return MAX_ITERATIONS-- > 0; + } + + public boolean MAX_BLOCKSTATES() { + return MAX_BLOCKSTATES-- > 0; + } + + public boolean MAX_ENTITIES() { + return MAX_ENTITIES-- > 0; + } + public boolean load(ConfigurationSection section, FaweLimit defaultLimit, boolean save) { this.MAX_CHANGES = section.getInt("max-changes", defaultLimit == null ? MAX_CHANGES : defaultLimit.MAX_CHANGES); this.MAX_FAILS = section.getInt("max-fails", defaultLimit == null ? MAX_FAILS : defaultLimit.MAX_FAILS); diff --git a/core/src/main/java/com/boydti/fawe/object/FaweOutputStream.java b/core/src/main/java/com/boydti/fawe/object/FaweOutputStream.java new file mode 100644 index 00000000..faf5868a --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/FaweOutputStream.java @@ -0,0 +1,76 @@ +package com.boydti.fawe.object; + +import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.jnbt.Tag; +import java.io.IOException; +import java.io.OutputStream; + +public class FaweOutputStream extends OutputStream { + + private final OutputStream parent; + + public FaweOutputStream(OutputStream parent) { + this.parent = parent; + } + + public OutputStream getParent() { + return parent; + } + + @Override + public void write(int b) throws IOException { + parent.write(b); + } + + public void write(int b, int amount) throws IOException { + for (int i = 0; i < amount; i++) { + write(b); + } + } + + public void writeLong(long l) throws IOException { + write((byte) (l >>> 64)); + write((byte) (l >>> 56)); + write((byte) (l >>> 48)); + write((byte) (l >>> 36)); + write((byte) (l >>> 24)); + write((byte) (l >>> 16)); + write((byte) (l >>> 8)); + write((byte) (l)); + } + + public void writeInt(int i) throws IOException { + write((byte) (i >>> 24)); + write((byte) (i >>> 16)); + write((byte) (i >>> 8)); + write((byte) (i)); + } + + public void writeShort(short s) throws IOException { + write((byte) (s >>> 8)); + write((byte) (s)); + } + + public void writeMedium(int m) throws IOException { + write((byte) (m >>> 16)); + write((byte) (m >>> 8)); + write((byte) (m)); + } + + private NBTOutputStream nbtOut; + + public void writeNBT(String name, Tag tag) throws IOException { + if (nbtOut == null) { + nbtOut = new NBTOutputStream(parent); + } + nbtOut.writeNamedTag(name,tag); + } + + @Override + public void close() throws IOException { + if (nbtOut != null) { + nbtOut.close(); + } + parent.close(); + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java index 6fa5a899..c26ed8a8 100644 --- a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java +++ b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java @@ -178,7 +178,7 @@ public abstract class FawePlayer { for (int i = editIds.size() - 1; i >= 0; i--) { int index = editIds.get(i); FaweStreamChangeSet set = new DiskStorageHistory(world, uuid, index); - EditSession edit = set.toEditSession(getPlayer()); + EditSession edit = set.toEditSession(FawePlayer.this); if (world.equals(getWorld())) { session.remember(edit, false, false); } else { diff --git a/core/src/main/java/com/boydti/fawe/object/FaweQueue.java b/core/src/main/java/com/boydti/fawe/object/FaweQueue.java index 430a4c72..7474e9c7 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweQueue.java @@ -200,6 +200,7 @@ public abstract class FaweQueue { if (Fawe.get().isMainThread()) { SetQueue.IMP.flush(this); } else { + enqueue(); final AtomicBoolean running = new AtomicBoolean(true); addNotifyTask(new Runnable() { @Override diff --git a/core/src/main/java/com/boydti/fawe/object/HistoryExtent.java b/core/src/main/java/com/boydti/fawe/object/HistoryExtent.java index b6847010..0bb504f9 100644 --- a/core/src/main/java/com/boydti/fawe/object/HistoryExtent.java +++ b/core/src/main/java/com/boydti/fawe/object/HistoryExtent.java @@ -26,9 +26,8 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class HistoryExtent extends AbstractDelegateExtent { - private com.boydti.fawe.object.changeset.FaweChangeSet changeSet; + private FaweChangeSet changeSet; private final FaweQueue queue; - private final FaweLimit limit; private final EditSession session; /** @@ -37,15 +36,18 @@ public class HistoryExtent extends AbstractDelegateExtent { * @param extent the extent * @param changeSet the change set */ - public HistoryExtent(final EditSession session, FaweLimit limit, final Extent extent, final FaweChangeSet changeSet, FaweQueue queue) { + public HistoryExtent(final EditSession session, final Extent extent, final FaweChangeSet changeSet, FaweQueue queue) { super(extent); checkNotNull(changeSet); - this.limit = limit; this.queue = queue; this.changeSet = changeSet; this.session = session; } + public FaweChangeSet getChangeSet() { + return changeSet; + } + public void setChangeSet(FaweChangeSet fcs) { this.changeSet = fcs; } diff --git a/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java b/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java index 567b8b9c..d9a67dff 100644 --- a/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java +++ b/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java @@ -13,42 +13,42 @@ public class NullChangeSet extends FaweChangeSet { } @Override - public boolean flush() { + public final boolean flush() { return false; } @Override - public void add(int x, int y, int z, int combinedFrom, int combinedTo) { + public final void add(int x, int y, int z, int combinedFrom, int combinedTo) { } @Override - public void addTileCreate(CompoundTag tag) { + public final void addTileCreate(CompoundTag tag) { } @Override - public void addTileRemove(CompoundTag tag) { + public final void addTileRemove(CompoundTag tag) { } @Override - public void addEntityRemove(CompoundTag tag) { + public final void addEntityRemove(CompoundTag tag) { } @Override - public void addEntityCreate(CompoundTag tag) { + public final void addEntityCreate(CompoundTag tag) { } @Override - public Iterator getIterator(boolean undo) { + public final Iterator getIterator(boolean undo) { return new ArrayList().iterator(); } @Override - public int size() { + public final int size() { return 0; } } diff --git a/core/src/main/java/com/boydti/fawe/object/RegionWrapper.java b/core/src/main/java/com/boydti/fawe/object/RegionWrapper.java index 1a193568..0e90b7f3 100644 --- a/core/src/main/java/com/boydti/fawe/object/RegionWrapper.java +++ b/core/src/main/java/com/boydti/fawe/object/RegionWrapper.java @@ -8,6 +8,10 @@ public class RegionWrapper { public int minZ; public int maxZ; + public static RegionWrapper GLOBAL() { + return new RegionWrapper(Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE); + } + public RegionWrapper(final int minX, final int maxX, final int minZ, final int maxZ) { this.maxX = maxX; this.minX = minX; @@ -82,6 +86,10 @@ public class RegionWrapper { return new Vector(this.maxX, 255, this.maxZ); } + public boolean isGlobal() { + return minX == Integer.MIN_VALUE && minZ == Integer.MIN_VALUE && maxX == Integer.MAX_VALUE && maxZ == Integer.MAX_VALUE; + } + public boolean contains(RegionWrapper current) { return current.minX >= minX && current.maxX <= maxX && current.minZ >= minZ && current.maxZ <= maxZ; } diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java b/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java index 63386d28..7e01ffe6 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java @@ -2,6 +2,7 @@ package com.boydti.fawe.object.changeset; import com.boydti.fawe.Fawe; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.FaweInputStream; import com.boydti.fawe.object.IntegerPair; import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.util.MainUtil; @@ -17,9 +18,6 @@ import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import java.util.UUID; -import net.jpountz.lz4.LZ4Compressor; -import net.jpountz.lz4.LZ4Factory; -import net.jpountz.lz4.LZ4InputStream; /** * Store the change on disk @@ -163,6 +161,9 @@ public class DiskStorageHistory extends FaweStreamChangeSet { bdFile.getParentFile().mkdirs(); bdFile.createNewFile(); osBD = getCompressedOS(new FileOutputStream(bdFile)); + // Mode + osBD.write((byte) MODE); + // Origin setOrigin(x, z); osBD.write((byte) (x >> 24)); osBD.write((byte) (x >> 16)); @@ -224,7 +225,10 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!bdFile.exists()) { return null; } - InputStream is = getCompressedIS(new FileInputStream(bdFile)); + InputStream is = MainUtil.getCompressedIS(new FileInputStream(bdFile)); + // skip mode + is.skip(1); + // origin int x = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read() << 0)); int z = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read() << 0)); setOrigin(x, z); @@ -236,7 +240,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!enttFile.exists()) { return null; } - return new NBTInputStream(getCompressedIS(new FileInputStream(enttFile))); + return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(enttFile))); } @Override @@ -244,7 +248,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!entfFile.exists()) { return null; } - return new NBTInputStream(getCompressedIS(new FileInputStream(entfFile))); + return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(entfFile))); } @Override @@ -252,7 +256,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!nbttFile.exists()) { return null; } - return new NBTInputStream(getCompressedIS(new FileInputStream(nbttFile))); + return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbttFile))); } @Override @@ -260,7 +264,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!nbtfFile.exists()) { return null; } - return new NBTInputStream(getCompressedIS(new FileInputStream(nbtfFile))); + return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbtfFile))); } public DiskStorageSummary summarize(RegionWrapper requiredRegion, boolean shallow) { @@ -274,14 +278,10 @@ public class DiskStorageHistory extends FaweStreamChangeSet { return summary = new DiskStorageSummary(ox, oz); } try (FileInputStream fis = new FileInputStream(bdFile)) { - LZ4Factory factory = LZ4Factory.fastestInstance(); - LZ4Compressor compressor = factory.fastCompressor(); - final LZ4InputStream gis; - if (Settings.COMPRESSION_LEVEL > 0) { - gis = new LZ4InputStream(new LZ4InputStream(fis)); - } else { - gis = new LZ4InputStream(fis); - } + FaweInputStream gis = MainUtil.getCompressedIS(fis); + // skip mode + gis.skip(1); + // origin ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0)); oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0)); setOrigin(ox, oz); @@ -293,7 +293,8 @@ public class DiskStorageHistory extends FaweStreamChangeSet { } byte[] buffer = new byte[9]; int i = 0; - while (!shallow || gis.hasBytesAvailableInDecompressedBuffer(9)) { + while (!shallow && i < Settings.BUFFER_SIZE) { + i++; if (gis.read(buffer) == -1) { fis.close(); gis.close(); @@ -316,16 +317,11 @@ public class DiskStorageHistory extends FaweStreamChangeSet { int ox = getOriginX(); int oz = getOriginZ(); if (ox == 0 && oz == 0 && bdFile.exists()) { - try { - FileInputStream fis = new FileInputStream(bdFile); - LZ4Factory factory = LZ4Factory.fastestInstance(); - LZ4Compressor compressor = factory.fastCompressor(); - final InputStream gis; - if (Settings.COMPRESSION_LEVEL > 0) { - gis = new LZ4InputStream(new LZ4InputStream(fis)); - } else { - gis = new LZ4InputStream(fis); - } + try (FileInputStream fis = new FileInputStream(bdFile)) { + final InputStream gis = MainUtil.getCompressedIS(fis); + // skip mode + gis.skip(1); + // origin ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0)); oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0)); setOrigin(ox, oz); diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java b/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java index 25e23be5..884a5796 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java @@ -4,18 +4,17 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.object.BytePair; import com.boydti.fawe.object.FaweChunk; -import com.boydti.fawe.object.RunnableVal2; +import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.RunnableVal2; +import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.TaskManager; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.EditSessionFactory; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.history.change.BlockChange; import com.sk89q.worldedit.history.change.Change; import com.sk89q.worldedit.history.change.EntityCreate; @@ -70,12 +69,8 @@ public abstract class FaweChangeSet implements ChangeSet { public abstract void addEntityCreate(CompoundTag tag); public abstract Iterator getIterator(boolean redo); - public EditSession toEditSession(Player player) { - EditSessionFactory factory = WorldEdit.getInstance().getEditSessionFactory(); - EditSession edit = factory.getEditSession(world, -1, null, player); - edit.setChangeSet(this); - edit.dequeue(); - return edit; + public EditSession toEditSession(FawePlayer player) { + return new EditSessionBuilder(world).player(player).autoQueue(false).fastmode(true).checkMemory(false).changeSet(this).build(); } public void add(EntityCreate change) { diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/FaweStreamChangeSet.java b/core/src/main/java/com/boydti/fawe/object/changeset/FaweStreamChangeSet.java index 0e45a611..9431447b 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/FaweStreamChangeSet.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/FaweStreamChangeSet.java @@ -1,6 +1,7 @@ package com.boydti.fawe.object.changeset; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.FaweOutputStream; import com.boydti.fawe.object.change.MutableBlockChange; import com.boydti.fawe.object.change.MutableEntityChange; import com.boydti.fawe.object.change.MutableTileChange; @@ -15,15 +16,25 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Iterator; -import net.jpountz.lz4.LZ4Compressor; -import net.jpountz.lz4.LZ4Factory; -import net.jpountz.lz4.LZ4InputStream; -import net.jpountz.lz4.LZ4OutputStream; public abstract class FaweStreamChangeSet extends FaweChangeSet { + public static final int MODE = 3; + public static final int HEADER_SIZE = 9; + + private final int compression; + public FaweStreamChangeSet(World world) { + this(world, Settings.COMPRESSION_LEVEL); + } + + public FaweStreamChangeSet(World world, int compression) { super(world); + this.compression = compression; + } + + public FaweOutputStream getCompressedOS(OutputStream os) throws IOException { + return MainUtil.getCompressedOS(os, compression); } @Override @@ -69,25 +80,6 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet { return originZ; } - public InputStream getCompressedIS(InputStream is) { - if (Settings.COMPRESSION_LEVEL > 0) { - is = new LZ4InputStream(new LZ4InputStream(is)); - } else { - is = new LZ4InputStream(is); - } - return is; - } - - public OutputStream getCompressedOS(OutputStream os) throws IOException { - LZ4Factory factory = LZ4Factory.fastestInstance(); - LZ4Compressor compressor = factory.fastCompressor(); - os = new LZ4OutputStream(os, Settings.BUFFER_SIZE, factory.fastCompressor()); - if (Settings.COMPRESSION_LEVEL > 0) { - os = new LZ4OutputStream(os, Settings.BUFFER_SIZE, factory.highCompressor()); - } - return os; - } - public void add(int x, int y, int z, int combinedFrom, int combinedTo) { blockSize++; try { diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java b/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java index c326c5cd..e35a2611 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java @@ -1,6 +1,8 @@ package com.boydti.fawe.object.changeset; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.FaweInputStream; +import com.boydti.fawe.object.FaweOutputStream; import com.boydti.fawe.util.MainUtil; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; @@ -21,7 +23,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { private byte[] ids; private ByteArrayOutputStream idsStream; - private OutputStream idsStreamZip; + private FaweOutputStream idsStreamZip; private byte[] entC; private ByteArrayOutputStream entCStream; @@ -96,12 +98,18 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { } setOrigin(x, z); idsStream = new ByteArrayOutputStream(Settings.BUFFER_SIZE); - return idsStreamZip = getCompressedOS(idsStream); + idsStreamZip = getCompressedOS(idsStream); + idsStreamZip.write(FaweStreamChangeSet.MODE); + idsStreamZip.writeInt(x); + idsStreamZip.writeInt(z); + return idsStreamZip; } @Override - public InputStream getBlockIS() { - return ids == null ? null : getCompressedIS(new ByteArrayInputStream(ids)); + public InputStream getBlockIS() throws IOException { + FaweInputStream result = ids == null ? null : MainUtil.getCompressedIS(new ByteArrayInputStream(ids)); + result.skip(FaweStreamChangeSet.HEADER_SIZE); + return result; } @Override @@ -142,21 +150,21 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { @Override public NBTInputStream getEntityCreateIS() throws IOException { - return entC == null ? null : new NBTInputStream(getCompressedIS(new ByteArrayInputStream(entC))); + return entC == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new ByteArrayInputStream(entC))); } @Override public NBTInputStream getEntityRemoveIS() throws IOException { - return entR == null ? null : new NBTInputStream(getCompressedIS(new ByteArrayInputStream(entR))); + return entR == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new ByteArrayInputStream(entR))); } @Override public NBTInputStream getTileCreateIS() throws IOException { - return tileC == null ? null : new NBTInputStream(getCompressedIS(new ByteArrayInputStream(tileC))); + return tileC == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new ByteArrayInputStream(tileC))); } @Override public NBTInputStream getTileRemoveIS() throws IOException { - return tileR == null ? null : new NBTInputStream(getCompressedIS(new ByteArrayInputStream(tileR))); + return tileR == null ? null : new NBTInputStream(MainUtil.getCompressedIS(new ByteArrayInputStream(tileR))); } } diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java b/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java index c6cc2bfa..bd55faa3 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java @@ -36,7 +36,9 @@ import java.util.UUID; */ public class DiskOptimizedClipboard extends FaweClipboard { - private static int HEADER_SIZE = 10; + public static int COMPRESSION = 0; + public static int MODE = 0; + public static int HEADER_SIZE = 14; protected int length; protected int height; @@ -53,7 +55,7 @@ public class DiskOptimizedClipboard extends FaweClipboard { private int last; public DiskOptimizedClipboard(int width, int height, int length, UUID uuid) { - this(width, height, length, new File(Fawe.imp().getDirectory(), "clipboard" + File.separator + uuid)); + this(width, height, length, new File(Fawe.imp().getDirectory(), "clipboard" + File.separator + uuid + ".bd")); } public DiskOptimizedClipboard(File file) throws IOException { @@ -64,13 +66,14 @@ public class DiskOptimizedClipboard extends FaweClipboard { this.raf = new BufferedRandomAccessFile(file, "rw", Settings.BUFFER_SIZE); raf.setLength(file.length()); long size = (raf.length() - HEADER_SIZE) >> 1; - raf.seek(0); + raf.seek(2); last = -1; raf.read(buffer); width = (((buffer[1] & 0xFF) << 8) + ((buffer[0] & 0xFF))); raf.read(buffer); + height = (((buffer[1] & 0xFF) << 8) + ((buffer[0] & 0xFF))); + raf.read(buffer); length = (((buffer[1] & 0xFF) << 8) + ((buffer[0] & 0xFF))); - height = (int) (size / (width * length)); area = width * length; autoCloseTask(); } @@ -86,7 +89,7 @@ public class DiskOptimizedClipboard extends FaweClipboard { if (raf == null) { open(); } - raf.seek(4); + raf.seek(8); last = -1; int ox = (((byte) raf.read() << 8) | ((byte) raf.read()) & 0xFF); int oy = (((byte) raf.read() << 8) | ((byte) raf.read()) & 0xFF); @@ -126,7 +129,7 @@ public class DiskOptimizedClipboard extends FaweClipboard { if (raf == null) { open(); } - raf.seek(4); + raf.seek(8); last = -1; raf.write((byte) (offset.getBlockX() >> 8)); raf.write((byte) (offset.getBlockX())); @@ -178,10 +181,12 @@ public class DiskOptimizedClipboard extends FaweClipboard { if (raf.length() != size) { raf.setLength(size); // write length etc - raf.seek(0); + raf.seek(1); last = 0; raf.write((width) & 0xff); raf.write(((width) >> 8) & 0xff); + raf.write((height) & 0xff); + raf.write(((height) >> 8) & 0xff); raf.write((length) & 0xff); raf.write(((length) >> 8) & 0xff); } @@ -291,6 +296,12 @@ public class DiskOptimizedClipboard extends FaweClipboard { return EditSession.nullBlock; } + @Override + public boolean setTile(int x, int y, int z, CompoundTag tag) { + nbtMap.put(new IntegerTrio(x, y, z), tag); + return true; + } + @Override public boolean setBlock(int x, int y, int z, BaseBlock block) { try { diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/FaweClipboard.java b/core/src/main/java/com/boydti/fawe/object/clipboard/FaweClipboard.java index bba91908..6fea42af 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/FaweClipboard.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/FaweClipboard.java @@ -1,6 +1,7 @@ package com.boydti.fawe.object.clipboard; import com.boydti.fawe.object.RunnableVal2; +import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.entity.BaseEntity; @@ -18,6 +19,8 @@ public abstract class FaweClipboard { public abstract boolean setBlock(int x, int y, int z, BaseBlock block); + public abstract boolean setTile(int x, int y, int z, CompoundTag tag); + public abstract Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity); public abstract List getEntities(); diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/LazyClipboard.java b/core/src/main/java/com/boydti/fawe/object/clipboard/LazyClipboard.java index 000d1e47..8be6511e 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/LazyClipboard.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/LazyClipboard.java @@ -1,32 +1,66 @@ package com.boydti.fawe.object.clipboard; +import com.boydti.fawe.object.RunnableVal2; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.regions.Region; +import java.util.Iterator; import java.util.List; public abstract class LazyClipboard extends FaweClipboard { + private final Region region; -// private final int width, height, length; -// -// public LazyClipboard(int width, int height, int length) { -// this.width = width; -// this.height = height; -// this.length = length; -// } -// -// @Override -// public void forEach(RunnableVal2 task, boolean air) { -// BlockVector pos = new BlockVector(0, 0, 0); -// for (pos.x = 0; pos.x < width; pos.x++) { -// for (pos.z = 0; pos.z < width; pos.z++) { -// for (pos.y = 0; pos.y < width; pos.y++) { -// task.run(pos, getBlock((int) pos.x, (int) pos.y, (int) pos.z)); -// } -// } -// } -// } + public LazyClipboard(Region region) { + this.region = region; + } + + public static LazyClipboard of(final EditSession editSession, Region region) { + final Vector origin = region.getMinimumPoint(); + final int mx = origin.getBlockX(); + final int my = origin.getBlockY(); + final int mz = origin.getBlockZ(); + return new LazyClipboard(region) { + @Override + public BaseBlock getBlock(int x, int y, int z) { + return editSession.getLazyBlock(mx + x, my + y, mz + z); + } + + public BaseBlock getBlockAbs(int x, int y, int z) { + return editSession.getLazyBlock(x, y, z); + } + + @Override + public List getEntities() { + return editSession.getEntities(getRegion()); + } + + @Override + public void forEach(RunnableVal2 task, boolean air) { + Iterator iter = getRegion().iterator(); + while (iter.hasNext()) { + BlockVector pos = iter.next(); + BaseBlock block = getBlockAbs((int) pos.x, (int) pos.y, (int) pos.z); + if (!air && block == EditSession.nullBlock) { + continue; + } + pos.x -= mx; + pos.y -= my; + pos.z -= mz; + task.run(pos, block); + } + } + }; + } + + public Region getRegion() { + return region; + } @Override public abstract BaseBlock getBlock(int x, int y, int z); @@ -39,6 +73,11 @@ public abstract class LazyClipboard extends FaweClipboard { throw new UnsupportedOperationException("Clipboard is immutable"); } + @Override + public boolean setTile(int x, int y, int z, CompoundTag tag) { + throw new UnsupportedOperationException("Clipboard is immutable"); + } + @Override public Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) { throw new UnsupportedOperationException("Clipboard is immutable"); diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java b/core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java index 0aab29bf..f7badf69 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java @@ -135,6 +135,12 @@ public class MemoryOptimizedClipboard extends FaweClipboard { } } + @Override + public boolean setTile(int x, int y, int z, CompoundTag tag) { + nbtMap.put(new IntegerTrio(x, y, z), tag); + return true; + } + @Override public boolean setBlock(int x, int y, int z, BaseBlock block) { final int id = block.getId(); diff --git a/core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java index 6aa0fac2..cd4d396d 100644 --- a/core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java +++ b/core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java @@ -30,7 +30,7 @@ public class ProcessedWEExtent extends FaweRegionExtent { @Override public Entity createEntity(final Location location, final BaseEntity entity) { - if (limit.MAX_ENTITIES-- < 0 || entity == null) { + if (!limit.MAX_ENTITIES() || entity == null) { return null; } return super.createEntity(location, entity); @@ -67,18 +67,18 @@ public class ProcessedWEExtent extends FaweRegionExtent { @Override public boolean setBlock(final Vector location, final BaseBlock block) throws WorldEditException { if (block.hasNbtData() && FaweCache.hasNBT(block.getType())) { - if (limit.MAX_BLOCKSTATES-- < 0) { + if (!limit.MAX_BLOCKSTATES()) { WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_TILES); return false; } } if (WEManager.IMP.maskContains(this.mask, (int) location.x, (int) location.z)) { - if (limit.MAX_CHANGES-- < 0) { + if (!limit.MAX_CHANGES()) { WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES); return false; } return super.setBlock(location, block); - } else if (limit.MAX_FAILS-- < 0) { + } else if (!limit.MAX_FAILS()) { WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); } return false; diff --git a/core/src/main/java/com/boydti/fawe/object/progress/DefaultProgressTracker.java b/core/src/main/java/com/boydti/fawe/object/progress/DefaultProgressTracker.java index 885ab7bf..4551a972 100644 --- a/core/src/main/java/com/boydti/fawe/object/progress/DefaultProgressTracker.java +++ b/core/src/main/java/com/boydti/fawe/object/progress/DefaultProgressTracker.java @@ -8,6 +8,9 @@ import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.TaskManager; +/** + * The default progress tracker uses titles + */ public class DefaultProgressTracker extends RunnableVal2 { private final FawePlayer player; @@ -18,9 +21,12 @@ public class DefaultProgressTracker extends RunnableVal2 64) { + if (totalQueue > 64) { done(); } return; } - if (amountQueue > 64 || amountDispatch > 64) { + // Only send a message after 64 chunks (i.e. ignore smaller edits) + if (totalQueue > 64) { send(); } } - private void done() { + private final void done() { TaskManager.IMP.task(new Runnable() { @Override public void run() { - final long time = System.currentTimeMillis() - start; - player.sendTitle("", BBC.PROGRESS_DONE.format(time / 1000d)); - TaskManager.IMP.later(new Runnable() { - @Override - public void run() { - player.resetTitle(); - } - }, 60); + doneTask(); } }); } - public void send() { - TaskManager.IMP.task(new Runnable() { + private long lastTick = 0; + + private final void send() { + // Avoid duplicates + long currentTick = System.currentTimeMillis() / 50; + if (currentTick > lastTick + Settings.DISPLAY_PROGRESS_INTERVAL) { + lastTick = currentTick; + TaskManager.IMP.task(new Runnable() { // Run on main thread + @Override + public void run() { + sendTask(); + } + }); + } + } + + public void doneTask() { + final long time = System.currentTimeMillis() - start; + player.sendTitle("", BBC.PROGRESS_DONE.format(time / 1000d)); + TaskManager.IMP.later(new Runnable() { // Run on main thread @Override public void run() { - long currentTick = System.currentTimeMillis() / 50; - if (currentTick > lastTick + Settings.DISPLAY_PROGRESS_INTERVAL) { - lastTick = currentTick; - String queue = StringMan.padRight("" + amountQueue, 3); - String dispatch = StringMan.padRight("" + amountDispatch, 3); - player.sendTitle("", BBC.PROGRESS_MESSAGE.format(queue, dispatch)); - } + doneTask(); } - }); + }, 60); + } + + public void sendTask() { + String queue = StringMan.padRight("" + amountQueue, 3); + String dispatch = StringMan.padRight("" + amountDispatch, 3); + player.sendTitle("", BBC.PROGRESS_MESSAGE.format(queue, dispatch)); } } diff --git a/core/src/main/java/com/boydti/fawe/object/schematic/FaweFormat.java b/core/src/main/java/com/boydti/fawe/object/schematic/FaweFormat.java new file mode 100644 index 00000000..bb5c4aaf --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/schematic/FaweFormat.java @@ -0,0 +1,377 @@ +package com.boydti.fawe.object.schematic; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.FaweInputStream; +import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.util.MainUtil; +import com.boydti.fawe.util.ReflectionUtils; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NamedTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.registry.WorldData; +import com.sk89q.worldedit.world.storage.NBTConversions; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public class FaweFormat implements ClipboardReader, ClipboardWriter { + private static final int MAX_SIZE = Short.MAX_VALUE - Short.MIN_VALUE; + + private FaweInputStream in; + private FaweOutputStream out; + private int mode; + + public FaweFormat(FaweInputStream in) { + this.in = in; + } + + public FaweFormat(FaweOutputStream out) { + this.out = out; + } + + private boolean compressed = false; + + public boolean compress(int i) throws IOException { + if (compressed) { + return false; + } + compressed = true; + if (in != null) { + in = MainUtil.getCompressedIS(in.getParent()); + } else if (out != null) { + out = MainUtil.getCompressedOS(out.getParent()); + } + return true; + } + + public void setWriteMode(int mode) { + this.mode = mode; + } + + @Override + public Clipboard read(WorldData data) throws IOException { + return read(data, UUID.randomUUID()); + } + + public Clipboard read(WorldData worldData, UUID clipboardId) throws IOException { + int mode = in.read(); + + BlockArrayClipboard clipboard; + int ox, oy, oz; + oy = 0; + + boolean from = false; + boolean small = true; + switch (mode) { + default: + return null; + case 3: + from = true; + case 2: + small = false; + case 1: { + ox = in.readInt(); + oz = in.readInt(); + FaweOutputStream tmp = new FaweOutputStream(new ByteArrayOutputStream(Settings.BUFFER_SIZE)); + int width = 0; + int height = 0; + int length = 0; + while (true) { + int x, y, z; + if (small) { + tmp.write(x = in.read()); + tmp.write(y = in.read()); + tmp.write(z = in.read()); + } else { + tmp.writeShort((short) (x = in.readUnsignedShort())); + tmp.write(y = in.read()); + tmp.writeShort((short) (z = in.readUnsignedShort())); + } + if (from) { + in.skip(2); + } + short combined; + tmp.writeShort(combined = in.readShort()); + if (combined == 0 || y == -1) { + break; + } + if (x > width) { + width = x; + } + if (y > height) { + height = y; + } + if(z > length) { + length = z; + } + } + Vector origin = new Vector(0, 0, 0); + CuboidRegion region = new CuboidRegion(origin, origin.add(width, height, length)); + clipboard = new BlockArrayClipboard(region, clipboardId); + width++; + height++; + length++; + byte[] array = ((ByteArrayOutputStream) tmp.getParent()).toByteArray(); + FaweInputStream part = new FaweInputStream(new ByteArrayInputStream(array)); + try { + for (int i = 0; i< array.length; i+= 9) { + int x, y, z; + if (small) { + x = in.read(); + y = in.read(); + z = in.read(); + } else { + x = in.readUnsignedShort(); + y = in.read(); + z = in.readUnsignedShort(); + } + if (from) { + in.skip(2); + } + int combined = in.readShort(); + int id = FaweCache.getId(combined); + int data = FaweCache.getData(combined); + BaseBlock block = FaweCache.getBlock(id, data); + clipboard.setBlock(x, y, z, block); + } + } catch (WorldEditException e) { + e.printStackTrace(); + return null; + } + break; + } + case 0: { + int width = in.readUnsignedShort(); + int height = in.readUnsignedShort(); + int length = in.readUnsignedShort(); + ox = in.readShort(); + oy = in.readShort(); + oz = in.readShort(); + + Vector origin = new Vector(0, 0, 0); + CuboidRegion region = new CuboidRegion(origin, origin.add(width, height, length).subtract(Vector.ONE)); + clipboard = new BlockArrayClipboard(region, clipboardId); + try { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + for (int z = 0; z < length; z++) { + int combined = in.readUnsignedShort(); + int id = FaweCache.getId(combined); + int data = FaweCache.getData(combined); + BaseBlock block = FaweCache.getBlock(id, data); + clipboard.setBlock(x, y, z, block); + } + } + } + } catch (WorldEditException e) { + e.printStackTrace(); + return null; + } + break; + } + } + try { + NamedTag namedTag; + while ((namedTag = in.readNBT()) != null) { + CompoundTag compound = (CompoundTag) namedTag.getTag(); + Map map = compound.getValue(); + if (map.containsKey("Rotation")) { + // Entity + String id = compound.getString("id"); + Location location = NBTConversions.toLocation(clipboard, compound.getListTag("Pos"), compound.getListTag("Rotation")); + if (!id.isEmpty()) { + BaseEntity state = new BaseEntity(id, compound); + clipboard.createEntity(location, state); + } + } else { + // Tile + int x = compound.getInt("x"); + int y = compound.getInt("y"); + int z = compound.getInt("z"); + clipboard.setTile(x, y, z, compound); + + } + } + } catch (Throwable ignore) {} + clipboard.setOrigin(new Vector(ox, oy, oz)); + return clipboard; + } + + @Override + public void write(Clipboard clipboard, WorldData worldData) throws IOException { + write(clipboard, worldData, "FAWE"); + } + + public void write(Clipboard clipboard, WorldData worldData, String owner) throws IOException { + Region region = clipboard.getRegion(); + int width = region.getWidth(); + int height = region.getHeight(); + int length = region.getLength(); + + if (width > MAX_SIZE) { + throw new IllegalArgumentException("Width of region too large for a .nbt"); + } + if (height > MAX_SIZE) { + throw new IllegalArgumentException("Height of region too large for a .nbt"); + } + if (length > MAX_SIZE) { + throw new IllegalArgumentException("Length of region too large for a .nbt"); + } + + Vector min = clipboard.getMinimumPoint(); + Vector max = clipboard.getMaximumPoint(); + Vector origin = clipboard.getOrigin().subtract(min); + + // Mode + out.write(mode); + ArrayDeque tiles = new ArrayDeque<>(); + boolean from = false; + boolean small = true; + switch (mode) { + default: + throw new IllegalArgumentException("Invalid mode: " + mode); + case 3: + from = true; + case 2: + small = false; + case 1: { + out.writeInt(origin.getBlockX()); + out.writeInt(origin.getBlockZ()); + for (Vector pt : clipboard.getRegion()) { + BaseBlock block = clipboard.getBlock(pt); + if (block == EditSession.nullBlock) { + continue; + } + int x = pt.getBlockX() - min.getBlockX(); + int y = pt.getBlockY() - min.getBlockY(); + int z = pt.getBlockZ() - min.getBlockZ(); + if (block.hasNbtData()) { + CompoundTag tile = block.getNbtData(); + Map map = ReflectionUtils.getMap(tile.getValue()); + map.put("id", new StringTag(block.getNbtId())); + map.put("x", new IntTag(x)); + map.put("y", new IntTag(y)); + map.put("z", new IntTag(z)); + tiles.add(tile); + } + if (small) { + out.write((byte) x); + out.write((byte) y); + out.write((byte) z); + } else { + out.writeShort((short) x); + out.write((byte) y); + out.writeShort((short) z); + } + if (from) { + out.writeShort((short) 0); + } + out.writeShort((short) FaweCache.getCombined(block)); + break; + } + int i = (small ? 3 : 5) + (from ? 4 : 2); + out.write(0, i); + } + case 0: { + // Dimensions + out.writeShort((short) width); + out.writeShort((short) height); + out.writeShort((short) length); + out.writeShort((short) origin.getBlockX()); + out.writeShort((short) origin.getBlockY()); + out.writeShort((short) origin.getBlockZ()); + Vector mutable = new Vector(0, 0, 0); + for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { + mutable.y = y; + for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { + mutable.x = x; + for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { + mutable.z = z; + BaseBlock block = clipboard.getBlock(mutable); + if (block == EditSession.nullBlock) { + out.writeShort((short) 0); + } else { + out.writeShort((short) FaweCache.getCombined(block)); + if (block.hasNbtData()) { + CompoundTag tile = block.getNbtData(); + Map map = ReflectionUtils.getMap(tile.getValue()); + map.put("id", new StringTag(block.getNbtId())); + map.put("x", new IntTag(x - min.getBlockX())); + map.put("y", new IntTag(y - min.getBlockY())); + map.put("z", new IntTag(z - min.getBlockZ())); + tiles.add(tile); + } + } + } + } + } + break; + } + } + for (CompoundTag tile : tiles) { + out.writeNBT("", FaweCache.asTag(tile)); + } + for (Entity entity : clipboard.getEntities()) { + BaseEntity state = entity.getState(); + if (state != null) { + CompoundTag entityTag = state.getNbtData(); + Map map = ReflectionUtils.getMap(entityTag.getValue()); + map.put("id", new StringTag(state.getTypeId())); + map.put("Pos", writeVector(entity.getLocation().toVector(), "Pos")); + map.put("Rotation", writeRotation(entity.getLocation(), "Rotation")); + out.writeNBT("", entityTag); + } + } + close(); + } + + @Override + public void close() throws IOException { + if (in != null) { + in.close(); + } + if (out != null) { + out.flush(); + out.close(); + } + } + + private Tag writeVector(Vector vector, String name) { + List list = new ArrayList(); + list.add(new DoubleTag(vector.getX())); + list.add(new DoubleTag(vector.getY())); + list.add(new DoubleTag(vector.getZ())); + return new ListTag(DoubleTag.class, list); + } + + private Tag writeRotation(Location location, String name) { + List list = new ArrayList(); + list.add(new FloatTag(location.getYaw())); + list.add(new FloatTag(location.getPitch())); + return new ListTag(FloatTag.class, list); + } +} diff --git a/core/src/main/java/com/boydti/fawe/regions/general/plot/FaweChunkManager.java b/core/src/main/java/com/boydti/fawe/regions/general/plot/FaweChunkManager.java new file mode 100644 index 00000000..d30efe07 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/regions/general/plot/FaweChunkManager.java @@ -0,0 +1,105 @@ +package com.boydti.fawe.regions.general.plot; + +import com.boydti.fawe.util.EditSessionBuilder; +import com.boydti.fawe.util.TaskManager; +import com.intellectualcrafters.plot.object.ChunkLoc; +import com.intellectualcrafters.plot.object.Location; +import com.intellectualcrafters.plot.object.Plot; +import com.intellectualcrafters.plot.util.ChunkManager; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.function.operation.ForwardExtentCopy; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.world.World; + +public class FaweChunkManager extends ChunkManager { + + private ChunkManager parent; + + public FaweChunkManager(ChunkManager parent) { + this.parent = parent; + } + + @Override + public int[] countEntities(Plot plot) { + return parent.countEntities(plot); + } + + @Override + public boolean loadChunk(String world, ChunkLoc loc, boolean force) { + return parent.loadChunk(world, loc, force); + } + + @Override + public void unloadChunk(String world, ChunkLoc loc, boolean save, boolean safe) { + parent.unloadChunk(world, loc, save, safe); + } + + @Override + public void clearAllEntities(Location pos1, Location pos2) { + parent.clearAllEntities(pos1, pos2); + } + + @Override + public void swap(final Location pos1, final Location pos2, final Location pos3, final Location pos4, final Runnable whenDone) { + TaskManager.IMP.async(new Runnable() { + @Override + public void run() { + EditSession sessionA = new EditSessionBuilder(pos1.getWorld()).checkMemory(false).fastmode(true).limitUnlimited().changeSetNull().autoQueue(false).build(); + EditSession sessionB = new EditSessionBuilder(pos3.getWorld()).checkMemory(false).fastmode(true).limitUnlimited().changeSetNull().autoQueue(false).build(); + CuboidRegion regionA = new CuboidRegion(new Vector(pos1.getX(), pos1.getY(), pos1.getZ()), new Vector(pos2.getX(), pos2.getY(), pos2.getZ())); + CuboidRegion regionB = new CuboidRegion(new Vector(pos3.getX(), pos3.getY(), pos3.getZ()), new Vector(pos4.getX(), pos4.getY(), pos4.getZ())); + ForwardExtentCopy copyA = new ForwardExtentCopy(sessionA, regionA, sessionB, regionB.getMinimumPoint()); + ForwardExtentCopy copyB = new ForwardExtentCopy(sessionB, regionB, sessionA, regionA.getMinimumPoint()); + try { + Operations.completeLegacy(copyA); + Operations.completeLegacy(copyB); + sessionA.flushQueue(); + sessionB.flushQueue(); + } catch (MaxChangedBlocksException e) { + e.printStackTrace(); + } + TaskManager.IMP.task(whenDone); + } + }); + } + + @Override + public boolean copyRegion(final Location pos1, final Location pos2, final Location pos3, final Runnable whenDone) { + TaskManager.IMP.async(new Runnable() { + @Override + public void run() { + EditSession from = new EditSessionBuilder(pos1.getWorld()).checkMemory(false).fastmode(true).limitUnlimited().changeSetNull().autoQueue(false).build(); + EditSession to = new EditSessionBuilder(pos3.getWorld()).checkMemory(false).fastmode(true).limitUnlimited().changeSetNull().autoQueue(false).build(); + CuboidRegion region = new CuboidRegion(new Vector(pos1.getX(), pos1.getY(), pos1.getZ()), new Vector(pos2.getX(), pos2.getY(), pos2.getZ())); + ForwardExtentCopy copy = new ForwardExtentCopy(from, region, to, new Vector(pos3.getX(), pos3.getY(), pos3.getZ())); + try { + Operations.completeLegacy(copy); + to.flushQueue(); + } catch (MaxChangedBlocksException e) { + e.printStackTrace(); + } + TaskManager.IMP.task(whenDone); + } + }); + return true; + } + + @Override + public boolean regenerateRegion(final Location pos1, final Location pos2, boolean ignore, final Runnable whenDone) { + TaskManager.IMP.async(new Runnable() { + @Override + public void run() { + EditSession editSession = new EditSessionBuilder(pos1.getWorld()).checkMemory(false).fastmode(true).limitUnlimited().changeSetNull().autoQueue(false).build(); + World world = editSession.getWorld(); + CuboidRegion region = new CuboidRegion(new Vector(pos1.getX(), pos1.getY(), pos1.getZ()), new Vector(pos2.getX(), pos2.getY(), pos2.getZ())); + world.regenerate(region, editSession); + editSession.flushQueue(); + TaskManager.IMP.task(whenDone); + } + }); + return true; + } +} diff --git a/core/src/main/java/com/boydti/fawe/regions/general/plot/FaweLocalBlockQueue.java b/core/src/main/java/com/boydti/fawe/regions/general/plot/FaweLocalBlockQueue.java new file mode 100644 index 00000000..5a9bd55e --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/regions/general/plot/FaweLocalBlockQueue.java @@ -0,0 +1,121 @@ +package com.boydti.fawe.regions.general.plot; + +import com.boydti.fawe.FaweAPI; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.util.SetQueue; +import com.intellectualcrafters.jnbt.CompoundTag; +import com.intellectualcrafters.plot.object.PlotBlock; +import com.intellectualcrafters.plot.util.StringMan; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.biome.Biomes; +import com.sk89q.worldedit.world.registry.BiomeRegistry; +import java.util.List; + +public class FaweLocalBlockQueue extends LocalBlockQueue { + + private FaweQueue IMP; + + + public FaweLocalBlockQueue(String world) { + super(world); + IMP = SetQueue.IMP.getNewQueue(world, true, false); + } + + @Override + public boolean next() { + return IMP.next() != null; + } + + @Override + public void startSet(boolean parallel) { + IMP.startSet(parallel); + } + + @Override + public void endSet(boolean parallel) { + IMP.endSet(parallel); + } + + @Override + public int size() { + return IMP.size(); + } + + @Override + public void optimize() { + IMP.optimize(); + } + + @Override + public void setModified(long l) { + IMP.setModified(l); + } + + @Override + public long getModified() { + return IMP.getModified(); + } + + @Override + public boolean setBlock(int x, int y, int z, int id, int data) { + return IMP.setBlock(x, y, z, (short) id ,(byte) data); + } + + @Override + public PlotBlock getBlock(int x, int y, int z) { + int combined = IMP.getCombinedId4Data(x, y, z); + return PlotBlock.get(FaweCache.getId(combined), FaweCache.getData(combined)); + } + + private BaseBiome biome; + private String lastBiome; + private BiomeRegistry reg; + + @Override + public boolean setBiome(int x, int z, String biome) { + if (!StringMan.isEqual(biome, lastBiome)) { + if (reg == null) { + World weWorld = FaweAPI.getWorld(IMP.getWorldName()); + reg = weWorld.getWorldData().getBiomeRegistry(); + } + List biomes = reg.getBiomes(); + lastBiome = biome; + this.biome = Biomes.findBiomeByName(biomes, biome, reg); + } + return IMP.setBiome(x, z, this.biome); + } + + @Override + public String getWorld() { + return IMP.getWorldName(); + } + + @Override + public void flush() { + IMP.flush(); + } + + @Override + public void refreshChunk(int x, int z) { + IMP.sendChunk(IMP.getFaweChunk(x, z), FaweQueue.RelightMode.NONE); + } + + @Override + public void fixChunkLighting(int x, int z) { + IMP.fixLighting(IMP.getFaweChunk(x, z), FaweQueue.RelightMode.OPTIMAL); + } + + @Override + public void regenChunk(int x, int z) { + IMP.regenerateChunk(x, z); + } + + @Override + public boolean setTile(int x, int y, int z, CompoundTag tag) { + IMP.setTile(x, y, z, (com.sk89q.jnbt.CompoundTag) FaweCache.asTag(tag)); + return true; + } +} diff --git a/core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java b/core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java new file mode 100644 index 00000000..4c9fa323 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/regions/general/plot/FaweSchematicHandler.java @@ -0,0 +1,159 @@ +package com.boydti.fawe.regions.general.plot; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.object.RunnableVal2; +import com.boydti.fawe.object.clipboard.LazyClipboard; +import com.boydti.fawe.util.EditSessionBuilder; +import com.boydti.fawe.util.SetQueue; +import com.boydti.fawe.util.TaskManager; +import com.intellectualcrafters.jnbt.CompoundTag; +import com.intellectualcrafters.jnbt.Tag; +import com.intellectualcrafters.plot.PS; +import com.intellectualcrafters.plot.object.Location; +import com.intellectualcrafters.plot.object.RegionWrapper; +import com.intellectualcrafters.plot.object.RunnableVal; +import com.intellectualcrafters.plot.util.MainUtil; +import com.intellectualcrafters.plot.util.SchematicHandler; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; +import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.SchematicWriter; +import com.sk89q.worldedit.regions.CuboidRegion; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.zip.GZIPOutputStream; + +public class FaweSchematicHandler extends SchematicHandler { + @Override + public boolean restoreTile(LocalBlockQueue queue, CompoundTag compoundTag, int x, int y, int z) { + if (queue instanceof FaweLocalBlockQueue) { + queue.setTile(x, y, z, compoundTag); + return true; + } + FaweQueue faweQueue = SetQueue.IMP.getNewQueue(queue.getWorld(), true, false); + faweQueue.setTile(x, y, z, (com.sk89q.jnbt.CompoundTag) FaweCache.asTag(compoundTag)); + faweQueue.flush(); + return false; + } + + @Override + public void getCompoundTag(final String world, final Set regions, final RunnableVal whenDone) { + TaskManager.IMP.async(new Runnable() { + @Override + public void run() { + Location[] corners = MainUtil.getCorners(world, regions); + Location pos1 = corners[0]; + Location pos2 = corners[1]; + final CuboidRegion region = new CuboidRegion(new Vector(pos1.getX(), pos1.getY(), pos1.getZ()), new Vector(pos2.getX(), pos2.getY(), pos2.getZ())); + final EditSession editSession = new EditSessionBuilder(pos1.getWorld()).checkMemory(false).fastmode(true).limitUnlimited().changeSetNull().autoQueue(false).build(); + + final int mx = pos1.getX(); + final int my = pos1.getY(); + final int mz = pos1.getZ(); + + LazyClipboard clipboard = new LazyClipboard(region) { + @Override + public BaseBlock getBlock(int x, int y, int z) { + return editSession.getLazyBlock(mx + x, my + y, mz + z); + } + + public BaseBlock getBlockAbs(int x, int y, int z) { + return editSession.getLazyBlock(x, y, z); + } + + @Override + public List getEntities() { + return editSession.getEntities(region); + } + + @Override + public void forEach(RunnableVal2 task, boolean air) { + Vector mutable = new Vector(0, 0, 0); + for (RegionWrapper region : regions) { + for (int z = region.minZ; z <= region.maxZ; z++) { + mutable.z = z - region.minZ; + for (int y = 0; y < 256; y++) { + mutable.y = y; + for (int x = region.minX; x <= region.maxX; x++) { + mutable.x = x - region.minX; + BaseBlock block = editSession.getLazyBlock(x, y, z); + if (!air && block == editSession.nullBlock) { + continue; + } + task.run(mutable, block); + } + } + } + } + } + }; + + Clipboard holder = new BlockArrayClipboard(region, clipboard); + com.sk89q.jnbt.CompoundTag weTag = SchematicWriter.writeTag(holder); + CompoundTag tag = new CompoundTag((Map) (Map) weTag.getValue()); + whenDone.run(tag); + } + }); + } + + @Override + public boolean save(CompoundTag tag, String path) { + if (tag == null) { + PS.debug("&cCannot save empty tag"); + return false; + } + try { + File tmp = MainUtil.getFile(PS.get().IMP.getDirectory(), path); + tmp.getParentFile().mkdirs(); + com.sk89q.jnbt.CompoundTag weTag = (com.sk89q.jnbt.CompoundTag) FaweCache.asTag(tag); + try (OutputStream stream = new FileOutputStream(tmp); NBTOutputStream output = new NBTOutputStream(new GZIPOutputStream(stream))) { + Map map = weTag.getValue(); + output.writeNamedTag("Schematic", map.containsKey("Schematic") ? map.get("Schematic") : weTag); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + return true; + } + + @Override + public void upload(final CompoundTag tag, final UUID uuid, final String file, final RunnableVal whenDone) { + if (tag == null) { + PS.debug("&cCannot save empty tag"); + com.intellectualcrafters.plot.util.TaskManager.runTask(whenDone); + return; + } + MainUtil.upload(uuid, file, "schematic", new RunnableVal() { + @Override + public void run(OutputStream output) { + try { + GZIPOutputStream gzip = new GZIPOutputStream(output, true); + com.sk89q.jnbt.CompoundTag weTag = (com.sk89q.jnbt.CompoundTag) FaweCache.asTag(tag); + NBTOutputStream nos = new NBTOutputStream(gzip); + Map map = weTag.getValue(); + nos.writeNamedTag("Schematic", map.containsKey("Schematic") ? map.get("Schematic") : weTag); + gzip.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }, whenDone); + } +} diff --git a/core/src/main/java/com/boydti/fawe/regions/general/PlotSquaredFeature.java b/core/src/main/java/com/boydti/fawe/regions/general/plot/PlotSquaredFeature.java similarity index 53% rename from core/src/main/java/com/boydti/fawe/regions/general/PlotSquaredFeature.java rename to core/src/main/java/com/boydti/fawe/regions/general/plot/PlotSquaredFeature.java index 64b82f43..7992861c 100644 --- a/core/src/main/java/com/boydti/fawe/regions/general/PlotSquaredFeature.java +++ b/core/src/main/java/com/boydti/fawe/regions/general/plot/PlotSquaredFeature.java @@ -1,12 +1,18 @@ -package com.boydti.fawe.regions.general; +package com.boydti.fawe.regions.general.plot; +import com.boydti.fawe.Fawe; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.regions.FaweMask; import com.boydti.fawe.regions.FaweMaskManager; import com.intellectualcrafters.plot.PS; +import com.intellectualcrafters.plot.generator.HybridPlotManager; import com.intellectualcrafters.plot.object.Plot; import com.intellectualcrafters.plot.object.PlotPlayer; import com.intellectualcrafters.plot.object.RegionWrapper; +import com.intellectualcrafters.plot.util.ChunkManager; +import com.intellectualcrafters.plot.util.SchematicHandler; +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; +import com.intellectualcrafters.plot.util.block.QueueProvider; import com.plotsquared.listener.WEManager; import com.sk89q.worldedit.BlockVector; import java.util.HashSet; @@ -15,7 +21,43 @@ import org.bukkit.entity.Player; public class PlotSquaredFeature extends FaweMaskManager { public PlotSquaredFeature() { super("PlotSquared"); + Fawe.debug("Optimizing PlotSquared"); PS.get().worldedit = null; + setupBlockQueue(); + setupSchematicHandler(); + setupChunkManager(); + } + + private void setupBlockQueue() { + try { + // If it's going to fail, throw an error now rather than later + new FaweLocalBlockQueue(null); + QueueProvider provider = QueueProvider.of(FaweLocalBlockQueue.class, null); + GlobalBlockQueue.IMP.setProvider(provider); + HybridPlotManager.REGENERATIVE_CLEAR = false; + Fawe.debug(" - QueueProvider: " + FaweLocalBlockQueue.class); + Fawe.debug(" - HybridPlotManager.REGENERATIVE_CLEAR: " + HybridPlotManager.REGENERATIVE_CLEAR); + } catch (Throwable e) { + Fawe.debug("Please update PlotSquared: http://ci.athion.net/job/PlotSquared/"); + } + } + + private void setupChunkManager() { + try { + ChunkManager.manager = new FaweChunkManager(ChunkManager.manager); + Fawe.debug(" - ChunkManager: " + ChunkManager.manager); + } catch (Throwable e) { + Fawe.debug("Please update PlotSquared: http://ci.athion.net/job/PlotSquared/"); + } + } + + private void setupSchematicHandler() { + try { + SchematicHandler.manager = new FaweSchematicHandler(); + Fawe.debug(" - SchematicHandler: " + SchematicHandler.manager); + } catch (Throwable e) { + Fawe.debug("Please update PlotSquared: http://ci.athion.net/job/PlotSquared/"); + } } @Override diff --git a/core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java b/core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java new file mode 100644 index 00000000..2dee8951 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java @@ -0,0 +1,146 @@ +package com.boydti.fawe.util; + +import com.boydti.fawe.FaweAPI; +import com.boydti.fawe.object.FaweLimit; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.NullChangeSet; +import com.boydti.fawe.object.RegionWrapper; +import com.boydti.fawe.object.changeset.DiskStorageHistory; +import com.boydti.fawe.object.changeset.FaweChangeSet; +import com.boydti.fawe.object.changeset.MemoryOptimizedHistory; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.event.extent.EditSessionEvent; +import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.util.eventbus.EventBus; +import com.sk89q.worldedit.world.World; +import java.util.UUID; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + + +import static com.google.common.base.Preconditions.checkNotNull; + +public class EditSessionBuilder { + private World world; + private FawePlayer player; + private FaweLimit limit; + private FaweChangeSet changeSet; + private RegionWrapper[] allowedRegions; + private Boolean autoQueue; + private Boolean fastmode; + private Boolean checkMemory; + private Boolean combineStages; + private BlockBag blockBag; + private EventBus eventBus; + private EditSessionEvent event; + + /** + * An EditSession builder
+ * - Unset values will revert to their default
+ *
+ * player: The player doing the edit (defaults to to console)
+ * limit: Block/Entity/Action limit (defaults to unlimited)
+ * changeSet: Stores changes (defaults to config.yml value)
+ * allowedRegions: Allowed editable regions (defaults to player's allowed regions, or everywhere)
+ * autoQueue: Changes can occur before flushQueue() (defaults true)
+ * fastmode: bypasses history (defaults to player fastmode or config.yml console history)
+ * checkMemory: If low memory checks are enabled (defaults to player's fastmode or true)
+ * combineStages: If history is combined with dispatching + * + * @param world A world must be provided for all EditSession(s) + */ + public EditSessionBuilder(@Nonnull World world){ + checkNotNull(world); + this.world = world; + } + + public EditSessionBuilder(@Nonnull String world) { + this(FaweAPI.getWorld(world)); + } + + public EditSessionBuilder player(@Nullable FawePlayer player) { + this.player = player; + return this; + } + + public EditSessionBuilder limit(@Nullable FaweLimit limit) { + this.limit = limit; + return this; + } + + public EditSessionBuilder limitUnlimited() { + return limit(FaweLimit.MAX.copy()); + } + + public EditSessionBuilder changeSet(@Nullable FaweChangeSet changeSet) { + this.changeSet = changeSet; + return this; + } + + public EditSessionBuilder changeSetNull() { + return changeSet(new NullChangeSet(world)); + } + + /** + * @param disk If it should be stored on disk + * @param uuid The uuid to store it under (if on disk) + * @param compression Compression level (0-9) + * @return + */ + public EditSessionBuilder changeSet(boolean disk, @Nullable UUID uuid, int compression) { + if (disk) { + this.changeSet = new DiskStorageHistory(world, uuid); + } else { + this.changeSet = new MemoryOptimizedHistory(world); + } + return this; + } + + public EditSessionBuilder allowedRegions(@Nullable RegionWrapper[] allowedRegions) { + this.allowedRegions = allowedRegions; + return this; + } + + public EditSessionBuilder allowedRegionsEverywhere() { + return allowedRegions(new RegionWrapper[]{RegionWrapper.GLOBAL()}); + } + + public EditSessionBuilder autoQueue(@Nullable Boolean autoQueue) { + this.autoQueue = autoQueue; + return this; + } + + public EditSessionBuilder fastmode(@Nullable Boolean fastmode) { + this.fastmode = fastmode; + return this; + } + + public EditSessionBuilder checkMemory(@Nullable Boolean checkMemory) { + this.checkMemory = checkMemory; + return this; + } + + public EditSessionBuilder combineStages(@Nullable Boolean combineStages) { + this.combineStages = combineStages; + return this; + } + + public EditSessionBuilder blockBag(@Nullable BlockBag blockBag) { + this.blockBag = blockBag; + return this; + } + + public EditSessionBuilder eventBus(@Nullable EventBus eventBus) { + this.eventBus = eventBus; + return this; + } + + public EditSessionBuilder event(@Nullable EditSessionEvent event) { + this.event = event; + return this; + } + + public EditSession build() { + return new EditSession(world, player, limit, changeSet, allowedRegions, autoQueue, fastmode, checkMemory, combineStages, blockBag, eventBus, event); + } +} diff --git a/core/src/main/java/com/boydti/fawe/util/ExtentTraverser.java b/core/src/main/java/com/boydti/fawe/util/ExtentTraverser.java new file mode 100644 index 00000000..bd7f74a4 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/util/ExtentTraverser.java @@ -0,0 +1,106 @@ +package com.boydti.fawe.util; + +import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import com.sk89q.worldedit.extent.Extent; +import java.lang.reflect.Field; + +public class ExtentTraverser { + private T root; + private ExtentTraverser parent; + + public ExtentTraverser(T root) { + this(root, null); + } + + public ExtentTraverser(T root, ExtentTraverser parent) { + this.root = root; + this.parent = parent; + } + + public boolean exists() { + return root != null; + } + + public T get() { + return root; + } + + public boolean setNext(T next) { + try { + Field field = AbstractDelegateExtent.class.getDeclaredField("extent"); + field.setAccessible(true); + field.set(root, next); + return true; + } catch (Throwable e) { + e.printStackTrace(); + return false; + } + } + + public boolean insert(T extent) { + try { + Field field = AbstractDelegateExtent.class.getDeclaredField("extent"); + field.setAccessible(true); + field.set(extent, field.get(root)); + field.set(root, extent); + return true; + } catch (Throwable e) { + e.printStackTrace(); + return false; + } + } + + public ExtentTraverser find(Class clazz) { + try { + ExtentTraverser value = this; + while (value != null) { + if (clazz.isInstance(value.root)) { + return (ExtentTraverser) value; + } + value = value.next(); + } + return null; + } catch (Throwable e) { + MainUtil.handleError(e); + return null; + } + } + + public ExtentTraverser find(Object object) { + try { + ExtentTraverser value = this; + while (value != null) { + if (value.root == object) { + return (ExtentTraverser) value; + } + value = value.next(); + } + return null; + } catch (Throwable e) { + MainUtil.handleError(e); + return null; + } + } + + public ExtentTraverser previous() { + return parent; + } + + public ExtentTraverser next() { + try { + if (root instanceof AbstractDelegateExtent) { + Field field = AbstractDelegateExtent.class.getDeclaredField("extent"); + field.setAccessible(true); + T value = (T) field.get(root); + if (value == null) { + return null; + } + return new ExtentTraverser<>(value, this); + } + return null; + } catch (Throwable e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/core/src/main/java/com/boydti/fawe/util/ExtentWrapper.java b/core/src/main/java/com/boydti/fawe/util/ExtentWrapper.java deleted file mode 100644 index 53f7dc83..00000000 --- a/core/src/main/java/com/boydti/fawe/util/ExtentWrapper.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.boydti.fawe.util; - -import com.sk89q.worldedit.extent.AbstractDelegateExtent; -import com.sk89q.worldedit.extent.Extent; - -public class ExtentWrapper extends AbstractDelegateExtent { - - public ExtentWrapper(final Extent extent) { - super(extent); - } -} diff --git a/core/src/main/java/com/boydti/fawe/util/MainUtil.java b/core/src/main/java/com/boydti/fawe/util/MainUtil.java index 50ff2239..c68246ba 100644 --- a/core/src/main/java/com/boydti/fawe/util/MainUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/MainUtil.java @@ -3,6 +3,8 @@ package com.boydti.fawe.util; import com.boydti.fawe.Fawe; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.FaweInputStream; +import com.boydti.fawe.object.FaweOutputStream; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RunnableVal; @@ -15,8 +17,9 @@ import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.entity.Entity; -import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.util.Location; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -38,8 +41,13 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.UUID; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import net.jpountz.lz4.LZ4Factory; +import net.jpountz.lz4.LZ4InputStream; +import net.jpountz.lz4.LZ4OutputStream; public class MainUtil { /* @@ -75,6 +83,50 @@ public class MainUtil { return getFile(base, path.endsWith("." + extension) ? path : path + "." + extension); } + public static FaweOutputStream getCompressedOS(OutputStream os) throws IOException { + return getCompressedOS(os, Settings.COMPRESSION_LEVEL); + } + + public static FaweOutputStream getCompressedOS(OutputStream os, int amount) throws IOException { + os.write((byte) amount); + os = new BufferedOutputStream(os, Settings.BUFFER_SIZE); + if (amount == 0) { + return new FaweOutputStream(os); + } + int gzipAmount = amount > 6 ? 1 : 0; + for (int i = 0; i < gzipAmount; i++) { + os = new GZIPOutputStream(os, true); + } + LZ4Factory factory = LZ4Factory.fastestInstance(); + int fastAmount = 1 + ((amount - 1) % 3); + for (int i = 0; i < fastAmount; i++) { + os = new LZ4OutputStream(os, Settings.BUFFER_SIZE, factory.fastCompressor()); + } + int highAmount = amount > 3 ? 1 : 0; + for (int i = 0; i < highAmount; i++) { + os = new LZ4OutputStream(os, Settings.BUFFER_SIZE, factory.highCompressor()); + } + return new FaweOutputStream(os); + } + + public static FaweInputStream getCompressedIS(InputStream is) throws IOException { + int amount = is.read(); + is = new BufferedInputStream(is, Settings.BUFFER_SIZE); + if (amount == 0) { + return new FaweInputStream(is); + } + LZ4Factory factory = LZ4Factory.fastestInstance(); + boolean gzip = amount > 6; + if (gzip) { + is = new GZIPInputStream(is); + } + amount = (1 + ((amount - 1) % 3)) + (amount > 3 ? 1 : 0); + for (int i = 0; i < amount; i++) { + is = new LZ4InputStream(is); + } + return new FaweInputStream(is); + } + public static URL upload(UUID uuid, String file, String extension, final RunnableVal writeTask) { if (writeTask == null) { Fawe.debug("&cWrite task cannot be null"); @@ -157,6 +209,46 @@ public class MainUtil { } } + public static void sendCompressedMessage(FaweStreamChangeSet set, FawePlayer actor) + { + try { + int elements = set.size(); + int compressedSize = set.getCompressedSize(); + if (compressedSize == 0) { + return; + } + /* + * BlockVector + * - reference to the object --> 8 bytes + * - object header (java internals) --> 8 bytes + * - double x, y, z --> 24 bytes + * + * BaseBlock + * - reference to the object --> 8 bytes + * - object header (java internals) --> 8 bytes + * - short id, data --> 4 bytes + * - NBTCompound (assuming null) --> 4 bytes + * + * There are usually two lists for the block changes: + * 2 * BlockVector + 2 * BaseBlock = 128b + * + * WE has a lot more overhead, this is just a generous lower bound + * + * This compares FAWE's usage to standard WE. + */ + int total = 128 * elements; + + int ratio = total / compressedSize; + int saved = total - compressedSize; + + if (ratio > 3 && Thread.currentThread() != Fawe.get().getMainThread() && actor != null) { + BBC.COMPRESSED.send(actor, saved, ratio); + } + } catch (Exception e) { + MainUtil.handleError(e); + } + } + public static File copyFile(File jar, String resource, File output) { try { if (output == null) { @@ -209,46 +301,6 @@ public class MainUtil { return null; } - public static void sendCompressedMessage(FaweStreamChangeSet set, Actor actor) - { - try { - int elements = set.size(); - int compressedSize = set.getCompressedSize(); - if (compressedSize == 0) { - return; - } - /* - * BlockVector - * - reference to the object --> 8 bytes - * - object header (java internals) --> 8 bytes - * - double x, y, z --> 24 bytes - * - * BaseBlock - * - reference to the object --> 8 bytes - * - object header (java internals) --> 8 bytes - * - short id, data --> 4 bytes - * - NBTCompound (assuming null) --> 4 bytes - * - * There are usually two lists for the block changes: - * 2 * BlockVector + 2 * BaseBlock = 128b - * - * WE has a lot more overhead, this is just a generous lower bound - * - * This compares FAWE's usage to standard WE. - */ - int total = 128 * elements; - - int ratio = total / compressedSize; - int saved = total - compressedSize; - - if (ratio > 3 && Thread.currentThread() != Fawe.get().getMainThread() && actor != null && actor.isPlayer() && actor.getSessionKey().isActive()) { - BBC.COMPRESSED.send(actor, saved, ratio); - } - } catch (Exception e) { - MainUtil.handleError(e); - } - } - public static void handleError(Throwable e) { handleError(e, true); } diff --git a/core/src/main/java/com/boydti/fawe/util/MemUtil.java b/core/src/main/java/com/boydti/fawe/util/MemUtil.java index 201e9fa2..defc1d0f 100644 --- a/core/src/main/java/com/boydti/fawe/util/MemUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/MemUtil.java @@ -17,6 +17,16 @@ public class MemUtil { return memory.get(); } + public static boolean isMemoryLimitedSlow() { + if (memory.get()) { + System.gc(); + System.gc(); + calculateMemory(); + return memory.get(); + } + return false; + } + public static int calculateMemory() { final long heapSize = Runtime.getRuntime().totalMemory(); final long heapMaxSize = Runtime.getRuntime().maxMemory(); @@ -46,6 +56,8 @@ public class MemUtil { } public static void memoryLimitedTask() { + System.gc(); + System.gc(); for (Runnable task : memoryLimitedTasks) { task.run(); } diff --git a/core/src/main/java/com/boydti/fawe/util/ReflectionUtils.java b/core/src/main/java/com/boydti/fawe/util/ReflectionUtils.java index 62ea5d2a..51ced849 100644 --- a/core/src/main/java/com/boydti/fawe/util/ReflectionUtils.java +++ b/core/src/main/java/com/boydti/fawe/util/ReflectionUtils.java @@ -188,6 +188,15 @@ public static List getList(List list) { } } + public static void setField(String fieldName, Object instance, Object value) { + try { + Field field = instance.getClass().getDeclaredField(fieldName); + setField(field, instance, value); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + public static void setField(final Field field, final Object instance, final Object value) { if (field == null) { throw new RuntimeException("No such field"); diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index 4223918e..6cfc6815 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -23,7 +23,6 @@ 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.FaweLimit; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FaweQueue; @@ -38,27 +37,22 @@ import com.boydti.fawe.object.changeset.MemoryOptimizedHistory; import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.extent.FastWorldEditExtent; import com.boydti.fawe.object.extent.FaweRegionExtent; -import com.boydti.fawe.object.extent.MemoryCheckingExtent; import com.boydti.fawe.object.extent.NullExtent; import com.boydti.fawe.object.extent.ProcessedWEExtent; -import com.boydti.fawe.object.progress.DefaultProgressTracker; +import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MemUtil; import com.boydti.fawe.util.Perm; import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.TaskManager; -import com.boydti.fawe.util.WEManager; import com.boydti.fawe.wrappers.WorldWrapper; +import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.blocks.BlockType; -import com.sk89q.worldedit.command.tool.BrushTool; -import com.sk89q.worldedit.command.tool.Tool; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; -import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.extent.EditSessionEvent; -import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.ChangeSetExtent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.MaskingExtent; @@ -133,8 +127,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Level; -import java.util.logging.Logger; +import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -153,9 +146,6 @@ import static com.sk89q.worldedit.regions.Regions.minimumBlockY; * using the {@link ChangeSetExtent}.

*/ public class EditSession implements Extent { - - private final Logger log = Logger.getLogger(EditSession.class.getCanonicalName()); - /** * Used by {@link #setBlock(Vector, BaseBlock, Stage)} to * determine which {@link Extent}s should be bypassed. @@ -165,25 +155,113 @@ public class EditSession implements Extent { } private World world; - private Actor actor; - private FaweChangeSet changeSet; - private EditSessionWrapper wrapper; - private MaskingExtent maskingExtent; - private FaweRegionExtent regionExtent; - private Extent primaryExtent; - private HistoryExtent history; - private Extent bypassReorderHistory; - private Extent bypassHistory; - private Extent bypassNone; - private SurvivalModeExtent lazySurvivalExtent; - private boolean fastmode; - private Mask oldMask; - private FaweLimit limit = FaweLimit.MAX.copy(); private FaweQueue queue; + private Extent bypassNone; + private HistoryExtent history; + private Extent bypassHistory; + private Extent bypassAll; + private FaweLimit limit; + private FawePlayer player; + private FaweChangeSet changeTask; + + private int changes = 0; + private BlockBag blockBag; public static final UUID CONSOLE = UUID.fromString("1-1-3-3-7"); public static final BaseBiome nullBiome = new BaseBiome(0); public static final BaseBlock nullBlock = FaweCache.CACHE_BLOCK[0]; + private static final Vector[] recurseDirections = { + PlayerDirection.NORTH.vector(), + PlayerDirection.EAST.vector(), + PlayerDirection.SOUTH.vector(), + PlayerDirection.WEST.vector(), + PlayerDirection.UP.vector(), + PlayerDirection.DOWN.vector(), }; + + public EditSession(@Nonnull World world, @Nullable FawePlayer player, @Nullable FaweLimit limit, @Nullable FaweChangeSet changeSet, @Nullable RegionWrapper[] allowedRegions, @Nullable Boolean autoQueue, @Nullable Boolean fastmode, @Nullable Boolean checkMemory, @Nullable Boolean combineStages, @Nullable BlockBag blockBag, @Nullable EventBus bus, @Nullable EditSessionEvent event) { + checkNotNull(world); + this.world = world = WorldWrapper.wrap((AbstractWorld) world); + if (bus == null) { + bus = WorldEdit.getInstance().getEventBus(); + } + if (event == null) { + event = new EditSessionEvent(world, player == null ? null : (player.getPlayer()), -1, null); + } + event.setEditSession(this); + if (player == null) { + player = FawePlayer.wrap(event.getActor()); + } + this.player = player; + if (changeSet == null) { + if (Settings.STORE_HISTORY_ON_DISK) { + UUID uuid = player == null ? CONSOLE : player.getUUID(); + changeSet = new DiskStorageHistory(world, uuid); + } else if (Settings.COMBINE_HISTORY_STAGE && Settings.COMPRESSION_LEVEL == 0) { + changeSet = new CPUOptimizedChangeSet(world); + } else { + changeSet = new MemoryOptimizedHistory(world); + } + } + if (limit == null) { + if (player == null) { + limit = FaweLimit.MAX.copy(); + } else { + limit = player.getLimit(); + } + } + if (allowedRegions == null) { + if (player != null && !player.hasWorldEditBypass()) { + allowedRegions = player.getCurrentRegions(); + if (allowedRegions.length == 1 && allowedRegions[0].isGlobal()) { + allowedRegions = null; + } + } + } + if (autoQueue == null) { + autoQueue = true; + } + if (fastmode == null) { + if (player == null) { + fastmode = Settings.CONSOLE_HISTORY; + } else { + fastmode = player.getSession().hasFastMode(); + } + } + if (checkMemory == null) { + checkMemory = player != null && !fastmode; + } + if (combineStages == null) { + combineStages = Settings.COMBINE_HISTORY_STAGE; + } + if (checkMemory) { + if (MemUtil.isMemoryLimitedSlow()) { + if (Perm.hasPermission(player, "worldedit.fast")) { + BBC.WORLDEDIT_OOM_ADMIN.send(player); + } + throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_LOW_MEMORY); + } + } + if (allowedRegions != null && allowedRegions.length == 0) { + throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_NO_REGION); + } + this.blockBag = blockBag; + this.limit = limit; + this.queue = SetQueue.IMP.getNewQueue(Fawe.imp().getWorldName(world), fastmode, autoQueue); + this.bypassAll = wrapExtent(new FastWorldEditExtent(world, queue), bus, event, Stage.BEFORE_CHANGE); + this.bypassHistory = (bypassNone = wrapExtent(bypassAll, bus, event, Stage.BEFORE_REORDER)); + if (!fastmode && !(changeSet instanceof NullChangeSet)) { + if (combineStages) { + changeTask = changeSet; + changeSet.addChangeTask(queue); + } else { + this.bypassNone = (history = new HistoryExtent(this, bypassHistory, changeSet, queue)); + } + } + if (allowedRegions != null) { + this.bypassNone = new ProcessedWEExtent(bypassNone, allowedRegions, limit); + } + bypassNone = wrapExtent(bypassNone, bus, event, Stage.BEFORE_HISTORY); + } /** * Create a new instance. @@ -210,9 +288,6 @@ public class EditSession implements Extent { this(WorldEdit.getInstance().getEventBus(), world, maxBlocks, blockBag, new EditSessionEvent(world, null, maxBlocks, null)); } - private int changes = 0; - private BlockBag blockBag; - /** * Construct the object with a maximum number of blocks and a block bag. * @@ -223,167 +298,12 @@ public class EditSession implements Extent { * @param event the event to call with the extent */ public EditSession(final EventBus eventBus, World world, final int maxBlocks, @Nullable final BlockBag blockBag, EditSessionEvent event) { - checkNotNull(eventBus); - checkArgument(maxBlocks >= -1, "maxBlocks >= -1 required"); - checkNotNull(event); - event.setEditSession(this); - - this.actor = event.getActor(); - // TODO block bag - this.blockBag = blockBag; - - // Invalid world: return null extent - if (world == null) { - Extent extent = this.regionExtent = new NullExtent(world, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); - this.bypassReorderHistory = extent; - this.bypassHistory = extent; - this.bypassNone = extent; - this.changeSet = new NullChangeSet(world); - this.wrapper = Fawe.imp().getEditSessionWrapper(this); - return; - } - - // Wrap the world - this.world = (world = WorldWrapper.wrap((AbstractWorld) world)); - - // Delegate some methods to an implementation specific class - this.wrapper = Fawe.imp().getEditSessionWrapper(this); - - // Not a player; bypass history - if ((actor == null) || !actor.isPlayer()) { - this.queue = SetQueue.IMP.getNewQueue(Fawe.imp().getWorldName(world), true, true); - queue.addEditSession(this); - Extent extent = primaryExtent = new FastWorldEditExtent(world, queue); - // Everything bypasses - extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_CHANGE); - extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_REORDER); - // History - if (Settings.CONSOLE_HISTORY) { - this.changeSet = Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(world, CONSOLE) : (Settings.COMBINE_HISTORY_STAGE && Settings.COMPRESSION_LEVEL == 0) ? new CPUOptimizedChangeSet(world) : new MemoryOptimizedHistory(world); - if (Settings.COMBINE_HISTORY_STAGE) { - changeSet.addChangeTask(queue); - } else { - extent = history = new HistoryExtent(this, limit, extent, changeSet, queue); - } - } - extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_HISTORY); - this.bypassReorderHistory = primaryExtent; - this.bypassHistory = primaryExtent; - this.bypassNone = extent; - this.changeSet = new NullChangeSet(world); - return; - } - - Extent extent; - RegionWrapper[] mask; - final FawePlayer fp = FawePlayer.wrap(actor); - final LocalSession session = fp.getSession(); - this.fastmode = session.hasFastMode(); - boolean bypass = fp.hasWorldEditBypass(); - this.queue = SetQueue.IMP.getNewQueue(Fawe.imp().getWorldName(world), bypass, true); - queue.setProgressTracker(new DefaultProgressTracker(fp)); - if (bypass) { - queue.addEditSession(this); - // Bypass skips processing and area restrictions - extent = primaryExtent = new FastWorldEditExtent(world, queue); - if (this.hasFastMode()) { - // Fastmode skips history and memory checks - extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_CHANGE); - extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_REORDER); - extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_HISTORY); - this.bypassReorderHistory = extent; - this.bypassHistory = extent; - this.bypassNone = extent; - return; - } - mask = null; - } else { - queue.addEditSession(this); - this.limit = fp.getLimit(); - mask = WEManager.IMP.getMask(fp); - if (mask.length == 0) { - // No allowed area; return null extent - extent = this.regionExtent = new NullExtent(world, BBC.WORLDEDIT_CANCEL_REASON_NO_REGION); - this.bypassReorderHistory = extent; - this.bypassHistory = extent; - this.bypassNone = extent; - return; - } - extent = primaryExtent = new FastWorldEditExtent(world, queue); - if (this.hasFastMode()) { - // Fastmode skips history, masking, and memory checks - extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_CHANGE); - extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_REORDER); - // Restrictions - extent = new ProcessedWEExtent(extent, mask, limit); - extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_HISTORY); - this.bypassReorderHistory = extent; - this.bypassHistory = extent; - this.bypassNone = extent; - return; - } else { - if (MemUtil.isMemoryLimited()) { - fp.sendMessage(BBC.WORLDEDIT_CANCEL_REASON.format(BBC.WORLDEDIT_CANCEL_REASON_LOW_MEMORY.s())); - if (Perm.hasPermission(fp, "worldedit.fast")) { - BBC.WORLDEDIT_OOM_ADMIN.send(fp); - } - // Memory limit reached; return null extent - extent = this.regionExtent = new NullExtent(world, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); - this.bypassReorderHistory = extent; - this.bypassHistory = extent; - this.bypassNone = extent; - return; - } - } - // Perform memory checks after reorder - extent = new MemoryCheckingExtent(fp, extent); - } - // Include history, masking and memory checking. - Extent wrapped; - // First two events - extent = wrapped = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_CHANGE); - extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_REORDER); - - // History - this.changeSet = Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(world, actor.getUniqueId()) : (Settings.COMBINE_HISTORY_STAGE && Settings.COMPRESSION_LEVEL == 0) ? new CPUOptimizedChangeSet(world) : new MemoryOptimizedHistory(world); - this.changeSet = this.wrapper.wrapChangeSet(this, limit, extent, this.changeSet, queue, fp); - if (Settings.COMBINE_HISTORY_STAGE) { - changeSet.addChangeTask(queue); - } else { - extent = history = new HistoryExtent(this, limit, extent, changeSet, queue); - } - // Region restrictions if mask is not null - if (mask != null) { - extent = this.regionExtent = new ProcessedWEExtent(extent, mask, limit); - } - - // Masking - final Player skp = (Player) actor; - final int item = skp.getItemInHand(); - boolean hasMask = session.getMask() != null; - if ((item != 0) && (!hasMask)) { - try { - final Tool tool = session.getTool(item); - if ((tool != null) && (tool instanceof BrushTool)) { - hasMask = ((BrushTool) tool).getMask() != null; - } - } catch (final Exception e) {} - } - if (hasMask) { - extent = this.maskingExtent = new MaskingExtent(extent, Masks.alwaysTrue()); - } - - // Before history event - extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_HISTORY); - - this.bypassReorderHistory = wrapped; - this.bypassHistory = wrapped; - this.bypassNone = extent; - return; + this(world, null, null, null, null, true, null, null, null, blockBag, eventBus, event); } public FaweRegionExtent getRegionExtent() { - return regionExtent; + ExtentTraverser traverser = new ExtentTraverser(bypassNone).find(FaweRegionExtent.class); + return traverser == null ? null : traverser.get(); } /** @@ -391,27 +311,24 @@ public class EditSession implements Extent { * @return */ @Nullable - public Actor getActor() { - return actor; + public FawePlayer getPlayer() { + return player; } public boolean cancel() { - // Cancel this - if (primaryExtent != null && queue != null) { - try { - WEManager.IMP.cancelEdit(primaryExtent, BBC.WORLDEDIT_CANCEL_REASON_MANUAL); - } catch (Throwable ignore) {} - NullExtent nullExtent = new NullExtent(world, BBC.WORLDEDIT_CANCEL_REASON_MANUAL); - primaryExtent = nullExtent; - regionExtent = nullExtent; - bypassReorderHistory = nullExtent; - bypassHistory = nullExtent; - bypassNone = nullExtent; - dequeue(); - queue.clear(); - return true; + ExtentTraverser traverser = new ExtentTraverser(bypassNone); + NullExtent nullExtent = new NullExtent(world, BBC.WORLDEDIT_CANCEL_REASON_MANUAL); + while (traverser != null) { + ExtentTraverser next = traverser.next(); + traverser.setNext(nullExtent); + traverser = next; } - return false; + bypassHistory = nullExtent; + bypassNone = nullExtent; + bypassAll = nullExtent; + dequeue(); + queue.clear(); + return true; } public void dequeue() { @@ -426,12 +343,8 @@ public class EditSession implements Extent { } } - public FastWorldEditExtent getPrimaryExtent() { - return (FastWorldEditExtent) primaryExtent; - } - public void debug(BBC message, Object... args) { - message.send(actor, args); + message.send(player, args); } public FaweQueue getQueue() { @@ -474,15 +387,21 @@ public class EditSession implements Extent { * @return the change set */ public ChangeSet getChangeSet() { - return this.changeSet; + return history != null ? history.getChangeSet() : changeTask; } public void setChangeSet(FaweChangeSet set) { - if (history != null) { - history.setChangeSet(set); + if (set == null) { + disableHistory(true); + } else { + if (history != null) { + history.setChangeSet(set); + } else { + changeTask = set; + set.addChangeTask(queue); + } } changes++; - this.changeSet = set; } /** @@ -510,14 +429,13 @@ public class EditSession implements Extent { * @return whether the queue is enabled */ public boolean isQueueEnabled() { - return false; + return true; } /** * Queue certain types of block for better reproduction of those blocks. */ - public void enableQueue() { - } + public void enableQueue() {} /** * Disable the queue. This will flush the queue. @@ -534,7 +452,8 @@ public class EditSession implements Extent { * @return mask, may be null */ public Mask getMask() { - return this.oldMask; + ExtentTraverser maskingExtent = new ExtentTraverser(bypassNone).find(MaskingExtent.class); + return maskingExtent != null ? maskingExtent.get().getMask() : null; } /** @@ -542,15 +461,15 @@ public class EditSession implements Extent { * * @param mask mask or null */ - public void setMask(final Mask mask) { - if (this.maskingExtent == null) { - return; - } - this.oldMask = mask; + public void setMask(Mask mask) { if (mask == null) { - this.maskingExtent.setMask(Masks.alwaysTrue()); - } else { - this.maskingExtent.setMask(mask); + mask = Masks.alwaysTrue(); + } + ExtentTraverser maskingExtent = new ExtentTraverser(bypassNone).find(MaskingExtent.class); + if (maskingExtent != null) { + maskingExtent.get().setMask(mask); + } else if (mask != Masks.alwaysTrue()) { + bypassNone = new MaskingExtent(bypassNone, mask); } } @@ -575,10 +494,12 @@ public class EditSession implements Extent { * @return the survival simulation extent */ public SurvivalModeExtent getSurvivalExtent() { - if (this.lazySurvivalExtent == null) { - this.lazySurvivalExtent = new SurvivalModeExtent(bypassReorderHistory, world); + ExtentTraverser survivalExtent = new ExtentTraverser(bypassNone).find(SurvivalModeExtent.class); + if (survivalExtent != null) { + return survivalExtent.get(); + } else { + return (SurvivalModeExtent) (bypassNone = new SurvivalModeExtent(bypassNone, getWorld())); } - return lazySurvivalExtent; } /** @@ -590,7 +511,31 @@ public class EditSession implements Extent { * @param enabled true to enable */ public void setFastMode(final boolean enabled) { - this.fastmode = enabled; + disableHistory(enabled); + } + + /** + * Disable history (or re-enable) + * @param disableHistory + */ + public void disableHistory(boolean disableHistory) { + if (history == null) { + return; + } + ExtentTraverser traverseHistory = new ExtentTraverser(bypassNone).find(HistoryExtent.class); + if (disableHistory) { + if (traverseHistory != null) { + ExtentTraverser beforeHistory = traverseHistory.previous(); + ExtentTraverser afterHistory = traverseHistory.next(); + beforeHistory.setNext(afterHistory.get()); + } + } else if (traverseHistory == null) { + ExtentTraverser traverseBypass = new ExtentTraverser(bypassNone).find(bypassHistory); + if (traverseBypass != null) { + ExtentTraverser beforeHistory = traverseBypass.previous(); + beforeHistory.setNext(history); + } + } } /** @@ -602,7 +547,7 @@ public class EditSession implements Extent { * @return true if enabled */ public boolean hasFastMode() { - return this.fastmode; + return history == null; } /** @@ -661,16 +606,17 @@ public class EditSession implements Extent { } public BaseBlock getLazyBlock(int x, int y, int z) { - if (limit != null && limit.MAX_CHECKS-- < 0) { + if (!limit.MAX_CHECKS()) { throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS); } int combinedId4Data = queue.getCombinedId4DataDebug(x, y, z, 0, this); - if (!FaweCache.hasNBT(combinedId4Data >> 4)) { + int id = FaweCache.getId(combinedId4Data); + if (!FaweCache.hasNBT(id)) { return FaweCache.CACHE_BLOCK[combinedId4Data]; } try { - BaseBlock block = this.world.getBlock(new Vector(x, y, z)); - return block; + CompoundTag tile = queue.getTileEntity(x, y, z); + return new BaseBlock(id, FaweCache.getData(combinedId4Data), tile); } catch (Throwable e) { MainUtil.handleError(e); return FaweCache.CACHE_BLOCK[combinedId4Data]; @@ -691,7 +637,7 @@ public class EditSession implements Extent { */ @Deprecated public int getBlockType(final Vector position) { - if (limit != null && limit.MAX_CHECKS-- < 0) { + if (!limit.MAX_CHECKS()) { throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS); } int combinedId4Data = queue.getCombinedId4DataDebug(position.getBlockX(), position.getBlockY(), position.getBlockZ(), 0, this); @@ -707,7 +653,7 @@ public class EditSession implements Extent { */ @Deprecated public int getBlockData(final Vector position) { - if (limit != null && limit.MAX_CHECKS-- < 0) { + if (!limit.MAX_CHECKS()) { throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS); } int combinedId4Data = queue.getCombinedId4DataDebug(position.getBlockX(), position.getBlockY(), position.getBlockZ(), 0, this); @@ -750,7 +696,104 @@ public class EditSession implements Extent { * @return height of highest block found or 'minY' */ public int getHighestTerrainBlock(final int x, final int z, final int minY, final int maxY, final boolean naturalOnly) { - return this.wrapper.getHighestTerrainBlock(x, z, minY, maxY, naturalOnly); + Vector pt = new Vector(x, 0, z); + for (int y = maxY; y >= minY; --y) { + BaseBlock block = getLazyBlock(x, y, z); + final int id = block.getId(); + int data; + switch (id) { + case 0: { + continue; + } + case 2: + case 4: + case 13: + case 14: + case 15: + case 20: + case 21: + case 22: + case 25: + case 30: + case 32: + case 37: + case 39: + case 40: + case 41: + case 42: + case 45: + case 46: + case 47: + case 48: + case 49: + case 51: + case 52: + case 54: + case 55: + case 56: + case 57: + case 58: + case 60: + case 61: + case 62: + case 7: + case 8: + case 9: + case 10: + case 11: + case 73: + case 74: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + case 85: + case 87: + case 88: + case 101: + case 102: + case 103: + case 110: + case 112: + case 113: + case 117: + case 121: + case 122: + case 123: + case 124: + case 129: + case 133: + case 138: + case 137: + case 140: + case 165: + case 166: + case 169: + case 170: + case 172: + case 173: + case 174: + case 176: + case 177: + case 181: + case 182: + case 188: + case 189: + case 190: + case 191: + case 192: + return y; + default: + data = 0; + } + if (naturalOnly ? BlockType.isNaturalTerrainBlock(id, data) : !BlockType.canPassThrough(id, data)) { + return y; + } + } + return minY; } /** @@ -770,7 +813,7 @@ public class EditSession implements Extent { case BEFORE_CHANGE: return this.bypassHistory.setBlock(position, block); case BEFORE_REORDER: - return this.bypassReorderHistory.setBlock(position, block); + return this.bypassAll.setBlock(position, block); } throw new RuntimeException("New enum entry added that is unhandled here"); @@ -802,7 +845,7 @@ public class EditSession implements Extent { public boolean smartSetBlock(final Vector position, final BaseBlock block) { this.changes++; try { - return this.bypassReorderHistory.setBlock(position, block); + return this.bypassAll.setBlock(position, block); } catch (final WorldEditException e) { throw new RuntimeException("Unexpected exception", e); } @@ -894,7 +937,10 @@ public class EditSession implements Extent { */ @Deprecated public void rememberChange(final Vector position, final BaseBlock existing, final BaseBlock block) { - this.changeSet.add(new BlockChange(position.toBlockVector(), existing, block)); + ChangeSet changeSet = getChangeSet(); + if (changeSet != null) { + changeSet.add(new BlockChange(position.toBlockVector(), existing, block)); + } } /** @@ -904,9 +950,9 @@ public class EditSession implements Extent { */ public void undo(final EditSession editSession) { final UndoContext context = new UndoContext(); - context.setExtent(editSession.primaryExtent); + context.setExtent(editSession.bypassAll); editSession.getQueue().setChangeTask(null); - Operations.completeSmart(ChangeSetExecutor.createUndo(this.changeSet, context), new Runnable() { + Operations.completeSmart(ChangeSetExecutor.createUndo(getChangeSet(), context), new Runnable() { @Override public void run() { editSession.flushQueue(); @@ -922,9 +968,9 @@ public class EditSession implements Extent { */ public void redo(final EditSession editSession) { final UndoContext context = new UndoContext(); - context.setExtent(editSession.primaryExtent); + context.setExtent(editSession.bypassAll); editSession.getQueue().setChangeTask(null); - Operations.completeSmart(ChangeSetExecutor.createRedo(this.changeSet, context), new Runnable() { + Operations.completeSmart(ChangeSetExecutor.createRedo(getChangeSet(), context), new Runnable() { @Override public void run() { editSession.flushQueue(); @@ -973,7 +1019,7 @@ public class EditSession implements Extent { } else { queue.dequeue(); } - if (changeSet != null) { + if (getChangeSet() != null) { if (Settings.COMBINE_HISTORY_STAGE && queue.size() > 0) { if (Fawe.get().isMainThread()) { SetQueue.IMP.flush(queue); @@ -988,13 +1034,13 @@ public class EditSession implements Extent { TaskManager.IMP.wait(running, Settings.QUEUE_DISCARD_AFTER); } } - changeSet.flush(); + ((FaweChangeSet) getChangeSet()).flush(); } } @Override public @Nullable Operation commit() { - return this.bypassNone.commit(); + return null; } /** @@ -1708,8 +1754,8 @@ public class EditSession implements Extent { if (pos.getBlockY() < 0) { pos = pos.setY(0); - } else if (((pos.getBlockY() + height) - 1) > this.world.getMaxY()) { - height = (this.world.getMaxY() - pos.getBlockY()) + 1; + } else if (((pos.getBlockY() + height) - 1) > 255) { + height = (255 - pos.getBlockY()) + 1; } final double invRadiusX = 1 / radiusX; @@ -1892,7 +1938,7 @@ public class EditSession implements Extent { continue; } - for (int y = this.world.getMaxY(); y >= 1; --y) { + for (int y = 255; y >= 1; --y) { final Vector pt = new Vector(x, y, z); final int id = this.getBlockType(pt); @@ -1946,7 +1992,7 @@ public class EditSession implements Extent { continue; } - for (int y = this.world.getMaxY(); y >= 1; --y) { + for (int y = 255; y >= 1; --y) { final Vector pt = new Vector(x, y, z); final int id = this.getBlockType(pt); @@ -1966,7 +2012,7 @@ public class EditSession implements Extent { } // Too high? - if (y == this.world.getMaxY()) { + if (y == 255) { break; } @@ -2020,7 +2066,7 @@ public class EditSession implements Extent { continue; } - loop: for (int y = this.world.getMaxY(); y >= 1; --y) { + loop: for (int y = 255; y >= 1; --y) { final Vector pt = new Vector(x, y, z); final int id = this.getBlockType(pt); final int data = this.getBlockData(pt); @@ -2279,7 +2325,7 @@ public class EditSession implements Extent { return FaweCache.getBlock((int) typeVariable.getValue(), (int) dataVariable.getValue()); } catch (final Exception e) { - EditSession.this.log.log(Level.WARNING, "Failed to create shape", e); + Fawe.debug("Failed to create shape: " + e); return null; } } @@ -2594,7 +2640,7 @@ public class EditSession implements Extent { return defaultBiomeType; } catch (final Exception e) { - EditSession.this.log.log(Level.WARNING, "Failed to create shape", e); + Fawe.debug("Failed to create shape: " + e); return null; } } @@ -2603,14 +2649,6 @@ public class EditSession implements Extent { return shape.generate(this, biomeType, hollow); } - private final Vector[] recurseDirections = { - PlayerDirection.NORTH.vector(), - PlayerDirection.EAST.vector(), - PlayerDirection.SOUTH.vector(), - PlayerDirection.WEST.vector(), - PlayerDirection.UP.vector(), - PlayerDirection.DOWN.vector(), }; - private double lengthSq(final double x, final double y, final double z) { return (x * x) + (y * y) + (z * z); } diff --git a/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index d1b35fdb..5e4c1223 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -106,7 +106,7 @@ public class ClipboardCommands { final int mx = origin.getBlockX(); final int my = origin.getBlockY(); final int mz = origin.getBlockZ(); - LazyClipboard lazyClipboard = new LazyClipboard() { + LazyClipboard lazyClipboard = new LazyClipboard(region) { @Override public BaseBlock getBlock(int x, int y, int z) { return editSession.getLazyBlock(mx + x, my + y, mz + z); diff --git a/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index 2b6eeb10..3857ab1f 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -117,9 +117,7 @@ public class SchematicCommands { in = new FileInputStream(f); } in = new BufferedInputStream(in); - final ClipboardReader reader = format.getReader(in); - final WorldData worldData = player.getWorld().getWorldData(); final Clipboard clipboard; if (reader instanceof SchematicReader) { diff --git a/core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java b/core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java index 652a6eb4..e2e739e0 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java +++ b/core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java @@ -78,26 +78,21 @@ public class SelectionCommand extends SimpleCommand { if (!testPermission(locals)) { throw new CommandPermissionsException(); } - Contextual operationFactory = delegate.call(args, locals); - Actor actor = locals.get(Actor.class); if (actor instanceof Player) { try { Player player = (Player) actor; LocalSession session = WorldEdit.getInstance().getSessionManager().get(player); Region selection = session.getSelection(player.getWorld()); - EditSession editSession = session.createEditSession(player); editSession.enableQueue(); locals.put(EditSession.class, editSession); session.tellVersion(player); - EditContext editContext = new EditContext(); editContext.setDestination(locals.get(EditSession.class)); editContext.setRegion(selection); Operation operation = operationFactory.createFromContext(editContext); - // Shortcut if (selection instanceof CuboidRegion && editSession.hasFastMode() && operation instanceof RegionVisitor) { CuboidRegion cuboid = (CuboidRegion) selection; diff --git a/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java b/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java index 822fcc82..c9a2cad1 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -302,7 +302,7 @@ public final class CommandManager { BBC.ACTION_COMPLETE.send(actor, (time / 1000d)); ChangeSet fcs = editSession.getChangeSet(); if (fcs != null && fcs instanceof FaweStreamChangeSet) { - MainUtil.sendCompressedMessage((FaweStreamChangeSet) fcs, editSession.getActor()); + MainUtil.sendCompressedMessage((FaweStreamChangeSet) fcs, editSession.getPlayer()); } } } diff --git a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java index a8872ad7..5ce30c25 100644 --- a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java +++ b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java @@ -23,6 +23,7 @@ import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; import com.boydti.fawe.object.clipboard.FaweClipboard; import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard; +import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector2D; @@ -189,6 +190,13 @@ public class BlockArrayClipboard implements Clipboard { return false; } + public boolean setTile(int x, int y, int z, CompoundTag tag) { + x -= mx; + y -= my; + z -= mz; + return IMP.setTile(x, y, z, tag); + } + public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException { x -= mx; y -= my; diff --git a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java index d4002df2..f8f5c053 100644 --- a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java +++ b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java @@ -19,12 +19,14 @@ package com.sk89q.worldedit.extent.clipboard.io; +import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; +import com.boydti.fawe.object.schematic.FaweFormat; import com.boydti.fawe.object.schematic.StructureFormat; +import com.boydti.fawe.util.MainUtil; import com.sk89q.jnbt.NBTConstants; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; - -import javax.annotation.Nullable; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; @@ -40,6 +42,8 @@ import java.util.Map; import java.util.Set; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; +import javax.annotation.Nullable; + import static com.google.common.base.Preconditions.checkNotNull; @@ -99,7 +103,10 @@ public enum ClipboardFormat { return "schematic"; } }, - // Added + /** + * The structure block format: + * http://minecraft.gamepedia.com/Structure_block_file_format + */ STRUCTURE("structure", "nbt") { @Override public ClipboardReader getReader(InputStream inputStream) throws IOException { @@ -128,10 +135,91 @@ public enum ClipboardFormat { public String getExtension() { return "nbt"; } - } + }, - // TODO add the FAWE clipboard / history formats - // .bd, .nbtf, .nbtt, .rabd + /** + * The FAWE file format: + * - Streamable for known dimensions with mode 0 + * - Streamable for unknown dimensions + * - 1: Max size 256 x 256 x 256 + * - 2: Max size 65535 x 65535 x 65535 + * - 3: Includes BlockChange information + * - O(1) Access to blocks if using compression level 0 and mode 0 + * + * DiskOptimizedClipboard: compression/mode -> 0/0 + * DiskStorageHistory: compression/mode -> Any/3 + * MemoryOptimizedHistory: compression/mode -> Any/3 + * FaweFormat: compression/mode -> Any/Any (slower) + * + */ + FAWE("fawe") { + /** + * Read a clipboard from a compressed stream (the first byte indicates the compression level) + * @param inputStream the input stream + * @return + * @throws IOException + */ + @Override + public ClipboardReader getReader(InputStream inputStream) throws IOException { + return new FaweFormat(MainUtil.getCompressedIS(inputStream)); + } + + /** + * Write a clipboard to a stream with compression level 8 + * @param outputStream the output stream + * @return + * @throws IOException + */ + @Override + public ClipboardWriter getWriter(OutputStream outputStream) throws IOException { + return getWriter(outputStream, 8); + } + + /** + * Write a clipboard to a stream + * @param os + * @param compression + * @return + * @throws IOException + */ + public ClipboardWriter getWriter(OutputStream os, int compression) throws IOException { + FaweFormat writer = new FaweFormat(new FaweOutputStream(os)); + writer.compress(compression); + return writer; + } + + /** + * Read or write blocks ids to a file + * @param file + * @return + * @throws IOException + */ + public DiskOptimizedClipboard getUncompressedReadWrite(File file) throws IOException { + return new DiskOptimizedClipboard(file); + } + + /** + * Read or write block ids to a new file + * @param width + * @param height + * @param length + * @param file + * @return + */ + public DiskOptimizedClipboard createUncompressedReadWrite(int width, int height, int length, File file) { + return new DiskOptimizedClipboard(width, height, length, file); + } + + @Override + public boolean isFormat(File file) { + return file.getName().endsWith(".fawe") || file.getName().endsWith(".bd"); + } + + @Override + public String getExtension() { + return "fawe"; + } + }, ; @@ -144,7 +232,7 @@ public enum ClipboardFormat { * * @param aliases an array of aliases by which this format may be referred to */ - private ClipboardFormat(String ... aliases) { + ClipboardFormat(String ... aliases) { this.aliases = aliases; } diff --git a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicWriter.java b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicWriter.java new file mode 100644 index 00000000..1830e07b --- /dev/null +++ b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicWriter.java @@ -0,0 +1,242 @@ +package com.sk89q.worldedit.extent.clipboard.io; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.object.RunnableVal2; +import com.boydti.fawe.object.clipboard.FaweClipboard; +import com.boydti.fawe.util.ReflectionUtils; +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.registry.WorldData; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Writes schematic files based that are compatible with MCEdit and other editors. + */ +public class SchematicWriter implements ClipboardWriter { + + private static final int MAX_SIZE = Short.MAX_VALUE - Short.MIN_VALUE; + private final NBTOutputStream outputStream; + + /** + * Create a new schematic writer. + * + * @param outputStream the output stream to write to + */ + public SchematicWriter(NBTOutputStream outputStream) { + checkNotNull(outputStream); + this.outputStream = outputStream; + } + + private static class ForEach extends RunnableVal2 { + int x = -1; + int y = 0; + int z = 0; + int index = 0; + + public int[] yarea; + public int[] zwidth; + public byte[] blocks; + public byte[] blockData; + public byte[] addBlocks; + public List tileEntities; + + public ForEach(int[] yarea, int[] zwidth, byte[] blocks, byte[] blockData, List tileEntities) { + this.yarea = yarea; + this.zwidth = zwidth; + this.blocks = blocks; + this.blockData = blockData; + this.tileEntities = tileEntities; + } + + @Override + public void run(Vector point, BaseBlock block) { + int x = (int) point.x; + int y = (int) point.y; + int z = (int) point.z; + if (this.x == x - 1 && this.y == y && this.z == z) { + index++; + x++; + } else { + index = yarea[y] + zwidth[z] + x; + } + int id = block.getId(); + blocks[index] = (byte) id; + if (FaweCache.hasData(id)) { + blockData[index] = (byte) block.getData(); + if (id > 255) { + if (addBlocks == null) { // Lazily create section + addBlocks = new byte[(blocks.length >> 1) + 1]; + } + addBlocks[index >> 1] = (byte) (((index & 1) == 0) ? addBlocks[index >> 1] & 0xF0 | (id >> 8) & 0xF : addBlocks[index >> 1] & 0xF | ((id >> 8) & 0xF) << 4); + } + } + CompoundTag rawTag = block.getNbtData(); + if (rawTag != null) { + Map values = ReflectionUtils.getMap(rawTag.getValue()); + values.put("id", new StringTag(block.getNbtId())); + values.put("x", new IntTag(x)); + values.put("y", new IntTag(y)); + values.put("z", new IntTag(z)); + tileEntities.add(rawTag); + } + } + } + + @Override + public void write(Clipboard clipboard, WorldData data) throws IOException { + outputStream.writeNamedTag("Schematic", writeTag(clipboard)); + } + + public static CompoundTag writeTag(Clipboard clipboard) { + Region region = clipboard.getRegion(); + Vector origin = clipboard.getOrigin(); + Vector min = region.getMinimumPoint(); + Vector offset = min.subtract(origin); + int width = region.getWidth(); + int height = region.getHeight(); + int length = region.getLength(); + + if (width > MAX_SIZE) { + throw new IllegalArgumentException("Width of region too large for a .schematic"); + } + if (height > MAX_SIZE) { + throw new IllegalArgumentException("Height of region too large for a .schematic"); + } + if (length > MAX_SIZE) { + throw new IllegalArgumentException("Length of region too large for a .schematic"); + } + + // ==================================================================== + // Metadata + // ==================================================================== + + HashMap schematic = new HashMap(); + schematic.put("Width", new ShortTag((short) width)); + schematic.put("Length", new ShortTag((short) length)); + schematic.put("Height", new ShortTag((short) height)); + schematic.put("Materials", new StringTag("Alpha")); + schematic.put("WEOriginX", new IntTag(min.getBlockX())); + schematic.put("WEOriginY", new IntTag(min.getBlockY())); + schematic.put("WEOriginZ", new IntTag(min.getBlockZ())); + schematic.put("WEOffsetX", new IntTag(offset.getBlockX())); + schematic.put("WEOffsetY", new IntTag(offset.getBlockY())); + schematic.put("WEOffsetZ", new IntTag(offset.getBlockZ())); + + final byte[] blocks = new byte[width * height * length]; + byte[] addBlocks = null; + final byte[] blockData = new byte[width * height * length]; + final List tileEntities = new ArrayList(); + // Precalculate index vars + int area = width * length; + final int[] yarea = new int[height]; + final int[] zwidth = new int[width]; + for (int i = 0; i < height; i++) { + yarea[i] = i * area; + } + for (int i = 0; i < width; i++) { + zwidth[i] = i * width; + } + if (clipboard instanceof BlockArrayClipboard) { + FaweClipboard faweClip = ((BlockArrayClipboard) clipboard).IMP; + ForEach forEach = new ForEach(yarea, zwidth, blocks, blockData, tileEntities); + faweClip.forEach(forEach, false); + addBlocks = forEach.addBlocks; + } else { + final int mx = (int) min.x; + final int my = (int) min.y; + final int mz = (int) min.z; + Vector mutable = new Vector(0, 0, 0); + ForEach forEach = new ForEach(yarea, zwidth, blocks, blockData, tileEntities); + for (Vector point : region) { + mutable.x = point.x - mx; + mutable.y = point.y - my; + mutable.z = point.z - mz; + forEach.run(mutable, clipboard.getBlock(point)); + } + addBlocks = forEach.addBlocks; + } + + schematic.put("Blocks", new ByteArrayTag(blocks)); + schematic.put("Data", new ByteArrayTag(blockData)); + schematic.put("TileEntities", new ListTag(CompoundTag.class, tileEntities)); + + if (addBlocks != null) { + schematic.put("AddBlocks", new ByteArrayTag(addBlocks)); + } + + List entities = new ArrayList(); + for (Entity entity : clipboard.getEntities()) { + BaseEntity state = entity.getState(); + + if (state != null) { + Map values = new HashMap(); + + // Put NBT provided data + CompoundTag rawTag = state.getNbtData(); + if (rawTag != null) { + values.putAll(rawTag.getValue()); + } + + // Store our location data, overwriting any + values.put("id", new StringTag(state.getTypeId())); + values.put("Pos", writeVector(entity.getLocation().toVector(), "Pos")); + values.put("Rotation", writeRotation(entity.getLocation(), "Rotation")); + + CompoundTag entityTag = new CompoundTag(values); + entities.add(entityTag); + } + } + + schematic.put("Entities", new ListTag(CompoundTag.class, entities)); + + CompoundTag schematicTag = new CompoundTag(schematic); + return schematicTag; + } + + private static Tag writeVector(Vector vector, String name) { + List list = new ArrayList(); + list.add(new DoubleTag(vector.getX())); + list.add(new DoubleTag(vector.getY())); + list.add(new DoubleTag(vector.getZ())); + return new ListTag(DoubleTag.class, list); + } + + private static Tag writeRotation(Location location, String name) { + List list = new ArrayList(); + list.add(new FloatTag(location.getYaw())); + list.add(new FloatTag(location.getPitch())); + return new ListTag(FloatTag.class, list); + } + + @Override + public void close() throws IOException { + outputStream.close(); + } + + public static Class inject() { + return SchematicWriter.class; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/sk89q/worldedit/function/operation/Operations.java b/core/src/main/java/com/sk89q/worldedit/function/operation/Operations.java index 65f2ab8e..5df6d03d 100644 --- a/core/src/main/java/com/sk89q/worldedit/function/operation/Operations.java +++ b/core/src/main/java/com/sk89q/worldedit/function/operation/Operations.java @@ -38,11 +38,9 @@ public final class Operations { * @throws WorldEditException WorldEdit exception */ public static void complete(Operation operation) throws WorldEditException { - try { - while (true) { - operation = operation.resume(context); - } - } catch (NullPointerException ignore) {} + while (operation != null) { + operation = operation.resume(context); + } } /** @@ -65,12 +63,12 @@ public final class Operations { */ public static void completeBlindly(Operation operation) { try { - while (true) { + while (operation != null) { operation = operation.resume(context); } } catch (WorldEditException e) { throw new RuntimeException(e); - } catch (NullPointerException ignore) {} + } } public static void completeSmart(final Operation op, final Runnable whenDone, final boolean threadsafe) { diff --git a/core/src/main/java/net/jpountz/lz4/LZ4InputStream.java b/core/src/main/java/net/jpountz/lz4/LZ4InputStream.java index 1e1ef192..0acc8424 100644 --- a/core/src/main/java/net/jpountz/lz4/LZ4InputStream.java +++ b/core/src/main/java/net/jpountz/lz4/LZ4InputStream.java @@ -71,7 +71,6 @@ public class LZ4InputStream extends InputStream { numBytesRemainingToSkip -= numBytesToSkip; decompressedBufferPosition += numBytesToSkip; } - return n - numBytesRemainingToSkip; } diff --git a/forge189/src/main/java/com/boydti/fawe/forge/ForgePlayer.java b/forge189/src/main/java/com/boydti/fawe/forge/ForgePlayer.java index eb23d929..144828fd 100644 --- a/forge189/src/main/java/com/boydti/fawe/forge/ForgePlayer.java +++ b/forge189/src/main/java/com/boydti/fawe/forge/ForgePlayer.java @@ -4,6 +4,7 @@ import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.wrappers.PlayerWrapper; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.forge.ForgeWorldEdit; import java.util.UUID; @@ -73,6 +74,6 @@ public class ForgePlayer extends FawePlayer { @Override public Player getPlayer() { - return ForgeWorldEdit.inst.wrap(this.parent); + return PlayerWrapper.wrap(ForgeWorldEdit.inst.wrap(this.parent)); } } diff --git a/sponge/build.gradle b/sponge/build.gradle index f3ef23fb..29a7889f 100644 --- a/sponge/build.gradle +++ b/sponge/build.gradle @@ -14,7 +14,7 @@ buildscript { } dependencies { classpath 'net.minecrell:VanillaGradle:2.0.3_1' - classpath 'net.minecraftforge.gradle:ForgeGradle:2.1-SNAPSHOT' + classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT' } } diff --git a/sponge/src/main/java/com/boydti/fawe/sponge/SpongePlayer.java b/sponge/src/main/java/com/boydti/fawe/sponge/SpongePlayer.java index 49a6e435..fb79963c 100644 --- a/sponge/src/main/java/com/boydti/fawe/sponge/SpongePlayer.java +++ b/sponge/src/main/java/com/boydti/fawe/sponge/SpongePlayer.java @@ -4,6 +4,7 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.wrappers.PlayerWrapper; import java.util.UUID; import net.minecraft.entity.player.EntityPlayerMP; import org.spongepowered.api.Sponge; @@ -71,6 +72,6 @@ public class SpongePlayer extends FawePlayer { @Override public com.sk89q.worldedit.entity.Player getPlayer() { - return (com.sk89q.worldedit.entity.Player) Fawe. imp().getWorldEditPlugin().wrap((EntityPlayerMP) this.parent); + return PlayerWrapper.wrap(Fawe. imp().getWorldEditPlugin().wrap((EntityPlayerMP) this.parent)); } }