Update to PS 3.4.1
- Added various optimizations for PlotSquared
- Support full schematic exporting (includes NBT now)

Added compression level option:
0 is no compression
1-9 = varying levels of compression at the expense of CPU
3 = Good fast compression
5 = Good high compression
8 = Lots of compression (going further has diminishing returns)

Add EditSession builder (for extra control over optimizations)
Added compression option to FaweChangeSet constructor
Added FAWE format (used for history / clipboard on disk)

Various minor optimizations

TODO bug fixes
This commit is contained in:
Jesse Boyd 2016-06-16 20:25:21 +10:00
parent fbb4ae9ddf
commit 9bf2d2b0c3
52 changed files with 2327 additions and 523 deletions

View File

@ -37,7 +37,7 @@ subprojects {
exclude(module: 'bukkit-classloader-check') exclude(module: 'bukkit-classloader-check')
} }
compile 'com.sk89q:worldguard:6.0.0-SNAPSHOT' 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 { repositories {

View File

@ -3,6 +3,7 @@ package com.boydti.fawe.bukkit;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FaweLocation;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.wrappers.PlayerWrapper;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.UUID; import java.util.UUID;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -92,7 +93,7 @@ public class BukkitPlayer extends FawePlayer<Player> {
@Override @Override
public com.sk89q.worldedit.entity.Player getPlayer() { public com.sk89q.worldedit.entity.Player getPlayer() {
return Fawe.<FaweBukkit> imp().getWorldEditPlugin().wrapPlayer(this.parent); return PlayerWrapper.wrap(Fawe.<FaweBukkit> imp().getWorldEditPlugin().wrapPlayer(this.parent));
} }
} }

View File

@ -8,6 +8,7 @@ import com.boydti.fawe.util.StringMan;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.entity.BukkitEntity;
import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File; import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;

View File

@ -399,7 +399,7 @@ public class Sniper {
FawePlayer<Object> fp = FawePlayer.wrap(getPlayer()); FawePlayer<Object> fp = FawePlayer.wrap(getPlayer());
LocalSession session = fp.getSession(); LocalSession session = fp.getSession();
baseQueue.enqueue(); baseQueue.enqueue();
session.remember(changeSet.toEditSession(fp.getPlayer())); session.remember(changeSet.toEditSession(fp));
changeQueue.flush(); changeQueue.flush();
com.sk89q.worldedit.world.World worldEditWorld = fp.getWorld(); com.sk89q.worldedit.world.World worldEditWorld = fp.getWorld();
changeQueue.setChangeSet(Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(worldEditWorld, fp.getUUID()) : new MemoryOptimizedHistory(worldEditWorld)); changeQueue.setChangeSet(Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(worldEditWorld, fp.getUUID()) : new MemoryOptimizedHistory(worldEditWorld));

View File

@ -131,7 +131,7 @@ public class BukkitQueue_1_10 extends BukkitQueue_0<Chunk, ChunkSection[], DataP
return; return;
} }
HashSet<EntityTrackerEntry> entities = new HashSet<>(); HashSet<EntityTrackerEntry> entities = new HashSet<>();
List<Entity>[] entitieSlices = playerChunk.chunk.getEntitySlices(); List<Entity>[] entitieSlices = nmsChunk.getEntitySlices();
for (List<Entity> slice : entitieSlices) { for (List<Entity> slice : entitieSlices) {
if (slice == null) { if (slice == null) {
continue; continue;
@ -152,7 +152,7 @@ public class BukkitQueue_1_10 extends BukkitQueue_0<Chunk, ChunkSection[], DataP
player.playerConnection.networkManager.a(); player.playerConnection.networkManager.a();
} }
// Send chunks // Send chunks
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(playerChunk.chunk, 65535); PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, 65535);
for (EntityPlayer player : players) { for (EntityPlayer player : players) {
player.playerConnection.sendPacket(packet); player.playerConnection.sendPacket(packet);
} }

View File

@ -10,7 +10,7 @@ import com.boydti.fawe.command.WorldEditRegion;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.regions.general.PlotSquaredFeature; import com.boydti.fawe.regions.general.plot.PlotSquaredFeature;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MemUtil; import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
@ -36,6 +36,7 @@ import com.sk89q.worldedit.extension.platform.PlatformManager;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.SchematicReader; import com.sk89q.worldedit.extent.clipboard.io.SchematicReader;
import com.sk89q.worldedit.extent.clipboard.io.SchematicWriter;
import com.sk89q.worldedit.extent.transform.BlockTransformExtent; import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch; import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
@ -259,7 +260,7 @@ public class Fawe {
HistoryCommands.inject(); // Translations HistoryCommands.inject(); // Translations
// Schematic // Schematic
SchematicReader.inject(); SchematicReader.inject();
// SchematicWriter.inject(); TODO SchematicWriter.inject();
ClipboardFormat.inject(); ClipboardFormat.inject();
// Brushes // Brushes
GravityBrush.inject(); // Fix for instant placement assumption GravityBrush.inject(); // Fix for instant placement assumption

View File

@ -10,11 +10,13 @@ import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MemUtil; import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.WEManager; import com.boydti.fawe.util.WEManager;
import com.boydti.fawe.wrappers.WorldWrapper;
import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
@ -27,6 +29,7 @@ import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
import com.sk89q.worldedit.world.AbstractWorld;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -59,30 +62,14 @@ import org.bukkit.Location;
* FaweAPI.[some method] * FaweAPI.[some method]
*/ */
public class FaweAPI { public class FaweAPI {
/** /**
* Get a new EditSessionfor a player<br> * Offers a lot of options for building an EditSession
* - The EditSession can be used from another thread<br> * @see com.boydti.fawe.util.EditSessionBuilder
* - 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)
* @param world * @param world
* @return * @return A new EditSessionBuilder
*/ */
public static EditSession getNewEditSession(World world) { public static EditSessionBuilder getEditSessionBuilder(World world) {
return WorldEdit.getInstance().getEditSessionFactory().getEditSession(world, -1); return new EditSessionBuilder(world);
} }
/** /**
@ -124,7 +111,7 @@ public class FaweAPI {
public static World getWorld(String worldName) { public static World getWorld(String worldName) {
for (World current : WorldEdit.getInstance().getServer().getWorlds()) { for (World current : WorldEdit.getInstance().getServer().getWorlds()) {
if (Fawe.imp().getWorldName(current).equals(worldName)) { if (Fawe.imp().getWorldName(current).equals(worldName)) {
return current; return WorldWrapper.wrap((AbstractWorld) current);
} }
} }
return null; return null;
@ -325,7 +312,7 @@ public class FaweAPI {
/** /**
* The DiskStorageHistory class is what FAWE uses to represent the undo on disk. * 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 world
* @param uuid * @param uuid
* @param index * @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<br> * If a schematic is too large to be pasted normally<br>
* - Skips any block history * - Skips any block history
* - Ignores nbt * - 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<br> * If a schematic is too large to be pasted normally<br>
* - Skips any block history * - Skips any block history
* - Ignores some block data * - 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 y_offset = loc.y + IntTag.class.cast(tagMap.get("WEOffsetY")).getValue();
final int z_offset = loc.z + IntTag.class.cast(tagMap.get("WEOffsetZ")).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); FaweQueue queue = SetQueue.IMP.getNewQueue(loc.world, true, true);
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
@ -477,9 +463,6 @@ public class FaweAPI {
} }
queue.enqueue(); queue.enqueue();
ids = null;
datas = null;
} }
/** /**
@ -513,4 +496,26 @@ public class FaweAPI {
public static BBC[] getTranslations() { public static BBC[] getTranslations() {
return BBC.values(); 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);
}
} }

View File

@ -5,7 +5,9 @@ import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.ByteTag; import com.sk89q.jnbt.ByteTag;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.DoubleTag; import com.sk89q.jnbt.DoubleTag;
import com.sk89q.jnbt.EndTag;
import com.sk89q.jnbt.FloatTag; import com.sk89q.jnbt.FloatTag;
import com.sk89q.jnbt.IntArrayTag;
import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.LongTag; import com.sk89q.jnbt.LongTag;
@ -14,6 +16,7 @@ import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.CuboidClipboard; import com.sk89q.worldedit.CuboidClipboard;
import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseBlock;
import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
@ -332,6 +335,10 @@ public class FaweCache {
return new ByteArrayTag(value); return new ByteArrayTag(value);
} }
public static IntArrayTag asTag(int[] value) {
return new IntArrayTag(value);
}
public static StringTag asTag(String value) { public static StringTag asTag(String value) {
return new StringTag(value); return new StringTag(value);
} }
@ -369,7 +376,22 @@ public class FaweCache {
return asTag((byte[]) value); return asTag((byte[]) value);
} else if (value instanceof Tag) { } else if (value instanceof Tag) {
return (Tag) value; return (Tag) value;
} else if (value == null) {
return null;
} else { } else {
Class<? extends Object> 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; return null;
} }
} }

View File

@ -6,7 +6,6 @@ import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.SetQueue;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.extension.platform.Actor;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -28,11 +27,8 @@ public class Cancel extends FaweCommand {
for (FaweQueue queue : queues) { for (FaweQueue queue : queues) {
Set<EditSession> sessions = queue.getEditSessions(); Set<EditSession> sessions = queue.getEditSessions();
for (EditSession session : sessions) { for (EditSession session : sessions) {
Actor actor = session.getActor(); FawePlayer currentPlayer = session.getPlayer();
if (actor == null) { if (currentPlayer == player) {
continue;
}
if (uuid.equals(actor.getUniqueId())) {
if (session.cancel()) { if (session.cancel()) {
cancelled++; cancelled++;
} }

View File

@ -41,7 +41,7 @@ public class Settings {
// Maybe confusing? // Maybe confusing?
// - `compression: false` just uses cheaper compression, but still compresses // - `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 boolean COMBINE_HISTORY_STAGE = false;
public static int PARALLEL_THREADS = 1; 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.use-disk", STORE_CLIPBOARD_ON_DISK);
options.put("clipboard.delete-after-days", DELETE_CLIPBOARD_AFTER_DAYS); options.put("clipboard.delete-after-days", DELETE_CLIPBOARD_AFTER_DAYS);
options.put("history.use-disk", STORE_HISTORY_ON_DISK); 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.chunk-wait-ms", CHUNK_WAIT);
options.put("history.delete-after-days", DELETE_HISTORY_AFTER_DAYS); options.put("history.delete-after-days", DELETE_HISTORY_AFTER_DAYS);
options.put("history.delete-on-logout", CLEAN_HISTORY_ON_LOGOUT); options.put("history.delete-on-logout", CLEAN_HISTORY_ON_LOGOUT);
options.put("history.enable-for-console", CONSOLE_HISTORY); 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("region-restrictions", REGION_RESTRICTIONS);
options.put("queue.extra-time-ms", ALLOCATE); options.put("queue.extra-time-ms", ALLOCATE);
options.put("queue.progress.display", DISPLAY_PROGRESS); options.put("queue.progress.display", DISPLAY_PROGRESS);
@ -144,7 +145,7 @@ public class Settings {
ENABLE_HARD_LIMIT = config.getBoolean("crash-mitigation"); ENABLE_HARD_LIMIT = config.getBoolean("crash-mitigation");
REGION_RESTRICTIONS = config.getBoolean("region-restrictions"); REGION_RESTRICTIONS = config.getBoolean("region-restrictions");
METRICS = config.getBoolean("metrics"); 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"); DELETE_HISTORY_AFTER_DAYS = config.getInt("history.delete-after-days");
CLEAN_HISTORY_ON_LOGOUT = config.getBoolean("history.delete-on-logout"); CLEAN_HISTORY_ON_LOGOUT = config.getBoolean("history.delete-on-logout");
CHUNK_WAIT = config.getInt("history.chunk-wait-ms"); CHUNK_WAIT = config.getInt("history.chunk-wait-ms");

View File

@ -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();
}
}

View File

@ -17,7 +17,32 @@ public class FaweLimit {
public static FaweLimit MAX; public static FaweLimit MAX;
static { 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_CHANGES = Integer.MAX_VALUE;
MAX.MAX_FAILS = Integer.MAX_VALUE; MAX.MAX_FAILS = Integer.MAX_VALUE;
MAX.MAX_CHECKS = Integer.MAX_VALUE; MAX.MAX_CHECKS = Integer.MAX_VALUE;
@ -26,6 +51,30 @@ public class FaweLimit {
MAX.MAX_ENTITIES = Integer.MAX_VALUE; 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) { 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_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); this.MAX_FAILS = section.getInt("max-fails", defaultLimit == null ? MAX_FAILS : defaultLimit.MAX_FAILS);

View File

@ -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();
}
}

View File

@ -178,7 +178,7 @@ public abstract class FawePlayer<T> {
for (int i = editIds.size() - 1; i >= 0; i--) { for (int i = editIds.size() - 1; i >= 0; i--) {
int index = editIds.get(i); int index = editIds.get(i);
FaweStreamChangeSet set = new DiskStorageHistory(world, uuid, index); FaweStreamChangeSet set = new DiskStorageHistory(world, uuid, index);
EditSession edit = set.toEditSession(getPlayer()); EditSession edit = set.toEditSession(FawePlayer.this);
if (world.equals(getWorld())) { if (world.equals(getWorld())) {
session.remember(edit, false, false); session.remember(edit, false, false);
} else { } else {

View File

@ -200,6 +200,7 @@ public abstract class FaweQueue {
if (Fawe.get().isMainThread()) { if (Fawe.get().isMainThread()) {
SetQueue.IMP.flush(this); SetQueue.IMP.flush(this);
} else { } else {
enqueue();
final AtomicBoolean running = new AtomicBoolean(true); final AtomicBoolean running = new AtomicBoolean(true);
addNotifyTask(new Runnable() { addNotifyTask(new Runnable() {
@Override @Override

View File

@ -26,9 +26,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/ */
public class HistoryExtent extends AbstractDelegateExtent { public class HistoryExtent extends AbstractDelegateExtent {
private com.boydti.fawe.object.changeset.FaweChangeSet changeSet; private FaweChangeSet changeSet;
private final FaweQueue queue; private final FaweQueue queue;
private final FaweLimit limit;
private final EditSession session; private final EditSession session;
/** /**
@ -37,15 +36,18 @@ public class HistoryExtent extends AbstractDelegateExtent {
* @param extent the extent * @param extent the extent
* @param changeSet the change set * @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); super(extent);
checkNotNull(changeSet); checkNotNull(changeSet);
this.limit = limit;
this.queue = queue; this.queue = queue;
this.changeSet = changeSet; this.changeSet = changeSet;
this.session = session; this.session = session;
} }
public FaweChangeSet getChangeSet() {
return changeSet;
}
public void setChangeSet(FaweChangeSet fcs) { public void setChangeSet(FaweChangeSet fcs) {
this.changeSet = fcs; this.changeSet = fcs;
} }

View File

@ -13,42 +13,42 @@ public class NullChangeSet extends FaweChangeSet {
} }
@Override @Override
public boolean flush() { public final boolean flush() {
return false; return false;
} }
@Override @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 @Override
public void addTileCreate(CompoundTag tag) { public final void addTileCreate(CompoundTag tag) {
} }
@Override @Override
public void addTileRemove(CompoundTag tag) { public final void addTileRemove(CompoundTag tag) {
} }
@Override @Override
public void addEntityRemove(CompoundTag tag) { public final void addEntityRemove(CompoundTag tag) {
} }
@Override @Override
public void addEntityCreate(CompoundTag tag) { public final void addEntityCreate(CompoundTag tag) {
} }
@Override @Override
public Iterator<Change> getIterator(boolean undo) { public final Iterator<Change> getIterator(boolean undo) {
return new ArrayList<Change>().iterator(); return new ArrayList<Change>().iterator();
} }
@Override @Override
public int size() { public final int size() {
return 0; return 0;
} }
} }

View File

@ -8,6 +8,10 @@ public class RegionWrapper {
public int minZ; public int minZ;
public int maxZ; 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) { public RegionWrapper(final int minX, final int maxX, final int minZ, final int maxZ) {
this.maxX = maxX; this.maxX = maxX;
this.minX = minX; this.minX = minX;
@ -82,6 +86,10 @@ public class RegionWrapper {
return new Vector(this.maxX, 255, this.maxZ); 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) { public boolean contains(RegionWrapper current) {
return current.minX >= minX && current.maxX <= maxX && current.minZ >= minZ && current.maxZ <= maxZ; return current.minX >= minX && current.maxX <= maxX && current.minZ >= minZ && current.maxZ <= maxZ;
} }

View File

@ -2,6 +2,7 @@ package com.boydti.fawe.object.changeset;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweInputStream;
import com.boydti.fawe.object.IntegerPair; import com.boydti.fawe.object.IntegerPair;
import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
@ -17,9 +18,6 @@ import java.io.OutputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import net.jpountz.lz4.LZ4Compressor;
import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4InputStream;
/** /**
* Store the change on disk * Store the change on disk
@ -163,6 +161,9 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
bdFile.getParentFile().mkdirs(); bdFile.getParentFile().mkdirs();
bdFile.createNewFile(); bdFile.createNewFile();
osBD = getCompressedOS(new FileOutputStream(bdFile)); osBD = getCompressedOS(new FileOutputStream(bdFile));
// Mode
osBD.write((byte) MODE);
// Origin
setOrigin(x, z); setOrigin(x, z);
osBD.write((byte) (x >> 24)); osBD.write((byte) (x >> 24));
osBD.write((byte) (x >> 16)); osBD.write((byte) (x >> 16));
@ -224,7 +225,10 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!bdFile.exists()) { if (!bdFile.exists()) {
return null; 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 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)); int z = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read() << 0));
setOrigin(x, z); setOrigin(x, z);
@ -236,7 +240,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!enttFile.exists()) { if (!enttFile.exists()) {
return null; return null;
} }
return new NBTInputStream(getCompressedIS(new FileInputStream(enttFile))); return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(enttFile)));
} }
@Override @Override
@ -244,7 +248,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!entfFile.exists()) { if (!entfFile.exists()) {
return null; return null;
} }
return new NBTInputStream(getCompressedIS(new FileInputStream(entfFile))); return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(entfFile)));
} }
@Override @Override
@ -252,7 +256,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!nbttFile.exists()) { if (!nbttFile.exists()) {
return null; return null;
} }
return new NBTInputStream(getCompressedIS(new FileInputStream(nbttFile))); return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbttFile)));
} }
@Override @Override
@ -260,7 +264,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (!nbtfFile.exists()) { if (!nbtfFile.exists()) {
return null; return null;
} }
return new NBTInputStream(getCompressedIS(new FileInputStream(nbtfFile))); return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbtfFile)));
} }
public DiskStorageSummary summarize(RegionWrapper requiredRegion, boolean shallow) { public DiskStorageSummary summarize(RegionWrapper requiredRegion, boolean shallow) {
@ -274,14 +278,10 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
return summary = new DiskStorageSummary(ox, oz); return summary = new DiskStorageSummary(ox, oz);
} }
try (FileInputStream fis = new FileInputStream(bdFile)) { try (FileInputStream fis = new FileInputStream(bdFile)) {
LZ4Factory factory = LZ4Factory.fastestInstance(); FaweInputStream gis = MainUtil.getCompressedIS(fis);
LZ4Compressor compressor = factory.fastCompressor(); // skip mode
final LZ4InputStream gis; gis.skip(1);
if (Settings.COMPRESSION_LEVEL > 0) { // origin
gis = new LZ4InputStream(new LZ4InputStream(fis));
} else {
gis = new LZ4InputStream(fis);
}
ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0)); 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)); oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0));
setOrigin(ox, oz); setOrigin(ox, oz);
@ -293,7 +293,8 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
} }
byte[] buffer = new byte[9]; byte[] buffer = new byte[9];
int i = 0; int i = 0;
while (!shallow || gis.hasBytesAvailableInDecompressedBuffer(9)) { while (!shallow && i < Settings.BUFFER_SIZE) {
i++;
if (gis.read(buffer) == -1) { if (gis.read(buffer) == -1) {
fis.close(); fis.close();
gis.close(); gis.close();
@ -316,16 +317,11 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
int ox = getOriginX(); int ox = getOriginX();
int oz = getOriginZ(); int oz = getOriginZ();
if (ox == 0 && oz == 0 && bdFile.exists()) { if (ox == 0 && oz == 0 && bdFile.exists()) {
try { try (FileInputStream fis = new FileInputStream(bdFile)) {
FileInputStream fis = new FileInputStream(bdFile); final InputStream gis = MainUtil.getCompressedIS(fis);
LZ4Factory factory = LZ4Factory.fastestInstance(); // skip mode
LZ4Compressor compressor = factory.fastCompressor(); gis.skip(1);
final InputStream gis; // origin
if (Settings.COMPRESSION_LEVEL > 0) {
gis = new LZ4InputStream(new LZ4InputStream(fis));
} else {
gis = new LZ4InputStream(fis);
}
ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0)); 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)); oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0));
setOrigin(ox, oz); setOrigin(ox, oz);

View File

@ -4,18 +4,17 @@ import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.BytePair; import com.boydti.fawe.object.BytePair;
import com.boydti.fawe.object.FaweChunk; 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.FaweQueue;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.EditSessionFactory;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.blocks.BaseBlock; 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.BlockChange;
import com.sk89q.worldedit.history.change.Change; import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.history.change.EntityCreate; import com.sk89q.worldedit.history.change.EntityCreate;
@ -70,12 +69,8 @@ public abstract class FaweChangeSet implements ChangeSet {
public abstract void addEntityCreate(CompoundTag tag); public abstract void addEntityCreate(CompoundTag tag);
public abstract Iterator<Change> getIterator(boolean redo); public abstract Iterator<Change> getIterator(boolean redo);
public EditSession toEditSession(Player player) { public EditSession toEditSession(FawePlayer player) {
EditSessionFactory factory = WorldEdit.getInstance().getEditSessionFactory(); return new EditSessionBuilder(world).player(player).autoQueue(false).fastmode(true).checkMemory(false).changeSet(this).build();
EditSession edit = factory.getEditSession(world, -1, null, player);
edit.setChangeSet(this);
edit.dequeue();
return edit;
} }
public void add(EntityCreate change) { public void add(EntityCreate change) {

View File

@ -1,6 +1,7 @@
package com.boydti.fawe.object.changeset; package com.boydti.fawe.object.changeset;
import com.boydti.fawe.config.Settings; 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.MutableBlockChange;
import com.boydti.fawe.object.change.MutableEntityChange; import com.boydti.fawe.object.change.MutableEntityChange;
import com.boydti.fawe.object.change.MutableTileChange; import com.boydti.fawe.object.change.MutableTileChange;
@ -15,15 +16,25 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; 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 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) { public FaweStreamChangeSet(World world) {
this(world, Settings.COMPRESSION_LEVEL);
}
public FaweStreamChangeSet(World world, int compression) {
super(world); super(world);
this.compression = compression;
}
public FaweOutputStream getCompressedOS(OutputStream os) throws IOException {
return MainUtil.getCompressedOS(os, compression);
} }
@Override @Override
@ -69,25 +80,6 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
return originZ; 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) { public void add(int x, int y, int z, int combinedFrom, int combinedTo) {
blockSize++; blockSize++;
try { try {

View File

@ -1,6 +1,8 @@
package com.boydti.fawe.object.changeset; package com.boydti.fawe.object.changeset;
import com.boydti.fawe.config.Settings; 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.MainUtil;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.jnbt.NBTOutputStream;
@ -21,7 +23,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet {
private byte[] ids; private byte[] ids;
private ByteArrayOutputStream idsStream; private ByteArrayOutputStream idsStream;
private OutputStream idsStreamZip; private FaweOutputStream idsStreamZip;
private byte[] entC; private byte[] entC;
private ByteArrayOutputStream entCStream; private ByteArrayOutputStream entCStream;
@ -96,12 +98,18 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet {
} }
setOrigin(x, z); setOrigin(x, z);
idsStream = new ByteArrayOutputStream(Settings.BUFFER_SIZE); 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 @Override
public InputStream getBlockIS() { public InputStream getBlockIS() throws IOException {
return ids == null ? null : getCompressedIS(new ByteArrayInputStream(ids)); FaweInputStream result = ids == null ? null : MainUtil.getCompressedIS(new ByteArrayInputStream(ids));
result.skip(FaweStreamChangeSet.HEADER_SIZE);
return result;
} }
@Override @Override
@ -142,21 +150,21 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet {
@Override @Override
public NBTInputStream getEntityCreateIS() throws IOException { 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 @Override
public NBTInputStream getEntityRemoveIS() throws IOException { 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 @Override
public NBTInputStream getTileCreateIS() throws IOException { 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 @Override
public NBTInputStream getTileRemoveIS() throws IOException { 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)));
} }
} }

View File

@ -36,7 +36,9 @@ import java.util.UUID;
*/ */
public class DiskOptimizedClipboard extends FaweClipboard { 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 length;
protected int height; protected int height;
@ -53,7 +55,7 @@ public class DiskOptimizedClipboard extends FaweClipboard {
private int last; private int last;
public DiskOptimizedClipboard(int width, int height, int length, UUID uuid) { 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 { public DiskOptimizedClipboard(File file) throws IOException {
@ -64,13 +66,14 @@ public class DiskOptimizedClipboard extends FaweClipboard {
this.raf = new BufferedRandomAccessFile(file, "rw", Settings.BUFFER_SIZE); this.raf = new BufferedRandomAccessFile(file, "rw", Settings.BUFFER_SIZE);
raf.setLength(file.length()); raf.setLength(file.length());
long size = (raf.length() - HEADER_SIZE) >> 1; long size = (raf.length() - HEADER_SIZE) >> 1;
raf.seek(0); raf.seek(2);
last = -1; last = -1;
raf.read(buffer); raf.read(buffer);
width = (((buffer[1] & 0xFF) << 8) + ((buffer[0] & 0xFF))); width = (((buffer[1] & 0xFF) << 8) + ((buffer[0] & 0xFF)));
raf.read(buffer); raf.read(buffer);
height = (((buffer[1] & 0xFF) << 8) + ((buffer[0] & 0xFF)));
raf.read(buffer);
length = (((buffer[1] & 0xFF) << 8) + ((buffer[0] & 0xFF))); length = (((buffer[1] & 0xFF) << 8) + ((buffer[0] & 0xFF)));
height = (int) (size / (width * length));
area = width * length; area = width * length;
autoCloseTask(); autoCloseTask();
} }
@ -86,7 +89,7 @@ public class DiskOptimizedClipboard extends FaweClipboard {
if (raf == null) { if (raf == null) {
open(); open();
} }
raf.seek(4); raf.seek(8);
last = -1; last = -1;
int ox = (((byte) raf.read() << 8) | ((byte) raf.read()) & 0xFF); int ox = (((byte) raf.read() << 8) | ((byte) raf.read()) & 0xFF);
int oy = (((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) { if (raf == null) {
open(); open();
} }
raf.seek(4); raf.seek(8);
last = -1; last = -1;
raf.write((byte) (offset.getBlockX() >> 8)); raf.write((byte) (offset.getBlockX() >> 8));
raf.write((byte) (offset.getBlockX())); raf.write((byte) (offset.getBlockX()));
@ -178,10 +181,12 @@ public class DiskOptimizedClipboard extends FaweClipboard {
if (raf.length() != size) { if (raf.length() != size) {
raf.setLength(size); raf.setLength(size);
// write length etc // write length etc
raf.seek(0); raf.seek(1);
last = 0; last = 0;
raf.write((width) & 0xff); raf.write((width) & 0xff);
raf.write(((width) >> 8) & 0xff); raf.write(((width) >> 8) & 0xff);
raf.write((height) & 0xff);
raf.write(((height) >> 8) & 0xff);
raf.write((length) & 0xff); raf.write((length) & 0xff);
raf.write(((length) >> 8) & 0xff); raf.write(((length) >> 8) & 0xff);
} }
@ -291,6 +296,12 @@ public class DiskOptimizedClipboard extends FaweClipboard {
return EditSession.nullBlock; 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 @Override
public boolean setBlock(int x, int y, int z, BaseBlock block) { public boolean setBlock(int x, int y, int z, BaseBlock block) {
try { try {

View File

@ -1,6 +1,7 @@
package com.boydti.fawe.object.clipboard; package com.boydti.fawe.object.clipboard;
import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.RunnableVal2;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.entity.BaseEntity; 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 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 Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity);
public abstract List<? extends Entity> getEntities(); public abstract List<? extends Entity> getEntities();

View File

@ -1,32 +1,66 @@
package com.boydti.fawe.object.clipboard; 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.blocks.BaseBlock;
import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.regions.Region;
import java.util.Iterator;
import java.util.List; import java.util.List;
public abstract class LazyClipboard extends FaweClipboard { public abstract class LazyClipboard extends FaweClipboard {
private final Region region;
// private final int width, height, length; public LazyClipboard(Region region) {
// this.region = region;
// public LazyClipboard(int width, int height, int length) { }
// this.width = width;
// this.height = height; public static LazyClipboard of(final EditSession editSession, Region region) {
// this.length = length; final Vector origin = region.getMinimumPoint();
// } final int mx = origin.getBlockX();
// final int my = origin.getBlockY();
// @Override final int mz = origin.getBlockZ();
// public void forEach(RunnableVal2<Vector, BaseBlock> task, boolean air) { return new LazyClipboard(region) {
// BlockVector pos = new BlockVector(0, 0, 0); @Override
// for (pos.x = 0; pos.x < width; pos.x++) { public BaseBlock getBlock(int x, int y, int z) {
// for (pos.z = 0; pos.z < width; pos.z++) { return editSession.getLazyBlock(mx + x, my + y, mz + z);
// for (pos.y = 0; pos.y < width; pos.y++) { }
// task.run(pos, getBlock((int) pos.x, (int) pos.y, (int) pos.z));
// } public BaseBlock getBlockAbs(int x, int y, int z) {
// } return editSession.getLazyBlock(x, y, z);
// } }
// }
@Override
public List<? extends Entity> getEntities() {
return editSession.getEntities(getRegion());
}
@Override
public void forEach(RunnableVal2<Vector, BaseBlock> task, boolean air) {
Iterator<BlockVector> 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 @Override
public abstract BaseBlock getBlock(int x, int y, int z); 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"); 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 @Override
public Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) { public Entity createEntity(Extent world, double x, double y, double z, float yaw, float pitch, BaseEntity entity) {
throw new UnsupportedOperationException("Clipboard is immutable"); throw new UnsupportedOperationException("Clipboard is immutable");

View File

@ -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 @Override
public boolean setBlock(int x, int y, int z, BaseBlock block) { public boolean setBlock(int x, int y, int z, BaseBlock block) {
final int id = block.getId(); final int id = block.getId();

View File

@ -30,7 +30,7 @@ public class ProcessedWEExtent extends FaweRegionExtent {
@Override @Override
public Entity createEntity(final Location location, final BaseEntity entity) { 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 null;
} }
return super.createEntity(location, entity); return super.createEntity(location, entity);
@ -67,18 +67,18 @@ public class ProcessedWEExtent extends FaweRegionExtent {
@Override @Override
public boolean setBlock(final Vector location, final BaseBlock block) throws WorldEditException { public boolean setBlock(final Vector location, final BaseBlock block) throws WorldEditException {
if (block.hasNbtData() && FaweCache.hasNBT(block.getType())) { 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); WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_TILES);
return false; return false;
} }
} }
if (WEManager.IMP.maskContains(this.mask, (int) location.x, (int) location.z)) { 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); WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES);
return false; return false;
} }
return super.setBlock(location, block); 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); WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
} }
return false; return false;

View File

@ -8,6 +8,9 @@ import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.StringMan;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
/**
* The default progress tracker uses titles
*/
public class DefaultProgressTracker extends RunnableVal2<FaweQueue.ProgressType, Integer> { public class DefaultProgressTracker extends RunnableVal2<FaweQueue.ProgressType, Integer> {
private final FawePlayer player; private final FawePlayer player;
@ -18,9 +21,12 @@ public class DefaultProgressTracker extends RunnableVal2<FaweQueue.ProgressType,
this.player = player; this.player = player;
} }
// Number of times a chunk was queued
private int totalQueue = 0;
// Current size of the queue
private int amountQueue = 0; private int amountQueue = 0;
// Number of chunks dispatched
private int amountDispatch = 0; private int amountDispatch = 0;
private long lastTick = 0;
@Override @Override
public void run(FaweQueue.ProgressType type, Integer amount) { public void run(FaweQueue.ProgressType type, Integer amount) {
@ -29,47 +35,60 @@ public class DefaultProgressTracker extends RunnableVal2<FaweQueue.ProgressType,
amountDispatch = amount; amountDispatch = amount;
break; break;
case QUEUE: case QUEUE:
totalQueue++;
amountQueue = amount; amountQueue = amount;
break; break;
case DONE: case DONE:
if (amountDispatch > 64) { if (totalQueue > 64) {
done(); done();
} }
return; return;
} }
if (amountQueue > 64 || amountDispatch > 64) { // Only send a message after 64 chunks (i.e. ignore smaller edits)
if (totalQueue > 64) {
send(); send();
} }
} }
private void done() { private final void done() {
TaskManager.IMP.task(new Runnable() { TaskManager.IMP.task(new Runnable() {
@Override @Override
public void run() { public void run() {
final long time = System.currentTimeMillis() - start; doneTask();
player.sendTitle("", BBC.PROGRESS_DONE.format(time / 1000d));
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
player.resetTitle();
}
}, 60);
} }
}); });
} }
public void send() { private long lastTick = 0;
TaskManager.IMP.task(new Runnable() {
@Override private final void send() {
public void run() { // Avoid duplicates
long currentTick = System.currentTimeMillis() / 50; long currentTick = System.currentTimeMillis() / 50;
if (currentTick > lastTick + Settings.DISPLAY_PROGRESS_INTERVAL) { if (currentTick > lastTick + Settings.DISPLAY_PROGRESS_INTERVAL) {
lastTick = currentTick; 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() {
doneTask();
}
}, 60);
}
public void sendTask() {
String queue = StringMan.padRight("" + amountQueue, 3); String queue = StringMan.padRight("" + amountQueue, 3);
String dispatch = StringMan.padRight("" + amountDispatch, 3); String dispatch = StringMan.padRight("" + amountDispatch, 3);
player.sendTitle("", BBC.PROGRESS_MESSAGE.format(queue, dispatch)); player.sendTitle("", BBC.PROGRESS_MESSAGE.format(queue, dispatch));
} }
}
});
}
} }

View File

@ -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<String, Tag> 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<CompoundTag> 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<String, Tag> 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<String, Tag> 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<String, Tag> 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<DoubleTag> list = new ArrayList<DoubleTag>();
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<FloatTag> list = new ArrayList<FloatTag>();
list.add(new FloatTag(location.getYaw()));
list.add(new FloatTag(location.getPitch()));
return new ListTag(FloatTag.class, list);
}
}

View File

@ -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;
}
}

View File

@ -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<BaseBiome> 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;
}
}

View File

@ -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<RegionWrapper> regions, final RunnableVal<CompoundTag> 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<? extends Entity> getEntities() {
return editSession.getEntities(region);
}
@Override
public void forEach(RunnableVal2<Vector, BaseBlock> 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<String, Tag>) (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<String, com.sk89q.jnbt.Tag> 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<URL> 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<OutputStream>() {
@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<String, com.sk89q.jnbt.Tag> map = weTag.getValue();
nos.writeNamedTag("Schematic", map.containsKey("Schematic") ? map.get("Schematic") : weTag);
gzip.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}, whenDone);
}
}

