From 4f0ede646e814d3e9ac9d5dae2bc4387ca6513ac Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Mon, 13 Jun 2016 14:47:50 +1000 Subject: [PATCH] Chat / Merge blocks placer / generator --- .../plot/api/PlotAPI.java | 19 +- .../com/plotsquared/bukkit/BukkitMain.java | 57 +- .../plotsquared/bukkit/chat/ArrayWrapper.java | 160 +- .../plotsquared/bukkit/chat/FancyMessage.java | 1582 +++++++++-------- .../bukkit/chat/JsonRepresentedObject.java | 14 +- .../plotsquared/bukkit/chat/JsonString.java | 54 +- .../plotsquared/bukkit/chat/MessagePart.java | 238 +-- .../plotsquared/bukkit/chat/Reflection.java | 367 ++-- .../bukkit/chat/TextualComponent.java | 479 +++-- .../bukkit/generator/BukkitPlotGenerator.java | 60 +- .../bukkit/util/BukkitChatManager.java | 3 +- .../bukkit/util/BukkitChunkManager.java | 82 +- .../bukkit/util/BukkitHybridUtils.java | 61 +- .../bukkit/util/block/BukkitLocalQueue.java | 127 ++ .../util/block/BukkitLocalQueue_1_7.java | 130 ++ ...eue_1_8.java => BukkitLocalQueue_1_8.java} | 168 +- ...1_8_3.java => BukkitLocalQueue_1_8_3.java} | 343 ++-- ...eue_1_9.java => BukkitLocalQueue_1_9.java} | 383 ++-- .../bukkit/util/block/FastChunk_1_8_3.java | 247 --- .../bukkit/util/block/FastChunk_1_9.java | 251 --- .../bukkit/util/block/FastQueue_1_7.java | 179 -- .../bukkit/util/block/GenChunk.java | 142 +- .../bukkit/util/block/SlowChunk.java | 60 - .../bukkit/util/block/SlowQueue.java | 287 --- .../intellectualcrafters/plot/IPlotMain.java | 7 +- .../com/intellectualcrafters/plot/PS.java | 11 +- .../plot/commands/Clear.java | 4 +- .../plot/commands/DebugExec.java | 8 +- .../plot/commands/Relight.java | 8 +- .../plot/commands/Set.java | 4 +- .../plot/commands/Template.java | 5 +- .../plot/commands/Trim.java | 5 +- .../plot/generator/AugmentedUtils.java | 101 +- .../plot/generator/ClassicPlotManager.java | 123 +- .../plot/generator/HybridGen.java | 23 +- .../plot/generator/HybridPlotManager.java | 28 +- .../plot/generator/HybridUtils.java | 73 +- .../generator/IndependentPlotGenerator.java | 6 +- .../plot/object/ChunkWrapper.java | 45 + .../plot/object/Plot.java | 32 +- .../plot/object/PlotArea.java | 12 +- .../plot/util/ChunkManager.java | 58 +- .../plot/util/MainUtil.java | 55 +- .../plot/util/MathMan.java | 116 +- .../plot/util/PlotChunk.java | 261 --- .../plot/util/PlotQueue.java | 32 - .../plot/util/SchematicHandler.java | 45 +- .../plot/util/SetQueue.java | 197 -- .../plot/util/TaskManager.java | 70 +- .../plot/util/block/BasicLocalBlockQueue.java | 282 +++ .../util/block/DelegateLocalBlockQueue.java | 121 ++ .../plot/util/block/GlobalBlockQueue.java | 297 ++++ .../plot/util/block/LocalBlockQueue.java | 105 ++ .../util/block/OffsetLocalBlockQueue.java | 24 + .../plot/util/block/QueueProvider.java | 30 + .../util/block/ScopedLocalBlockQueue.java | 88 + .../listener/ProcessedWEExtent.java | 6 - Sponge/build.gradle | 1 - .../com/plotsquared/sponge/SpongeMain.java | 32 +- .../generator/SpongeAugmentedGenerator.java | 60 +- .../sponge/generator/SpongeTerrainGen.java | 14 +- .../sponge/util/SpongeChatManager.java | 3 +- .../sponge/util/SpongeHybridUtils.java | 52 +- .../sponge/util/block/FastChunk.java | 246 --- .../sponge/util/block/FastQueue.java | 326 ---- .../sponge/util/block/GenChunk.java | 74 +- .../sponge/util/block/SendChunk.java | 108 -- .../sponge/util/block/SlowChunk.java | 65 - .../sponge/util/block/SlowQueue.java | 226 --- .../sponge/util/block/SpongeLocalQueue.java | 500 ++++++ fanciful-master.zip | Bin 0 -> 23329 bytes 71 files changed, 4523 insertions(+), 4959 deletions(-) create mode 100644 Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue.java create mode 100644 Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue_1_7.java rename Bukkit/src/main/java/com/plotsquared/bukkit/util/block/{FastQueue_1_8.java => BukkitLocalQueue_1_8.java} (74%) rename Bukkit/src/main/java/com/plotsquared/bukkit/util/block/{FastQueue_1_8_3.java => BukkitLocalQueue_1_8_3.java} (61%) rename Bukkit/src/main/java/com/plotsquared/bukkit/util/block/{FastQueue_1_9.java => BukkitLocalQueue_1_9.java} (56%) delete mode 100644 Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastChunk_1_8_3.java delete mode 100644 Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastChunk_1_9.java delete mode 100644 Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_7.java delete mode 100644 Bukkit/src/main/java/com/plotsquared/bukkit/util/block/SlowChunk.java delete mode 100644 Bukkit/src/main/java/com/plotsquared/bukkit/util/block/SlowQueue.java create mode 100644 Core/src/main/java/com/intellectualcrafters/plot/object/ChunkWrapper.java delete mode 100644 Core/src/main/java/com/intellectualcrafters/plot/util/PlotChunk.java delete mode 100644 Core/src/main/java/com/intellectualcrafters/plot/util/PlotQueue.java delete mode 100644 Core/src/main/java/com/intellectualcrafters/plot/util/SetQueue.java create mode 100644 Core/src/main/java/com/intellectualcrafters/plot/util/block/BasicLocalBlockQueue.java create mode 100644 Core/src/main/java/com/intellectualcrafters/plot/util/block/DelegateLocalBlockQueue.java create mode 100644 Core/src/main/java/com/intellectualcrafters/plot/util/block/GlobalBlockQueue.java create mode 100644 Core/src/main/java/com/intellectualcrafters/plot/util/block/LocalBlockQueue.java create mode 100644 Core/src/main/java/com/intellectualcrafters/plot/util/block/OffsetLocalBlockQueue.java create mode 100644 Core/src/main/java/com/intellectualcrafters/plot/util/block/QueueProvider.java create mode 100644 Core/src/main/java/com/intellectualcrafters/plot/util/block/ScopedLocalBlockQueue.java delete mode 100644 Sponge/src/main/java/com/plotsquared/sponge/util/block/FastChunk.java delete mode 100644 Sponge/src/main/java/com/plotsquared/sponge/util/block/FastQueue.java delete mode 100644 Sponge/src/main/java/com/plotsquared/sponge/util/block/SendChunk.java delete mode 100644 Sponge/src/main/java/com/plotsquared/sponge/util/block/SlowChunk.java delete mode 100644 Sponge/src/main/java/com/plotsquared/sponge/util/block/SlowQueue.java create mode 100644 Sponge/src/main/java/com/plotsquared/sponge/util/block/SpongeLocalQueue.java create mode 100644 fanciful-master.zip diff --git a/Bukkit/src/main/java/com/intellectualcrafters/plot/api/PlotAPI.java b/Bukkit/src/main/java/com/intellectualcrafters/plot/api/PlotAPI.java index bce651d3b..8b6014da8 100644 --- a/Bukkit/src/main/java/com/intellectualcrafters/plot/api/PlotAPI.java +++ b/Bukkit/src/main/java/com/intellectualcrafters/plot/api/PlotAPI.java @@ -16,22 +16,21 @@ import com.intellectualcrafters.plot.util.ChunkManager; import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.Permissions; import com.intellectualcrafters.plot.util.SchematicHandler; -import com.intellectualcrafters.plot.util.SetQueue; import com.intellectualcrafters.plot.util.UUIDHandler; +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; import com.intellectualcrafters.plot.uuid.UUIDWrapper; import com.plotsquared.bukkit.util.BukkitUtil; -import org.bukkit.Location; -import org.bukkit.OfflinePlayer; -import org.bukkit.World; -import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.UUID; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; /** * PlotSquared API. @@ -140,10 +139,10 @@ public class PlotAPI { /** * Get the block/biome set queue - * @return SetQueue.IMP + * @return GlobalBlockQueue.IMP */ - public SetQueue getSetQueue() { - return SetQueue.IMP; + public GlobalBlockQueue getBlockQueue() { + return GlobalBlockQueue.IMP; } /** diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java index 10bdfe5dc..b418a2cb7 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java @@ -23,7 +23,6 @@ import com.intellectualcrafters.plot.util.EconHandler; import com.intellectualcrafters.plot.util.EventUtil; import com.intellectualcrafters.plot.util.InventoryUtil; import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotQueue; import com.intellectualcrafters.plot.util.SchematicHandler; import com.intellectualcrafters.plot.util.SetupUtils; import com.intellectualcrafters.plot.util.StringMan; @@ -31,6 +30,7 @@ import com.intellectualcrafters.plot.util.TaskManager; import com.intellectualcrafters.plot.util.UUIDHandler; import com.intellectualcrafters.plot.util.UUIDHandlerImplementation; import com.intellectualcrafters.plot.util.WorldUtil; +import com.intellectualcrafters.plot.util.block.QueueProvider; import com.intellectualcrafters.plot.uuid.UUIDWrapper; import com.plotsquared.bukkit.database.plotme.ClassicPlotMeConnector; import com.plotsquared.bukkit.database.plotme.LikePlotMeConverter; @@ -60,20 +60,25 @@ import com.plotsquared.bukkit.util.BukkitUtil; import com.plotsquared.bukkit.util.Metrics; import com.plotsquared.bukkit.util.SendChunk; import com.plotsquared.bukkit.util.SetGenCB; -import com.plotsquared.bukkit.util.block.FastQueue_1_7; -import com.plotsquared.bukkit.util.block.FastQueue_1_8; -import com.plotsquared.bukkit.util.block.FastQueue_1_8_3; -import com.plotsquared.bukkit.util.block.FastQueue_1_9; -import com.plotsquared.bukkit.util.block.SlowQueue; +import com.plotsquared.bukkit.util.block.BukkitLocalQueue; +import com.plotsquared.bukkit.util.block.BukkitLocalQueue_1_7; +import com.plotsquared.bukkit.util.block.BukkitLocalQueue_1_8; +import com.plotsquared.bukkit.util.block.BukkitLocalQueue_1_8_3; +import com.plotsquared.bukkit.util.block.BukkitLocalQueue_1_9; import com.plotsquared.bukkit.uuid.DefaultUUIDWrapper; import com.plotsquared.bukkit.uuid.FileUUIDHandler; import com.plotsquared.bukkit.uuid.LowerOfflineUUIDWrapper; import com.plotsquared.bukkit.uuid.OfflineUUIDWrapper; import com.plotsquared.bukkit.uuid.SQLUUIDHandler; import com.sk89q.worldedit.WorldEdit; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.OfflinePlayer; import org.bukkit.World; @@ -87,13 +92,6 @@ import org.bukkit.metadata.MetadataValue; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.UUID; - public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain { public static WorldEdit worldEdit; @@ -417,7 +415,7 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain } @Override - public PlotQueue initPlotQueue() { + public QueueProvider initBlockQueue() { try { new SendChunk(); MainUtil.canSendChunk = true; @@ -426,32 +424,15 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain MainUtil.canSendChunk = false; } if (PS.get().checkVersion(getServerVersion(), 1, 9, 0)) { - try { - return new FastQueue_1_9(); - } catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException e) { - e.printStackTrace(); - return new SlowQueue(); - } + return QueueProvider.of(BukkitLocalQueue_1_9.class, BukkitLocalQueue.class); + } + if (PS.get().checkVersion(getServerVersion(), 1, 8, 3)) { + return QueueProvider.of(BukkitLocalQueue_1_8_3.class, BukkitLocalQueue.class); } if (PS.get().checkVersion(getServerVersion(), 1, 8, 0)) { - try { - return new FastQueue_1_8_3(); - } catch (NoSuchMethodException | ClassNotFoundException | NoSuchFieldException e) { - e.printStackTrace(); - try { - return new FastQueue_1_8(); - } catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException e2) { - e2.printStackTrace(); - return new SlowQueue(); - } - } - } - try { - return new FastQueue_1_7(); - } catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException e) { - e.printStackTrace(); - return new SlowQueue(); + return QueueProvider.of(BukkitLocalQueue_1_8.class, BukkitLocalQueue.class); } + return QueueProvider.of(BukkitLocalQueue_1_7.class, BukkitLocalQueue.class); } @Override diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/chat/ArrayWrapper.java b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/ArrayWrapper.java index 1eecfb330..68fc6b723 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/chat/ArrayWrapper.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/ArrayWrapper.java @@ -9,93 +9,103 @@ import java.util.Collection; /** * 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. + *

* - *

This class is intended for use as a key to a map. - * - * @author Glen Husman * @param The type of elements in the array. + * @author Glen Husman * @see Arrays */ public final class ArrayWrapper { - private E[] array; + /** + * Creates an array wrapper with some elements. + * + * @param elements The elements of the array. + */ + public ArrayWrapper(E... elements) { + setArray(elements); + } - /** - * Creates an array wrapper with some elements. - * @param elements The elements of the array. - */ - public ArrayWrapper(E... elements) { - setArray(elements); - } + private E[] _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(); - } + /** + * Retrieves a reference to the wrapped array instance. + * + * @return The array wrapped by this instance. + */ + public E[] getArray() { + return _array; + } - if (size < 0) { - size = 0; - // Ugly hack: Count it ourselves - for (@SuppressWarnings("unused") T element : list) { - size++; - } - } + /** + * 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; + } - 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; - } + /** + * 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); + } - /** - * Retrieves a reference to the wrapped array instance. - * @return The array wrapped by this instance. - */ - public E[] getArray() { - return this.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); + } - /** - * 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."); - this.array = 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(); + } - /** - * 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(this.array, ((ArrayWrapper) other).array); - } - /** - * Gets the hash code represented by this objects value. - * @see Arrays#hashCode(Object[]) - * @return This object's hash code. - */ - @Override - public int hashCode() { - return Arrays.hashCode(this.array); - } + 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/Bukkit/src/main/java/com/plotsquared/bukkit/chat/FancyMessage.java b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/FancyMessage.java index 0c6a23eac..ba61fe261 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/chat/FancyMessage.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/FancyMessage.java @@ -1,25 +1,10 @@ package com.plotsquared.bukkit.chat; -import static com.plotsquared.bukkit.chat.TextualComponent.rawText; - import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.stream.JsonWriter; -import com.intellectualcrafters.configuration.serialization.ConfigurationSerializable; -import com.intellectualcrafters.configuration.serialization.ConfigurationSerialization; -import org.bukkit.Achievement; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.Statistic; -import org.bukkit.Statistic.Type; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - import java.io.IOException; import java.io.StringWriter; import java.lang.reflect.Constructor; @@ -29,849 +14,874 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Level; +import org.bukkit.Achievement; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.Statistic.Type; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.ConfigurationSerialization; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + + +import static com.plotsquared.bukkit.chat.TextualComponent.rawText; /** - * Represents a formattable message. Such messages can use elements such as colors, formatting codes, hover and click data, and other features - * provided by the vanilla Minecraft JSON message formatter. - * This class allows plugins to emulate the functionality of the vanilla Minecraft - * tellraw command. - *

This class follows the builder pattern, allowing for method chaining. - * It is set up such that invocations of property-setting methods will affect - * the current editing component, and a call to {@link #then(String)} or - * {@link #text(TextualComponent)} will append a new editing component to the - * end of the message, optionally initializing it with text. Further - * property-setting method calls will affect that editing component.

+ * Represents a formattable message. Such messages can use elements such as colors, formatting codes, hover and click data, and other features provided by the vanilla Minecraft JSON message formatter. + * This class allows plugins to emulate the functionality of the vanilla Minecraft tellraw command. + *

+ * This class follows the builder pattern, allowing for method chaining. + * It is set up such that invocations of property-setting methods will affect the current editing component, + * and a call to {@link #then()} or {@link #then(String)} will append a new editing component to the end of the message, + * optionally initializing it with text. Further property-setting method calls will affect that editing component. + *

*/ public class FancyMessage implements JsonRepresentedObject, Cloneable, Iterable, ConfigurationSerializable { - private static final JsonParser _stringParser = new JsonParser(); - private static Constructor nmsPacketPlayOutChatConstructor; - // The ChatSerializer's instance of Gson - private static Object nmsChatSerializerGsonInstance; - private static Method fromJsonMethod; + static { + ConfigurationSerialization.registerClass(FancyMessage.class); + } - static { - ConfigurationSerialization.registerClass(FancyMessage.class); - } + private List messageParts; + private String jsonString; + private boolean dirty; - private List messageParts; - private String jsonString; - private boolean dirty; + private static Constructor nmsPacketPlayOutChatConstructor; - /** - * Creates a JSON message with text. - * @param firstPartText The existing text in the message. - */ - public FancyMessage(String firstPartText) { - this(rawText(firstPartText)); - } + @Override + public FancyMessage clone() throws CloneNotSupportedException { + FancyMessage instance = (FancyMessage) super.clone(); + instance.messageParts = new ArrayList(messageParts.size()); + for (int i = 0; i < messageParts.size(); i++) { + instance.messageParts.add(i, messageParts.get(i).clone()); + } + instance.dirty = false; + instance.jsonString = null; + return instance; + } - public FancyMessage(TextualComponent firstPartText) { - this.messageParts = new ArrayList<>(); - this.messageParts.add(new MessagePart(firstPartText)); - this.jsonString = null; - this.dirty = false; + /** + * Creates a JSON message with text. + * + * @param firstPartText The existing text in the message. + */ + public FancyMessage(final String firstPartText) { + this(rawText(firstPartText)); + } - if (nmsPacketPlayOutChatConstructor == null) { - try { - nmsPacketPlayOutChatConstructor = - Reflection.getNMSClass("PacketPlayOutChat").getDeclaredConstructor(Reflection.getNMSClass("IChatBaseComponent")); - nmsPacketPlayOutChatConstructor.setAccessible(true); - } catch (NoSuchMethodException e) { - Bukkit.getLogger().log(Level.SEVERE, "Could not find Minecraft method or constructor.", e); - } catch (SecurityException e) { - Bukkit.getLogger().log(Level.WARNING, "Could not access constructor.", e); - } - } - } + public FancyMessage(final com.plotsquared.bukkit.chat.TextualComponent firstPartText) { + messageParts = new ArrayList(); + messageParts.add(new MessagePart(firstPartText)); + jsonString = null; + dirty = false; - /** - * Creates a JSON message without text. - */ - public FancyMessage() { - this((TextualComponent) null); - } + if (nmsPacketPlayOutChatConstructor == null) { + try { + nmsPacketPlayOutChatConstructor = com.plotsquared.bukkit.chat.Reflection.getNMSClass("PacketPlayOutChat").getDeclaredConstructor(com.plotsquared.bukkit.chat.Reflection.getNMSClass("IChatBaseComponent")); + nmsPacketPlayOutChatConstructor.setAccessible(true); + } catch (NoSuchMethodException e) { + Bukkit.getLogger().log(Level.SEVERE, "Could not find Minecraft method or constructor.", e); + } catch (SecurityException e) { + Bukkit.getLogger().log(Level.WARNING, "Could not access constructor.", e); + } + } + } - /** - * Deserialize a JSON-represented message from a mapping of key-value pairs. - * This is called by the Bukkit serialization API. - * It is not intended for direct public API consumption. - * @param serialized The key-value mapping which represents a fancy message. - */ - @SuppressWarnings("unchecked") - public static FancyMessage deserialize(Map serialized) { - FancyMessage msg = new FancyMessage(); - msg.messageParts = (List) serialized.get("messageParts"); - msg.jsonString = serialized.containsKey("JSON") ? serialized.get("JSON").toString() : null; - msg.dirty = !serialized.containsKey("JSON"); - return msg; - } + /** + * Creates a JSON message without text. + */ + public FancyMessage() { + this((com.plotsquared.bukkit.chat.TextualComponent) null); + } - /** - * Deserialize a fancy message from its JSON representation. This JSON - * representation is of the format of hat returned by - * {@link #toJSONString()}, and is compatible with vanilla inputs. - * @param json The JSON string which represents a fancy message. - * @return A {@code FancyMessage} representing the parametrized JSON message. - */ - public static FancyMessage deserialize(String json) { - JsonObject serialized = _stringParser.parse(json).getAsJsonObject(); - JsonArray extra = serialized.getAsJsonArray("extra"); // Get the extra component - FancyMessage returnVal = new FancyMessage(); - returnVal.messageParts.clear(); - for (JsonElement mPrt : extra) { - MessagePart component = new MessagePart(); - JsonObject messagePart = mPrt.getAsJsonObject(); - for (Map.Entry entry : messagePart.entrySet()) { - // Deserialize text - if (TextualComponent.isTextKey(entry.getKey())) { - // The map mimics the YAML serialization, which has a "key" field and one or more "value" fields - Map serializedMapForm = new HashMap<>(); // Must be object due to Bukkit serializer API compliance - serializedMapForm.put("key", entry.getKey()); - if (entry.getValue().isJsonPrimitive()) { - // Assume string - serializedMapForm.put("value", entry.getValue().getAsString()); - } else { - // Composite object, but we assume each element is a string - for (Map.Entry compositeNestedElement : entry.getValue().getAsJsonObject().entrySet()) { - serializedMapForm.put("value." + compositeNestedElement.getKey(), compositeNestedElement.getValue().getAsString()); - } - } - component.text = TextualComponent.deserialize(serializedMapForm); - } else if (MessagePart.stylesToNames.inverse().containsKey(entry.getKey())) { - if (entry.getValue().getAsBoolean()) { - component.styles.add(MessagePart.stylesToNames.inverse().get(entry.getKey())); - } - } else if (entry.getKey().equals("color")) { - component.color = ChatColor.valueOf(entry.getValue().getAsString().toUpperCase()); - } else if (entry.getKey().equals("clickEvent")) { - JsonObject object = entry.getValue().getAsJsonObject(); - component.clickActionName = object.get("action").getAsString(); - component.clickActionData = object.get("value").getAsString(); - } else if (entry.getKey().equals("hoverEvent")) { - JsonObject object = entry.getValue().getAsJsonObject(); - component.hoverActionName = object.get("action").getAsString(); - if (object.get("value").isJsonPrimitive()) { - // Assume string - component.hoverActionData = new JsonString(object.get("value").getAsString()); - } else { - // Assume composite type - // The only composite type we currently store is another FancyMessage - // Therefore, recursion time! - component.hoverActionData = - deserialize(object.get("value").toString() /* This should properly serialize the JSON object as a JSON string */); - } - } else if (entry.getKey().equals("insertion")) { - component.insertionData = entry.getValue().getAsString(); - } else if (entry.getKey().equals("with")) { - for (JsonElement object : entry.getValue().getAsJsonArray()) { - if (object.isJsonPrimitive()) { - component.translationReplacements.add(new JsonString(object.getAsString())); - } else { - // Only composite type stored in this array is - again - FancyMessages - // Recurse within this function to parse this as a translation replacement - component.translationReplacements.add(deserialize(object.toString())); - } - } - } - } - returnVal.messageParts.add(component); - } - return returnVal; - } + /** + * Sets the text of the current editing component to a value. + * + * @param text The new text of the current editing component. + * @return This builder instance. + */ + public FancyMessage text(String text) { + MessagePart latest = latest(); + latest.text = rawText(text); + dirty = true; + return this; + } - @Override - public FancyMessage clone() throws CloneNotSupportedException { - FancyMessage instance = (FancyMessage) super.clone(); - instance.messageParts = new ArrayList<>(this.messageParts.size()); - for (int i = 0; i < this.messageParts.size(); i++) { - instance.messageParts.add(i, this.messageParts.get(i).clone()); - } - instance.dirty = false; - instance.jsonString = null; - return instance; - } + /** + * Sets the text of the current editing component to a value. + * + * @param text The new text of the current editing component. + * @return This builder instance. + */ + public FancyMessage text(com.plotsquared.bukkit.chat.TextualComponent text) { + MessagePart latest = latest(); + latest.text = text; + dirty = true; + return this; + } - /** - * Sets the text of the current editing component to a value. - * @param text The new text of the current editing component. - * @return This builder instance. - */ - public FancyMessage text(String text) { - MessagePart latest = latest(); - latest.text = rawText(text); - this.dirty = true; - return this; - } + /** + * Sets the color of the current editing component to a value. + * + * @param color The new color of the current editing component. + * @return This builder instance. + * @throws IllegalArgumentException If the specified {@code ChatColor} enumeration value is not a color (but a format value). + */ + public FancyMessage color(final ChatColor color) { + if (!color.isColor()) { + throw new IllegalArgumentException(color.name() + " is not a color"); + } + latest().color = color; + dirty = true; + return this; + } - /** - * Sets the text of the current editing component to a value. - * @param text The new text of the current editing component. - * @return This builder instance. - */ - public FancyMessage text(TextualComponent text) { - MessagePart latest = latest(); - latest.text = text; - this.dirty = true; - return this; - } + /** + * Sets the stylization of the current editing component. + * + * @param styles The array of styles to apply to the editing component. + * @return This builder instance. + * @throws IllegalArgumentException If any of the enumeration values in the array do not represent formatters. + */ + public FancyMessage style(ChatColor... styles) { + for (final ChatColor style : styles) { + if (!style.isFormat()) { + throw new IllegalArgumentException(style.name() + " is not a style"); + } + } + latest().styles.addAll(Arrays.asList(styles)); + dirty = true; + return this; + } - /** - * Sets the color of the current editing component to a value. - * @param color The new color of the current editing component. - * @return This builder instance. - * @exception IllegalArgumentException If the specified {@code ChatColor} enumeration value is not a color (but a format value). - */ - public FancyMessage color(ChatColor color) { - latest().color = color; - this.dirty = true; - return this; - } + /** + * Set the behavior of the current editing component to instruct the client to open a file on the client side filesystem when the currently edited part of the {@code FancyMessage} is clicked. + * + * @param path The path of the file on the client filesystem. + * @return This builder instance. + */ + public FancyMessage file(final String path) { + onClick("open_file", path); + return this; + } - /** - * Sets the stylization of the current editing component. - * @param styles The array of styles to apply to the editing component. - * @return This builder instance. - * @exception IllegalArgumentException If any of the enumeration values in the array do not represent formatters. - */ - public FancyMessage style(ChatColor... styles) { - for (ChatColor style : styles) { - if (!style.isFormat()) { - throw new IllegalArgumentException(style.name() + " is not a style"); - } - } - latest().styles.addAll(Arrays.asList(styles)); - this.dirty = true; - return this; - } + /** + * Set the behavior of the current editing component to instruct the client to open a webpage in the client's web browser when the currently edited part of the {@code FancyMessage} is clicked. + * + * @param url The URL of the page to open when the link is clicked. + * @return This builder instance. + */ + public FancyMessage link(final String url) { + onClick("open_url", url); + return this; + } - /** - * Set the behavior of the current editing component to instruct the client to open a file on the client side filesystem when the currently - * edited part of the {@code FancyMessage} is clicked. - * @param path The path of the file on the client filesystem. - * @return This builder instance. - */ - public FancyMessage file(String path) { - onClick("open_file", path); - return this; - } + /** + * Set the behavior of the current editing component to instruct the client to replace the chat input box content with the specified string when the currently edited part of the {@code FancyMessage} is clicked. + * The client will not immediately send the command to the server to be executed unless the client player submits the command/chat message, usually with the enter key. + * + * @param command The text to display in the chat bar of the client. + * @return This builder instance. + */ + public FancyMessage suggest(final String command) { + onClick("suggest_command", command); + return this; + } - /** - * Set the behavior of the current editing component to instruct the client to open a webpage in the client's web browser when the currently - * edited part of the {@code FancyMessage} is clicked. - * @param url The URL of the page to open when the link is clicked. - * @return This builder instance. - */ - public FancyMessage link(String url) { - onClick("open_url", url); - return this; - } + /** + * Set the behavior of the current editing component to instruct the client to append the chat input box content with the specified string when the currently edited part of the {@code FancyMessage} is SHIFT-CLICKED. + * The client will not immediately send the command to the server to be executed unless the client player submits the command/chat message, usually with the enter key. + * + * @param command The text to append to the chat bar of the client. + * @return This builder instance. + */ + public FancyMessage insert(final String command) { + latest().insertionData = command; + dirty = true; + return this; + } - /** - * Set the behavior of the current editing component to instruct the client to replace the chat input box content with the specified string - * when the currently edited part of the {@code FancyMessage} is clicked. - * The client will not immediately send the command to the server to be executed unless the client player submits the command/chat message, - * usually with the enter key. - * @param command The text to display in the chat bar of the client. - * @return This builder instance. - */ - public FancyMessage suggest(String command) { - onClick("suggest_command", command); - return this; - } + /** + * Set the behavior of the current editing component to instruct the client to send the specified string to the server as a chat message when the currently edited part of the {@code FancyMessage} is clicked. + * The client will immediately send the command to the server to be executed when the editing component is clicked. + * + * @param command The text to display in the chat bar of the client. + * @return This builder instance. + */ + public FancyMessage command(final String command) { + onClick("run_command", command); + return this; + } - /** - * Set the behavior of the current editing component to instruct the client to append the chat input box content with the specified string when - * the currently edited part of the {@code FancyMessage} is SHIFT-CLICKED. - * The client will not immediately send the command to the server to be executed unless the client player submits the command/chat message, - * usually with the enter key. - * @param command The text to append to the chat bar of the client. - * @return This builder instance. - */ - public FancyMessage insert(String command) { - latest().insertionData = command; - this.dirty = true; - return this; - } + /** + * Set the behavior of the current editing component to display information about an achievement when the client hovers over the text. + *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

+ * + * @param name The name of the achievement to display, excluding the "achievement." prefix. + * @return This builder instance. + */ + public FancyMessage achievementTooltip(final String name) { + onHover("show_achievement", new JsonString("achievement." + name)); + return this; + } - /** - * Set the behavior of the current editing component to instruct the client to send the specified string to the server as a chat message when - * the currently edited part of the {@code FancyMessage} is clicked. - * The client will immediately send the command to the server to be executed when the editing component is clicked. - * @param command The text to display in the chat bar of the client. - * @return This builder instance. - */ - public FancyMessage command(String command) { - onClick("run_command", command); - return this; - } + /** + * Set the behavior of the current editing component to display information about an achievement when the client hovers over the text. + *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

+ * + * @param which The achievement to display. + * @return This builder instance. + */ + public FancyMessage achievementTooltip(final Achievement which) { + try { + Object achievement = com.plotsquared.bukkit.chat.Reflection.getMethod(com.plotsquared.bukkit.chat.Reflection.getOBCClass("CraftStatistic"), "getNMSAchievement", Achievement.class).invoke(null, which); + return achievementTooltip((String) com.plotsquared.bukkit.chat.Reflection.getField(com.plotsquared.bukkit.chat.Reflection.getNMSClass("Achievement"), "name").get(achievement)); + } catch (IllegalAccessException e) { + Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e); + return this; + } catch (IllegalArgumentException e) { + Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e); + return this; + } catch (InvocationTargetException e) { + Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e); + return this; + } + } - /** - * Set the behavior of the current editing component to display information about an achievement when the client hovers over the text. - *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

- * @param name The name of the achievement to display, excluding the "achievement." prefix. - * @return This builder instance. - */ - public FancyMessage achievementTooltip(String name) { - onHover("show_achievement", new JsonString("achievement." + name)); - return this; - } + /** + * Set the behavior of the current editing component to display information about a parameterless statistic when the client hovers over the text. + *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

+ * + * @param which The statistic to display. + * @return This builder instance. + * @throws IllegalArgumentException If the statistic requires a parameter which was not supplied. + */ + public FancyMessage statisticTooltip(final Statistic which) { + Type type = which.getType(); + if (type != Type.UNTYPED) { + throw new IllegalArgumentException("That statistic requires an additional " + type + " parameter!"); + } + try { + Object statistic = com.plotsquared.bukkit.chat.Reflection.getMethod(com.plotsquared.bukkit.chat.Reflection.getOBCClass("CraftStatistic"), "getNMSStatistic", Statistic.class).invoke(null, which); + return achievementTooltip((String) com.plotsquared.bukkit.chat.Reflection.getField(com.plotsquared.bukkit.chat.Reflection.getNMSClass("Statistic"), "name").get(statistic)); + } catch (IllegalAccessException e) { + Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e); + return this; + } catch (IllegalArgumentException e) { + Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e); + return this; + } catch (InvocationTargetException e) { + Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e); + return this; + } + } - /** - * Set the behavior of the current editing component to display information about an achievement when the client hovers over the text. - *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

- * @param which The achievement to display. - * @return This builder instance. - */ - public FancyMessage achievementTooltip(Achievement which) { - try { - Object achievement = - Reflection.getMethod(Reflection.getOBCClass("CraftStatistic"), "getNMSAchievement", Achievement.class).invoke(null, which); - return achievementTooltip((String) Reflection.getField(Reflection.getNMSClass("Achievement"), "name").get(achievement)); - } catch (IllegalAccessException e) { - Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e); - return this; - } catch (IllegalArgumentException e) { - Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e); - return this; - } catch (InvocationTargetException e) { - Bukkit.getLogger().log(Level.WARNING, "A error has occurred during invoking of method.", e); - return this; - } - } + /** + * Set the behavior of the current editing component to display information about a statistic parameter with a material when the client hovers over the text. + *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

+ * + * @param which The statistic to display. + * @param item The sole material parameter to the statistic. + * @return This builder instance. + * @throws IllegalArgumentException If the statistic requires a parameter which was not supplied, or was supplied a parameter that was not required. + */ + public FancyMessage statisticTooltip(final Statistic which, Material item) { + Type type = which.getType(); + if (type == Type.UNTYPED) { + throw new IllegalArgumentException("That statistic needs no additional parameter!"); + } + if ((type == Type.BLOCK && item.isBlock()) || type == Type.ENTITY) { + throw new IllegalArgumentException("Wrong parameter type for that statistic - needs " + type + "!"); + } + try { + Object statistic = com.plotsquared.bukkit.chat.Reflection.getMethod(com.plotsquared.bukkit.chat.Reflection.getOBCClass("CraftStatistic"), "getMaterialStatistic", Statistic.class, Material.class).invoke(null, which, item); + return achievementTooltip((String) com.plotsquared.bukkit.chat.Reflection.getField(com.plotsquared.bukkit.chat.Reflection.getNMSClass("Statistic"), "name").get(statistic)); + } catch (IllegalAccessException e) { + Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e); + return this; + } catch (IllegalArgumentException e) { + Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e); + return this; + } catch (InvocationTargetException e) { + Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e); + return this; + } + } - /** - * Set the behavior of the current editing component to display information about a parameterless statistic when the client hovers over the text. - *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

- * @param which The statistic to display. - * @return This builder instance. - * @exception IllegalArgumentException If the statistic requires a parameter which was not supplied. - */ - public FancyMessage statisticTooltip(Statistic which) { - Type type = which.getType(); - if (type != Type.UNTYPED) { - throw new IllegalArgumentException("That statistic requires an additional " + type + " parameter!"); - } - try { - Object statistic = Reflection.getMethod(Reflection.getOBCClass("CraftStatistic"), "getNMSStatistic", Statistic.class).invoke(null, which); - return achievementTooltip((String) Reflection.getField(Reflection.getNMSClass("Statistic"), "name").get(statistic)); - } catch (IllegalAccessException e) { - Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e); - return this; - } catch (IllegalArgumentException e) { - Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e); - return this; - } catch (InvocationTargetException e) { - Bukkit.getLogger().log(Level.WARNING, "A error has occurred during invoking of method.", e); - return this; - } - } + /** + * Set the behavior of the current editing component to display information about a statistic parameter with an entity type when the client hovers over the text. + *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

+ * + * @param which The statistic to display. + * @param entity The sole entity type parameter to the statistic. + * @return This builder instance. + * @throws IllegalArgumentException If the statistic requires a parameter which was not supplied, or was supplied a parameter that was not required. + */ + public FancyMessage statisticTooltip(final Statistic which, EntityType entity) { + Type type = which.getType(); + if (type == Type.UNTYPED) { + throw new IllegalArgumentException("That statistic needs no additional parameter!"); + } + if (type != Type.ENTITY) { + throw new IllegalArgumentException("Wrong parameter type for that statistic - needs " + type + "!"); + } + try { + Object statistic = com.plotsquared.bukkit.chat.Reflection.getMethod(com.plotsquared.bukkit.chat.Reflection.getOBCClass("CraftStatistic"), "getEntityStatistic", Statistic.class, EntityType.class).invoke(null, which, entity); + return achievementTooltip((String) com.plotsquared.bukkit.chat.Reflection.getField(com.plotsquared.bukkit.chat.Reflection.getNMSClass("Statistic"), "name").get(statistic)); + } catch (IllegalAccessException e) { + Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e); + return this; + } catch (IllegalArgumentException e) { + Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e); + return this; + } catch (InvocationTargetException e) { + Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e); + return this; + } + } - /** - * Set the behavior of the current editing component to display information about a statistic parameter with a material when the client hovers - * over the text. - *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

- * @param which The statistic to display. - * @param item The sole material parameter to the statistic. - * @return This builder instance. - * @exception IllegalArgumentException If the statistic requires a parameter which was not supplied, or was supplied a parameter that was not - * required. - */ - public FancyMessage statisticTooltip(Statistic which, Material item) { - Type type = which.getType(); - if (type == Type.UNTYPED) { - throw new IllegalArgumentException("That statistic needs no additional parameter!"); - } - if (type == Type.BLOCK && item.isBlock() || type == Type.ENTITY) { - throw new IllegalArgumentException("Wrong parameter type for that statistic - needs " + type + "!"); - } - try { - Object statistic = Reflection.getMethod(Reflection.getOBCClass("CraftStatistic"), "getMaterialStatistic", Statistic.class, Material.class) - .invoke(null, which, item); - return achievementTooltip((String) Reflection.getField(Reflection.getNMSClass("Statistic"), "name").get(statistic)); - } catch (IllegalAccessException e) { - Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e); - return this; - } catch (IllegalArgumentException e) { - Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e); - return this; - } catch (InvocationTargetException e) { - Bukkit.getLogger().log(Level.WARNING, "A error has occurred during invoking of method.", e); - return this; - } - } + /** + * Set the behavior of the current editing component to display information about an item when the client hovers over the text. + *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

+ * + * @param itemJSON A string representing the JSON-serialized NBT data tag of an {@link ItemStack}. + * @return This builder instance. + */ + public FancyMessage itemTooltip(final String itemJSON) { + onHover("show_item", new JsonString(itemJSON)); // Seems a bit hacky, considering we have a JSON object as a parameter + return this; + } - /** - * Set the behavior of the current editing component to display information about a statistic parameter with an entity type when the client - * hovers over the text. - *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

- * @param which The statistic to display. - * @param entity The sole entity type parameter to the statistic. - * @return This builder instance. - * @exception IllegalArgumentException If the statistic requires a parameter which was not supplied, or was supplied a parameter that was not - * required. - */ - public FancyMessage statisticTooltip(Statistic which, EntityType entity) { - Type type = which.getType(); - if (type == Type.UNTYPED) { - throw new IllegalArgumentException("That statistic needs no additional parameter!"); - } - if (type != Type.ENTITY) { - throw new IllegalArgumentException("Wrong parameter type for that statistic - needs " + type + "!"); - } - try { - Object statistic = Reflection.getMethod(Reflection.getOBCClass("CraftStatistic"), "getEntityStatistic", Statistic.class, EntityType.class) - .invoke(null, which, entity); - return achievementTooltip((String) Reflection.getField(Reflection.getNMSClass("Statistic"), "name").get(statistic)); - } catch (IllegalAccessException e) { - Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e); - return this; - } catch (IllegalArgumentException e) { - Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e); - return this; - } catch (InvocationTargetException e) { - Bukkit.getLogger().log(Level.WARNING, "A error has occurred during invoking of method.", e); - return this; - } - } + /** + * Set the behavior of the current editing component to display information about an item when the client hovers over the text. + *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

+ * + * @param itemStack The stack for which to display information. + * @return This builder instance. + */ + public FancyMessage itemTooltip(final ItemStack itemStack) { + try { + Object nmsItem = com.plotsquared.bukkit.chat.Reflection.getMethod(com.plotsquared.bukkit.chat.Reflection.getOBCClass("inventory.CraftItemStack"), "asNMSCopy", ItemStack.class).invoke(null, itemStack); + return itemTooltip(com.plotsquared.bukkit.chat.Reflection.getMethod(com.plotsquared.bukkit.chat.Reflection.getNMSClass("ItemStack"), "save", com.plotsquared.bukkit.chat.Reflection.getNMSClass("NBTTagCompound")).invoke(nmsItem, com.plotsquared.bukkit.chat.Reflection.getNMSClass("NBTTagCompound").newInstance()).toString()); + } catch (Exception e) { + e.printStackTrace(); + return this; + } + } - /** - * Set the behavior of the current editing component to display information about an item when the client hovers over the text. - *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

- * @param itemJSON A string representing the JSON-serialized NBT data tag of an {@link ItemStack}. - * @return This builder instance. - */ - public FancyMessage itemTooltip(String itemJSON) { - onHover("show_item", new JsonString(itemJSON)); // Seems a bit hacky, considering we have a JSON object as a parameter - return this; - } + /** + * Set the behavior of the current editing component to display raw text when the client hovers over the text. + *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

+ * + * @param text The text, which supports newlines, which will be displayed to the client upon hovering. + * @return This builder instance. + */ + public FancyMessage tooltip(final String text) { + onHover("show_text", new JsonString(text)); + return this; + } - /** - * Set the behavior of the current editing component to display information about an item when the client hovers over the text. - *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

- * @param itemStack The stack for which to display information. - * @return This builder instance. - */ - public FancyMessage itemTooltip(ItemStack itemStack) { - try { - Object nmsItem = - Reflection.getMethod(Reflection.getOBCClass("inventory.CraftItemStack"), "asNMSCopy", ItemStack.class).invoke(null, itemStack); - return itemTooltip(Reflection.getMethod(Reflection.getNMSClass("ItemStack"), "save", Reflection.getNMSClass("NBTTagCompound")) - .invoke(nmsItem, Reflection.getNMSClass("NBTTagCompound").newInstance()).toString()); - } catch (IllegalAccessException | IllegalArgumentException | InstantiationException | InvocationTargetException e) { - e.printStackTrace(); - return this; - } - } + /** + * Set the behavior of the current editing component to display raw text when the client hovers over the text. + *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

+ * + * @param lines The lines of text which will be displayed to the client upon hovering. The iteration order of this object will be the order in which the lines of the tooltip are created. + * @return This builder instance. + */ + public FancyMessage tooltip(final Iterable lines) { + tooltip(com.plotsquared.bukkit.chat.ArrayWrapper.toArray(lines, String.class)); + return this; + } - /** - * Set the behavior of the current editing component to display raw text when the client hovers over the text. - *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

- * @param text The text, which supports newlines, which will be displayed to the client upon hovering. - * @return This builder instance. - */ - public FancyMessage tooltip(String text) { - onHover("show_text", new JsonString(text)); - return this; - } + /** + * Set the behavior of the current editing component to display raw text when the client hovers over the text. + *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

+ * + * @param lines The lines of text which will be displayed to the client upon hovering. + * @return This builder instance. + */ + public FancyMessage tooltip(final String... lines) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < lines.length; i++) { + builder.append(lines[i]); + if (i != lines.length - 1) { + builder.append('\n'); + } + } + tooltip(builder.toString()); + return this; + } - /** - * Set the behavior of the current editing component to display raw text when the client hovers over the text. - *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

- * @param lines The lines of text which will be displayed to the client upon hovering. The iteration order of this object will be the order in - * which the lines of the tooltip are created. - * @return This builder instance. - */ - public FancyMessage tooltip(Iterable lines) { - tooltip(ArrayWrapper.toArray(lines, String.class)); - return this; - } - - /* + /** + * Set the behavior of the current editing component to display formatted text when the client hovers over the text. + *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

+ * + * @param text The formatted text which will be displayed to the client upon hovering. + * @return This builder instance. + */ + public FancyMessage formattedTooltip(FancyMessage text) { + for (MessagePart component : text.messageParts) { + if (component.clickActionData != null && component.clickActionName != null) { + throw new IllegalArgumentException("The tooltip text cannot have click data."); + } else if (component.hoverActionData != null && component.hoverActionName != null) { + throw new IllegalArgumentException("The tooltip text cannot have a tooltip."); + } + } + onHover("show_text", text); + return this; + } - /** - * If the text is a translatable key, and it has replaceable values, this function can be used to set the replacements that will be used in the - * message. - * @param replacements The replacements, in order, that will be used in the language-specific message. - * @return This builder instance. - *//* ------------ - public FancyMessage translationReplacements(final Iterable replacements){ - for(CharSequence str : replacements){ - latest().translationReplacements.add(new JsonString(str)); - } + /** + * Set the behavior of the current editing component to display the specified lines of formatted text when the client hovers over the text. + *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

+ * + * @param lines The lines of formatted text which will be displayed to the client upon hovering. + * @return This builder instance. + */ + public FancyMessage formattedTooltip(FancyMessage... lines) { + if (lines.length < 1) { + onHover(null, null); // Clear tooltip + return this; + } - return this; - } + FancyMessage result = new FancyMessage(); + result.messageParts.clear(); // Remove the one existing text component that exists by default, which destabilizes the object - */ + for (int i = 0; i < lines.length; i++) { + try { + for (MessagePart component : lines[i]) { + if (component.clickActionData != null && component.clickActionName != null) { + throw new IllegalArgumentException("The tooltip text cannot have click data."); + } else if (component.hoverActionData != null && component.hoverActionName != null) { + throw new IllegalArgumentException("The tooltip text cannot have a tooltip."); + } + if (component.hasText()) { + result.messageParts.add(component.clone()); + } + } + if (i != lines.length - 1) { + result.messageParts.add(new MessagePart(rawText("\n"))); + } + } catch (CloneNotSupportedException e) { + Bukkit.getLogger().log(Level.WARNING, "Failed to clone object", e); + return this; + } + } + return formattedTooltip(result.messageParts.isEmpty() ? null : result); // Throws NPE if size is 0, intended + } - /** - * Set the behavior of the current editing component to display raw text when the client hovers over the text. - *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

- * @param lines The lines of text which will be displayed to the client upon hovering. - * @return This builder instance. - */ - public FancyMessage tooltip(String... lines) { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < lines.length; i++) { - builder.append(lines[i]); - if (i != lines.length - 1) { - builder.append('\n'); - } - } - tooltip(builder.toString()); - return this; - } + /** + * Set the behavior of the current editing component to display the specified lines of formatted text when the client hovers over the text. + *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

+ * + * @param lines The lines of text which will be displayed to the client upon hovering. The iteration order of this object will be the order in which the lines of the tooltip are created. + * @return This builder instance. + */ + public FancyMessage formattedTooltip(final Iterable lines) { + return formattedTooltip(com.plotsquared.bukkit.chat.ArrayWrapper.toArray(lines, FancyMessage.class)); + } - /** - * Set the behavior of the current editing component to display formatted text when the client hovers over the text. - *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

- * @param text The formatted text which will be displayed to the client upon hovering. - * @return This builder instance. - */ - public FancyMessage formattedTooltip(FancyMessage text) { - for (MessagePart component : text.messageParts) { - if (component.clickActionData != null && component.clickActionName != null) { - throw new IllegalArgumentException("The tooltip text cannot have click data."); - } else if (component.hoverActionData != null && component.hoverActionName != null) { - throw new IllegalArgumentException("The tooltip text cannot have a tooltip."); - } - } - onHover("show_text", text); - return this; - } + /** + * If the text is a translatable key, and it has replaceable values, this function can be used to set the replacements that will be used in the message. + * + * @param replacements The replacements, in order, that will be used in the language-specific message. + * @return This builder instance. + */ + public FancyMessage translationReplacements(final String... replacements) { + for (String str : replacements) { + latest().translationReplacements.add(new JsonString(str)); + } + dirty = true; - /** - * Set the behavior of the current editing component to display the specified lines of formatted text when the client hovers over the text. - *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

- * @param lines The lines of formatted text which will be displayed to the client upon hovering. - * @return This builder instance. - */ - public FancyMessage formattedTooltip(FancyMessage... lines) { - if (lines.length < 1) { - onHover(null, null); // Clear tooltip - return this; - } + return this; + } + /* - FancyMessage result = new FancyMessage(); - result.messageParts.clear(); // Remove the one existing text component that exists by default, which destabilizes the object + /** + * If the text is a translatable key, and it has replaceable values, this function can be used to set the replacements that will be used in the message. + * @param replacements The replacements, in order, that will be used in the language-specific message. + * @return This builder instance. + */ /* ------------ + public FancyMessage translationReplacements(final Iterable replacements){ + for(CharSequence str : replacements){ + latest().translationReplacements.add(new JsonString(str)); + } - for (int i = 0; i < lines.length; i++) { - try { - for (MessagePart component : lines[i]) { - if (component.clickActionData != null && component.clickActionName != null) { - throw new IllegalArgumentException("The tooltip text cannot have click data."); - } else if (component.hoverActionData != null && component.hoverActionName != null) { - throw new IllegalArgumentException("The tooltip text cannot have a tooltip."); - } - if (component.hasText()) { - result.messageParts.add(component.clone()); - } - } - if (i != lines.length - 1) { - result.messageParts.add(new MessagePart(rawText("\n"))); - } - } catch (CloneNotSupportedException e) { - Bukkit.getLogger().log(Level.WARNING, "Failed to clone object", e); - return this; - } - } - return formattedTooltip(result.messageParts.isEmpty() ? null : result); // Throws NPE if size is 0, intended - } + return this; + } - /** - * Set the behavior of the current editing component to display the specified lines of formatted text when the client hovers over the text. - *

Tooltips do not inherit display characteristics, such as color and styles, from the message component on which they are applied.

- * @param lines The lines of text which will be displayed to the client upon hovering. The iteration order of this object will be the order in - * which the lines of the tooltip are created. - * @return This builder instance. - */ - public FancyMessage formattedTooltip(Iterable lines) { - return formattedTooltip(ArrayWrapper.toArray(lines, FancyMessage.class)); - } + */ - /** - * If the text is a translatable key, and it has replaceable values, this function can be used to set the replacements that will be used in the - * message. - * @param replacements The replacements, in order, that will be used in the language-specific message. - * @return This builder instance. - */ - public FancyMessage translationReplacements(String... replacements) { - for (String str : replacements) { - latest().translationReplacements.add(new JsonString(str)); - } - this.dirty = true; + /** + * If the text is a translatable key, and it has replaceable values, this function can be used to set the replacements that will be used in the message. + * + * @param replacements The replacements, in order, that will be used in the language-specific message. + * @return This builder instance. + */ + public FancyMessage translationReplacements(final FancyMessage... replacements) { + for (FancyMessage str : replacements) { + latest().translationReplacements.add(str); + } - return this; - } + dirty = true; - /** - * If the text is a translatable key, and it has replaceable values, this function can be used to set the replacements that will be used in the - * message. - * @param replacements The replacements, in order, that will be used in the language-specific message. - * @return This builder instance. - */ - public FancyMessage translationReplacements(FancyMessage... replacements) { - Collections.addAll(latest().translationReplacements, replacements); + return this; + } - this.dirty = true; + /** + * If the text is a translatable key, and it has replaceable values, this function can be used to set the replacements that will be used in the message. + * + * @param replacements The replacements, in order, that will be used in the language-specific message. + * @return This builder instance. + */ + public FancyMessage translationReplacements(final Iterable replacements) { + return translationReplacements(com.plotsquared.bukkit.chat.ArrayWrapper.toArray(replacements, FancyMessage.class)); + } - return this; - } + /** + * Terminate construction of the current editing component, and begin construction of a new message component. + * After a successful call to this method, all setter methods will refer to a new message component, created as a result of the call to this method. + * + * @param text The text which will populate the new message component. + * @return This builder instance. + */ + public FancyMessage then(final String text) { + return then(rawText(text)); + } - /** - * If the text is a translatable key, and it has replaceable values, this function can be used to set the replacements that will be used in the - * message. - * @param replacements The replacements, in order, that will be used in the language-specific message. - * @return This builder instance. - */ - public FancyMessage translationReplacements(Iterable replacements) { - return translationReplacements(ArrayWrapper.toArray(replacements, FancyMessage.class)); - } + /** + * Terminate construction of the current editing component, and begin construction of a new message component. + * After a successful call to this method, all setter methods will refer to a new message component, created as a result of the call to this method. + * + * @param text The text which will populate the new message component. + * @return This builder instance. + */ + public FancyMessage then(final com.plotsquared.bukkit.chat.TextualComponent text) { + if (!latest().hasText()) { + throw new IllegalStateException("previous message part has no text"); + } + messageParts.add(new MessagePart(text)); + dirty = true; + return this; + } - /** - * Terminate construction of the current editing component, and begin construction of a new message component. - * After a successful call to this method, all setter methods will refer to a new message component, created as a result of the call to this - * method. - * @param text The text which will populate the new message component. - * @return This builder instance. - */ - public FancyMessage then(String text) { - return then(rawText(text)); - } + /** + * Terminate construction of the current editing component, and begin construction of a new message component. + * After a successful call to this method, all setter methods will refer to a new message component, created as a result of the call to this method. + * + * @return This builder instance. + */ + public FancyMessage then() { + if (!latest().hasText()) { + throw new IllegalStateException("previous message part has no text"); + } + messageParts.add(new MessagePart()); + dirty = true; + return this; + } - /** - * Terminate construction of the current editing component, and begin construction of a new message component. - * After a successful call to this method, all setter methods will refer to a new message component, created as a result of the call to this - * method. - * @param text The text which will populate the new message component. - * @return This builder instance. - */ - public FancyMessage then(TextualComponent text) { - if (!latest().hasText()) { - throw new IllegalStateException("previous message part has no text"); - } - this.messageParts.add(new MessagePart(text)); - this.dirty = true; - return this; - } + @Override + public void writeJson(JsonWriter writer) throws IOException { + if (messageParts.size() == 1) { + latest().writeJson(writer); + } else { + writer.beginObject().name("text").value("").name("extra").beginArray(); + for (final MessagePart part : this) { + part.writeJson(writer); + } + writer.endArray().endObject(); + } + } - /** - * Terminate construction of the current editing component, and begin construction of a new message component. - * After a successful call to this method, all setter methods will refer to a new message component, created as a result of the call to this - * method. - * @return This builder instance. - */ - public FancyMessage then() { - if (!latest().hasText()) { - throw new IllegalStateException("previous message part has no text"); - } - this.messageParts.add(new MessagePart()); - this.dirty = true; - return this; - } + /** + * Serialize this fancy message, converting it into syntactically-valid JSON using a {@link JsonWriter}. + * This JSON should be compatible with vanilla formatter commands such as {@code /tellraw}. + * + * @return The JSON string representing this object. + */ + public String toJSONString() { + if (!dirty && jsonString != null) { + return jsonString; + } + StringWriter string = new StringWriter(); + JsonWriter json = new JsonWriter(string); + try { + writeJson(json); + json.close(); + } catch (IOException e) { + throw new RuntimeException("invalid message"); + } + jsonString = string.toString(); + dirty = false; + return jsonString; + } - @Override - public void writeJson(JsonWriter writer) throws IOException { - if (this.messageParts.size() == 1) { - latest().writeJson(writer); - } else { - writer.beginObject().name("text").value("").name("extra").beginArray(); - for (MessagePart part : this) { - part.writeJson(writer); - } - writer.endArray().endObject(); - } - } + /** + * Sends this message to a player. The player will receive the fully-fledged formatted display of this message. + * + * @param player The player who will receive the message. + */ + public void send(Player player) { + send(player, toJSONString()); + } - /** - * Serialize this fancy message, converting it into syntactically-valid JSON using a {@link JsonWriter}. - * This JSON should be compatible with vanilla formatter commands such as {@code /tellraw}. - * @return The JSON string representing this object. - */ - public String toJSONString() { - if (!this.dirty && this.jsonString != null) { - return this.jsonString; - } - StringWriter string = new StringWriter(); - JsonWriter json = new JsonWriter(string); - try { - writeJson(json); - json.close(); - } catch (IOException ignored) { - throw new RuntimeException("invalid message"); - } - this.jsonString = string.toString(); - this.dirty = false; - return this.jsonString; - } + private void send(CommandSender sender, String jsonString) { + if (!(sender instanceof Player)) { + sender.sendMessage(toOldMessageFormat()); + return; + } + Player player = (Player) sender; + try { + Object handle = com.plotsquared.bukkit.chat.Reflection.getHandle(player); + Object connection = com.plotsquared.bukkit.chat.Reflection.getField(handle.getClass(), "playerConnection").get(handle); + com.plotsquared.bukkit.chat.Reflection.getMethod(connection.getClass(), "sendPacket", com.plotsquared.bukkit.chat.Reflection.getNMSClass("Packet")).invoke(connection, createChatPacket(jsonString)); + } catch (IllegalArgumentException e) { + Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e); + } catch (IllegalAccessException e) { + Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e); + } catch (InstantiationException e) { + Bukkit.getLogger().log(Level.WARNING, "Underlying class is abstract.", e); + } catch (InvocationTargetException e) { + Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e); + } catch (NoSuchMethodException e) { + Bukkit.getLogger().log(Level.WARNING, "Could not find method.", e); + } catch (ClassNotFoundException e) { + Bukkit.getLogger().log(Level.WARNING, "Could not find class.", e); + } + } - /** - * Sends this message to a player. The player will receive the fully-fledged formatted display of this message. - * @param player The player who will receive the message. - */ - public void send(Player player) { - send(player, toJSONString()); - } + // The ChatSerializer's instance of Gson + private static Object nmsChatSerializerGsonInstance; + private static Method fromJsonMethod; - private void send(CommandSender sender, String jsonString) { - if (!(sender instanceof Player)) { - sender.sendMessage(toOldMessageFormat()); - return; - } - Player player = (Player) sender; - try { - Object handle = Reflection.getHandle(player); - Object connection = Reflection.getField(handle.getClass(), "playerConnection").get(handle); - Reflection.getMethod(connection.getClass(), "sendPacket", Reflection.getNMSClass("Packet")) - .invoke(connection, createChatPacket(jsonString)); - } catch (IllegalArgumentException e) { - Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e); - } catch (IllegalAccessException e) { - Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e); - } catch (InstantiationException e) { - Bukkit.getLogger().log(Level.WARNING, "Underlying class is abstract.", e); - } catch (InvocationTargetException e) { - Bukkit.getLogger().log(Level.WARNING, "A error has occurred during invoking of method.", e); - } catch (NoSuchMethodException e) { - Bukkit.getLogger().log(Level.WARNING, "Could not find method.", e); - } catch (ClassNotFoundException e) { - Bukkit.getLogger().log(Level.WARNING, "Could not find class.", e); - } - } + private Object createChatPacket(String json) throws IllegalArgumentException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException { + if (nmsChatSerializerGsonInstance == null) { + // Find the field and its value, completely bypassing obfuscation + Class chatSerializerClazz; - private Object createChatPacket(String json) - throws IllegalArgumentException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, - ClassNotFoundException { - if (nmsChatSerializerGsonInstance == null) { - // Find the field and its value, completely bypassing obfuscation - Class chatSerializerClazz; + // Get the three parts of the version string (major version is currently unused) + // vX_Y_RZ + // X = major + // Y = minor + // Z = revision + final String version = com.plotsquared.bukkit.chat.Reflection.getVersion(); + String[] split = version.substring(1, version.length() - 1).split("_"); // Remove trailing dot + //int majorVersion = Integer.parseInt(split[0]); + int minorVersion = Integer.parseInt(split[1]); + int revisionVersion = Integer.parseInt(split[2].substring(1)); // Substring to ignore R - String version = Reflection.getVersion(); - double majorVersion = Double.parseDouble(version.replace('_', '.').substring(1, 4)); - int lesserVersion = Integer.parseInt(version.substring(6, 7)); + if (minorVersion < 8 || (minorVersion == 8 && revisionVersion == 1)) { + chatSerializerClazz = com.plotsquared.bukkit.chat.Reflection.getNMSClass("ChatSerializer"); + } else { + chatSerializerClazz = com.plotsquared.bukkit.chat.Reflection.getNMSClass("IChatBaseComponent$ChatSerializer"); + } - if (majorVersion < 1.8 || majorVersion == 1.8 && lesserVersion == 1) { - chatSerializerClazz = Reflection.getNMSClass("ChatSerializer"); - } else { - chatSerializerClazz = Reflection.getNMSClass("IChatBaseComponent$ChatSerializer"); - } + if (chatSerializerClazz == null) { + throw new ClassNotFoundException("Can't find the ChatSerializer class"); + } - if (chatSerializerClazz == null) { - throw new ClassNotFoundException("Can't find the ChatSerializer class"); - } + for (Field declaredField : chatSerializerClazz.getDeclaredFields()) { + if (Modifier.isFinal(declaredField.getModifiers()) && Modifier.isStatic(declaredField.getModifiers()) && declaredField.getType().getName().endsWith("Gson")) { + // We've found our field + declaredField.setAccessible(true); + nmsChatSerializerGsonInstance = declaredField.get(null); + fromJsonMethod = nmsChatSerializerGsonInstance.getClass().getMethod("fromJson", String.class, Class.class); + break; + } + } + } - for (Field declaredField : chatSerializerClazz.getDeclaredFields()) { - if (Modifier.isFinal(declaredField.getModifiers()) && Modifier.isStatic(declaredField.getModifiers()) && declaredField.getType() - .getName().endsWith("Gson")) { - // We've found our field - declaredField.setAccessible(true); - nmsChatSerializerGsonInstance = declaredField.get(null); - fromJsonMethod = nmsChatSerializerGsonInstance.getClass().getMethod("fromJson", String.class, Class.class); - break; - } - } - } + // Since the method is so simple, and all the obfuscated methods have the same name, it's easier to reimplement 'IChatBaseComponent a(String)' than to reflectively call it + // Of course, the implementation may change, but fuzzy matches might break with signature changes + Object serializedChatComponent = fromJsonMethod.invoke(nmsChatSerializerGsonInstance, json, com.plotsquared.bukkit.chat.Reflection.getNMSClass("IChatBaseComponent")); - /* - Since the method is so simple, and all the obfuscated methods have the same name, it's easier to reimplement 'IChatBaseComponent a(String)' - than to reflectively call it - Of course, the implementation may change, but fuzzy matches might break with signature changes - */ - Object serializedChatComponent = fromJsonMethod.invoke(nmsChatSerializerGsonInstance, json, Reflection.getNMSClass("IChatBaseComponent")); + return nmsPacketPlayOutChatConstructor.newInstance(serializedChatComponent); + } - return nmsPacketPlayOutChatConstructor.newInstance(serializedChatComponent); - } + /** + * Sends this message to a command sender. + * If the sender is a player, they will receive the fully-fledged formatted display of this message. + * Otherwise, they will receive a version of this message with less formatting. + * + * @param sender The command sender who will receive the message. + * @see #toOldMessageFormat() + */ + public void send(CommandSender sender) { + send(sender, toJSONString()); + } - /** - * Sends this message to a command sender. - * If the sender is a player, they will receive the fully-fledged formatted display of this message. - * Otherwise, they will receive a version of this message with less formatting. - * @param sender The command sender who will receive the message. - * @see #toOldMessageFormat() - */ - public void send(CommandSender sender) { - send(sender, toJSONString()); - } + /** + * Sends this message to multiple command senders. + * + * @param senders The command senders who will receive the message. + * @see #send(CommandSender) + */ + public void send(final Iterable senders) { + String string = toJSONString(); + for (final CommandSender sender : senders) { + send(sender, string); + } + } - /** - * Sends this message to multiple command senders. - * @param senders The command senders who will receive the message. - * @see #send(CommandSender) - */ - public void send(Iterable senders) { - String string = toJSONString(); - for (CommandSender sender : senders) { - send(sender, string); - } - } + /** + * Convert this message to a human-readable string with limited formatting. + * This method is used to send this message to clients without JSON formatting support. + *

+ * Serialization of this message by using this message will include (in this order for each message part): + *

    + *
  1. The color of each message part.
  2. + *
  3. The applicable stylizations for each message part.
  4. + *
  5. The core text of the message part.
  6. + *
+ * The primary omissions are tooltips and clickable actions. Consequently, this method should be used only as a last resort. + *

+ *

+ * Color and formatting can be removed from the returned string by using {@link ChatColor#stripColor(String)}.

+ * + * @return A human-readable string representing limited formatting in addition to the core text of this message. + */ + public String toOldMessageFormat() { + StringBuilder result = new StringBuilder(); + for (MessagePart part : this) { + result.append(part.color == null ? "" : part.color); + for (ChatColor formatSpecifier : part.styles) { + result.append(formatSpecifier); + } + result.append(part.text); + } + return result.toString(); + } - /** - * Convert this message to a human-readable string with limited formatting. - * This method is used to send this message to clients without JSON formatting support. - *

- * Serialization of this message by using this message will include (in this order for each message part): - *

    - *
  1. The color of each message part.
  2. - *
  3. The applicable stylization for each message part.
  4. - *
  5. The core text of the message part.
  6. - *
- * The primary omissions are tooltips and clickable actions. Consequently, this method should be used only as a last resort. - *

- *

- * Color and formatting can be removed from the returned string by using {@link ChatColor#stripColor(String)}.

- * @return A human-readable string representing limited formatting in addition to the core text of this message. - */ - public String toOldMessageFormat() { - StringBuilder result = new StringBuilder(); - for (MessagePart part : this) { - result.append(part.color == null ? "" : part.color); - for (ChatColor formatSpecifier : part.styles) { - result.append(formatSpecifier); - } - result.append(part.text); - } - return result.toString(); - } + private MessagePart latest() { + return messageParts.get(messageParts.size() - 1); + } - private MessagePart latest() { - return this.messageParts.get(this.messageParts.size() - 1); - } + private void onClick(final String name, final String data) { + final MessagePart latest = latest(); + latest.clickActionName = name; + latest.clickActionData = data; + dirty = true; + } - private void onClick(String name, String data) { - MessagePart latest = latest(); - latest.clickActionName = name; - latest.clickActionData = data; - this.dirty = true; - } + private void onHover(final String name, final JsonRepresentedObject data) { + final MessagePart latest = latest(); + latest.hoverActionName = name; + latest.hoverActionData = data; + dirty = true; + } - private void onHover(String name, JsonRepresentedObject data) { - MessagePart latest = latest(); - latest.hoverActionName = name; - latest.hoverActionData = data; - this.dirty = true; - } + // Doc copied from interface + public Map serialize() { + HashMap map = new HashMap(); + map.put("messageParts", messageParts); +// map.put("JSON", toJSONString()); + return map; + } - // Doc copied from interface - @Override - public Map serialize() { - HashMap map = new HashMap<>(); - map.put("messageParts", this.messageParts); - // map.put("JSON", toJSONString()); - return map; - } + /** + * Deserializes a JSON-represented message from a mapping of key-value pairs. + * This is called by the Bukkit serialization API. + * It is not intended for direct public API consumption. + * + * @param serialized The key-value mapping which represents a fancy message. + */ + @SuppressWarnings("unchecked") + public static FancyMessage deserialize(Map serialized) { + FancyMessage msg = new FancyMessage(); + msg.messageParts = (List) serialized.get("messageParts"); + msg.jsonString = serialized.containsKey("JSON") ? serialized.get("JSON").toString() : null; + msg.dirty = !serialized.containsKey("JSON"); + return msg; + } + + /** + * Internally called method. Not for API consumption. + */ + public Iterator iterator() { + return messageParts.iterator(); + } + + private static JsonParser _stringParser = new JsonParser(); + + /** + * Deserializes a fancy message from its JSON representation. This JSON representation is of the format of + * that returned by {@link #toJSONString()}, and is compatible with vanilla inputs. + * + * @param json The JSON string which represents a fancy message. + * @return A {@code FancyMessage} representing the parameterized JSON message. + */ + public static FancyMessage deserialize(String json) { + JsonObject serialized = _stringParser.parse(json).getAsJsonObject(); + JsonArray extra = serialized.getAsJsonArray("extra"); // Get the extra component + FancyMessage returnVal = new FancyMessage(); + returnVal.messageParts.clear(); + for (JsonElement mPrt : extra) { + MessagePart component = new MessagePart(); + JsonObject messagePart = mPrt.getAsJsonObject(); + for (Map.Entry entry : messagePart.entrySet()) { + // Deserialize text + if (com.plotsquared.bukkit.chat.TextualComponent.isTextKey(entry.getKey())) { + // The map mimics the YAML serialization, which has a "key" field and one or more "value" fields + Map serializedMapForm = new HashMap(); // Must be object due to Bukkit serializer API compliance + serializedMapForm.put("key", entry.getKey()); + if (entry.getValue().isJsonPrimitive()) { + // Assume string + serializedMapForm.put("value", entry.getValue().getAsString()); + } else { + // Composite object, but we assume each element is a string + for (Map.Entry compositeNestedElement : entry.getValue().getAsJsonObject().entrySet()) { + serializedMapForm.put("value." + compositeNestedElement.getKey(), compositeNestedElement.getValue().getAsString()); + } + } + component.text = com.plotsquared.bukkit.chat.TextualComponent.deserialize(serializedMapForm); + } else if (MessagePart.stylesToNames.inverse().containsKey(entry.getKey())) { + if (entry.getValue().getAsBoolean()) { + component.styles.add(MessagePart.stylesToNames.inverse().get(entry.getKey())); + } + } else if (entry.getKey().equals("color")) { + component.color = ChatColor.valueOf(entry.getValue().getAsString().toUpperCase()); + } else if (entry.getKey().equals("clickEvent")) { + JsonObject object = entry.getValue().getAsJsonObject(); + component.clickActionName = object.get("action").getAsString(); + component.clickActionData = object.get("value").getAsString(); + } else if (entry.getKey().equals("hoverEvent")) { + JsonObject object = entry.getValue().getAsJsonObject(); + component.hoverActionName = object.get("action").getAsString(); + if (object.get("value").isJsonPrimitive()) { + // Assume string + component.hoverActionData = new JsonString(object.get("value").getAsString()); + } else { + // Assume composite type + // The only composite type we currently store is another FancyMessage + // Therefore, recursion time! + component.hoverActionData = deserialize(object.get("value").toString() /* This should properly serialize the JSON object as a JSON string */); + } + } else if (entry.getKey().equals("insertion")) { + component.insertionData = entry.getValue().getAsString(); + } else if (entry.getKey().equals("with")) { + for (JsonElement object : entry.getValue().getAsJsonArray()) { + if (object.isJsonPrimitive()) { + component.translationReplacements.add(new JsonString(object.getAsString())); + } else { + // Only composite type stored in this array is - again - FancyMessages + // Recurse within this function to parse this as a translation replacement + component.translationReplacements.add(deserialize(object.toString())); + } + } + } + } + returnVal.messageParts.add(component); + } + return returnVal; + } - /** - * Internally called method. Not for API consumption. - */ - @Override - public Iterator iterator() { - return this.messageParts.iterator(); - } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/chat/JsonRepresentedObject.java b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/JsonRepresentedObject.java index 0b3d7a98c..e76593f29 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/chat/JsonRepresentedObject.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/JsonRepresentedObject.java @@ -9,11 +9,11 @@ import java.io.IOException; */ 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. - */ - void writeJson(JsonWriter writer) throws IOException; - + /** + * 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/Bukkit/src/main/java/com/plotsquared/bukkit/chat/JsonString.java b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/JsonString.java index 823511bb1..e62d98364 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/chat/JsonString.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/JsonString.java @@ -1,12 +1,12 @@ package com.plotsquared.bukkit.chat; -import com.google.gson.stream.JsonWriter; -import com.intellectualcrafters.configuration.serialization.ConfigurationSerializable; - import java.io.IOException; import java.util.HashMap; import java.util.Map; +import com.google.gson.stream.JsonWriter; +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. @@ -14,34 +14,34 @@ import java.util.Map; */ final class JsonString implements JsonRepresentedObject, ConfigurationSerializable { - private final String value; + private String _value; - public JsonString(CharSequence value) { - this.value = value == null ? null : value.toString(); - } + public JsonString(CharSequence value) { + _value = value == null ? null : value.toString(); + } - public static JsonString deserialize(Map map) { - return new JsonString(map.get("stringValue").toString()); - } + @Override + public void writeJson(JsonWriter writer) throws IOException { + writer.value(getValue()); + } - @Override - public void writeJson(JsonWriter writer) throws IOException { - writer.value(getValue()); - } + public String getValue() { + return _value; + } - public String getValue() { - return this.value; - } + public Map serialize() { + HashMap theSingleValue = new HashMap(); + theSingleValue.put("stringValue", _value); + return theSingleValue; + } - @Override - public Map serialize() { - HashMap theSingleValue = new HashMap<>(); - theSingleValue.put("stringValue", this.value); - return theSingleValue; - } + public static JsonString deserialize(Map map) { + return new JsonString(map.get("stringValue").toString()); + } + + @Override + public String toString() { + return _value; + } - @Override - public String toString() { - return this.value; - } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/chat/MessagePart.java b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/MessagePart.java index 6c5d17e16..dcdc71ee3 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/chat/MessagePart.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/MessagePart.java @@ -3,10 +3,10 @@ package com.plotsquared.bukkit.chat; import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableBiMap; import com.google.gson.stream.JsonWriter; -import com.intellectualcrafters.configuration.serialization.ConfigurationSerializable; -import com.intellectualcrafters.configuration.serialization.ConfigurationSerialization; import org.bukkit.Bukkit; import org.bukkit.ChatColor; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.ConfigurationSerialization; import java.io.IOException; import java.util.ArrayList; @@ -19,133 +19,137 @@ import java.util.logging.Level; */ final class MessagePart implements JsonRepresentedObject, ConfigurationSerializable, Cloneable { - static final BiMap stylesToNames; + ChatColor color = ChatColor.WHITE; + ArrayList styles = new ArrayList(); + String clickActionName = null, clickActionData = null, hoverActionName = null; + JsonRepresentedObject hoverActionData = null; + TextualComponent text = null; + String insertionData = null; + ArrayList translationReplacements = new ArrayList(); - static { - ImmutableBiMap.Builder builder = ImmutableBiMap.builder(); - for (ChatColor style : ChatColor.values()) { - if (!style.isFormat()) { - continue; - } + MessagePart(final TextualComponent text) { + this.text = text; + } - String styleName; - switch (style) { - case MAGIC: - styleName = "obfuscated"; - break; - case UNDERLINE: - styleName = "underlined"; - break; - default: - styleName = style.name().toLowerCase(); - break; - } + MessagePart() { + this.text = null; + } - builder.put(style, styleName); - } - stylesToNames = builder.build(); - } + boolean hasText() { + return text != null; + } - static { - ConfigurationSerialization.registerClass(MessagePart.class); - } + @Override + @SuppressWarnings("unchecked") + public MessagePart clone() throws CloneNotSupportedException { + MessagePart obj = (MessagePart) super.clone(); + obj.styles = (ArrayList) styles.clone(); + if (hoverActionData instanceof JsonString) { + obj.hoverActionData = new JsonString(((JsonString) hoverActionData).getValue()); + } else if (hoverActionData instanceof FancyMessage) { + obj.hoverActionData = ((FancyMessage) hoverActionData).clone(); + } + obj.translationReplacements = (ArrayList) translationReplacements.clone(); + return obj; - ChatColor color = ChatColor.WHITE; - ArrayList styles = new ArrayList<>(); - String clickActionName = null, clickActionData = null, hoverActionName = null; - JsonRepresentedObject hoverActionData = null; - TextualComponent text = null; - String insertionData = null; - ArrayList translationReplacements = new ArrayList<>(); + } - MessagePart(TextualComponent text) { - this.text = text; - } + static final BiMap stylesToNames; - MessagePart() { - this.text = null; - } + static { + ImmutableBiMap.Builder builder = ImmutableBiMap.builder(); + for (final ChatColor style : ChatColor.values()) { + if (!style.isFormat()) { + continue; + } - @SuppressWarnings("unchecked") - public static MessagePart deserialize(Map serialized) { - MessagePart part = new MessagePart((TextualComponent) serialized.get("text")); - part.styles = (ArrayList) serialized.get("styles"); - part.color = ChatColor.getByChar(serialized.get("color").toString()); - part.hoverActionName = (String) serialized.get("hoverActionName"); - part.hoverActionData = (JsonRepresentedObject) serialized.get("hoverActionData"); - part.clickActionName = (String) serialized.get("clickActionName"); - part.clickActionData = (String) serialized.get("clickActionData"); - part.insertionData = (String) serialized.get("insertion"); - part.translationReplacements = (ArrayList) serialized.get("translationReplacements"); - return part; - } + String styleName; + switch (style) { + case MAGIC: + styleName = "obfuscated"; + break; + case UNDERLINE: + styleName = "underlined"; + break; + default: + styleName = style.name().toLowerCase(); + break; + } - boolean hasText() { - return this.text != null; - } + builder.put(style, styleName); + } + stylesToNames = builder.build(); + } - @Override - @SuppressWarnings("unchecked") - public MessagePart clone() throws CloneNotSupportedException { - MessagePart obj = (MessagePart) super.clone(); - obj.styles = (ArrayList) this.styles.clone(); - if (this.hoverActionData instanceof JsonString) { - obj.hoverActionData = new JsonString(((JsonString) this.hoverActionData).getValue()); - } else if (this.hoverActionData instanceof FancyMessage) { - obj.hoverActionData = ((FancyMessage) this.hoverActionData).clone(); - } - obj.translationReplacements = (ArrayList) this.translationReplacements.clone(); - return obj; + public void writeJson(JsonWriter json) { + try { + json.beginObject(); + text.writeJson(json); + json.name("color").value(color.name().toLowerCase()); + for (final ChatColor style : styles) { + json.name(stylesToNames.get(style)).value(true); + } + if (clickActionName != null && clickActionData != null) { + json.name("clickEvent") + .beginObject() + .name("action").value(clickActionName) + .name("value").value(clickActionData) + .endObject(); + } + if (hoverActionName != null && hoverActionData != null) { + json.name("hoverEvent") + .beginObject() + .name("action").value(hoverActionName) + .name("value"); + hoverActionData.writeJson(json); + json.endObject(); + } + if (insertionData != null) { + json.name("insertion").value(insertionData); + } + if (translationReplacements.size() > 0 && text != null && TextualComponent.isTranslatableText(text)) { + json.name("with").beginArray(); + for (JsonRepresentedObject obj : translationReplacements) { + obj.writeJson(json); + } + json.endArray(); + } + json.endObject(); + } catch (IOException e) { + Bukkit.getLogger().log(Level.WARNING, "A problem occured during writing of JSON string", e); + } + } - } + public Map serialize() { + HashMap map = new HashMap(); + map.put("text", text); + map.put("styles", styles); + map.put("color", color.getChar()); + map.put("hoverActionName", hoverActionName); + map.put("hoverActionData", hoverActionData); + map.put("clickActionName", clickActionName); + map.put("clickActionData", clickActionData); + map.put("insertion", insertionData); + map.put("translationReplacements", translationReplacements); + return map; + } - @Override - public void writeJson(JsonWriter json) { - try { - json.beginObject(); - this.text.writeJson(json); - json.name("color").value(this.color.name().toLowerCase()); - for (ChatColor style : this.styles) { - json.name(stylesToNames.get(style)).value(true); - } - if ((this.clickActionName != null) && (this.clickActionData != null)) { - json.name("clickEvent").beginObject().name("action").value(this.clickActionName).name("value").value(this.clickActionData) - .endObject(); - } - if ((this.hoverActionName != null) && (this.hoverActionData != null)) { - json.name("hoverEvent").beginObject().name("action").value(this.hoverActionName).name("value"); - this.hoverActionData.writeJson(json); - json.endObject(); - } - if (this.insertionData != null) { - json.name("insertion").value(this.insertionData); - } - if (!this.translationReplacements.isEmpty() && (this.text != null) && TextualComponent.isTranslatableText(this.text)) { - json.name("with").beginArray(); - for (JsonRepresentedObject obj : this.translationReplacements) { - obj.writeJson(json); - } - json.endArray(); - } - json.endObject(); - } catch (IOException e) { - Bukkit.getLogger().log(Level.WARNING, "A problem occurred during writing of JSON string", e); - } - } + @SuppressWarnings("unchecked") + public static MessagePart deserialize(Map serialized) { + MessagePart part = new MessagePart((TextualComponent) serialized.get("text")); + part.styles = (ArrayList) serialized.get("styles"); + part.color = ChatColor.getByChar(serialized.get("color").toString()); + part.hoverActionName = (String) serialized.get("hoverActionName"); + part.hoverActionData = (JsonRepresentedObject) serialized.get("hoverActionData"); + part.clickActionName = (String) serialized.get("clickActionName"); + part.clickActionData = (String) serialized.get("clickActionData"); + part.insertionData = (String) serialized.get("insertion"); + part.translationReplacements = (ArrayList) serialized.get("translationReplacements"); + return part; + } - @Override - public Map serialize() { - HashMap map = new HashMap<>(); - map.put("text", this.text); - map.put("styles", this.styles); - map.put("color", this.color.getChar()); - map.put("hoverActionName", this.hoverActionName); - map.put("hoverActionData", this.hoverActionData); - map.put("clickActionName", this.clickActionName); - map.put("clickActionData", this.clickActionData); - map.put("insertion", this.insertionData); - map.put("translationReplacements", this.translationReplacements); - return map; - } + static { + ConfigurationSerialization.registerClass(MessagePart.class); + } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/chat/Reflection.java b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/Reflection.java index a67b886e6..636c20091 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/chat/Reflection.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/Reflection.java @@ -1,9 +1,8 @@ package com.plotsquared.bukkit.chat; -import com.intellectualcrafters.plot.PS; +import org.bukkit.Bukkit; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; @@ -15,191 +14,205 @@ import java.util.Map; */ 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; - /** - * 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 static synchronized String getVersion() { - return PS.get().IMP.getNMSPackage(); - } + private Reflection() { } - /** - * 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 static synchronized Class getNMSClass(String className) { - if (_loadedNMSClasses.containsKey(className)) { - return _loadedNMSClasses.get(className); - } + /** + * 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) { + if (Bukkit.getServer() == null) { + // The server hasn't started, static initializer call? + return null; + } + String name = Bukkit.getServer().getClass().getPackage().getName(); + _versionString = name.substring(name.lastIndexOf('.') + 1) + "."; + } - String fullName = "net.minecraft.server." + getVersion() + '.' + className; - Class clazz; - try { - clazz = Class.forName(fullName); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - _loadedNMSClasses.put(className, null); - return null; - } - _loadedNMSClasses.put(className, clazz); - return clazz; - } + return _versionString; + } - /** - * 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 static synchronized Class getOBCClass(String className) { - if (_loadedOBCClasses.containsKey(className)) { - return _loadedOBCClasses.get(className); - } + /** + * Stores loaded classes from the {@code net.minecraft.server} package. + */ + private static final Map> _loadedNMSClasses = new HashMap>(); - String fullName = "org.bukkit.craftbukkit." + getVersion() + '.' + className; - Class clazz; - try { - clazz = Class.forName(fullName); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - _loadedOBCClasses.put(className, null); - return null; - } - _loadedOBCClasses.put(className, clazz); - return clazz; - } + /** + * Stores loaded classes from the {@code org.bukkit.craftbukkit} package (and subpackages). + */ + private static final Map> _loadedOBCClasses = new HashMap>(); - /** - * 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 static synchronized Object getHandle(Object obj) { - try { - return getMethod(obj.getClass(), "getHandle").invoke(obj); - } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException e) { - e.printStackTrace(); - return null; - } - } + /** + * 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); + } - /** - * 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 static synchronized 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; - } - } + String fullName = "net.minecraft.server." + getVersion() + className; + Class clazz = null; + try { + clazz = Class.forName(fullName); + } catch (Exception e) { + e.printStackTrace(); + _loadedNMSClasses.put(className, null); + return null; + } + _loadedNMSClasses.put(className, clazz); + return clazz; + } - /** - * 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 static synchronized Method getMethod(Class clazz, String name, Class... args) { - if (!_loadedMethods.containsKey(clazz)) { - _loadedMethods.put(clazz, new HashMap>, Method>>()); - } + /** + * 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); + } - Map>, Method>> loadedMethodNames = _loadedMethods.get(clazz); - if (!loadedMethodNames.containsKey(name)) { - loadedMethodNames.put(name, new HashMap>, Method>()); - } + String fullName = "org.bukkit.craftbukkit." + getVersion() + className; + Class clazz = null; + try { + clazz = Class.forName(fullName); + } catch (Exception e) { + e.printStackTrace(); + _loadedOBCClasses.put(className, null); + return null; + } + _loadedOBCClasses.put(className, clazz); + return clazz; + } - Map>, Method> loadedSignatures = loadedMethodNames.get(name); - ArrayWrapper> wrappedArg = new ArrayWrapper<>(args); - if (loadedSignatures.containsKey(wrappedArg)) { - return loadedSignatures.get(wrappedArg); - } + /** + * 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) { + try { + return getMethod(obj.getClass(), "getHandle").invoke(obj); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } - 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; - } + private static final Map, Map> _loadedFields = new HashMap, Map>(); + + /** + * 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 (Exception e) { + // Error loading + e.printStackTrace(); + // Cache field as not existing + loaded.put(name, null); + return null; + } + } + + /** + * 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, Map>, Method>>>(); + + /** + * 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>, Method>>()); + } + + Map>, Method>> loadedMethodNames = _loadedMethods.get(clazz); + if (!loadedMethodNames.containsKey(name)) { + loadedMethodNames.put(name, new HashMap>, Method>()); + } + + 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/Bukkit/src/main/java/com/plotsquared/bukkit/chat/TextualComponent.java b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/TextualComponent.java index a3651d15a..002aea95b 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/chat/TextualComponent.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/chat/TextualComponent.java @@ -3,8 +3,8 @@ package com.plotsquared.bukkit.chat; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.gson.stream.JsonWriter; -import com.intellectualcrafters.configuration.serialization.ConfigurationSerializable; -import com.intellectualcrafters.configuration.serialization.ConfigurationSerialization; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.ConfigurationSerialization; import java.io.IOException; import java.util.HashMap; @@ -18,285 +18,280 @@ import java.util.Map; */ public abstract class TextualComponent implements Cloneable { - static { - ConfigurationSerialization.registerClass(TextualComponent.ArbitraryTextTypeComponent.class); - ConfigurationSerialization.registerClass(TextualComponent.ComplexTextTypeComponent.class); - } + static { + ConfigurationSerialization.registerClass(TextualComponent.ArbitraryTextTypeComponent.class); + ConfigurationSerialization.registerClass(TextualComponent.ComplexTextTypeComponent.class); + } - static TextualComponent deserialize(Map map) { - if (map.containsKey("key") && (map.size() == 2) && map.containsKey("value")) { - // Arbitrary text component - return ArbitraryTextTypeComponent.deserialize(map); - } else if ((map.size() >= 2) && map.containsKey("key") && !map.containsKey("value") /* It contains keys that START WITH value */) { - // Complex JSON object - return ComplexTextTypeComponent.deserialize(map); - } + @Override + public String toString() { + return getReadableString(); + } - return null; - } + /** + * @return The JSON key used to represent text components of this type. + */ + public abstract String getKey(); - static boolean isTextKey(String key) { - return key.equals("translate") || key.equals("text") || key.equals("score") || key.equals("selector"); - } + /** + * @return A readable String + */ + public abstract String getReadableString(); - static boolean isTranslatableText(TextualComponent component) { - return (component instanceof ComplexTextTypeComponent) && component.getKey().equals("translate"); - } + /** + * Clones a textual component instance. + * The returned object should not reference this textual component instance, but should maintain the same key and value. + */ + @Override + public abstract TextualComponent clone() throws CloneNotSupportedException; - /** - * Create a textual component representing a string literal. - * This is the default type of textual component when a single string literal is given to a method. - * @param textValue The text which will be represented. - * @return The text component representing the specified literal text. - */ - public static TextualComponent rawText(String textValue) { - return new ArbitraryTextTypeComponent("text", textValue); - } + /** + * Writes the text data represented by this textual component to the specified JSON writer object. + * A new object within the writer is not started. + * + * @param writer The object to which to write the JSON data. + * @throws IOException If an error occurs while writing to the stream. + */ + public abstract void writeJson(JsonWriter writer) throws IOException; - /** - * Create a textual component representing a localized string. - * The client will see this text component as their localized version of the specified string key, which can be overridden by a - * resource pack. - *

- * If the specified translation key is not present on the client resource pack, the translation key will be displayed as a string literal to - * the client. - *

- * @param translateKey The string key which maps to localized text. - * @return The text component representing the specified localized text. - */ - public static TextualComponent localizedText(String translateKey) { - return new ArbitraryTextTypeComponent("translate", translateKey); - } + static TextualComponent deserialize(Map map) { + if (map.containsKey("key") && map.size() == 2 && map.containsKey("value")) { + // Arbitrary text component + return ArbitraryTextTypeComponent.deserialize(map); + } else if (map.size() >= 2 && map.containsKey("key") && !map.containsKey("value") /* It contains keys that START WITH value */) { + // Complex JSON object + return ComplexTextTypeComponent.deserialize(map); + } - private static void throwUnsupportedSnapshot() { - throw new UnsupportedOperationException("This feature is only supported in snapshot releases."); - } + return null; + } - /** - * Create a textual component representing a scoreboard value. - * The client will see their own score for the specified objective as the text represented by this component. - *

- * This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients. - *

- * @param scoreboardObjective The name of the objective for which to display the score. - * @return The text component representing the specified scoreboard score (for the viewing player), or {@code null} if an error occurs during - * JSON serialization. - */ - public static TextualComponent objectiveScore(String scoreboardObjective) { - return objectiveScore("*", scoreboardObjective); - } + static boolean isTextKey(String key) { + return key.equals("translate") || key.equals("text") || key.equals("score") || key.equals("selector"); + } - /** - * Create a textual component representing a scoreboard value. - * The client will see the score of the specified player for the specified objective as the text represented by this component. - *

- * This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients. - *

- * @param playerName The name of the player whos score will be shown. If this string represents the single-character sequence "*", the viewing - * player's score will be displayed. - * Standard minecraft selectors (@a, @p, etc) are not supported. - * @param scoreboardObjective The name of the objective for which to display the score. - * @return The text component representing the specified scoreboard score for the specified player, or {@code null} if an error occurs during - * JSON serialization. - */ - public static TextualComponent objectiveScore(String playerName, String scoreboardObjective) { - throwUnsupportedSnapshot(); // Remove this line when the feature is released to non-snapshot versions, in addition to updating ALL THE - // OVERLOADS documentation accordingly + static boolean isTranslatableText(TextualComponent component) { + return component instanceof ComplexTextTypeComponent && ((ComplexTextTypeComponent) component).getKey().equals("translate"); + } - return new ComplexTextTypeComponent("score", - ImmutableMap.builder().put("name", playerName).put("objective", scoreboardObjective).build()); - } + /** + * Internal class used to represent all types of text components. + * Exception validating done is on keys and values. + */ + private static final class ArbitraryTextTypeComponent extends TextualComponent implements ConfigurationSerializable { - /** - * Create a textual component representing a player name, retrievable by using a standard minecraft selector. - * The client will see the players or entities captured by the specified selector as the text represented by this component. - *

- * This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients. - *

- * @param selector The minecraft player or entity selector which will capture the entities whose string representations will be displayed in - * the place of this text component. - * @return The text component representing the name of the entities captured by the selector. - */ - public static TextualComponent selector(String selector) { - throwUnsupportedSnapshot(); // Remove this line when the feature is released to non-snapshot versions, in addition to updating ALL THE - // OVERLOADS documentation accordingly + public ArbitraryTextTypeComponent(String key, String value) { + setKey(key); + setValue(value); + } - return new ArbitraryTextTypeComponent("selector", selector); - } + @Override + public String getKey() { + return _key; + } - @Override - public String toString() { - return getReadableString(); - } + public void setKey(String key) { + Preconditions.checkArgument(key != null && !key.isEmpty(), "The key must be specified."); + _key = key; + } - /** - * @return The JSON key used to represent text components of this type. - */ - public abstract String getKey(); + public String getValue() { + return _value; + } - /** - * @return A readable String - */ - public abstract String getReadableString(); + public void setValue(String value) { + Preconditions.checkArgument(value != null, "The value must be specified."); + _value = value; + } - /** - * Clones a textual component instance. - * The returned object should not reference this textual component instance, but should maintain the same key and value. - */ - @Override - public abstract TextualComponent clone() throws CloneNotSupportedException; + private String _key; + private String _value; - /** - * Writes the text data represented by this textual component to the specified JSON writer object. - * A new object within the writer is not started. - * @param writer The object to which to write the JSON data. - * @throws IOException If an error occurs while writing to the stream. - */ - public abstract void writeJson(JsonWriter writer) throws IOException; + @Override + public TextualComponent clone() throws CloneNotSupportedException { + // Since this is a private and final class, we can just reinstantiate this class instead of casting super.clone + return new ArbitraryTextTypeComponent(getKey(), getValue()); + } - /** - * Internal class used to represent all types of text components. - * Exception validating done is on keys and values. - */ - private static final class ArbitraryTextTypeComponent extends TextualComponent implements ConfigurationSerializable { + @Override + public void writeJson(JsonWriter writer) throws IOException { + writer.name(getKey()).value(getValue()); + } - private String _key; - private String _value; + @SuppressWarnings("serial") + public Map serialize() { + return new HashMap() {{ + put("key", getKey()); + put("value", getValue()); + }}; + } - public ArbitraryTextTypeComponent(String key, String value) { - setKey(key); - setValue(value); - } + public static ArbitraryTextTypeComponent deserialize(Map map) { + return new ArbitraryTextTypeComponent(map.get("key").toString(), map.get("value").toString()); + } - public static ArbitraryTextTypeComponent deserialize(Map map) { - return new ArbitraryTextTypeComponent(map.get("key").toString(), map.get("value").toString()); - } + @Override + public String getReadableString() { + return getValue(); + } + } - @Override - public String getKey() { - return this._key; - } + /** + * Internal class used to represent a text component with a nested JSON value. + * Exception validating done is on keys and values. + */ + private static final class ComplexTextTypeComponent extends TextualComponent implements ConfigurationSerializable { - public void setKey(String key) { - Preconditions.checkArgument((key != null) && !key.isEmpty(), "The key must be specified."); - this._key = key; - } + public ComplexTextTypeComponent(String key, Map values) { + setKey(key); + setValue(values); + } - public String getValue() { - return this._value; - } + @Override + public String getKey() { + return _key; + } - public void setValue(String value) { - Preconditions.checkArgument(value != null, "The value must be specified."); - this._value = value; - } + public void setKey(String key) { + Preconditions.checkArgument(key != null && !key.isEmpty(), "The key must be specified."); + _key = key; + } - @Override - public TextualComponent clone() { - // Since this is a private and final class, we can just reinstantiate this class instead of casting super.clone - return new ArbitraryTextTypeComponent(getKey(), getValue()); - } + public Map getValue() { + return _value; + } - @Override - public void writeJson(JsonWriter writer) throws IOException { - writer.name(getKey()).value(getValue()); - } + public void setValue(Map value) { + Preconditions.checkArgument(value != null, "The value must be specified."); + _value = value; + } - @Override - @SuppressWarnings("serial") - public Map serialize() { - return new HashMap() { - { - put("key", getKey()); - put("value", getValue()); - } - }; - } + private String _key; + private Map _value; - @Override - public String getReadableString() { - return getValue(); - } - } + @Override + public TextualComponent clone() throws CloneNotSupportedException { + // Since this is a private and final class, we can just reinstantiate this class instead of casting super.clone + return new ComplexTextTypeComponent(getKey(), getValue()); + } - /** - * Internal class used to represent a text component with a nested JSON value. - * Exception validating done is on keys and values. - */ - private static final class ComplexTextTypeComponent extends TextualComponent implements ConfigurationSerializable { + @Override + public void writeJson(JsonWriter writer) throws IOException { + writer.name(getKey()); + writer.beginObject(); + for (Map.Entry jsonPair : _value.entrySet()) { + writer.name(jsonPair.getKey()).value(jsonPair.getValue()); + } + writer.endObject(); + } - private String _key; - private Map _value; + @SuppressWarnings("serial") + public Map serialize() { + return new java.util.HashMap() {{ + put("key", getKey()); + for (Map.Entry valEntry : getValue().entrySet()) { + put("value." + valEntry.getKey(), valEntry.getValue()); + } + }}; + } - public ComplexTextTypeComponent(String key, Map values) { - setKey(key); - setValue(values); - } + public static ComplexTextTypeComponent deserialize(Map map) { + String key = null; + Map value = new HashMap(); + for (Map.Entry valEntry : map.entrySet()) { + if (valEntry.getKey().equals("key")) { + key = (String) valEntry.getValue(); + } else if (valEntry.getKey().startsWith("value.")) { + value.put(((String) valEntry.getKey()).substring(6) /* Strips out the value prefix */, valEntry.getValue().toString()); + } + } + return new ComplexTextTypeComponent(key, value); + } - public static ComplexTextTypeComponent deserialize(Map map) { - String key = null; - Map value = new HashMap<>(); - for (Map.Entry valEntry : map.entrySet()) { - if (valEntry.getKey().equals("key")) { - key = (String) valEntry.getValue(); - } else if (valEntry.getKey().startsWith("value.")) { - value.put(valEntry.getKey().substring(6) /* Strips out the value prefix */, valEntry.getValue().toString()); - } - } - return new ComplexTextTypeComponent(key, value); - } + @Override + public String getReadableString() { + return getKey(); + } + } - @Override - public String getKey() { - return this._key; - } + /** + * Create a textual component representing a string literal. + * This is the default type of textual component when a single string literal is given to a method. + * + * @param textValue The text which will be represented. + * @return The text component representing the specified literal text. + */ + public static TextualComponent rawText(String textValue) { + return new ArbitraryTextTypeComponent("text", textValue); + } - public void setKey(String key) { - Preconditions.checkArgument((key != null) && !key.isEmpty(), "The key must be specified."); - this._key = key; - } - public Map getValue() { - return this._value; - } + /** + * Create a textual component representing a localized string. + * The client will see this text component as their localized version of the specified string key, which can be overridden by a resource pack. + *

+ * If the specified translation key is not present on the client resource pack, the translation key will be displayed as a string literal to the client. + *

+ * + * @param translateKey The string key which maps to localized text. + * @return The text component representing the specified localized text. + */ + public static TextualComponent localizedText(String translateKey) { + return new ArbitraryTextTypeComponent("translate", translateKey); + } - public void setValue(Map value) { - Preconditions.checkArgument(value != null, "The value must be specified."); - this._value = value; - } + private static void throwUnsupportedSnapshot() { + throw new UnsupportedOperationException("This feature is only supported in snapshot releases."); + } - @Override - public TextualComponent clone() { - // Since this is a private and final class, we can just reinstantiate this class instead of casting super.clone - return new ComplexTextTypeComponent(getKey(), getValue()); - } + /** + * Create a textual component representing a scoreboard value. + * The client will see their own score for the specified objective as the text represented by this component. + *

+ * This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients. + *

+ * + * @param scoreboardObjective The name of the objective for which to display the score. + * @return The text component representing the specified scoreboard score (for the viewing player), or {@code null} if an error occurs during JSON serialization. + */ + public static TextualComponent objectiveScore(String scoreboardObjective) { + return objectiveScore("*", scoreboardObjective); + } - @Override - public void writeJson(JsonWriter writer) throws IOException { - writer.name(getKey()); - writer.beginObject(); - for (Map.Entry jsonPair : this._value.entrySet()) { - writer.name(jsonPair.getKey()).value(jsonPair.getValue()); - } - writer.endObject(); - } + /** + * Create a textual component representing a scoreboard value. + * The client will see the score of the specified player for the specified objective as the text represented by this component. + *

+ * This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients. + *

+ * + * @param playerName The name of the player whos score will be shown. If this string represents the single-character sequence "*", the viewing player's score will be displayed. + * Standard minecraft selectors (@a, @p, etc) are not supported. + * @param scoreboardObjective The name of the objective for which to display the score. + * @return The text component representing the specified scoreboard score for the specified player, or {@code null} if an error occurs during JSON serialization. + */ + public static TextualComponent objectiveScore(String playerName, String scoreboardObjective) { + throwUnsupportedSnapshot(); // Remove this line when the feature is released to non-snapshot versions, in addition to updating ALL THE OVERLOADS documentation accordingly - @Override - @SuppressWarnings("serial") - public Map serialize() { - return new HashMap() { - { - put("key", getKey()); - for (Map.Entry valEntry : getValue().entrySet()) { - put("value." + valEntry.getKey(), valEntry.getValue()); - } - } - }; - } + return new ComplexTextTypeComponent("score", ImmutableMap.builder() + .put("name", playerName) + .put("objective", scoreboardObjective) + .build()); + } - @Override - public String getReadableString() { - return getKey(); - } - } + /** + * Create a textual component representing a player name, retrievable by using a standard minecraft selector. + * The client will see the players or entities captured by the specified selector as the text represented by this component. + *

+ * This method is currently guaranteed to throw an {@code UnsupportedOperationException} as it is only supported on snapshot clients. + *

+ * + * @param selector The minecraft player or entity selector which will capture the entities whose string representations will be displayed in the place of this text component. + * @return The text component representing the name of the entities captured by the selector. + */ + public static TextualComponent selector(String selector) { + throwUnsupportedSnapshot(); // Remove this line when the feature is released to non-snapshot versions, in addition to updating ALL THE OVERLOADS documentation accordingly + + return new ArbitraryTextTypeComponent("selector", selector); + } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/generator/BukkitPlotGenerator.java b/Bukkit/src/main/java/com/plotsquared/bukkit/generator/BukkitPlotGenerator.java index bde42e9fc..3abd55d98 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/generator/BukkitPlotGenerator.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/generator/BukkitPlotGenerator.java @@ -5,6 +5,8 @@ import com.intellectualcrafters.plot.generator.GeneratorWrapper; import com.intellectualcrafters.plot.generator.HybridGen; import com.intellectualcrafters.plot.generator.IndependentPlotGenerator; import com.intellectualcrafters.plot.object.ChunkLoc; +import com.intellectualcrafters.plot.object.ChunkWrapper; +import com.intellectualcrafters.plot.object.Location; import com.intellectualcrafters.plot.object.PlotArea; import com.intellectualcrafters.plot.object.PlotId; import com.intellectualcrafters.plot.object.PlotManager; @@ -12,25 +14,26 @@ import com.intellectualcrafters.plot.object.PseudoRandom; import com.intellectualcrafters.plot.object.SetupObject; import com.intellectualcrafters.plot.util.ChunkManager; import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue; +import com.intellectualcrafters.plot.util.MathMan; +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; +import com.intellectualcrafters.plot.util.block.ScopedLocalBlockQueue; import com.plotsquared.bukkit.util.BukkitUtil; import com.plotsquared.bukkit.util.block.GenChunk; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Random; +import java.util.Set; import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Random; -import java.util.Set; - public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrapper { - private final PlotChunk chunkSetter; + private final GenChunk chunkSetter; private final PseudoRandom random = new PseudoRandom(); private final IndependentPlotGenerator plotGenerator; private final List populators = new ArrayList<>(); @@ -46,14 +49,20 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap this.plotGenerator = generator; this.platformGenerator = this; this.populators.add(new BlockPopulator() { + + private LocalBlockQueue queue; + @Override public void populate(World world, Random r, Chunk c) { + if (queue == null) { + queue = GlobalBlockQueue.IMP.getNewQueue(world.getName(), false); + } ChunkLoc loc = new ChunkLoc(c.getX(), c.getZ()); byte[][] resultData; if (!BukkitPlotGenerator.this.dataMap.containsKey(loc)) { - GenChunk result = (GenChunk) BukkitPlotGenerator.this.chunkSetter; + GenChunk result = BukkitPlotGenerator.this.chunkSetter; // Set the chunk location - result.setChunkWrapper(SetQueue.IMP.new ChunkWrapper(world.getName(), loc.x, loc.z)); + result.setChunk(c); // Set the result data result.result = new short[16][]; result.result_data = new byte[16][]; @@ -81,10 +90,10 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap } BukkitPlotGenerator.this.random.state = c.getX() << 16 | c.getZ() & 0xFFFF; PlotArea area = PS.get().getPlotArea(world.getName(), null); - SetQueue.ChunkWrapper wrap = SetQueue.IMP.new ChunkWrapper(area.worldname, c.getX(), c.getZ()); - PlotChunk chunk = SetQueue.IMP.queue.getChunk(wrap); + ChunkWrapper wrap = new ChunkWrapper(area.worldname, c.getX(), c.getZ()); + ScopedLocalBlockQueue chunk = queue.getForChunk(wrap.x, wrap.z); if (BukkitPlotGenerator.this.plotGenerator.populateChunk(chunk, area, BukkitPlotGenerator.this.random)) { - chunk.addToQueue(); + queue.flush(); } } }); @@ -123,13 +132,16 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap } @Override - public void generateChunk(final PlotChunk result, PlotArea settings, PseudoRandom random) { + public void generateChunk(final ScopedLocalBlockQueue result, PlotArea settings, PseudoRandom random) { World w = BukkitUtil.getWorld(world); - Random r = new Random(result.getChunkWrapper().hashCode()); + Location min = result.getMin(); + int cx = min.getX() >> 4; + int cz = min.getZ() >> 4; + Random r = new Random(MathMan.pair((short) cx, (short) cz)); BiomeGrid grid = new BiomeGrid() { @Override public void setBiome(int x, int z, Biome biome) { - result.setBiome(x, z, biome.ordinal()); + result.setBiome(x, z, biome.name()); } @Override @@ -139,13 +151,13 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap }; try { // ChunkData will spill a bit - ChunkData data = cg.generateChunkData(w, r, result.getX(), result.getZ(), grid); + ChunkData data = cg.generateChunkData(w, r, cx, cz, grid); if (data != null) { return; } } catch (Throwable ignored) {} // Populator spillage - short[][] tmp = cg.generateExtBlockSections(w, r, result.getX(), result.getZ(), grid); + short[][] tmp = cg.generateExtBlockSections(w, r, cx, cz, grid); if (tmp != null) { for (int i = 0; i < tmp.length; i++) { short[] section = tmp[i]; @@ -170,11 +182,11 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap } } for (BlockPopulator populator : cg.getDefaultPopulators(w)) { - populator.populate(w, r, (Chunk) result.getChunk()); + populator.populate(w, r, w.getChunkAt(cx, cz)); } } }; - this.chunkSetter = new GenChunk(null, SetQueue.IMP.new ChunkWrapper(world, 0, 0)); + this.chunkSetter = new GenChunk(null, new ChunkWrapper(world, 0, 0)); if (cg != null) { this.populators.addAll(cg.getDefaultPopulators(BukkitUtil.getWorld(world))); } @@ -245,7 +257,7 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap public ChunkData generateChunkData(World world, Random random, int cx, int cz, BiomeGrid grid) { GenChunk result = (GenChunk) this.chunkSetter; // Set the chunk location - result.setChunkWrapper(SetQueue.IMP.new ChunkWrapper(world.getName(), cx, cz)); + result.setChunk(new ChunkWrapper(world.getName(), cx, cz)); // Set the result data result.cd = createChunkData(world); result.grid = grid; @@ -266,7 +278,7 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap return result.cd; } - public void generate(World world, int cx, int cz, PlotChunk result) { + public void generate(World world, int cx, int cz, ScopedLocalBlockQueue result) { // Load if improperly loaded if (!this.loaded) { String name = world.getName(); @@ -293,7 +305,7 @@ public class BukkitPlotGenerator extends ChunkGenerator implements GeneratorWrap public short[][] generateExtBlockSections(World world, Random r, int cx, int cz, BiomeGrid grid) { GenChunk result = (GenChunk) this.chunkSetter; // Set the chunk location - result.setChunkWrapper(SetQueue.IMP.new ChunkWrapper(world.getName(), cx, cz)); + result.setChunk(new ChunkWrapper(world.getName(), cx, cz)); // Set the result data result.result = new short[16][]; result.result_data = new byte[16][]; diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitChatManager.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitChatManager.java index 7673dcf21..3e7c226f5 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitChatManager.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitChatManager.java @@ -1,6 +1,7 @@ package com.plotsquared.bukkit.util; import com.intellectualcrafters.plot.config.C; +import com.intellectualcrafters.plot.config.Settings; import com.intellectualcrafters.plot.object.ConsolePlayer; import com.intellectualcrafters.plot.object.PlotMessage; import com.intellectualcrafters.plot.object.PlotPlayer; @@ -45,7 +46,7 @@ public class BukkitChatManager extends ChatManager { @Override public void send(PlotMessage plotMessage, PlotPlayer player) { - if (player instanceof ConsolePlayer) { + if (player instanceof ConsolePlayer || !Settings.Chat.INTERACTIVE) { player.sendMessage(plotMessage.$(this).toOldMessageFormat()); } else { plotMessage.$(this).send(((BukkitPlayer) player).player); diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitChunkManager.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitChunkManager.java index d4e71f680..d20b0174a 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitChunkManager.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitChunkManager.java @@ -9,17 +9,22 @@ import com.intellectualcrafters.plot.object.Plot; import com.intellectualcrafters.plot.object.PlotArea; import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.object.PlotLoc; -import com.intellectualcrafters.plot.object.PlotPlayer; import com.intellectualcrafters.plot.object.RegionWrapper; import com.intellectualcrafters.plot.object.RunnableVal; import com.intellectualcrafters.plot.util.ChunkManager; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue; -import com.intellectualcrafters.plot.util.StringMan; import com.intellectualcrafters.plot.util.TaskManager; -import com.intellectualcrafters.plot.util.UUIDHandler; -import com.intellectualcrafters.plot.util.WorldUtil; +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; +import com.intellectualcrafters.plot.util.block.ScopedLocalBlockQueue; import com.plotsquared.bukkit.object.entity.EntityWrapper; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.DyeColor; @@ -52,16 +57,6 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - public class BukkitChunkManager extends ChunkManager { public static boolean isIn(RegionWrapper region, int x, int z) { @@ -91,6 +86,9 @@ public class BukkitChunkManager extends ChunkManager { String worldName1 = world1.getName(); String worldName2 = world2.getName(); + LocalBlockQueue queue1 = GlobalBlockQueue.IMP.getNewQueue(worldName1, false); + LocalBlockQueue queue2 = GlobalBlockQueue.IMP.getNewQueue(worldName2, false); + for (int x = Math.max(r1.minX, sx); x <= Math.min(r1.maxX, sx + 15); x++) { for (int z = Math.max(r1.minZ, sz); z <= Math.min(r1.maxZ, sz + 15); z++) { map.saveBlocks(world1, 256, sx, sz, relX, relZ, false); @@ -105,24 +103,26 @@ public class BukkitChunkManager extends ChunkManager { byte data2 = block2.getData(); if (id1 == 0) { if (id2 != 0) { - SetQueue.IMP.setBlock(worldName1, x, y, z, (short) id2, data2); - SetQueue.IMP.setBlock(worldName2, xx, y, zz, (short) 0, (byte) 0); + queue1.setBlock(x, y, z, (short) id2, data2); + queue2.setBlock(xx, y, zz, (short) 0, (byte) 0); } } else if (id2 == 0) { - SetQueue.IMP.setBlock(worldName1, x, y, z, (short) 0, (byte) 0); - SetQueue.IMP.setBlock(worldName2, xx, y, zz, (short) id1, data1); + queue1.setBlock(x, y, z, (short) 0, (byte) 0); + queue2.setBlock(xx, y, zz, (short) id1, data1); } else if (id1 == id2) { if (data1 != data2) { block1.setData(data2); block2.setData(data1); } } else { - SetQueue.IMP.setBlock(worldName1, x, y, z, (short) id2, data2); - SetQueue.IMP.setBlock(worldName2, xx, y, zz, (short) id1, data1); + queue1.setBlock(x, y, z, (short) id2, data2); + queue2.setBlock(xx, y, zz, (short) id1, data1); } } } } + queue1.enqueue(); + queue2.enqueue(); return map; } @@ -138,22 +138,6 @@ public class BukkitChunkManager extends ChunkManager { return chunks; } - @Override - public void regenerateChunk(String world, ChunkLoc loc) { - World worldObj = Bukkit.getWorld(world); - worldObj.regenerateChunk(loc.x, loc.z); - SetQueue.IMP.queue.sendChunk(world, Collections.singletonList(loc)); - for (Entry entry : UUIDHandler.getPlayers().entrySet()) { - PlotPlayer pp = entry.getValue(); - Location pLoc = pp.getLocation(); - if (!StringMan.isEqual(world, pLoc.getWorld()) || !pLoc.getChunkLoc().equals(loc)) { - continue; - } - pLoc.setY(WorldUtil.IMP.getHighestBlock(world, pLoc.getX(), pLoc.getZ())); - pp.teleport(pLoc); - } - } - @Override public boolean copyRegion(Location pos1, Location pos2, Location newPos, final Runnable whenDone) { final int relX = newPos.getX() - pos1.getX(); @@ -166,6 +150,7 @@ public class BukkitChunkManager extends ChunkManager { final String newWorldName = newWorld.getName(); List chunks = new ArrayList<>(); final ContentMap map = new ContentMap(); + final LocalBlockQueue queue = GlobalBlockQueue.IMP.getNewQueue(newWorldName, false); ChunkManager.chunkTask(pos1, pos2, new RunnableVal() { @Override public void run(int[] value) { @@ -193,11 +178,12 @@ public class BukkitChunkManager extends ChunkManager { for (int y = 0; y < blocks.length; y++) { PlotBlock block = blocks[y]; if (block != null) { - SetQueue.IMP.setBlock(newWorldName, loc.x, y, loc.z, block); + queue.setBlock(loc.x, y, loc.z, block); } } } - SetQueue.IMP.addTask(new Runnable() { + queue.enqueue(); + GlobalBlockQueue.IMP.addTask(new Runnable() { @Override public void run() { map.restoreBlocks(newWorld, 0, 0); @@ -247,12 +233,13 @@ public class BukkitChunkManager extends ChunkManager { if (!chunkObj.load(false)) { continue; } + final LocalBlockQueue queue = GlobalBlockQueue.IMP.getNewQueue(world, false); RegionWrapper currentPlotClear = new RegionWrapper(pos1.getX(), pos2.getX(), pos1.getZ(), pos2.getZ()); if (xxb >= p1x && xxt <= p2x && zzb >= p1z && zzt <= p2z) { AugmentedUtils.bypass(ignoreAugment, new Runnable() { @Override public void run() { - regenerateChunk(world, chunk); + queue.regenChunkSafe(chunk.x, chunk.z); } }); continue; @@ -320,13 +307,12 @@ public class BukkitChunkManager extends ChunkManager { AugmentedUtils.bypass(ignoreAugment, new Runnable() { @Override public void run() { - setChunkInPlotArea(null, new RunnableVal>() { + setChunkInPlotArea(null, new RunnableVal() { @Override - public void run(PlotChunk value) { - int cx = value.getX(); - int cz = value.getZ(); - int bx = cx << 4; - int bz = cz << 4; + public void run(ScopedLocalBlockQueue value) { + Location min = value.getMin(); + int bx = min.getX(); + int bz = min.getZ(); for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { PlotLoc loc = new PlotLoc(bx + x, bz + z); @@ -424,7 +410,7 @@ public class BukkitChunkManager extends ChunkManager { maps.add(swapChunk(world1, world2, chunk1, chunk2, region1, region2)); } } - SetQueue.IMP.addTask(new Runnable() { + GlobalBlockQueue.IMP.addTask(new Runnable() { @Override public void run() { for (ContentMap map : maps) { diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitHybridUtils.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitHybridUtils.java index c336347b1..c3f2dd26c 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitHybridUtils.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitHybridUtils.java @@ -2,7 +2,6 @@ package com.plotsquared.bukkit.util; import com.intellectualcrafters.plot.generator.HybridUtils; import com.intellectualcrafters.plot.object.Location; -import com.intellectualcrafters.plot.util.expiry.PlotAnalysis; import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.object.RegionWrapper; import com.intellectualcrafters.plot.object.RunnableVal; @@ -10,19 +9,20 @@ import com.intellectualcrafters.plot.util.ChunkManager; import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.MathMan; import com.intellectualcrafters.plot.util.TaskManager; +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; +import com.intellectualcrafters.plot.util.expiry.PlotAnalysis; +import java.util.HashSet; +import java.util.Random; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Biome; -import org.bukkit.block.Block; import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator.BiomeGrid; import org.bukkit.material.Directional; import org.bukkit.material.MaterialData; -import java.util.HashSet; -import java.util.Random; - public class BukkitHybridUtils extends HybridUtils { @Override @@ -42,6 +42,7 @@ public class BukkitHybridUtils extends HybridUtils { TaskManager.runTaskAsync(new Runnable() { @Override public void run() { + final LocalBlockQueue queue = GlobalBlockQueue.IMP.getNewQueue(world, false); final World worldObj = Bukkit.getWorld(world); final ChunkGenerator gen = worldObj.getGenerator(); if (gen == null) { @@ -254,10 +255,10 @@ public class BukkitHybridUtils extends HybridUtils { for (int z = minZ; z <= maxZ; z++) { int zz = cbz + z; for (int y = 0; y < 256; y++) { - Block block = worldObj.getBlockAt(xx, y, zz); + PlotBlock block = queue.getBlock(xx, y, zz); int xr = xb + x; int zr = zb + z; - newBlocks[y][xr][zr] = (short) block.getTypeId(); + newBlocks[y][xr][zr] = block.id; } } } @@ -272,50 +273,4 @@ public class BukkitHybridUtils extends HybridUtils { } }); } - - @Override - public int checkModified(String worldName, int x1, int x2, int y1, int y2, int z1, int z2, - PlotBlock[] blocks) { - World world = BukkitUtil.getWorld(worldName); - int count = 0; - for (int y = y1; y <= y2; y++) { - for (int x = x1; x <= x2; x++) { - for (int z = z1; z <= z2; z++) { - Block block = world.getBlockAt(x, y, z); - int id = block.getTypeId(); - boolean same = false; - for (PlotBlock p : blocks) { - if (id == p.id) { - same = true; - break; - } - } - if (!same) { - count++; - } - } - } - } - return count; - } - - @Override - public int get_ey(String worldName, int sx, int ex, int sz, int ez, int sy) { - World world = BukkitUtil.getWorld(worldName); - int maxY = world.getMaxHeight(); - int ey = sy; - for (int x = sx; x <= ex; x++) { - for (int z = sz; z <= ez; z++) { - for (int y = sy; y < maxY; y++) { - if (y > ey) { - Block block = world.getBlockAt(x, y, z); - if (!block.getType().equals(Material.AIR)) { - ey = y; - } - } - } - } - } - return ey; - } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue.java new file mode 100644 index 000000000..4d1086956 --- /dev/null +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue.java @@ -0,0 +1,127 @@ +package com.plotsquared.bukkit.util.block; + +import com.intellectualcrafters.plot.object.PlotBlock; +import com.intellectualcrafters.plot.util.MainUtil; +import com.intellectualcrafters.plot.util.StringMan; +import com.intellectualcrafters.plot.util.block.BasicLocalBlockQueue; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; + +public class BukkitLocalQueue extends BasicLocalBlockQueue { + + public BukkitLocalQueue(String world) { + super(world); + } + + @Override + public LocalChunk getLocalChunk(int x, int z) { + return (LocalChunk) new BasicLocalChunk(this, x, z) { + // Custom stuff? + }; + } + + @Override + public void optimize() { + + } + + @Override + public PlotBlock getBlock(int x, int y, int z) { + World worldObj = Bukkit.getWorld(getWorld()); + Block block = worldObj.getBlockAt(x, y, z); + if (block == null) { + return PlotBlock.get(0, 0); + } + int id = block.getTypeId(); + if (id == 0) { + return PlotBlock.get(0, 0); + } + return PlotBlock.get(id, block.getData()); + } + + @Override + public void refreshChunk(int x, int z) { + World worldObj = Bukkit.getWorld(getWorld()); + worldObj.refreshChunk(x, z); + } + + @Override + public void fixChunkLighting(int x, int z) { + // Do nothing + } + + @Override + public final void regenChunk(int x, int z) { + World worldObj = Bukkit.getWorld(getWorld()); + worldObj.regenerateChunk(x, z); + } + + @Override + public final void setComponents(LocalChunk lc) { + setBlocks(lc); + setBiomes(lc); + } + + public World getBukkitWorld() { + return Bukkit.getWorld(getWorld()); + } + + public Chunk getChunk(int x, int z) { + return getBukkitWorld().getChunkAt(x, z); + } + + public void setBlocks(LocalChunk lc) { + World worldObj = Bukkit.getWorld(getWorld()); + Chunk chunk = worldObj.getChunkAt(lc.getX(), lc.getZ()); + chunk.load(true); + for (int layer = 0; layer < lc.blocks.length; layer++) { + PlotBlock[] blocksLayer = (PlotBlock[]) lc.blocks[layer]; + if (blocksLayer != null) { + for (int j = 0; j < blocksLayer.length; j++) { + PlotBlock block = blocksLayer[j]; + int x = MainUtil.x_loc[layer][j]; + int y = MainUtil.y_loc[layer][j]; + int z = MainUtil.y_loc[layer][j]; + Block existing = chunk.getBlock(x, y, z); + int existingId = existing.getTypeId(); + if (existingId == block.id) { + if (existingId == 0) { + continue; + } + if (existing.getData() == block.data) { + continue; + } + } + existing.setTypeIdAndData(block.id, block.data, false); + } + } + } + } + + public void setBiomes(LocalChunk lc) { + if (lc.biomes != null) { + World worldObj = Bukkit.getWorld(getWorld()); + int bx = lc.getX() << 4; + int bz = lc.getX() << 4; + String last = null; + Biome biome = null; + for (int x = 0; x < lc.biomes.length; x++) { + String[] biomes2 = lc.biomes[x]; + if (biomes2 != null) { + for (int y = 0; y < biomes2.length; y++) { + String biomeStr = biomes2[y]; + if (biomeStr != null) { + if (last == null || !StringMan.isEqual(last, biomeStr)) { + biome = Biome.valueOf(biomeStr.toUpperCase()); + } + worldObj.setBiome(bx, bz, biome); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue_1_7.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue_1_7.java new file mode 100644 index 000000000..05b611e7c --- /dev/null +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue_1_7.java @@ -0,0 +1,130 @@ +package com.plotsquared.bukkit.util.block; + +import com.intellectualcrafters.plot.object.ChunkWrapper; +import com.intellectualcrafters.plot.object.PlotBlock; +import com.intellectualcrafters.plot.util.MainUtil; +import com.intellectualcrafters.plot.util.ReflectionUtils; +import com.intellectualcrafters.plot.util.TaskManager; +import com.plotsquared.bukkit.util.SendChunk; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.World; + + +import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass; + +public class BukkitLocalQueue_1_7 extends BukkitLocalQueue { + + private final ReflectionUtils.RefClass classBlock = getRefClass("{nms}.Block"); + private final ReflectionUtils.RefClass classChunk = getRefClass("{nms}.Chunk"); + private final ReflectionUtils.RefClass classWorld = getRefClass("{nms}.World"); + private final ReflectionUtils.RefClass classCraftWorld = getRefClass("{cb}.CraftWorld"); + private final ReflectionUtils.RefMethod methodGetHandle; + private final ReflectionUtils.RefMethod methodGetChunkAt; + private final ReflectionUtils.RefMethod methodA; + private final ReflectionUtils.RefMethod methodGetById; + private final ReflectionUtils.RefMethod methodInitLighting; + private final SendChunk sendChunk; + + private final HashMap toUpdate = new HashMap<>(); + + public BukkitLocalQueue_1_7(String world) throws NoSuchMethodException, ClassNotFoundException, NoSuchFieldException { + super(world); + this.methodGetHandle = this.classCraftWorld.getMethod("getHandle"); + this.methodGetChunkAt = this.classWorld.getMethod("getChunkAt", int.class, int.class); + this.methodA = this.classChunk.getMethod("a", int.class, int.class, int.class, this.classBlock, int.class); + this.methodGetById = this.classBlock.getMethod("getById", int.class); + this.methodInitLighting = this.classChunk.getMethod("initLighting"); + this.sendChunk = new SendChunk(); + TaskManager.runTaskRepeat(new Runnable() { + @Override + public void run() { + if (BukkitLocalQueue_1_7.this.toUpdate.isEmpty()) { + return; + } + int count = 0; + ArrayList chunks = new ArrayList<>(); + Iterator> i = BukkitLocalQueue_1_7.this.toUpdate.entrySet().iterator(); + while (i.hasNext() && (count < 128)) { + chunks.add(i.next().getValue()); + i.remove(); + count++; + } + if (count == 0) { + return; + } + update(chunks); + } + }, 1); + MainUtil.initCache(); + } + + public void update(Collection chunks) { + if (chunks.isEmpty()) { + return; + } + if (!MainUtil.canSendChunk) { + for (Chunk chunk : chunks) { + chunk.getWorld().refreshChunk(chunk.getX(), chunk.getZ()); + chunk.unload(true, false); + chunk.load(); + } + return; + } + try { + this.sendChunk.sendChunk(chunks); + } catch (Throwable e) { + e.printStackTrace(); + MainUtil.canSendChunk = false; + } + } + + @Override + public void fixChunkLighting(int x, int z) { + Object c = this.methodGetHandle.of(getChunk(x, z)).call(); + this.methodInitLighting.of(c).call(); + } + + @Override + public void setBlocks(LocalChunk lc) { + Chunk chunk = getChunk(lc.getX(), lc.getZ()); + chunk.load(true); + World world = chunk.getWorld(); + ChunkWrapper wrapper = new ChunkWrapper(getWorld(), lc.getX(), lc.getZ()); + if (!this.toUpdate.containsKey(wrapper)) { + this.toUpdate.put(wrapper, chunk); + } + Object w = this.methodGetHandle.of(world).call(); + Object c = this.methodGetChunkAt.of(w).call(lc.getX(), lc.getZ()); + for (int i = 0; i < lc.blocks.length; i++) { + PlotBlock[] result2 = lc.blocks[i]; + if (result2 == null) { + continue; + } + for (int j = 0; j < 4096; j++) { + int x = MainUtil.x_loc[i][j]; + int y = MainUtil.y_loc[i][j]; + int z = MainUtil.z_loc[i][j]; + PlotBlock newBlock = result2[j]; + if (newBlock.id == -1) { + chunk.getBlock(x, y, z).setData(newBlock.data, false); + continue; + } + Object block = this.methodGetById.call(newBlock.id); + this.methodA.of(c).call(x, y, z, block, newBlock.data); + } + } + fixChunkLighting(lc.getX(), lc.getZ()); + } + + @Override + public void refreshChunk(int x, int z) { + update(Arrays.asList(Bukkit.getWorld(getWorld()).getChunkAt(x, z))); + } +} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_8.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue_1_8.java similarity index 74% rename from Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_8.java rename to Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue_1_8.java index b5cc3cc7d..ed0234e38 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_8.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue_1_8.java @@ -1,47 +1,44 @@ package com.plotsquared.bukkit.util.block; -import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass; - -import com.intellectualcrafters.plot.object.ChunkLoc; +import com.intellectualcrafters.plot.object.ChunkWrapper; import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefClass; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefConstructor; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefMethod; -import com.intellectualcrafters.plot.util.SetQueue; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; +import com.intellectualcrafters.plot.util.ReflectionUtils; import com.intellectualcrafters.plot.util.TaskManager; import com.plotsquared.bukkit.util.SendChunk; -import org.bukkit.Chunk; -import org.bukkit.World; -import org.bukkit.block.Biome; -import org.bukkit.block.Block; - import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; -import java.util.Map.Entry; +import java.util.Map; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.block.Block; -public class FastQueue_1_8 extends SlowQueue { - private final RefMethod methodInitLighting; - private final RefClass classBlock = getRefClass("{nms}.Block"); - private final RefClass classBlockPosition = getRefClass("{nms}.BlockPosition"); - private final RefClass classIBlockData = getRefClass("{nms}.IBlockData"); - private final RefClass classChunk = getRefClass("{nms}.Chunk"); - private final RefClass classWorld = getRefClass("{nms}.World"); - private final RefClass classCraftWorld = getRefClass("{cb}.CraftWorld"); +import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass; + +public class BukkitLocalQueue_1_8 extends BukkitLocalQueue { + + private final ReflectionUtils.RefMethod methodInitLighting; + private final ReflectionUtils.RefClass classBlock = getRefClass("{nms}.Block"); + private final ReflectionUtils.RefClass classBlockPosition = getRefClass("{nms}.BlockPosition"); + private final ReflectionUtils.RefClass classIBlockData = getRefClass("{nms}.IBlockData"); + private final ReflectionUtils.RefClass classChunk = getRefClass("{nms}.Chunk"); + private final ReflectionUtils.RefClass classWorld = getRefClass("{nms}.World"); + private final ReflectionUtils.RefClass classCraftWorld = getRefClass("{cb}.CraftWorld"); private final HashMap toUpdate = new HashMap<>(); - private final RefMethod methodGetHandle; - private final RefMethod methodGetChunkAt; - private final RefMethod methodA; - private final RefMethod methodGetByCombinedId; - private final RefConstructor constructorBlockPosition; + private final ReflectionUtils.RefMethod methodGetHandle; + private final ReflectionUtils.RefMethod methodGetChunkAt; + private final ReflectionUtils.RefMethod methodA; + private final ReflectionUtils.RefMethod methodGetByCombinedId; + private final ReflectionUtils.RefConstructor constructorBlockPosition; private final SendChunk sendChunk; - public FastQueue_1_8() throws NoSuchMethodException, ClassNotFoundException, NoSuchFieldException { + public BukkitLocalQueue_1_8(String world) throws NoSuchMethodException, ClassNotFoundException, NoSuchFieldException { + super(world); this.methodInitLighting = this.classChunk.getMethod("initLighting"); this.constructorBlockPosition = this.classBlockPosition.getConstructor(int.class, int.class, int.class); this.methodGetByCombinedId = this.classBlock.getMethod("getByCombinedId", int.class); @@ -52,12 +49,12 @@ public class FastQueue_1_8 extends SlowQueue { TaskManager.runTaskRepeat(new Runnable() { @Override public void run() { - if (FastQueue_1_8.this.toUpdate.isEmpty()) { + if (BukkitLocalQueue_1_8.this.toUpdate.isEmpty()) { return; } int count = 0; ArrayList chunks = new ArrayList<>(); - Iterator> i = FastQueue_1_8.this.toUpdate.entrySet().iterator(); + Iterator> i = BukkitLocalQueue_1_8.this.toUpdate.entrySet().iterator(); while (i.hasNext() && count < 128) { chunks.add(i.next().getValue()); i.remove(); @@ -72,44 +69,25 @@ public class FastQueue_1_8 extends SlowQueue { MainUtil.initCache(); } - public void update(Collection chunks) { - if (chunks.isEmpty()) { - return; - } - if (!MainUtil.canSendChunk) { - for (Chunk chunk : chunks) { - chunk.getWorld().refreshChunk(chunk.getX(), chunk.getZ()); - chunk.unload(true, false); - chunk.load(); - } - return; - } - try { - this.sendChunk.sendChunk(chunks); - } catch (Throwable e) { - e.printStackTrace(); - MainUtil.canSendChunk = false; - } + @Override + public void fixChunkLighting(int x, int z) { + Object c = this.methodGetHandle.of(getChunk(x, z)).call(); + this.methodInitLighting.of(c).call(); } - /** - * This should be overridden by any specialized queues. - * @param plotChunk - */ @Override - public void execute(PlotChunk plotChunk) { - SlowChunk sc = (SlowChunk) plotChunk; - Chunk chunk = plotChunk.getChunk(); - ChunkWrapper wrapper = plotChunk.getChunkWrapper(); + public void setBlocks(LocalChunk lc) { + Chunk chunk = getChunk(lc.getX(), lc.getZ()); + chunk.load(true); + World world = chunk.getWorld(); + ChunkWrapper wrapper = new ChunkWrapper(getWorld(), lc.getX(), lc.getZ()); if (!this.toUpdate.containsKey(wrapper)) { this.toUpdate.put(wrapper, chunk); } - chunk.load(true); - World world = chunk.getWorld(); Object w = this.methodGetHandle.of(world).call(); - Object c = this.methodGetChunkAt.of(w).call(wrapper.x, wrapper.z); - for (int i = 0; i < sc.result.length; i++) { - PlotBlock[] result2 = sc.result[i]; + Object c = this.methodGetChunkAt.of(w).call(lc.getX(), lc.getZ()); + for (int i = 0; i < lc.blocks.length; i++) { + PlotBlock[] result2 = lc.blocks[i]; if (result2 == null) { continue; } @@ -329,55 +307,31 @@ public class FastQueue_1_8 extends SlowQueue { this.methodA.of(chunk).call(pos, combined); } } - int[][] biomes = sc.biomes; - Biome[] values = Biome.values(); - if (biomes != null) { - for (int x = 0; x < 16; x++) { - int[] array = biomes[x]; - if (array == null) { - continue; - } - for (int z = 0; z < 16; z++) { - int biome = array[z]; - if (biome == 0) { - continue; - } - chunk.getBlock(x, 0, z).setBiome(values[biome]); - } + fixChunkLighting(lc.getX(), lc.getZ()); + } + + public void update(Collection chunks) { + if (chunks.isEmpty()) { + return; + } + if (!MainUtil.canSendChunk) { + for (Chunk chunk : chunks) { + chunk.getWorld().refreshChunk(chunk.getX(), chunk.getZ()); + chunk.unload(true, false); + chunk.load(); } + return; + } + try { + this.sendChunk.sendChunk(chunks); + } catch (Throwable e) { + e.printStackTrace(); + MainUtil.canSendChunk = false; } } - /** - * This should be overridden by any specialized queues. - * @param wrap - */ @Override - public PlotChunk getChunk(ChunkWrapper wrap) { - return new SlowChunk(wrap); - } - - /** - * This should be overridden by any specialized queues. - * @param fixAll - */ - @Override - public boolean fixLighting(PlotChunk chunk, boolean fixAll) { - Object c = this.methodGetHandle.of(chunk.getChunk()).call(); - this.methodInitLighting.of(c).call(); - return true; - } - - /** - * This should be overridden by any specialized queues. - * @param locations - */ - @Override - public void sendChunk(String world, Collection locations) { - for (ChunkLoc loc : locations) { - ChunkWrapper wrapper = SetQueue.IMP.new ChunkWrapper(world, loc.x, loc.z); - this.toUpdate.remove(wrapper); - } - this.sendChunk.sendChunk(world, locations); + public void refreshChunk(int x, int z) { + update(Arrays.asList(Bukkit.getWorld(getWorld()).getChunkAt(x, z))); } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_8_3.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue_1_8_3.java similarity index 61% rename from Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_8_3.java rename to Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue_1_8_3.java index e04449c35..342d289c8 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_8_3.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue_1_8_3.java @@ -1,66 +1,62 @@ package com.plotsquared.bukkit.util.block; -import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass; - import com.intellectualcrafters.plot.object.ChunkLoc; +import com.intellectualcrafters.plot.object.ChunkWrapper; import com.intellectualcrafters.plot.object.PseudoRandom; import com.intellectualcrafters.plot.util.ChunkManager; import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefClass; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefConstructor; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefField; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefMethod; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefMethod.RefExecutor; -import com.intellectualcrafters.plot.util.SetQueue; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; +import com.intellectualcrafters.plot.util.ReflectionUtils; import com.intellectualcrafters.plot.util.TaskManager; +import com.intellectualcrafters.plot.util.block.BasicLocalBlockQueue; import com.plotsquared.bukkit.util.SendChunk; -import org.bukkit.Chunk; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.World.Environment; -import org.bukkit.block.Biome; - import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; -import java.util.Map.Entry; +import java.util.Map; import java.util.Set; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Material; +import org.bukkit.World; -public class FastQueue_1_8_3 extends SlowQueue { + +import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass; + +public class BukkitLocalQueue_1_8_3 extends BukkitLocalQueue { private final SendChunk sendChunk; private final HashMap toUpdate = new HashMap<>(); - private final RefMethod methodGetHandleChunk; - private final RefMethod methodGetHandleWorld; - private final RefMethod methodInitLighting; - private final RefConstructor classBlockPositionConstructor; - private final RefConstructor classChunkSectionConstructor; - private final RefMethod methodX; - private final RefMethod methodAreNeighborsLoaded; - private final RefField fieldSections; - private final RefField fieldWorld; - private final RefMethod methodGetIdArray; - private final RefMethod methodGetWorld; - private final RefField tileEntityListTick; + private final ReflectionUtils.RefMethod methodGetHandleChunk; + private final ReflectionUtils.RefMethod methodGetHandleWorld; + private final ReflectionUtils.RefMethod methodInitLighting; + private final ReflectionUtils.RefConstructor classBlockPositionConstructor; + private final ReflectionUtils.RefConstructor classChunkSectionConstructor; + private final ReflectionUtils.RefMethod methodX; + private final ReflectionUtils.RefMethod methodAreNeighborsLoaded; + private final ReflectionUtils.RefField fieldSections; + private final ReflectionUtils.RefField fieldWorld; + private final ReflectionUtils.RefMethod methodGetIdArray; + private final ReflectionUtils.RefMethod methodGetWorld; + private final ReflectionUtils.RefField tileEntityListTick; - public FastQueue_1_8_3() throws NoSuchMethodException, ClassNotFoundException, NoSuchFieldException { - RefClass classCraftChunk = getRefClass("{cb}.CraftChunk"); - RefClass classCraftWorld = getRefClass("{cb}.CraftWorld"); + public BukkitLocalQueue_1_8_3(String world) throws NoSuchMethodException, ClassNotFoundException, NoSuchFieldException { + super(world); + ReflectionUtils.RefClass classCraftChunk = getRefClass("{cb}.CraftChunk"); + ReflectionUtils.RefClass classCraftWorld = getRefClass("{cb}.CraftWorld"); this.methodGetHandleChunk = classCraftChunk.getMethod("getHandle"); - RefClass classChunk = getRefClass("{nms}.Chunk"); + ReflectionUtils.RefClass classChunk = getRefClass("{nms}.Chunk"); this.methodInitLighting = classChunk.getMethod("initLighting"); - RefClass classBlockPosition = getRefClass("{nms}.BlockPosition"); + ReflectionUtils.RefClass classBlockPosition = getRefClass("{nms}.BlockPosition"); this.classBlockPositionConstructor = classBlockPosition.getConstructor(int.class, int.class, int.class); - RefClass classWorld = getRefClass("{nms}.World"); + ReflectionUtils.RefClass classWorld = getRefClass("{nms}.World"); this.methodX = classWorld.getMethod("x", classBlockPosition.getRealClass()); this.fieldSections = classChunk.getField("sections"); this.fieldWorld = classChunk.getField("world"); - RefClass classChunkSection = getRefClass("{nms}.ChunkSection"); + ReflectionUtils.RefClass classChunkSection = getRefClass("{nms}.ChunkSection"); this.methodGetIdArray = classChunkSection.getMethod("getIdArray"); this.methodAreNeighborsLoaded = classChunk.getMethod("areNeighborsLoaded", int.class); this.classChunkSectionConstructor = classChunkSection.getConstructor(int.class, boolean.class, char[].class); @@ -71,12 +67,12 @@ public class FastQueue_1_8_3 extends SlowQueue { TaskManager.runTaskRepeat(new Runnable() { @Override public void run() { - if (FastQueue_1_8_3.this.toUpdate.isEmpty()) { + if (BukkitLocalQueue_1_8_3.this.toUpdate.isEmpty()) { return; } int count = 0; ArrayList chunks = new ArrayList<>(); - Iterator> i = FastQueue_1_8_3.this.toUpdate.entrySet().iterator(); + Iterator> i = BukkitLocalQueue_1_8_3.this.toUpdate.entrySet().iterator(); while (i.hasNext() && count < 128) { chunks.add(i.next().getValue()); i.remove(); @@ -91,42 +87,184 @@ public class FastQueue_1_8_3 extends SlowQueue { MainUtil.initCache(); } - public void update(Collection chunks) { - if (chunks.isEmpty()) { - return; + @Override + public LocalChunk getLocalChunk(int x, int z) { + return new CharLocalChunk_1_8_3(this, x, z); + } + + public class CharLocalChunk_1_8_3 extends CharLocalChunk { + public short[] count; + public short[] air; + public short[] relight; + + public CharLocalChunk_1_8_3(BasicLocalBlockQueue parent, int x, int z) { + super(parent, x, z); + this.count = new short[16]; + this.air = new short[16]; + this.relight = new short[16]; } - if (!MainUtil.canSendChunk) { - for (Chunk chunk : chunks) { - chunk.getWorld().refreshChunk(chunk.getX(), chunk.getZ()); - chunk.unload(true, false); - chunk.load(); + + @Override + public void setBlock(int x, int y, int z, int id, int data) { + int i = MainUtil.CACHE_I[y][x][z]; + int j = MainUtil.CACHE_J[y][x][z]; + char[] vs = this.blocks[i]; + if (vs == null) { + vs = this.blocks[i] = new char[4096]; + this.count[i]++; + } else if (vs[j] == 0) { + this.count[i]++; + } + switch (id) { + case 0: + this.air[i]++; + vs[j] = (char) 1; + return; + case 10: + case 11: + case 39: + case 40: + case 51: + case 74: + case 89: + case 122: + case 124: + case 138: + case 169: + this.relight[i]++; + case 2: + case 4: + case 13: + case 14: + case 15: + case 20: + case 21: + case 22: + case 30: + case 32: + case 37: + case 41: + case 42: + case 45: + case 46: + case 47: + case 48: + case 49: + case 55: + case 56: + case 57: + case 58: + case 60: + case 7: + case 8: + case 9: + case 73: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 85: + case 87: + case 88: + case 101: + case 102: + case 103: + case 110: + case 112: + case 113: + case 121: + case 129: + case 133: + case 165: + case 166: + case 170: + case 172: + case 173: + case 174: + case 181: + case 182: + case 188: + case 189: + case 190: + case 191: + case 192: + vs[j] = (char) (id << 4); + return; + case 130: + case 76: + case 62: + this.relight[i]++; + case 54: + case 146: + case 61: + case 65: + case 68: + case 50: + if (data < 2) { + data = 2; + } + default: + vs[j] = (char) ((id << 4) + data); + return; } - return; } - try { - this.sendChunk.sendChunk(chunks); - } catch (Throwable e) { - e.printStackTrace(); - MainUtil.canSendChunk = false; + + public char[] getIdArray(int i) { + return this.blocks[i]; + } + + public int getCount(int i) { + return this.count[i]; + } + + public int getAir(int i) { + return this.air[i]; + } + + public void setCount(int i, short value) { + this.count[i] = value; + } + + public int getRelight(int i) { + return this.relight[i]; + } + + public int getTotalCount() { + int total = 0; + for (int i = 0; i < 16; i++) { + total += this.count[i]; + } + return total; + } + + public int getTotalRelight() { + if (getTotalCount() == 0) { + Arrays.fill(this.count, (short) 1); + Arrays.fill(this.relight, Short.MAX_VALUE); + return Short.MAX_VALUE; + } + int total = 0; + for (int i = 0; i < 16; i++) { + total += this.relight[i]; + } + return total; } } - /** - * This should be overridden by any specialized queues. - * @param plotChunk - */ @Override - public void execute(PlotChunk plotChunk) { - FastChunk_1_8_3 fs = (FastChunk_1_8_3) plotChunk; - Chunk chunk = plotChunk.getChunk(); + public void setBlocks(LocalChunk lc) { + CharLocalChunk_1_8_3 fs = (CharLocalChunk_1_8_3) lc; + Chunk chunk = getChunk(lc.getX(), lc.getZ()); + chunk.load(true); World world = chunk.getWorld(); - ChunkWrapper wrapper = plotChunk.getChunkWrapper(); + ChunkWrapper wrapper = new ChunkWrapper(getWorld(), lc.getX(), lc.getZ()); if (!this.toUpdate.containsKey(wrapper)) { this.toUpdate.put(wrapper, chunk); } - chunk.load(true); try { - boolean flag = world.getEnvironment() == Environment.NORMAL; + boolean flag = world.getEnvironment() == World.Environment.NORMAL; // Sections Method getHandle = chunk.getClass().getDeclaredMethod("getHandle"); @@ -147,10 +285,10 @@ public class FastQueue_1_8_3 extends SlowQueue { // Trim tiles boolean removed = false; - Set> entrySet = (Set>) (Set) tiles.entrySet(); - Iterator> iterator = entrySet.iterator(); + Set> entrySet = (Set>) (Set) tiles.entrySet(); + Iterator> iterator = entrySet.iterator(); while (iterator.hasNext()) { - Entry tile = iterator.next(); + Map.Entry tile = iterator.next(); Object pos = tile.getKey(); if (getX == null) { Class clazz2 = pos.getClass().getSuperclass(); @@ -222,23 +360,7 @@ public class FastQueue_1_8_3 extends SlowQueue { } catch (IllegalArgumentException | SecurityException | ReflectiveOperationException e) { e.printStackTrace(); } - int[][] biomes = fs.biomes; - Biome[] values = Biome.values(); - if (biomes != null) { - for (int x = 0; x < 16; x++) { - int[] array = biomes[x]; - if (array == null) { - continue; - } - for (int z = 0; z < 16; z++) { - int biome = array[z]; - if (biome == 0) { - continue; - } - chunk.getBlock(x, 0, z).setBiome(values[biome]); - } - } - } + fixLighting(chunk, fs, true); } public Object newChunkSection(int i, boolean flag, char[] ids) throws ReflectiveOperationException { @@ -249,24 +371,14 @@ public class FastQueue_1_8_3 extends SlowQueue { return (char[]) this.methodGetIdArray.of(obj).call(); } - /** - * This should be overridden by any specialized queues. - * @param wrap - */ @Override - public PlotChunk getChunk(ChunkWrapper wrap) { - return new FastChunk_1_8_3(wrap); + public void fixChunkLighting(int x, int z) { + Object c = this.methodGetHandleChunk.of(getChunk(x, z)).call(); + this.methodInitLighting.of(c).call(); } - /** - * This should be overridden by any specialized queues - * @param plotChunk - */ - @Override - public boolean fixLighting(PlotChunk plotChunk, boolean fixAll) { + public boolean fixLighting(Chunk chunk, CharLocalChunk_1_8_3 bc, boolean fixAll) { try { - FastChunk_1_8_3 bc = (FastChunk_1_8_3) plotChunk; - Chunk chunk = bc.getChunk(); if (!chunk.isLoaded()) { chunk.load(false); } else { @@ -279,7 +391,7 @@ public class FastQueue_1_8_3 extends SlowQueue { if (fixAll && !(boolean) this.methodAreNeighborsLoaded.of(c).call(1)) { World world = chunk.getWorld(); - ChunkWrapper wrapper = bc.getChunkWrapper(); + ChunkWrapper wrapper = new ChunkWrapper(getWorld(), chunk.getX(), chunk.getZ()); for (int x = wrapper.x - 1; x <= wrapper.x + 1; x++) { for (int z = wrapper.z - 1; z <= wrapper.z + 1; z++) { if (x != 0 && z != 0) { @@ -310,7 +422,7 @@ public class FastQueue_1_8_3 extends SlowQueue { int X = chunk.getX() << 4; int Z = chunk.getZ() << 4; - RefExecutor relight = this.methodX.of(w); + ReflectionUtils.RefMethod.RefExecutor relight = this.methodX.of(w); for (int j = 0; j < sections.length; j++) { Object section = sections[j]; if (section == null) { @@ -398,17 +510,28 @@ public class FastQueue_1_8_3 extends SlowQueue { return array[j] >> 4; } - /** - * This should be overridden by any specialized queues. - * @param world - * @param locations - */ - @Override - public void sendChunk(String world, Collection locations) { - for (ChunkLoc loc : locations) { - ChunkWrapper wrapper = SetQueue.IMP.new ChunkWrapper(world, loc.x, loc.z); - this.toUpdate.remove(wrapper); + public void update(Collection chunks) { + if (chunks.isEmpty()) { + return; } - this.sendChunk.sendChunk(world, locations); + if (!MainUtil.canSendChunk) { + for (Chunk chunk : chunks) { + chunk.getWorld().refreshChunk(chunk.getX(), chunk.getZ()); + chunk.unload(true, false); + chunk.load(); + } + return; + } + try { + this.sendChunk.sendChunk(chunks); + } catch (Throwable e) { + e.printStackTrace(); + MainUtil.canSendChunk = false; + } + } + + @Override + public void refreshChunk(int x, int z) { + update(Arrays.asList(Bukkit.getWorld(getWorld()).getChunkAt(x, z))); } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_9.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue_1_9.java similarity index 56% rename from Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_9.java rename to Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue_1_9.java index 6b8885772..03ef49963 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_9.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/BukkitLocalQueue_1_9.java @@ -1,73 +1,67 @@ package com.plotsquared.bukkit.util.block; -import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass; - import com.intellectualcrafters.plot.object.ChunkLoc; +import com.intellectualcrafters.plot.object.ChunkWrapper; import com.intellectualcrafters.plot.object.PseudoRandom; import com.intellectualcrafters.plot.util.ChunkManager; import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefClass; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefConstructor; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefField; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefMethod; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefMethod.RefExecutor; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; -import com.plotsquared.bukkit.util.BukkitUtil; -import org.bukkit.Chunk; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.World.Environment; -import org.bukkit.block.Biome; - +import com.intellectualcrafters.plot.util.ReflectionUtils; +import com.intellectualcrafters.plot.util.block.BasicLocalBlockQueue; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; -import java.util.Map.Entry; +import java.util.Map; import java.util.Set; +import org.bukkit.Chunk; +import org.bukkit.Material; +import org.bukkit.World; -public class FastQueue_1_9 extends SlowQueue { + +import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass; + +public class BukkitLocalQueue_1_9 extends BukkitLocalQueue { private final Object air; -// private final HashMap toUpdate = new HashMap<>(); - private final RefMethod methodGetHandleChunk; - private final RefMethod methodInitLighting; - private final RefConstructor classBlockPositionConstructor; - private final RefConstructor classChunkSectionConstructor; - private final RefMethod methodW; - private final RefMethod methodAreNeighborsLoaded; - private final RefField fieldSections; - private final RefField fieldWorld; - private final RefMethod methodGetBlocks; - private final RefMethod methodGetType; - private final RefMethod methodSetType; - private final RefMethod methodGetCombinedId; - private final RefMethod methodGetByCombinedId; - private final RefMethod methodGetWorld; + // private final HashMap toUpdate = new HashMap<>(); + private final ReflectionUtils.RefMethod methodGetHandleChunk; + private final ReflectionUtils.RefMethod methodInitLighting; + private final ReflectionUtils.RefConstructor classBlockPositionConstructor; + private final ReflectionUtils.RefConstructor classChunkSectionConstructor; + private final ReflectionUtils.RefMethod methodW; + private final ReflectionUtils.RefMethod methodAreNeighborsLoaded; + private final ReflectionUtils.RefField fieldSections; + private final ReflectionUtils.RefField fieldWorld; + private final ReflectionUtils.RefMethod methodGetBlocks; + private final ReflectionUtils.RefMethod methodGetType; + private final ReflectionUtils.RefMethod methodSetType; + private final ReflectionUtils.RefMethod methodGetCombinedId; + private final ReflectionUtils.RefMethod methodGetByCombinedId; + private final ReflectionUtils.RefMethod methodGetWorld; - private final RefField tileEntityListTick; + private final ReflectionUtils.RefField tileEntityListTick; - - public FastQueue_1_9() throws NoSuchFieldException, NoSuchMethodException, ClassNotFoundException { - RefClass classCraftChunk = getRefClass("{cb}.CraftChunk"); + public BukkitLocalQueue_1_9(String world) throws NoSuchMethodException, ClassNotFoundException, NoSuchFieldException { + super(world); + ReflectionUtils.RefClass classCraftChunk = getRefClass("{cb}.CraftChunk"); this.methodGetHandleChunk = classCraftChunk.getMethod("getHandle"); - RefClass classChunk = getRefClass("{nms}.Chunk"); + ReflectionUtils.RefClass classChunk = getRefClass("{nms}.Chunk"); this.methodInitLighting = classChunk.getMethod("initLighting"); - RefClass classBlockPosition = getRefClass("{nms}.BlockPosition"); + ReflectionUtils.RefClass classBlockPosition = getRefClass("{nms}.BlockPosition"); this.classBlockPositionConstructor = classBlockPosition.getConstructor(int.class, int.class, int.class); - RefClass classWorld = getRefClass("{nms}.World"); + ReflectionUtils.RefClass classWorld = getRefClass("{nms}.World"); this.tileEntityListTick = classWorld.getField("tileEntityListTick"); this.methodGetWorld = classChunk.getMethod("getWorld"); this.methodW = classWorld.getMethod("w", classBlockPosition.getRealClass()); this.fieldSections = classChunk.getField("sections"); this.fieldWorld = classChunk.getField("world"); - RefClass classBlock = getRefClass("{nms}.Block"); - RefClass classIBlockData = getRefClass("{nms}.IBlockData"); + ReflectionUtils.RefClass classBlock = getRefClass("{nms}.Block"); + ReflectionUtils.RefClass classIBlockData = getRefClass("{nms}.IBlockData"); this.methodGetCombinedId = classBlock.getMethod("getCombinedId", classIBlockData.getRealClass()); this.methodGetByCombinedId = classBlock.getMethod("getByCombinedId", int.class); - RefClass classChunkSection = getRefClass("{nms}.ChunkSection"); + ReflectionUtils.RefClass classChunkSection = getRefClass("{nms}.ChunkSection"); this.methodGetBlocks = classChunkSection.getMethod("getBlocks"); this.methodGetType = classChunkSection.getMethod("getType", int.class, int.class, int.class); this.methodSetType = classChunkSection.getMethod("setType", int.class, int.class, int.class, classIBlockData.getRealClass()); @@ -77,19 +71,180 @@ public class FastQueue_1_9 extends SlowQueue { MainUtil.initCache(); } - /** - * This should be overridden by any specialized queues. - * @param plotChunk - */ @Override - public void execute(PlotChunk plotChunk) { - final FastChunk_1_9 fs = (FastChunk_1_9) plotChunk; - Chunk chunk = plotChunk.getChunk(); - World world = chunk.getWorld(); - ChunkWrapper wrapper = plotChunk.getChunkWrapper(); + public LocalChunk getLocalChunk(int x, int z) { + return new CharLocalChunk_1_8_3(this, x, z); + } + + public class CharLocalChunk_1_8_3 extends CharLocalChunk { + public short[] count; + public short[] air; + public short[] relight; + + public CharLocalChunk_1_8_3(BasicLocalBlockQueue parent, int x, int z) { + super(parent, x, z); + this.count = new short[16]; + this.air = new short[16]; + this.relight = new short[16]; + } + + @Override + public void setBlock(int x, int y, int z, int id, int data) { + int i = MainUtil.CACHE_I[y][x][z]; + int j = MainUtil.CACHE_J[y][x][z]; + char[] vs = this.blocks[i]; + if (vs == null) { + vs = this.blocks[i] = new char[4096]; + this.count[i]++; + } else if (vs[j] == 0) { + this.count[i]++; + } + switch (id) { + case 0: + this.air[i]++; + vs[j] = (char) 1; + return; + case 10: + case 11: + case 39: + case 40: + case 51: + case 74: + case 89: + case 122: + case 124: + case 138: + case 169: + this.relight[i]++; + case 2: + case 4: + case 13: + case 14: + case 15: + case 20: + case 21: + case 22: + case 30: + case 32: + case 37: + case 41: + case 42: + case 45: + case 46: + case 47: + case 48: + case 49: + case 55: + case 56: + case 57: + case 58: + case 60: + case 7: + case 8: + case 9: + case 73: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 85: + case 87: + case 88: + case 101: + case 102: + case 103: + case 110: + case 112: + case 113: + case 121: + case 129: + case 133: + case 165: + case 166: + case 170: + case 172: + case 173: + case 174: + case 181: + case 182: + case 188: + case 189: + case 190: + case 191: + case 192: + vs[j] = (char) (id << 4); + return; + case 130: + case 76: + case 62: + this.relight[i]++; + case 54: + case 146: + case 61: + case 65: + case 68: + case 50: + if (data < 2) { + data = 2; + } + default: + vs[j] = (char) ((id << 4) + data); + return; + } + } + + public char[] getIdArray(int i) { + return this.blocks[i]; + } + + public int getCount(int i) { + return this.count[i]; + } + + public int getAir(int i) { + return this.air[i]; + } + + public void setCount(int i, short value) { + this.count[i] = value; + } + + public int getRelight(int i) { + return this.relight[i]; + } + + public int getTotalCount() { + int total = 0; + for (int i = 0; i < 16; i++) { + total += this.count[i]; + } + return total; + } + + public int getTotalRelight() { + if (getTotalCount() == 0) { + Arrays.fill(this.count, (short) 1); + Arrays.fill(this.relight, Short.MAX_VALUE); + return Short.MAX_VALUE; + } + int total = 0; + for (int i = 0; i < 16; i++) { + total += this.relight[i]; + } + return total; + } + } + + @Override + public void setBlocks(LocalChunk lc) { + CharLocalChunk_1_8_3 fs = (CharLocalChunk_1_8_3) lc; + Chunk chunk = getChunk(lc.getX(), lc.getZ()); chunk.load(true); + World world = chunk.getWorld(); try { - boolean flag = world.getEnvironment() == Environment.NORMAL; + boolean flag = world.getEnvironment() == World.Environment.NORMAL; // Sections Method getHandle = chunk.getClass().getDeclaredMethod("getHandle"); @@ -109,10 +264,10 @@ public class FastQueue_1_9 extends SlowQueue { Method zm = null; // Trim tiles boolean removed = false; - Set> entrySet = (Set>) (Set) tiles.entrySet(); - Iterator> iterator = entrySet.iterator(); + Set> entrySet = (Set>) (Set) tiles.entrySet(); + Iterator> iterator = entrySet.iterator(); while (iterator.hasNext()) { - Entry tile = iterator.next(); + Map.Entry tile = iterator.next(); Object pos = tile.getKey(); if (xm == null) { Class clazz2 = pos.getClass().getSuperclass(); @@ -125,7 +280,7 @@ public class FastQueue_1_9 extends SlowQueue { int lz = (int) zm.invoke(pos) & 15; int j = MainUtil.CACHE_I[ly][lx][lz]; int k = MainUtil.CACHE_J[ly][lx][lz]; - int[] array = fs.getIdArray(j); + char[] array = fs.getIdArray(j); if (array == null) { continue; } @@ -150,32 +305,25 @@ public class FastQueue_1_9 extends SlowQueue { if (fs.getCount(j) == 0) { continue; } - int[] newArray = fs.getIdArray(j); + char[] newArray = fs.getIdArray(j); if (newArray == null) { continue; } Object section = sections[j]; if (section == null || fs.getCount(j) >= 4096) { - char[] array = new char[4096]; - for (int i = 0; i < newArray.length; i++) { - int combined = newArray[i]; - int id = combined & 4095; - int data = combined >> 12; - array[i] = (char) ((id << 4) + data); - } - section = sections[j] = newChunkSection(j << 4, flag, array); + section = sections[j] = newChunkSection(j << 4, flag, fs.getIdArray(j)); continue; } Object currentArray = getBlocks(section); - RefExecutor setType = this.methodSetType.of(section); + ReflectionUtils.RefMethod.RefExecutor setType = this.methodSetType.of(section); boolean fill = true; for (int k = 0; k < newArray.length; k++) { - int n = newArray[k]; + char n = newArray[k]; switch (n) { case 0: fill = false; continue; - case -1: { + case 1: { fill = false; int x = MainUtil.x_loc[j][k]; int y = MainUtil.y_loc[j][k]; @@ -187,7 +335,9 @@ public class FastQueue_1_9 extends SlowQueue { int x = MainUtil.x_loc[j][k]; int y = MainUtil.y_loc[j][k]; int z = MainUtil.z_loc[j][k]; - Object iBlock = this.methodGetByCombinedId.call((int) n); + int id = n >> 4; + int data = n & 15; + Object iBlock = this.methodGetByCombinedId.call((int) (id & 0xFFF) + (data << 12)); setType.call(x, y & 15, z, iBlock); } } @@ -199,24 +349,8 @@ public class FastQueue_1_9 extends SlowQueue { } catch (IllegalArgumentException | SecurityException | ReflectiveOperationException e) { e.printStackTrace(); } - int[][] biomes = fs.biomes; - Biome[] values = Biome.values(); - if (biomes != null) { - for (int x = 0; x < 16; x++) { - int[] array = biomes[x]; - if (array == null) { - continue; - } - for (int z = 0; z < 16; z++) { - int biome = array[z]; - if (biome == 0) { - continue; - } - chunk.getBlock(x, 0, z).setBiome(values[biome]); - } - } - } - world.refreshChunk(fs.getX(), fs.getZ()); + fixLighting(chunk, fs, true); + refreshChunk(fs.getX(), fs.getZ()); } public Object newChunkSection(int i, boolean flag, char[] ids) throws ReflectiveOperationException { @@ -227,38 +361,27 @@ public class FastQueue_1_9 extends SlowQueue { return this.methodGetBlocks.of(obj).call(); } - /** - * This should be overridden by any specialized queues - * @param wrap - */ @Override - public PlotChunk getChunk(ChunkWrapper wrap) { - return new FastChunk_1_9(wrap); + public void fixChunkLighting(int x, int z) { + Object c = this.methodGetHandleChunk.of(getChunk(x, z)).call(); + this.methodInitLighting.of(c).call(); } - /** - * This should be overridden by any specialized queues - * @param pc - */ - @Override - public boolean fixLighting(PlotChunk pc, boolean fixAll) { + public boolean fixLighting(Chunk chunk, CharLocalChunk_1_8_3 bc, boolean fixAll) { try { - FastChunk_1_9 bc = (FastChunk_1_9) pc; - Chunk chunk = bc.getChunk(); if (!chunk.isLoaded()) { chunk.load(false); } else { - chunk.unload(true, true); + chunk.unload(true, false); chunk.load(false); } // Initialize lighting Object c = this.methodGetHandleChunk.of(chunk).call(); + ChunkWrapper wrapper = new ChunkWrapper(getWorld(), bc.getX(), bc.getZ()); if (fixAll && !(boolean) this.methodAreNeighborsLoaded.of(c).call(1)) { World world = chunk.getWorld(); - ChunkWrapper wrapper = bc.getChunkWrapper(); - String worldName = wrapper.world; for (int x = wrapper.x - 1; x <= wrapper.x + 1; x++) { for (int z = wrapper.z - 1; z <= wrapper.z + 1; z++) { if (x != 0 && z != 0) { @@ -266,7 +389,7 @@ public class FastQueue_1_9 extends SlowQueue { while (!other.isLoaded()) { other.load(true); } - ChunkManager.manager.loadChunk(worldName, new ChunkLoc(x, z), true); + ChunkManager.manager.loadChunk(getWorld(), new ChunkLoc(x, z), true); } } } @@ -284,7 +407,7 @@ public class FastQueue_1_9 extends SlowQueue { int X = chunk.getX() << 4; int Z = chunk.getZ() << 4; - RefExecutor relight = this.methodW.of(w); + ReflectionUtils.RefMethod.RefExecutor relight = this.methodW.of(w); for (int j = 0; j < sections.length; j++) { Object section = sections[j]; if (section == null) { @@ -293,7 +416,7 @@ public class FastQueue_1_9 extends SlowQueue { if (bc.getRelight(j) == 0 && !fixAll || bc.getCount(j) == 0 || bc.getCount(j) >= 4096 && bc.getAir(j) == 0) { continue; } - int[] array = bc.getIdArray(j); + char[] array = bc.getIdArray(j); if (array != null) { int l = PseudoRandom.random.random(2); for (int k = 0; k < array.length; k++) { @@ -329,7 +452,7 @@ public class FastQueue_1_9 extends SlowQueue { int x = MainUtil.x_loc[j][k]; int y = MainUtil.y_loc[j][k]; int z = MainUtil.z_loc[j][k]; - if (isSurrounded(bc.getIdArrays(), x, y, z)) { + if (isSurrounded(bc.blocks, x, y, z)) { continue; } Object pos = this.classBlockPositionConstructor.create(X + x, y, Z + z); @@ -345,7 +468,12 @@ public class FastQueue_1_9 extends SlowQueue { return false; } - public boolean isSurrounded(int[][] sections, int x, int y, int z) { + @Override + public void refreshChunk(int x, int z) { + getBukkitWorld().refreshChunk(x, z); + } + + public boolean isSurrounded(char[][] sections, int x, int y, int z) { return isSolid(getId(sections, x, y + 1, z)) && isSolid(getId(sections, x + 1, y - 1, z)) && isSolid(getId(sections, x - 1, y, z)) @@ -361,29 +489,15 @@ public class FastQueue_1_9 extends SlowQueue { return false; } - public int getId(int[][] sections, int x, int y, int z) { - if (x < 0 || x > 15 || z < 0 || z > 15) { - return 1; - } - if (y < 0 || y > 255) { - return 1; - } - int i = MainUtil.CACHE_I[y][x][z]; - int[] section = sections[i]; + public int getId(char[] section, int x, int y, int z) { if (section == null) { return 0; } int j = MainUtil.CACHE_J[y][x][z]; - return section[j]; + return section[j] >> 4; } - public int getId(Object section, int x, int y, int z) { - int j = MainUtil.CACHE_J[y][x][z]; - Object iBlock = this.methodGetType.of(section).call(x, y & 15, z); - return (int) this.methodGetCombinedId.call(iBlock); - } - - public int getId(Object[] sections, int x, int y, int z) { + public int getId(char[][] sections, int x, int y, int z) { if (x < 0 || x > 15 || z < 0 || z > 15) { return 1; } @@ -391,23 +505,10 @@ public class FastQueue_1_9 extends SlowQueue { return 1; } int i = MainUtil.CACHE_I[y][x][z]; - Object section = sections[i]; + char[] section = sections[i]; if (section == null) { return 0; } return getId(section, x, y, z); } - - /** - * This should be overridden by any specialized queues - * @param world - * @param locations - */ - @Override - public void sendChunk(String world, Collection locations) { - World worldObj = BukkitUtil.getWorld(world); - for (ChunkLoc loc : locations) { - worldObj.refreshChunk(loc.x, loc.z); - } - } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastChunk_1_8_3.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastChunk_1_8_3.java deleted file mode 100644 index 0ccb98fd0..000000000 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastChunk_1_8_3.java +++ /dev/null @@ -1,247 +0,0 @@ -package com.plotsquared.bukkit.util.block; - -import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; -import com.plotsquared.bukkit.util.BukkitUtil; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; - -import java.util.Arrays; - -public class FastChunk_1_8_3 extends PlotChunk { - - public char[][] ids; - public short[] count; - public short[] air; - public short[] relight; - public int[][] biomes; - public Chunk chunk; - - public FastChunk_1_8_3(ChunkWrapper chunk) { - super(chunk); - this.ids = new char[16][]; - this.count = new short[16]; - this.air = new short[16]; - this.relight = new short[16]; - } - - @Override - public Chunk getChunkAbs() { - ChunkWrapper loc = getChunkWrapper(); - return BukkitUtil.getWorld(loc.world).getChunkAt(loc.x, loc.z); - } - - @Override - public Chunk getChunk() { - if (this.chunk == null) { - ChunkWrapper cl = getChunkWrapper(); - this.chunk = Bukkit.getWorld(cl.world).getChunkAt(cl.x, cl.z); - } - return this.chunk; - } - - @Override - public void setChunkWrapper(ChunkWrapper loc) { - super.setChunkWrapper(loc); - this.chunk = null; - } - - /** - * Get the number of block changes in a specified section. - * @param i - * @return - */ - public int getCount(int i) { - return this.count[i]; - } - - public int getAir(int i) { - return this.air[i]; - } - - public void setCount(int i, short value) { - this.count[i] = value; - } - - /** - * Get the number of block changes in a specified section. - * @param i - * @return - */ - public int getRelight(int i) { - return this.relight[i]; - } - - public int getTotalCount() { - int total = 0; - for (int i = 0; i < 16; i++) { - total += this.count[i]; - } - return total; - } - - public int getTotalRelight() { - if (getTotalCount() == 0) { - Arrays.fill(this.count, (short) 1); - Arrays.fill(this.relight, Short.MAX_VALUE); - return Short.MAX_VALUE; - } - int total = 0; - for (int i = 0; i < 16; i++) { - total += this.relight[i]; - } - return total; - } - - /** - * Get the raw data for a section. - * @param i - * @return - */ - public char[] getIdArray(int i) { - return this.ids[i]; - } - - @Override - public void setBlock(int x, int y, int z, int id, byte data) { - int i = MainUtil.CACHE_I[y][x][z]; - int j = MainUtil.CACHE_J[y][x][z]; - char[] vs = this.ids[i]; - if (vs == null) { - vs = this.ids[i] = new char[4096]; - this.count[i]++; - } else if (vs[j] == 0) { - this.count[i]++; - } - switch (id) { - case 0: - this.air[i]++; - vs[j] = (char) 1; - return; - case 10: - case 11: - case 39: - case 40: - case 51: - case 74: - case 89: - case 122: - case 124: - case 138: - case 169: - this.relight[i]++; - case 2: - case 4: - case 13: - case 14: - case 15: - case 20: - case 21: - case 22: - case 30: - case 32: - case 37: - case 41: - case 42: - case 45: - case 46: - case 47: - case 48: - case 49: - case 55: - case 56: - case 57: - case 58: - case 60: - case 7: - case 8: - case 9: - case 73: - case 78: - case 79: - case 80: - case 81: - case 82: - case 83: - case 85: - case 87: - case 88: - case 101: - case 102: - case 103: - case 110: - case 112: - case 113: - case 121: - case 129: - case 133: - case 165: - case 166: - case 170: - case 172: - case 173: - case 174: - case 181: - case 182: - case 188: - case 189: - case 190: - case 191: - case 192: - vs[j] = (char) (id << 4); - return; - case 130: - case 76: - case 62: - this.relight[i]++; - case 54: - case 146: - case 61: - case 65: - case 68: - case 50: - if (data < 2) { - data = 2; - } - default: - vs[j] = (char) ((id << 4) + data); - return; - } - } - - @Override - public PlotChunk clone() { - FastChunk_1_8_3 toReturn = new FastChunk_1_8_3(getChunkWrapper()); - toReturn.air = this.air.clone(); - toReturn.count = this.count.clone(); - toReturn.relight = this.relight.clone(); - toReturn.ids = new char[this.ids.length][]; - for (int i = 0; i < this.ids.length; i++) { - char[] matrix = this.ids[i]; - if (matrix != null) { - toReturn.ids[i] = new char[matrix.length]; - System.arraycopy(matrix, 0, toReturn.ids[i], 0, matrix.length); - } - } - return toReturn; - } - - @Override - public PlotChunk shallowClone() { - FastChunk_1_8_3 toReturn = new FastChunk_1_8_3(getChunkWrapper()); - toReturn.air = this.air; - toReturn.count = this.count; - toReturn.relight = this.relight; - toReturn.ids = this.ids; - return toReturn; - } - - @Override - public void setBiome(int x, int z, int biome) { - if (this.biomes == null) { - this.biomes = new int[16][16]; - } - this.biomes[x][z] = biome; - } -} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastChunk_1_9.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastChunk_1_9.java deleted file mode 100644 index ef6fcca73..000000000 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastChunk_1_9.java +++ /dev/null @@ -1,251 +0,0 @@ -package com.plotsquared.bukkit.util.block; - -import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; -import com.plotsquared.bukkit.util.BukkitUtil; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; - -import java.util.Arrays; - -public class FastChunk_1_9 extends PlotChunk { - - public int[][] ids; - public short[] count; - public short[] air; - public short[] relight; - public int[][] biomes; - public Chunk chunk; - - public FastChunk_1_9(ChunkWrapper chunk) { - super(chunk); - this.ids = new int[16][]; - this.count = new short[16]; - this.air = new short[16]; - this.relight = new short[16]; - } - - @Override - public Chunk getChunkAbs() { - ChunkWrapper loc = getChunkWrapper(); - return BukkitUtil.getWorld(loc.world).getChunkAt(loc.x, loc.z); - } - - @Override - public Chunk getChunk() { - if (this.chunk == null) { - ChunkWrapper cl = getChunkWrapper(); - this.chunk = Bukkit.getWorld(cl.world).getChunkAt(cl.x, cl.z); - } - return this.chunk; - } - - @Override - public void setChunkWrapper(ChunkWrapper loc) { - super.setChunkWrapper(loc); - this.chunk = null; - } - - /** - * Get the number of block changes in a specified section. - * @param i - * @return - */ - public int getCount(int i) { - return this.count[i]; - } - - public int getAir(int i) { - return this.air[i]; - } - - public void setCount(int i, short value) { - this.count[i] = value; - } - - /** - * Get the number of block changes in a specified section. - * @param i - * @return - */ - public int getRelight(int i) { - return this.relight[i]; - } - - public int getTotalCount() { - int total = 0; - for (int i = 0; i < 16; i++) { - total += this.count[i]; - } - return total; - } - - public int getTotalRelight() { - if (getTotalCount() == 0) { - Arrays.fill(this.count, (short) 1); - Arrays.fill(this.relight, Short.MAX_VALUE); - return Short.MAX_VALUE; - } - int total = 0; - for (int i = 0; i < 16; i++) { - total += this.relight[i]; - } - return total; - } - - /** - * Get the raw data for a section. - * @param i - * @return - */ - public int[] getIdArray(int i) { - return this.ids[i]; - } - - public int[][] getIdArrays() { - return this.ids; - } - - @Override - public void setBlock(int x, int y, int z, int id, byte data) { - int i = MainUtil.CACHE_I[y][x][z]; - int j = MainUtil.CACHE_J[y][x][z]; - int[] vs = this.ids[i]; - if (vs == null) { - vs = this.ids[i] = new int[4096]; - this.count[i]++; - } else if (vs[j] == 0) { - this.count[i]++; - } - switch (id) { - case 0: - this.air[i]++; - vs[j] = -1; - return; - case 10: - case 11: - case 39: - case 40: - case 51: - case 74: - case 89: - case 122: - case 124: - case 138: - case 169: - this.relight[i]++; - case 2: - case 4: - case 13: - case 14: - case 15: - case 20: - case 21: - case 22: - case 30: - case 32: - case 37: - case 41: - case 42: - case 45: - case 46: - case 47: - case 48: - case 49: - case 55: - case 56: - case 57: - case 58: - case 60: - case 7: - case 8: - case 9: - case 73: - case 78: - case 79: - case 80: - case 81: - case 82: - case 83: - case 85: - case 87: - case 88: - case 101: - case 102: - case 103: - case 110: - case 112: - case 113: - case 121: - case 129: - case 133: - case 165: - case 166: - case 170: - case 172: - case 173: - case 174: - case 181: - case 182: - case 188: - case 189: - case 190: - case 191: - case 192: - vs[j] = id; - return; - case 130: - case 76: - case 62: - this.relight[i]++; - case 54: - case 146: - case 61: - case 65: - case 68: - case 50: - if (data < 2) { - data = 2; - } - default: - vs[j] = id + (data << 12); - return; - } - } - - @Override - public PlotChunk clone() { - FastChunk_1_9 toReturn = new FastChunk_1_9(getChunkWrapper()); - toReturn.air = this.air.clone(); - toReturn.count = this.count.clone(); - toReturn.relight = this.relight.clone(); - toReturn.ids = new int[this.ids.length][]; - for (int i = 0; i < this.ids.length; i++) { - int[] matrix = this.ids[i]; - if (matrix != null) { - toReturn.ids[i] = new int[matrix.length]; - System.arraycopy(matrix, 0, toReturn.ids[i], 0, matrix.length); - } - } - return toReturn; - } - - @Override - public PlotChunk shallowClone() { - FastChunk_1_9 toReturn = new FastChunk_1_9(getChunkWrapper()); - toReturn.air = this.air; - toReturn.count = this.count; - toReturn.relight = this.relight; - toReturn.ids = this.ids; - return toReturn; - } - - @Override - public void setBiome(int x, int z, int biome) { - if (this.biomes == null) { - this.biomes = new int[16][16]; - } - this.biomes[x][z] = biome; - } -} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_7.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_7.java deleted file mode 100644 index b29f5826f..000000000 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/FastQueue_1_7.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.plotsquared.bukkit.util.block; - -import static com.intellectualcrafters.plot.util.ReflectionUtils.getRefClass; - -import com.intellectualcrafters.plot.object.ChunkLoc; -import com.intellectualcrafters.plot.object.PlotBlock; -import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefClass; -import com.intellectualcrafters.plot.util.ReflectionUtils.RefMethod; -import com.intellectualcrafters.plot.util.SetQueue; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; -import com.intellectualcrafters.plot.util.TaskManager; -import com.plotsquared.bukkit.util.BukkitUtil; -import com.plotsquared.bukkit.util.SendChunk; -import org.bukkit.Chunk; -import org.bukkit.World; -import org.bukkit.block.Biome; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map.Entry; - -public class FastQueue_1_7 extends SlowQueue { - - private final RefClass classBlock = getRefClass("{nms}.Block"); - private final RefClass classChunk = getRefClass("{nms}.Chunk"); - private final RefClass classWorld = getRefClass("{nms}.World"); - private final RefClass classCraftWorld = getRefClass("{cb}.CraftWorld"); - private final RefMethod methodGetHandle; - private final RefMethod methodGetChunkAt; - private final RefMethod methodA; - private final RefMethod methodGetById; - private final RefMethod methodInitLighting; - private final SendChunk sendChunk; - - private final HashMap toUpdate = new HashMap<>(); - - public FastQueue_1_7() throws NoSuchMethodException, ClassNotFoundException, NoSuchFieldException { - this.methodGetHandle = this.classCraftWorld.getMethod("getHandle"); - this.methodGetChunkAt = this.classWorld.getMethod("getChunkAt", int.class, int.class); - this.methodA = this.classChunk.getMethod("a", int.class, int.class, int.class, this.classBlock, int.class); - this.methodGetById = this.classBlock.getMethod("getById", int.class); - this.methodInitLighting = this.classChunk.getMethod("initLighting"); - this.sendChunk = new SendChunk(); - TaskManager.runTaskRepeat(new Runnable() { - @Override - public void run() { - if (FastQueue_1_7.this.toUpdate.isEmpty()) { - return; - } - int count = 0; - ArrayList chunks = new ArrayList<>(); - Iterator> i = FastQueue_1_7.this.toUpdate.entrySet().iterator(); - while (i.hasNext() && (count < 128)) { - chunks.add(i.next().getValue()); - i.remove(); - count++; - } - if (count == 0) { - return; - } - update(chunks); - } - }, 1); - MainUtil.initCache(); - } - - public void update(Collection chunks) { - if (chunks.isEmpty()) { - return; - } - if (!MainUtil.canSendChunk) { - for (Chunk chunk : chunks) { - chunk.getWorld().refreshChunk(chunk.getX(), chunk.getZ()); - chunk.unload(true, false); - chunk.load(); - } - return; - } - try { - this.sendChunk.sendChunk(chunks); - } catch (Throwable e) { - e.printStackTrace(); - MainUtil.canSendChunk = false; - } - } - - /** - * This should be overridden by any specialized queues - * @param plotChunk - */ - @Override - public void execute(PlotChunk plotChunk) { - SlowChunk sc = (SlowChunk) plotChunk; - Chunk chunk = plotChunk.getChunk(); - ChunkWrapper wrapper = plotChunk.getChunkWrapper(); - if (!this.toUpdate.containsKey(wrapper)) { - this.toUpdate.put(wrapper, chunk); - } - chunk.load(true); - World world = chunk.getWorld(); - Object w = this.methodGetHandle.of(world).call(); - Object c = this.methodGetChunkAt.of(w).call(wrapper.x, wrapper.z); - for (int i = 0; i < sc.result.length; i++) { - PlotBlock[] result2 = sc.result[i]; - if (result2 == null) { - continue; - } - for (int j = 0; j < 4096; j++) { - int x = MainUtil.x_loc[i][j]; - int y = MainUtil.y_loc[i][j]; - int z = MainUtil.z_loc[i][j]; - PlotBlock newBlock = result2[j]; - if (newBlock.id == -1) { - chunk.getBlock(x, y, z).setData(newBlock.data, false); - continue; - } - Object block = this.methodGetById.call(newBlock.id); - this.methodA.of(c).call(x, y, z, block, newBlock.data); - } - } - int[][] biomes = sc.biomes; - Biome[] values = Biome.values(); - if (biomes != null) { - for (int x = 0; x < 16; x++) { - int[] array = biomes[x]; - if (array == null) { - continue; - } - for (int z = 0; z < 16; z++) { - int biome = array[z]; - if (biome == 0) { - continue; - } - chunk.getBlock(x, 0, z).setBiome(values[biome]); - } - } - } - } - - /** - * This should be overridden by any specialized queues - * @param wrap - */ - @Override - public PlotChunk getChunk(ChunkWrapper wrap) { - return new SlowChunk(wrap); - } - - /** - * This should be overridden by any specialized queues - * @param chunk - * @param fixAll - */ - @Override - public boolean fixLighting(PlotChunk chunk, boolean fixAll) { - Object c = this.methodGetHandle.of(chunk.getChunk()).call(); - this.methodInitLighting.of(c).call(); - return true; - } - - /** - * This should be overridden by any specialized queues - * @param world - * @param locations - */ - @Override - public void sendChunk(String world, Collection locations) { - World worldObj = BukkitUtil.getWorld(world); - for (ChunkLoc loc : locations) { - ChunkWrapper wrapper = SetQueue.IMP.new ChunkWrapper(world, loc.x, loc.z); - this.toUpdate.remove(wrapper); - } - this.sendChunk.sendChunk(world, locations); - } -} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/GenChunk.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/GenChunk.java index 286f63aee..51446d309 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/GenChunk.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/GenChunk.java @@ -1,62 +1,109 @@ package com.plotsquared.bukkit.util.block; +import com.intellectualcrafters.plot.object.ChunkWrapper; +import com.intellectualcrafters.plot.object.Location; +import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; +import com.intellectualcrafters.plot.util.block.ScopedLocalBlockQueue; import com.plotsquared.bukkit.util.BukkitUtil; import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.generator.ChunkGenerator.BiomeGrid; import org.bukkit.generator.ChunkGenerator.ChunkData; +import org.bukkit.material.MaterialData; -public class GenChunk extends PlotChunk { +public class GenChunk extends ScopedLocalBlockQueue { public final Biome[] biomes; - public Chunk chunk; public short[][] result; public byte[][] result_data; public ChunkData cd; public BiomeGrid grid; + public Chunk chunk; + public String world; + public int cx; + public int cz; + public GenChunk(Chunk chunk, ChunkWrapper wrap) { - super(wrap); - if ((this.chunk = chunk) == null && wrap != null) { + super(null, new Location(null, 0, 0, 0), new Location(null, 15, 255, 15)); + if ((this.chunk = chunk) == null && (wrap) != null) { World world = BukkitUtil.getWorld(wrap.world); if (world != null) { - chunk = world.getChunkAt(wrap.x, wrap.z); + this.chunk = world.getChunkAt(wrap.x, wrap.z); } } this.biomes = Biome.values(); } - @Override - public Chunk getChunkAbs() { - ChunkWrapper wrap = getChunkWrapper(); - if (this.chunk == null || wrap.x != this.chunk.getX() || wrap.z != this.chunk.getZ()) { - this.chunk = BukkitUtil.getWorld(wrap.world).getChunkAt(wrap.x, wrap.z); + public void setChunk(Chunk chunk) { + this.chunk = chunk; + } + + public void setChunk(ChunkWrapper wrap) { + chunk = null; + world = wrap.world; + cx = wrap.x; + cz = wrap.z; + } + + public Chunk getChunk() { + if (chunk == null) { + World worldObj = BukkitUtil.getWorld(world); + if (worldObj != null) { + this.chunk = worldObj.getChunkAt(cx, cz); + } } - return this.chunk; + return chunk; + } + + public ChunkWrapper getChunkWrapper() { + if (chunk == null) { + return new ChunkWrapper(world, cx, cz); + } + return new ChunkWrapper(chunk.getWorld().getName(), chunk.getX(), chunk.getZ()); } @Override - public void setBiome(int x, int z, int biome) { + public void fillBiome(String biomeName) { + if (grid == null) { + return; + } + Biome biome = Biome.valueOf(biomeName.toUpperCase()); + for (int x = 0; x <= 15; x++) { + for (int z = 0; z < 15; z++) { + this.grid.setBiome(x, z, biome); + } + } + } + + @Override + public boolean setBiome(int x, int z, String biome) { + return setBiome(x, z, Biome.valueOf(biome.toUpperCase())); + } + + public boolean setBiome(int x, int z, int biome) { if (this.grid != null) { this.grid.setBiome(x, z, this.biomes[biome]); + return true; } + return false; } - public void setBiome(int x, int z, Biome biome) { + public boolean setBiome(int x, int z, Biome biome) { if (this.grid != null) { this.grid.setBiome(x, z, biome); + return true; } + return false; } @Override - public void setBlock(int x, int y, int z, int id, byte data) { + public boolean setBlock(int x, int y, int z, int id, int data) { if (this.result == null) { - this.cd.setBlock(x, y, z, id, data); - return; + this.cd.setBlock(x, y, z, id, (byte) data); + return true; } int i = MainUtil.CACHE_I[y][x][z]; short[] v = this.result[i]; @@ -70,13 +117,61 @@ public class GenChunk extends PlotChunk { if (vd == null) { this.result_data[i] = vd = new byte[4096]; } - vd[j] = data; + vd[j] = (byte) data; } + return true; } @Override - public PlotChunk clone() { - GenChunk toReturn = new GenChunk(getChunkAbs(), getChunkWrapper()); + public PlotBlock getBlock(int x, int y, int z) { + int i = MainUtil.CACHE_I[y][x][z]; + if (result == null) { + MaterialData md = cd.getTypeAndData(x, y, z); + return PlotBlock.get(md.getItemTypeId(), md.getData()); + } + short[] array = result[i]; + if (array == null) { + return PlotBlock.get(0, 0); + } + int j = MainUtil.CACHE_J[y][x][z]; + short id = array[j]; + if (id == 0) { + return PlotBlock.get(id, 0); + } + byte[] dataArray = result_data[i]; + if (dataArray == null) { + return PlotBlock.get(id, 0); + } + return PlotBlock.get(id, dataArray[j]); + } + + public int getX() { + return chunk == null ? cx : chunk.getX(); + } + + public int getZ() { + return chunk == null ? cz : chunk.getZ(); + } + + @Override + public String getWorld() { + return chunk == null ? world : chunk.getWorld().getName(); + } + + @Override + public Location getMax() { + return new Location(getWorld(), 15 + (getX() << 4), 255, 15 + (getZ() << 4)); + } + + @Override + public Location getMin() { + return new Location(getWorld(), getX() << 4, 0, getZ() << 4); + } + + + + public GenChunk clone() { + GenChunk toReturn = new GenChunk(chunk, new ChunkWrapper(getWorld(), chunk.getX(), chunk.getZ())); if (this.result != null) { for (int i = 0; i < this.result.length; i++) { short[] matrix = this.result[i]; @@ -97,9 +192,8 @@ public class GenChunk extends PlotChunk { return toReturn; } - @Override - public PlotChunk shallowClone() { - GenChunk toReturn = new GenChunk(getChunkAbs(), getChunkWrapper()); + public GenChunk shallowClone() { + GenChunk toReturn = new GenChunk(chunk, new ChunkWrapper(getWorld(), chunk.getX(), chunk.getZ())); toReturn.result = this.result; toReturn.result_data = this.result_data; toReturn.cd = this.cd; diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/SlowChunk.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/SlowChunk.java deleted file mode 100644 index a87b4cdea..000000000 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/SlowChunk.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.plotsquared.bukkit.util.block; - -import com.intellectualcrafters.plot.object.PlotBlock; -import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; -import com.plotsquared.bukkit.util.BukkitUtil; -import org.bukkit.Chunk; - -public class SlowChunk extends PlotChunk { - - public PlotBlock[][] result = new PlotBlock[16][]; - public int[][] biomes; - - public SlowChunk(ChunkWrapper chunk) { - super(chunk); - } - - @Override - public Chunk getChunkAbs() { - ChunkWrapper loc = getChunkWrapper(); - return BukkitUtil.getWorld(loc.world).getChunkAt(loc.x, loc.z); - } - - @Override - public void setBiome(int x, int z, int biome) { - if (this.biomes == null) { - this.biomes = new int[16][16]; - } - this.biomes[x][z] = biome; - } - - @Override - public void setBlock(int x, int y, int z, int id, byte data) { - if (this.result[y >> 4] == null) { - this.result[y >> 4] = new PlotBlock[4096]; - } - this.result[MainUtil.CACHE_I[y][x][z]][MainUtil.CACHE_J[y][x][z]] = PlotBlock.get((short) id, data); - } - - @Override - public PlotChunk clone() { - SlowChunk toReturn = new SlowChunk(getChunkWrapper()); - for (int i = 0; i < this.result.length; i++) { - PlotBlock[] matrix = this.result[i]; - if (matrix != null) { - toReturn.result[i] = new PlotBlock[matrix.length]; - System.arraycopy(matrix, 0, toReturn.result[i], 0, matrix.length); - } - } - return toReturn; - } - - @Override - public PlotChunk shallowClone() { - SlowChunk toReturn = new SlowChunk(getChunkWrapper()); - toReturn.result = this.result; - return toReturn; - } -} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/SlowQueue.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/SlowQueue.java deleted file mode 100644 index 222269f75..000000000 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/block/SlowQueue.java +++ /dev/null @@ -1,287 +0,0 @@ -package com.plotsquared.bukkit.util.block; - -import com.intellectualcrafters.plot.PS; -import com.intellectualcrafters.plot.object.ChunkLoc; -import com.intellectualcrafters.plot.object.PlotBlock; -import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.PlotQueue; -import com.intellectualcrafters.plot.util.SetQueue; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; -import com.plotsquared.bukkit.util.BukkitUtil; -import org.bukkit.Chunk; -import org.bukkit.block.Biome; -import org.bukkit.block.Block; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; - -public class SlowQueue implements PlotQueue { - - private final ConcurrentHashMap> blocks = new ConcurrentHashMap<>(); - - public SlowQueue() { - MainUtil.initCache(); - } - - @Override - public boolean setBlock(String world, int x, int y, int z, short id, byte data) { - if (y > 255 || y < 0) { - return false; - } - ChunkWrapper wrap = SetQueue.IMP.new ChunkWrapper(world, x >> 4, z >> 4); - x = x & 15; - z = z & 15; - PlotChunk result = this.blocks.get(wrap); - if (result == null) { - result = getChunk(wrap); - result.setBlock(x, y, z, id, data); - PlotChunk previous = this.blocks.put(wrap, result); - if (previous == null) { - return true; - } - this.blocks.put(wrap, previous); - result = previous; - } - result.setBlock(x, y, z, id, data); - return true; - } - - @Override - public void setChunk(PlotChunk chunk) { - this.blocks.put(chunk.getChunkWrapper(), chunk); - } - - @Override - public PlotChunk next() { - if (!PS.get().isMainThread(Thread.currentThread())) { - throw new IllegalStateException("Must be called from main thread!"); - } - try { - if (this.blocks.isEmpty()) { - return null; - } - Iterator>> iterator = this.blocks.entrySet().iterator(); - PlotChunk toReturn = iterator.next().getValue(); - if (SetQueue.IMP.isWaiting()) { - return null; - } - iterator.remove(); - execute(toReturn); - fixLighting(toReturn, true); - return toReturn; - } catch (Throwable e) { - e.printStackTrace(); - return null; - } - } - - @Override - public PlotChunk next(ChunkWrapper wrap, boolean fixLighting) { - if (!PS.get().isMainThread(Thread.currentThread())) { - throw new IllegalStateException("Must be called from main thread!"); - } - try { - if (this.blocks.isEmpty()) { - return null; - } - PlotChunk toReturn = this.blocks.remove(wrap); - if (toReturn == null) { - return null; - } - execute(toReturn); - fixLighting(toReturn, fixLighting); - return toReturn; - } catch (Throwable e) { - e.printStackTrace(); - return null; - } - } - - @Override - public void clear() { - this.blocks.clear(); - } - - @Override - public void regenerateChunk(String world, ChunkLoc loc) { - BukkitUtil.getWorld(world).regenerateChunk(loc.x, loc.z); - } - - /** - * This should be overridden by any specialized queues. - * @param plotChunk - */ - public void execute(PlotChunk plotChunk) { - SlowChunk sc = (SlowChunk) plotChunk; - Chunk chunk = plotChunk.getChunk(); - chunk.load(true); - for (int i = 0; i < sc.result.length; i++) { - PlotBlock[] result2 = sc.result[i]; - if (result2 == null) { - continue; - } - for (int j = 0; j < 4096; j++) { - int x = MainUtil.x_loc[i][j]; - int y = MainUtil.y_loc[i][j]; - int z = MainUtil.z_loc[i][j]; - Block block = chunk.getBlock(x, y, z); - PlotBlock newBlock = result2[j]; - if (newBlock == null) { - continue; - } - switch (newBlock.id) { - case -1: - if (block.getData() == newBlock.data) { - continue; - } - block.setData(newBlock.data); - continue; - case 0: - case 2: - case 4: - case 13: - case 14: - case 15: - case 20: - case 21: - case 22: - case 25: - case 30: - case 32: - case 37: - case 39: - case 40: - case 41: - case 42: - case 45: - case 46: - case 47: - case 48: - case 49: - case 51: - case 52: - case 54: - case 55: - case 56: - case 57: - case 58: - case 60: - case 61: - case 62: - case 7: - case 8: - case 9: - case 10: - case 11: - case 73: - case 74: - case 78: - case 79: - case 80: - case 81: - case 82: - case 83: - case 84: - case 85: - case 87: - case 88: - case 101: - case 102: - case 103: - case 110: - case 112: - case 113: - case 117: - case 121: - case 122: - case 123: - case 124: - case 129: - case 133: - case 138: - case 137: - case 140: - case 165: - case 166: - case 169: - case 170: - case 172: - case 173: - case 174: - case 176: - case 177: - case 181: - case 182: - case 188: - case 189: - case 190: - case 191: - case 192: - if (block.getTypeId() == newBlock.id) { - continue; - } - block.setTypeId(newBlock.id, false); - continue; - default: - if (block.getTypeId() == newBlock.id && block.getData() == newBlock.data) { - continue; - } - if (newBlock.data == 0) { - block.setTypeId(newBlock.id, false); - } else { - block.setTypeIdAndData(newBlock.id, newBlock.data, false); - } - continue; - } - } - } - int[][] biomes = sc.biomes; - Biome[] values = Biome.values(); - if (biomes != null) { - for (int x = 0; x < 16; x++) { - int[] array = biomes[x]; - if (array == null) { - continue; - } - for (int z = 0; z < 16; z++) { - int biome = array[z]; - if (biome == 0) { - continue; - } - chunk.getBlock(x, 0, z).setBiome(values[biome]); - } - } - } - } - - /** - * This should be overridden by any specialized queues. - * @param wrap - */ - @Override - public PlotChunk getChunk(ChunkWrapper wrap) { - return new SlowChunk(wrap); - } - - /** - * This should be overridden by any specialized queues. - * @param fixAll - */ - @Override - public boolean fixLighting(PlotChunk chunk, boolean fixAll) { - // Do nothing - return true; - } - - /** - * This should be overridden by any specialized queues. - * @param locations - */ - @Override - public void sendChunk(String world, Collection locations) { - // Do nothing - } -} diff --git a/Core/src/main/java/com/intellectualcrafters/plot/IPlotMain.java b/Core/src/main/java/com/intellectualcrafters/plot/IPlotMain.java index 68901d2f1..aeacc6a88 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/IPlotMain.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/IPlotMain.java @@ -11,13 +11,12 @@ import com.intellectualcrafters.plot.util.ChunkManager; import com.intellectualcrafters.plot.util.EconHandler; import com.intellectualcrafters.plot.util.EventUtil; import com.intellectualcrafters.plot.util.InventoryUtil; -import com.intellectualcrafters.plot.util.PlotQueue; import com.intellectualcrafters.plot.util.SchematicHandler; import com.intellectualcrafters.plot.util.SetupUtils; import com.intellectualcrafters.plot.util.TaskManager; import com.intellectualcrafters.plot.util.UUIDHandlerImplementation; import com.intellectualcrafters.plot.util.WorldUtil; - +import com.intellectualcrafters.plot.util.block.QueueProvider; import java.io.File; import java.util.List; @@ -135,10 +134,10 @@ public interface IPlotMain extends ILogger { EconHandler getEconomyHandler(); /** - * Get the {@link PlotQueue} class. + * Get the {@link com.intellectualcrafters.plot.util.block.QueueProvider} class. * @return */ - PlotQueue initPlotQueue(); + QueueProvider initBlockQueue(); /** * Get the {@link WorldUtil} class. diff --git a/Core/src/main/java/com/intellectualcrafters/plot/PS.java b/Core/src/main/java/com/intellectualcrafters/plot/PS.java index 92248870f..ed42d2f9c 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/PS.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/PS.java @@ -39,18 +39,17 @@ import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.MathMan; import com.intellectualcrafters.plot.util.ReflectionUtils; import com.intellectualcrafters.plot.util.SchematicHandler; -import com.intellectualcrafters.plot.util.SetQueue; import com.intellectualcrafters.plot.util.SetupUtils; import com.intellectualcrafters.plot.util.StringMan; import com.intellectualcrafters.plot.util.TaskManager; import com.intellectualcrafters.plot.util.UUIDHandler; import com.intellectualcrafters.plot.util.WorldUtil; import com.intellectualcrafters.plot.util.area.QuadMap; +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; import com.intellectualcrafters.plot.util.expiry.ExpireManager; import com.intellectualcrafters.plot.util.expiry.ExpiryTask; import com.plotsquared.listener.WESubscriber; import com.sk89q.worldedit.WorldEdit; - import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -112,7 +111,6 @@ public class PS { public YamlConfiguration worlds; public YamlConfiguration storage; public YamlConfiguration commands; - public TaskManager TASK; public WorldEdit worldedit; public URL update; private ILogger logger; @@ -153,7 +151,7 @@ public class PS { if (getJavaVersion() < 1.8) { PS.log(C.CONSOLE_JAVA_OUTDATED_1_8); } - this.TASK = this.IMP.getTaskManager(); + TaskManager.IMP = this.IMP.getTaskManager(); setupConfigs(); this.translationFile = MainUtil.getFile(this.IMP.getDirectory(), Settings.Paths.TRANSLATIONS + File.separator + "PlotSquared.use_THIS.yml"); @@ -219,7 +217,8 @@ public class PS { // World Util WorldUtil.IMP = this.IMP.initWorldUtil(); // Set block - SetQueue.IMP.queue = this.IMP.initPlotQueue(); + GlobalBlockQueue.IMP = new GlobalBlockQueue(IMP.initBlockQueue(), 1); + GlobalBlockQueue.IMP.runTask(); // Set chunk ChunkManager.manager = this.IMP.initChunkManager(); // Schematic handler @@ -1802,7 +1801,7 @@ public class PS { */ public void disable() { try { - this.TASK = null; + TaskManager.IMP = null; this.database = null; // Validate that all data in the db is correct final HashSet plots = new HashSet<>(); diff --git a/Core/src/main/java/com/intellectualcrafters/plot/commands/Clear.java b/Core/src/main/java/com/intellectualcrafters/plot/commands/Clear.java index 925438343..e19aba3b4 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/commands/Clear.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/commands/Clear.java @@ -10,7 +10,7 @@ import com.intellectualcrafters.plot.object.RunnableVal2; import com.intellectualcrafters.plot.object.RunnableVal3; import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.Permissions; -import com.intellectualcrafters.plot.util.SetQueue; +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; import com.plotsquared.general.commands.Command; import com.plotsquared.general.commands.CommandDeclaration; @@ -45,7 +45,7 @@ public class Clear extends Command { @Override public void run() { plot.unlink(); - SetQueue.IMP.addTask(new Runnable() { + GlobalBlockQueue.IMP.addTask(new Runnable() { @Override public void run() { plot.removeRunning(); diff --git a/Core/src/main/java/com/intellectualcrafters/plot/commands/DebugExec.java b/Core/src/main/java/com/intellectualcrafters/plot/commands/DebugExec.java index 9d1346b6d..9e58bc6f3 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/commands/DebugExec.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/commands/DebugExec.java @@ -27,18 +27,17 @@ import com.intellectualcrafters.plot.util.EventUtil; import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.MathMan; import com.intellectualcrafters.plot.util.SchematicHandler; -import com.intellectualcrafters.plot.util.SetQueue; import com.intellectualcrafters.plot.util.SetupUtils; import com.intellectualcrafters.plot.util.StringMan; import com.intellectualcrafters.plot.util.TaskManager; import com.intellectualcrafters.plot.util.UUIDHandler; import com.intellectualcrafters.plot.util.WorldUtil; +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; import com.intellectualcrafters.plot.util.expiry.ExpireManager; import com.intellectualcrafters.plot.util.expiry.PlotAnalysis; import com.plotsquared.general.commands.Command; import com.plotsquared.general.commands.CommandDeclaration; import com.plotsquared.listener.WEManager; - import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -48,7 +47,6 @@ import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.UUID; - import javax.script.Bindings; import javax.script.ScriptContext; import javax.script.ScriptEngine; @@ -119,12 +117,12 @@ public class DebugExec extends SubCommand { // Instances this.scope.put("PS", PS.get()); - this.scope.put("SetQueue", SetQueue.IMP); + this.scope.put("GlobalBlockQueue", GlobalBlockQueue.IMP); this.scope.put("ExpireManager", ExpireManager.IMP); if (PS.get().worldedit != null) { this.scope.put("WEManager", new WEManager()); } - this.scope.put("TaskManager", PS.get().TASK); + this.scope.put("TaskManager", TaskManager.IMP); this.scope.put("TitleManager", AbstractTitle.TITLE_CLASS); this.scope.put("ConsolePlayer", ConsolePlayer.getConsole()); this.scope.put("SchematicHandler", SchematicHandler.manager); diff --git a/Core/src/main/java/com/intellectualcrafters/plot/commands/Relight.java b/Core/src/main/java/com/intellectualcrafters/plot/commands/Relight.java index 21bcfb646..4b20ca2c6 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/commands/Relight.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/commands/Relight.java @@ -8,8 +8,7 @@ import com.intellectualcrafters.plot.object.RunnableVal; import com.intellectualcrafters.plot.object.RunnableVal2; import com.intellectualcrafters.plot.object.RunnableVal3; import com.intellectualcrafters.plot.util.ChunkManager; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; import com.plotsquared.general.commands.Command; import com.plotsquared.general.commands.CommandDeclaration; import java.util.HashSet; @@ -28,12 +27,11 @@ public class Relight extends Command { return; } HashSet regions = plot.getRegions(); + final LocalBlockQueue queue = plot.getArea().getQueue(false); ChunkManager.chunkTask(plot, new RunnableVal() { @Override public void run(int[] value) { - SetQueue.ChunkWrapper cw = SetQueue.IMP.new ChunkWrapper(plot.getArea().worldname, value[0], value[1]); - PlotChunk pc = SetQueue.IMP.queue.getChunk(cw); - pc.fixLighting(); + queue.fixChunkLighting(value[0], value[1]); } }, new Runnable() { @Override diff --git a/Core/src/main/java/com/intellectualcrafters/plot/commands/Set.java b/Core/src/main/java/com/intellectualcrafters/plot/commands/Set.java index 893e2a33d..07de4b379 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/commands/Set.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/commands/Set.java @@ -12,10 +12,10 @@ import com.intellectualcrafters.plot.object.PlotManager; import com.intellectualcrafters.plot.object.PlotPlayer; import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.Permissions; -import com.intellectualcrafters.plot.util.SetQueue; import com.intellectualcrafters.plot.util.StringComparison; import com.intellectualcrafters.plot.util.StringMan; import com.intellectualcrafters.plot.util.WorldUtil; +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; import com.plotsquared.general.commands.Command; import com.plotsquared.general.commands.CommandDeclaration; @@ -114,7 +114,7 @@ public class Set extends SubCommand { current.setComponent(component, blocks); } MainUtil.sendMessage(player, C.GENERATING_COMPONENT); - SetQueue.IMP.addTask(new Runnable() { + GlobalBlockQueue.IMP.addTask(new Runnable() { @Override public void run() { plot.removeRunning(); diff --git a/Core/src/main/java/com/intellectualcrafters/plot/commands/Template.java b/Core/src/main/java/com/intellectualcrafters/plot/commands/Template.java index 2ff6d7ac0..e564cc961 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/commands/Template.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/commands/Template.java @@ -13,12 +13,11 @@ import com.intellectualcrafters.plot.object.PlotManager; import com.intellectualcrafters.plot.object.PlotPlayer; import com.intellectualcrafters.plot.object.SetupObject; import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.SetQueue; import com.intellectualcrafters.plot.util.SetupUtils; import com.intellectualcrafters.plot.util.TaskManager; import com.intellectualcrafters.plot.util.WorldUtil; +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; import com.plotsquared.general.commands.CommandDeclaration; - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -156,7 +155,7 @@ public class Template extends SubCommand { setup.step = new ConfigurationNode[0]; setup.world = world; SetupUtils.manager.setupWorld(setup); - SetQueue.IMP.addTask(new Runnable() { + GlobalBlockQueue.IMP.addTask(new Runnable() { @Override public void run() { MainUtil.sendMessage(player, "Done!"); diff --git a/Core/src/main/java/com/intellectualcrafters/plot/commands/Trim.java b/Core/src/main/java/com/intellectualcrafters/plot/commands/Trim.java index 1aeb53454..674c0cc3d 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/commands/Trim.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/commands/Trim.java @@ -13,6 +13,8 @@ import com.intellectualcrafters.plot.util.ChunkManager; import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.TaskManager; import com.intellectualcrafters.plot.util.WorldUtil; +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; import com.plotsquared.general.commands.CommandDeclaration; import java.io.File; @@ -189,10 +191,11 @@ public class Trim extends SubCommand { } } } + final LocalBlockQueue queue = GlobalBlockQueue.IMP.getNewQueue(world, false); TaskManager.objectTask(chunks, new RunnableVal() { @Override public void run(ChunkLoc value) { - ChunkManager.manager.regenerateChunk(world, value); + queue.regenChunk(value.x, value.z); } }, this); } diff --git a/Core/src/main/java/com/intellectualcrafters/plot/generator/AugmentedUtils.java b/Core/src/main/java/com/intellectualcrafters/plot/generator/AugmentedUtils.java index 21322cfa9..baa6d93df 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/generator/AugmentedUtils.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/generator/AugmentedUtils.java @@ -1,16 +1,16 @@ package com.intellectualcrafters.plot.generator; import com.intellectualcrafters.plot.PS; -import com.intellectualcrafters.plot.object.LazyResult; +import com.intellectualcrafters.plot.object.Location; import com.intellectualcrafters.plot.object.PlotArea; import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.object.PlotManager; import com.intellectualcrafters.plot.object.PseudoRandom; import com.intellectualcrafters.plot.object.RegionWrapper; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; - +import com.intellectualcrafters.plot.util.block.DelegateLocalBlockQueue; +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; +import com.intellectualcrafters.plot.util.block.ScopedLocalBlockQueue; import java.util.Set; public class AugmentedUtils { @@ -23,18 +23,10 @@ public class AugmentedUtils { enabled = true; } - public static boolean generate(final String world, final int cx, final int cz, LazyResult> lazyChunk) { + public static boolean generate(final String world, final int cx, final int cz, LocalBlockQueue queue) { if (!enabled) { return false; } - if (lazyChunk == null) { - lazyChunk = new LazyResult>() { - @Override - public PlotChunk create() { - return SetQueue.IMP.queue.getChunk(SetQueue.IMP.new ChunkWrapper(world, cx, cz)); - } - }; - } final int bx = cx << 4; final int bz = cz << 4; RegionWrapper region = new RegionWrapper(bx, bx + 15, bz, bz + 15); @@ -44,7 +36,6 @@ public class AugmentedUtils { } PseudoRandom r = new PseudoRandom(); r.state = (cx << 16) | (cz & 0xFFFF); - ChunkWrapper wrap = SetQueue.IMP.new ChunkWrapper(world, cx, cz); boolean toReturn = false; for (final PlotArea area : areas) { if (area.TYPE == 0) { @@ -57,8 +48,11 @@ public class AugmentedUtils { if (generator == null) { continue; } - final PlotChunk result = lazyChunk.getOrCreate(); - final PlotChunk primaryMask; + // Mask + if (queue == null) { + queue = GlobalBlockQueue.IMP.getNewQueue(world, false); + } + LocalBlockQueue primaryMask; // coords int bxx; int bzz; @@ -70,42 +64,29 @@ public class AugmentedUtils { bzz = Math.max(0, area.getRegion().minZ - bz); txx = Math.min(15, area.getRegion().maxX - bx); tzz = Math.min(15, area.getRegion().maxZ - bz); - primaryMask = new PlotChunk(wrap) { + primaryMask = new DelegateLocalBlockQueue(queue) { @Override - public Object getChunkAbs() { - return null; - } - - @Override - public void setBlock(int x, int y, int z, int id, byte data) { - if (area.contains(bx + x, bz + z)) { - result.setBlock(x, y, z, id, data); + public boolean setBlock(int x, int y, int z, int id, int data) { + if (area.contains(x, z)) { + return super.setBlock(x, y, z, id, data); } + return false; } @Override - public void setBiome(int x, int z, int biome) { - if (area.contains(bx + x, bz + z)) { - result.setBiome(x, z, biome); + public boolean setBiome(int x, int z, String biome) { + if (area.contains(x, z)) { + return super.setBiome(x, z, biome); } - } - - @Override - public PlotChunk clone() { - return null; - } - - @Override - public PlotChunk shallowClone() { - return null; + return false; } }; } else { bxx = bzz = 0; txx = tzz = 15; - primaryMask = result; + primaryMask = queue; } - PlotChunk secondaryMask; + LocalBlockQueue secondaryMask; PlotBlock air = PlotBlock.get((short) 0, (byte) 0); if (area.TERRAIN == 2) { PlotManager manager = area.getPlotManager(); @@ -118,7 +99,7 @@ public class AugmentedUtils { boolean can = manager.getPlotId(area, rx, 0, rz) == null; if (can) { for (int y = 1; y < 128; y++) { - result.setBlock(x, y, z, air); + queue.setBlock(x, y, z, air); } canPlace[x][z] = can; has = true; @@ -129,30 +110,18 @@ public class AugmentedUtils { continue; } toReturn = true; - secondaryMask = new PlotChunk(wrap) { + secondaryMask = new DelegateLocalBlockQueue(primaryMask) { @Override - public Object getChunkAbs() { - return null; - } - - @Override - public void setBlock(int x, int y, int z, int id, byte data) { + public boolean setBlock(int x, int y, int z, int id, int data) { if (canPlace[x][z]) { - primaryMask.setBlock(x, y, z, id, data); + return super.setBlock(x, y, z, id, data); } + return false; } @Override - public void setBiome(int x, int z, int biome) {} - - @Override - public PlotChunk clone() { - return null; - } - - @Override - public PlotChunk shallowClone() { - return null; + public boolean setBiome(int x, int y, String biome) { + return super.setBiome(x, y, biome); } }; } else { @@ -160,19 +129,17 @@ public class AugmentedUtils { for (int x = bxx; x <= txx; x++) { for (int z = bzz; z <= tzz; z++) { for (int y = 1; y < 128; y++) { - result.setBlock(x, y, z, air); + queue.setBlock(x, y, z, air); } } } toReturn = true; } - generator.generateChunk(secondaryMask, area, r); - generator.populateChunk(secondaryMask, area, r); - } - if (lazyChunk.get() != null) { - lazyChunk.get().addToQueue(); - lazyChunk.get().flush(false); + ScopedLocalBlockQueue scoped = new ScopedLocalBlockQueue(secondaryMask, new Location(area.worldname, bx, 0, bz), new Location(area.worldname, bx + 15, 255, bz + 15)); + generator.generateChunk(scoped, area, r); + generator.populateChunk(scoped, area, r); } + queue.flush(); return toReturn; } } diff --git a/Core/src/main/java/com/intellectualcrafters/plot/generator/ClassicPlotManager.java b/Core/src/main/java/com/intellectualcrafters/plot/generator/ClassicPlotManager.java index 8551f4fe4..f558bab1a 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/generator/ClassicPlotManager.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/generator/ClassicPlotManager.java @@ -7,9 +7,8 @@ import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.object.PlotId; import com.intellectualcrafters.plot.object.PseudoRandom; import com.intellectualcrafters.plot.object.RegionWrapper; -import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.SetQueue; - +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; import java.util.ArrayList; /** @@ -55,20 +54,22 @@ public class ClassicPlotManager extends SquarePlotManager { if (dpw.WALL_BLOCK.id != 0 || !dpw.WALL_BLOCK.equals(dpw.CLAIMED_WALL_BLOCK)) { setWall(dpw, plot.getId(), new PlotBlock[]{dpw.WALL_BLOCK}); } - SetQueue.IMP.addTask(whenDone); + GlobalBlockQueue.IMP.addTask(whenDone); return true; } public boolean setFloor(PlotArea plotArea, PlotId plotId, PlotBlock[] blocks) { Plot plot = plotArea.getPlotAbs(plotId); + LocalBlockQueue queue = plotArea.getQueue(false); if (plot.isBasePlot()) { ClassicPlotWorld dpw = (ClassicPlotWorld) plotArea; for (RegionWrapper region : plot.getRegions()) { Location pos1 = new Location(plotArea.worldname, region.minX, dpw.PLOT_HEIGHT, region.minZ); Location pos2 = new Location(plotArea.worldname, region.maxX, dpw.PLOT_HEIGHT, region.maxZ); - MainUtil.setCuboidAsync(plotArea.worldname, pos1, pos2, blocks); + queue.setCuboid(pos1, pos2, blocks); } } + queue.enqueue(); return true; } @@ -77,11 +78,13 @@ public class ClassicPlotManager extends SquarePlotManager { if (!plot.isBasePlot()) { return false; } + LocalBlockQueue queue = plotArea.getQueue(false); for (RegionWrapper region : plot.getRegions()) { Location pos1 = new Location(plotArea.worldname, region.minX, 1, region.minZ); Location pos2 = new Location(plotArea.worldname, region.maxX, 255, region.maxZ); - MainUtil.setCuboidAsync(plotArea.worldname, pos1, pos2, blocks); + queue.setCuboid(pos1, pos2, blocks); } + queue.enqueue(); return true; } @@ -91,11 +94,13 @@ public class ClassicPlotManager extends SquarePlotManager { return false; } ClassicPlotWorld dpw = (ClassicPlotWorld) plotArea; + LocalBlockQueue queue = plotArea.getQueue(false); for (RegionWrapper region : plot.getRegions()) { Location pos1 = new Location(plotArea.worldname, region.minX, dpw.PLOT_HEIGHT + 1, region.minZ); Location pos2 = new Location(plotArea.worldname, region.maxX, 255, region.maxZ); - MainUtil.setCuboidAsync(plotArea.worldname, pos1, pos2, blocks); + queue.setCuboid(pos1, pos2, blocks); } + queue.enqueue(); return true; } @@ -105,11 +110,13 @@ public class ClassicPlotManager extends SquarePlotManager { return false; } ClassicPlotWorld dpw = (ClassicPlotWorld) plotArea; + LocalBlockQueue queue = plotArea.getQueue(false); for (RegionWrapper region : plot.getRegions()) { Location pos1 = new Location(plotArea.worldname, region.minX, 1, region.minZ); Location pos2 = new Location(plotArea.worldname, region.maxX, dpw.PLOT_HEIGHT - 1, region.maxZ); - MainUtil.setCuboidAsync(plotArea.worldname, pos1, pos2, blocks); + queue.setCuboid(pos1, pos2, blocks); } + queue.enqueue(); return true; } @@ -120,8 +127,10 @@ public class ClassicPlotManager extends SquarePlotManager { } Location[] corners = plot.getCorners(); ClassicPlotWorld dpw = (ClassicPlotWorld) plotArea; - SetQueue.IMP.setBlock(plotArea.worldname, (corners[0].getX() + corners[1].getX()) / 2, dpw.PLOT_HEIGHT, + LocalBlockQueue queue = plotArea.getQueue(false); + queue.setBlock((corners[0].getX() + corners[1].getX()) / 2, dpw.PLOT_HEIGHT, (corners[0].getZ() + corners[1].getZ()) / 2, blocks[0]); + queue.enqueue(); return true; } @@ -134,11 +143,12 @@ public class ClassicPlotManager extends SquarePlotManager { Location bottom = plot.getBottomAbs(); Location top = plot.getExtendedTopAbs(); PseudoRandom random = new PseudoRandom(); + LocalBlockQueue queue = plotArea.getQueue(false); if (!plot.getMerged(0)) { int z = bottom.getZ(); for (int x = bottom.getX(); x <= top.getX(); x++) { for (int y = dpw.PLOT_HEIGHT; y <= 255; y++) { - SetQueue.IMP.setBlock(plotArea.worldname, x, y, z, blocks[random.random(blocks.length)]); + queue.setBlock(x, y, z, blocks[random.random(blocks.length)]); } } } @@ -146,7 +156,7 @@ public class ClassicPlotManager extends SquarePlotManager { int x = bottom.getX(); for (int z = bottom.getZ(); z <= top.getZ(); z++) { for (int y = dpw.PLOT_HEIGHT; y <= 255; y++) { - SetQueue.IMP.setBlock(plotArea.worldname, x, y, z, blocks[random.random(blocks.length)]); + queue.setBlock(x, y, z, blocks[random.random(blocks.length)]); } } } @@ -155,7 +165,7 @@ public class ClassicPlotManager extends SquarePlotManager { int z = top.getZ(); for (int x = bottom.getX(); x <= top.getX(); x++) { for (int y = dpw.PLOT_HEIGHT; y <= 255; y++) { - SetQueue.IMP.setBlock(plotArea.worldname, x, y, z, blocks[random.random(blocks.length)]); + queue.setBlock(x, y, z, blocks[random.random(blocks.length)]); } } } @@ -163,7 +173,7 @@ public class ClassicPlotManager extends SquarePlotManager { int x = top.getX(); for (int z = bottom.getZ(); z <= top.getZ(); z++) { for (int y = dpw.PLOT_HEIGHT; y <= 255; y++) { - SetQueue.IMP.setBlock(plotArea.worldname, x, y, z, blocks[random.random(blocks.length)]); + queue.setBlock(x, y, z, blocks[random.random(blocks.length)]); } } } @@ -171,9 +181,10 @@ public class ClassicPlotManager extends SquarePlotManager { for (RegionWrapper region : plot.getRegions()) { Location pos1 = new Location(plotArea.worldname, region.minX, 255, region.minZ); Location pos2 = new Location(plotArea.worldname, region.maxX, 255, region.maxZ); - MainUtil.setCuboidAsync(plotArea.worldname, pos1, pos2, blocks); + queue.setCuboid(pos1, pos2, blocks); } } + queue.enqueue(); return true; } @@ -186,11 +197,12 @@ public class ClassicPlotManager extends SquarePlotManager { Location bot = plot.getExtendedBottomAbs().subtract(plot.getMerged(3) ? 0 : 1, 0, plot.getMerged(0) ? 0 : 1); Location top = plot.getExtendedTopAbs().add(1, 0, 1); PseudoRandom random = new PseudoRandom(); + LocalBlockQueue queue = plotArea.getQueue(false); if (!plot.getMerged(0)) { int z = bot.getZ(); for (int x = bot.getX(); x < top.getX(); x++) { for (int y = 1; y <= dpw.WALL_HEIGHT; y++) { - SetQueue.IMP.setBlock(plotArea.worldname, x, y, z, blocks[random.random(blocks.length)]); + queue.setBlock(x, y, z, blocks[random.random(blocks.length)]); } } } @@ -198,7 +210,7 @@ public class ClassicPlotManager extends SquarePlotManager { int x = bot.getX(); for (int z = bot.getZ(); z < top.getZ(); z++) { for (int y = 1; y <= dpw.WALL_HEIGHT; y++) { - SetQueue.IMP.setBlock(plotArea.worldname, x, y, z, blocks[random.random(blocks.length)]); + queue.setBlock(x, y, z, blocks[random.random(blocks.length)]); } } } @@ -206,7 +218,7 @@ public class ClassicPlotManager extends SquarePlotManager { int z = top.getZ(); for (int x = bot.getX(); x < top.getX() + (plot.getMerged(1) ? 0 : 1); x++) { for (int y = 1; y <= dpw.WALL_HEIGHT; y++) { - SetQueue.IMP.setBlock(plotArea.worldname, x, y, z, blocks[random.random(blocks.length)]); + queue.setBlock(x, y, z, blocks[random.random(blocks.length)]); } } } @@ -214,10 +226,11 @@ public class ClassicPlotManager extends SquarePlotManager { int x = top.getX(); for (int z = bot.getZ(); z < top.getZ() + (plot.getMerged(2) ? 0 : 1); z++) { for (int y = 1; y <= dpw.WALL_HEIGHT; y++) { - SetQueue.IMP.setBlock(plotArea.worldname, x, y, z, blocks[random.random(blocks.length)]); + queue.setBlock(x, y, z, blocks[random.random(blocks.length)]); } } } + queue.enqueue(); return true; } @@ -230,31 +243,33 @@ public class ClassicPlotManager extends SquarePlotManager { Location bot = plot.getExtendedBottomAbs().subtract(plot.getMerged(3) ? 0 : 1, 0, plot.getMerged(0) ? 0 : 1); Location top = plot.getExtendedTopAbs().add(1, 0, 1); PseudoRandom random = new PseudoRandom(); + LocalBlockQueue queue = plotArea.getQueue(false); int y = dpw.WALL_HEIGHT + 1; if (!plot.getMerged(0)) { int z = bot.getZ(); for (int x = bot.getX(); x < top.getX(); x++) { - SetQueue.IMP.setBlock(plotArea.worldname, x, y, z, blocks[random.random(blocks.length)]); + queue.setBlock(x, y, z, blocks[random.random(blocks.length)]); } } if (!plot.getMerged(3)) { int x = bot.getX(); for (int z = bot.getZ(); z < top.getZ(); z++) { - SetQueue.IMP.setBlock(plotArea.worldname, x, y, z, blocks[random.random(blocks.length)]); + queue.setBlock(x, y, z, blocks[random.random(blocks.length)]); } } if (!plot.getMerged(2)) { int z = top.getZ(); for (int x = bot.getX(); x < top.getX() + (plot.getMerged(1) ? 0 : 1); x++) { - SetQueue.IMP.setBlock(plotArea.worldname, x, y, z, blocks[random.random(blocks.length)]); + queue.setBlock(x, y, z, blocks[random.random(blocks.length)]); } } if (!plot.getMerged(1)) { int x = top.getX(); for (int z = bot.getZ(); z < top.getZ() + (plot.getMerged(2) ? 0 : 1); z++) { - SetQueue.IMP.setBlock(plotArea.worldname, x, y, z, blocks[random.random(blocks.length)]); + queue.setBlock(x, y, z, blocks[random.random(blocks.length)]); } } + queue.enqueue(); return true; } @@ -270,24 +285,26 @@ public class ClassicPlotManager extends SquarePlotManager { int ex = sx + dpw.ROAD_WIDTH - 1; int sz = pos1.getZ() - 2; int ez = pos2.getZ() + 2; - MainUtil.setSimpleCuboidAsync(plotArea.worldname, + LocalBlockQueue queue = plotArea.getQueue(false); + queue.setCuboid( new Location(plotArea.worldname, sx, Math.min(dpw.WALL_HEIGHT, dpw.ROAD_HEIGHT) + 1, sz + 1), new Location(plotArea.worldname, ex, 255, ez - 1), PlotBlock.get((short) 0, (byte) 0)); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx, 0, sz + 1), + queue.setCuboid(new Location(plotArea.worldname, sx, 0, sz + 1), new Location(plotArea.worldname, ex, 0, ez - 1), PlotBlock.get((short) 7, (byte) 0)); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx, 1, sz + 1), + queue.setCuboid(new Location(plotArea.worldname, sx, 1, sz + 1), new Location(plotArea.worldname, sx, dpw.WALL_HEIGHT, ez - 1), dpw.WALL_FILLING); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx, dpw.WALL_HEIGHT + 1, sz + 1), + queue.setCuboid(new Location(plotArea.worldname, sx, dpw.WALL_HEIGHT + 1, sz + 1), new Location(plotArea.worldname, sx, dpw.WALL_HEIGHT + 1, ez - 1), dpw.WALL_BLOCK); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, ex, 1, sz + 1), + queue.setCuboid(new Location(plotArea.worldname, ex, 1, sz + 1), new Location(plotArea.worldname, ex, dpw.WALL_HEIGHT, ez - 1), dpw.WALL_FILLING); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, ex, dpw.WALL_HEIGHT + 1, sz + 1), + queue.setCuboid(new Location(plotArea.worldname, ex, dpw.WALL_HEIGHT + 1, sz + 1), new Location(plotArea.worldname, ex, dpw.WALL_HEIGHT + 1, ez - 1), dpw.WALL_BLOCK); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx + 1, 1, sz + 1), + queue.setCuboid(new Location(plotArea.worldname, sx + 1, 1, sz + 1), new Location(plotArea.worldname, ex - 1, dpw.ROAD_HEIGHT, ez - 1), dpw.ROAD_BLOCK); + queue.enqueue(); return true; } @@ -300,23 +317,25 @@ public class ClassicPlotManager extends SquarePlotManager { int ez = sz + dpw.ROAD_WIDTH - 1; int sx = pos1.getX() - 2; int ex = pos2.getX() + 2; - MainUtil.setSimpleCuboidAsync(plotArea.worldname, + LocalBlockQueue queue = plotArea.getQueue(false); + queue.setCuboid( new Location(plotArea.worldname, sx + 1, Math.min(dpw.WALL_HEIGHT, dpw.ROAD_HEIGHT) + 1, sz), new Location(plotArea.worldname, ex - 1, 255, ez), PlotBlock.get((short) 0, (byte) 0)); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx + 1, 0, sz), + queue.setCuboid(new Location(plotArea.worldname, sx + 1, 0, sz), new Location(plotArea.worldname, ex - 1, 0, ez), PlotBlock.get((short) 7, (byte) 0)); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx + 1, 1, sz), + queue.setCuboid(new Location(plotArea.worldname, sx + 1, 1, sz), new Location(plotArea.worldname, ex - 1, dpw.WALL_HEIGHT, sz), dpw.WALL_FILLING); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx + 1, dpw.WALL_HEIGHT + 1, sz), + queue.setCuboid(new Location(plotArea.worldname, sx + 1, dpw.WALL_HEIGHT + 1, sz), new Location(plotArea.worldname, ex - 1, dpw.WALL_HEIGHT + 1, sz), dpw.WALL_BLOCK); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx + 1, 1, ez), + queue.setCuboid(new Location(plotArea.worldname, sx + 1, 1, ez), new Location(plotArea.worldname, ex - 1, dpw.WALL_HEIGHT, ez), dpw.WALL_FILLING); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx + 1, dpw.WALL_HEIGHT + 1, ez), + queue.setCuboid(new Location(plotArea.worldname, sx + 1, dpw.WALL_HEIGHT + 1, ez), new Location(plotArea.worldname, ex - 1, dpw.WALL_HEIGHT + 1, ez), dpw.WALL_BLOCK); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx + 1, 1, sz + 1), + queue.setCuboid(new Location(plotArea.worldname, sx + 1, 1, sz + 1), new Location(plotArea.worldname, ex - 1, dpw.ROAD_HEIGHT, ez - 1), dpw.ROAD_BLOCK); + queue.enqueue(); return true; } @@ -328,13 +347,15 @@ public class ClassicPlotManager extends SquarePlotManager { int ex = sx + dpw.ROAD_WIDTH - 1; int sz = pos2.getZ() + 1; int ez = sz + dpw.ROAD_WIDTH - 1; - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx + 1, dpw.ROAD_HEIGHT + 1, sz + 1), + LocalBlockQueue queue = plotArea.getQueue(false); + queue.setCuboid(new Location(plotArea.worldname, sx + 1, dpw.ROAD_HEIGHT + 1, sz + 1), new Location(plotArea.worldname, ex - 1, 255, ez - 1), PlotBlock.get( (short) 0, (byte) 0)); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx + 1, 0, sz + 1), + queue.setCuboid(new Location(plotArea.worldname, sx + 1, 0, sz + 1), new Location(plotArea.worldname, ex - 1, 0, ez - 1), PlotBlock.get((short) 7, (byte) 0)); - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx + 1, 1, sz + 1), + queue.setCuboid(new Location(plotArea.worldname, sx + 1, 1, sz + 1), new Location(plotArea.worldname, ex - 1, dpw.ROAD_HEIGHT, ez - 1), dpw.ROAD_BLOCK); + queue.enqueue(); return true; } @@ -347,12 +368,14 @@ public class ClassicPlotManager extends SquarePlotManager { int ex = sx + dpw.ROAD_WIDTH - 1; int sz = pos1.getZ() - 1; int ez = pos2.getZ() + 1; - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx, Math.min(dpw.PLOT_HEIGHT, dpw.ROAD_HEIGHT) + 1, sz), + LocalBlockQueue queue = plotArea.getQueue(false); + queue.setCuboid(new Location(plotArea.worldname, sx, Math.min(dpw.PLOT_HEIGHT, dpw.ROAD_HEIGHT) + 1, sz), new Location(plotArea.worldname, ex, 255, ez), PlotBlock.get((short) 0, (byte) 0)); - MainUtil.setCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx, 1, sz + 1), + queue.setCuboid(new Location(plotArea.worldname, sx, 1, sz + 1), new Location(plotArea.worldname, ex, dpw.PLOT_HEIGHT - 1, ez - 1), dpw.MAIN_BLOCK); - MainUtil.setCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx, dpw.PLOT_HEIGHT, sz + 1), + queue.setCuboid(new Location(plotArea.worldname, sx, dpw.PLOT_HEIGHT, sz + 1), new Location(plotArea.worldname, ex, dpw.PLOT_HEIGHT, ez - 1), dpw.TOP_BLOCK); + queue.enqueue(); return true; } @@ -365,12 +388,14 @@ public class ClassicPlotManager extends SquarePlotManager { int ez = sz + dpw.ROAD_WIDTH - 1; int sx = pos1.getX() - 1; int ex = pos2.getX() + 1; - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx, Math.min(dpw.PLOT_HEIGHT, dpw.ROAD_HEIGHT) + 1, sz), + LocalBlockQueue queue = plotArea.getQueue(false); + queue.setCuboid(new Location(plotArea.worldname, sx, Math.min(dpw.PLOT_HEIGHT, dpw.ROAD_HEIGHT) + 1, sz), new Location(plotArea.worldname, ex, 255, ez), PlotBlock.get((short) 0, (byte) 0)); - MainUtil.setCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx + 1, 1, sz), + queue.setCuboid(new Location(plotArea.worldname, sx + 1, 1, sz), new Location(plotArea.worldname, ex - 1, dpw.PLOT_HEIGHT - 1, ez), dpw.MAIN_BLOCK); - MainUtil.setCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx + 1, dpw.PLOT_HEIGHT, sz), + queue.setCuboid(new Location(plotArea.worldname, sx + 1, dpw.PLOT_HEIGHT, sz), new Location(plotArea.worldname, ex - 1, dpw.PLOT_HEIGHT, ez), dpw.TOP_BLOCK); + queue.enqueue(); return true; } @@ -382,12 +407,14 @@ public class ClassicPlotManager extends SquarePlotManager { int ex = sx + dpw.ROAD_WIDTH - 1; int sz = location.getZ() + 1; int ez = sz + dpw.ROAD_WIDTH - 1; - MainUtil.setSimpleCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx, dpw.ROAD_HEIGHT + 1, sz), + LocalBlockQueue queue = plotArea.getQueue(false); + queue.setCuboid(new Location(plotArea.worldname, sx, dpw.ROAD_HEIGHT + 1, sz), new Location(plotArea.worldname, ex, 255, ez), PlotBlock.get((short) 0, (byte) 0)); - MainUtil.setCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx, 1, sz), + queue.setCuboid(new Location(plotArea.worldname, sx, 1, sz), new Location(plotArea.worldname, ex, dpw.ROAD_HEIGHT - 1, ez), dpw.MAIN_BLOCK); - MainUtil.setCuboidAsync(plotArea.worldname, new Location(plotArea.worldname, sx, dpw.ROAD_HEIGHT, sz), + queue.setCuboid(new Location(plotArea.worldname, sx, dpw.ROAD_HEIGHT, sz), new Location(plotArea.worldname, ex, dpw.ROAD_HEIGHT, ez), dpw.TOP_BLOCK); + queue.enqueue(); return true; } diff --git a/Core/src/main/java/com/intellectualcrafters/plot/generator/HybridGen.java b/Core/src/main/java/com/intellectualcrafters/plot/generator/HybridGen.java index 12049d377..e4da8f87a 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/generator/HybridGen.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/generator/HybridGen.java @@ -1,14 +1,15 @@ package com.intellectualcrafters.plot.generator; import com.intellectualcrafters.jnbt.CompoundTag; +import com.intellectualcrafters.plot.object.Location; import com.intellectualcrafters.plot.object.PlotArea; import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.object.PlotId; import com.intellectualcrafters.plot.object.PlotManager; import com.intellectualcrafters.plot.object.PseudoRandom; import com.intellectualcrafters.plot.util.MathMan; -import com.intellectualcrafters.plot.util.PlotChunk; import com.intellectualcrafters.plot.util.SchematicHandler; +import com.intellectualcrafters.plot.util.block.ScopedLocalBlockQueue; import java.util.HashMap; import java.util.Map.Entry; @@ -20,12 +21,12 @@ public class HybridGen extends IndependentPlotGenerator { } @Override - public void generateChunk(PlotChunk result, PlotArea settings, PseudoRandom random) { + public void generateChunk(ScopedLocalBlockQueue result, PlotArea settings, PseudoRandom random) { HybridPlotWorld hpw = (HybridPlotWorld) settings; // Biome for (short x = 0; x < 16; x++) { for (short z = 0; z < 16; z++) { - result.setBiome(x, z, hpw.PLOT_BIOME); + result.fillBiome(hpw.PLOT_BIOME); } } // Bedrock @@ -37,10 +38,11 @@ public class HybridGen extends IndependentPlotGenerator { } } // Coords - int cx = result.getX(); - int cz = result.getZ(); - int bx = (cx << 4) - hpw.ROAD_OFFSET_X; - int bz = (cz << 4) - hpw.ROAD_OFFSET_Z; + Location min = result.getMin(); + int cx = min.getX() >> 4; + int cz = min.getZ() >> 4; + int bx = (min.getX()) - hpw.ROAD_OFFSET_X; + int bz = (min.getZ()) - hpw.ROAD_OFFSET_Z; short rbx; if (bx < 0) { rbx = (short) (hpw.SIZE + (bx % hpw.SIZE)); @@ -183,11 +185,12 @@ public class HybridGen extends IndependentPlotGenerator { } @Override - public boolean populateChunk(PlotChunk result, PlotArea settings, PseudoRandom random) { + public boolean populateChunk(ScopedLocalBlockQueue result, PlotArea settings, PseudoRandom random) { HybridPlotWorld hpw = (HybridPlotWorld) settings; if (hpw.G_SCH_STATE != null) { - int cx = result.getX(); - int cz = result.getZ(); + Location min = result.getMin(); + int cx = min.getX() >> 4; + int cz = min.getZ() >> 4; int p1x = cx << 4; int p1z = cz << 4; int bx = p1x - hpw.ROAD_OFFSET_X; diff --git a/Core/src/main/java/com/intellectualcrafters/plot/generator/HybridPlotManager.java b/Core/src/main/java/com/intellectualcrafters/plot/generator/HybridPlotManager.java index c94334d22..2a50df195 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/generator/HybridPlotManager.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/generator/HybridPlotManager.java @@ -3,7 +3,6 @@ package com.intellectualcrafters.plot.generator; import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.commands.Template; import com.intellectualcrafters.plot.config.Settings; -import com.intellectualcrafters.plot.object.ChunkLoc; import com.intellectualcrafters.plot.object.FileBytes; import com.intellectualcrafters.plot.object.Location; import com.intellectualcrafters.plot.object.Plot; @@ -14,9 +13,8 @@ import com.intellectualcrafters.plot.object.RunnableVal; import com.intellectualcrafters.plot.util.ChunkManager; import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.MathMan; -import com.intellectualcrafters.plot.util.SetQueue; -import com.intellectualcrafters.plot.util.WorldUtil; - +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -72,6 +70,7 @@ public class HybridPlotManager extends ClassicPlotManager { private void createSchemAbs(HybridPlotWorld hpw, Location pos1, Location pos2, boolean clear) { int size = hpw.SIZE; + LocalBlockQueue queue = hpw.getQueue(false); for (int x = pos1.getX(); x <= pos2.getX(); x++) { short absX = (short) ((x - hpw.ROAD_OFFSET_X) % size); if (absX < 0) { @@ -85,16 +84,17 @@ public class HybridPlotManager extends ClassicPlotManager { HashMap blocks = hpw.G_SCH.get(MathMan.pair(absX, absZ)); if (clear) { for (short y = (short) 0; y <= hpw.SCHEMATIC_HEIGHT; y++) { - SetQueue.IMP.setBlock(hpw.worldname, x, y, z, 0); + queue.setBlock(x, y, z, 0); } } if (blocks != null) { for (Entry entry : blocks.entrySet()) { - SetQueue.IMP.setBlock(hpw.worldname, x, entry.getKey(), z, entry.getValue()); + queue.setBlock(x, entry.getKey(), z, entry.getValue()); } } } } + queue.enqueue(); } @Override @@ -157,13 +157,14 @@ public class HybridPlotManager extends ClassicPlotManager { bedrock = PlotBlock.get((short) 0, (byte) 0); } final PlotBlock air = PlotBlock.get((short) 0, (byte) 0); - final String biome = WorldUtil.IMP.getBiomeList()[dpw.PLOT_BIOME]; + final String biome = dpw.PLOT_BIOME; + final LocalBlockQueue queue = plotArea.getQueue(false); ChunkManager.chunkTask(pos1, pos2, new RunnableVal() { @Override public void run(int[] value) { // If the chunk isn't near the edge and it isn't an augmented world we can just regen the whole chunk if (canRegen && (value[6] == 0)) { - ChunkManager.manager.regenerateChunk(world, new ChunkLoc(value[0], value[1])); + queue.regenChunk(value[0], value[1]); return; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -174,25 +175,26 @@ public class HybridPlotManager extends ClassicPlotManager { // These two locations are for each component (e.g. bedrock, main block, floor, air) Location bot = new Location(world, value[2], 0, value[3]); Location top = new Location(world, value[4], 1, value[5]); - MainUtil.setSimpleCuboidAsync(world, bot, top, bedrock); + queue.setCuboid(bot, top, bedrock); // Each component has a different layer bot.setY(1); top.setY(dpw.PLOT_HEIGHT); - MainUtil.setCuboidAsync(world, bot, top, filling); + queue.setCuboid(bot, top, filling); bot.setY(dpw.PLOT_HEIGHT); top.setY(dpw.PLOT_HEIGHT + 1); - MainUtil.setCuboidAsync(world, bot, top, plotfloor); + queue.setCuboid(bot, top, plotfloor); bot.setY(dpw.PLOT_HEIGHT + 1); top.setY(256); - MainUtil.setSimpleCuboidAsync(world, bot, top, air); + queue.setCuboid(bot, top, air); // And finally set the schematic, the y value is unimportant for this function pastePlotSchematic(dpw, bot, top); } }, new Runnable() { @Override public void run() { + queue.enqueue(); // And notify whatever called this when plot clearing is done - SetQueue.IMP.addTask(whenDone); + GlobalBlockQueue.IMP.addTask(whenDone); } }, 10); return true; diff --git a/Core/src/main/java/com/intellectualcrafters/plot/generator/HybridUtils.java b/Core/src/main/java/com/intellectualcrafters/plot/generator/HybridUtils.java index 1beb8856b..4c8064435 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/generator/HybridUtils.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/generator/HybridUtils.java @@ -8,7 +8,6 @@ import com.intellectualcrafters.plot.flag.Flags; import com.intellectualcrafters.plot.object.ChunkLoc; import com.intellectualcrafters.plot.object.Location; import com.intellectualcrafters.plot.object.Plot; -import com.intellectualcrafters.plot.util.expiry.PlotAnalysis; import com.intellectualcrafters.plot.object.PlotArea; import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.object.PlotId; @@ -18,9 +17,10 @@ import com.intellectualcrafters.plot.object.RunnableVal; import com.intellectualcrafters.plot.util.ChunkManager; import com.intellectualcrafters.plot.util.MathMan; import com.intellectualcrafters.plot.util.SchematicHandler; -import com.intellectualcrafters.plot.util.SetQueue; import com.intellectualcrafters.plot.util.TaskManager; - +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; +import com.intellectualcrafters.plot.util.expiry.PlotAnalysis; import java.io.File; import java.util.ArrayDeque; import java.util.ArrayList; @@ -107,7 +107,27 @@ public abstract class HybridUtils { run.run(); } - public abstract int checkModified(String world, int x1, int x2, int y1, int y2, int z1, int z2, PlotBlock[] blocks); + public int checkModified(LocalBlockQueue queue, int x1, int x2, int y1, int y2, int z1, int z2, PlotBlock[] blocks) { + int count = 0; + for (int y = y1; y <= y2; y++) { + for (int x = x1; x <= x2; x++) { + for (int z = z1; z <= z2; z++) { + PlotBlock block = queue.getBlock(x, y, z); + boolean same = false; + for (PlotBlock p : blocks) { + if (block.id == p.id) { + same = true; + break; + } + } + if (!same) { + count++; + } + } + } + } + return count; + } public final ArrayList getChunks(ChunkLoc region) { ArrayList chunks = new ArrayList<>(); @@ -139,6 +159,7 @@ public abstract class HybridUtils { whenDone.value = 0; final ClassicPlotWorld cpw = (ClassicPlotWorld) plotArea; final ArrayDeque zones = new ArrayDeque<>(plot.getRegions()); + final LocalBlockQueue queue = GlobalBlockQueue.IMP.getNewQueue(cpw.worldname, false); Runnable run = new Runnable() { @Override public void run() { @@ -159,11 +180,10 @@ public abstract class HybridUtils { int bz = value[3]; int ex = value[4]; int ez = value[5]; - whenDone.value += checkModified(plot.getArea().worldname, bx, ex, 1, cpw.PLOT_HEIGHT - 1, bz, ez, cpw.MAIN_BLOCK); - whenDone.value += checkModified(plot.getArea().worldname, bx, ex, cpw.PLOT_HEIGHT, cpw.PLOT_HEIGHT, bz, ez, cpw.TOP_BLOCK); + whenDone.value += checkModified(queue, bx, ex, 1, cpw.PLOT_HEIGHT - 1, bz, ez, cpw.MAIN_BLOCK); + whenDone.value += checkModified(queue, bx, ex, cpw.PLOT_HEIGHT, cpw.PLOT_HEIGHT, bz, ez, cpw.TOP_BLOCK); whenDone.value += checkModified( - plot.getArea().worldname, bx, ex, cpw.PLOT_HEIGHT + 1, 255, bz, ez, - new PlotBlock[]{PlotBlock.get((short) 0, (byte) 0)}); + queue, bx, ex, cpw.PLOT_HEIGHT + 1, 255, bz, ez, new PlotBlock[]{PlotBlock.get((short) 0, (byte) 0)}); } }, this, 5); @@ -281,7 +301,7 @@ public abstract class HybridUtils { PS.debug("&d - Potentially skipping 1024 chunks"); PS.debug("&d - TODO: recommend chunkster if corrupt"); } - SetQueue.IMP.addTask(new Runnable() { + GlobalBlockQueue.IMP.addTask(new Runnable() { @Override public void run() { TaskManager.runTaskLater(task, 20); @@ -297,6 +317,7 @@ public abstract class HybridUtils { public boolean setupRoadSchematic(Plot plot) { final String world = plot.getArea().worldname; + final LocalBlockQueue queue = GlobalBlockQueue.IMP.getNewQueue(world, false); Location bot = plot.getBottomAbs().subtract(1, 0, 1); Location top = plot.getTopAbs(); final HybridPlotWorld plotworld = (HybridPlotWorld) plot.getArea(); @@ -305,10 +326,10 @@ public abstract class HybridUtils { int sy = plotworld.ROAD_HEIGHT; int ex = bot.getX(); int ez = top.getZ(); - int ey = get_ey(world, sx, ex, sz, ez, sy); + int ey = get_ey(queue, sx, ex, sz, ez, sy); int bz = sz - plotworld.ROAD_WIDTH; int tz = sz - 1; - int ty = get_ey(world, sx, ex, bz, tz, sy); + int ty = get_ey(queue, sx, ex, bz, tz, sy); Set sideRoad = new HashSet<>(Collections.singletonList(new RegionWrapper(sx, ex, sy, ey, sz, ez))); final Set intersection = new HashSet<>(Collections.singletonList(new RegionWrapper(sx, ex, sy, ty, bz, tz))); @@ -332,7 +353,22 @@ public abstract class HybridUtils { return true; } - public abstract int get_ey(String world, int sx, int ex, int sz, int ez, int sy); + public int get_ey(LocalBlockQueue queue, int sx, int ex, int sz, int ez, int sy) { + int ey = sy; + for (int x = sx; x <= ex; x++) { + for (int z = sz; z <= ez; z++) { + for (int y = sy; y < 256; y++) { + if (y > ey) { + PlotBlock block = queue.getBlock(x, y, z); + if (block.id != 0) { + ey = y; + } + } + } + } + } + return ey; + } public boolean regenerateRoad(final PlotArea area, final ChunkLoc chunk, int extend) { int x = chunk.x << 4; @@ -359,6 +395,7 @@ public abstract class HybridUtils { PlotId id2 = manager.getPlotId(plotWorld, ex, 0, ez); x -= plotWorld.ROAD_OFFSET_X; z -= plotWorld.ROAD_OFFSET_Z; + LocalBlockQueue queue = GlobalBlockQueue.IMP.getNewQueue(plotWorld.worldname, false); if (id1 == null || id2 == null || id1 != id2) { boolean result = ChunkManager.manager.loadChunk(area.worldname, chunk, false); if (result) { @@ -399,26 +436,20 @@ public abstract class HybridUtils { if (condition) { HashMap blocks = plotWorld.G_SCH.get(MathMan.pair(absX, absZ)); for (short y = (short) plotWorld.ROAD_HEIGHT; y <= plotWorld.ROAD_HEIGHT + plotWorld.SCHEMATIC_HEIGHT + extend; y++) { - SetQueue.IMP.setBlock(area.worldname, x + X + plotWorld.ROAD_OFFSET_X, y, z + Z + plotWorld.ROAD_OFFSET_Z, 0); + queue.setBlock(x + X + plotWorld.ROAD_OFFSET_X, y, z + Z + plotWorld.ROAD_OFFSET_Z, 0); } if (blocks != null) { for (Entry entry : blocks.entrySet()) { - SetQueue.IMP.setBlock(area.worldname, x + X + plotWorld.ROAD_OFFSET_X, entry.getKey(), - z + Z + plotWorld.ROAD_OFFSET_Z, entry.getValue()); + queue.setBlock(x + X + plotWorld.ROAD_OFFSET_X, entry.getKey(), z + Z + plotWorld.ROAD_OFFSET_Z, entry.getValue()); } } } } } - SetQueue.IMP.addTask(new Runnable() { - @Override - public void run() { - ChunkManager.manager.unloadChunk(area.worldname, chunk, true, true); - } - }); return true; } } + queue.enqueue(); return false; } } diff --git a/Core/src/main/java/com/intellectualcrafters/plot/generator/IndependentPlotGenerator.java b/Core/src/main/java/com/intellectualcrafters/plot/generator/IndependentPlotGenerator.java index 81ac2d5a5..7cdbc7613 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/generator/IndependentPlotGenerator.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/generator/IndependentPlotGenerator.java @@ -6,7 +6,7 @@ import com.intellectualcrafters.plot.object.PlotId; import com.intellectualcrafters.plot.object.PlotManager; import com.intellectualcrafters.plot.object.PseudoRandom; import com.intellectualcrafters.plot.object.SetupObject; -import com.intellectualcrafters.plot.util.PlotChunk; +import com.intellectualcrafters.plot.util.block.ScopedLocalBlockQueue; /** * This class allows for implementation independent world generation. @@ -28,9 +28,9 @@ public abstract class IndependentPlotGenerator { * @param settings * @param random */ - public abstract void generateChunk(PlotChunk result, PlotArea settings, PseudoRandom random); + public abstract void generateChunk(ScopedLocalBlockQueue result, PlotArea settings, PseudoRandom random); - public boolean populateChunk(PlotChunk result, PlotArea settings, PseudoRandom random) { + public boolean populateChunk(ScopedLocalBlockQueue result, PlotArea settings, PseudoRandom random) { return false; } diff --git a/Core/src/main/java/com/intellectualcrafters/plot/object/ChunkWrapper.java b/Core/src/main/java/com/intellectualcrafters/plot/object/ChunkWrapper.java new file mode 100644 index 000000000..82901c820 --- /dev/null +++ b/Core/src/main/java/com/intellectualcrafters/plot/object/ChunkWrapper.java @@ -0,0 +1,45 @@ +package com.intellectualcrafters.plot.object; + +import com.intellectualcrafters.plot.util.MathMan; +import com.intellectualcrafters.plot.util.StringMan; + +public class ChunkWrapper { + + public final int x; + public final int z; + public final String world; + + public ChunkWrapper(String world, int x, int z) { + this.world = world; + this.x = x; + this.z = z; + } + + @Override + public int hashCode() { + return MathMan.pair((short) x, (short) z); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (this.hashCode() != obj.hashCode()) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ChunkWrapper other = (ChunkWrapper) obj; + return (this.x == other.x) && (this.z == other.z) && StringMan.isEqual(this.world, other.world); + } + + @Override + public String toString() { + return this.world + ":" + this.x + "," + this.z; + } +} \ No newline at end of file diff --git a/Core/src/main/java/com/intellectualcrafters/plot/object/Plot.java b/Core/src/main/java/com/intellectualcrafters/plot/object/Plot.java index af05f693f..478086c23 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/object/Plot.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/object/Plot.java @@ -17,14 +17,14 @@ import com.intellectualcrafters.plot.util.EventUtil; import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.Permissions; import com.intellectualcrafters.plot.util.SchematicHandler; -import com.intellectualcrafters.plot.util.SetQueue; import com.intellectualcrafters.plot.util.StringMan; import com.intellectualcrafters.plot.util.TaskManager; import com.intellectualcrafters.plot.util.UUIDHandler; import com.intellectualcrafters.plot.util.WorldUtil; +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; import com.intellectualcrafters.plot.util.expiry.PlotAnalysis; import com.plotsquared.listener.PlotListener; - import java.awt.Rectangle; import java.awt.geom.Area; import java.awt.geom.PathIterator; @@ -782,7 +782,7 @@ public class Plot { manager.claimPlot(Plot.this.area, current); } } - SetQueue.IMP.addTask(run); + GlobalBlockQueue.IMP.addTask(run); return; } Plot current = queue.poll(); @@ -794,10 +794,11 @@ public class Plot { } }; if (!isMerged() && this.area.getRegion().equals(getLargestRegion())) { + final LocalBlockQueue blockQueue = area.getQueue(false); ChunkManager.largeRegionTask(this.area.worldname, this.area.getRegion(), new RunnableVal() { @Override public void run(ChunkLoc value) { - ChunkManager.manager.regenerateChunk(Plot.this.area.worldname, value); + blockQueue.regenChunk(value.x, value.z); } }, whenDone); } else { @@ -1246,20 +1247,17 @@ public class Plot { * This should not need to be called */ public void refreshChunks() { - TaskManager.runTask(new Runnable() { - @Override - public void run() { - HashSet chunks = new HashSet<>(); - for (RegionWrapper region : Plot.this.getRegions()) { - for (int x = region.minX >> 4; x <= region.maxX >> 4; x++) { - for (int z = region.minZ >> 4; z <= region.maxZ >> 4; z++) { - chunks.add(new ChunkLoc(x, z)); - } + LocalBlockQueue queue = GlobalBlockQueue.IMP.getNewQueue(area.worldname, false); + HashSet chunks = new HashSet<>(); + for (RegionWrapper region : Plot.this.getRegions()) { + for (int x = region.minX >> 4; x <= region.maxX >> 4; x++) { + for (int z = region.minZ >> 4; z <= region.maxZ >> 4; z++) { + if (chunks.add(new ChunkLoc(x, z))) { + queue.refreshChunk(x, z); } } - SetQueue.IMP.queue.sendChunk(Plot.this.area.worldname, chunks); } - }); + } } /** Remove the plot sign if it is set. */ @@ -1269,7 +1267,9 @@ public class Plot { return; } Location loc = manager.getSignLoc(this.area, this); - SetQueue.IMP.setBlock(this.area.worldname, loc.getX(), loc.getY(), loc.getZ(), 0); + LocalBlockQueue queue = GlobalBlockQueue.IMP.getNewQueue(area.worldname, false); + queue.setBlock(loc.getX(), loc.getY(), loc.getZ(), 0); + queue.flush(); } /** Set the plot sign if plot signs are enabled. */ diff --git a/Core/src/main/java/com/intellectualcrafters/plot/object/PlotArea.java b/Core/src/main/java/com/intellectualcrafters/plot/object/PlotArea.java index ecb85aa64..16bd991a7 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/object/PlotArea.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/object/PlotArea.java @@ -15,9 +15,9 @@ import com.intellectualcrafters.plot.util.EventUtil; import com.intellectualcrafters.plot.util.MainUtil; import com.intellectualcrafters.plot.util.PlotGameMode; import com.intellectualcrafters.plot.util.StringMan; -import com.intellectualcrafters.plot.util.WorldUtil; import com.intellectualcrafters.plot.util.area.QuadMap; - +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -48,7 +48,7 @@ public abstract class PlotArea { public boolean ALLOW_SIGNS = true; public boolean MOB_SPAWNING = false; public boolean MOB_SPAWNER_SPAWNING = false; - public int PLOT_BIOME = 1; + public String PLOT_BIOME = "FOREST"; public boolean PLOT_CHAT = false; public boolean SCHEMATIC_CLAIM_SPECIFY = false; public boolean SCHEMATIC_ON_CLAIM = false; @@ -91,6 +91,10 @@ public abstract class PlotArea { this.worldhash = worldName.hashCode(); } + public LocalBlockQueue getQueue(boolean autoQueue) { + return GlobalBlockQueue.IMP.getNewQueue(worldname, autoQueue); + } + /** * Create a new PlotArea object with no functionality/information. * - Mainly used during startup before worlds are created as a temporary object @@ -211,7 +215,7 @@ public abstract class PlotArea { this.AUTO_MERGE = config.getBoolean("plot.auto_merge"); this.MAX_PLOT_MEMBERS = config.getInt("limits.max-members"); this.ALLOW_SIGNS = config.getBoolean("plot.create_signs"); - this.PLOT_BIOME = WorldUtil.IMP.getBiomeFromString(Configuration.BIOME.parseString(config.getString("plot.biome"))); + this.PLOT_BIOME = Configuration.BIOME.parseString(config.getString("plot.biome")); this.SCHEMATIC_ON_CLAIM = config.getBoolean("schematic.on_claim"); this.SCHEMATIC_FILE = config.getString("schematic.file"); this.SCHEMATIC_CLAIM_SPECIFY = config.getBoolean("schematic.specify_on_claim"); diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/ChunkManager.java b/Core/src/main/java/com/intellectualcrafters/plot/util/ChunkManager.java index ff93b902b..00cf94cdd 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/util/ChunkManager.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/util/ChunkManager.java @@ -4,24 +4,23 @@ import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.object.ChunkLoc; import com.intellectualcrafters.plot.object.Location; import com.intellectualcrafters.plot.object.Plot; -import com.intellectualcrafters.plot.object.PlotPlayer; import com.intellectualcrafters.plot.object.RegionWrapper; import com.intellectualcrafters.plot.object.RunnableVal; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; - +import com.intellectualcrafters.plot.util.block.GlobalBlockQueue; +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; +import com.intellectualcrafters.plot.util.block.OffsetLocalBlockQueue; +import com.intellectualcrafters.plot.util.block.ScopedLocalBlockQueue; import java.io.File; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; -import java.util.Map; import java.util.Set; public abstract class ChunkManager { public static ChunkManager manager = null; - private static RunnableVal> CURRENT_FORCE_CHUNK; - private static RunnableVal> CURRENT_ADD_CHUNK; + private static RunnableVal CURRENT_FORCE_CHUNK; + private static RunnableVal CURRENT_ADD_CHUNK; public static ChunkLoc getChunkChunk(Location location) { int x = location.getX() >> 9; @@ -29,40 +28,41 @@ public abstract class ChunkManager { return new ChunkLoc(x, z); } - public static void setChunkInPlotArea(RunnableVal> force, RunnableVal> add, String world, ChunkLoc loc) { + public static void setChunkInPlotArea(RunnableVal force, RunnableVal add, String world, ChunkLoc loc) { + LocalBlockQueue queue = GlobalBlockQueue.IMP.getNewQueue(world, false); if (PS.get().isAugmented(world)) { - ChunkWrapper wrap = SetQueue.IMP.new ChunkWrapper(world, loc.x, loc.z); - PlotChunk chunk = SetQueue.IMP.queue.getChunk(wrap); + OffsetLocalBlockQueue offset = new OffsetLocalBlockQueue(queue, loc.x >> 4, 0, loc.z >> 4); + ScopedLocalBlockQueue scoped = new ScopedLocalBlockQueue(offset, new Location(world, 0, 0, 0), new Location(world, 15, 255, 15)); if (force != null) { - force.run(chunk); + force.run(scoped); + } else { + scoped.regenChunk(loc.x, loc.z); + if (add != null) { + add.run(scoped); + } } - manager.regenerateChunk(world, loc); - if (add != null) { - add.run(chunk); - } - chunk.addToQueue(); - chunk.flush(true); + queue.flush(); } else { CURRENT_FORCE_CHUNK = force; CURRENT_ADD_CHUNK = add; - manager.regenerateChunk(world, loc); + queue.regenChunk(loc.x, loc.z); CURRENT_FORCE_CHUNK = null; CURRENT_ADD_CHUNK = null; } } - public static boolean preProcessChunk(PlotChunk chunk) { + public static boolean preProcessChunk(ScopedLocalBlockQueue queue) { if (CURRENT_FORCE_CHUNK != null) { - CURRENT_FORCE_CHUNK.run(chunk); + CURRENT_FORCE_CHUNK.run(queue); CURRENT_FORCE_CHUNK = null; return true; } return false; } - public static boolean postProcessChunk(PlotChunk chunk) { + public static boolean postProcessChunk(ScopedLocalBlockQueue queue) { if (CURRENT_ADD_CHUNK != null) { - CURRENT_ADD_CHUNK.run(chunk); + CURRENT_ADD_CHUNK.run(queue); CURRENT_ADD_CHUNK = null; return true; } @@ -228,20 +228,6 @@ public abstract class ChunkManager { return chunks; } - public void regenerateChunk(String world, ChunkLoc loc) { - SetQueue.IMP.regenerateChunk(world, loc); - SetQueue.IMP.queue.sendChunk(world, Collections.singletonList(loc)); - for (Map.Entry entry : UUIDHandler.getPlayers().entrySet()) { - PlotPlayer pp = entry.getValue(); - Location pLoc = pp.getLocation(); - if (!StringMan.isEqual(world, pLoc.getWorld()) || !pLoc.getChunkLoc().equals(loc)) { - continue; - } - pLoc.setY(WorldUtil.IMP.getHighestBlock(world, pLoc.getX(), pLoc.getZ())); - pp.teleport(pLoc); - } - } - public void deleteRegionFiles(String world, Collection chunks) { deleteRegionFiles(world, chunks, null); } diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/MainUtil.java b/Core/src/main/java/com/intellectualcrafters/plot/util/MainUtil.java index 6630bd7a1..3622067a3 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/util/MainUtil.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/util/MainUtil.java @@ -8,18 +8,14 @@ import com.intellectualcrafters.plot.database.DBFunc; import com.intellectualcrafters.plot.flag.Flag; import com.intellectualcrafters.plot.flag.FlagManager; import com.intellectualcrafters.plot.flag.Flags; -import com.intellectualcrafters.plot.object.ChunkLoc; import com.intellectualcrafters.plot.object.ConsolePlayer; import com.intellectualcrafters.plot.object.Location; import com.intellectualcrafters.plot.object.Plot; import com.intellectualcrafters.plot.object.PlotArea; -import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.object.PlotId; import com.intellectualcrafters.plot.object.PlotPlayer; -import com.intellectualcrafters.plot.object.PseudoRandom; import com.intellectualcrafters.plot.object.RegionWrapper; import com.intellectualcrafters.plot.object.RunnableVal; - import java.io.File; import java.io.IOException; import java.io.OutputStream; @@ -202,7 +198,7 @@ public class MainUtil { * @return true if any changes were made */ public static boolean resetBiome(PlotArea area, Location pos1, Location pos2) { - String biome = WorldUtil.IMP.getBiomeList()[area.PLOT_BIOME]; + String biome = area.PLOT_BIOME; if (!StringMan.isEqual(WorldUtil.IMP.getBiome(area.worldname, (pos1.getX() + pos2.getX()) / 2, (pos1.getZ() + pos2.getZ()) / 2), biome)) { MainUtil.setBiome(area.worldname, pos1.getX(), pos1.getZ(), pos2.getX(), pos2.getZ(), biome); return true; @@ -491,15 +487,6 @@ public class MainUtil { return area.getPlotAbs(id); } - /** - * Resend the chunk at a location. - * @param world - * @param loc - */ - public static void update(String world, ChunkLoc loc) { - SetQueue.IMP.queue.sendChunk(world, Collections.singletonList(loc)); - } - public static File getFile(File base, String path) { if (Paths.get(path).isAbsolute()) { return new File(path); @@ -507,46 +494,6 @@ public class MainUtil { return new File(base, path); } - /** - * Set a cuboid asynchronously to a set of blocks. - * @param world - * @param pos1 - * @param pos2 - * @param blocks - */ - public static void setCuboidAsync(String world, Location pos1, Location pos2, PlotBlock[] blocks) { - if (blocks.length == 1) { - setSimpleCuboidAsync(world, pos1, pos2, blocks[0]); - return; - } - for (int y = pos1.getY(); y <= Math.min(255, pos2.getY()); y++) { - for (int x = pos1.getX(); x <= pos2.getX(); x++) { - for (int z = pos1.getZ(); z <= pos2.getZ(); z++) { - int i = PseudoRandom.random.random(blocks.length); - PlotBlock block = blocks[i]; - SetQueue.IMP.setBlock(world, x, y, z, block); - } - } - } - } - - /** - * Set a cuboid asynchronously to a block. - * @param world - * @param pos1 - * @param pos2 - * @param newBlock - */ - public static void setSimpleCuboidAsync(String world, Location pos1, Location pos2, PlotBlock newBlock) { - for (int y = pos1.getY(); y <= Math.min(255, pos2.getY()); y++) { - for (int x = pos1.getX(); x <= pos2.getX(); x++) { - for (int z = pos1.getZ(); z <= pos2.getZ(); z++) { - SetQueue.IMP.setBlock(world, x, y, z, newBlock); - } - } - } - } - /** * Synchronously set the biome in a selection. * @param world diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/MathMan.java b/Core/src/main/java/com/intellectualcrafters/plot/util/MathMan.java index 6e9c67833..eddd75621 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/util/MathMan.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/util/MathMan.java @@ -21,6 +21,120 @@ public class MathMan { } } + private final static int[] table = { + 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, + 59, 61, 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, + 84, 86, 87, 89, 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, + 103, 104, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, 128, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145, + 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, 156, 157, + 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, + 169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, + 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, + 189, 189, 190, 191, 192, 192, 193, 193, 194, 195, 195, 196, 197, 197, + 198, 199, 199, 200, 201, 201, 202, 203, 203, 204, 204, 205, 206, 206, + 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 213, 214, 214, 215, + 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, 221, 222, 222, 223, + 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, 230, 231, + 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, + 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, + 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, + 253, 254, 254, 255 + }; + + public static long pairInt(int x, int y) { + return (((long)x) << 32) | (y & 0xffffffffL); + } + + public static int unpairIntX(long pair) { + return (int)(pair >> 32); + } + + public static int unpairIntY(long pair) { + return (int)pair; + } + + public static byte pair16(byte x, byte y) { + return (byte) (x + (y << 4)); + } + + public static byte unpair16x(byte value) { + return (byte) (value & 0xF); + } + + public static byte unpair16y(byte value) { + return (byte) ((value >> 4) & 0xF); + } + + public static int sqrt(int x) { + int xn; + + if (x >= 0x10000) { + if (x >= 0x1000000) { + if (x >= 0x10000000) { + if (x >= 0x40000000) { + xn = table[x >> 24] << 8; + } else { + xn = table[x >> 22] << 7; + } + } else { + if (x >= 0x4000000) { + xn = table[x >> 20] << 6; + } else { + xn = table[x >> 18] << 5; + } + } + + xn = (xn + 1 + (x / xn)) >> 1; + xn = (xn + 1 + (x / xn)) >> 1; + return ((xn * xn) > x) ? --xn : xn; + } else { + if (x >= 0x100000) { + if (x >= 0x400000) { + xn = table[x >> 16] << 4; + } else { + xn = table[x >> 14] << 3; + } + } else { + if (x >= 0x40000) { + xn = table[x >> 12] << 2; + } else { + xn = table[x >> 10] << 1; + } + } + + xn = (xn + 1 + (x / xn)) >> 1; + + return ((xn * xn) > x) ? --xn : xn; + } + } else { + if (x >= 0x100) { + if (x >= 0x1000) { + if (x >= 0x4000) { + xn = (table[x >> 8]) + 1; + } else { + xn = (table[x >> 6] >> 1) + 1; + } + } else { + if (x >= 0x400) { + xn = (table[x >> 4] >> 2) + 1; + } else { + xn = (table[x >> 2] >> 3) + 1; + } + } + + return ((xn * xn) > x) ? --xn : xn; + } else { + if (x >= 0) { + return table[x] >> 4; + } + } + } + throw new IllegalArgumentException("Invalid number:" + x); + } + + public static double getMean(int[] array) { double count = 0; for (int i : array) { @@ -115,7 +229,7 @@ public class MathMan { } public static double sqrtApprox(double d) { - return Double.longBitsToDouble(((Double.doubleToLongBits(d) - (1L << 52)) >> 1) + (1L << 61)); + return Double.longBitsToDouble(((Double.doubleToLongBits(d) - (1l << 52)) >> 1) + (1l << 61)); } public static float invSqrt(float x) { diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/PlotChunk.java b/Core/src/main/java/com/intellectualcrafters/plot/util/PlotChunk.java deleted file mode 100644 index a8497a5ce..000000000 --- a/Core/src/main/java/com/intellectualcrafters/plot/util/PlotChunk.java +++ /dev/null @@ -1,261 +0,0 @@ -package com.intellectualcrafters.plot.util; - -import com.intellectualcrafters.plot.PS; -import com.intellectualcrafters.plot.object.Location; -import com.intellectualcrafters.plot.object.Plot; -import com.intellectualcrafters.plot.object.PlotArea; -import com.intellectualcrafters.plot.object.PlotBlock; -import com.intellectualcrafters.plot.object.PlotManager; -import com.intellectualcrafters.plot.object.PseudoRandom; -import com.intellectualcrafters.plot.object.RunnableVal3; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; - -/** - * The PlotChunk class is primarily used for world generation and mass block placement.
- * - With mass block placement, it is associated with a queue
- * - World Generation has no queue, so don't use those methods in that case - * @param - */ -public abstract class PlotChunk implements Cloneable { - private ChunkWrapper chunk; - private T objChunk; - - /** - * A FaweSections object represents a chunk and the blocks that you wish to change in it. - */ - public PlotChunk(final ChunkWrapper chunk) { - this.chunk = chunk; - } - - public ChunkWrapper getChunkWrapper() { - return this.chunk; - } - - public void setChunkWrapper(final ChunkWrapper loc) { - this.chunk = loc; - this.objChunk = null; - } - - public int getX() { - return chunk.x; - } - - public int getZ() { - return chunk.z; - } - - /** - * Adds this PlotChunk to the SetQueue for later block placement
- * - Will cause issues if not the right type for the implementation - */ - public void addToQueue() { - if (chunk == null) { - throw new IllegalArgumentException("Chunk location cannot be null!"); - } - ((PlotQueue) SetQueue.IMP.queue).setChunk(this); - } - - /** - * Force the queue to finish processing this chunk - * @param fixLighting - */ - public void flush(boolean fixLighting) { - ((PlotQueue) SetQueue.IMP.queue).next(getChunkWrapper(), fixLighting); - } - - /** - * Force the queue to fix lighting for this chunk - */ - public void fixLighting() { - ((PlotQueue) SetQueue.IMP.queue).fixLighting(this, true); - } - - /** - * Fill this chunk with a block - * @param id - * @param data - */ - public void fill(int id, byte data) { - fillCuboid(0, 15, 0, 255, 0, 15, id, data); - } - - /** - * Fill this chunk with blocks (random) - * @param blocks - */ - public void fill(PlotBlock[] blocks) { - fillCuboid(0, 15, 0, 255, 0, 15, blocks); - } - - /** - * Fill a cuboid in this chunk with a block - * @param x1 - * @param x2 - * @param y1 - * @param y2 - * @param z1 - * @param z2 - * @param id - * @param data - */ - public void fillCuboid(int x1, int x2, int y1, int y2, int z1, int z2, int id, byte data) { - for (int x = x1; x <= x2; x++) { - for (int y = y1; y <= y2; y++) { - for (int z = z1; z <= z2; z++) { - setBlock(x, y, z, id, data); - } - } - } - } - - /** - * Fill a cuboid in this chunk with blocks - * @param x1 - * @param x2 - * @param y1 - * @param y2 - * @param z1 - * @param z2 - * @param blocks - */ - public void fillCuboid(int x1, int x2, int y1, int y2, int z1, int z2, PlotBlock[] blocks) { - if (blocks.length == 1) { - fillCuboid(x1, x2, y1, y2, z1, z2, blocks[0]); - return; - } - if (chunk != null) { - PseudoRandom.random.state = (chunk.x << 16) | (chunk.z & 0xFFFF); - } - for (int x = x1; x <= x2; x++) { - for (int y = y1; y <= y2; y++) { - for (int z = z1; z <= z2; z++) { - setBlock(x, y, z, blocks[PseudoRandom.random.random(blocks.length)]); - } - } - } - } - - /** - * Run a task for each x,z value corresponding to the plot at that location
- * - Plot: The plot at the x,z (may be null)
- * - Location: The location in the chunk (y = 0)
- * - PlotChunk: Reference to this chunk object
- * @param task - */ - public void mapByType2D(RunnableVal3 task) { - int bx = getX() << 4; - int bz = getZ() << 4; - String world = getChunkWrapper().world; - PlotArea area = PS.get().getPlotArea(world, null); - Location loc = new Location(world, bx, 0, bz); - if (area != null) { - PlotManager manager = area.getPlotManager(); - for (int x = 0; x < 16; x++) { - loc.setX(bx + x); - for (int z = 0; z < 16; z++) { - loc.setZ(bz + z); - task.run(area.getPlotAbs(loc), x, z); - } - } - } else { - for (int x = 0; x < 16; x++) { - loc.setX(bx + x); - for (int z = 0; z < 16; z++) { - loc.setZ(bz + z); - task.run(loc.getPlotAbs(), x, z); - } - } - } - } - - /** - * Fill a cuboid in this chunk with a block - * @param x1 - * @param x2 - * @param y1 - * @param y2 - * @param z1 - * @param z2 - * @param block - */ - public void fillCuboid(int x1, int x2, int y1, int y2, int z1, int z2, PlotBlock block) { - fillCuboid(x1, x2, y1, y2, z1, z2, block.id, block.data); - } - - /** - * Get the implementation specific chunk - * @Nullable If no location is tied to this container - * @return Chunk - */ - public T getChunk() { - return objChunk != null ? objChunk : getChunkAbs(); - } - - /** - * Get the implementation specific chunk (no caching) - * @return - */ - public abstract T getChunkAbs(); - - /** - * Set a block in this container - * @param x - * @param y - * @param z - * @param id - * @param data - */ - public abstract void setBlock(final int x, final int y, final int z, final int id, final byte data); - - /** - * Set a block in this container - * @param x - * @param y - * @param z - * @param block - */ - public void setBlock(int x, int y, int z, PlotBlock block) { - setBlock(x, y, z, block.id, block.data); - } - - /** - * Set a biome in this container - * @param x - * @param z - * @param biome - */ - public abstract void setBiome(int x, int z, int biome); - - @Override - public int hashCode() { - return chunk.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof PlotChunk)) { - return false; - } - return chunk.equals(((PlotChunk) obj).chunk); - } - - @Override - public String toString() { - return getChunkWrapper().toString(); - } - - /** - * Attempt to clone this PlotChunk object
- * - Depending on the implementation, this may not work - * @return - */ - @Override - public abstract PlotChunk clone(); - - /** - * Attempt a shallow clone i.e. block mappings share the same reference
- * - Depending on the implementation, this may not work - * @return - */ - public abstract PlotChunk shallowClone(); -} diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/PlotQueue.java b/Core/src/main/java/com/intellectualcrafters/plot/util/PlotQueue.java deleted file mode 100644 index 940a9c42c..000000000 --- a/Core/src/main/java/com/intellectualcrafters/plot/util/PlotQueue.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.intellectualcrafters.plot.util; - -import com.intellectualcrafters.plot.object.ChunkLoc; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; - -import java.util.Collection; - -public interface PlotQueue { - - boolean setBlock(String world, int x, int y, int z, short id, byte data); - - PlotChunk getChunk(ChunkWrapper wrap); - - void setChunk(PlotChunk chunk); - - boolean fixLighting(PlotChunk chunk, boolean fixAll); - - void sendChunk(String world, Collection locs); - - /** - * Gets the {@link PlotChunk} and sets the requested blocks. - * @return - */ - PlotChunk next(); - - PlotChunk next(ChunkWrapper wrap, boolean fixLighting); - - void clear(); - - void regenerateChunk(String world, ChunkLoc loc); -} - diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/SchematicHandler.java b/Core/src/main/java/com/intellectualcrafters/plot/util/SchematicHandler.java index c99ed8659..2f8b460d8 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/util/SchematicHandler.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/util/SchematicHandler.java @@ -24,7 +24,7 @@ import com.intellectualcrafters.plot.object.PlotArea; import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.object.RegionWrapper; import com.intellectualcrafters.plot.object.RunnableVal; - +import com.intellectualcrafters.plot.util.block.LocalBlockQueue; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -136,8 +136,7 @@ public abstract class SchematicHandler { * * @return boolean true if succeeded */ - public void paste(final Schematic schematic, final Plot plot, final int xOffset, final int yOffset, final int zOffset, final boolean autoHeight, - final RunnableVal whenDone) { + public void paste(final Schematic schematic, final Plot plot, final int xOffset, final int yOffset, final int zOffset, final boolean autoHeight, final RunnableVal whenDone) { TaskManager.runTask(new Runnable() { @Override public void run() { @@ -160,7 +159,7 @@ public abstract class SchematicHandler { } } - + final LocalBlockQueue queue = plot.getArea().getQueue(false); Dimension dimension = schematic.getSchematicDimension(); final int WIDTH = dimension.getX(); final int LENGTH = dimension.getZ(); @@ -322,10 +321,10 @@ public abstract class SchematicHandler { case 190: case 191: case 192: - SetQueue.IMP.setBlock(plot.getArea().worldname, xx, yy, zz, id); + queue.setBlock(xx, yy, zz, id); break; default: - SetQueue.IMP.setBlock(plot.getArea().worldname, xx, yy, zz, PlotBlock.get((short) id, datas[i])); + queue.setBlock(xx, yy, zz, PlotBlock.get((short) id, datas[i])); break; } } @@ -333,31 +332,17 @@ public abstract class SchematicHandler { } } if (!chunks.isEmpty()) { - final Runnable task = this; - // Run when the queue is free - SetQueue.IMP.addTask(new Runnable() { - @Override - public void run() { - System.gc(); - TaskManager.runTaskLaterAsync(task, 80); - } - }); + this.run(); } else { - System.gc(); - // Finished - SetQueue.IMP.addTask(new Runnable() { - @Override - public void run() { - for (Map.Entry entry : schematic.getTiles().entrySet()) { - BlockLoc loc = entry.getKey(); - restoreTile(plot.getArea().worldname, entry.getValue(), p1x + xOffset + loc.x, loc.y + y_offset_actual, p1z + zOffset + loc.z); - } - if (whenDone != null) { - whenDone.value = true; - whenDone.run(); - } - } - }); + queue.flush(); + for (Map.Entry entry : schematic.getTiles().entrySet()) { + BlockLoc loc = entry.getKey(); + restoreTile(plot.getArea().worldname, entry.getValue(), p1x + xOffset + loc.x, loc.y + y_offset_actual, p1z + zOffset + loc.z); + } + if (whenDone != null) { + whenDone.value = true; + whenDone.run(); + } } } }); diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/SetQueue.java b/Core/src/main/java/com/intellectualcrafters/plot/util/SetQueue.java deleted file mode 100644 index 6b4e414f3..000000000 --- a/Core/src/main/java/com/intellectualcrafters/plot/util/SetQueue.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.intellectualcrafters.plot.util; - -import com.intellectualcrafters.plot.object.ChunkLoc; -import com.intellectualcrafters.plot.object.PlotBlock; - -import java.util.ArrayDeque; -import java.util.concurrent.atomic.AtomicInteger; - -public class SetQueue { - - public static final SetQueue IMP = new SetQueue(); - private final AtomicInteger time_waiting = new AtomicInteger(2); - private final AtomicInteger time_current = new AtomicInteger(0); - private final ArrayDeque runnables = new ArrayDeque<>(); - public PlotQueue queue; - private long last; - private long last2; - - public SetQueue() { - TaskManager.runTaskRepeat(new Runnable() { - @Override - public void run() { - long free = 50 + Math.min(50 + SetQueue.this.last - (SetQueue.this.last = System.currentTimeMillis()), - SetQueue.this.last2 - System.currentTimeMillis()); - SetQueue.this.time_current.incrementAndGet(); - do { - if (isWaiting()) { - return; - } - PlotChunk current = SetQueue.this.queue.next(); - if (current == null) { - SetQueue.this.time_waiting.set(Math.max(SetQueue.this.time_waiting.get(), SetQueue.this.time_current.get() - 2)); - tasks(); - return; - } - } while ((SetQueue.this.last2 = System.currentTimeMillis()) - SetQueue.this.last < free); - SetQueue.this.time_waiting.set(SetQueue.this.time_current.get() - 1); - } - }, 1); - } - - public boolean forceChunkSet() { - PlotChunk set = this.queue.next(); - return set != null; - } - - public boolean isWaiting() { - return this.time_waiting.get() >= this.time_current.get(); - } - - public boolean isDone() { - return (this.time_waiting.get() + 1) < this.time_current.get(); - } - - public void setWaiting() { - this.time_waiting.set(this.time_current.get() + 1); - } - - public boolean addTask(Runnable whenDone) { - if (isDone()) { - // Run - tasks(); - if (whenDone != null) { - whenDone.run(); - } - return true; - } - if (whenDone != null) { - this.runnables.add(whenDone); - } - return false; - } - - public boolean tasks() { - if (this.runnables.isEmpty()) { - return false; - } - ArrayDeque tmp = this.runnables.clone(); - this.runnables.clear(); - for (Runnable runnable : tmp) { - runnable.run(); - } - return true; - } - - /** - * @param world - * @param x - * @param y - * @param z - * @param id - * @param data - * @return - */ - public boolean setBlock(String world, int x, int y, int z, short id, byte data) { - if ((y > 255) || (y < 0)) { - return false; - } - SetQueue.IMP.setWaiting(); - return this.queue.setBlock(world, x, y, z, id, data); - } - - /** - * - * @param world - * @param x - * @param y - * @param z - * @param block - * @return - */ - public boolean setBlock(String world, int x, int y, int z, PlotBlock block) { - if ((y > 255) || (y < 0)) { - return false; - } - SetQueue.IMP.setWaiting(); - return this.queue.setBlock(world, x, y, z, block.id, block.data); - } - - /** - * @param world The world - * @param x The x coordinate - * @param y The y coordinate - * @param z The z coordinate - * @param id - * @return - */ - public boolean setBlock(String world, int x, int y, int z, short id) { - if ((y > 255) || (y < 0)) { - return false; - } - SetQueue.IMP.setWaiting(); - return this.queue.setBlock(world, x, y, z, id, (byte) 0); - } - - /** - * - * @param world The world - * @param x The x coordinate - * @param y The y coordinate - * @param z The z coordinate - * @param id - * @return - */ - public boolean setBlock(String world, int x, int y, int z, int id) { - if (y > 255 || y < 0) { - return false; - } - SetQueue.IMP.setWaiting(); - return this.queue.setBlock(world, x, y, z, (short) id, (byte) 0); - } - - public void regenerateChunk(String world, ChunkLoc loc) { - this.queue.regenerateChunk(world, loc); - } - - public class ChunkWrapper { - - public final int x; - public final int z; - public final String world; - - public ChunkWrapper(String world, int x, int z) { - this.world = world; - this.x = x; - this.z = z; - } - - @Override - public int hashCode() { - return (this.x << 16) | (this.z & 0xFFFF); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (this.hashCode() != obj.hashCode()) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ChunkWrapper other = (ChunkWrapper) obj; - return (this.x == other.x) && (this.z == other.z) && StringMan.isEqual(this.world, other.world); - } - - @Override - public String toString() { - return this.world + ":" + this.x + "," + this.z; - } - } -} diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/TaskManager.java b/Core/src/main/java/com/intellectualcrafters/plot/util/TaskManager.java index 69bd7299d..05c9172e7 100644 --- a/Core/src/main/java/com/intellectualcrafters/plot/util/TaskManager.java +++ b/Core/src/main/java/com/intellectualcrafters/plot/util/TaskManager.java @@ -7,51 +7,97 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; public abstract class TaskManager { + public static TaskManager IMP; + public static final HashSet TELEPORT_QUEUE = new HashSet<>(); public static final HashMap tasks = new HashMap<>(); public static AtomicInteger index = new AtomicInteger(0); + public T sync(final RunnableVal function) { + return sync(function, Integer.MAX_VALUE); + } + + public T sync(final RunnableVal function, int timeout) { + if (PS.get().isMainThread(Thread.currentThread())) { + function.run(); + return function.value; + } + final AtomicBoolean running = new AtomicBoolean(true); + RunnableVal run = new RunnableVal() { + @Override + public void run(RuntimeException value) { + try { + function.run(); + } catch (RuntimeException e) { + this.value = e; + } catch (Throwable neverHappens) { + neverHappens.printStackTrace(); + } finally { + running.set(false); + } + synchronized (function) { + function.notifyAll(); + } + } + }; + TaskManager.IMP.task(run); + try { + synchronized (function) { + while (running.get()) { + function.wait(timeout); + } + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (run.value != null) { + throw run.value; + } + return function.value; + } + public static int runTaskRepeat(Runnable runnable, int interval) { if (runnable != null) { - if (PS.get().TASK == null) { + if (IMP == null) { throw new IllegalArgumentException("disabled"); } - return PS.get().TASK.taskRepeat(runnable, interval); + return IMP.taskRepeat(runnable, interval); } return -1; } public static int runTaskRepeatAsync(Runnable runnable, int interval) { if (runnable != null) { - if (PS.get().TASK == null) { + if (IMP == null) { throw new IllegalArgumentException("disabled"); } - return PS.get().TASK.taskRepeat(runnable, interval); + return IMP.taskRepeat(runnable, interval); } return -1; } public static void runTaskAsync(Runnable runnable) { if (runnable != null) { - if (PS.get().TASK == null) { + if (IMP == null) { runnable.run(); return; } - PS.get().TASK.taskAsync(runnable); + IMP.taskAsync(runnable); } } public static void runTask(Runnable runnable) { if (runnable != null) { - if (PS.get().TASK == null) { + if (IMP == null) { runnable.run(); return; } - PS.get().TASK.task(runnable); + IMP.task(runnable); } } @@ -62,21 +108,21 @@ public abstract class TaskManager { */ public static void runTaskLater(Runnable runnable, int delay) { if (runnable != null) { - if (PS.get().TASK == null) { + if (IMP == null) { runnable.run(); return; } - PS.get().TASK.taskLater(runnable, delay); + IMP.taskLater(runnable, delay); } } public static void runTaskLaterAsync(Runnable runnable, int delay) { if (runnable != null) { - if (PS.get().TASK == null) { + if (IMP == null) { runnable.run(); return; } - PS.get().TASK.taskLaterAsync(runnable, delay); + IMP.taskLaterAsync(runnable, delay); } } diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/block/BasicLocalBlockQueue.java b/Core/src/main/java/com/intellectualcrafters/plot/util/block/BasicLocalBlockQueue.java new file mode 100644 index 000000000..92c65630f --- /dev/null +++ b/Core/src/main/java/com/intellectualcrafters/plot/util/block/BasicLocalBlockQueue.java @@ -0,0 +1,282 @@ +package com.intellectualcrafters.plot.util.block; + +import com.intellectualcrafters.plot.object.PlotBlock; +import com.intellectualcrafters.plot.object.RunnableVal; +import com.intellectualcrafters.plot.util.MainUtil; +import com.intellectualcrafters.plot.util.MathMan; +import com.intellectualcrafters.plot.util.TaskManager; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedDeque; + +public abstract class BasicLocalBlockQueue extends LocalBlockQueue { + + private final String world; + private long modified; + + private final ConcurrentHashMap blocks = new ConcurrentHashMap<>(); + private final ConcurrentLinkedDeque chunks = new ConcurrentLinkedDeque<>(); + + public BasicLocalBlockQueue(String world) { + super(world); + this.world = world; + this.modified = System.currentTimeMillis(); + } + + public abstract LocalChunk getLocalChunk(int x, int z); + + @Override + public abstract PlotBlock getBlock(int x, int y, int z); + + public abstract void setComponents(LocalChunk lc); + + @Override + public final String getWorld() { + return world; + } + + private LocalChunk lastWrappedChunk; + private int lastX = Integer.MIN_VALUE; + private int lastZ = Integer.MIN_VALUE; + + @Override + public final boolean next() { + lastX = Integer.MIN_VALUE; + lastZ = Integer.MIN_VALUE; + try { + if (this.blocks.size() == 0) { + return false; + } + synchronized (blocks) { + LocalChunk chunk = chunks.poll(); + if (chunk != null) { + blocks.remove(chunk.longHash()); + this.execute(chunk); + return true; + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + return false; + } + + public final boolean execute(final LocalChunk lc) { + if (lc == null) { + return false; + } + this.setComponents(lc); + return true; + } + + @Override + public void startSet(boolean parallel) { + // Do nothing + } + + @Override + public void endSet(boolean parallel) { + // Do nothing + } + + @Override + public final int size() { + return chunks.size(); + } + + @Override + public final void setModified(long modified) { + this.modified = modified; + } + + @Override + public final long getModified() { + return modified; + } + + @Override + public final boolean setBlock(int x, int y, int z, int id, int data) { + if ((y > 255) || (y < 0)) { + return false; + } + int cx = x >> 4; + int cz = z >> 4; + if (cx != lastX || cz != lastZ) { + lastX = cx; + lastZ = cz; + long pair = (long) (cx) << 32 | (cz) & 0xFFFFFFFFL; + lastWrappedChunk = this.blocks.get(pair); + if (lastWrappedChunk == null) { + lastWrappedChunk = this.getLocalChunk(x >> 4, z >> 4); + lastWrappedChunk.setBlock(x & 15, y, z & 15, id, data); + LocalChunk previous = this.blocks.put(pair, lastWrappedChunk); + if (previous == null) { + chunks.add(lastWrappedChunk); + return true; + } + this.blocks.put(pair, previous); + lastWrappedChunk = previous; + } + } + lastWrappedChunk.setBlock(x & 15, y, z & 15, id, data); + return true; + } + + @Override + public final boolean setBiome(int x, int z, String biome) { + long pair = (long) (x >> 4) << 32 | (z >> 4) & 0xFFFFFFFFL; + LocalChunk result = this.blocks.get(pair); + if (result == null) { + result = this.getLocalChunk(x >> 4, z >> 4); + LocalChunk previous = this.blocks.put(pair, result); + if (previous != null) { + this.blocks.put(pair, previous); + result = previous; + } else { + chunks.add(result); + } + } + result.setBiome(x & 15, z & 15, biome); + return true; + } + + public final void setChunk(LocalChunk chunk) { + LocalChunk previous = this.blocks.put(chunk.longHash(), (LocalChunk) chunk); + if (previous != null) { + chunks.remove(previous); + } + chunks.add((LocalChunk) chunk); + } + + public abstract class LocalChunk { + public final BasicLocalBlockQueue parent; + public final int z; + public final int x; + + public T[] blocks; + public String[][] biomes; + + public LocalChunk(BasicLocalBlockQueue parent, int x, int z) { + this.parent = parent; + this.x = x; + this.z = z; + } + + /** + * Get the parent queue this chunk belongs to + * @return + */ + public BasicLocalBlockQueue getParent() { + return parent; + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + /** + * Add the chunk to the queue + */ + public void addToQueue() { + parent.setChunk(this); + } + + public void fill(int id, int data) { + fillCuboid(0, 15, 0, 255, 0, 15, id, data); + } + + /** + * Fill a cuboid in this chunk with a block + * @param x1 + * @param x2 + * @param y1 + * @param y2 + * @param z1 + * @param z2 + * @param id + * @param data + */ + public void fillCuboid(int x1, int x2, int y1, int y2, int z1, int z2, int id, int data) { + for (int x = x1; x <= x2; x++) { + for (int y = y1; y <= y2; y++) { + for (int z = z1; z <= z2; z++) { + setBlock(x, y, z, id, data); + } + } + } + } + + public abstract void setBlock(final int x, final int y, final int z, final int id, final int data); + + public void setBiome(int x, int z, String biome) { + if (this.biomes == null) { + this.biomes = new String[16][]; + } + String[] index = this.biomes[x]; + if (index == null) { + index = this.biomes[x] = new String[16]; + } + index[z] = biome; + } + + public long longHash() { + return MathMan.pairInt(x, z); + } + + @Override + public int hashCode() { + return MathMan.pair((short) x, (short) z); + } + } + + public class BasicLocalChunk extends LocalChunk { + public BasicLocalChunk(BasicLocalBlockQueue parent, int x, int z) { + super(parent, x, z); + blocks = new PlotBlock[16][]; + } + + public void setBlock(final int x, final int y, final int z, final int id, final int data) { + PlotBlock block = PlotBlock.get(id, data); + int i = MainUtil.CACHE_I[y][x][z]; + int j = MainUtil.CACHE_J[y][x][z]; + PlotBlock[] array = blocks[i]; + if (array == null) { + array = (blocks[i] = new PlotBlock[4096]); + } + array[j] = block; + } + } + + public class CharLocalChunk extends LocalChunk { + + public CharLocalChunk(BasicLocalBlockQueue parent, int x, int z) { + super(parent, x, z); + blocks = new char[16][]; + } + + public void setBlock(final int x, final int y, final int z, final int id, final int data) { + PlotBlock block = PlotBlock.get(id, data); + int i = MainUtil.CACHE_I[y][x][z]; + int j = MainUtil.CACHE_J[y][x][z]; + char[] array = blocks[i]; + if (array == null) { + array = (blocks[i] = new char[4096]); + } + array[j] = (char) ((block.id << 4) + block.data); + } + } + + @Override + public void flush() { + GlobalBlockQueue.IMP.dequeue(this); + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Object value) { + while (next()); + } + }); + } +} diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/block/DelegateLocalBlockQueue.java b/Core/src/main/java/com/intellectualcrafters/plot/util/block/DelegateLocalBlockQueue.java new file mode 100644 index 000000000..2e4d98f96 --- /dev/null +++ b/Core/src/main/java/com/intellectualcrafters/plot/util/block/DelegateLocalBlockQueue.java @@ -0,0 +1,121 @@ +package com.intellectualcrafters.plot.util.block; + +import com.intellectualcrafters.plot.object.PlotBlock; + +public class DelegateLocalBlockQueue extends LocalBlockQueue { + + private final LocalBlockQueue parent; + + public LocalBlockQueue getParent() { + return parent; + } + + @Override + public boolean next() { + return parent.next(); + } + + @Override + public void startSet(boolean parallel) { + if (parent != null) { + parent.startSet(parallel); + } + } + + @Override + public void endSet(boolean parallel) { + if (parent != null) { + parent.endSet(parallel); + } + } + + @Override + public int size() { + if (parent != null) { + return parent.size(); + } + return 0; + } + + @Override + public void optimize() { + if (parent != null) { + parent.optimize(); + } + } + + @Override + public void setModified(long modified) { + if (parent != null) { + parent.setModified(modified); + } + } + + @Override + public long getModified() { + if (parent != null) { + return parent.getModified(); + } + return 0; + } + + @Override + public boolean setBlock(int x, int y, int z, int id, int data) { + return parent.setBlock(x, y, z, id, data); + } + + @Override + public PlotBlock getBlock(int x, int y, int z) { + return parent.getBlock(x, y, z); + } + + @Override + public boolean setBiome(int x, int y, String biome) { + return parent.setBiome(x, y, biome); + } + + @Override + public String getWorld() { + return parent.getWorld(); + } + + @Override + public void flush() { + if (parent != null) { + parent.flush(); + } + } + + @Override + public void refreshChunk(int x, int z) { + if (parent != null) { + parent.refreshChunk(x, z); + } + } + + @Override + public void fixChunkLighting(int x, int z) { + if (parent != null) { + parent.fixChunkLighting(x, z); + } + } + + @Override + public void regenChunk(int x, int z) { + if (parent != null) { + parent.regenChunk(x, z); + } + } + + @Override + public void enqueue() { + if (parent != null) { + parent.enqueue(); + } + } + + public DelegateLocalBlockQueue(LocalBlockQueue parent) { + super(parent == null ? null : parent.getWorld()); + this.parent = parent; + } +} diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/block/GlobalBlockQueue.java b/Core/src/main/java/com/intellectualcrafters/plot/util/block/GlobalBlockQueue.java new file mode 100644 index 000000000..96bf92106 --- /dev/null +++ b/Core/src/main/java/com/intellectualcrafters/plot/util/block/GlobalBlockQueue.java @@ -0,0 +1,297 @@ +package com.intellectualcrafters.plot.util.block; + +import com.intellectualcrafters.plot.PS; +import com.intellectualcrafters.plot.object.RunnableVal2; +import com.intellectualcrafters.plot.util.TaskManager; +import java.util.ArrayList; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.atomic.AtomicBoolean; + +public class GlobalBlockQueue { + + public static GlobalBlockQueue IMP; + private final int PARALLEL_THREADS; + + private QueueProvider provider; + private final ConcurrentLinkedDeque activeQueues; + private final ConcurrentLinkedDeque inactiveQueues; + private final ConcurrentLinkedDeque runnables; + + /** + * Used to calculate elapsed time in milliseconds and ensure block placement doesn't lag the server + */ + private long last; + private long secondLast; + private long lastSuccess; + private final AtomicBoolean running; + + public enum QueueStage { + INACTIVE, ACTIVE, NONE; + } + + public GlobalBlockQueue(QueueProvider provider, int threads) { + this.provider = provider; + activeQueues = new ConcurrentLinkedDeque<>(); + inactiveQueues = new ConcurrentLinkedDeque<>(); + runnables = new ConcurrentLinkedDeque<>(); + running = new AtomicBoolean(); + this.PARALLEL_THREADS = threads; + } + + private final RunnableVal2 SET_TASK = new RunnableVal2() { + @Override + public void run(Long free, LocalBlockQueue queue) { + do { + boolean more = queue.next(); + if (!more) { + lastSuccess = last; + if (inactiveQueues.size() == 0 && activeQueues.size() == 0) { + tasks(); + } + return; + } + } while (((GlobalBlockQueue.this.secondLast = System.currentTimeMillis()) - GlobalBlockQueue.this.last) < free); + } + }; + + public void GlobalBlockQueueProvider(QueueProvider provider) { + this.provider = provider; + } + + public LocalBlockQueue getNewQueue(String world, boolean autoQueue) { + LocalBlockQueue queue = provider.getNewQueue(world); + if (autoQueue) { + inactiveQueues.add(queue); + } + return queue; + } + + public boolean stop() { + if (!running.get()) { + return false; + } + running.set(false); + return true; + } + + public boolean runTask() { + if (running.get()) { + return false; + } + running.set(true); + TaskManager.runTaskRepeat(new Runnable() { + @Override + public void run() { + if (inactiveQueues.isEmpty() && activeQueues.isEmpty()) { + lastSuccess = System.currentTimeMillis(); + tasks(); + return; + } + SET_TASK.value1 = 50 + Math.min((50 + GlobalBlockQueue.this.last) - (GlobalBlockQueue.this.last = System.currentTimeMillis()), GlobalBlockQueue.this.secondLast - System.currentTimeMillis()); + SET_TASK.value2 = getNextQueue(); + if (SET_TASK.value2 == null) { + return; + } + if (!PS.get().isMainThread(Thread.currentThread())) { + throw new IllegalStateException("This shouldn't be possible for placement to occur off the main thread"); + } + // Disable the async catcher as it can't discern async vs parallel + SET_TASK.value2.startSet(true); + try { + if (PARALLEL_THREADS <= 1) { + SET_TASK.run(); + } else { + ArrayList threads = new ArrayList(); + for (int i = 0; i < PARALLEL_THREADS; i++) { + threads.add(new Thread(SET_TASK)); + } + for (Thread thread : threads) { + thread.start(); + } + for (Thread thread : threads) { + try { + thread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + } finally { + // Enable it again (note that we are still on the main thread) + SET_TASK.value2.endSet(true); + } + } + }, 1); + return true; + } + + public QueueStage getStage(LocalBlockQueue queue) { + if (activeQueues.contains(queue)) { + return QueueStage.ACTIVE; + } else if (inactiveQueues.contains(queue)) { + return QueueStage.INACTIVE; + } + return QueueStage.NONE; + } + + public boolean isStage(LocalBlockQueue queue, QueueStage stage) { + switch (stage) { + case ACTIVE: + return activeQueues.contains(queue); + case INACTIVE: + return inactiveQueues.contains(queue); + case NONE: + return !activeQueues.contains(queue) && !inactiveQueues.contains(queue); + } + return false; + } + + public void enqueue(LocalBlockQueue queue) { + inactiveQueues.remove(queue); + if (queue.size() > 0 && !activeQueues.contains(queue)) { + queue.optimize(); + activeQueues.add(queue); + } + } + + public void dequeue(LocalBlockQueue queue) { + inactiveQueues.remove(queue); + activeQueues.remove(queue); + } + + public List getAllQueues() { + ArrayList list = new ArrayList(activeQueues.size() + inactiveQueues.size()); + list.addAll(inactiveQueues); + list.addAll(activeQueues); + return list; + } + + public List getActiveQueues() { + return new ArrayList<>(activeQueues); + } + + public List getInactiveQueues() { + return new ArrayList<>(inactiveQueues); + } + + public void flush(LocalBlockQueue queue) { + SET_TASK.value1 = Long.MAX_VALUE; + SET_TASK.value2 = queue; + if (SET_TASK.value2 == null) { + return; + } + if (PS.get().isMainThread(Thread.currentThread())) { + throw new IllegalStateException("Must be flushed on the main thread!"); + } + // Disable the async catcher as it can't discern async vs parallel + SET_TASK.value2.startSet(true); + try { + if (PARALLEL_THREADS <= 1) { + SET_TASK.run(); + } else { + ArrayList threads = new ArrayList(); + for (int i = 0; i < PARALLEL_THREADS; i++) { + threads.add(new Thread(SET_TASK)); + } + for (Thread thread : threads) { + thread.start(); + } + for (Thread thread : threads) { + try { + thread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + } finally { + // Enable it again (note that we are still on the main thread) + SET_TASK.value2.endSet(true); + dequeue(queue); + } + } + + public LocalBlockQueue getNextQueue() { + long now = System.currentTimeMillis(); + while (activeQueues.size() > 0) { + LocalBlockQueue queue = activeQueues.peek(); + if (queue != null && queue.size() > 0) { + queue.setModified(now); + return queue; + } else { + activeQueues.poll(); + } + } + int size = inactiveQueues.size(); + if (size > 0) { + Iterator iter = inactiveQueues.iterator(); + try { + int total = 0; + LocalBlockQueue firstNonEmpty = null; + while (iter.hasNext()) { + LocalBlockQueue queue = iter.next(); + long age = now - queue.getModified(); + total += queue.size(); + if (queue.size() == 0) { + if (age > 1000) { + iter.remove(); + } + continue; + } + if (firstNonEmpty == null) { + firstNonEmpty = queue; + } + if (total > 64) { + firstNonEmpty.setModified(now); + return firstNonEmpty; + } + if (age > 60000) { + queue.setModified(now); + return queue; + } + } + } catch (ConcurrentModificationException e) { + e.printStackTrace(); + } + } + return null; + } + + public boolean isDone() { + return activeQueues.size() == 0 && inactiveQueues.size() == 0; + } + + public boolean addTask(final Runnable whenDone) { + if (this.isDone()) { + // Run + this.tasks(); + if (whenDone != null) { + whenDone.run(); + } + return true; + } + if (whenDone != null) { + this.runnables.add(whenDone); + } + return false; + } + + public synchronized boolean tasks() { + if (this.runnables.isEmpty()) { + return false; + } + final ConcurrentLinkedDeque tmp = new ConcurrentLinkedDeque<>(this.runnables); + this.runnables.clear(); + for (final Runnable runnable : tmp) { + runnable.run(); + } + return true; + } +} diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/block/LocalBlockQueue.java b/Core/src/main/java/com/intellectualcrafters/plot/util/block/LocalBlockQueue.java new file mode 100644 index 000000000..6a314777c --- /dev/null +++ b/Core/src/main/java/com/intellectualcrafters/plot/util/block/LocalBlockQueue.java @@ -0,0 +1,105 @@ +package com.intellectualcrafters.plot.util.block; + +import com.intellectualcrafters.plot.object.ChunkLoc; +import com.intellectualcrafters.plot.object.Location; +import com.intellectualcrafters.plot.object.PlotBlock; +import com.intellectualcrafters.plot.object.PlotPlayer; +import com.intellectualcrafters.plot.object.PseudoRandom; +import com.intellectualcrafters.plot.util.StringMan; +import com.intellectualcrafters.plot.util.UUIDHandler; +import com.intellectualcrafters.plot.util.WorldUtil; +import java.util.Map; + +public abstract class LocalBlockQueue { + + public LocalBlockQueue(String world) { + // Implement this elsewhere + } + + public ScopedLocalBlockQueue getForChunk(int x, int z) { + return new ScopedLocalBlockQueue(this, new Location(getWorld(), x << 4, 0, z << 4), new Location(getWorld(), 15 + (x << 4), 255, 15 + (z << 4))); + } + + public abstract boolean next(); + + public abstract void startSet(boolean parallel); + + public abstract void endSet(boolean parallel); + + public abstract int size(); + + public abstract void optimize(); + + public abstract void setModified(long modified); + + public abstract long getModified(); + + public abstract boolean setBlock(final int x, final int y, final int z, final int id, final int data); + + public final boolean setBlock(int x, int y, int z, int id) { + return setBlock(x, y, z, id, 0); + } + + public final boolean setBlock(int x, int y, int z, PlotBlock block) { + return setBlock(x, y, z, block.id, block.data); + } + + public abstract PlotBlock getBlock(int x, int y, int z); + + public abstract boolean setBiome(int x, int z, String biome); + + public abstract String getWorld(); + + public abstract void flush(); + + public final void setModified() { + setModified(System.currentTimeMillis()); + } + + public abstract void refreshChunk(int x, int z); + + public abstract void fixChunkLighting(int x, int z); + + public abstract void regenChunk(int x, int z); + + public final void regenChunkSafe(int x, int z) { + regenChunk(x, z); + fixChunkLighting(x, z); + ChunkLoc loc = new ChunkLoc(x, z); + for (Map.Entry entry : UUIDHandler.getPlayers().entrySet()) { + PlotPlayer pp = entry.getValue(); + Location pLoc = pp.getLocation(); + if (!StringMan.isEqual(getWorld(), pLoc.getWorld()) || !pLoc.getChunkLoc().equals(loc)) { + continue; + } + pLoc.setY(WorldUtil.IMP.getHighestBlock(getWorld(), pLoc.getX(), pLoc.getZ())); + pp.teleport(pLoc); + } + } + + public void enqueue() { + GlobalBlockQueue.IMP.enqueue(this); + } + + public final void setCuboid(Location pos1, Location pos2, PlotBlock block) { + for (int y = pos1.getY(); y <= Math.min(255, pos2.getY()); y++) { + for (int x = pos1.getX(); x <= pos2.getX(); x++) { + for (int z = pos1.getZ(); z <= pos2.getZ(); z++) { + setBlock(x, y, z, block); + } + } + } + } + + public final void setCuboid(Location pos1, Location pos2, PlotBlock[] blocks) { + for (int y = pos1.getY(); y <= Math.min(255, pos2.getY()); y++) { + for (int x = pos1.getX(); x <= pos2.getX(); x++) { + for (int z = pos1.getZ(); z <= pos2.getZ(); z++) { + int i = PseudoRandom.random.random(blocks.length); + PlotBlock block = blocks[i]; + setBlock(x, y, z, block); + } + } + } + } +} diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/block/OffsetLocalBlockQueue.java b/Core/src/main/java/com/intellectualcrafters/plot/util/block/OffsetLocalBlockQueue.java new file mode 100644 index 000000000..e490aa530 --- /dev/null +++ b/Core/src/main/java/com/intellectualcrafters/plot/util/block/OffsetLocalBlockQueue.java @@ -0,0 +1,24 @@ +package com.intellectualcrafters.plot.util.block; + +public class OffsetLocalBlockQueue extends DelegateLocalBlockQueue { + private final int ox; + private final int oy; + private final int oz; + + public OffsetLocalBlockQueue(LocalBlockQueue parent, int ox, int oy, int oz) { + super(parent); + this.ox = ox; + this.oy = oy; + this.oz = oz; + } + + @Override + public boolean setBiome(int x, int y, String biome) { + return super.setBiome(ox + x, oy + y, biome); + } + + @Override + public boolean setBlock(int x, int y, int z, int id, int data) { + return super.setBlock(ox + x, oy + y, oz + z, id, data); + } +} diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/block/QueueProvider.java b/Core/src/main/java/com/intellectualcrafters/plot/util/block/QueueProvider.java new file mode 100644 index 000000000..5919905b2 --- /dev/null +++ b/Core/src/main/java/com/intellectualcrafters/plot/util/block/QueueProvider.java @@ -0,0 +1,30 @@ +package com.intellectualcrafters.plot.util.block; + +public abstract class QueueProvider { + public abstract LocalBlockQueue getNewQueue(String world); + + public static QueueProvider of(final Class primary, final Class fallback) { + return new QueueProvider() { + + private boolean failed = false; + + @Override + public LocalBlockQueue getNewQueue(String world) { + if (!failed) { + try { + return (LocalBlockQueue) primary.getConstructors()[0].newInstance(world); + } catch (Throwable e) { + e.printStackTrace(); + failed = true; + } + } + try { + return (LocalBlockQueue) fallback.getConstructors()[0].newInstance(world); + } catch (Throwable e) { + e.printStackTrace(); + } + return null; + } + }; + } +} diff --git a/Core/src/main/java/com/intellectualcrafters/plot/util/block/ScopedLocalBlockQueue.java b/Core/src/main/java/com/intellectualcrafters/plot/util/block/ScopedLocalBlockQueue.java new file mode 100644 index 000000000..34946abe8 --- /dev/null +++ b/Core/src/main/java/com/intellectualcrafters/plot/util/block/ScopedLocalBlockQueue.java @@ -0,0 +1,88 @@ +package com.intellectualcrafters.plot.util.block; + +import com.intellectualcrafters.plot.PS; +import com.intellectualcrafters.plot.object.Location; +import com.intellectualcrafters.plot.object.Plot; +import com.intellectualcrafters.plot.object.PlotArea; +import com.intellectualcrafters.plot.object.PlotManager; +import com.intellectualcrafters.plot.object.RunnableVal3; + +public class ScopedLocalBlockQueue extends DelegateLocalBlockQueue { + private final int minX; + private final int minY; + private final int minZ; + + private final int maxX; + private final int maxY; + private final int maxZ; + + public ScopedLocalBlockQueue(LocalBlockQueue parent, Location min, Location max) { + super(parent); + this.minX = min.getX(); + this.minY = min.getY(); + this.minZ = min.getZ(); + + this.maxX = max.getX(); + this.maxY = max.getY(); + this.maxZ = max.getZ(); + } + + + @Override + public boolean setBiome(int x, int z, String biome) { + return x >= minX && x <= maxX && z >= minZ && z <= maxZ && super.setBiome(x + minX, z + minZ, biome); + } + + public void fillBiome(String biome) { + for (int x = minX; x <= maxX; x++) { + for (int z = minZ; z < maxZ; z++) { + setBiome(x, z, biome); + } + } + } + + @Override + public boolean setBlock(int x, int y, int z, int id, int data) { + return x >= minX && x <= maxX && y >= minY && y <= maxY && z >= minZ && z <= maxZ && super.setBlock(x + minX, y + minY, z + minZ, id, data); + } + + public Location getMin() { + return new Location(getWorld(), minX, minY, minZ); + } + + public Location getMax() { + return new Location(getWorld(), maxX, maxY, maxZ); + } + + /** + * Run a task for each x,z value corresponding to the plot at that location
+ * - Plot: The plot at the x,z (may be null)
+ * - Location: The location in the chunk (y = 0)
+ * - PlotChunk: Reference to this chunk object
+ * @param task + */ + public void mapByType2D(RunnableVal3 task) { + int bx = minX; + int bz = minZ; + PlotArea area = PS.get().getPlotArea(getWorld(), null); + Location loc = new Location(getWorld(), bx, 0, bz); + if (area != null) { + PlotManager manager = area.getPlotManager(); + for (int x = 0; x < 16; x++) { + loc.setX(bx + x); + for (int z = 0; z < 16; z++) { + loc.setZ(bz + z); + task.run(area.getPlotAbs(loc), x, z); + } + } + } else { + for (int x = 0; x < 16; x++) { + loc.setX(bx + x); + for (int z = 0; z < 16; z++) { + loc.setZ(bz + z); + task.run(loc.getPlotAbs(), x, z); + } + } + } + } +} diff --git a/Core/src/main/java/com/plotsquared/listener/ProcessedWEExtent.java b/Core/src/main/java/com/plotsquared/listener/ProcessedWEExtent.java index 3b76d39dc..5024f2a77 100644 --- a/Core/src/main/java/com/plotsquared/listener/ProcessedWEExtent.java +++ b/Core/src/main/java/com/plotsquared/listener/ProcessedWEExtent.java @@ -215,17 +215,11 @@ public class ProcessedWEExtent extends AbstractDelegateExtent { case 190: case 191: case 192: -// if (Settings.EXPERIMENTAL_FAST_ASYNC_WORLDEDIT) { -// SetQueue.IMP.setBlock(this.world, x, y, z, id); -// } else { super.setBlock(location, block); } break; default: -// if (Settings.EXPERIMENTAL_FAST_ASYNC_WORLDEDIT) { -// SetQueue.IMP.setBlock(this.world, x, y, z, PlotBlock.get((short) id, (byte) block.getData())); -// } else { super.setBlock(location, block); } diff --git a/Sponge/build.gradle b/Sponge/build.gradle index 77600da0f..8855fe517 100644 --- a/Sponge/build.gradle +++ b/Sponge/build.gradle @@ -20,7 +20,6 @@ apply plugin: 'com.github.johnrengelman.shadow' dependencies { compile project(':Core') compile 'org.spongepowered:spongeapi:5.0.0-SNAPSHOT' - //compile 'org.mcstats.sponge:metrics:R8-SNAPSHOT' } sourceCompatibility = 1.8 diff --git a/Sponge/src/main/java/com/plotsquared/sponge/SpongeMain.java b/Sponge/src/main/java/com/plotsquared/sponge/SpongeMain.java index 0466dc3f7..a627a83d7 100644 --- a/Sponge/src/main/java/com/plotsquared/sponge/SpongeMain.java +++ b/Sponge/src/main/java/com/plotsquared/sponge/SpongeMain.java @@ -20,7 +20,6 @@ import com.intellectualcrafters.plot.util.EconHandler; import com.intellectualcrafters.plot.util.EventUtil; import com.intellectualcrafters.plot.util.InventoryUtil; import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotQueue; import com.intellectualcrafters.plot.util.SchematicHandler; import com.intellectualcrafters.plot.util.SetupUtils; import com.intellectualcrafters.plot.util.StringMan; @@ -28,6 +27,7 @@ import com.intellectualcrafters.plot.util.TaskManager; import com.intellectualcrafters.plot.util.UUIDHandler; import com.intellectualcrafters.plot.util.UUIDHandlerImplementation; import com.intellectualcrafters.plot.util.WorldUtil; +import com.intellectualcrafters.plot.util.block.QueueProvider; import com.intellectualcrafters.plot.uuid.UUIDWrapper; import com.plotsquared.sponge.generator.SpongePlotGenerator; import com.plotsquared.sponge.listener.ChunkProcessor; @@ -47,11 +47,17 @@ import com.plotsquared.sponge.util.SpongeSetupUtils; import com.plotsquared.sponge.util.SpongeTaskManager; import com.plotsquared.sponge.util.SpongeTitleManager; import com.plotsquared.sponge.util.SpongeUtil; -import com.plotsquared.sponge.util.block.FastQueue; -import com.plotsquared.sponge.util.block.SlowQueue; +import com.plotsquared.sponge.util.block.SpongeLocalQueue; import com.plotsquared.sponge.uuid.SpongeLowerOfflineUUIDWrapper; import com.plotsquared.sponge.uuid.SpongeOnlineUUIDWrapper; import com.plotsquared.sponge.uuid.SpongeUUIDHandler; +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.spongepowered.api.Game; import org.spongepowered.api.Server; @@ -63,20 +69,11 @@ import org.spongepowered.api.event.game.state.GameAboutToStartServerEvent; import org.spongepowered.api.plugin.Plugin; import org.spongepowered.api.plugin.PluginContainer; import org.spongepowered.api.profile.GameProfileManager; -import org.spongepowered.api.world.Chunk; import org.spongepowered.api.world.World; import org.spongepowered.api.world.gen.GenerationPopulator; import org.spongepowered.api.world.gen.WorldGenerator; import org.spongepowered.api.world.gen.WorldGeneratorModifier; -import java.io.File; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - @Plugin(id = "plotsquared", name = "PlotSquared", description = "Easy, yet powerful Plot World generation and management.", url = "https://github.com/IntellectualSites/PlotSquared", version = "3.3.3") public class SpongeMain implements IPlotMain { @@ -364,14 +361,9 @@ public class SpongeMain implements IPlotMain { } @Override - public PlotQueue initPlotQueue() { - try { - MainUtil.canSendChunk = true; - return new FastQueue(); - } catch (RuntimeException e) { - e.printStackTrace(); - } - return new SlowQueue(); + public QueueProvider initBlockQueue() { + MainUtil.canSendChunk = true; + return QueueProvider.of(SpongeLocalQueue.class, null); } @Override diff --git a/Sponge/src/main/java/com/plotsquared/sponge/generator/SpongeAugmentedGenerator.java b/Sponge/src/main/java/com/plotsquared/sponge/generator/SpongeAugmentedGenerator.java index 44365f5a8..f71317949 100644 --- a/Sponge/src/main/java/com/plotsquared/sponge/generator/SpongeAugmentedGenerator.java +++ b/Sponge/src/main/java/com/plotsquared/sponge/generator/SpongeAugmentedGenerator.java @@ -2,19 +2,17 @@ package com.plotsquared.sponge.generator; import com.flowpowered.math.vector.Vector3i; import com.intellectualcrafters.plot.generator.AugmentedUtils; -import com.intellectualcrafters.plot.object.LazyResult; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; +import com.intellectualcrafters.plot.object.PlotBlock; +import com.intellectualcrafters.plot.util.block.DelegateLocalBlockQueue; import com.plotsquared.sponge.util.SpongeUtil; +import java.util.List; +import org.spongepowered.api.block.BlockState; import org.spongepowered.api.world.World; import org.spongepowered.api.world.extent.ImmutableBiomeArea; import org.spongepowered.api.world.extent.MutableBlockVolume; import org.spongepowered.api.world.gen.GenerationPopulator; import org.spongepowered.api.world.gen.WorldGenerator; -import java.util.List; - public class SpongeAugmentedGenerator implements GenerationPopulator { private static SpongeAugmentedGenerator generator; @@ -43,38 +41,28 @@ public class SpongeAugmentedGenerator implements GenerationPopulator { int bz = min.getZ(); int cx = bx >> 4; int cz = bz >> 4; - AugmentedUtils.generate(world.getName(), cx, cz, new LazyResult>() { + AugmentedUtils.generate(world.getName(), cx, cz, new DelegateLocalBlockQueue(null) { @Override - public PlotChunk create() { - ChunkWrapper wrap = SetQueue.IMP.new ChunkWrapper(world.getName(), cx, cz); - return new PlotChunk(wrap) { - @Override - public ChunkWrapper getChunkAbs() { - return getChunkWrapper(); - } - @Override - public void setBlock(int x, int y, int z, int id, byte data) { - terrain.setBlock(bx + x, y, bz + z, SpongeUtil.getBlockState(id, data)); - } - @Override - public void setBiome(int x, int z, int biome) { - // TODO FIXME - } - @Override - public PlotChunk clone() { - throw new UnsupportedOperationException("NOT IMPLEMENTED YET"); - } - @Override - public PlotChunk shallowClone() { - throw new UnsupportedOperationException("NOT IMPLEMENTED YET"); - } - @Override - public void addToQueue() {} - @Override - public void flush(boolean fixLighting) {} - }; + public boolean setBlock(int x, int y, int z, int id, int data) { + terrain.setBlock(bx + x, y, bz + z, SpongeUtil.getBlockState(id, data)); + return true; + } + + @Override + public PlotBlock getBlock(int x, int y, int z) { + BlockState block = terrain.getBlock(bx + x, y, bz + z); + return block == null ? PlotBlock.get(0, 0) : SpongeUtil.getPlotBlock(block); + } + + @Override + public boolean setBiome(int x, int z, String biome) { + return false; // TODO ? + } + + @Override + public String getWorld() { + return world.getName(); } }); } - } diff --git a/Sponge/src/main/java/com/plotsquared/sponge/generator/SpongeTerrainGen.java b/Sponge/src/main/java/com/plotsquared/sponge/generator/SpongeTerrainGen.java index 055a33404..b1ca84ce6 100644 --- a/Sponge/src/main/java/com/plotsquared/sponge/generator/SpongeTerrainGen.java +++ b/Sponge/src/main/java/com/plotsquared/sponge/generator/SpongeTerrainGen.java @@ -1,19 +1,17 @@ package com.plotsquared.sponge.generator; -import org.spongepowered.api.world.World; -import org.spongepowered.api.world.extent.ImmutableBiomeArea; -import org.spongepowered.api.world.extent.MutableBlockVolume; -import org.spongepowered.api.world.gen.GenerationPopulator; - import com.flowpowered.math.vector.Vector3i; import com.intellectualcrafters.plot.PS; import com.intellectualcrafters.plot.generator.IndependentPlotGenerator; +import com.intellectualcrafters.plot.object.ChunkWrapper; import com.intellectualcrafters.plot.object.PlotArea; import com.intellectualcrafters.plot.object.PseudoRandom; import com.intellectualcrafters.plot.util.ChunkManager; -import com.intellectualcrafters.plot.util.SetQueue; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; import com.plotsquared.sponge.util.block.GenChunk; +import org.spongepowered.api.world.World; +import org.spongepowered.api.world.extent.ImmutableBiomeArea; +import org.spongepowered.api.world.extent.MutableBlockVolume; +import org.spongepowered.api.world.gen.GenerationPopulator; public class SpongeTerrainGen implements GenerationPopulator { @@ -36,7 +34,7 @@ public class SpongeTerrainGen implements GenerationPopulator { Vector3i min = terrain.getBlockMin(); int cx = min.getX() >> 4; int cz = min.getZ() >> 4; - ChunkWrapper wrap = SetQueue.IMP.new ChunkWrapper(worldname, cx, cz); + ChunkWrapper wrap = new ChunkWrapper(worldname, cx, cz); // Create the result object GenChunk result = new GenChunk(terrain, null, wrap); // Catch any exceptions diff --git a/Sponge/src/main/java/com/plotsquared/sponge/util/SpongeChatManager.java b/Sponge/src/main/java/com/plotsquared/sponge/util/SpongeChatManager.java index 6481d7a56..017589969 100644 --- a/Sponge/src/main/java/com/plotsquared/sponge/util/SpongeChatManager.java +++ b/Sponge/src/main/java/com/plotsquared/sponge/util/SpongeChatManager.java @@ -1,5 +1,6 @@ package com.plotsquared.sponge.util; +import com.intellectualcrafters.plot.config.Settings; import com.intellectualcrafters.plot.object.ConsolePlayer; import com.intellectualcrafters.plot.object.PlotMessage; import com.intellectualcrafters.plot.object.PlotPlayer; @@ -139,7 +140,7 @@ public class SpongeChatManager extends ChatManager { @Override public void send(PlotMessage plotMessage, PlotPlayer player) { - if (player instanceof ConsolePlayer) { + if (player instanceof ConsolePlayer || !Settings.Chat.INTERACTIVE) { player.sendMessage(plotMessage.$(this).build().toPlain()); } else { ((SpongePlayer) player).player.sendMessage(plotMessage.$(this).build()); diff --git a/Sponge/src/main/java/com/plotsquared/sponge/util/SpongeHybridUtils.java b/Sponge/src/main/java/com/plotsquared/sponge/util/SpongeHybridUtils.java index f3580acc2..12e47e913 100644 --- a/Sponge/src/main/java/com/plotsquared/sponge/util/SpongeHybridUtils.java +++ b/Sponge/src/main/java/com/plotsquared/sponge/util/SpongeHybridUtils.java @@ -1,60 +1,12 @@ package com.plotsquared.sponge.util; import com.intellectualcrafters.plot.generator.HybridUtils; -import com.intellectualcrafters.plot.util.expiry.PlotAnalysis; -import com.intellectualcrafters.plot.object.PlotBlock; import com.intellectualcrafters.plot.object.RegionWrapper; import com.intellectualcrafters.plot.object.RunnableVal; -import org.spongepowered.api.block.BlockState; -import org.spongepowered.api.block.BlockTypes; -import org.spongepowered.api.world.World; +import com.intellectualcrafters.plot.util.expiry.PlotAnalysis; public class SpongeHybridUtils extends HybridUtils { - - @Override - public int checkModified(String worldName, int x1, int x2, int y1, int y2, int z1, int z2, PlotBlock[] blocks) { - World world = SpongeUtil.getWorld(worldName); - int count = 0; - for (int y = y1; y <= y2; y++) { - for (int x = x1; x <= x2; x++) { - for (int z = z1; z <= z2; z++) { - BlockState state = world.getBlock(x, y, z); - PlotBlock block = SpongeUtil.getPlotBlock(state); - boolean same = false; - for (PlotBlock p : blocks) { - if (block.id == p.id) { - same = true; - break; - } - } - if (!same) { - count++; - } - } - } - } - return count; - } - - @Override - public int get_ey(String worldName, int sx, int ex, int sz, int ez, int sy) { - World world = SpongeUtil.getWorld(worldName); - int ey = sy; - for (int x = sx; x <= ex; x++) { - for (int z = sz; z <= ez; z++) { - for (int y = sy; y < 256; y++) { - if (y > ey) { - BlockState state = world.getBlock(x, y, z); - if (state.getType() != BlockTypes.AIR) { - ey = y; - } - } - } - } - } - return ey; - } - + @Override public void analyzeRegion(String world, RegionWrapper region, RunnableVal whenDone) { throw new UnsupportedOperationException("NOT IMPLEMENTED YET"); diff --git a/Sponge/src/main/java/com/plotsquared/sponge/util/block/FastChunk.java b/Sponge/src/main/java/com/plotsquared/sponge/util/block/FastChunk.java deleted file mode 100644 index 646c8cf56..000000000 --- a/Sponge/src/main/java/com/plotsquared/sponge/util/block/FastChunk.java +++ /dev/null @@ -1,246 +0,0 @@ -package com.plotsquared.sponge.util.block; - -import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; -import com.plotsquared.sponge.util.SpongeUtil; -import org.spongepowered.api.world.Chunk; - -import java.util.Arrays; - -public class FastChunk extends PlotChunk { - - public char[][] ids; - public short[] count; - public short[] air; - public short[] relight; - public int[][] biomes; - public Chunk chunk; - - public FastChunk(ChunkWrapper chunk) { - super(chunk); - this.ids = new char[16][]; - this.count = new short[16]; - this.air = new short[16]; - this.relight = new short[16]; - } - - @Override - public Chunk getChunkAbs() { - ChunkWrapper loc = getChunkWrapper(); - return SpongeUtil.getWorld(loc.world).getChunk(loc.x, 0, loc.z).get(); - } - - @Override - public Chunk getChunk() { - if (this.chunk == null) { - ChunkWrapper cl = getChunkWrapper(); - this.chunk = SpongeUtil.getWorld(cl.world).getChunk(cl.x, 0, cl.z).get(); - } - return this.chunk; - } - - @Override - public void setChunkWrapper(ChunkWrapper loc) { - super.setChunkWrapper(loc); - this.chunk = null; - } - - /** - * Get the number of block changes in a specified section. - * @param i - * @return - */ - public int getCount(int i) { - return this.count[i]; - } - - public int getAir(int i) { - return this.air[i]; - } - - public void setCount(int i, short value) { - this.count[i] = value; - } - - /** - * Get the number of block changes in a specified section. - * @param i - * @return - */ - public int getRelight(int i) { - return this.relight[i]; - } - - public int getTotalCount() { - int total = 0; - for (int i = 0; i < 16; i++) { - total += this.count[i]; - } - return total; - } - - public int getTotalRelight() { - if (getTotalCount() == 0) { - Arrays.fill(this.count, (short) 1); - Arrays.fill(this.relight, Short.MAX_VALUE); - return Short.MAX_VALUE; - } - int total = 0; - for (int i = 0; i < 16; i++) { - total += this.relight[i]; - } - return total; - } - - /** - * Get the raw data for a section. - * @param i - * @return - */ - public char[] getIdArray(int i) { - return this.ids[i]; - } - - @Override - public void setBlock(int x, int y, int z, int id, byte data) { - int i = MainUtil.CACHE_I[y][x][z]; - int j = MainUtil.CACHE_J[y][x][z]; - char[] vs = this.ids[i]; - if (vs == null) { - vs = this.ids[i] = new char[4096]; - this.count[i]++; - } else if (vs[j] == 0) { - this.count[i]++; - } - switch (id) { - case 0: - this.air[i]++; - vs[j] = (char) 1; - return; - case 10: - case 11: - case 39: - case 40: - case 51: - case 74: - case 89: - case 122: - case 124: - case 138: - case 169: - this.relight[i]++; - case 2: - case 4: - case 13: - case 14: - case 15: - case 20: - case 21: - case 22: - case 30: - case 32: - case 37: - case 41: - case 42: - case 45: - case 46: - case 47: - case 48: - case 49: - case 55: - case 56: - case 57: - case 58: - case 60: - case 7: - case 8: - case 9: - case 73: - case 78: - case 79: - case 80: - case 81: - case 82: - case 83: - case 85: - case 87: - case 88: - case 101: - case 102: - case 103: - case 110: - case 112: - case 113: - case 121: - case 129: - case 133: - case 165: - case 166: - case 170: - case 172: - case 173: - case 174: - case 181: - case 182: - case 188: - case 189: - case 190: - case 191: - case 192: - vs[j] = (char) (id << 4); - return; - case 130: - case 76: - case 62: - this.relight[i]++; - case 54: - case 146: - case 61: - case 65: - case 68: - case 50: - if (data < 2) { - data = 2; - } - default: - vs[j] = (char) ((id << 4) + data); - return; - } - } - - @Override - public PlotChunk clone() { - FastChunk toReturn = new FastChunk(getChunkWrapper()); - toReturn.air = this.air.clone(); - toReturn.count = this.count.clone(); - toReturn.relight = this.relight.clone(); - toReturn.ids = new char[this.ids.length][]; - for (int i = 0; i < this.ids.length; i++) { - char[] matrix = this.ids[i]; - if (matrix != null) { - toReturn.ids[i] = new char[matrix.length]; - System.arraycopy(matrix, 0, toReturn.ids[i], 0, matrix.length); - } - } - return toReturn; - } - - @Override - public PlotChunk shallowClone() { - FastChunk toReturn = new FastChunk(getChunkWrapper()); - toReturn.air = this.air; - toReturn.count = this.count; - toReturn.relight = this.relight; - toReturn.ids = this.ids; - return toReturn; - } - - @Override - public void setBiome(int x, int z, int biome) { - if (this.biomes == null) { - this.biomes = new int[16][16]; - } - this.biomes[x][z] = biome; - } -} diff --git a/Sponge/src/main/java/com/plotsquared/sponge/util/block/FastQueue.java b/Sponge/src/main/java/com/plotsquared/sponge/util/block/FastQueue.java deleted file mode 100644 index 791443e43..000000000 --- a/Sponge/src/main/java/com/plotsquared/sponge/util/block/FastQueue.java +++ /dev/null @@ -1,326 +0,0 @@ -package com.plotsquared.sponge.util.block; - -import com.intellectualcrafters.plot.object.ChunkLoc; -import com.intellectualcrafters.plot.object.PseudoRandom; -import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; -import com.intellectualcrafters.plot.util.TaskManager; -import com.plotsquared.sponge.util.SpongeUtil; -import net.minecraft.entity.Entity; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.ClassInheritanceMultiMap; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.chunk.BlockStateContainer; -import net.minecraft.world.chunk.storage.ExtendedBlockStorage; -import org.spongepowered.api.world.Chunk; -import org.spongepowered.api.world.World; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -public class FastQueue extends SlowQueue { - - public final SendChunk chunkSender; - public final HashMap toUpdate = new HashMap<>(); - - public FastQueue() throws RuntimeException { - TaskManager.runTaskRepeat(new Runnable() { - @Override - public void run() { - if (FastQueue.this.toUpdate.isEmpty()) { - return; - } - int count = 0; - ArrayList chunks = new ArrayList<>(); - Iterator> i = FastQueue.this.toUpdate.entrySet().iterator(); - while (i.hasNext() && (count < 128)) { - chunks.add(i.next().getValue()); - i.remove(); - count++; - } - if (count == 0) { - return; - } - update(chunks); - } - }, 1); - this.chunkSender = new SendChunk(); - MainUtil.initCache(); - } - - public void update(Collection chunks) { - if (chunks.isEmpty()) { - return; - } - if (!MainUtil.canSendChunk) { - for (Chunk chunk : chunks) { - chunk.unloadChunk(); - chunk.loadChunk(false); - } - return; - } - try { - this.chunkSender.sendChunk(chunks); - } catch (Throwable e) { - e.printStackTrace(); - MainUtil.canSendChunk = false; - } - } - - /** - * This should be overridden by any specialized queues. - * @param plotChunk - */ - @Override - public void execute(PlotChunk plotChunk) { - FastChunk fs = (FastChunk) plotChunk; - Chunk spongeChunk = plotChunk.getChunk(); - net.minecraft.world.World nmsWorld = (net.minecraft.world.World) spongeChunk.getWorld(); - ChunkWrapper wrapper = plotChunk.getChunkWrapper(); - if (!this.toUpdate.containsKey(wrapper)) { - this.toUpdate.put(wrapper, spongeChunk); - } - spongeChunk.loadChunk(true); - try { - boolean flag = !nmsWorld.provider.getHasNoSky(); - // Sections - net.minecraft.world.chunk.Chunk nmsChunk = (net.minecraft.world.chunk.Chunk) spongeChunk; - ExtendedBlockStorage[] sections = nmsChunk.getBlockStorageArray(); - Map tiles = nmsChunk.getTileEntityMap(); - ClassInheritanceMultiMap[] entities = nmsChunk.getEntityLists(); - // Trim tiles - Set> entryset = tiles.entrySet(); - Iterator> iterator = entryset.iterator(); - while (iterator.hasNext()) { - Entry tile = iterator.next(); - BlockPos pos = tile.getKey(); - int lx = pos.getX() & 15; - int ly = pos.getY(); - int lz = pos.getZ() & 15; - int j = MainUtil.CACHE_I[ly][lx][lz]; - int k = MainUtil.CACHE_J[ly][lx][lz]; - char[] array = fs.getIdArray(j); - if (array == null) { - continue; - } - if (array[k] != 0) { - iterator.remove(); - } - } - // Trim entities - for (int i = 0; i < 16; i++) { - if ((entities[i] != null) && (fs.getCount(i) >= 4096)) { - entities[i].clear(); - } - } - // Efficiently merge sections - for (int j = 0; j < sections.length; j++) { - if (fs.getCount(j) == 0) { - continue; - } - char[] newArray = fs.getIdArray(j); - if (newArray == null) { - continue; - } - ExtendedBlockStorage section = sections[j]; - if ((section == null) || (fs.getCount(j) >= 4096)) { - section = new ExtendedBlockStorage(j << 4, flag); - //section.setData(newArray); //todo - sections[j] = section; - continue; - } - BlockStateContainer currentArray = section.getData(); - boolean fill = true; - for (int k = 0; k < newArray.length; k++) { - char n = newArray[k]; - switch (n) { - case 0: - fill = false; - continue; - case 1: - fill = false; - //currentArray[k] = 0; //todo - continue; - default: - //currentArray[k] = n; //todo - continue; - } - } - if (fill) { - fs.setCount(j, Short.MAX_VALUE); - } - } - // Clear - } catch (Throwable e) { - e.printStackTrace(); - } - int[][] biomes = fs.biomes; - if (biomes != null) { - for (int x = 0; x < 16; x++) { - int[] array = biomes[x]; - if (array == null) { - continue; - } - for (int z = 0; z < 16; z++) { - int biome = array[z]; - if (biome == 0) { - continue; - } - spongeChunk.setBiome(x, z, SpongeUtil.getBiome(biome)); - } - } - } - } - - /** - * This should be overridden by any specialized queues. - * @param wrap - */ - @Override - public PlotChunk getChunk(ChunkWrapper wrap) { - return new FastChunk(wrap); - } - - /** - * This should be overridden by any specialized queues. - * @param pc - */ - @Override - public boolean fixLighting(PlotChunk pc, boolean fixAll) { - try { - FastChunk bc = (FastChunk) pc; - Chunk spongeChunk = bc.getChunk(); - net.minecraft.world.chunk.Chunk nmsChunk = (net.minecraft.world.chunk.Chunk) spongeChunk; - if (!spongeChunk.isLoaded()) { - if (!spongeChunk.loadChunk(false)) { - return false; - } - } else { - spongeChunk.unloadChunk(); - spongeChunk.loadChunk(false); - } - // TODO load adjacent chunks - nmsChunk.generateSkylightMap(); - if (bc.getTotalRelight() == 0 && !fixAll) { - return true; - } - ExtendedBlockStorage[] sections = nmsChunk.getBlockStorageArray(); - net.minecraft.world.World nmsWorld = nmsChunk.getWorld(); - - int X = pc.getX() << 4; - int Z = pc.getZ() << 4; - - - for (int j = 0; j < sections.length; j++) { - ExtendedBlockStorage section = sections[j]; - if (section == null) { - continue; - } - if ((bc.getRelight(j) == 0 && !fixAll) || bc.getCount(j) == 0 || (bc.getCount(j) >= 4096 && bc.getAir(j) == 0)) { - continue; - } - BlockStateContainer array = section.getData(); - int l = PseudoRandom.random.random(2); - for (int k = 0; k < array.getSerializedSize(); k++) { - int i = 0; - //i = array[k]; //todo - if (i < 16) { - continue; - } - short id = (short) (i >> 4); - switch (id) { // Lighting - default: - if (!fixAll) { - continue; - } - if ((k & 1) == l) { - l = 1 - l; - continue; - } - case 10: - case 11: - case 39: - case 40: - case 50: - case 51: - case 62: - case 74: - case 76: - case 89: - case 122: - case 124: - case 130: - case 138: - case 169: - int x = MainUtil.x_loc[j][k]; - int y = MainUtil.y_loc[j][k]; - int z = MainUtil.z_loc[j][k]; - if (isSurrounded(sections, x, y, z)) { - continue; - } - BlockPos pos = new BlockPos(X + x, y, Z + z); - nmsWorld.checkLight(pos); - } - } - } - return true; - } catch (Throwable e) { - e.printStackTrace(); - } - return false; - } - - public boolean isSurrounded(ExtendedBlockStorage[] sections, int x, int y, int z) { - return isSolid(getId(sections, x, y + 1, z)) - && isSolid(getId(sections, x + 1, y - 1, z)) - && isSolid(getId(sections, x - 1, y, z)) - && isSolid(getId(sections, x, y, z + 1)) - && isSolid(getId(sections, x, y, z - 1)); - } - - public boolean isSolid(int i) { - //return i != 0 && Block.getBlockById(i).isOpaqueCube(); - throw new UnsupportedOperationException("Unsupported"); - } - - public int getId(ExtendedBlockStorage[] sections, int x, int y, int z) { - if (x < 0 || x > 15 || z < 0 || z > 15) { - return 1; - } - if (y < 0 || y > 255) { - return 1; - } - int i = MainUtil.CACHE_I[y][x][z]; - ExtendedBlockStorage section = sections[i]; - if (section == null) { - return 0; - } - BlockStateContainer array = section.getData(); - int j = MainUtil.CACHE_J[y][x][z]; - //return array[j] >> 4; //todo: fix for 1.9.4 - return 0; - } - - /** - * This should be overridden by any specialized queues. - * @param world - * @param locations - */ - @Override - public void sendChunk(String world, Collection locations) { - World spongeWorld = SpongeUtil.getWorld(world); - for (ChunkLoc loc : locations) { - ChunkWrapper wrapper = SetQueue.IMP.new ChunkWrapper(world, loc.x, loc.z); - if (!this.toUpdate.containsKey(wrapper)) { - this.toUpdate.put(wrapper, spongeWorld.getChunk(loc.x, 0, loc.z).get()); - } - } - } -} diff --git a/Sponge/src/main/java/com/plotsquared/sponge/util/block/GenChunk.java b/Sponge/src/main/java/com/plotsquared/sponge/util/block/GenChunk.java index 7ac21077a..1ce7d9a82 100644 --- a/Sponge/src/main/java/com/plotsquared/sponge/util/block/GenChunk.java +++ b/Sponge/src/main/java/com/plotsquared/sponge/util/block/GenChunk.java @@ -1,58 +1,88 @@ package com.plotsquared.sponge.util.block; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; +import com.intellectualcrafters.plot.object.ChunkWrapper; +import com.intellectualcrafters.plot.object.Location; +import com.intellectualcrafters.plot.object.PlotBlock; +import com.intellectualcrafters.plot.util.block.ScopedLocalBlockQueue; import com.plotsquared.sponge.util.SpongeUtil; -import org.spongepowered.api.world.Chunk; +import org.spongepowered.api.world.biome.BiomeType; import org.spongepowered.api.world.extent.MutableBiomeArea; import org.spongepowered.api.world.extent.MutableBlockVolume; -public class GenChunk extends PlotChunk { +public class GenChunk extends ScopedLocalBlockQueue { private final MutableBlockVolume terrain; private final MutableBiomeArea biome; private final int bz; private final int bx; + private final String world; + public boolean modified = false; public GenChunk(MutableBlockVolume terrain, MutableBiomeArea biome, ChunkWrapper wrap) { - super(wrap); + super(null, new Location(null, 0, 0, 0), new Location(null, 15, 255, 15)); this.bx = wrap.x << 4; this.bz = wrap.z << 4; this.terrain = terrain; this.biome = biome; + this.world = wrap.world; } - + @Override - public Chunk getChunkAbs() { - ChunkWrapper wrap = getChunkWrapper(); - return SpongeUtil.getWorld(wrap.world).getChunk(wrap.x << 4, 0, wrap.z << 4).orElse(null); - } - - @Override - public void setBiome(int x, int z, int biome) { - if (this.biome != null) { - this.biome.setBiome(this.bx + x, this.bz + z, SpongeUtil.getBiome(biome)); + public void fillBiome(String biomeName) { + if (this.biome == null) { + return; + } + BiomeType biome = SpongeUtil.getBiome(biomeName.toUpperCase()); + for (int x = 0; x <= 15; x++) { + for (int z = 0; z < 15; z++) { + this.biome.setBiome(this.bx + x, this.bz + z, biome); + } } } - + @Override - public void setBlock(int x, int y, int z, int id, byte data) { + public boolean setBiome(int x, int z, String biomeName) { + modified = true; + BiomeType biome = SpongeUtil.getBiome(biomeName.toUpperCase()); + this.biome.setBiome(this.bx + x, this.bz + z, biome); + return true; + } + + @Override + public boolean setBlock(int x, int y, int z, int id, int data) { + modified = true; this.terrain.setBlock(this.bx + x, y, this.bz + z, SpongeUtil.getBlockState(id, data)); + return true; } @Override - public void setChunkWrapper(ChunkWrapper loc) { - super.setChunkWrapper(loc); + public PlotBlock getBlock(int x, int y, int z) { + return SpongeUtil.getPlotBlock(this.terrain.getBlock(this.bx + x, y, this.bz + z)); } @Override - public PlotChunk clone() { + public String getWorld() { + return this.world; + } + + @Override + public Location getMax() { + return new Location(getWorld(), 15 + bx, 255, 15 + bz); + } + + @Override + public Location getMin() { + return new Location(getWorld(), bx, 0, bz); + } + + + + public GenChunk clone() { throw new UnsupportedOperationException("NOT IMPLEMENTED YET"); } - @Override - public PlotChunk shallowClone() { + public GenChunk shallowClone() { throw new UnsupportedOperationException("NOT IMPLEMENTED YET"); } } diff --git a/Sponge/src/main/java/com/plotsquared/sponge/util/block/SendChunk.java b/Sponge/src/main/java/com/plotsquared/sponge/util/block/SendChunk.java deleted file mode 100644 index 1cc330b34..000000000 --- a/Sponge/src/main/java/com/plotsquared/sponge/util/block/SendChunk.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.plotsquared.sponge.util.block; - -import com.flowpowered.math.vector.Vector3i; -import com.intellectualcrafters.plot.PS; -import com.intellectualcrafters.plot.object.ChunkLoc; -import com.intellectualcrafters.plot.object.Location; -import com.intellectualcrafters.plot.object.Plot; -import com.intellectualcrafters.plot.object.PlotPlayer; -import com.intellectualcrafters.plot.util.TaskManager; -import com.intellectualcrafters.plot.util.UUIDHandler; -import com.plotsquared.sponge.object.SpongePlayer; -import com.plotsquared.sponge.util.SpongeUtil; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.NetHandlerPlayServer; -import net.minecraft.network.play.server.SPacketChunkData; -import org.spongepowered.api.entity.living.player.Player; -import org.spongepowered.api.world.Chunk; -import org.spongepowered.api.world.World; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; - -/** - * An utility that can be used to send chunks, rather than using bukkit code to do so (uses heavy NMS) - * - - */ -public class SendChunk { - - public void sendChunk(Collection input) { - HashSet chunks = new HashSet(input); - HashMap> map = new HashMap<>(); - for (Chunk chunk : chunks) { - String world = chunk.getWorld().getName(); - ArrayList list = map.get(world); - if (list == null) { - list = new ArrayList<>(); - map.put(world, list); - } - list.add(chunk); - ((net.minecraft.world.chunk.Chunk) chunk).generateSkylightMap(); - } - for (Entry entry : UUIDHandler.getPlayers().entrySet()) { - PlotPlayer pp = entry.getValue(); - Plot plot = pp.getCurrentPlot(); - Location loc = null; - String world; - if (plot != null) { - world = plot.getArea().worldname; - } else { - loc = pp.getLocation(); - world = loc.getWorld(); - } - ArrayList list = map.get(world); - if (list == null) { - continue; - } - if (loc == null) { - loc = pp.getLocation(); - } - int cx = loc.getX() >> 4; - int cz = loc.getZ() >> 4; - Player player = ((SpongePlayer) pp).player; - int view = player.getViewDistance(); - EntityPlayer nmsPlayer = (EntityPlayer) player; - if (!(nmsPlayer instanceof EntityPlayerMP)) { - PS.debug("Cannot send chunk change to: " + pp.getName()); - return; - } - EntityPlayerMP nmsPlayerMP = (EntityPlayerMP) nmsPlayer; - for (Chunk chunk : list) { - Vector3i min = chunk.getBlockMin(); - int dx = Math.abs(cx - (min.getX() >> 4)); - int dz = Math.abs(cz - (min.getZ() >> 4)); - if ((dx > view) || (dz > view)) { - continue; - } - chunks.remove(chunk); - NetHandlerPlayServer con = nmsPlayerMP.connection; - net.minecraft.world.chunk.Chunk nmsChunk = (net.minecraft.world.chunk.Chunk) chunk; - SPacketChunkData packet = new SPacketChunkData(nmsChunk, 65535); - con.sendPacket(packet); - } - } - for (Chunk chunk : chunks) { - TaskManager.runTask(new Runnable() { - @Override - public void run() { - chunk.unloadChunk(); - } - }); - } - } - - public void sendChunk(String worldName, List chunkLocations) { - World spongeWorld = SpongeUtil.getWorld(worldName); - ArrayList chunks = new ArrayList<>(); - for (ChunkLoc loc : chunkLocations) { - chunks.add(spongeWorld.getChunk(loc.x, 0, loc.z).get()); - } - sendChunk(chunks); - } -} diff --git a/Sponge/src/main/java/com/plotsquared/sponge/util/block/SlowChunk.java b/Sponge/src/main/java/com/plotsquared/sponge/util/block/SlowChunk.java deleted file mode 100644 index 4c5635d3e..000000000 --- a/Sponge/src/main/java/com/plotsquared/sponge/util/block/SlowChunk.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.plotsquared.sponge.util.block; - -import com.intellectualcrafters.plot.object.PlotBlock; -import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; -import com.plotsquared.sponge.util.SpongeUtil; -import org.spongepowered.api.world.Chunk; - -public class SlowChunk extends PlotChunk { - - public PlotBlock[][] result = new PlotBlock[16][]; - public int[][] biomes; - private PlotBlock lastBlock; - - public SlowChunk(ChunkWrapper chunk) { - super(chunk); - } - - @Override - public Chunk getChunkAbs() { - ChunkWrapper loc = getChunkWrapper(); - return SpongeUtil.getWorld(loc.world).getChunk(loc.x << 4, 0, loc.z << 4).get(); - } - - @Override - public void setBiome(int x, int z, int biome) { - if (this.biomes == null) { - this.biomes = new int[16][16]; - } - this.biomes[x][z] = biome; - } - - @Override - public void setBlock(int x, int y, int z, int id, byte data) { - if (this.result[y >> 4] == null) { - this.result[y >> 4] = new PlotBlock[4096]; - } - if (id == this.lastBlock.id && data == this.lastBlock.data) { - this.result[MainUtil.CACHE_I[x][y][z]][MainUtil.CACHE_J[x][y][z]] = this.lastBlock; - } else { - this.result[MainUtil.CACHE_I[x][y][z]][MainUtil.CACHE_J[x][y][z]] = PlotBlock.get((short) id, data); - } - } - - @Override - public PlotChunk clone() { - SlowChunk toReturn = new SlowChunk(getChunkWrapper()); - for (int i = 0; i < this.result.length; i++) { - PlotBlock[] matrix = this.result[i]; - if (matrix != null) { - toReturn.result[i] = new PlotBlock[matrix.length]; - System.arraycopy(matrix, 0, toReturn.result[i], 0, matrix.length); - } - } - return toReturn; - } - - @Override - public PlotChunk shallowClone() { - SlowChunk toReturn = new SlowChunk(getChunkWrapper()); - toReturn.result = this.result; - return toReturn; - } -} diff --git a/Sponge/src/main/java/com/plotsquared/sponge/util/block/SlowQueue.java b/Sponge/src/main/java/com/plotsquared/sponge/util/block/SlowQueue.java deleted file mode 100644 index 0c4aa5cfa..000000000 --- a/Sponge/src/main/java/com/plotsquared/sponge/util/block/SlowQueue.java +++ /dev/null @@ -1,226 +0,0 @@ -package com.plotsquared.sponge.util.block; - -import com.flowpowered.math.vector.Vector3i; -import com.intellectualcrafters.plot.PS; -import com.intellectualcrafters.plot.object.ChunkLoc; -import com.intellectualcrafters.plot.object.PlotBlock; -import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.PlotChunk; -import com.intellectualcrafters.plot.util.PlotQueue; -import com.intellectualcrafters.plot.util.SetQueue; -import com.intellectualcrafters.plot.util.SetQueue.ChunkWrapper; -import com.plotsquared.sponge.util.SpongeUtil; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.gen.ChunkProviderServer; -import org.spongepowered.api.block.BlockState; -import org.spongepowered.api.world.Chunk; -import org.spongepowered.api.world.World; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; - -public class SlowQueue implements PlotQueue { - - private final ConcurrentHashMap> blocks = new ConcurrentHashMap<>(); - - @Override - public boolean setBlock(String world, int x, int y, int z, short id, byte data) { - if (y > 255 || y < 0) { - return false; - } - ChunkWrapper wrap = SetQueue.IMP.new ChunkWrapper(world, x >> 4, z >> 4); - x = x & 15; - z = z & 15; - PlotChunk result = this.blocks.get(wrap); - if (result == null) { - result = getChunk(wrap); - result.setBlock(x, y, z, id, data); - PlotChunk previous = this.blocks.put(wrap, result); - if (previous == null) { - return true; - } - this.blocks.put(wrap, previous); - result = previous; - } - result.setBlock(x, y, z, id, data); - return true; - } - - @Override - public void setChunk(PlotChunk chunk) { - this.blocks.put(chunk.getChunkWrapper(), chunk); - } - - @Override - public PlotChunk next() { - if (!PS.get().isMainThread(Thread.currentThread())) { - throw new IllegalStateException("Must be called from main thread!"); - } - try { - if (this.blocks.isEmpty()) { - return null; - } - Iterator>> iter = this.blocks.entrySet().iterator(); - PlotChunk toReturn = iter.next().getValue(); - if (SetQueue.IMP.isWaiting()) { - return null; - } - iter.remove(); - execute(toReturn); - fixLighting(toReturn, true); - return toReturn; - } catch (Throwable e) { - e.printStackTrace(); - return null; - } - } - - @Override - public PlotChunk next(ChunkWrapper wrap, boolean fixLighting) { - if (!PS.get().isMainThread(Thread.currentThread())) { - throw new IllegalStateException("Must be called from main thread!"); - } - try { - if (this.blocks.isEmpty()) { - return null; - } - PlotChunk toReturn = this.blocks.remove(wrap); - if (toReturn == null) { - return null; - } - execute(toReturn); - fixLighting(toReturn, fixLighting); - return toReturn; - } catch (Throwable e) { - e.printStackTrace(); - return null; - } - } - - @Override - public void clear() { - this.blocks.clear(); - } - - @Override - public void regenerateChunk(String world, ChunkLoc loc) { - World spongeWorld = SpongeUtil.getWorld(world); - net.minecraft.world.World nmsWorld = (net.minecraft.world.World) spongeWorld; - Optional chunkOpt = spongeWorld.getChunk(loc.x, 0, loc.z); - if (chunkOpt.isPresent()) { - try { - Chunk spongeChunk = chunkOpt.get(); - IChunkProvider provider = nmsWorld.getChunkProvider(); - if (!(provider instanceof ChunkProviderServer)) { - PS.debug("Not valid world generator for: " + world); - } -/* ChunkProviderServer chunkServer = (ChunkProviderServer) provider; - IChunkProvider chunkProvider = chunkServer.serverChunkGenerator; - - long pos = ChunkCoordIntPair.chunkXZ2Int(loc.x, loc.z); - net.minecraft.world.chunk.Chunk mcChunk = (net.minecraft.world.chunk.Chunk) spongeChunk; - if (chunkServer.chunkExists(loc.x, loc.z)) { - mcChunk = chunkServer.loadChunk(loc.x, loc.z); - mcChunk.onChunkUnload(); - } - Field fieldDroppedChunksSet; - try { - fieldDroppedChunksSet = chunkServer.getClass().getField("droppedChunksSet"); - } catch (Throwable t) { - fieldDroppedChunksSet = ReflectionUtils.findField(chunkServer.getClass(), Set.class); - } - Set set = (Set) fieldDroppedChunksSet.get(chunkServer); - set.remove(pos); - ReflectionUtils.findField(chunkServer.getClass(),) - chunkServer.id2ChunkMap.remove(pos); - mcChunk = chunkProvider.provideChunk(loc.x, loc.z); - chunkServer.id2ChunkMap.add(pos, mcChunk); - chunkServer.loadedChunks.add(mcChunk); - if (mcChunk != null) { - mcChunk.onChunkLoad(); - mcChunk.populateChunk(chunkProvider, chunkProvider, loc.x, loc.z); - SetQueue.IMP.queue.sendChunk(world, Arrays.asList(loc)); - } - else { - PS.debug("CHUNK IS NULL!?"); - }*/ - } catch (Throwable e) { - e.printStackTrace(); - } - } - } - - /** - * This should be overriden by any specialized queues. - * @param plotChunk - */ - public void execute(PlotChunk plotChunk) { - SlowChunk sc = (SlowChunk) plotChunk; - Chunk chunk = plotChunk.getChunk(); - chunk.loadChunk(true); - Vector3i min = chunk.getBlockMin(); - int bx = min.getX(); - int bz = min.getZ(); - for (int i = 0; i < sc.result.length; i++) { - PlotBlock[] result2 = sc.result[i]; - if (result2 == null) { - continue; - } - for (int j = 0; j < 4096; j++) { - int x = MainUtil.x_loc[i][j]; - int y = MainUtil.y_loc[i][j]; - int z = MainUtil.z_loc[i][j]; - PlotBlock newBlock = result2[j]; - BlockState state = SpongeUtil.getBlockState(newBlock.id, newBlock.data); - chunk.setBlock(bx + x, y, bz + z, state, false); - } - } - int[][] biomes = sc.biomes; - if (biomes != null) { - for (int x = 0; x < 16; x++) { - int[] array = biomes[x]; - if (array == null) { - continue; - } - for (int z = 0; z < 16; z++) { - int biome = array[z]; - if (biome == 0) { - continue; - } - chunk.setBiome(bx + x, bz + z, SpongeUtil.getBiome(biome)); - } - } - } - } - - /** - * This should be overriden by any specialized queues. - * @param wrap - */ - @Override - public PlotChunk getChunk(ChunkWrapper wrap) { - return new SlowChunk(wrap); - } - - /** - * This should be overriden by any specialized queues. - * @param fixAll - */ - @Override - public boolean fixLighting(PlotChunk chunk, boolean fixAll) { - // Do nothing - return true; - } - - /** - * This should be overriden by any specialized queues. - * @param locs - */ - @Override - public void sendChunk(String world, Collection locs) { - // Do nothing - } -} diff --git a/Sponge/src/main/java/com/plotsquared/sponge/util/block/SpongeLocalQueue.java b/Sponge/src/main/java/com/plotsquared/sponge/util/block/SpongeLocalQueue.java new file mode 100644 index 000000000..52aed64b9 --- /dev/null +++ b/Sponge/src/main/java/com/plotsquared/sponge/util/block/SpongeLocalQueue.java @@ -0,0 +1,500 @@ +package com.plotsquared.sponge.util.block; + +import com.intellectualcrafters.plot.object.PlotBlock; +import com.intellectualcrafters.plot.object.PseudoRandom; +import com.intellectualcrafters.plot.util.MainUtil; +import com.intellectualcrafters.plot.util.StringMan; +import com.intellectualcrafters.plot.util.TaskManager; +import com.intellectualcrafters.plot.util.block.BasicLocalBlockQueue; +import com.plotsquared.sponge.util.SpongeUtil; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashSet; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityTracker; +import net.minecraft.entity.EntityTrackerEntry; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.play.server.SPacketChunkData; +import net.minecraft.network.play.server.SPacketDestroyEntities; +import net.minecraft.server.management.PlayerChunkMap; +import net.minecraft.util.ClassInheritanceMultiMap; +import net.minecraft.util.IntHashMap; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.WorldServer; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.BlockTypes; +import org.spongepowered.api.world.World; +import org.spongepowered.api.world.biome.BiomeType; +import org.spongepowered.api.world.extent.UnmodifiableBlockVolume; +import org.spongepowered.api.world.extent.worker.MutableBlockVolumeWorker; +import org.spongepowered.api.world.extent.worker.procedure.BlockVolumeMapper; + +public class SpongeLocalQueue extends BasicLocalBlockQueue { + + public SpongeLocalQueue(String world) { + super(world); + } + + @Override + public LocalChunk getLocalChunk(int x, int z) { + return new CharLocalChunk_Sponge(this, x, z) { + // Custom stuff? + }; + } + + @Override + public void optimize() { + + } + + public World getSpongeWorld() { + return SpongeUtil.getWorld(getWorld()); + } + + @Override + public PlotBlock getBlock(int x, int y, int z) { + + World worldObj = getSpongeWorld(); + BlockState block = worldObj.getBlock(x, y, z); + if (block == null) { + return PlotBlock.get(0, 0); + } + return SpongeUtil.getPlotBlock(block); + } + + @Override + public void refreshChunk(int x, int z) { + World world = getSpongeWorld(); + Chunk nmsChunk = ((net.minecraft.world.World) world).getChunkProvider().provideChunk(x, z); + if (nmsChunk == null || !nmsChunk.isLoaded()) { + return; + } + try { + ChunkPos pos = nmsChunk.getChunkCoordIntPair(); + WorldServer w = (WorldServer) nmsChunk.getWorld(); + PlayerChunkMap chunkMap = w.getPlayerChunkMap(); + if (!chunkMap.contains(x, z)) { + return; + } + EntityTracker tracker = w.getEntityTracker(); + HashSet players = new HashSet<>(); + for (EntityPlayer player : w.playerEntities) { + if (player instanceof EntityPlayerMP) { + if (chunkMap.isPlayerWatchingChunk((EntityPlayerMP) player, x, z)) { + players.add((EntityPlayerMP) player); + } + } + } + if (players.size() == 0) { + return; + } + HashSet entities = new HashSet<>(); + ClassInheritanceMultiMap[] entitieSlices = nmsChunk.getEntityLists(); + IntHashMap entries = null; + for (Field field : tracker.getClass().getDeclaredFields()) { + if (field.getType() == IntHashMap.class) { + field.setAccessible(true); + entries = (IntHashMap) field.get(tracker); + } + } + for (ClassInheritanceMultiMap slice : entitieSlices) { + if (slice == null) { + continue; + } + for (Entity ent : slice) { + EntityTrackerEntry entry = entries != null ? entries.lookup(ent.getEntityId()) : null; + if (entry == null) { + continue; + } + entities.add(entry); + SPacketDestroyEntities packet = new SPacketDestroyEntities(ent.getEntityId()); + for (EntityPlayerMP player : players) { + player.connection.sendPacket(packet); + } + } + } + // Send chunks + SPacketChunkData packet = new SPacketChunkData(nmsChunk, 65535); + for (EntityPlayerMP player : players) { + player.connection.sendPacket(packet); + } + // send ents + for (EntityTrackerEntry entry : entities) { + try { + TaskManager.IMP.taskLater(new Runnable() { + @Override + public void run() { + for (EntityPlayerMP player : players) { + if (entry.isVisibleTo(player)) { + entry.removeFromTrackedPlayers(player); + if (entry.getTrackedEntity() != player) { + entry.updatePlayerEntity(player); + } + } + } + } + }, 2); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + @Override + public void fixChunkLighting(int x, int z) { + Chunk nmsChunk = getChunk(getSpongeWorld(), x, z); + nmsChunk.generateSkylightMap(); + } + + public class CharLocalChunk_Sponge extends CharLocalChunk { + public short[] count; + public short[] air; + public short[] relight; + + public CharLocalChunk_Sponge(BasicLocalBlockQueue parent, int x, int z) { + super(parent, x, z); + this.count = new short[16]; + this.air = new short[16]; + this.relight = new short[16]; + } + + @Override + public void setBlock(int x, int y, int z, int id, int data) { + int i = MainUtil.CACHE_I[y][x][z]; + int j = MainUtil.CACHE_J[y][x][z]; + char[] vs = this.blocks[i]; + if (vs == null) { + vs = this.blocks[i] = new char[4096]; + this.count[i]++; + } else if (vs[j] == 0) { + this.count[i]++; + } + switch (id) { + case 0: + this.air[i]++; + vs[j] = (char) 1; + return; + case 10: + case 11: + case 39: + case 40: + case 51: + case 74: + case 89: + case 122: + case 124: + case 138: + case 169: + this.relight[i]++; + case 2: + case 4: + case 13: + case 14: + case 15: + case 20: + case 21: + case 22: + case 30: + case 32: + case 37: + case 41: + case 42: + case 45: + case 46: + case 47: + case 48: + case 49: + case 55: + case 56: + case 57: + case 58: + case 60: + case 7: + case 8: + case 9: + case 73: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 85: + case 87: + case 88: + case 101: + case 102: + case 103: + case 110: + case 112: + case 113: + case 121: + case 129: + case 133: + case 165: + case 166: + case 170: + case 172: + case 173: + case 174: + case 181: + case 182: + case 188: + case 189: + case 190: + case 191: + case 192: + vs[j] = (char) (id << 4); + return; + case 130: + case 76: + case 62: + this.relight[i]++; + case 54: + case 146: + case 61: + case 65: + case 68: + case 50: + if (data < 2) { + data = 2; + } + default: + vs[j] = (char) ((id << 4) + data); + return; + } + } + + public char[] getIdArray(int i) { + return this.blocks[i]; + } + + public int getCount(int i) { + return this.count[i]; + } + + public int getAir(int i) { + return this.air[i]; + } + + public void setCount(int i, short value) { + this.count[i] = value; + } + + public int getRelight(int i) { + return this.relight[i]; + } + + public int getTotalCount() { + int total = 0; + for (int i = 0; i < 16; i++) { + total += this.count[i]; + } + return total; + } + + public int getTotalRelight() { + if (getTotalCount() == 0) { + Arrays.fill(this.count, (short) 1); + Arrays.fill(this.relight, Short.MAX_VALUE); + return Short.MAX_VALUE; + } + int total = 0; + for (int i = 0; i < 16; i++) { + total += this.relight[i]; + } + return total; + } + } + + public boolean isSurrounded(char[][] sections, int x, int y, int z) { + return isSolid(getId(sections, x, y + 1, z)) + && isSolid(getId(sections, x + 1, y - 1, z)) + && isSolid(getId(sections, x - 1, y, z)) + && isSolid(getId(sections, x, y, z + 1)) + && isSolid(getId(sections, x, y, z - 1)); + } + + public boolean isSolid(int i) { + return i != 0 && Block.getBlockById(i).isVisuallyOpaque(); + } + + public int getId(char[][] sections, int x, int y, int z) { + if (x < 0 || x > 15 || z < 0 || z > 15) { + return 1; + } + if (y < 0 || y > 255) { + return 1; + } + int i = MainUtil.CACHE_I[y][x][z]; + char[] section = sections[i]; + if (section == null) { + return 0; + } + int j = MainUtil.CACHE_I[y][x][z]; + int combined = section[j]; + return combined >> 4; + } + + public boolean fixLighting(CharLocalChunk_Sponge bc, Chunk nmsChunk) { + try { + if (!nmsChunk.isLoaded()) { + return false; + } + ExtendedBlockStorage[] sections = nmsChunk.getBlockStorageArray(); + nmsChunk.generateSkylightMap(); + net.minecraft.world.World nmsWorld = nmsChunk.getWorld(); + + int X = bc.getX() << 4; + int Z = bc.getZ() << 4; + + BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(0, 0, 0); + for (int j = 0; j < sections.length; j++) { + ExtendedBlockStorage section = sections[j]; + if (section == null) { + continue; + } + if ((bc.getCount(j) == 0) || ((bc.getCount(j) >= 4096) && (bc.getAir(j) == 0)) || bc.getAir(j) == 4096) { + continue; + } + char[] array = bc.getIdArray(j); + if (array != null) { + int l = PseudoRandom.random.random(2); + for (int k = 0; k < array.length; k++) { + int i = array[k]; + if (i < 16) { + continue; + } + short id = (short) (i >> 4); + switch (id) { // Lighting + default: + if ((k & 1) == l) { + l = 1 - l; + continue; + } + case 10: + case 11: + case 39: + case 40: + case 50: + case 51: + case 62: + case 74: + case 76: + case 89: + case 122: + case 124: + case 130: + case 138: + case 169: + int x = MainUtil.x_loc[j][k]; + int y = MainUtil.y_loc[j][k]; + int z = MainUtil.z_loc[j][k]; + if (isSurrounded(bc.blocks, x, y, z)) { + continue; + } + pos.setPos(X + x, y, Z + z); + nmsWorld.checkLight(pos); + } + } + } + } + return true; + } catch (Throwable e) { + e.printStackTrace(); + } + return false; + } + + @Override + public final void regenChunk(int x, int z) { + World worldObj = getSpongeWorld(); + throw new UnsupportedOperationException("NOT SUPPORTED"); + } + + @Override + public final void setComponents(LocalChunk lc) { + setBlocks(lc); + setBiomes(lc); + } + + public Chunk getChunk(World world, int x, int z) { + net.minecraft.world.chunk.Chunk chunk = ((net.minecraft.world.World) world).getChunkProvider().provideChunk(x, z); + if (chunk != null && !chunk.isLoaded()) { + chunk.onChunkLoad(); + } + return chunk; + } + + private BlockState AIR = BlockTypes.AIR.getDefaultState(); + + public void setBlocks(LocalChunk lc) { + World worldObj = getSpongeWorld(); + org.spongepowered.api.world.Chunk spongeChunk = (org.spongepowered.api.world.Chunk) getChunk(worldObj, lc.getX(), lc.getZ()); + char[][] ids = ((CharLocalChunk) lc).blocks; + MutableBlockVolumeWorker blockWorker = spongeChunk.getBlockWorker(); + blockWorker.map(new BlockVolumeMapper() { + @Override + public BlockState map(UnmodifiableBlockVolume volume, int xx, int y, int zz) { + int x = xx & 15; + int z = zz & 15; + int i = MainUtil.CACHE_I[y][x][z]; + char[] array = ids[i]; + if (array == null) { + return null; + } + int combinedId = array[MainUtil.CACHE_J[y][x][z]]; + switch (combinedId) { + case 0: + return null; + case 1: + return AIR; + default: + int id = combinedId >> 4; + Block block = Block.getBlockById(id); + int data = combinedId & 0xf; + IBlockState ibd; + if (data != 0) { + ibd = block.getStateFromMeta(data); + } else { + ibd = block.getDefaultState(); + } + return (BlockState) ibd; + } + } + }); + } + + public void setBiomes(LocalChunk lc) { + if (lc.biomes != null) { + World worldObj = getSpongeWorld(); + int bx = lc.getX() << 4; + int bz = lc.getX() << 4; + String last = null; + BiomeType biome = null; + for (int x = 0; x < lc.biomes.length; x++) { + String[] biomes2 = lc.biomes[x]; + if (biomes2 != null) { + for (int y = 0; y < biomes2.length; y++) { + String biomeStr = biomes2[y]; + if (biomeStr != null) { + if (last == null || !StringMan.isEqual(last, biomeStr)) { + biome = SpongeUtil.getBiome(biomeStr.toUpperCase()); + } + worldObj.setBiome(bx, bz, biome); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/fanciful-master.zip b/fanciful-master.zip new file mode 100644 index 0000000000000000000000000000000000000000..7d00bd41ffcbf07ee51bbb9a0933e9a1ef785e26 GIT binary patch literal 23329 zcmbSyW0Yn=mTlU$ZQC{~ZQHggZKKk*ZQGURS7}t*cIK;|p6U1cb|b-9Klq z8xiO1*f-8Oin5?!FhGC#HK8<+0xq?xwx7+GpebA0|EEu z>1inbry10Lm~|Ur;^+M_+xata{)ribg_Wz7g}sBb+1~)f<9K0)nBasw@&*%Xf?Xor zEeli-6is#H(cXuWV0(P=MNW!2`S`6CI58_&tWybsGGn9rb%k38T2k5`Ax%{$HBpSlAYec+gw}^aIH5nU{T6 za1)o-w8V4Wy<>2tP4!6-&Z_`o&Gw<4+=ivRTuF~(k`4B!>x0FkI>PG*c=ZQ^XXdpc z+(CyiO{=jY*nt;d-GG@m_b0gU27*F6R6zVn(6;E}^8Nq?693W=YxX&oWB--$1q3Hv zei+0Fmis7zQPB37EWfs`eF4G%XRQRErLT*?8u3cv^J8rr@FmGwN49r zpvs=)ojv2DS!4;^y`2VYeXriO492zh!&efEu5*}!OT3T1024nShJ)j&9%up)9KE|v zYtTA7uZ%1lU`6#gnPhqZ*pUKaeBD((TrW<0Z`+5Uw~XMb)uZ>{^O%ut9{8F`5(W$0?S5B4mXVWcw&zaamKIGD%de0*N z9EGw*L)wLxiHho17b%m4d(k~yutjZ6C?%Q-UFa|Py*BQe;{t!6omPn=l}Bt#933iw z(SQ=_C8RzT=s5^fNj~dRX#73)l>H=FHARP>FbyJ=W)_U%2qfjki=kH4l4iO?mptJH z-5+f+Sp;pm{Gt+pLspo2Z~wViqB6*~xRMEH2u#oVj;Av*MJm8XYhDCHy9Pz#Y#5q7 za7UN8ma5s>(*xPIz*8?$Luedq78!O{rJHvf^bPXw{WsUCN1pYM|APMUU+jP4zslmm zVshdPcBX&x;8+#8_(dk9o+Ax7%5pI~BoQepoo3pGHSxE=W9Q0*Q=T8gOAVwx8}^?c zaX(VkgFpquZl=1u?ry%f%8aVllZjJ*dM~0p+%zSlEzJyfa14e^xUUD~ zI4P|_=0|iL{-n6B={Z&@MJuL!Az z+Kri~FX`Bxm;SmlVdG%rQ2r2^57T?aYIN|#aD-ruxSbl0SV(2BM`l(-vaiQ1n7wBq z)~>_E|NH)Vr zoO*U5>Q>cCUOz5j-P&oxfjU0*TrKch4cpB_`;&4mw#yWP=33rdcnv~pO3MVMIh`dA>VR=#>L7QmYEg@HO*Bq-K10SOCFqlYs@WDAwFD*lc zr=8t%0_or--g@`Joj!Ss=0}>gG@jZN9P2-=*6+GWq9ymApU;2CC7D=&*e7oV?O~Ec zVg9|#k!`?w2!a9ul|%n?%5-$FWAOZc__sw(&S9Mij=!vL@XU--R=U$jbO#%2&KLLs zhi!9AtpQu7h8{aL98-{!J+m9mxX-M<)`jB+tc}lyKE~ay@No`%<g*BkT0ew z1XH^~VxVv;YJRx}RNLqmi~ZClzm_Irv3nN06bQd!?x#iwD;`R>q%AK#l%Po)233gV zj`YMii?UbQ2XVwWpsB8s8UELH1a@Dw3kv$a#6 zSE>y>87@%_lmd=kkGvD2oM{1d*^4A3Xvc1~WU)@Vi`Qm5$zmgIvhw65Z?eD*70WR^ zFs|MvRUnQnZ&?qHal=o^7*8@)HU~#Khf4S+>!e%%K=6Z?vMI5b9~pE}4iUX1W zdLo(|%o;F(m-{E(dAbuCX2_-Z$?SUBV1tsYL9+r!|LZ$CD?~EZVVhRY5bHWh*-r%b zF9s(Y#}u(@hX} z%=sa8iB4E2d~Fp-l_eSf1b3ZFa@g+9cyiZ@wk4MV>v1kGxp5-?MiDKc2Nt;0;s%xr zHUU>nyWgx;LpGw3l22Pz9X-pCt7w2c%=vEdM(aKMKE)xx3%dhH0dcK#tW9hxTX@xC7s zVcje923{BE8?4CK&SMueES@LeSoH5eJWfXpB9Z z@`;Z-7=fsuB(*`G;DMpUH|_xKk9CWE+|5NGg2lCxkCr1}KmRRwj9A9=a$(fS_~0zi zB~a-fFYkjy5k8fv+bTKfve8C#s%>YIjtPEdJ-d_{uZ=)w)-ix5hxh5=u=ebSK8mb@ zTs&z`o9AdWWx#x4-}c~$wEAO&kq~p@!3Ow(c-E{c%UK;-#dzkNkwpRCXo4c?qKkh{ zfDh*d)d@060XZDD3a$A2(WmS2EoNITe5S)B3cvU+;Kq-Q9@trNhnI_B5VMW(#Sb0f z1qL6u<*6jqOnfeVBWN(`Vl@aU!RNH-erfD;PdQz*L~`F($&%(9d9%G9Uy1WTxMtb$ zRv^3e&mYW0uz)oPng)q-TGTJ19^T7iGLP`Vz68<+}CH~wT zc*&W$xcvD9{(B0#;J5KWm2Awrsk2P>2}+K*IrLit9gTFF8tpRV0zhZ^;>pWeYoSW8 zuAN|`lK=GYdpJJgGsQMZa?C*#&h=B%&{(ais?uWa#i&wgDoq{7ikHBPv>c~~7qM*3HbbwE1B=^1@BT?*)@ZzaxQj;=6XJF_H1wQE3LD+DqfC zd0Cw7aD&UX`LP#`e-r9ip}2 z%B%%Tg^bCf(DW_J!m75kBwNE+Aqu>Dt7xFGQXSFVBdwK;J59Dlxk+_zWs6@$3qs}N zyyW}aYhBr_g8qInE-=zH2nE}DRccNGv2Sgh>7o0#jm~hl0Ocjf8fOO6I=3C}j z5Dkz+n7h#nqEgt5Ln%?fyc2moWetr|ohLc1;W&v~m)!ojp`H+r>e-uOB1UT0!XEuM z&G8pJ4pd;Knx7~UBK=cvz*wEQ`-bg%C{R#qKmz_B6tZ`md#sjeh>}+U>?3*UJwm{U zAh(g3N2hjOKIe#IV>IUMQ#6bHBT<;NIzv`U<^ zBe{<;90MvLE44}A#MfbmRsy+5u+EW+OyGpGq2T!d_b#k;{E48Fe}T64bwd2!M9Fh1 zGLU8v2X%ZokIJ0|zv#4qL3Yf9uQhJf?Lqw9OviMoYpyMhdRG;>SlI+8D+>P&rTRY41e*1^HV3jhcf@j#jw`(jaU>@9t7 zb6F0*Lu>6JA1>FC&;pNfwKUyhV(>8CUbj$n@XnXwJDl1YTH~Xi_<(2v%W38DdZeP7F-Pd6g!UV`*TxkDP=pKpO+3GIdm9?#mvqQ7^tY!)GO$*9d@fb3@FqKvQJsA z9HFH7N#K}fyR0)f4c|w?K1m}NH}s&8I?5CU{c$(}`KFQ|RZa^)dJpzs|B{CqlpJit zJtf+xlF9?-6wmfEF1K--dXg{}mzeU|je|?EA&&Q{2dvlj+09e-GUH4-rB=9P;3prk z(?sU7@h{|$lO!CeJ7r>u+^MNnjMsj0WhX3evek9Pqw$tXfHXF{x z4ez_XEpY9S^{1>TRaT^p!5Vl$Dt#?w`yIxOCEl;lixv#g4qBG486@S26W{*M4=t== zQw8SG_MI5s&}1i53Je9D=h3j2_@G+d&{nhRw}h4O;S7i!eOGHGN-!5KR8>g}TQ_ty zC%u52&2cKfGzzPVzM-;eRv`e8as0|y*~9l^JuoiU5^si(C=x|8z5NU;|Y7o{QD2oh{$K+dVu zr<_bFf=L9$n8HDaCvB>sJ7LlN@4>yQC}>+hgKm9r`l6vPWzsLFtEKeJn~ApjzFT(@ zNzvKbtKO!v@pyl^4Q%bX0Sk4ef(r~)8t#tipzUa7WH&z9O)^xXN7PH-uv&MbuNeEV zB*kPN-R!1Du8atY=MtF;o;XL=cFPF&;piz=nKZvaUFgv|RKeb9LwcFG#Tw&*K-l(2;QH|oX-YlqPqOwqT0;4jo7%jkaGucKx zNsGwupb$R6fy2-nVKWP`noFs8T}uY>`sGa+KO7T%!dcomnIy(#$J`}=cSk%J&T zPm>hAecV)j91W*Zh6-3ag(b@Nq)gJSREr<)uPgNiam5>F8MfIWw+`aeR?c6N*6Zsz z=LW%Rc(!(`wh7*S=xHqb_CqC;JYQURle1auuByieV4KV+e$P#h0dStcC*^XmJdRC% zAfvNe)?d|BjWl{zHju?HA}Lk}5}@~~&QX$=p02fHH}eGe+x4A%79i?>7Zb9)yKw0PuP^2@_^Rk7r>qlL3RnbOchKKdT=h zu-X6|2WK1{636{ZbOj;=EoS~K9&uObI3e>_l^|RgQ@G5^ke!{kvwYj;#?{$@IikKek z>}O#RpPP2Z7w%F6b|3;*)oJ`p#!ARrTK+ZrxTVi2Rq?4(Mvf2Q`i*0`CG!}5vIf*U zUtz&xJIUQA64zxElTm){AOFmnMCr#CzU9JPywDX7<|o%H$o+ORGT*lT<_~F6{>yC| znaV1REqoUj#EcR?q*svhrE80S!*r;33~?mnqpe|VS<<|pSr_=tZL!C!w{0q|doT4J zR6fwX8%-8f`|T?P*<~56=UBI;A4)PBG#=k(wKM0VYiHjCj<`SmQFJM+aLyKW@eI=N z!^CK)4%$KE2Uxs7GTC@a=I)dT^W4ab({In?F@-$BPr2`(+`@UquTJU#@PaszU!NLc z=<)J1akGBiAQ5$wK?w94AchW}FzO@q+njUZ#%KG`v|Zk~WT^&we$X6;%Y7*(ZO-Q& zRAbHqPk5gGz9fydyhi2}m&IBU5jue@)N}8#*ugb|0gBOZRSK%AH=VXTH=7-&e}4dco50O|Yz1AA|eB&5}&6Z@>4>*O@jw7~65oZcJo zLmsRAXQ6RCh~{+ddf}{^D*`D_^b&|8mke`(+J(oNKwFqVC*I$Pm#Bp#Am3qn^{H^l z^{_bo$<76Cy=KZpEyQrt&jJmo^1or`a6Rvc{XSj{Ui7W~!N|VSQwg~9u#*Pr+?7g| zid8PvtbsH^lqyTU>ko~g&z`|h1jnQ$VPZ?*6q?h(=6K5BD;vMy$%B_5V3U}5q(=or zjG1Tj6DU*;o{xtC*~!ABROcIuGvxu7;DBFHaP#R87F&i0zDu|ue?1Rbek=CJ^kj!# z0pnsvuw-+G;{oAzAW|0y2`@7Mg^L00l=1GV0@j1u+=h$D;P!K1y_?(&>m){aKbZrj zXGI425}~^uuI;^ClGFZ`epfh5PT33sjfHLHGc4aOg42%+)Zmyb<4T7^1A1_zd>eAbaodWMu zcdYCnU3z;OkQ(T<$*qltN4JVsdk8 z@OVLCBFJ%7=NXXd>6(|@rOz?U9Y9qtV;Kvj?3%QIkUnQ8C}4qKAP-8Z_rb0fF2M~{ zTPrMf+Amj8aa!ebutXf)9g3Uqv({-6jeGCqv=VJ5!k66eopth%<*@yIlVas6jb|>C zn!3Vsk)0z^8l`-~Qx(%6RcFNtZtRqeZyXg2=hdHP4INNln#%cu0c=>(N}*oSq%=1p zQ}%d88-W!(BCGfqWzPq^8uC(CN&odXD6UrakD}6B`0G~OU3<>)ZTp}hW`COk4`Oyb zGs2gKE=xF6t&rnKX-VC&_b=Ip{H@C@FP3xiw}HOff$_Kpd&+N}^~B&)YE6uMiB_Z8 zVH6fpTV45CysOf1cReFgouwJ4MF*AIh?09Y6j6}oeRqfa`*)o`FeRl_((xc1bl$Dt zva1Kr-J5hWTx@6zq%+xV59^&xxu1}4C}cZHr&U=MBDf6?V+4;a>tr&{zBzk$a#~Mw z{~kA}y8u_B+3&hC8LgRKmdH2*>S#GkSuh7p56g~-j?bNDQJjv`6(x=otC_{w+}+v_ z#d~^+!G^Xue(n9GxM*2zlimfPLtTR5!qhUgjkMEW%4E-_mojXT>GodInd^k7fza(b zmodwZzTJK{7axf93AJ?XQwT;pe2KZ|cfI{K7q)(55S$v`C~9IHeyjRXrp~nF_YEP; zILs)8q45Ptbt*fLj*<>SWK$19^AU3~e z)ZN$H`)mo(7|*tVBD=yF+E8t>jBlNy01>rcEC-Y(3!eL@rL2!>F~Jmbf=uCOBrx|K zj|;V2q&e4Th;tpupX30Qo^{0`UfL?_B%sm-PtWk1H*e0P)JH{6X6I?8<_lUYuKDw) zX|R`$kNnMsekhF+MDzN?J8AV%*pyxJ@`Te}scim1biWV}Iv0t03CLU13WB$cVSWC! z0?tS{vA*@go5{|_9rwmUR#BVH{KQopRx5dk``%78Nls*2I)Gq#uE1X^P%xDt+A)D( zd%BlYl2HP|e_7S%`&X$7f*)}i(em$E=tQh=LSb4NKMuohWW`l*kf3kay{Ralg%ou1 zzrN6lEn)F1LN*{nV9%}x6*lu5o~D4nB?3 zm2p?9pLHkI|42|?eKHEGM?-Vgu&Ji7lPWMs_TY$xzE@y>2qICV?4}mF66%S&gax2o zFkI!x?d>iCvXZI0C_vx3YA*_=xN@8lXL1$+%<3cU7a!JnveEbgYKFLk2!3T`>DTZ^ErM%MiX$CJy4wgZsw0uFCVAHDus zpt-5LsR}OorlM$m!IECF*ws6lyc_EbvTm4B#@53n98frfD8Knsnp}2jwwA5%lULGK%Pz9~hKMo5ebO(BSB4XrU}qBu@0s+Nyia)ju{e*b zGP*XOQ2D=$;n)wJ7h}Qll1NoL7*sziF6ZL(JfLCqgZt8OvMF4`?$GxUV%P7}v zu?d*2Py#io1L&A_IcoSlP5{^STCo(EDJ%P|1;^=F@F~LI%bp#qyw!38!d4kJAAJ(`iXX^G0IWGbcG)SbzfYbYe;n!juU&04`=TA z$Z#V==2|9}{M76p3!HF^N%XO@h%BJWYag9rw{bJ1PAfr_gI`eGl zqYr4h8!hy4>3@(n@$wRDa$Zt#4Zui zF^f_jmRlJ|iqNFI(`KClKo0~vcz!D~+P!1!OTa`7e~ES~2mVB({kY7BorQA(k!ZkX zI8dn9JJ%*`_=8zI_2xd0oYnNssXE1tMCkZ*64W|;QO1@UJ>jjfwzESXy8m3FJ5Vq zO%%vnd?djb4qWa45^}*wpBmsa0ka(zEEu-5D)?ne3fBNG|FD2z08URw!(eyc_k~bq zQMUwOOa{KegV5MJh=Ui3&i{q#KkD;xBS|zWSR}C#1X4q7Q}3#{9Ck!yybw=1y;aut zxQ_O<$RCpBZt>*{Y`y7&T}piC6XvB(PcZS$hLgT|+Z=3hcmV%-CcIh;M|hlXy$0#I zgLd9zl|GF7G{$uLBbi7g7-tX$g8rD`;Qp4P2If0)+MEJ(|NQk*U4JDmLP99~cQ}}Y zEn2rL5QS!&??{yl89q){8&2D>#P_Y^6cWE694!gE^7Bii;_@?ED!POu;atZ9FHDgn zb8!i-3@i$vj1puW2AME@tb=fwr^{A4i5#z|8JSv6z-in~6AdDQTGG9$GVXYk#E`_l z8`dLx<6KjqNEws6P1cqxcZ^jJ_dMnzE$Dajgj-;9bME511r7#pC|ixp3FH+OLZ6#` zE9%VUb~K)7AmZ*syhaZ8`uNFc^X-5|k4h0`@ebUwECROmnf)5yTW^MKf%g-osjFk$ z>WscV9!EyValXPIe5@GgzUrPo9^jev&{$?WD67Js&_<|z(&}zks%BCb3TeJFyf{Ne z;8H#X$s9i7VyV?DH@(2&og&}Qv34t?48r}@Zulk)0fFx}>KT-_{%a2xUVF?!Z`3*u zd&^7!a;3ectgGf`8n(Hb*_*;fUi4{7)8m_#1OL&Zsdw21As!OPb{kO$Cr!5ojl}p+ z&htwILh&9hicvVTAE<-coffG(y?eq}VVLkGXmt8~VBPeN99t>_b+~iz6rkcpC-AZ`~(hIt&d6MEP(&Nk^tEU|LR z{gr!?48M232?jP|XIjbIH{L_wEcIwp2h3Bk6l#5YYo?E_0-W zBHlfcZi3mFS1!Wr2jX* z`&DD60E%8RuA}Stx$AQUxqNFYTrr0Qwoz*~Xn6r1LuU=%AVqx@dS}&e0>C4$#?Wy9 zACG`YP2(9KbnFPjZ0)q_)}$6c#yn+=7$-1qjc9>fyaPMw0in{-Ci=HHU%bkFyjoD# zUa)ys?<3rbEB+tmaK1Ja(^BuTtiUx7>&7r|Rw-+n@k*-)y7C_td#oKSPx51<8O>L2 zilShL;3w)mCAt~#%PK>Bgwz@nm9=5P36>By>v@V!9-QS-w^nl{62G&id%TIH@+>Ev(f3zCxWU6_`hfPMv{0_>EU&AalBfa z5&AdoUXTSE+9lAyjTUg^Ot1b-k;O%(Rb=phhQUV64owfHpFEB{zXn^pop88D0A8A} zGmUhh4IVT&GECSJg$zzC0@}BZnVGR5NE?bpGbSL7z+r6RKd~`*3~W54Z5~>wDk{qN z-{W9DXph@#;@So?kVv$u{VH7vOS*O!X;NT-@+5IIep94=wD(xroaPT_tH_e{@%iC< zFPHK@)kI9bKZZdG-s84H0`>iv1jFsI;PE+df9d;Xf2D5PM-1!+e&$P@+mejWNkfgwv8m-CD9WS3$v$UP@$d%U+P%)T#;DJwb{R#zNYThS*1k73-c z41n~ePq#edQS${lkE4BVu0zWR$U)hi^bd7wlC&S;93Id0R-!E(wz3ji9|qIF-ai9@ zqOs^{x+Gtk@!I+WAE)2B`NS2WVAdi$Qfvm8F*s_2vny9rW~OzQ6n?NW_4)xt0{pS( zmX3Q7Ez#M*_Q%`ZQyHa{iV9+1mwXxH$6|thiCUnF&f_GyL1zEdWJ5B=ONvm_xCIrbi3O?}K9*rqSb#;> z#{+{xWOFpPnMY-M{ooNi=9IR(!1*8cs{ba!)ZWBu@8 z#YOT@$(*O#$}j(#cO_Q;l@h6oqhfQ@F>NaUYDR$~Q;_l`N%Vx}*I*yLf@e__c{XD}alYb_p95I$~X@I|B5AljC#lMwuM86TRy%e=BPFYmEge9;zNbFc8q)pCR*a zq&Q_<9PE|N9G%Tv%kfZhvz!*r7VrN)dzPURD>HJ#8v!{+wDllrWR?HP0vYomrACXGay zf!>>=N{Gdc$Yoja-?AzwZ33rFXP!_`Dixm;aku|65l4Z+Yy$?1qY~vz5KY-*17N@`U|56B56U z0rU6-2i&VZrydfTC1^8*F%dsTGFvKTsZ>hzX7hMoS{5C2q(Y&&*VS~^iKp9dcoCl^ zKoXnnteLnEC5O1MU0HJ|rQco*+FR?#ZQ0;VQc{-mj26l{Eob;uw+|_5k#D;)y-l(; zSmP+wM9lKIj{DWNAiLHQCQu2kdgp#3T~+di%03(7##BB9!?D9_7w(a}Zzb9>_v%kG zU|G+8`66K}c*pi_JO2O+@?ZUtu;FycV)md1tN>7K#RF7zj2SX8sRKe{_@uUU@0fUN zss`c?39TbXZeXc=M;E$$oZpttW_NXH`ENeGLgforCm62RBOhssWIIwv4ia{N+ z`dHM^DKPj{*+zV|`VDf0?Q zdt!eFVIIN5v3swy@OqjdI*yU^5!CN%xr#QGH-m17a-v<|o4SsE7YwOPPL?MfRyjq;f z;#6zFqJMlfZwe^5&wOAU@&B)KkH7j9wA%@AKR6IjASw`$(7zGk|5?AFXyp9=#dVFQ ztjjtFl7MU<$S$#{nQEy;)%37Zwivd~<*>bP7?o6zI0_z6^SMp zq^}_%p*tpxm3Og^#BGr&H=}A}k@%#CT5Gzzmp)8|G|jCk|;6UzuEQ$&sP`9HkZhlp1zbq2<_OePC(__rPNn3w^h`&3l*x^W|Lf~PZr5F zc!b_WmIn|`WTETKjr)#91UgI+;TB&Xr?aTNMAgB?+ViocSu2;YUq)pn&}f@U&{R~S zKm;NH14pY_XAg{%(Qq$szN68kFJP8loyIc2Ve-)BzPy_^64Xw~f>c;w?q+nHugpW! z?zTQX9gxXW6P5%4$${+d`zNjora0%rUwuE!$vfT}J9{+0QObe&+gM&jEGS^B^c&Pm zqU=Rj5P27o55wy2oZRqy+A#Q%Vz*mC+zXrl7CeUSJZti?4){p*T}7Wx zm4jiUYc{pO&sL)dAFNRs(A!3)w<=&kHO@C@bK^c4BcqGE$&l}E9_82+&az0To)9B! zm&t~G1|n79#}}12f@iT08ONd@52V=`VU{hW)dI|QpI-O%g|*A{nq7OVq#Or!7BvRs zS8!EE-S;n$u1hqX6jyU3{6vsZ0mC^jKvd?ViSBBdD~FOtU*`y4(cG1dn&!tM@R4OL ztW261J^_29=uc)NiV=m%=1zE3{w}#EpiA_HgFqO37g^C*Uf;mn1-1kJVyCsw^?2ZY z-efv^Z@^a74`MPHS^tgsHi+Dh+jAwA>#`A{^{HF1qdk_C(+|3}i5c+r#w9hN9^n*% z@>~K;h_P4d(Gr=7ird*)z5&B`Z#^GX@U}SvIUQ}GLm{fokqWkTZ;Cgt)*ftq6sM~L z77P4)iZ%!_og2KOiMzkLhea1!BQIpC!rl_q(lQehUG!n}Oj^%42F%+`>NN4}UdXs< z4N3)QWclT2`F85N0uHt#8A_I3i872=1V(CiAcDVcbD@8&p#JwR-5N%bJvCrI(JbgF zhhK5(83^NAlCaMa7Lgcfdx;a|w&tXOdE-qtWcf)MovXu|Gk6eQtuQ@n;yboVrHm>n z#2|+W(ix_RvGyFxasKX3XUaTVBq!=$Ovm#WH8b*bcG6WtPz*SJ`}(G925e0)anl-e zPPET-p4I+t`s@5WekN*;K;@jvjg7)AN-TO)Zm+vaq^_CemMvJnBj4UxE0~_%G9w<=uD^wnsb&AOoE{7{C0l9U(AR8O*-Xk}r?j~%9rgJ)VAY8X4Pl+Tno zN@2G3Y{i_C<`-OP13H9Y_Q1uyK4^Aj9fJyY!vK8SjAgPg43pLiH-uGWtD{v7w}jo< z*aBsuQ3MoMdBCe&)r$oy`6lBlt(!SGZe-5Uv(I#be}oN=_X zqs(iqNeB=oZ!r=qD7KLP0^zM3Jfn%d_V0v+Q)F}Yd}j^nV&G{55xaRt6o?F=C^FpB z*+V4$tYKmnjBO9RVf*<1x>>@K!2DGHG{K$I00D{r8^?sInWw9pk*%nMouh;OA1(Oz z0^~qj)`heUS>Q(>$l(Any0>PED8hVEaZ;Q0LA3^h(hY&i4xE%Rdv*p8mQzN{r%&fR z&|%H$np5|$N_r;Z$D#qWQhIOUQrjPosy4gJ{XlM}ZstlF$)`<@a-=rzyjTV&M8A#<&F5BmO@ zUyMvMHMLBko#NrK05oDt&snn9?p7R{#Fz3_TK4l5uz)yF+lN5>y*%M%{K|{$?)da| zx=a&|O_Pu|_FGY#uEQCN2I=F4r;{t2x#YP7nv7*4_B0pk-VbW8sAn`U2u7{muvOjy zcI&F0NwM~0cUtynuTk}&6m7Dp-*i8X+*1kCZ_%*(X`0|=k(F|-DOiafhi+ec%D5zr zs}`8BrW2)FIHpa|@GVi%4H;!M?OR2(IDYXf$QID^QjYw3a_+%^!3<)@W}+xa^f1Ae z>p(ak&9?$oeOj;$WD~*-Zm*X}X_!EFG;j4PEczkt1ksGSU#MHc6kcZt6CYdTE-R%6 zkq>mBdQKOYM%^uP&@{V$Gk$U7#fJk=%c*Qk%HHo9xm0F%Iv=Vn4baDT{QamHVLg7! z%KJ%j`%H_TM?m}ul$33-4O?PLGD-?LnS^HR{K}l8#hk)aG6|4K#~nZg8${$4vGgXL zTkpv7>Owcg@Yq8-uV4YMx-?8m(TA_cAubR%q_UiFg5Z1C!*krfti`McZ#Xg+l&++e z7hk67*$(d(*VeD89|j)Js1p!LF!s($voh9`g0>lJaAZMH&e9okqV>=Xy+1T$$lB9iJp;$k!qQg+Vf@1GkB8}wf zhLr@XYM;RfMg{_{QX_tD$K}q29DqrO>JyLG*?_t(2fi0U8W5tpMqcb5q5u8Jq5uOA z8sOQNaW!B$)>5?(m zo9lrkLW000la?c>&n?DCAKSQ3lF7SKb`vM*a&1M_V zjM}UzeZBeQvMI-S@;dYo5er~~JY@lQ!+jr9?1mVi7`S*ccU;{n{u}f=AzF>C3plWp z8J3GJ#ML2oZYf8ZVwe>LeiTT`RdA?O1VVW_n7QH71S?A^lAD{O7x`5nseyg9*~mye zFOw&6UCR#XKUJF#X2QCW@ z>tJ-lai;l@5D|L-_1SJkQed&Yw2?4YQn32H;cGe_nrAj!oQF5(6n|`9BL?TpAs#5f z&7)0Qxj|@M9kj@X?xf*1Pr z9s_Iv$xlr=c1>bF!~{Fg%IFaN$e$4qjfD)qr6A^I71Pk1;X~p>y2O~ z><@32D!|FlGqpl1ZEBI-nV<`Hu0?t9&ak8-J}x>f%viHN z*(Guc@136qpXEH!Q(9Qg9eo_nQ*+C;&$uyamPiel=2_}d1*p`JPAxaf4r<+B5ET|o zZ=c?g7M9TxYZbV_AN(#Ne6-Lfbpo&t!1v(PR!CQ0CGSgPzgM%XA@~oTo)< zF?(zy-Saqcy_SmeSySbrk|u;kcX{oq=MM5-iTz%(5fh(F0KoEW{8~n%z}%D&`y-L% za>m=mf>qhXYv%APm@82mKF~2uhR;qF`LX5TC*F#9%I=Z@dq3$t#E)mgAWuJ-IuR<~ zhw#mLOD2%r;#}vEX|IjL_e1s_rY{fVA$<|Z5=XygH8G$xERPJsmGENFMr##U>2>ww zPoNWis>-!M{d|wHxr32a0fl6=TDkYhY)gP8~xJJ+|!SOUTMcZbWiGn zz;86#_X(2{TSH(}8|_`#$82bcrQ(1`G&?!Ti{YLSWkd4Qi`nwUNBiXf_?OHJ#aXmk z%LkNj?ZBujyZF(z6aKQ;nJLr*cJ_qBmXRfOuh`Bt4<0p9K6&z#exMADmMxE1tTvud zQ{_b9{l)a&^H?E#y0UT9242u6ey#rP4wdLG$jJ#u)Va&k{(9+}JLM723G{7b6Uq_n zlv}&g-Z8R@%SU<(gI`BW@$M|_P_q^6TiRFjH_Y!(ojZjN*HN zAui1tmet@Wz=-Iqi`93rY)@NSf0f)fyM>9vh?cJ8y#w^v z5tv%-9 z_+7qlVR~QhhliUFBTurazZ)*TDKU?j81e~|^2dzL@-DYm&2d-xQ(?5k=T<3~Mk+35 z@N@p!Ujws)UQxSVh6Gx_l}Zgj=-(Q9}8%+@*xY?q1hSr66Q4QKmIU^~`}m((Ds zoo8_pB?b2rSIZ~R%ePJMEP8neTOlF_Q4c(E-BIFEy|X?N3eOU!2#7S&bQP&NFnGd) z+3;YvMvFcrEtjD4_NuOw95$j~y*N1G+Aj;Q9OVBRFc~))i@!b??oL|qei(ZG+dbw# zHl>jKkMh;t%=KUCZlU{^fQ;-M%#4lfjQ*vD8Rq{bq?@ai?LWTiUvG9);>_yg{@fHA z|B>w?{|>IOv$K(xhO?2QqnY#H>-A7|xxd`)@T>G0R}skkV#v`qS#B76AnPDwjaUw7E1JorRx$3ODBddX zUYR25^OuPUIQbpWRt$XWE1~|Gs8(^fp~Fl42hOD>(@Lx&ub37vx_X z%L^qN%?WEowe>+Q4RhQu{Rw@01lgBs~N5{Q`t)e02G#~tVEt@hEC@adaDc7Nk3Q7VqPqVhE4TIPn+nU8$ zu0`2BQ@kfOj->;@$^!@k)JBB$EP=a-iRnPISSqPPa)H0w#FgOmVL?*N5PX>?!Y`t= z9;GaGXxqS0?XL)Tnj%Wrou31?WHwl$PkOQfNN+1pcq?)+U3qqm=RMRR5mdlwH0KYN zckL+!NU=v7Pk<%gsa4~dgsz1B#0ZBTKu`trQOskz2#2DT$4o}YMv^PKK05}31(&t1sWZAjsoM1#5=}|)9=Vl6H(t0#@asu3G7JOHv@DfQ_UYj zJ2QBrpu!&a{0eo7Pag`XsU+oC#D<`E8*=>-@Q-hw27Z6;!o4n&IQ6~)TKGsdD2-Z{ zaMNaM08We@+`n{twhLPt2ZeJ|m|-eb5J^OZP|!)P;b0ymfBK$ex4qTM>Ow{-R?|Y+)BdirK z$6@lf7AS&?3Fi24hmfIpmWbhF8Jk#g#J=g=NidStOH2Y#dS7tEB+_c!88OJbn6{1x zrK9S)GETSc7_uFyWXd#yIjT`VGY*|cg1xX5L|1mcs!x)O)v-{RWhUof2@HM+xMFiF zy+fZG<5W+TnrwX?4dc~by+VC`nqgTzUPlpnfcF@(ry4ZuwMsonja-~qcuD@BPR=|W z%Jq%o3}b7u8%wq%kv03+vK>SmyX?e>Y(>bvWEwtzD!YXNc4ca{p z@>y1OhD!(R^O%_ox>LLG!I!-tJjUzB2&W@%s_@@8+1wY(DSI*M|orjq8IqGQT~ zhlWo-gfZEkPic%@JY1(2d}HOxXlMj|q|bW;)IHy9B;~_H^4AlxAJVg_9`SnqDLBI1 zxa8gn;NxY15KiN}+6Q~F9xaO;Oi<33C!Lk1(ipzmqEkfN^BW9frlzbBvI|b((7zPa zA=l~Snk)V7pHF-{VX&Y;7hZ!nSc}eX!hV{Tf z=bklh?Tvq|mw7|%dqVL(S<|E4@B0@GKCFiHSHzj2vBPiyVarKe0NTXfQ-ZSw?xW{b8a0y&%7WSQE8te-unVKnN8)>Fj#pVsCzTDW~G4 ztHZltkK17e4UeoET0_$&d&F1@!L|C~UnGma%*~B`lT3C@RtD7A`9X_^YE&ibkv>bN zmZ2lvy`Qm*GcKR)?BZpSo$(d24rg;aOcf=c7y;YPHZTFj=VN8l^q`MSjf$+4>}frc zyI+XjC^)HiWWXxCv>Wq8+9u7smJ+%0c9PCgk_Cn3YTW%LxUZh19e8fH6#ZCGQmY4`PPAFDL~cIbbSPjc`D|Xy>0D+a~I4E zY!39&ETMyT=k^G?u65`ulONX+X-7#mg_$`kx=+9h`@;vZbhG40sw1pk6n7*jW?EU( z)-KE7+}?|?EzJ&kVwy6L)k)q8EHn3O`h+?@fc`M)w8cLYgGk<3XLaZm$U~GNLaL8p z+fsBIsH@jbI2_%)T_%6ePlXE$OZ_S%DIwvm{5+JIrdD5lC8Y=WC_MWWR-~!vt=)+; zw}S03rflU5#`O*_xrZJY7X)v}75Le7V>s0Y#v|wGpg2uXe{(}=dDXMq$6;PD1_hLC z+V$e$mKLY+*JtdD^_gs5EvzOf0G#p3cctA3+B(fUahQ_{^abB zEHZVRJ{-Ua*6C%ShYY$T3o-aDVdecT)y&{5Yl~eaniD`j@ zJ6m^BzlgVQ1WhAX)MQ1`B8ZyNO@OtDiLC!7P}8m##PV@Xew1c0a8PJ0=G^i79e2!} zqC8h4QNY4Kdc++Tp6#RgAmlXATA!nDM0O@5bsah*#^sqCllSS}l`CMOC&QFo>Fk1O zV)_OP{bJlIG3krOUN=6C+`XT3tUZmRfKsV%*_I)6{Y6^^uu}tN!2?vM3`!*QLR=Wkgdy_=Cj@CjBZe{nJFL2Q>a2OsW<&N)D7k>xk_ zv!mYg04@{VqorbU5Tt}6PIW~l&BvCdqae4Op2tK>M*T2j&Uxw}!&i&zvNgQ9WOWSJ zC~w9ZPCw*#y=)JRI%X1hSsc?fRKN7sY>QiMV()_^`*kZm6f)}NH8C_^Gfwpm_NV0M z>>Oudj5nf{Ip4ztNfRQop32fp%L8werB^@C^==Nz*vMQOkhB-QFoab5_+>;_ZjF0c zZbpSGL$G?DQNgTRqpGml$CpBOLS-OW11|Vr^HLpB<+iqo+cM^I_Fd#X?Luqs!PeI} z9RVNIG0%MC{0p!oGxZ8Zb(C1YC=5woF%8JNn~ZTDJo%?EMxH9aUcLd%2-u@Ee zpxSs+$B_=E>p!1x6&yva631Jne$2)IK3h9CuQf4bKa; zH23cxCG5!z5KK;5=F!w&*J_eRf#VisfI?R~MgW5q<|X8-Anrm-XHj+YZRM#_Kmb{J zYn_Y~<7=fHp$neQL++RQTxaP%i;hUJxQvhs<2J3)ECl`g6iHUoNf zH0W%zpX0R?V3y0`k6-pl1@>y+od3#tKmzj?C+%u#BZQ-|IMp?q6{u(7oW9@#A8#k0 zLIse`p1sp&ejesf;?6i88%IU;_)>lP)qOgb#OAEdO$*GKN)LRu3}EFid$&GNJ0@Ow zS!91-7CMy0i>aiFk0%Ck=Ti|Z45g~q=yHk4we*-L_cNneoYE49ZesjMKyOo>RtHQr z%hbK_(S+?m(e)X$q~2ZkzHVLj3CGlkvGMQh;{GgI%x0C-+{?K6!AnTnHC_1)Lr3cM z{;&{|eSTx}qI4lb1?@9iumzFztG?6iKP?yV8F;Ol~6=vBQC^w{xhe3cg={CXNZ zvuI#IiwSww8+bx}n*G_P7n@{0_`)FFN+SJ@zsl}?JV4u`2N$HW;2P#b;klSqw*6b;^6k-DFK zMAfb8UCycA8*^d$M+*5O>Sf?TIHY={$SReS_U3^Be@`kmt= zMaFDL@$b2~ZiL`oq1eZ%4L! zen}xS1e>k=(&1O>_Vqi@%#R?p-zKTED{_XI;jR0MwDXhbJ1_R%efQ7@N;&}2W=G-x zLM48006VollDd+%!x3G(r2f0>uFY|zwxsP9L|dHvf4BX0vjx%n=e0+ujc*-U9xtFmMEl4Zr+o5c&2GUwO!Dnj~7W|IS z?v-_7a<+^~%V9)gN8R5DaJS-^u>lbO<$*w^<|3QNPb1Elj7*qJUQt>B3UN_zb(4n5 g$jLz5oR7&$$v8_v6lIS|$wL*KU8P+hrxj%X2kZRYhyVZp literal 0 HcmV?d00001