diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index 0815b7298..fb72c84de 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -27,6 +27,7 @@ import com.boydti.fawe.util.Jars; import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.WEManager; import com.boydti.fawe.util.image.ImageViewer; +import com.boydti.fawe.util.task.Task; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitPlayer; import io.papermc.lib.PaperLib; @@ -83,7 +84,13 @@ public class FaweBukkit implements IFawe, Listener { e.printStackTrace(); Bukkit.getServer().shutdown(); } - + + //Vault is Spigot/Paper only so this needs to be done in the Bukkit module + setupVault(); + + //PlotSquared support is limited to Spigot/Paper as of 02/20/2020 + TaskManager.IMP.later(this::setupPlotSquared, 0); + // Registered delayed Event Listeners TaskManager.IMP.task(() -> { // Fix for ProtocolSupport @@ -93,7 +100,7 @@ public class FaweBukkit implements IFawe, Listener { Bukkit.getPluginManager().registerEvents(FaweBukkit.this, FaweBukkit.this.plugin); // The tick limiter - new ChunkListener_9(); + new ChunkListener_9(); }); } @@ -143,8 +150,7 @@ public class FaweBukkit implements IFawe, Listener { @Override public void debug(final String message) { - ConsoleCommandSender console = Bukkit.getConsoleSender(); - console.sendMessage(message); + Bukkit.getConsoleSender().sendMessage(message); } @Override @@ -187,8 +193,7 @@ public class FaweBukkit implements IFawe, Listener { /** * Vault isn't required, but used for setting player permissions (WorldEdit bypass) */ - @Override - public void setupVault() { + private void setupVault() { try { this.vault = new VaultUtil(); } catch (final Throwable ignored) { @@ -298,7 +303,7 @@ public class FaweBukkit implements IFawe, Listener { @Override public String getPlatform() { - return "bukkit"; + return "Bukkit"; } @Override @@ -319,8 +324,7 @@ public class FaweBukkit implements IFawe, Listener { return null; } - @Override - public void setupPlotSquared() { + private void setupPlotSquared() { WEManager.IMP.managers.add(new com.boydti.fawe.bukkit.regions.plotsquared.PlotSquaredFeature()); log.debug("Plugin 'PlotSquared' found. Using it now."); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java b/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java index 095d67cc6..a394cf03c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java @@ -16,6 +16,8 @@ import com.github.luben.zstd.util.Native; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.session.request.Request; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; @@ -151,6 +153,20 @@ public class Fawe { } debugPlain((String) s); } + + /** + * Write something to the console + * + * @param c The Component to be printed + */ + public static void debug(Component c) { + Actor actor = Request.request().getActor(); + if (actor != null && actor.isPlayer()) { + actor.printDebug(c); + return; + } + debugPlain(c.toString()); + } /** * The platform specific implementation @@ -178,7 +194,6 @@ public class Fawe { */ this.setupMemoryListener(); this.timer = new FaweTimer(); - Fawe.this.IMP.setupVault(); // Delayed worldedit setup TaskManager.IMP.later(() -> { @@ -187,7 +202,6 @@ public class Fawe { // transformParser = new DefaultTransformParser(getWorldEdit()); visualQueue = new VisualQueue(3); WEManager.IMP.managers.addAll(Fawe.this.IMP.getMaskManagers()); - IMP.setupPlotSquared(); } catch (Throwable ignored) {} }, 0); @@ -303,7 +317,6 @@ public class Fawe { } catch (Throwable e) { debug("====== Failed to load config ======"); debug("Please validate your yaml files:"); - debug("===================================="); e.printStackTrace(); debug("===================================="); } @@ -328,8 +341,7 @@ public class Fawe { Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL = Math.min(6, Settings.IMP.CLIPBOARD.COMPRESSION_LEVEL); Settings.IMP.HISTORY.COMPRESSION_LEVEL = Math.min(6, Settings.IMP.HISTORY.COMPRESSION_LEVEL); debug("====== ZSTD COMPRESSION BINDING NOT FOUND ======"); - debug(e); - debug("==============================================="); + debug(e.getMessage()); debug("FAWE will work but won't compress data as much"); debug("==============================================="); } @@ -339,25 +351,22 @@ public class Fawe { } catch (Throwable e) { e.printStackTrace(); debug("====== LZ4 COMPRESSION BINDING NOT FOUND ======"); - debug(e); - debug("==============================================="); + debug(e.getMessage()); debug("FAWE will work but compression will be slower"); debug(" - Try updating your JVM / OS"); debug(" - Report this issue if you cannot resolve it"); debug("==============================================="); } } - try { - String arch = System.getenv("PROCESSOR_ARCHITECTURE"); - String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432"); - boolean x86OS = !(arch.endsWith("64") || wow64Arch != null && wow64Arch.endsWith("64")); - boolean x86JVM = System.getProperty("sun.arch.data.model").equals("32"); - if (x86OS != x86JVM) { - debug("====== UPGRADE TO 64-BIT JAVA ======"); - debug("You are running 32-bit Java on a 64-bit machine"); - debug("===================================="); - } - } catch (Throwable ignore) {} + String arch = System.getenv("PROCESSOR_ARCHITECTURE"); + String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432"); + boolean x86OS = !(arch.endsWith("64") || wow64Arch != null && wow64Arch.endsWith("64")); + boolean x86JVM = System.getProperty("sun.arch.data.model").equals("32"); + if (x86OS != x86JVM) { + debug("====== UPGRADE TO 64-BIT JAVA ======"); + debug("You are running 32-bit Java on a 64-bit machine"); + debug("===================================="); + } } private void setupMemoryListener() { @@ -391,7 +400,6 @@ public class Fawe { } } catch (Throwable ignored) { debug("====== MEMORY LISTENER ERROR ======"); - debug("==================================="); debug("FAWE needs access to the JVM memory system:"); debug(" - Change your Java security settings"); debug(" - Disable this with `max-memory-percent: -1`"); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java index 0374687d8..0c72d462d 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java @@ -18,9 +18,7 @@ public interface IFawe { File getDirectory(); Player wrap(final Object obj); - - void setupVault(); - + TaskManager getTaskManager(); Collection getMaskManagers(); @@ -45,5 +43,4 @@ public interface IFawe { Preloader getPreloader(); - void setupPlotSquared(); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java index 16d2b5e50..0c91b5ce4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java @@ -41,9 +41,9 @@ import java.util.UUID; import javax.annotation.Nullable; /** - * A clipboard with disk backed storage. (lower memory + loads on crash) - * - Uses an auto closable RandomAccessFile for getting / setting id / data - * - I don't know how to reduce nbt / entities to O(2) complexity, so it is stored in memory. + * A clipboard with disk backed storage. (lower memory + loads on crash) - Uses an auto closable + * RandomAccessFile for getting / setting id / data - I don't know how to reduce nbt / entities to + * O(2) complexity, so it is stored in memory. */ public class DiskOptimizedClipboard extends LinearClipboard { @@ -57,18 +57,27 @@ public class DiskOptimizedClipboard extends LinearClipboard { private FileChannel fileChannel; private boolean hasBiomes; + private int ylast; + private int ylasti; + private int zlast; + private int zlasti; public DiskOptimizedClipboard(BlockVector3 dimensions, UUID uuid) { - this(dimensions, MainUtil.getFile(Fawe.get() != null ? Fawe.imp().getDirectory() : new File("."), Settings.IMP.PATHS.CLIPBOARD + File.separator + uuid + ".bd")); + this(dimensions, MainUtil + .getFile(Fawe.get() != null ? Fawe.imp().getDirectory() : new File("."), + Settings.IMP.PATHS.CLIPBOARD + File.separator + uuid + ".bd")); } public DiskOptimizedClipboard(BlockVector3 dimensions) { - this(dimensions, MainUtil.getFile(Fawe.imp() != null ? Fawe.imp().getDirectory() : new File("."), Settings.IMP.PATHS.CLIPBOARD + File.separator + UUID.randomUUID() + ".bd")); + this(dimensions, MainUtil + .getFile(Fawe.imp() != null ? Fawe.imp().getDirectory() : new File("."), + Settings.IMP.PATHS.CLIPBOARD + File.separator + UUID.randomUUID() + ".bd")); } public DiskOptimizedClipboard(BlockVector3 dimensions, File file) { super(dimensions); - if (getWidth() > Character.MAX_VALUE || getHeight() > Character.MAX_VALUE || getLength() > Character.MAX_VALUE) { + if (getWidth() > Character.MAX_VALUE || getHeight() > Character.MAX_VALUE + || getLength() > Character.MAX_VALUE) { throw new IllegalArgumentException("Too large"); } nbtMap = new HashMap<>(); @@ -99,21 +108,6 @@ public class DiskOptimizedClipboard extends LinearClipboard { } } - @Override - public URI getURI() { - return file.toURI(); - } - - private static BlockVector3 readSize(File file) { - try (DataInputStream is = new DataInputStream(new FileInputStream(file))) { - is.skipBytes(2); - return BlockVector3.at(is.readChar(), is.readChar(), is.readChar()); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - public DiskOptimizedClipboard(File file) { super(readSize(file)); nbtMap = new HashMap<>(); @@ -130,6 +124,21 @@ public class DiskOptimizedClipboard extends LinearClipboard { } } + private static BlockVector3 readSize(File file) { + try (DataInputStream is = new DataInputStream(new FileInputStream(file))) { + is.skipBytes(2); + return BlockVector3.at(is.readChar(), is.readChar(), is.readChar()); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + @Override + public URI getURI() { + return file.toURI(); + } + public File getFile() { return file; } @@ -186,7 +195,9 @@ public class DiskOptimizedClipboard extends LinearClipboard { @Override public void streamBiomes(IntValueReader task) { - if (!hasBiomes()) return; + if (!hasBiomes()) { + return; + } int index = 0; int mbbIndex = HEADER_SIZE + (getVolume() << 1); try { @@ -209,7 +220,8 @@ public class DiskOptimizedClipboard extends LinearClipboard { public BlockArrayClipboard toClipboard() { try { - CuboidRegion region = new CuboidRegion(BlockVector3.at(0, 0, 0), BlockVector3.at(getWidth() - 1, getHeight() - 1, getLength() - 1)); + CuboidRegion region = new CuboidRegion(BlockVector3.at(0, 0, 0), + BlockVector3.at(getWidth() - 1, getHeight() - 1, getLength() - 1)); int ox = byteBuffer.getShort(8); int oy = byteBuffer.getShort(10); int oz = byteBuffer.getShort(12); @@ -240,7 +252,9 @@ public class DiskOptimizedClipboard extends LinearClipboard { } private void closeDirectBuffer(ByteBuffer cb) { - if (cb == null || !cb.isDirect()) return; + if (cb == null || !cb.isDirect()) { + return; + } // we could use this type cast and call functions without reflection code, // but static import from sun.* package is risky for non-SUN virtual machine. //try { ((sun.nio.ch.DirectBuffer)cb).cleaner().clean(); } catch (Exception ex) { } @@ -256,7 +270,8 @@ public class DiskOptimizedClipboard extends LinearClipboard { final Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe"); theUnsafeField.setAccessible(true); final Object theUnsafe = theUnsafeField.get(null); - final Method invokeCleanerMethod = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class); + final Method invokeCleanerMethod = unsafeClass + .getMethod("invokeCleaner", ByteBuffer.class); invokeCleanerMethod.invoke(theUnsafe, cb); } catch (Exception e) { System.gc(); @@ -283,11 +298,6 @@ public class DiskOptimizedClipboard extends LinearClipboard { } } - private int ylast; - private int ylasti; - private int zlast; - private int zlasti; - @Override public Collection getTileEntities() { return nbtMap.values(); @@ -410,7 +420,8 @@ public class DiskOptimizedClipboard extends LinearClipboard { @Nullable @Override public Entity createEntity(Location location, BaseEntity entity) { - BlockArrayClipboard.ClipboardEntity ret = new BlockArrayClipboard.ClipboardEntity(location, entity); + BlockArrayClipboard.ClipboardEntity ret = new BlockArrayClipboard.ClipboardEntity(location, + entity); entities.add(ret); return ret; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/MainUtil.java b/worldedit-core/src/main/java/com/boydti/fawe/util/MainUtil.java index 051967c53..25dfbbdea 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/MainUtil.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/MainUtil.java @@ -4,7 +4,6 @@ import static java.lang.System.arraycopy; import static org.slf4j.LoggerFactory.getLogger; import com.boydti.fawe.Fawe; -import com.sk89q.worldedit.util.formatting.WorldEditText; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import com.boydti.fawe.config.Settings; @@ -30,7 +29,6 @@ import com.sk89q.worldedit.history.changeset.ChangeSet; import com.sk89q.worldedit.util.Location; import java.awt.Graphics2D; import java.awt.image.BufferedImage; -import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -62,7 +60,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Scanner; import java.util.UUID; @@ -727,7 +724,7 @@ public class MainUtil { public static Object copyNd(Object arr) { if (arr.getClass().isArray()) { int innerArrayLength = Array.getLength(arr); - Class component = arr.getClass().getComponentType(); + Class component = arr.getClass().getComponentType(); Object newInnerArray = Array.newInstance(component, innerArrayLength); if (component.isPrimitive()) { arraycopy(arr, 0, newInnerArray, 0, innerArrayLength); @@ -740,7 +737,7 @@ public class MainUtil { } return newInnerArray; } else { - return arr;//cant deep copy an opac object?? + return arr;//can't deep copy an opac object?? } } diff --git a/worldedit-core/src/main/java/net/jpountz/util/Native.java b/worldedit-core/src/main/java/net/jpountz/util/Native.java index f72b6bfbb..a4af2319d 100644 --- a/worldedit-core/src/main/java/net/jpountz/util/Native.java +++ b/worldedit-core/src/main/java/net/jpountz/util/Native.java @@ -12,17 +12,18 @@ package net.jpountz.util; * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * + * This file has been modified for use in the FAWE project. */ -import com.boydti.fawe.Fawe; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.FilenameFilter; -/** - * FOR INTERNAL USE ONLY - */ +/** FOR INTERNAL USE ONLY */ public enum Native { ; @@ -31,7 +32,7 @@ public enum Native { WINDOWS("win32", "so"), LINUX("linux", "so"), MAC("darwin", "dylib"), SOLARIS("solaris", "so"); public final String name, libExtension; - OS(String name, String libExtension) { + private OS(String name, String libExtension) { this.name = name; this.libExtension = libExtension; } @@ -53,13 +54,15 @@ public enum Native { return OS.SOLARIS; } else { throw new UnsupportedOperationException("Unsupported operating system: " - + osName); + + osName); } } private static String resourceName() { OS os = os(); - return "/" + os.name + "/" + arch() + "/liblz4-java." + os.libExtension; + String packagePrefix = Native.class.getPackage().getName().replace('.', '/'); + + return "/" + packagePrefix + "/" + os.name + "/" + arch() + "/liblz4-java." + os.libExtension; } private static boolean loaded = false; @@ -68,21 +71,57 @@ public enum Native { return loaded; } + private static void cleanupOldTempLibs() { + String tempFolder = new File(System.getProperty("java.io.tmpdir")).getAbsolutePath(); + File dir = new File(tempFolder); + + File[] tempLibFiles = dir.listFiles((dir1, name) -> + name.startsWith("liblz4-java-") && !name.endsWith(".lck")); + if(tempLibFiles != null) { + for(File tempLibFile : tempLibFiles) { + File lckFile = new File(tempLibFile.getAbsolutePath() + ".lck"); + if(!lckFile.exists()) { + try { + tempLibFile.delete(); + } + catch(SecurityException e) { + System.err.println("Failed to delete old temp lib" + e.getMessage()); + } + } + } + } + } + public static synchronized void load() { if (loaded) { return; } + + cleanupOldTempLibs(); + + // Try to load lz4-java (liblz4-java.so on Linux) from the java.library.path. + try { + System.loadLibrary("lz4-java"); + loaded = true; + return; + } catch (UnsatisfiedLinkError ex) { + // Doesn't exist, so proceed to loading bundled library. + } + String resourceName = resourceName(); - InputStream is = Fawe.class.getResourceAsStream(resourceName); + InputStream is = Native.class.getResourceAsStream(resourceName); if (is == null) { throw new UnsupportedOperationException("Unsupported OS/arch, cannot find " + resourceName + ". Please try building from source."); } - File tempLib; + File tempLib = null; + File tempLibLock = null; try { - tempLib = File.createTempFile("liblz4-java", "." + os().libExtension); + // Create the .lck file first to avoid a race condition + // with other concurrently running Java processes using lz4-java. + tempLibLock = File.createTempFile("liblz4-java-", "." + os().libExtension + ".lck"); + tempLib = new File(tempLibLock.getAbsolutePath().replaceFirst(".lck$", "")); // copy to tempLib - FileOutputStream out = new FileOutputStream(tempLib); - try { + try (FileOutputStream out = new FileOutputStream(tempLib)) { byte[] buf = new byte[4096]; while (true) { int read = is.read(buf); @@ -91,34 +130,27 @@ public enum Native { } out.write(buf, 0, read); } - try { - out.close(); - out = null; - } catch (IOException e) { - // ignore - } - System.load(tempLib.getAbsolutePath()); - loaded = true; - } finally { - try { - if (out != null) { - out.close(); - } - } catch (IOException e) { - // ignore - } - if (tempLib != null && tempLib.exists()) { - if (!loaded) { - tempLib.delete(); - } else { - // try to delete on exit, does it work on Windows? - tempLib.deleteOnExit(); - } - } } + System.load(tempLib.getAbsolutePath()); + loaded = true; } catch (IOException e) { - throw new ExceptionInInitializerError("Cannot unpack liblz4-java"); + throw new ExceptionInInitializerError("Cannot unpack liblz4-java: " + e); + } finally { + if (!loaded) { + if (tempLib != null && tempLib.exists()) { + if (!tempLib.delete()) { + throw new ExceptionInInitializerError("Cannot unpack liblz4-java / cannot delete a temporary native library " + tempLib); + } + } + if (tempLibLock != null && tempLibLock.exists()) { + if (!tempLibLock.delete()) { + throw new ExceptionInInitializerError("Cannot unpack liblz4-java / cannot delete a temporary lock file " + tempLibLock); + } + } + } else { + tempLib.deleteOnExit(); + tempLibLock.deleteOnExit(); + } } } - }