View File

@ -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.object.FawePlayer;
import com.boydti.fawe.regions.FaweMask; import com.boydti.fawe.regions.FaweMask;
import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.regions.FaweMaskManager;
import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.PS;
import com.intellectualcrafters.plot.generator.HybridPlotManager;
import com.intellectualcrafters.plot.object.Plot; import com.intellectualcrafters.plot.object.Plot;
import com.intellectualcrafters.plot.object.PlotPlayer; import com.intellectualcrafters.plot.object.PlotPlayer;
import com.intellectualcrafters.plot.object.RegionWrapper; 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.plotsquared.listener.WEManager;
import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.BlockVector;
import java.util.HashSet; import java.util.HashSet;
@ -15,7 +21,43 @@ import org.bukkit.entity.Player;
public class PlotSquaredFeature extends FaweMaskManager { public class PlotSquaredFeature extends FaweMaskManager {
public PlotSquaredFeature() { public PlotSquaredFeature() {
super("PlotSquared"); super("PlotSquared");
Fawe.debug("Optimizing PlotSquared");
PS.get().worldedit = null; 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 @Override

View File

@ -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<br>
* - Unset values will revert to their default<br>
* <br>
* player: The player doing the edit (defaults to to console)<br>
* limit: Block/Entity/Action limit (defaults to unlimited)<br>
* changeSet: Stores changes (defaults to config.yml value)<br>
* allowedRegions: Allowed editable regions (defaults to player's allowed regions, or everywhere)<br>
* autoQueue: Changes can occur before flushQueue() (defaults true)<br>
* fastmode: bypasses history (defaults to player fastmode or config.yml console history)<br>
* checkMemory: If low memory checks are enabled (defaults to player's fastmode or true)<br>
* 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);
}
}

View File

@ -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<T extends Extent> {
private T root;
private ExtentTraverser<T> parent;
public ExtentTraverser(T root) {
this(root, null);
}
public ExtentTraverser(T root, ExtentTraverser<T> 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 <U extends Extent> ExtentTraverser<U> find(Class<U> clazz) {
try {
ExtentTraverser<T> value = this;
while (value != null) {
if (clazz.isInstance(value.root)) {
return (ExtentTraverser<U>) value;
}
value = value.next();
}
return null;
} catch (Throwable e) {
MainUtil.handleError(e);
return null;
}
}
public <U extends Extent> ExtentTraverser<U> find(Object object) {
try {
ExtentTraverser<T> value = this;
while (value != null) {
if (value.root == object) {
return (ExtentTraverser<U>) value;
}
value = value.next();
}
return null;
} catch (Throwable e) {
MainUtil.handleError(e);
return null;
}
}
public ExtentTraverser<T> previous() {
return parent;
}
public ExtentTraverser<T> 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;
}
}
}

View File

@ -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);
}
}

