From 8356004ec9021a729d78728cd90a909b704ce8bf Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Sat, 26 Oct 2019 14:21:49 +0100 Subject: [PATCH] chunk sending / cfi --- worldedit-bukkit/build.gradle.kts | 4 + .../com/boydti/fawe/bukkit/FaweBukkit.java | 21 +- .../adapter/mc1_14/BukkitAdapter_1_14.java | 9 +- .../adapter/mc1_14/Spigot_v1_14_R4.java | 9 + .../boydti/fawe/bukkit/chat/ArrayWrapper.java | 111 ------ .../bukkit/chat/JsonRepresentedObject.java | 18 - .../boydti/fawe/bukkit/chat/JsonString.java | 46 --- .../boydti/fawe/bukkit/chat/Reflection.java | 201 ----------- .../fawe/bukkit/listener/ChunkCache.java | 4 - .../bukkit/preloader/PluginPreloader.java | 196 +++++++++++ .../bukkit/util/image/BukkitImageViewer.java | 296 ++++++++-------- .../worldedit/bukkit/BukkitBlockRegistry.java | 10 + .../sk89q/worldedit/bukkit/BukkitPlayer.java | 22 ++ .../sk89q/worldedit/bukkit/BukkitWorld.java | 20 +- .../worldedit/bukkit/WorldEditPlugin.java | 17 +- .../main/java/com/boydti/fawe/FaweCache.java | 3 + .../src/main/java/com/boydti/fawe/IFawe.java | 4 + .../com/boydti/fawe/beta/ChunkFuture.java | 61 ---- .../java/com/boydti/fawe/beta/IBlocks.java | 16 + .../java/com/boydti/fawe/beta/IChunkGet.java | 2 + .../java/com/boydti/fawe/beta/IChunkSet.java | 5 + .../fawe/beta/IDelegateQueueExtent.java | 10 - .../com/boydti/fawe/beta/IQueueExtent.java | 16 - .../fawe/beta/implementation/ChunkPacket.java | 126 +++++++ .../implementation/ChunkSendProcessor.java | 40 +++ .../implementation/ParallelQueueExtent.java | 1 + .../beta/implementation/QueueHandler.java | 147 +++++--- .../implementation/blocks/BitSetBlocks.java | 5 + .../implementation/blocks/CharBlocks.java | 2 +- .../implementation/blocks/CharSetBlocks.java | 5 + .../fawe/beta/preloader/AsyncPreloader.java | 110 ++++++ .../boydti/fawe/beta/preloader/Preloader.java | 9 + .../com/boydti/fawe/command/CFICommands.java | 6 +- .../java/com/boydti/fawe/object/Lazy.java | 8 + .../brush/visualization/VisualExtent.java | 4 +- .../brush/visualization/cfi/CFIDrawer.java | 142 ++++++++ .../cfi/HeightMapMCAGenerator.java | 315 +++++++++--------- .../brush/visualization/cfi/MCAWriter.java | 7 +- .../visualization/cfi/WritableMCAChunk.java | 190 ++++++----- .../collection/BlockVector3ChunkMap.java | 5 + .../object/collection/DifferentialArray.java | 6 +- .../collection/DifferentialBlockBuffer.java | 74 ++-- .../object/io/FastByteArrayOutputStream.java | 11 + .../plotquared/FaweLocalBlockQueue.java | 2 +- .../boydti/fawe/util/DelegateTextureUtil.java | 2 +- .../main/java/com/boydti/fawe/util/Jars.java | 8 +- .../boydti/fawe/util/RandomTextureUtil.java | 6 +- .../com/boydti/fawe/util/TextureUtil.java | 4 +- .../boydti/fawe/wrappers/WorldWrapper.java | 4 +- .../com/sk89q/worldedit/LocalSession.java | 9 +- .../worldedit/command/tool/DistanceWand.java | 3 +- .../worldedit/entity/MapMetadatable.java | 4 + .../com/sk89q/worldedit/entity/Player.java | 11 +- .../extension/platform/PlayerProxy.java | 6 + .../com/sk89q/worldedit/world/NullWorld.java | 2 +- .../java/com/sk89q/worldedit/world/World.java | 2 +- 56 files changed, 1378 insertions(+), 999 deletions(-) delete mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/ArrayWrapper.java delete mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonRepresentedObject.java delete mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonString.java delete mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/Reflection.java delete mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ChunkCache.java create mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/preloader/PluginPreloader.java delete mode 100644 worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFuture.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkPacket.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkSendProcessor.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/beta/preloader/AsyncPreloader.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/beta/preloader/Preloader.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/object/Lazy.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/CFIDrawer.java diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index a8d6d3c4f..2fdabbcca 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -20,6 +20,7 @@ repositories { maven { url = uri("https://repo.destroystokyo.com/repository/maven-public//") } maven { url = uri("http://repo.dmulloy2.net/content/groups/public/") } maven { url = uri("http://ci.ender.zone/plugin/repository/everything/") } + maven { url = uri("https://repo.inventivetalent.org/content/groups/public/")} } configurations.all { @@ -49,6 +50,9 @@ dependencies { exclude("com.sk89q.worldedit.worldedit-libs", "bukkit") exclude("com.sk89q.worldedit.worldedit-libs", "core") } + "implementation"("org.inventivetalent:mapmanager:1.7.3-SNAPSHOT") { isTransitive = false } + "implementation"("org.inventivetalent:mapmanager:1.7.3-SNAPSHOT") { isTransitive = false } + "implementation"("com.massivecraft:factions:2.8.0") { isTransitive = false } "implementation"("com.drtshock:factions:1.6.9.5") { isTransitive = false } "implementation"("com.github.TechFortress:GriefPrevention:16.12.0") { isTransitive = false } 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 bad0d4270..d33ed175f 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 @@ -3,6 +3,8 @@ package com.boydti.fawe.bukkit; import com.boydti.fawe.Fawe; import com.boydti.fawe.IFawe; import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.beta.preloader.AsyncPreloader; +import com.boydti.fawe.beta.preloader.Preloader; import com.boydti.fawe.bukkit.adapter.BukkitQueueHandler; import com.boydti.fawe.bukkit.listener.BrushListener; import com.boydti.fawe.bukkit.listener.BukkitImageListener; @@ -55,14 +57,14 @@ import org.bukkit.plugin.java.JavaPlugin; public class FaweBukkit implements IFawe, Listener { -// private final WorldEditPlugin plugin; private final Plugin plugin; private VaultUtil vault; private ItemUtil itemUtil; private boolean listeningImages; private BukkitImageListener imageListener; - //private CFIPacketListener packetListener; + + public static boolean PAPER; public VaultUtil getVault() { return this.vault; @@ -70,6 +72,13 @@ public class FaweBukkit implements IFawe, Listener { public FaweBukkit(Plugin plugin) { this.plugin = plugin; + try { + Class.forName("com.destroystokyo.paper.Namespaced"); + PAPER = true; + } catch (Throwable e) { + e.printStackTrace(); + // TODO no paper + } try { Settings.IMP.TICK_LIMITER.ENABLED = !Bukkit.hasWhitelist(); Fawe.set(this); @@ -398,4 +407,12 @@ public class FaweBukkit implements IFawe, Listener { return null; // return ((BlocksHubBukkit) blocksHubPlugin).getApi(); } + + @Override + public Preloader getPreloader() { + if (PAPER) { + return new AsyncPreloader(); + } + return null; + } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java index 8e80c56f1..d7682364e 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java @@ -2,6 +2,7 @@ package com.boydti.fawe.bukkit.adapter.mc1_14; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.bukkit.FaweBukkit; import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.BitArray4096; @@ -21,6 +22,7 @@ import net.minecraft.server.v1_14_R1.DataPaletteBlock; import net.minecraft.server.v1_14_R1.DataPaletteLinear; import net.minecraft.server.v1_14_R1.GameProfileSerializer; import net.minecraft.server.v1_14_R1.IBlockData; +import net.minecraft.server.v1_14_R1.PacketPlayOutMapChunk; import net.minecraft.server.v1_14_R1.PlayerChunk; import net.minecraft.server.v1_14_R1.PlayerChunkMap; import org.bukkit.craftbukkit.v1_14_R1.CraftChunk; @@ -133,8 +135,6 @@ public class BukkitAdapter_1_14 { } } - private static boolean PAPER = true; - public static Chunk ensureLoaded(net.minecraft.server.v1_14_R1.World nmsWorld, int X, int Z) { Chunk nmsChunk = nmsWorld.getChunkIfLoaded(X, Z); if (nmsChunk != null) { @@ -143,7 +143,7 @@ public class BukkitAdapter_1_14 { if (Fawe.isMainThread()) { return nmsWorld.getChunkAt(X, Z); } - if (PAPER) { + if (FaweBukkit.PAPER) { CraftWorld craftWorld = nmsWorld.getWorld(); CompletableFuture future = craftWorld.getChunkAtAsync(X, Z, true); try { @@ -154,8 +154,7 @@ public class BukkitAdapter_1_14 { } catch (ExecutionException e) { e.printStackTrace(); } catch (Throwable e) { - System.out.println("Error, cannot load chunk async (paper not installed?)"); - PAPER = false; + e.printStackTrace(); } } // TODO optimize diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/Spigot_v1_14_R4.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/Spigot_v1_14_R4.java index b6bf9693b..812689158 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/Spigot_v1_14_R4.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/Spigot_v1_14_R4.java @@ -19,6 +19,7 @@ package com.boydti.fawe.bukkit.adapter.mc1_14; +import com.bekvon.bukkit.residence.commands.material; import com.boydti.fawe.Fawe; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; @@ -132,6 +133,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.OptionalInt; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.function.Supplier; @@ -590,6 +592,13 @@ public final class Spigot_v1_14_R4 extends CachedBukkitAdapter implements Bukkit } } + @Override + public OptionalInt getInternalBlockStateId(BlockState state) { + BlockMaterial_1_14 material = (BlockMaterial_1_14) state.getMaterial(); + IBlockData mcState = material.getCraftBlockData().getState(); + return OptionalInt.of(Block.REGISTRY_ID.getId(mcState)); + } + @Override public BlockState adapt(BlockData blockData) { CraftBlockData cbd = ((CraftBlockData) blockData); diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/ArrayWrapper.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/ArrayWrapper.java deleted file mode 100644 index d24b7cc19..000000000 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/ArrayWrapper.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.boydti.fawe.bukkit.chat; - -import java.lang.reflect.Array; -import java.util.Arrays; -import java.util.Collection; -import org.apache.commons.lang.Validate; - -/** - * Represents a wrapper around an array class of an arbitrary reference type, - * which properly implements "value" hash code and equality functions. - *

- * This class is intended for use as a key to a map. - *

- * - * @param The type of elements in the array. - * @author Glen Husman - * @see Arrays - */ -public final class ArrayWrapper { - - /** - * Creates an array wrapper with some elements. - * - * @param elements The elements of the array. - */ - @SafeVarargs - public ArrayWrapper(E... elements) { - setArray(elements); - } - - private E[] _array; - - /** - * Retrieves a reference to the wrapped array instance. - * - * @return The array wrapped by this instance. - */ - public E[] getArray() { - return _array; - } - - /** - * Set this wrapper to wrap a new array instance. - * - * @param array The new wrapped array. - */ - public void setArray(E[] array) { - Validate.notNull(array, "The array must not be null."); - _array = array; - } - - /** - * Determines if this object has a value equivalent to another object. - * - * @see Arrays#equals(Object[], Object[]) - */ - @SuppressWarnings("rawtypes") - @Override - public boolean equals(Object other) { - if (!(other instanceof ArrayWrapper)) { - return false; - } - return Arrays.equals(_array, ((ArrayWrapper) other)._array); - } - - /** - * Gets the hash code represented by this objects value. - * - * @return This object's hash code. - * @see Arrays#hashCode(Object[]) - */ - @Override - public int hashCode() { - return Arrays.hashCode(_array); - } - - /** - * Converts an iterable element collection to an array of elements. - * The iteration order of the specified object will be used as the array element order. - * - * @param list The iterable of objects which will be converted to an array. - * @param c The type of the elements of the array. - * @return An array of elements in the specified iterable. - */ - @SuppressWarnings("unchecked") - public static T[] toArray(Iterable list, Class c) { - int size = -1; - if (list instanceof Collection) { - @SuppressWarnings("rawtypes") - Collection coll = (Collection) list; - size = coll.size(); - } - - - if (size < 0) { - size = 0; - // Ugly hack: Count it ourselves - for (@SuppressWarnings("unused") T element : list) { - size++; - } - } - - T[] result = (T[]) Array.newInstance(c, size); - int i = 0; - for (T element : list) { // Assumes iteration order is consistent - result[i++] = element; // Assign array element at index THEN increment counter - } - return result; - } - -} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonRepresentedObject.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonRepresentedObject.java deleted file mode 100644 index 0996a1aaf..000000000 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonRepresentedObject.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.boydti.fawe.bukkit.chat; - -import com.google.gson.stream.JsonWriter; -import java.io.IOException; - -/** - * Represents an object that can be serialized to a JSON writer instance. - */ -interface JsonRepresentedObject { - - /** - * Writes the JSON representation of this object to the specified writer. - * @param writer The JSON writer which will receive the object. - * @throws IOException If an error occurs writing to the stream. - */ - public void writeJson(JsonWriter writer) throws IOException; - -} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonString.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonString.java deleted file mode 100644 index 115bdf66e..000000000 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/JsonString.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.boydti.fawe.bukkit.chat; - -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import org.bukkit.configuration.serialization.ConfigurationSerializable; - -/** - * Represents a JSON string value. - * Writes by this object will not write name values nor begin/end objects in the JSON stream. - * All writes merely write the represented string value. - */ -final class JsonString implements JsonRepresentedObject, ConfigurationSerializable { - - private String _value; - - public JsonString(CharSequence value) { - _value = value == null ? null : value.toString(); - } - - @Override - public void writeJson(JsonWriter writer) throws IOException { - writer.value(getValue()); - } - - public String getValue() { - return _value; - } - - public Map serialize() { - HashMap theSingleValue = new HashMap<>(); - theSingleValue.put("stringValue", _value); - return theSingleValue; - } - - public static JsonString deserialize(Map map) { - return new JsonString(map.get("stringValue").toString()); - } - - @Override - public String toString() { - return _value; - } - -} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/Reflection.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/Reflection.java deleted file mode 100644 index a60d4cdda..000000000 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/chat/Reflection.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.boydti.fawe.bukkit.chat; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import org.bukkit.Bukkit; - -/** - * A class containing static utility methods and caches which are intended as reflective conveniences. - * Unless otherwise noted, upon failure methods will return {@code null}. - */ -public final class Reflection { - - /** - * Stores loaded classes from the {@code net.minecraft.server} package. - */ - private static final Map> _loadedNMSClasses = new HashMap<>(); - /** - * Stores loaded classes from the {@code org.bukkit.craftbukkit} package (and subpackages). - */ - private static final Map> _loadedOBCClasses = new HashMap<>(); - private static final Map, Map> _loadedFields = new HashMap<>(); - /** - * Contains loaded methods in a cache. - * The map maps [types to maps of [method names to maps of [parameter types to method instances]]]. - */ - private static final Map, Map>, Method>>> _loadedMethods = new HashMap<>(); - private static String _versionString; - - private Reflection() { } - - /** - * Gets the version string from the package name of the CraftBukkit server implementation. - * This is needed to bypass the JAR package name changing on each update. - * - * @return The version string of the OBC and NMS packages, including the trailing dot. - */ - public synchronized static String getVersion() { - if (_versionString == null) { - String name = Bukkit.getServer().getClass().getPackage().getName(); - _versionString = name.substring(name.lastIndexOf('.') + 1) + "."; - } - - return _versionString; - } - - /** - * Gets a {@link Class} object representing a type contained within the {@code net.minecraft.server} versioned package. - * The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously). - * - * @param className The name of the class, excluding the package, within NMS. - * @return The class instance representing the specified NMS class, or {@code null} if it could not be loaded. - */ - public synchronized static Class getNMSClass(String className) { - if (_loadedNMSClasses.containsKey(className)) { - return _loadedNMSClasses.get(className); - } - - String fullName = "net.minecraft.server." + getVersion() + className; - Class clazz; - try { - clazz = Class.forName(fullName); - } catch (ClassNotFoundException e) { - _loadedNMSClasses.put(className, null); - throw new RuntimeException(e); - } - _loadedNMSClasses.put(className, clazz); - return clazz; - } - - /** - * Gets a {@link Class} object representing a type contained within the {@code org.bukkit.craftbukkit} versioned package. - * The class instances returned by this method are cached, such that no lookup will be done twice (unless multiple threads are accessing this method simultaneously). - * - * @param className The name of the class, excluding the package, within OBC. This name may contain a subpackage name, such as {@code inventory.CraftItemStack}. - * @return The class instance representing the specified OBC class, or {@code null} if it could not be loaded. - */ - public synchronized static Class getOBCClass(String className) { - if (_loadedOBCClasses.containsKey(className)) { - return _loadedOBCClasses.get(className); - } - - String fullName = "org.bukkit.craftbukkit." + getVersion() + className; - Class clazz; - try { - clazz = Class.forName(fullName); - } catch (ClassNotFoundException e) { - _loadedOBCClasses.put(className, null); - throw new RuntimeException(e); - } - _loadedOBCClasses.put(className, clazz); - return clazz; - } - - /** - * Attempts to get the NMS handle of a CraftBukkit object. - *

- * The only match currently attempted by this method is a retrieval by using a parameterless {@code getHandle()} method implemented by the runtime type of the specified object. - *

- * - * @param obj The object for which to retrieve an NMS handle. - * @return The NMS handle of the specified object, or {@code null} if it could not be retrieved using {@code getHandle()}. - */ - public synchronized static Object getHandle(Object obj) throws InvocationTargetException, IllegalAccessException, IllegalArgumentException { - return getMethod(obj.getClass(), "getHandle").invoke(obj); - } - - /** - * Retrieves a {@link Field} instance declared by the specified class with the specified name. - * Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field - * returned will be an instance or static field. - *

- * A global caching mechanism within this class is used to store fields. Combined with synchronization, this guarantees that - * no field will be reflectively looked up twice. - *

- *

- * If a field is deemed suitable for return, {@link Field#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned. - * This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance. - *

- * - * @param clazz The class which contains the field to retrieve. - * @param name The declared name of the field in the class. - * @return A field object with the specified name declared by the specified class. - * @see Class#getDeclaredField(String) - */ - public synchronized static Field getField(Class clazz, String name) { - Map loaded; - if (!_loadedFields.containsKey(clazz)) { - loaded = new HashMap<>(); - _loadedFields.put(clazz, loaded); - } else { - loaded = _loadedFields.get(clazz); - } - if (loaded.containsKey(name)) { - // If the field is loaded (or cached as not existing), return the relevant value, which might be null - return loaded.get(name); - } - try { - Field field = clazz.getDeclaredField(name); - field.setAccessible(true); - loaded.put(name, field); - return field; - } catch (NoSuchFieldException | SecurityException e) { - // Error loading - e.printStackTrace(); - // Cache field as not existing - loaded.put(name, null); - return null; - } - } - - /** - * Retrieves a {@link Method} instance declared by the specified class with the specified name and argument types. - * Java access modifiers are ignored during this retrieval. No guarantee is made as to whether the field - * returned will be an instance or static field. - *

- * A global caching mechanism within this class is used to store method. Combined with synchronization, this guarantees that - * no method will be reflectively looked up twice. - *

- * If a method is deemed suitable for return, {@link Method#setAccessible(boolean) setAccessible} will be invoked with an argument of {@code true} before it is returned. - * This ensures that callers do not have to check or worry about Java access modifiers when dealing with the returned instance. - *

- * This method does not search superclasses of the specified type for methods with the specified signature. - * Callers wishing this behavior should use {@link Class#getDeclaredMethod(String, Class...)}. - * - * @param clazz The class which contains the method to retrieve. - * @param name The declared name of the method in the class. - * @param args The formal argument types of the method. - * @return A method object with the specified name declared by the specified class. - */ - public synchronized static Method getMethod(Class clazz, String name, Class... args) { - if (!_loadedMethods.containsKey(clazz)) { - _loadedMethods.put(clazz, new HashMap<>()); - } - - Map>, Method>> loadedMethodNames = _loadedMethods.get(clazz); - if (!loadedMethodNames.containsKey(name)) { - loadedMethodNames.put(name, new HashMap<>()); - } - - Map>, Method> loadedSignatures = loadedMethodNames.get(name); - ArrayWrapper> wrappedArg = new ArrayWrapper<>(args); - if (loadedSignatures.containsKey(wrappedArg)) { - return loadedSignatures.get(wrappedArg); - } - - for (Method m : clazz.getMethods()) { - if (m.getName().equals(name) && Arrays.equals(args, m.getParameterTypes())) { - m.setAccessible(true); - loadedSignatures.put(wrappedArg, m); - return m; - } - } - loadedSignatures.put(wrappedArg, null); - return null; - } - -} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ChunkCache.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ChunkCache.java deleted file mode 100644 index 115740f29..000000000 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ChunkCache.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.boydti.fawe.bukkit.listener; - -public class ChunkCache { -} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/preloader/PluginPreloader.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/preloader/PluginPreloader.java new file mode 100644 index 000000000..dcc98dc26 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/preloader/PluginPreloader.java @@ -0,0 +1,196 @@ +package com.boydti.fawe.bukkit.preloader; + +import com.boydti.fawe.Fawe; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.PluginBase; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginLoader; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.InputStream; +import java.lang.ref.PhantomReference; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Logger; + +public class PluginPreloader extends PluginBase { + private World world; + private Set loaded; + private int index; + private AtomicBoolean invalidator; + private final Object invalidatorLock; + + public PluginPreloader() { + invalidator = new AtomicBoolean(); + invalidatorLock = new Object(); + } + + public AtomicBoolean invalidate() { + synchronized (invalidatorLock) { + invalidator.set(false); + return invalidator = new AtomicBoolean(true); + } + } + + private synchronized void unload() { + World oldWorld = world; + if (oldWorld != null) { + Set toUnload = loaded; + if (loaded != null && index > 0) { + Iterator iter = toUnload.iterator(); + Fawe.get().getQueueHandler().sync(() -> { + for (int i = 0; i < index && iter.hasNext(); i++) { + BlockVector2 chunk = iter.next(); + world.removePluginChunkTicket(chunk.getX(), chunk.getZ(), this); + } + }); + } + } + this.world = null; + this.loaded = null; + this.index = 0; + } + + public void update(Region region) { + AtomicBoolean invalidator = invalidate(); + synchronized (this) { + com.sk89q.worldedit.world.World weWorld = region.getWorld(); + if (weWorld == null) { + return; + } + unload(); + index = 0; + world = BukkitAdapter.adapt(weWorld); + loaded = region.getChunks(); + Iterator iter = loaded.iterator(); + + if (!invalidator.get()) return; + Fawe.get().getQueueHandler().syncWhenFree(() -> { + for (; iter.hasNext() && invalidator.get();index++) { + BlockVector2 chunk = iter.next(); + if (!world.isChunkLoaded(chunk.getX(), chunk.getZ())) { + world.addPluginChunkTicket(chunk.getX(), chunk.getZ(), this); + } + } + }); + } + } + + public void clear() { + invalidate(); + unload(); + } + + @Override + public @NotNull File getDataFolder() { + return null; + } + + @Override + public @NotNull PluginDescriptionFile getDescription() { + return null; + } + + @Override + public @NotNull FileConfiguration getConfig() { + return null; + } + + @Override + public @Nullable InputStream getResource(@NotNull String filename) { + return null; + } + + @Override + public void saveConfig() { + + } + + @Override + public void saveDefaultConfig() { + + } + + @Override + public void saveResource(@NotNull String resourcePath, boolean replace) { + + } + + @Override + public void reloadConfig() { + + } + + @Override + public @NotNull PluginLoader getPluginLoader() { + return null; + } + + @Override + public @NotNull Server getServer() { + return null; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public void onDisable() { + + } + + @Override + public void onLoad() { + + } + + @Override + public void onEnable() { + + } + + @Override + public boolean isNaggable() { + return false; + } + + @Override + public void setNaggable(boolean canNag) { + + } + + @Override + public @Nullable ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, @Nullable String id) { + return null; + } + + @Override + public @NotNull Logger getLogger() { + return null; + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + return false; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) { + return null; + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java index 73a0877ba..b49a35b3f 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/util/image/BukkitImageViewer.java @@ -19,173 +19,173 @@ import org.bukkit.entity.ItemFrame; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; -//import org.inventivetalent.mapmanager.MapManagerPlugin; -//import org.inventivetalent.mapmanager.controller.MapController; -//import org.inventivetalent.mapmanager.controller.MultiMapController; -//import org.inventivetalent.mapmanager.manager.MapManager; -//import org.inventivetalent.mapmanager.wrapper.MapWrapper; +import org.inventivetalent.mapmanager.MapManagerPlugin; +import org.inventivetalent.mapmanager.controller.MapController; +import org.inventivetalent.mapmanager.controller.MultiMapController; +import org.inventivetalent.mapmanager.manager.MapManager; +import org.inventivetalent.mapmanager.wrapper.MapWrapper; public class BukkitImageViewer implements ImageViewer { -// private final MapManager mapManager; -// private final Player player; -// private BufferedImage last; + private final MapManager mapManager; + private final Player player; + private BufferedImage last; private ItemFrame[][] frames; -// private boolean reverse; + private boolean reverse; public BukkitImageViewer(Player player) { -// mapManager = ((MapManagerPlugin) Bukkit.getPluginManager().getPlugin("MapManager")).getMapManager(); -// this.player = player; + mapManager = ((MapManagerPlugin) Bukkit.getPluginManager().getPlugin("MapManager")).getMapManager(); + this.player = player; } -// + public void selectFrame(ItemFrame start) { -// Location pos1 = start.getLocation().clone(); -// Location pos2 = start.getLocation().clone(); -// -// BlockFace facing = start.getFacing(); -// int planeX = facing.getModX() == 0 ? 1 : 0; -// int planeY = facing.getModY() == 0 ? 1 : 0; -// int planeZ = facing.getModZ() == 0 ? 1 : 0; -// -// ItemFrame[][] res = find(pos1, pos2, facing); -// Location tmp; -// while (true) { -// if (res != null) { -// frames = res; -// } -// tmp = pos1.clone().subtract(planeX, planeY, planeZ); -// if ((res = find(tmp, pos2, facing)) != null) { -// pos1 = tmp; -// continue; -// } -// tmp = pos2.clone().add(planeX, planeY, planeZ); -// if ((res = find(pos1, tmp, facing)) != null) { -// pos2 = tmp; -// continue; -// } -// tmp = pos1.clone().subtract(planeX, 0, planeZ); -// if ((res = find(tmp, pos2, facing)) != null) { -// pos1 = tmp; -// continue; -// } -// tmp = pos2.clone().add(planeX, 0, planeZ); -// if ((res = find(pos1, tmp, facing)) != null) { -// pos2 = tmp; -// continue; -// } -// tmp = pos1.clone().subtract(0, 1, 0); -// if ((res = find(tmp, pos2, facing)) != null) { -// pos1 = tmp; -// continue; -// } -// tmp = pos2.clone().add(0, 1, 0); -// if ((res = find(pos1, tmp, facing)) != null) { -// pos2 = tmp; -// continue; -// } -// break; -// } + Location pos1 = start.getLocation().clone(); + Location pos2 = start.getLocation().clone(); + + BlockFace facing = start.getFacing(); + int planeX = facing.getModX() == 0 ? 1 : 0; + int planeY = facing.getModY() == 0 ? 1 : 0; + int planeZ = facing.getModZ() == 0 ? 1 : 0; + + ItemFrame[][] res = find(pos1, pos2, facing); + Location tmp; + while (true) { + if (res != null) { + frames = res; + } + tmp = pos1.clone().subtract(planeX, planeY, planeZ); + if ((res = find(tmp, pos2, facing)) != null) { + pos1 = tmp; + continue; + } + tmp = pos2.clone().add(planeX, planeY, planeZ); + if ((res = find(pos1, tmp, facing)) != null) { + pos2 = tmp; + continue; + } + tmp = pos1.clone().subtract(planeX, 0, planeZ); + if ((res = find(tmp, pos2, facing)) != null) { + pos1 = tmp; + continue; + } + tmp = pos2.clone().add(planeX, 0, planeZ); + if ((res = find(pos1, tmp, facing)) != null) { + pos2 = tmp; + continue; + } + tmp = pos1.clone().subtract(0, 1, 0); + if ((res = find(tmp, pos2, facing)) != null) { + pos1 = tmp; + continue; + } + tmp = pos2.clone().add(0, 1, 0); + if ((res = find(pos1, tmp, facing)) != null) { + pos2 = tmp; + continue; + } + break; + } } -// + public ItemFrame[][] getItemFrames() { return frames; } -// -// private ItemFrame[][] find(Location pos1, Location pos2, BlockFace facing) { -// try { -// Location distance = pos2.clone().subtract(pos1).add(1, 1, 1); -// int width = Math.max(distance.getBlockX(), distance.getBlockZ()); -// ItemFrame[][] frames = new ItemFrame[width][distance.getBlockY()]; -// -// World world = pos1.getWorld(); -// -// this.reverse = (facing == BlockFace.NORTH || facing == BlockFace.EAST); -// int v = 0; -// for (double y = pos1.getY(); y <= pos2.getY(); y++, v++) { -// int h = 0; -// for (double z = pos1.getZ(); z <= pos2.getZ(); z++) { -// for (double x = pos1.getX(); x <= pos2.getX(); x++, h++) { -// Location pos = new Location(world, x, y, z); -// Collection entities = world.getNearbyEntities(pos, 0.1, 0.1, 0.1); -// boolean contains = false; -// for (Entity ent : entities) { -// if (ent instanceof ItemFrame && ((ItemFrame) ent).getFacing() == facing) { -// ItemFrame itemFrame = (ItemFrame) ent; -// itemFrame.setRotation(Rotation.NONE); -// contains = true; -// frames[reverse ? width - 1 - h : h][v] = (ItemFrame) ent; -// break; -// } -// } -// if (!contains) return null; -// } -// } -// } -// return frames; -// } catch (Throwable e) { -// e.printStackTrace(); -// } -// return null; -// } + + private ItemFrame[][] find(Location pos1, Location pos2, BlockFace facing) { + try { + Location distance = pos2.clone().subtract(pos1).add(1, 1, 1); + int width = Math.max(distance.getBlockX(), distance.getBlockZ()); + ItemFrame[][] frames = new ItemFrame[width][distance.getBlockY()]; + + World world = pos1.getWorld(); + + this.reverse = (facing == BlockFace.NORTH || facing == BlockFace.EAST); + int v = 0; + for (double y = pos1.getY(); y <= pos2.getY(); y++, v++) { + int h = 0; + for (double z = pos1.getZ(); z <= pos2.getZ(); z++) { + for (double x = pos1.getX(); x <= pos2.getX(); x++, h++) { + Location pos = new Location(world, x, y, z); + Collection entities = world.getNearbyEntities(pos, 0.1, 0.1, 0.1); + boolean contains = false; + for (Entity ent : entities) { + if (ent instanceof ItemFrame && ((ItemFrame) ent).getFacing() == facing) { + ItemFrame itemFrame = (ItemFrame) ent; + itemFrame.setRotation(Rotation.NONE); + contains = true; + frames[reverse ? width - 1 - h : h][v] = (ItemFrame) ent; + break; + } + } + if (!contains) return null; + } + } + } + return frames; + } catch (Throwable e) { + e.printStackTrace(); + } + return null; + } @Override public void view(Drawable drawable) { -// view(null, drawable); + view(null, drawable); } -// -// private void view(@Nullable BufferedImage image, @Nullable Drawable drawable) { -// if (image == null && drawable == null) throw new IllegalArgumentException("An image or drawable must be provided. Both cannot be null"); -// boolean initializing = last == null; -// -// if (this.frames != null) { -// if (image == null && drawable != null) image = drawable.draw(); -// last = image; -// int width = frames.length; -// int height = frames[0].length; -// BufferedImage scaled = ImageUtil.getScaledInstance(image, 128 * width, 128 * height, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); -// MapWrapper mapWrapper = mapManager.wrapMultiImage(scaled, width, height); -// MultiMapController controller = (MultiMapController) mapWrapper.getController(); -// controller.addViewer(player); -// controller.sendContent(player); -// controller.showInFrames(player, frames, true); -// } else { -// int slot = getMapSlot(player); -// if (slot == -1) { -// if (initializing) { -// player.getInventory().setItemInMainHand(new ItemStack(Material.MAP)); -// } else { -// return; -// } -// } else if (player.getInventory().getHeldItemSlot() != slot) { -// player.getInventory().setHeldItemSlot(slot); -// } -// if (image == null && drawable != null) image = drawable.draw(); -// last = image; -// BufferedImage scaled = ImageUtil.getScaledInstance(image, 128, 128, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); -// MapWrapper mapWrapper = mapManager.wrapImage(scaled); -// MapController controller = mapWrapper.getController(); -// controller.addViewer(player); -// controller.sendContent(player); -// controller.showInHand(player, true); -// } -// } -// -// private int getMapSlot(Player player) { -// PlayerInventory inventory = player.getInventory(); -// for (int i = 0; i < 9; i++) { -// ItemStack item = inventory.getItem(i); -// if (item != null && item.getType() == Material.MAP) { -// return i; -// } -// } -// return -1; -// } -// + + private void view(@Nullable BufferedImage image, @Nullable Drawable drawable) { + if (image == null && drawable == null) throw new IllegalArgumentException("An image or drawable must be provided. Both cannot be null"); + boolean initializing = last == null; + + if (this.frames != null) { + if (image == null && drawable != null) image = drawable.draw(); + last = image; + int width = frames.length; + int height = frames[0].length; + BufferedImage scaled = ImageUtil.getScaledInstance(image, 128 * width, 128 * height, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); + MapWrapper mapWrapper = mapManager.wrapMultiImage(scaled, width, height); + MultiMapController controller = (MultiMapController) mapWrapper.getController(); + controller.addViewer(player); + controller.sendContent(player); + controller.showInFrames(player, frames, true); + } else { + int slot = getMapSlot(player); + if (slot == -1) { + if (initializing) { + player.getInventory().setItemInMainHand(new ItemStack(Material.MAP)); + } else { + return; + } + } else if (player.getInventory().getHeldItemSlot() != slot) { + player.getInventory().setHeldItemSlot(slot); + } + if (image == null && drawable != null) image = drawable.draw(); + last = image; + BufferedImage scaled = ImageUtil.getScaledInstance(image, 128, 128, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); + MapWrapper mapWrapper = mapManager.wrapImage(scaled); + MapController controller = mapWrapper.getController(); + controller.addViewer(player); + controller.sendContent(player); + controller.showInHand(player, true); + } + } + + private int getMapSlot(Player player) { + PlayerInventory inventory = player.getInventory(); + for (int i = 0; i < 9; i++) { + ItemStack item = inventory.getItem(i); + if (item != null && item.getType() == Material.MAP) { + return i; + } + } + return -1; + } + public void refresh() { -// if (last != null) view(last, null); + if (last != null) view(last, null); } @Override public void close() throws IOException { -// last = null; + last = null; } } \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java index c1427985b..a8463ce59 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockRegistry.java @@ -34,6 +34,7 @@ import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.Map; +import java.util.OptionalInt; public class BukkitBlockRegistry extends BundledBlockRegistry { @@ -93,6 +94,10 @@ public class BukkitBlockRegistry extends BundledBlockRegistry { this.material = bukkitMaterial; } + public int getId() { + return material.getId(); + } + @Override public boolean isAir() { switch (material) { @@ -132,4 +137,9 @@ public class BukkitBlockRegistry extends BundledBlockRegistry { } return blocks; } + + @Override + public OptionalInt getInternalBlockStateId(BlockState state) { + return WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBlockStateId(state); + } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java index 92aaf29d5..091b3242d 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java @@ -24,6 +24,10 @@ import com.boydti.fawe.bukkit.FaweBukkit; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.util.TaskManager; +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.injector.netty.WirePacket; import com.sk89q.util.StringUtil; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; @@ -47,10 +51,13 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.gamemode.GameModes; + +import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.UUID; +import java.util.function.Supplier; import javax.annotation.Nullable; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -359,6 +366,21 @@ public class BukkitPlayer extends AbstractPlayerActor { } } + @Override + public void sendFakeChunk(int chunkX, int chunkZ, Supplier data) { + ProtocolManager manager = ProtocolLibrary.getProtocolManager(); + // check if the chunk is in range + if (true) { + try { + byte[] raw = data.get(); + WirePacket packet = new WirePacket(PacketType.Play.Server.MAP_CHUNK, raw); + manager.sendWirePacket(getPlayer(), packet); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + } + @Override public void sendTitle(String title, String sub) { player.sendTitle(ChatColor.GOLD + title, ChatColor.GOLD + sub, 0, 70, 20); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 48ff11687..412e8cc42 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -21,7 +21,9 @@ package com.sk89q.worldedit.bukkit; import static com.google.common.base.Preconditions.checkNotNull; +import com.boydti.fawe.Fawe; import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.bukkit.FaweBukkit; import com.boydti.fawe.bukkit.adapter.mc1_14.BukkitGetBlocks_1_14; import com.boydti.fawe.config.Settings; import com.sk89q.jnbt.CompoundTag; @@ -317,8 +319,19 @@ public class BukkitWorld extends AbstractWorld { @Override public void checkLoadedChunk(BlockVector3 pt) { World world = getWorld(); - - world.getChunkAt(pt.getBlockX() >> 4, pt.getBlockZ() >> 4); + int X = pt.getBlockX() >> 4; + int Z = pt.getBlockZ() >> 4; + if (Fawe.isMainThread()) { + world.getChunkAt(X, Z); + } else if (!world.isChunkLoaded(X, Z)) { + if (FaweBukkit.PAPER) { + world.getChunkAtAsync(X, Z, true); + } else { + Fawe.get().getQueueHandler().sync(() -> { + world.getChunkAt(X, Z); + }); + } + } } @Override @@ -516,7 +529,8 @@ public class BukkitWorld extends AbstractWorld { } @Override - public void sendChunk(int X, int Z, int mask) { + public void refreshChunk(int X, int Z) { + getWorld().refreshChunk(X, Z); } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index 4e782bf29..74b06d1f3 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -84,6 +84,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.WorldInitEvent; import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.metadata.MetadataValue; @@ -560,16 +561,24 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter */ public BukkitPlayer wrapPlayer(Player player) { synchronized (player) { - @NotNull List meta = player.getMetadata("WE"); - if (meta == null || meta.isEmpty()) { - BukkitPlayer wePlayer = new BukkitPlayer(this, player); + BukkitPlayer wePlayer = getCachedPlayer(player); + if (wePlayer == null) { + wePlayer = new BukkitPlayer(this, player); player.setMetadata("WE", new FixedMetadataValue(this, wePlayer)); return wePlayer; } - return (BukkitPlayer) meta.get(0).value(); + return wePlayer; } } + public BukkitPlayer getCachedPlayer(Player player) { + List meta = player.getMetadata("WE"); + if (meta == null || meta.isEmpty()) { + return null; + } + return (BukkitPlayer) meta.get(0).value(); + } + public Actor wrapCommandSender(CommandSender sender) { if (sender instanceof Player) { return wrapPlayer((Player) sender); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java b/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java index 9fe5ff141..5dd9dd93f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweCache.java @@ -228,6 +228,8 @@ public enum FaweCache implements Trimable { * Holds data for a palette used in a chunk section */ public final class Palette { + public int bitsPerEntry; + public int paletteToBlockLength; /** * Reusable buffer array, MUST check paletteToBlockLength for actual length @@ -319,6 +321,7 @@ public enum FaweCache implements Trimable { // Construct palette Palette palette = PALETTE_CACHE.get(); + palette.bitsPerEntry = bitsPerEntry; palette.paletteToBlockLength = num_palette; palette.paletteToBlock = paletteToBlock; 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 c9f5ae2c6..f10cffab2 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java @@ -1,6 +1,8 @@ package com.boydti.fawe; import com.boydti.fawe.beta.implementation.QueueHandler; +import com.boydti.fawe.beta.preloader.AsyncPreloader; +import com.boydti.fawe.beta.preloader.Preloader; import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.util.TaskManager; @@ -54,4 +56,6 @@ public interface IFawe { QueueHandler getQueueHandler(); + Preloader getPreloader(); + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFuture.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFuture.java deleted file mode 100644 index 38fecb9c9..000000000 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/ChunkFuture.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.boydti.fawe.beta; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -public class ChunkFuture implements Future { - - private final IChunk chunk; - private volatile boolean cancelled; - private volatile boolean done; - - public ChunkFuture(IChunk chunk) { - this.chunk = chunk; - } - - public IChunk getChunk() { - return chunk; - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - cancelled = true; - return !done; - } - - @Override - public boolean isCancelled() { - return cancelled; - } - - @Override - public boolean isDone() { - return done; - } - - @Override - public Void get() throws InterruptedException, ExecutionException { - synchronized (chunk) { - if (!done) { - this.wait(); - } - } - return null; - } - - @Override - public Void get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - synchronized (chunk) { - if (!done) { - this.wait(unit.toMillis(timeout)); - if (!done) { - throw new TimeoutException(); - } - } - } - return null; - } -} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java index e8150b1fa..b52e19ece 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java @@ -1,5 +1,6 @@ package com.boydti.fawe.beta; +import com.boydti.fawe.FaweCache; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; @@ -19,5 +20,20 @@ public interface IBlocks extends Trimable { BlockState getBlock(int x, int y, int z); + Map getTiles(); + + Set getEntities(); + + BiomeType getBiomeType(int x, int z); + + default int getBitMask() { + int mask = 0; + for (int layer = 0; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) { + if (hasSection(layer)); + mask |= (1 << layer); + } + return mask; + } + IBlocks reset(); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java index 8b83cdc12..e41d5686a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java @@ -28,8 +28,10 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent { CompoundTag getTag(int x, int y, int z); + @Override Map getTiles(); + @Override Set getEntities(); @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java index 698bfaee1..f3d49af3f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java @@ -38,8 +38,13 @@ public interface IChunkSet extends IBlocks, OutputExtent { BiomeType[] getBiomes(); + @Override + BiomeType getBiomeType(int x, int z); + + @Override Map getTiles(); + @Override Set getEntities(); @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java index 1c64d3f30..bd0cfc147 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java @@ -45,16 +45,6 @@ public interface IDelegateQueueExtent extends IQueueExtent { return getParent().isQueueEnabled(); } - @Override - default void clearBlockUpdates(Player... players) { - getParent().clearBlockUpdates(players); - } - - @Override - default void sendBlockUpdates(Player... players) { - getParent().sendBlockUpdates(players); - } - @Override default void enableQueue() { getParent().enableQueue(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java index 3248641ae..11f358703 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IQueueExtent.java @@ -31,22 +31,6 @@ public interface IQueueExtent extends Flushable, Trimable, Extent, IBatchProcess return true; } - /** - * Clear any block updates - * @param players - */ - default void clearBlockUpdates(Player... players) { - throw new UnsupportedOperationException("TODO NOT IMPLEMENTED"); - } - - /** - * Send all the chunks as block updates - * @param players - */ - default void sendBlockUpdates(Player... players) { - throw new UnsupportedOperationException("TODO NOT IMPLEMENTED"); - } - /** * Must ensure that it is enqueued with QueueHandler */ diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkPacket.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkPacket.java new file mode 100644 index 000000000..d0499694d --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkPacket.java @@ -0,0 +1,126 @@ +package com.boydti.fawe.beta.implementation; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IBlocks; +import com.boydti.fawe.object.FaweOutputStream; +import com.boydti.fawe.object.io.FastByteArrayOutputStream; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.world.block.BlockID; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.registry.BlockRegistry; + +import java.util.function.Function; +import java.util.function.Supplier; + +public class ChunkPacket implements Function, Supplier { + + private final boolean full; + private final boolean biomes; + private final IBlocks chunk; + private final int chunkX; + private final int chunkZ; + + public ChunkPacket(int chunkX, int chunkZ, IBlocks chunk, boolean replaceAllSections, boolean sendBiomeData) { + this.chunkX = chunkX; + this.chunkZ = chunkZ; + this.chunk = chunk; + this.full = replaceAllSections; + this.biomes = sendBiomeData; + } + + @Override + @Deprecated + public byte[] get() { + System.out.println("TODO deprecated, use buffer"); + return apply(new byte[8192]); + } + + @Override + public byte[] apply(byte[] buffer) { + try { + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(); + FaweOutputStream fos = new FaweOutputStream(baos); + + fos.writeInt(this.chunkX); + fos.writeInt(this.chunkZ); + + fos.writeBoolean(this.full); + + fos.writeVarInt(this.chunk.getBitMask()); // writeVarInt + + // TODO write NBTTagCompound of HeightMaps + fos.writeVarInt(0); // (Entities / NBT) + + // TODO write chunk data to byte[] + { + BlockRegistry registry = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry(); + try (FastByteArrayOutputStream sectionByteArray = new FastByteArrayOutputStream(buffer); FaweOutputStream sectionWriter = new FaweOutputStream(sectionByteArray)) { + for (int layer = 0; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) { + if (!this.chunk.hasSection(layer)) continue; + char[] ids = this.chunk.getArray(layer); + FaweCache.Palette palette = FaweCache.IMP.toPalette(0, ids); + + int nonEmpty = 0; // TODO optimize into same loop as toPalette + for (char id :ids) { + if (id != 0) nonEmpty++; + } + sectionWriter.writeShort(nonEmpty); // non empty + sectionWriter.writeByte(palette.bitsPerEntry); // bits per block + sectionWriter.writeVarInt(palette.paletteToBlockLength); + for (int i = 0; i < palette.paletteToBlockLength; i++) { + int ordinal = palette.paletteToBlock[i]; + switch (ordinal) { + case BlockID.CAVE_AIR: + case BlockID.VOID_AIR: + case BlockID.AIR: + case BlockID.__RESERVED__: + sectionWriter.writeByte(0); + break; + default: + BlockState state = BlockState.getFromOrdinal(ordinal); + int mcId = registry.getInternalBlockStateId(state).getAsInt(); + sectionWriter.writeVarInt(mcId); + } + } + sectionWriter.writeVarInt(palette.blockStatesLength); + for (int i = 0; i < palette.blockStatesLength; i++) { + sectionWriter.writeLong(palette.blockStates[i]); + } + } + + // TODO write biomes +// boolean writeBiomes = true; +// for (int x = 0; x < 16; x++) { +// for (int z = 0; z < 16; z++) { +// BiomeType biome = this.chunk.getBiomeType(x, z); +// if (biome == null) { +// if (writeBiomes) { +// break; +// } else { +// biome = BiomeTypes.FOREST; +// } +// } +// } +// } + if (this.full) { + for (int i = 0; i < 256; i++) { + sectionWriter.writeInt(0); + } + } + + fos.writeVarInt(sectionByteArray.getSize()); + for (byte[] arr : sectionByteArray.toByteArrays()) { + fos.write(arr); + } + } + } + // TODO entities / NBT + fos.writeVarInt(0); // (Entities / NBT) + return baos.toByteArray(); + } catch (Throwable e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkSendProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkSendProcessor.java new file mode 100644 index 000000000..53635ba07 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ChunkSendProcessor.java @@ -0,0 +1,40 @@ +package com.boydti.fawe.beta.implementation; + +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.object.extent.NullExtent; +import com.google.common.base.Suppliers; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import com.sk89q.worldedit.extent.Extent; + +import java.util.function.Supplier; +import java.util.stream.Stream; + +public class ChunkSendProcessor extends AbstractDelegateExtent implements IBatchProcessor { + private final Supplier> players; + + public ChunkSendProcessor(Supplier> players) { + super(new NullExtent()); + this.players = players; + } + + @Override + public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { + int chunkX = chunk.getX(); + int chunkZ = chunk.getZ(); + boolean replaceAll = true; + boolean sendBiome = set.getBiomes() != null; + ChunkPacket packet = new ChunkPacket(chunkX, chunkZ, set, replaceAll, sendBiome); + Supplier packetData = Suppliers.memoize(packet::get); + players.get().forEach(plr -> plr.sendFakeChunk(chunkX, chunkZ, packetData)); + return set; + } + + @Override + public Extent construct(Extent child) { + return null; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ParallelQueueExtent.java index b520c29ce..8fe062908 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ParallelQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/ParallelQueueExtent.java @@ -170,6 +170,7 @@ public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrap if (vset instanceof Region) { setBlocks((Region) vset, pattern); } + // TODO optimize parallel for (BlockVector3 blockVector3 : vset) { pattern.apply(this, blockVector3, blockVector3); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java index e3eaff4ca..8997ff71d 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/QueueHandler.java @@ -19,6 +19,7 @@ import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Queue; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; @@ -39,6 +40,7 @@ public abstract class QueueHandler implements Trimable, Runnable { private ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool(); private ThreadPoolExecutor blockingExecutor = FaweCache.IMP.newBlockingExecutor(); private ConcurrentLinkedQueue syncTasks = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue syncWhenFree = new ConcurrentLinkedQueue<>(); private Map>> chunkGetCache = new HashMap<>(); private CleanableThreadLocal queuePool = new CleanableThreadLocal<>(QueueHandler.this::create); @@ -60,53 +62,61 @@ public abstract class QueueHandler implements Trimable, Runnable { throw new IllegalStateException("Not main thread"); } if (!syncTasks.isEmpty()) { - long now = System.currentTimeMillis(); - targetTPS = 18 - Math.max(Settings.IMP.QUEUE.EXTRA_TIME_MS * 0.05, 0); - long diff = 50 + this.last - (this.last = now); - long absDiff = Math.abs(diff); - if (diff == 0) { - allocate = Math.min(50, allocate + 1); - } else if (diff < 0) { - allocate = Math.max(5, allocate + diff); - } else if (!Fawe.get().getTimer().isAbove(targetTPS)) { - allocate = Math.max(5, allocate - 1); - } - long currentAllocate = allocate - absDiff; + long currentAllocate = getAllocate(); if (!MemUtil.isMemoryFree()) { // TODO reduce mem usage + // FaweCache trim + // Preloader trim } - boolean wait = false; - do { - Runnable task = syncTasks.poll(); - if (task == null) { - if (wait) { - synchronized (syncTasks) { - try { - syncTasks.wait(1); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - task = syncTasks.poll(); - wait = false; - } else { - break; - } - } - if (task != null) { - task.run(); - wait = true; - } - } while (System.currentTimeMillis() - now < currentAllocate); + operate(syncTasks, last, currentAllocate); + } else if (!syncWhenFree.isEmpty()) { + operate(syncWhenFree, last, getAllocate()); + } else { + // trim?? } - while (!syncTasks.isEmpty()) { - final FutureTask task = syncTasks.poll(); + } + + private long getAllocate() { + long now = System.currentTimeMillis(); + targetTPS = 18 - Math.max(Settings.IMP.QUEUE.EXTRA_TIME_MS * 0.05, 0); + long diff = 50 + this.last - (this.last = now); + long absDiff = Math.abs(diff); + if (diff == 0) { + allocate = Math.min(50, allocate + 1); + } else if (diff < 0) { + allocate = Math.max(5, allocate + diff); + } else if (!Fawe.get().getTimer().isAbove(targetTPS)) { + allocate = Math.max(5, allocate - 1); + } + return allocate - absDiff; + } + + private void operate(Queue queue, long start, long currentAllocate) { + boolean wait = false; + do { + Runnable task = queue.poll(); + if (task == null) { + if (wait) { + synchronized (syncTasks) { + try { + queue.wait(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + task = queue.poll(); + wait = false; + } else { + break; + } + } if (task != null) { task.run(); + wait = true; } - } + } while (System.currentTimeMillis() - start < currentAllocate); } public > void complete(Future task) { @@ -136,50 +146,83 @@ public abstract class QueueHandler implements Trimable, Runnable { } public Future sync(Runnable run, T value) { + return sync(run, value, syncTasks); + } + + public Future sync(Runnable run) { + return sync(run, syncTasks); + } + + public Future sync(Callable call) throws Exception { + return sync(call, syncTasks); + } + + public Future sync(Supplier call) { + return sync(call, syncTasks); + } + + // Lower priorty sync task (runs only when there are no other tasks) + public Future syncWhenFree(Runnable run, T value) { + return sync(run, value, syncWhenFree); + } + + public Future syncWhenFree(Runnable run) { + return sync(run, syncWhenFree); + } + + public Future syncWhenFree(Callable call) throws Exception { + return sync(call, syncWhenFree); + } + + public Future syncWhenFree(Supplier call) { + return sync(call, syncWhenFree); + } + + private Future sync(Runnable run, T value, Queue queue) { if (Fawe.isMainThread()) { run.run(); return Futures.immediateFuture(value); } final FutureTask result = new FutureTask<>(run, value); - syncTasks.add(result); - notifySync(); + queue.add(result); + notifySync(queue); return result; } - public Future sync(Runnable run) { + private Future sync(Runnable run, Queue queue) { if (Fawe.isMainThread()) { run.run(); return Futures.immediateCancelledFuture(); } final FutureTask result = new FutureTask<>(run, null); - syncTasks.add(result); - notifySync(); + queue.add(result); + notifySync(queue); return result; } - public Future sync(Callable call) throws Exception { + private Future sync(Callable call, Queue queue) throws Exception { if (Fawe.isMainThread()) { return Futures.immediateFuture(call.call()); } final FutureTask result = new FutureTask<>(call); - syncTasks.add(result); - notifySync(); + queue.add(result); + notifySync(queue); return result; } - public Future sync(Supplier call) { + private Future sync(Supplier call, Queue queue) { if (Fawe.isMainThread()) { return Futures.immediateFuture(call.get()); } final FutureTask result = new FutureTask<>(call::get); - syncTasks.add(result); - notifySync(); + queue.add(result); + notifySync(queue); return result; } - private void notifySync() { - synchronized (syncTasks) { - syncTasks.notifyAll(); + private void notifySync(Object object) { + synchronized (object) { + object.notifyAll(); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java index fae95bc04..ed068a773 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java @@ -115,6 +115,11 @@ public class BitSetBlocks implements IChunkSet { return null; } + @Override + public BiomeType getBiomeType(int x, int z) { + return null; + } + @Override public Map getTiles() { return null; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java index 483625740..0f1b0e960 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java @@ -11,7 +11,7 @@ import com.sk89q.worldedit.world.block.BlockTypes; import java.util.Map; import java.util.Set; -public class CharBlocks implements IBlocks { +public abstract class CharBlocks implements IBlocks { public static final Section FULL = new Section() { @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java index 2c470eef6..7ce8c822f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java @@ -43,6 +43,11 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { return biomes; } + @Override + public BiomeType getBiomeType(int x, int z) { + return biomes[(z << 4) | x]; + } + @Override public Map getTiles() { return tiles == null ? Collections.emptyMap() : tiles; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/preloader/AsyncPreloader.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/preloader/AsyncPreloader.java new file mode 100644 index 000000000..99b955074 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/preloader/AsyncPreloader.java @@ -0,0 +1,110 @@ +package com.boydti.fawe.beta.preloader; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.object.collection.MutablePair; +import com.boydti.fawe.util.FaweTimer; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.World; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + + +public class AsyncPreloader implements Preloader, Runnable { + private final ConcurrentHashMap>> update; + + public AsyncPreloader() { + this.update = new ConcurrentHashMap<>(); + Fawe.get().getQueueHandler().async(this); + } + + @Override + public void cancel(Player player) { + cancelAndGet(player); + } + + private MutablePair> cancelAndGet(Player player) { + MutablePair> existing = update.get(player.getUniqueId()); + if (existing != null) { + existing.setValue(null); + } + return existing; + } + + @Override + public void update(Player player) { + LocalSession session = WorldEdit.getInstance().getSessionManager().getIfPresent(player); + if (session == null) return; + World world = player.getWorld(); + MutablePair> existing = cancelAndGet(player); + try { + Region region = session.getSelection(world); + if (!(region instanceof CuboidRegion) || region.getArea() > 50466816) { + // TOO LARGE or NOT CUBOID + return; + } + if (existing == null) { + MutablePair> previous = update.putIfAbsent(player.getUniqueId(), existing = new MutablePair<>()); + if (previous != null) { + existing = previous; + } + synchronized (existing) { // Ensure key & value are mutated together + existing.setKey(world); + existing.setValue(region.getChunks()); + } + synchronized (update) { + update.notify(); + } + } + } catch (IncompleteRegionException ignore){} + } + + @Override + public void run() { + FaweTimer timer = Fawe.get().getTimer(); + try { + while (true) { + if (!update.isEmpty()) { + if (timer.getTPS() > 19) { + Iterator>>> plrIter = update.entrySet().iterator(); + Map.Entry>> entry = plrIter.next(); + MutablePair> pair = entry.getValue(); + World world = pair.getKey(); + Set chunks = pair.getValue(); + if (chunks != null) { + Iterator chunksIter = chunks.iterator(); + while (chunksIter.hasNext() && pair.getValue() == chunks) { // Ensure the queued load is still valid + BlockVector2 chunk = chunksIter.next(); + queueLoad(world, chunk); + } + } + plrIter.remove(); + } else { + Thread.sleep(1000); + } + } else { + synchronized (update) { + update.wait(); + } + } + } + } catch (InterruptedException e) { + e.printStackTrace(); + return; + } + } + + public void queueLoad(World world, BlockVector2 chunk) { + world.checkLoadedChunk(BlockVector3.at(chunk.getX() << 4, 0, chunk.getZ() << 4)); + } +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/preloader/Preloader.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/preloader/Preloader.java new file mode 100644 index 000000000..e24a974f9 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/preloader/Preloader.java @@ -0,0 +1,9 @@ +package com.boydti.fawe.beta.preloader; + +import com.sk89q.worldedit.entity.Player; + +public interface Preloader { + void cancel(Player player); + + void update(Player player); +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java b/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java index 521563bbb..95eae94a8 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/command/CFICommands.java @@ -432,7 +432,7 @@ public class CFICommands { Clipboard clipboard = holder.getClipboard(); boolean[] ids = new boolean[BlockTypes.size()]; for (BlockVector3 pt : clipboard.getRegion()) { - ids[clipboard.getBlock(pt).getInternalBlockTypeId()] = true; + ids[clipboard.getBlock(pt).getBlockType().getInternalId()] = true; } blocks = new HashSet<>(); for (int combined = 0; combined < ids.length; combined++) { @@ -606,7 +606,7 @@ public class CFICommands { @CommandPermissions("worldedit.anvil.cfi") public void waterId(Player fp, BlockStateHolder block) throws WorldEditException { CFISettings settings = assertSettings(fp); - settings.getGenerator().setWaterId(block.getBlockType().getInternalId()); + settings.getGenerator().setWater(block.toImmutableState()); fp.print("Set water id!"); settings.resetComponent(); @@ -621,7 +621,7 @@ public class CFICommands { @CommandPermissions("worldedit.anvil.cfi") public void baseId(Player fp, BlockStateHolder block) throws WorldEditException { CFISettings settings = assertSettings(fp); - settings.getGenerator().setBedrockId(block.getBlockType().getInternalId()); + settings.getGenerator().setBedrock(block.toImmutableState()); fp.print(TextComponent.of("Set base id!")); settings.resetComponent(); component(fp); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/Lazy.java b/worldedit-core/src/main/java/com/boydti/fawe/object/Lazy.java new file mode 100644 index 000000000..0110f624b --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/Lazy.java @@ -0,0 +1,8 @@ +package com.boydti.fawe.object; + +import java.util.function.Supplier; + +public interface Lazy extends Supplier { + Supplier init(); + public default T get() { return init().get(); } +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/VisualExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/VisualExtent.java index 92bae872e..360668fbe 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/VisualExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/VisualExtent.java @@ -47,7 +47,7 @@ public class VisualExtent extends AbstractDelegateExtent { @Override public Operation commit() { IQueueExtent queue = (IQueueExtent) getExtent(); - queue.sendBlockUpdates(this.player); + System.out.println("Send block updates"); return null; } @@ -59,7 +59,7 @@ public class VisualExtent extends AbstractDelegateExtent { public void clear() { IQueueExtent queue = (IQueueExtent) getExtent(); - queue.clearBlockUpdates(player); + System.out.println("Clear"); queue.cancel(); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/CFIDrawer.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/CFIDrawer.java new file mode 100644 index 000000000..57715ea79 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/CFIDrawer.java @@ -0,0 +1,142 @@ +package com.boydti.fawe.object.brush.visualization.cfi; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.TextureUtil; +import com.sk89q.worldedit.world.block.BlockID; +import com.sk89q.worldedit.world.block.BlockTypes; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.TimeUnit; + +public final class CFIDrawer { + private final HeightMapMCAGenerator gen; + private final TextureUtil tu; + private final ForkJoinPool pool; + + public CFIDrawer(HeightMapMCAGenerator generator, TextureUtil textureUtil) { + this.gen = generator; + this.tu = textureUtil; + this.pool = new ForkJoinPool(); + } + + public CFIDrawer(HeightMapMCAGenerator generator) { + this(generator, Fawe.get().getCachedTextureUtil(false, 0, 100)); + } + + public BufferedImage draw() { + BufferedImage img = new BufferedImage(gen.getWidth(), gen.getLength(), BufferedImage.TYPE_INT_RGB); + final char[] overlay = gen.overlay == null ? gen.floor.get() : gen.overlay.get(); + final char[] floor = gen.floor.get(); + final char[] main = gen.main.get(); + final byte[] heights = gen.heights.get(); + final byte[] biomes = gen.biomes.get(); + final int waterHeight = gen.primitives.waterHeight; + final int width = gen.getWidth(); + final int length = gen.getLength(); + + int[] raw = ((DataBufferInt) img.getRaster().getDataBuffer()).getData(); + + int parallelism = pool.getParallelism(); + int size = (heights.length + parallelism - 1) / parallelism; + for (int i = 0; i < parallelism; i++) { + int start = i * size; + int end = Math.min(heights.length, start + size); + pool.submit((Runnable) () -> { + for (int index = start; index < end; index ++) { + int height = (heights[index] & 0xFF); + char ordinal; + if ((ordinal = overlay[index]) == 0) { + height--; + ordinal = floor[index]; + if (ordinal == 0) { + height--; + ordinal = main[index]; + } + } + // draw ordinal + int color; + switch (ordinal >> 4) { + case 2: + color = getAverageBiomeColor(biomes, width, index); + break; + case 78: + color = (0xDD << 16) + (0xDD << 8) + (0xDD << 0); + break; + default: + color = tu.getColor(BlockTypes.getFromStateOrdinal(ordinal)); + break; + } + int slope = getSlope(heights, width, index, height); + if (slope != 0) { + slope = (slope << 3) + (slope << 2); + int r = MathMan.clamp(((color >> 16) & 0xFF) + slope, 0, 255); + int g = MathMan.clamp(((color >> 8) & 0xFF) + slope, 0, 255); + int b = MathMan.clamp(((color >> 0) & 0xFF) + slope, 0, 255); + color = (r << 16) + (g << 8) + (b << 0); + } + if (height + 1 < waterHeight) { + char waterId = gen.primitives.waterOrdinal; + int waterColor = 0; + switch (waterId) { + case BlockID.WATER: + color = tu.averageColor((0x11 << 16) + (0x66 << 8) + (0xCC), color); + break; + case BlockID.LAVA: + color = (0xCC << 16) + (0x33 << 8) + (0); + break; + default: + color = tu.getColor(BlockTypes.getFromStateOrdinal(waterId)); + break; + } + } + raw[index] = color; + } + }); + } + pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + pool.shutdownNow(); + return img; + } + + private final int getAverageBiomeColor(byte[] biomes, int width, int index) { + int c0 = tu.getBiome(biomes[index] & 0xFF).grassCombined; + int c2 = getBiome(biomes, index + 1 + width, index); + int c1 = getBiome(biomes, index - 1 - width, index); +// int c3 = getBiome(biomes, index + width, index); +// int c4 = getBiome(biomes, index - width, index); + int r = ((c0 >> 16) & 0xFF) + ((c1 >> 16) & 0xFF) + ((c2 >> 16) & 0xFF);// + ((c3 >> 16) & 0xFF) + ((c4 >> 16) & 0xFF); + int g = ((c0 >> 8) & 0xFF) + ((c1 >> 8) & 0xFF) + ((c2 >> 8) & 0xFF);// + ((c3 >> 8) & 0xFF) + ((c4 >> 8) & 0xFF); + int b = ((c0) & 0xFF) + ((c1) & 0xFF) + ((c2) & 0xFF);// + ((c3) & 0xFF) + ((c4) & 0xFF); + r = r * 85 >> 8; + g = g * 85 >> 8; + b = b * 85 >> 8; + return (r << 16) + (g << 8) + (b); + } + + private final int getBiome(byte[] biomes, int newIndex, int index) { + if (newIndex < 0 || newIndex >= biomes.length) newIndex = index; + int biome = biomes[newIndex] & 0xFF; + return tu.getBiome(biome).grassCombined; + } + + private int getSlope(byte[] heights, int width, int index, int height) { + return ( + + getHeight(heights, index + 1, height) +// + getHeight(heights, index + width, height) + + getHeight(heights, index + width + 1, height) + - getHeight(heights, index - 1, height) +// - getHeight(heights, index - width, height) + - getHeight(heights, index - width - 1, height) + ); + } + + private int getHeight(byte[] heights, int index, int height) { + if (index < 0 || index >= heights.length) return height; + return heights[index] & 0xFF; + } +} + + diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java index 982321c81..b5e8aa4ab 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java @@ -3,6 +3,8 @@ package com.boydti.fawe.object.brush.visualization.cfi; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.implementation.ChunkPacket; import com.boydti.fawe.beta.implementation.FallbackChunkGet; import com.boydti.fawe.object.FaweInputStream; import com.boydti.fawe.object.FaweOutputStream; @@ -15,7 +17,6 @@ import com.boydti.fawe.object.collection.DifferentialBlockBuffer; import com.boydti.fawe.object.collection.LocalBlockVector2DSet; import com.boydti.fawe.object.collection.SummedAreaTable; import com.boydti.fawe.object.exception.FaweChunkLoadException; -import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.schematic.Schematic; import com.boydti.fawe.util.CachedTextureUtil; import com.boydti.fawe.util.RandomTextureUtil; @@ -23,6 +24,7 @@ import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.TextureUtil; import com.boydti.fawe.util.image.Drawable; import com.boydti.fawe.util.image.ImageViewer; +import com.google.common.base.Suppliers; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; @@ -64,6 +66,7 @@ import java.util.Arrays; import java.util.List; import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Supplier; import javax.annotation.Nullable; // TODO FIXME @@ -73,9 +76,9 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr private final DifferentialBlockBuffer blocks; protected final DifferentialArray heights; protected final DifferentialArray biomes; - protected final DifferentialArray floor; - protected final DifferentialArray main; - protected DifferentialArray overlay; + protected final DifferentialArray floor; + protected final DifferentialArray main; + protected DifferentialArray overlay; protected final CFIPrimitives primitives = new CFIPrimitives(); private CFIPrimitives oldPrimitives = new CFIPrimitives(); @@ -86,8 +89,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int worldThickness; boolean randomVariation = true; int biomePriority; - int waterId = BlockID.WATER; - int bedrockId = BlockID.BEDROCK; + char waterOrdinal = BlockID.WATER; + char bedrockOrdinal = BlockID.BEDROCK; boolean modifiedMain; @Override @@ -225,13 +228,13 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr blocks = new DifferentialBlockBuffer(width, length); heights = new DifferentialArray<>(new byte[getArea()]); biomes = new DifferentialArray<>(new byte[getArea()]); - floor = new DifferentialArray<>(new int[getArea()]); - main = new DifferentialArray<>(new int[getArea()]); + floor = new DifferentialArray<>(new char[getArea()]); + main = new DifferentialArray<>(new char[getArea()]); - int stone = BlockID.STONE; - int grass = BlockTypes.GRASS_BLOCK.getDefaultState().with(PropertyKey.SNOWY, false).getInternalId(); - Arrays.fill(main.getIntArray(), stone); - Arrays.fill(floor.getIntArray(), grass); + char stone = BlockID.STONE; + char grass = BlockTypes.GRASS_BLOCK.getDefaultState().with(PropertyKey.SNOWY, false).getOrdinalChar(); + Arrays.fill(overlay.getCharArray(), stone); + Arrays.fill(overlay.getCharArray(), grass); } public Metadatable getMetaData() { @@ -259,10 +262,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr return player; } - private int[][][] getChunkArray(int x, int z) { - int[][][][][] blocksData = blocks.get(); + private char[][][] getChunkArray(int x, int z) { + char[][][][][] blocksData = blocks.get(); if (blocksData == null) return null; - int[][][][] arr = blocksData[z]; + char[][][][] arr = blocksData[z]; return arr != null ? arr[x] : null; } @@ -320,8 +323,20 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } @Override - public void sendChunk(int X, int Z, int mask) { - throw new UnsupportedOperationException("TODO NOT IMPLEMENTED"); // add method to adapter to send custom chunk + public void refreshChunk(int chunkX, int chunkZ) { + IChunkSet chunk = getChunk(chunkX, chunkZ); + ChunkPacket packet = new ChunkPacket(chunkX, chunkZ, chunk, true, true); + player.sendFakeChunk(chunkX, chunkZ, packet::get); + } + + public IChunkSet getChunk(int chunkX, int chunkZ) { + // TODO don't generate new Writeable MCA chunk + System.out.println("TODO don't generate new Writeable MCA chunk"); + WritableMCAChunk tmp = new WritableMCAChunk(); + int bx = chunkX << 4; + int bz = chunkZ << 4; + write(tmp, bx, bx + 15, bz, bz + 15); + return tmp; } public TextureUtil getRawTextureUtil() { @@ -349,8 +364,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } } - public void setBedrockId(int bedrockId) { - this.primitives.bedrockId = bedrockId; + public void setBedrock(BlockState bedrock) { + this.primitives.bedrockOrdinal = bedrock.getOrdinalChar(); } public void setFloorThickness(int floorThickness) { @@ -365,8 +380,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr this.primitives.waterHeight = waterHeight; } - public void setWaterId(int waterId) { - this.primitives.waterId = waterId; + public void setWater(BlockState water) { + this.primitives.waterOrdinal = water.getOrdinalChar(); } public void setTextureRandomVariation(boolean randomVariation) { @@ -390,10 +405,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } public void smooth(BlockVector2 min, BlockVector2 max, int radius, int iterations) { - int snowLayer = BlockTypes.SNOW.getInternalId(); - int snowBlock = BlockTypes.SNOW_BLOCK.getInternalId(); + int snowLayer = BlockTypes.SNOW.getDefaultState().getOrdinalChar(); + int snowBlock = BlockTypes.SNOW_BLOCK.getDefaultState().getOrdinalChar(); - int[] floor = this.floor.get(); + char[] floor = this.floor.get(); byte[] heights = this.heights.get(); int width = getWidth(); @@ -421,7 +436,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int index = zIndex + minX; for (int x = minX; x <= maxX; x++, index++, localIndex++) { int combined = floor[index]; - if (BlockTypes.getFromStateId(combined) == BlockTypes.SNOW) { + if (BlockTypes.getFromStateOrdinal(combined) == BlockTypes.SNOW) { layers[localIndex] = (char) (((heights[index] & 0xFF) << 3) + (floor[index] >> BlockTypes.BIT_OFFSET) - 7); } else { layers[localIndex] = (char) ((heights[index] & 0xFF) << 3); @@ -453,16 +468,15 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr private final void setLayerHeight(int index, int blockHeight, int layerHeight) { int floorState = floor.get()[index]; - BlockType type = BlockTypes.getFromStateId(floorState); - switch (type.getInternalId()) { + switch (floorState) { case BlockID.SNOW: case BlockID.SNOW_BLOCK: if (layerHeight != 0) { this.heights.setByte(index, (byte) (blockHeight + 1)); - this.floor.setInt(index, BlockTypes.SNOW.getInternalId() + layerHeight); + this.floor.setInt(index, BlockTypes.SNOW.getDefaultState().getOrdinalChar() + layerHeight); } else { this.heights.setByte(index, (byte) blockHeight); - this.floor.setInt(index, BlockTypes.SNOW_BLOCK.getInternalId()); + this.floor.setInt(index, BlockTypes.SNOW_BLOCK.getDefaultState().getOrdinalChar()); } break; default: @@ -479,16 +493,15 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr private final void setLayerHeightRaw(int index, int blockHeight, int layerHeight) { int floorState = floor.get()[index]; - BlockType type = BlockTypes.getFromStateId(floorState); - switch (type.getInternalId()) { + switch (floorState) { case BlockID.SNOW: case BlockID.SNOW_BLOCK: if (layerHeight != 0) { this.heights.getByteArray()[index] = (byte) (blockHeight + 1); - this.floor.getIntArray()[index] = BlockTypes.SNOW.getInternalId() + layerHeight; + this.overlay.getCharArray()[index] = (char) (BlockTypes.SNOW.getDefaultState().getOrdinalChar() + layerHeight); } else { this.heights.getByteArray()[index] = (byte) blockHeight; - this.floor.getIntArray()[index] = BlockTypes.SNOW_BLOCK.getInternalId(); + this.overlay.getCharArray()[index] = BlockTypes.SNOW_BLOCK.getDefaultState().getOrdinalChar(); } break; default: @@ -498,7 +511,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } private void smooth(BufferedImage img, Mask mask, boolean white, int radius, int iterations) { - int[] floor = this.floor.get(); + char[] floor = this.floor.get(); byte[] heights = this.heights.get(); long[] copy = new long[heights.length]; @@ -511,7 +524,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int j = 0; j < iterations; j++) { for (int i = 0; i < heights.length; i++) { int combined = floor[i]; - if (BlockTypes.getFromStateId(combined) == BlockTypes.SNOW) { + if (BlockTypes.getFromStateOrdinal(combined) == BlockTypes.SNOW) { layers[i] = (char) (((heights[i] & 0xFF) << 3) + (floor[i] >> BlockTypes.BIT_OFFSET) - 7); } else { layers[i] = (char) ((heights[i] & 0xFF) << 3); @@ -704,7 +717,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr return setBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ(), block); } - private boolean setBlock(int x, int y, int z, int combined) { + private boolean setBlock(int x, int y, int z, char combined) { int index = z * getWidth() + x; if (index < 0 || index >= getArea()) return false; int height = heights.getByte(index) & 0xFF; @@ -713,8 +726,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr floor.setInt(index, combined); return true; case 1: - int mainId = main.getInt(index); - int floorId = floor.getInt(index); + char mainId = overlay.getChar(index); + char floorId = overlay.getChar(index); floor.setInt(index, combined); byte currentHeight = heights.getByte(index); @@ -870,7 +883,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int cz = scz; cz <= ecz; cz++) { for (int cx = scx; cx <= ecx; cx++) { - world.sendChunk(cx + OX, cz + OZ, 0); + world.refreshChunk(cx + OX, cz + OZ); } } } @@ -890,20 +903,19 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr return BiomeTypes.get(biomes.getByte(index)); } -// @Override - public int getCombinedId4Data(int x, int y, int z) throws FaweChunkLoadException { + public int getOrdinal(int x, int y, int z) throws FaweChunkLoadException { int index = z * getWidth() + x; if (y < 0) return 0; if (index < 0 || index >= getArea() || x < 0 || x >= getWidth()) return 0; int height = heights.getByte(index) & 0xFF; if (y > height) { if (y == height + 1) { - return overlay != null ? overlay.getInt(index) : 0; + return overlay != null ? overlay.getChar(index) : 0; } if (blocks != null) { short chunkX = (short) (x >> 4); short chunkZ = (short) (z >> 4); - int[][][] map = getChunkArray(chunkX, chunkZ); + char[][][] map = getChunkArray(chunkX, chunkZ); if (map != null) { int combined = get(map, x, y, z); if (combined != 0) { @@ -912,16 +924,16 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } } if (y <= primitives.waterHeight) { - return primitives.waterId << 4; + return primitives.waterOrdinal; } return 0; } else if (y == height) { - return floor.getInt(index); + return overlay.getChar(index); } else { if (blocks != null) { short chunkX = (short) (x >> 4); short chunkZ = (short) (z >> 4); - int[][][] map = getChunkArray(chunkX, chunkZ); + char[][][] map = getChunkArray(chunkX, chunkZ); if (map != null) { int combined = get(map, x, y, z); if (combined != 0) { @@ -929,13 +941,13 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } } } - return main.getInt(index); + return overlay.getChar(index); } } @Override public boolean setBlock(int x, int y, int z, BlockStateHolder block) throws WorldEditException { - return this.setBlock(x, y, z, block.getInternalId()); + return this.setBlock(x, y, z, block.getOrdinalChar()); } @Override @@ -950,7 +962,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public BlockState getFloor(int x, int z) { int index = z * getWidth() + x; - return BlockState.getFromInternalId(floor.getInt(index)); + return BlockState.getFromOrdinal(overlay.getChar(index)); } public int getHeight(int x, int z) { @@ -964,19 +976,19 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setFloor(int x, int z, BlockStateHolder block) { int index = z * getWidth() + x; - floor.setInt(index, block.getInternalId()); + floor.setInt(index, block.getOrdinalChar()); } @Override public BlockState getBlock(int x, int y, int z) { - return BlockState.getFromInternalId(getCombinedId4Data(x, y, z)); + return BlockState.getFromOrdinal(getOrdinal(x, y, z)); } @Override public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) { int index = z * getWidth() + x; if (index < 0 || index >= getArea()) index = Math.floorMod(index, getArea()); - return ((heights.getByte(index) & 0xFF) << 3) + (floor.getInt(index) & 0xFF) + 1; + return ((heights.getByte(index) & 0xFF) << 3) + (overlay.getChar(index) & 0xFF) + 1; } @Override @@ -1017,9 +1029,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr @Override public BufferedImage draw() { - // TODO NOT IMPLEMENTED -// return new HeightMapMCADrawer(this).draw(); - return null; + return new CFIDrawer(this).draw(); } public void setBiomePriority(int value) { @@ -1044,12 +1054,12 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int maxIndex = getArea() - 1; biomes.record(() -> floor.record(() -> main.record(() -> { - int[] mainArr = main.get(); - int[] floorArr = floor.get(); + char[] mainArr = main.get(); + char[] floorArr = floor.get(); byte[] biomesArr = biomes.get(); int index = 0; - int[] buffer = new int[2]; + char[] buffer = new char[2]; for (int z = 0; z < img.getHeight(); z++) { mutable.mutZ(z); for (int x = 0; x < img.getWidth(); x++, index++) { @@ -1065,7 +1075,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } int color = img.getRGB(x, z); if (textureUtil.getIsBlockCloserThanBiome(buffer, color, primitives.biomePriority)) { - int combined = buffer[0]; + char combined = buffer[0]; mainArr[index] = combined; floorArr[index] = combined; } @@ -1079,23 +1089,20 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); TextureUtil textureUtil = getTextureUtil(); - int widthIndex = img.getWidth() - 1; int heightIndex = img.getHeight() - 1; - int maxIndex = getArea() - 1; biomes.record(() -> floor.record(() -> main.record(() -> { - int[] mainArr = main.get(); - int[] floorArr = floor.get(); + char[] mainArr = main.get(); + char[] floorArr = floor.get(); byte[] biomesArr = biomes.get(); - int[] buffer = new int[2]; + char[] buffer = new char[2]; int index = 0; for (int y = 0; y < img.getHeight(); y++) { - boolean yBiome = y > 0 && y < heightIndex; for (int x = 0; x < img.getWidth(); x++, index++) { int color = img.getRGB(x, y); if (textureUtil.getIsBlockCloserThanBiome(buffer, color, primitives.biomePriority)) { - int combined = buffer[0]; + char combined = buffer[0]; mainArr[index] = combined; floorArr[index] = combined; } @@ -1135,8 +1142,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr TextureUtil textureUtil = getTextureUtil(); floor.record(() -> main.record(() -> { - int[] mainArr = main.get(); - int[] floorArr = floor.get(); + char[] mainArr = main.get(); + char[] floorArr = floor.get(); int index = 0; for (int z = 0; z < getLength(); z++) { @@ -1147,7 +1154,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int color = img.getRGB(x, z); BlockType block = textureUtil.getNearestBlock(color); if (block != null) { - int combined = block.getInternalId(); + char combined = block.getDefaultState().getOrdinalChar(); mainArr[index] = combined; floorArr[index] = combined; } @@ -1165,8 +1172,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr floor.record(() -> main.record(() -> { - int[] mainArr = main.get(); - int[] floorArr = floor.get(); + char[] mainArr = main.get(); + char[] floorArr = floor.get(); int index = 0; for (int z = 0; z < getLength(); z++) { @@ -1178,7 +1185,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int color = img.getRGB(x, z); BlockType block = textureUtil.getNearestBlock(color); if (block != null) { - int combined = block.getInternalId(); + char combined = block.getDefaultState().getOrdinalChar(); mainArr[index] = combined; floorArr[index] = combined; } else { @@ -1197,8 +1204,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr TextureUtil textureUtil = getTextureUtil(); floor.record(() -> main.record(() -> { - int[] mainArr = main.get(); - int[] floorArr = floor.get(); + char[] mainArr = main.get(); + char[] floorArr = floor.get(); int index = 0; for (int z = 0; z < img.getHeight(); z++) { @@ -1206,7 +1213,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int color = img.getRGB(x, z); BlockType block = textureUtil.getNearestBlock(color); if (block != null) { - int combined = block.getInternalId(); + char combined = block.getDefaultState().getOrdinalChar(); mainArr[index] = combined; floorArr[index] = combined; } else { @@ -1224,8 +1231,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr TextureUtil textureUtil = getTextureUtil(); floor.record(() -> main.record(() -> { - int[] mainArr = main.get(); - int[] floorArr = floor.get(); + char[] mainArr = main.get(); + char[] floorArr = floor.get(); int index = 0; for (int y = 0; y < img.getHeight(); y++) { @@ -1233,8 +1240,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int color = img.getRGB(x, y); BlockType[] layer = textureUtil.getNearestLayer(color); if (layer != null) { - floorArr[index] = layer[0].getInternalId(); - mainArr[index] = layer[1].getInternalId(); + floorArr[index] = layer[0].getDefaultState().getOrdinalChar(); + mainArr[index] = layer[1].getDefaultState().getOrdinalChar(); } index++; } @@ -1260,18 +1267,18 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setOverlay(BufferedImage img, Pattern pattern, boolean white) { if (pattern instanceof BlockStateHolder) { - setOverlay(img, ((BlockStateHolder) pattern).getInternalId(), white); + setOverlay(img, ((BlockStateHolder) pattern).getOrdinalChar(), white); } else if (pattern instanceof BlockType) { - setOverlay(img, ((BlockType) pattern).getInternalId(), white); + setOverlay(img, ((BlockType) pattern).getDefaultState().getOrdinalChar(), white); } else { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); if (overlay == null) { - overlay = new DifferentialArray<>(new int[getArea()]); + overlay = new DifferentialArray<>(new char[getArea()]); } overlay.record(() -> { - int[] overlayArr = overlay.get(); + char[] overlayArr = overlay.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1281,7 +1288,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr .nextInt(256) <= height) { mutable.mutX(x); mutable.mutY(height); - overlayArr[index] = pattern.apply(mutable).getInternalId(); + overlayArr[index] = pattern.apply(mutable).getOrdinalChar(); } } } @@ -1292,14 +1299,14 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setMain(BufferedImage img, Pattern pattern, boolean white) { if (pattern instanceof BlockStateHolder) { - setMain(img, ((BlockStateHolder) pattern).getInternalId(), white); + setMain(img, ((BlockStateHolder) pattern).getOrdinalChar(), white); } else { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); primitives.modifiedMain = true; main.record(() -> { - int[] mainArr = main.get(); + char[] mainArr = main.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1309,7 +1316,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr .nextInt(256) <= height) { mutable.mutX(x); mutable.mutY(height); - mainArr[index] = pattern.apply(mutable).getInternalId(); + mainArr[index] = pattern.apply(mutable).getOrdinalChar(); } } } @@ -1319,13 +1326,13 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setFloor(BufferedImage img, Pattern pattern, boolean white) { if (pattern instanceof BlockStateHolder) { - setFloor(img, ((BlockStateHolder) pattern).getInternalId(), white); + setFloor(img, ((BlockStateHolder) pattern).getOrdinalChar(), white); } else { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); floor.record(() -> { - int[] floorArr = floor.get(); + char[] floorArr = floor.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1335,7 +1342,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr .nextInt(256) <= height) { mutable.mutX(x); mutable.mutY(height); - floorArr[index] = pattern.apply(mutable).getInternalId(); + floorArr[index] = pattern.apply(mutable).getOrdinalChar(); } } } @@ -1345,15 +1352,15 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setColumn(BufferedImage img, Pattern pattern, boolean white) { if (pattern instanceof BlockStateHolder) { - setColumn(img, ((BlockStateHolder) pattern).getInternalId(), white); + setColumn(img, ((BlockStateHolder) pattern).getOrdinalChar(), white); } else { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); primitives.modifiedMain = true; main.record(() -> floor.record(() -> { - int[] floorArr = floor.get(); - int[] mainArr = main.get(); + char[] floorArr = floor.get(); + char[] mainArr = main.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1363,7 +1370,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr .nextInt(256) <= height) { mutable.mutX(x); mutable.mutY(height); - int combined = pattern.apply(mutable).getInternalId(); + char combined = pattern.apply(mutable).getOrdinalChar(); mainArr[index] = combined; floorArr[index] = combined; } @@ -1375,10 +1382,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setOverlay(Mask mask, Pattern pattern) { if (pattern instanceof BlockStateHolder) { - setOverlay(mask, ((BlockStateHolder) pattern).getInternalId()); + setOverlay(mask, ((BlockStateHolder) pattern).getOrdinalChar()); } else { int index = 0; - if (overlay == null) overlay = new DifferentialArray<>(new int[getArea()]); + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { @@ -1386,7 +1393,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - overlay.setInt(index, pattern.apply(mutable).getInternalId()); + overlay.setInt(index, pattern.apply(mutable).getOrdinalChar()); } } } @@ -1395,7 +1402,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setFloor(Mask mask, Pattern pattern) { if (pattern instanceof BlockStateHolder) { - setFloor(mask, ((BlockStateHolder) pattern).getInternalId()); + setFloor(mask, ((BlockStateHolder) pattern).getOrdinalChar()); } else { int index = 0; for (int z = 0; z < getLength(); z++) { @@ -1405,7 +1412,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - floor.setInt(index, pattern.apply(mutable).getInternalId()); + floor.setInt(index, pattern.apply(mutable).getOrdinalChar()); } } } @@ -1414,7 +1421,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setMain(Mask mask, Pattern pattern) { if (pattern instanceof BlockStateHolder) { - setMain(mask, ((BlockStateHolder) pattern).getInternalId()); + setMain(mask, ((BlockStateHolder) pattern).getOrdinalChar()); } else { primitives.modifiedMain = true; int index = 0; @@ -1425,7 +1432,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - main.setInt(index, pattern.apply(mutable).getInternalId()); + main.setInt(index, pattern.apply(mutable).getOrdinalChar()); } } } @@ -1434,7 +1441,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setColumn(Mask mask, Pattern pattern) { if (pattern instanceof BlockStateHolder) { - setColumn(mask, ((BlockStateHolder) pattern).getInternalId()); + setColumn(mask, ((BlockStateHolder) pattern).getOrdinalChar()); } else { primitives.modifiedMain = true; int index = 0; @@ -1445,7 +1452,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr mutable.mutX(x); mutable.mutY(y); if (mask.test(mutable)) { - int combined = pattern.apply(mutable).getInternalId(); + int combined = pattern.apply(mutable).getOrdinalChar(); floor.setInt(index, combined); main.setInt(index, combined); } @@ -1460,10 +1467,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setFloor(Pattern value) { if (value instanceof BlockStateHolder) { - setFloor(((BlockStateHolder) value).getInternalId()); + setFloor(((BlockStateHolder) value).getOrdinalChar()); } else { floor.record(() -> { - int[] floorArr = floor.get(); + char[] floorArr = floor.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1471,7 +1478,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); - floorArr[index] = value.apply(mutable).getInternalId(); + floorArr[index] = value.apply(mutable).getOrdinalChar(); } } }); @@ -1480,11 +1487,11 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setColumn(Pattern value) { if (value instanceof BlockStateHolder) { - setColumn(((BlockStateHolder) value).getInternalId()); + setColumn(((BlockStateHolder) value).getOrdinalChar()); } else { main.record(() -> floor.record(() -> { - int[] floorArr = floor.get(); - int[] mainArr = main.get(); + char[] floorArr = floor.get(); + char[] mainArr = main.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1492,7 +1499,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); - int combined = value.apply(mutable).getInternalId(); + char combined = value.apply(mutable).getOrdinalChar(); mainArr[index] = combined; floorArr[index] = combined; } @@ -1503,10 +1510,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public void setMain(Pattern value) { if (value instanceof BlockStateHolder) { - setMain(((BlockStateHolder) value).getInternalId()); + setMain(((BlockStateHolder) value).getOrdinalChar()); } else { main.record(() -> { - int[] mainArr = main.get(); + char[] mainArr = main.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1514,7 +1521,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); - mainArr[index] = value.apply(mutable).getInternalId(); + mainArr[index] = value.apply(mutable).getOrdinalChar(); } } }); @@ -1522,12 +1529,12 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } public void setOverlay(Pattern value) { - if (overlay == null) overlay = new DifferentialArray<>(new int[getArea()]); + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); if (value instanceof BlockStateHolder) { - setOverlay(((BlockStateHolder) value).getInternalId()); + setOverlay(((BlockStateHolder) value).getOrdinalChar()); } else { overlay.record(() -> { - int[] overlayArr = overlay.get(); + char[] overlayArr = overlay.get(); int index = 0; for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); @@ -1535,7 +1542,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr int y = heights.getByte(index) & 0xFF; mutable.mutX(x); mutable.mutY(y); - overlayArr[index] = value.apply(mutable).getInternalId(); + overlayArr[index] = value.apply(mutable).getOrdinalChar(); } } }); @@ -1567,16 +1574,16 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr public WritableMCAChunk write(WritableMCAChunk chunk, int csx, int cex, int csz, int cez) { byte[] heights = this.heights.get(); byte[] biomes = this.biomes.get(); - int[] main = this.main.get(); - int[] floor = this.floor.get(); - int[] overlay = this.overlay != null ? this.overlay.get() : null; + char[] main = this.main.get(); + char[] floor = this.floor.get(); + char[] overlay = this.overlay != null ? this.overlay.get() : null; try { int[] indexes = FaweCache.IMP.INDEX_STORE.get(); int index; int maxY = 0; int minY = Integer.MAX_VALUE; - int[] heightMap = chunk.biomes; + byte[] heightMap = chunk.biomes; int globalIndex; for (int z = csz; z <= cez; z++) { globalIndex = z * getWidth() + csx; @@ -1584,7 +1591,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr for (int x = csx; x <= cex; x++, index++, globalIndex++) { indexes[index] = globalIndex; int height = heights[globalIndex] & 0xFF; - heightMap[index] = height; + heightMap[index] = (byte) height; maxY = Math.max(maxY, height); minY = Math.min(minY, height); } @@ -1599,7 +1606,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } if (primitives.waterHeight != 0) { int maxIndex = primitives.waterHeight << 8; - Arrays.fill(chunk.blocks, 0, maxIndex, primitives.waterId); + Arrays.fill(chunk.blocks, 0, maxIndex, primitives.waterOrdinal); } if (primitives.modifiedMain) { // If the main block is modified, we can't short circuit this @@ -1607,7 +1614,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr index = (z & 15) << 4; for (int x = csx; x <= cex; x++, index++) { globalIndex = indexes[index]; - int mainCombined = main[globalIndex]; + char mainCombined = main[globalIndex]; for (int y = 0; y < minY; y++) { chunk.blocks[index + (y << 8)] = mainCombined; } @@ -1615,7 +1622,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } } else { int maxIndex = minY << 8; - Arrays.fill(chunk.blocks, 0, maxIndex, BlockID.STONE); + Arrays.fill(chunk.blocks, 0, maxIndex, (char) BlockID.STONE); } final boolean hasFloorThickness = primitives.floorThickness != 0 || primitives.worldThickness != 0; @@ -1630,13 +1637,13 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr index = (z & 15) << 4; for (int x = csx; x <= cex; x++, index++) { globalIndex = indexes[index]; - int height = heightMap[index]; + int height = heightMap[index] & 0xFF; int maxMainY = height; int minMainY = minY; - int mainCombined = main[globalIndex]; + char mainCombined = main[globalIndex]; - int floorCombined = floor[globalIndex]; + char floorCombined = floor[globalIndex]; if (hasFloorThickness) { if (x > 0) maxMainY = Math.min(heights[globalIndex - 1] & 0xFF, maxMainY); if (x < getWidth() - 1) maxMainY = Math.min(heights[globalIndex + 1] & 0xFF, maxMainY); @@ -1671,33 +1678,33 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } if (hasOverlay) { - int overlayCombined = overlay[globalIndex]; + char overlayCombined = overlay[globalIndex]; int overlayIndex = index + (height + 1 << 8); chunk.blocks[overlayIndex] = overlayCombined; } - if (primitives.bedrockId != 0) { - chunk.blocks[index] = primitives.bedrockId; + if (primitives.bedrockOrdinal != 0) { + chunk.blocks[index] = primitives.bedrockOrdinal; } } } - int[][][] localBlocks = getChunkArray(chunk.getX(), chunk.getZ()); + char[][][] localBlocks = getChunkArray(chunk.getX(), chunk.getZ()); if (localBlocks != null) { index = 0; for (int layer = 0; layer < 16; layer++) { int by = layer << 4; int ty = by + 15; for (int y = by; y <= ty; y++, index += 256) { - int[][] yBlocks = localBlocks[y]; + char[][] yBlocks = localBlocks[y]; if (yBlocks != null) { chunk.hasSections[layer] = true; for (int z = 0; z < yBlocks.length; z++) { - int[] zBlocks = yBlocks[z]; + char[] zBlocks = yBlocks[z]; if (zBlocks != null) { int zIndex = index + (z << 4); for (int x = 0; x < zBlocks.length; x++, zIndex++) { - int combined = zBlocks[x]; + char combined = zBlocks[x]; if (combined == 0) continue; chunk.blocks[zIndex] = combined; } @@ -1719,24 +1726,24 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr return chunk; } - private void setUnsafe(int[][][] map, int combined, int x, int y, int z) { - int[][] yMap = map[y]; + private void setUnsafe(char[][][] map, char combined, int x, int y, int z) { + char[][] yMap = map[y]; if (yMap == null) { - map[y] = yMap = new int[16][]; + map[y] = yMap = new char[16][]; } - int[] zMap = yMap[z]; + char[] zMap = yMap[z]; if (zMap == null) { - yMap[z] = zMap = new int[16]; + yMap[z] = zMap = new char[16]; } zMap[x] = combined; } - private int get(int[][][] map, int x, int y, int z) { - int[][] yMap = map[y]; + private int get(char[][][] map, int x, int y, int z) { + char[][] yMap = map[y]; if (yMap == null) { return 0; } - int[] zMap = yMap[z & 15]; + char[] zMap = yMap[z & 15]; if (zMap == null) { return 0; } @@ -1745,7 +1752,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr private void setOverlay(Mask mask, int combined) { int index = 0; - if (overlay == null) overlay = new DifferentialArray<>(new int[getArea()]); + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); for (int z = 0; z < getLength(); z++) { mutable.mutZ(z); for (int x = 0; x < getWidth(); x++, index++) { @@ -1807,29 +1814,29 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } } - private void setFloor(int value) { + private void setFloor(char value) { floor.record(() -> Arrays.fill(floor.get(), value)); } - private void setColumn(int value) { + private void setColumn(char value) { setFloor(value); setMain(value); } - private void setMain(int value) { + private void setMain(char value) { primitives.modifiedMain = true; main.record(() -> Arrays.fill(main.get(), value)); } - private void setOverlay(int value) { - if (overlay == null) overlay = new DifferentialArray<>(new int[getArea()]); + private void setOverlay(char value) { + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); overlay.record(() -> Arrays.fill(overlay.get(), value)); } - private void setOverlay(BufferedImage img, int combined, boolean white) { + private void setOverlay(BufferedImage img, char combined, boolean white) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); - if (overlay == null) overlay = new DifferentialArray<>(new int[getArea()]); + if (overlay == null) overlay = new DifferentialArray<>(new char[getArea()]); overlay.record(() -> { int index = 0; @@ -1845,7 +1852,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr }); } - private void setMain(BufferedImage img, int combined, boolean white) { + private void setMain(BufferedImage img, char combined, boolean white) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); primitives.modifiedMain = true; @@ -1864,7 +1871,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr }); } - private void setFloor(BufferedImage img, int combined, boolean white) { + private void setFloor(BufferedImage img, char combined, boolean white) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); @@ -1882,7 +1889,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr }); } - private void setColumn(BufferedImage img, int combined, boolean white) { + private void setColumn(BufferedImage img, char combined, boolean white) { if (img.getWidth() != getWidth() || img.getHeight() != getLength()) throw new IllegalArgumentException("Input image dimensions do not match the current height map!"); primitives.modifiedMain = true; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java index 51994ec62..0a2cd68f1 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/MCAWriter.java @@ -84,7 +84,7 @@ public abstract class MCAWriter implements Extent { @Override protected WritableMCAChunk initialValue() { WritableMCAChunk chunk = new WritableMCAChunk(); - Arrays.fill(chunk.blocks, BlockID.AIR); + Arrays.fill(chunk.blocks, (char) BlockID.AIR); // Arrays.fill(chunk.skyLight, (byte) 255); return chunk; } @@ -142,11 +142,12 @@ public abstract class MCAWriter implements Extent { pool.submit(() -> { try { WritableMCAChunk chunk = chunkStore.get(); - chunk.clear(fcx, fcz); + chunk.reset(); + chunk.setPosition(fcx, fcz); chunk = write(chunk, csx, cex, csz, cez); if (chunk != null) { // Generation offset - chunk.setLoc( fcx + (getOffsetX() >> 4), fcz + (getOffsetZ() >> 4)); + chunk.setPosition( fcx + (getOffsetX() >> 4), fcz + (getOffsetZ() >> 4)); // Compress byte[] bytes = chunk.toBytes(byteStore1.get()); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java index fbe06d76c..463044fd6 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/WritableMCAChunk.java @@ -1,39 +1,44 @@ package com.boydti.fawe.object.brush.visualization.cfi; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.object.collection.BitArray4096; +import com.boydti.fawe.object.collection.BlockVector3ChunkMap; import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.boydti.fawe.util.MathMan; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.NBTConstants; import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockID; import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; -public class WritableMCAChunk { +public class WritableMCAChunk implements IChunkSet { public final boolean[] hasSections = new boolean[16]; - public final byte[] skyLight = new byte[65536]; - public final byte[] blockLight = new byte[65536]; public boolean hasBiomes = false; - public final int[] biomes = new int[256]; + public final byte[] biomes = new byte[256]; - public final int[] blocks = new int[65536]; + public final char[] blocks = new char[65536]; - public Map tiles = new HashMap<>(); + public BlockVector3ChunkMap tiles = new BlockVector3ChunkMap(); public Map entities = new HashMap<>(); public long inhabitedTime = System.currentTimeMillis(); public long lastUpdate; @@ -54,14 +59,18 @@ public class WritableMCAChunk { return chunkZ; } - public void setLoc(int X, int Z) { + @Override + public boolean hasSection(int layer) { + return hasSections[layer]; + } + + public void setPosition(int X, int Z) { this.chunkX = X; this.chunkZ = Z; } - public void clear(int X, int Z) { - this.chunkX = X; - this.chunkZ = Z; + @Override + public IChunkSet reset() { if (!tiles.isEmpty()) { tiles.clear(); } @@ -76,6 +85,7 @@ public class WritableMCAChunk { blocks[i] = BlockID.AIR; } Arrays.fill(hasSections, false); + return this; } public void write(NBTOutputStream nbtOut) throws IOException { @@ -192,13 +202,13 @@ public class WritableMCAChunk { } - out.writeNamedTagName("BlockLight", NBTConstants.TYPE_BYTE_ARRAY); - out.writeInt(2048); - out.write(blockLight, layer << 11, 1 << 11); - - out.writeNamedTagName("SkyLight", NBTConstants.TYPE_BYTE_ARRAY); - out.writeInt(2048); - out.write(skyLight, layer << 11, 1 << 11); +// out.writeNamedTagName("BlockLight", NBTConstants.TYPE_BYTE_ARRAY); +// out.writeInt(2048); +// out.write(blockLight, layer << 11, 1 << 11); +// +// out.writeNamedTagName("SkyLight", NBTConstants.TYPE_BYTE_ARRAY); +// out.writeInt(2048); +// out.write(skyLight, layer << 11, 1 << 11); out.writeEndTag(); @@ -250,6 +260,15 @@ public class WritableMCAChunk { return deleted; } + @Override + public boolean isEmpty() { + if (deleted) return true; + for (boolean hasSection : hasSections) { + if (hasSection) return false; + } + return true; + } + public boolean isModified() { return modified != 0; } @@ -272,14 +291,17 @@ public class WritableMCAChunk { return bitMask; } - public void setTile(int x, int y, int z, CompoundTag tile) { + @Override + public boolean setTile(int x, int y, int z, CompoundTag tile) { setModified(); - short pair = MathMan.tripleBlockCoord(x, y, z); if (tile != null) { - tiles.put(pair, tile); + tiles.put(x, y, z, tile); } else { - tiles.remove(pair); + if (tiles.remove(x, y, z) == null) { + return false; + } } + return true; } public void setEntity(CompoundTag entityTag) { @@ -289,17 +311,39 @@ public class WritableMCAChunk { entities.put(new UUID(most, least), entityTag); } - public void setBiome(int x, int z, BiomeType biome) { + @Override + public BiomeType getBiomeType(int x, int z) { + return BiomeTypes.get(this.biomes[(z << 4) | x] & 0xFF); + } + + @Override + public BiomeType[] getBiomes() { + BiomeType[] tmp = new BiomeType[256]; + for (int i = 0; i < 256; i++) { + tmp[i] = BiomeTypes.get(this.biomes[i] & 0xFF); + } + return tmp; + } + + @Override + public boolean setBiome(BlockVector2 pos, BiomeType biome) { + return this.setBiome(pos.getX(), 0, pos.getZ(), biome); + } + + @Override + public boolean setBiome(int x, int y, int z, BiomeType biome) { setModified(); - biomes[x + (z << 4)] = biome.getInternalId(); + biomes[x + (z << 4)] = (byte) biome.getInternalId(); + return true; } public Set getEntities() { return new HashSet<>(entities.values()); } - public Map getTiles() { - return tiles == null ? new HashMap<>() : tiles; + @Override + public Map getTiles() { + return tiles == null ? Collections.emptyMap() : tiles; } public CompoundTag getTile(int x, int y, int z) { @@ -310,94 +354,51 @@ public class WritableMCAChunk { return tiles.get(pair); } - public boolean doesSectionExist(int cy) { - return hasSections[cy]; - } - private final int getIndex(int x, int y, int z) { return x | (z << 4) | (y << 8); } - public int getBlockCombinedId(int x, int y, int z) { + public int getBlockOrdinal(int x, int y, int z) { return blocks[x | (z << 4) | (y << 8)]; } - public BiomeType[] getBiomeArray() { - return null; + @Override + public BlockState getBlock(int x, int y, int z) { + int ordinal = getBlockOrdinal(x, y, z); + return BlockState.getFromOrdinal(ordinal); } public Set getEntityRemoves() { return new HashSet<>(); } - public void setSkyLight(int x, int y, int z, int value) { - setNibble(getIndex(x, y, z), skyLight, value); + @Override + public boolean setBlock(int x, int y, int z, BlockStateHolder holder) { + setBlock(x, y, z, holder.getOrdinalChar()); + holder.applyTileEntity(this, x, y, z); + return true; } - public void setBlockLight(int x, int y, int z, int value) { - setNibble(getIndex(x, y, z), blockLight, value); - } - - public int getSkyLight(int x, int y, int z) { - if (!hasSections[y >> 4]) { - return 0; - } - return getNibble(getIndex(x, y, z), skyLight); - } - - public int getBlockLight(int x, int y, int z) { - if (!hasSections[y >> 4]) { - return 0; - } - return getNibble(getIndex(x, y, z), blockLight); - } - - public void setFullbright() { - for (int layer = 0; layer < 16; layer++) { - if (hasSections[layer]) { - Arrays.fill(skyLight, layer << 7, ((layer + 1) << 7), (byte) 255); - } + @Override + public void setBlocks(int layer, char[] data) { + int offset = layer << 12; + for (int i = 0; i < 4096; i++) { + blocks[offset + i] = data[i]; } } - public void removeLight() { - for (int i = 0; i < 16; i++) { - removeLight(i); + @Override + public char[] getArray(int layer) { + char[] tmp = FaweCache.IMP.SECTION_BITS_TO_CHAR.get(); + int offset = layer << 12; + for (int i = 0; i < 4096; i++) { + tmp[i] = blocks[offset + i]; } + return tmp; } - public void removeLight(int i) { - if (hasSections[i]) { - Arrays.fill(skyLight, i << 7, ((i + 1) << 7), (byte) 0); - Arrays.fill(blockLight, i << 7, ((i + 1) << 7), (byte) 0); - } - } - - public int getNibble(int index, byte[] array) { - int indexShift = index >> 1; - if ((index & 1) == 0) { - return array[indexShift] & 15; - } else { - return array[indexShift] >> 4 & 15; - } - } - - public void setNibble(int index, byte[] array, int value) { - int indexShift = index >> 1; - byte existing = array[indexShift]; - int valueShift = value << 4; - if (existing == value + valueShift) { - return; - } - if ((index & 1) == 0) { - array[indexShift] = (byte) (existing & 240 | value); - } else { - array[indexShift] = (byte) (existing & 15 | valueShift); - } - } - - public void setBlock(int x, int y, int z, int combinedId) { - blocks[getIndex(x, y, z)] = combinedId; + public void setBlock(int x, int y, int z, char ordinal) { + blocks[getIndex(x, y, z)] = ordinal; } public void setBiome(BiomeType biome) { @@ -407,4 +408,9 @@ public class WritableMCAChunk { public void removeEntity(UUID uuid) { entities.remove(uuid); } + + @Override + public boolean trim(boolean aggressive) { + return isEmpty(); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BlockVector3ChunkMap.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BlockVector3ChunkMap.java index bdb98ccd6..07bfbcb45 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BlockVector3ChunkMap.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/BlockVector3ChunkMap.java @@ -47,6 +47,11 @@ public class BlockVector3ChunkMap implements Map, IAdaptedMa return map.put(key, value); } + public T remove(int x, int y, int z) { + short key = MathMan.tripleBlockCoord(x, y, z); + return map.remove(key); + } + public boolean contains(int x, int y, int z) { short key = MathMan.tripleBlockCoord(x, y, z); return map.containsKey(key); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialArray.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialArray.java index 9141e7d6d..ac1ccfae0 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialArray.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialArray.java @@ -265,9 +265,9 @@ public final class DifferentialArray implements DifferentialCollection { return dataBytes[index]; } -// public char getChar(int index) { -// return dataChars[index]; -// } + public char getChar(int index) { + return dataChars[index]; + } public int getInt(int index) { return dataInts[index]; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialBlockBuffer.java b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialBlockBuffer.java index 233b3f9fa..a7bf2eed4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialBlockBuffer.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/collection/DifferentialBlockBuffer.java @@ -9,12 +9,12 @@ import java.lang.reflect.Array; * Records changes made through the {@link #set(int, int, int, int)} method
* Changes are not recorded if you edit the raw data */ -public final class DifferentialBlockBuffer implements DifferentialCollection { +public final class DifferentialBlockBuffer implements DifferentialCollection { private final int width, length; private final int t1, t2; - private int[][][][][] data; - private int[][][][][] changes; + private char[][][][][] data; + private char[][][][][] changes; public DifferentialBlockBuffer(int width, int length) { this.width = width; @@ -24,7 +24,7 @@ public final class DifferentialBlockBuffer implements DifferentialCollection> 4; int chunkZ = z >> 4; if (data == null) { - data = new int[t1][][][][]; - changes = new int[0][][][][]; + data = new char[t1][][][][]; + changes = new char[0][][][][]; } - int[][][][] arr = data[chunkZ]; + char[][][][] arr = data[chunkZ]; if (arr == null) { - arr = data[chunkZ] = new int[t2][][][]; + arr = data[chunkZ] = new char[t2][][][]; } - int[][][] arr2 = arr[chunkX]; + char[][][] arr2 = arr[chunkX]; if (arr2 == null) { - arr2 = arr[chunkX] = new int[256][][]; + arr2 = arr[chunkX] = new char[256][][]; } - int[][] yMap = arr2[y]; + char[][] yMap = arr2[y]; if (yMap == null) { - arr2[y] = yMap = new int[16][]; + arr2[y] = yMap = new char[16][]; } boolean newSection; int current; - int[] zMap = yMap[localZ]; + char[] zMap = yMap[localZ]; if (zMap == null) { - yMap[localZ] = zMap = new int[16]; + yMap[localZ] = zMap = new char[16]; if (changes == null) { - changes = new int[t1][][][][]; + changes = new char[t1][][][][]; } else if (changes != null && changes.length != 0) { - initialChange(changes, chunkX, chunkZ, localX, localZ, y, (int) -combined); + initialChange(changes, chunkX, chunkZ, localX, localZ, y, (char) -combined); } } else { - if (changes == null || changes.length == 0) changes = new int[t1][][][][]; - appendChange(changes, chunkX, chunkZ, localX, localZ, y, (int) (zMap[localX] - combined)); + if (changes == null || changes.length == 0) changes = new char[t1][][][][]; + appendChange(changes, chunkX, chunkZ, localX, localZ, y, (char) (zMap[localX] - combined)); } zMap[localX] = combined; } - private void initialChange(int[][][][][] src, int chunkX, int chunkZ, int localX, int localZ, int y, int combined) { - int[][][][] arr = src[chunkZ]; + private void initialChange(char[][][][][] src, int chunkX, int chunkZ, int localX, int localZ, int y, char combined) { + char[][][][] arr = src[chunkZ]; if (arr == null) { - src[chunkZ] = new int[0][][][]; + src[chunkZ] = new char[0][][][]; return; } else if (arr.length == 0) return; - int[][][] arr2 = arr[chunkX]; + char[][][] arr2 = arr[chunkX]; if (arr2 == null) { - arr[chunkX] = new int[0][][]; + arr[chunkX] = new char[0][][]; return; } else if (arr2.length == 0) return; - int[][] yMap = arr2[y]; + char[][] yMap = arr2[y]; if (yMap == null) { - arr2[y] = new int[0][]; + arr2[y] = new char[0][]; return; } else if (yMap.length == 0) return; - int[] zMap = yMap[localZ]; + char[] zMap = yMap[localZ]; if (zMap == null) { - yMap[localZ] = new int[0]; + yMap[localZ] = new char[0]; return; } else if (zMap.length == 0) return; @@ -189,23 +189,23 @@ public final class DifferentialBlockBuffer implements DifferentialCollection