From 6bd8ec5791364a78413c2f584d6f5d286af1704e Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Thu, 27 Dec 2012 17:46:09 +0100 Subject: [PATCH 01/26] Updated to 1.9.1-SNAPSHOT for development towards the next version. --- ProtocolLib/pom.xml | 2 +- ProtocolLib/src/main/resources/plugin.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ProtocolLib/pom.xml b/ProtocolLib/pom.xml index c95ebec1..a4291eae 100644 --- a/ProtocolLib/pom.xml +++ b/ProtocolLib/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.comphenix.protocol ProtocolLib - 1.9.0 + 1.9.1-SNAPSHOT jar Provides read/write access to the Minecraft protocol. diff --git a/ProtocolLib/src/main/resources/plugin.yml b/ProtocolLib/src/main/resources/plugin.yml index 68783116..96a0216e 100644 --- a/ProtocolLib/src/main/resources/plugin.yml +++ b/ProtocolLib/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ProtocolLib -version: 1.9.0 +version: 1.9.1-SNAPSHOT description: Provides read/write access to the Minecraft protocol. author: Comphenix website: http://www.comphenix.net/ProtocolLib From f995accd0c35a23596e6cfc6adc428003f1fb829 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Thu, 27 Dec 2012 19:00:49 +0100 Subject: [PATCH 02/26] Let maven automatically insert the correct version in plugin.yml --- ProtocolLib/src/main/resources/plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProtocolLib/src/main/resources/plugin.yml b/ProtocolLib/src/main/resources/plugin.yml index 96a0216e..e4edf705 100644 --- a/ProtocolLib/src/main/resources/plugin.yml +++ b/ProtocolLib/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ProtocolLib -version: 1.9.1-SNAPSHOT +version: ${project.version} description: Provides read/write access to the Minecraft protocol. author: Comphenix website: http://www.comphenix.net/ProtocolLib From 711ffb1db733e600416337d6dd33e667ec5805b8 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 2 Jan 2013 20:16:53 +0100 Subject: [PATCH 03/26] Updated Metrics to version 6. --- .../comphenix/protocol/metrics/Metrics.java | 177 ++++++++++-------- .../protocol/metrics/Scheduling.java | 81 ++++++++ 2 files changed, 179 insertions(+), 79 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/metrics/Scheduling.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/metrics/Metrics.java b/ProtocolLib/src/main/java/com/comphenix/protocol/metrics/Metrics.java index 3ee7d6e9..3ff786ff 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/metrics/Metrics.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/metrics/Metrics.java @@ -70,85 +70,73 @@ import java.util.UUID; import java.util.logging.Level; /** - *

- * The metrics class obtains data about a plugin and submits statistics about it to the metrics backend. - *

- *

- * Public methods provided by this class: - *

+ *

The metrics class obtains data about a plugin and submits statistics about it to the metrics backend.

+ * Public methods provided by this class:

* * Graph createGraph(String name);
- * void addCustomData(Metrics.Plotter plotter);
+ * void addCustomData(BukkitMetrics.Plotter plotter);
* void start();
*
*/ -class Metrics { +public class Metrics { /** * The current revision number */ - private final static int REVISION = 5; - + private final static int REVISION = 6; /** * The base url of the metrics domain */ private static final String BASE_URL = "http://mcstats.org"; - /** * The url used to report a server's status */ private static final String REPORT_URL = "/report/%s"; - /** - * The separator to use for custom data. This MUST NOT change unless you are hosting your own - * version of metrics and want to change it. + * The separator to use for custom data. This MUST NOT change unless you are hosting your own version of metrics and + * want to change it. */ private static final String CUSTOM_DATA_SEPARATOR = "~~"; - /** * Interval of time to ping (in minutes) */ private static final int PING_INTERVAL = 10; - /** * The plugin this metrics submits for */ private final Plugin plugin; - /** * All of the custom graphs to submit to metrics */ private final Set graphs = Collections.synchronizedSet(new HashSet()); - /** * The default graph, used for addCustomData when you don't want a specific graph */ private final Graph defaultGraph = new Graph("Default"); - /** * The plugin configuration file */ private final YamlConfiguration configuration; - /** * The plugin configuration file */ private final File configurationFile; - /** * Unique server id */ private final String guid; - + /** + * Debug mode + */ + private final boolean debug; /** * Lock for synchronization */ private final Object optOutLock = new Object(); - /** - * Id of the scheduled task + * The scheduled task */ - private volatile int taskId = -1; + private volatile Scheduling.TaskWrapper task = null; public Metrics(final Plugin plugin) throws IOException { if (plugin == null) { @@ -164,6 +152,7 @@ class Metrics { // add some defaults configuration.addDefault("opt-out", false); configuration.addDefault("guid", UUID.randomUUID().toString()); + configuration.addDefault("debug", false); // Do we need to create the file? if (configuration.get("guid", null) == null) { @@ -173,11 +162,12 @@ class Metrics { // Load the guid then guid = configuration.getString("guid"); + debug = configuration.getBoolean("debug", false); } /** - * Construct and create a Graph that can be used to separate specific plotters to their own graphs - * on the metrics website. Plotters can be added to the graph object returned. + * Construct and create a Graph that can be used to separate specific plotters to their own graphs on the metrics + * website. Plotters can be added to the graph object returned. * * @param name The name of the graph * @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given @@ -198,7 +188,7 @@ class Metrics { } /** - * Add a Graph object to Metrics that represents data for the plugin that should be sent to the backend + * Add a Graph object to BukkitMetrics that represents data for the plugin that should be sent to the backend * * @param graph The name of the graph */ @@ -228,14 +218,13 @@ class Metrics { } /** - * Start measuring statistics. This will immediately create an async repeating task as the plugin and send - * the initial data to the metrics backend, and then after that it will post in increments of - * PING_INTERVAL * 1200 ticks. + * Start measuring statistics. This will immediately create an async repeating task as the plugin and send the + * initial data to the metrics backend, and then after that it will post in increments of PING_INTERVAL * 1200 + * ticks. * * @return True if statistics measuring is running, otherwise false. */ - @SuppressWarnings("deprecation") - public boolean start() { + public boolean start() { synchronized (optOutLock) { // Did we opt out? if (isOptOut()) { @@ -243,12 +232,12 @@ class Metrics { } // Is metrics already running? - if (taskId >= 0) { + if (task != null) { return true; } // Begin hitting the server with glorious data - taskId = plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(plugin, new Runnable() { + task = Scheduling.runAsynchronously(plugin, new Runnable() { private boolean firstPost = true; @@ -257,11 +246,11 @@ class Metrics { // This has to be synchronized or it can collide with the disable method. synchronized (optOutLock) { // Disable Task, if it is running and the server owner decided to opt-out - if (isOptOut() && taskId > 0) { - plugin.getServer().getScheduler().cancelTask(taskId); - taskId = -1; + if (isOptOut() && task != null) { + task.cancel(); + task = null; // Tell all plotters to stop gathering information. - for (Graph graph : graphs){ + for (Graph graph : graphs) { graph.onOptOut(); } } @@ -276,7 +265,9 @@ class Metrics { // Each post thereafter will be a ping firstPost = false; } catch (IOException e) { - Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage()); + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage()); + } } } }, 0, PING_INTERVAL * 1200); @@ -291,15 +282,19 @@ class Metrics { * @return true if metrics should be opted out of it */ public boolean isOptOut() { - synchronized(optOutLock) { + synchronized (optOutLock) { try { // Reload the metrics file configuration.load(getConfigFile()); } catch (IOException ex) { - Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + } return true; } catch (InvalidConfigurationException ex) { - Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + } return true; } return configuration.getBoolean("opt-out", false); @@ -307,30 +302,30 @@ class Metrics { } /** - * Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task. - * - * @throws IOException - */ + * Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task. + * + * @throws java.io.IOException + */ public void enable() throws IOException { // This has to be synchronized or it can collide with the check in the task. synchronized (optOutLock) { - // Check if the server owner has already set opt-out, if not, set it. - if (isOptOut()) { - configuration.set("opt-out", false); - configuration.save(configurationFile); - } + // Check if the server owner has already set opt-out, if not, set it. + if (isOptOut()) { + configuration.set("opt-out", false); + configuration.save(configurationFile); + } - // Enable Task, if it is not running - if (taskId < 0) { - start(); - } + // Enable Task, if it is not running + if (task == null) { + start(); + } } } /** * Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task. * - * @throws IOException + * @throws java.io.IOException */ public void disable() throws IOException { // This has to be synchronized or it can collide with the check in the task. @@ -342,9 +337,9 @@ class Metrics { } // Disable Task, if it is running - if (taskId > 0) { - this.plugin.getServer().getScheduler().cancelTask(taskId); - taskId = -1; + if (task != null) { + task.cancel(); + task = null; } } } @@ -370,17 +365,45 @@ class Metrics { * Generic method that posts a plugin to the metrics website */ private void postPlugin(final boolean isPing) throws IOException { - // The plugin's description file containg all of the plugin data such as name, version, author, etc - final PluginDescriptionFile description = plugin.getDescription(); + // Server software specific section + PluginDescriptionFile description = plugin.getDescription(); + String pluginName = description.getName(); + boolean onlineMode = Bukkit.getServer().getOnlineMode(); // TRUE if online mode is enabled + String pluginVersion = description.getVersion(); + String serverVersion = Bukkit.getVersion(); + int playersOnline = Bukkit.getServer().getOnlinePlayers().length; + + // END server software specific section -- all code below does not use any code outside of this class / Java // Construct the post data final StringBuilder data = new StringBuilder(); + + // The plugin's description file containg all of the plugin data such as name, version, author, etc data.append(encode("guid")).append('=').append(encode(guid)); - encodeDataPair(data, "version", description.getVersion()); - encodeDataPair(data, "server", Bukkit.getVersion()); - encodeDataPair(data, "players", Integer.toString(Bukkit.getServer().getOnlinePlayers().length)); + encodeDataPair(data, "version", pluginVersion); + encodeDataPair(data, "server", serverVersion); + encodeDataPair(data, "players", Integer.toString(playersOnline)); encodeDataPair(data, "revision", String.valueOf(REVISION)); + // New data as of R6 + String osname = System.getProperty("os.name"); + String osarch = System.getProperty("os.arch"); + String osversion = System.getProperty("os.version"); + String java_version = System.getProperty("java.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + // normalize os arch .. amd64 -> x86_64 + if (osarch.equals("amd64")) { + osarch = "x86_64"; + } + + encodeDataPair(data, "osname", osname); + encodeDataPair(data, "osarch", osarch); + encodeDataPair(data, "osversion", osversion); + encodeDataPair(data, "cores", Integer.toString(coreCount)); + encodeDataPair(data, "online-mode", Boolean.toString(onlineMode)); + encodeDataPair(data, "java_version", java_version); + // If we're pinging, append it if (isPing) { encodeDataPair(data, "ping", "true"); @@ -411,7 +434,7 @@ class Metrics { } // Create the url - URL url = new URL(BASE_URL + String.format(REPORT_URL, encode(plugin.getDescription().getName()))); + URL url = new URL(BASE_URL + String.format(REPORT_URL, encode(pluginName))); // Connect to the website URLConnection connection; @@ -474,8 +497,8 @@ class Metrics { } /** - *

Encode a key/value data pair to be used in a HTTP post request. This INCLUDES a & so the first - * key/value pair MUST be included manually, e.g:

+ *

Encode a key/value data pair to be used in a HTTP post request. This INCLUDES a & so the first key/value pair + * MUST be included manually, e.g:

* * StringBuffer data = new StringBuffer(); * data.append(encode("guid")).append('=').append(encode(guid)); @@ -506,11 +529,10 @@ class Metrics { public static class Graph { /** - * The graph's name, alphanumeric and spaces only :) - * If it does not comply to the above when submitted, it is rejected + * The graph's name, alphanumeric and spaces only :) If it does not comply to the above when submitted, it is + * rejected */ private final String name; - /** * The set of plotters that are contained within this graph */ @@ -550,7 +572,7 @@ class Metrics { /** * Gets an unmodifiable set of the plotter objects in the graph * - * @return an unmodifiable {@link Set} of the plotter objects + * @return an unmodifiable {@link java.util.Set} of the plotter objects */ public Set getPlotters() { return Collections.unmodifiableSet(plotters); @@ -572,11 +594,10 @@ class Metrics { } /** - * Called when the server owner decides to opt-out of Metrics while the server is running. + * Called when the server owner decides to opt-out of BukkitMetrics while the server is running. */ protected void onOptOut() { } - } /** @@ -606,10 +627,9 @@ class Metrics { } /** - * Get the current value for the plotted point. Since this function defers to an external function - * it may or may not return immediately thus cannot be guaranteed to be thread friendly or safe. - * This function can be called from any thread so care should be taken when accessing resources - * that need to be synchronized. + * Get the current value for the plotted point. Since this function defers to an external function it may or may + * not return immediately thus cannot be guaranteed to be thread friendly or safe. This function can be called + * from any thread so care should be taken when accessing resources that need to be synchronized. * * @return the current value for the point to be plotted. */ @@ -644,6 +664,5 @@ class Metrics { final Plotter plotter = (Plotter) object; return plotter.name.equals(name) && plotter.getValue() == getValue(); } - } } \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/metrics/Scheduling.java b/ProtocolLib/src/main/java/com/comphenix/protocol/metrics/Scheduling.java new file mode 100644 index 00000000..42e6db77 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/metrics/Scheduling.java @@ -0,0 +1,81 @@ +package com.comphenix.protocol.metrics; + +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; + +/** + * Allows us to stay backwards compatible with older versions of Bukkit. + * + * @author Kristian + */ +class Scheduling { + /** + * Represents a backwards compatible Bukkit task. + */ + public static interface TaskWrapper { + /** + * Cancel the current task. + */ + public void cancel(); + } + + /** + * Schedule a given task for asynchronous execution. + * @param plugin - the owner plugin. + * @param runnable - the task to run. + * @param firstDelay - the amount of time to wait until executing the task for the first time. + * @param repeatDelay - the amount of time inbetween each execution. If less than zero, the task is only executed once. + * @return A cancel token. + */ + public static TaskWrapper runAsynchronously(final Plugin plugin, Runnable runnable, long firstDelay, long repeatDelay) { + return runAsynchronously(plugin, plugin.getServer().getScheduler(), runnable, firstDelay, repeatDelay); + } + + /** + * Schedule a given task for asynchronous execution. + * @param plugin - the owner plugin. + * @param scheduler - the current Bukkit scheduler. + * @param runnable - the task to run. + * @param firstDelay - the amount of time to wait until executing the task for the first time. + * @param repeatDelay - the amount of time inbetween each execution. If less than zero, the task is only executed once. + * @return A cancel token. + */ + public static TaskWrapper runAsynchronously(final Plugin plugin, final BukkitScheduler scheduler, Runnable runnable, long firstDelay, long repeatDelay) { + try { + @SuppressWarnings("deprecation") + final int taskID = scheduler.scheduleAsyncRepeatingTask(plugin, runnable, firstDelay, repeatDelay); + + // Return the cancellable object + return new TaskWrapper() { + @Override + public void cancel() { + scheduler.cancelTask(taskID); + } + }; + + } catch (NoSuchMethodError e) { + return tryUpdatedVersion(plugin, scheduler, runnable, firstDelay, repeatDelay); + } + } + + /** + * Attempt to do the same with the updated scheduling method. + * @param plugin - the owner plugin. + * @param scheduler - the current Bukkit scheduler. + * @param runnable - the task to run. + * @param firstDelay - the amount of time to wait until executing the task for the first time. + * @param repeatDelay - the amount of time inbetween each execution. If less than zero, the task is only executed once. + * @return A cancel token. + */ + private static TaskWrapper tryUpdatedVersion(final Plugin plugin, final BukkitScheduler scheduler, Runnable runnable, long firstDelay, long repeatDelay) { + final BukkitTask task = scheduler.runTaskTimerAsynchronously(plugin, runnable, firstDelay, repeatDelay); + + return new TaskWrapper() { + @Override + public void cancel() { + task.cancel(); + } + }; + } +} From 3723e73e81d608144dd2ac486988b930cc5cd9a6 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 2 Jan 2013 20:17:15 +0100 Subject: [PATCH 04/26] Reverted the attempt at automating the version number in plugin.yml. --- ProtocolLib/dependency-reduced-pom.xml | 2 +- ProtocolLib/src/main/resources/plugin.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ProtocolLib/dependency-reduced-pom.xml b/ProtocolLib/dependency-reduced-pom.xml index ea37303c..33fc026b 100644 --- a/ProtocolLib/dependency-reduced-pom.xml +++ b/ProtocolLib/dependency-reduced-pom.xml @@ -4,7 +4,7 @@ com.comphenix.protocol ProtocolLib ProtocolLib - 1.9.0 + 1.9.1-SNAPSHOT Provides read/write access to the Minecraft protocol. http://dev.bukkit.org/server-mods/protocollib/ diff --git a/ProtocolLib/src/main/resources/plugin.yml b/ProtocolLib/src/main/resources/plugin.yml index e4edf705..a9ad304f 100644 --- a/ProtocolLib/src/main/resources/plugin.yml +++ b/ProtocolLib/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ProtocolLib -version: ${project.version} +version: 1.9.1 description: Provides read/write access to the Minecraft protocol. author: Comphenix website: http://www.comphenix.net/ProtocolLib From 6996db9e63040fbdfd5afca8b701506082e75a5b Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 2 Jan 2013 20:37:24 +0100 Subject: [PATCH 05/26] Add a serializer for reading and writing ItemStacks to byte arrays. To aid discoverability, we add a short-cut to this serializer in PacketContainer, next to the getByteArrays() method. --- .../protocol/events/PacketContainer.java | 9 +++ .../protocol/utility/StreamSerializer.java | 73 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java index c724a266..b819ee04 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java @@ -51,6 +51,7 @@ import com.comphenix.protocol.reflect.cloning.ImmutableDetector; import com.comphenix.protocol.reflect.cloning.AggregateCloner.BuilderParameters; import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.utility.MinecraftReflection; +import com.comphenix.protocol.utility.StreamSerializer; import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.WrappedDataWatcher; @@ -235,6 +236,14 @@ public class PacketContainer implements Serializable { public StructureModifier getByteArrays() { return structureModifier.withType(byte[].class); } + + /** + * Retrieve a serializer for reading and writing ItemStacks stored in a byte array. + * @return A instance of the serializer. + */ + public StreamSerializer getByteArraySerializer() { + return new StreamSerializer(); + } /** * Retrieves a read/write structure for every int array field. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java new file mode 100644 index 00000000..2edbb3cf --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java @@ -0,0 +1,73 @@ +package com.comphenix.protocol.utility; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; + +import org.bukkit.inventory.ItemStack; + +import com.comphenix.protocol.reflect.FuzzyReflection; + +/** + * Utility methods for reading and writing Minecraft objects to streams. + * + * @author Kristian + */ +public class StreamSerializer { + // Cached methods + private static Method readItemMethod; + private static Method writeItemMethod; + + /** + * Read or deserialize an item stack from an underlying input stream. + *

+ * To supply a byte array, wrap it in a {@link java.io.ByteArrayInputStream ByteArrayInputStream} + * and {@link java.io.DataInputStream DataInputStream}. + * + * @param input - the target input stream. + * @return The resulting item stack. + * @throws IOException If the operation failed due to reflection or corrupt data. + */ + public ItemStack deserializeItemStack(DataInputStream input) throws IOException { + if (readItemMethod == null) + readItemMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()). + getMethodByParameters("readPacket", + MinecraftReflection.getItemStackClass(), + new Class[] {DataInputStream.class}); + try { + Object nmsItem = readItemMethod.invoke(null, input); + + // Convert back to a Bukkit item stack + return MinecraftReflection.getBukkitItemStack(nmsItem); + + } catch (Exception e) { + throw new IOException("Cannot read item stack.", e); + } + } + + /** + * Write or serialize an item stack to the given output stream. + *

+ * To supply a byte array, wrap it in a {@link java.io.ByteArrayOutputStream ByteArrayOutputStream} + * and {@link java.io.DataOutputStream DataOutputStream}. + * + * @param output - the target output stream. + * @param stack - the item stack that will be written. + * @throws IOException If the operation fails due to reflection problems. + */ + public void serializeItemStack(DataOutputStream output, ItemStack stack) throws IOException { + Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack); + + if (writeItemMethod == null) + writeItemMethod = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()). + getMethodByParameters("writePacket", new Class[] { + MinecraftReflection.getItemStackClass(), + DataOutputStream.class }); + try { + writeItemMethod.invoke(null, nmsItem, output); + } catch (Exception e) { + throw new IOException("Cannot write item stack " + stack, e); + } + } +} From 07ae10bcd6788d8738d38fdaeaf44fd38cf85652 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 2 Jan 2013 20:39:10 +0100 Subject: [PATCH 06/26] This is a snapshot version. --- ProtocolLib/src/main/resources/plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProtocolLib/src/main/resources/plugin.yml b/ProtocolLib/src/main/resources/plugin.yml index a9ad304f..96a0216e 100644 --- a/ProtocolLib/src/main/resources/plugin.yml +++ b/ProtocolLib/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ProtocolLib -version: 1.9.1 +version: 1.9.1-SNAPSHOT description: Provides read/write access to the Minecraft protocol. author: Comphenix website: http://www.comphenix.net/ProtocolLib From 73e71ff95466e693afffc867989f6ac6ae921fc8 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Sun, 6 Jan 2013 20:14:25 +0100 Subject: [PATCH 07/26] Don't load classes on secondary threads. ADDRESSES TICKET-27. We'll move the "load class" call to the main thread, hopefully fixing ticket 27. The secondary thread will still call the "define class" protected method, which may not be thread safe. --- .../reflect/compiler/BackgroundCompiler.java | 76 +++++++++++------- .../reflect/compiler/StructureCompiler.java | 79 ++++++++++++++----- 2 files changed, 107 insertions(+), 48 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/BackgroundCompiler.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/BackgroundCompiler.java index 6f636c49..e8447303 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/BackgroundCompiler.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/BackgroundCompiler.java @@ -150,41 +150,59 @@ public class BackgroundCompiler { if (executor == null || executor.isShutdown()) return; - try { - executor.submit(new Callable() { - @Override - public Object call() throws Exception { - StructureModifier modifier = uncompiled; - - // Do our compilation - try { - modifier = compiler.compile(modifier); - listener.onCompiled(modifier); + // Create the worker that will compile our modifier + Callable worker = new Callable() { + @Override + public Object call() throws Exception { + StructureModifier modifier = uncompiled; + + // Do our compilation + try { + modifier = compiler.compile(modifier); + listener.onCompiled(modifier); - } catch (Throwable e) { - // Disable future compilations! - setEnabled(false); - - // Inform about this error as best as we can - if (reporter != null) { - reporter.reportDetailed(BackgroundCompiler.this, - "Cannot compile structure. Disabing compiler.", e, uncompiled); - } else { - System.err.println("Exception occured in structure compiler: "); - e.printStackTrace(); - } + } catch (Throwable e) { + // Disable future compilations! + setEnabled(false); + + // Inform about this error as best as we can + if (reporter != null) { + reporter.reportDetailed(BackgroundCompiler.this, + "Cannot compile structure. Disabing compiler.", e, uncompiled); + } else { + System.err.println("Exception occured in structure compiler: "); + e.printStackTrace(); } - - // We'll also return the new structure modifier - return modifier; - } - }); + + // We'll also return the new structure modifier + return modifier; + + } + }; + + try { + // Lookup the previous class name on the main thread. + // This is necessary as the Bukkit class loaders are not thread safe + if (compiler.lookupClassLoader(uncompiled)) { + try { + worker.call(); + } catch (Exception e) { + // Impossible! + e.printStackTrace(); + } + + } else { + + // Perform the compilation on a seperate thread + executor.submit(worker); + } + } catch (RejectedExecutionException e) { // Occures when the underlying queue is overflowing. Since the compilation // is only an optmization and not really essential we'll just log this failure // and move on. - Logger.getLogger("Minecraft").log(Level.WARNING, "Unable to schedule compilation task.", e); + reporter.reportWarning(this, "Unable to schedule compilation task.", e); } } } @@ -209,7 +227,7 @@ public class BackgroundCompiler { try { executor.awaitTermination(timeout, unit); } catch (InterruptedException e) { - // Unlikely to ever occur. + // Unlikely to ever occur - it's the main thread e.printStackTrace(); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/StructureCompiler.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/StructureCompiler.java index cf25ebe7..579c069a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/StructureCompiler.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/StructureCompiler.java @@ -21,9 +21,9 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import com.comphenix.protocol.reflect.StructureModifier; import com.google.common.base.Objects; @@ -98,6 +98,10 @@ public final class StructureCompiler { private Class targetType; private Class fieldType; + public StructureKey(StructureModifier source) { + this(source.getTargetType(), source.getFieldType()); + } + public StructureKey(Class targetType, Class fieldType) { this.targetType = targetType; this.fieldType = fieldType; @@ -123,7 +127,7 @@ public final class StructureCompiler { private volatile static Method defineMethod; @SuppressWarnings("rawtypes") - private Map compiledCache = new HashMap(); + private Map compiledCache = new ConcurrentHashMap(); // The class loader we'll store our classes private ClassLoader loader; @@ -142,6 +146,37 @@ public final class StructureCompiler { this.loader = loader; } + /** + * Lookup the current class loader for any previously generated classes before we attempt to generate something. + * @param source - the structure modifier to look up. + * @return TRUE if we successfully found a previously generated class, FALSE otherwise. + */ + public boolean lookupClassLoader(StructureModifier source) { + StructureKey key = new StructureKey(source); + + // See if there's a need to lookup the class name + if (compiledCache.containsKey(key)) { + return true; + } + + try { + String className = getCompiledName(source); + + // This class might have been generated before. Try to load it. + Class before = loader.loadClass(PACKAGE_NAME.replace('/', '.') + "." + className); + + if (before != null) { + compiledCache.put(key, before); + return true; + } + } catch (ClassNotFoundException e) { + // That's ok. + } + + // We need to compile the class + return false; + } + /** * Compiles the given structure modifier. *

@@ -158,7 +193,7 @@ public final class StructureCompiler { return source; } - StructureKey key = new StructureKey(source.getTargetType(), source.getFieldType()); + StructureKey key = new StructureKey(source); Class compiledClass = compiledCache.get(key); if (!compiledCache.containsKey(key)) { @@ -195,29 +230,35 @@ public final class StructureCompiler { return type.getCanonicalName().replace("[]", "Array").replace(".", "_"); } + /** + * Retrieve the compiled name of a given structure modifier. + * @param source - the structure modifier. + * @return The unique, compiled name of a compiled structure modifier. + */ + private String getCompiledName(StructureModifier source) { + Class targetType = source.getTargetType(); + + // Concat class and field type + return "CompiledStructure$" + + getSafeTypeName(targetType) + "$" + + getSafeTypeName(source.getFieldType()); + } + + /** + * Compile a structure modifier. + * @param source - structure modifier. + * @return The compiled structure modifier. + */ private Class generateClass(StructureModifier source) { ClassWriter cw = new ClassWriter(0); - - @SuppressWarnings("rawtypes") - Class targetType = source.getTargetType(); + Class targetType = source.getTargetType(); - String className = "CompiledStructure$" + - getSafeTypeName(targetType) + "$" + - getSafeTypeName(source.getFieldType()); + String className = getCompiledName(source); String targetSignature = Type.getDescriptor(targetType); String targetName = targetType.getName().replace('.', '/'); - try { - // This class might have been generated before. Try to load it. - Class before = loader.loadClass(PACKAGE_NAME.replace('/', '.') + "." + className); - - if (before != null) - return before; - } catch (ClassNotFoundException e) { - // That's ok. - } - + // Define class cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, PACKAGE_NAME + "/" + className, null, COMPILED_CLASS, null); From 671654aaaf914ee826117cc190e1d9899a46c41e Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Mon, 7 Jan 2013 10:14:25 +0100 Subject: [PATCH 08/26] Add support for reading and writing NBT tags in packets. This wraps the internal NBT read/write system in Minecraft. --- .../protocol/events/PacketContainer.java | 12 + .../reflect/compiler/BackgroundCompiler.java | 2 - .../protocol/utility/MinecraftReflection.java | 8 + .../protocol/wrappers/BukkitConverters.java | 28 ++ .../wrappers/nbt/AbstractConverted.java | 34 ++ .../wrappers/nbt/ConvertedCollection.java | 119 +++++ .../protocol/wrappers/nbt/ConvertedList.java | 138 ++++++ .../protocol/wrappers/nbt/ConvertedMap.java | 139 ++++++ .../protocol/wrappers/nbt/ConvertedSet.java | 10 + .../protocol/wrappers/nbt/NbtBase.java | 52 +++ .../protocol/wrappers/nbt/NbtCompound.java | 440 ++++++++++++++++++ .../protocol/wrappers/nbt/NbtElement.java | 213 +++++++++ .../protocol/wrappers/nbt/NbtFactory.java | 250 ++++++++++ .../protocol/wrappers/nbt/NbtList.java | 311 +++++++++++++ .../protocol/wrappers/nbt/NbtType.java | 141 ++++++ .../protocol/wrappers/nbt/NbtWrapper.java | 24 + .../protocol/wrappers/nbt/NbtFactoryTest.java | 46 ++ 17 files changed, 1965 insertions(+), 2 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/AbstractConverted.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedCollection.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedList.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedMap.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedSet.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtWrapper.java create mode 100644 ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java index b819ee04..1751b370 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java @@ -56,6 +56,7 @@ import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedWatchableObject; +import com.comphenix.protocol.wrappers.nbt.NbtWrapper; import com.google.common.base.Function; import com.google.common.collect.Maps; @@ -364,6 +365,17 @@ public class PacketContainer implements Serializable { ChunkPosition.getConverter()); } + /** + * Retrieves a read/write structure for NBT classes. + * @return A modifier for NBT classes. + */ + public StructureModifier> getNbtModifier() { + // Allow access to the NBT class in packet 130 + return structureModifier.withType( + MinecraftReflection.getNBTBaseClass(), + BukkitConverters.getNbtConverter()); + } + /** * Retrieves a read/write structure for collections of chunk positions. *

diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/BackgroundCompiler.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/BackgroundCompiler.java index e8447303..4359e080 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/BackgroundCompiler.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/compiler/BackgroundCompiler.java @@ -24,8 +24,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.Nullable; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index 31094d57..f9924a3a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -376,6 +376,14 @@ public class MinecraftReflection { return getMinecraftClass("WatchableObject"); } + /** + * Retrieve the NBT base class. + * @return The NBT base class. + */ + public static Class getNBTBaseClass() { + return getMinecraftClass("NBTBase"); + } + /** * Retrieve the ItemStack[] class. * @return The ItemStack[] class. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java index 7d6f421e..7b78bb53 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java @@ -34,6 +34,8 @@ import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.utility.MinecraftReflection; +import com.comphenix.protocol.wrappers.nbt.NbtFactory; +import com.comphenix.protocol.wrappers.nbt.NbtWrapper; /** * Contains several useful equivalent converters for normal Bukkit types. @@ -208,6 +210,32 @@ public class BukkitConverters { }); } + /** + * Retrieve an equivalent converter for net.minecraft.server NBT classes and their wrappers. + * @return An equivalent converter for NBT. + */ + public static EquivalentConverter> getNbtConverter() { + return getIgnoreNull(new EquivalentConverter>() { + @Override + public Object getGeneric(Class genericType, NbtWrapper specific) { + return specific.getHandle(); + } + + @Override + public NbtWrapper getSpecific(Object generic) { + return NbtFactory.fromNMS(generic); + } + + @Override + @SuppressWarnings("unchecked") + public Class> getSpecificType() { + // Damn you Java AGAIN + Class dummy = NbtWrapper.class; + return (Class>) dummy; + } + }); + } + /** * Retrieve a converter for NMS entities and Bukkit entities. * @param world - the current world. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/AbstractConverted.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/AbstractConverted.java new file mode 100644 index 00000000..9084f2bf --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/AbstractConverted.java @@ -0,0 +1,34 @@ +package com.comphenix.protocol.wrappers.nbt; + +import javax.annotation.Nullable; + +import com.google.common.base.Function; + +abstract class AbstractConverted { + /** + * Convert a value from the inner map to the outer visible map. + * @param inner - the inner value. + * @return The outer value. + */ + protected abstract VOuter toOuter(VInner inner); + + /** + * Convert a value from the outer map to the internal inner map. + * @param outer - the outer value. + * @return The inner value. + */ + protected abstract VInner toInner(VOuter outer); + + /** + * Retrieve a function delegate that converts inner objects to outer objects. + * @return A function delegate. + */ + protected Function getOuterConverter() { + return new Function() { + @Override + public VOuter apply(@Nullable VInner param) { + return toOuter(param); + } + }; + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedCollection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedCollection.java new file mode 100644 index 00000000..7faf22de --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedCollection.java @@ -0,0 +1,119 @@ +package com.comphenix.protocol.wrappers.nbt; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; + +abstract class ConvertedCollection extends AbstractConverted implements Collection { + // Inner collection + private Collection inner; + + public ConvertedCollection(Collection inner) { + this.inner = inner; + } + + @Override + public boolean add(VOuter e) { + return inner.add(toInner(e)); + } + + @Override + public boolean addAll(Collection c) { + boolean modified = false; + + for (VOuter outer : c) + modified |= add(outer); + return modified; + } + + @Override + public void clear() { + inner.clear(); + } + + @Override + @SuppressWarnings("unchecked") + public boolean contains(Object o) { + return inner.contains(toInner((VOuter) o)); + } + + @Override + public boolean containsAll(Collection c) { + for (Object outer : c) { + if (!contains(outer)) + return false; + } + return true; + } + + @Override + public boolean isEmpty() { + return inner.isEmpty(); + } + + @Override + public Iterator iterator() { + return Iterators.transform(inner.iterator(), getOuterConverter()); + } + + @Override + @SuppressWarnings("unchecked") + public boolean remove(Object o) { + return inner.remove(toInner((VOuter) o)); + } + + @Override + public boolean removeAll(Collection c) { + boolean modified = false; + + for (Object outer : c) + modified |= remove(outer); + return modified; + } + + @Override + @SuppressWarnings("unchecked") + public boolean retainAll(Collection c) { + List innerCopy = Lists.newArrayList(); + + // Convert all the elements + for (Object outer : c) + innerCopy.add(toInner((VOuter) outer)); + return inner.retainAll(innerCopy); + } + + @Override + public int size() { + return inner.size(); + } + + @Override + @SuppressWarnings("unchecked") + public Object[] toArray() { + Object[] array = inner.toArray(); + + for (int i = 0; i < array.length; i++) + array[i] = toOuter((VInner) array[i]); + return array; + } + + @Override + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + T[] array = a; + int index = 0; + + if (array.length < size()) { + array = (T[]) Array.newInstance(a.getClass().getComponentType(), size()); + } + + // Build the output array + for (VInner innerValue : inner) + array[index++] = (T) toOuter(innerValue); + return array; + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedList.java new file mode 100644 index 00000000..5fcf0286 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedList.java @@ -0,0 +1,138 @@ +package com.comphenix.protocol.wrappers.nbt; + +import java.util.Collection; +import java.util.List; +import java.util.ListIterator; + +abstract class ConvertedList extends ConvertedCollection implements List { + private List inner; + + public ConvertedList(List inner) { + super(inner); + this.inner = inner; + } + + @Override + public void add(int index, VOuter element) { + inner.add(index, toInner(element)); + } + + @Override + public boolean addAll(int index, Collection c) { + return inner.addAll(index, getInnerCollection(c)); + } + + @Override + public VOuter get(int index) { + return toOuter(inner.get(index)); + } + + @Override + @SuppressWarnings("unchecked") + public int indexOf(Object o) { + return inner.indexOf(toInner((VOuter) o)); + } + + @Override + @SuppressWarnings("unchecked") + public int lastIndexOf(Object o) { + return inner.lastIndexOf(toInner((VOuter) o)); + } + + @Override + public ListIterator listIterator() { + return listIterator(0); + } + + @Override + public ListIterator listIterator(int index) { + final ListIterator innerIterator = inner.listIterator(index); + + return new ListIterator() { + @Override + public void add(VOuter e) { + innerIterator.add(toInner(e)); + } + + @Override + public boolean hasNext() { + return innerIterator.hasNext(); + } + + @Override + public boolean hasPrevious() { + return innerIterator.hasPrevious(); + } + + @Override + public VOuter next() { + return toOuter(innerIterator.next()); + } + + @Override + public int nextIndex() { + return innerIterator.nextIndex(); + } + + @Override + public VOuter previous() { + return toOuter(innerIterator.previous()); + } + + @Override + public int previousIndex() { + return innerIterator.previousIndex(); + } + + @Override + public void remove() { + innerIterator.remove(); + } + + @Override + public void set(VOuter e) { + innerIterator.set(toInner(e)); + } + }; + } + + @Override + public VOuter remove(int index) { + return toOuter(inner.remove(index)); + } + + @Override + public VOuter set(int index, VOuter element) { + return toOuter(inner.set(index, toInner(element))); + } + + @Override + public List subList(int fromIndex, int toIndex) { + return new ConvertedList(inner.subList(fromIndex, toIndex)) { + @Override + protected VInner toInner(VOuter outer) { + return ConvertedList.this.toInner(outer); + } + + @Override + protected VOuter toOuter(VInner inner) { + return ConvertedList.this.toOuter(inner); + } + }; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private ConvertedCollection getInnerCollection(Collection c) { + return new ConvertedCollection(c) { + @Override + protected VOuter toInner(VInner outer) { + return ConvertedList.this.toOuter(outer); + } + + @Override + protected VInner toOuter(VOuter inner) { + return ConvertedList.this.toInner(inner); + } + }; + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedMap.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedMap.java new file mode 100644 index 00000000..62ec3ffa --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedMap.java @@ -0,0 +1,139 @@ +package com.comphenix.protocol.wrappers.nbt; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +abstract class ConvertedMap extends AbstractConverted implements Map { + // Inner map + private Map inner; + + public ConvertedMap(Map inner) { + if (inner == null) + throw new IllegalArgumentException("Inner map cannot be NULL."); + this.inner = inner; + } + + @Override + public void clear() { + inner.clear(); + } + + @Override + public boolean containsKey(Object key) { + return inner.containsKey(key); + } + + @Override + @SuppressWarnings("unchecked") + public boolean containsValue(Object value) { + return inner.containsValue(toInner((VOuter) value)); + } + + @Override + public Set> entrySet() { + return new ConvertedSet, Entry>(inner.entrySet()) { + @Override + protected Entry toInner(final Entry outer) { + return new Entry() { + @Override + public Key getKey() { + return outer.getKey(); + } + + @Override + public VInner getValue() { + return ConvertedMap.this.toInner(outer.getValue()); + } + + @Override + public VInner setValue(VInner value) { + return ConvertedMap.this.toInner(outer.setValue(ConvertedMap.this.toOuter(value))); + } + + @Override + public String toString() { + return String.format("\"%s\": %s", getKey(), getValue()); + } + }; + } + + @Override + protected Entry toOuter(final Entry inner) { + return new Entry() { + @Override + public Key getKey() { + return inner.getKey(); + } + + @Override + public VOuter getValue() { + return ConvertedMap.this.toOuter(inner.getValue()); + } + + @Override + public VOuter setValue(VOuter value) { + return ConvertedMap.this.toOuter(inner.setValue(ConvertedMap.this.toInner(value))); + } + + @Override + public String toString() { + return String.format("\"%s\": %s", getKey(), getValue()); + } + }; + } + }; + } + + @Override + public VOuter get(Object key) { + return toOuter(inner.get(key)); + } + + @Override + public boolean isEmpty() { + return inner.isEmpty(); + } + + @Override + public Set keySet() { + return inner.keySet(); + } + + @Override + public VOuter put(Key key, VOuter value) { + return toOuter(inner.put(key, toInner(value))); + } + + @Override + public void putAll(Map m) { + for (Entry entry : m.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public VOuter remove(Object key) { + return toOuter(inner.remove(key)); + } + + @Override + public int size() { + return inner.size(); + } + + @Override + public Collection values() { + return new ConvertedCollection(inner.values()) { + @Override + protected VOuter toOuter(VInner inner) { + return ConvertedMap.this.toOuter(inner); + } + + @Override + protected VInner toInner(VOuter outer) { + return ConvertedMap.this.toInner(outer); + } + }; + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedSet.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedSet.java new file mode 100644 index 00000000..10e86287 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedSet.java @@ -0,0 +1,10 @@ +package com.comphenix.protocol.wrappers.nbt; + +import java.util.Collection; +import java.util.Set; + +abstract class ConvertedSet extends ConvertedCollection implements Set { + public ConvertedSet(Collection inner) { + super(inner); + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java new file mode 100644 index 00000000..c6925309 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java @@ -0,0 +1,52 @@ +package com.comphenix.protocol.wrappers.nbt; + +import com.comphenix.protocol.wrappers.nbt.NbtBase; +import com.comphenix.protocol.wrappers.nbt.NbtType; + +/** + * Represents a generic container for an NBT element. + * @author Kristian + * + * @param - type of the value that is stored. + */ +public interface NbtBase { + /** + * Retrieve the type of this NBT element. + * @return The type of this NBT element. + */ + public abstract NbtType getType(); + + /** + * Retrieve the name of this NBT tag. + *

+ * This will be an empty string if the NBT tag is stored in a list. + * @return Name of the tag. + */ + public abstract String getName(); + + /** + * Set the name of this NBT tag. + *

+ * This will be ignored if the NBT tag is stored in a list. + * @param name - name of the tag. + */ + public abstract void setName(String name); + + /** + * Retrieve the value of this NBT tag. + * @return Value of this tag. + */ + public abstract TType getValue(); + + /** + * Set the value of this NBT tag. + * @param newValue - the new value of this tag. + */ + public abstract void setValue(TType newValue); + + /** + * Clone the current NBT tag. + * @return The cloned tag. + */ + public abstract NbtBase clone(); +} \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java new file mode 100644 index 00000000..0da68c11 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java @@ -0,0 +1,440 @@ +package com.comphenix.protocol.wrappers.nbt; + +import java.io.DataOutput; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Represents a mapping of arbitrary NBT elements and their unique names. + *

+ * Use {@link NbtFactory} to load or create an instance. + * + * @author Kristian + */ +public class NbtCompound implements NbtWrapper>>, Iterable> { + // A list container + private NbtElement> container; + + // Saved wrapper map + private ConvertedMap> savedMap; + + /** + * Construct a new NBT compound wrapper. + * @param name - the name of the wrapper. + * @return The wrapped NBT compound. + */ + public static NbtCompound fromName(String name) { + // Simplify things for the caller + return (NbtCompound) NbtFactory.>>ofType(NbtType.TAG_COMPOUND, name); + } + + /** + * Construct a new NBT compound wrapper initialized with a given list of NBT values. + * @param name - the name of the compound wrapper. + * @param list - the list of elements to add. + * @return The new wrapped NBT compound. + */ + public static NbtCompound fromList(String name, Collection> list) { + NbtCompound copy = new NbtCompound(name); + + for (NbtBase base : list) + copy.getValue().put(base.getName(), base); + return copy; + } + + /** + * Construct a wrapped compound from a given NMS handle. + * @param handle - the NMS handle. + */ + NbtCompound(Object handle) { + this.container = new NbtElement>(handle); + } + + @Override + public Object getHandle() { + return container.getHandle(); + } + + @Override + public NbtType getType() { + return NbtType.TAG_COMPOUND; + } + + @Override + public String getName() { + return container.getName(); + } + + @Override + public void setName(String name) { + container.setName(name); + } + + /** + * Retrieve a Set view of the keys of each entry in this compound. + * @return The keys of each entry. + */ + public Set getKeys() { + return getValue().keySet(); + } + + /** + * Retrieve a Collection view of the entries in this compound. + * @return A view of each NBT tag in this compound. + */ + public Collection> asCollection(){ + return getValue().values(); + } + + @Override + public Map> getValue() { + // Return a wrapper map + if (savedMap == null) { + savedMap = new ConvertedMap>(container.getValue()) { + @Override + protected Object toInner(NbtBase outer) { + if (outer == null) + return null; + return NbtFactory.fromBase(outer).getHandle(); + } + + protected NbtBase toOuter(Object inner) { + if (inner == null) + return null; + return NbtFactory.fromNMS(inner); + }; + + @Override + public String toString() { + return NbtCompound.this.toString(); + } + }; + } + return savedMap; + } + + @Override + public void setValue(Map> newValue) { + // Write all the entries + for (Map.Entry> entry : newValue.entrySet()) { + put(entry.getValue()); + } + } + + /** + * Retrieve the value of a given entry. + * @param key - key of the entry to retrieve. + * @return The value of this entry. + */ + @SuppressWarnings("unchecked") + public NbtBase getValue(String key) { + return (NbtBase) getValue().get(key); + } + + /** + * Retrieve a value, or throw an exception. + * @param key - the key to retrieve. + * @return The value of the entry. + */ + private NbtBase getValueExact(String key) { + NbtBase value = getValue(key); + + // Only return a legal key + if (value != null) + return value; + else + throw new IllegalArgumentException("Cannot find key " + key); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public NbtBase>> clone() { + return (NbtBase) container.clone(); + } + + /** + * Set a entry based on its name. + * @param entry - entry with a name and value. + * @return This compound, for chaining. + */ + public NbtCompound put(NbtBase entry) { + getValue().put(entry.getName(), entry); + return this; + } + + /** + * Retrieve the string value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The string value of the entry. + */ + public String getString(String key) { + return (String) getValueExact(key).getValue(); + } + + /** + * Associate a NBT string value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + public NbtCompound put(String key, String value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the byte value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The byte value of the entry. + */ + public Byte getByte(String key) { + return (Byte) getValueExact(key).getValue(); + } + + /** + * Associate a NBT byte value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + public NbtCompound put(String key, byte value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the short value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The short value of the entry. + */ + public Short getShort(String key) { + return (Short) getValueExact(key).getValue(); + } + + /** + * Associate a NBT short value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + public NbtCompound put(String key, short value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the integer value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The integer value of the entry. + */ + public Integer getInteger(String key) { + return (Integer) getValueExact(key).getValue(); + } + + /** + * Associate a NBT integer value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + public NbtCompound put(String key, int value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the long value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The long value of the entry. + */ + public Long getLong(String key) { + return (Long) getValueExact(key).getValue(); + } + + /** + * Associate a NBT long value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + public NbtCompound put(String key, long value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the float value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The float value of the entry. + */ + public Float getFloat(String key) { + return (Float) getValueExact(key).getValue(); + } + + /** + * Associate a NBT float value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + public NbtCompound put(String key, float value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the double value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The double value of the entry. + */ + public Double getDouble(String key) { + return (Double) getValueExact(key).getValue(); + } + + /** + * Associate a NBT double value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + public NbtCompound put(String key, double value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the byte array value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The byte array value of the entry. + */ + public byte[] getByteArray(String key) { + return (byte[]) getValueExact(key).getValue(); + } + + /** + * Associate a NBT byte array value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + public NbtCompound put(String key, byte[] value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the integer array value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The integer array value of the entry. + */ + public int[] getIntegerArray(String key) { + return (int[]) getValueExact(key).getValue(); + } + + /** + * Associate a NBT integer array value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + public NbtCompound put(String key, int[] value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the compound (map) value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The compound value of the entry. + */ + @SuppressWarnings("rawtypes") + public NbtCompound getCompound(String key) { + return (NbtCompound) ((NbtBase) getValueExact(key)); + } + + /** + * Associate a NBT compound with its name as key. + * @param compound - the compound value. + * @return This current compound, for chaining. + */ + public NbtCompound put(NbtCompound compound) { + getValue().put(compound.getName(), compound); + return this; + } + + /** + * Retrieve the NBT list value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The NBT list value of the entry. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public NbtList getList(String key) { + return (NbtList) getValueExact(key); + } + + /** + * Associate a NBT list with the given key. + * @param list - the list value. + * @return This current compound, for chaining. + */ + public NbtCompound put(NbtList list) { + getValue().put(list.getName(), list); + return this; + } + + /** + * Associate a new NBT list with the given key. + * @param key - the key and name of the new NBT list. + * @param list - the list of NBT elements. + * @return This current compound, for chaining. + */ + public NbtCompound put(String key, Collection> list) { + return put(NbtList.fromList(key, list)); + } + + @Override + public void write(DataOutput destination) { + NbtFactory.toStream(container, destination); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof NbtCompound) { + NbtCompound other = (NbtCompound) obj; + return container.equals(other.container); + } + return false; + } + + @Override + public int hashCode() { + return container.hashCode(); + } + + @Override + public Iterator> iterator() { + return getValue().values().iterator(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + + builder.append("{"); + builder.append("\"name\": \"" + getName() + "\""); + + for (NbtBase element : this) { + builder.append(", "); + + // Wrap in quotation marks + if (element.getType() == NbtType.TAG_STRING) + builder.append("\"" + element.getName() + "\": \"" + element.getValue() + "\""); + else + builder.append("\"" + element.getName() + "\": " + element.getValue()); + } + + builder.append("}"); + return builder.toString(); + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java new file mode 100644 index 00000000..5ba7c16a --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java @@ -0,0 +1,213 @@ +package com.comphenix.protocol.wrappers.nbt; + +import java.io.DataOutput; +import java.lang.reflect.Method; + +import com.comphenix.protocol.reflect.FieldAccessException; +import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.reflect.StructureModifier; +import com.comphenix.protocol.utility.MinecraftReflection; +import com.google.common.base.Objects; + +/** + * Represents an arbitrary NBT tag element, composite or not. + *

+ * Use {@link NbtFactory} to load or create an instance. + * @author Kristian + * + * @param - type of the value field. + */ +public class NbtElement implements NbtWrapper { + // Structure modifier for the base class + private static StructureModifier baseModifier; + + // For retrieving the current type ID + private static Method methodGetTypeID; + + // For cloning handles + private static Method methodClone; + + // Structure modifiers for the different NBT elements + private static StructureModifier[] modifiers = new StructureModifier[NbtType.values().length]; + + // The underlying NBT object + private Object handle; + + // Saved type + private NbtType type; + + /** + * Initialize a NBT wrapper for a generic element. + * @param handle - the NBT element to wrap. + */ + NbtElement(Object handle) { + this.handle = handle; + } + + /** + * Retrieve the modifier (with no target) that is used to read and write the NBT name. + * @return A modifier for accessing the NBT name. + */ + protected static StructureModifier getBaseModifier() { + if (baseModifier == null) { + Class base = MinecraftReflection.getNBTBaseClass(); + + // This will be the same for all classes, so we'll share modifier + baseModifier = new StructureModifier(base, Object.class, false).withType(String.class); + } + + return baseModifier.withType(String.class); + } + + /** + * Retrieve a modifier (with no target) that is used to read and write the NBT value. + * @return The value modifier. + */ + protected StructureModifier getCurrentModifier() { + NbtType type = getType(); + + return getCurrentBaseModifier().withType(type.getValueType()); + } + + /** + * Get the object modifier (with no target) for the current underlying NBT object. + * @return The generic modifier. + */ + @SuppressWarnings("unchecked") + protected StructureModifier getCurrentBaseModifier() { + int index = getType().ordinal(); + StructureModifier modifier = (StructureModifier) modifiers[index]; + + // Double checked locking + if (modifier == null) { + synchronized (this) { + if (modifiers[index] == null) { + modifiers[index] = new StructureModifier(handle.getClass(), MinecraftReflection.getNBTBaseClass(), false); + } + modifier = (StructureModifier) modifiers[index]; + } + } + + return modifier; + } + + /** + * Retrieve the underlying NBT tag object. + * @return The underlying Minecraft tag object. + */ + @Override + public Object getHandle() { + return handle; + } + + @Override + public NbtType getType() { + if (methodGetTypeID == null) { + // Use the base class + methodGetTypeID = FuzzyReflection.fromClass(MinecraftReflection.getNBTBaseClass()). + getMethodByParameters("getTypeID", byte.class, new Class[0]); + } + if (type == null) { + try { + type = NbtType.getTypeFromID((Byte) methodGetTypeID.invoke(handle)); + } catch (Exception e) { + throw new FieldAccessException("Cannot get NBT type of " + handle, e); + } + } + + return type; + } + + NbtType getSubType() { + int subID = getCurrentBaseModifier().withType(byte.class).withTarget(handle).read(0); + return NbtType.getTypeFromID(subID); + } + + void setSubType(NbtType type) { + byte subID = (byte) type.getRawID(); + getCurrentBaseModifier().withType(byte.class).withTarget(handle).write(0, subID); + } + + @Override + public String getName() { + return getBaseModifier().withTarget(handle).read(0); + } + + @Override + public void setName(String name) { + getBaseModifier().withTarget(handle).write(0, name); + } + + @Override + public TType getValue() { + return getCurrentModifier().withTarget(handle).read(0); + } + + @Override + public void setValue(TType newValue) { + getCurrentModifier().withTarget(handle).write(0, newValue); + } + + @Override + public void write(DataOutput destination) { + NbtFactory.toStream(this, destination); + } + + @Override + public NbtBase clone() { + if (methodClone == null) { + Class base = MinecraftReflection.getNBTBaseClass(); + + // Use the base class + methodClone = FuzzyReflection.fromClass(base). + getMethodByParameters("clone", base, new Class[0]); + } + + try { + return NbtFactory.fromNMS(methodClone.invoke(handle)); + } catch (Exception e) { + throw new FieldAccessException("Unable to clone " + handle, e); + } + } + + @Override + public int hashCode() { + return Objects.hashCode(getName(), getType(), getValue()); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof NbtBase) { + NbtBase other = (NbtBase) obj; + + // Make sure we're dealing with the same type + if (other.getType().equals(getType())) { + return Objects.equal(getName(), other.getName()) && + Objects.equal(getValue(), other.getValue()); + } + } + return false; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + String name = getName(); + + result.append("{"); + + if (name != null && name.length() > 0) + result.append("name: '" + name + "', "); + + result.append("value: "); + + // Wrap quotation marks + if (getType() == NbtType.TAG_STRING) + result.append("'" + getValue() + "'"); + else + result.append(getValue()); + + result.append("}"); + return result.toString(); + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java new file mode 100644 index 00000000..5a4bf3bf --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java @@ -0,0 +1,250 @@ +package com.comphenix.protocol.wrappers.nbt; + +import java.io.DataInput; +import java.io.DataOutput; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import com.comphenix.protocol.reflect.FieldAccessException; +import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.utility.MinecraftReflection; + +/** + * Factory methods for creating NBT elements, lists and compounds. + * + * @author Kristian + */ +public class NbtFactory { + // Used to create the underlying tag + private static Method methodCreateTag; + + // Used to read and write NBT + private static Method methodWrite; + private static Method methodLoad; + + /** + * Get a NBT wrapper from a NBT base. + * @param base - the base class. + * @return A NBT wrapper. + */ + @SuppressWarnings("unchecked") + public static NbtWrapper fromBase(NbtBase base) { + if (base instanceof NbtElement) { + return (NbtElement) base; + } else if (base instanceof NbtCompound) { + return (NbtWrapper) base; + } else if (base instanceof NbtList) { + return (NbtWrapper) base; + } else { + if (base.getType() == NbtType.TAG_COMPOUND) { + // Load into a NBT-backed wrapper + NbtCompound copy = NbtCompound.fromName(base.getName()); + T value = base.getValue(); + + copy.setValue((Map>) value); + return (NbtWrapper) copy; + + } else if (base.getType() == NbtType.TAG_LIST) { + // As above + NbtList copy = NbtList.fromName(base.getName()); + + copy.setValue((List>) base.getValue()); + return (NbtWrapper) copy; + + } else { + // Copy directly + NbtWrapper copy = ofType(base.getType(), base.getName()); + + copy.setValue(base.getValue()); + return copy; + } + } + } + + /** + * Initialize a NBT wrapper. + * @param handle - the underlying net.minecraft.server object to wrap. + * @return A NBT wrapper. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static NbtWrapper fromNMS(Object handle) { + NbtElement partial = new NbtElement(handle); + + // See if this is actually a compound tag + if (partial.getType() == NbtType.TAG_COMPOUND) + return (NbtWrapper) new NbtCompound(handle); + else if (partial.getType() == NbtType.TAG_LIST) + return new NbtList(handle); + else + return partial; + } + + /** + * Write the content of a wrapped NBT tag to a stream. + * @param value - the NBT tag to write. + * @param destination - the destination stream. + */ + public static void toStream(NbtBase value, DataOutput destination) { + if (methodWrite == null) { + Class base = MinecraftReflection.getNBTBaseClass(); + + // Use the base class + methodWrite = FuzzyReflection.fromClass(base). + getMethodByParameters("writeNBT", base, DataOutput.class); + } + + try { + methodWrite.invoke(null, fromBase(value).getHandle(), destination); + } catch (Exception e) { + throw new FieldAccessException("Unable to write NBT " + value, e); + } + } + + /** + * Load an NBT tag from a stream. + * @param source - the input stream. + * @return An NBT tag. + */ + public static NbtBase fromStream(DataInput source) { + if (methodLoad == null) { + Class base = MinecraftReflection.getNBTBaseClass(); + + // Use the base class + methodLoad = FuzzyReflection.fromClass(base). + getMethodByParameters("load", base, new Class[] { DataInput.class }); + } + + try { + return fromNMS(methodLoad.invoke(null, source)); + } catch (Exception e) { + throw new FieldAccessException("Unable to read NBT from " + source, e); + } + } + + public static NbtBase of(String name, String value) { + return ofType(NbtType.TAG_STRING, name, value); + } + + public static NbtBase of(String name, byte value) { + return ofType(NbtType.TAG_BYTE, name, value); + } + + public static NbtBase of(String name, short value) { + return ofType(NbtType.TAG_SHORT, name, value); + } + + public static NbtBase of(String name, int value) { + return ofType(NbtType.TAG_INT, name, value); + } + + public static NbtBase of(String name, long value) { + return ofType(NbtType.TAG_LONG, name, value); + } + + public static NbtBase of(String name, float value) { + return ofType(NbtType.TAG_FLOAT, name, value); + } + + public static NbtBase of(String name, double value) { + return ofType(NbtType.TAG_DOUBlE, name, value); + } + + public static NbtBase of(String name, byte[] value) { + return ofType(NbtType.TAG_BYTE_ARRAY, name, value); + } + + public static NbtBase of(String name, int[] value) { + return ofType(NbtType.TAG_INT_ARRAY, name, value); + } + + /** + * Construct a new NBT compound wrapper initialized with a given list of NBT values. + * @param name - the name of the compound wrapper. + * @param list - the list of elements to add. + * @return The new wrapped NBT compound. + */ + public static NbtCompound ofCompound(String name, Collection> list) { + return NbtCompound.fromList(name, list); + } + + /** + * Construct a NBT list of out an array of values. + * @param name - name of this list. + * @param elements - elements to add. + * @return The new filled NBT list. + */ + public static NbtList ofList(String name, T... elements) { + return NbtList.fromArray(name, elements); + } + + /** + * Create a new NBT wrapper from a given type. + * @param type - the NBT type. + * @param name - the name of the NBT tag. + * @return The new wrapped NBT tag. + * @throws FieldAccessException If we're unable to create the underlying tag. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static NbtWrapper ofType(NbtType type, String name) { + if (type == null) + throw new IllegalArgumentException("type cannot be NULL."); + if (type == NbtType.TAG_END) + throw new IllegalArgumentException("Cannot create a TAG_END."); + + if (methodCreateTag == null) { + Class base = MinecraftReflection.getNBTBaseClass(); + + // Use the base class + methodCreateTag = FuzzyReflection.fromClass(base). + getMethodByParameters("createTag", base, new Class[] { byte.class, String.class }); + } + + try { + Object handle = methodCreateTag.invoke(null, (byte) type.getRawID(), name); + + if (type == NbtType.TAG_COMPOUND) + return (NbtWrapper) new NbtCompound(handle); + else if (type == NbtType.TAG_LIST) + return (NbtWrapper) new NbtList(handle); + else + return new NbtElement(handle); + + } catch (Exception e) { + // Inform the caller + throw new FieldAccessException( + String.format("Cannot create NBT element %s (type: %s)", name, type), + e); + } + } + + /** + * Create a new NBT wrapper from a given type. + * @param type - the NBT type. + * @param name - the name of the NBT tag. + * @param value - the value of the new tag. + * @return The new wrapped NBT tag. + * @throws FieldAccessException If we're unable to create the underlying tag. + */ + public static NbtWrapper ofType(NbtType type, String name, T value) { + NbtWrapper created = ofType(type, name); + + // Update the value + created.setValue(value); + return created; + } + + /** + * Create a new NBT wrapper from a given type. + * @param type - type of the NBT value. + * @param name - the name of the NBT tag. + * @param value - the value of the new tag. + * @return The new wrapped NBT tag. + * @throws FieldAccessException If we're unable to create the underlying tag. + * @throws IllegalArgumentException If the given class type is not valid NBT. + */ + public static NbtWrapper ofType(Class type, String name, T value) { + return ofType(NbtType.getTypeFromClass(type), name, value); + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java new file mode 100644 index 00000000..c3bd131d --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java @@ -0,0 +1,311 @@ +package com.comphenix.protocol.wrappers.nbt; + +import java.io.DataOutput; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import javax.annotation.Nullable; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.collect.Iterables; + +/** + * Represents a list of NBT tags of the same type without names. + *

+ * Use {@link NbtFactory} to load or create an instance. + * + * @author Kristian + * + * @param - the value type of each NBT tag. + */ +public class NbtList implements NbtWrapper>>, Iterable { + /** + * The name of every NBT tag in a list. + */ + public static String EMPTY_NAME = ""; + + // A list container + private NbtElement> container; + + // Saved wrapper list + private ConvertedList> savedList; + + /** + * Construct a new empty NBT list. + * @param name - name of this list. + * @return The new empty NBT list. + */ + public static NbtList fromName(String name) { + return (NbtList) NbtFactory.>>ofType(NbtType.TAG_LIST, name); + } + + /** + * Construct a NBT list of out an array of values.. + * @param name - name of this list. + * @param elements - values to add. + * @return The new filled NBT list. + */ + public static NbtList fromArray(String name, T... elements) { + NbtList result = fromName(name); + + for (T element : elements) { + if (element == null) + throw new IllegalArgumentException("An NBT list cannot contain a null element!"); + result.add(NbtFactory.ofType(element.getClass(), EMPTY_NAME, element)); + } + return result; + } + + /** + * Construct a NBT list of out a list of NBT elements. + * @param name - name of this list. + * @param elements - elements to add. + * @return The new filled NBT list. + */ + public static NbtList fromList(String name, Collection elements) { + NbtList result = fromName(name); + + for (T element : elements) { + if (element == null) + throw new IllegalArgumentException("An NBT list cannot contain a null element!"); + result.add(NbtFactory.ofType(element.getClass(), EMPTY_NAME, element)); + } + return result; + } + + NbtList(Object handle) { + this.container = new NbtElement>(handle); + } + + @Override + public Object getHandle() { + return container.getHandle(); + } + + @Override + public NbtType getType() { + return NbtType.TAG_LIST; + } + + /** + * Get the type of each element. + * @return Element type. + */ + public NbtType getElementType() { + return container.getSubType(); + } + + @Override + public String getName() { + return container.getName(); + } + + @Override + public void setName(String name) { + container.setName(name); + } + + @Override + public List> getValue() { + if (savedList == null) { + savedList = new ConvertedList>(container.getValue()) { + @Override + public boolean add(NbtBase e) { + if (e == null) + throw new IllegalArgumentException("Cannot store NULL elements in list."); + if (!e.getName().equals(EMPTY_NAME)) + throw new IllegalArgumentException("Cannot add a named NBT tag " + e + " to a list."); + if (size() == 0) + container.setSubType(e.getType()); + + return super.add(e); + } + + @Override + public void add(int index, NbtBase element) { + if (element == null) + throw new IllegalArgumentException("Cannot store NULL elements in list."); + if (!element.getName().equals(EMPTY_NAME)) + throw new IllegalArgumentException("Cannot add a the named NBT tag " + element + " to a list."); + if (index == 0) + container.setSubType(element.getType()); + + super.add(index, element); + } + + @Override + public boolean addAll(Collection> c) { + boolean empty = size() == 0; + boolean result = false; + + for (NbtBase element : c) { + add(element); + result = true; + } + + // See if we now added our first object(s) + if (empty && result) { + container.setSubType(get(0).getType()); + } + return result; + } + + @Override + protected Object toInner(NbtBase outer) { + if (outer == null) + return null; + return NbtFactory.fromBase(outer).getHandle(); + } + + @Override + protected NbtBase toOuter(Object inner) { + if (inner == null) + return null; + return NbtFactory.fromNMS(inner); + } + + @Override + public String toString() { + return NbtList.this.toString(); + } + }; + } + return savedList; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public NbtBase>> clone() { + return (NbtBase) container.clone(); + } + + public void add(NbtBase element) { + getValue().add(element); + } + + @SuppressWarnings("unchecked") + public void add(String value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @SuppressWarnings("unchecked") + public void add(byte value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @SuppressWarnings("unchecked") + public void add(short value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @SuppressWarnings("unchecked") + public void add(int value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @SuppressWarnings("unchecked") + public void add(long value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @SuppressWarnings("unchecked") + public void add(double value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @SuppressWarnings("unchecked") + public void add(byte[] value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @SuppressWarnings("unchecked") + public void add(int[] value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + public int size() { + return getValue().size(); + } + + public TType getValue(int index) { + return getValue().get(index).getValue(); + } + + /** + * Retrieve each NBT tag in this list. + * @return A view of NBT tag in this list. + */ + public Collection> asCollection() { + return getValue(); + } + + @Override + public void setValue(List> newValue) { + NbtBase lastElement = null; + List list = container.getValue(); + list.clear(); + + // Set each underlying element + for (NbtBase type : newValue) { + if (type != null) { + lastElement = type; + list.add(NbtFactory.fromBase(type).getHandle()); + } else { + list.add(null); + } + } + + // Update the sub type as well + if (lastElement != null) { + container.setSubType(lastElement.getType()); + } + } + + @Override + public void write(DataOutput destination) { + NbtFactory.toStream(container, destination); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof NbtList) { + @SuppressWarnings("unchecked") + NbtList other = (NbtList) obj; + return container.equals(other.container); + } + return false; + } + + @Override + public int hashCode() { + return container.hashCode(); + } + + @Override + public Iterator iterator() { + return Iterables.transform(getValue(), new Function, TType>() { + @Override + public TType apply(@Nullable NbtBase param) { + return param.getValue(); + } + }).iterator(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + + builder.append("{\"name\": \"" + getName() + "\", \"value\": ["); + + if (size() > 0) { + if (getElementType() == NbtType.TAG_STRING) + builder.append("\"" + Joiner.on("\", \"").join(this) + "\""); + else + builder.append(Joiner.on(", ").join(this)); + } + + builder.append("]}"); + return builder.toString(); + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java new file mode 100644 index 00000000..f6f950f2 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java @@ -0,0 +1,141 @@ +package com.comphenix.protocol.wrappers.nbt; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Represents all the element types + * + * @author Kristian + */ +public enum NbtType { + /** + * Used to mark the end of compound tags. CANNOT be constructed. + */ + TAG_END(0, Void.class), + + /** + * A signed 1 byte integral type. Sometimes used for booleans. + */ + TAG_BYTE(1, byte.class), + + /** + * A signed 2 byte integral type. + */ + TAG_SHORT(2, short.class), + + /** + * A signed 4 byte integral type. + */ + TAG_INT(3, int.class), + + /** + * A signed 8 byte integral type. + */ + TAG_LONG(4, long.class), + + /** + * A signed 4 byte floating point type. + */ + TAG_FLOAT(5, float.class), + + /** + * A signed 8 byte floating point type. + */ + TAG_DOUBlE(6, double.class), + + /** + * An array of bytes. + */ + TAG_BYTE_ARRAY(7, byte[].class), + + /** + * An array of TAG_Int's payloads.. + */ + TAG_INT_ARRAY(11, int[].class), + + /** + * A UTF-8 string + */ + TAG_STRING(8, String.class), + + /** + * A list of tag payloads, without repeated tag IDs or any tag names. + */ + TAG_LIST(9, List.class), + + /** + * A list of fully formed tags, including their IDs, names, and payloads. No two tags may have the same name. + */ + TAG_COMPOUND(10, Map.class); + + private int rawID; + private Class valueType; + + // Used to lookup a specified NBT + private static NbtType[] lookup; + + // Lookup NBT by class + private static Map, NbtType> classLookup; + + static { + NbtType[] values = values(); + lookup = new NbtType[values.length]; + classLookup = new HashMap, NbtType>(); + + // Initialize lookup tables + for (NbtType type : values) { + lookup[type.getRawID()] = type; + classLookup.put(type.getValueType(), type); + } + } + + private NbtType(int rawID, Class valueType) { + this.rawID = rawID; + this.valueType = valueType; + } + + /** + * Retrieves the raw unique integer that identifies the type of the parent NBT element. + * @return Integer that uniquely identifying the type. + */ + public int getRawID() { + return rawID; + } + + /** + * Retrieves the type of the value stored in the NBT element. + * @return Type of the stored value. + */ + public Class getValueType() { + return valueType; + } + + /** + * Retrieve an NBT type from a given raw ID. + * @param rawID - the raw ID to lookup. + * @return The associated NBT value. + */ + public static NbtType getTypeFromID(int rawID) { + if (rawID < 0 || rawID >= lookup.length) + throw new IllegalArgumentException("Unrecognized raw ID " + rawID); + return lookup[rawID]; + } + + /** + * Retrieve an NBT type from the given Java class. + * @param clazz - type of the value the NBT type can contain. + * @return The NBT type. + * @throws IllegalArgumentException If this class type cannot be represented by NBT tags. + */ + public static NbtType getTypeFromClass(Class clazz) { + NbtType result = classLookup.get(clazz); + + // Try to lookup this value + if (result != null) + return result; + else + throw new IllegalArgumentException("No NBT tag can represent a " + clazz); + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtWrapper.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtWrapper.java new file mode 100644 index 00000000..61106dc3 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtWrapper.java @@ -0,0 +1,24 @@ +package com.comphenix.protocol.wrappers.nbt; + +import java.io.DataOutput; + +/** + * Indicates that this NBT wraps an underlying net.minecraft.server instance. + * + * @author Kristian + * + * @param - type of the value that is stored. + */ +public interface NbtWrapper extends NbtBase { + /** + * Retrieve the underlying net.minecraft.server instance. + * @return The NMS instance. + */ + public Object getHandle(); + + /** + * Write the current NBT tag to an output stream. + * @param destination - the destination stream. + */ + public void write(DataOutput destination); +} diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java new file mode 100644 index 00000000..d5bbfbf5 --- /dev/null +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java @@ -0,0 +1,46 @@ +package com.comphenix.protocol.wrappers.nbt; + +import static org.junit.Assert.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; + +import org.junit.BeforeClass; +import org.junit.Test; + +import com.comphenix.protocol.utility.MinecraftReflection; + +public class NbtFactoryTest { + @BeforeClass + public static void initializeBukkit() { + // Initialize reflection + MinecraftReflection.setMinecraftPackage("net.minecraft.server.v1_4_6", "org.bukkit.craftbukkit.v1_4_6"); + } + + @Test + public void testFromStream() { + NbtCompound compound = NbtCompound.fromName("tag"); + + compound.put("name", "Test Testerson"); + compound.put("age", 42); + + compound.put(NbtFactory.ofList("nicknames", "a", "b", "c")); + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + DataOutput test = new DataOutputStream(buffer); + compound.write(test); + + ByteArrayInputStream source = new ByteArrayInputStream(buffer.toByteArray()); + DataInput input = new DataInputStream(source); + + NbtCompound cloned = (NbtCompound) NbtFactory.fromStream(input); + + assertEquals(compound.getString("name"), cloned.getString("name")); + assertEquals(compound.getInteger("age"), cloned.getInteger("age")); + assertEquals(compound.getList("nicknames"), cloned.getList("nicknames")); + } +} From 8abb93114f6fcc7aa668910076a146f733520934 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Mon, 7 Jan 2013 20:15:21 +0100 Subject: [PATCH 09/26] Ensure that we can create compounds and lists. --- .../protocol/wrappers/nbt/NbtCompound.java | 14 +++----------- .../protocol/wrappers/nbt/NbtFactory.java | 11 ++++++++++- .../protocol/wrappers/nbt/NbtType.java | 7 +++++++ .../protocol/events/PacketContainerTest.java | 18 ++++++++++++++++++ 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java index 0da68c11..06e49e36 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java @@ -36,10 +36,10 @@ public class NbtCompound implements NbtWrapper>>, Iterabl * @param list - the list of elements to add. * @return The new wrapped NBT compound. */ - public static NbtCompound fromList(String name, Collection> list) { - NbtCompound copy = new NbtCompound(name); + public static NbtCompound fromList(String name, Collection> list) { + NbtCompound copy = fromName(name); - for (NbtBase base : list) + for (NbtBase base : list) copy.getValue().put(base.getName(), base); return copy; } @@ -80,14 +80,6 @@ public class NbtCompound implements NbtWrapper>>, Iterabl return getValue().keySet(); } - /** - * Retrieve a Collection view of the entries in this compound. - * @return A view of each NBT tag in this compound. - */ - public Collection> asCollection(){ - return getValue().values(); - } - @Override public Map> getValue() { // Return a wrapper map diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java index 5a4bf3bf..cbf66f62 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java @@ -165,10 +165,19 @@ public class NbtFactory { * @param list - the list of elements to add. * @return The new wrapped NBT compound. */ - public static NbtCompound ofCompound(String name, Collection> list) { + public static NbtCompound ofCompound(String name, Collection> list) { return NbtCompound.fromList(name, list); } + /** + * Construct a new NBT compound wrapper. + * @param name - the name of the compound wrapper. + * @return The new wrapped NBT compound. + */ + public static NbtCompound ofCompound(String name) { + return NbtCompound.fromName(name); + } + /** * Construct a NBT list of out an array of values. * @param name - name of this list. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java index f6f950f2..728ebbe4 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java @@ -4,6 +4,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.google.common.primitives.Primitives; + /** * Represents all the element types * @@ -88,6 +90,11 @@ public enum NbtType { for (NbtType type : values) { lookup[type.getRawID()] = type; classLookup.put(type.getValueType(), type); + + // Add a wrapper type + if (type.getValueType().isPrimitive()) { + classLookup.put(Primitives.wrap(type.getValueType()), type); + } } } diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java index bbee0a76..f2fa2bff 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java @@ -32,6 +32,8 @@ import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedWatchableObject; +import com.comphenix.protocol.wrappers.nbt.NbtCompound; +import com.comphenix.protocol.wrappers.nbt.NbtFactory; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -233,6 +235,22 @@ public class PacketContainerTest { assertEquals(testValue, worldAccess.read(0)); } + @Test + public void testGetNbtModifier() { + PacketContainer updateTileEntity = new PacketContainer(132); + + NbtCompound compound = NbtFactory.ofCompound("test"); + compound.put("test", "name"); + compound.put(NbtFactory.ofList("ages", 1, 2, 3)); + + updateTileEntity.getNbtModifier().write(0, compound); + + NbtCompound result = (NbtCompound) updateTileEntity.getNbtModifier().read(0); + + assertEquals(compound.getString("test"), result.getString("test")); + assertEquals(compound.getList("ages"), result.getList("ages")); + } + @Test public void testGetDataWatcherModifier() { PacketContainer mobSpawnPacket = new PacketContainer(Packets.Server.MOB_SPAWN); From 6b3d85af624fbfbebc185aed77c7b21157ca4bd9 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Mon, 7 Jan 2013 20:24:15 +0100 Subject: [PATCH 10/26] Change "clone" to "deepClone", in line with PacketContainer. --- .../java/com/comphenix/protocol/wrappers/nbt/NbtBase.java | 2 +- .../com/comphenix/protocol/wrappers/nbt/NbtCompound.java | 4 ++-- .../com/comphenix/protocol/wrappers/nbt/NbtElement.java | 8 ++++---- .../java/com/comphenix/protocol/wrappers/nbt/NbtList.java | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java index c6925309..1b9e521e 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java @@ -48,5 +48,5 @@ public interface NbtBase { * Clone the current NBT tag. * @return The cloned tag. */ - public abstract NbtBase clone(); + public abstract NbtBase deepClone(); } \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java index 06e49e36..3481cee8 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java @@ -141,8 +141,8 @@ public class NbtCompound implements NbtWrapper>>, Iterabl } @SuppressWarnings({"unchecked", "rawtypes"}) - public NbtBase>> clone() { - return (NbtBase) container.clone(); + public NbtBase>> deepClone() { + return (NbtBase) container.deepClone(); } /** diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java index 5ba7c16a..9a1d4314 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java @@ -19,13 +19,13 @@ import com.google.common.base.Objects; */ public class NbtElement implements NbtWrapper { // Structure modifier for the base class - private static StructureModifier baseModifier; + private static volatile StructureModifier baseModifier; // For retrieving the current type ID - private static Method methodGetTypeID; + private static volatile Method methodGetTypeID; // For cloning handles - private static Method methodClone; + private static volatile Method methodClone; // Structure modifiers for the different NBT elements private static StructureModifier[] modifiers = new StructureModifier[NbtType.values().length]; @@ -154,7 +154,7 @@ public class NbtElement implements NbtWrapper { } @Override - public NbtBase clone() { + public NbtBase deepClone() { if (methodClone == null) { Class base = MinecraftReflection.getNBTBaseClass(); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java index c3bd131d..c33380a1 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java @@ -176,8 +176,8 @@ public class NbtList implements NbtWrapper>>, Iterabl } @SuppressWarnings({"unchecked", "rawtypes"}) - public NbtBase>> clone() { - return (NbtBase) container.clone(); + public NbtBase>> deepClone() { + return (NbtBase) container.deepClone(); } public void add(NbtBase element) { From 38137cea2c8ea8c14fcca3d7092a3b2cc4a3895e Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Mon, 7 Jan 2013 20:37:21 +0100 Subject: [PATCH 11/26] Verify element type in tag list. --- .../protocol/wrappers/nbt/NbtList.java | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java index c33380a1..ac1c0946 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java @@ -111,27 +111,33 @@ public class NbtList implements NbtWrapper>>, Iterabl public List> getValue() { if (savedList == null) { savedList = new ConvertedList>(container.getValue()) { + // Check and see if the element is valid + private void verifyElement(NbtBase element) { + if (element == null) + throw new IllegalArgumentException("Cannot store NULL elements in list."); + if (!element.getName().equals(EMPTY_NAME)) + throw new IllegalArgumentException("Cannot add a the named NBT tag " + element + " to a list."); + + // Check element type + if (size() > 0) { + if (!element.getType().equals(getElementType())) { + throw new IllegalArgumentException( + "Cannot add " + element + " of " + element.getType() + " to a list of type " + getElementType()); + } + } else { + container.setSubType(element.getType()); + } + } + @Override public boolean add(NbtBase e) { - if (e == null) - throw new IllegalArgumentException("Cannot store NULL elements in list."); - if (!e.getName().equals(EMPTY_NAME)) - throw new IllegalArgumentException("Cannot add a named NBT tag " + e + " to a list."); - if (size() == 0) - container.setSubType(e.getType()); - + verifyElement(e); return super.add(e); } @Override public void add(int index, NbtBase element) { - if (element == null) - throw new IllegalArgumentException("Cannot store NULL elements in list."); - if (!element.getName().equals(EMPTY_NAME)) - throw new IllegalArgumentException("Cannot add a the named NBT tag " + element + " to a list."); - if (index == 0) - container.setSubType(element.getType()); - + verifyElement(element); super.add(index, element); } From 70589a6263d1e616d47ca7147e6a528ead8d01b5 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Mon, 7 Jan 2013 23:39:16 +0100 Subject: [PATCH 12/26] Added the ability to read NBT ItemStack tags. --- .../protocol/wrappers/nbt/NbtCompound.java | 133 +++++++++++++++++- .../protocol/wrappers/nbt/NbtFactory.java | 69 +++++++++ .../protocol/wrappers/nbt/NbtList.java | 1 + 3 files changed, 197 insertions(+), 6 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java index 3481cee8..e59110d4 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java @@ -72,6 +72,15 @@ public class NbtCompound implements NbtWrapper>>, Iterabl container.setName(name); } + /** + * Determine if an entry with the given key exists or not. + * @param key - the key to lookup. + * @return TRUE if an entry with the given key exists, FALSE otherwise. + */ + public boolean containsKey(String key) { + return getValue().containsKey(key); + } + /** * Retrieve a Set view of the keys of each entry in this compound. * @return The keys of each entry. @@ -118,17 +127,36 @@ public class NbtCompound implements NbtWrapper>>, Iterabl /** * Retrieve the value of a given entry. * @param key - key of the entry to retrieve. - * @return The value of this entry. + * @return The value of this entry, or NULL if not found. */ @SuppressWarnings("unchecked") public NbtBase getValue(String key) { return (NbtBase) getValue().get(key); } + /** + * Retrieve a value by its key, or assign and return a new NBT element if it doesn't exist. + * @param key - the key of the entry to find or create. + * @param type - the NBT element we will create if not found. + * @return The value that was retrieved or just created. + */ + public NbtBase getValueOrDefault(String key, NbtType type) { + NbtBase nbt = getValue(key); + + // Create or get a compound + if (nbt == null) + put(nbt = NbtFactory.ofType(type, key)); + else if (nbt.getType() != type) + throw new IllegalArgumentException("Cannot get tag " + nbt + ": Not a " + type); + + return nbt; + } + /** * Retrieve a value, or throw an exception. * @param key - the key to retrieve. * @return The value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. */ private NbtBase getValueExact(String key) { NbtBase value = getValue(key); @@ -159,11 +187,21 @@ public class NbtCompound implements NbtWrapper>>, Iterabl * Retrieve the string value of an entry identified by a given key. * @param key - the key of the entry. * @return The string value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. */ public String getString(String key) { return (String) getValueExact(key).getValue(); } + /** + * Retrieve the string value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + public String getStringOrDefault(String key) { + return (String) getValueOrDefault(key, NbtType.TAG_STRING).getValue(); + } + /** * Associate a NBT string value with the given key. * @param key - the key and NBT name. @@ -179,11 +217,21 @@ public class NbtCompound implements NbtWrapper>>, Iterabl * Retrieve the byte value of an entry identified by a given key. * @param key - the key of the entry. * @return The byte value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. */ - public Byte getByte(String key) { + public byte getByte(String key) { return (Byte) getValueExact(key).getValue(); } + /** + * Retrieve the byte value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + public byte getByteOrDefault(String key) { + return (Byte) getValueOrDefault(key, NbtType.TAG_BYTE).getValue(); + } + /** * Associate a NBT byte value with the given key. * @param key - the key and NBT name. @@ -199,11 +247,21 @@ public class NbtCompound implements NbtWrapper>>, Iterabl * Retrieve the short value of an entry identified by a given key. * @param key - the key of the entry. * @return The short value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. */ public Short getShort(String key) { return (Short) getValueExact(key).getValue(); } + /** + * Retrieve the short value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + public short getShortOrDefault(String key) { + return (Short) getValueOrDefault(key, NbtType.TAG_SHORT).getValue(); + } + /** * Associate a NBT short value with the given key. * @param key - the key and NBT name. @@ -219,11 +277,21 @@ public class NbtCompound implements NbtWrapper>>, Iterabl * Retrieve the integer value of an entry identified by a given key. * @param key - the key of the entry. * @return The integer value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. */ - public Integer getInteger(String key) { + public int getInteger(String key) { return (Integer) getValueExact(key).getValue(); } + /** + * Retrieve the integer value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + public int getIntegerOrDefault(String key) { + return (Integer) getValueOrDefault(key, NbtType.TAG_INT).getValue(); + } + /** * Associate a NBT integer value with the given key. * @param key - the key and NBT name. @@ -239,11 +307,21 @@ public class NbtCompound implements NbtWrapper>>, Iterabl * Retrieve the long value of an entry identified by a given key. * @param key - the key of the entry. * @return The long value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. */ - public Long getLong(String key) { + public long getLong(String key) { return (Long) getValueExact(key).getValue(); } + /** + * Retrieve the long value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + public long getLongOrDefault(String key) { + return (Long) getValueOrDefault(key, NbtType.TAG_LONG).getValue(); + } + /** * Associate a NBT long value with the given key. * @param key - the key and NBT name. @@ -259,11 +337,21 @@ public class NbtCompound implements NbtWrapper>>, Iterabl * Retrieve the float value of an entry identified by a given key. * @param key - the key of the entry. * @return The float value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. */ - public Float getFloat(String key) { + public float getFloat(String key) { return (Float) getValueExact(key).getValue(); } + /** + * Retrieve the float value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + public float getFloatOrDefault(String key) { + return (Float) getValueOrDefault(key, NbtType.TAG_FLOAT).getValue(); + } + /** * Associate a NBT float value with the given key. * @param key - the key and NBT name. @@ -279,11 +367,21 @@ public class NbtCompound implements NbtWrapper>>, Iterabl * Retrieve the double value of an entry identified by a given key. * @param key - the key of the entry. * @return The double value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. */ - public Double getDouble(String key) { + public double getDouble(String key) { return (Double) getValueExact(key).getValue(); } + /** + * Retrieve the double value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + public double getDoubleOrDefault(String key) { + return (Double) getValueOrDefault(key, NbtType.TAG_DOUBlE).getValue(); + } + /** * Associate a NBT double value with the given key. * @param key - the key and NBT name. @@ -299,6 +397,7 @@ public class NbtCompound implements NbtWrapper>>, Iterabl * Retrieve the byte array value of an entry identified by a given key. * @param key - the key of the entry. * @return The byte array value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. */ public byte[] getByteArray(String key) { return (byte[]) getValueExact(key).getValue(); @@ -319,6 +418,7 @@ public class NbtCompound implements NbtWrapper>>, Iterabl * Retrieve the integer array value of an entry identified by a given key. * @param key - the key of the entry. * @return The integer array value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. */ public int[] getIntegerArray(String key) { return (int[]) getValueExact(key).getValue(); @@ -339,12 +439,22 @@ public class NbtCompound implements NbtWrapper>>, Iterabl * Retrieve the compound (map) value of an entry identified by a given key. * @param key - the key of the entry. * @return The compound value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. */ @SuppressWarnings("rawtypes") public NbtCompound getCompound(String key) { return (NbtCompound) ((NbtBase) getValueExact(key)); } + /** + * Retrieve a compound (map) value by its key, or create a new compound if it doesn't exist. + * @param key - the key of the entry to find or create. + * @return The compound value that was retrieved or just created. + */ + public NbtCompound getCompoundOrDefault(String key) { + return (NbtCompound) getValueOrDefault(key, NbtType.TAG_COMPOUND); + } + /** * Associate a NBT compound with its name as key. * @param compound - the compound value. @@ -359,12 +469,23 @@ public class NbtCompound implements NbtWrapper>>, Iterabl * Retrieve the NBT list value of an entry identified by a given key. * @param key - the key of the entry. * @return The NBT list value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. */ @SuppressWarnings({"unchecked", "rawtypes"}) public NbtList getList(String key) { return (NbtList) getValueExact(key); } + /** + * Retrieve a NBT list value by its key, or create a new list if it doesn't exist. + * @param key - the key of the entry to find or create. + * @return The compound value that was retrieved or just created. + */ + @SuppressWarnings("unchecked") + public NbtList getListOrDefault(String key) { + return (NbtList) getValueOrDefault(key, NbtType.TAG_LIST); + } + /** * Associate a NBT list with the given key. * @param list - the list value. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java index cbf66f62..0223c011 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java @@ -7,9 +7,13 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import org.bukkit.inventory.ItemStack; + import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.utility.MinecraftReflection; +import com.comphenix.protocol.wrappers.BukkitConverters; /** * Factory methods for creating NBT elements, lists and compounds. @@ -24,6 +28,39 @@ public class NbtFactory { private static Method methodWrite; private static Method methodLoad; + // Item stack trickery + private static StructureModifier itemStackModifier; + + /** + * Attempt to cast this wrapper as a compund. + * @return This instance as a compound. + * @throws UnsupportedOperationException If this is not a compound. + */ + public static NbtCompound asCompound(NbtWrapper wrapper) { + if (wrapper instanceof NbtCompound) + return (NbtCompound) wrapper; + else if (wrapper != null) + throw new UnsupportedOperationException( + "Cannot cast a " + wrapper.getClass() + "( " + wrapper.getType() + ") to TAG_COMPUND."); + else + throw new IllegalArgumentException("Wrapper cannot be NULL."); + } + + /** + * Attempt to cast this wrapper as a list. + * @return This instance as a list. + * @throws UnsupportedOperationException If this is not a list. + */ + public static NbtList asList(NbtWrapper wrapper) { + if (wrapper instanceof NbtList) + return (NbtList) wrapper; + else if (wrapper != null) + throw new UnsupportedOperationException( + "Cannot cast a " + wrapper.getClass() + "( " + wrapper.getType() + ") to TAG_LIST."); + else + throw new IllegalArgumentException("Wrapper cannot be NULL."); + } + /** * Get a NBT wrapper from a NBT base. * @param base - the base class. @@ -62,7 +99,39 @@ public class NbtFactory { } } } + + /** + * Construct a wrapper for an NBT tag stored (in memory) in an item stack. + *

+ * The item stack must be a wrapper for a CraftItemStack. Use + * {@link MinecraftReflection#getBukkitItemStack(ItemStack)} if not. + * @param stack - the item stack. + * @return A wrapper for its NBT tag. + */ + public static NbtWrapper fromItemStack(ItemStack stack) { + if (!MinecraftReflection.isCraftItemStack(stack)) + throw new IllegalArgumentException("Stack must be a CraftItemStack."); + Object nmsStack = MinecraftReflection.getMinecraftItemStack(stack); + + if (itemStackModifier == null) { + itemStackModifier = new StructureModifier(nmsStack.getClass(), Object.class, false); + } + + // Use the first and best NBT tag + StructureModifier> modifier = itemStackModifier. + withTarget(nmsStack). + withType(MinecraftReflection.getNBTBaseClass(), BukkitConverters.getNbtConverter()); + NbtWrapper result = modifier.read(0); + + // Create the tag if it doesn't exist + if (result == null) { + result = NbtFactory.ofCompound("tag"); + modifier.write(0, result); + } + return result; + } + /** * Initialize a NBT wrapper. * @param handle - the underlying net.minecraft.server object to wrap. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java index ac1c0946..f3162187 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java @@ -300,6 +300,7 @@ public class NbtList implements NbtWrapper>>, Iterabl @Override public String toString() { + // Essentially JSON StringBuilder builder = new StringBuilder(); builder.append("{\"name\": \"" + getName() + "\", \"value\": ["); From 9fb5f7f2dd7a78bd155f898b1849cc9ddce406a0 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Tue, 8 Jan 2013 02:00:42 +0100 Subject: [PATCH 13/26] Fine tuning of the NBT system. Added more test cases. --- .../protocol/reflect/PrettyPrinter.java | 20 +++++ .../protocol/wrappers/nbt/NbtBase.java | 2 + .../protocol/wrappers/nbt/NbtFactory.java | 54 +++++++++++++ .../wrappers/nbt/NbtCompoundTest.java | 78 +++++++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/PrettyPrinter.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/PrettyPrinter.java index ad285df8..f33fb1b2 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/PrettyPrinter.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/PrettyPrinter.java @@ -37,6 +37,20 @@ public class PrettyPrinter { */ public final static int RECURSE_DEPTH = 3; + /** + * Print the content of an object. + * @param object - the object to serialize. + * @param stop - superclass that will stop the process. + * @return String representation of the class. + * @throws IllegalAccessException + */ + public static String printObject(Object object) throws IllegalAccessException { + if (object == null) + throw new IllegalArgumentException("object cannot be NULL."); + + return printObject(object, object.getClass(), Object.class); + } + /** * Print the content of an object. * @param object - the object to serialize. @@ -45,6 +59,9 @@ public class PrettyPrinter { * @throws IllegalAccessException */ public static String printObject(Object object, Class start, Class stop) throws IllegalAccessException { + if (object == null) + throw new IllegalArgumentException("object cannot be NULL."); + return printObject(object, start, stop, RECURSE_DEPTH); } @@ -56,6 +73,9 @@ public class PrettyPrinter { * @throws IllegalAccessException */ public static String printObject(Object object, Class start, Class stop, int hierachyDepth) throws IllegalAccessException { + if (object == null) + throw new IllegalArgumentException("object cannot be NULL."); + StringBuilder output = new StringBuilder(); Set previous = new HashSet(); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java index 1b9e521e..0ef91f75 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java @@ -34,6 +34,8 @@ public interface NbtBase { /** * Retrieve the value of this NBT tag. + *

+ * Is either a primitive wrapper, a list or a map. * @return Value of this tag. */ public abstract TType getValue(); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java index 0223c011..55aeb313 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java @@ -192,38 +192,92 @@ public class NbtFactory { } } + /** + * Constructs a NBT tag of type string. + * @param name - name of the tag. + * @param value - value of the tag. + * @return The constructed NBT tag. + */ public static NbtBase of(String name, String value) { return ofType(NbtType.TAG_STRING, name, value); } + /** + * Constructs a NBT tag of type byte. + * @param name - name of the tag. + * @param value - value of the tag. + * @return The constructed NBT tag. + */ public static NbtBase of(String name, byte value) { return ofType(NbtType.TAG_BYTE, name, value); } + /** + * Constructs a NBT tag of type short. + * @param name - name of the tag. + * @param value - value of the tag. + * @return The constructed NBT tag. + */ public static NbtBase of(String name, short value) { return ofType(NbtType.TAG_SHORT, name, value); } + /** + * Constructs a NBT tag of type int. + * @param name - name of the tag. + * @param value - value of the tag. + * @return The constructed NBT tag. + */ public static NbtBase of(String name, int value) { return ofType(NbtType.TAG_INT, name, value); } + /** + * Constructs a NBT tag of type long. + * @param name - name of the tag. + * @param value - value of the tag. + * @return The constructed NBT tag. + */ public static NbtBase of(String name, long value) { return ofType(NbtType.TAG_LONG, name, value); } + /** + * Constructs a NBT tag of type float. + * @param name - name of the tag. + * @param value - value of the tag. + * @return The constructed NBT tag. + */ public static NbtBase of(String name, float value) { return ofType(NbtType.TAG_FLOAT, name, value); } + /** + * Constructs a NBT tag of type double. + * @param name - name of the tag. + * @param value - value of the tag. + * @return The constructed NBT tag. + */ public static NbtBase of(String name, double value) { return ofType(NbtType.TAG_DOUBlE, name, value); } + /** + * Constructs a NBT tag of type byte array. + * @param name - name of the tag. + * @param value - value of the tag. + * @return The constructed NBT tag. + */ public static NbtBase of(String name, byte[] value) { return ofType(NbtType.TAG_BYTE_ARRAY, name, value); } + /** + * Constructs a NBT tag of type int array. + * @param name - name of the tag. + * @param value - value of the tag. + * @return The constructed NBT tag. + */ public static NbtBase of(String name, int[] value) { return ofType(NbtType.TAG_INT_ARRAY, name, value); } diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java new file mode 100644 index 00000000..b217aec4 --- /dev/null +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java @@ -0,0 +1,78 @@ +package com.comphenix.protocol.wrappers.nbt; + +import static org.junit.Assert.*; + +import org.junit.BeforeClass; +import org.junit.Test; + +import com.comphenix.protocol.utility.MinecraftReflection; + +public class NbtCompoundTest { + @BeforeClass + public static void setupBukkit() { + MinecraftReflection.setMinecraftPackage("net.minecraft.server.v1_4_6", "org.bukkit.craftbukkit.v1_4_6"); + } + + @Test + public void testCustomTags() { + NbtCustomTag test = new NbtCustomTag("hello", 12); + + NbtCompound map = NbtCompound.fromName("test"); + map.put(test); + + // Note that the custom tag will be cloned + assertEquals(12, map.getInteger("hello")); + } + + /** + * Represents a custom NBT tag. + * + * @author Kristian + * + * @param - the value of the tag. + */ + public static class NbtCustomTag implements NbtBase { + private String name; + private TValue value; + private NbtType type; + + public NbtCustomTag(String name, TValue value) { + if (value == null) + throw new IllegalArgumentException("Cannot create a custom tag from NULL."); + this.value = value; + this.name = name; + this.type = NbtType.getTypeFromClass(value.getClass()); + + } + + @Override + public NbtType getType() { + return type; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public TValue getValue() { + return value; + } + + @Override + public void setValue(TValue newValue) { + this.value = newValue; + } + + @Override + public NbtBase deepClone() { + return new NbtCustomTag(name, value); + } + } +} From 79daec662ce0d428f39a24848e6603c311d29304 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Tue, 8 Jan 2013 02:05:15 +0100 Subject: [PATCH 14/26] Added GPL license information. --- .../wrappers/nbt/AbstractConverted.java | 17 +++++++++++++++++ .../wrappers/nbt/ConvertedCollection.java | 17 +++++++++++++++++ .../protocol/wrappers/nbt/ConvertedList.java | 17 +++++++++++++++++ .../protocol/wrappers/nbt/ConvertedMap.java | 17 +++++++++++++++++ .../protocol/wrappers/nbt/ConvertedSet.java | 17 +++++++++++++++++ .../protocol/wrappers/nbt/NbtBase.java | 17 +++++++++++++++++ .../protocol/wrappers/nbt/NbtCompound.java | 17 +++++++++++++++++ .../protocol/wrappers/nbt/NbtElement.java | 17 +++++++++++++++++ .../protocol/wrappers/nbt/NbtFactory.java | 17 +++++++++++++++++ .../protocol/wrappers/nbt/NbtList.java | 17 +++++++++++++++++ .../protocol/wrappers/nbt/NbtType.java | 17 +++++++++++++++++ .../protocol/wrappers/nbt/NbtWrapper.java | 17 +++++++++++++++++ .../protocol/MinecraftVersionTest.java | 17 +++++++++++++++++ .../concurrency/BlockingHashMapTest.java | 17 +++++++++++++++++ .../protocol/events/PacketContainerTest.java | 17 +++++++++++++++++ .../injector/SortedCopyOnWriteArrayTest.java | 17 +++++++++++++++++ .../protocol/wrappers/nbt/NbtCompoundTest.java | 17 +++++++++++++++++ .../protocol/wrappers/nbt/NbtFactoryTest.java | 17 +++++++++++++++++ 18 files changed, 306 insertions(+) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/AbstractConverted.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/AbstractConverted.java index 9084f2bf..e3670a3d 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/AbstractConverted.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/AbstractConverted.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import javax.annotation.Nullable; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedCollection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedCollection.java index 7faf22de..9af8189a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedCollection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedCollection.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import java.lang.reflect.Array; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedList.java index 5fcf0286..15e21092 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedList.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import java.util.Collection; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedMap.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedMap.java index 62ec3ffa..7f58f6bd 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedMap.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedMap.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import java.util.Collection; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedSet.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedSet.java index 10e86287..15c072eb 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedSet.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedSet.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import java.util.Collection; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java index 0ef91f75..eb1b86a7 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import com.comphenix.protocol.wrappers.nbt.NbtBase; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java index e59110d4..52e60c1a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import java.io.DataOutput; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java index 9a1d4314..0756ab35 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import java.io.DataOutput; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java index 55aeb313..d1654d29 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import java.io.DataInput; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java index f3162187..16514c31 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import java.io.DataOutput; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java index 728ebbe4..16b1787a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import java.util.HashMap; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtWrapper.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtWrapper.java index 61106dc3..7317d474 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtWrapper.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtWrapper.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import java.io.DataOutput; diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/MinecraftVersionTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/MinecraftVersionTest.java index b69c29fd..8f98de70 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/MinecraftVersionTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/MinecraftVersionTest.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol; import static org.junit.Assert.*; diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/concurrency/BlockingHashMapTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/concurrency/BlockingHashMapTest.java index 7be7e705..75126855 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/concurrency/BlockingHashMapTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/concurrency/BlockingHashMapTest.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.concurrency; import static org.junit.Assert.*; diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java index f2fa2bff..8359c753 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.events; import static org.junit.Assert.*; diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/injector/SortedCopyOnWriteArrayTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/injector/SortedCopyOnWriteArrayTest.java index 775a197e..57280526 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/injector/SortedCopyOnWriteArrayTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/injector/SortedCopyOnWriteArrayTest.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.injector; import static org.junit.Assert.*; diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java index b217aec4..a485fc31 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import static org.junit.Assert.*; diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java index d5bbfbf5..531fb58c 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java @@ -1,3 +1,20 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + package com.comphenix.protocol.wrappers.nbt; import static org.junit.Assert.*; From 4756de8066f0a489f87984bd1753a058424964e8 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Tue, 8 Jan 2013 03:16:21 +0100 Subject: [PATCH 15/26] Factory methods should return NBTWrapper. --- .../protocol/wrappers/nbt/NbtElement.java | 2 +- .../protocol/wrappers/nbt/NbtFactory.java | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java index 0756ab35..488e681a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java @@ -41,7 +41,7 @@ public class NbtElement implements NbtWrapper { // For retrieving the current type ID private static volatile Method methodGetTypeID; - // For cloning handles + // For handling cloning private static volatile Method methodClone; // Structure modifiers for the different NBT elements diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java index d1654d29..0a512869 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java @@ -172,7 +172,7 @@ public class NbtFactory { * @param value - the NBT tag to write. * @param destination - the destination stream. */ - public static void toStream(NbtBase value, DataOutput destination) { + public static void toStream(NbtWrapper value, DataOutput destination) { if (methodWrite == null) { Class base = MinecraftReflection.getNBTBaseClass(); @@ -193,7 +193,7 @@ public class NbtFactory { * @param source - the input stream. * @return An NBT tag. */ - public static NbtBase fromStream(DataInput source) { + public static NbtWrapper fromStream(DataInput source) { if (methodLoad == null) { Class base = MinecraftReflection.getNBTBaseClass(); @@ -215,7 +215,7 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtBase of(String name, String value) { + public static NbtWrapper of(String name, String value) { return ofType(NbtType.TAG_STRING, name, value); } @@ -225,7 +225,7 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtBase of(String name, byte value) { + public static NbtWrapper of(String name, byte value) { return ofType(NbtType.TAG_BYTE, name, value); } @@ -235,7 +235,7 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtBase of(String name, short value) { + public static NbtWrapper of(String name, short value) { return ofType(NbtType.TAG_SHORT, name, value); } @@ -245,7 +245,7 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtBase of(String name, int value) { + public static NbtWrapper of(String name, int value) { return ofType(NbtType.TAG_INT, name, value); } @@ -255,7 +255,7 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtBase of(String name, long value) { + public static NbtWrapper of(String name, long value) { return ofType(NbtType.TAG_LONG, name, value); } @@ -265,7 +265,7 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtBase of(String name, float value) { + public static NbtWrapper of(String name, float value) { return ofType(NbtType.TAG_FLOAT, name, value); } @@ -275,7 +275,7 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtBase of(String name, double value) { + public static NbtWrapper of(String name, double value) { return ofType(NbtType.TAG_DOUBlE, name, value); } @@ -285,7 +285,7 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtBase of(String name, byte[] value) { + public static NbtWrapper of(String name, byte[] value) { return ofType(NbtType.TAG_BYTE_ARRAY, name, value); } @@ -295,7 +295,7 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtBase of(String name, int[] value) { + public static NbtWrapper of(String name, int[] value) { return ofType(NbtType.TAG_INT_ARRAY, name, value); } From 749f0066218c785edb208881b2d746e49e97c986 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Tue, 8 Jan 2013 03:33:24 +0100 Subject: [PATCH 16/26] Add the ability to serialize ItemStacks to strings. --- .../protocol/utility/StreamSerializer.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java index 2edbb3cf..5203a22d 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java @@ -1,9 +1,12 @@ package com.comphenix.protocol.utility; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Method; +import java.math.BigInteger; import org.bukkit.inventory.ItemStack; @@ -46,6 +49,24 @@ public class StreamSerializer { } } + /** + * Deserialize an item stack from a base-32 encoded string. + * @param input - base-32 encoded string. + * @return A deserialized item stack. + * @throws IOException If the operation failed due to reflection or corrupt data. + */ + public ItemStack deserializeItemStack(String input) throws IOException { + try { + BigInteger base32 = new BigInteger(input, 32); + ByteArrayInputStream inputStream = new ByteArrayInputStream(base32.toByteArray()); + + return deserializeItemStack(new DataInputStream(inputStream)); + + } catch (NumberFormatException e) { + throw new IOException("Input is not valid base 32.", e); + } + } + /** * Write or serialize an item stack to the given output stream. *

@@ -70,4 +91,20 @@ public class StreamSerializer { throw new IOException("Cannot write item stack " + stack, e); } } + + /** + * Serialize an item stack as a base-32 encoded string. + * @param stack - the item stack to serialize. + * @return A base-32 representation of the given item stack. + * @throws IOException If the operation fails due to reflection problems. + */ + public String serializeItemStack(ItemStack stack) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + DataOutputStream dataOutput = new DataOutputStream(outputStream); + + serializeItemStack(dataOutput, stack); + + // Serialize that array + return new BigInteger(1, outputStream.toByteArray()).toString(32); + } } From c2ea92ab375bcd0813e5f8c25408cce78ecdfc41 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Tue, 8 Jan 2013 21:11:29 +0100 Subject: [PATCH 17/26] Refactoring: Made NbtList and NbtCompound into generic interfaces. --- .../protocol/wrappers/nbt/NbtCompound.java | 425 +++--------- .../protocol/wrappers/nbt/NbtFactory.java | 32 +- .../protocol/wrappers/nbt/NbtList.java | 387 +++-------- .../wrappers/nbt/WrappedCompound.java | 606 ++++++++++++++++++ .../{NbtElement.java => WrappedElement.java} | 4 +- .../protocol/wrappers/nbt/WrappedList.java | 346 ++++++++++ .../protocol/events/PacketContainerTest.java | 3 +- .../wrappers/nbt/NbtCompoundTest.java | 2 +- .../protocol/wrappers/nbt/NbtFactoryTest.java | 2 +- 9 files changed, 1138 insertions(+), 669 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java rename ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/{NbtElement.java => WrappedElement.java} (98%) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java index 52e60c1a..35cc0075 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java @@ -1,23 +1,5 @@ -/* - * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. - * Copyright (C) 2012 Kristian S. Stangeland - * - * This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program; - * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA - */ - package com.comphenix.protocol.wrappers.nbt; -import java.io.DataOutput; import java.util.Collection; import java.util.Iterator; import java.util.Map; @@ -30,541 +12,290 @@ import java.util.Set; * * @author Kristian */ -public class NbtCompound implements NbtWrapper>>, Iterable> { - // A list container - private NbtElement> container; - - // Saved wrapper map - private ConvertedMap> savedMap; - - /** - * Construct a new NBT compound wrapper. - * @param name - the name of the wrapper. - * @return The wrapped NBT compound. - */ - public static NbtCompound fromName(String name) { - // Simplify things for the caller - return (NbtCompound) NbtFactory.>>ofType(NbtType.TAG_COMPOUND, name); - } - - /** - * Construct a new NBT compound wrapper initialized with a given list of NBT values. - * @param name - the name of the compound wrapper. - * @param list - the list of elements to add. - * @return The new wrapped NBT compound. - */ - public static NbtCompound fromList(String name, Collection> list) { - NbtCompound copy = fromName(name); - - for (NbtBase base : list) - copy.getValue().put(base.getName(), base); - return copy; - } - - /** - * Construct a wrapped compound from a given NMS handle. - * @param handle - the NMS handle. - */ - NbtCompound(Object handle) { - this.container = new NbtElement>(handle); - } - - @Override - public Object getHandle() { - return container.getHandle(); - } - - @Override - public NbtType getType() { - return NbtType.TAG_COMPOUND; - } - - @Override - public String getName() { - return container.getName(); - } - - @Override - public void setName(String name) { - container.setName(name); - } - +public interface NbtCompound extends NbtBase>>, Iterable> { /** * Determine if an entry with the given key exists or not. * @param key - the key to lookup. * @return TRUE if an entry with the given key exists, FALSE otherwise. */ - public boolean containsKey(String key) { - return getValue().containsKey(key); - } - + public abstract boolean containsKey(String key); + /** * Retrieve a Set view of the keys of each entry in this compound. * @return The keys of each entry. */ - public Set getKeys() { - return getValue().keySet(); - } - - @Override - public Map> getValue() { - // Return a wrapper map - if (savedMap == null) { - savedMap = new ConvertedMap>(container.getValue()) { - @Override - protected Object toInner(NbtBase outer) { - if (outer == null) - return null; - return NbtFactory.fromBase(outer).getHandle(); - } - - protected NbtBase toOuter(Object inner) { - if (inner == null) - return null; - return NbtFactory.fromNMS(inner); - }; - - @Override - public String toString() { - return NbtCompound.this.toString(); - } - }; - } - return savedMap; - } + public abstract Set getKeys(); - @Override - public void setValue(Map> newValue) { - // Write all the entries - for (Map.Entry> entry : newValue.entrySet()) { - put(entry.getValue()); - } - } - /** * Retrieve the value of a given entry. * @param key - key of the entry to retrieve. * @return The value of this entry, or NULL if not found. */ - @SuppressWarnings("unchecked") - public NbtBase getValue(String key) { - return (NbtBase) getValue().get(key); - } - + public abstract NbtBase getValue(String key); + /** * Retrieve a value by its key, or assign and return a new NBT element if it doesn't exist. * @param key - the key of the entry to find or create. * @param type - the NBT element we will create if not found. * @return The value that was retrieved or just created. */ - public NbtBase getValueOrDefault(String key, NbtType type) { - NbtBase nbt = getValue(key); + public abstract NbtBase getValueOrDefault(String key, NbtType type); - // Create or get a compound - if (nbt == null) - put(nbt = NbtFactory.ofType(type, key)); - else if (nbt.getType() != type) - throw new IllegalArgumentException("Cannot get tag " + nbt + ": Not a " + type); - - return nbt; - } - - /** - * Retrieve a value, or throw an exception. - * @param key - the key to retrieve. - * @return The value of the entry. - * @throws IllegalArgumentException If the key doesn't exist. - */ - private NbtBase getValueExact(String key) { - NbtBase value = getValue(key); - - // Only return a legal key - if (value != null) - return value; - else - throw new IllegalArgumentException("Cannot find key " + key); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - public NbtBase>> deepClone() { - return (NbtBase) container.deepClone(); - } - /** * Set a entry based on its name. * @param entry - entry with a name and value. * @return This compound, for chaining. */ - public NbtCompound put(NbtBase entry) { - getValue().put(entry.getName(), entry); - return this; - } - + public abstract NbtCompound put(NbtBase entry); + /** * Retrieve the string value of an entry identified by a given key. * @param key - the key of the entry. * @return The string value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public String getString(String key) { - return (String) getValueExact(key).getValue(); - } - + public abstract String getString(String key); + /** * Retrieve the string value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public String getStringOrDefault(String key) { - return (String) getValueOrDefault(key, NbtType.TAG_STRING).getValue(); - } - + public abstract String getStringOrDefault(String key); + /** * Associate a NBT string value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, String value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, String value); + /** * Retrieve the byte value of an entry identified by a given key. * @param key - the key of the entry. * @return The byte value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public byte getByte(String key) { - return (Byte) getValueExact(key).getValue(); - } - + public abstract byte getByte(String key); + /** * Retrieve the byte value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public byte getByteOrDefault(String key) { - return (Byte) getValueOrDefault(key, NbtType.TAG_BYTE).getValue(); - } - + public abstract byte getByteOrDefault(String key); + /** * Associate a NBT byte value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, byte value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, byte value); + /** * Retrieve the short value of an entry identified by a given key. * @param key - the key of the entry. * @return The short value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public Short getShort(String key) { - return (Short) getValueExact(key).getValue(); - } - + public abstract Short getShort(String key); + /** * Retrieve the short value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public short getShortOrDefault(String key) { - return (Short) getValueOrDefault(key, NbtType.TAG_SHORT).getValue(); - } - + public abstract short getShortOrDefault(String key); + /** * Associate a NBT short value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, short value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, short value); + /** * Retrieve the integer value of an entry identified by a given key. * @param key - the key of the entry. * @return The integer value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public int getInteger(String key) { - return (Integer) getValueExact(key).getValue(); - } - + public abstract int getInteger(String key); + /** * Retrieve the integer value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public int getIntegerOrDefault(String key) { - return (Integer) getValueOrDefault(key, NbtType.TAG_INT).getValue(); - } - + public abstract int getIntegerOrDefault(String key); + /** * Associate a NBT integer value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, int value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, int value); + /** * Retrieve the long value of an entry identified by a given key. * @param key - the key of the entry. * @return The long value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public long getLong(String key) { - return (Long) getValueExact(key).getValue(); - } - + public abstract long getLong(String key); + /** * Retrieve the long value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public long getLongOrDefault(String key) { - return (Long) getValueOrDefault(key, NbtType.TAG_LONG).getValue(); - } - + public abstract long getLongOrDefault(String key); + /** * Associate a NBT long value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, long value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, long value); + /** * Retrieve the float value of an entry identified by a given key. * @param key - the key of the entry. * @return The float value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public float getFloat(String key) { - return (Float) getValueExact(key).getValue(); - } - + public abstract float getFloat(String key); + /** * Retrieve the float value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public float getFloatOrDefault(String key) { - return (Float) getValueOrDefault(key, NbtType.TAG_FLOAT).getValue(); - } - + public abstract float getFloatOrDefault(String key); + /** * Associate a NBT float value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, float value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, float value); + /** * Retrieve the double value of an entry identified by a given key. * @param key - the key of the entry. * @return The double value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public double getDouble(String key) { - return (Double) getValueExact(key).getValue(); - } - + public abstract double getDouble(String key); + /** * Retrieve the double value of an existing entry, or from a new default entry if it doesn't exist. * @param key - the key of the entry. * @return The value that was retrieved or just created. */ - public double getDoubleOrDefault(String key) { - return (Double) getValueOrDefault(key, NbtType.TAG_DOUBlE).getValue(); - } - + public abstract double getDoubleOrDefault(String key); + /** * Associate a NBT double value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, double value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, double value); + /** * Retrieve the byte array value of an entry identified by a given key. * @param key - the key of the entry. * @return The byte array value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public byte[] getByteArray(String key) { - return (byte[]) getValueExact(key).getValue(); - } - + public abstract byte[] getByteArray(String key); + /** * Associate a NBT byte array value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, byte[] value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, byte[] value); + /** * Retrieve the integer array value of an entry identified by a given key. * @param key - the key of the entry. * @return The integer array value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - public int[] getIntegerArray(String key) { - return (int[]) getValueExact(key).getValue(); - } - + public abstract int[] getIntegerArray(String key); + /** * Associate a NBT integer array value with the given key. * @param key - the key and NBT name. * @param value - the value. * @return This current compound, for chaining. */ - public NbtCompound put(String key, int[] value) { - getValue().put(key, NbtFactory.of(key, value)); - return this; - } - + public abstract NbtCompound put(String key, int[] value); + /** * Retrieve the compound (map) value of an entry identified by a given key. * @param key - the key of the entry. * @return The compound value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - @SuppressWarnings("rawtypes") - public NbtCompound getCompound(String key) { - return (NbtCompound) ((NbtBase) getValueExact(key)); - } - + public abstract NbtCompound getCompound(String key); + /** * Retrieve a compound (map) value by its key, or create a new compound if it doesn't exist. * @param key - the key of the entry to find or create. * @return The compound value that was retrieved or just created. */ - public NbtCompound getCompoundOrDefault(String key) { - return (NbtCompound) getValueOrDefault(key, NbtType.TAG_COMPOUND); - } - + public abstract NbtCompound getCompoundOrDefault(String key); + /** * Associate a NBT compound with its name as key. * @param compound - the compound value. * @return This current compound, for chaining. */ - public NbtCompound put(NbtCompound compound) { - getValue().put(compound.getName(), compound); - return this; - } - + public abstract NbtCompound put(WrappedCompound compound); + /** * Retrieve the NBT list value of an entry identified by a given key. * @param key - the key of the entry. * @return The NBT list value of the entry. * @throws IllegalArgumentException If the key doesn't exist. */ - @SuppressWarnings({"unchecked", "rawtypes"}) - public NbtList getList(String key) { - return (NbtList) getValueExact(key); - } - + public abstract NbtList getList(String key); + /** * Retrieve a NBT list value by its key, or create a new list if it doesn't exist. * @param key - the key of the entry to find or create. * @return The compound value that was retrieved or just created. */ - @SuppressWarnings("unchecked") - public NbtList getListOrDefault(String key) { - return (NbtList) getValueOrDefault(key, NbtType.TAG_LIST); - } - + public abstract NbtList getListOrDefault(String key); + /** * Associate a NBT list with the given key. * @param list - the list value. * @return This current compound, for chaining. */ - public NbtCompound put(NbtList list) { - getValue().put(list.getName(), list); - return this; - } - + public abstract NbtCompound put(WrappedList list); + /** * Associate a new NBT list with the given key. * @param key - the key and name of the new NBT list. * @param list - the list of NBT elements. * @return This current compound, for chaining. */ - public NbtCompound put(String key, Collection> list) { - return put(NbtList.fromList(key, list)); - } - - @Override - public void write(DataOutput destination) { - NbtFactory.toStream(container, destination); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof NbtCompound) { - NbtCompound other = (NbtCompound) obj; - return container.equals(other.container); - } - return false; - } - - @Override - public int hashCode() { - return container.hashCode(); - } + public abstract NbtCompound put(String key, Collection> list); - @Override - public Iterator> iterator() { - return getValue().values().iterator(); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - - builder.append("{"); - builder.append("\"name\": \"" + getName() + "\""); - - for (NbtBase element : this) { - builder.append(", "); - - // Wrap in quotation marks - if (element.getType() == NbtType.TAG_STRING) - builder.append("\"" + element.getName() + "\": \"" + element.getValue() + "\""); - else - builder.append("\"" + element.getName() + "\": " + element.getValue()); - } - - builder.append("}"); - return builder.toString(); - } -} + /** + * Retrieve an iterator view of the NBT tags stored in this compound. + * @return The tags stored in this compound. + */ + public abstract Iterator> iterator(); +} \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java index 0a512869..53c9d7c6 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java @@ -85,16 +85,16 @@ public class NbtFactory { */ @SuppressWarnings("unchecked") public static NbtWrapper fromBase(NbtBase base) { - if (base instanceof NbtElement) { - return (NbtElement) base; - } else if (base instanceof NbtCompound) { + if (base instanceof WrappedElement) { + return (WrappedElement) base; + } else if (base instanceof WrappedCompound) { return (NbtWrapper) base; - } else if (base instanceof NbtList) { + } else if (base instanceof WrappedList) { return (NbtWrapper) base; } else { if (base.getType() == NbtType.TAG_COMPOUND) { // Load into a NBT-backed wrapper - NbtCompound copy = NbtCompound.fromName(base.getName()); + WrappedCompound copy = WrappedCompound.fromName(base.getName()); T value = base.getValue(); copy.setValue((Map>) value); @@ -102,7 +102,7 @@ public class NbtFactory { } else if (base.getType() == NbtType.TAG_LIST) { // As above - NbtList copy = NbtList.fromName(base.getName()); + WrappedList copy = WrappedList.fromName(base.getName()); copy.setValue((List>) base.getValue()); return (NbtWrapper) copy; @@ -156,13 +156,13 @@ public class NbtFactory { */ @SuppressWarnings({"unchecked", "rawtypes"}) public static NbtWrapper fromNMS(Object handle) { - NbtElement partial = new NbtElement(handle); + WrappedElement partial = new WrappedElement(handle); // See if this is actually a compound tag if (partial.getType() == NbtType.TAG_COMPOUND) - return (NbtWrapper) new NbtCompound(handle); + return (NbtWrapper) new WrappedCompound(handle); else if (partial.getType() == NbtType.TAG_LIST) - return new NbtList(handle); + return new WrappedList(handle); else return partial; } @@ -306,7 +306,7 @@ public class NbtFactory { * @return The new wrapped NBT compound. */ public static NbtCompound ofCompound(String name, Collection> list) { - return NbtCompound.fromList(name, list); + return WrappedCompound.fromList(name, list); } /** @@ -314,8 +314,8 @@ public class NbtFactory { * @param name - the name of the compound wrapper. * @return The new wrapped NBT compound. */ - public static NbtCompound ofCompound(String name) { - return NbtCompound.fromName(name); + public static WrappedCompound ofCompound(String name) { + return WrappedCompound.fromName(name); } /** @@ -325,7 +325,7 @@ public class NbtFactory { * @return The new filled NBT list. */ public static NbtList ofList(String name, T... elements) { - return NbtList.fromArray(name, elements); + return WrappedList.fromArray(name, elements); } /** @@ -354,11 +354,11 @@ public class NbtFactory { Object handle = methodCreateTag.invoke(null, (byte) type.getRawID(), name); if (type == NbtType.TAG_COMPOUND) - return (NbtWrapper) new NbtCompound(handle); + return (NbtWrapper) new WrappedCompound(handle); else if (type == NbtType.TAG_LIST) - return (NbtWrapper) new NbtList(handle); + return (NbtWrapper) new WrappedList(handle); else - return new NbtElement(handle); + return new WrappedElement(handle); } catch (Exception e) { // Inform the caller diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java index 16514c31..f4db93e2 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java @@ -1,33 +1,9 @@ -/* - * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. - * Copyright (C) 2012 Kristian S. Stangeland - * - * This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program; - * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA - */ - package com.comphenix.protocol.wrappers.nbt; -import java.io.DataOutput; import java.util.Collection; import java.util.Iterator; import java.util.List; -import javax.annotation.Nullable; - -import com.google.common.base.Function; -import com.google.common.base.Joiner; -import com.google.common.collect.Iterables; - /** * Represents a list of NBT tags of the same type without names. *

@@ -37,299 +13,108 @@ import com.google.common.collect.Iterables; * * @param - the value type of each NBT tag. */ -public class NbtList implements NbtWrapper>>, Iterable { +public interface NbtList extends NbtBase>>, Iterable { /** * The name of every NBT tag in a list. */ public static String EMPTY_NAME = ""; - // A list container - private NbtElement> container; - - // Saved wrapper list - private ConvertedList> savedList; - - /** - * Construct a new empty NBT list. - * @param name - name of this list. - * @return The new empty NBT list. - */ - public static NbtList fromName(String name) { - return (NbtList) NbtFactory.>>ofType(NbtType.TAG_LIST, name); - } - - /** - * Construct a NBT list of out an array of values.. - * @param name - name of this list. - * @param elements - values to add. - * @return The new filled NBT list. - */ - public static NbtList fromArray(String name, T... elements) { - NbtList result = fromName(name); - - for (T element : elements) { - if (element == null) - throw new IllegalArgumentException("An NBT list cannot contain a null element!"); - result.add(NbtFactory.ofType(element.getClass(), EMPTY_NAME, element)); - } - return result; - } - - /** - * Construct a NBT list of out a list of NBT elements. - * @param name - name of this list. - * @param elements - elements to add. - * @return The new filled NBT list. - */ - public static NbtList fromList(String name, Collection elements) { - NbtList result = fromName(name); - - for (T element : elements) { - if (element == null) - throw new IllegalArgumentException("An NBT list cannot contain a null element!"); - result.add(NbtFactory.ofType(element.getClass(), EMPTY_NAME, element)); - } - return result; - } - - NbtList(Object handle) { - this.container = new NbtElement>(handle); - } - - @Override - public Object getHandle() { - return container.getHandle(); - } - - @Override - public NbtType getType() { - return NbtType.TAG_LIST; - } - /** * Get the type of each element. * @return Element type. */ - public NbtType getElementType() { - return container.getSubType(); - } - - @Override - public String getName() { - return container.getName(); - } + public abstract NbtType getElementType(); - @Override - public void setName(String name) { - container.setName(name); - } + /** + * Add a NBT list or NBT compound to the list. + * @param element + */ + public abstract void add(NbtBase element); - @Override - public List> getValue() { - if (savedList == null) { - savedList = new ConvertedList>(container.getValue()) { - // Check and see if the element is valid - private void verifyElement(NbtBase element) { - if (element == null) - throw new IllegalArgumentException("Cannot store NULL elements in list."); - if (!element.getName().equals(EMPTY_NAME)) - throw new IllegalArgumentException("Cannot add a the named NBT tag " + element + " to a list."); - - // Check element type - if (size() > 0) { - if (!element.getType().equals(getElementType())) { - throw new IllegalArgumentException( - "Cannot add " + element + " of " + element.getType() + " to a list of type " + getElementType()); - } - } else { - container.setSubType(element.getType()); - } - } - - @Override - public boolean add(NbtBase e) { - verifyElement(e); - return super.add(e); - } - - @Override - public void add(int index, NbtBase element) { - verifyElement(element); - super.add(index, element); - } - - @Override - public boolean addAll(Collection> c) { - boolean empty = size() == 0; - boolean result = false; - - for (NbtBase element : c) { - add(element); - result = true; - } - - // See if we now added our first object(s) - if (empty && result) { - container.setSubType(get(0).getType()); - } - return result; - } - - @Override - protected Object toInner(NbtBase outer) { - if (outer == null) - return null; - return NbtFactory.fromBase(outer).getHandle(); - } - - @Override - protected NbtBase toOuter(Object inner) { - if (inner == null) - return null; - return NbtFactory.fromNMS(inner); - } - - @Override - public String toString() { - return NbtList.this.toString(); - } - }; - } - return savedList; - } + /** + * Add a new string element to the list. + * @param value - the string element to add. + * @throws IllegalArgumentException If this is not a list of strings. + */ + public abstract void add(String value); - @SuppressWarnings({"unchecked", "rawtypes"}) - public NbtBase>> deepClone() { - return (NbtBase) container.deepClone(); - } - - public void add(NbtBase element) { - getValue().add(element); - } - - @SuppressWarnings("unchecked") - public void add(String value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - @SuppressWarnings("unchecked") - public void add(byte value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - @SuppressWarnings("unchecked") - public void add(short value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - @SuppressWarnings("unchecked") - public void add(int value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - @SuppressWarnings("unchecked") - public void add(long value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } + /** + * Add a new byte element to the list. + * @param value - the byte element to add. + * @throws IllegalArgumentException If this is not a list of bytes. + */ + public abstract void add(byte value); - @SuppressWarnings("unchecked") - public void add(double value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - @SuppressWarnings("unchecked") - public void add(byte[] value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - @SuppressWarnings("unchecked") - public void add(int[] value) { - add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); - } - - public int size() { - return getValue().size(); - } - - public TType getValue(int index) { - return getValue().get(index).getValue(); - } + /** + * Add a new short element to the list. + * @param value - the short element to add. + * @throws IllegalArgumentException If this is not a list of shorts. + */ + public abstract void add(short value); + + /** + * Add a new integer element to the list. + * @param value - the string element to add. + * @throws IllegalArgumentException If this is not a list of integers. + */ + public abstract void add(int value); + + /** + * Add a new long element to the list. + * @param value - the string element to add. + * @throws IllegalArgumentException If this is not a list of longs. + */ + public abstract void add(long value); + + /** + * Add a new double element to the list. + * @param value - the double element to add. + * @throws IllegalArgumentException If this is not a list of doubles. + */ + public abstract void add(double value); + + /** + * Add a new byte array element to the list. + * @param value - the byte array element to add. + * @throws IllegalArgumentException If this is not a list of byte arrays. + */ + public abstract void add(byte[] value); + + /** + * Add a new int array element to the list. + * @param value - the int array element to add. + * @throws IllegalArgumentException If this is not a list of int arrays. + */ + public abstract void add(int[] value); + + /** + * Remove a given object from the list. + * @param remove - the object to remove. + */ + public abstract void remove(Object remove); + + /** + * Retrieve an element by index. + * @param index - index of the element to retrieve. + * @return The element to retrieve. + * @throws IndexOutOfBoundsException If the index is out of range (index < 0 || index >= size()) + */ + public abstract TType getValue(int index); + /** + * Retrieve the number of elements in this list. + * @return The number of elements in this list. + */ + public abstract int size(); + /** * Retrieve each NBT tag in this list. * @return A view of NBT tag in this list. */ - public Collection> asCollection() { - return getValue(); - } - - @Override - public void setValue(List> newValue) { - NbtBase lastElement = null; - List list = container.getValue(); - list.clear(); - - // Set each underlying element - for (NbtBase type : newValue) { - if (type != null) { - lastElement = type; - list.add(NbtFactory.fromBase(type).getHandle()); - } else { - list.add(null); - } - } - - // Update the sub type as well - if (lastElement != null) { - container.setSubType(lastElement.getType()); - } - } - - @Override - public void write(DataOutput destination) { - NbtFactory.toStream(container, destination); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof NbtList) { - @SuppressWarnings("unchecked") - NbtList other = (NbtList) obj; - return container.equals(other.container); - } - return false; - } - - @Override - public int hashCode() { - return container.hashCode(); - } + public abstract Collection> asCollection(); - @Override - public Iterator iterator() { - return Iterables.transform(getValue(), new Function, TType>() { - @Override - public TType apply(@Nullable NbtBase param) { - return param.getValue(); - } - }).iterator(); - } - - @Override - public String toString() { - // Essentially JSON - StringBuilder builder = new StringBuilder(); - - builder.append("{\"name\": \"" + getName() + "\", \"value\": ["); - - if (size() > 0) { - if (getElementType() == NbtType.TAG_STRING) - builder.append("\"" + Joiner.on("\", \"").join(this) + "\""); - else - builder.append(Joiner.on(", ").join(this)); - } - - builder.append("]}"); - return builder.toString(); - } -} + /** + * Iterate over all the elements in this list. + */ + public abstract Iterator iterator(); +} \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java new file mode 100644 index 00000000..2a5315dd --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java @@ -0,0 +1,606 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.comphenix.protocol.wrappers.nbt; + +import java.io.DataOutput; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * A concrete implementation of an NbtCompound that wraps an underlying NMS Compound. + * + * @author Kristian + */ +public class WrappedCompound implements NbtWrapper>>, Iterable>, NbtCompound { + // A list container + private WrappedElement> container; + + // Saved wrapper map + private ConvertedMap> savedMap; + + /** + * Construct a new NBT compound wrapper. + * @param name - the name of the wrapper. + * @return The wrapped NBT compound. + */ + public static WrappedCompound fromName(String name) { + // Simplify things for the caller + return (WrappedCompound) NbtFactory.>>ofType(NbtType.TAG_COMPOUND, name); + } + + /** + * Construct a new NBT compound wrapper initialized with a given list of NBT values. + * @param name - the name of the compound wrapper. + * @param list - the list of elements to add. + * @return The new wrapped NBT compound. + */ + public static NbtCompound fromList(String name, Collection> list) { + WrappedCompound copy = fromName(name); + + for (NbtBase base : list) + copy.getValue().put(base.getName(), base); + return copy; + } + + /** + * Construct a wrapped compound from a given NMS handle. + * @param handle - the NMS handle. + */ + WrappedCompound(Object handle) { + this.container = new WrappedElement>(handle); + } + + @Override + public Object getHandle() { + return container.getHandle(); + } + + @Override + public NbtType getType() { + return NbtType.TAG_COMPOUND; + } + + @Override + public String getName() { + return container.getName(); + } + + @Override + public void setName(String name) { + container.setName(name); + } + + /** + * Determine if an entry with the given key exists or not. + * @param key - the key to lookup. + * @return TRUE if an entry with the given key exists, FALSE otherwise. + */ + @Override + public boolean containsKey(String key) { + return getValue().containsKey(key); + } + + /** + * Retrieve a Set view of the keys of each entry in this compound. + * @return The keys of each entry. + */ + @Override + public Set getKeys() { + return getValue().keySet(); + } + + @Override + public Map> getValue() { + // Return a wrapper map + if (savedMap == null) { + savedMap = new ConvertedMap>(container.getValue()) { + @Override + protected Object toInner(NbtBase outer) { + if (outer == null) + return null; + return NbtFactory.fromBase(outer).getHandle(); + } + + protected NbtBase toOuter(Object inner) { + if (inner == null) + return null; + return NbtFactory.fromNMS(inner); + }; + + @Override + public String toString() { + return WrappedCompound.this.toString(); + } + }; + } + return savedMap; + } + + @Override + public void setValue(Map> newValue) { + // Write all the entries + for (Map.Entry> entry : newValue.entrySet()) { + put(entry.getValue()); + } + } + + /** + * Retrieve the value of a given entry. + * @param key - key of the entry to retrieve. + * @return The value of this entry, or NULL if not found. + */ + @Override + @SuppressWarnings("unchecked") + public NbtBase getValue(String key) { + return (NbtBase) getValue().get(key); + } + + /** + * Retrieve a value by its key, or assign and return a new NBT element if it doesn't exist. + * @param key - the key of the entry to find or create. + * @param type - the NBT element we will create if not found. + * @return The value that was retrieved or just created. + */ + @Override + public NbtBase getValueOrDefault(String key, NbtType type) { + NbtBase nbt = getValue(key); + + // Create or get a compound + if (nbt == null) + put(nbt = NbtFactory.ofType(type, key)); + else if (nbt.getType() != type) + throw new IllegalArgumentException("Cannot get tag " + nbt + ": Not a " + type); + + return nbt; + } + + /** + * Retrieve a value, or throw an exception. + * @param key - the key to retrieve. + * @return The value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + private NbtBase getValueExact(String key) { + NbtBase value = getValue(key); + + // Only return a legal key + if (value != null) + return value; + else + throw new IllegalArgumentException("Cannot find key " + key); + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public NbtBase>> deepClone() { + return (NbtBase) container.deepClone(); + } + + /** + * Set a entry based on its name. + * @param entry - entry with a name and value. + * @return This compound, for chaining. + */ + @Override + public NbtCompound put(NbtBase entry) { + getValue().put(entry.getName(), entry); + return this; + } + + /** + * Retrieve the string value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The string value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public String getString(String key) { + return (String) getValueExact(key).getValue(); + } + + /** + * Retrieve the string value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public String getStringOrDefault(String key) { + return (String) getValueOrDefault(key, NbtType.TAG_STRING).getValue(); + } + + /** + * Associate a NBT string value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, String value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the byte value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The byte value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public byte getByte(String key) { + return (Byte) getValueExact(key).getValue(); + } + + /** + * Retrieve the byte value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public byte getByteOrDefault(String key) { + return (Byte) getValueOrDefault(key, NbtType.TAG_BYTE).getValue(); + } + + /** + * Associate a NBT byte value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, byte value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the short value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The short value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public Short getShort(String key) { + return (Short) getValueExact(key).getValue(); + } + + /** + * Retrieve the short value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public short getShortOrDefault(String key) { + return (Short) getValueOrDefault(key, NbtType.TAG_SHORT).getValue(); + } + + /** + * Associate a NBT short value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, short value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the integer value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The integer value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public int getInteger(String key) { + return (Integer) getValueExact(key).getValue(); + } + + /** + * Retrieve the integer value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public int getIntegerOrDefault(String key) { + return (Integer) getValueOrDefault(key, NbtType.TAG_INT).getValue(); + } + + /** + * Associate a NBT integer value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, int value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the long value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The long value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public long getLong(String key) { + return (Long) getValueExact(key).getValue(); + } + + /** + * Retrieve the long value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public long getLongOrDefault(String key) { + return (Long) getValueOrDefault(key, NbtType.TAG_LONG).getValue(); + } + + /** + * Associate a NBT long value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, long value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the float value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The float value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public float getFloat(String key) { + return (Float) getValueExact(key).getValue(); + } + + /** + * Retrieve the float value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public float getFloatOrDefault(String key) { + return (Float) getValueOrDefault(key, NbtType.TAG_FLOAT).getValue(); + } + + /** + * Associate a NBT float value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, float value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the double value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The double value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public double getDouble(String key) { + return (Double) getValueExact(key).getValue(); + } + + /** + * Retrieve the double value of an existing entry, or from a new default entry if it doesn't exist. + * @param key - the key of the entry. + * @return The value that was retrieved or just created. + */ + @Override + public double getDoubleOrDefault(String key) { + return (Double) getValueOrDefault(key, NbtType.TAG_DOUBlE).getValue(); + } + + /** + * Associate a NBT double value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, double value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the byte array value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The byte array value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public byte[] getByteArray(String key) { + return (byte[]) getValueExact(key).getValue(); + } + + /** + * Associate a NBT byte array value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, byte[] value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the integer array value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The integer array value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + public int[] getIntegerArray(String key) { + return (int[]) getValueExact(key).getValue(); + } + + /** + * Associate a NBT integer array value with the given key. + * @param key - the key and NBT name. + * @param value - the value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, int[] value) { + getValue().put(key, NbtFactory.of(key, value)); + return this; + } + + /** + * Retrieve the compound (map) value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The compound value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + @SuppressWarnings("rawtypes") + public NbtCompound getCompound(String key) { + return (NbtCompound) ((NbtBase) getValueExact(key)); + } + + /** + * Retrieve a compound (map) value by its key, or create a new compound if it doesn't exist. + * @param key - the key of the entry to find or create. + * @return The compound value that was retrieved or just created. + */ + @Override + public NbtCompound getCompoundOrDefault(String key) { + return (NbtCompound) getValueOrDefault(key, NbtType.TAG_COMPOUND); + } + + /** + * Associate a NBT compound with its name as key. + * @param compound - the compound value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(WrappedCompound compound) { + getValue().put(compound.getName(), compound); + return this; + } + + /** + * Retrieve the NBT list value of an entry identified by a given key. + * @param key - the key of the entry. + * @return The NBT list value of the entry. + * @throws IllegalArgumentException If the key doesn't exist. + */ + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public NbtList getList(String key) { + return (NbtList) getValueExact(key); + } + + /** + * Retrieve a NBT list value by its key, or create a new list if it doesn't exist. + * @param key - the key of the entry to find or create. + * @return The compound value that was retrieved or just created. + */ + @Override + @SuppressWarnings("unchecked") + public NbtList getListOrDefault(String key) { + return (NbtList) getValueOrDefault(key, NbtType.TAG_LIST); + } + + /** + * Associate a NBT list with the given key. + * @param list - the list value. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(WrappedList list) { + getValue().put(list.getName(), list); + return this; + } + + /** + * Associate a new NBT list with the given key. + * @param key - the key and name of the new NBT list. + * @param list - the list of NBT elements. + * @return This current compound, for chaining. + */ + @Override + public NbtCompound put(String key, Collection> list) { + return put(WrappedList.fromList(key, list)); + } + + @Override + public void write(DataOutput destination) { + NbtFactory.toStream(container, destination); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof WrappedCompound) { + WrappedCompound other = (WrappedCompound) obj; + return container.equals(other.container); + } + return false; + } + + @Override + public int hashCode() { + return container.hashCode(); + } + + @Override + public Iterator> iterator() { + return getValue().values().iterator(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + + builder.append("{"); + builder.append("\"name\": \"" + getName() + "\""); + + for (NbtBase element : this) { + builder.append(", "); + + // Wrap in quotation marks + if (element.getType() == NbtType.TAG_STRING) + builder.append("\"" + element.getName() + "\": \"" + element.getValue() + "\""); + else + builder.append("\"" + element.getName() + "\": " + element.getValue()); + } + + builder.append("}"); + return builder.toString(); + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedElement.java similarity index 98% rename from ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java rename to ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedElement.java index 488e681a..963d7bdf 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtElement.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedElement.java @@ -34,7 +34,7 @@ import com.google.common.base.Objects; * * @param - type of the value field. */ -public class NbtElement implements NbtWrapper { +public class WrappedElement implements NbtWrapper { // Structure modifier for the base class private static volatile StructureModifier baseModifier; @@ -57,7 +57,7 @@ public class NbtElement implements NbtWrapper { * Initialize a NBT wrapper for a generic element. * @param handle - the NBT element to wrap. */ - NbtElement(Object handle) { + WrappedElement(Object handle) { this.handle = handle; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java new file mode 100644 index 00000000..4f2df203 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java @@ -0,0 +1,346 @@ +/* + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.comphenix.protocol.wrappers.nbt; + +import java.io.DataOutput; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import javax.annotation.Nullable; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.collect.Iterables; + +/** + * Represents a concrete implementation of an NBT list that wraps an underlying NMS list. + * @author Kristian + * + * @param - the type of the value in each NBT sub element. + */ +public class WrappedList implements NbtWrapper>>, Iterable, NbtList { + // A list container + private WrappedElement> container; + + // Saved wrapper list + private ConvertedList> savedList; + + /** + * Construct a new empty NBT list. + * @param name - name of this list. + * @return The new empty NBT list. + */ + public static WrappedList fromName(String name) { + return (WrappedList) NbtFactory.>>ofType(NbtType.TAG_LIST, name); + } + + /** + * Construct a NBT list of out an array of values.. + * @param name - name of this list. + * @param elements - values to add. + * @return The new filled NBT list. + */ + public static WrappedList fromArray(String name, T... elements) { + WrappedList result = fromName(name); + + for (T element : elements) { + if (element == null) + throw new IllegalArgumentException("An NBT list cannot contain a null element!"); + result.add(NbtFactory.ofType(element.getClass(), EMPTY_NAME, element)); + } + return result; + } + + /** + * Construct a NBT list of out a list of NBT elements. + * @param name - name of this list. + * @param elements - elements to add. + * @return The new filled NBT list. + */ + public static WrappedList fromList(String name, Collection elements) { + WrappedList result = fromName(name); + + for (T element : elements) { + if (element == null) + throw new IllegalArgumentException("An NBT list cannot contain a null element!"); + result.add(NbtFactory.ofType(element.getClass(), EMPTY_NAME, element)); + } + return result; + } + + public WrappedList(Object handle) { + this.container = new WrappedElement>(handle); + } + + @Override + public Object getHandle() { + return container.getHandle(); + } + + @Override + public NbtType getType() { + return NbtType.TAG_LIST; + } + + /** + * Get the type of each element. + * @return Element type. + */ + @Override + public NbtType getElementType() { + return container.getSubType(); + } + + @Override + public String getName() { + return container.getName(); + } + + @Override + public void setName(String name) { + container.setName(name); + } + + @Override + public List> getValue() { + if (savedList == null) { + savedList = new ConvertedList>(container.getValue()) { + // Check and see if the element is valid + private void verifyElement(NbtBase element) { + if (element == null) + throw new IllegalArgumentException("Cannot store NULL elements in list."); + if (!element.getName().equals(EMPTY_NAME)) + throw new IllegalArgumentException("Cannot add a the named NBT tag " + element + " to a list."); + + // Check element type + if (size() > 0) { + if (!element.getType().equals(getElementType())) { + throw new IllegalArgumentException( + "Cannot add " + element + " of " + element.getType() + " to a list of type " + getElementType()); + } + } else { + container.setSubType(element.getType()); + } + } + + @Override + public boolean add(NbtBase e) { + verifyElement(e); + return super.add(e); + } + + @Override + public void add(int index, NbtBase element) { + verifyElement(element); + super.add(index, element); + } + + @Override + public boolean addAll(Collection> c) { + boolean empty = size() == 0; + boolean result = false; + + for (NbtBase element : c) { + add(element); + result = true; + } + + // See if we now added our first object(s) + if (empty && result) { + container.setSubType(get(0).getType()); + } + return result; + } + + @Override + protected Object toInner(NbtBase outer) { + if (outer == null) + return null; + return NbtFactory.fromBase(outer).getHandle(); + } + + @Override + protected NbtBase toOuter(Object inner) { + if (inner == null) + return null; + return NbtFactory.fromNMS(inner); + } + + @Override + public String toString() { + return WrappedList.this.toString(); + } + }; + } + return savedList; + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public NbtBase>> deepClone() { + return (NbtBase) container.deepClone(); + } + + @Override + public void add(NbtBase element) { + getValue().add(element); + } + + @Override + @SuppressWarnings("unchecked") + public void add(String value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(byte value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(short value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(int value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(long value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(double value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(byte[] value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + @SuppressWarnings("unchecked") + public void add(int[] value) { + add((NbtBase) NbtFactory.of(EMPTY_NAME, value)); + } + + @Override + public int size() { + return getValue().size(); + } + + @Override + public TType getValue(int index) { + return getValue().get(index).getValue(); + } + + /** + * Retrieve each NBT tag in this list. + * @return A view of NBT tag in this list. + */ + @Override + public Collection> asCollection() { + return getValue(); + } + + @Override + public void setValue(List> newValue) { + NbtBase lastElement = null; + List list = container.getValue(); + list.clear(); + + // Set each underlying element + for (NbtBase type : newValue) { + if (type != null) { + lastElement = type; + list.add(NbtFactory.fromBase(type).getHandle()); + } else { + list.add(null); + } + } + + // Update the sub type as well + if (lastElement != null) { + container.setSubType(lastElement.getType()); + } + } + + @Override + public void write(DataOutput destination) { + NbtFactory.toStream(container, destination); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof WrappedList) { + @SuppressWarnings("unchecked") + WrappedList other = (WrappedList) obj; + return container.equals(other.container); + } + return false; + } + + @Override + public int hashCode() { + return container.hashCode(); + } + + @Override + public Iterator iterator() { + return Iterables.transform(getValue(), new Function, TType>() { + @Override + public TType apply(@Nullable NbtBase param) { + return param.getValue(); + } + }).iterator(); + } + + @Override + public String toString() { + // Essentially JSON + StringBuilder builder = new StringBuilder(); + + builder.append("{\"name\": \"" + getName() + "\", \"value\": ["); + + if (size() > 0) { + if (getElementType() == NbtType.TAG_STRING) + builder.append("\"" + Joiner.on("\", \"").join(this) + "\""); + else + builder.append(Joiner.on(", ").join(this)); + } + + builder.append("]}"); + return builder.toString(); + } + + @Override + public void remove(Object remove) { + getValue().remove(remove); + } +} diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java index 8359c753..6df0bf45 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java @@ -50,6 +50,7 @@ import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedWatchableObject; import com.comphenix.protocol.wrappers.nbt.NbtCompound; +import com.comphenix.protocol.wrappers.nbt.WrappedCompound; import com.comphenix.protocol.wrappers.nbt.NbtFactory; import com.google.common.collect.Iterables; @@ -256,7 +257,7 @@ public class PacketContainerTest { public void testGetNbtModifier() { PacketContainer updateTileEntity = new PacketContainer(132); - NbtCompound compound = NbtFactory.ofCompound("test"); + WrappedCompound compound = NbtFactory.ofCompound("test"); compound.put("test", "name"); compound.put(NbtFactory.ofList("ages", 1, 2, 3)); diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java index a485fc31..3b807eb3 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java @@ -34,7 +34,7 @@ public class NbtCompoundTest { public void testCustomTags() { NbtCustomTag test = new NbtCustomTag("hello", 12); - NbtCompound map = NbtCompound.fromName("test"); + WrappedCompound map = WrappedCompound.fromName("test"); map.put(test); // Note that the custom tag will be cloned diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java index 531fb58c..f31d301b 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java @@ -40,7 +40,7 @@ public class NbtFactoryTest { @Test public void testFromStream() { - NbtCompound compound = NbtCompound.fromName("tag"); + WrappedCompound compound = WrappedCompound.fromName("tag"); compound.put("name", "Test Testerson"); compound.put("age", 42); From 342630680507f924bd45397c34cba7fe83bcaa9c Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Tue, 8 Jan 2013 22:24:07 +0100 Subject: [PATCH 18/26] Finalize the operation to make the NBT wrappers into interfaces. --- .../protocol/events/PacketContainer.java | 4 +- .../protocol/wrappers/BukkitConverters.java | 18 +-- .../wrappers/nbt/AbstractConverted.java | 8 ++ .../wrappers/nbt/ConvertedCollection.java | 8 ++ .../protocol/wrappers/nbt/ConvertedList.java | 8 ++ .../protocol/wrappers/nbt/ConvertedMap.java | 8 ++ .../protocol/wrappers/nbt/ConvertedSet.java | 8 ++ .../protocol/wrappers/nbt/NbtBase.java | 12 +- .../protocol/wrappers/nbt/NbtCompound.java | 3 + .../protocol/wrappers/nbt/NbtFactory.java | 106 ++++++++++-------- .../protocol/wrappers/nbt/NbtList.java | 3 + .../protocol/wrappers/nbt/NbtWrapper.java | 2 + .../wrappers/nbt/WrappedCompound.java | 8 +- .../protocol/wrappers/nbt/WrappedElement.java | 22 ++-- .../protocol/wrappers/nbt/WrappedList.java | 8 +- .../protocol/events/PacketContainerTest.java | 3 +- 16 files changed, 150 insertions(+), 79 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java index 1751b370..4c679cb3 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketContainer.java @@ -56,7 +56,7 @@ import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedWatchableObject; -import com.comphenix.protocol.wrappers.nbt.NbtWrapper; +import com.comphenix.protocol.wrappers.nbt.NbtBase; import com.google.common.base.Function; import com.google.common.collect.Maps; @@ -369,7 +369,7 @@ public class PacketContainer implements Serializable { * Retrieves a read/write structure for NBT classes. * @return A modifier for NBT classes. */ - public StructureModifier> getNbtModifier() { + public StructureModifier> getNbtModifier() { // Allow access to the NBT class in packet 130 return structureModifier.withType( MinecraftReflection.getNBTBaseClass(), diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java index 7b78bb53..95606336 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java @@ -34,8 +34,8 @@ import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.utility.MinecraftReflection; +import com.comphenix.protocol.wrappers.nbt.NbtBase; import com.comphenix.protocol.wrappers.nbt.NbtFactory; -import com.comphenix.protocol.wrappers.nbt.NbtWrapper; /** * Contains several useful equivalent converters for normal Bukkit types. @@ -214,24 +214,24 @@ public class BukkitConverters { * Retrieve an equivalent converter for net.minecraft.server NBT classes and their wrappers. * @return An equivalent converter for NBT. */ - public static EquivalentConverter> getNbtConverter() { - return getIgnoreNull(new EquivalentConverter>() { + public static EquivalentConverter> getNbtConverter() { + return getIgnoreNull(new EquivalentConverter>() { @Override - public Object getGeneric(Class genericType, NbtWrapper specific) { - return specific.getHandle(); + public Object getGeneric(Class genericType, NbtBase specific) { + return NbtFactory.fromBase(specific).getHandle(); } @Override - public NbtWrapper getSpecific(Object generic) { + public NbtBase getSpecific(Object generic) { return NbtFactory.fromNMS(generic); } @Override @SuppressWarnings("unchecked") - public Class> getSpecificType() { + public Class> getSpecificType() { // Damn you Java AGAIN - Class dummy = NbtWrapper.class; - return (Class>) dummy; + Class dummy = NbtBase.class; + return (Class>) dummy; } }); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/AbstractConverted.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/AbstractConverted.java index e3670a3d..554d4c6b 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/AbstractConverted.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/AbstractConverted.java @@ -21,6 +21,14 @@ import javax.annotation.Nullable; import com.google.common.base.Function; +/** + * Represents an object that transform elements of type VInner to type VOuter and back again. + * + * @author Kristian + * + * @param - the first type. + * @param - the second type. + */ abstract class AbstractConverted { /** * Convert a value from the inner map to the outer visible map. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedCollection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedCollection.java index 9af8189a..dea203e6 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedCollection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedCollection.java @@ -25,6 +25,14 @@ import java.util.List; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; +/** + * Represents a collection that wraps another collection by transforming the elements going in and out. + * + * @author Kristian + * + * @param - type of the element in the inner invisible collection. + * @param - type of the elements publically accessible in the outer collection. + */ abstract class ConvertedCollection extends AbstractConverted implements Collection { // Inner collection private Collection inner; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedList.java index 15e21092..3562a7c5 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedList.java @@ -21,6 +21,14 @@ import java.util.Collection; import java.util.List; import java.util.ListIterator; +/** + * Represents a list that wraps another list by transforming the items going in and out. + * + * @author Kristian + * + * @param - type of the items in the inner invisible list. + * @param - type of the items publically accessible in the outer list. + */ abstract class ConvertedList extends ConvertedCollection implements List { private List inner; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedMap.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedMap.java index 7f58f6bd..b662d0da 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedMap.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedMap.java @@ -21,6 +21,14 @@ import java.util.Collection; import java.util.Map; import java.util.Set; +/** + * Represents a map that wraps another map by transforming the entries going in and out. + * + * @author Kristian + * + * @param - type of the value in the entries in the inner invisible map. + * @param - type of the value in the entries publically accessible in the outer map. + */ abstract class ConvertedMap extends AbstractConverted implements Map { // Inner map private Map inner; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedSet.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedSet.java index 15c072eb..5aa0d203 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedSet.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/ConvertedSet.java @@ -20,6 +20,14 @@ package com.comphenix.protocol.wrappers.nbt; import java.util.Collection; import java.util.Set; +/** + * Represents a set that wraps another set by transforming the items going in and out. + * + * @author Kristian + * + * @param - type of the element in the inner invisible set. + * @param - type of the elements publically accessible in the outer set. + */ abstract class ConvertedSet extends ConvertedCollection implements Set { public ConvertedSet(Collection inner) { super(inner); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java index eb1b86a7..985f6e22 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java @@ -22,8 +22,10 @@ import com.comphenix.protocol.wrappers.nbt.NbtType; /** * Represents a generic container for an NBT element. - * @author Kristian + *

+ * Use {@link NbtFactory} to load or create an instance. * + * @author Kristian * @param - type of the value that is stored. */ public interface NbtBase { @@ -52,7 +54,13 @@ public interface NbtBase { /** * Retrieve the value of this NBT tag. *

- * Is either a primitive wrapper, a list or a map. + * Is either a primitive {@link java.lang.Number wrapper}, {@link java.lang.String String}, + * {@link java.util.List List} or a {@link java.util.Map Map}. + *

+ * All operations that modify collections directly, such as {@link java.util.List#add(Object) List.add(Object)} or + * {@link java.util.Map#clear() Map.clear()}, are considered optional. This also include members in {@link java.util.Iterator Iterator} and + * {@link java.util.ListIterator ListIterator}. Operations that are not implemented throw a + * {@link java.lang.UnsupportedOperationException UnsupportedOperationException}. * @return Value of this tag. */ public abstract TType getValue(); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java index 35cc0075..ee066ffe 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java @@ -9,6 +9,9 @@ import java.util.Set; * Represents a mapping of arbitrary NBT elements and their unique names. *

* Use {@link NbtFactory} to load or create an instance. + *

+ * The {@link NbtBase#getValue()} method returns a {@link java.util.Map} that will correctly return the content + * of this NBT compound, but may throw an {@link UnsupportedOperationException} for any of the write operations. * * @author Kristian */ diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java index 53c9d7c6..95885165 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java @@ -49,47 +49,47 @@ public class NbtFactory { private static StructureModifier itemStackModifier; /** - * Attempt to cast this wrapper as a compund. + * Attempt to cast this NBT tag as a compund. + * @param tag - the NBT tag to cast. * @return This instance as a compound. * @throws UnsupportedOperationException If this is not a compound. */ - public static NbtCompound asCompound(NbtWrapper wrapper) { - if (wrapper instanceof NbtCompound) - return (NbtCompound) wrapper; - else if (wrapper != null) + public static NbtCompound asCompound(NbtBase tag) { + if (tag instanceof NbtCompound) + return (NbtCompound) tag; + else if (tag != null) throw new UnsupportedOperationException( - "Cannot cast a " + wrapper.getClass() + "( " + wrapper.getType() + ") to TAG_COMPUND."); + "Cannot cast a " + tag.getClass() + "( " + tag.getType() + ") to TAG_COMPUND."); else - throw new IllegalArgumentException("Wrapper cannot be NULL."); + throw new IllegalArgumentException("Tag cannot be NULL."); } /** - * Attempt to cast this wrapper as a list. + * Attempt to cast this NBT tag as a list. + * @param tag - the NBT tag to cast. * @return This instance as a list. * @throws UnsupportedOperationException If this is not a list. */ - public static NbtList asList(NbtWrapper wrapper) { - if (wrapper instanceof NbtList) - return (NbtList) wrapper; - else if (wrapper != null) + public static NbtList asList(NbtBase tag) { + if (tag instanceof NbtList) + return (NbtList) tag; + else if (tag != null) throw new UnsupportedOperationException( - "Cannot cast a " + wrapper.getClass() + "( " + wrapper.getType() + ") to TAG_LIST."); + "Cannot cast a " + tag.getClass() + "( " + tag.getType() + ") to TAG_LIST."); else - throw new IllegalArgumentException("Wrapper cannot be NULL."); + throw new IllegalArgumentException("Tag cannot be NULL."); } /** * Get a NBT wrapper from a NBT base. + *

+ * This may clone the content if the NbtBase is not a NbtWrapper. * @param base - the base class. * @return A NBT wrapper. */ @SuppressWarnings("unchecked") public static NbtWrapper fromBase(NbtBase base) { - if (base instanceof WrappedElement) { - return (WrappedElement) base; - } else if (base instanceof WrappedCompound) { - return (NbtWrapper) base; - } else if (base instanceof WrappedList) { + if (base instanceof NbtWrapper) { return (NbtWrapper) base; } else { if (base.getType() == NbtType.TAG_COMPOUND) { @@ -109,7 +109,7 @@ public class NbtFactory { } else { // Copy directly - NbtWrapper copy = ofType(base.getType(), base.getName()); + NbtWrapper copy = ofWrapper(base.getType(), base.getName()); copy.setValue(base.getValue()); return copy; @@ -136,17 +136,17 @@ public class NbtFactory { } // Use the first and best NBT tag - StructureModifier> modifier = itemStackModifier. + StructureModifier> modifier = itemStackModifier. withTarget(nmsStack). withType(MinecraftReflection.getNBTBaseClass(), BukkitConverters.getNbtConverter()); - NbtWrapper result = modifier.read(0); + NbtBase result = modifier.read(0); // Create the tag if it doesn't exist if (result == null) { result = NbtFactory.ofCompound("tag"); modifier.write(0, result); } - return result; + return fromBase(result); } /** @@ -215,8 +215,8 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtWrapper of(String name, String value) { - return ofType(NbtType.TAG_STRING, name, value); + public static NbtBase of(String name, String value) { + return ofWrapper(NbtType.TAG_STRING, name, value); } /** @@ -225,8 +225,8 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtWrapper of(String name, byte value) { - return ofType(NbtType.TAG_BYTE, name, value); + public static NbtBase of(String name, byte value) { + return ofWrapper(NbtType.TAG_BYTE, name, value); } /** @@ -235,8 +235,8 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtWrapper of(String name, short value) { - return ofType(NbtType.TAG_SHORT, name, value); + public static NbtBase of(String name, short value) { + return ofWrapper(NbtType.TAG_SHORT, name, value); } /** @@ -245,8 +245,8 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtWrapper of(String name, int value) { - return ofType(NbtType.TAG_INT, name, value); + public static NbtBase of(String name, int value) { + return ofWrapper(NbtType.TAG_INT, name, value); } /** @@ -255,8 +255,8 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtWrapper of(String name, long value) { - return ofType(NbtType.TAG_LONG, name, value); + public static NbtBase of(String name, long value) { + return ofWrapper(NbtType.TAG_LONG, name, value); } /** @@ -265,8 +265,8 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtWrapper of(String name, float value) { - return ofType(NbtType.TAG_FLOAT, name, value); + public static NbtBase of(String name, float value) { + return ofWrapper(NbtType.TAG_FLOAT, name, value); } /** @@ -275,8 +275,8 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtWrapper of(String name, double value) { - return ofType(NbtType.TAG_DOUBlE, name, value); + public static NbtBase of(String name, double value) { + return ofWrapper(NbtType.TAG_DOUBlE, name, value); } /** @@ -285,8 +285,8 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtWrapper of(String name, byte[] value) { - return ofType(NbtType.TAG_BYTE_ARRAY, name, value); + public static NbtBase of(String name, byte[] value) { + return ofWrapper(NbtType.TAG_BYTE_ARRAY, name, value); } /** @@ -295,12 +295,12 @@ public class NbtFactory { * @param value - value of the tag. * @return The constructed NBT tag. */ - public static NbtWrapper of(String name, int[] value) { - return ofType(NbtType.TAG_INT_ARRAY, name, value); + public static NbtBase of(String name, int[] value) { + return ofWrapper(NbtType.TAG_INT_ARRAY, name, value); } /** - * Construct a new NBT compound wrapper initialized with a given list of NBT values. + * Construct a new NBT compound initialized with a given list of NBT values. * @param name - the name of the compound wrapper. * @param list - the list of elements to add. * @return The new wrapped NBT compound. @@ -314,7 +314,7 @@ public class NbtFactory { * @param name - the name of the compound wrapper. * @return The new wrapped NBT compound. */ - public static WrappedCompound ofCompound(String name) { + public static NbtCompound ofCompound(String name) { return WrappedCompound.fromName(name); } @@ -328,6 +328,16 @@ public class NbtFactory { return WrappedList.fromArray(name, elements); } + /** + * Construct a NBT list of out a list of values. + * @param name - name of this list. + * @param elements - elements to add. + * @return The new filled NBT list. + */ + public static NbtList ofList(String name, Collection elements) { + return WrappedList.fromList(name, elements); + } + /** * Create a new NBT wrapper from a given type. * @param type - the NBT type. @@ -336,7 +346,7 @@ public class NbtFactory { * @throws FieldAccessException If we're unable to create the underlying tag. */ @SuppressWarnings({"unchecked", "rawtypes"}) - public static NbtWrapper ofType(NbtType type, String name) { + public static NbtWrapper ofWrapper(NbtType type, String name) { if (type == null) throw new IllegalArgumentException("type cannot be NULL."); if (type == NbtType.TAG_END) @@ -376,8 +386,8 @@ public class NbtFactory { * @return The new wrapped NBT tag. * @throws FieldAccessException If we're unable to create the underlying tag. */ - public static NbtWrapper ofType(NbtType type, String name, T value) { - NbtWrapper created = ofType(type, name); + public static NbtWrapper ofWrapper(NbtType type, String name, T value) { + NbtWrapper created = ofWrapper(type, name); // Update the value created.setValue(value); @@ -393,7 +403,7 @@ public class NbtFactory { * @throws FieldAccessException If we're unable to create the underlying tag. * @throws IllegalArgumentException If the given class type is not valid NBT. */ - public static NbtWrapper ofType(Class type, String name, T value) { - return ofType(NbtType.getTypeFromClass(type), name, value); + public static NbtWrapper ofWrapper(Class type, String name, T value) { + return ofWrapper(NbtType.getTypeFromClass(type), name, value); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java index f4db93e2..1aa823a2 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java @@ -8,6 +8,9 @@ import java.util.List; * Represents a list of NBT tags of the same type without names. *

* Use {@link NbtFactory} to load or create an instance. + *

+ * The {@link NbtBase#getValue()} method returns a {@link java.util.List} that will correctly return the content + * of this NBT list, but may throw an {@link UnsupportedOperationException} for any of the write operations. * * @author Kristian * diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtWrapper.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtWrapper.java index 7317d474..da1916f0 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtWrapper.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtWrapper.java @@ -21,6 +21,8 @@ import java.io.DataOutput; /** * Indicates that this NBT wraps an underlying net.minecraft.server instance. + *

+ * Use {@link NbtFactory} to load or create instances. * * @author Kristian * diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java index 2a5315dd..899fae1e 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java @@ -28,7 +28,7 @@ import java.util.Set; * * @author Kristian */ -public class WrappedCompound implements NbtWrapper>>, Iterable>, NbtCompound { +class WrappedCompound implements NbtWrapper>>, Iterable>, NbtCompound { // A list container private WrappedElement> container; @@ -42,7 +42,7 @@ public class WrappedCompound implements NbtWrapper>>, Ite */ public static WrappedCompound fromName(String name) { // Simplify things for the caller - return (WrappedCompound) NbtFactory.>>ofType(NbtType.TAG_COMPOUND, name); + return (WrappedCompound) NbtFactory.>>ofWrapper(NbtType.TAG_COMPOUND, name); } /** @@ -63,7 +63,7 @@ public class WrappedCompound implements NbtWrapper>>, Ite * Construct a wrapped compound from a given NMS handle. * @param handle - the NMS handle. */ - WrappedCompound(Object handle) { + public WrappedCompound(Object handle) { this.container = new WrappedElement>(handle); } @@ -164,7 +164,7 @@ public class WrappedCompound implements NbtWrapper>>, Ite // Create or get a compound if (nbt == null) - put(nbt = NbtFactory.ofType(type, key)); + put(nbt = NbtFactory.ofWrapper(type, key)); else if (nbt.getType() != type) throw new IllegalArgumentException("Cannot get tag " + nbt + ": Not a " + type); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedElement.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedElement.java index 963d7bdf..ff855cfc 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedElement.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedElement.java @@ -27,14 +27,12 @@ import com.comphenix.protocol.utility.MinecraftReflection; import com.google.common.base.Objects; /** - * Represents an arbitrary NBT tag element, composite or not. - *

- * Use {@link NbtFactory} to load or create an instance. - * @author Kristian + * Represents a wrapped NBT tag element, composite or not. * + * @author Kristian * @param - type of the value field. */ -public class WrappedElement implements NbtWrapper { +class WrappedElement implements NbtWrapper { // Structure modifier for the base class private static volatile StructureModifier baseModifier; @@ -57,7 +55,7 @@ public class WrappedElement implements NbtWrapper { * Initialize a NBT wrapper for a generic element. * @param handle - the NBT element to wrap. */ - WrappedElement(Object handle) { + public WrappedElement(Object handle) { this.handle = handle; } @@ -135,12 +133,20 @@ public class WrappedElement implements NbtWrapper { return type; } - NbtType getSubType() { + /** + * Retrieve the sub element type of the underlying NMS NBT list. + * @return The NBT sub type. + */ + public NbtType getSubType() { int subID = getCurrentBaseModifier().withType(byte.class).withTarget(handle).read(0); return NbtType.getTypeFromID(subID); } - void setSubType(NbtType type) { + /** + * Set the sub element type of the underlying NMS NBT list. + * @param type - the new sub element type. + */ + public void setSubType(NbtType type) { byte subID = (byte) type.getRawID(); getCurrentBaseModifier().withType(byte.class).withTarget(handle).write(0, subID); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java index 4f2df203..24fa7a05 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java @@ -34,7 +34,7 @@ import com.google.common.collect.Iterables; * * @param - the type of the value in each NBT sub element. */ -public class WrappedList implements NbtWrapper>>, Iterable, NbtList { +class WrappedList implements NbtWrapper>>, Iterable, NbtList { // A list container private WrappedElement> container; @@ -47,7 +47,7 @@ public class WrappedList implements NbtWrapper>>, Ite * @return The new empty NBT list. */ public static WrappedList fromName(String name) { - return (WrappedList) NbtFactory.>>ofType(NbtType.TAG_LIST, name); + return (WrappedList) NbtFactory.>>ofWrapper(NbtType.TAG_LIST, name); } /** @@ -62,7 +62,7 @@ public class WrappedList implements NbtWrapper>>, Ite for (T element : elements) { if (element == null) throw new IllegalArgumentException("An NBT list cannot contain a null element!"); - result.add(NbtFactory.ofType(element.getClass(), EMPTY_NAME, element)); + result.add(NbtFactory.ofWrapper(element.getClass(), EMPTY_NAME, element)); } return result; } @@ -79,7 +79,7 @@ public class WrappedList implements NbtWrapper>>, Ite for (T element : elements) { if (element == null) throw new IllegalArgumentException("An NBT list cannot contain a null element!"); - result.add(NbtFactory.ofType(element.getClass(), EMPTY_NAME, element)); + result.add(NbtFactory.ofWrapper(element.getClass(), EMPTY_NAME, element)); } return result; } diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java index 6df0bf45..8359c753 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java @@ -50,7 +50,6 @@ import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedWatchableObject; import com.comphenix.protocol.wrappers.nbt.NbtCompound; -import com.comphenix.protocol.wrappers.nbt.WrappedCompound; import com.comphenix.protocol.wrappers.nbt.NbtFactory; import com.google.common.collect.Iterables; @@ -257,7 +256,7 @@ public class PacketContainerTest { public void testGetNbtModifier() { PacketContainer updateTileEntity = new PacketContainer(132); - WrappedCompound compound = NbtFactory.ofCompound("test"); + NbtCompound compound = NbtFactory.ofCompound("test"); compound.put("test", "name"); compound.put(NbtFactory.ofList("ages", 1, 2, 3)); From dbc28c0035a84dd04abe677de4d7bbf6d1e6a828 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 9 Jan 2013 04:50:04 +0100 Subject: [PATCH 19/26] Added the ability to serialize and deserialize NBT to many formats. --- .../protocol/wrappers/nbt/NbtBase.java | 7 + .../protocol/wrappers/nbt/NbtCompound.java | 12 +- .../protocol/wrappers/nbt/NbtFactory.java | 54 +--- .../protocol/wrappers/nbt/NbtList.java | 18 +- .../protocol/wrappers/nbt/NbtType.java | 27 +- .../protocol/wrappers/nbt/NbtVisitor.java | 43 +++ .../wrappers/nbt/WrappedCompound.java | 30 +- .../protocol/wrappers/nbt/WrappedElement.java | 9 +- .../protocol/wrappers/nbt/WrappedList.java | 103 ++++-- .../wrappers/nbt/io/NbtBinarySerializer.java | 88 +++++ .../nbt/io/NbtConfigurationSerializer.java | 301 ++++++++++++++++++ .../wrappers/nbt/io/NbtTextSerializer.java | 119 +++++++ .../wrappers/nbt/NbtCompoundTest.java | 5 + .../protocol/wrappers/nbt/NbtFactoryTest.java | 3 +- .../io/NbtConfigurationSerializerTest.java | 37 +++ 15 files changed, 772 insertions(+), 84 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtVisitor.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtBinarySerializer.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializer.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtTextSerializer.java create mode 100644 ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializerTest.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java index 985f6e22..4fda61b8 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtBase.java @@ -29,6 +29,13 @@ import com.comphenix.protocol.wrappers.nbt.NbtType; * @param - type of the value that is stored. */ public interface NbtBase { + /** + * Accepts a NBT visitor. + * @param visitor - the hierarchical NBT visitor. + * @return TRUE if the parent should continue processing children at the current level, FALSE otherwise. + */ + public abstract boolean accept(NbtVisitor visitor); + /** * Retrieve the type of this NBT element. * @return The type of this NBT element. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java index ee066ffe..ba27a6cb 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtCompound.java @@ -10,7 +10,7 @@ import java.util.Set; *

* Use {@link NbtFactory} to load or create an instance. *

- * The {@link NbtBase#getValue()} method returns a {@link java.util.Map} that will correctly return the content + * The {@link NbtBase#getValue()} method returns a {@link java.util.Map} that will return the full content * of this NBT compound, but may throw an {@link UnsupportedOperationException} for any of the write operations. * * @author Kristian @@ -73,6 +73,14 @@ public interface NbtCompound extends NbtBase>>, Iterable< * @return This current compound, for chaining. */ public abstract NbtCompound put(String key, String value); + + /** + * Inserts an entry after cloning it and renaming it to "key". + * @param key - the name of the entry. + * @param entry - the entry to insert. + * @return This current compound, for chaining. + */ + public abstract NbtCompound put(String key, NbtBase entry); /** * Retrieve the byte value of an entry identified by a given key. @@ -286,7 +294,7 @@ public interface NbtCompound extends NbtBase>>, Iterable< * @param list - the list value. * @return This current compound, for chaining. */ - public abstract NbtCompound put(WrappedList list); + public abstract NbtCompound put(NbtList list); /** * Associate a new NBT list with the given key. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java index 95885165..4c932ade 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java @@ -17,8 +17,6 @@ package com.comphenix.protocol.wrappers.nbt; -import java.io.DataInput; -import java.io.DataOutput; import java.lang.reflect.Method; import java.util.Collection; import java.util.List; @@ -40,11 +38,7 @@ import com.comphenix.protocol.wrappers.BukkitConverters; public class NbtFactory { // Used to create the underlying tag private static Method methodCreateTag; - - // Used to read and write NBT - private static Method methodWrite; - private static Method methodLoad; - + // Item stack trickery private static StructureModifier itemStackModifier; @@ -102,7 +96,7 @@ public class NbtFactory { } else if (base.getType() == NbtType.TAG_LIST) { // As above - WrappedList copy = WrappedList.fromName(base.getName()); + NbtList copy = WrappedList.fromName(base.getName()); copy.setValue((List>) base.getValue()); return (NbtWrapper) copy; @@ -166,49 +160,7 @@ public class NbtFactory { else return partial; } - - /** - * Write the content of a wrapped NBT tag to a stream. - * @param value - the NBT tag to write. - * @param destination - the destination stream. - */ - public static void toStream(NbtWrapper value, DataOutput destination) { - if (methodWrite == null) { - Class base = MinecraftReflection.getNBTBaseClass(); - - // Use the base class - methodWrite = FuzzyReflection.fromClass(base). - getMethodByParameters("writeNBT", base, DataOutput.class); - } - try { - methodWrite.invoke(null, fromBase(value).getHandle(), destination); - } catch (Exception e) { - throw new FieldAccessException("Unable to write NBT " + value, e); - } - } - - /** - * Load an NBT tag from a stream. - * @param source - the input stream. - * @return An NBT tag. - */ - public static NbtWrapper fromStream(DataInput source) { - if (methodLoad == null) { - Class base = MinecraftReflection.getNBTBaseClass(); - - // Use the base class - methodLoad = FuzzyReflection.fromClass(base). - getMethodByParameters("load", base, new Class[] { DataInput.class }); - } - - try { - return fromNMS(methodLoad.invoke(null, source)); - } catch (Exception e) { - throw new FieldAccessException("Unable to read NBT from " + source, e); - } - } - /** * Constructs a NBT tag of type string. * @param name - name of the tag. @@ -276,7 +228,7 @@ public class NbtFactory { * @return The constructed NBT tag. */ public static NbtBase of(String name, double value) { - return ofWrapper(NbtType.TAG_DOUBlE, name, value); + return ofWrapper(NbtType.TAG_DOUBLE, name, value); } /** diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java index 1aa823a2..85ef13de 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtList.java @@ -23,11 +23,27 @@ public interface NbtList extends NbtBase>>, Iterable< public static String EMPTY_NAME = ""; /** - * Get the type of each element. + * Get the type of each element. + *

+ * This will be {@link NbtType#TAG_END TAG_END} if the NBT list has just been created. * @return Element type. */ public abstract NbtType getElementType(); + /** + * Set the type of each element. + * @param type - type of each element. + */ + public abstract void setElementType(NbtType type); + + /** + * Add a value to a typed list by attempting to convert it to the nearest value. + *

+ * Note that the list must be typed by setting {@link #setElementType(NbtType)} before calling this function. + * @param value - the value to add. + */ + public abstract void addClosest(Object value); + /** * Add a NBT list or NBT compound to the list. * @param element diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java index 16b1787a..2ef6e59d 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtType.java @@ -62,7 +62,7 @@ public enum NbtType { /** * A signed 8 byte floating point type. */ - TAG_DOUBlE(6, double.class), + TAG_DOUBLE(6, double.class), /** * An array of bytes. @@ -113,13 +113,25 @@ public enum NbtType { classLookup.put(Primitives.wrap(type.getValueType()), type); } } + + // Additional lookup + classLookup.put(NbtList.class, TAG_LIST); + classLookup.put(NbtCompound.class, TAG_COMPOUND); } private NbtType(int rawID, Class valueType) { this.rawID = rawID; this.valueType = valueType; } - + + /** + * Determine if the given NBT can store multiple children NBT tags. + * @return TRUE if this is a composite NBT tag, FALSE otherwise. + */ + public boolean isComposite() { + return this == TAG_COMPOUND || this == TAG_LIST; + } + /** * Retrieves the raw unique integer that identifies the type of the parent NBT element. * @return Integer that uniquely identifying the type. @@ -157,9 +169,16 @@ public enum NbtType { NbtType result = classLookup.get(clazz); // Try to lookup this value - if (result != null) + if (result != null) { return result; - else + } else { + // Look for interfaces + for (Class implemented : clazz.getInterfaces()) { + if (classLookup.containsKey(implemented)) + return classLookup.get(implemented); + } + throw new IllegalArgumentException("No NBT tag can represent a " + clazz); + } } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtVisitor.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtVisitor.java new file mode 100644 index 00000000..8a9a3ec9 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtVisitor.java @@ -0,0 +1,43 @@ +package com.comphenix.protocol.wrappers.nbt; + +/** + * A visitor that can enumerate a NBT tree structure. + * + * @author Kristian + */ +public interface NbtVisitor { + /** + * Visit a leaf node, which is a NBT tag with a primitive or String value. + * @param node - the visited leaf node. + * @return TRUE to continue visiting children at this level, FALSE otherwise. + */ + public boolean visit(NbtBase node); + + /** + * Begin visiting a list node that contains multiple child nodes of the same type. + * @param list - the NBT tag to process. + * @return TRUE to visit the child nodes of this list, FALSE otherwise. + */ + public boolean visitEnter(NbtList list); + + /** + * Begin visiting a compound node that contains multiple child nodes of different types. + * @param compound - the NBT tag to process. + * @return TRUE to visit the child nodes of this compound, FALSE otherwise. + */ + public boolean visitEnter(NbtCompound compound); + + /** + * Stop visiting a list node. + * @param list - the list we're done visiting. + * @return TRUE for the parent to visit any subsequent sibling nodes, FALSE otherwise. + */ + public boolean visitLeave(NbtList list); + + /** + * Stop visiting a compound node. + * @param compound - the compound we're done visting. + * @return TRUE for the parent to visit any subsequent sibling nodes, FALSE otherwise + */ + public boolean visitLeave(NbtCompound compound); +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java index 899fae1e..410e093d 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedCompound.java @@ -23,6 +23,8 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; +import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer; + /** * A concrete implementation of an NbtCompound that wraps an underlying NMS Compound. * @@ -66,6 +68,19 @@ class WrappedCompound implements NbtWrapper>>, Iterable>(handle); } + + @Override + public boolean accept(NbtVisitor visitor) { + // Enter this node? + if (visitor.visitEnter(this)) { + for (NbtBase node : this) { + if (!node.accept(visitor)) + break; + } + } + + return visitor.visitLeave(this); + } @Override public Object getHandle() { @@ -420,7 +435,7 @@ class WrappedCompound implements NbtWrapper>>, Iterable>>, Iterable NbtCompound put(WrappedList list) { + public NbtCompound put(NbtList list) { getValue().put(list.getName(), list); return this; } + @Override + public NbtCompound put(String key, NbtBase entry) { + // Don't modify the original NBT + NbtBase clone = entry.deepClone(); + + clone.setName(key); + return put(clone); + } + /** * Associate a new NBT list with the given key. * @param key - the key and name of the new NBT list. @@ -561,7 +585,7 @@ class WrappedCompound implements NbtWrapper>>, Iterable implements NbtWrapper { return modifier; } + @Override + public boolean accept(NbtVisitor visitor) { + return visitor.visit(this); + } + /** * Retrieve the underlying NBT tag object. * @return The underlying Minecraft tag object. @@ -173,7 +179,8 @@ class WrappedElement implements NbtWrapper { @Override public void write(DataOutput destination) { - NbtFactory.toStream(this, destination); + // No need to cache this object + NbtBinarySerializer.DEFAULT.serialize(this, destination); } @Override diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java index 24fa7a05..7e281b04 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/WrappedList.java @@ -24,6 +24,7 @@ import java.util.List; import javax.annotation.Nullable; +import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.Iterables; @@ -41,13 +42,17 @@ class WrappedList implements NbtWrapper>>, Iterable> savedList; + // Element type + private NbtType elementType = NbtType.TAG_END; + /** * Construct a new empty NBT list. * @param name - name of this list. * @return The new empty NBT list. */ - public static WrappedList fromName(String name) { - return (WrappedList) NbtFactory.>>ofWrapper(NbtType.TAG_LIST, name); + @SuppressWarnings("unchecked") + public static NbtList fromName(String name) { + return (NbtList) NbtFactory.>>ofWrapper(NbtType.TAG_LIST, name); } /** @@ -56,13 +61,18 @@ class WrappedList implements NbtWrapper>>, Iterable WrappedList fromArray(String name, T... elements) { - WrappedList result = fromName(name); + @SuppressWarnings({"unchecked", "rawtypes"}) + public static NbtList fromArray(String name, T... elements) { + NbtList result = fromName(name); for (T element : elements) { if (element == null) throw new IllegalArgumentException("An NBT list cannot contain a null element!"); - result.add(NbtFactory.ofWrapper(element.getClass(), EMPTY_NAME, element)); + + if (element instanceof NbtBase) + result.add((NbtBase) element); + else + result.add(NbtFactory.ofWrapper(element.getClass(), EMPTY_NAME, element)); } return result; } @@ -73,21 +83,44 @@ class WrappedList implements NbtWrapper>>, Iterable WrappedList fromList(String name, Collection elements) { - WrappedList result = fromName(name); + @SuppressWarnings({"unchecked", "rawtypes"}) + public static NbtList fromList(String name, Collection elements) { + NbtList result = fromName(name); for (T element : elements) { if (element == null) throw new IllegalArgumentException("An NBT list cannot contain a null element!"); - result.add(NbtFactory.ofWrapper(element.getClass(), EMPTY_NAME, element)); + + if (element instanceof NbtBase) + result.add((NbtBase) element); + else + result.add(NbtFactory.ofWrapper(element.getClass(), EMPTY_NAME, element)); } return result; } + /** + * Construct a list from an NMS instance. + * @param handle - NMS instance. + */ public WrappedList(Object handle) { this.container = new WrappedElement>(handle); + this.elementType = container.getSubType(); } + @Override + public boolean accept(NbtVisitor visitor) { + // Enter this node? + if (visitor.visitEnter(this)) { + for (NbtBase node : getValue()) { + if (!node.accept(visitor)) + break; + } + } + + return visitor.visitLeave(this); + } + @Override public Object getHandle() { return container.getHandle(); @@ -98,15 +131,17 @@ class WrappedList implements NbtWrapper>>, Iterable implements NbtWrapper>>, Iterable 0) { + if (getElementType() != NbtType.TAG_END) { if (!element.getType().equals(getElementType())) { throw new IllegalArgumentException( "Cannot add " + element + " of " + element.getType() + " to a list of type " + getElementType()); @@ -153,18 +188,12 @@ class WrappedList implements NbtWrapper>>, Iterable> c) { - boolean empty = size() == 0; boolean result = false; for (NbtBase element : c) { add(element); result = true; } - - // See if we now added our first object(s) - if (empty && result) { - container.setSubType(get(0).getType()); - } return result; } @@ -197,6 +226,38 @@ class WrappedList implements NbtWrapper>>, Iterable) value); + + } else { + // Just add it + add((NbtBase) NbtFactory.ofWrapper(getElementType(), EMPTY_NAME, value)); + } + } + @Override public void add(NbtBase element) { getValue().add(element); @@ -293,7 +354,7 @@ class WrappedList implements NbtWrapper>>, Iterable void serialize(NbtBase value, DataOutput destination) { + if (methodWrite == null) { + Class base = MinecraftReflection.getNBTBaseClass(); + + // Use the base class + methodWrite = FuzzyReflection.fromClass(base). + getMethodByParameters("writeNBT", base, DataOutput.class); + } + + try { + methodWrite.invoke(null, NbtFactory.fromBase(value).getHandle(), destination); + } catch (Exception e) { + throw new FieldAccessException("Unable to write NBT " + value, e); + } + } + + /** + * Load an NBT tag from a stream. + * @param source - the input stream. + * @return An NBT tag. + */ + public NbtWrapper deserialize(DataInput source) { + if (methodLoad == null) { + Class base = MinecraftReflection.getNBTBaseClass(); + + // Use the base class + methodLoad = FuzzyReflection.fromClass(base). + getMethodByParameters("load", base, new Class[] { DataInput.class }); + } + + try { + return NbtFactory.fromNMS(methodLoad.invoke(null, source)); + } catch (Exception e) { + throw new FieldAccessException("Unable to read NBT from " + source, e); + } + } + + /** + * Load an NBT compound from a stream. + * @param source - the input stream. + * @return An NBT compound. + */ + @SuppressWarnings("rawtypes") + public NbtCompound deserializeCompound(DataInput source) { + // I always seem to override generics ... + return (NbtCompound) (NbtBase) deserialize(source); + } + + /** + * Load an NBT list from a stream. + * @param source - the input stream. + * @return An NBT list. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public NbtList deserializeList(DataInput source) { + return (NbtList) (NbtBase) deserialize(source); + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializer.java new file mode 100644 index 00000000..acdf3271 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializer.java @@ -0,0 +1,301 @@ +package com.comphenix.protocol.wrappers.nbt.io; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import com.comphenix.protocol.wrappers.nbt.NbtBase; +import com.comphenix.protocol.wrappers.nbt.NbtCompound; +import com.comphenix.protocol.wrappers.nbt.NbtFactory; +import com.comphenix.protocol.wrappers.nbt.NbtList; +import com.comphenix.protocol.wrappers.nbt.NbtType; +import com.comphenix.protocol.wrappers.nbt.NbtVisitor; +import com.comphenix.protocol.wrappers.nbt.NbtWrapper; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.primitives.Ints; + +/** + * Serialize and deserialize NBT information from a configuration section. + *

+ * Note that data types may be internally preserved by modifying the serialized name. This may + * be visible to the end-user. + * + * @author Kristian + */ +public class NbtConfigurationSerializer { + /** + * The default delimiter that is used to store the data type in YAML. + */ + public static final String TYPE_DELIMITER = "$"; + + /** + * A standard YAML serializer. + */ + public static final NbtConfigurationSerializer DEFAULT = new NbtConfigurationSerializer(); + + private String dataTypeDelimiter; + + /** + * Construct a serializer using {@link #TYPE_DELIMITER} as the default delimiter. + */ + public NbtConfigurationSerializer() { + this.dataTypeDelimiter = TYPE_DELIMITER; + } + + /** + * Construct a serializer using the given value as a delimiter. + * @param dataTypeDelimiter - the local data type delimiter. + */ + public NbtConfigurationSerializer(String dataTypeDelimiter) { + this.dataTypeDelimiter = dataTypeDelimiter; + } + + /** + * Retrieve the current data type delimiter. + * @return The current data type delimiter. + */ + public String getDataTypeDelimiter() { + return dataTypeDelimiter; + } + + /** + * Write the content of a NBT tag to a configuration section. + * @param value - the NBT tag to write. + * @param destination - the destination section. + */ + public void serialize(NbtBase value, final ConfigurationSection destination) { + value.accept(new NbtVisitor() { + private ConfigurationSection current = destination; + + // The current list we're working on + private List currentList; + + // Store the index of a configuration section that works like a list + private Map workingIndex = Maps.newHashMap(); + + @Override + public boolean visitEnter(NbtCompound compound) { + current = current.createSection(compound.getName()); + return true; + } + + @Override + public boolean visitEnter(NbtList list) { + Integer listIndex = getNextIndex(); + String name = getEncodedName(list, listIndex); + + if (list.getElementType().isComposite()) { + // Use a configuration section to store this list + current = current.createSection(name); + workingIndex.put(current, 0); + } else { + currentList = Lists.newArrayList(); + current.set(name, currentList); + } + return true; + } + + @Override + public boolean visitLeave(NbtCompound compound) { + current = current.getParent(); + return true; + } + + @Override + public boolean visitLeave(NbtList list) { + // Write the list to the configuration section + if (currentList != null) { + // Save and reset the temporary list + currentList = null; + } else { + // Go up a level + workingIndex.remove(current); + current = current.getParent(); + } + return true; + } + + @Override + public boolean visit(NbtBase node) { + // Are we working on a list? + if (currentList == null) { + Integer listIndex = getNextIndex(); + String name = getEncodedName(node, listIndex); + + // Save member + current.set(name, node.getValue()); + + } else { + currentList.add(node.getValue()); + } + return true; + } + + private Integer getNextIndex() { + Integer listIndex = workingIndex.get(current); + + if (listIndex != null) + return workingIndex.put(current, listIndex + 1); + else + return null; + } + + // We need to store the data type somehow + private String getEncodedName(NbtBase node, Integer index) { + if (index != null) + return index + dataTypeDelimiter + node.getType().getRawID(); + else + return node.getName() + dataTypeDelimiter + node.getType().getRawID(); + } + + private String getEncodedName(NbtList node, Integer index) { + if (index != null) + return index + dataTypeDelimiter + node.getElementType().getRawID(); + else + return node.getName() + dataTypeDelimiter + node.getElementType().getRawID(); + } + }); + } + + /** + * Read a NBT tag from a root configuration. + * @param root - configuration that contains the NBT tag. + * @param nodeName - name of the NBT tag. + * @return The read NBT tag. + */ + @SuppressWarnings("unchecked") + public NbtWrapper deserialize(ConfigurationSection root, String nodeName) { + return (NbtWrapper) readNode(root, nodeName); + } + + /** + * Read a NBT compound from a root configuration. + * @param root - configuration that contains the NBT compound. + * @param nodeName - name of the NBT compound. + * @return The read NBT compound. + */ + public NbtCompound deserializeCompound(YamlConfiguration root, String nodeName) { + return (NbtCompound) readNode(root, nodeName); + } + + /** + * Read a NBT compound from a root configuration. + * @param root - configuration that contains the NBT compound. + * @param nodeName - name of the NBT compound. + * @return The read NBT compound. + */ + @SuppressWarnings("unchecked") + public NbtList deserializeList(YamlConfiguration root, String nodeName) { + return (NbtList) readNode(root, nodeName); + } + + @SuppressWarnings("unchecked") + private NbtWrapper readNode(ConfigurationSection parent, String name) { + String[] decoded = getDecodedName(name); + Object node = parent.get(name); + NbtType type = NbtType.TAG_END; + + // It's possible that the caller isn't aware of the encoded name itself + if (node == null) { + for (String key : parent.getKeys(false)) { + decoded = getDecodedName(key); + + // Great + if (decoded[0].equals(name)) { + node = parent.get(decoded[0]); + break; + } + } + + // Inform the caller of the problem + if (node == null) { + throw new IllegalArgumentException("Unable to find node " + name + " in " + parent); + } + } + + // Attempt to decode a NBT type + if (decoded.length > 1) { + type = NbtType.getTypeFromID(Integer.parseInt(decoded[1])); + } + + // Is this a compound? + if (node instanceof ConfigurationSection) { + // Is this a list of a map? + if (type != NbtType.TAG_END) { + NbtList list = NbtFactory.ofList(decoded[0]); + ConfigurationSection section = (ConfigurationSection) node; + List sorted = sortSet(section.getKeys(false)); + + // Read everything in order + for (String key : sorted) { + NbtBase base = (NbtBase) readNode(section, key.toString()); + base.setName(NbtList.EMPTY_NAME); + list.getValue().add(base); + } + return (NbtWrapper) list; + + } else { + NbtCompound compound = NbtFactory.ofCompound(decoded[0]); + ConfigurationSection section = (ConfigurationSection) node; + + // As above + for (String key : section.getKeys(false)) + compound.put(readNode(section, key)); + return (NbtWrapper) compound; + } + + } else { + // We need to know + if (type == NbtType.TAG_END) { + throw new IllegalArgumentException("Cannot find encoded type of " + decoded[0] + " in " + name); + } + + if (node instanceof List) { + NbtList list = NbtFactory.ofList(decoded[0]); + list.setElementType(type); + + for (Object value : (List) node) { + list.addClosest(value); + } + + // Add the list + return (NbtWrapper) list; + + } else { + // Normal node + return NbtFactory.ofWrapper(type, decoded[0], node); + } + } + } + + private List sortSet(Set unsorted) { + // Convert to integers + List sorted = new ArrayList(unsorted); + + Collections.sort(sorted, new Comparator() { + @Override + public int compare(String o1, String o2) { + // Parse the name + int index1 = Integer.parseInt(getDecodedName(o1)[0]); + int index2 = Integer.parseInt(getDecodedName(o2)[0]); + return Ints.compare(index1, index2); + } + }); + return sorted; + } + + private String[] getDecodedName(String nodeName) { + int delimiter = nodeName.lastIndexOf('$'); + + if (delimiter > 0) + return new String[] { nodeName.substring(0, delimiter), nodeName.substring(delimiter + 1) }; + else + return new String[] { nodeName }; + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtTextSerializer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtTextSerializer.java new file mode 100644 index 00000000..b64ddb55 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtTextSerializer.java @@ -0,0 +1,119 @@ +package com.comphenix.protocol.wrappers.nbt.io; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.math.BigInteger; + +import com.comphenix.protocol.wrappers.nbt.NbtBase; +import com.comphenix.protocol.wrappers.nbt.NbtCompound; +import com.comphenix.protocol.wrappers.nbt.NbtList; +import com.comphenix.protocol.wrappers.nbt.NbtWrapper; + +/** + * Serializes NBT to a base N (default 32) encoded string and back. + * + * @author Kristian + */ +public class NbtTextSerializer { + /** + * The default radix to use while converting to text. + */ + public static final int STANDARD_BASE = 32; + + /** + * A default instance of this serializer. + */ + public static final NbtTextSerializer DEFAULT = new NbtTextSerializer(); + + private NbtBinarySerializer binarySerializer; + private int baseRadix; + + public NbtTextSerializer() { + this(new NbtBinarySerializer(), STANDARD_BASE); + } + + /** + * Construct a serializer with a custom binary serializer and base radix. + * @param binary - binary serializer. + * @param baseRadix - base radix in the range 2 - 32. + */ + public NbtTextSerializer(NbtBinarySerializer binary, int baseRadix) { + this.binarySerializer = binary; + this.baseRadix = baseRadix; + } + + /** + * Retrieve the binary serializer that is used. + * @return The binary serializer. + */ + public NbtBinarySerializer getBinarySerializer() { + return binarySerializer; + } + + /** + * Retrieve the base radix. + * @return The base radix. + */ + public int getBaseRadix() { + return baseRadix; + } + + /** + * Serialize a NBT tag to a String. + * @param value - the NBT tag to serialize. + * @return The NBT tag in base N form. + */ + public String serialize(NbtBase value) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + DataOutputStream dataOutput = new DataOutputStream(outputStream); + + binarySerializer.serialize(value, dataOutput); + + // Serialize that array + return new BigInteger(1, outputStream.toByteArray()).toString(baseRadix); + } + + /** + * Deserialize a NBT tag from a base N encoded string. + * @param input - the base N string. + * @return The NBT tag contained in the string. + * @throws IOException If we are unable to parse the input. + */ + public NbtWrapper deserialize(String input) throws IOException { + try { + BigInteger baseN = new BigInteger(input, baseRadix); + ByteArrayInputStream inputStream = new ByteArrayInputStream(baseN.toByteArray()); + + return binarySerializer.deserialize(new DataInputStream(inputStream)); + + } catch (NumberFormatException e) { + throw new IOException("Input is not valid base " + baseRadix + ".", e); + } + } + + /** + * Deserialize a NBT compound from a base N encoded string. + * @param input - the base N string. + * @return The NBT tag contained in the string. + * @throws IOException If we are unable to parse the input. + */ + @SuppressWarnings("rawtypes") + public NbtCompound deserializeCompound(String input) throws IOException { + // I always seem to override generics ... + return (NbtCompound) (NbtBase) deserialize(input); + } + + /** + * Deserialize a NBT list from a base N encoded string. + * @param input - the base N string. + * @return The NBT tag contained in the string. + * @throws IOException If we are unable to parse the input. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public NbtList deserializeList(String input) throws IOException { + return (NbtList) (NbtBase) deserialize(input); + } +} diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java index 3b807eb3..bfeea39d 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtCompoundTest.java @@ -90,6 +90,11 @@ public class NbtCompoundTest { @Override public NbtBase deepClone() { return new NbtCustomTag(name, value); + } + + @Override + public boolean accept(NbtVisitor visitor) { + return visitor.visit(this); } } } diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java index f31d301b..c2504df0 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/NbtFactoryTest.java @@ -30,6 +30,7 @@ import org.junit.BeforeClass; import org.junit.Test; import com.comphenix.protocol.utility.MinecraftReflection; +import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer; public class NbtFactoryTest { @BeforeClass @@ -54,7 +55,7 @@ public class NbtFactoryTest { ByteArrayInputStream source = new ByteArrayInputStream(buffer.toByteArray()); DataInput input = new DataInputStream(source); - NbtCompound cloned = (NbtCompound) NbtFactory.fromStream(input); + NbtCompound cloned = NbtBinarySerializer.DEFAULT.deserializeCompound(input); assertEquals(compound.getString("name"), cloned.getString("name")); assertEquals(compound.getInteger("age"), cloned.getInteger("age")); diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializerTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializerTest.java new file mode 100644 index 00000000..3b3888a3 --- /dev/null +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializerTest.java @@ -0,0 +1,37 @@ +package com.comphenix.protocol.wrappers.nbt.io; + +import static org.junit.Assert.*; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.comphenix.protocol.utility.MinecraftReflection; +import com.comphenix.protocol.wrappers.nbt.NbtCompound; +import com.comphenix.protocol.wrappers.nbt.NbtFactory; + +public class NbtConfigurationSerializerTest { + @BeforeClass + public static void initializeBukkit() { + // Initialize reflection + MinecraftReflection.setMinecraftPackage("net.minecraft.server.v1_4_6", "org.bukkit.craftbukkit.v1_4_6"); + } + + @SuppressWarnings("unchecked") + @Test + public void testSerialization() { + NbtCompound compound = NbtFactory.ofCompound("hello"); + compound.put("age", (short) 30); + compound.put("name", "test"); + compound.put(NbtFactory.ofList("telephone", "12345678", "81549300")); + + compound.put(NbtFactory.ofList("lists", NbtFactory.ofList("", "a", "a", "b", "c"))); + + YamlConfiguration yaml = new YamlConfiguration(); + NbtConfigurationSerializer.DEFAULT.serialize(compound, yaml); + + NbtCompound result = NbtConfigurationSerializer.DEFAULT.deserializeCompound(yaml, "hello"); + + assertEquals(compound, result); + } +} From f791e2726f0d7a71fd5e0755ae97e8c432ced592 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 9 Jan 2013 04:52:53 +0100 Subject: [PATCH 20/26] Inform about the purpose of the fromItemStack (fromItemTag) method. --- .../com/comphenix/protocol/wrappers/nbt/NbtFactory.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java index 4c932ade..6ae15713 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/NbtFactory.java @@ -112,14 +112,16 @@ public class NbtFactory { } /** - * Construct a wrapper for an NBT tag stored (in memory) in an item stack. + * Construct a wrapper for an NBT tag stored (in memory) in an item stack. This is where + * auxillary data such as enchanting, name and lore is stored. It doesn't include the items + * material, damage value or count. *

* The item stack must be a wrapper for a CraftItemStack. Use * {@link MinecraftReflection#getBukkitItemStack(ItemStack)} if not. * @param stack - the item stack. * @return A wrapper for its NBT tag. */ - public static NbtWrapper fromItemStack(ItemStack stack) { + public static NbtWrapper fromItemTag(ItemStack stack) { if (!MinecraftReflection.isCraftItemStack(stack)) throw new IllegalArgumentException("Stack must be a CraftItemStack."); From 0597084c3dd73a89186e5a93f7af2eb0d3a41453 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 9 Jan 2013 07:05:36 +0100 Subject: [PATCH 21/26] Switched to a better base64 encoding and decoding method. --- .../protocol/utility/StreamSerializer.java | 25 +++----- .../wrappers/nbt/io/NbtTextSerializer.java | 57 ++++++------------- 2 files changed, 27 insertions(+), 55 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java index 5203a22d..ac4fe360 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java @@ -6,9 +6,8 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Method; -import java.math.BigInteger; - import org.bukkit.inventory.ItemStack; +import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; import com.comphenix.protocol.reflect.FuzzyReflection; @@ -50,21 +49,15 @@ public class StreamSerializer { } /** - * Deserialize an item stack from a base-32 encoded string. - * @param input - base-32 encoded string. + * Deserialize an item stack from a base-64 encoded string. + * @param input - base-64 encoded string. * @return A deserialized item stack. * @throws IOException If the operation failed due to reflection or corrupt data. */ public ItemStack deserializeItemStack(String input) throws IOException { - try { - BigInteger base32 = new BigInteger(input, 32); - ByteArrayInputStream inputStream = new ByteArrayInputStream(base32.toByteArray()); - - return deserializeItemStack(new DataInputStream(inputStream)); - - } catch (NumberFormatException e) { - throw new IOException("Input is not valid base 32.", e); - } + ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(input)); + + return deserializeItemStack(new DataInputStream(inputStream)); } /** @@ -93,9 +86,9 @@ public class StreamSerializer { } /** - * Serialize an item stack as a base-32 encoded string. + * Serialize an item stack as a base-64 encoded string. * @param stack - the item stack to serialize. - * @return A base-32 representation of the given item stack. + * @return A base-64 representation of the given item stack. * @throws IOException If the operation fails due to reflection problems. */ public String serializeItemStack(ItemStack stack) throws IOException { @@ -105,6 +98,6 @@ public class StreamSerializer { serializeItemStack(dataOutput, stack); // Serialize that array - return new BigInteger(1, outputStream.toByteArray()).toString(32); + return Base64Coder.encodeLines(outputStream.toByteArray()); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtTextSerializer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtTextSerializer.java index b64ddb55..c9e2b8ed 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtTextSerializer.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtTextSerializer.java @@ -5,7 +5,8 @@ import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.math.BigInteger; + +import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; import com.comphenix.protocol.wrappers.nbt.NbtBase; import com.comphenix.protocol.wrappers.nbt.NbtCompound; @@ -13,36 +14,28 @@ import com.comphenix.protocol.wrappers.nbt.NbtList; import com.comphenix.protocol.wrappers.nbt.NbtWrapper; /** - * Serializes NBT to a base N (default 32) encoded string and back. + * Serializes NBT to a base-64 encoded string and back. * * @author Kristian */ public class NbtTextSerializer { - /** - * The default radix to use while converting to text. - */ - public static final int STANDARD_BASE = 32; - /** * A default instance of this serializer. */ public static final NbtTextSerializer DEFAULT = new NbtTextSerializer(); private NbtBinarySerializer binarySerializer; - private int baseRadix; public NbtTextSerializer() { - this(new NbtBinarySerializer(), STANDARD_BASE); + this(new NbtBinarySerializer()); } /** - * Construct a serializer with a custom binary serializer and base radix. + * Construct a serializer with a custom binary serializer. * @param binary - binary serializer. - * @param baseRadix - base radix in the range 2 - 32. */ - public NbtTextSerializer(NbtBinarySerializer binary, int baseRadix) { + public NbtTextSerializer(NbtBinarySerializer binary) { this.binarySerializer = binary; - this.baseRadix = baseRadix; } /** @@ -54,17 +47,9 @@ public class NbtTextSerializer { } /** - * Retrieve the base radix. - * @return The base radix. - */ - public int getBaseRadix() { - return baseRadix; - } - - /** - * Serialize a NBT tag to a String. + * Serialize a NBT tag to a base-64 encoded string. * @param value - the NBT tag to serialize. - * @return The NBT tag in base N form. + * @return The NBT tag in base-64 form. */ public String serialize(NbtBase value) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); @@ -73,30 +58,24 @@ public class NbtTextSerializer { binarySerializer.serialize(value, dataOutput); // Serialize that array - return new BigInteger(1, outputStream.toByteArray()).toString(baseRadix); + return Base64Coder.encodeLines(outputStream.toByteArray()); } /** - * Deserialize a NBT tag from a base N encoded string. - * @param input - the base N string. + * Deserialize a NBT tag from a base-64 encoded string. + * @param input - the base-64 string. * @return The NBT tag contained in the string. * @throws IOException If we are unable to parse the input. */ public NbtWrapper deserialize(String input) throws IOException { - try { - BigInteger baseN = new BigInteger(input, baseRadix); - ByteArrayInputStream inputStream = new ByteArrayInputStream(baseN.toByteArray()); - - return binarySerializer.deserialize(new DataInputStream(inputStream)); - - } catch (NumberFormatException e) { - throw new IOException("Input is not valid base " + baseRadix + ".", e); - } + ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(input)); + + return binarySerializer.deserialize(new DataInputStream(inputStream)); } /** - * Deserialize a NBT compound from a base N encoded string. - * @param input - the base N string. + * Deserialize a NBT compound from a base-64 encoded string. + * @param input - the base-64 string. * @return The NBT tag contained in the string. * @throws IOException If we are unable to parse the input. */ @@ -107,8 +86,8 @@ public class NbtTextSerializer { } /** - * Deserialize a NBT list from a base N encoded string. - * @param input - the base N string. + * Deserialize a NBT list from a base-64 encoded string. + * @param input - the base-64 string. * @return The NBT tag contained in the string. * @throws IOException If we are unable to parse the input. */ From 221ce71b97b27a87661a7144a2aa5c2ba4016e2c Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 9 Jan 2013 07:06:57 +0100 Subject: [PATCH 22/26] Increment to 1.9.2-SNAPSHOT. --- ProtocolLib/pom.xml | 2 +- ProtocolLib/src/main/resources/plugin.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ProtocolLib/pom.xml b/ProtocolLib/pom.xml index a4291eae..8d50d8cd 100644 --- a/ProtocolLib/pom.xml +++ b/ProtocolLib/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.comphenix.protocol ProtocolLib - 1.9.1-SNAPSHOT + 1.9.2-SNAPSHOT jar Provides read/write access to the Minecraft protocol. diff --git a/ProtocolLib/src/main/resources/plugin.yml b/ProtocolLib/src/main/resources/plugin.yml index 96a0216e..d295eed5 100644 --- a/ProtocolLib/src/main/resources/plugin.yml +++ b/ProtocolLib/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ProtocolLib -version: 1.9.1-SNAPSHOT +version: 1.9.2-SNAPSHOT description: Provides read/write access to the Minecraft protocol. author: Comphenix website: http://www.comphenix.net/ProtocolLib From 10d5163ef543d4cb60c6f2b4be8247075434b7ad Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 9 Jan 2013 14:45:00 +0100 Subject: [PATCH 23/26] Added the ability to save int arrays in configurations. --- .../nbt/io/NbtConfigurationSerializer.java | 56 +++++++++++++++++-- .../io/NbtConfigurationSerializerTest.java | 1 + 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializer.java index acdf3271..7ddc9f1c 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializer.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializer.java @@ -1,5 +1,7 @@ package com.comphenix.protocol.wrappers.nbt.io; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -129,10 +131,10 @@ public class NbtConfigurationSerializer { String name = getEncodedName(node, listIndex); // Save member - current.set(name, node.getValue()); + current.set(name, fromNodeValue(node)); } else { - currentList.add(node.getValue()); + currentList.add(fromNodeValue(node)); } return true; } @@ -261,7 +263,7 @@ public class NbtConfigurationSerializer { list.setElementType(type); for (Object value : (List) node) { - list.addClosest(value); + list.addClosest(toNodeValue(value, type)); } // Add the list @@ -269,7 +271,7 @@ public class NbtConfigurationSerializer { } else { // Normal node - return NbtFactory.ofWrapper(type, decoded[0], node); + return NbtFactory.ofWrapper(type, decoded[0], toNodeValue(node, type)); } } } @@ -290,7 +292,51 @@ public class NbtConfigurationSerializer { return sorted; } - private String[] getDecodedName(String nodeName) { + // Ensure that int arrays are converted to byte arrays + private Object fromNodeValue(NbtBase base) { + if (base.getType() == NbtType.TAG_INT_ARRAY) + return toByteArray((int[]) base.getValue()); + else + return base.getValue(); + } + + // Convert them back + public Object toNodeValue(Object value, NbtType type) { + if (type == NbtType.TAG_INT_ARRAY) + return toIntegerArray((byte[]) value); + else + return value; + } + + /** + * Convert an integer array to an equivalent byte array. + * @param data - the integer array with the data. + * @return An equivalent byte array. + */ + private static byte[] toByteArray(int[] data) { + ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 4); + IntBuffer intBuffer = byteBuffer.asIntBuffer(); + + intBuffer.put(data); + return byteBuffer.array(); + } + + /** + * Convert a byte array to the equivalent integer array. + *

+ * Note that the number of byte elements are only perserved if the byte size is a multiple of four. + * @param data - the byte array to convert. + * @return The equivalent integer array. + */ + private static int[] toIntegerArray(byte[] data) { + IntBuffer source = ByteBuffer.wrap(data).asIntBuffer(); + IntBuffer copy = IntBuffer.allocate(source.capacity()); + + copy.put(source); + return copy.array(); + } + + private static String[] getDecodedName(String nodeName) { int delimiter = nodeName.lastIndexOf('$'); if (delimiter > 0) diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializerTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializerTest.java index 3b3888a3..916875ab 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializerTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/wrappers/nbt/io/NbtConfigurationSerializerTest.java @@ -23,6 +23,7 @@ public class NbtConfigurationSerializerTest { NbtCompound compound = NbtFactory.ofCompound("hello"); compound.put("age", (short) 30); compound.put("name", "test"); + compound.put("values", new int[] { 1, 2, 3}); compound.put(NbtFactory.ofList("telephone", "12345678", "81549300")); compound.put(NbtFactory.ofList("lists", NbtFactory.ofList("", "a", "a", "b", "c"))); From f2c955985c4f41c678cfa2afe078df7eaa7a8d96 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 9 Jan 2013 23:35:14 +0100 Subject: [PATCH 24/26] Increment to version 1.10.0 --- ProtocolLib/pom.xml | 2 +- ProtocolLib/src/main/resources/plugin.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ProtocolLib/pom.xml b/ProtocolLib/pom.xml index 8d50d8cd..6869a976 100644 --- a/ProtocolLib/pom.xml +++ b/ProtocolLib/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.comphenix.protocol ProtocolLib - 1.9.2-SNAPSHOT + 1.10.0 jar Provides read/write access to the Minecraft protocol. diff --git a/ProtocolLib/src/main/resources/plugin.yml b/ProtocolLib/src/main/resources/plugin.yml index d295eed5..e9253c4b 100644 --- a/ProtocolLib/src/main/resources/plugin.yml +++ b/ProtocolLib/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ProtocolLib -version: 1.9.2-SNAPSHOT +version: 1.10.0 description: Provides read/write access to the Minecraft protocol. author: Comphenix website: http://www.comphenix.net/ProtocolLib From 12c15b23aba9e02de2daba78e026c6aa5c58ab83 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Thu, 10 Jan 2013 00:21:51 +0100 Subject: [PATCH 25/26] Increment to 2.0.0 instead. This is to indiciate that this version is incompatible with older versions. Although, it should really have occured at 1.8.0. --- ProtocolLib/dependency-reduced-pom.xml | 2 +- ProtocolLib/pom.xml | 2 +- ProtocolLib/src/main/resources/plugin.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ProtocolLib/dependency-reduced-pom.xml b/ProtocolLib/dependency-reduced-pom.xml index 33fc026b..ed003908 100644 --- a/ProtocolLib/dependency-reduced-pom.xml +++ b/ProtocolLib/dependency-reduced-pom.xml @@ -4,7 +4,7 @@ com.comphenix.protocol ProtocolLib ProtocolLib - 1.9.1-SNAPSHOT + 1.10.0 Provides read/write access to the Minecraft protocol. http://dev.bukkit.org/server-mods/protocollib/ diff --git a/ProtocolLib/pom.xml b/ProtocolLib/pom.xml index 6869a976..a81c34d1 100644 --- a/ProtocolLib/pom.xml +++ b/ProtocolLib/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.comphenix.protocol ProtocolLib - 1.10.0 + 2.0.0 jar Provides read/write access to the Minecraft protocol. diff --git a/ProtocolLib/src/main/resources/plugin.yml b/ProtocolLib/src/main/resources/plugin.yml index e9253c4b..5acbb65b 100644 --- a/ProtocolLib/src/main/resources/plugin.yml +++ b/ProtocolLib/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ProtocolLib -version: 1.10.0 +version: 2.0.0 description: Provides read/write access to the Minecraft protocol. author: Comphenix website: http://www.comphenix.net/ProtocolLib From 9c86a36afb834ea4d8c3070049b33f8c0717afcb Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Thu, 10 Jan 2013 00:51:52 +0100 Subject: [PATCH 26/26] Stop tracking the dependency reduced POM. --- .gitignore | 1 + ProtocolLib/dependency-reduced-pom.xml | 218 ------------------------- 2 files changed, 1 insertion(+), 218 deletions(-) create mode 100644 .gitignore delete mode 100644 ProtocolLib/dependency-reduced-pom.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..916e17c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +dependency-reduced-pom.xml diff --git a/ProtocolLib/dependency-reduced-pom.xml b/ProtocolLib/dependency-reduced-pom.xml deleted file mode 100644 index ed003908..00000000 --- a/ProtocolLib/dependency-reduced-pom.xml +++ /dev/null @@ -1,218 +0,0 @@ - - - 4.0.0 - com.comphenix.protocol - ProtocolLib - ProtocolLib - 1.10.0 - Provides read/write access to the Minecraft protocol. - http://dev.bukkit.org/server-mods/protocollib/ - - - aadnk - Kristian S. Stangeland - kr_stang@hotmail.com - http://comphenix.net/ - - developer - maintainer - - 1 - - - - - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - http://www.gnu.org/licenses/gpl-2.0.txt - repo - - - - scm:git:git://github.com/aadnk/ProtocolLib.git - scm:git:git@github.com:aadnk/ProtocolLib.git - https://github.com/aadnk/ProtocolLib - - - src/main/java - src/test/java - clean install - - - src/main/resources - - **/*.java - - - - - - maven-shade-plugin - 2.0 - - - package - - shade - - - false - true - - - net.sf - com.comphenix.net.sf - - - - - org.bukkit:craftbukkit - junit:junit - - - - - - - - maven-compiler-plugin - 2.3.2 - - 1.6 - 1.6 - - - - - - - release-sign-artifacts - - - - maven-source-plugin - 2.2 - - - attach-sources - - jar - - - - - - maven-javadoc-plugin - 2.9 - - - attach-javadocs - - jar - - - - - - maven-gpg-plugin - 1.4 - - - sign-artifacts - verify - - sign - - - - - - - - - - - bukkit-rep - http://repo.bukkit.org/content/groups/public - - - - - org.bukkit - craftbukkit - 1.4.6-R0.1 - provided - - - junit - junit - 4.10 - test - - - hamcrest-core - org.hamcrest - - - - - org.mockito - mockito-all - 1.8.4 - test - - - org.powermock - powermock-module-junit4 - 1.5 - test - - - powermock-module-junit4-common - org.powermock - - - - - org.powermock - powermock-api-mockito - 1.5 - test - - - powermock-api-support - org.powermock - - - - - - - - org.codehaus.mojo - findbugs-maven-plugin - 2.5.2 - - High - Default - - - - - - - comphenix-releases - Comphenix Maven Releases - http://repo.comphenix.net/content/repositories/releases/ - - - comphenix-snapshots - Comphenix Maven Snapshots - http://repo.comphenix.net/content/repositories/snapshots/ - - - - 1.5 - cp1252 - - -