View File

@ -3,6 +3,8 @@ package com.boydti.fawe.util;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweInputStream;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal;
@ -15,8 +17,9 @@ import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -38,8 +41,13 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.UUID; import java.util.UUID;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4InputStream;
import net.jpountz.lz4.LZ4OutputStream;
public class MainUtil { public class MainUtil {
/* /*
@ -75,6 +83,50 @@ public class MainUtil {
return getFile(base, path.endsWith("." + extension) ? path : path + "." + extension); 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<OutputStream> writeTask) { public static URL upload(UUID uuid, String file, String extension, final RunnableVal<OutputStream> writeTask) {
if (writeTask == null) { if (writeTask == null) {
Fawe.debug("&cWrite task cannot be 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) { public static File copyFile(File jar, String resource, File output) {
try { try {
if (output == null) { if (output == null) {
@ -209,46 +301,6 @@ public class MainUtil {
return null; 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) { public static void handleError(Throwable e) {
handleError(e, true); handleError(e, true);
} }

View File

@ -17,6 +17,16 @@ public class MemUtil {
return memory.get(); return memory.get();
} }
public static boolean isMemoryLimitedSlow() {
if (memory.get()) {
System.gc();
System.gc();
calculateMemory();
return memory.get();
}
return false;
}
public static int calculateMemory() { public static int calculateMemory() {
final long heapSize = Runtime.getRuntime().totalMemory(); final long heapSize = Runtime.getRuntime().totalMemory();
final long heapMaxSize = Runtime.getRuntime().maxMemory(); final long heapMaxSize = Runtime.getRuntime().maxMemory();
@ -46,6 +56,8 @@ public class MemUtil {
} }
public static void memoryLimitedTask() { public static void memoryLimitedTask() {
System.gc();
System.gc();
for (Runnable task : memoryLimitedTasks) { for (Runnable task : memoryLimitedTasks) {
task.run(); task.run();
} }

View File

@ -188,6 +188,15 @@ public static <T> List<T> getList(List<T> 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) { public static void setField(final Field field, final Object instance, final Object value) {
if (field == null) { if (field == null) {
throw new RuntimeException("No such field"); throw new RuntimeException("No such field");

View File

@ -23,7 +23,6 @@ import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.EditSessionWrapper;
import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue; 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.exception.FaweException;
import com.boydti.fawe.object.extent.FastWorldEditExtent; import com.boydti.fawe.object.extent.FastWorldEditExtent;
import com.boydti.fawe.object.extent.FaweRegionExtent; 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.NullExtent;
import com.boydti.fawe.object.extent.ProcessedWEExtent; 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.MainUtil;
import com.boydti.fawe.util.MemUtil; import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.Perm; import com.boydti.fawe.util.Perm;
import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.WEManager;
import com.boydti.fawe.wrappers.WorldWrapper; import com.boydti.fawe.wrappers.WorldWrapper;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.blocks.BlockID;
import com.sk89q.worldedit.blocks.BlockType; 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.BaseEntity;
import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.event.extent.EditSessionEvent; 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.ChangeSetExtent;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.MaskingExtent; import com.sk89q.worldedit.extent.MaskingExtent;
@ -133,8 +127,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import javax.annotation.Nonnull;
import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -153,9 +146,6 @@ import static com.sk89q.worldedit.regions.Regions.minimumBlockY;
* using the {@link ChangeSetExtent}.</p> * using the {@link ChangeSetExtent}.</p>
*/ */
public class EditSession implements Extent { public class EditSession implements Extent {
private final Logger log = Logger.getLogger(EditSession.class.getCanonicalName());
/** /**
* Used by {@link #setBlock(Vector, BaseBlock, Stage)} to * Used by {@link #setBlock(Vector, BaseBlock, Stage)} to
* determine which {@link Extent}s should be bypassed. * determine which {@link Extent}s should be bypassed.
@ -165,25 +155,113 @@ public class EditSession implements Extent {
} }
private World world; 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 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 UUID CONSOLE = UUID.fromString("1-1-3-3-7");
public static final BaseBiome nullBiome = new BaseBiome(0); public static final BaseBiome nullBiome = new BaseBiome(0);
public static final BaseBlock nullBlock = FaweCache.CACHE_BLOCK[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. * 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)); 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. * 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 * @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) { public EditSession(final EventBus eventBus, World world, final int maxBlocks, @Nullable final BlockBag blockBag, EditSessionEvent event) {
checkNotNull(eventBus); this(world, null, null, null, null, true, null, null, null, blockBag, eventBus, event);
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;
} }
public FaweRegionExtent getRegionExtent() { public FaweRegionExtent getRegionExtent() {
return regionExtent; ExtentTraverser<FaweRegionExtent> traverser = new ExtentTraverser(bypassNone).find(FaweRegionExtent.class);
return traverser == null ? null : traverser.get();
} }
/** /**
@ -391,28 +311,25 @@ public class EditSession implements Extent {
* @return * @return
*/ */
@Nullable @Nullable
public Actor getActor() { public FawePlayer getPlayer() {
return actor; return player;
} }
public boolean cancel() { public boolean cancel() {
// Cancel this ExtentTraverser traverser = new ExtentTraverser(bypassNone);
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); NullExtent nullExtent = new NullExtent(world, BBC.WORLDEDIT_CANCEL_REASON_MANUAL);
primaryExtent = nullExtent; while (traverser != null) {
regionExtent = nullExtent; ExtentTraverser next = traverser.next();
bypassReorderHistory = nullExtent; traverser.setNext(nullExtent);
traverser = next;
}
bypassHistory = nullExtent; bypassHistory = nullExtent;
bypassNone = nullExtent; bypassNone = nullExtent;
bypassAll = nullExtent;
dequeue(); dequeue();
queue.clear(); queue.clear();
return true; return true;
} }
return false;
}
public void dequeue() { public void dequeue() {
if (queue != null) { if (queue != null) {
@ -426,12 +343,8 @@ public class EditSession implements Extent {
} }
} }
public FastWorldEditExtent getPrimaryExtent() {
return (FastWorldEditExtent) primaryExtent;
}
public void debug(BBC message, Object... args) { public void debug(BBC message, Object... args) {
message.send(actor, args); message.send(player, args);
} }
public FaweQueue getQueue() { public FaweQueue getQueue() {
@ -474,15 +387,21 @@ public class EditSession implements Extent {
* @return the change set * @return the change set
*/ */
public ChangeSet getChangeSet() { public ChangeSet getChangeSet() {
return this.changeSet; return history != null ? history.getChangeSet() : changeTask;
} }
public void setChangeSet(FaweChangeSet set) { public void setChangeSet(FaweChangeSet set) {
if (set == null) {
disableHistory(true);
} else {
if (history != null) { if (history != null) {
history.setChangeSet(set); history.setChangeSet(set);
} else {
changeTask = set;
set.addChangeTask(queue);
}
} }
changes++; changes++;
this.changeSet = set;
} }
/** /**
@ -510,14 +429,13 @@ public class EditSession implements Extent {
* @return whether the queue is enabled * @return whether the queue is enabled
*/ */
public boolean isQueueEnabled() { public boolean isQueueEnabled() {
return false; return true;
} }
/** /**
* Queue certain types of block for better reproduction of those blocks. * 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. * Disable the queue. This will flush the queue.
@ -534,7 +452,8 @@ public class EditSession implements Extent {
* @return mask, may be null * @return mask, may be null
*/ */
public Mask getMask() { public Mask getMask() {
return this.oldMask; ExtentTraverser<MaskingExtent> 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 * @param mask mask or null
*/ */
public void setMask(final Mask mask) { public void setMask(Mask mask) {
if (this.maskingExtent == null) {
return;
}
this.oldMask = mask;
if (mask == null) { if (mask == null) {
this.maskingExtent.setMask(Masks.alwaysTrue()); mask = Masks.alwaysTrue();
} else { }
this.maskingExtent.setMask(mask); ExtentTraverser<MaskingExtent> 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 * @return the survival simulation extent
*/ */
public SurvivalModeExtent getSurvivalExtent() { public SurvivalModeExtent getSurvivalExtent() {
if (this.lazySurvivalExtent == null) { ExtentTraverser<SurvivalModeExtent> survivalExtent = new ExtentTraverser(bypassNone).find(SurvivalModeExtent.class);
this.lazySurvivalExtent = new SurvivalModeExtent(bypassReorderHistory, world); 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 * @param enabled true to enable
*/ */
public void setFastMode(final boolean enabled) { 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 * @return true if enabled
*/ */
public boolean hasFastMode() { 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) { 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); throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS);
} }
int combinedId4Data = queue.getCombinedId4DataDebug(x, y, z, 0, this); 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]; return FaweCache.CACHE_BLOCK[combinedId4Data];
} }
try { try {
BaseBlock block = this.world.getBlock(new Vector(x, y, z)); CompoundTag tile = queue.getTileEntity(x, y, z);
return block; return new BaseBlock(id, FaweCache.getData(combinedId4Data), tile);
} catch (Throwable e) { } catch (Throwable e) {
MainUtil.handleError(e); MainUtil.handleError(e);
return FaweCache.CACHE_BLOCK[combinedId4Data]; return FaweCache.CACHE_BLOCK[combinedId4Data];
@ -691,7 +637,7 @@ public class EditSession implements Extent {
*/ */
@Deprecated @Deprecated
public int getBlockType(final Vector position) { 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); throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS);
} }
int combinedId4Data = queue.getCombinedId4DataDebug(position.getBlockX(), position.getBlockY(), position.getBlockZ(), 0, this); int combinedId4Data = queue.getCombinedId4DataDebug(position.getBlockX(), position.getBlockY(), position.getBlockZ(), 0, this);
@ -707,7 +653,7 @@ public class EditSession implements Extent {
*/ */
@Deprecated @Deprecated
public int getBlockData(final Vector position) { 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); throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS);
} }
int combinedId4Data = queue.getCombinedId4DataDebug(position.getBlockX(), position.getBlockY(), position.getBlockZ(), 0, this); 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' * @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) { 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: case BEFORE_CHANGE:
return this.bypassHistory.setBlock(position, block); return this.bypassHistory.setBlock(position, block);
case BEFORE_REORDER: 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"); 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) { public boolean smartSetBlock(final Vector position, final BaseBlock block) {
this.changes++; this.changes++;
try { try {
return this.bypassReorderHistory.setBlock(position, block); return this.bypassAll.setBlock(position, block);
} catch (final WorldEditException e) { } catch (final WorldEditException e) {
throw new RuntimeException("Unexpected exception", e); throw new RuntimeException("Unexpected exception", e);
} }
@ -894,7 +937,10 @@ public class EditSession implements Extent {
*/ */
@Deprecated @Deprecated
public void rememberChange(final Vector position, final BaseBlock existing, final BaseBlock block) { 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) { public void undo(final EditSession editSession) {
final UndoContext context = new UndoContext(); final UndoContext context = new UndoContext();
context.setExtent(editSession.primaryExtent); context.setExtent(editSession.bypassAll);
editSession.getQueue().setChangeTask(null); editSession.getQueue().setChangeTask(null);
Operations.completeSmart(ChangeSetExecutor.createUndo(this.changeSet, context), new Runnable() { Operations.completeSmart(ChangeSetExecutor.createUndo(getChangeSet(), context), new Runnable() {
@Override @Override
public void run() { public void run() {
editSession.flushQueue(); editSession.flushQueue();
@ -922,9 +968,9 @@ public class EditSession implements Extent {
*/ */
public void redo(final EditSession editSession) { public void redo(final EditSession editSession) {
final UndoContext context = new UndoContext(); final UndoContext context = new UndoContext();
context.setExtent(editSession.primaryExtent); context.setExtent(editSession.bypassAll);
editSession.getQueue().setChangeTask(null); editSession.getQueue().setChangeTask(null);
Operations.completeSmart(ChangeSetExecutor.createRedo(this.changeSet, context), new Runnable() { Operations.completeSmart(ChangeSetExecutor.createRedo(getChangeSet(), context), new Runnable() {
@Override @Override
public void run() { public void run() {
editSession.flushQueue(); editSession.flushQueue();
@ -973,7 +1019,7 @@ public class EditSession implements Extent {
} else { } else {
queue.dequeue(); queue.dequeue();
} }
if (changeSet != null) { if (getChangeSet() != null) {
if (Settings.COMBINE_HISTORY_STAGE && queue.size() > 0) { if (Settings.COMBINE_HISTORY_STAGE && queue.size() > 0) {
if (Fawe.get().isMainThread()) { if (Fawe.get().isMainThread()) {
SetQueue.IMP.flush(queue); SetQueue.IMP.flush(queue);
@ -988,13 +1034,13 @@ public class EditSession implements Extent {
TaskManager.IMP.wait(running, Settings.QUEUE_DISCARD_AFTER); TaskManager.IMP.wait(running, Settings.QUEUE_DISCARD_AFTER);
} }
} }
changeSet.flush(); ((FaweChangeSet) getChangeSet()).flush();
} }
} }
@Override @Override
public @Nullable Operation commit() { public @Nullable Operation commit() {
return this.bypassNone.commit(); return null;
} }
/** /**
@ -1708,8 +1754,8 @@ public class EditSession implements Extent {
if (pos.getBlockY() < 0) { if (pos.getBlockY() < 0) {
pos = pos.setY(0); pos = pos.setY(0);
} else if (((pos.getBlockY() + height) - 1) > this.world.getMaxY()) { } else if (((pos.getBlockY() + height) - 1) > 255) {
height = (this.world.getMaxY() - pos.getBlockY()) + 1; height = (255 - pos.getBlockY()) + 1;
} }
final double invRadiusX = 1 / radiusX; final double invRadiusX = 1 / radiusX;
@ -1892,7 +1938,7 @@ public class EditSession implements Extent {
continue; 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 Vector pt = new Vector(x, y, z);
final int id = this.getBlockType(pt); final int id = this.getBlockType(pt);
@ -1946,7 +1992,7 @@ public class EditSession implements Extent {
continue; 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 Vector pt = new Vector(x, y, z);
final int id = this.getBlockType(pt); final int id = this.getBlockType(pt);
@ -1966,7 +2012,7 @@ public class EditSession implements Extent {
} }
// Too high? // Too high?
if (y == this.world.getMaxY()) { if (y == 255) {
break; break;
} }
@ -2020,7 +2066,7 @@ public class EditSession implements Extent {
continue; 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 Vector pt = new Vector(x, y, z);
final int id = this.getBlockType(pt); final int id = this.getBlockType(pt);
final int data = this.getBlockData(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()); return FaweCache.getBlock((int) typeVariable.getValue(), (int) dataVariable.getValue());
} catch (final Exception e) { } catch (final Exception e) {
EditSession.this.log.log(Level.WARNING, "Failed to create shape", e); Fawe.debug("Failed to create shape: " + e);
return null; return null;
} }
} }
@ -2594,7 +2640,7 @@ public class EditSession implements Extent {
return defaultBiomeType; return defaultBiomeType;
} catch (final Exception e) { } catch (final Exception e) {
EditSession.this.log.log(Level.WARNING, "Failed to create shape", e); Fawe.debug("Failed to create shape: " + e);
return null; return null;
} }
} }
@ -2603,14 +2649,6 @@ public class EditSession implements Extent {
return shape.generate(this, biomeType, hollow); 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) { private double lengthSq(final double x, final double y, final double z) {
return (x * x) + (y * y) + (z * z); return (x * x) + (y * y) + (z * z);
} }

View File

@ -106,7 +106,7 @@ public class ClipboardCommands {
final int mx = origin.getBlockX(); final int mx = origin.getBlockX();
final int my = origin.getBlockY(); final int my = origin.getBlockY();
final int mz = origin.getBlockZ(); final int mz = origin.getBlockZ();
LazyClipboard lazyClipboard = new LazyClipboard() { LazyClipboard lazyClipboard = new LazyClipboard(region) {
@Override @Override
public BaseBlock getBlock(int x, int y, int z) { public BaseBlock getBlock(int x, int y, int z) {
return editSession.getLazyBlock(mx + x, my + y, mz + z); return editSession.getLazyBlock(mx + x, my + y, mz + z);

View File

@ -117,9 +117,7 @@ public class SchematicCommands {
in = new FileInputStream(f); in = new FileInputStream(f);
} }
in = new BufferedInputStream(in); in = new BufferedInputStream(in);
final ClipboardReader reader = format.getReader(in); final ClipboardReader reader = format.getReader(in);
final WorldData worldData = player.getWorld().getWorldData(); final WorldData worldData = player.getWorld().getWorldData();
final Clipboard clipboard; final Clipboard clipboard;
if (reader instanceof SchematicReader) { if (reader instanceof SchematicReader) {

View File

@ -78,26 +78,21 @@ public class SelectionCommand extends SimpleCommand<Operation> {
if (!testPermission(locals)) { if (!testPermission(locals)) {
throw new CommandPermissionsException(); throw new CommandPermissionsException();
} }
Contextual<? extends Operation> operationFactory = delegate.call(args, locals); Contextual<? extends Operation> operationFactory = delegate.call(args, locals);
Actor actor = locals.get(Actor.class); Actor actor = locals.get(Actor.class);
if (actor instanceof Player) { if (actor instanceof Player) {
try { try {
Player player = (Player) actor; Player player = (Player) actor;
LocalSession session = WorldEdit.getInstance().getSessionManager().get(player); LocalSession session = WorldEdit.getInstance().getSessionManager().get(player);
Region selection = session.getSelection(player.getWorld()); Region selection = session.getSelection(player.getWorld());
EditSession editSession = session.createEditSession(player); EditSession editSession = session.createEditSession(player);
editSession.enableQueue(); editSession.enableQueue();
locals.put(EditSession.class, editSession); locals.put(EditSession.class, editSession);
session.tellVersion(player); session.tellVersion(player);
EditContext editContext = new EditContext(); EditContext editContext = new EditContext();
editContext.setDestination(locals.get(EditSession.class)); editContext.setDestination(locals.get(EditSession.class));
editContext.setRegion(selection); editContext.setRegion(selection);
Operation operation = operationFactory.createFromContext(editContext); Operation operation = operationFactory.createFromContext(editContext);
// Shortcut // Shortcut
if (selection instanceof CuboidRegion && editSession.hasFastMode() && operation instanceof RegionVisitor) { if (selection instanceof CuboidRegion && editSession.hasFastMode() && operation instanceof RegionVisitor) {
CuboidRegion cuboid = (CuboidRegion) selection; CuboidRegion cuboid = (CuboidRegion) selection;

View File

@ -302,7 +302,7 @@ public final class CommandManager {
BBC.ACTION_COMPLETE.send(actor, (time / 1000d)); BBC.ACTION_COMPLETE.send(actor, (time / 1000d));
ChangeSet fcs = editSession.getChangeSet(); ChangeSet fcs = editSession.getChangeSet();
if (fcs != null && fcs instanceof FaweStreamChangeSet) { if (fcs != null && fcs instanceof FaweStreamChangeSet) {
MainUtil.sendCompressedMessage((FaweStreamChangeSet) fcs, editSession.getActor()); MainUtil.sendCompressedMessage((FaweStreamChangeSet) fcs, editSession.getPlayer());
} }
} }
} }

View File

@ -23,6 +23,7 @@ import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.object.clipboard.FaweClipboard; import com.boydti.fawe.object.clipboard.FaweClipboard;
import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard; import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.Vector2D;
@ -189,6 +190,13 @@ public class BlockArrayClipboard implements Clipboard {
return false; 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 { public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException {
x -= mx; x -= mx;
y -= my; y -= my;

View File

@ -19,12 +19,14 @@
package com.sk89q.worldedit.extent.clipboard.io; 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.object.schematic.StructureFormat;
import com.boydti.fawe.util.MainUtil;
import com.sk89q.jnbt.NBTConstants; import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.jnbt.NBTOutputStream;
import javax.annotation.Nullable;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -40,6 +42,8 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -99,7 +103,10 @@ public enum ClipboardFormat {
return "schematic"; return "schematic";
} }
}, },
// Added /**
* The structure block format:
* http://minecraft.gamepedia.com/Structure_block_file_format
*/
STRUCTURE("structure", "nbt") { STRUCTURE("structure", "nbt") {
@Override @Override
public ClipboardReader getReader(InputStream inputStream) throws IOException { public ClipboardReader getReader(InputStream inputStream) throws IOException {
@ -128,10 +135,91 @@ public enum ClipboardFormat {
public String getExtension() { public String getExtension() {
return "nbt"; return "nbt";
} }
},
/**
* 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));
} }
// TODO add the FAWE clipboard / history formats /**
// .bd, .nbtf, .nbtt, .rabd * 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 * @param aliases an array of aliases by which this format may be referred to
*/ */
private ClipboardFormat(String ... aliases) { ClipboardFormat(String ... aliases) {
this.aliases = aliases; this.aliases = aliases;
} }

View File

@ -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<Vector, BaseBlock> {
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<Tag> tileEntities;
public ForEach(int[] yarea, int[] zwidth, byte[] blocks, byte[] blockData, List<Tag> 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<String, Tag> 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<String, Tag> schematic = new HashMap<String, Tag>();
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<Tag> tileEntities = new ArrayList<Tag>();
// 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<Tag> entities = new ArrayList<Tag>();
for (Entity entity : clipboard.getEntities()) {
BaseEntity state = entity.getState();
if (state != null) {
Map<String, Tag> values = new HashMap<String, Tag>();
// 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<DoubleTag> list = new ArrayList<DoubleTag>();
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<FloatTag> list = new ArrayList<FloatTag>();
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;
}
}

View File

@ -38,11 +38,9 @@ public final class Operations {
* @throws WorldEditException WorldEdit exception * @throws WorldEditException WorldEdit exception
*/ */
public static void complete(Operation operation) throws WorldEditException { public static void complete(Operation operation) throws WorldEditException {
try { while (operation != null) {
while (true) {
operation = operation.resume(context); operation = operation.resume(context);
} }
} catch (NullPointerException ignore) {}
} }
/** /**
@ -65,12 +63,12 @@ public final class Operations {
*/ */
public static void completeBlindly(Operation operation) { public static void completeBlindly(Operation operation) {
try { try {
while (true) { while (operation != null) {
operation = operation.resume(context); operation = operation.resume(context);
} }
} catch (WorldEditException e) { } catch (WorldEditException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} catch (NullPointerException ignore) {} }
} }
public static void completeSmart(final Operation op, final Runnable whenDone, final boolean threadsafe) { public static void completeSmart(final Operation op, final Runnable whenDone, final boolean threadsafe) {

View File

@ -71,7 +71,6 @@ public class LZ4InputStream extends InputStream {
numBytesRemainingToSkip -= numBytesToSkip; numBytesRemainingToSkip -= numBytesToSkip;
decompressedBufferPosition += numBytesToSkip; decompressedBufferPosition += numBytesToSkip;
} }
return n - numBytesRemainingToSkip; return n - numBytesRemainingToSkip;
} }

View File

@ -4,6 +4,7 @@ import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FaweLocation;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.wrappers.PlayerWrapper;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.forge.ForgeWorldEdit; import com.sk89q.worldedit.forge.ForgeWorldEdit;
import java.util.UUID; import java.util.UUID;
@ -73,6 +74,6 @@ public class ForgePlayer extends FawePlayer<EntityPlayerMP> {
@Override @Override
public Player getPlayer() { public Player getPlayer() {
return ForgeWorldEdit.inst.wrap(this.parent); return PlayerWrapper.wrap(ForgeWorldEdit.inst.wrap(this.parent));
} }
} }

View File

@ -14,7 +14,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'net.minecrell:VanillaGradle:2.0.3_1' classpath 'net.minecrell:VanillaGradle:2.0.3_1'
classpath 'net.minecraftforge.gradle:ForgeGradle:2.1-SNAPSHOT' classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT'
} }
} }

View File

@ -4,6 +4,7 @@ import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FaweLocation;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.wrappers.PlayerWrapper;
import java.util.UUID; import java.util.UUID;
import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.EntityPlayerMP;
import org.spongepowered.api.Sponge; import org.spongepowered.api.Sponge;
@ -71,6 +72,6 @@ public class SpongePlayer extends FawePlayer<Player> {
@Override @Override
public com.sk89q.worldedit.entity.Player getPlayer() { public com.sk89q.worldedit.entity.Player getPlayer() {
return (com.sk89q.worldedit.entity.Player) Fawe.<FaweSponge> imp().getWorldEditPlugin().wrap((EntityPlayerMP) this.parent); return PlayerWrapper.wrap(Fawe.<FaweSponge> imp().getWorldEditPlugin().wrap((EntityPlayerMP) this.parent));
} }
} }